OSDN Git Service

first commit
authorpeeweedee <peeweedee@users.sourceforge.jp>
Wed, 29 May 2013 04:18:09 +0000 (13:18 +0900)
committerpeeweedee <peeweedee@users.sourceforge.jp>
Wed, 29 May 2013 04:18:09 +0000 (13:18 +0900)
291 files changed:
TinyBannavi/01_readme.txt [new file with mode: 0644]
TinyBannavi/02_usage.txt [new file with mode: 0644]
TinyBannavi/04_license.txt [new file with mode: 0644]
TinyBannavi/05_history.txt [new file with mode: 0644]
TinyBannavi/TaiNavi for Mac.app/Contents/Info.plist [new file with mode: 0644]
TinyBannavi/TaiNavi for Mac.app/Contents/Resources/tainavi.icns [new file with mode: 0644]
TinyBannavi/_lock_ [new file with mode: 0644]
TinyBannavi/_update.cmd [new file with mode: 0644]
TinyBannavi/_update.sh [new file with mode: 0644]
TinyBannavi/icon/bookmark-new-list-4.png [new file with mode: 0644]
TinyBannavi/icon/camera.png [new file with mode: 0644]
TinyBannavi/icon/checkbox.png [new file with mode: 0644]
TinyBannavi/icon/colorize-2.png [new file with mode: 0644]
TinyBannavi/icon/down-arrow.png [new file with mode: 0644]
TinyBannavi/icon/folder-video.png [new file with mode: 0644]
TinyBannavi/icon/help-browser-2.png [new file with mode: 0644]
TinyBannavi/icon/internet-news-reader.png [new file with mode: 0644]
TinyBannavi/icon/media-record-3.png [new file with mode: 0644]
TinyBannavi/icon/remocon.png [new file with mode: 0644]
TinyBannavi/icon/remocon16.png [new file with mode: 0644]
TinyBannavi/icon/remostat.png [new file with mode: 0644]
TinyBannavi/icon/remostat16.png [new file with mode: 0644]
TinyBannavi/icon/system-search-2.png [new file with mode: 0644]
TinyBannavi/icon/system-shutdown-2.png [new file with mode: 0644]
TinyBannavi/icon/system-software-update-2.png [new file with mode: 0644]
TinyBannavi/icon/system.png [new file with mode: 0644]
TinyBannavi/icon/tainavi.icns [new file with mode: 0644]
TinyBannavi/icon/tainavi.ico [new file with mode: 0644]
TinyBannavi/icon/tainavi.png [new file with mode: 0644]
TinyBannavi/icon/tainavi16.png [new file with mode: 0644]
TinyBannavi/icon/taisync-alarm.png [new file with mode: 0644]
TinyBannavi/icon/taisync-alert.png [new file with mode: 0644]
TinyBannavi/icon/taisync.png [new file with mode: 0644]
TinyBannavi/icon/tool_timer.png [new file with mode: 0644]
TinyBannavi/icon/user-offline.png [new file with mode: 0644]
TinyBannavi/icon/utilities-log_viewer.png [new file with mode: 0644]
TinyBannavi/icon/video-television.png [new file with mode: 0644]
TinyBannavi/icon/view-calendar-time-spent.png [new file with mode: 0644]
TinyBannavi/icon/view-calendar-timeline.png [new file with mode: 0644]
TinyBannavi/icon/view-close.png [new file with mode: 0644]
TinyBannavi/icon/view-fullscreen-5.png [new file with mode: 0644]
TinyBannavi/icon/view-nofullscreen-3.png [new file with mode: 0644]
TinyBannavi/icon/view-split-top-bottom-3.png [new file with mode: 0644]
TinyBannavi/packetcapture.cmd [new file with mode: 0644]
TinyBannavi/remotectrl.cmd [new file with mode: 0644]
TinyBannavi/splash.gif [new file with mode: 0644]
TinyBannavi/src/.gitignore [new file with mode: 0644]
TinyBannavi/src/epgdump/Aribstr.java [new file with mode: 0644]
TinyBannavi/src/epgdump/ContentDesc.java [new file with mode: 0644]
TinyBannavi/src/epgdump/EEVTDhead.java [new file with mode: 0644]
TinyBannavi/src/epgdump/EEVTDitem.java [new file with mode: 0644]
TinyBannavi/src/epgdump/EEVTDtail.java [new file with mode: 0644]
TinyBannavi/src/epgdump/EIT_CONTROL.java [new file with mode: 0644]
TinyBannavi/src/epgdump/EITbody.java [new file with mode: 0644]
TinyBannavi/src/epgdump/EIThead.java [new file with mode: 0644]
TinyBannavi/src/epgdump/Eit.java [new file with mode: 0644]
TinyBannavi/src/epgdump/Epgdump.java [new file with mode: 0644]
TinyBannavi/src/epgdump/Main.java [new file with mode: 0644]
TinyBannavi/src/epgdump/SDTbody.java [new file with mode: 0644]
TinyBannavi/src/epgdump/SDThead.java [new file with mode: 0644]
TinyBannavi/src/epgdump/SECcache.java [new file with mode: 0644]
TinyBannavi/src/epgdump/SEVTdesc.java [new file with mode: 0644]
TinyBannavi/src/epgdump/STATION.java [new file with mode: 0644]
TinyBannavi/src/epgdump/SVCdesc.java [new file with mode: 0644]
TinyBannavi/src/epgdump/SVT_CONTROL.java [new file with mode: 0644]
TinyBannavi/src/epgdump/Sdt.java [new file with mode: 0644]
TinyBannavi/src/epgdump/SeriesDesc.java [new file with mode: 0644]
TinyBannavi/src/epgdump/TSpacket.java [new file with mode: 0644]
TinyBannavi/src/epgdump/Ts.java [new file with mode: 0644]
TinyBannavi/src/epgdump/Util.java [new file with mode: 0644]
TinyBannavi/src/httpDump/Main.java [new file with mode: 0644]
TinyBannavi/src/httpDump/Server.java [new file with mode: 0644]
TinyBannavi/src/httpDump/Viewer.java [new file with mode: 0644]
TinyBannavi/src/niseRD/Main.java [new file with mode: 0644]
TinyBannavi/src/niseRD/Server.java [new file with mode: 0644]
TinyBannavi/src/niseRD/Viewer.java [new file with mode: 0644]
TinyBannavi/src/remoteCtrl/Viewer.java [new file with mode: 0644]
TinyBannavi/src/statusView/Viewer.java [new file with mode: 0644]
TinyBannavi/src/taiSync/Env.java [new file with mode: 0644]
TinyBannavi/src/taiSync/HTTPServer.java [new file with mode: 0644]
TinyBannavi/src/taiSync/POPServer.java [new file with mode: 0644]
TinyBannavi/src/taiSync/RecorderInfo.java [new file with mode: 0644]
TinyBannavi/src/taiSync/ReqCtrl.java [new file with mode: 0644]
TinyBannavi/src/taiSync/ReserveCtrl.java [new file with mode: 0644]
TinyBannavi/src/taiSync/ReserveInfo.java [new file with mode: 0644]
TinyBannavi/src/taiSync/SMTPServer.java [new file with mode: 0644]
TinyBannavi/src/taiSync/VersionInfo.java [new file with mode: 0644]
TinyBannavi/src/taiSync/Viewer.java [new file with mode: 0644]
TinyBannavi/src/tainavi/AVSetting.java [new file with mode: 0644]
TinyBannavi/src/tainavi/AVs.java [new file with mode: 0644]
TinyBannavi/src/tainavi/AbsChannelConvertView.java [new file with mode: 0644]
TinyBannavi/src/tainavi/AbsChannelDatSettingView.java [new file with mode: 0644]
TinyBannavi/src/tainavi/AbsChannelSettingView.java [new file with mode: 0644]
TinyBannavi/src/tainavi/AbsChannelSortView.java [new file with mode: 0644]
TinyBannavi/src/tainavi/AbsExtensionDialog.java [new file with mode: 0644]
TinyBannavi/src/tainavi/AbsKeywordDialog.java [new file with mode: 0644]
TinyBannavi/src/tainavi/AbsListedView.java [new file with mode: 0644]
TinyBannavi/src/tainavi/AbsPaperColorsDialog.java [new file with mode: 0644]
TinyBannavi/src/tainavi/AbsPaperView.java [new file with mode: 0644]
TinyBannavi/src/tainavi/AbsRecordedListView.java [new file with mode: 0644]
TinyBannavi/src/tainavi/AbsRecorderSettingView.java [new file with mode: 0644]
TinyBannavi/src/tainavi/AbsReserveDialog.java [new file with mode: 0644]
TinyBannavi/src/tainavi/AbsReserveListView.java [new file with mode: 0644]
TinyBannavi/src/tainavi/AbsSettingView.java [new file with mode: 0644]
TinyBannavi/src/tainavi/AbsToolBar.java [new file with mode: 0644]
TinyBannavi/src/tainavi/AreaCode.java [new file with mode: 0644]
TinyBannavi/src/tainavi/ArpCommand.java [new file with mode: 0644]
TinyBannavi/src/tainavi/BackgroundWorker.java [new file with mode: 0644]
TinyBannavi/src/tainavi/Bounds.java [new file with mode: 0644]
TinyBannavi/src/tainavi/BroadcastType.java [new file with mode: 0644]
TinyBannavi/src/tainavi/CHAVSetting.java [new file with mode: 0644]
TinyBannavi/src/tainavi/Center.java [new file with mode: 0644]
TinyBannavi/src/tainavi/ChannelCode.java [new file with mode: 0644]
TinyBannavi/src/tainavi/ChannelConvert.java [new file with mode: 0644]
TinyBannavi/src/tainavi/ChannelSettingPanel.java [new file with mode: 0644]
TinyBannavi/src/tainavi/ChannelSort.java [new file with mode: 0644]
TinyBannavi/src/tainavi/ChippedBorder.java [new file with mode: 0644]
TinyBannavi/src/tainavi/ClipboardInfo.java [new file with mode: 0644]
TinyBannavi/src/tainavi/ClipboardInfoList.java [new file with mode: 0644]
TinyBannavi/src/tainavi/CommonSwingUtils.java [new file with mode: 0644]
TinyBannavi/src/tainavi/CommonUtils.java [new file with mode: 0644]
TinyBannavi/src/tainavi/ContentIdDIMORA.java [new file with mode: 0644]
TinyBannavi/src/tainavi/ContentIdEDCB.java [new file with mode: 0644]
TinyBannavi/src/tainavi/ContentIdREGZA.java [new file with mode: 0644]
TinyBannavi/src/tainavi/ContentIdSyobo.java [new file with mode: 0644]
TinyBannavi/src/tainavi/DashBorder.java [new file with mode: 0644]
TinyBannavi/src/tainavi/DebugPrintStream.java [new file with mode: 0644]
TinyBannavi/src/tainavi/DebugShowTat.java [new file with mode: 0644]
TinyBannavi/src/tainavi/DumpHttp.java [new file with mode: 0644]
TinyBannavi/src/tainavi/EditorColumn.java [new file with mode: 0644]
TinyBannavi/src/tainavi/EditorComboColumn.java [new file with mode: 0644]
TinyBannavi/src/tainavi/EncryptPassword.java [new file with mode: 0644]
TinyBannavi/src/tainavi/Env.java [new file with mode: 0644]
TinyBannavi/src/tainavi/ExtProgram.java [new file with mode: 0644]
TinyBannavi/src/tainavi/FieldUtils.java [new file with mode: 0644]
TinyBannavi/src/tainavi/GetEventId.java [new file with mode: 0644]
TinyBannavi/src/tainavi/GetRDStatus.java [new file with mode: 0644]
TinyBannavi/src/tainavi/HDDRecorder.java [new file with mode: 0644]
TinyBannavi/src/tainavi/HDDRecorderList.java [new file with mode: 0644]
TinyBannavi/src/tainavi/HDDRecorderUtils.java [new file with mode: 0644]
TinyBannavi/src/tainavi/JCCLabel.java [new file with mode: 0644]
TinyBannavi/src/tainavi/JCheckBoxPanel.java [new file with mode: 0644]
TinyBannavi/src/tainavi/JColorChooseSlider.java [new file with mode: 0644]
TinyBannavi/src/tainavi/JComboBoxPanel.java [new file with mode: 0644]
TinyBannavi/src/tainavi/JComboBoxWithPopup.java [new file with mode: 0644]
TinyBannavi/src/tainavi/JDetailPanel.java [new file with mode: 0644]
TinyBannavi/src/tainavi/JListSortDialog.java [new file with mode: 0644]
TinyBannavi/src/tainavi/JNETable.java [new file with mode: 0644]
TinyBannavi/src/tainavi/JOptOptionPane.java [new file with mode: 0644]
TinyBannavi/src/tainavi/JRMLabel.java [new file with mode: 0644]
TinyBannavi/src/tainavi/JRadioButtonPanel.java [new file with mode: 0644]
TinyBannavi/src/tainavi/JSliderPanel.java [new file with mode: 0644]
TinyBannavi/src/tainavi/JTXTButton.java [new file with mode: 0644]
TinyBannavi/src/tainavi/JTXTLabel.java [new file with mode: 0644]
TinyBannavi/src/tainavi/JTableRowHeader.java [new file with mode: 0644]
TinyBannavi/src/tainavi/JTaggedLayeredPane.java [new file with mode: 0644]
TinyBannavi/src/tainavi/JTextAreaWithPopup.java [new file with mode: 0644]
TinyBannavi/src/tainavi/JTextFieldPanel.java [new file with mode: 0644]
TinyBannavi/src/tainavi/JTextFieldWithPopup.java [new file with mode: 0644]
TinyBannavi/src/tainavi/JTimebarLabel.java [new file with mode: 0644]
TinyBannavi/src/tainavi/JTreeLabel.java [new file with mode: 0644]
TinyBannavi/src/tainavi/JWideComboBox.java [new file with mode: 0644]
TinyBannavi/src/tainavi/LikeReserveItem.java [new file with mode: 0644]
TinyBannavi/src/tainavi/LikeReserveList.java [new file with mode: 0644]
TinyBannavi/src/tainavi/Locker.java [new file with mode: 0644]
TinyBannavi/src/tainavi/LogViewer.java [new file with mode: 0644]
TinyBannavi/src/tainavi/MapCtrl.java [new file with mode: 0644]
TinyBannavi/src/tainavi/MarkChar.java [new file with mode: 0644]
TinyBannavi/src/tainavi/MarkedHistory.java [new file with mode: 0644]
TinyBannavi/src/tainavi/MarkedHistoryList.java [new file with mode: 0644]
TinyBannavi/src/tainavi/MarkedProgramList.java [new file with mode: 0644]
TinyBannavi/src/tainavi/PaperColorsMap.java [new file with mode: 0644]
TinyBannavi/src/tainavi/PassedProgram.java [new file with mode: 0644]
TinyBannavi/src/tainavi/PassedProgramList.java [new file with mode: 0644]
TinyBannavi/src/tainavi/PickedProgram.java [new file with mode: 0644]
TinyBannavi/src/tainavi/PlugIn_CSPDimora.java [new file with mode: 0644]
TinyBannavi/src/tainavi/PlugIn_CSPSkyperfectTV2012.java [new file with mode: 0644]
TinyBannavi/src/tainavi/PlugIn_CSPTVKingdom.java [new file with mode: 0644]
TinyBannavi/src/tainavi/PlugIn_CSPTVKingdom110.java [new file with mode: 0644]
TinyBannavi/src/tainavi/PlugIn_CSPtheTelevisionA.java [new file with mode: 0644]
TinyBannavi/src/tainavi/PlugIn_CSPtheTelevisionD.java [new file with mode: 0644]
TinyBannavi/src/tainavi/PlugIn_RecDIGA.java [new file with mode: 0644]
TinyBannavi/src/tainavi/PlugIn_RecDIGA_DMR_BW200.java [new file with mode: 0644]
TinyBannavi/src/tainavi/PlugIn_RecDIGA_DMR_BWT2100.java [new file with mode: 0644]
TinyBannavi/src/tainavi/PlugIn_RecDIGA_DMR_BZT710.java [new file with mode: 0644]
TinyBannavi/src/tainavi/PlugIn_RecDIGA_DMR_BZT720.java [new file with mode: 0644]
TinyBannavi/src/tainavi/PlugIn_RecGoogleCalendar.java [new file with mode: 0644]
TinyBannavi/src/tainavi/PlugIn_RecRD_A600.java [new file with mode: 0644]
TinyBannavi/src/tainavi/PlugIn_RecRD_BR610.java [new file with mode: 0644]
TinyBannavi/src/tainavi/PlugIn_RecRD_BZ700.java [new file with mode: 0644]
TinyBannavi/src/tainavi/PlugIn_RecRD_BZ810.java [new file with mode: 0644]
TinyBannavi/src/tainavi/PlugIn_RecRD_BZ810_TSync.java [new file with mode: 0644]
TinyBannavi/src/tainavi/PlugIn_RecRD_E300.java [new file with mode: 0644]
TinyBannavi/src/tainavi/PlugIn_RecRD_EDCB.java [new file with mode: 0644]
TinyBannavi/src/tainavi/PlugIn_RecRD_H1.java [new file with mode: 0644]
TinyBannavi/src/tainavi/PlugIn_RecRD_H1EX.java [new file with mode: 0644]
TinyBannavi/src/tainavi/PlugIn_RecRD_H2.java [new file with mode: 0644]
TinyBannavi/src/tainavi/PlugIn_RecRD_M190.java [new file with mode: 0644]
TinyBannavi/src/tainavi/PlugIn_RecRD_MAIL.java [new file with mode: 0644]
TinyBannavi/src/tainavi/PlugIn_RecRD_MAIL_M190.java [new file with mode: 0644]
TinyBannavi/src/tainavi/PlugIn_RecRD_MAIL_RE2.java [new file with mode: 0644]
TinyBannavi/src/tainavi/PlugIn_RecRD_MAIL_Z1.java [new file with mode: 0644]
TinyBannavi/src/tainavi/PlugIn_RecRD_MAIL_Z3.java [new file with mode: 0644]
TinyBannavi/src/tainavi/PlugIn_RecRD_MAIL_Z9500.java [new file with mode: 0644]
TinyBannavi/src/tainavi/PlugIn_RecRD_NULL.java [new file with mode: 0644]
TinyBannavi/src/tainavi/PlugIn_RecRD_S1004K.java [new file with mode: 0644]
TinyBannavi/src/tainavi/PlugIn_RecRD_S304K.java [new file with mode: 0644]
TinyBannavi/src/tainavi/PlugIn_RecRD_S600.java [new file with mode: 0644]
TinyBannavi/src/tainavi/PlugIn_RecRD_TVTest.java [new file with mode: 0644]
TinyBannavi/src/tainavi/PlugIn_RecRD_TvRock.java [new file with mode: 0644]
TinyBannavi/src/tainavi/PlugIn_RecRD_X5.java [new file with mode: 0644]
TinyBannavi/src/tainavi/PlugIn_RecRD_X5EX.java [new file with mode: 0644]
TinyBannavi/src/tainavi/PlugIn_RecRD_X8.java [new file with mode: 0644]
TinyBannavi/src/tainavi/PlugIn_RecRD_XS41.java [new file with mode: 0644]
TinyBannavi/src/tainavi/PlugIn_RecRD_XS57.java [new file with mode: 0644]
TinyBannavi/src/tainavi/PlugIn_RecRD_Z160.java [new file with mode: 0644]
TinyBannavi/src/tainavi/PlugIn_RecRD_Z160_TSync.java [new file with mode: 0644]
TinyBannavi/src/tainavi/PlugIn_RecRD_Z260.java [new file with mode: 0644]
TinyBannavi/src/tainavi/PlugIn_RecRD_iEPG.java [new file with mode: 0644]
TinyBannavi/src/tainavi/PlugIn_RecRD_iEPG2.java [new file with mode: 0644]
TinyBannavi/src/tainavi/PlugIn_TVPDimora.java [new file with mode: 0644]
TinyBannavi/src/tainavi/PlugIn_TVPEDCB.java [new file with mode: 0644]
TinyBannavi/src/tainavi/PlugIn_TVPMSN.java [new file with mode: 0644]
TinyBannavi/src/tainavi/PlugIn_TVPTVGuide.java [new file with mode: 0644]
TinyBannavi/src/tainavi/PlugIn_TVPTVKingdom.java [new file with mode: 0644]
TinyBannavi/src/tainavi/PlugIn_TVPtheTelevision.java [new file with mode: 0644]
TinyBannavi/src/tainavi/ProgDateList.java [new file with mode: 0644]
TinyBannavi/src/tainavi/ProgDetailList.java [new file with mode: 0644]
TinyBannavi/src/tainavi/ProgList.java [new file with mode: 0644]
TinyBannavi/src/tainavi/RecordedInfo.java [new file with mode: 0644]
TinyBannavi/src/tainavi/RecorderInfo.java [new file with mode: 0644]
TinyBannavi/src/tainavi/RecorderInfoList.java [new file with mode: 0644]
TinyBannavi/src/tainavi/RecorderList.java [new file with mode: 0644]
TinyBannavi/src/tainavi/ReserveList.java [new file with mode: 0644]
TinyBannavi/src/tainavi/RowHeightChangeListener.java [new file with mode: 0644]
TinyBannavi/src/tainavi/RowItem.java [new file with mode: 0644]
TinyBannavi/src/tainavi/RowItemList.java [new file with mode: 0644]
TinyBannavi/src/tainavi/SearchGroup.java [new file with mode: 0644]
TinyBannavi/src/tainavi/SearchGroupList.java [new file with mode: 0644]
TinyBannavi/src/tainavi/SearchKey.java [new file with mode: 0644]
TinyBannavi/src/tainavi/SearchProgram.java [new file with mode: 0644]
TinyBannavi/src/tainavi/SearchResult.java [new file with mode: 0644]
TinyBannavi/src/tainavi/SemLocker.java [new file with mode: 0644]
TinyBannavi/src/tainavi/StatusTextArea.java [new file with mode: 0644]
TinyBannavi/src/tainavi/StatusWindow.java [new file with mode: 0644]
TinyBannavi/src/tainavi/SwingBackgroundWorker.java [new file with mode: 0644]
TinyBannavi/src/tainavi/SwingLocker.java [new file with mode: 0644]
TinyBannavi/src/tainavi/Syobocal.java [new file with mode: 0644]
TinyBannavi/src/tainavi/TVProgram.java [new file with mode: 0644]
TinyBannavi/src/tainavi/TVProgramIterator.java [new file with mode: 0644]
TinyBannavi/src/tainavi/TVProgramList.java [new file with mode: 0644]
TinyBannavi/src/tainavi/TVProgramUtils.java [new file with mode: 0644]
TinyBannavi/src/tainavi/TatCount.java [new file with mode: 0644]
TinyBannavi/src/tainavi/TextEditPopupMenu.java [new file with mode: 0644]
TinyBannavi/src/tainavi/TextValueSet.java [new file with mode: 0644]
TinyBannavi/src/tainavi/TraceKey.java [new file with mode: 0644]
TinyBannavi/src/tainavi/TraceProgram.java [new file with mode: 0644]
TinyBannavi/src/tainavi/TreeExpansionReg.java [new file with mode: 0644]
TinyBannavi/src/tainavi/VWColorCellRenderer.java [new file with mode: 0644]
TinyBannavi/src/tainavi/VWColorCharCellRenderer.java [new file with mode: 0644]
TinyBannavi/src/tainavi/VWColorCharCellRenderer2.java [new file with mode: 0644]
TinyBannavi/src/tainavi/VWColorChooserDialog.java [new file with mode: 0644]
TinyBannavi/src/tainavi/VWDetailCellRenderer.java [new file with mode: 0644]
TinyBannavi/src/tainavi/VWFont.java [new file with mode: 0644]
TinyBannavi/src/tainavi/VWKeywordGroupDialog.java [new file with mode: 0644]
TinyBannavi/src/tainavi/VWListedTreeNode.java [new file with mode: 0644]
TinyBannavi/src/tainavi/VWLookAndFeel.java [new file with mode: 0644]
TinyBannavi/src/tainavi/VWMainWindow.java [new file with mode: 0644]
TinyBannavi/src/tainavi/VWStatusTextArea.java [new file with mode: 0644]
TinyBannavi/src/tainavi/VWStatusWindow.java [new file with mode: 0644]
TinyBannavi/src/tainavi/VWTimer.java [new file with mode: 0644]
TinyBannavi/src/tainavi/VWTimerRiseEvent.java [new file with mode: 0644]
TinyBannavi/src/tainavi/VWTimerRiseListener.java [new file with mode: 0644]
TinyBannavi/src/tainavi/VWTraceKeyDialog.java [new file with mode: 0644]
TinyBannavi/src/tainavi/VWTreeCellRenderer.java [new file with mode: 0644]
TinyBannavi/src/tainavi/VWUpdate.java [new file with mode: 0644]
TinyBannavi/src/tainavi/VersionInfo.java [new file with mode: 0644]
TinyBannavi/src/tainavi/Viewer.java [new file with mode: 0644]
TinyBannavi/src/tainavi/b64.java [new file with mode: 0644]
TinyBannavi/src/tainavi/clipboardItem.java [new file with mode: 0644]
TinyBannavi/src/tainavi/paperColors.java [new file with mode: 0644]
TinyBannavi/src/tainavi/unzip.java [new file with mode: 0644]
TinyBannavi/src/tainaviboot/Boot.java [new file with mode: 0644]
TinyBannavi/src/tainaviboot/Boot.txt [new file with mode: 0644]
TinyBannavi/src/todo.txt [new file with mode: 0644]
TinyBannavi/statviewer.cmd [new file with mode: 0644]
TinyBannavi/tinybannavi.cmd [new file with mode: 0644]
TinyBannavi/tinybannavi.sh [new file with mode: 0644]
TinyBannavi/tinysync.cmd [new file with mode: 0644]
TinyBannavi/tinysync.sh [new file with mode: 0644]
TinyBannavi/削除.cm_ [new file with mode: 0644]

diff --git a/TinyBannavi/01_readme.txt b/TinyBannavi/01_readme.txt
new file mode 100644 (file)
index 0000000..d96b322
--- /dev/null
@@ -0,0 +1,38 @@
+----------------------------------------------------------------------------\r
+【ソフト名】  タイニー番組ナビゲータ\r
+【作 成 者】  「」\r
+【1次配布元】 https://sourceforge.jp/projects/tainavi\r
+----------------------------------------------------------------------------\r
+\r
+ こんにちは、タイニー番組ナビゲータへようこそ!\r
+\r
+ タイニー番組ナビゲータ(略称:鯛ナビ)は、かつて東芝のハードディスクレコーダーがアナログ機だった時代に\r
+ PCからの予約作業を支援するソフトとして好評を博した素敵ソフト「番組ナビゲータ」の使い勝手を\r
+ 現行のデジタル機においても再現すべく開発中の番組ナビゲータ・クローンソフトです。\r
+\r
+ 主要な機能は番組ナビゲータからのまるパクですが、以下のような特徴をもっています。\r
+\r
+ ■テレビ番組情報検索・閲覧機能\r
+  ・Web番組表から番組情報を取得して、リスト形式や新聞形式で表示することができます。\r
+  ・タイトルや番組内容、出演者やジャンルなどさまざまな条件で番組を検索することができます。\r
+  ・アニメマニア御用達サイト「しょぼいカレンダー」掲載の番組情報の閲覧に対応しています。\r
+  ・ラジオ番組対応は廃止されました(2011/12)。\r
+ ■レコーダー連携機能\r
+  ・番組ナビゲータで可能だった予約の登録に加え、更新や削除も行うことができます。\r
+  ・予約情報は、リスト形式や新聞形式を表示した際に予約枠として重ね合わせ表示ができます。\r
+  ・東芝RDシリーズのほかパナソニックDIGAやTvRockなどにも対応しており、異なる機種を統一したインタフェースで扱うことができます。\r
+  ・複数台のレコーダーを一括して管理することができます。\r
+ ■外部インタフェース部分のプラグイン化\r
+  ・Web番組表やレコーダーとの外部インタフェース部分はプラグイン化しており、サイトの改修や新機種の追加に臨機応変に対応できます。\r
+  ・主要なWeb番組表にマルチ対応しているため、常用しているサイトに障害がおきた時も他の番組表を選択することで利用を継続できます。\r
+ ■オープンソース&マルチプラットフォーム対応\r
+  ・100% Pure Javaで製作しており、同一バイナリで複数のプラットフォームに対応しています(Windows XP/Vista/7、Linux、MacOSXほか)。\r
+  ・オープンソースとして公開していますのでどなたでも自由にカスタマイズができます。すでに幾つか頂いていますが、フィードバックも大歓迎です!\r
+ ■見果てぬ夢\r
+  ・ネットブックでも快適動作…\r
+\r
+ 【謝辞】\r
+  番組ナビゲータという素晴らしいソフトを開発され、また、「番組ナビゲータ」という名称の使用を快諾いただいた\r
+  ◆kzz0PzTAMM氏に心から感謝いたします。\r
+\r
+以上です。
\ No newline at end of file
diff --git a/TinyBannavi/02_usage.txt b/TinyBannavi/02_usage.txt
new file mode 100644 (file)
index 0000000..ff479de
--- /dev/null
@@ -0,0 +1,6 @@
+\r
+プロジェクトWikiページを参照願います。\r
+\r
+ http://sourceforge.jp/projects/tainavi/wiki/FrontPage\r
+\r
+以上\r
diff --git a/TinyBannavi/04_license.txt b/TinyBannavi/04_license.txt
new file mode 100644 (file)
index 0000000..df97702
--- /dev/null
@@ -0,0 +1,140 @@
+\r
+■使用条件等\r
+\r
+  タイニー番組ナビゲータは成恵の世界ウェアです。\r
+\r
+  成恵の世界ウェアとは....\r
\r
+   成恵の世界ウェアとは、丸川トモヒロ氏の描かれる唯一無二のコミック成恵の世界をサポートするために捧げられているコンセプトです。\r
+   氏は、世界でもっとも素晴らしいコミックの、「ナミナミ」クリエーターです!\r
+   成恵の世界ウェアの規約では、個人の使用においては タイニー番組ナビゲータ をフリーで使用することができます。あなたはこのプログラムを\r
+   好きなだけ何回でも使用することができます。どんなに長い間使用しても、全く制限されません。\r
+   実を言えば、永久に使い続けることができます。それからもちろん、一定の条件を満たすならば(下記を参照のこと)、あなたはこのソフトを\r
+   自由にコピーすることができます。けれども、もしこのソフトがあなたにとってとても役に立つものであるならば、「レジスト」することをお勧めします。\r
+\r
+  レジストの方法....\r
+\r
+   レジストする方法は、出かけていって成恵の世界の掲載されている雑誌、あるいはコミックを買ってきて、注意深く読み、その作品の 印象を\r
+   2chかmixiあたりにアップする だけです。あなたが本当に作品を買ったということを私に確信させるために、それぞれの作品の詳細な解説を\r
+   つけることを忘れないでください ;-) 。あるいはいもげのスレあたりであなたが 雑誌、コミック を持っている写真を載せてくれてもかまいません。\r
+   あなたが実際に成恵の世界のコミックを所有しているということを、私に確信させることができたならば、それはすなわちレジストしたことになります。\r
+\r
+   なんですって? 近所の書店が成恵の世界を置いていない? そんなカホな!つまり彼らは、正しいジュブナイルSF愛好機関ではないということですね、\r
+   私に言わせるならば。でもまあ心配はいりません。多くのオンラインブックストアでは、品揃えのいいことを自慢にしています。もしまだ成恵の世界を\r
+   読んだことがなくて、どれから始めたらいいかわからないというのであれば、最初の素晴らしい第一巻から順に手を出すこと をお薦めします。\r
+   "魔砲少女四号ちゃん" も、いつ読んでも素晴らしいですね。\r
+\r
+   もしあなたがすでに成恵の世界のファンであって、いくつかの(あるいは全部の)コミックを持っているのなら、あなたには既にレジストの権利があります!\r
+   それから、万が一、実はあなたが丸川トモヒロ氏である ならば、あなたはもちろん、このソフトや他の成恵の世界ウェアについての、一生涯の\r
+   レジストレーションを得ています。残念ながら、彼はインターネットに興味がなさそうですが ;-) 。\r
+   私は成恵の世界とは関係がないことを、ここで記述しておきます。私が得る利益は、私の好きな作品を応援することができるということと、\r
+   できることなら他の人がこの類いまれなコミックに気付く手助けになるかもしれない、ということだけです。\r
+\r
+  なぜレジストするのか...\r
+\r
+   絶対必要というわけではありませんが、レジストすれば以下のような素敵な結果をもたらすことになります。\r
+\r
+     1. 私が幸せになる(なぜなら私は成恵の世界を好きだから)!\r
+     2. 丸川トモヒロ氏が幸せになり、またさらに作品を創ってくれるようになる。\r
+     3. 世界中がさらに幸せな場所になる!\r
+     4. そして最も重要なのは、あなたが幸せになるということです。なぜなら、この魅力的な成恵の世界のコミックを、あなた自身で所有するのですから! \r
+\r
+   これまで、プログラムにレジストすることで世界をよりよいものする、なんてことが何回ありました? ;-)\r
+\r
+  Legalese...\r
+\r
+   Tiny Bangumi Navigator is Copyright 2009-2013 by 「」 (That being me)\r
+\r
+   Tiny Bangumi Navigatorは以下のサイトを参照しています...\r
+\r
+   しょぼいカレンダー(http://cal.syoboi.jp/rss.php)\r
+   これは、アニメ番組の録画漏れを防ぐ目的で参照されます。\r
+\r
+   Tiny Bangumi Navigatorは以下のアイコンパッケージGPL版の一部を同梱しています...\r
+\r
+       Open Icon Library(http://openiconlibrary.sourceforge.net/)\r
+       これは鯛ナビのツールバーアイコンに利用されています。\r
+       \r
+   Windows版Tiny Bangumi Navigatorは以下のサードパーティによるコードを同梱しています...\r
+\r
+   The JRE7(Java SE Runtime Environment Version 7) by Oracle Corporation\r
+   これは、鯛ナビの実行環境(JavaVM等)となります。\r
+\r
+   Tiny Bangumi NavigatorのEpgdumpパッケージは以下のコードを流用しています...\r
+\r
+       /*\r
+        * 本パッケージは epgdumpr2 (tomy ◆CfWlfzSGyg氏) をベースに鯛ナビ用のJavaコードに書き換えたものです。\r
+        * 本パッケージのライセンスは原著を踏襲するものとします。\r
+        * \r
+        * 以下 readme.txtよりの抜粋\r
+        * ----------\r
+        * xmltv-epg\r
+        * \r
+        * MPEG-TSに含まれるepgをxmlで出力するプログラムです。\r
+        * ◆N/E9PqspSk氏がrecfriio Solaris版(http://2sen.dip.jp/cgi-bin/friioup/source/up0737.zip)に含まれるepgdumpを\r
+        * Linux版を改造したものをベースにxmltv用のxmlファイルを作成します。\r
+        * \r
+        * (中略)\r
+        * \r
+        * epgdumpライセンス(Solaris版より引用):\r
+        * >epgdumpに関しては、BonTest Ver.1.40からそのままソースを持ってきている部分も\r
+        * >あるため、そのライセンスに従いします。\r
+        * >BonTestのReadme.txtより\r
+        * >>\r
+        * >>3.ライセンスについて\r
+        * >>  ・本パッケージに含まれる全てのソースコード、バイナリについて著作権は一切主張しません。\r
+        * >>  ・オリジナルのまま又は改変し、各自のソフトウェアに自由に添付、組み込むことができます。\r
+        * >>  ・但しGPLに従うことを要求しますのでこれらを行う場合はソースコードの開示が必須となります。\r
+        * >>  ・このとき本ソフトウェアの著作権表示を行うかどうかは任意です。\r
+        * >>  ・本ソフトウェアはFAAD2のライブラリ版バイナリを使用しています。\r
+        * >>\r
+        * >>   "Code from FAAD2 is copyright (c) Nero AG, www.nero.com"\r
+        * >>\r
+        * >>  ・ビルドに必要な環境\r
+        * >>   - Microsoft Visual Studio 2005 以上 ※MFCが必要\r
+        * >>   - Microsoft Windows SDK v6.0 以上  ※DirectShow基底クラスのコンパイル済みライブラリが必要\r
+        * >>   - Microsoft DirectX 9.0 SDK 以上\r
+        * \r
+        * Special Thanks:\r
+        * ・Solaris版開発者の方\r
+        * ・拡張ツール中の人\r
+        * ・◆N/E9PqspSk氏\r
+        * ・ARIB(資料の無料ダウンロードに対して)\r
+        * \r
+        */\r
+\r
+   Tiny Bangumi NavigatorのTaiNavi.exeコマンドは以下のツールを利用して作成されています...\r
+\r
+       exewrap(http://code.google.com/p/exewrap/)\r
+       tdm-gcc-4.7.1-2(http://tdm-gcc.tdragon.net/)\r
+\r
+   注意: 自己の責任のもとで使用してください!\r
+\r
+   Tiny Bangumi Navigator は安全で役に立つプログラムであると信じていますが、これが正しく、あるいは完全に動くかどうかの保証をするものではありません。\r
+   また思いがけないバグが損害を与えてしまったり、システムを危険にさらしたり、あるいはツアルツィー人の置き土産を起こしてしまったりしないという保証もいたしません。\r
+\r
+   このソフトウェアは「現状のままで」提供されるものであり、説明された、もしくは言外に含まれた、または法令による、いかなる保証もありません。このソフトウェアを\r
+   使用して発生したいかなる損害も、作者はその責を負わないものとします。これらの条件に同意する場合にだけこのソフトウェアを使用してください!\r
+\r
+  再配布について...\r
+\r
+  リリースバイナリに関して...\r
+\r
+   私はこのプログラムに関するすべての権利を保持しますが、以下の条件を満たす限り自由に再配布してかまいません。\r
+\r
+     1. 配布にかかるメディアや輸送等の実費( 5000 ペリカを越えてはなりません)を除いて無償であること。\r
+     2. このプログラムと関連するファイルは、オリジナルのアーカイブを保持しているものであって、いかなる変更もされていないこと。\r
+     3. 他の商業製品の一部として含まれるのではないこと。\r
+     4. 配布の過程において、いかなるバイソンも傷つけないこと。^_^ \r
+\r
+   この製品は、アクセスやダウンロードが無償でできる限りにおいて、電子的手段でダウンロード可能なあらゆるソフトウェアコレクションに含めても構いません\r
+   (ウェブサイト、FTP サイト、および BBS を含みます)。 この製品を商用の製品に含める場合、あるいは CD-ROM や他の営利目的のメディアに収録する場合は、\r
+   事前に許可は必要ありません。以下の email アドレスを使用して私に連絡してください: peeweedee@users.sourceforge.jp\r
+   多くのシェアウェア/フリーウェアの CD-ROM コレクションなどについては、喜んで許可を与えるでしょう。そのような使用について、きちんと把握しておきたいだけですので。\r
+\r
+  ソースに関して...\r
+\r
+   改変・流用・引用について、特に制限は設けません。ご自由にお使いください。\r
+\r
+\r
+以上です。
\ No newline at end of file
diff --git a/TinyBannavi/05_history.txt b/TinyBannavi/05_history.txt
new file mode 100644 (file)
index 0000000..3b3cf5a
--- /dev/null
@@ -0,0 +1,2869 @@
+★☆☆☆☆☆☆お知らせ☆☆☆☆☆☆☆☆★\r
+自動バージョンアップ後の再起動時、旧ver.のbinとenvはそれぞれbin.oldとenv.oldにバックアップされます。\r
+バージョンアップ時にになにか不都合がありましたら旧ver.に戻してしのいでください。\r
+また原因追及にご協力いただける方は、その際発生したlog.txt/log.txt.bakを開発元までお送りください。\r
+★☆☆☆☆☆☆☆☆☆☆☆☆☆☆☆☆☆☆★\r
+疑問に思われることがありましたらまずはこちらに一度目を通してみることをおすすめします。\r
+\r
+よくあるお問い合わせ : http://sourceforge.jp/projects/tainavi/wiki/FAQ\r
+バグトラッキングシステム : http://sourceforge.jp/projects/tainavi/ticket?status[]=1&status[]=2\r
+2chの番ナビスレ:http://toro.2ch.net/test/read.cgi/av/1352223253/\r
+★☆☆☆☆☆☆☆☆☆☆☆☆☆☆☆☆☆☆★\r
+\r
+3.22.1β+1.5.12(2013-05-23)\r
+■変更点\r
+ ・(web番組表対応) タイトル/番組詳細に"R-15""R-18""R指定"を含む番組に【R】マーク\r
+ ・(web番組表対応) タイトル/番組詳細に"初放送"を含む番組に【初】マーク\r
+ ・(web番組表対応) [初]→(初)変更 ※リピート放送の初回を示すマーク\r
+ ・(新聞形式) 過去ログ検索履歴の直近数件を保存できるようにした(>>383.) ※鯛ナビを終了すると履歴は消える\r
+ ・(新聞形式) 過去ログ検索結果の絞り込みが可能に\r
+ ・(新聞形式) 過去ログ検索結果の検索日付順を昇順から降順に変更(結果の表示順序はかわらない)\r
+ ・(新聞形式) 番組枠等の枠線を細くした(下と右の枠線を描画しないようにしただけ)\r
+ ・(新聞形式) 番組詳細の最大表示行数設定の追加\r
+ ・(新聞形式) 開始時刻が1分ずれている予約同士を重複と判断してエンコーダマーク調整が動くようにした(EPG予約のレコーダとプログラム予約のレコーダを重ね合わせ表示した場合等の処置)\r
+ ・(予約ダイアログ) 類似予約コンボボックスをテーブル形式に変更\r
+ ・(予約一覧) 予約実行ON/OFFをボタンクリックのみで変更できるようにした \r
+ ・(その他) 右クリックメニューの予約ON/OFF・予約削除アイテムで、ツールバーで選択中のレコーダのものは太字にするようにした \r
+ ・(その他) 過去ログ検索の検索件数上限を設定可能にした \r
+■バグ修正\r
+ ・(レコーダ対応[DIGA BWT2100/BZT710/720]) HTMLソースをunescapeしていなかった問題を修正(>>412.)\r
+ ・(新聞形式) 予約待機枠強調が働かない番組がある問題を修正\r
+ ・(新聞形式) [新聞]過去ログ表示→[リスト]過去ログ検索→[新聞]戻って番組欄にマウスオーバー…した際に例外が発生する問題を修正\r
+ ・(新聞形式) 過去ログ検索結果から新聞形式にジャンプすると表示がおかしい問題を修正\r
+ ・(新聞形式) リスト形式や本体予約一覧から予約の操作を行うと、非表示状態のページにある放送局の予約枠が隠れず表示されてしまう問題を修正\r
+ ・(予約ダイアログ) 選択中のレコーダに類似予約が複数あった場合、一番開始日時の近いものを優先するように修正\r
+ ・(予約ダイアログ) 類似予約自動選択が有効な場合でも類似予約を選択しない状態に変更できるようにした(>>140.)\r
+ ・(予約ダイアログ) 24:00開始の番組を、1分前倒しの「23:59から」かつ「繰り返し」で予約していた場合、類似予約自動選択で日付が繰り返しでなく単日で自動設定されてしまう問題を修正\r
+\r
+3.22+1.5.12(2013-04-21)\r
+■変更点\r
+ ・(その他) Windows用起動バイナリTaiNavi.exeを作成\r
+  -exewrap(のjvmオプションをiniファイルに外出しできるようにしたもの)を使って作成しています\r
+  -タスクバーやWindows8のスタート画面にピン留めできるようになりました。起動中に再度押すとウィンドウがアクティブになります\r
+  -プロセス名がTaiNavi.exeになります\r
+  -ヒープ領域指定(-Xmx)の自動設定処理の実装に失敗しました。ヒープ不足で起動できないと警告された場合、TaiNavi.iniのVMARGS指定の-Xmx1024mを小さ目の値に変更してください\r
+  -自力でjreを探しに行かなくなっているので、jre非同梱版利用の場合はJAVA_HOME環境変数を正しく設定してください\r
+  -avast!を使っている場合は、初回実行時に警告(使用頻度が低いのであやしいです、みたいなの)を受けるかもしれません。当家では受けました\r
+ ・(その他) jre6フォルダをjreフォルダに名前変更\r
+ ・(その他) mail/とcalendar/のプラグインファイルをbin/tainavi/フォルダにおいても起動に失敗しなくなったので(3.21)、そちらに移動\r
+ ・(その他) ログファイル出力をデフォルトで有効にし、-l(ログ有効)を廃止し、-L(ログ無効)を追加\r
+ ・(Web番組表対応) 3文字系の情報(【新番組】など)からのフラグ取得に対応\r
+■バグ修正\r
+ ・(その他) Windowsでのオンラインアップデート時に過去フォルダが2世代残るようにした(>>326.)\r
+ ・(その他) 現在時刻系のタイマーが3個てんでバラバラに存在していたので統合\r
+ ・(その他) デバッグログが設定に関係なく大量に発生していたのである程度抑制した\r
+\r
+■バージョンアップ時の補足\r
+ ・Java同梱版のJREを7u17から7u21に変更しました(開発自体は6で行っています)。\r
+\r
+★☆☆☆☆☆☆☆☆☆☆☆☆☆☆☆☆☆☆★\r
+■未修正の既知の問題\r
+ ・(新聞形式) ページャー有効時、リスト形式から予約を行うと、関係ない放送局の上に予約枠が表示される。ページャーを操作すると直る(予約枠の追加時に追加ページを確認していないと思われる。そのうち直す)\r
+ ・(新聞形式) 過去ログ番組枠上のマウス操作で例外発生(調査中)\r
+ ・(新聞形式) 「先週の番組欄へジャンプ」で、違う放送局の上にジャンプする(ページャーの操作ミス?調査中)\r
+ ・(リスト形式) 複数番組をまたいで長時間予約した際に、先頭の2番組のみしか予約マークが表示されない(調査中)\r
+ ・(予約ダイアログ) 番組IDが未取得なのに番組追従が「する」になっているので、TvRockプラグインでプログラム予約すると毎回警告音が鳴る(予約ダイアログが改修途中のため。そのうち直す)\r
+ ・(CHコンバート設定) 更新が行えない(未完成のため。そのうち作り込む)\r
+ ・(web番組表対応[EDCB]) BS/CSの拡張ジャンル情報を取得していない(ARIB TR-B15)\r
+ ・(番組追跡) CSで帯で放送している番組に対してリピート検出をかけると、誤判定して一番先の放送しかリストアップされない(当面調査見送り)\r
+\r
+ -(web番組表対応[EDCB]) .datに情報があっても過去ログが優先される場合がある?(調査中) →過去月のキャッシュの問題だった\r
+\r
+ ×(レコーダ対応[EDCB]) EDCB直で登録した予約の情報を取得した際、ジャンルが正しく取得できない(apiでの予約取得への移行により、ジャンル情報がとれなくなったので調査打ち切りとする)\r
+★☆☆☆☆☆☆☆☆☆☆☆☆☆☆☆☆☆☆★\r
+Windows以外のプラットフォームの対応状況が微妙なため、Java7以降対応は当面保留します。\r
+★☆☆☆☆☆☆☆☆☆☆☆☆☆☆☆☆☆☆★\r
+\r
+--- 3.21 -> 3.22 ---\r
+\r
+3.21.5β+1.5.12(2013-04-11)\r
+■バグ修正\r
+ ・(Web番組表対応[スカパー!]) BS局の番組表がとれない問題を修正(>>395.)\r
+\r
+3.21.4β+1.5.12(2013-04-10)\r
+■変更点\r
+ ・(Web番組表対応[スカパー!]) スカパー!公式サイトの改訂により放送局一覧が取得できなくなったので対応(>>387.)\r
+\r
+★対応チケット\r
+http://sourceforge.jp/ticket/browse.php?group_id=4315&tid=31120\r
+\r
+3.21.3β+1.5.12(2013-04-09)\r
+■バグ修正\r
+ ・(レコーダ対応[DIGA BWT2100/BZT710/720]) 新規登録ができない問題を修正(>>#31118.)\r
+\r
+★対応チケット\r
+http://sourceforge.jp/ticket/browse.php?group_id=4315&tid=31118\r
+\r
+3.21.2β+1.5.12(2013-04-08)\r
+■変更点\r
+ ・(Web番組表対応) 番組詳細に「"OVA"あり」かつ「(全x話)"+OVA"なし」の場合、【特】フラグを立てるようにした\r
+ ・(Web番組表対応) 番組詳細に「(テレビ)"未放送"あり」の場合、【特】フラグを立てるようにした\r
+ ・(Web番組表対応) 【特】フラグが立っている番組では終了時間1分短縮設定を無視するようにした (AT-X対策)\r
+ ・(レコーダ対応) 予約詳細取得確認ダイアログで、Yes/Noの選択を既定動作にするかどうかのチェックボックスを追加\r
+ ・(レコーダ対応[DIGA BWT2100/BZT710/720]) 予約取得処理を予約一覧取得と予約詳細取得に分離し、後者の実行を都度選択できるようにした\r
+ ・(レコーダ対応[DIGA BWT2100/BZT710/720]) 予約一覧取得で取得できる情報の内容を強化\r
+  -タイトルは取れないので、鯛ナビ上の既存情報から補完できないものは未定となる("★★番組詳細を取得しなおしてください★★")\r
+  -以下の録画パターン以外は取れないので、すべて毎週予約として取り込まれる\r
+   ・単日予約\r
+   ・毎週予約(曜日指定)\r
+   ・月~金(開始時刻が0:00~23:59のもののみ)\r
+   ・月~土(開始時刻が0:00~23:59のもののみ)\r
+ ・(その他) 番組追跡への追加を行うとリスト形式にジャンプする動作を廃止\r
+■バグ修正\r
+ ・(レコーダ対応[DIGA BWT2100/BZT710/720]) 繰り返しパターン=毎日の予約を正しく認識できなくなった問題を修正\r
+ ・(レコーダ対応[DIGA BWT2100/BZT710/720]) 終了時刻「未定」が再び例外を起こすようになった問題を修正\r
+ ・(Web番組表対応[EDCB]) タイトルなしの番組情報でNullPointerExceptionが発生する場合があった問題を修正\r
+\r
+3.21.1β+1.5.12(2013-03-16)\r
+■変更点\r
+ ・(レコーダ対応[DIGA BWT2100/BZT710/720])\r
+  -新規予約:EPG予約(番組予約)とプログラム予約(時間予約)を選択できるようにした\r
+  -新規予約:予約実行時にDIGAの予約一覧からとれる情報を、鯛ナビ上の既存エントリへ反映する処理を追加(現状は「実行ON/OFF」「重複警告」のフラグのみ。時間移動などはフォローしていしない)\r
+  -予約3種:予約実行時にDIGAから鯛ナビにまだ取り込まれていない予約があったら取り込む処理を追加\r
+  -予約更新:EPG予約では、予約のON/OFF以外の変更は拒否\r
+  -予約情報取得:本体で入れたEPGかつ繰り返しの予約が、番組表取得ミスや番組休止などで追跡できなかった場合にタイトルに"★★DIGAが追跡に失敗★★"を付加するようにした\r
+  -予約情報取得:鯛ナビで対応していない繰り返し予約パターンは単日予約として取得するように変更\r
+  ★注意★ EPG予約で帯予約の場合、予約の開始が予約を入れる時に使った番組の日付からになるので注意(月~金の予約をその週の月曜日に入れようとしたが、水曜の番組を使った場合、その週の月・火は録画の対象からはずれる)\r
+ ・(Web番組表対応/キーワード検索) 視聴(年齢)?制限フラグ|R15\+|R18\+ → 【R】\r
+■バグ修正\r
+ ・(その他) GTK LookAndFeelを選択した時にフォントの変更ができない問題に対処 (@see env/_gtkrc-2.0)\r
+ ・(レコーダ対応[DIGA BWT2100/BZT710/720]) 新規予約時、「鯛ナビから新規に入れた予約」と「DIGAにのみ存在する予約(本体で入れたものとか)」の区別がと、どれが鯛ナビから新規に投入した予約なのかわからなくなってしまい情報の不一致が発生する問題を修正\r
+ ・(レコーダ対応[DIGA BWT2100/BZT710/720]) ↓3.20のこれがうまく直っていないような気がしたので再修正\r
+ ・(レコーダ対応[DIGA BWT2100/BZT710/720]) 終了時刻が「未定」の場合に予約一覧が取得できなくなる問題を修正(>>267.) (開始時刻から1時間後に強制設定する)\r
+ ・(レコーダ対応[DIGA BWT2100/BZT710/720]) 2100プラグインのみ、BUSY待ちタイムアウト値を67.5秒(7.5秒×9回)に延ばした(>>) (他は37.5秒)\r
+\r
+3.21+1.5.12(2013-03-06)\r
+■変更点\r
+ ・(予約一覧) タイトルの絞り込み検索を追加(ツールバーから。空文字列検索で結果リセット)\r
+ ・(新聞形式) 現在時刻線の位置を変更可能にした(上端から1~180分) →ツールバーのパレットアイコンから\r
+ ・(その他) env/boudns.xml 廃止\r
+■バグ修正\r
+ ・(しょぼかる) 番組詳細.contains("繰り下げ")→延長フラグ=trueが機能していなかった問題を修正\r
+ ・(レコーダ対応[TvRock]) ↓3.20のこれがうまく直っていなかったので再修正\r
+ ・(レコーダ対応[TvRock]) 新規予約&更新時にCHコードを履歴に保存しなくなっていた問題を修正 (登録は正常にできるが、再起動すると「★放送局名不正」になって予約一覧再取得しないとどうにもこうにも)\r
+ ・(レコーダ対応) メール系・カレンダー系プラグインに必要なライブラリが揃っていない場合に起動できなくなる問題に対処\r
+\r
+■バージョンアップ時の補足\r
+ ・Java同梱版のJREを6u41から7u17に変更しました(開発自体は6で行っています)。\r
+\r
+--- 3.20 -> 3.21 ---\r
+\r
+3.20.2β+1.5.12(2013-03-04)\r
+■変更点\r
+ ・(録画結果一覧) タイトルの絞り込み検索を追加(ツールバーから。空文字列検索で結果リセット)\r
+■バグ修正\r
+ ・(りもこん) バッチファイルのjreのパス設定を鯛ナビ用バッチと同じにした\r
+ ・(その他) envs.txtから表示マーク関連のエントリを削除すると鯛ナビが起動できなくなる問題を修正\r
+\r
+3.20.1β+1.5.12(2013-02-24)\r
+■変更点\r
+ ・(その他) りもこんの起動バッチを修正(>>329.)\r
+■バグ修正\r
+ ・(レコーダ対応[DIGA BWT2100/BZT710/720]) 予約新規/更新時に地デジ以外の放送局情報が不正になる問題を修正(>>318.)\r
+ ・(レコーダ対応[DIGA BWT2100/BZT710/720]) 予約一覧取得時に地デジ以外の放送局情報が不正になる問題を修正(↑と同根) ※selectタグ中のoptionタグの抽出式が間違っていたため、最初のoptionタグである地デジ以外で正しい情報を取得できなくなっていた\r
+ ・(レコーダ対応[DIGA BWT2100/BZT710/720]) 予約新規/更新時に重複警告があってもトラップできなくなっていた問題を修正\r
+\r
+3.20+1.5.12(2013-02-23)\r
+■変更点\r
+ ・(リスト形式) 新番組/最終回ノードにジャンル別サブノードを追加\r
+ ・(その他) env/laf.txt 廃止\r
+■バグ修正\r
+ ・(レコーダ対応[DIGA BWT2100/BZT710/720]) ログイン処理がリトライタイムアウトになる場合がままあったのでリトライ間隔を5000→7500に延長\r
+ ・(レコーダ対応[DIGA BWT2100/BZT710/720]) 「DIGAと通信中です。しばらくお待ちください。」を待ちきれずに即失敗終了にしてしまう問題を修正\r
+ ・(レコーダ対応[DIGA BWT2100/BZT710/720]) 予約一覧取得で「本体操作中、または現在実行できない操作です。」をトラップしていないので予約一覧が空っぽになってしまう問題を修正(>>267.)\r
+ ・(レコーダ対応[DIGA BWT2100/BZT710/720]) 終了時刻が「未定」の場合に予約一覧が取得できなくなる問題を修正(>>267.) (開始時刻から1時間後に強制設定する)\r
+ ・(レコーダ対応[DIGA BWT2100/BZT710/720]) いつのまにかチャンネル変更操作が右クリックメニューに表示されなくなっていた問題を修正\r
+ ・(レコーダ対応[DIGA系他]) レコーダ設定>最大同時録画数の自動設定で"1"を指定すると鯛ナビからの予約更新後に予約一覧が表示できなくなる問題を修正\r
+ ・(レコーダ対応[TvRock]) 新規予約&更新時にCHコードを履歴に保存しなくなっていた問題を修正 (登録は正常にできるが、再起動すると「★放送局名不正」になって予約一覧再取得しないとどうにもこうにも)\r
+ ・(レコーダ対応[TvRock]) 番組情報取得の予約の取り込み不具合への対応 ※更新はできるが、新規登録はまだできない\r
+ ・(レコーダ対応) 録画結果一覧の取得の成否の判定が反転していてBeep音鳴りまくりだった問題を修正(>>291.)\r
+ ・(レコーダ設定) 「上へ」「下へ」ボタンを押してもテーブルの表示が更新されない問題を修正\r
+ ・(web番組表対応[スカパー!]) タイトル・番組詳細中の「\"」をアンエスケープしていなかった問題を修正 (過去ログ分には適用されません)\r
+ ・(レコーダ設定) 「上へ」「下へ」ボタンを押してもテーブルの表示が更新されない問題を修正\r
+ ・(オンラインアップデート) 「起動時にアップデートを確認する」の設定が有効にならない問題を修正(>>166.)\r
+\r
+★対応チケット\r
+http://sourceforge.jp/ticket/browse.php?group_id=4315&tid=30294\r
+http://sourceforge.jp/ticket/browse.php?group_id=4315&tid=30530\r
+\r
+■バージョンアップ時の補足\r
+ ・Java同梱版のJRE6のバージョンをupdate39からupdate41に変更しました。\r
+\r
+--- 3.19 -> 3.20 ---\r
+\r
+3.19+1.5.12(2013-02-06)\r
+■変更点\r
+ ・(レコーダ対応[DIGA BWT2100/BZT710/720]) 予約一覧に「重複」という表示のあるものに重複警告フラグを立てて強調表示するようにした\r
+■バグ修正\r
+ ・(レコーダ対応) 予約情報が取得できなくなった大問題をあわてて修正(>>282,284.) ※TvRock/EDCBを除く\r
+ ・(レコーダ対応[DIGA BWT2100/BZT710/720]) 画質"DR(USB)"があると予約一覧の取得に失敗する問題を修正(>>#30667.)\r
+  →USBの画質設定値は決め打ちで、HDD+200としている(例.DR(HDD)["7"]→DR(USB)["207"])。DR以外は推測なので、正しい値となっているかは不明\r
+ ・(録画結果一覧) CHコード設定の不備に起因して録画結果の重複取り込みが発生する問題があったのを修正\r
+\r
+★対応チケット\r
+http://sourceforge.jp/ticket/browse.php?group_id=4315&tid=30667\r
+\r
+--- 3.18 -> 3.19 ---\r
+\r
+3.18+1.5.12(2013-02-05)\r
+■変更点\r
+\r
+ ・(web番組表対応[EDCB]) dat直接読みとは別に、APIを利用したリモート取得に対応(/api/EnumEventInfo)\r
+  -【dat読みからの切り替え方法】 オプションの「url」キーに正しいURL(http://EDCBのホスト名orIPアドレス:ポート番号/)を設定する\r
+  -CSの拡張ジャンル情報はとれない(datはdatの読み取りを直せばとれるようになるが、APIの方はEDCBから情報がもらえないのでとれない)\r
+\r
+ ・(レコーダ対応[EDCB]) 予約一覧取得をAPI利用に変更して高速化(/reserve.html → /api/EnumReserveInfo)\r
+ ・(レコーダ対応[EDCB]) 予約一覧の詳細情報の取得方法を変更(EPGの番組説明は取得できなくなった。apiでは取得できないので仕方がない)\r
+ ・(レコーダ対応[TvRock/EDCB]) 録画結果一覧は取得実行の度にリセットしていたが、履歴として保存するようにした(現在固定で90日まで保存)\r
+ ・(レコーダ対応[EDCB]) ↑の履歴にあるものは再取得せず、前回取得時からの差分のみの取得となるように変更 (キッチリ前回の続きから、とはできないので多少無駄な情報取得は発生する)\r
+ ・(本体予約一覧) 録画結果一覧に成功した録画の履歴がある場合に強調表示(紫)するようにした ★「同一タイトル(先頭の「無料≫」などは無視)」「同一放送局(CHコードが取得できない場合はこの判定はしない)」で「成功している(と思われる)」「MPEGドロップが0」の14日以内の録画結果がある「単日」予約に対して強調表示\r
+ ・(本体予約一覧) 「自動」予約欄を追加\r
+ ・(ツールバー) 本体予約取得ボタンに拡張メニューを追加(現在実装しているのは「録画結果一覧のみ取得」だけ)\r
+ ・(ツールバー) 本体予約取得ボタンで予約取得のみしか行わなくするオプションを追加(>>270.) (各種設定。デフォルトはOFF)\r
+ ・(web番組表対応) キッズステの最終回表記に対応(サブタイトル中に「(終)」を含む)\r
+ ・(web番組表対応[webザテ]) ジャンルコード14「拡張エリア - 広帯域CS デジタル放送拡張用情報  映画 - 邦画」に対応 (キッズステのアニメ映画など。鯛ナビではジャンル「映画」と判定)\r
+ ・(web番組表対応[しょぼかる]) 番組詳細に「副音声」という文字が含まれていたら音声多重フラグ([多])を立てるようにした(2012/12/22のひだまり、武装神姫など)\r
+ ・(レコーダ設定) レコーダ個別にGoogleカレンダー連携の設定を行えるようにした (NULLプラグインのみ有効無効の設定があったが、こちらに移行して廃止。再設定が必要かも)\r
+ ・(レコーダ設定) レコーダ個別にチャンネル操作有効/無効の設定を行えるようにした (まちがって録画鯖の操作を行わないようにするため)\r
+ ・(レコーダ設定) テーブルの行選択をすると、入力エリアに値をコピーしてそちらで編集したのち置換更新できるようにした (テーブルの色セルはいじれなくなった)\r
+ ・(レコーダ設定) MACアドレスの取得ボタンを追加 (ping.exe+arp.exe)\r
+ ・(キーワード検索) 検索条件に「二か国語放送」「吹替放送」を追加(>>193.)\r
+ ・(録画結果一覧[TvRock]) 「予約は実行されませんでした」「ターゲットアプリケーションの異常終了」など拾っていなかったメッセージに対応 (全部対応できたかどうかは不明だし、「システム休止からの復帰」など意図的に捨ててるものもある)\r
+ ・(本体予約一覧/録画結果一覧) タイトル欄のソート条件の無視対象に"[終]""[無料]""【無料】"を追加\r
+■バグ修正\r
+ ・(録画結果一覧[EDCB]) 「開始時刻が変更されました」がエラー扱いになっていたので修正\r
+ ・(リスト形式) 「現在放送中」ノードを選択した時、ソーターが有効のままだと並び順が狂うので、ソーターをリセットするようにした\r
+ ・(リスト形式/本体予約一覧/録画結果一覧) たぶん「タブを開いたときの再描画」と「レコーダコンボボックスで選択を実行した時の再描画」がかち合って起動障害を起こしていると思われるのが、全面的に見直すのはちょっとしんどいのでsynchronized()を挿入して様子をみる(>>240.)\r
+ ・(web番組表対応[Dimora]) 地域情報が取得できなかったときにヌルぽが発生しないようにした(>>271.)\r
+ ・(web番組表対応[主にスカパー!]) 過去月のキャッシュファイルが存在していると使ってしまう問題を修正(取得範囲を8日間に設定していると問題になる)\r
+ ・(web番組表対応) NGワードによるあぼーん処理で、消去するべき不要な情報が残っていたためゴミ情報が表示されていた問題を修正(>>160->269.)\r
+ ・(web番組表対応) CH設定タブで放送局情報の取得を行う際、Proxy設定が反映されない場合がある問題を修正\r
+ ・(web番組表対応) ネットにつながらなかったりサイトの改廃がおこなわれたりで、地域情報や放送局情報が取得できなくなっているときに、初回起動時の情報取得に失敗して鯛ナビが起動できなくなる問題を修正(>>#30506.)\r
+ ・(レコーダ対応[EDCB]) フリーテキストオプションが再起動しないと反映されない問題を修正\r
+ ・(レコーダ対応[EDCB]) 取得した放送局一覧の並び順がおかしかったので放送局ID昇順になるように修正\r
+ ・(予約ダイアログ) 繰返し予約ができないレコーダの場合でも録画日付コンボボックスが操作できるようになっていた問題を修正\r
+ ・(予約ダイアログ) 空きエンコーダ選択が正しく動作しない場合がある問題を修正(「終了1分短縮」かつ「開始1分前倒し」に設定していた場合に、NHK以外の局で発生していた。開始時刻を予約枠でなく番組枠で判断していたため)\r
+ ・(予約ダイアログ) 空きエンコーダ選択無効のレコーダ利用時に裏番組リストが表示されない問題を修正\r
+ ・(予約ダイアログ) 本体予約一覧からのオープンで、EPG予約を開いてからプログラム予約を開くと「番組ID取得」ボタンが有効になっている問題を修正\r
+\r
+★対応チケット\r
+http://sourceforge.jp/ticket/browse.php?group_id=4315&tid=30506\r
+http://sourceforge.jp/ticket/browse.php?group_id=4315&tid=30532 ★未完了!\r
+http://sourceforge.jp/ticket/browse.php?group_id=4315&tid=30536\r
+http://sourceforge.jp/ticket/browse.php?group_id=4315&tid=30539 ★未完了!\r
+\r
+■バージョンアップ時の補足\r
+ ・Java同梱版のJRE6のバージョンをupdate38からupdate39に変更しました。\r
+\r
+--- 3.17 -> 3.18 ---\r
+\r
+3.17.4β+1.5.12(2013-01-16)\r
+■変更点\r
+\r
+ ・(録画結果一覧) 新設\r
+  -現時点での対応レコーダはEDCB、TvRockのみ\r
+  -情報は、予約一覧取得時に一緒に取得する(将来的に独立する可能性大)\r
+  -背景色の意味は以下の通り\r
+    (1)黄色:失敗した可能性が高いもの(EDCB:結果が「録画終了」以外のもの、TvRock:終了時のDiskFreeが0.1%以下のもの[2TBのHDDで残り容量が2GBを切っているという状況を想定])\r
+    (2)赤色:画像・音声までドロップしたと思われるもの\r
+    (3)桃色:画像・音声はドロップしていないと思われるもの\r
+  -EDCBでの情報削除は未実装\r
+\r
+3.17.3β+1.5.12(2013-01-15)\r
+■変更点\r
+ ・(本体予約一覧) 番組名でソートする場合に、タイトルの先頭の記号のいくつかを削除(replaceAll("^(\\[(新|無|字|HV)\\]|無料≫)+", ""))したり話数を正規化([第#]\\d→[第#]0\\d)したり全半大小同一視したりして極力話数順に並ぶようにした(ソート用の内部データの話なので表示上はかわらない)\r
+ ・(本体予約一覧[EDCB/TvRock]) チューナー不足(赤)・警告(黄色)になっている予約を、鯛ナビ上でも強調表示(どちらでも黄色)するようにした(EDCB/TvRockから予約一覧を取得した結果のみ反映される。鯛ナビから予約を入れた際に自動でチェックするわけではない)(>>239.)\r
+■バグ修正\r
+ ・(新聞形式) 「マギ」という予約名に対して「マギ #14」という番組情報が類似予約としてひっかからない問題を修正\r
+ ・(レコーダ対応[TvRock]) 「EPG予約」で「番組追跡がOFF」の予約が本体予約一覧に表示されない問題を修正\r
+ ・(レコーダ対応[TvRock]) 番組追跡で24時をまたいでの時間変更があると開始時刻が正しく表示されない問題を修正(ただし、年をまたぐ場合は追いかけられない)\r
+ ・(その他) 起動時の地域一覧/放送局一覧取得にProxy設定が反映されない問題を修正(VerUpの方は、VerUP間隔指定無視ともどもまだ)\r
+\r
+3.17.2β+1.5.12(2013-01-07)\r
+■バグ修正\r
+ ・(Web番組表対応[Webザテ]) 一部の番組で、新番組フラグが立たない問題を修正(以前は「ほげほげ #1 新」と表示されていたものが「ほげほげ #1」としか表示されなくなっていた)(>>236.)\r
+ ・(キーワード検索) 「サブタイトルを番組追跡の対象から除外する」を有効にしているとタイトルからカットしたサブタイトル部分がキーワード検索の対象から外れる問題を修正(>>230,231.)\r
+\r
+★対応チケット\r
+http://sourceforge.jp/ticket/browse.php?group_id=4315&tid=30449\r
+\r
+3.17.1β+1.5.12(2012-12-25)\r
+■変更点\r
+ ・(Web番組表対応) タイトル先頭「特[::]」の番組に特番フラグを付加するようにした\r
+ ・(Web番組表対応) ジャンルが「アニメ」で、かつジャンルリストに「映画/アニメ」がないときに、番組詳細に「映画」が含まれていたらジャンルリストに「映画/アニメ」を追加するようにした\r
+   ★\r
+   ★ 情報番組などがひっかかる場合がありますので、キーワード検索のアニメ映画の条件に「番組長45分以上」などの制限を追加してください\r
+   ★\r
+ ・(リスト形式) 過去番組の背景色の優先度を予約強調・ピックアップ強調よりも上位に変更\r
+ ・(リスト形式) 8日先の番組の文字色を薄くするようにした(繰り返し予約で8日先の時間変更にあわせて時刻変更を行ってしまい、当日の番組を撮り逃した…とかやらかさないように)\r
+■バグ修正\r
+ ・(しょぼかる) 取得できない番組情報があったのを修正(rss2.phpで抽出できない番組があったため、rss.phpに戻した)\r
+ ・(番組追跡) 「タイトルに話数が含まれる場合に以降を分離する」が無効の際、番組追跡の比較対象にサブタイトルまで含まれていて本来の役目を果たしていなかった問題を修正(「サブタイトルを番組追跡の対象から除外する」オプション追加、デフォルトはON)\r
+ ・(予約ダイアログ) 旧RDデジ系(R1/R2でなくDR/REなRD)で、予約ダイアログを開いたとき画質に連動した空きエンコーダ選択が行われない問題を修正(>>144.)\r
+ ・(レコーダ対応[EpgDataCap_Bon]) 「指定サービス対象」で"+字幕"・"+カルーセル"・"+字幕&カルーセル"を選んでも反映されない問題を修正(>>140.)\r
+ ・(リスト形式) 一括予約が途中で止まる問題を修正(>>138.)\r
+\r
+★対応チケット\r
+http://sourceforge.jp/ticket/browse.php?group_id=4315&tid=30214\r
+http://sourceforge.jp/ticket/browse.php?group_id=4315&tid=30250\r
+http://sourceforge.jp/ticket/browse.php?group_id=4315&tid=30290\r
+\r
+3.17+1.5.12(2012-12-16)\r
+■変更点\r
+ ・(Web番組表対応[webザテ、TV王国]) 番組詳細の個別取得を廃止\r
+ ・(レコーダ対応[BZ700(700,710)]) HTMLの書式変更によりエンコーダ情報が取得できなくなっていた問題に対応\r
+ ・(右クリックメニュー) CHAN-TORU予約ページへのジャンプ機能を追加\r
+\r
+★CHAN-TORU予約ページへのジャンプ機能 の注意事項\r
+ ・地上波&BSでエリアに「全国」を指定している場合のみ、地上波に関してはCH設定で有効にしている必要がある\r
+  エリアが全国でない場合は、有効でも無効でもどちらでも構わない\r
+ ・BSとCDに関しては有効でも無効でもどちらでも構わない\r
+ ・TV王国系プラグイン以外のWeb番組表から実行する場合は、CHコンバート設定で連携が取れていないといけない(放送局名が一致していないといけない)\r
+ ・予約ページはデフォルトブラウザで開くので、デフォルトブラウザはChromeやChromeFrame on IEなどのWebKit系ブラウザにする必要がある\r
+ ・予約ページのHTMLだけを呼び出しているので、CHAN-TORU上で開いたのと見た目が違う\r
+ ・SONY機を持っていないので、予約ページが開けるところまでしか確認していない。本当に予約が実行できるかどうかは不明\r
+\r
+★対応チケット\r
+http://sourceforge.jp/ticket/browse.php?group_id=4315&tid=30299\r
+\r
+■バージョンアップ時の補足\r
+ ・Java同梱版のJRE6のバージョンをupdate37からupdate38に変更しました。\r
+\r
+--- 3.16 -> 3.17 ---\r
+\r
+3.16.13β+1.5.12(2012-12-09)\r
+■変更点\r
+ ・(リスト形式) ピックアップノードを予約待機ノードの下に移動\r
+ ・(リスト形式) 重複チェックが表示上の並び順に従って実施されていたのを、開始時刻昇順にチェックするように変更\r
+ ・(新聞形式) 時刻行のフォントサイズを番組詳細のフォントサイズと同じにした\r
+ ・(MacOSX) 予約マークのOsakaフォント強制を復活(>>139.)\r
+ ・(その他) テーブルの行の高さをフォントサイズ変更にあわせて変わるようにした(反応しないテーブルがあるかもしれないけど)\r
+ ・(その他) フルスクリーンモードからウィドウモードに戻した時に、ステータスエリアの状態をウィンドウモード時の状態に戻すようにした\r
+■バグ修正\r
+ ・(リスト形式) 隣接行以外の番組間で重複チェックが働かない問題を修正(>>151.)\r
+ ・(新聞形式) 起動中に29時をまたいだあと放送局別を表示をすると、描画が前日分から開始されてしまう問題を修正(ただし、タブ移動をしない間は変わらない)\r
+ ・(予約ダイアログ) リスト形式/新聞形式から開いた場合、類似予約に番組IDがあっても利用されない問題を修正(>>140.)\r
+ ・(予約ダイアログ) 開くたびに類似予約コンボボックスが膨らんでいく問題を修正\r
+ ・(予約ダイアログ) 類似予約からの情報引き継ぎが無効になっていると、コンボボックスを操作しても情報引き継ぎが行えない問題を修正\r
+ ・(レコーダ設定) エンコーダの色選択ができなくなっていた問題を修正\r
+ ・(CHソート設定) 有効無効ボタンの文言の表示が変だったので修正\r
+ ・(CHソート設定) 新聞形式で日付系ノード以外を選択している時に更新確定ボタンを押すと例外が発生する問題を修正\r
+ ・(CHソート設定) 新聞形式で日付系ノード以外を選択している時なのに更新確定ボタンを押すとページャーが有効になってしまう問題を修正\r
+ ・(CHコンバート設定) CH設定更新後、プラグインコンボボックスを操作しないとCH設定の変更がテーブルに反映されない問題を修正\r
+ ・(MacOSX) 起動時に1回ダミーのフォント変更を入れないと、各種設定からフォント変更を行っても反映されない問題を修正\r
+ ・(MacOSX) 起動途中でランダムに止まることがある問題に手を入れてみた(原因不明なので完治かどうかは不明)\r
+ ・(各種設定) フォントサイズコンボボックスを操作しても反応しないタブがある問題を修正\r
+ ・(その他) フォント変更の際、テーブルの色つきセルのフォントが再起動しないと変更されない問題を修正\r
+ ・(その他) フルスクリーンモードからウィドウモードに戻した時にステータスエリアの状態が復帰されなくなっていた問題を修正\r
+ ・(その他) フルスクリーンモードからウィドウモードに戻した時にサイドツリーの幅が復帰されなくなっていた問題を修正\r
+ ・(その他) ポップアップの幅が広がるコンボボックスで、ポップアップの表示が変になる問題を修正\r
+\r
+★対応チケット\r
+http://sourceforge.jp/ticket/browse.php?group_id=4315&tid=30249\r
+http://sourceforge.jp/ticket/browse.php?group_id=4315&tid=30250\r
+http://sourceforge.jp/ticket/browse.php?group_id=4315&tid=30251\r
+\r
+3.16.12β+1.5.12(2012-11-28)\r
+■変更点\r
+ ・(Web番組表対応) マルチジャンルでジャンル=その他-その他以外が存在する場合はその他-その他をカットするようにした\r
+■バグ修正\r
+ ・(番組追跡) リピート判定で、話数番号を文字列比較としていたために「第9話」が「第10話」より新しいと判定されてしまう問題を修正\r
+ ・(リスト形式) CHコードの設定が完了していない放送局の予約があると例外が発生して起動しなくなる問題を修正(>>ticket.)\r
+ ・(Web番組表対応[Dimora]) 番組IDのevent_id部が0000で初期化されない場合がある問題を修正\r
+ ・(Web番組表対応) マルチジャンルにジャンル=その他-その他が重複して発生する問題を修正\r
+ ・(しょぼかる) 「[!]しょぼかるに存在しない」マークが表示されなくなった問題を修正\r
+\r
+★対応チケット\r
+http://sourceforge.jp/ticket/browse.php?group_id=4315&tid=30206\r
+\r
+3.16.11β+1.5.12(2012-11-25)\r
+■バグ修正\r
+ ・(ピックアップ) 3.16.10βでの修正漏れ…\r
+\r
+3.16.10β+1.5.12(2012-11-25)\r
+■バグ修正\r
+ ・(ピックアップ) バージョンアップ時のピックアップ情報の移行に失敗して起動できなくなったりする問題を修正(>>127.)\r
+ ・(その他) 多重起動ロックが効かなくなった問題を修正(>>129.)\r
+\r
+★対応チケット\r
+http://sourceforge.jp/ticket/browse.php?group_id=4315&tid=30189\r
+http://sourceforge.jp/ticket/browse.php?group_id=4315&tid=30190\r
+\r
+3.16.9β+1.5.12(2012-11-25)\r
+■変更点\r
+ ・(ピックアップ) 保存ファイルをXML形式からテキスト形式に変更\r
+■バグ修正\r
+ ・(レコーダ対応[EpgDataCap_Bon、TvRock]) REGZA形式の番組IDでもEPG予約できるように変更(>>123.)\r
+ ・(リスト形式) 現在放送中行の背景色が、設定を変えても再起動すると戻ってしまう問題を修正(>>125.)\r
+\r
+★対応チケット\r
+http://sourceforge.jp/ticket/browse.php?group_id=4315&tid=30182\r
+http://sourceforge.jp/ticket/browse.php?group_id=4315&tid=30183\r
+\r
+3.16.8β+1.5.12(2012-11-23)\r
+■変更点\r
+ ・(Web番組表対応[Dimora]) 8日目の情報が取得できなくなる場合がある問題に対応(暫定)\r
+■バグ修正\r
+ ・(CHコード設定[M190]) 予約一覧の取得ができなくなった問題に対応(>>118.)\r
+\r
+★対応チケット\r
+http://sourceforge.jp/ticket/browse.php?group_id=4315&tid=30176\r
+\r
+3.16.7β+1.5.12(2012-11-23)\r
+■変更点\r
+ ・(レコーダ対応[NULL]) CHコード設定の自動化。ただし「更新を確定する」ボタンのPUSHは必要\r
+■バグ修正\r
+ ・(各種設定) ページあたりの放送局数を設定しても再起動すると7に戻ってしまう問題を修正(>>115.)\r
+ ・(各種設定) ツリーペーンの幅の動機を設定解除してもリスト形式のペーンの幅に新聞形式のペーンの幅が同期してしまう問題を修正(>>115.)\r
+ ・(ツールボックス) レコーダ選択コンボボックスにNULLプラグインが表示されない問題を修正(>>115.)\r
+ ・(レコーダ対応[NULL]) 再起動すると予約一覧が空になってしまう問題を修正(>>115.)\r
+\r
+★対応チケット\r
+http://sourceforge.jp/ticket/browse.php?group_id=4315&tid=30174\r
+\r
+3.16.6β+1.5.12(2012-11-22)\r
+■変更点\r
+ ・(リスト形式) 現在放送中ノードにジャンル別サブノードを追加\r
+ ・(リスト形式) 現在放送中ノードで、終了した番組はリスト上部にまとめて、放送中の番組は終了時刻昇順に並べ替えて表示するように変更\r
+ ・(Web番組表対応[スカパー!]) 番組表の情報のみで番組ID取得を実行できるようにした\r
+ ・(CHコード設定[M190]) レコーダの放送局名のコンボボックス選択肢化\r
+ ・(CHコード設定[RD-X5/X5EX/H1/H1EX/H1EX]) CHコード設定をコンボボックス化(今更意味はないでしょうが…)\r
+ ・(予約ダイアログ) 番組追従をジャンル別AV自動設定の対象に追加\r
+ ・(その他) onlyLoadProgramオプション指定時は自動バージョンアップを無効にした(しわすれていた)\r
+ ・(Web番組表対応) ジャンルが「アニメ」で、かつジャンルリストに「映画/アニメ」がないときに、タイトルに「劇場版」が含まれていたらジャンルリストに「映画/アニメ」を追加するようにした\r
+ ・(しょぼかる) 引き継ぎ先のジャンルが「映画」でかつサブジャンルが「アニメ」でないときに、ジャンルリストに「映画/アニメ」を追加するようにした\r
+ ・(その他) onlyLoadProgramオプション指定時は自動バージョンアップを無効にした(しわすれていた)\r
+ ・(その他) onlyLoadProgramオプションで使う履歴ファイルの書式を変えたのでenv/olp.historyをリリース媒体からコピーして使ってね\r
+■バグ修正\r
+ ・(CHコード設定[M190]) 予約一覧の取得をするたびに予約ダイアログの各選択肢のアイテムが増殖していく問題を修正\r
+ ・(しょぼかる) EDCB番組表以外からの番組IDの引継ぎが行えない問題を修正\r
+ ・(ツールバー) 「CSのみ取得」メニューが動作しなかった問題を修正\r
+ ・(ツールバー) 過去ログの日付指定ジャンプが動作しなくなった問題を修正\r
+ ・(Web番組表対応[スカパー!]) 放送局リストを取得したときにCH番号でソートするはずができていなかった問題を修正\r
+ ・(Web番組表対応[MSN]) エリアリストと放送局リストが取得できなくなっていた問題、取得に失敗すると起動できなくなる問題を修正\r
+ ・(レコーダ対応[EDCB]) Dimora/webザテ/TV王国からのEPG予約が行えない問題を修正(>>75,95.)\r
+ ・(レコーダ対応[RDアナ系]) 放送局名「外部入力」の予約時の放送局名でのウワガキ機能が機能しなくなっていた問題を修正(>>106.)\r
+ ・(本体予約一覧) CHコード設定が完了していない放送局名を持つ予約があると一覧が表示されない問題を修正\r
+ ・(各種設定) 起動時の「現在放送中の行の強調」の値が「予約行の強調」の値になっている問題を修正(>>90.)\r
+ ・(各種設定) 新規インストール時にクリップボードアイテムの選択肢が表示されない問題を修正(>>92.)\r
+ ・(リスト形式) 現在放送中の自動更新タイマーの不具合を修正\r
+ ・(リスト形式) 現在放送中の自動更新時にリストが縮小するとOutOfBounds例外が発生する場合がある問題を修正\r
+ ・(リスト形式) 現在放送中の自動更新時にリストの選択行が変わってしまう問題を修正\r
+ ・(リスト形式) 番組表や予約一覧の取得中に自動更新タイマーがトリガーされて処理中のデータに接触してしまう問題を修正\r
+ ・(予約ダイアログ) コンボボックスの連動関連のコードが腐っていたので全面リライト ★まだ途中なのでなにか問題が発生するかもしれません\r
+ ・(その他) 起動中にステータスウィンドウの[x]ボタンで鯛ナビを強制終了できるようにした\r
+ ・(その他) Puppy Linux(というかBusyBoxというかBusyBoxのash)でtinybannavi.shが実行できなかった問題を修正\r
+\r
+■既知の問題\r
+ ・(その他) Ubuntuで番組欄へのジャンプを行うと、ポインタの座標は移動する(ツールチップなどは移動先の情報が表示される)がカーソル自身は元の位置に表示されたままになってしまう\r
+\r
+★対応チケット\r
+http://sourceforge.jp/ticket/browse.php?group_id=4315&tid=30119\r
+http://sourceforge.jp/ticket/browse.php?group_id=4315&tid=30145\r
+http://sourceforge.jp/ticket/browse.php?group_id=4315&tid=30157\r
+http://sourceforge.jp/ticket/browse.php?group_id=4315&tid=30158\r
+\r
+3.16.5β+1.5.12(2012-11-15)\r
+■変更点\r
+ ・(Web番組表対応[Dimora、Webザテ、TV王国]) 番組表の情報のみで番組ID取得を実行できるようにした\r
+ ・(レコーダ対応[TvRock、iEPG2]) ↑の変更に対応\r
+ ・(レコーダ対応[TvRock]) 視聴コマンドを実装\r
+ ・(CHコード設定) 複数選択での削除を実装(>>84.)\r
+ ・(その他) onlyLoadProgramオプション追加\r
+■バグ修正\r
+ ・(レコーダ対応[TvRock]) TvRock番組表未取得状態でEPG予約を行おうとして失敗した際に「登録済みです」という誤ったエラーメッセージが出る問題を修正\r
+ ・(レコーダ対応[TvRock]) EPG予約で更新が行えない(単日予約なのに繰り返し予約と出てはねられる)問題を修正\r
+ ・(レコーダ設定) 同一のレコーダを重複登録できてしまう問題を修正\r
+ ・(CHコード設定) レコーダ設定で更新の確定をすると、「レコーダの放送局名」のコンボボックスが空になってしまい再起動するまで設定が行えなくなる問題を修正\r
+ ・(予約ダイアログ) ジャンル別AV設定の登録で、同一の登録キーに対して複数の設定が登録され重複が発生する問題を修正 ★意図している設定が自動反映されない場合があった\r
+ ・(各種設定) Proxyの設定がレコーダへのアクセスまで影響してしまう問題を修正(>>85.)\r
+\r
+★対応チケット\r
+http://sourceforge.jp/ticket/browse.php?group_id=4315&tid=30109\r
+\r
+3.16.4β+1.5.12(2012-11-14)\r
+■変更点\r
+ ・(リスト形式) 起動時の選択ノードの初期値を予約待機から現在放送中に変更\r
+ ・(リスト形式) 現在放送中ノードのオプション欄に終了or開始までの時間を表示するようにした\r
+ ・(CHコード設定[BZ700/810/610、Z260/160、TVTest、EDCB]) レコーダの放送局名のコンボボックス選択肢化\r
+ ・(CHコード設定[EDCB]) レコーダの放送局名を、こういうの(140610784855056)をやめて日本語名で選択肢が表示されるようにもどしたよ ★再編集は必要ないが、一回CHコード設定で「更新確定」ボタンを押した方がいい\r
+ ・(レコーダ対応[TvRock]) EPG予約(番組追従)に対応 ★TVTestプラグインとの併用を推奨\r
+ ・(レコーダ対応[TVTest]) CHコード設定の書式を変えた。従来の設定ファイルは使えない。まあwikiにも使い方書いてないし使っている人はいないか\r
+ ・(レコーダ対応[iEPGデジタル]) 番組ID取得に対応するレコーダプラグインを追加(RDデジ系、TVTest)\r
+ ・(キーワード検索ダイアログ) テキスト入力コンポーネントに右クリックメニューを追加\r
+■バグ修正\r
+ ・(リスト形式/新聞形式) 現在放送中/現在日時ノードの自動更新を、タイマー開始時刻に関係なく毎分00(+3くらい)秒にキックされるようにした\r
+ ・(レコーダ対応[EDCB、TVTest]) 在京局と難視聴地域対策局の放送局名が同じで問題があったため、TvRockにならって後者の先頭に"臨)"を付加するようにした\r
+ ・(レコーダ対応[BZ700系、Z260系]) RD側に存在しないジャンルの場合、「ジャンルなし」で登録するはずが、値なしで登録を行っていた問題を修正\r
+ ・(レコーダ対応[BZ700系、Z260系]) 予約名を86バイト、番組説明を75*5バイトまでに制限\r
+ ・(レコーダ対応[BZ700系、Z260系]) 単日予約の場合に曜日をURLエンコードせずに送っていたのを変更\r
+ ・(リスト形式) 過去ログ検索が失敗する問題を修正\r
+ ・(予約管理) 現在放送中の予約が終了済みと判断されて予約一覧に表示されない問題を修正\r
+\r
+※BZ700系…BZ700/BZ810/BR610\r
+※Z260系…Z260/Z160\r
+\r
+3.16.3β+1.5.12(2012-11-12)\r
+■変更点\r
+ ・(リスト形式) 予約実行行強調とは独立した、ピックアップ行強調を追加\r
+ ・(リスト形式) ピックアップノードをツリー上部に移動\r
+ ・(リスト形式) 現在放送中ノードと現在放送中行の強調表示を追加(>>43.) ★現在放送中ノードではタイマーで1分ごとに自動更新\r
+ ・(新聞形式) 過去ログ中の放送局に対して、現在の有効局リストでフィルタして絞り込みを行い、マッチしないものは表示せず捨てていたのを、捨てずに表示するように変更\r
+ ・(新聞形式) 地上波とBSのノードを分割(>>31.改題)\r
+ ・(新聞形式) 地上波/BS/CSノードでの放送局のフィルタリングを、プラグイン種別だけでなくエリアも参照して行うように変更\r
+ ・(本体予約一覧) エンコーダ欄をエンコーダ色表示するようにした(>>967.改題)\r
+ ・(各種設定) 新聞形式の描画方式の選択の説明をわかりやすいように変更\r
+ ・(CH設定とか) JTableの実装を変更したので複数選択での上下移動とか実現した ★したけど、もともとどれとどれのテーブルが非対応だったかよくわからなくなったのでどれとどれが直ったのか明言できない\r
+ ・(CHコード設定) TvRock、EDCBの入力方法を抜本的に見直した。TvRockとTVTestはレコーダの放送局名をコンボボックスで選択できるようにした\r
+ ・(キーワード検索) 予約待機への表示の除外設定を追加\r
+ ・(キーワード検索) 設定ダイアログに条件の上下移動ボタンを追加\r
+ ・(MacOSX) 予約マークの表示フォントにOsakaを強制するのをやめた(標準フォントではマークが小さな表示になってしまいますが)\r
+ ・(過去ログ) XMLEncoderによるシリアライズ処理をやめて独自形式のテキストファイルに変更 ★ファイルサイズが約半分に、読み出し処理の所要時間が実測で約1/3に短縮 ★古いXML形式にアクセスすると自動で新形式に変換します。範囲指定で過去ログ検索を行うと一気に変換できます。また、XML形式の削除はしないので邪魔になったら整理してください。\r
+ ・(デバッグログ) log.txtに全部吐く方法ではダンプの分離作業だけで日が暮れたりlog.txtに入りきらずあふれたりといったことが往々にしてあるので、初期のころのように1リクエスト1ファイルの個別ダンプを作成するように変更した\r
+ ・(CHコンバート設定) ChannelConvert.datの編集をようやくGUI化 ★時間切れのため更新がまだ実装されていない。次回持越し\r
+■バグ修正\r
+ ・(リスト形式) ピックアップされている番組に予約を行うと通常予約なのに警告マークが出る問題を修正(>>950.)\r
+ ・(リスト形式) 予約と重なっているが、開始時間はずれているという番組に予約マークが表示されない問題を修正(>>972.) ★それ以外にも既存バグがいろいろと仕込んであったのでそれもまとめて修正\r
+ ・(リスト形式) 開始時間1分前倒し設定にしている場合に、前の番組にも予約マークがついてしまって嬉しくない問題を修正\r
+ ・(新聞形式) ページャーを各種設定でON/OFFしても再起動しないと反映されない問題を修正(>>991.) ★それ以外にも新規/既存のバグがいろいろと仕込んであったのでそれもまとめて修正\r
+ ・(ツールバー) 番組表の個別取得メニューでCSのメニューが処理されなくなっていた問題を修正\r
+ ・(過去ログ) 過去ログが見れなくなった問題を修正(>>991.ほか)\r
+ ・(過去ログ) ページャーが有効だと過去ログ表示に問題が発生する場合があったのを修正(過去ログ中の放送局数が、現在の放送局数と一致しないと不正動作を起こしていた)\r
+ ・(レコーダ対応[TVTest]) まともに視聴コマンドが動くように修正\r
+ ・(その他) 開始終了日時リストの生成メソッド(CommonUtils#getStartEndList())の繰り返し予約向けロジックが腐っていたので全面リライト(>>33.)\r
+\r
+★対応チケット\r
+http://sourceforge.jp/ticket/browse.php?group_id=4315&tid=30020\r
+http://sourceforge.jp/ticket/browse.php?group_id=4315&tid=30024\r
+http://sourceforge.jp/ticket/browse.php?group_id=4315&tid=30047\r
+http://sourceforge.jp/ticket/browse.php?group_id=4315&tid=30061\r
+http://sourceforge.jp/ticket/browse.php?group_id=4315&tid=30070\r
+\r
+3.16.2β+1.5.12(2012-11-06)\r
+■変更点\r
+  none\r
+■バグ修正\r
+ ・(自動バージョンアップ) 自動バージョンアップが実行されなくなる場合があった問題を修正\r
+ ・(自動バージョンアップ) 起動後一番最初の処理にしなければいけないところが、間違って後方に移動してしまっていたのを修正\r
+\r
+3.16.1β+1.5.12(2012-11-06)\r
+■変更点\r
+  none\r
+■バグ修正\r
+ ・(予約ダイアログ) 延長警告と1分短縮のチェックボックスの状態がリセットされない問題を修正\r
+ ・(本体予約一覧) 繰り返し予約の展開をOFFにできない問題を修正\r
+ ・(リスト形式、本体予約一覧) テーブルのカラム幅の設定をファイルから読みだすのに失敗して起動できなくなる問題に対処、したつもり(最悪カラム幅がリセットされても起動だけはできるようになったはず)\r
+ ・(新聞形式) ページャー有効時にリスト形式からのジャンプを行うと正しい位置に飛ばない場合がある問題を修正\r
+ ・(Linux) ダイアログ関連がルックアンドフィールの変更をうけつけない問題を修正\r
+\r
+★対応チケット\r
+http://sourceforge.jp/ticket/browse.php?group_id=4315&tid=30014\r
+http://sourceforge.jp/ticket/browse.php?group_id=4315&tid=30016\r
+http://sourceforge.jp/ticket/browse.php?group_id=4315&tid=30019\r
+http://sourceforge.jp/ticket/browse.php?group_id=4315&tid=30017\r
+\r
+3.16+1.5.12(2012-11-05)\r
+■変更点\r
+ ・(CHソート設定) デフォルトで有効に変更(有効でも無効でも処理速度にはまったく関係ありません!)\r
+ ・(CHソート設定) なんらかの操作を行うまでは「更新を確定する」ボタンは押せないようにした\r
+ ・(CHソート設定) ページャーが有効な場合、その放送局が表示されるページ番号を表示するようにした\r
+ ・(CHソート設定) 複数行選択しての移動を可能にした\r
+ ・(リスト形式) タイトル前方のマークを別欄に独立させられるようにした(マークをテキストで表示させるのも限界か?Bitmapに変えたいがアイコンにつくるの面倒くさすぎる…)\r
+ ・(その他) タブの並び順を変更(CH/CHコード/CHソート → CH/CHソート/CHコード)\r
+ ・(その他) ★延長警告★→(延)設定で同時に[NEW]→(N)設定を追加\r
+ ・(その他) タイトルバーに新聞形式の番組枠の数を状況表示するようにした(使用数/確保数)\r
+■バグ修正\r
+ ・(新聞形式) ページャーが有効だと起動時とかページャーOFF→ONにした直後とか番組枠が描画されていない状態で開始する問題を修正\r
+ ・(新聞形式) 番組枠フレームバッファサイズの保存を無効にしたはずが生き残っていてメモリを無駄食いしてたので正しく無効化した\r
+ ・(リスト形式) リスト形式でサイドツリーの幅を変更しても新聞形式が連動しない問題を修正(逆はOK)\r
+ ・(色選択ダイアログ) ルックアンドフィールを変更するとカスタムチューザーが素のJColorChooserに戻ってしまっていたのに今更ながら気づいたので修正\r
+\r
+★対応チケット\r
+http://sourceforge.jp/ticket/browse.php?group_id=4315&tid=29929\r
+http://sourceforge.jp/ticket/browse.php?group_id=4315&tid=29932\r
+http://sourceforge.jp/ticket/browse.php?group_id=4315&tid=29981\r
+\r
+--- 3.15 -> 3.16 ---\r
+\r
+3.15.5α+1.5.12(2012-11-03)\r
+■変更点\r
+ ・(各種設定) 更新完了時に番組表の再取得を行うのは、番組表に関係する項目に変更があった場合のみになるようにした\r
+ ・(リスト形式、新聞形式) ツールバーのレコーダ選択肢に「ピックアップ」を追加し、予約欄をピックアップマークのみに絞り込み表示できるようにした\r
+ ・(新聞形式) ページャーコンボボックスに、選択肢に対応する放送局名を表示するようにした\r
+■バグ修正\r
+ ・(レコーダ対応[BZ810]) AVC録画を指定してもVR録画になるバグを修正(DRの場合はエンコーダに3/4、それ以外は1/2を渡していたがどうも最近はどちらでも3/4を渡すことになってるらしい。もとから?途中で変わった?謎)(>>メール.)\r
+ ・(CHコード設定) 「削除」ボタンが表示されなくなっていた問題を修正(レイアウトミスで他の部品に隠れていた)(>>934.)\r
+ ・(CHコード設定) CH設定タブでの変更がCHコード設定タブに即時反映されない問題を修正(>>934.)\r
+ ・(しょぼかる) しょぼかる連携が一部動作しなくなった問題を修正(ChannelConvert.datを反映しないベアな放送局名を利用するようになってしまっていたため)\r
+ ・(本体予約一覧) 起動時に登録してあるレコーダの数だけ予約情報が複製、表示されてバイバイン状態になる問題を修正(予約情報抽出のレコーダループの外にいるべきアイテム追加作業がループの中にいたため)\r
+ ・(リスト形式) 裏番組マークがなぜか同一放送局の番組しか選択しないようになっていたという謎の条件式を逆転(自局の番組にマークついても裏番組ではないよね…)\r
+ ・(新聞形式) ページャー有効時に、新聞形式へのジャンプやレコーダ選択コンボボックスによる予約枠の書き換えががいつの間にか正しく動作しなくなっていた問題を修正(本当にいつの間に…)\r
+ ・(リスト形式、新聞形式) しょぼかるリストで右クリックメニューを開いても「予約」アイテムが表示されない問題を修正(ダブルクリックでは開けていたので実害はないと思いますが)\r
+\r
+★対応チケット\r
+※α版につきチケット管理対象外\r
+\r
+3.15.4α+1.5.12(2012-11-02)\r
+■変更点\r
+ ・(予約ダイアログ[主にEDCB対応]) 番組IDの取得機能を追加。CHコード設定さえ完了していれば、EDCB番組表を使わなくてもEPG予約ができるよ、多分。 ※番組IDの取得は放送局+開始日時なので、取得先とみてるWeb番組表で差異があったら取得できないよ\r
+ ・(レコーダ対応[REGZA DBR-M190]) 予約一覧取得の際にCHコードのサンプル一覧をlog.txtに出力するようにした\r
+ ・(CHコード設定) 一部のレコーダプラグインに設定ヘルプを表示させるようにした\r
+ ・(リスト形式) 予約実行状態の行の背景色のデフォルト値を変更\r
+ ・(しょぼかる) 取得データをrssからrss2に変更\r
+ ・(しょぼかる) マルチフラグ対応\r
+ ・(しょぼかる) マルチジャンル対応(アニメのほかには、映画くらいしか対応してないですが…)\r
+ ・(しょぼかる) 「(移)先週なかったか時間が違う」「【先】先行放送」に対応\r
+ ・(本体予約一覧) 繰り返し予約の展開ON/OFFの設定を追加\r
+ ・(Web番組表対応) 起動時に初期化するプラグインを選択中のプラグインのみにして起動時間の短縮をはかった\r
+ ※(その他) 開発途中のα版のため機能未実装の部品がありますが、無視してください\r
+■バグ修正\r
+ ・(レコーダ対応[BZ700/BZ810/BR610]) HTMLの書式変更によりエンコーダ情報が取得できなくなっていた問題に対応(>>mailほか.)\r
+ ・(リスト形式) ソータが効いている状態で、表示行されるが減少する方向に操作をすると例外が発生して動作が停止する問題を修正(>>895.)\r
+ ・(リスト形式) 予約実行行の強調表示を無効にすると予約待機で表示した一覧が全部グレーアウトしてしまう問題を修正(>>899.)\r
+ ・(Web番組表対応[MSN、TV王国]) BSのページ数カウンタファイルの読み出しに失敗する問題を修正(直さない状態でも、動作に影響はない)\r
+ ・(Web番組表対応[EDCB]) 不正なEPG.datでNumberFormatExceptionが発生して死ぬ場合があった問題を修正(ほかにもあるかもしれないが)\r
+ ・(Web番組表対応) タイトルと番組詳細で話数表記が重複してる場合に重複を排除する機能が働かなくなっていた問題を修正\r
+ ・(レコーダ対応[EDCB]) 鯛ナビ内部では予約IDをユニークなものとして扱っているが、これはEDCBと合わないので、管理用にユニークなIDを振るようにした\r
+ ・(その他) 画面上部の番組詳細欄の件、Look&Feelを変えると背景色がColor.WHITEに戻ってしまう問題に対応\r
+\r
+ ★(全般)\r
+  ・従来環境設定ファイルの取り込みなどの初期化が終わってからウィンドウを作成していたが、初期化処理中に例外が発生すると\r
+   まったくウィンドウが表示されずゾンビになってしまう問題があったが、本バージョンからウィンドウを作成してから初期化処理を行うようにした\r
+  ・初期化処理が完了するまでウィンドウ内に表示するものがないのでスプラッシュを復活した\r
+  ・DOS窓廃止とステータスウィンドウへの起動時メッセージの出力強化に伴い、WindowsのDOS窓でMS932出力していたのを終了 →MS932出力のせいでeclipseでconsoleが化けるからバグとり面倒だったんだよ!!\r
+  ・クラスやenumをそのままXMLEncoder()にかけたのはガチで失敗だった。二度とやらない\r
+\r
+  ・現在デバッグログが山のように出ますが、無視してください\r
\r
+ ★(ソースを見てる方向け)\r
+  ・ソースコードの編集が苦痛になってきたので全体的な整理を断行しました。\r
+   自家製ビルドでプラグインの自作などを行っている方は間違いなくそのままでは再ビルドできません。\r
+   細かいところで新たなバグが混入している可能性も否定できません。\r
+   キモのところにはjavadocを入れておいたので解析してください。\r
+   すいません。\r
+   【やったこと】\r
+   -ファイル入出力系を極力CommonUtilsの共通メソッドで置き換え\r
+   -日付操作系(getDate()→getDate529()など)\r
+   -レコーダプラグインのCloneable化\r
+   -情報を保持する系のクラスや設定ファイルを大幅に整理 → 既存ソース流用の自家製プラグインは動かないでしょう\r
+   -今後javadocを追加していきたい。自分でもメソッドの内容のわからない時が多いので…\r
+   -メインであるViewer.classをずっと放置していたがあまりにも肥大化しすぎた(12K超えてた!)ため分割作業を実施。とりあえずタブごとの内部クラスはわけた。結果5Kまで縮小\r
+   【できなかったこと、今後やるかもしれないこと】\r
+   -Viewer.classに残るツールバーとタブをまたぐメソッド群を分離するかどうか検討中\r
+   -CommonUtilsとか用途ごとにサブパッケージにわけようと思ったが修正量が多すぎて断念\r
+   -ReserveListをReserveInfoとReserveInfoListに再構成しようと思ったが修正量が多すぎて断念\r
+\r
+★対応チケット\r
+※α版につきチケット管理対象外\r
+\r
+3.15.3β+1.5.12(2012-10-22)\r
+■変更点\r
+ ・(本体予約一覧) ソーターを有効にした\r
+■バグ修正\r
+ ・(リスト形式/本体予約一覧) 表示行数が0行の時動作が停止する問題を修正\r
+ ・(リスト形式) ソーターを動かしたときに重複マークが更新されない問題を修正\r
+ ・(しょぼかる連携) ジャンル=アニメ以外の番組にも「しょぼかるに存在しない」マークが付加されるようになってしまった問題を修正\r
+\r
+3.15.2β+1.5.12(2012-10-21)\r
+■変更点\r
+ ・(レコーダ対応[EDCB]) 同一番組の重複登録に対応 ※同一放送局・同一開始終了日時で重複している番組は、チューナーの種別によって別個の予約と判断されますので必ず異なるチューナーを設定してください\r
+ ・(レコーダ対応[EPGデジタル]) EDCB番組表との組み合わせでEDCBに食わせることができるtvpidファイルを生成できるようにした\r
+ ・(予約ダイアログ) 空きチューナがみつからないときに警告メッセージを表示するようにした(>>885.)\r
+ ・(リスト形式) 予約実行状態の行の背景色を変えて強調表示できるようにした\r
+ ・(予約一覧) 実行OFF行のグレーアウトを実装\r
+ ・(過去ログ) 何日先のログまで過去ログ用に先行保存しておくか調整できるようにした(1~8日、デフォルトは4日、従来は7or8日) \r
+■バグ修正\r
+ ・(レコーダ対応[EDCB]) EPG予約の削除が動作していなかった問題を修正\r
+ ・(レコーダ対応[EDCB]) EPG予約の場合、予約一覧に表示される開始・終了日時と実際の開始・終了日時が異なる場合がある問題に対応 ※一覧のは登録時の値のまま変化せず、詳細のはEPGで追従した値に変わるみたい\r
+ ・(Web番組表対応[Dimora]) サブタイトル分離を有効にしているとき、サブタイトルが二重に番組詳細に追加されてしまう問題を修正\r
+ ・(リスト形式) ソーターを使っていると過去番組のグレー表示がずれる問題を修正 ※またか!\r
+ ・(その他) ジャンル判定のコードを整理(内部的な話ですが…)\r
+ ・(その他) Calendar.get(Calendar.MONTH)とやるところが全部Calendar.get(Calendar.MONDAY)になっていた。なんという恐怖!でも動作上問題がなかったのが不満…\r
+\r
+★対応チケット\r
+http://sourceforge.jp/ticket/browse.php?group_id=4315&tid=29901\r
+\r
+3.15.1β+1.5.12(2012-10-20)\r
+■変更点\r
+ ・(リスト形式) 過去番組がグレー表示の判定基準を開始日時から終了日時に変更 ※開始時刻基準だとそうはならないが、終了時刻基準だとグレーが歯抜けになることが多いので微妙ではある\r
+ ・(しょぼかる) しょぼかるから通常の番組表へのフラグの引き継ぎとは逆に、EDCB番組表からしょぼかるへの番組IDの引き継ぎを行ってしょぼかるリストからもEPG予約できるようにした ※枠移動していると番組IDを継承できない。周辺検索するか?\r
+■バグ修正\r
+ ・(Web番組表) (18)等、括弧つきの数字がタイトル中にあると未定義フラグとしてログ出力されてしまう問題を修正\r
+ ・(リスト形式) 「番組詳細」が非表示だと過去番組がグレー表示されない問題を修正\r
+ ・(番組追跡) リピート放送の検知が誤動作するようになった問題を修正\r
+ ・(しょぼかる) 「しょぼかるの検索結果も有効な放送局のみに絞る」を無効にした際、「番組追跡>しょぼかるのみ」のノードまで無効局の番組がリストアップされてしまい見づらくてしょうがなくなっていた問題を修正\r
+ ・(その他) NHKのアニメ番組のタイトルに先頭についている「アニメ」を削除する機能が働かない問題を修正 ※マルチジャンル対応の改修漏れ\r
+ ・(その他) 環境によっては指定されたヒープサイズを予約できず鯛ナビが起動しない問題を修正(>>881.) ※起動バッチで、最大ヒープサイズの指定(-Xmx)を環境にあわせて1024MB→768MB→512MB→256MBのように動的に変更するようにした(Windowsのみ)\r
+\r
+★対応チケット\r
+http://sourceforge.jp/ticket/browse.php?group_id=4315&tid=29898\r
+\r
+3.15+1.5.12(2012-10-19)\r
+■変更点\r
+ ★(Web番組表対応[EDCB]) 東京地デジ&BS&CSで、しょぼかる連携が動作するように ChannelConvert.dat を整理した ★★★自動では更新されません!!★★★\r
+ ・(Web番組表対応[EDCB]) 番組詳細の【ジャンル】以前と以降の記述の管理を物理的に分離した(内部的な話ですが)\r
+ ・(Web番組表対応[EDCB]) 番組詳細にやたらと改行が入りまくるのを調整\r
+ ・(レコーダ対応[EDCB]) プリセット・番組追従ほか、WebUIから指定できる設定にすべて(ホントに?)対応\r
+ ・(リスト形式) 「しょぼかるの検索結果も有効な放送局のみに絞る」オプションを追加 ★デフォルトはONで、これは従来通りの動き \r
+ ・(しょぼかる連携) 番組表取得の際に、しょぼかる連携が有効な放送局の一覧を通知するようにした ★次回は編集をGUI化したい\r
+ ・(番組追跡) リピート放送の表示をON/OFFできるようにした(>>815.) ★「番組を表示しない(デフォルトON)」の設定と「[初]マークを付ける(デフォルトOFF)」は別の設定にわかれているので注意\r
+ ・(その他) 「実行ONのエントリのみ予約マークを表示する」のデフォルトを「無効」から「有効」に変更\r
+ ・(その他) 「タイトルに話数が含まれる場合に以降を分離する」のデフォルトを「有効」から「無効」に変更\r
+ ・(その他) 重い処理の一部に所要時間を入れてみた。今後も追加していきたい所存\r
+ ・(その他) ツリーペーンのrootノードの表示ON/OFF設定を追加(>>825.)\r
+ ・(その他) 放送局を沢山登録しても大丈夫なように、-Xmx512MB→-Xmx1024MBに変更(>>851.)\r
+■バグ修正\r
+ ・(リスト形式) 検索結果で日跨りの番組の一部が重複して表示されるようになった問題を修正(>>843.)\r
+ ・(Web番組表対応[EDCB]) メモリ消費が激しい問題を修正\r
+ ・(Web番組表対応[EDCB]) 番組詳細を取得できない場合があった問題を修正\r
+ ・(Web番組表対応) レコーダのポート番号が指定されていない場合、ジャンル別録画設定が動作しなかったり予約ダイアログが開かなかったりする問題を修正(>>860.)\r
+ ・(レコーダ対応[EDCB]) chtype.xmlが、予約一覧再取得のたびに膨らんでいく問題を修正(>>872.) \r
+ ・(レコーダ対応[EDCB]) 予約情報一覧取得の際の、既存一覧からの情報引き継ぎで単純に予約IDだけ見て実施していた問題を修正\r
+ ・(レコーダ対応[EDCB]) 予約情報一覧取得の際の、既存一覧からの情報引き継ぎで単純に予約IDだけ見て実施していた問題を修正\r
+ ・(その他) CHコード設定不正時に大量に発生していた下記メッセージを抑制 → 予約一覧を(レコーダ/キャッシュ)から読み込んだ時だけだすようにした\r
+    【警告(?)】レコーダの放送局名「"+r.getChannel()+"」をWeb番組表の放送局名に変換できません。CHコード設定を修正してください。\r
+ ・(その他) static修飾子の使い方が悪くて問題を起こす可能性があったのを修正\r
+\r
+★対応チケット\r
+http://sourceforge.jp/ticket/browse.php?group_id=4315&tid=29871\r
+http://sourceforge.jp/ticket/browse.php?group_id=4315&tid=29863\r
+http://sourceforge.jp/ticket/browse.php?group_id=4315&tid=29888\r
+http://sourceforge.jp/ticket/browse.php?group_id=4315&tid=29857\r
+http://sourceforge.jp/ticket/browse.php?group_id=4315&tid=29802\r
+http://sourceforge.jp/ticket/browse.php?group_id=4315&tid=29846\r
+\r
+■バージョンアップ時の補足\r
+ ・Java同梱版のJRE6のバージョンをupdate33からupdate37に変更しました。\r
+  もうJava6のアップデートはないでしょうから、次からはJava7になりそうですね。\r
+\r
+--- 3.14 -> 3.15 ---\r
+\r
+★★★★★★★★重要★★★★★★★★★★\r
+EDCBプラグインはCHコード設定の書式を変えたので既存ユーザはwikiをみて書き換えてください。\r
+※「放送局コード」の値で「レコーダの放送局名」を上書きするだけですが。\r
+★★★★★★★★★★★★★★★★★★★★\r
+3.14.12β+1.5.12(2012-10-16)\r
+■変更点\r
+ ・(レコーダ対応[EDCB]) Web番組表[EDCB]プラグインとの組み合わせでEPG予約をできるようにした(>>787.) ★細かい使い方はwikiを参照してください \r
+ ・(レコーダ対応[TvRock]) 時間追従の有効な予約の場合は番組詳細を取得するようにした\r
+ ・(リスト形式) 番組追跡・キーワード検索の各ノードについて、条件に一致する番組が存在しないものを赤で警告表示するようにした ※チェック漏れ対策強化月間\r
+ ・(Web番組表対応[Dimora]) マルチジャンル対応 ※ジャンルのリストは番組詳細末尾に記載されます\r
+ ・(その他) マルチジャンル対応改修 ※改修ターゲット:ジャンル別リスト表示、キーワード検索、しょぼかるフラグ引き継ぎ ※過去ログはマルチジャンル非対応\r
+ ・(Web番組表対応[EDCB]) TSファイルから番組情報を抜き出して番組表を作成する機能を実装 ※生TSファイルを直接参照もできる「はず」ですが、途中切り上げの処理を入れてないのでファイルサイズが大きいといつまでたっても読み込みが終わらないので推奨しません\r
+ ・(Web番組表対応[MSN]) アイコン「zen、kou」((前編)、(後編))、「s、3d、eiga、hand」(無視)に対応したほか、未定義のものがあった場合はログに出力するようにした(>>838.)\r
+ ・(Web番組表対応) ジャンル「キッズ」を廃止 ※papercolors.xmlの読み込み時にエラーログが出るけど無視で。気になるなら各種設定タブを一回更新すればOK。\r
+ ・(リスト形式) 同一番組重複表示抑制の重複判定条件に、開始日時+放送局名に加えて番組タイトルも追加した ※しょぼかるのGyaoのように同一時間に複数の番組がある場合への対処\r
+ ・(予約ダイアログ) ジャンル別画質・音質設定の保存に、ジャンルによらないデフォルトの「既定化」保存を追加\r
+■バグ修正\r
+ ・(レコーダ対応[RD-XS57/S600]) 予約登録時と予約一覧取得時の画質の表記が一致していないことに起因して更新時に正しい画質が設定されない問題を修正(>>832.)\r
+ ・(リスト形式) 開始日時以外でソートしていると一括予約が正しく動作しない問題を修正(>>801.)\r
+ ・(予約操作) 操作に失敗してもステータスウィンドウに「成功」と表示される場合があった問題を修正\r
+ ・(番組追跡) Web番組表の放送局名に")"が含まれていると番組追跡への登録がうまくいかない場合があった問題を修正\r
+■対応チケット\r
+https://sourceforge.jp/ticket/browse.php?group_id=4315&tid=29813\r
+https://sourceforge.jp/ticket/browse.php?group_id=4315&tid=29789\r
+https://sourceforge.jp/ticket/browse.php?group_id=4315&tid=29842\r
+https://sourceforge.jp/ticket/browse.php?group_id=4315&tid=29838\r
+\r
+3.14.11β+1.5.12(2012-10-13)\r
+■バグ修正\r
+ ・(その他) XMLファイルの書き出しで一時ファイル[filname.tmp]をclose()せずに正式ファイル[filename]にリネームしようとして失敗する場合があった問題を修正\r
+\r
+★★★★★★★★重要★★★★★★★★★★\r
+改行を含んだ番組詳細をもつ予約をTvRockに投入した結果、TvRockが起動できなくなるという問題を修正しました。\r
+ただし、TvRockに投入ずみの既存の予約を修正できるわけではないので、バージョンアップ後予約を更新するか、\r
+tvrock.schをテキストエディタで開いてSUBTITLE項目の改行をすべて削除してください。\r
+★★★★★★★★★★★★★★★★★★★★\r
+\r
+3.14.10β+1.5.12(2012-10-08)\r
+■変更点\r
+ ・(Web番組表対応) 強制再取得メニューに「CS[プライマリ]のみ」「CS[セカンダリ]のみ」を追加(>>745.)\r
+ ・(レコーダ対応[DIGA BZT710]) BWT2100コードベースに移行\r
+ ・(レコーダ対応[EDCB]) EPG予約の場合番組詳細をEDCBから取得するようにした\r
+ ・(レコーダ対応[EDCB]) プログラム予約の更新が「開始・終了日時」と「タイトル」の変更を伴わないものであれば削除→登録ではなく更新1アクションのみで済むようにした\r
+ ・(レコーダ対応[EDCB]) 予約で指定できる項目に以下を追加「連続録画動作」「ぴったり録画」「復帰後再起動」\r
+ ・(レコーダ対応[EDCB]) 「録画モード」コンボボックス中から「無効」は削除し、「予約実行」チェックボックスで操作するように変更した\r
+ ・(レコーダ対応[EDCB]) 予約一覧取得の際に使用可能なCHコードの一覧をlog.txtに出力するようにした\r
+ ・(レコーダ対応) 繰り返し予約のできないレコーダの場合は予約ダイアログで日付コンボボックスを操作できないようにした ※EDCB、iEPG系、MAIL系\r
+ ・(リスト形式) 開始日時が過去のものをグレー表示するようにした\r
+ ・(番組追跡) 「リピート放送を除く」 ※デフォルトはOFFです\r
+ ・(Web番組表対応[しょぼかる]) 無料放送情報を利用するようにした\r
+ ・(その他) スプラッシュ画面を廃止 ※Linuxで、アップデート確認ダイアログがスプラッシュに隠れて操作できなくなる場合があったため。WMによる?\r
+ ・(その他) アップデート時にenvのバックアップを取るようにした ※env.old\r
+ ・(その他) 予約ダイアログのレイアウトを(また…)変更した\r
+ ・(その他) Linuxでのデフォルトスキンを"GTK"にした\r
+ ・(その他) 新聞形式の番組枠用のバッファの上限数を記録しないようにした(起動時は900固定とした)\r
+■バグ修正\r
+ ・(新聞形式) サイドツリーの過去ログ一覧が設定値未満しかリストアップされない問題を修正(>>764.)\r
+ ・(レコーダ対応[DIGA BWT2100/BZT710/720]) スカパーHD予約があると予約一覧が取得できない問題を修正(>>772.)\r
+ ・(レコーダ対応[RD-S600]) 予約登録ができない問題(音質指定が正しく動作していなかったことに起因する問題)を修正(>>774.)\r
+ ・(レコーダ対応[RD-XS57]) 上記と同様の問題のほか、音質・チャプタ分割・記録先指定が正しく動作していなかった問題を修正\r
+ ・(レコーダ対応[RD-BZ700/BZ810/BR610]) マジックチャプター関連が異常動作していた問題を修正(>>794.)\r
+ ・(レコーダ対応[EDCB]) 予約件数が30件を超えると31件目以降が予約一覧取得で取得できない問題を修正(>>780.)\r
+ ・(レコーダ対応[EDCB]) 予約実行の有効/無効が鯛ナビの予約一覧に反映されない問題を修正(>>783.)\r
+ ・(レコーダ対応[EDCB]) EPG予約に対して更新を行うとプログラム予約に変わってしまう問題を修正\r
+ ・(レコーダ対応[EDCB]) 新規登録時の予約IDを自前で生成していたため、EDCBと鯛ナビの予約一覧に差異があると(EDCBで入れた予約が鯛ナビに反映されてないとか)不正な予約IDが発生する問題を修正\r
+ ・(レコーダ対応[EDCB]) 空きチューナー検索がEDCBにあわなかったので動作しないように修正\r
+ ・(レコーダ対応[TvRock]) 時間追従の有効な予約に対して更新を行うと、時間追従が解除されてしまう問題を修正\r
+ ・(レコーダ対応[TvRock]) 改行のあるタイトル、番組詳細を予約で投入するとTvRockが起動できなくなってしまう問題を修正\r
+ ・(Web番組表対応) 当日過去情報がないよ系で新規に局を追加した際、当日の過去ログがない状態で情報取得を行うと補填されるべき過去時間帯が「番組情報がありません」ではなくNULL扱いとなり表示が崩れる問題を修正\r
+ ・(Web番組表対応[Dimora]) 1局も放送局を有効にしていない場合でも番組表サイトへ番組情報を収集に行く問題を修正\r
+ ・(Web番組表対応[Dimora]) 話数重複排除の際タイトルの末尾に空白文字が残ってしまう問題を修正\r
+ ・(Web番組表対応[Dimora]) キャッシュファイルが一時保存ファイルのまま正式ファイルに置き換わらないことがある問題を修正\r
+ ・(Web番組表対応[すかぱー!]) キャッシュファイルが存在しない日付があるとそこで読み込み処理および読み込み後の整理処理が中断してしまう問題を修正\r
+ ・(Web番組表対応[しょぼかる]) 8日間取得が動作していなかった問題を修正\r
+ ・(Web番組表対応[しょぼかる]) 番組詳細が検索対象になっていなかった問題を修正\r
+ ・(その他) 起動時意味もなくレコーダ一覧設定ファイルを更新していた問題を修正\r
+ ・(その他) LinuxでGTK Look & Feelを選択するとツールバーのキーワード検索ボックスがの幅が0になる問題を修正\r
+ ・(その他) log.txtをエディタなどで開いていても動作が止まらないようにした ※ただし、その間ログは出力されない\r
+\r
+3.14.9β+1.5.12(2012-09-30)\r
+■変更点\r
+ ・(Web番組表対応[すかぱー本家系]) サイトリニューアルに伴い旧3本廃止、新規1本追加 ※X日取得設定時、1局につきX+1回(X回ではない!)アクセスするので注意\r
+ ・(Web番組表対応[すかぱー系]) webザテ(アナ)とTV王国(スカパー)をCS番組表プライマリ側に移動\r
+ ・(その他) サイドツリーの状態保存方法を変えた ※バージョンアップ後初回の起動ではツリーが閉じた状態になるので注意\r
+ ・(その他) Windowsのwith JRE版でもDOSプロンプトを閉じるようにした ※もういいかなって…\r
+\r
+3.14.8β+1.5.12(2012-09-28)\r
+★★★★★★★★重要★★★★★★★★★★\r
+Macでの起動手順が変更になりました。\r
+ 「TaiNavi for Mac」アイコンをクリックして実行してください。\r
+※tainybannavi.commandは廃止になりました。コマンドラインから起動する場合はtinybannavi.shを利用してください。\r
+★★★★★★★★★★★★★★★★★★★★\r
+■変更点\r
+ ・(Web番組表対応[Dimora、Dimora(CSデジタル)、TV王国、webザテ]) タイトルと詳細で話数が重複して表記されている場合にタイトル側を削る処理を追加(タイトル話数分離有効時のみ)ほか見栄えを少々修正\r
+ ・(Web番組表対応[TV王国]) タイトル中のフラグの取り方をDimoraで実装した方式に置き換え\r
+ ・(その他) Macでの起動手順を変更\r
+ ・(その他) Linuxでフォントのアンチエイリアスを有効にした\r
+■バグ修正\r
+ ・(Web番組表対応[Dimora、Dimora(CSデジタル)、スカパー系]) 無料放送フラグをひろえなかった問題を修正\r
+\r
+3.14.7β+1.5.12(2012-09-27)\r
+★★★★★★★★重要★★★★★★★★★★\r
+ 3.14.6β+1.5.12(2012-09-24)の注意事項も必ず参照してください!\r
+★★★★★★★★★★★★★★★★★★★★\r
+■変更点\r
+ ・(レコーダ対応[DIGA DMR-BZT720]) 追加(>>mail.)\r
+ ・(新聞形式) サイドツリーの過去ログのノードをディスク上に存在するもののみ表示するように戻した(>>724.)\r
+ ・(その他) 番組詳細ページを持たないWeb番組表(Dimoraなど)の場合、右クリックメニューの実行アイテムうち「%DETAILURL%」を含むもの(ex."ブラウザで番組詳細のページを開く")はグレーアウトするようにした(>>729.)\r
+ ・(キーワード検索テキストボックス) 絞り込み検索を実装(>>733.)\r
+ ・(キーワード検索テキストボックス) 従来番組名&番組詳細一致固定だったが、先頭にキーワードをつけると処理が変わるようにした(詳細は後述)\r
+■バグ修正\r
+ ・(レコーダ対応[DIGA BWT2100]) 操作時DIGAにアクセスできないと接続リトライを繰り返して戻ってこなくなる問題を修正\r
+ ・(レコーダ対応[DIGA BZT710]) 日時指定の予約が予約一覧取得で正しく処理できない問題を修正\r
+ ・(Web番組表対応[Dimora、Dimora(CSデジタル)]) タイトル中のフラグ情報の取得に関する修正(フラグでないものまでフラグ扱いにして削除していたことへの対応)\r
+■バージョンアップ時の補足\r
+・改修後のキーワード検索ボックスの書式の説明\r
+  1.検索:(オプション1) (オプション2) キーワード \r
+  2.過去ログ検索:開始日[(YYYY/)MM/DD] 終了日[(YYYY/)MM/DD] (オプション2) キーワード ※YYYY/は省略可能\r
+  3.過去ログ閲覧:日付[YYYY/MM/DD]\r
+   ※オプション1:@filter..絞込検索(過去ログは対象外)\r
+   ※オプション2:#title..番組名一致、#detail..番組詳細一致、なし..番組名&番組詳細一致\r
+   ※オプションは単語を先頭アルファベット1文字に省略してもOK\r
+\r
+3.14.6β+1.5.12(2012-09-24)\r
+\r
+★★★★★★★★重要★★★★★★★★★★\r
+■REGZA DBR-M190プラグインの使用に関してはWikiを参照してください。\r
+\r
+http://sourceforge.jp/projects/tainavi/wiki/はじめてのたいなび(REGZAサーバ、REGZAテレビ編)\r
+★★★★★★★★★★★★★★★★★★★★\r
+\r
+★★★★★★★★重要★★★★★★★★★★\r
+■REGZA DBR-Z160/260プラグインの使用に関しする補足がありますので、Wikiを参照してください。\r
+\r
+http://sourceforge.jp/projects/tainavi/wiki/はじめてのたいなび(REGZAブルーレイ【DBR-Z160世代以降】編)\r
+★★★★★★★★★★★★★★★★★★★★\r
+\r
+★★★★★★★★重要★★★★★★★★★★\r
+■お手数ですが、設定ファイルの修正(env\ChannelConvert.dat)をお願いします。\r
+ ご自分で修正したことがない場合は update\TinyBannavi\env\ChannelConvert.dat で置き換えるだけでもOKです。\r
+\r
+●対象1:\r
+ env\ChannelConvert.dat 474行目付近\r
+\r
+修正内容:\r
+ 以下の行をコメントアウト.\r
+\r
+ (修正前)\r
+  "放送大学2","放送大学BS2"\r
+  "放送大学3","放送大学BS3"\r
+\r
+ (修正後)\r
+  #"放送大学2","放送大学BS2"\r
+  #"放送大学3","放送大学BS3"\r
+\r
+●対象2:\r
+ env\ChannelConvert.dat 461行目付近\r
+\r
+修正内容:\r
+ 以下の行を追加.\r
+\r
+(追加行)\r
+#>>---2012.09.23-ADD---\r
+\r
+# Dimora -> RD-Style\r
+\r
+"NST1",        "新潟総合テレビ"\r
+"だいいちテレビ1",      "だいいちテレビ"\r
+"奈良テレビ1",  "奈良テレビ"\r
+"BS日本映画専門ch",      "日本映画専門チャンネル"\r
+"ディズニーチャンネル",      "ディズニー・チャンネル"\r
+"Dlife",       "D-Life"\r
+"NHKBS1(101)", "NHK BS1"\r
+"NHKBSプレミアム(103)",   "NHK BSプレミアム"\r
+"BS-TBS"             ,"BS-TBS"\r
+\r
+#<<---2012.09.23-ADD---\r
+\r
+★★★★★★★★★★★★★★★★★★★★\r
+\r
+■変更点\r
+ ・(レコーダ対応[REGZA DBR-M190(Mail)]) 追加(>>649.)\r
+ ・(レコーダ対応[REGZA DBR-M190]) 追加(>>651.)\r
+ ・(レコーダ対応[REGZA DBR-Z160]) 追加(>>mail.)\r
+ ・(レコーダ対応[REGZA DBR-Z260]) 追加(>>mail.)\r
+ ・(レコーダ対応[RD(Mail)]) REGZA系メール予約プラグイン同様、予約結果を記憶するように修正\r
+ ・(Web番組表対応[Dimora、Dimora(CSデジタル)]) 追加\r
+ ・(Web番組表対応) 初回起動時の地上波・BSのデフォルト番組表をDimoraに変更\r
+ ・(その他) 予約ダイアログのレイアウトを変更\r
+ ・(その他) ログのスイッチサイズを5M→10Mに変更\r
+■バグ修正\r
+ ・(Web番組表対応[webザ・テ、TVガイド]) 「放送大学」関連で不具合があったのを修正(>>698.)\r
+ ・(Web番組表対応[webザ・テ(CSデジタル)]) 放送局名の再取得時に放送局名変換(ChannelConvert.dat)が適用されない問題を修正\r
+\r
+■バージョンアップ時の補足\r
+ ※Dimoraは1つの番組に対して複数個のジャンルコードが割り当てられる場合があります。\r
+  鯛ナビでは複数個あっても最初のジャンルコードしかみないため、おもっているのと異なるジャンルに振り分けられる場合があります。\r
+  ご了承ください。\r
+ ※BSのNHKが2つずつあるのは仕様です。\r
+\r
+3.14.5β+1.5.12(2012-08-06)\r
+■変更点\r
+ ・(レコーダ対応[REGZA Z3(Mail)]) 追加(>>633.)\r
+ ・(Web番組表対応[しょぼかる]) "繰り下げ"表示のあるものに延長警告フラグを立てるようにした\r
+■バグ修正\r
+ ・(Web番組表対応[MSN]) 出演者情報が拾えない場合があった問題を修正\r
+\r
+3.14.4β+1.5.12(2012-07-17)\r
+■変更点\r
+ ・(Web番組表対応[webザ・テ(CS)、王国(CS)、スカパー系]) またまた最終回の表記方法が変わって情報が拾えない場合があったのを修正\r
+ ・(Web番組表対応[webザ・テ、王国]) 5.1ch番組の表記変更に対応\r
+ ・(Web番組表対応) "[5.1]"マークを追加(5.1chサラウンド)\r
+ ・(その他) 初回起動時のデフォルトのウィンドウ幅を1024→1134に変更\r
+\r
+3.14.3β+1.5.12(2012-07-04)\r
+■バグ修正\r
+ ・(レコーダ対応[TvRock]) TvRock本体の番組表から入れた予約のうち、00:00~09:59の開始・終了時刻を持つ予約が予約一覧に表示できなかった問題を修正(>>617.)\r
+\r
+3.14.2β+1.5.12(2012-07-03)\r
+■変更点\r
+ ・(Web番組表対応[webザ・テ(CS)]) 新番組と最終回の表記方法の変更がwebザ・テでも行われていたので反映\r
+\r
+3.14.1β+1.5.12(2012-07-03)\r
+■変更点\r
+ ・(Web番組表対応[王国(CS)]) 無料放送の表記方法が変わって情報が拾えない場合があったのを修正\r
+ ・(Web番組表対応[王国(CS)]) 新番組と最終回の表記方法が変わって情報が拾えない場合があったのを修正\r
+ ・(Web番組表対応[しょぼかる]) しょぼかるのフラグの引き継ぎを地上波&BSにしか行っていなかったのをCS/CS2ndまで対象に変更\r
+  ※ChannelConvert.datを編集して、しょぼかる側の放送局名(ex."AT-X")とWeb番組表の放送局名(ex."333ch AT-X"、"333 - アニメシアターX(AT-X)")を同じ文字列にする必要があります。\r
+   ex. しょぼかる側の表記にあわせるように、Web番組表側の表記を変更する場合の設定例\r
+    "333ch AT-X","AT-X"\r
+    "333 - アニメシアターX(AT-X)","AT-X"\r
+ ・(Web番組表対応[しょぼかる]) 放送局絞込みを「しょぼかる」ノードにも適用(「全しょぼかる」のみ非適用)\r
+\r
+3.14+1.5.12(2012-06-29)\r
+■変更点\r
+ ・(Web番組表対応[auone]) サービス終了に伴い廃止\r
+ ・(リスト形式) 「しょぼかるのみ」ノードを予約待機の下に移動して、かつ、選択局のみに絞り込むように変更\r
+ ・(その他) tinybannavi.cmdを、javaw.exeへのパスが切られてなくても極力開始できるように変更\r
+■バグ修正\r
+ ・(レコーダ対応[TvRock]) 録画終了後の動作設定などが予約に反映されない場合がある問題を修正\r
+\r
+■バージョンアップ時の補足\r
+ ・Java同梱版のJRE6のバージョンをupdate30からupdate33に変更しました。\r
+\r
+--- 3.13 -> 3.14 ---\r
+\r
+3.13.11β+1.5.12(2012-06-11)\r
+■変更点\r
+ ・(Web番組表対応) オプション有効時、NHKの「アニメ 」だけでなく「アニメ・」の場合も削除対象とした\r
+■バグ修正\r
+ ・(新聞形式) ページング有効時、番組欄へジャンプで現在開いているページとは別のページへのジャンプが正しく動作しない問題を修正(>>579.)\r
+ ・(リスト形式) 自動選択数が設定値より+1多く選択される問題を修正(>>579.)\r
+ ・(Web番組表対応[テレビ王国]) CS側にwaitが入っていなかったので修正\r
+\r
+3.13.10β+1.5.12(2012-06-09)\r
+■変更点\r
+ ・(リスト形式、新聞形式) サイドツリーのルートノードを非表示にした\r
+ ・(Web番組表対応[テレビ王国]) 1アクセスごとに1.2秒waitを入れるようにした(連続アクセス規制らしい現象が発生したため)(>>567.)\r
+■バグ修正\r
+ ・(キーワード検索) チャンネル名指定で正規表現での指定が動作していなかった問題を修正(>>554.)\r
+\r
+3.13.9β+1.5.12(2012-06-02)\r
+■変更点\r
+ ・(レコーダ対応[DIGA系、iEPG系、NULL]) 最大同時録画数(チューナー数)を手動で指定できるようにした\r
+  ※詳細はwikiを参照してください\r
+   http://sourceforge.jp/projects/tainavi/wiki/操作方法(DIGAの予約にチューナー番号を割り振る)\r
+■バグ修正\r
+ ・(レコーダ対応[EDCB]) 予約一覧の取得に失敗する問題を修正\r
+\r
+3.13.8β+1.5.12(2012-05-03)\r
+■変更点\r
+ ・(レコーダ対応[REGZA RE2(Mail)]) 追加(>>mail.)\r
+■バグ修正\r
+ ・(新聞形式) ViewのサイズがJViewportの可視部分のサイズより小さい場合に「番組欄へジャンプする」が正しく動作しない問題を修正(>>.514)\r
+ ・(新聞形式) ジャンプの際にViewのサイズが変化する場合、「番組欄へジャンプする」が正しく動作しない問題を修正\r
+ ・(新聞形式) 「番組欄へジャンプする」のジャンプ先の垂直位置を開始後16pt固定から開始後5min位置に修正\r
+ ・(Web番組表[しょぼかる]) 放送局名のHTMLエスケープをデコードしていなかった問題を修正\r
+\r
+3.13.7β+1.5.12(2012-04-08)\r
+■変更点\r
+ ・(レコーダ対応[Z160(TaiSync)]) 追加(>>mail.)\r
+ ・(リスト形式) 番組追跡並べ替えダイアログに「削除」を追加(>>505.)\r
+ ・(リスト形式) 番組追跡並べ替えダイアログで複数行選択してまとめて移動できるようにした\r
+ ・(本体予約一覧) 予約編集後に、選択していた行にフォーカスが戻るようにした\r
+■バグ修正\r
+ ・(その他) フルスクリーンボタンで切り替えを行った時、タブが移動してしまう場合がある問題を修正\r
+\r
+3.13.6β+1.5.12(2012-04-01)\r
+■変更点\r
+ ・(リスト形式) ピックアップ追加を行った後にフォーカスがピックアップ行に戻るようにした(>>466.)\r
+ ・(ツールバー) 選択したレコーダIDを記憶するようにした(>>471.)\r
+ ・(新聞形式) リストのスナップショット機能を追加(>>481.)\r
+ ・(新聞形式) 番組枠の幅のデフォルトを150→125に、タイトルフォントサイズのデフォルトを12→13に、番組詳細のタブサイズのデフォルトを2→8に変更\r
+ ・(レコーダ対応[TvRock]) 登録時のタイトルを、番組詳細同様200Byte以下に制限\r
+ ・(Web番組表対応) サブタイトル分離処理において、タイトル中に『TOKYO「萌」探偵』のように「ほげほげ」を含む場合、「ほげほげ」をサブタイトルとして切り出さないようにした\r
+■バグ修正\r
+ ・(リスト形式) ソーターが有効になっている場合にフォーカスの戻る位置が正しくなかった問題を修正(>>467.)\r
+ ・(リスト形式) ソーターが有効になっている場合に未予約行の自動選択動作が正しくなかった問題を修正\r
+ ・(リスト形式) 予約待機ではなくしょぼかるに[NEW]マークが表示されてしまう問題を修正\r
+ ・(新聞形式) 「先週の番組欄へジャンプ」したあと、再度リスト形式や本体予約一覧から「番組欄へジャンプ」をすると先週の番組表が表示されたままになってしまう問題を修正\r
+ ・(Web番組表対応[TV王国]) マルチチャンネルの2CH目の情報が取得できなくなっていた問題を修正\r
+\r
+3.13.5β+1.5.12(2012-03-10)\r
+■変更点\r
+ ・(キーワード検索) ダイアログの文言を変更「大小同一視無効」→「文字列比較は完全一致で」(>>455.)\r
+ ・(リスト形式) 予約待機上の情報で、番組詳細に変更があったものに"(更)"マークを付加できるようにした&表示/非表示を設定できるようにした ※バージョンアップ後初回起動時は過去情報がないので新着以外のすべてのエントリにマークがつきます\r
+ ・(リスト形式) 終了日時が過去のエントリに対してピックアップを行おうとしたら「できません」とメッセージを出力するようにした\r
+■バグ修正\r
+ ・(新聞形式) ツールチップをON→OFFに設定変更後、新聞形式を開くと過去のツールチップが引き続き表示される問題を修正\r
+ ・(リスト形式) 予約待機>新着一覧にピックアップエントリが表示されてしまう問題を修正\r
+ ・(リスト形式) 予約待機外でピックアップしたエントリを予約待機上でピックアップ解除しようとしてもできない問題を修正\r
+\r
+3.13.4β+1.5.12(2012-02-19)\r
+■バグ修正\r
+ ・(各種設定) 「番組詳細列を表示」のチェックを外すと鯛ナビが終了できなくなる問題を修正(>>434.)\r
+\r
+3.13.3β+1.5.12(2012-02-18)\r
+■変更点\r
+ ・(レコーダ対応[DIGA DMR-BZT710]) 寄贈コードをマージさせて頂きました(>>mail.)\r
+■バグ修正\r
+ ・(新聞形式) 現在日時表示中に日跨り(29時またがり)が起きた時、現在時刻線が表示領域上端にはりつく問題を修正\r
+\r
+3.13.2β+1.5.12(2012-01-20)\r
+■変更点\r
+ ・(新聞形式・本体予約一覧) カラム幅を記憶するようにした(>>369.)\r
+ ・(新聞形式) ダブルクリック操作に「ブラウザで番組詳細のページを開く」を追加(>>396.)\r
+ ・(新聞形式) キーワード検索でタイトルのマッチ箇所の強調色を変更できるようにした(>>397.)\r
+\r
+3.13.1β+1.5.12(2012-01-14)\r
+■変更点\r
+ ・(ツールバー) 番組詳細エリア&ステータスエリアのShow/Hide切り替えボタンを追加(>>381.ほか)\r
+ ・(Web番組表[しょぼかる]) 特番フラグについて、Web番組表側のジャンルが「アニメ」「映画」の場合に加えて「音楽」の場合も引き継ぐようにした\r
+■バグ修正\r
+ ・(リスト形式・新聞形式) 手動でディバイダーを動かした際、サイドツリーの幅が最上段のパス表示テキストの幅より狭くできなかった問題を修正\r
+ ・(その他) Windowsのログアウト/シャットダウンでJavaアプリケーションが停止する問題に暫定的に対応(see below)(>>389.)\r
+   ※Bug ID:6660258 Java application stops Windows logout/shutdown (regression in 1.5.0_14)\r
+    http://bugs.sun.com/view_bug.do?bug_id=6660258\r
+   ※tinybannavi.cmdへの修正になるので、独自にバッチを構築していると反映されません。\r
+    その場合はjavaw.exeの引数に-Xrsを追加してください。\r
+\r
+3.13+1.5.12(2011-12-22)\r
+■変更点\r
+ ・(リスト形式) リスト形式の右クリックメニューでも予約ON/OFFメニューを選択できるように変更\r
+ ・(その他) オンラインアップデートの自動確認間隔を「毎日~6日おき」に設定できるようにした&確認の実行を1日1回に制限した ※デフォルトは2日おき ※設定が初期化されますので、いままで「無効」に設定されていた方は再度設定お願いします\r
+■バグ修正\r
+ ・(ピックアップ) リスト形式の右クリックメニューからピックアップの追加を行うと、24:00-29:59開始の番組の場合日付が1日あとにずれる問題を修正(>>334.)\r
+ ・(ピックアップ) 検索結果リスト表示で、ピックアップをON/OFFしてもピックアップマーク(★)の表示が連動しない問題を修正\r
+ ・(ピックアップ) リスト表示で、再放送の番組などにピックアップマーク(★)が表示されない問題を修正\r
+ ・(新聞形式) 現在日時を開いている状態で日付をまたいだ際に自動更新が行われると、現在時刻線が表示されなくなる問題を修正\r
+\r
+■バージョンアップ時の補足\r
+ ・Java同梱版のJRE6のバージョンをupdate29からupdate30に変更しました。\r
+  SSL/TLSまわりのバグフィックスが行われているそうなのでメールプラグインやGoogleカレンダー連携を利用されている方は置き換えるがいいよ。\r
+\r
+--- 3.12 -> 3.13 ---\r
+\r
+3.12.1β+1.5.12(2011-12-16)\r
+■バグ修正\r
+ ・(Web番組表対応[TVガイド]) 「タイトルに話数が含まれる場合に以降を分離する」有効時の特定ジャンル(映画・ドキュメンタリー・ドラマ)の例外動作が働いていなかった問題を修正\r
+\r
+3.12+1.5.12(2011-12-09)\r
+■変更点\r
+ ・(Web番組表対応[Yahooラ・テ]) Yahooからクレームが来たので廃止\r
+\r
+--- 3.11 -> 3.12 ---\r
+\r
+3.11.14β+1.5.12(2011-12-09)\r
+■変更点\r
+ ・(レコーダ対応[H1EX]) 寄贈コードをマージさせて頂きました(>>271.)\r
+ ・(レコーダ対応) NULLプラグインでもカレンダー連携ができるようにした(する/しない選択可) ※デフォルトはしない\r
+\r
+3.11.13β+1.5.12(2011-12-03)\r
+■変更点\r
+ ・(リスト形式) 登録キーワード検索結果でもタイトルのマッチ部分の色分けをできるようにした\r
+ ・(リスト形式) ピックアップの予約列にピックアップマーク(★)を表示できるようにした&表示/非表示を設定できるようにした ※デフォルトは表示\r
+ ・(Web番組表対応[TVガイド]) 「タイトルに話数が含まれる場合に以降を分離する」の対象にした\r
+■バグ修正\r
+ ・(ピックアップ) リスト形式の左クリックメニューからの追加・削除が行えなくなっていた問題を修正(>>258.) ※3.11.12βでの過去ログの左クリックメニュー対応の際にエンバグ\r
+ ・(ピックアップ) リスト形式(のピックアップノード以外)で、ピックアップされているエントリーで左クリックメニューを開いても「ピックアップから削除」と表示されない問題を修正\r
+\r
+3.11.12β+1.5.12(2011-12-01)\r
+■変更点\r
+ ・(その他) 多重起動を禁止できるようにした(>>205.) ※多重起動しようとしたら既存のウィンドウをフォアグラウンドにしようかと思ったけど正攻法が見つからなかったので保留\r
+ ・(リスト形式) 「長さ」「スコア」「閾値」のソータを文字列比較から数値比較に変更(>>207.)\r
+ ・(キーワード検索) 「開始日時に」「サブジャンルに」「生放送」追加(>>208.) ※「開始日時に」は正規表現利用可\r
+ ・(Web番組表対応) 当日最初の番組枠の開始時刻が"18:00"~"24:00"の場合はその番組は前日扱いに(>>209.)\r
+ ・(リスト形式・新聞形式) 過去ログでも左クリックメニューの一部を操作できるようにした(>>227.)\r
+ ・(リスト形式) 行番号を表示できるようにした&行番号の表示/非表示を設定できるようにした(本体予約一覧の行番号表示も連動)(>>237.) ※デフォルトは非表示\r
+ ・(リスト形式) 予約待機の新着に[NEW]マークを付加できるようにした&表示/非表示を設定できるようにした(>>238.) ※ポップアップとかはしない。そういうポリシー。(今は無効のタイマーアイコンが復活したら対応するかもね…)\r
+ ・(リスト形式) 開始時刻が一致しない(1分前倒し設定の場合は1分前でない)場合の予約マークとして「◇」を追加\r
+ ・(Web番組表対応[TV王国]) タイトルが「.* #1(・#2)?$」の場合に新番組フラグを立てるようにした\r
+ ・(その他) 一部デフォルト動作の変更「番組詳細列を表示する→ON」「レコーダコンボボックスを新聞形式でも有効に→ON」\r
+ ・(Web番組表対応全般) クラスやコードを再編成していろいろ手を加えたので何か起きるかもしれない。起きないかもしれない。\r
+■バグ修正\r
+ ・(CHコード設定) 最終行で「放送局名を削除する」を押しても削除できない問題&無選択時に「放送局名を削除する」を押すとぬるぽな問題を修正(>>225.)\r
+ ・(新聞形式) 新聞形式右端の放送局へのジャンプにマウスカーソルが追随できない問題&マルチモニタ時のジャンプでマウスカーソルがあさっての方向に行く問題を修正\r
+\r
+3.11.11β+1.5.12(2011-11-22)\r
+■変更点\r
+ ・(新聞形式) テキストアンチエイリアスの方式を選択できるようにした(>>200.) ※AA無効だと小さい文字はギザギザになってしまうので、MS PゴシックとかMS UI Gothicみたいに小さい文字用のビットマップを含んでいるフォントを利用するとよいよ \r
+■バグ修正\r
+ ・(新聞形式) パレットをキャンセルしても表示が戻らない問題を修正\r
+\r
+3.11.10β+1.5.12(2011-11-21)\r
+■変更点\r
+ ・(新聞形式) スナップショットの画像形式をJPEG/PNG/BMPから選べるようにした\r
+■バグ修正\r
+ ・(新聞形式) エンコーダの選択肢がないレコーダプラグイン(iEPGとかDIGAとか)利用でかつ予約情報が存在しているとぬるぽで起動できない問題を修正(>>191.) ※3.11.9βでピックアップ枠の文字色変更対応した際にエンバグ\r
+\r
+3.11.9β+1.5.12(2011-11-19)\r
+■変更点\r
+ ・(レコーダ対応[BR610]) 寄贈コードをマージさせて頂きました(>>#60651.)\r
+ ・(新聞形式) ピックアップ枠、予約枠の文字の色を変更できるようにした(>>159.)\r
+ ・(新聞形式) ページング有効時のスナップショットのファイル名がsnapshot[ページ番号].jpgになるようにした(>>179.)\r
+ ・(新聞形式) ページング有効時に、1ボタンで全ページ連続してスナップショットをとれるようにした\r
+ ・(新聞形式) 過去ログ検索実行時にペーンのクリアを実行するようにした ※内部のデータの整合がとれなくなるため\r
+ ・(リスト形式) 過去ログ上でのダブルクリックでも新聞欄にジャンプできるようにした\r
+ ・(過去ログ) 過去ログ検索の終了日の書式をYYYY/MM/DDから(YYYY/)?MM/DDに変更\r
+■バグ修正\r
+ ・(新聞形式) スナップショットの放送局名表示がずれる問題を修正&タイムバーを表示できるようにした(>>157.)\r
+ ・(各種設定) 起動時に「ダブルクリック時の動作」の状態が表示に反映されない問題を修正(>>163.)\r
+ ・(新聞形式[イTVGuide、Yahoo]) 当日の番組情報が「番組情報がありません」のみになった場合に補完機能が動作しない問題を修正\r
+\r
+3.11.8β+1.5.12(2011-11-19)\r
+■変更点\r
+ ・(Web番組表対応[イTVGuide、Yahoo]) 当日の過去時刻の取得不可領域を過去ログから補完する処理を追加\r
+ ・(Web番組表対応) 過去ログの残す/残さないを選択できるようにした\r
+ ・(新聞形式) 予約待機にマッチした番組への検索マッチ枠の表示/非表示をツールバーに移動&枠の太さを設定できるようにした\r
+\r
+3.11.7β+1.5.12(2011-11-16)\r
+■変更点\r
+ ・(その他) システムトレイに隠せるようにした(>>130.) ※MacではDockアイコンが消せずUbuntuでは隠すと二度と開けなくなるので実質Windowsのみの機能\r
+ ・(Web番組表対応[イTVGuide]) シンプル版から取得するように変更(>>131.) ※詳細個別取得がなくなりました ※要「Webサイトから放送局リストを取得し直す」実行\r
+ ・(その他) ツールチップの初期遅延値と消去までの遅延時間を変更できるようにした\r
+ ・(新聞形式) 通常表示の日付切り替えをだいぶ高速化できたかも。簡素表示OFFでお試しください\r
+■バグ修正\r
+ ・(新聞形式) ページャーをON→OFFしてもコンボボックスが無効にならない場合がある問題を修正\r
+ ・(新聞形式) 描画高速化ONの場合、パレットアイコンから番組枠のサイズ等を変更しても反映されない問題を修正\r
+■既知の問題\r
+ マルチモニタかつ縦横混在構成だとコンポーネントの画面上の座標がうまく取得できず、新聞形式へのジャンプなどに問題が生じる\r
+\r
+3.11.6β+1.5.12(2011-11-12)\r
+■変更点\r
+ ・(新聞形式) 検索マッチ枠とその表示・非表示設定の追加(>>121.) ※デフォルトは非表示です\r
+ ・(新聞形式) 新聞形式でも先週の番組枠へのジャンプメニューを表示\r
+ ・(Web番組表対応[Yahoo(テ/ラ)]) サイトリニューアルに対応\r
+\r
+3.11.5β+1.5.12(2011-11-11)\r
+■変更点\r
+ ・(新聞形式) 1時間あたりのドット数を30~600[10刻み]に変更(>>115.)\r
+ ・(リスト形式) 番組詳細列とその表示・非表示設定の追加(>>117.) ※デフォルトは非表示です\r
+ ・(リスト形式) しょぼかる無効の場合はツリーに表示しないようにした(>>118.)\r
+ ・(リスト形式) ダブルクリック時の動作を{予約ダイアログを開く|番組欄へジャンプする}のどれかに切り替えられるようにした(>>119.) ※予約一覧には影響しません\r
+ ・(新聞形式) 簡素表示設定を素のJLabelから通常表示{ShowStart=false,ShowDetail=false}に変更の上、ON/OFF設定をツールバーに移動\r
+ ・(新聞形式) ツールチップを通常表示でも表示できるよう変更\r
+\r
+3.11.4β+1.5.12(2011-11-10)\r
+■変更点\r
+ ・(リスト形式・新聞形式) 「★延長注意★」の短縮表示設定を追加(>>105.) ※デフォルトは通常表示です\r
+ ・(新聞形式) 開始時刻枠・番組詳細枠の非表示設定を追加(>>111.) ※デフォルトは表示です\r
+■バグ修正\r
+ ・(延長警告管理) 非延長警告条件の動作が、3.10.26βの修正で3.8.17β以前の状態に戻ってしまっていた問題を再修正\r
+\r
+3.11.3β+1.5.12(2011-11-08)\r
+■変更点\r
+ ・(リスト形式・本体予約一覧) 次回実行予定の一週間前の新聞欄にジャンプする機能を追加\r
+■バグ修正\r
+ ・(その他) MacOSXなどで、フルスクリーン時にウィンドウの一部がメニューバーに隠れてしまう問題を修正(>>88.)\r
+\r
+3.11.2β+1.5.12(2011-11-06)\r
+■変更点\r
+ ・(新聞形式) マウスオーバー時のハイライトを無効にできるようにした(>>63.)\r
+ ・(新聞形式) リスト形式/本体予約一覧から新聞形式へのジャンプ時に該当番組枠の上にカーソルを移動するようにした(>>68.)\r
+\r
+3.11.1β+1.5.12(2011-11-06)\r
+■変更点\r
+ ・(新聞形式) 表示設定ダイアログの[×]ボタンがキャンセル動作になるよう変更(>>39.)\r
+ ・(新聞形式) フォント設定・枠サイズ設定を独立させてツールバーに移動(>>44.)\r
+ ・(新聞形式) マウスオーバー時のハイライト色を変更できるようにした(>>45.)\r
+ ・(Web番組表対応[TV王国]) 地デジのマルチチャンネル編成に対応 ※3CH以上ある局でも対応は2チャンネルまで\r
+■バグ修正\r
+ ・(Web番組表対応[Yahoo、イTVGuide]) 24~29時の間に取得した当日の番組表で予約をしようとすると、日付が前日になってしまう問題を修正(>>58.)\r
+\r
+3.11+1.5.12(2011-11-02)\r
+■変更点\r
+ ・(新聞形式) ジャンル別背景色設定を独立させてツールバーに移動(>>10.)\r
+ ・(Web番組表対応[auone、MSN、TV王国、Yahoo]) BSページが10ページになるまでは再取得時に自動拡張できるようにした(>>30.) ※要「Webサイトから放送局リストを取得し直す」実行\r
+\r
+■バージョンアップ時の補足\r
+ ・JRE同梱版のJREのバージョンをupdate27からupdate29に変更しました。\r
+  セキュリティ強化が行われているそうなので利用されている方は置き換えてください。\r
+\r
+--- 3.10 -> 3.11 ---\r
+\r
+3.10.27β+1.5.12(2011-10-22)\r
+■変更点\r
+ ・(Web番組表対応) HTTPプロキシの有効無効をワンクリック(?)で変えられるようにした(>>941.)\r
+ ・(Web番組表対応ほか) 表示できるマークの種類を拡張した(>>942.) ※注意\r
+   既存-【新】新番組、【終】最終回、【無料】無料放送、[再]再放送(以上4種はON/OFF設定できません)、【初】初回放送、[生]生放送、[!]しょぼかる未定義\r
+   追加-[PV]ペイパービュー、[字]文字多重放送、[二]二か国語放送、[多]音声多重放送、[吹]吹き替え、[デ]データ放送\r
+ ・(リスト形式&新聞形式) マーク表示のON/OFFを設定できるようにした ※デフォルトは全部ON\r
+ ・(Web番組表対応[webザ・テ]) 地上波の放送局名変更に対応して「NHKEテレn地域名」を「NHK Eテレn・地域名」に強制変更させる修正を加えた\r
+■バグ修正\r
+ ・(Web番組表対応) HTTPプロキシの設定を削除しても再起動するまで反映されていなかった問題を修正\r
+\r
+※注意事項\r
+ ・Web番組表上に記載がなければ当然表示はされませんので、「表示されない」ことを質問される場合はブラウザでWeb番組表を開いて確認してからにしてください。\r
+ ・未表示や追加の対応依頼は「どのWeb番組表」の「どの日時」の「どの番組」の「どのマーク」かという詳細情報と一緒にお願いします。ない場合は全てスルーします。\r
+\r
+3.10.26β+1.5.12(2011-10-21)\r
+■変更点\r
+ ・(CH設定) 「放送局リストを再構築する」ボタンを各Web番組表エリアに移動\r
+ ・(CH設定) 「放送局リストを再構築する」→「Web番組表から放送局リストを取得し直す」に文言修正\r
+ ・(Web番組表対応[webザ・テ]) BSの地域設定を東京→BSに変更(自動で再設定されます)\r
+■バグ修正\r
+ ・(延長警告管理) 「延長感染源にしない」条件が動作しない場合があった問題を修正\r
+ ・(CH設定) 左右両方の放送局リストがともに「有効」と表示されていた問題を修正(>>923.)\r
+\r
+3.10.25β+1.5.12(2011-10-14)\r
+■変更点\r
+ ・(アップデート) JOptionDialogにsetAlwaysOnTop(true)を設定できるようにした(>>904.)\r
+ ・(Web番組表対応[auone]) BSデジタル3、4に対応\r
+ ・(その他) 教育→Eテレや新BSにあわせてChannelConvert.datなどを見直し ※\r
+■バグ修正\r
+ ・(その他) ログファイルが大きいとログビューアを開くのにやたらと時間がかかっていた問題を修正\r
+\r
+■バージョンアップ時の補足\r
+ ※放送局名の見直しに伴い、NHK総合&Eテレについてバージョンアップ前の情報との間に放送局名の齟齬が発生するかもしれません\r
+  必要に応じて再設定をお願いします\r
+\r
+ ①アップデート後、update\TinyBannavi\env\ChannelConvert.datでenv\ChannelConvert.datを更新する\r
+ ②起動する\r
+ ③【Web番組表の放送局名の更新】「CH設定」で「放送局リストを再構築する」を実行したのち、抜けてしまった局があれば入れ直して(多分Eテレだけだと思いますが)、「更新を確定する」\r
+ ④【レコーダーの放送局名との対応付けの更新】「CHコード設定」で旧放送局名の設定値を新放送局名にコピーしたのち、旧放送局名を「放送局名を削除する」して、「更新を確定する」\r
+ ⑤【予約情報中の放送局名の更新】ツールバーから「レコーダーから予約情報を再取得する」を実行\r
+ ⑥念のためWeb番組表も再取得する\r
+ ⑦番組追跡やキーワード検索の放送局名指定に影響がないか確認する\r
+\r
+3.10.24β+1.5.12(2011-10-10)\r
+■バグ修正\r
+ ・(Web番組表対応[auone]) BSの番組表が正しく表示できない問題を修正(>>898.)\r
+ ・(ピックアップ) 番組詳細に番組詳細ではなくタイトル文字列を保存していた問題を修正(>>900.)\r
+\r
+3.10.23β+1.5.12(2011-10-08)\r
+■変更点\r
+ ・(Web番組表対応[MSN]) BS 12~243に対応(>>895.) ※要・放送局リスト再構築\r
+\r
+3.10.22β+1.5.12(2011-10-08)\r
+■バグ修正\r
+ ・(キーワード検索) 「次のいずれかの条件に一致」中の「を含む番組を除く」条件の処理をもとに戻した(>>886.) ※3.10.21βでのエンバグの修正\r
+ ・(予約一覧) 新聞形式へのジャンプが行えなくなった問題を修正(>>890.) ※3.10.21βでのエンバグの修正\r
+\r
+3.10.21β+1.5.12(2011-10-06)\r
+■変更点\r
+ ・(予約一覧) 行番号表示を追加(>>873.)\r
+ ・(その他) デバッグログ出力のON・OFFを操作できるようにした\r
+ ・(タイニーシンク) RDが登録完了通知に実際とは異なるチャンネル番号を返却してくる問題に対応\r
+■バグ修正\r
+ ・(レコーダ対応[TvRock]) 「予約リスト」ページのデフォルトが「一覧」以外になっていると予約情報がうまく取得できない問題を修正(>>843.)\r
+ ・(キーワード検索) 「無料放送」条件の検索が失敗する問題を修正(>>877.)\r
+ ・(キーワード検索) 「次のいずれかの条件に一致」中の「を含む番組を除く」条件が正しく処理されない問題を修正\r
+ ・(新聞形式) 放送局別表示にしている状態で予約の操作を行うとまちがった位置に予約枠が表示されることがある問題を修正\r
+\r
+3.10.20β+1.5.11(2011-10-01)\r
+■変更点\r
+ ・(リスト形式) 「予約実行OFFも表示する」設定の際、予約実行OFFエントリの予約マークを"×"で表示するように変更\r
+■バグ修正\r
+ ・(その他) 番組欄へのジャンプが正しく動作しなくなった問題の修正 ※3.10.19βでのエンバグの修正\r
+ ・(その他) 29時をまたいだ番組上のクリック位置をうまく算出できない問題を修正\r
+\r
+3.10.19β+1.5.11(2011-09-30)\r
+■変更点\r
+ ・(新聞形式) 予約枠を選んで予約実行ON・OFFを操作できるようにした(>>815.)\r
+ ・(リスト形式・新聞形式) クリック位置の日時に接触する予約枠も削除候補に表示するようにした\r
+ ・(本体予約一覧) CHコード設定の「レコーダの放送局名」の設定に不備がある場合、「放送局」欄を空白でなく"★放送局名不正"と表示するようにした\r
+ ・(CHコード設定) CHコード設定不備系のエラーメッセージを噛み砕いだわかりやすい表現に変更\r
+ ・(Web番組表対応[goo系]) TVガイドへのリンクに変更されたので削除\r
+ ・(Web番組表対応[auone]) 追加\r
+ ・(Web番組表対応[Yahoo]) BS 234~531に対応 ※要・放送局リスト再構築\r
+ ・(Web番組表対応) NHKの「アニメ 」で始まるタイトルから「アニメ 」部分を自動削除するオプションを追加 ※デフォルトはONです\r
+ ・(オンラインアップデート) 起動時のチェックを起動シーケンスの最初の方に移動\r
+ ・(オンラインアップデート) 起動時にアップデートを行ったらプロセスを終了するようにした\r
+ \r
+3.10.18β+1.5.11(2011-09-27)\r
+■変更点\r
+ ・(リスト形式) 番組追跡、キーワード検索、延長警告のアイテム並べ替えを実装(>>813,820.)\r
+ ・(キーワード検索) 「無料放送」を検索条件に追加(>>814.)\r
+ ・(その他) リスト形式と新聞形式のツリーペーンの幅を非同期にできるようにした(>>825.) ※デフォルトは同期するです\r
+■バグ修正\r
+ ・(その他) Proxy設定をしていると起動に失敗する問題を修正(>>811.) ※3.10.17βでのエンバグの修正\r
+ ・(ピックアップ) ミドルクリックでの削除がデータファイルに保存されない問題を修正\r
+\r
+3.10.17β+1.5.11(2011-09-22)\r
+■変更点\r
+ ・(Web番組表[webザ・テ、TV王国]) WOWWOWの無料放送フラグに対応(>>788.)\r
+ ・(ピックアップ) ミドルクリックでの追加・削除を実装(>>789.)\r
+■バグ修正\r
+ ・(Web番組表[スカパー、スカパー詳細++、TV王国CS]) キャッシュ上の開始日をチェックしていなかった問題を修正(>>790.)\r
+ ・(過去ログ) ファイル名禁止文字を含む放送局が保存できない問題を修正(>>806.)\r
+ ・(レコーダ対応) 予約情報の再取得を実行してエンコーダ情報をリセットしてもレコーダ設定のエンコーダ一覧に即時反映されない問題を修正\r
+\r
+3.10.16β+1.5.11(2011-09-18)\r
+■変更点\r
+ ・(キーワードグループ) 孫ノードからキーワードの編集/削除を行えるようにした(>>775.)\r
+ ・(キーワードグループ) 孫ノードを選択した際にも検索結果一覧が表示されるようにした\r
+ ・(キーワード検索) ダイアログからの新規登録時に所属するグループを1個選択できるようにした\r
+ ・(ツールバー) 検索テキストボックスが空でも★で登録ダイアログが出るようにした\r
+■バグ修正\r
+ ・(Web番組表[イTVGuide]) 番組詳細の個別取得後にタイトルの検索インデックスを更新していなかったため、正規表現検索でひっかからないエントリが発生していた問題を修正(>>778.)\r
+\r
+3.10.15β+1.5.11(2011-09-17)\r
+■変更点\r
+ ・(その他) ラジオ番組も登録キーワードや番組追跡の検索対象になるようにした(>>754.)\r
+ ・(キーワード検索テキストボックス) 検索語を""で囲んだ場合は完全一致検索になるようにした(>>761.)\r
+ ・(リスト形式) キーワードグループ機能を実装(>>769.)\r
+ ・(Web番組表[しょぼかる]) 最終回フラグも引き継ぐようにした\r
+■バグ修正\r
+ ・(レコーダ対応[XS41/X5/XS57]) 番組詳細の改行コードを0x0a→0x0d0aに置換していなかった問題を修正(>>756.)\r
+ ・(タイニーシンク) 録画開始時刻までに予約・削除の投入が間に合わなかった場合、そのエントリ以降処理が進まなくなる問題を修正\r
+ ・(タイニーシンク) 複数レコ登録時、1台のレコで受け付け拒否が起こると他のレコへの処理まで行われなくなる問題を修正\r
+\r
+★タイニーシンクをご利用の場合はタイニーシンクも再起動してください。\r
+\r
+3.10.14β+1.5.10(2011-09-09)\r
+■変更点\r
+ ・(Web番組表[TV王国]) 番組詳細の個別取得に対応\r
+■バグ修正\r
+ ・(Web番組表[webザ・テ]) 番組詳細のキュッシュからの取得にもウェイトがかかっていた問題を修正(>>731.)\r
+ ・(Web番組表[MSN]) [再]などフラグが立つ場合にタイトルが空文字列になってしまう問題を修正(>>732.)\r
+ ・(Web番組表[TV王国]) []で囲まれたタイトルが空文字列になってしまう問題を修正(>>742.)\r
+ ・(その他) キーワード検索でタイトルが空文字列のエントリを表示しようとすると例外を起こす問題を修正(>>742.)\r
+\r
+3.10.13β+1.5.10(2011-09-04)\r
+■変更点\r
+ ・(その他) 当日の終了済み予約も表示する(要再起動)(>>719.) ※デフォルトはOFFです\r
+ ・(その他) 予約詳細情報を常に取得しない設定を追加(>>719.)\r
+ ・(Web番組表[webザ・テ]) 番組詳細の個別取得に若干ウェイトを挿入\r
+■バグ修正\r
+ ・(Web番組表[TV王国CS]) 無料放送フラグを拾っていなかった問題を修正\r
+\r
+3.10.12β+1.5.10(2011-09-02)\r
+■変更点\r
+ ・(Web番組表[webザ・テ]) 番組詳細の個別取得に対応(>>713.) ※デフォルトはOFFです\r
+ ・(Web番組表[イTVGuide]) 番組詳細の個別取得のON/OFFを設定できるようにした ※デフォルトはOFFです。現在当該プラグインを利用している方はONに設定してください。各種設定>可能なら番組詳細を個別取得する\r
+ ・(レコーダ対応[TvRock]) 追跡マークを拾っていなかったのを表示されるようにした\r
+ ・(Web番組表[webザ・テ、TV王国]) ジャンル=ドキュメンタリーの場合も話数以降分離をしないようにした(NHKスペシャルとかタイトルだけではさっぱり内容不明なので)\r
+■バグ修正\r
+ ・(Web番組表[MSN]) 新番組等のフラグを正しく抽出できていなかった問題を修正\r
+ ・(その他) しょぼかるに当該日の情報が一件もない場合だと[!]マークが表示されなかった問題を修正\r
+\r
+3.10.11β+1.5.10(2011-08-28)\r
+■変更点\r
+ ・(その他) 地上波&BSのアニメ番組で、しょぼかる側に開始日時の一致する情報が存在しないものには「[!]」マークを表示するようにした(>>693.)\r
+ ・(その他) Web番組表再取得のサブメニューに「ラジオのみ取得」「しょぼかるのみ取得」を追加(>>693.)\r
+ ・(Web番組表[しょぼかる]) ジャンル=映画の場合を特番扱いにするようにした(>>694.) ※「アニメ特番」のキーワード検索条件を「ジャンル==アニメ&&特番」から「特番」のみに変更してください\r
+ ・(新聞形式) しょぼかるノードに「しょぼかるのみ」サブノードを追加(>>694.) ※①放送局縛りのないキーワード検索の結果を含むため他県の局が表示される場合があります、②地上波&BS側のジャンルがアニメでない場合(声優番組=音楽番組扱いなど)はアンマッチになるため排除されません\r
+■バグ修正\r
+ ・(Web番組表[しょぼかる]) 取得タイトルがShortTitleになっていたのをTitleに修正(>>694.)\r
+■既知の問題\r
+ ・しょぼかるで、Gyao!などのように同一時間に複数の番組が登録されている場合はそのうち1つしか表示できない\r
+\r
+3.10.10β+1.5.10(2011-08-27)\r
+■変更点\r
+ ・(Web番組表[しょぼかる]) 参照先をicalからrssに変更\r
+ ・(Web番組表[しょぼかる]) 「【注】」を特番マークとして認識するようにした\r
+ ・(キーワード検索) 検索条件に「特番」を追加\r
+\r
+3.10.9β+1.5.10(2011-08-25)\r
+■変更点\r
+ ・(レコーダ対応[DIGA XW120/BWT2100]) 予約実行ON/OFFが処理されるようにした(>>664.)\r
+ ・(レコーダ対応[EpgDataCap_Bon]) 内蔵番組表からの予約の場合追跡マークがつくようにした\r
+■バグ修正\r
+ ・(その他) 起動時に過去ログが生成されない場合がある問題を修正\r
+\r
+3.10.8β+1.5.10(2011-08-22)\r
+■変更点\r
+ ・(予約ダイアログ) タイトルに日付を付加するボタンを追加\r
+ ・(新聞形式) 番組名と番組詳細のフォントサイズと色の設定を独立\r
+■バグ修正\r
+ ・(レコーダ対応[DIGA XW120/BWT2100]) 番組指定予約の予約詳細がとりこめなかった問題を修正(>>656.) ※番組指定予約の場合追跡マークがつきます\r
+\r
+3.10.7β+1.5.10(2011-08-15)\r
+■変更点\r
+ ・(レコーダ対応[EpgDataCap_Bon]) 追加(>>629.)\r
+ ・(レコーダ対応) BZ700/810プラグインのように予約詳細を取得するか確認するダイアログが出るものについて、常に取得することにして確認を省略できるオプションを追加 ※各種設定>常に予約詳細情報を取得する\r
+ ・(新聞形式) 実行OFFの場合エンコーダ名をグレーで表示するようにした(>>630.) \r
+■既知の問題\r
+ ・過去ログ表示を行った後に過去ログ検索を行うと、内部の過去ログバッファと新聞形式の番組枠との対応が崩れて例外が発生する\r
+\r
+3.10.6β+1.5.10(2011-08-06)\r
+■バグ修正\r
+ ・(その他) Web番組表が自動ロードされる設定だと過去ログが生成されない問題を修正(>>mail.)\r
+\r
+3.10.5β+1.5.10(2011-08-06)\r
+■バグ修正\r
+ ・(新聞形式) 描画速度優先ON時にWeb番組表取得に失敗すると、次の起動時に立ち上がらなくなる問題を修正(>>594.)\r
+ ・(レコーダ対応[BZ700/BZ810]) 予約情報の詳細取得時に、自動チャプタ1・録画のりしろ・録画優先度・ライン音声選択・自動削除が正しく取得できなかった問題を修正(>>605.)\r
+ ・(Web番組表対応[TVガイド]) 番組詳細キャッシュが不正な場合に番組表キャッシュをロードできなくなる問題を修正(>>604.)\r
+ ・(Web番組表対応[TVガイド]) キャッシュに過去月同日のデータが残っている場合に当日のデータとして使用してしまう問題を修正\r
+\r
+3.10.4β+1.5.10(2011-07-28)\r
+■変更点\r
+ ・(リスト形式) しょぼかるノードの子ノードに全しょぼかるを追加\r
+ ・(その他[for Windows ONLY]) ファイルのオープンをjava.awt.Desktop.open()ではなくjava.lang.Runtime.getRuntime.exec("rundll32")で行えるようにした(>>552.) ※URIでのファイル指定に対応していないアプリケーションへの対応\r
+■バグ修正\r
+ ・(リスト形式) 短縮予約をしている場合に、予約マークに延長警告が反映されない問題を修正 ★\r
+ ・(レコーダ対応[iEPG]) 日付が0パディングじゃないとエラーになるアプリケーションがある問題をに対応(>>550.)\r
+ ・(その他) skinフォルダが存在しないと起動に失敗する問題を修正(>>555.)\r
+\r
+★予約マークが以下の様に変更になります。\r
+    "●"=通常の予約\r
+    "○"=延長警告対象だが終了時刻が延長されていない予約\r
+    "◎"=延長警告対象ではないが終了時刻が延長されている予約 \r
+    "▲"=予約終了時刻が番組表の終了時刻より1分早い予約\r
+    "△"=同上かつ延長警告対象だが終了時刻が延長されていない予約 ※変更\r
+    "▼"=予約終了時刻が番組表の終了時刻より2分早い予約 ※変更\r
+    "▽"=同上かつ延長警告対象だが終了時刻が延長されていない予約 ※変更\r
+\r
+3.10.3β+1.5.10(2011-07-21)\r
+■変更点\r
+ ・(Web番組表対応) NGワード機能を追加(>>526.) ※NGワードの登録は各種設定にて\r
+ ・(Web番組表対応) goo(ひかりTV)追加\r
+ ・(Web番組表対応[goo]) Webサイトの情報形式の変更に対応 ※gooを利用中の場合は、「放送局リストを再構築する」を実行してください\r
+ ・(新聞形式) ピックアップした番組の位置に枠を表示させるようにした(>>529.) ※枠の色は各種設定にて ★\r
+■バグ修正\r
+ ・(Web番組表対応[スカパー詳細+]) 24:00-29:00の間情報取得ができなくなっていた問題を修正\r
+\r
+★ピックアップは日時と長さを記憶しているだけなので、番組表をリロードした際に番組が移動していたりすると追跡はできず不一致になります。\r
+ そうなった場合はリスト形式からしか削除できませんので念のため。\r
+\r
+3.10.2β+1.5.10(2011-07-16)\r
+■変更点\r
+ ・(Web番組表対応[webザテ、TV王国]) タイトルの話数以降の分離をする・しないを選択できるようにした(>>504.) ※デフォルトは"する"です\r
+ ・(Web番組表対応) 生放送情報を抽出するようにした(>>515.)\r
+ ・(Web番組表対応) "[再]"マークがタイトルの一部になっていたのを分離した\r
+ ・(リスト形式) 番組追跡に新規登録する際に、登録ダイアログを開くように変更\r
+ ・(その他) キーワード検索ダイアログにプレビューボタンを追加\r
+ ・(オンラインアップデート) プロジェクトサイトから更新情報を取得できない場合に警告するようにした\r
+■バグ修正\r
+ ・(リスト形式) SHIFTorCTRLでの複数アイテム選択ができなくなっていた問題を修正(>>505.)\r
+ ・(リスト形式) キーワード検索のラベルを変更した際に、ツリー表示に即反映されていなかった問題を修正\r
+ ・(Web番組表対応[Yahoo(ラ・テ)]) 放送局を選択していなくてもWebアクセスが行われる問題を修正\r
+ ・(Web番組表対応[webザ・テ(CSアナ)]) タイトルが取得できなくなっていた問題を修正(>>516.)\r
+\r
+3.10.1β+1.5.10(2011-07-09)\r
+■変更点\r
+ ・(Web番組表対応[goo]) 追加 ※1アクセスごとに2秒waitが入るので遅めです\r
+■バグ修正\r
+ ・(Web番組表対応[TVガイド]) 当日最初の番組が5:00より後だった場合に番組表が上にずれる問題を修正(>>491.)\r
+\r
+■バージョンアップ時の補足\r
+ ・env/ChannelConvert.datがgooテレビ番組表対応版になりました。\r
+  置き換えてください。\r
+\r
+★8日分取得対応プラグイン\r
+ <地上波&BS>\r
+ ・goo\r
+ ・MSN\r
+ ・TVガイド\r
+ ・テレビ王国\r
+ ・Yahoo!\r
+ <CS>\r
+ ・スカパー詳細+\r
+ ・テレビ王国スカパー\r
+ ・テレビ王国e2\r
+\r
+\r
+3.10+1.5.10(2011-07-05)\r
+■変更点\r
+ ・(レコーダ対応[TvRock]) エンコーダに「キャプチャ」を選択できるようにした(>>etc.) ★\r
+ ・(Web番組表対応[RD-Style]) サービス終了に伴い削除\r
+ ・(Web番組表対応[@nifty]) TVガイドへのリンクに変更されたので削除\r
+ ・(Web番組表対応[TVガイド]) 追加 ※データ取得方法の都合で、他のプラグインより時間がかかります\r
+■バグ修正\r
+ ・(レコーダ対応[DIGA BWT2100/DIGA XW120]) 予約情報の再取得が失敗するようになっていた問題を修正(>>etc.)\r
+ ・(レコーダ対応[DIGA XW120]) 再取得でタイトルが空の予約の場合に不正な文字列が取得される問題を修正\r
+ ・(レコーダ対応[TvRock]) 予約情報の再取得が失敗するようになっていた問題を修正\r
+ ・(Web番組表対応) 初回起動時に番組表が正常に取得できない場合に鯛ナビが起動できない問題を修正(>>465.)\r
+\r
+★エンコーダに「キャプチャ」を選択した場合の設定などについて。\r
+ ・TvRockのデバイス名は「キャプチャ」としてください(初期値と同じ設定ですので、通常は変更の必要はないはずです)。\r
+ ・CHコード設定は次のように指定してください。\r
+  書式)\r
+   "<Web番組表の放送局名>","<Web番組表の放送局名>","キャプチャ:<Web番組表の放送局名>"\r
+  例)\r
+   "TOKYO MX・2","TOKYO MX・2","キャプチャ:TOKYO MX・2"\r
+ ・すでに「キャプチャ」で登録された予約がある場合はうまく動作しない場合がありますので、鯛ナビから登録しなおしてください。\r
+ ・表示色設定は「色1」と共有で個別設定はできません。お察しください。\r
+\r
+■バージョンアップ時の補足\r
+ ・env/ChannelConvert.datがインターネットTVガイド対応版になりました。\r
+  置き換えてください。\r
+ ・JRE同梱版のJREのバージョンをupdate24からupdate26に変更しました。\r
+  セキュリティ強化が行われているそうなので利用されている方は置き換えてください。\r
+\r
+★8日分取得対応プラグイン\r
+ <地上波&BS>\r
+ ・MSN\r
+ ・TVガイド\r
+ ・テレビ王国\r
+ ・Yahoo!\r
+ <CS>\r
+ ・スカパー詳細+\r
+ ・テレビ王国スカパー\r
+ ・テレビ王国e2\r
+\r
+--- 3.9 -> 3.10 ---\r
+\r
+3.9.4β+1.5.10(2011-06-16)\r
+■変更点\r
+ ・(レコーダ対応[BZ810(TaiSync)]) 自動削除、ライン音声選択、録画のりしろを選択肢に追加 ※\r
+ ・(レコーダ対応[TvRock]) wakeup/shutdown時にスリープタイマー解除などを行っていたが、wakeup時にマジックパケットを送るだけに変更\r
+ ・(レコーダ対応[BZ700/810]) 取得したタイトルのHTMLエスケープ解除の対象を拡大(>>433.)\r
+ ・(レコーダ対応[X5/X5EX/H1/H2]) 自動レート変更が起きた時もエラーにならないようにした(>>mail.)\r
+ ・(予約ダイアログ) AV自動設定にフォルダも含めるようにした(>>mail.)\r
+■バグ修正\r
+ ・(レコーダ対応[X5/X5EX/H1/H2]) 予約情報を再取得した際に、各種選択肢の情報が引き継がれない問題を修正(>>mail.)\r
+ ・(タイニーシンク[BZ810対応]) R2の予約情報がR1として表示されてしまう問題を修正\r
+ ・(タイニーシンク) 開始時刻を過ぎたエントリに対して削除を実行すると永久ループを起こす問題を修正\r
+\r
+■バージョンアップ時の補足\r
+ ※env/mail_bz810.defを置き換えてください。タイニーシンクも再起動してください。\r
+\r
+3.9.3β+1.5.9(2011-06-10)\r
+■変更点\r
+ ・(レコーダ対応[TvRock]) 録画優先度、録画終了後、コンピュータ名、終了後コマンド、待機時間、録画開始、録画終了を選択肢に追加 ※\r
+■バグ修正\r
+ ・(レコーダ対応[BZ700/BZ810]) 追加した選択肢の情報がAV設定などに保存されなかった問題を修正\r
+ ・(レコーダ対応[TvRock]) 予約情報の再取得が正しく動作しない問題を修正\r
+ ・(Web番組表) 一日の長さが1439分になってしまう問題を修正(>>429.)\r
+ ・(その他) Linux/MacOSXで、オンラインアップデートを実行した際、アップデートに成功しても「失敗」と表示される問題を修正\r
+\r
+■バージョンアップ時の補足\r
+ ※バージョンアップ後、予約情報の再取得を実行して必要な情報を収集してください。\r
+  また前回書洩らしましたが、BZ700/BZ810・X5/X5EX/H1/H2の追加選択肢についても同様に予約情報の再取得を実行して必要な情報を収集してください。\r
+\r
+3.9.2β+1.5.9(2011-06-08)\r
+■変更点\r
+ ・(Web番組表) NHK Eテレに対応(>>419.) ※\r
+ ・(レコーダ対応[X5/X5EX/H1/H2]) 記録先デバイス、高レート節約、ライン音声選択、DVD記録時画面比を選択肢に追加(>>mail.)\r
+ ・(レコーダ対応[BZ700/BZ810]) 録画優先度、自動削除、ライン音声選択、録画のりしろを選択肢に追加\r
+ ・(新聞形式) 放送局の自由並べ替えを実装 ※デフォルトはOFFです\r
+■バグ修正\r
+ ・(Web番組表) キャッシュファイルがない日付の情報が、再取得ボタンを押さないと取得できなかった問題を修正(>>mail.)\r
+ ・(Web番組表) CS[セカンダリ]を設定しても再起動すると表示されない問題を修正(>>mail.、417.)\r
+ ・(レコーダ対応[BZ810]) R1でDR指定だと予約が登録できない問題を修正\r
+\r
+■バージョンアップ時の補足\r
+ ※必要に応じてChannelConvert.datを修正してください。ご自分で手を入れてない場合は単純に上書きでも構いません。\r
+\r
+3.9.1β+1.5.9(2011-06-03)\r
+■変更点\r
+ ・(レコーダ対応) REGZA Z1(Mail)プラグインを追加(>>364.)\r
+ ・(レコーダ対応[RD-H1]) バージョンYK15への対応を追加(>>400.)\r
+ ・(新聞形式) レコーダ選択を新聞形式にも反映(>>380.) ※デフォルトはOFFです\r
+ ・(Web番組表[テレビ王国]) 番組詳細中の"〜"(波ダッシュ)を"~"(全角チルダ)で置き換えるようにした(>>399.) ※タイトルの方はすでに対応ずみです\r
+ ・(Web番組表) "−"(全角ハイフン[E28892])をRDに流すと化けるので"-"(全角ハイフン[EFBC8D])で置き換えるようにした\r
+ ・(カレンダー連携) Googleカレンダーの削除に使っているテキスト検索の動作が異常になっているので暫定的な対策を実施\r
+ ・(その他) 右クリックメニューに「キーワード検索への追加」を追加\r
+■バグ修正\r
+ ・(CHコード設定) 放送局名を追加するボタンを押すとぬるぽな問題を修正(>>359.)\r
+ ・(新聞形式) いったん簡素表示に切り替えると、通常表示に戻らなくなってしまう問題を修正(>>404.)\r
+ ・(Web番組表) -nowebaccessを指定してもWeb番組表にアクセスする場合がある問題を修正\r
+ ・(レコーダ対応[REGZA Z9500]) 予約情報にIDを振っていなかったため予約情報の削除が行えなかった問題を修正\r
+ ・(リスト形式) CH設定を変更するとピックアップ情報がクリアされてしまう問題を修正\r
+\r
+3.9+1.5.9(2011-05-29)\r
+■変更点\r
+ ・(レコーダ対応) RD-H1プラグインを追加(>>351.)\r
+ ・(レコーダ対応) RD-BZ810プラグインを追加 ★\r
+ ・(レコーダ対応) RD-BZ810(+タイニーシンク)プラグインを追加\r
+■バグ修正\r
+ ・(色選択ダイアログ) RGBタブで10進数の直接入力が行えなかった問題を修正\r
+\r
+■おまけ\r
+ ・(Web番組表) Yahoo!ラジオ番組表プラグインを追加 ★\r
+\r
+★RD-BZ810プラグイン\r
+ ・エンコーダーがR2の場合、[AVC]や[VR]を選択しても無視してすべて[DR]に変更されます。\r
+  ネットdeナビに[AVC]を選択するインタフェースがないためです。\r
+  ※RD-BZ810(+タイニーシンク)プラグインではR2で[AVC]が利用可能です。\r
+\r
+★ラジオ番組表プラグイン\r
+ ・利用可能な機能は、新聞形式での表示とツールバーからの検索機能、ピックアップ機能のみです。\r
+  またWeb上に2日間のデータしかないため表示も2日分のみとなります。\r
+\r
+■バージョンアップ時の補足\r
+ ・じっこうするよ.cmd等Windows向けの実行バッチをtinybannavi.cmdなどの英字名に変更しました。\r
+  Javaのzipライブラリが日本語ファイルを通さないためです。ご了承ください。\r
+\r
+--- 3.8 -> 3.9 ---\r
+\r
+3.8.18β+1.5.8(2011-05-27)\r
+■変更点\r
+ ・(Web番組表) 8日分取得対応(>>269.) ※デフォルトはOFFです\r
+ ・(レコーダ対応) NULLプラグインを追加(>>290.)\r
+ ・(レコーダ対応) RD-E300プラグインを追加\r
+ ・(新聞形式) ピックアップノードを追加(>>307.)\r
+ ・(CHコード設定) [SET]ボタンが使えない場合はグレーアウトさせるようにした\r
+ ・(CHコード設定) 上下移動ボタンを追加\r
+ ・(その他) オンラインアップデート機能を追加 ※実際に動くのは次のリリースが出てからですが…\r
+\r
+★8日分取得対応プラグイン\r
+ <地上波&BS>\r
+ ・MSN\r
+ ・@nifty\r
+ ・rd-style\r
+ ・テレビ王国\r
+ ・Yahoo!\r
+ <CS>\r
+ ・スカパー詳細+\r
+ ・テレビ王国スカパー\r
+ ・テレビ王国e2\r
+\r
+★ピックアップノード\r
+ ・指定した番組情報を記憶します。\r
+ ・ピックアップノードまたは予約待機ノードで表示される一覧で確認できます。\r
+ ・番組情報は放送終了時刻を過ぎたら自動削除されます。\r
+ ・番組情報を指定して削除も行えます。\r
+\r
+3.8.17β+1.5.8(2011-05-20)\r
+■変更点\r
+ ・(リスト形式) 裏番組予約マーク機能を追加(>>217.) ※デフォルトはOFFです ※隣接番組を重複扱いしないオプションに影響を受けます\r
+ ・(レコーダ対応[BZ700]) 個々の予約画面を開いて詳細情報を取得する動作を追加(>>220ほか.) ※取得時に実行するか確認ダイアログが出ます\r
+ ・(レコーダ対応) RD-H2プラグインを追加(>>248.)\r
+ ・(その他) MacOSXで、予約マークや重複マークの「●」「■」が小さい文字で表示されてしまう問題に対応\r
+ ・(Web番組表) 取得結果のエラーチェックを強化(①1日に番組枠が1つ以下しか存在しない場合、②番組枠が24時間分存在しない場合を警告するようにした)\r
+■バグ修正\r
+ ・(番組延長警告) 非延長警告条件の動作が予定していた動作をしていなかった問題を修正\r
+ ・(番組延長警告) 編集ダイアログを開いたとき、非延長警告条件なのに延長警告条件としてセットされてしまう問題を修正\r
+\r
+★裏番組予約マーク\r
+ ・リスト形式で、当該番組に時間が重なる(すなわち裏番組の)予約情報がある場合に、予約列に「■」が表示されます\r
+\r
+★非延長警告条件の動作\r
+ (旧)非延長警告条件に合致した番組以降はすべて警告対象から外す\r
+ (新)警告条件に合致した番組であっても非延長警告条件に合致した場合は警告トリガーにしない\r
+\r
+■バージョンアップ時の補足\r
+ ・tinybannavi.shを更新してください。\r
+ ・env\RdChannelCode.dat.RD-H2をコピーしてください。\r
+\r
+■既知の問題点\r
+ ・ツールバーから番組表再取得を行うとリスト形式に検索結果が表示されなくなる時がある。再起動すれば治る。条件不明…\r
+\r
+3.8.16β+1.5.8(2011-05-14)\r
+■変更点\r
+ ・(レコーダ対応[BZ700]) 記録先フォルダにUSB上のフォルダが表示されるようにした(>>207-3.)\r
+ ・(レコーダ対応[BZ700]) 番組追っかけの情報が鯛ナビからの更新で上書きされないように"title_link_submit"・"sport_ext_submit"を送らないようにした(>>208-1.)\r
+ ・(レコーダ対応[BZ700]) 予約実行ON/OFFの際は情報が鯛ナビからの更新で上書きされないようにbExecのみを送るようにした(>>208-2.)\r
+ ・(CHコード設定) Web番組表に存在しない放送局をリストに追加するUIを追加(>>209.)\r
+ ・(検索キーワード登録ダイアログ) MacOSXで、レイアウトの関係で文字が見切れる部分の修正コードをマージ(>>210.)\r
+ ・(その他) MacOSXでのJTableの罫線が見えるように変更\r
+ ・(その他) MacOSXのDockアイコン、アプリケーション名表示への対処\r
+■バグ修正\r
+ ・(予約ダイアログ) エンコーダ→画質の連動 と 画質→エンコーダの連動 が、DR1/2 では働かない問題を修正(>>207-1.)\r
+ ・(レコーダ対応[BZ700]) 記録先デバイスの情報を取得していなかった問題を修正(>>207-2.)\r
+ ・(カレンダー連携) 帯予約の更新・削除を行った際、古いカレンダーエントリーが削除されない問題を修正\r
+ ・(その他) レコーダを一つも登録せずに操作を続けるといろいろ不具合が出る問題を修正\r
+ ・(レコーダ対応[S1004K/Mail]) 画質に[VR]AT、[TSE]ATを選択できなかった問題を修正\r
+\r
+■バージョンアップ時の補足\r
+ ・env\mail.defを更新してください。\r
+ ・icon\tainavi.icnsをコピーしてください。\r
+ ・tinybannavi.shを更新してください。\r
+\r
+3.8.15.1β(remofix)+1.5.8(2011-05-08)\r
+■バグ修正\r
+ ・(りもこん) 新しいコードを設定できない問題を修正\r
+\r
+3.8.15.1β+1.5.8(2011-05-05)\r
+■バグ修正\r
+ ・(レコーダ対応[XS57/A600/S600/X8/S304K/BZ700]) 録画設定の取得に必ず失敗してしまう問題を修正(>>202.)\r
+\r
+3.8.15β+1.5.8(2011-05-05)\r
+■変更点\r
+ ・(レコーダ対応[BZ700]) 画質にAF/AN/AE/AT 4.7/8.5/9.4/25/50の選択肢を追加(>>185-187,196.)\r
+ ・(レコーダ対応[BZ700]) TS/TSEをDR/AVCに変更(>>185-187,196.)\r
+ ・(レコーダ対応[BZ700]) 画質の情報のうちAF/AN/AE/AT 25/50が予約一覧から取得できない問題に対応(>>196.)\r
+ ・(予約管理) レコーダから予約情報を取得した後に予約一覧にジャンプしないように変更\r
+ ・(新聞形式) スナップショット/印刷機能を実装 ※オンメモリで巨大なビットマップを展開するのでメモリをドカ食いします\r
+ ・(Web番組表[地上波]) Yahoo!テレビ.Gガイドプラグインを追加\r
+■バグ修正\r
+ ・(レコーダ対応[RDデジ系]) RDから予約一覧を取得した際に画質のSP/LPの文字を消していたため鯛ナビが保持している情報と齟齬が出てしまっていた問題を修正(>>185-187.)\r
+ ・(レコーダ対応) 予約削除に成功しても失敗しても関係なく「予約が削除されました」と表示される問題を修正(>>185-187.)\r
+ ・(レコーダ対応) レコーダから情報を取得する際に、HTMLを解析できなかった場合(失敗した場合)にキャッシュがリセットされる問題を修正(>>194,197.)\r
+ ・(予約管理) レコーダから予約情報を取得した際に、新聞形式に予約マークが即時反映されない問題を修正(>>192.)\r
+ ・(Web番組表[MSN]) 番組タイトルからHTMLタグを削除していなかった問題を修正\r
+\r
+■おまけ\r
+ ・(りもこん) カスタマイズ用のUIを追加\r
+\r
+■バージョンアップ時の補足\r
+ ・起動後にレコーダからの情報取得を行ってください。\r
+ ・必要に応じてenv\ChannelConvert.datを更新してください。\r
+ ・必要に応じてenv\RemoteCtrl.defを更新してください。\r
+ ・icon\camera.pngをコピーしてください。\r
\r
+■メモ\r
+ ・MSN(日刊)\r
+ ・@nifty(東ニュ)\r
+ ・rd-style(日刊)\r
+ ・webザテレビジョン(角川)\r
+ ・テレビ王国(角川)\r
+ ・Yahoo!(IPG) NEW!\r
+\r
+3.8.14.1β+1.5.8(2011-04-29)\r
+■バグ修正\r
+ ・(検索) 過去ログ検索結果で番組詳細ウィンドウが正しく表示されない問題を修正\r
+\r
+3.8.14β+1.5.8(2011-04-28)\r
+■変更点\r
+ ・(色選択ダイアログ) ダイアログをオープンした際に、現在値を初期値として表示するようにした(>>173.)\r
+ ・(色選択ダイアログ) RGBタブを16進数で入力できるようにした(>>173.)\r
+ ・(色選択ダイアログ) 前回開いていた時のタブ位置などの情報を忘れないようにした\r
+ ・(レコーダー対応) REGZA RD-BZ700プラグインを追加(>>176-177.)\r
+ ・(各種設定) レコーダ設定を各種設定から分離\r
+ ・(検索) 過去ログ検索を実装\r
+   -ツールバーのキーワード検索ボックスを利用\r
+    書式) [YYYY/]MM/DD[ yyyy/mm/dd] keyword1[... keywordN]\r
+      YYYY/MM/DD 検索開始日付(YYYYを省略した場合は現在の年)\r
+      yyyy/mm/dd 検索終了日付(省略した場合は前日まで)\r
+■バグ修正\r
+ ・(チャンネルリモコン(RD系)) RDが起動していないとRDにアクセスしに行ったまま帰ってこなくなる問題を修正\r
+\r
+■バージョンアップ時の補足\r
+ ・GoogleCalendarプラグイン/RD(Mail)系プラグインをご利用中の方はお手数ですが置き換えを行ってください。\r
+\r
+3.8.13β+1.5.8(2011-04-23)\r
+■変更点\r
+ ・(Web番組表) CSプラグインを、スカパー本家と番組表サイトの2種を併存できるようにした(>>142.)\r
+ ・(レコーダー対応) RD-XS57プラグインを追加(>>160.)\r
+ >>102.氏の修正パッチ&改修案をマージ(>>145.、163.)\r
+---ここから\r
+ ・(レコーダー対応[RD-XS57/A600/S600/S304K/X8])\r
+   broadcast値の選択方法を変更しました。今回大きく手を入れたところですので問題がないか確認お願いします。\r
+   以下変更後のロジック詳細\r
+    -(uva|uvd|bsa|bsd|csd|l1|l2|l3)_ch_valueに定義のある放送局コードの場合は対応するbroadcast値を選択\r
+    -上記以外の放送局コードの場合、以下のルールでbroadcast値を選択(使われないコード?)\r
+     (1)放送局名が"C*"の場合は外部入力(L1)\r
+     (2)放送局名が"SP*"の場合は外部入力(L3)\r
+     (3)(1)、(2)以外の場合は外部入力(L2)\r
+ ・(RDリモートステータスビューア) 既存部分中の未解析箇所を修正していただきました\r
+---ここまで\r
+ ・(タイトルバー) 現在日時を常時表示にした\r
+ ・(タイトルバー) メモリ情報を表示するようにした ※ヒープの情報なのでプロセス全体のメモリではないです\r
+ ・(Web番組表[地上波]) MSNエンタメプラグインを追加\r
+ ・(Web番組表[CS]) Gガイド.テレビ王国プラグインを追加\r
+■バグ修正\r
+ ・(TvRockプラグイン) レコーダからの情報再取得に失敗する場合がある問題を修正\r
+ ・(RD-X8/S600プラグイン) 記録先デバイスの情報をRDに渡していなかった問題を修正\r
+\r
+■バージョンアップ時の補足\r
+ ・RD-XS57/A600/S600/S304K/X8プラグインご利用の場合は、起動後にレコーダからの情報取得を行い、\r
+  broadcast選択のための補助情報を取得してください。\r
+\r
+■メモ\r
+ ・MSN(日刊) NEW!\r
+ ・@nifty(東ニュ)\r
+ ・rd-style(日刊)\r
+ ・webザテレビジョン(角川)\r
+ ・テレビ王国(角川)\r
+\r
+3.8.12.1β+1.5.8(2011-04-19)\r
+■バグ修正\r
+ ・(予約管理・タイニーシンク) 深夜帯(24:00-28:59)の間、週次の予約の翌週分が発生しない問題を修正\r
+\r
+3.8.12β+1.5.7(2011-04-12)\r
+■変更点\r
+ ・(類似予約) 検索範囲を指定できるようにした(前後1~24時間以内の予約のみ検出)(>>136.) ※デフォルトはOFFです\r
+ ・(予約ダイアログ) 自レコーダに類似予約がない場合、他レコーダの類似情報を引き継いでいたが、引き継がないようにした\r
+ ・(予約ダイアログ) 予約実行OFFの場合は赤字で強調するようにした\r
+■バグ修正\r
+ ・(予約管理) レコーダから再取得した際にジャンル情報等が失われる問題を修正\r
+ ・(予約管理) 「深夜帯の帯番組を深夜帯に予約すると当日分のみ予約されない問題」が再発したのを再度修正orz\r
+\r
+3.8.11β+1.5.6(2011-04-10)\r
+■変更点\r
+ ・(新聞形式) 現在時刻線表示中は、現在日時をタイトルバーに表示するようにした\r
+ ・(その他) Windows XPでの初回起動時のデフォルトフォントがMS UI Gothicになるようにした\r
+ ・(タイニーシンク) 予約実行OFFの予約はRDに反映しないようにした(>>128.)\r
+ ・(タイニーシンク) 進捗状況表示で「予約実行OFF(赤字取り消し線)」を追加\r
+ ・(ツールバー) ヘルプ(プロジェクトwikiへのリンク)ボタンを追加\r
+ ・(その他) 起動時にレコーダーから情報収集するオプションを追加(-loadrec)\r
+■バグ修正\r
+ ・(その他) 日本以外のTimeZoneの地域でも鯛ナビ自体はAsia/Tokyo固定で動作するようにした(>>129.)\r
+ ・(新聞形式) 現在時刻線表示中に日付が変わっても、番組表が現在日付に書き換わらない問題を修正\r
+ ・(予約管理・タイニーシンク) 深夜帯(24:00-28:59)の間、繰り返し予約に過去日の予約が発生する問題を修正\r
+\r
+3.8.10β+1.5.5(2011-04-05)\r
+■変更点\r
+ ・(予約管理) RDユーザ向けに、予約枠が従来通りの表示となる「【注意】深夜の帯予約を1日前にずらす」オプションを追加\r
+ ・(GoogleCalendarプラグイン) 深夜帯の帯番組の予約に対応\r
+■バグ修正\r
+ ・(予約管理) 深夜帯の帯番組を深夜帯に予約すると当日分のみ予約されない問題を修正\r
+\r
+3.8.9β+1.5.5(2011-04-04) ※緊急リリース\r
+■変更点\r
+ ・(TvRockプラグイン) 深夜帯の帯番組の予約に対応\r
+ ・(タイニーシンク) 進捗状況表示で「削除済みでテーブルのリフレッシュ待ち(青字取り消し線)」を追加\r
+ ・(RDリモートステータスビューア) 改善案と取り込み漏れを吸収(>>124.)\r
+■バグ修正\r
+ ・(予約管理・タイニーシンク) 深夜帯の帯番組を予約すると当日分のみ予約されない問題を修正\r
+\r
+■評価リリース\r
+ ・(DIGA/DIGA BWT2100プラグイン) 深夜帯の帯番組の予約に対応\r
+   ※動作報告お待ちしております\r
+\r
+3.8.8β+1.5.4(2011-04-03)\r
+■変更点\r
+ >>102.氏の修正パッチをマージ\r
+---ここから\r
+ ・(RDリモートステータスビューア) BZ700で拡張された箇所を追加していただきました\r
+ ・(RDリモートステータスビューア) 既存部分中の未解析箇所を修正していただきました\r
+ ・(RDリモートステータスビューア) タイムサーチのナイスな実装を提供していただきました\r
+---ここまで\r
+ ・(予約ダイアログ) 放送局別画質・音質自動設定の記憶項目に記録先デバイス、チャプタ分割3種を追加(>>115.)\r
+ ・(タイニーシンク) http://ip:port/reserve.htmで予約の反映状況を目視できるようにした ※基本的にデバッグ用\r
+■バグ修正\r
+ ・(A600/S600/X8/S302K/S1004Kプラグイン・タイニーシンク) タイトルが空欄のエントリをRDから取り込めなかった問題を修正(>>113.) ※デジ系RDのみ\r
+ ・(予約ダイアログ) 類似予約の引き継ぎ項目にDVD互換モード、記録先デバイス、チャプタ分割3種が含まれていなかった問題を修正\r
+ ・(チャンネルリモコン・RDリモートステータスビューア) 新機種が出てデータの形式がかわってもとりあえずハングアップだけはしないように修正(したつもり)(>>102.)\r
+\r
+3.8.7.1β+1.5.3.1(2011-04-02) ※緊急リリース\r
+■バグ修正\r
+ ・(予約管理・タイニーシンク) 日またがりの予約が投入されない場合がある問題を修正\r
+\r
+3.8.7β+1.5.3(2011-04-01)\r
+■変更点\r
+ ・(その他) ChannelConvert.datを「NHK BS1」「NHK BSプレミアム」対応版に置き換え\r
+■バグ修正\r
+ ・(予約管理・タイニーシンク) 帯予約(月~木・金・土)で開始時間が24:00~28:59の場合に日程が一日前にずれる問題を修正\r
+ ・(タイニーシンク) RDに反映されていないエントリを削除しようとするときにヌルポインタ参照していた問題を修正\r
+ ・(タイニーシンク) 「RDに反映されない過去日の~」の修正ミスを再修正\r
+ ・(その他) ChannelConvert.dat中の"め〜テレ"が文字化けしていたので修正…orz\r
+\r
+3.8.6β+1.5.2(2011-03-30)\r
+■変更点\r
+ ・(X8/S304K/S1004Kプラグイン+タイニーシンク) A600プラグインのタイトル自動補完対応を横展開\r
+ ・(カレンダー連携) 予約実行がOFFの予約はカレンダーへの登録を行わないようにした\r
+ ・(Web番組表プラグイン) HTML特殊文字のデコードをwebザテレビジョン以外にも横展開 ※タイトルのみ\r
+ ・(Web番組表プラグイン) "〜"(波ダッシュ)をRDに流すと化けるので"~"(全角チルダ)で置き換えるようにした ※タイトルのみ\r
+ ・(Webザテレビジョンプラグイン) "<新>"の記載のある番組に新番組フラグを立てるようにした\r
+ ・(その他) ログビューアを追加 ※ログのアクセス排他に問題があるので、起動中のログ確認はログビューアを利用してください\r
+■バグ修正\r
+ ・(予約一覧) レコーダー情報の再取得を行うと、予約情報一覧の番組詳細情報がクリアされてしまう問題を修正 ※デジ系レコーダーのみ\r
+ ・(予約一覧) 予約の編集で予約先のレコーダーとは別のレコーダーが選択できるようになっていた問題を修正\r
+ ・(予約ダイアログ) 放送局コードが設定されていないチャンネルを開くときにヌルポインタ参照していた問題を修正\r
+ ・(タイニーシンク) RDに反映されない過去日の予約/削除エントリが発生した際に、永久にエントリが残留してしまう問題を修正\r
+\r
+3.8.5β+1.5.1(2011-03-25)\r
+■変更点\r
+ ・(予約一覧) 予約実行のON/OFFを予約ダイアログを開かずに右クリックメニューから更新できるようにした\r
+■バグ修正\r
+ ・(その他) MacOSXで、クリーンインストール時に起動できない問題があったのを修正\r
+\r
+3.8.4β+1.5.1(2011-03-24)\r
+■変更点\r
+ ・(予約一覧) 予約一覧から予約情報の編集を行えるようにした(>>80.)\r
+ ・(web番組表プラグイン) 地上波&BSの番組表の番組詳細が空の場合に、しょぼかるの番組詳細をコピーするようにした ※アニメ番組のみ\r
+ ・(キーワード検索) ツールバーからのキーワード検索の結果で、タイトルのマッチした部分を強調表示するようにした(>>10.)\r
+ ・(その他) JTextField(ツールバーの検索ボックスとか)、JTextArea(予約ダイアログの番組詳細とか)、JComboBox(各種設定のシャットダウンコマンドとか)にコピペ用右クリックメニューを追加\r
+\r
+3.8.3β+1.5.1(2011-03-17)\r
+■変更点\r
+ ・(その他) 起動オプションにWeb番組表へのアクセスを抑制するオプション(-nowebaccess)を追加\r
+     ※指定のWeb番組表がダウンしている際に、このオプションを付けて起動して他のWeb番組表に切り替えてください\r
+      ただし設定で「web番組表キャッシュの有効時間」を0にしている場合は起動時の自動アクセスを行わないので、指定不要です\r
+ ・(その他) アニメを見ない方向けに、しょぼかるへのアクセスを行わない設定ができるようにした ※デフォルトは"行う"です\r
+ ・(すかぱー本家系以外プラグイン) 放送局リストをCh番号順にソートするようにした\r
+ ・(RD-X5/X5EXプラグイン) CHコード設定の[SET]ボタンを使えるようにした ※レコーダ内の放送局情報が必要なため「レコーダからの情報再取得」を実行してください\r
+■バグ修正\r
+ ・(その他) フルスクリーン時のサイドツリーのin/out動作を改善\r
+ ・(その他) Web番組表へのアクセスに失敗したとき、取得済みデータまでクリアしてしまう不具合を修正\r
+ ・(その他) Web番組表切り替え等を行ったときに、過去ログが重複発生してしまう場合がある不具合を修正\r
+\r
+3.8.2β+1.5.1(2011-03-10)\r
+■変更点\r
+ ・(CH設定) 放送局リストの再構築ボタンを追加\r
+ ・(CH設定) 放送局情報の引き継ぎでエリア情報が極力引き継がれるよう修正した\r
+ ・(すかぱー本家系プラグイン) 放送局リストをCh番号順にソートするようにした\r
+ ・(その他) ログスイッチを行うようにした ※2MBを超えるとlog.txt.bakにrenameします)\r
+ ・(その他) Windows環境の場合のみ、標準出力の文字コードがシフトJISになるようにした\r
+ ・(タイニーシンク) POP/SMTP待ち受けポートを指定したアドレスにバインドできるようにした\r
+            ※利用方法についてはプロジェクトwikiを参照してください\r
+■バグ修正\r
+ ・(CH設定) 地域移動したときに選択局情報が引き継がれない問題を修正\r
+\r
+3.8.1β+1.5(2011-03-06)\r
+■変更点\r
+ ・(番組追跡) あいまい検索の閾値のデフォルト値を変更できるようにした(>>63.)\r
+ ・(その他) 番組詳細が画面上部の表示欄に収まりきらない場合に、スクロールバーを使って確認できるようにした(>>65.)\r
+ ・(ツールバー) 番組表の取得を、地上波&BSとCSで個別に実行できるようにした\r
+ ・(ツールバー) CSの取得が長時間におよぶ場合を考慮して、CSの取得終了と同時にシャットダウンコマンドを実行できるようにした\r
+ ・(その他) 起動オプションにProxyの強制設定を追加。書式→ -proxy IPアドレス[:ポート番号(省略時は8080)]\r
+        ※Proxy経由でしかinternet接続が許可されていない環境では、鯛ナビを起動できない問題への対処\r
+■バグ修正\r
+ ・(webザテレビジョンプラグイン) タイトル文字列中のHTMLエスケープをデコードしていなかった問題を修正\r
+ ・(その他) 描画速度優先時、番組表データが前日以前に取得した状態のまま起動すると、起動に失敗する場合がある問題を修正\r
+\r
+3.8+1.5(2011-02-20)\r
+■変更点\r
+ ・(新聞形式) サイドツリーに放送局別ノードを追加(>>51.)\r
+■バグ修正\r
+ ・(すかぱー系プラグイン) 「ほげほげ #1」となっている番組に新番組フラグが立っていなかった問題を修正\r
+ ・(リスト形式) 番組追跡の編集で、番組タイトルを修正した際に、ツリーに変更が即時反映されなかった問題を修正\r
+\r
+■バージョンアップ時の補足\r
+ ・JRE同梱版のJREのバージョンをupdate22からupdate24に変更しました。\r
+  セキュリティ強化が行われているそうなので利用されている方は置き換えてください。\r
+\r
+3.7.10β+1.4.3β\r
+■バグ修正\r
+ ・(TvRockプラグイン) 登録・更新時に番組詳細が200Byteを超えるとTvRockからレスポンスが帰ってこなくなるので、200Byteでカットするように変更\r
+\r
+3.7.9β+1.4.3β\r
+■変更点\r
+ ・(DIGA系プラグイン&CHコード設定) 放送局コードの自動設定動作をDIGAにも展開した\r
+■バージョンアップ時の補足\r
+ ・(DIGA系プラグイン) CHコード設定の地上デジタル・BSの表記を"000"・"BS999"から"地上D 000"・"BS 999"に変更しました。\r
+   既存の設定については、自動でコンバートします。設定を追加する場合は、プロジェクトwiki「はじめてのたいなび(DIGA編)」を参照してください。\r
+\r
+3.7.8β+1.4.3β\r
+■変更点\r
+ ・(RDデジ系プラグイン&CHコード設定) 放送局コードの自動設定ボタンを追加 ※プロジェクトwiki「はじめてのたいなび(RDデジ機【RD-X8まで】編)」を参照してください\r
+\r
+3.7.7β+1.4.3β\r
+■変更点\r
+ ・(RD-S304Kプラグイン) RD本体に直接予約登録できるようにした(登録のみ、更新・削除は不可) ※従来のRdChannelCode.dat.RD-S304Kは使えません。置き換えてください\r
+ ・(その他) 外部LookAndFeelライブラリを利用できるようにした ※http://www.javootoo.com/などからLookAndFeelのファイルをDLしてjarファイルをskinフォルダに置いてください\r
+■バグ修正\r
+ ・(RD-A600プラグイン) CMチャプタ分割の取得値をキャッシュファイルに保存し忘れていた問題を修正\r
+ \r
+3.7.6β+1.4.3β\r
+■変更点\r
+ ・(その他) 裏番組通知の番組名が、常に番組表でポイントしたものになるように変更\r
+ ・(RD-A600プラグイン) 予約一覧リロード時にタイトル自動補完フラグを引き継ぐよう修正\r
+\r
+3.7.5β+1.4.3β\r
+■変更点\r
+ ・(CH設定タブ) Web番組表を切り替えた際に、放送局未選択のWeb番組表だった場合は、カレントWeb番組表の放送局を自動選局するようにした(>>25.)\r
+ ・(その他) >>27.氏の修正パッチをマージ\r
+---ここから\r
+ ・(A600)適当な対応ですが...ステータスの取得が可能\r
+ ・(A600)予約ダイアログの内容をA600ぽく変更\r
+ ・(A600)予約登録時、「予約名」、「番組詳細」をあえて空欄に(これで「何かの番組 #1」の#1が更新されます)\r
+ ・(A600)スカパー!無印(SPxxx)の予約登録が可能\r
+ ・(共通)裏番組チェックの強化 \r
+ ・(A600)RdChannelCode.dat.VARDIA RD-A600の追加\r
+ ・(A600)「視聴する」が可能、ただしスカパー!無印(SPxxx)は外部入力L3まで\r
+---ここまで\r
+ ・(その他) LookAndFeelを変更できるようにした ※初期値は、Windowsの場合は"Windows"、それ以外はシステムデフォルト\r
+■バグ修正\r
+ ・(その他) 放送局を一つも選択せずに終了すると、起動できなくなる場合があった問題を修正\r
+\r
+■バージョンアップ時の補足\r
+ ・(env/ChannelConvert.dat) 関東圏&大阪版から全国版に拡張しました。関東圏&大阪在住の方は入れ替えても問題ないと思いますが、\r
+   それ以外の方は、放送局名が強制変更されてしまう可能性が大なので、(1)入れ替えてCH設定・CHコード設定・番組追跡の放送局名等を\r
+   再度設定しなおすか、(2)そもそも入れ替えない、のいずれかで対処お願いします。\r
+\r
+3.7.4β+1.4.3β\r
+■変更点\r
+ ・(DIGA DMR-BWT2100プラグイン) リリースにマージ(>>13.)\r
+\r
+3.7.3β+1.4.3β\r
+■変更点\r
+ ・(リスト形式/新聞形式) 初回放送マーク(【初】)を表示するようにした\r
+■バグ修正\r
+ ・(RD-Styleプラグイン) 初回放送情報が拾えていなかった問題を修正\r
+ ・(しょぼかるプラグイン) 日またがりの処理が不正で新番組フラグを引き継ぎできない問題があったのを修正\r
+\r
+3.7.2β+1.4.3β\r
+■変更点\r
+ ・(番組追跡) 「再放送を除く」オプションを追加(>>968-1.) ※デフォルトはOFFです\r
+ ・(web番組表プラグイン) しょぼかるの新番組フラグを地上波&BS番組表に反映するようにした ※アニメ番組のみ ※適当なデータがなかったので仮実装。実際に動くのは年明けかな\r
+■バグ修正\r
+ ・(GoogleCalendarプラグイン) 同タイトルで繰り返し予約と単日予約が混在していると、更新と削除が正しく動作しない問題を修正\r
+ ・(リスト形式) 番組追跡&キーワード検索編集後、結果がリストに即時反映されていなかった問題を修正\r
+\r
+3.7.1β+1.4.3β\r
+■バグ修正\r
+ ・(RD-X5/X5EXプラグイン) フォルダ名に半角&が入るとそこから先が切れてしまう問題を修正(>>970.)\r
+\r
+3.7β+1.4.3β\r
+■変更点\r
+ ・(RDリモートステータスビュー(りもすて)) リリースにマージ\r
+ ・(りもこん) レコーダ一覧を鯛ナビ・タイニーシンク両方の設定を利用するように変更 ※タイニーシンク経由だったS1004K系の操作がRD直アクセスに変更になります\r
+ ・(りもこん) 終了時の位置、選択レコーダを記憶するようにした\r
+ ・(スカパープラグイン&詳細++) 日またがりの場合、元データ上翌日分が空欄になっていて繋がっているかどうか判定できなかったが、\r
+    よく見ると休止は「休止」と明記されているようなので空欄は直前の番組の継続と判定するようにした(>>935.) \r
+ ・(予約ダイアログ) 画質にTS、TSEを選択した場合は音質コンボボックスを無効にするようにした \r
+ ・(鯛ナビ・タイニーシンク) 内部的な効率化にともなう若干のコード修正 \r
+■バグ修正\r
+ ・(鯛ナビ・タイニーシンク) 環境によって?通信が正しく行われない事象があるようなので気持ち対処したもより\r
+ ・(GoogleCalendarプラグイン) 繰り返し予約を更新した時、古いエントリが削除されず残ってしまっていた問題を修正\r
+ ・(予約ダイアログ) 同一レコーダに類似予約が複数存在した場合、先頭の類似予約以外選択できなかった問題を修正\r
+\r
+■バージョンアップ時の補足\r
+ ・bin/以外に以下のファイルも更新してください。\r
+  1.iconフォルダ \r
+\r
+3.6β+1.4β\r
+■バグ修正\r
+ ・(予約ダイアログ) 新規エントリの開始終了日時の計算が不正だったため、W録自動選択が正しく動作しない場合があった問題を修正(>>918.)\r
+ ・(ツールバー) ツールバーからの過去ログ閲覧が正しく働いていなかった問題を修正\r
+\r
+\r
+\r
+3.5β+1.4β\r
+\r
+★★★ CAUTION - TvRockプラグインをご利用の方々へ ★★★\r
+\r
+TvRockのTvRock番組表の認証設定について従来、認証なし/Basic認証/Digest認証いずれの設定でも可としておりましたが、\r
+Digest認証を選択した場合正しいID/Passwordを入力しても認証がはじかれてしまう(=コネクションリセットが発生する)場合があることが判明しました。\r
+現時点では(1)発生条件が不明(問題ない入力と問題のある入力の違いが不明)であること、(2)IE8、Firefox3.6.10からの操作でも同様の現象が発生する\r
+ことなどを考慮しまして、鯛ナビでは、認証なしまたはBasic認証のみの対応ということに変更させていただきます。以上、ご了承のほどよろしくお願いいたします。\r
+\r
+★★★ CAUTION - TvRockプラグインをご利用の方々へ ★★★\r
+\r
+■変更点\r
+ ・(ツールバー) 検索テキストボックスの入力が YYYY/MM/DD 形式の場合、過去ログ閲覧を行うようにした ※サイドツリーに表示されなくなった古いデータはここから直接指定してください\r
+■バグ修正\r
+ ・(新聞形式) 時間バーの色設定を変更しても、再起動するまで反映されなくなっていた問題を修正\r
+ ・(新聞形式) ツールバーからのWeb番組表再取得後、サイドツリーが即時更新されない問題を修正\r
+ ・(TvRockプラグイン) コネクションリセットされてしまった際のリトライ処理が不十分だったため、処理が反映されていないのに正常終了してしまう場合があった問題を修正 ※基本的にリトライアウトになります\r
+\r
+3.4β+1.4β\r
+■変更点\r
+ ・(タイニーシンク) POP/SMTP待ちうけポートを設定で変更できるようにした ※POPポート利用がかちあうソフトなどは、ポートフォワーダー等で対処してください\r
+ ・(タイニーシンク) 多重起動などでPOP/SMTP待ちうけポートが開けなかった場合に、エラーダイアログを表示するようにした\r
+ ・(新聞形式) 過去ログ選択をツールバーからサイドツリーに移動\r
+ ・(りもこん) ボタン定義を外部ファイル化 ※env/RemoteCtrl.def\r
+ ・(りもこん) キーボードから操作できるようにした(>>91.) ※要RemoteCtrl.def設定\r
+■バグ修正\r
+ ・(S1004Kプラグイン) すかぱー連携等ライン入力を使用する予約で、RDから予約一覧の再取得をおこなうと放送局名が「外部入力」に戻ってしまう問題を修正\r
+ ・(新聞形式) 設定した局名の背景色が反映されなくなっていた問題を修正(>>896-1.)\r
+ ・(新聞形式) 新聞形式、本体予約一覧からの登録・更新・削除が、新聞形式の予約枠に即時反映されていない問題を修正\r
+ ・(番組追跡) キーワードの編集をしたあと、変更が検索結果に反映されなくなっていた問題を修正\r
+■既知の問題\r
+ ・(新聞形式) 放送局名の部分が描画が怪しい(>>896-2.) ※とりあえずupdateUI()を入れた。要継続調査\r
+■その他\r
+ ・(その他) JavaMail/JAFのリンクが移動していたので説明文を修正\r
+ ・(その他) Javaの権利者表示をSUN MicrosystemsからOracleに修正\r
+■バージョンアップ時の補足\r
+ ・bin/以外に以下のファイルも更新してください。\r
+  1.env/RemoteCtrl.def \r
+  2.icon/remocon.png \r
+\r
+3.3β+1.3β\r
+■変更点\r
+ ・(webザテレビジョンプラグイン) サイトの番組詳細ページがいつの間にか充実していたので、それへのリンクを抽出するようにした\r
+ ・(タイニーシンク) 複数のmail.defを使い分けられるように拡張した ※現時点では特に意味のある修正ではありません(future use)\r
+ ・(その他) channelNameAlias.datをChannelConvert.datに名称変更 ※番ナビの同等ファイルの名前にあわせた ※鯛ナビを起動すると自動で切り替わります\r
+ ・(レコーダープラグイン関連) socketのconnect timeout値を10s→5sに短縮、read timeout値を10s→15sに延長\r
+■バグ修正\r
+ ・(リスト形式) 直前の予約の時間が被さっている場合(延長警告管理で延長した予約など)に、予約マークがその予約のマークで置き換えられてしまう問題を修正\r
+ ・(新聞形式) すかぱーの局だけ選択しているときに日付ツリーが構築されない問題を修正\r
+ ・(webザテレビジョンプラグイン) タイトルの先頭が"「第n…"の場合に、タイトルが正しく取得できなかった問題を修正\r
+ ・(タイニーシンク) アイコンファイルを読み込めなかった場合に異常動作していた問題を修正(>>877.) ※単に起動させないようにしただけ\r
+\r
+3.2.1β+1.2β\r
+■バグ修正\r
+ ・(RD系Mailプラグイン) 自動チャプター関連の修正が入っていなかった問題を修正(>>868.)\r
+\r
+3.2β+1.2β\r
+■変更点\r
+ ・(予約ダイアログ) チューナー・エンコーダ自動選択時に、隣接番組を重複と扱うかどうか設定できるようにした(>>862-1.) ※デフォルトはOFFです\r
+ ・(その他) 予約・キーワード削除時に確認ダイアログを表示させるようにした(>>862-2.) ※デフォルトはOFFです\r
+ ・(新聞形式) 現在時刻線をの位置を固定して、番組表側をスクロールさせられるようにした ※デフォルトはONです\r
+ ・(新聞形式) 「描画速度向上(メモリ消費大)」のデフォルトをOFFにした\r
+ ・(新聞形式) 過去ログ閲覧機能を追加\r
+ ・(じっこうするよ.cmd) javaの実行オプションにヒープ領域の上限を拡大する指定を追加\r
+■バグ修正\r
+ ・(タイニーシンク) RDから予約完了通知・削除完了通知以外がSMTPされてきた場合が考慮されていなかった問題を修正\r
+ ・(リスト形式) 29時またぎの番組の長さ表示が間違った値になっていた問題を修正\r
+ ・(番組追跡・キーワード検索) キーワード登録後、再起動するまで検索結果に内容が反映されない場合があった問題を修正\r
+ ・(リスト形式) Ctrlクリック、若しくは、Shiftクリックで複数の番組選択が出来なくなってしまっていた問題を修正(>>865.)\r
+\r
+3.1β+1.1β\r
+■変更点\r
+ ・(RD系) 自動チャプター分割絡みの設定を追加(>>#52772)※1\r
+ ・(新聞形式) 起動速度と占有メモリを犠牲にして日付ビューの表示速度向上を図った※2\r
+ ・(その他) チューナー・エンコーダー別色設定を4つから8つに拡大\r
+ ・(TVTestプラグイン) ローカルマシン上のTVTestのチャンネルを操作できるようにした\r
+■既知の問題\r
+ ・(タイニーシンク) RDからエラー通知が返ってきてもトラップしていないので滞留チェックにひっかかるまでエラーに気づかない(※要修正)\r
+\r
+※1\r
+ バージョンアップ後、最初の鯛ナビ起動時にはRDの電源をONにしておいてください。\r
+ RD本体から自動チャプター関連の情報を取得する必要があるためですm(_ _)m\r
+※2\r
+ 環境によってはJavaVMのヒープ領域を食い尽くして動作不良を起こす場合があります。\r
+ javaのオプションに-Xmx512mとかをつけてヒープの上限をあげてみてください。\r
+ また今回の手法はopenjdkと相性が悪いらしく、逆に激しく遅くなってしまいます。\r
+ まったくもって意味不明の現象なのですが、仕方ないのでopenjdkではこの設定は\r
+ OFFにして使用してください。\r
+\r
+\r
+3.0+1.0\r
+■変更点\r
+ ・(番組追跡) あいまいスコア算出の正引きと逆引きの順番を入れ替えた(※表面上スコアが変わる場合がありますが、動作に違いはありません)\r
+ ・(番組追跡) あいまいスコア算出の逆引きを省略できるようにした(※非推奨)\r
+ ・(番組追跡) 速度向上\r
+ ・(キーワード検索) 速度向上(ツールバーからの検索は対象外)\r
+ ・(新聞形式) 若干の速度向上\r
+ ・(新聞形式) 簡素表示(+ツールチップ表示)を復活\r
+ ・(本体予約一覧) 速度向上\r
+ ・(その他) フォント未指定時の規定値を変更\r
+\r
+2.16β+0.3β\r
+■変更点\r
+ ・(予約ダイアログ) 2.1βでこっそり変更した番組名選択の優先順位を設定で変更できるようにした(>>846.)\r
+ ・(予約ダイアログ) 類似予約の検索に、番組追跡のキーワードと閾値を利用するようにした(>>847.)※\r
+\r
+ ※リスト形式からの予約で、かつ番組追跡にヒットした情報からの予約の場合のみ動作します。(今回の修正点)\r
+  それ以外は検索キーワード=Web番組表の番組名、閾値=35固定となります。(現状どおり)\r
+\r
+2.15β+0.3β\r
+■変更点\r
+ ・(REGZA Z9500(Mail)プラグイン) 追加(>>835.)\r
+\r
+2.14β+0.3β\r
+■変更点\r
+ ・(予約ダイアログ) TvRock選択時に、チューナーの選択肢を地上波予約は場合は"地上Dn"、BS/CSの場合は"BSn"のみを表示するようにした\r
+ ・(予約ダイアログ) レコーダコンボボックス変更時、(1)類似予約があるならそのAV設定情報を継承し、(2)ないなら空きチューナーの自動割り当てを行うようにした\r
+■バグ修正\r
+ ・(予約ダイアログ) 裏番組がない状態にもかかわらず、AVデフォルト設定が正しく反映されなくなっていた問題を修正(>>829.)\r
+   ※裏番組が無い場合はAVデフォルト設定が初期値として設定されますが、\r
+     裏番組が有る場合はAVデフォルト設定が無視される場合があります\r
+     (ex. RE/TSEがデフォでかつ裏番組で使用されている場合TS2が自動選択されますがTS2/TSEとは設定できない、など)\r
+\r
+2.13β+0.3β\r
+■変更点\r
+ ・(予約ダイアログ) W録搭載機種での予約時エンコーダ自動設定(暫定)(>>814.)\r
+\r
+2.12β+0.3β\r
+■変更点\r
+ ・(延長警告機能) 独立して存在していたスポーツ延長警告機能を汎用アイテムとしてとりこんだ\r
+   ※既存のスポーツ延長警告の設定は、リスト形式の延長警告管理に「●スポーツ延長警告」として設定しなおされます。\r
+    また既存の設定に加えて「番組長が60分以上である」という制限が追加されます。不要な方は、お手数ですが条件を削除してください。\r
+   ※今回の修正は、5分10分といったスポーツ情報番組が延長警告のトリガーになっていて無駄に警告だらけになっていたのを\r
+    抑える目的で行いました。そういった意味では2.11βの修正の延長線上のリリースということになります。\r
+\r
+2.11β+0.3β\r
+■変更点\r
+ ・(キーワード検索) 番組の長さ(分)を条件に指定できるようにした(>>804.)\r
+\r
+2.10β+0.3β\r
+■変更点\r
+ ・(タイニーシンク) 自動登録を8日先固定で行っていたのを、7~1日先までに変更できるようにした(RDのマニュアル予約上限が64件までなので少しでも予約エントリを節約するための処置)\r
+■バグ修正\r
+ ・(ツールバー・新聞形式) 番組表再取得後、表示されている番組表と保持している番組情報に齟齬が出る問題を修正\r
+\r
+2.9β+0.2β\r
+■変更点\r
+ ・(ツールバー・リスト形式) 番組表再取得後、リストを自動更新するようにした\r
+ ・(タイニーシンク) 滞留チェックの間隔を指定できるようにした\r
+ ・(タイニーシンク) 滞留警告の対象を指定できるようにした\r
+ ・(タイニーシンク) 繰り返し予約の予約名に日付をつけられるようにした\r
+ ・(タイニーシンク) デバッグ(詳細)ログの出力を制御できるようにした\r
+ ・(タイニーシンク) 予約リストの出力を次開始日時順にソートするようにした\r
+ ・(タイニーシンク) システムトレイアイコンメニューに「ログファイルを開く」を追加した\r
+ ・(リモコンっぽいもの) 簡単ダビングボタンを追加した\r
+\r
+2.8.2β+0.1.4β\r
+■変更点\r
+ ・(タイニーシンク) 24Hごとに一回または起動時に、RDと未同期の予約エントリがないか確認し、滞留していればアイコン点滅で警告するようにした\r
+\r
+\r
+\r
+★タイニーシンク0.1.3βから、RDからのSMTP通知が必須になりました。wikiページを参照して設定変更してください★\r
+\r
+2.8.2β+0.1.3β\r
+■バグ修正\r
+ ・(タイニーシンク) 本体で予約した繰り返しエントリの予約IDを見失うことがある問題を修正\r
+ ・(タイニーシンク) 電源OFF時のPOP3アクセスで、アクセスされる度に同じエントリを再度予約してしまう問題を修正\r
+           ※POPと対で来るSMTPから予約IDの通知を使って情報の同期を行うように変更しました\r
+■既知の問題\r
+ ・(Mailプラグイン/タイニーシンク) 同じデータを送っているのに、処理される時と無視される時がある。原因不明… \r
+           → 重複リクエストと判定すると受け付けないらしい?無視されたら捨てるしかないかも\r
+\r
+2.8.2β+0.1.2β\r
+■変更点\r
+ ・(タイニーシンク) RDがリクエストを無視した時に処理を中断し、システムトレイアイコンまたはタイトルバーアイコンを点滅させて警告するようにした\r
+ ・(タイニーシンク) 多重起動防止(というほど素敵なものじゃないが)を追加\r
+ ・(タイニーシンク+RD-S1004Kプラグイン) チャンネルコントローラー機能を実装\r
+■バグ修正\r
+ ・(Mailプラグイン/タイニーシンク) 鯛ナビからの予約実行ON/OFFの情報が予約に反映されていなかった問題を修正\r
+ ・(タイニーシンク) RDからの予約実行ON/OFFの情報が予約リスト取得結果に反映されていなかった問題を修正\r
+ ・(タイニーシンク) 鯛ナビから操作を行わないと繰り返し予約の日付リスト再構成が動作していなかった問題を修正\r
+■既知の問題\r
+ ・(Mailプラグイン/タイニーシンク) 同じデータを送っているのに、処理される時と無視される時がある。原因不明… \r
+\r
+2.8.1β\r
+■変更点\r
+ ・(タイニーシンク) システムトレイに収納できるようにした\r
+ ・(タイニーシンク) SMTPでRDからの処理結果を受け取れるようにした(受け取れるだけで、その情報を元に処理を行うとかは現段階ではなし)\r
+ ・(RD-S304Kプラグイン) 番組追跡の判別条件を2.6βの水準まで戻した\r
+■バグ修正\r
+ ・(タイニーシンク) 本体で登録した繰り返し予約を誤認識しないように修正した\r
+ ・(タイニーシンク) UTF-8→ISO2022JP変換で一部文字が化ける問題を修正した\r
+\r
+2.8β\r
+■変更点\r
+ ・(タイニーシンク) (主に)RD-X9/S1004K/S304K向けの予約操作支援ツール追加。まだまだα版\r
+ ・(RD-S1004Kプラグイン) 追加。タイニーシンクと組みで利用します\r
+ ・(Mail(RD)プラグイン) 記録先デバイスの選択を追加(>>731.)\r
+■バグ修正\r
+ ・(RD-X8/S600プラグイン) 新規予約エントリの取得方法に問題があったのを修正\r
+ ・(その他) CalendarやMailのエントリに対してもwakeupリクエストを飛ばしていたのを修正\r
+ ・(Mail(RD)プラグイン) TS1・TS2選択時に音質設定がされていると予約がはねられる問題を修正(>>731.)\r
+ ・(Mail(RD)プラグイン) TS2選択時にマジックチャプターをOFFにしていないと予約がはねられる問題を修正\r
+ ・(Mail(RD)プラグイン) 一部文字が化ける(UTF-8→ISO-2022-JP)問題を修正\r
+ ・(Mail(RD)プラグイン) リロード実行のたびに各種コンボボックスが長くなっていく問題を修正\r
+\r
+2.7β\r
+■変更点\r
+ ・(その他) 起動時・番組欄へのジャンプ時、選択されているツリーのエントリが選択表示されるように修正(>>721.)\r
+ ・(TvRockプラグイン) TvRockからのレスポンス受信中に、コネクションがリセットされてしまう問題を暫定対処(その2)\r
+ ・(各種設定) 更新確定時レコ情報再取得(キャッシュ優先)を行うように修正\r
+ ・(その他) 右クリックメニューの汎用アイテムのデフォルトコマンドに「ブラウザで番組詳細のページを開く」を追加\r
+   (※webザテレビジョン系は対象外←jsのpopupのうえ番組表情の情報と差がないので)\r
+   (※2.6β→2.7β移行の場合は都合により表示されません。一度実行アイテムをすべて削除してから再起動してください)\r
+ ・(いくつかのWeb番組表プラグイン) 進捗状況をわかりやすくした(何リク目/最大リク数、みたいな?)\r
+ ・(タイマー) - future use -\r
+■バグ修正\r
+ ・(その他) 各種設定>類似予約がある場合は情報を引き継ぐ>OFF時に、類似予約があるエントリで予約ダイアログを開くと、レコーダのエンコーダ情報等が初期設定されない問題を修正(>>701.)\r
+ ・(予約ダイアログ) 類似予約の存在しないレコーダを選択しても更新ボタンが有効のままだった問題を修正\r
+\r
+2.6β\r
+■変更点\r
+ ・(TvRockプラグイン) TvRockからのレスポンス受信中に、コネクションがリセットされてしまう問題を暫定対処\r
+ ・(その他) 設定タブ群の表示/非表示を設定できるようにした\r
+ ・(その他) ツールバーが狭くなってきたのでアイコンを利用して横幅を狭めた\r
+ ・(その他) 右クリックメニューの「googleで検索」を汎用アイテム化\r
+ ・(タイマー) Web番組表キャッシュ期限になったらリロードを自動実行 ←テスト実装。特定時間に特定動作とかのほうがいいかな…\r
+■バグ修正\r
+ ・(RDアナ系プラグイン) 予約新規登録時、CS放送局名が予約リストに反映されない問題を修正(>>701.)\r
+ ・(RDアナ系プラグイン) 予約詳細情報をurlエンコードせずにレコーダに送りつけていた問題を修正\r
+ ・(その他) レコーダからの情報取得部分を修正した際、改行コードを考慮せずにいたため既存のレコーダで情報取得ができなくなったりしていた問題を修正(>>702.)\r
+ ・(その他) 未来日の番組情報がまだ存在しない場合に、存在しない情報を参照にいって例外を発生させていた問題を修正\r
+ ・(その他) 一週間より先の単日予約情報が本体から取得できない(というか捨てていた)問題を修正\r
+\r
+2.5β\r
+■変更点\r
+ ・(その他) 予約マークを表示したりレコ情報を取得したり電源ON/OFFしたりするレコーダを選択できるようにした\r
+ ・(本体予約一覧) 番組追跡マーク予定地を設置。S304Kプラグインのみ実装中\r
+■バグ修正\r
+ ・(キーワード検索) 条件が一個もない検索アイテムが登録できてしまう問題を修正\r
+■既知の問題\r
+ ・(RD系(番組追跡)) 番組追跡で日付移動しても、ネットdeナビ上からは認識できない問題が発覚(>>628.)\r
+\r
+\r
+\r
+★TvRock対応の操作手順はこちらへ→http://sourceforge.jp/projects/tainavi/wiki/TvRock対応\r
+\r
+\r
+\r
+2.4.2β\r
+■変更点\r
+ ・(TvRockプラグイン) コンピュータ名や待機時間などのデフォルト値をTvRockから取得して設定するようにした(※hostsの変更が不要になりました)\r
+\r
+2.4.1β\r
+■バグ修正\r
+ ・(TvRockプラグイン) 新規登録時の予約IDの取得に失敗していた問題を修正(※一度レコ情報再取得を行ってリセットしてください)\r
+\r
+2.4β\r
+■変更点\r
+ ・(その他) Webアクセスの状況表示ウィンドウを追加\r
+ ・(その他) -nオプション廃止\r
+ ・(その他) 29時をまたいで同タイトルが続いている場合は同一番組とみなして終了時刻を延長する機能を実装\r
+ ・(その他) beep音の有効無効を設定できるようにした(>>677.暫定)\r
+ ・(各種設定) レコーダIP&PORTにOSのファイル名禁止文字(\/:*?"<>|)を設定できないようにした\r
+ ・(本体予約一覧) 予約エントリの右クリックメニューに、新聞形式へのジャンプを追加\r
+ ・(SkyperfecTV(詳細+)プラグイン) 追加。1局×7回リクエストを発行するので重いです\r
+ ・(SkyperfecTV(詳細++)プラグイン) 追加。1局×1回リクエスト+1番組×1回リクエストを発行するので超重いです\r
+ ・(TvRockプラグイン) 追加\r
+■バグ修正\r
+ ・(その他) MACアドレスに0x80以上の値が入っていた場合にNumberFormatExceptionが発生していた問題を修正\r
+ ・(RD-S600プラグイン) 予約一覧のエンコーダと音質設定が取得できなかった問題を修正(>>674.)\r
+\r
+2.3β\r
+■変更点\r
+ ・(新聞形式) タイムバーの幅を細くした(>>641.)\r
+ ・(新聞形式) チューナ・エンコーダ表示を白抜き文字にした\r
+ ・(予約ダイアログ) 「実行」チェックボックスを追加した(主にRD向け)\r
+ ・(その他) 「実行=OFF」の場合予約マークが表示されないようにした(>>642.)(デフォルトでは表示されるようにいなっているので設定変更してください)\r
+ ・(その他) チューナ・エンコーダ別色分けを実装(最大4つまで)\r
+ ・(その他) 初期起動時、画面中央に表示されるようにした\r
+■バグ修正\r
+ ・(その他) 「各種設定」タブ「更新確定」ボタン押下後、レコーダにアクセスできなくなる問題を修正(再起動すればアクセスできる)\r
+ ・(その他) 1番目の放送局に対してのジャンプが失敗する問題を修正(ジャンプ対象の局名が有効でないと判断される)\r
+ ・(新聞形式) フルスクリーンモードでツリーのスクロールバーをドラッグしようとするとツリーが隠れてしまって操作できない問題を修正\r
+ ・(各種設定) 画面幅が1024px未満の場合、設定画面が見切れてしまう問題を修正\r
+ ・(各種設定) Java for Linuxで、JPasswordFieldが入力不可になってしまう問題を暫定的に対処\r
+ ・(Googleカレンダー連携) RedirectRequiredExceptionを拾うようにした\r
+\r
+2.2β\r
+■変更点\r
+ ・(新聞形式) 重複予約時に、チューナ/エンコーダマークが右下で重なって見えずらくなる問題に対処(>>638.)(※1)\r
+ ・(Gガイド.テレビ王国プラグイン) Webサイトの情報形式の変更に対応(>>639.)(※2)\r
+ ・(右クリックメニュー) 番組名+番組詳細メニューを復活\r
+ ・(その他) 警告類表示時に、beep発音を追加(一部例外あり)\r
+■バグ修正\r
+ ・(各種設定) 一部のスライダーで設定した値が記録されない(ry\r
+ ・(番組追跡) 同じ名前のエントリが複数登録できてしまう問題を修正\r
+ ・(新聞形式) 日付指定を選択した後、ツールバーから「現在日時」にジャンプして、さらに元の日付を選択すると新聞形式が更新されない問題を修正\r
+■補足\r
+ (※1) レコーダ優先度順に、右下→左下→右上(ここで打ち止め。4つ目からはずっと右上のまま)にマーク表示\r
+ (※2) env/area.Gガイド.テレビ王国.xml、env/center.Gガイド.テレビ王国.*.xmlが存在している場合は\r
+     それらを削除してから起動してください。\r
+\r
+2.1.2β\r
+■変更点\r
+ ・(その他) 番組情報のコピーの形式を変更(>>634-1.)\r
+■バグ修正\r
+ ・(各種設定) 一部のスライダーで設定した値が記録されない問題を修正\r
+\r
+2.1.1β\r
+■変更点\r
+ ・(各種設定) スポーツ延長の延長時間の下限を0に変更(>>629-3.)\r
+■バグ修正\r
+ ・(各種設定) Proxy設定を行ってない環境で2.0→2.1βバージョンアップを行うと鯛ナビが起動しない問題を修正(>>629-1.)\r
+\r
+2.1β\r
+■変更点\r
+ ・(各種設定) パスワード欄を隠すようにした。設定ファイル中のパスワード項目も暗号化するようにした(※1)\r
+ ・(各種設定) レコーダの色設定枠を追加した\r
+ ・(各種設定) レコーダ種別にMailまたはCalendarを選択した場合、入力項目説明がそれにあった表示になるようにした\r
+ ・(各種設定) 数値設定項目を極力スライドバーに置き換えた\r
+ ・(番組追跡) あいまい検索のマッチ条件を見直し、見つからないから閾値を緩めるのではなく、引っかかりすぎるのを閾値で絞るというポリシーに変更(>>612-4.)\r
+ ・(Googleカレンダー連携) 更新・削除機能を追加\r
+ ・(予約ダイアログ) 2.0でこっそり変更した自動フォルダ設定のCaseSensitveを元に戻した(>>619-1.)\r
+ ・(予約ダイアログ) 録画日時のリセットボタンを追加(>>619-4.)\r
+ ・(予約ダイアログ) 類似予約情報の引き継ぎ方法を見直した(※2)\r
+ ・(リスト形式) リスト形式からも削除を行えるようにした\r
+ ・(クリップボード) コピー内容を拡充+選択、並べ替えが出来るようにした(>>625-1.)\r
+■バグ修正\r
+ ・(新聞形式) リスト形式からのジャンプ時、表示領域外を指定することがあった問題を修正(>>619-2.)\r
+ ・(しょぼかる連携) 日またがりデータ(24:00~28:59)の取り扱いを考慮していなかった問題を修正(>>621-1.)\r
+■補足\r
+(※1)暗号強度は平文よりはまし、という程度なので注意のこと。マスターパスワードでも設定すればいいのですが起動のたびに入力なんて面倒だし。\r
+    なお、「更新を確定する」ボタンを押下すると自動的に平文→暗号文に変換されるので、できる限り対応をお願いします。\r
+(※2)「各種設定」→「類似予約がある場合は情報を引き継ぐ」がOFFの場合:コンボボックスで類似予約を選択するまで更新不許可。新規予約を想定。\r
+    ONの場合:自動で類似予約の情報を引き継ぎ、更新もそのまま実行可能。更新実行を想定。\r
+    あと、この変更に伴い、過去に廃止した類似予約の予約名を優先させる件を再度復活させた。\r
+■既知の問題\r
+ ・(キャッシュファイル) キャッシュファイルが壊れていると鯛ナビが起動しない\r
+\r
+2.0\r
+■変更点\r
+ ・(新聞形式・その他) フルスクリーンモードを追加した(※1)(>>587-1.)\r
+ ・(新聞形式) ページング機能を追加した\r
+ ・(予約ダイアログ) 開始・終了時間を1分ずつ前後に増減させるボタンを追加した(>>600-2.)\r
+ ・(キーワード検索) 大小全半かなカナ空白文字ー同一視をON/OFF出来るようにした(既定値はON)(>>mail)\r
+ ・(番組追跡) あいまい検索時のあいまい度の閾値を個別に設定できるようにした(>>612-4.)\r
+ ・(しょぼかる連携) キーワード検索に対応した(>>612-5.)\r
+ ・(Googleカレンダー連携) Googleカレンダー連携機能を追加\r
+■バグ修正\r
+ ・(その他) ダイアログのサイズにタイトルバーの高さが考慮されていなかった問題を修正(>>600-1.)\r
+ ・(予約ダイアログ) AV自動設定が行われると、自動フォルダ選択が無効になる問題を修正(>>612-1.)\r
+ ・(キーワード検索) ジャンル指定条件を削除する際、削除したジャンルしかコンボボックスに再登録していなかった問題を修正(>>612-2.)\r
+ ・(キーワード検索) ジャンル指定での絞り込み処理が不正だった問題を修正(>>612-3.)\r
+ ・(番組追跡) あいまい度算出式が番ナビに準拠していなかった問題を修正(※2)\r
+■補足\r
+ (※1)フルスクリーン/ウィンドウモードはツールバーの全/窓ボタンで切り替えます。\r
+     新聞形式でフルスクリーン時、通常はツリーが隠れていますがタイムバーの上にマウスポインタを持っていくとひょっこり出てきます。\r
+ (※2)この修正により検索結果に変化があるかもしれません。ご注意願います。\r
+     また、リスト形式の「スコア」欄にその番組のあいまい度スコアを記載していますので、設定の参考にしてください。\r
+\r
+1.49.3β\r
+■変更点\r
+ ・(VARDIA RD-S304Kプラグイン) 追加(予約確認が出来る機能しかありません…)\r
+■バグ修正\r
+ ・(VARDIA RD-X8プラグイン) 本体予約情報取得で、音質設定を取得していなかった問題を修正\r
+\r
+1.49.2β\r
+■バグ修正\r
+ ・(本体予約一覧) 重複マークが正しく表示されない問題を修正\r
+\r
+1.49.1β\r
+■変更点\r
+ ・(web番組表プラグイン) ナローバンド対策としてリードタイムアウト値を10秒→30秒に延長\r
+ ・(web番組表プラグイン) HTTPプロキシの設定を追加\r
+■バグ修正\r
+ ・(新聞形式) 番組詳細にフォント指定が反映されていなかった問題を修正\r
+ ・(SkyperfecTVプラグイン) 終了日時(内部形式)が正しくなかった問題を修正\r
+\r
+1.49β\r
+■変更点\r
+ ・(リスト形式・本体予約一覧) 右クリックした時に選択アイテムにフォーカスを移動するように変更\r
+ ・(予約ダイアログ) 番組詳細にスクロールバーを追加\r
+ ・(キーワード検索) チャンネル名での検索に大小全角半角同一視を導入\r
+ ・(CHコード設定) 確定ボタン押下後、確定結果をテーブルに反映するようにした\r
+ ・(各種設定) レコーダ登録後の入力欄をクリアするようにした\r
+ ・(その他) jTableで列の移動が可能だったのを禁止\r
+■バグ修正\r
+ ・(iEPG/iEPGデジタルプラグイン) 拡張子.tvpi/.tvpidに関連付けが行われていなかった場合に発生していた例外を無視していた問題を修正\r
+ ・(予約ダイアログ) 類似予約がないのに更新ボタンがenableになっていた問題を修正\r
+ ・(CHコード設定) なぜかレコーダコンボボックスがEditableになっていたのを修正\r
+ ・(リスト形式) 終了済みエントリを表示しない設定がうまく動作していなかった問題を修正\r
+ ・(RD-Styleプラグイン) 終了日時(内部形式)が正しくなかった問題を修正\r
+ ・(Gガイド.テレビ王国プラグイン) タイトル分割ルールがwebザテレビジョンプラグインと異なっていたのを修正\r
+\r
+1.48β\r
+■変更点\r
+ ・(リスト形式)選択行から番組欄へのジャンプメニューを追加\r
+ ・(予約ダイアログ) 類似候補が複数存在する場合、更新対象をコンボボックスで選べるようにした\r
+ ・(予約ダイアログ) 放送局別画質・音質自動設定を追加\r
+■バグ修正\r
+ ・(RD-Styleプラグイン) キャッシュファイルの不整合問題を修正\r
+ ・(Gガイド.テレビ王国プラグイン) キャッシュファイルの不整合問題を修正\r
+ ・(@nifty TV番組表プラグイン) キャッシュファイルの不整合問題を修正\r
+ ・(新聞形式) ツリーに過去日が表示されないようにした\r
+ ・(各種設定) 新規レコーダ追加確定後、レコ情報を自動取得していないのでアレコレだけど、\r
+   確定押下のたびにレコ情報再取得をやってるとうざいので警告メッセージ表示にとどめる\r
+ ・(SkyperfecTVプラグイン) 番組詳細が検索対象から外れていた問題を修正\r
+ ・(リスト形式)ジャンル選択・サブジャンル選択するとリストがもりもり増殖していく問題を修正\r
+ ・(RD-XS41/X5/X5EXプラグイン) 更新・削除時のABC問題が修正されきっていなかった問題を修正\r
+ ・(RD-S600/X8プラグイン) 外部入力での予約が正しく動作しない問題を修正\r
+ ・(本体予約一覧・レコーダプラグイン各種) 予約情報再取得時の放送局名引継ぎが\r
+   外部入力以外にも適用されていて不正な動作を引き起こしていた問題を修正\r
+ ・(予約ダイアログ) レコーダー切り替え時、自動フォルダ設定が機能していなかった問題を修正\r
+\r
+1.47β\r
+■変更点\r
+ ・(予約一覧・RD系レコプラグイン) 予約実行ON/OFFの取得に対応\r
+\r
+1.46β\r
+■変更点\r
+ ・(番組追跡・キーワード検索) 「じ,ぢ」「ず,づ」「半角カナ,全角カナ」「○数字,数字」を同一視するようにした\r
+■バグ修正\r
+ ・(キーワード検索) 正規表現の文法に則っていない例外をキャッチしていない問題を修正\r
+ ・(各種設定) web番組表データキャッシュの有効期限の調整が反映されていなかった問題を修正\r
+    +設定=0の時は起動時のweb番組表確認をスルーするように変更\r
+ ・(リスト形式) 一括予約が動作しなくなっていた問題を修正\r
+ ・(レコ情報再取得) メッセージがおかしくなっていたのを修正\r
+\r
+1.45.1β\r
+■バグ修正\r
+ ・(キーワード検索・web番組表プラグイン各種) アルファベット大小文字や半角全角の同一視が働いていなかった問題を修正\r
+    →番組内容の検索についての実装漏れがあったのを修正\r
+ ・(キーワード検索・延長警告管理) キーワード登録時に放送局名が不正になる問題を修正\r
+\r
+1.45β\r
+■変更点\r
+ ・(RD-Styleプラグイン) 判別できた範囲でサブジャンルを追加\r
+ ・(番組追跡・キーワード検索) 「アラビア数字,漢数字,ローマ数字」、長音、スペースの有無を同一視するようにした\r
+ ・(リスト) 番組追跡の編集ダイアログを追加\r
+■バグ修正\r
+ ・(番組追跡・キーワード検索) アルファベット大小文字や半角全角の同一視が働いていなかった問題を修正\r
+■既知の問題\r
+ ・なんらかのタイミングで番組情報と予約情報が一日ずれる。番組情報を再取得すると直る\r
+\r
+1.44β\r
+■変更点\r
+ ・(リスト・新聞形式) ツリーのノードの展開状態を保存するようにした\r
+ ・(キーワード検索) 右クリックメニューにお気に入り度変更を追加\r
+ ・(web番組表プラグイン各種・キーワード検索) 検索条件に初回放送を追加\r
+    ※適当な入力データがなかったため、RD-Style以外は稼動試験できていませんm(_ _)m\r
+ ・(Gガイド.テレビ王国プラグイン) サブジャンル取得に対応\r
+ ・(iEPGデジタルプラグイン) ジャンル・サブジャンルの出力に対応\r
+ ・(ツールバー検索) 空白区切りのAND条件検索に対応\r
+■補足\r
+ ジャンル分け詳細化の影響で、ジャンルに紐付けしていた録画レート・音質等設定が\r
+ 旧バージョンから引継ぎできなくなっていました。移行時の考慮漏れです、申し訳ありません。\r
+ 再登録お願いします。\r
+\r
+1.43β\r
+■変更点\r
+ ・(予約待機・番組追跡・キーワード検索) お気に入り度を実装\r
+ ・(予約待機・番組追跡・キーワード検索) 検索条件にジャンル・新番組・最終回・再放送を追加\r
+ ・(webザテレビジョンプラグイン) ジャンル分類の充実に対応\r
+ ・(Gガイド.テレビ王国プラグイン) ジャンル分類の充実に対応\r
+ ・(RD-Styleプラグイン) ジャンル分類の充実に対応\r
+ ・(RD系レコプラグイン) ジャンル分類の充実に対応\r
+ ・(Gガイド.テレビ王国プラグイン) 最終回抽出の条件を追加\r
+ ・(リスト形式) キーワード編集後にノードの順番が入れ替わらないようにした\r
+ ・(リスト形式) ツリーのノードの展開状態を記憶するようにした\r
+ ・(新聞形式) ツリーのノードの展開状態を記憶するようにした\r
+ ・(その他) 「番組情報がありません」でも無駄に予約ダイアログが開いていたのを止めた\r
+ ・(リスト形式) キーワード検索を少しだけ高速化\r
+\r
+1.42β\r
+■変更点\r
+ ・(CH設定) 「全国」対応にあわせて放送局欄の隣にエリア欄を追加\r
+ ・(CH設定) 地上波&BSのweb番組表既定値を「webザテレビジョン」にした\r
+ ・(各種設定) web番組表データキャッシュの有効期限を調整できるようにした\r
+■バグ修正\r
+ ・(@nifty TV番組表プラグイン) エリア「全国」の扱いに問題があったのを修正\r
+ ・(webザテレビジョンプラグイン) 放送エリアと放送局の関連付けに問題があったのを修正\r
+ ・(webザテレビジョンプラグイン) BSの扱いに問題があったのを修正\r
+ ・(Gガイド.テレビ王国プラグイン) 放送エリアと放送局の関連付けに問題があったのを修正\r
+ ・(Gガイド.テレビ王国プラグイン) BSの扱いに問題があったのを修正\r
+ ・(SkyperfecTVプラグイン) 放送局名エイリアス未対応だった問題を修正\r
+ ・(リスト形式) ジャンル別表示で終了済みエントリの表示/非表示コントロールが聞かなかった問題を修正\r
+■お願い\r
+  ※最近ずっとこればっかりですが、今回もweb番組表プラグインに手を入れまくったので…\r
+\r
+   「env/area.*.xml・env/center.*.xmlを削除して再起動し、CH設定を再設定お願いしますm(_ _)m」\r
+\r
+  とりあえずひと段落ついたので、このお願いがでるのは当分ないでしょう(o´Д`)=з\r
+\r
+1.41β\r
+■変更点\r
+ ・(Gガイド.テレビ王国プラグイン) 最終回抽出の条件を追加\r
+ ・(リスト形式) 終了済みエントリの表示/非表示を選択できるようにした\r
+\r
+1.40β\r
+■変更点\r
+ ・(Gガイド.テレビ王国プラグイン) 追加\r
+ ・(新聞形式) 【新】【終】などを強調表示するようにした\r
+■バグ修正\r
+ ・(@nifty TV番組表プラグイン) エリア「全国」を選択すると動作不良を起こす問題を修正\r
+  ※env/area.*.xml・env/center.*.xmlを削除して再起動し、CH設定を再設定お願いしますm(_ _)m\r
+\r
+1.39β\r
+■変更点\r
+ ・(webザテレビジョン(CS)プラグイン) Yahoo!(CS)の代わりに追加しました。アナとデジがあるんですが私はすかぱー使ってないんで違いがよくわかりませんです\r
+ ・(@nifty TV番組表プラグイン) 大幅リニューアル(内部的に:-P)\r
+  ※RD-Styleプラグイン同様「NHK総合」「NHK教育」はそれぞれ「NHK総合・エリア名」「NHK教育・エリア名」になります。ご注意を。\r
+  ※env/area.*.xml・env/center.*.xmlを削除して再起動し、CH設定を再設定お願いしますm(_ _)m\r
+■バグ修正\r
+ ・(RD-Styleプラグイン) エリア「全国」を選択すると動作不良を起こす問題を修正\r
+ ・(新聞形式) 右クリックメニューで削除候補になるはずのものが表示されない問題を修正\r
+ ・(リスト形式) 「web番組表を変更」した後「リストを更新」すると表示がグズグズに崩れる問題を修正\r
+ ・(新聞形式) 「web番組表を変更」した後「番組枠をマウスオーバー」すると上部詳細表示が不一致になりまくる(内部的にはグズグズに崩れている)問題を修正\r
+\r
+1.38β\r
+■バグ修正\r
+ ・(新聞形式) 放送局別表示で28時以降のスペースが表示され時刻バーが崩れる問題を修正\r
+ ・(webザテレビジョンプラグイン) 開始時刻が5:00以前のエントリの予約ダイアログで、終了時刻が正しく表示されなかった問題を修正\r
+ ・(RD-Styleプラグイン) 開始時刻が5:00以前のエントリの予約ダイアログで、終了時刻が正しく表示されなかった問題を修正\r
+ ・(RD-Styleプラグイン) 終了時刻が29:00以降のエントリの予約ダイアログで、終了時刻が正しく拾えなかった問題を修正\r
+   ※番組詳細に「(n・nn終了」と記載のあるもののみが対象です\r
+\r
+1.37β\r
+■バグ修正\r
+ ・(webザテレビジョンプラグイン) ザテレビジョンで予約が正常に出来ない場合がある。発生条件は、先頭番組が4:00以前の場合にそのCH固定で発生\r
+  先頭~24:00開始の予約が録画設定画面の時点で日付が1日加算されている。の問題を修正\r
+ ・(本体) env/area.*.xmlを削除した状態で起動すると、番組表が真っ白になってしまう問題を修正\r
+  ※すでに現象が発生している方にはお手数ですが、env/area.*.xml・env/center.*.xmlを削除\r
+   して再起動し、CH設定を再設定お願いしますm(_ _)m\r
+\r
+1.36β\r
+■変更点\r
+ ・(新聞形式) 再び簡素表示をカット\r
+■バグ修正\r
+ ・(各レコーダープラグイン) 更新実行後、更新内容がキャッシュファイルに反映されていなかった問題を修正\r
+ ・(XS41/X5/X5EXプラグイン) 削除・更新の対象が、「タイトル」「チャンネル」「予約パターン」の3項目が一致するものに変更になりました\r
+\r
+1.35β\r
+■変更点\r
+ ・(新聞形式) 詳細表示モードのレンダリング速度を向上\r
+ ・(webザテレビジョンプラグイン) 新番組マーク・最終回マークがタイトルや番組詳細に隠れているパターンがあったので見つけた範囲で対応\r
+■バグ修正\r
+ ・(RD-X8以外のレコーダープラグイン) 本体からの予約一覧取得時に、検索用インデックスを作成していないため類似予約検出・削除対象検出が行われなかった問題を修正\r
+\r
+1.34β\r
+■変更点\r
+ ・(webザテレビジョンプラグイン・RD-Styleプラグイン) エリア設定に「全国」を追加、複数エリアの放送局を一括管理できるようにした\r
+  ※変更を反映するためenv/area.*.xmlを削除してから再起動してください\r
+  ※本対応に伴い、RD-Styleプラグインの「NHK総合」「NHK教育」はそれぞれ「NHK総合・エリア名」「NHK教育・エリア名」になります。ご注意を。\r
+  ※@niftyプラグインは対応する予定がありません。悪しからずm(_ _)m\r
+ ・(webザテレビジョンプラグイン) タイトル分割のルールを追加\r
+\r
+1.33β\r
+■変更点\r
+ ・(webザテレビジョンプラグイン) タイトル本文と番組詳細がごっちゃになって設定されているものを極力タイトルと番組詳細に分離するようにした\r
+■バグ修正\r
+ ・(webザテレビジョンプラグイン) 番組詳細にIMGタグが含まれている場合に表示が不正になる問題を修正\r
+ ・(webザテレビジョンプラグイン) 予約ダイアログで日付が不正な値を示す問題を修正\r
+ ・(iEPG/iEPG2プラグイン) 予約の履歴管理が出来ていなかった問題を修正\r
+ ・(新聞形式) 更新を行うと元の枠が消去されずに残ってしまう問題を修正\r
+ ・(新聞形式・web番組表プラグイン各種) 当日28:00以降の番組予約枠が翌日4:00のスペースに描画される問題を修正\r
+  なお、この問題への対応に伴い取得時間帯が他と番組表と異なるYahoo!(CS)プラグインは廃止します。代替プラグインの予定は未定。\r
+\r
+1.32β\r
+■変更点\r
+ ・(予約ダイアログ) DVD互換モードの設定をジャンルごとに登録できるようにした\r
+■バグ修正\r
+ ・(webザテレビジョンプラグイン) 北海道が選択できない問題を修正\r
+ ・(webザテレビジョンプラグイン) 未選択局のデータも無駄にダウンロードしていたのを修正\r
+ ・(webザテレビジョンプラグイン) 日付表示が間違って表示されてしまう問題の修正\r
+ ・(web番組表プラグイン各種) 「放送局選択→終了→channelNameAlias.dat登録→再起動」すると選択局が表示されなくなってしまう問題を修正\r
+\r
+1.31.3β\r
+■バグ修正\r
+ ・(新聞形式) 簡素表示復活リクエストに反応\r
+\r
+1.31.2β\r
+■バグ修正\r
+ ・(しょぼかるプラグイン) 微妙なバグを修正、差し替えました\r
+\r
+1.31.1β\r
+■バグ修正\r
+ ・(web番組表プラグイン各種) 微妙なバグを修正、差し替えました\r
+\r
+1.31β\r
+■変更点\r
+ ・(webザテレビジョンプラグイン) データの取得元をアナログ版からデジタル版に変更した\r
+ ・(新聞形式) 詳細表示版で現実的描画速度を実現できたので、簡素モード&詳細チップ表示を廃止\r
+ ・(web番組表プラグイン各種) 放送局名エイリアス設定の追加(*1)\r
+ ・(しょぼいカレンダープラグイン) ↑に機能統合したのでsyobocal.datは発展的解消\r
+ ・(その他) 設定変更時の再起動を不要にした\r
+■バグ修正\r
+ ・(webザテレビジョンプラグイン) エリア=「東京」以外でデータが取得できなかった問題を修正\r
+■その他\r
+ (*1)放送局名表示は番組表サイトごとに少しずつ違っています(ex.ちばテレビ、チバテレビ…)。\r
+  なのでサイト切り替えを行うと番組追跡や予約実行に支障がでてしまいます。\r
+  そこで、各サイトが表示する放送局名を別の名前に置き換えるルールを設定できるようにしました。\r
+  ファイルは env/channelNameAlias.dat で、手作業での更新が必要になります。\r
+  リリース物件には、サンプルとして@niftyとwebザテレビジョンをRD-Style(東京版)の放送局名に\r
+  置き換える定義例を添付しておきますのでご参照ください。\r
+\r
+  あともう一件、しょぼいカレンダープラグインで使っていた env/syobocal.dat は機能が重複している\r
+  ので env/channelNameAlias.dat に吸収しました。ご了承ください。\r
+\r
+\r
+1.30β\r
+■変更点\r
+ ・(iEPGデジタルプラグイン) 追加(拡張子.tvpidに予約アプリなどを関連付けてください)\r
+ ・(iEPGプラグイン) 予約履歴を残すようにした\r
+■バグ修正\r
+ ・(新聞形式) 表上から削除操作しても予約枠が残ってしまう問題の修正\r
+\r
+1.29β\r
+■変更点\r
+ ・(新聞形式) 猫の手スクロールを実装できた\r
+ ・(webザテレビジョンプラグイン) 追加\r
+ ・(HDDレコーダプラグイン) 予約・更新時にRdChannelCode.dat未設定だった場合、その旨明示するようにした\r
+■バグ修正\r
+ ・(予約ダイアログ) 「更新」処理が動作しなくなっていた問題を修正\r
+\r
+1.28β\r
+■変更点\r
+ ・(DIGA DMR-BW200プラグイン) 追加\r
+ ・(新聞形式) 番組欄の高さを変更できるようにした\r
+ ・(新聞形式) 新聞形式から予約削除できるようにした\r
+■バグ修正\r
+ ・(DIGA DMR-XW120プラグイン) 次回実行日付の表示が1970/1/1固定になっていた問題を修正\r
+ ・(DIGA DMR-XW120プラグイン) 新聞形式表示時、繰り返し予約の表示に不具合があったのを修正\r
+■その他\r
+ ・(メール予約プラグイン) Hotmailでの動作を確認しました\r
+\r
+1.27β\r
+■変更点\r
+ ・(メール予約プラグイン) @niftyやBIGLOBEからのメール送信に対応\r
+ ・(メール予約プラグイン) 識別子を"SMTPサーバ:SMTPポート:RD(Mail)"から"MAIL:送信先メアド:RD(Mail)"に変更\r
+               (複数レコーダ→複数送信先メアドがある場合を想定しての変更です)\r
+\r
+1.26β\r
+■変更点\r
+ ・(RD系プラグイン) DVD互換モードの設定を追加\r
+ ・(メール予約プラグイン) メール予約機能を追加\r
+■バグ修正\r
+ ・(RD-X5/X5EX/XS41プラグイン) ジャンル設定が反映されない問題を修正\r
+ ・(iEPGプラグイン) 予約日付が1970/1/1固定になっていた問題を修正\r
+\r
+1.25β\r
+■バグ修正\r
+ ・(@nifty TV番組表プラグイン) 現在日付より過去の番組表が、現在年でなく一年先と認識されてしまう問題を修正\r
+\r
+1.24β\r
+■変更点\r
+ ・(レコーダプラグイン) 予約登録・更新時にレコーダから返却される警告メッセージをステータスエリアに表示するようにした\r
+\r
+1.23β\r
+■バグ修正\r
+ ・(RD-S600プラグイン) 予約完了時にRDから録画モードの自動変更のメッセージが送られてきた場合を正常終了とみなすように変更\r
+ ・(RD-S600プラグイン) 録画先フォルダ指定のコードがすっぽり抜けていたのを修正\r
+\r
+1.22β\r
+■バグ修正\r
+ ・(@nifty TV番組表プラグイン) 年またぎの処理行われていなかったのを修正\r
+\r
+1.21β\r
+■変更点\r
+ ・(その他) レコーダー、番組表との通信部分にコネクションタイムアウト(10秒)を追加\r
+\r
+■バグ修正\r
+ ・(キーワード検索) 「番組名、内容に」設定の際、番組詳細が検索ターゲットになっていなかった不具合を修正\r
+\r
+1.20β\r
+■変更点\r
+ ・(新聞形式・各種設定) タイムバーの色設定を4つの時間帯(6~11,12~17,18~23,24~5)個別に設定できるようにした\r
+\r
+1.19β\r
+■変更点\r
+ ・(新聞形式) タイムバーの色設定を追加\r
+ ・(その他) 再起動時に前回終了時開いていたタブを開くようにした\r
+ ・(キーワード検索) 「キーワード検索ボタン」を押さなくてもEnterで検索を開始するようにした\r
+\r
+1.18β\r
+■変更点\r
+ ・(各種ダイアログ) キャンセルボタンを追加した\r
+ ・(キーワード検索) 「次のいずれかの条件に一致」の動作を番ナビ相当に変更した\r
+\r
+1.17β\r
+■変更点\r
+ ・(新聞形式) 日付欄の文字短縮、土日の色分けをした\r
+ ・(RDリモコン) ボタン描画がマウスに反応するようにした\r
+■バグ修正\r
+ ・(SkyperfecTVプラグイン) 番組情報がないコマの扱いが不正で、それ以降の番組情報が取得できなかった不具合を修正\r
+\r
+1.16β\r
+■変更点\r
+ ・(色選択ダイアログ) 64色パレットを追加\r
+■バグ修正\r
+ ・(SkyperfecTVプラグイン) 番組長が不正になる不具合の修正(日毎表示と週毎表示で取得される値が異なっていた)\r
+\r
+1.15β\r
+■変更点\r
+ ・(SkyperfecTVプラグイン) Web番組表取得の高速化(局あたり1日×7回取得していたのを1週×1回に修正)\r
+■バグ修正\r
+ ・(その他) 無効局が検索対象になってしまう問題が全然解決していなかった問題を修正\r
+\r
+1.14β\r
+■バグ修正\r
+ ・(その他) バグの取りこぼしを修正\r
+\r
+1.13β\r
+■変更点\r
+ ・(その他) 色選択ダイアログに「決定」ボタンを設置\r
+■バグ修正\r
+ ・(その他) 1.12βでいろいろと盛大にバグを作りこんでいたのを修正\r
+\r
+1.12β\r
+■変更点\r
+ ・(リスト形式、新聞形式) ツリーの幅を変えられるようにした\r
+ ・(CH設定) Web番組表設定を別タブに分離\r
+ ・(CH設定) 放送局の表示順をGUI上で並べ変えられるようにした\r
+ ・(CHコード設定) GUIから編集できるようにした(暫定対応)\r
+ ・(新聞形式) 局名に色をつけられるようにした\r
+ ・(その他) 色選択ダイアログを×ボタンを押すまで閉じないようにした\r
+■補足\r
+ ・CH設定からみの変更に伴い、env/center.*.xmlを削除してから起動&再設定を行ってください\r
+  また、env/channelorder.dat.*は廃止しました\r
+\r
+1.11β\r
+■変更点\r
+ ・(新聞形式) 日付表示の放送局名を左ダブルクリックすると放送局表示に、\r
+  放送局表示の日付を左ダブルクリックすると日付表示に切り替わるようにした\r
+ ・(各種設定) 放送局有効・無効の設定をそれっぽくリニューアル\r
+ ・(その他) タイトルバーにバージョン情報を表示\r
+\r
+1.10β\r
+■変更点\r
+ ・(東芝系プラグイン) すかぱー連動なんかでLine3とか使っちゃってるとレコーダから予約情報の再取得を実行したとき\r
+  放送局名が"外部入力(L3)"とかなって涙目(;_;)みたいな事態を防ぐため、レコーダから予約情報の再取得しても\r
+  放送局名は鯛ナビが持っていた放送局名を継承するよう変更\r
+\r
+1.9β\r
+■バグ修正\r
+ ・(SkyperfecTVプラグイン) 検索や予約ダイアログで番組の日付が一日ずれる問題を修正\r
+\r
+1.8β\r
+■変更点\r
+ ・(予約ダイアログ) レコーダ名、CH名の幅を増やすようレイアウトを変更\r
+ ・(予約ダイアログ) レコーダ選択時に「レコーダ」ラベルの色を設定色で表示するよう変更\r
+■バグ修正\r
+ ・(予約ダイアログほか) 類似予約の日付表示が過去日になっていたのを修正\r
+ ・(予約ダイアログ) iEPGプラグインが動作しなくなっていたのを修正\r
+\r
+1.7β\r
+■変更点\r
+ ・(SkyperfecTVプラグイン) 情報取得方法変更への対応\r
+■バグ修正\r
+ ・(SkyperfecTVプラグイン) てんで機能していなかったのを修正\r
+\r
+1.6β\r
+■変更点\r
+ ・(予約ダイアログ) 予約名を、類似予約の番組名ではなく、番組表の番組名を優先するように変更\r
+ ・(RD-X5/XS41プラグイン) レコーダ情報をキャッシュファイルに持つようにし、鯛ナビ起動時のレコーダチェックは本体を見に行かないよう変更\r
+ ・(RD-X8/S600/X5EXプラグイン) 予約情報0件の場合でも起動時レコーダチェックはキャッシュファイルですますよう変更\r
+\r
+1.5β\r
+■変更点\r
+ ・(新聞形式・予約一覧) 使用エンコーダを表示するようにした(*1)\r
+■バグ修正\r
+ ・(RD-Styleプラグイン) 番組詳細にゴミが残る問題を修正\r
+ ・(RD-Styleプラグイン) ジャンルが正しく認識されない問題を修正\r
+ ・(SkyperfecTVプラグイン) 24:00~28:59の間にデータ取得すると当日分のデータが表示されない問題を修正\r
+ ・(Yahoo!(CSのみ)プラグイン) 24:00~27:59の間にデータ取得するとデータの日付がずれる問題を修正(*2)\r
+■既知の問題点\r
+ ・(*1)レコーダがエンコーダ自動振り替えを行った場合をチェックしていない\r
+    対応するには予約一覧を再取得すればいいのだがそれはそれで問題が…\r
+ ・(*2)Yahoo!が24:00過ぎに過去日のデータを取得できないようにしてしまうため、\r
+    24:00~27:59までの間にデータ取得を行うとデータ抜けが発生する\r
+    対応するには…いいアイデアが浮かばないなぁ(>_<)\r
+\r
+1.4β\r
+■変更点\r
+・(新聞形式)放送局毎の表示幅を各種設定で設定できるようにした\r
+・(新聞形式)「地上波&BS」ノードと「CS」ノードを追加\r
+・(リスト形式・新聞形式)「番組名をコピー」「番組名と詳細をコピー」メニューを追加\r
+\r
+1.3β\r
+■変更点\r
+ ・(おまけ) RDリモコンをでっちあげてみた\r
+■バグ修正\r
+ ・(すかぱー系プラグイン) 番組表の追加・削除で放送局と番組表の組み合わせが不一致になる問題を修正\r
+\r
+1.2β\r
+■変更点\r
+ ・(その他) スプラッシュスクリーンに処理経過を表示するようにした\r
+ ・(リスト形式) 重複マークを本体予約一覧と同じようにした\r
+■バグ修正\r
+ ・(本体予約一覧・Web番組表プラグイン) 日またがりの予約の次回実行予定日時が不正になる問題を修正\r
+ ・(予約待機・レコーダープラグイン) 過去日時のエントリが予約対象になる問題を修正\r
+ ・(新聞形式) 放送局別ツリーがchannelorderを反映していなかった問題を修正\r
+\r
+1.1β\r
+■変更点\r
+ ・(その他) 予約終了時刻が番組表上の終了時刻より早いとき、1分だけ早い場合(▲)と2分以上早い場合(△)をひと目で分かるようにした\r
+ ・(録画予約) 録画予約時に、スポーツ延長と終了時刻短縮をON/OFF選択できるようにした\r
+ ・(新聞形式) 現在日時の現在時刻線を1分毎に更新するようにした\r
+ ・(本体予約一覧) 終了時刻と開始時刻が連続している場合を"□"で表示するようにした\r
+ ・(リスト形式) 予約済みマークにレコーダの色指定を反映\r
+ ・(各種設定・Web番組表プラグイン等) HTTPアクセス時のUser-Agentの値を指定できるようにした\r
+■バグ修正\r
+ ・(本体予約一覧) 削除失敗時のメッセージを修正\r
+ ・(本体予約一覧) 開始時刻と終了時刻が一致するエントリ同士の場合、重複警告が行われない問題を修正\r
+ ・(本体予約一覧) 帯予約の次回実行日時が正しく処理されない問題を修正\r
+ ・(本体予約一覧) 取得した録画モードが"TS"の場合は"[TS]"と表示されるように統一した\r
+ ・(番組追跡) 同タイトル・別放送局のエントリの削除がうまくいかない問題を修正 (タイトルのみで処理を行っていたため)\r
+ ・(すかぱー系プラグイン) 初回放送が「#1・#2」という風に二話連続で放映する場合「新」番組マークが付かないので新番組と認識できなかった問題に対処\r
+\r
+1.0 正式版リリース
\ No newline at end of file
diff --git a/TinyBannavi/TaiNavi for Mac.app/Contents/Info.plist b/TinyBannavi/TaiNavi for Mac.app/Contents/Info.plist
new file mode 100644 (file)
index 0000000..ea3b8a6
--- /dev/null
@@ -0,0 +1,14 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!DOCTYPE plist PUBLIC "-//Apple Computer//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
+<plist version="1.0">
+  <dict>
+    <key>CFBundleExecutable</key>
+    <string>tinybannavi.sh</string>
+    <key>CFBundleIconFile</key>
+    <string>tainavi.icns</string>
+    <key>CFBundlePackageType</key>
+    <string>APPL</string>
+    <key>CFBundleSignature</key>
+    <string>????</string>
+  </dict>
+</plist>
diff --git a/TinyBannavi/TaiNavi for Mac.app/Contents/Resources/tainavi.icns b/TinyBannavi/TaiNavi for Mac.app/Contents/Resources/tainavi.icns
new file mode 100644 (file)
index 0000000..30d1691
Binary files /dev/null and b/TinyBannavi/TaiNavi for Mac.app/Contents/Resources/tainavi.icns differ
diff --git a/TinyBannavi/_lock_ b/TinyBannavi/_lock_
new file mode 100644 (file)
index 0000000..e69de29
diff --git a/TinyBannavi/_update.cmd b/TinyBannavi/_update.cmd
new file mode 100644 (file)
index 0000000..56871fd
--- /dev/null
@@ -0,0 +1,87 @@
+@ECHO OFF\r
+\r
+SET WD=%CD%\r
+\r
+CD %~dp0\r
+\r
+ECHO ...\83A\83b\83v\83f\81[\83g\90æ\82ª\91\8dÝ\82·\82é\82©\8am\94F\82µ\82Ü\82·.\r
+IF NOT EXIST ..\..\bin\tainavi\Viewer.class GOTO ERROREXIT\r
+\r
+@REM JavaRuntime\82ð\83R\83s\81[\r
+IF EXIST jre (\r
+       IF EXIST ..\..\jre6 (\r
+               IF EXIST ..\..\jre6.old\ RD /S /Q ..\..\jre6.old\\r
+               REN jre jre.new\r
+       )\r
+       IF EXIST ..\..\jre (\r
+               IF EXIST ..\..\jre.old\ RD /S /Q ..\..\jre.old\\r
+               REN jre jre.new\r
+       )\r
+)\r
+IF EXIST jre.new (\r
+       ECHO ...JavaRuntime\82ð\83R\83s\81[\82µ\82Ü\82·.\r
+       MOVE /Y jre.new ..\..\\r
+)\r
+\r
+IF EXIST ..\..\calendar\PlugIn_RecGoogleCalendar.class (\r
+       DEL /F ..\..\calendar\PlugIn_Rec*.class\r
+)\r
+\r
+IF EXIST ..\..\javamail\PlugIn_RecRD_MAIL.class (\r
+       DEL /F ..\..\javamail\PlugIn_Rec*.class\r
+)\r
+\r
+IF EXIST bin (\r
+       IF EXIST ..\..\bin.old2\ RD /S /Q ..\..\bin.old2\\r
+       IF EXIST ..\..\bin.new\ RD /S /Q ..\..\bin.new\\r
+       REN bin bin.new\r
+)\r
+IF EXIST bin.new (\r
+       ECHO ...\83o\83C\83i\83\8a\82ð\83R\83s\81[\82µ\82Ü\82·.\r
+       MOVE /Y bin.new ..\..\\r
+)\r
+\r
+ECHO ...\90V\8bK\82É\92Ç\89Á\82³\82ê\82½\90Ý\92è\83t\83@\83C\83\8b\82ð\83R\83s\81[\82µ\82Ü\82·.\r
+IF EXIST ..\..\env.old2\ RD /S /Q ..\..\env.old2\\r
+IF EXIST ..\..\env.old\ REN ..\..\env.old\ env.old2\r
+MD ..\..\env.old\\r
+XCOPY ..\..\env ..\..\env.old\ /S /E /Q /Y\r
+FOR %%i IN (env\*.*) DO (\r
+       IF NOT EXIST "..\..\env\%%~nxi" COPY "%%i" ..\..\env\\r
+)\r
+\r
+ECHO ...\90V\8bK\82É\92Ç\89Á\82³\82ê\82½\83A\83C\83R\83\93\82ð\83R\83s\81[\82µ\82Ü\82·.\r
+FOR %%i IN (icon\*.*) DO (\r
+       IF NOT EXIST "..\..\icon\%%~nxi" COPY "%%i" ..\..\icon\\r
+)\r
+\r
+ECHO ...\83X\83N\83\8a\83v\83g\82ð\83R\83s\81[\82µ\82Ü\82·.\r
+FOR %%i IN (*.cmd) DO (\r
+       IF NOT "%%i" == "_update.cmd" COPY "%%i" ..\..\\r
+)\r
+FOR %%i IN (*.sh) DO (\r
+       COPY "%%i" ..\..\\r
+)\r
+DEL /F ..\..\_update.cmd\r
+DEL /F ..\..\_update.sh\r
+COPY tinybannavi.sh ..\..\tinybannavi.command\r
+\r
+IF EXIST ..\..\TaiNavi.exe.old DEL /F ..\..\TaiNavi.exe.old\r
+IF EXIST ..\..\TaiNavi.exe REN ..\..\TaiNavi.exe TaiNavi.exe.old\r
+COPY /Y TaiNavi.exe ..\..\\r
+IF NOT EXIST ..\..\TaiNavi.ini COPY TaiNavi.ini ..\..\\r
+\r
+ECHO ...\83e\83L\83X\83g\82ð\83R\83s\81[\82µ\82Ü\82·.\r
+FOR %%i IN (*.txt) DO (\r
+       COPY "%%i" ..\..\\r
+)\r
+\r
+CD %WD%\r
+EXIT 0\r
+\r
+:ERROREXIT\r
+ECHO \83A\83b\83v\83f\81[\83g\82É\8e¸\94s\82µ\82Ü\82µ\82½.\r
+PAUSE\r
+\r
+CD %WD%\r
+EXIT 1\r
diff --git a/TinyBannavi/_update.sh b/TinyBannavi/_update.sh
new file mode 100644 (file)
index 0000000..ef54e31
--- /dev/null
@@ -0,0 +1,53 @@
+#!/bin/sh -e
+
+WD=$PWD
+
+cd `dirname $0`
+
+if [ ! -f ../../bin/tainavi/Viewer.class ]; then
+       echo "COULD NOT EXECUTE."
+       exit 1
+fi
+
+echo "...INSTALL BINARIES."
+if [ -d ../../bin.new ]; then
+       rm -rf ../../bin.new
+fi
+mv bin ../../bin.new
+
+echo "...INSTALL NEW ENVIRONMENT FILES."
+find env ! -name env -prune -type f -exec cp -np {} ../../{} \;
+
+echo "...INSTALL NEW ICON FILES."
+find icon ! -name icon -prune -type f -exec cp -np {} ../../{} \;
+
+if [ -f ../../javamail/PlugIn_RecGoogleCalendar.class ]; then
+       rm -f ../../javamail/PlugIn_Rec*.class
+fi
+if [ -f ../../javamail/PlugIn_RecRD_MAIL.class ]; then
+       rm -f ../../javamail/PlugIn_Rec*.class
+fi
+
+echo "...INSTALL SCRIPT FILES."
+chmod +x *.sh
+find . ! -name . -prune -type f -name \*\.cmd -exec cp -fp {} ../../{} \;
+find . ! -name . -prune -type f -name \*\.sh -a ! -name _update.sh -exec cp -fp {} ../../{} \;
+if [ ! -d "../../TaiNavi for Mac.app/" ]; then
+       tar cf - "TaiNavi for Mac.app" | ( cd ../../; tar xf - )
+       rm -f ../../tinybannavi.command
+fi
+rm -f ../../_update.cmd
+rm -f ../../_update.sh
+cp -fp tinybannavi.sh "../../TaiNavi for Mac.app/Contents/MacOS/tinybannavi.sh"
+
+cp -fp TaiNavi.exe ../../TaiNavi.exe
+if [ ! -f ../../TaiNavi.ini ]; then
+       cp -fp TaiNavi.ini ../../
+fi
+
+echo "...INSTALL TEXT FILES."
+rm -f ../../*.txt
+find . ! -name . -prune -type f -name \*\.txt -exec cp -fp {} ../../{} \;
+
+cd $WD
+exit 0
diff --git a/TinyBannavi/icon/bookmark-new-list-4.png b/TinyBannavi/icon/bookmark-new-list-4.png
new file mode 100644 (file)
index 0000000..190715f
Binary files /dev/null and b/TinyBannavi/icon/bookmark-new-list-4.png differ
diff --git a/TinyBannavi/icon/camera.png b/TinyBannavi/icon/camera.png
new file mode 100644 (file)
index 0000000..cf5dc77
Binary files /dev/null and b/TinyBannavi/icon/camera.png differ
diff --git a/TinyBannavi/icon/checkbox.png b/TinyBannavi/icon/checkbox.png
new file mode 100644 (file)
index 0000000..b96fe91
Binary files /dev/null and b/TinyBannavi/icon/checkbox.png differ
diff --git a/TinyBannavi/icon/colorize-2.png b/TinyBannavi/icon/colorize-2.png
new file mode 100644 (file)
index 0000000..9eb74f0
Binary files /dev/null and b/TinyBannavi/icon/colorize-2.png differ
diff --git a/TinyBannavi/icon/down-arrow.png b/TinyBannavi/icon/down-arrow.png
new file mode 100644 (file)
index 0000000..474953b
Binary files /dev/null and b/TinyBannavi/icon/down-arrow.png differ
diff --git a/TinyBannavi/icon/folder-video.png b/TinyBannavi/icon/folder-video.png
new file mode 100644 (file)
index 0000000..c7e5e66
Binary files /dev/null and b/TinyBannavi/icon/folder-video.png differ
diff --git a/TinyBannavi/icon/help-browser-2.png b/TinyBannavi/icon/help-browser-2.png
new file mode 100644 (file)
index 0000000..5d2ddb2
Binary files /dev/null and b/TinyBannavi/icon/help-browser-2.png differ
diff --git a/TinyBannavi/icon/internet-news-reader.png b/TinyBannavi/icon/internet-news-reader.png
new file mode 100644 (file)
index 0000000..a8963d9
Binary files /dev/null and b/TinyBannavi/icon/internet-news-reader.png differ
diff --git a/TinyBannavi/icon/media-record-3.png b/TinyBannavi/icon/media-record-3.png
new file mode 100644 (file)
index 0000000..1738246
Binary files /dev/null and b/TinyBannavi/icon/media-record-3.png differ
diff --git a/TinyBannavi/icon/remocon.png b/TinyBannavi/icon/remocon.png
new file mode 100644 (file)
index 0000000..954c90e
Binary files /dev/null and b/TinyBannavi/icon/remocon.png differ
diff --git a/TinyBannavi/icon/remocon16.png b/TinyBannavi/icon/remocon16.png
new file mode 100644 (file)
index 0000000..756ff48
Binary files /dev/null and b/TinyBannavi/icon/remocon16.png differ
diff --git a/TinyBannavi/icon/remostat.png b/TinyBannavi/icon/remostat.png
new file mode 100644 (file)
index 0000000..81c22f7
Binary files /dev/null and b/TinyBannavi/icon/remostat.png differ
diff --git a/TinyBannavi/icon/remostat16.png b/TinyBannavi/icon/remostat16.png
new file mode 100644 (file)
index 0000000..2101740
Binary files /dev/null and b/TinyBannavi/icon/remostat16.png differ
diff --git a/TinyBannavi/icon/system-search-2.png b/TinyBannavi/icon/system-search-2.png
new file mode 100644 (file)
index 0000000..3bf4d12
Binary files /dev/null and b/TinyBannavi/icon/system-search-2.png differ
diff --git a/TinyBannavi/icon/system-shutdown-2.png b/TinyBannavi/icon/system-shutdown-2.png
new file mode 100644 (file)
index 0000000..a25526e
Binary files /dev/null and b/TinyBannavi/icon/system-shutdown-2.png differ
diff --git a/TinyBannavi/icon/system-software-update-2.png b/TinyBannavi/icon/system-software-update-2.png
new file mode 100644 (file)
index 0000000..8ba022d
Binary files /dev/null and b/TinyBannavi/icon/system-software-update-2.png differ
diff --git a/TinyBannavi/icon/system.png b/TinyBannavi/icon/system.png
new file mode 100644 (file)
index 0000000..cc4c768
Binary files /dev/null and b/TinyBannavi/icon/system.png differ
diff --git a/TinyBannavi/icon/tainavi.icns b/TinyBannavi/icon/tainavi.icns
new file mode 100644 (file)
index 0000000..30d1691
Binary files /dev/null and b/TinyBannavi/icon/tainavi.icns differ
diff --git a/TinyBannavi/icon/tainavi.ico b/TinyBannavi/icon/tainavi.ico
new file mode 100644 (file)
index 0000000..e4d9dae
Binary files /dev/null and b/TinyBannavi/icon/tainavi.ico differ
diff --git a/TinyBannavi/icon/tainavi.png b/TinyBannavi/icon/tainavi.png
new file mode 100644 (file)
index 0000000..f89c384
Binary files /dev/null and b/TinyBannavi/icon/tainavi.png differ
diff --git a/TinyBannavi/icon/tainavi16.png b/TinyBannavi/icon/tainavi16.png
new file mode 100644 (file)
index 0000000..50f1847
Binary files /dev/null and b/TinyBannavi/icon/tainavi16.png differ
diff --git a/TinyBannavi/icon/taisync-alarm.png b/TinyBannavi/icon/taisync-alarm.png
new file mode 100644 (file)
index 0000000..b9f5240
Binary files /dev/null and b/TinyBannavi/icon/taisync-alarm.png differ
diff --git a/TinyBannavi/icon/taisync-alert.png b/TinyBannavi/icon/taisync-alert.png
new file mode 100644 (file)
index 0000000..34f6934
Binary files /dev/null and b/TinyBannavi/icon/taisync-alert.png differ
diff --git a/TinyBannavi/icon/taisync.png b/TinyBannavi/icon/taisync.png
new file mode 100644 (file)
index 0000000..60e7d8f
Binary files /dev/null and b/TinyBannavi/icon/taisync.png differ
diff --git a/TinyBannavi/icon/tool_timer.png b/TinyBannavi/icon/tool_timer.png
new file mode 100644 (file)
index 0000000..998eefd
Binary files /dev/null and b/TinyBannavi/icon/tool_timer.png differ
diff --git a/TinyBannavi/icon/user-offline.png b/TinyBannavi/icon/user-offline.png
new file mode 100644 (file)
index 0000000..99c4556
Binary files /dev/null and b/TinyBannavi/icon/user-offline.png differ
diff --git a/TinyBannavi/icon/utilities-log_viewer.png b/TinyBannavi/icon/utilities-log_viewer.png
new file mode 100644 (file)
index 0000000..64e8dd7
Binary files /dev/null and b/TinyBannavi/icon/utilities-log_viewer.png differ
diff --git a/TinyBannavi/icon/video-television.png b/TinyBannavi/icon/video-television.png
new file mode 100644 (file)
index 0000000..9e9eb6b
Binary files /dev/null and b/TinyBannavi/icon/video-television.png differ
diff --git a/TinyBannavi/icon/view-calendar-time-spent.png b/TinyBannavi/icon/view-calendar-time-spent.png
new file mode 100644 (file)
index 0000000..26312e3
Binary files /dev/null and b/TinyBannavi/icon/view-calendar-time-spent.png differ
diff --git a/TinyBannavi/icon/view-calendar-timeline.png b/TinyBannavi/icon/view-calendar-timeline.png
new file mode 100644 (file)
index 0000000..8e1bbaa
Binary files /dev/null and b/TinyBannavi/icon/view-calendar-timeline.png differ
diff --git a/TinyBannavi/icon/view-close.png b/TinyBannavi/icon/view-close.png
new file mode 100644 (file)
index 0000000..c7f7c6c
Binary files /dev/null and b/TinyBannavi/icon/view-close.png differ
diff --git a/TinyBannavi/icon/view-fullscreen-5.png b/TinyBannavi/icon/view-fullscreen-5.png
new file mode 100644 (file)
index 0000000..1634b19
Binary files /dev/null and b/TinyBannavi/icon/view-fullscreen-5.png differ
diff --git a/TinyBannavi/icon/view-nofullscreen-3.png b/TinyBannavi/icon/view-nofullscreen-3.png
new file mode 100644 (file)
index 0000000..f4e4b4e
Binary files /dev/null and b/TinyBannavi/icon/view-nofullscreen-3.png differ
diff --git a/TinyBannavi/icon/view-split-top-bottom-3.png b/TinyBannavi/icon/view-split-top-bottom-3.png
new file mode 100644 (file)
index 0000000..0af7eaf
Binary files /dev/null and b/TinyBannavi/icon/view-split-top-bottom-3.png differ
diff --git a/TinyBannavi/packetcapture.cmd b/TinyBannavi/packetcapture.cmd
new file mode 100644 (file)
index 0000000..e071a28
--- /dev/null
@@ -0,0 +1,3 @@
+SET JAVA=java\r
+IF EXIST JRE6 SET JAVA=JRE6\BIN\JAVA\r
+%JAVA% -cp bin -Dfile.encoding=UTF-8 httpDump.Viewer\r
diff --git a/TinyBannavi/remotectrl.cmd b/TinyBannavi/remotectrl.cmd
new file mode 100644 (file)
index 0000000..929972b
--- /dev/null
@@ -0,0 +1,36 @@
+@ECHO OFF\r
+\r
+CD /D %~dp0\r
+\r
+IF NOT EXIST .\bin\remoteCtrl\Viewer.class GOTO ERROREXIT\r
+\r
+REM SET JAVA_OPTS=-Dawt.useSystemAAFontSettings=on -Dswing.aatext=true -Dswing.defaultlaf=com.sun.java.swing.plaf.windows.WindowsLookAndFeel\r
+SET JAVA_OPTS=-Dswing.defaultlaf=com.sun.java.swing.plaf.windows.WindowsLookAndFeel\r
+SET JPATH=%PATH%;C:\Program Files\Java\jre6\bin;C:\Program Files (x86)\Java\jre6\bin;C:\Program Files\Java\jre7\bin;C:\Program Files (x86)\Java\jre7\bin\r
+SET JEXE=javaw.exe\r
+SET JAVA=\r
+IF EXIST JRE6 (\r
+       SET JAVA=jre6\bin\javaw.exe\r
+) ELSE (\r
+       FOR %%I IN ( %JEXE% ) DO (\r
+               SET JAVA=%%~$JPATH:I\r
+       )\r
+)\r
+IF NOT EXIST "%JAVA%" (\r
+       ECHO.\r
+       ECHO Java\82ª\82Ý\82Â\82©\82è\82Ü\82¹\82ñ\81B\88È\89º\82ð\8am\94F\82µ\82Ä\82­\82¾\82³\82¢\81B\r
+       ECHO.\r
+       ECHO \87@Java\82ª\83C\83\93\83X\83g\81[\83\8b\82³\82ê\82Ä\82¢\82é\82©\81B\r
+       ECHO \87A%JEXE%\82ªPATH\8aÂ\8b«\95Ï\90\94\82Å\8ew\92è\82³\82ê\82é\8fê\8f\8a\82É\91\8dÝ\82µ\82Ä\82¢\82é\82©\81B\r
+       ECHO.\r
+       PAUSE\r
+       GOTO :EOF\r
+)\r
+ECHO \8e\9f\82ÌJava\82ª\8eg\97p\82³\82ê\82Ü\82·\81F"%JAVA%"\r
+\r
+START "RemoCtrl" "%JAVA%" -cp bin -Dfile.encoding=UTF-8 %JAVA_OPTS% remoteCtrl.Viewer\r
+GOTO :EOF\r
+\r
+:ERROREXIT\r
+ECHO \8eÀ\8ds\82Å\82«\82Ü\82¹\82ñ.\r
+PAUSE\r
diff --git a/TinyBannavi/splash.gif b/TinyBannavi/splash.gif
new file mode 100644 (file)
index 0000000..24410cf
Binary files /dev/null and b/TinyBannavi/splash.gif differ
diff --git a/TinyBannavi/src/.gitignore b/TinyBannavi/src/.gitignore
new file mode 100644 (file)
index 0000000..7c397f6
--- /dev/null
@@ -0,0 +1 @@
+/testfield
diff --git a/TinyBannavi/src/epgdump/Aribstr.java b/TinyBannavi/src/epgdump/Aribstr.java
new file mode 100644 (file)
index 0000000..0c216d4
--- /dev/null
@@ -0,0 +1,683 @@
+package epgdump;\r
+\r
+import java.io.UnsupportedEncodingException;\r
+\r
+public class Aribstr {\r
+       \r
+       private static final int CODE_UNKNOWN                   = 0;    // 不明なグラフィックセット(非対応)\r
+       private static final int CODE_KANJI                             = 1;    // Kanji\r
+       private static final int CODE_ALPHANUMERIC              = 2;    // Alphanumeric\r
+       private static final int CODE_HIRAGANA                  = 3;    // Hiragana\r
+       private static final int CODE_KATAKANA                  = 4;    // Katakana\r
+       private static final int CODE_MOSAIC_A                  = 5;    // Mosaic A\r
+       private static final int CODE_MOSAIC_B                  = 6;    // Mosaic B\r
+       private static final int CODE_MOSAIC_C                  = 7;    // Mosaic C\r
+       private static final int CODE_MOSAIC_D                  = 8;    // Mosaic D\r
+       private static final int CODE_PROP_ALPHANUMERIC = 9;    // Proportional Alphanumeric\r
+       private static final int CODE_PROP_HIRAGANA     = 10;   // Proportional Hiragana\r
+       private static final int CODE_PROP_KATAKANA     = 11;   // Proportional Katakana\r
+       private static final int CODE_JIS_X0201_KATAKANA= 12;   // JIS X 0201 Katakana\r
+       private static final int CODE_JIS_KANJI_PLANE_1 = 13;   // JIS compatible Kanji Plane 1\r
+       private static final int CODE_JIS_KANJI_PLANE_2 = 14;   // JIS compatible Kanji Plane 2\r
+       private static final int CODE_ADDITIONAL_SYMBOLS= 15;   // Additional symbols\r
+\r
+       private static int[] m_CodeG = new int[4];\r
+       private static int m_pLockingGL;\r
+       private static int m_pLockingGR;\r
+       private static int m_pSingleGL;\r
+\r
+       private static int m_byEscSeqCount;\r
+       private static int m_byEscSeqIndex;\r
+       private static boolean m_bIsEscSeqDrcs;\r
+\r
+       static final boolean abCharSizeTable[] = {\r
+               false,  // CODE_UNKNOWN                                 不明なグラフィックセット(非対応)\r
+               true,   // CODE_KANJI                                   Kanji\r
+               false,  // CODE_ALPHANUMERIC                    Alphanumeric\r
+               false,  // CODE_HIRAGANA                                Hiragana\r
+               false,  // CODE_KATAKANA                                Katakana\r
+               false,  // CODE_MOSAIC_A                                Mosaic A\r
+               false,  // CODE_MOSAIC_B                                Mosaic B\r
+               false,  // CODE_MOSAIC_C                                Mosaic C\r
+               false,  // CODE_MOSAIC_D                                Mosaic D\r
+               false,  // CODE_PROP_ALPHANUMERIC               Proportional Alphanumeric\r
+               false,  // CODE_PROP_HIRAGANA                   Proportional Hiragana\r
+               false,  // CODE_PROP_KATAKANA                   Proportional Katakana\r
+               false,  // CODE_JIS_X0201_KATAKANA              JIS X 0201 Katakana\r
+               true,   // CODE_JIS_KANJI_PLANE_1               JIS compatible Kanji Plane 1\r
+               true,   // CODE_JIS_KANJI_PLANE_2               JIS compatible Kanji Plane 2\r
+               true    // CODE_ADDITIONAL_SYMBOLS              Additional symbols\r
+       };\r
+       \r
+       private static enum STRING_SIZE {\r
+               STR_SMALL,              //SSZ\r
+               STR_MEDIUM,             //MSZ\r
+               STR_NORMAL,             //NSZ\r
+               STR_MICRO,              //SZX 0x60\r
+               STR_HIGH_W,             //SZX 0x41\r
+               STR_WIDTH_W,    //SZX 0x44\r
+               STR_W,                  //SZX 0x45\r
+               STR_SPECIAL_1,  //SZX 0x6B\r
+               STR_SPECIAL_2,  //SZX 0x64\r
+       };\r
+       \r
+       private static STRING_SIZE m_emStrSize;\r
+       \r
+       //\r
+       private static boolean IsSmallCharMode() {\r
+               boolean bRet = false;\r
+               switch ( m_emStrSize ) {\r
+               case STR_SMALL:\r
+                       bRet = true;\r
+                       break;\r
+               case STR_MEDIUM:\r
+                       bRet = true;\r
+                       break;\r
+               case STR_NORMAL:\r
+                       bRet = false;\r
+                       break;\r
+               case STR_MICRO:\r
+                       bRet = true;\r
+                       break;\r
+               case STR_HIGH_W:\r
+                       bRet = false;\r
+                       break;\r
+               case STR_WIDTH_W:\r
+                       bRet = false;\r
+                       break;\r
+               case STR_W:\r
+                       bRet = false;\r
+                       break;\r
+               case STR_SPECIAL_1:\r
+                       bRet = false;\r
+                       break;\r
+               case STR_SPECIAL_2:\r
+                       bRet = false;\r
+                       break;\r
+               default:\r
+                       break;\r
+               }\r
+               return bRet;\r
+       }\r
+\r
+       //\r
+       public static int AribToString(byte[] lpszDst, byte[] pSrcData, int dwSrcLen) {\r
+               try {\r
+                       return AribToStringInternal(lpszDst, pSrcData, dwSrcLen);\r
+               }\r
+               catch ( Exception e ) {\r
+                       e.printStackTrace();\r
+                       return 0;\r
+               }\r
+       }\r
+       \r
+       //\r
+       private static int AribToStringInternal(byte[] lpszDst, byte[] pSrcData, int dwSrcLen) throws UnsupportedEncodingException {\r
+               \r
+               if ( pSrcData == null || dwSrcLen == 0 || lpszDst == null )\r
+                       return 0;\r
+                 \r
+               int dwSrcPos = 0;\r
+               int dwDstLen = 0;\r
+               int dwSrcData;\r
+         \r
+               // 状態初期設定\r
+               m_byEscSeqCount = 0;\r
+               m_pSingleGL = -1;\r
+\r
+               m_CodeG[0] = CODE_KANJI;\r
+               m_CodeG[1] = CODE_ALPHANUMERIC;\r
+               m_CodeG[2] = CODE_HIRAGANA;\r
+               m_CodeG[3] = CODE_KATAKANA;\r
+\r
+               m_pLockingGL = 0;\r
+               m_pLockingGR = 2;\r
+\r
+               m_emStrSize = STRING_SIZE.STR_NORMAL;\r
+\r
+               while ( dwSrcPos < dwSrcLen ) {\r
+                       dwSrcData = pSrcData[dwSrcPos] & 0xFF;\r
+\r
+                       if ( m_byEscSeqCount == 0 ) {\r
+             \r
+                               // GL/GR領域\r
+                               if ( (dwSrcData >= 0x21) && (dwSrcData <= 0x7E) ){\r
+                                       // GL領域\r
+                                       final int CurCodeSet = (m_pSingleGL != -1) ? m_CodeG[m_pSingleGL] : m_CodeG[m_pLockingGL];\r
+                                       m_pSingleGL = -1;\r
+                                       \r
+                                       if ( abCharSizeTable[CurCodeSet] ) {\r
+                                               // 2バイトコード\r
+                                               if ( (dwSrcLen - dwSrcPos) < 2)\r
+                                                       break;\r
+                                               \r
+                                               dwDstLen += ProcessCharCode(lpszDst, dwDstLen, (pSrcData[dwSrcPos + 0] << 8) | pSrcData[dwSrcPos + 1], CurCodeSet);\r
+                                               dwSrcPos++;\r
+                                       }\r
+                                       else{\r
+                                               // 1バイトコード\r
+                                               dwDstLen += ProcessCharCode(lpszDst, dwDstLen, dwSrcData, CurCodeSet);\r
+                                       }\r
+                               }\r
+                               else if ( (dwSrcData >= 0xA1) && (dwSrcData <= 0xFE) ) {\r
+                                       // GR領域\r
+                                       final int CurCodeSet = m_CodeG[m_pLockingGR];\r
+                                       \r
+                                       if ( abCharSizeTable[CurCodeSet] ) {\r
+                                               // 2バイトコード\r
+                                               if ( (dwSrcLen - dwSrcPos) < 2 )\r
+                                                       break;\r
+                                               \r
+                                               dwDstLen += ProcessCharCode(lpszDst, dwDstLen, ((pSrcData[dwSrcPos + 0] & 0x7F) << 8) | (pSrcData[dwSrcPos + 1] & 0x7F), CurCodeSet);\r
+                                               dwSrcPos++;\r
+                                       }\r
+                                       else{\r
+                                               // 1バイトコード\r
+                                               dwDstLen += ProcessCharCode(lpszDst, dwDstLen, (dwSrcData & 0x7F), CurCodeSet);\r
+                                       }\r
+                               }\r
+                               else {\r
+                                       // 制御コード\r
+                                       switch ( dwSrcData ) {\r
+                                       case 0x0F       : LockingShiftGL(0);                                    break;  // LS0\r
+                                       case 0x0E       : LockingShiftGL(1);                                    break;  // LS1\r
+                                       case 0x19       : SingleShiftGL(2);                                             break;  // SS2\r
+                                       case 0x1D       : SingleShiftGL(3);                                             break;  // SS3\r
+                                       case 0x1B       : m_byEscSeqCount = 1;                                  break;  // ESC\r
+                                       case 0x89       : m_emStrSize = STRING_SIZE.STR_MEDIUM; break;  // MSZ\r
+                                       case 0x8A       : m_emStrSize = STRING_SIZE.STR_NORMAL; break;  // NSZ\r
+                                       case 0x20       :\r
+                                       case 0xA0       :\r
+                                               //SP 空白\r
+                                               //空白は文字サイズの影響あり\r
+                                               if ( IsSmallCharMode() == false ) {\r
+                                                       lpszDst[dwDstLen] = 0x20;\r
+                                                       lpszDst[dwDstLen+1] = 0x00;\r
+                                                       //strcpy(&lpszDst[dwDstLen], " ");\r
+                                                       //dwDstLen += 3;\r
+                                                       dwDstLen += 2;\r
+                                               }\r
+                                               else {\r
+                                                       lpszDst[dwDstLen++] = 0x20;\r
+                                                       //lpszDst[dwDstLen++] = TEXT(' ');\r
+                                               }\r
+                                               break;\r
+                                       default         : break;        // 非対応\r
+                                       }\r
+                               }\r
+                       }\r
+                       else {\r
+                               // エスケープシーケンス処理\r
+                               ProcessEscapeSeq(dwSrcData);\r
+                       }\r
+                       \r
+                       dwSrcPos++;\r
+               }\r
+\r
+               // 終端文字\r
+               lpszDst[dwDstLen] = 0x00;\r
+               //lpszDst[dwDstLen] = TEXT('\0');\r
+\r
+               return dwDstLen;\r
+       }\r
+\r
+       //\r
+       private static int ProcessCharCode(byte[] lpszDst, int ptr, final int wCode, final int CodeSet) throws UnsupportedEncodingException {\r
+               switch ( CodeSet ) {\r
+               case CODE_KANJI :\r
+               case CODE_JIS_KANJI_PLANE_1 :\r
+               case CODE_JIS_KANJI_PLANE_2 :\r
+                       // 漢字コード出力\r
+                       return PutKanjiChar(lpszDst, ptr, wCode);\r
+\r
+               case CODE_ALPHANUMERIC :\r
+               case CODE_PROP_ALPHANUMERIC :\r
+                       // 英数字コード出力\r
+                       if( IsSmallCharMode() == false ){\r
+                               //全角テーブルコード取得\r
+                               return PutAlphanumericChar(lpszDst, ptr, wCode);\r
+                       }\r
+                       else {\r
+                               //半角はそのまま出力\r
+                               lpszDst[ptr] = (byte) (wCode & 0x00FF);\r
+                               return 1;\r
+                       }\r
+\r
+               case CODE_HIRAGANA :\r
+               case CODE_PROP_HIRAGANA :\r
+                       // ひらがなコード出力\r
+                       return PutHiraganaChar(lpszDst, ptr, wCode);\r
+\r
+               case CODE_PROP_KATAKANA :\r
+               case CODE_KATAKANA :\r
+                       // カタカナコード出力\r
+                       return PutKatakanaChar(lpszDst, ptr, wCode);\r
+\r
+               case CODE_JIS_X0201_KATAKANA :\r
+                       // JISカタカナコード出力\r
+                       return PutJisKatakanaChar(lpszDst, ptr, wCode);\r
+\r
+               case CODE_ADDITIONAL_SYMBOLS :\r
+                       // 追加シンボルコード出力\r
+                       return PutSymbolsChar(lpszDst, ptr, wCode);\r
+\r
+               default :\r
+                       return 0;\r
+               }\r
+       }\r
+       \r
+       //\r
+       private static final int PutKanjiChar(byte[] lpszDst, int ptr, final int wCode) throws UnsupportedEncodingException {\r
+               byte[] code = new byte[8];\r
+\r
+               code[0] = 0x1B;\r
+               code[1] = 0x24;\r
+               code[2] = 0x40;\r
+               code[3] = (byte) ((wCode & 0x7F00) >>> 8);\r
+               code[4] = (byte) (wCode & 0x007F);\r
+               code[5] = 0x1B;\r
+               code[6] = 0x28;\r
+               code[7] = 0x4A;\r
+               //code[8] = 0x00;\r
+\r
+               byte[] utf8Bytes = new String(code,"ISO-2022-JP").getBytes(Util.thisEncoding);\r
+               System.arraycopy(utf8Bytes, 0, lpszDst, ptr, utf8Bytes.length);\r
+               lpszDst[ptr+utf8Bytes.length] = 0x00;\r
+                               \r
+               return utf8Bytes.length;\r
+       }\r
+       \r
+       //\r
+       private static byte[] acAlphanumericTable = null; \r
+       private static final int PutAlphanumericChar(byte[] lpszDst, int ptr, final int wCode) throws UnsupportedEncodingException {\r
+               // 英数字全角文字コード変換\r
+               if ( acAlphanumericTable == null ) {\r
+                       acAlphanumericTable = (\r
+                                       "                "+\r
+                                       "                "+\r
+                                       " !”#$%&’()*+,-./"+\r
+                                       "0123456789:;<=>?"+\r
+                                       "@ABCDEFGHIJKLMNO"+\r
+                                       "PQRSTUVWXYZ[¥]^_"+\r
+                                       " abcdefghijklmno"+\r
+                                       "pqrstuvwxyz{|} ̄ "\r
+                                       ).getBytes(Util.thisEncoding);\r
+               }\r
+               \r
+               /*\r
+               #ifdef _UNICODE\r
+                       lpszDst[0] = acAlphanumericTableZenkaku[wCode];\r
+       \r
+                       return 1;\r
+               #else\r
+               */\r
+                       lpszDst[ptr+0] = acAlphanumericTable[wCode * 3 + 0];\r
+                       lpszDst[ptr+1] = acAlphanumericTable[wCode * 3 + 1];\r
+                       lpszDst[ptr+2] = acAlphanumericTable[wCode * 3 + 2];\r
+       \r
+                       return 3;\r
+               /*\r
+               #endif\r
+               */\r
+       }\r
+       \r
+       //\r
+       private static byte[] acHiraganaTable = null;\r
+       private static final int PutHiraganaChar(byte[] lpszDst, int ptr, final int wCode) throws UnsupportedEncodingException {\r
+               // ひらがな文字コード変換\r
+               if ( acHiraganaTable == null ) {\r
+                       acHiraganaTable = (\r
+                                       "                "+\r
+                                       "                "+\r
+                                       " ぁあぃいぅうぇえぉおかがきぎく"+\r
+                                       "ぐけげこごさざしじすずせぜそぞた"+\r
+                                       "だちぢっつづてでとどなにぬねのは"+\r
+                                       "ばぱひびぴふぶぷへべぺほぼぽまみ"+\r
+                                       "むめもゃやゅゆょよらりるれろゎわ"+\r
+                                       "ゐゑをん   ゝゞー。「」、・ "\r
+                                       ).getBytes(Util.thisEncoding);\r
+               }\r
+       /*\r
+       #ifdef _UNICODE\r
+               lpszDst[0] = acHiraganaTable[wCode];\r
+\r
+               return 1UL;\r
+       #else\r
+       */\r
+               lpszDst[ptr+0] = acHiraganaTable[wCode * 3 + 0];\r
+               lpszDst[ptr+1] = acHiraganaTable[wCode * 3 + 1];\r
+               lpszDst[ptr+2] = acHiraganaTable[wCode * 3 + 2];\r
+\r
+               return 3;\r
+       /*\r
+       #endif\r
+       */\r
+       }\r
+       \r
+       //\r
+       private static byte[] acKatakanaTable = null;\r
+       private static final int PutKatakanaChar(byte[] lpszDst, int ptr, final int wCode) throws UnsupportedEncodingException {\r
+               // カタカナ英数字文字コード変換\r
+               if ( acKatakanaTable == null ) {\r
+                       acKatakanaTable = ( \r
+                                       "                "+\r
+                                       "                "+\r
+                                       " ァアィイゥウェエォオカガキギク"+\r
+                                       "グケゲコゴサザシジスズセゼソゾタ"+\r
+                                       "ダチヂッツヅテデトドナニヌネノハ"+\r
+                                       "バパヒビピフブプヘベペホボポマミ"+\r
+                                       "ムメモャヤュユョヨラリルレロヮワ"+\r
+                                       "ヰヱヲンヴヵヶヽヾー。「」、・ "\r
+                                       ).getBytes(Util.thisEncoding);\r
+               }\r
+       /*\r
+       #ifdef _UNICODE\r
+               lpszDst[0] = acKatakanaTable[wCode];\r
+\r
+               return 1UL;\r
+       #else\r
+       */\r
+               lpszDst[ptr+0] = acKatakanaTable[wCode * 3 + 0];\r
+               lpszDst[ptr+1] = acKatakanaTable[wCode * 3 + 1];\r
+               lpszDst[ptr+2] = acKatakanaTable[wCode * 3 + 2];\r
+\r
+               return 3;\r
+       /*\r
+       #endif\r
+       */\r
+       }\r
+       \r
+       //\r
+       private static byte[] acJisKatakanaTable = null;\r
+       private static final int PutJisKatakanaChar(byte[] lpszDst, int ptr, final int wCode) throws UnsupportedEncodingException {\r
+               // JISカタカナ文字コード変換\r
+               if ( acJisKatakanaTable == null ) {\r
+                       acJisKatakanaTable = ( \r
+                                       "                "+\r
+                                       "                "+\r
+                                       " 。「」、・ヲァィゥェォャュョッ"+\r
+                                       "ーアイウエオカキクケコサシスセソ"+\r
+                                       "タチツテトナニヌネノハヒフヘホマ"+\r
+                                       "ミムメモヤユヨラリルレロワン゛゜"+\r
+                                       "                "+\r
+                                       "                "\r
+                                       ).getBytes(Util.thisEncoding);\r
+               }\r
+       \r
+       /*\r
+       #ifdef _UNICODE\r
+               lpszDst[0] = acJisKatakanaTable[wCode];\r
+\r
+               return 1UL;\r
+       #else\r
+       */\r
+               lpszDst[ptr+0] = acJisKatakanaTable[wCode * 3 + 0];\r
+               lpszDst[ptr+1] = acJisKatakanaTable[wCode * 3 + 1];\r
+               lpszDst[ptr+2] = acJisKatakanaTable[wCode * 3 + 2];\r
+\r
+               return 3;\r
+       /*\r
+       #endif\r
+       */\r
+       }\r
+\r
+       //\r
+       private static final String aszSymbolsTable1[] =\r
+               {\r
+                       "【HV】",     "【SD】",     "【P】",    "【W】",    "【MV】",     "【手】",    "【字】",    "【双】",            // 0x7A50 - 0x7A57      90/48 - 90/55\r
+                       "【デ】",    "【S】",    "【二】",    "【多】",    "【解】",    "【SS】",     "【B】",    "【N】",            // 0x7A58 - 0x7A5F      90/56 - 90/63\r
+                       "■",  "●",  "【天】",    "【交】",    "【映】",    "【無】",    "【料】",    "【年齢制限】",   // 0x7A60 - 0x7A67      90/64 - 90/71\r
+                       "【前】",    "【後】",    "【再】",    "【新】",    "【初】",    "【終】",    "【生】",    "【販】",            // 0x7A68 - 0x7A6F      90/72 - 90/79\r
+                       "【声】",    "【吹】",    "【PPV】",    "(秘)",    "ほか"                                                                // 0x7A70 - 0x7A74      90/80 - 90/84\r
+               };\r
+\r
+       private static final String aszSymbolsTable2[] =\r
+               {\r
+                       "→",          "←",          "↑",          "↓",          "●",          "○",          "年",          "月",                  // 0x7C21 - 0x7C28      92/01 - 92/08\r
+                       "日",          "円",          "㎡",          "㎥",          "㎝",          "㎠",          "㎤",          "0.",                 // 0x7C29 - 0x7C30      92/09 - 92/16\r
+                       "1.",         "2.",         "3.",         "4.",         "5.",         "6.",         "7.",         "8.",                 // 0x7C31 - 0x7C38      92/17 - 92/24\r
+                       "9.",         "氏",          "副",          "元",          "故",          "前",          "[新]",                "0,",                 // 0x7C39 - 0x7C40      92/25 - 92/32\r
+                       "1,",         "2,",         "3,",         "4,",         "5,",         "6,",         "7,",         "8,",                 // 0x7C41 - 0x7C48      92/33 - 92/40\r
+                       "9,",         "(社",         "(財",         "(有",         "(株",         "(代",         "(問",         "▶",                          // 0x7C49 - 0x7C50      92/41 - 92/48\r
+                       "◀",                  "〖",          "〗",          "⟐",                  "^2",           "^3",           "(CD",          "(vn",                  // 0x7C51 - 0x7C58      92/49 - 92/56\r
+                       "(ob",          "(cb",          "(ce",          "mb",           "(hp",          "(br",          "(p",           "(s",                   // 0x7C59 - 0x7C60      92/57 - 92/64\r
+                       "(ms",          "(t",           "(bs",          "(b",           "(tb",          "(tp",          "(ds",          "(ag",                  // 0x7C61 - 0x7C68      92/65 - 92/72\r
+                       "(eg",          "(vo",          "(fl",          "(ke",          "y",            "(sa",          "x",            "(sy",                  // 0x7C69 - 0x7C70      92/73 - 92/80\r
+                       "n",            "(or",          "g",            "(pe",          "r",            "(R",           "(C",           "(箏",                 // 0x7C71 - 0x7C78      92/81 - 92/88\r
+                       "DJ",           "[演]",                "Fax"                                                                                                                                           // 0x7C79 - 0x7C7B      92/89 - 92/91\r
+               };\r
+\r
+       private static final String aszSymbolsTable3[] =\r
+               {\r
+                       "㈪",          "㈫",          "㈬",          "㈭",          "㈮",          "㈯",          "㈰",          "㈷",                  // 0x7D21 - 0x7D28      93/01 - 93/08\r
+                       "㍾",          "㍽",          "㍼",          "㍻",          "№",          "℡",          "〶",          "○",                  // 0x7D29 - 0x7D30      93/09 - 93/16\r
+                       "〔本〕",            "〔三〕",            "〔二〕",            "〔安〕",            "〔点〕",            "〔打〕",            "〔盗〕",            "〔勝〕",                    // 0x7D31 - 0x7D38      93/17 - 93/24\r
+                       "〔敗〕",            "〔S〕",            "[投]",            "[捕]",            "[一]",            "[二]",            "[三]",            "[遊]",                    // 0x7D39 - 0x7D40      93/25 - 93/32\r
+                       "[左]",            "[中]",            "[右]",            "[指]",            "[走]",            "[打]",            "㍑",          "㎏",                  // 0x7D41 - 0x7D48      93/33 - 93/40\r
+                       "㎐",          "ha",           "㎞",          "㎢",          "㍱",          "・",                  "・",                  "1/2",                  // 0x7D49 - 0x7D50      93/41 - 93/48\r
+                       "0/3",          "1/3",          "2/3",          "1/4",          "3/4",          "1/5",          "2/5",          "3/5",                  // 0x7D51 - 0x7D58      93/49 - 93/56\r
+                       "4/5",          "1/6",          "5/6",          "1/7",          "1/8",          "1/9",          "1/10",         "☀",                  // 0x7D59 - 0x7D60      93/57 - 93/64\r
+                       "☁",          "☂",          "☃",          "☖",          "☗",          "▽",          "▼",          "♦",                  // 0x7D61 - 0x7D68      93/65 - 93/72\r
+                       "♥",          "♣",          "♠",          "⌺",                  "⦿",          "‼",          "⁉",          "(曇/晴",             // 0x7D69 - 0x7D70      93/73 - 93/80\r
+                       "☔",                  "(雨",         "(雪",         "(大雪",              "⚡",                  "(雷雨",              " ",          "・",                          // 0x7D71 - 0x7D78      93/81 - 93/88\r
+                       "・",                  "♬",          "☎"                                                                                                                                                   // 0x7D79 - 0x7D7B      93/89 - 93/91\r
+               };\r
+\r
+       private static final String aszSymbolsTable4[] =\r
+               {\r
+                       "Ⅰ",          "Ⅱ",          "Ⅲ",          "Ⅳ",          "Ⅴ",          "Ⅵ",          "Ⅶ",          "Ⅷ",                  // 0x7E21 - 0x7E28      94/01 - 94/08\r
+                       "Ⅸ",          "Ⅹ",          "Ⅺ",          "Ⅻ",          "⑰",          "⑱",          "⑲",          "⑳",                  // 0x7E29 - 0x7E30      94/09 - 94/16\r
+                       "⑴",          "⑵",          "⑶",          "⑷",          "⑸",          "⑹",          "⑺",          "⑻",                  // 0x7E31 - 0x7E38      94/17 - 94/24\r
+                       "⑼",          "⑽",          "⑾",          "⑿",          "㉑",          "㉒",          "㉓",          "㉔",                  // 0x7E39 - 0x7E40      94/25 - 94/32\r
+                       "(A",           "(B",           "(C",           "(D",           "(E",           "(F",           "(G",           "(H",                   // 0x7E41 - 0x7E48      94/33 - 94/40\r
+                       "(I",           "(J",           "(K",           "(L",           "(M",           "(N",           "(O",           "(P",                   // 0x7E49 - 0x7E50      94/41 - 94/48\r
+                       "(Q",           "(R",           "(S",           "(T",           "(U",           "(V",           "(W",           "(X",                   // 0x7E51 - 0x7E58      94/49 - 94/56\r
+                       "(Y",           "(Z",           "㉕",          "㉖",          "㉗",          "㉘",          "㉙",          "㉚",                  // 0x7E59 - 0x7E60      94/57 - 94/64\r
+                       "①",          "②",          "③",          "④",          "⑤",          "⑥",          "⑦",          "⑧",                  // 0x7E61 - 0x7E68      94/65 - 94/72\r
+                       "⑨",          "⑩",          "⑪",          "⑫",          "⑬",          "⑭",          "⑮",          "⑯",                  // 0x7E69 - 0x7E70      94/73 - 94/80\r
+                       "❶",          "❷",          "❸",          "❹",          "❺",          "❻",          "❼",          "❽",                  // 0x7E71 - 0x7E78      94/81 - 94/88\r
+                       "❾",          "❿",          "⓫",          "⓬",          "㉛"                                                                                                   // 0x7E79 - 0x7E7D      94/89 - 94/93\r
+               };\r
+\r
+       private static final String aszSymbolsTable5[] =\r
+               {\r
+                       "㐂",          "亭",          "份",          "仿",          "侚",          "俉",          "傜",          "儞",                  // 0x7521 - 0x7528      85/01 - 85/08\r
+                       "冼",          "㔟",          "匇",          "卡",          "卬",          "詹",          "吉",          "呍",                  // 0x7529 - 0x7530      85/09 - 85/16\r
+                       "咖",          "咜",          "咩",          "唎",          "啊",          "噲",          "囤",          "圳",                  // 0x7531 - 0x7538      85/17 - 85/24\r
+                       "圴",          "塚",          "墀",          "姤",          "娣",          "婕",          "寬",          "﨑",                  // 0x7539 - 0x7540      85/25 - 85/32\r
+                       "㟢",          "庬",          "弴",          "彅",          "德",          "怗",          "恵",          "愰",                  // 0x7541 - 0x7548      85/33 - 85/40\r
+                       "昤",          "曈",          "曙",          "曺",          "曻",          "桒",          "・",                  "椑",                  // 0x7549 - 0x7550      85/41 - 85/48\r
+                       "椻",          "橅",          "檑",          "櫛",          "・",                  "・",                  "・",                  "毱",                  // 0x7551 - 0x7558      85/49 - 85/56\r
+                       "泠",          "洮",          "海",          "涿",          "淊",          "淸",          "渚",          "潞",                  // 0x7559 - 0x7560      85/57 - 85/64\r
+                       "濹",          "灤",          "・",                  "・",                  "煇",          "燁",          "爀",          "玟",                  // 0x7561 - 0x7568      85/65 - 85/72\r
+                       "・",                  "珉",          "珖",          "琛",          "琡",          "琢",          "琦",          "琪",                  // 0x7569 - 0x7570      85/73 - 85/80\r
+                       "琬",          "琹",          "瑋",          "㻚",          "畵",          "疁",          "睲",          "䂓",                  // 0x7571 - 0x7578      85/81 - 85/88\r
+                       "磈",          "磠",          "祇",          "禮",          "・",                  "・"                                                                           // 0x7579 - 0x757E      85/89 - 85/94\r
+               };\r
+\r
+       private static final String aszSymbolsTable6[] =\r
+               {\r
+                       "・",                  "秚",          "稞",          "筿",          "簱",          "䉤",          "綋",          "羡",                  // 0x7621 - 0x7628      86/01 - 86/08\r
+                       "脘",          "脺",          "・",                  "芮",          "葛",          "蓜",          "蓬",          "蕙",                  // 0x7629 - 0x7630      86/09 - 86/16\r
+                       "藎",          "蝕",          "蟬",          "蠋",          "裵",          "角",          "諶",          "跎",                  // 0x7631 - 0x7638      86/17 - 86/24\r
+                       "辻",          "迶",          "郝",          "鄧",          "鄭",          "醲",          "鈳",          "銈",                  // 0x7639 - 0x7640      86/25 - 86/32\r
+                       "錡",          "鍈",          "閒",          "雞",          "餃",          "饀",          "髙",          "鯖",                  // 0x7641 - 0x7648      86/33 - 86/40\r
+                       "鷗",          "麴",          "麵"                                                                                                                                                   // 0x7649 - 0x764B      86/41 - 86/43\r
+               };\r
+       private static final int PutSymbolsChar(byte[] lpszDst, int ptr, final int wCode) throws UnsupportedEncodingException {\r
+               // 追加シンボル文字コード変換(とりあえず必要そうなものだけ)\r
+\r
+               // シンボルを変換する\r
+               String newS = null;\r
+               if ( (wCode >= 0x7A50) && (wCode <= 0x7A74) ) {\r
+                       newS = aszSymbolsTable1[wCode - 0x7A50];\r
+               }\r
+               else if ( (wCode >= 0x7C21) && (wCode <= 0x7C7B) ){\r
+                       newS = aszSymbolsTable2[wCode - 0x7C21];\r
+               }\r
+               else if ( (wCode >= 0x7D21) && (wCode <= 0x7D7B) ) {\r
+                       newS = aszSymbolsTable3[wCode - 0x7D21];\r
+               }\r
+               else if ( (wCode >= 0x7E21) && (wCode <= 0x7E7D) ) {\r
+                       newS = aszSymbolsTable4[wCode - 0x7E21];\r
+               }\r
+               else if ( (wCode >= 0x7521) && (wCode <= 0x757E) ) {\r
+                       newS = aszSymbolsTable5[wCode - 0x7521];\r
+               }\r
+               else if ( (wCode >= 0x7621) && (wCode <= 0x764B) ) {\r
+                       newS = aszSymbolsTable6[wCode - 0x7621];\r
+               }\r
+               else{\r
+                       newS = "・";\r
+               }\r
+\r
+               byte[] newB = newS.getBytes(Util.thisEncoding);\r
+               System.arraycopy(newB, 0, lpszDst, ptr, newB.length);\r
+               \r
+               return newB.length;\r
+       }\r
+       \r
+       //\r
+       private static void ProcessEscapeSeq(final int byCode) {\r
+               // エスケープシーケンス処理\r
+               switch ( m_byEscSeqCount ) {\r
+                       // 1バイト目\r
+               case 1:\r
+                       switch ( byCode ) {\r
+                               // Invocation of code elements\r
+                       case 0x6E       : LockingShiftGL(2);    m_byEscSeqCount = 0;    return;         // LS2\r
+                       case 0x6F       : LockingShiftGL(3);    m_byEscSeqCount = 0;    return;         // LS3\r
+                       case 0x7E       : LockingShiftGR(1);    m_byEscSeqCount = 0;    return;         // LS1R\r
+                       case 0x7D       : LockingShiftGR(2);    m_byEscSeqCount = 0;    return;         // LS2R\r
+                       case 0x7C       : LockingShiftGR(3);    m_byEscSeqCount = 0;    return;         // LS3R\r
+\r
+                               // Designation of graphic sets\r
+                       case 0x24       :       \r
+                       case 0x28       : m_byEscSeqIndex = 0;          break;\r
+                       case 0x29       : m_byEscSeqIndex = 1;          break;\r
+                       case 0x2A       : m_byEscSeqIndex = 2;          break;\r
+                       case 0x2B       : m_byEscSeqIndex = 3;          break;\r
+                       default         : m_byEscSeqCount = 0;          return;         // エラー\r
+                       }\r
+                       break;\r
+\r
+                       // 2バイト目\r
+               case 2:\r
+                       if ( DesignationGSET(m_byEscSeqIndex, byCode) ) {\r
+                               m_byEscSeqCount = 0;\r
+                               return;\r
+                       }\r
+                               \r
+                       switch ( byCode ) {\r
+                       case 0x20       : m_bIsEscSeqDrcs = true;       break;\r
+                       case 0x28       : m_bIsEscSeqDrcs = true;       m_byEscSeqIndex = 0;    break;\r
+                       case 0x29       : m_bIsEscSeqDrcs = false;      m_byEscSeqIndex = 1;    break;\r
+                       case 0x2A       : m_bIsEscSeqDrcs = false;      m_byEscSeqIndex = 2;    break;\r
+                       case 0x2B       : m_bIsEscSeqDrcs = false;      m_byEscSeqIndex = 3;    break;\r
+                       default         : m_byEscSeqCount = 0;          return;         // エラー\r
+                       }\r
+                       break;\r
+\r
+                       // 3バイト目\r
+               case 3  :\r
+                       if ( ! m_bIsEscSeqDrcs ) {\r
+                               if ( DesignationGSET(m_byEscSeqIndex, byCode) ) {\r
+                                       m_byEscSeqCount = 0;\r
+                                       return;\r
+                               }\r
+                       }\r
+                       else{\r
+                               if ( DesignationDRCS(m_byEscSeqIndex, byCode) ) {\r
+                                       m_byEscSeqCount = 0;\r
+                                       return;\r
+                               }\r
+                       }\r
+\r
+                       if ( byCode == 0x20 ) {\r
+                               m_bIsEscSeqDrcs = true;\r
+                       }\r
+                       else {\r
+                               // エラー\r
+                               m_byEscSeqCount = 0;\r
+                               return;\r
+                       }\r
+                       break;\r
+\r
+                       // 4バイト目\r
+               case 4  :\r
+                       DesignationDRCS(m_byEscSeqIndex, byCode);\r
+                       m_byEscSeqCount = 0;\r
+                       return;\r
+               }\r
+\r
+               m_byEscSeqCount++;\r
+       }\r
+\r
+       //\r
+       private static void LockingShiftGL(final int byIndexG) {\r
+               // LSx\r
+               m_pLockingGL = byIndexG;\r
+       }\r
+       \r
+       //\r
+       private static void LockingShiftGR(final int byIndexG)  {\r
+               // LSxR\r
+               m_pLockingGR = byIndexG;\r
+       }\r
+       \r
+       //\r
+       private static void SingleShiftGL(final int byIndexG) {\r
+               // SSx\r
+               m_pSingleGL  = byIndexG;\r
+       }\r
+       \r
+       //\r
+       private static final boolean DesignationGSET(final int byIndexG, final int byCode)\r
+       {\r
+               // Gのグラフィックセットを割り当てる\r
+               switch ( byCode ) {\r
+               case 0x42       : m_CodeG[byIndexG] = CODE_KANJI;                               return true;    // Kanji\r
+               case 0x4A       : m_CodeG[byIndexG] = CODE_ALPHANUMERIC;                return true;    // Alphanumeric\r
+               case 0x30       : m_CodeG[byIndexG] = CODE_HIRAGANA;                    return true;    // Hiragana\r
+               case 0x31       : m_CodeG[byIndexG] = CODE_KATAKANA;                    return true;    // Katakana\r
+               case 0x32       : m_CodeG[byIndexG] = CODE_MOSAIC_A;                    return true;    // Mosaic A\r
+               case 0x33       : m_CodeG[byIndexG] = CODE_MOSAIC_B;                    return true;    // Mosaic B\r
+               case 0x34       : m_CodeG[byIndexG] = CODE_MOSAIC_C;                    return true;    // Mosaic C\r
+               case 0x35       : m_CodeG[byIndexG] = CODE_MOSAIC_D;                    return true;    // Mosaic D\r
+               case 0x36       : m_CodeG[byIndexG] = CODE_PROP_ALPHANUMERIC;   return true;    // Proportional Alphanumeric\r
+               case 0x37       : m_CodeG[byIndexG] = CODE_PROP_HIRAGANA;               return true;    // Proportional Hiragana\r
+               case 0x38       : m_CodeG[byIndexG] = CODE_PROP_KATAKANA;               return true;    // Proportional Katakana\r
+               case 0x49       : m_CodeG[byIndexG] = CODE_JIS_X0201_KATAKANA;  return true;    // JIS X 0201 Katakana\r
+               case 0x39       : m_CodeG[byIndexG] = CODE_JIS_KANJI_PLANE_1;   return true;    // JIS compatible Kanji Plane 1\r
+               case 0x3A       : m_CodeG[byIndexG] = CODE_JIS_KANJI_PLANE_2;   return true;    // JIS compatible Kanji Plane 2\r
+               case 0x3B       : m_CodeG[byIndexG] = CODE_ADDITIONAL_SYMBOLS;  return true;    // Additional symbols\r
+               default         : return false;         // 不明なグラフィックセット\r
+               }\r
+       }\r
+\r
+       //\r
+       private static final boolean DesignationDRCS(final int byIndexG, final int byCode) {\r
+               // DRCSのグラフィックセットを割り当てる\r
+               switch ( byCode ) {\r
+               case 0x40       : m_CodeG[byIndexG] = CODE_UNKNOWN;                             return true;    // DRCS-0\r
+               case 0x41       : m_CodeG[byIndexG] = CODE_UNKNOWN;                             return true;    // DRCS-1\r
+               case 0x42       : m_CodeG[byIndexG] = CODE_UNKNOWN;                             return true;    // DRCS-2\r
+               case 0x43       : m_CodeG[byIndexG] = CODE_UNKNOWN;                             return true;    // DRCS-3\r
+               case 0x44       : m_CodeG[byIndexG] = CODE_UNKNOWN;                             return true;    // DRCS-4\r
+               case 0x45       : m_CodeG[byIndexG] = CODE_UNKNOWN;                             return true;    // DRCS-5\r
+               case 0x46       : m_CodeG[byIndexG] = CODE_UNKNOWN;                             return true;    // DRCS-6\r
+               case 0x47       : m_CodeG[byIndexG] = CODE_UNKNOWN;                             return true;    // DRCS-7\r
+               case 0x48       : m_CodeG[byIndexG] = CODE_UNKNOWN;                             return true;    // DRCS-8\r
+               case 0x49       : m_CodeG[byIndexG] = CODE_UNKNOWN;                             return true;    // DRCS-9\r
+               case 0x4A       : m_CodeG[byIndexG] = CODE_UNKNOWN;                             return true;    // DRCS-10\r
+               case 0x4B       : m_CodeG[byIndexG] = CODE_UNKNOWN;                             return true;    // DRCS-11\r
+               case 0x4C       : m_CodeG[byIndexG] = CODE_UNKNOWN;                             return true;    // DRCS-12\r
+               case 0x4D       : m_CodeG[byIndexG] = CODE_UNKNOWN;                             return true;    // DRCS-13\r
+               case 0x4E       : m_CodeG[byIndexG] = CODE_UNKNOWN;                             return true;    // DRCS-14\r
+               case 0x4F       : m_CodeG[byIndexG] = CODE_UNKNOWN;                             return true;    // DRCS-15\r
+               case 0x70       : m_CodeG[byIndexG] = CODE_UNKNOWN;                             return true;    // Macro\r
+               default         : return false;         // 不明なグラフィックセット\r
+               }\r
+       }\r
+\r
+}\r
diff --git a/TinyBannavi/src/epgdump/ContentDesc.java b/TinyBannavi/src/epgdump/ContentDesc.java
new file mode 100644 (file)
index 0000000..320339c
--- /dev/null
@@ -0,0 +1,9 @@
+package epgdump;\r
+\r
+public class ContentDesc {\r
+\r
+       int descriptor_tag;\r
+       int descriptor_length;\r
+       byte[] content = new byte[Util.MAXSECLEN];\r
+\r
+}\r
diff --git a/TinyBannavi/src/epgdump/EEVTDhead.java b/TinyBannavi/src/epgdump/EEVTDhead.java
new file mode 100644 (file)
index 0000000..1d9bcfd
--- /dev/null
@@ -0,0 +1,12 @@
+package epgdump;\r
+\r
+public class EEVTDhead {\r
+\r
+       int  descriptor_tag;\r
+       int  descriptor_length;\r
+       int  descriptor_number;\r
+       int  last_descriptor_number;\r
+       byte[] ISO_639_language_code = new byte[3];\r
+       int  length_of_items;\r
+\r
+}\r
diff --git a/TinyBannavi/src/epgdump/EEVTDitem.java b/TinyBannavi/src/epgdump/EEVTDitem.java
new file mode 100644 (file)
index 0000000..5a907de
--- /dev/null
@@ -0,0 +1,13 @@
+package epgdump;\r
+\r
+public class EEVTDitem {\r
+\r
+       int  item_description_length;\r
+       byte[] item_description = new byte[Util.MAXSECLEN];\r
+       int  item_length;\r
+       byte[] item = new byte[Util.MAXSECLEN];\r
+       \r
+       /* 退避用 */\r
+       //int  descriptor_number;\r
+\r
+}\r
diff --git a/TinyBannavi/src/epgdump/EEVTDtail.java b/TinyBannavi/src/epgdump/EEVTDtail.java
new file mode 100644 (file)
index 0000000..6c167bf
--- /dev/null
@@ -0,0 +1,8 @@
+package epgdump;\r
+\r
+public class EEVTDtail {\r
+\r
+       int  text_length;\r
+       byte[] text = new byte[Util.MAXSECLEN];\r
+\r
+}\r
diff --git a/TinyBannavi/src/epgdump/EIT_CONTROL.java b/TinyBannavi/src/epgdump/EIT_CONTROL.java
new file mode 100644 (file)
index 0000000..84974c3
--- /dev/null
@@ -0,0 +1,88 @@
+package epgdump;\r
+\r
+import java.util.ArrayList;\r
+\r
+public class EIT_CONTROL {\r
+\r
+       int             table_id ;\r
+       int             service_id ;\r
+       int             event_id ;                      // イベントID\r
+       \r
+    int                yy;\r
+    int                mm;\r
+    int                dd;\r
+    int                hh;\r
+    int                hm;\r
+       int             ss;\r
+       int             dhh;\r
+       int             dhm;\r
+       int             dss;\r
+       int             ehh;\r
+       int             ehm;\r
+       int             ess;\r
+       \r
+       String  title ;                 // タイトル\r
+       String  subtitle ;              // サブタイトル\r
+       String  detail ;                // 番組詳細\r
+       String  performer ;             // 出演者\r
+       \r
+       ArrayList<String> content_type = new ArrayList<String>() ;              // ジャンル情報\r
+       \r
+       /* 個々にもたせる必要はない。メモリを無駄に消費するだけだった。\r
+       // テンポラリ\r
+       byte[] d_tmp = new byte[Util.MAXSECLEN];\r
+       int d_tmp_len = -1;\r
+       byte[] p_tmp = new byte[Util.MAXSECLEN];\r
+       int p_tmp_len = -1;\r
+       String dDesc;\r
+       String pDesc;\r
+       */\r
+       \r
+       // XMLEncoder/Decoder用\r
+       public void setTable_id(int d) { table_id = d; }\r
+       public int getTable_id() { return table_id; }\r
+       public void setService_id(int d) { service_id = d; }\r
+       public int getService_id() { return service_id; }\r
+       public void setEvent_id(int d) { event_id = d; }\r
+       public int getEvent_id() { return event_id; }\r
+       \r
+       public void setYy(int d) { yy = d; }\r
+       public int getYy() { return yy; }\r
+       public void setMm(int d) { mm = d; }\r
+       public int getMm() { return mm; }\r
+       public void setDd(int d) { dd = d; }\r
+       public int getDd() { return dd; }\r
+       \r
+       public void setHh(int d) { hh = d; }\r
+       public int getHh() { return hh; }\r
+       public void setHm(int d) { hm = d; }\r
+       public int getHm() { return hm; }\r
+       public void setSs(int d) { ss = d; }\r
+       public int getSs() { return ss; }\r
+\r
+       public void setDhh(int d) { dhh = d; }\r
+       public int getDhh() { return dhh; }\r
+       public void setDhm(int d) { dhm = d; }\r
+       public int getDhm() { return dhm; }\r
+       public void setDss(int d) { dss = d; }\r
+       public int getDss() { return dss; }\r
+\r
+       public void setEhh(int d) { ehh = d; }\r
+       public int getEhh() { return ehh; }\r
+       public void setEhm(int d) { ehm = d; }\r
+       public int getEhm() { return ehm; }\r
+       public void setEss(int d) { ess = d; }\r
+       public int getEss() { return ess; }\r
+\r
+       public void setTitle(String s) { title = s; }\r
+       public String getTitle() { return title; }\r
+       public void setSubtitle(String s) { subtitle = s; }\r
+       public String getSubtitle() { return subtitle; }\r
+       public void setDetail(String s) { detail = s; }\r
+       public String getDetail() { return detail; }\r
+       public void setPerformer(String s) { performer = s; }\r
+       public String getPerformer() { return performer; }\r
+       \r
+       public void setContent_type(ArrayList<String> a) { content_type = a; }\r
+       public ArrayList<String> getContent_type() { return content_type; }\r
+}\r
diff --git a/TinyBannavi/src/epgdump/EITbody.java b/TinyBannavi/src/epgdump/EITbody.java
new file mode 100644 (file)
index 0000000..b854e2f
--- /dev/null
@@ -0,0 +1,22 @@
+package epgdump;\r
+\r
+public class EITbody {\r
+\r
+       int event_id;\r
+       byte[] start_time = new byte[5];\r
+       byte[] duration = new byte[3];\r
+       int running_status;\r
+       int free_CA_mode;\r
+       int descriptors_loop_length;\r
+       /* 以下は解析結果保存用 */\r
+       int yy;\r
+       int mm;\r
+       int dd;\r
+       int hh;\r
+       int hm;\r
+       int ss;\r
+       int dhh;\r
+       int dhm;\r
+       int dss;\r
+\r
+}\r
diff --git a/TinyBannavi/src/epgdump/EIThead.java b/TinyBannavi/src/epgdump/EIThead.java
new file mode 100644 (file)
index 0000000..62709f5
--- /dev/null
@@ -0,0 +1,21 @@
+package epgdump;\r
+\r
+public class EIThead {\r
+\r
+       byte table_id;\r
+       int section_syntax_indicator;\r
+       int reserved_future_use;\r
+       int reserved1;\r
+       int section_length;\r
+       int service_id;\r
+       int reserved2;\r
+       int version_number;\r
+       int current_next_indicator;\r
+       int section_number;\r
+       int last_section_number;\r
+       int transport_stream_id;\r
+       int original_network_id;\r
+       int segment_last_section_number;\r
+       int last_table_id;\r
+\r
+}\r
diff --git a/TinyBannavi/src/epgdump/Eit.java b/TinyBannavi/src/epgdump/Eit.java
new file mode 100644 (file)
index 0000000..c43c741
--- /dev/null
@@ -0,0 +1,601 @@
+package epgdump;\r
+\r
+import java.io.UnsupportedEncodingException;\r
+import java.util.ArrayList;\r
+\r
+public class Eit {\r
+\r
+       private static final byte[] convtmp = new byte[Util.MAXSECLEN];\r
+       \r
+       //\r
+       private int parseEIThead(byte[] data, EIThead h) {\r
+               int[] boff = {0};\r
+\r
+               //memset(h, 0, sizeof(EIThead));\r
+\r
+               h.table_id = (byte) Util.getBit(data, 0, boff, 8);\r
+               h.section_syntax_indicator = Util.getBit(data, 0, boff, 1);\r
+               h.reserved_future_use = Util.getBit(data, 0, boff, 1);\r
+               h.reserved1 = Util.getBit(data, 0, boff, 2);\r
+               h.section_length =Util.getBit(data, 0, boff,12);\r
+               h.service_id = Util.getBit(data, 0, boff, 16);\r
+               h.reserved2 = Util.getBit(data, 0, boff, 2);\r
+               h.version_number = Util.getBit(data, 0, boff, 5);\r
+               h.current_next_indicator = Util.getBit(data, 0, boff, 1);\r
+               h.section_number = Util.getBit(data, 0, boff, 8);\r
+               h.last_section_number = Util.getBit(data, 0, boff, 8);\r
+               h.transport_stream_id = Util.getBit(data, 0, boff, 16);\r
+               h.original_network_id = Util.getBit(data, 0, boff, 16);\r
+               h.segment_last_section_number = Util.getBit(data, 0, boff, 8);\r
+               h.last_table_id = Util.getBit(data, 0, boff, 8);\r
+         \r
+               return 14;\r
+       }\r
+\r
+       //\r
+       private int parseEITbody(byte[] data, int ptr, EITbody b) throws NumberFormatException {\r
+               int[] boff = {0};\r
+               int tnum;\r
+\r
+               //memset(b, 0, sizeof(EITbody));\r
+\r
+               b.event_id = Util.getBit(data, ptr, boff, 16);\r
+\r
+               System.arraycopy(data, ptr+boff[0]/8, b.start_time, 0, 5);\r
+               //memcpy(b.start_time, data + boff / 8, 5);\r
+               /* b.start_time = getBit(data, &boff, 40); */\r
+               boff[0] += 40;\r
+               System.arraycopy(data, ptr+boff[0]/8, b.duration, 0, 3);\r
+               //memcpy(b.duration, data + boff / 8, 3);\r
+               /* b.duration = getBit(data, &boff, 24); */\r
+               boff[0] += 24;\r
+               b.running_status = Util.getBit(data, ptr, boff, 3);\r
+               b.free_CA_mode = Util.getBit(data, ptr, boff, 1);\r
+               b.descriptors_loop_length = Util.getBit(data, ptr, boff, 12);\r
+\r
+               // 日付変換 - [鯛ナビ] どういう計算?\r
+               tnum = (b.start_time[0] & 0xFF) << 8 | (b.start_time[1] & 0xFF);\r
+               \r
+               b.yy = (int) ((tnum - 15078.2) / 365.25);\r
+               b.mm = (int) (((tnum - 14956.1) - (int)(b.yy * 365.25)) / 30.6001);\r
+               b.dd = (tnum - 14956) - (int)(b.yy * 365.25) - (int)(b.mm * 30.6001);\r
+\r
+               if ( b.mm == 14 || b.mm == 15 ) {\r
+                       b.yy += 1;\r
+                       b.mm = b.mm - 1 - (1 * 12);\r
+               }\r
+               else {\r
+                       b.mm = b.mm - 1;\r
+               }\r
+\r
+               b.yy += 1900;\r
+         \r
+               b.hh = Util.hex2dec(b.start_time[2]);\r
+               b.hm = Util.hex2dec(b.start_time[3]);\r
+               b.ss = Util.hex2dec(b.start_time[4]);\r
+               \r
+               if ( ((b.duration[0]&0xFF) == 0xFF) && ((b.duration[1]&0xFF) == 0xFF) && ((b.duration[2]&0xFF) == 0xFF) ) {\r
+                       b.dhh = b.dhm = b.dss = 0;\r
+               }\r
+               else {\r
+                       b.dhh = Util.hex2dec(b.duration[0]);\r
+                       b.dhm = Util.hex2dec(b.duration[1]);\r
+                       b.dss = Util.hex2dec(b.duration[2]);\r
+               }\r
+               \r
+               return 12;\r
+       }\r
+       \r
+       //\r
+       private EIT_CONTROL     searcheit(SVT_CONTROL svt, int servid, int eventid) {\r
+               if ( svt == null ) {\r
+                       return null;\r
+               }\r
+               for ( EIT_CONTROL cur : svt.eittop ) {\r
+                       if ( (cur.event_id == eventid) && (cur.service_id == servid) ){\r
+                               return cur ;\r
+                       }\r
+               }\r
+               return null ;\r
+       }\r
+\r
+       //\r
+       private int parseSEVTdesc(byte[] data, int ptr, SEVTdesc desc) {\r
+               int[] boff = {0};\r
+         \r
+               //memset(desc, 0, sizeof(SEVTdesc));\r
+\r
+               desc.descriptor_tag = Util.getBit(data, ptr, boff, 8);\r
+               if ( (desc.descriptor_tag & 0xFF) != ShortEventDescriptor ) {\r
+                       return -1;\r
+               }\r
+               desc.descriptor_length = Util.getBit(data, ptr, boff, 8);\r
+               System.arraycopy(data, ptr+boff[0]/8, desc.ISO_639_language_code, 0, 3);\r
+               //memcpy(desc.ISO_639_language_code, data + boff / 8, 3);\r
+               /* desc.ISO_639_language_code = Util.getBit(data, ptr, boff, 24); */\r
+               boff[0] += 24;\r
+               desc.event_name_length = Util.getBit(data, ptr, boff, 8);\r
+               Util.getStr(desc.event_name, data, ptr, boff, desc.event_name_length);\r
+               desc.text_length = Util.getBit(data, ptr, boff, 8);\r
+               Util.getStr(desc.text, data, ptr, boff, desc.text_length);\r
+\r
+               return desc.descriptor_length + 2;\r
+       }\r
+       \r
+       //\r
+       private int parseContentDesc(byte[] data, int ptr, ContentDesc desc) {\r
+               int[] boff = {0};\r
+         \r
+               //memset(desc, 0, sizeof(ContentDesc));\r
+       \r
+               desc.descriptor_tag = Util.getBit(data, ptr, boff, 8);\r
+               if ( (desc.descriptor_tag & 0xFF) != ContentDescriptor ) {\r
+                       return -1;\r
+               }\r
+               desc.descriptor_length = Util.getBit(data, ptr, boff, 8);\r
+               System.arraycopy(data, ptr+boff[0]/8, desc.content, 0, desc.descriptor_length);\r
+               //memcpy(desc.content, data+(boff/8), desc.descriptor_length);\r
+               //getStr(desc.content, data, &boff, desc.descriptor_length);\r
+               return desc.descriptor_length + 2;\r
+       }\r
+       \r
+       //\r
+       private int parseSeriesDesc(byte[] data, int ptr, SeriesDesc desc) {\r
+               int[] boff = {0};\r
+         \r
+               //memset(desc, 0, sizeof(SeriesDesc));\r
+\r
+               desc.descriptor_tag = Util.getBit(data, ptr, boff, 8);\r
+               if ( (desc.descriptor_tag & 0xFF) != 0xD5 ) {\r
+                       return -1;\r
+               }\r
+               desc.descriptor_length = Util.getBit(data, ptr, boff, 8);\r
+               desc.series_id = Util.getBit(data, ptr, boff, 16);\r
+               desc.repeat_label = Util.getBit(data, ptr, boff, 4);\r
+               desc.program_pattern = Util.getBit(data, ptr, boff, 3);\r
+               desc.expire_date_valid_flag = Util.getBit(data, ptr, boff, 1);\r
+\r
+               desc.expire_date = Util.getBit(data, ptr, boff, 16);\r
+               //memcpy(desc.expire_date, data + boff / 8, 2);\r
+               //boff += 16;\r
+\r
+               desc.episode_number = Util.getBit(data, ptr, boff, 12);\r
+               desc.last_episode_number = Util.getBit(data, ptr, boff, 12);\r
+\r
+               Util.getStr(desc.series_name_char, data, ptr, boff, desc.descriptor_length - 8);\r
+               return desc.descriptor_length + 2;\r
+       }\r
+\r
+\r
+       //\r
+       private int parseEEVTDhead(byte[] data, int ptr, EEVTDhead desc) {\r
+               int[] boff = {0};\r
+         \r
+               //memset(desc, 0, sizeof(EEVTDhead));\r
+       \r
+               desc.descriptor_tag = Util.getBit(data, ptr, boff, 8);\r
+               if ( (desc.descriptor_tag & 0xFF) != ExtendedEventDescriptor ) {\r
+                       return -1;\r
+               }\r
+               desc.descriptor_length = Util.getBit(data, ptr, boff, 8);\r
+               desc.descriptor_number = Util.getBit(data, ptr, boff, 4);\r
+               desc.last_descriptor_number = Util.getBit(data, ptr, boff, 4);\r
+               System.arraycopy(data, ptr+boff[0]/8, desc.ISO_639_language_code, 0, 3);\r
+               //memcpy(desc.ISO_639_language_code, data + boff[0] / 8, 3);\r
+               /* desc.ISO_639_language_code = Util.getBit(data, ptr, boff, 24); */\r
+               boff[0] += 24;\r
+       \r
+               desc.length_of_items = Util.getBit(data, ptr, boff, 8);\r
+       \r
+               return 7;\r
+       }\r
+       \r
+       //\r
+       private int parseEEVTDitem(byte[] data, int ptr, EEVTDitem desc) {\r
+               int[] boff = {0};\r
+         \r
+               //memset(desc, 0, sizeof(EEVTDitem));\r
+       \r
+               desc.item_description_length = Util.getBit(data, ptr, boff, 8);\r
+               System.arraycopy(data, ptr+boff[0]/8, desc.item_description, 0, desc.item_description_length);\r
+               //Util.getStr(desc.item_description, data, ptr, boff, desc.item_description_length);\r
+               boff[0] += desc.item_description_length * 8;\r
+       \r
+               desc.item_length = Util.getBit(data, ptr, boff, 8);\r
+               System.arraycopy(data, ptr+boff[0] / 8, desc.item, 0, desc.item_length);\r
+               //memcpy(desc.item, data + (boff / 8), desc.item_length);\r
+               /* getStr(desc.item, data, &boff, desc.item_length); */\r
+       \r
+               return desc.item_description_length + desc.item_length + 2;\r
+       }\r
+       \r
+       //\r
+       private int parseEEVTDtail(byte[] data, int ptr, EEVTDtail desc) {\r
+               int[] boff = {0};\r
+         \r
+               //memset(desc, 0, sizeof(EEVTDtail));\r
+\r
+               desc.text_length = Util.getBit(data, ptr, boff, 8);\r
+               Util.getStr(desc.text, data, ptr, boff, desc.text_length);\r
+\r
+               return desc.text_length + 1;\r
+       }\r
+       \r
+       // [鯛ナビ] タイトルの整形は鯛ナビ本体でやるので省略\r
+       private void enqueue(ArrayList<EIT_CONTROL> top, EIT_CONTROL eitptr) {\r
+               //\r
+               for ( int i=0; i<top.size(); i++ ) {\r
+                       EIT_CONTROL cur = top.get(i); \r
+                       int ryy = cur.yy - eitptr.yy;\r
+                       int rmm = cur.mm - eitptr.mm;\r
+                       int rdd = cur.dd - eitptr.dd;\r
+                       int rc = (ryy == 0)?((rmm == 0)?((rdd == 0)?(0):(rdd)):(rmm)):(ryy); \r
+                       if ( rc == 0 ) {\r
+                               int rhh = cur.hh - eitptr.hh;\r
+                               int rhm = cur.hm - eitptr.hm;\r
+                               int rss = cur.ss - eitptr.ss;\r
+                               rc = (rhh == 0)?((rhm == 0)?((rss == 0)?(0):(rss)):(rhm)):(rhh);\r
+                               if ( rc == 0 ) {\r
+                                       return;\r
+                               }\r
+                               if ( rc > 0 ) {\r
+                                       top.add(i,eitptr);\r
+                                       return;\r
+                               }\r
+                       }\r
+                       if ( rc > 0 ) {\r
+                               top.add(i,eitptr);\r
+                               return;\r
+                       }\r
+               }\r
+               top.add(eitptr);\r
+       }\r
+\r
+       //\r
+       private static SVT_CONTROL preSearchedSvt = null;\r
+       private SVT_CONTROL searchsvt(ArrayList<SVT_CONTROL> svttop, EIThead eith) {\r
+               if ( preSearchedSvt != null ) {\r
+                       if ( preSearchedSvt.enabled &&\r
+                                       eith.service_id == preSearchedSvt.servive_id &&\r
+                                       eith.original_network_id == preSearchedSvt.original_network_id &&\r
+                                       eith.transport_stream_id == preSearchedSvt.transport_stream_id ) {\r
+                               return preSearchedSvt;\r
+                       }\r
+               }\r
+               for ( SVT_CONTROL svtcur : svttop ) {\r
+                       if ( svtcur.enabled &&\r
+                                       eith.service_id == svtcur.servive_id &&\r
+                                       eith.original_network_id == svtcur.original_network_id &&\r
+                                       eith.transport_stream_id == svtcur.transport_stream_id ) {\r
+                               return preSearchedSvt = svtcur;\r
+                       }\r
+               }\r
+               return preSearchedSvt = null;\r
+       }\r
+\r
+       /*\r
+        *  [鯛ナビ] ここが肝\r
+        */\r
+       \r
+       private static final int ShortEventDescriptor           = 0x4D;\r
+       private static final int ExtendedEventDescriptor        = 0x4E;\r
+       private static final int ContentDescriptor                      = 0x54;\r
+       \r
+       private static byte[] d_tmp = new byte[Util.MAXSECLEN];\r
+       private static int d_tmp_len = -1;\r
+       private static byte[] p_tmp = new byte[Util.MAXSECLEN];\r
+       private static int p_tmp_len = -1;\r
+       private static String dDesc;\r
+       private static String pDesc;\r
+       \r
+       public boolean dumpEIT(byte[] data, ArrayList<SVT_CONTROL> svttop) {\r
+               try {\r
+                       return _dumpEIT(data, svttop);\r
+               }\r
+               catch ( NumberFormatException e ) {\r
+                       System.err.println(e.toString());\r
+               }\r
+               catch ( Exception e ) {\r
+                       e.printStackTrace();\r
+               }\r
+               return false;\r
+       }\r
+       private boolean _dumpEIT(byte[] data, ArrayList<SVT_CONTROL> svttop) throws UnsupportedEncodingException,NumberFormatException {\r
+               \r
+               // EIT\r
+               EIThead eith = new EIThead();\r
+               int len = parseEIThead(data, eith);\r
+               int ptr = len;\r
+               int loop_len = eith.section_length - (len - 3 + 4);     // 3は共通ヘッダ長 4はCRC\r
+               \r
+               SVT_CONTROL svt = searchsvt(svttop, eith);\r
+               \r
+               while ( loop_len > 0 ) {\r
+                       \r
+                       // 番組情報を一個ずつ処理する\r
+                       \r
+                       EITbody eitb = new EITbody();\r
+                       len = parseEITbody(data, ptr, eitb);\r
+                       ptr += len;\r
+                       loop_len -= len;\r
+\r
+                       // [鯛ナビ] ptr、loop_lenの扱いに不具合があったので修正\r
+                       int ptrbody = ptr;\r
+                       \r
+                       ptr += eitb.descriptors_loop_length;\r
+                       loop_len -= eitb.descriptors_loop_length;\r
+                       \r
+                       if ( svt == null ) {\r
+                               // 登録先がなければスキップ\r
+                               continue ;\r
+                       }\r
+\r
+                       if (Util.debug) System.err.println(String.format("on=%02x tr=%02x sv=%02x ev=%02x", eith.original_network_id, eith.transport_stream_id, eith.service_id, eitb.event_id));\r
+                   \r
+                       // [鯛ナビ] cur->ehh = eitb.dhhはバグ?\r
+                       EIT_CONTROL cur = searcheit(svt, eith.service_id, eitb.event_id);\r
+                       if ( cur == null ) {\r
+                               switch ( data[ptrbody]&0xFF ) {\r
+                               case ShortEventDescriptor:              // parseSEVTdesc - タイトル\r
+                               case ExtendedEventDescriptor:   // parseEEVTDhead - 番組詳細\r
+                               case ContentDescriptor:                 // parseContentDesc - ジャンル\r
+                                       break;\r
+                               default:\r
+                                       continue;       // 上記以外は無視しちゃえ\r
+                               }\r
+                               \r
+                               int[] ehms = { eitb.hh, eitb.hm, eitb.ss };\r
+                               timecmp(ehms, eitb.dhh, eitb.dhm, eitb.dss);\r
+                               int ehh = ehms[0];\r
+                               int ehm = ehms[1];\r
+                               int ess = ehms[2];\r
+                               \r
+                               cur = new EIT_CONTROL();\r
+                               cur.title = "(タイトルがありません)"; // タイトルがみつからない場合もあるので一応ダミーを設定しておく\r
+                               cur.event_id = eitb.event_id ;\r
+                               cur.service_id = eith.service_id ;\r
+                               cur.yy = eitb.yy;\r
+                               cur.mm = eitb.mm;\r
+                               cur.dd = eitb.dd;\r
+                               cur.hh = eitb.hh;\r
+                               cur.hm = eitb.hm;\r
+                               cur.ss = eitb.ss;\r
+                               cur.dhh = eitb.dhh;     // ここらへん\r
+                               cur.dhm = eitb.dhm;     // ここらへん\r
+                               cur.dss = eitb.dss;     // ここらへん\r
+                               cur.ehh = ehh;          // ここらへん\r
+                               cur.ehm = ehm;          // ここらへん\r
+                               cur.ess = ess ;         // ここらへん\r
+                               cur.table_id = eith.table_id ;\r
+                               enqueue(svt.eittop, cur);\r
+                               \r
+                               if ( Util.debug ) {\r
+                                       System.err.println(String.format("%04d-%02d-%02dT%02d:%02d - %02d:%02d - %02d:%02d - %s - %s",\r
+                                                       cur.yy,cur.mm,cur.dd,cur.hh,cur.hm,cur.ehh,cur.ehm,cur.dhh,cur.dhm,\r
+                                                       cur.title, cur.subtitle));\r
+                               }\r
+                       }\r
+                       \r
+                       // テンポラリをリセット\r
+                       p_tmp_len = -1;\r
+                       pDesc = null;\r
+                       d_tmp_len = -1;\r
+                       dDesc = null;\r
+                       \r
+                       int pretag = -1;\r
+                       int loop_blen = eitb.descriptors_loop_length;\r
+                       \r
+                       while ( loop_blen > 0 ) {\r
+                               SEVTdesc sevtd = new SEVTdesc();\r
+                               len = parseSEVTdesc(data, ptrbody, sevtd);\r
+                               if ( len > 0 ) {\r
+                                       \r
+                                       /*\r
+                                        * 基本情報\r
+                                        */\r
+                                       \r
+                                       cur.title = Util.getText(sevtd.event_name, Util.strlen(sevtd.event_name), Util.thisEncoding).trim();\r
+                                       cur.subtitle = Util.getText(sevtd.text, Util.strlen(sevtd.text), Util.thisEncoding).trim();\r
+                                       cur.subtitle = ( ! cur.subtitle.matches("^[  \\t\\r\\n]*$"))?(cur.subtitle):(null);\r
+                               }\r
+                               else {\r
+                                       \r
+                                       /*\r
+                                        * 詳細情報だが、基本情報よりも先に来る場合があるので注意 \r
+                                        */\r
+                                       \r
+                                       EEVTDhead eevthead = new EEVTDhead();\r
+                                       len = parseEEVTDhead(data, ptrbody, eevthead);\r
+\r
+                                       if ( len > 0 ) {\r
+                                               \r
+                                               // 連続する拡張イベントは、漢字コードが泣き別れして分割されるようだ。\r
+                                               // 連続かどうかは、item_description_lengthが設定されているかどうかで判断できるようだ。\r
+                                               \r
+                                               // 拡張形式イベント記述子の場合\r
+                                               ptrbody += len;\r
+                                               loop_blen -= len;\r
+                                               \r
+                                               int loop_elen = eevthead.length_of_items;\r
+                                               while ( loop_elen > 0 ) {\r
+                                                       EEVTDitem eevtitem = new EEVTDitem();\r
+                                                       len = parseEEVTDitem(data, ptrbody, eevtitem);\r
+                                                       \r
+                                                       /* 例:\r
+                                                               on=04 tr=40d1 sv=b5 ev=44dd\r
+                                                               # 08 210 200 8 <-- ここから\r
+                                                               # 00 90 88 0\r
+                                                               # 08 210 200 8\r
+                                                               # 00 108 106 0\r
+                                                               # 08 210 200 8\r
+                                                               # 00 97 95 0\r
+                                                               # 08 210 200 8\r
+                                                               # 00 29 27 0   <-- ここまで連続している\r
+                                                               # 06 163 155 6\r
+                                                       */\r
+                                                       \r
+                                                       int curtag = data[ptrbody]&0xFF;\r
+                                                       String curdesc = getDesc(eevtitem);\r
+                                                       \r
+                                                       if (Util.debug) System.err.println(String.format("# %02x %d %d %d %s",curtag,len,eevtitem.item_length,eevtitem.item_description_length,curdesc));\r
+                                               \r
+                                                       if ( d_tmp_len == -1 ) {\r
+                                                               // 初回ならリセットしよう\r
+                                                               cur.detail = "";\r
+                                                               d_tmp_len = 0;\r
+                                                               cur.performer = "";\r
+                                                               p_tmp_len = 0;\r
+\r
+                                                       }\r
+                                                       \r
+                                                       attachDesc(cur,eevtitem,curdesc,curtag,pretag);\r
+                                                       if ( curtag != 0x00 ) {\r
+                                                               pretag = curtag;\r
+                                                       }\r
+\r
+                                                       ptrbody += len;\r
+                                                       loop_elen -= len;\r
+                                                       loop_blen -= len;\r
+                                               }\r
+                                               \r
+                                               EEVTDtail eevttail = new EEVTDtail();\r
+                                               len = parseEEVTDtail(data, ptrbody, eevttail);\r
+                                       }\r
+                                       else {\r
+                                               \r
+                                               /*\r
+                                                * ここから先は、実は使っていない \r
+                                                */\r
+                                               \r
+                                               // 拡張形式イベント記述子以外の場合\r
+                                               ContentDesc contentDesc = new ContentDesc();\r
+                                               len = parseContentDesc(data, ptrbody, contentDesc);\r
+                                               \r
+                                               if ( len > 0 ) {\r
+                                                       if ( cur != null ) {\r
+                                                               if ( cur.content_type.size() == 0 ) {\r
+                                                                       for ( int i=0; i<contentDesc.descriptor_length; i+=2 ) {\r
+                                                                               cur.content_type.add(String.format("%02X", contentDesc.content[i]&0xFF));\r
+                                                                       }\r
+                                                               }\r
+                                                               else {\r
+                                                                       //System.err.println("[DUP G]");\r
+                                                               }\r
+                                                       }\r
+                                               }\r
+                                               else {\r
+                                                       SeriesDesc seriesDesc = new SeriesDesc();\r
+                                                       len = parseSeriesDesc(data, ptrbody, seriesDesc);\r
+                                                       if ( len > 0 ) {\r
+                                                               // 処理はない\r
+                                                       }\r
+                                                       else {\r
+                                                               len = Util.parseOTHERdesc(data, ptrbody);\r
+                                                       }\r
+                                               }\r
+                                       }\r
+                               }\r
+                               \r
+                               ptrbody += len;\r
+                               loop_blen -= len;\r
+                       }\r
+                       \r
+                       // 拡張形式イベント記述子のまとめ処理\r
+                       attachDesc(cur, null, null, -1, -1);\r
+                       if (cur.detail != null) cur.detail = cur.detail.replaceFirst("\n+$", "");\r
+                       if (cur.performer != null) cur.performer = cur.performer.replaceFirst("\n+$", "");\r
+               }\r
+               \r
+               return true;\r
+       }\r
+\r
+       // 拡張形式イベント記述子を番組詳細として再構築するよ\r
+       private void attachDesc(EIT_CONTROL cur, EEVTDitem eevtitem, String curdesc, int curtag, int pretag) {\r
+               if ( eevtitem == null || curtag == 0x06 || (curtag == 0x00 && pretag == 0x06) ) {\r
+                       // 出演者情報(それ以外もあるっぽいけど)\r
+                       if ( curtag != 0x00 ) {\r
+                               // 先頭タグで\r
+                               if ( p_tmp_len > 0 ) {\r
+                                       // なんかデータあるなら文字列化しておく\r
+                                       if ( pDesc != null && pDesc.length() > 0) {\r
+                                               if ( ! pDesc.matches("^[\\[[((【].*") ) {\r
+                                                       cur.performer += "【"+pDesc+"】\n";\r
+                                               }\r
+                                               else {\r
+                                                       cur.performer += pDesc+"\n";\r
+                                               }\r
+                                       }\r
+                                       int[] bo = {0};\r
+                                       int ln = Util.getStr(convtmp, p_tmp, 0, bo, p_tmp_len);\r
+                                       cur.performer += Util.getText(convtmp, ln, Util.thisEncoding).trim()+"\n\n";\r
+                                       p_tmp_len = 0;\r
+                               }\r
+                               //\r
+                               pDesc = curdesc;\r
+                       }\r
+                       if ( eevtitem != null ) {\r
+                               // まとめ処理の場合はここは通らない\r
+                               System.arraycopy(eevtitem.item, 0, p_tmp, p_tmp_len, eevtitem.item_length);\r
+                               p_tmp_len += eevtitem.item_length;\r
+                       }\r
+               }\r
+               if ( eevtitem == null || (curtag != 0x00 && curtag != 0x06) || (curtag == 0x00 && pretag != 0x06) ) {\r
+                       // 出演者情報以外\r
+                       if ( curtag != 0x00 ) {\r
+                               // 先頭タグで\r
+                               if ( d_tmp_len > 0 ) {\r
+                                       // なんかデータあるなら文字列化しておく\r
+                                       if ( dDesc != null && dDesc.length() > 0) {\r
+                                               if ( ! dDesc.matches("^[\\[[((【].*") ) {\r
+                                                       cur.detail += "【"+dDesc+"】\n";\r
+                                               }\r
+                                               else {\r
+                                                       cur.detail += dDesc+"\n";\r
+                                               }\r
+                                       }\r
+                                       int[] bo = {0};\r
+                                       int ln = Util.getStr(convtmp, d_tmp, 0, bo, d_tmp_len);\r
+                                       cur.detail += Util.getText(convtmp, ln, Util.thisEncoding).trim()+"\n\n";\r
+                                       d_tmp_len = 0;\r
+                               }\r
+                               //\r
+                               dDesc = curdesc;\r
+                       }\r
+                       if ( eevtitem != null ) {\r
+                               // まとめ処理の場合はここは通らない\r
+                               System.arraycopy(eevtitem.item, 0, d_tmp, d_tmp_len, eevtitem.item_length);\r
+                               d_tmp_len += eevtitem.item_length;\r
+                       }\r
+               }\r
+       }\r
+       \r
+       //\r
+       private String getDesc(EEVTDitem eevtitem) {\r
+               if ( eevtitem.item_description_length > 0 ) {\r
+                       // どうも"番組内容"とか"制作著作"とかかいてあるみたい\r
+                       int[] bo = {0};\r
+                       int ln = Util.getStr(convtmp, eevtitem.item_description, 0, bo, eevtitem.item_description_length);\r
+                       return Util.getText(convtmp, ln, Util.thisEncoding);\r
+               }\r
+               return null;\r
+       }\r
+       \r
+       //\r
+       private void timecmp(int[] thms, int dhh, int dmm, int dss) {\r
+       \r
+               int ama;\r
+       \r
+               thms[2] += dss;\r
+               ama = thms[2] % 60;\r
+               thms[1] += (thms[2] / 60);\r
+               thms[2] = ama;\r
+       \r
+               thms[1] += dmm;\r
+               ama   = thms[1] % 60;\r
+               thms[0] += (thms[1] / 60);\r
+               thms[1]  = ama;\r
+       \r
+               thms[0] += dhh;\r
+       \r
+       }\r
+}\r
diff --git a/TinyBannavi/src/epgdump/Epgdump.java b/TinyBannavi/src/epgdump/Epgdump.java
new file mode 100644 (file)
index 0000000..a646bca
--- /dev/null
@@ -0,0 +1,194 @@
+package epgdump;\r
+\r
+import java.io.FileInputStream;\r
+import java.io.PushbackInputStream;\r
+import java.util.ArrayList;\r
+\r
+/*\r
+ * 本パッケージは epgdumpr2 (tomy ◆CfWlfzSGyg氏) をベースに鯛ナビ用のJavaコードに書き換えたものです。\r
+ * 本パッケージのライセンスは原著を踏襲するものとします。\r
+ * \r
+ * 以下 readme.txtよりの抜粋\r
+ * ----------\r
+ * xmltv-epg\r
+ * \r
+ * MPEG-TSに含まれるepgをxmlで出力するプログラムです。\r
+ * ◆N/E9PqspSk氏がrecfriio Solaris版(http://2sen.dip.jp/cgi-bin/friioup/source/up0737.zip)に含まれるepgdumpを\r
+ * Linux版を改造したものをベースにxmltv用のxmlファイルを作成します。\r
+ * \r
+ * (中略)\r
+ * \r
+ * epgdumpライセンス(Solaris版より引用):\r
+ * >epgdumpに関しては、BonTest Ver.1.40からそのままソースを持ってきている部分も\r
+ * >あるため、そのライセンスに従いします。\r
+ * >BonTestのReadme.txtより\r
+ * >>\r
+ * >>3.ライセンスについて\r
+ * >>  ・本パッケージに含まれる全てのソースコード、バイナリについて著作権は一切主張しません。\r
+ * >>  ・オリジナルのまま又は改変し、各自のソフトウェアに自由に添付、組み込むことができます。\r
+ * >>  ・但しGPLに従うことを要求しますのでこれらを行う場合はソースコードの開示が必須となります。\r
+ * >>  ・このとき本ソフトウェアの著作権表示を行うかどうかは任意です。\r
+ * >>  ・本ソフトウェアはFAAD2のライブラリ版バイナリを使用しています。\r
+ * >>\r
+ * >>   "Code from FAAD2 is copyright (c) Nero AG, www.nero.com"\r
+ * >>\r
+ * >>  ・ビルドに必要な環境\r
+ * >>   - Microsoft Visual Studio 2005 以上 ※MFCが必要\r
+ * >>   - Microsoft Windows SDK v6.0 以上  ※DirectShow基底クラスのコンパイル済みライブラリが必要\r
+ * >>   - Microsoft DirectX 9.0 SDK 以上\r
+ * \r
+ * Special Thanks:\r
+ * ・Solaris版開発者の方\r
+ * ・拡張ツール中の人\r
+ * ・◆N/E9PqspSk氏\r
+ * ・ARIB(資料の無料ダウンロードに対して)\r
+ * \r
+ */\r
+\r
+/*\r
+ * 書き換え中に見つかった元ソースに存在する既存バグは以下の通り。応急処置はしたが「例外を回避する」ことしかしてないので正しい動作かどうかは不明。\r
+ * \r
+ * 1. TSpacket.payloadの操作でoverrunが発生する(Cだと厳密に境界チェックされないので特にどうこうはないが) →応急処置2箇所:Sdt.java、TSpacket.java\r
+ * 2. SECcache.bufの操作でoverrunが発生する(こちらはcygwinバイナリでもコアダンプが発生しているので問題) →応急処置1箇所:SECcache.java\r
+ * 3. Eit.dumpEITの操作でポインタの操作ミスがあった(詳細データの取得漏れが発生していた) →処置済み:dumpEIT()は全般的に整理\r
+ */\r
+\r
+public class Epgdump {\r
+\r
+       // 興味のあるpidを指定\r
+       private static final int[] pids = {0x11,0x12,0x26,0x27};\r
+\r
+       private static final int SECCOUNT = 4;\r
+       \r
+       private static final Ts ts = new Ts();\r
+       private static final Sdt sdt = new Sdt();\r
+       private static final Eit eit = new Eit();\r
+       \r
+       //\r
+       private void GetSDT(PushbackInputStream infile, ArrayList<SVT_CONTROL> svttop, ArrayList<SECcache> secs, int count) {\r
+               SECcache bsecs;\r
+               \r
+               while ( (bsecs = ts.readTS(infile, secs, count)) != null ) {\r
+                       // SDT\r
+                       if ( (bsecs.pid & 0xFF) == 0x11 ) {\r
+                               sdt.dumpSDT(bsecs.buf, svttop);\r
+                       }\r
+               }\r
+       }\r
+       \r
+       //\r
+       private void GetEIT(PushbackInputStream infile, ArrayList<SVT_CONTROL> svttop, ArrayList<SECcache> secs, int count) {\r
+               SECcache bsecs;\r
+               \r
+               while ( (bsecs = ts.readTS(infile, secs, count)) != null ) {\r
+                       // EIT\r
+                       switch ( bsecs.pid & 0xFF ) {\r
+                       case 0x12:\r
+                       case 0x26:\r
+                       case 0x27:\r
+                               eit.dumpEIT(bsecs.buf, svttop);\r
+                               break;\r
+                       }\r
+               }\r
+       }\r
+       \r
+       //\r
+       public ArrayList<SVT_CONTROL> getSvtControl(String file) {\r
+               \r
+               // 興味のあるpidを指定\r
+               ArrayList<SECcache> secs = new ArrayList<SECcache>();\r
+               for ( int pid : pids ) {\r
+                       SECcache sec = new SECcache(); \r
+                       sec.pid = pid;\r
+                       secs.add(sec);\r
+               }\r
+               \r
+               FileInputStream in = null;\r
+               PushbackInputStream infile = null;\r
+               try {\r
+                       in = new FileInputStream(file); \r
+                       infile = new PushbackInputStream(in);\r
+                       ArrayList<SVT_CONTROL> svttop = new ArrayList<SVT_CONTROL>();\r
+                       GetSDT(infile, svttop, secs, SECCOUNT);\r
+                       if ( svttop.size() > 0 ) {\r
+                               return svttop;\r
+                       }\r
+               }\r
+               catch ( Exception e ) {\r
+                       e.printStackTrace();\r
+               }\r
+               finally {\r
+                       if ( infile != null ) try { infile.close(); } catch (Exception e) {}\r
+                       if ( in != null ) try { in.close(); } catch (Exception e) {}\r
+               }\r
+               return null;\r
+       }\r
+\r
+       //\r
+       public ArrayList<SVT_CONTROL> getEitControl(String file, ArrayList<SVT_CONTROL> svttop) {\r
+               \r
+               // 興味のあるpidを指定\r
+               ArrayList<SECcache> secs = new ArrayList<SECcache>();\r
+               for ( int pid : pids ) {\r
+                       SECcache sec = new SECcache(); \r
+                       sec.pid = pid;\r
+                       secs.add(sec);\r
+               }\r
+               \r
+               FileInputStream in = null;\r
+               PushbackInputStream infile = null;\r
+               try {\r
+                       in = new FileInputStream(file); \r
+                       infile = new PushbackInputStream(in);\r
+                       GetEIT(infile, svttop, secs, SECCOUNT);\r
+                       return svttop;\r
+               }\r
+               catch ( Exception e ) {\r
+                       e.printStackTrace();\r
+               }\r
+               finally {\r
+                       if ( infile != null ) try { infile.close(); } catch (Exception e) {}\r
+                       if ( in != null ) try { in.close(); } catch (Exception e) {}\r
+               }\r
+               return null;\r
+       }\r
+       \r
+       //\r
+       public void enqueueSVT(ArrayList<SVT_CONTROL> svta, SVT_CONTROL svtnew) {\r
+               if ( svtnew.servicename.matches("^[-\\s]*$") ) {\r
+                       // 拒否\r
+                       return;\r
+               }\r
+               int i=0;\r
+               for ( ; i<svta.size(); i++ ) {\r
+                       SVT_CONTROL svtcur = svta.get(i);\r
+                       //\r
+                       if ( svtcur.original_network_id < svtnew.original_network_id ) {\r
+                               continue;\r
+                       }\r
+                       else if ( svtcur.original_network_id > svtnew.original_network_id ) {\r
+                               // 決定\r
+                               break;\r
+                       }\r
+                       //\r
+                       if ( svtcur.transport_stream_id < svtnew.transport_stream_id ) {\r
+                               continue;\r
+                       }\r
+                       else if ( svtcur.transport_stream_id > svtnew.transport_stream_id ) {\r
+                               break;\r
+                       }\r
+                       //\r
+                       if ( svtcur.servive_id == svtnew.servive_id ) {\r
+                               // 拒否\r
+                               return;\r
+                       }\r
+                       else if ( svtcur.servive_id < svtnew.servive_id ) {\r
+                               continue;\r
+                       }\r
+                       else {\r
+                               break;\r
+                       }\r
+               }\r
+               svta.add(i,svtnew);\r
+       }\r
+}\r
diff --git a/TinyBannavi/src/epgdump/Main.java b/TinyBannavi/src/epgdump/Main.java
new file mode 100644 (file)
index 0000000..85cbb4d
--- /dev/null
@@ -0,0 +1,127 @@
+package epgdump;\r
+\r
+import java.io.File;\r
+import java.io.UnsupportedEncodingException;\r
+import java.util.ArrayList;\r
+\r
+/*\r
+ * これはデバッグ用のダミーコードです。\r
+ * 実際の本体はEpgdump.javaです。\r
+ */\r
+\r
+public class Main {\r
+\r
+       /**\r
+        * @param args\r
+        * @throws UnsupportedEncodingException \r
+        */\r
+       @SuppressWarnings("unchecked")\r
+       public static void main(String[] args) throws UnsupportedEncodingException {\r
+               \r
+               Epgdump epg = new Epgdump();\r
+               \r
+               int mode = 0;\r
+               \r
+               boolean showSvt = false;\r
+               boolean showEit = true;\r
+               int evid = 0xB6B0;\r
+               //int evid = -1;\r
+               \r
+               String root;\r
+               String fnext;\r
+               String fnpre = "00044671";\r
+               //String fnpre = null;\r
+               if ( mode == 1 ) {\r
+                       root = "F:/Videos/";\r
+                       fnext = ".ts";\r
+               }\r
+               else {\r
+                       root = "D:/PT2/EpgDataCap_Bon/Setting/EpgData/";\r
+                       fnext = ".dat";\r
+               }\r
+               \r
+               new File("Z:/epgdump.log").delete();\r
+               System.setOut(new tainavi.DebugPrintStream(System.out,"Z:/epgdump.log",true));\r
+               System.setErr(new tainavi.DebugPrintStream(System.err,"Z:/epgdump.log",true));\r
+               \r
+               ArrayList<SVT_CONTROL> svta = null;\r
+               File d = new File(root);\r
+               if ( d.isDirectory() ) {\r
+                       svta = (ArrayList<SVT_CONTROL>) tainavi.CommonUtils.readXML("Z:/svt.xml");\r
+                       if ( svta == null ) {\r
+                               svta  = new ArrayList<SVT_CONTROL>();\r
+                               // SVT\r
+                               for ( String fn : d.list() ) {\r
+                                       File f = new File(root+fn);\r
+                                       if ( f.isFile() && fn.endsWith(fnext) ) {\r
+                                               System.out.print("fn="+fn);\r
+                                               ArrayList<SVT_CONTROL> svttop = epg.getSvtControl(root+fn);\r
+                                               for ( SVT_CONTROL svt : svttop ) {\r
+                                                       epg.enqueueSVT(svta, svt);\r
+                                               }\r
+                                               System.out.println(", sz="+svta.size());\r
+                                       }\r
+                               }\r
+                               \r
+                               tainavi.CommonUtils.writeXML("Z:/svt.xml",svta);\r
+                       }\r
+                       for ( SVT_CONTROL svt : svta ) {\r
+                               svt.enabled = true;\r
+                               if ( showSvt ) {\r
+                                       System.out.print(String.format("+ onid=%04x",svt.original_network_id));\r
+                                       System.out.print(String.format(" tid=%04x",svt.transport_stream_id));\r
+                                       System.out.print(String.format(" evid=%04x",svt.servive_id));\r
+                                       System.out.println(String.format(" %s",svt.servicename));\r
+                               }\r
+                       }\r
+                       \r
+                       // EIT\r
+                       for ( String fn : d.list() ) {\r
+                               File f = new File(root+fn);\r
+                               if ( f.isFile() && fn.endsWith(fnext) && (fnpre == null || fnpre!=null && fn.startsWith(fnpre)) ) {\r
+                                       System.out.print(fn);\r
+                                       epg.getEitControl(root+fn, svta);\r
+                                       int cnt = 0;\r
+                                       int cnte = 0;\r
+                                       for ( SVT_CONTROL svt : svta ) {\r
+                                               for ( EIT_CONTROL eit : svt.eittop ) {\r
+                                                       if (eit.title!=null&&eit.content_type.size()>0) cnt++;\r
+                                               }\r
+                                               cnte += svt.eittop.size();\r
+                                       }\r
+                                       System.out.println(", sz="+cnt+"/"+cnte);\r
+                               }\r
+                       }\r
+                       if ( showEit ) {\r
+                               for ( SVT_CONTROL svt : svta ) {\r
+                                       System.out.println("@ "+svt.servicename);\r
+                                       if ( svt.eittop.size() <= 0 ) {\r
+                                               continue;\r
+                                       }\r
+                                       System.out.print(String.format("+ onid=%04x",svt.original_network_id));\r
+                                       System.out.print(String.format(" tid=%04x",svt.transport_stream_id));\r
+                                       System.out.print(String.format(" svid=%04x",svt.servive_id));\r
+                                       System.out.println(String.format(" %s",svt.servicename));\r
+                                       for ( EIT_CONTROL eit : svt.eittop ) {\r
+                                               if ( evid != -1 && eit.event_id != evid ) {\r
+                                                       continue;\r
+                                               }\r
+                                               if ( eit.title == null ) {\r
+                                                       continue;\r
+                                               }\r
+                                               System.out.println(String.format("%04x %04d-%02d-%02dT%02d:%02d - %02d:%02d - %02d:%02d - %s - %s - %s",\r
+                                                               eit.event_id,eit.yy,eit.mm,eit.dd,eit.hh,eit.hm,eit.ehh,eit.ehm,eit.dhh,eit.dhm,\r
+                                                               eit.title, eit.subtitle, (eit.detail!=null)?(eit.detail.replaceAll("\n", "+")):("")));\r
+                                               if (eit.detail != null) System.out.println("+ "+eit.detail);\r
+                                               if (eit.performer != null) System.out.println("- "+eit.performer);\r
+                                               for ( int i=0; i<eit.content_type.size(); i++ ) {\r
+                                                       System.out.print(String.format(" * %s",eit.content_type.get(i)));\r
+                                               }\r
+                                               System.out.println("");\r
+                                       }\r
+                               }\r
+                       }\r
+               }\r
+       }\r
+\r
+}\r
diff --git a/TinyBannavi/src/epgdump/SDTbody.java b/TinyBannavi/src/epgdump/SDTbody.java
new file mode 100644 (file)
index 0000000..04f678e
--- /dev/null
@@ -0,0 +1,14 @@
+package epgdump;\r
+\r
+public class SDTbody {\r
+\r
+       int  service_id;\r
+       int  reserved_future_use1;\r
+       int  EIT_user_defined_flags;\r
+       int  EIT_schedule_flag;\r
+       int  EIT_present_following_flag;\r
+       int  running_status;\r
+       int  free_CA_mode;\r
+       int  descriptors_loop_length;\r
+\r
+}\r
diff --git a/TinyBannavi/src/epgdump/SDThead.java b/TinyBannavi/src/epgdump/SDThead.java
new file mode 100644 (file)
index 0000000..c21e22c
--- /dev/null
@@ -0,0 +1,19 @@
+package epgdump;\r
+\r
+public class SDThead {\r
+\r
+       int  table_id;\r
+       int  section_syntax_indicator;\r
+       int  reserved_future_use1;\r
+       int  reserved1;\r
+       int  section_length;\r
+       int  transport_stream_id;\r
+       int  reserved2;\r
+       int  version_number;\r
+       int  current_next_indicator;\r
+       int  section_number;\r
+       int  last_section_number;\r
+       int  original_network_id;\r
+       int  reserved_future_use2;\r
+\r
+}\r
diff --git a/TinyBannavi/src/epgdump/SECcache.java b/TinyBannavi/src/epgdump/SECcache.java
new file mode 100644 (file)
index 0000000..4c889bd
--- /dev/null
@@ -0,0 +1,13 @@
+package epgdump;\r
+\r
+public class SECcache {\r
+       \r
+       int                     pid;\r
+       byte[]          buf = new byte[Util.MAXSECLEN+2];       // [鯛ナビ] 元ソースには+2しないといけないバグがあるっぽい\r
+       int                     seclen;\r
+       int                     setlen;\r
+       TSpacket        cur = new TSpacket();\r
+       int                     curlen;\r
+       int                     cont;\r
+\r
+}\r
diff --git a/TinyBannavi/src/epgdump/SEVTdesc.java b/TinyBannavi/src/epgdump/SEVTdesc.java
new file mode 100644 (file)
index 0000000..85b4686
--- /dev/null
@@ -0,0 +1,13 @@
+package epgdump;\r
+\r
+public class SEVTdesc {\r
+\r
+       int  descriptor_tag;\r
+       int  descriptor_length;\r
+       byte[] ISO_639_language_code = new byte[3];\r
+       int  event_name_length;\r
+       byte[] event_name = new byte[Util.MAXSECLEN];\r
+       int  text_length;\r
+       byte[] text = new byte[Util.MAXSECLEN];\r
+\r
+}\r
diff --git a/TinyBannavi/src/epgdump/STATION.java b/TinyBannavi/src/epgdump/STATION.java
new file mode 100644 (file)
index 0000000..c89b9ac
--- /dev/null
@@ -0,0 +1,11 @@
+package epgdump;\r
+\r
+public class STATION {\r
+       \r
+       byte[]  name;\r
+       //byte[]        ontv;\r
+       int             tsId;           // OriginalNetworkID\r
+       int             onId;           // TransportStreamID\r
+       int             svId;           // ServiceID\r
+\r
+}\r
diff --git a/TinyBannavi/src/epgdump/SVCdesc.java b/TinyBannavi/src/epgdump/SVCdesc.java
new file mode 100644 (file)
index 0000000..2621b95
--- /dev/null
@@ -0,0 +1,13 @@
+package epgdump;\r
+\r
+public class SVCdesc {\r
+\r
+       int  descriptor_tag;\r
+       int  descriptor_length;\r
+       int  service_type;\r
+       int  service_provider_name_length;\r
+       byte[] service_provider_name = new byte[Util.MAXSECLEN];\r
+       int  service_name_length;\r
+       byte[] service_name = new byte[Util.MAXSECLEN];\r
+\r
+}\r
diff --git a/TinyBannavi/src/epgdump/SVT_CONTROL.java b/TinyBannavi/src/epgdump/SVT_CONTROL.java
new file mode 100644 (file)
index 0000000..31bdde4
--- /dev/null
@@ -0,0 +1,30 @@
+package epgdump;\r
+\r
+import java.util.ArrayList;\r
+\r
+public class SVT_CONTROL {\r
+\r
+       int             servive_id ;                            // イベントID\r
+       int             original_network_id ;   // OriginalNetworkID\r
+       int             transport_stream_id ;   // TransporrtStreamID\r
+       String  servicename ;                   // サービス名\r
+\r
+       boolean enabled = false;\r
+       \r
+       ArrayList<EIT_CONTROL> eittop = new ArrayList<EIT_CONTROL>();\r
+       \r
+       \r
+       // XMLEncoder/Decoder用\r
+       public void setServive_id(int d) { servive_id = d; }\r
+       public int getServive_id() { return servive_id; }\r
+       public void setOriginal_network_id(int d) { original_network_id = d; }\r
+       public int getOriginal_network_id() { return original_network_id; }\r
+       public void setTransport_stream_id(int d) { transport_stream_id = d; }\r
+       public int getTransport_stream_id() { return transport_stream_id; }\r
+       public void setServicename(String s) { servicename = s; }\r
+       public String getServicename() { return servicename; }\r
+       public void setEnabled(boolean b) { enabled = true; }\r
+       public boolean getEnabled() { return enabled; }\r
+       public void setEittop(ArrayList<EIT_CONTROL> a) { eittop = a; }\r
+       public ArrayList<EIT_CONTROL> getEittop() { return eittop; }\r
+}\r
diff --git a/TinyBannavi/src/epgdump/Sdt.java b/TinyBannavi/src/epgdump/Sdt.java
new file mode 100644 (file)
index 0000000..a36c072
--- /dev/null
@@ -0,0 +1,133 @@
+package epgdump;\r
+\r
+import java.io.UnsupportedEncodingException;\r
+import java.util.ArrayList;\r
+\r
+public class Sdt {\r
+\r
+       //\r
+       private int parseSDThead(byte[] data, int ptr, SDThead h) {\r
+               int[] boff = {0};\r
+\r
+               //memset(h, 0, sizeof(SDThead));\r
+\r
+               h.table_id = Util.getBit(data, ptr, boff, 8);\r
+               h.section_syntax_indicator = Util.getBit(data, ptr, boff, 1);\r
+               h.reserved_future_use1 = Util.getBit(data, ptr, boff, 1);\r
+               h.reserved1 = Util.getBit(data, ptr, boff, 2);\r
+               h.section_length = Util.getBit(data, ptr, boff, 12);\r
+               h.transport_stream_id = Util.getBit(data, ptr, boff, 16);\r
+               h.reserved2 = Util.getBit(data, ptr, boff, 2);\r
+               h.version_number = Util.getBit(data, ptr, boff, 5);\r
+               h.current_next_indicator = Util.getBit(data, ptr, boff, 1);\r
+               h.section_number = Util.getBit(data, ptr, boff, 8);\r
+               h.last_section_number = Util.getBit(data, ptr, boff, 8);\r
+               h.original_network_id = Util.getBit(data, ptr, boff, 16);\r
+               h.reserved_future_use2 = Util.getBit(data, ptr, boff, 8);\r
+\r
+               return 11;\r
+       }\r
+       \r
+       //\r
+       private int parseSDTbody(byte[] data, int ptr, SDTbody b) {\r
+               int[] boff = {0};\r
+\r
+               //memset(b, 0, sizeof(SDTbody));\r
+\r
+               b.service_id = Util.getBit(data, ptr, boff, 16);\r
+               b.reserved_future_use1 = Util.getBit(data, ptr, boff, 3);\r
+               b.EIT_user_defined_flags = Util.getBit(data, ptr, boff, 3);\r
+               b.EIT_schedule_flag = Util.getBit(data, ptr, boff, 1);\r
+               b.EIT_present_following_flag = Util.getBit(data, ptr, boff, 1);\r
+               b.running_status = Util.getBit(data, ptr, boff, 3);\r
+               b.free_CA_mode = Util.getBit(data, ptr, boff, 1);\r
+               b.descriptors_loop_length = Util.getBit(data, ptr, boff, 12);\r
+\r
+               return 5;\r
+       }\r
+       \r
+       //\r
+       private int parseSVCdesc(byte[] data, int ptr, SVCdesc desc) {\r
+               int[] boff = {0};\r
+         \r
+               //memset(desc, 0, sizeof(SVCdesc));\r
+\r
+               desc.descriptor_tag = Util.getBit(data, ptr, boff, 8);\r
+               desc.descriptor_length = Util.getBit(data, ptr, boff, 8);\r
+               desc.service_type = Util.getBit(data, ptr, boff, 8);\r
+               desc.service_provider_name_length = Util.getBit(data, ptr, boff, 8);\r
+               Util.getStr(desc.service_provider_name, data, ptr, boff, desc.service_provider_name_length);\r
+               desc.service_name_length = Util.getBit(data, ptr, boff, 8);\r
+               Util.getStr(desc.service_name, data, ptr, boff, desc.service_name_length);\r
+\r
+               return desc.descriptor_length + 2;\r
+       }\r
+       \r
+       //\r
+       private int serachid(ArrayList<SVT_CONTROL> top, int service_id) {\r
+               for ( SVT_CONTROL cur : top ) {\r
+                       if ( cur.servive_id == service_id ) {\r
+                               return 1 ;\r
+                       }\r
+               }\r
+               return 0 ;\r
+       }\r
+       \r
+       //\r
+       private void enqueue_sdt(ArrayList<SVT_CONTROL> top, SVT_CONTROL sdtptr) {\r
+               for ( int i=0; i<top.size(); i++ ) {\r
+                       SVT_CONTROL cur = top.get(i); \r
+                       if ( sdtptr.servive_id < cur.servive_id ) {\r
+                               top.add(i,sdtptr);\r
+                               break;\r
+                       }\r
+               }\r
+               top.add(sdtptr);\r
+       }\r
+\r
+\r
+       //\r
+       public boolean dumpSDT(byte[] data, ArrayList<SVT_CONTROL> top) {\r
+               try {\r
+                       return _dumpSDT(data, top);\r
+               }\r
+               catch ( Exception e ) {\r
+                       e.printStackTrace();\r
+               }\r
+               return false;\r
+       }\r
+       private boolean _dumpSDT(byte[] data, ArrayList<SVT_CONTROL> top) throws UnsupportedEncodingException {\r
+\r
+               // SDT\r
+               SDThead  sdth = new SDThead();\r
+               int len = parseSDThead(data, 0, sdth); \r
+               int ptr = len;\r
+               int loop_len = sdth.section_length - (len - 3 + 4); // 3は共通ヘッダ長 4はCRC\r
+               while ( loop_len > 0 ) {\r
+                       SDTbody  sdtb = new SDTbody();\r
+                       len = parseSDTbody(data, ptr, sdtb);\r
+                       ptr += len;\r
+                       loop_len -= len;\r
+                       \r
+                       SVCdesc  desc = new SVCdesc();\r
+                       parseSVCdesc(data, ptr, desc);\r
+\r
+                       int rc = serachid(top, sdtb.service_id);\r
+                       if ( rc == 0 ) {\r
+                               SVT_CONTROL svtptr = new SVT_CONTROL();\r
+                               svtptr.servive_id = sdtb.service_id;\r
+                               svtptr.original_network_id = sdth.original_network_id;\r
+                               svtptr.transport_stream_id = sdth.transport_stream_id;\r
+                               svtptr.servive_id = sdtb.service_id;\r
+                               svtptr.servicename = Util.getText(desc.service_name, Util.strlen(desc.service_name), Util.thisEncoding); \r
+                               //memcpy(svtptr->servicename, desc.service_name, strlen(desc.service_name));\r
+                               enqueue_sdt(top, svtptr);\r
+                       }\r
+\r
+                       ptr += sdtb.descriptors_loop_length;\r
+                       loop_len -= sdtb.descriptors_loop_length;\r
+               }\r
+         \r
+               return true;\r
+       }\r
+}\r
diff --git a/TinyBannavi/src/epgdump/SeriesDesc.java b/TinyBannavi/src/epgdump/SeriesDesc.java
new file mode 100644 (file)
index 0000000..f47aea4
--- /dev/null
@@ -0,0 +1,16 @@
+package epgdump;\r
+\r
+public class SeriesDesc {\r
+\r
+       int descriptor_tag;\r
+       int descriptor_length;\r
+       int series_id;\r
+       int repeat_label;\r
+       int program_pattern;\r
+       int expire_date_valid_flag;\r
+       int expire_date;\r
+       int episode_number;\r
+       int last_episode_number;\r
+       byte[] series_name_char = new byte[Util.MAXSECLEN];\r
+\r
+}\r
diff --git a/TinyBannavi/src/epgdump/TSpacket.java b/TinyBannavi/src/epgdump/TSpacket.java
new file mode 100644 (file)
index 0000000..501dce0
--- /dev/null
@@ -0,0 +1,20 @@
+package epgdump;\r
+\r
+public class TSpacket {\r
+\r
+       private static final int TSPAYLOADMAX = 184;\r
+       \r
+       int             sync;\r
+       int             transport_error_indicator;\r
+       int             payload_unit_start_indicator;\r
+       int             transport_priority;\r
+       int             pid;\r
+       int             transport_scrambling_control;\r
+       int             adaptation_field_control;\r
+       int             continuity_counter;\r
+       int             adaptation_field;\r
+       byte[]  payload = new byte[TSPAYLOADMAX+1];     // [鯛ナビ] 元ソースには+1しないといけないバグがあるっぽい\r
+       int             payloadlen;\r
+       int             rcount;\r
+\r
+}\r
diff --git a/TinyBannavi/src/epgdump/Ts.java b/TinyBannavi/src/epgdump/Ts.java
new file mode 100644 (file)
index 0000000..274f782
--- /dev/null
@@ -0,0 +1,387 @@
+package epgdump;\r
+\r
+import java.io.IOException;\r
+import java.io.PushbackInputStream;\r
+import java.util.ArrayList;\r
+import java.util.Arrays;\r
+\r
+public class Ts {\r
+\r
+       private static final int EOF = -1;\r
+       \r
+       private static final int RET_NONE = -1;\r
+       private static final int RET_GOTO_RETRY = -2;\r
+       private static final int RET_RETURN_NULL = -3;\r
+       \r
+       private static int rcount  = 0;\r
+       private static int ridx = -1;\r
+       \r
+       //\r
+       public SECcache readTS(PushbackInputStream in, ArrayList<SECcache> secs, int size) {\r
+               \r
+               try {\r
+                       if ( ! skip2syncbyte(in) ) {\r
+                               return null;\r
+                       }\r
+                       \r
+       //retry:\r
+                       \r
+                       // 戻すべき残りがあるか?\r
+                       while ( true ) {\r
+                               int ret = foo1(secs);\r
+                               if ( ret >= 0 ) {\r
+                                       return secs.get(ret);\r
+                               }\r
+                               else if ( ret == RET_GOTO_RETRY ) {\r
+                                       continue;\r
+                               }\r
+                               \r
+                               ret = foo2(in,secs,size);\r
+                               if ( ret >= 0 ) {\r
+                                       return secs.get(ret);\r
+                               }\r
+                               else if ( ret == RET_RETURN_NULL ) {\r
+                                       return null;\r
+                               }\r
+                               else if ( ret == RET_GOTO_RETRY ) {\r
+                                       continue;\r
+                               }\r
+                       }\r
+               }\r
+               catch ( Exception e ) {\r
+                       e.printStackTrace();\r
+                       return null;\r
+               }\r
+       }\r
+\r
+       //\r
+       private boolean skip2syncbyte(PushbackInputStream in) throws IOException {\r
+               int inchar;\r
+               \r
+               // syncバイトまで読み飛ばし\r
+               if ( rcount == 0 ) {\r
+                       do {\r
+                               if ( (inchar = in.read()) != EOF ) {\r
+                                       if ( (inchar & 0xFF) == 0x47 ) {\r
+                                               in.unread(inchar);\r
+                                               break;\r
+                                       }\r
+                               }\r
+                       } while ( inchar != EOF );\r
+                       if ( inchar == EOF ) {\r
+                               return false;\r
+                       }\r
+               }\r
+               return true;\r
+       }\r
+       \r
+       // fooとかいうな!\r
+       \r
+       private int foo1(ArrayList<SECcache> secs) {\r
+               \r
+               if ( ridx >= 0 && secs.get(ridx).cont != 0 ) {\r
+                       // バッファチェック\r
+                       if ( (secs.get(ridx).cur.payload[secs.get(ridx).curlen] & 0xFF) == 0xFF ) {\r
+                               secs.get(ridx).cont = 0;\r
+                               secs.get(ridx).seclen = 0;\r
+                               secs.get(ridx).setlen = 0;\r
+                               secs.get(ridx).curlen = 0;\r
+                       }\r
+                       else {\r
+                               int len = secs.get(ridx).cur.payloadlen - secs.get(ridx).curlen;\r
+                               if ( len == 0 ) {\r
+                                       secs.get(ridx).cont = 0;\r
+                                       secs.get(ridx).seclen = 0;\r
+                                       secs.get(ridx).setlen = 0;\r
+                                       secs.get(ridx).curlen = 0;\r
+                               }\r
+                               else {\r
+                                       /* ここでseclenが跨るようにTS分割されていると困るな @@\r
+                                          if(secs[ridx].pid == 0x12) {\r
+                                          int check = secs[ridx].cur.payload[secs[ridx].curlen] & 0xFF;\r
+                                          if(!(check == 0x4E ||\r
+                                          check == 0x4F ||\r
+                                          (check >= 0x50 && check <= 0x6F))) {\r
+                                          secs[ridx].curlen -= 3;\r
+                                          }\r
+                                          }\r
+                                       */\r
+\r
+                                       int[] boff = {12};\r
+                                       try { \r
+                                       secs.get(ridx).seclen = Util.getBit(secs.get(ridx).cur.payload, secs.get(ridx).curlen, boff, 12) + 3; // ヘッダ\r
+                                       }\r
+                                       catch ( Exception e ) {\r
+                                               System.err.println(secs.get(ridx).curlen);\r
+                                               e.printStackTrace();\r
+                                       }\r
+                                       \r
+                                       /*\r
+                                         if(secs[ridx].seclen == 2334) {\r
+                                         printf("aa");\r
+                                         }\r
+                                       */\r
+                                       \r
+                                       // TSデータ長-設定済みデータ長\r
+                                       if ( secs.get(ridx).seclen > len ) {\r
+                                               System.arraycopy(secs.get(ridx).cur.payload, secs.get(ridx).curlen, secs.get(ridx).buf, 0, len);\r
+                                               //memcpy(secs[ridx].buf, &secs[ridx].cur.payload[secs[ridx].curlen], len);\r
+                                               secs.get(ridx).setlen = len;\r
+                                               secs.get(ridx).curlen = 0;\r
+                                               secs.get(ridx).cont   = 1;\r
+                                               // 次のレコード読み込み\r
+                                       }\r
+                                       else {\r
+                                               System.arraycopy(secs.get(ridx).cur.payload, secs.get(ridx).curlen, secs.get(ridx).buf, 0, secs.get(ridx).seclen);\r
+                                               //memcpy(secs[ridx].buf, &secs[ridx].cur.payload[secs[ridx].curlen], secs[ridx].seclen);\r
+                                               secs.get(ridx).setlen = secs.get(ridx).seclen;\r
+                                               secs.get(ridx).curlen += secs.get(ridx).seclen;\r
+                                               secs.get(ridx).cont = 1;\r
+\r
+                                               // CRCのチェック\r
+                                               if ( checkcrc(secs.get(ridx)) ) {\r
+                                                       return ridx; /* 戻る */\r
+                                               }\r
+                                               return RET_GOTO_RETRY; /* もう一回 */\r
+                                       }\r
+                               }\r
+                       }\r
+               }\r
+               return RET_NONE;\r
+       }\r
+       private int foo2(PushbackInputStream in, ArrayList<SECcache> secs, int size) throws IOException {\r
+               TSpacket pk = new TSpacket();\r
+               \r
+               byte[] buf = new byte[1024];\r
+               \r
+               int[] boff = new int[1];\r
+               int len;\r
+               int payptr;\r
+               \r
+               int inchar;\r
+               \r
+               int roffset = 0;\r
+               while ( true ) {\r
+                       if ( in.read(buf, roffset, 188-roffset) < 1 ) {\r
+                               /* 残りの処理? */\r
+                               return RET_RETURN_NULL;\r
+                       }\r
+                       roffset = 0;\r
+                       rcount++;\r
+\r
+                       if ( (buf[0] & 0xFF) != 0x47) {\r
+                                       /* 最初はbuf中に0x47があるかチェック */\r
+                               int i = 1;\r
+                               for ( ; i<188; i++ ) {\r
+                                       if( (buf[i] & 0xFF) == 0x47 ) {\r
+                                               break;\r
+                                       }\r
+                               }\r
+\r
+                               if ( i < 188 ) {\r
+                                       /* そこから再読み込みして欲しいのでseek */\r
+                                       //fseek(in, (188 - i) * -1, SEEK_CUR);\r
+                                       roffset = i;\r
+                                       int[] tmp = new int[188-i];\r
+                                       System.arraycopy(buf, i, tmp, 0, 188-i);\r
+                                       System.arraycopy(tmp, 0, buf, 0, 188-i);\r
+                                       //memmove(buf, buf + i, 188 - i);\r
+                                       continue;\r
+                               }\r
+\r
+                               do {\r
+                                       if ( (inchar = in.read()) != EOF ) {\r
+                                               if ( (inchar & 0xFF) == 0x47 ) {\r
+                                                       in.unread(inchar);\r
+                                                       break;\r
+                                               }\r
+                                       }\r
+                               } while ( inchar != EOF );\r
+                               if ( inchar == EOF ) {\r
+                                       return RET_RETURN_NULL;\r
+                               }\r
+                               continue;\r
+                       }\r
+\r
+                       /*\r
+                         if(rcount == 406502) {\r
+                         printf("aa");\r
+                         }\r
+                       */\r
+\r
+           \r
+                       pk.rcount = rcount;\r
+           \r
+                       boff[0] = 0;\r
+                       pk.sync = Util.getBit(buf, 0, boff, 8);\r
+                       pk.transport_error_indicator = Util.getBit(buf, 0, boff, 1);\r
+                       pk.payload_unit_start_indicator = Util.getBit(buf, 0, boff, 1);\r
+                       pk.transport_priority = Util.getBit(buf, 0, boff, 1);\r
+                       pk.pid = Util.getBit(buf, 0, boff, 13);\r
+                       pk.transport_scrambling_control = Util.getBit(buf, 0, boff, 2);\r
+                       pk.adaptation_field_control = Util.getBit(buf, 0, boff, 2);\r
+                       pk.continuity_counter = Util.getBit(buf, 0, boff, 4);\r
+\r
+                       /*\r
+                         adaptation_field_control 2 bslbf\r
+                         continuity_counter 4 uimsbf\r
+                         if(adaptation_field_control = = '10' || adaptation_field_control = = '11'){\r
+                         adaptation_field()\r
+                         }\r
+                         ...\r
+                         adaptation_field() {\r
+                         adaptation_field_length 8 uimsbf\r
+                         if (adaptation_field_length > 0) {\r
+                         discontinuity_indicator....\r
+\r
+                         00 Reserved for future use by ISO/IEC\r
+                         01 No adaptation_field, payload only\r
+                         10 Adaptation_field only, no payload\r
+                         11 Adaptation_field followed by payload\r
+\r
+                       */\r
+\r
+                       pk.payloadlen = 184;\r
+\r
+                       if ( pk.adaptation_field_control == 2 ) {\r
+                               continue;\r
+                       }\r
+\r
+                       if ( pk.adaptation_field_control == 3 ) {\r
+                               len = Util.getBit(buf, 0, boff, 8);\r
+                               payptr = (boff[0] / 8) + len;\r
+                               pk.payloadlen -= (len + 1);\r
+                       }\r
+                       else {\r
+                               payptr = (boff[0] / 8);\r
+                       }\r
+                       if ( pk.payloadlen < 0 ) {\r
+                               continue ;\r
+                       }\r
+\r
+                       /* \r
+                          if the Transport Stream packet carries the first byte of a PSI section, the payload_unit_start_indicator value\r
+                          shall be '1', indicating that the first byte of the payload of this Transport Stream packet carries the pointer_field.\r
+                       */\r
+                       if ( pk.payload_unit_start_indicator == 1 ) {\r
+                               /* pointer_fieldはいらない */\r
+                               payptr += 1;\r
+                               pk.payloadlen -= 1;\r
+                       }\r
+                       Arrays.fill(pk.payload, (byte) 0xFF);\r
+                       System.arraycopy(buf, payptr, pk.payload, 0, pk.payloadlen);\r
+                       //memset(pk.payload, 0xFF, sizeof(pk.payload));\r
+                       //memcpy(pk.payload, payptr, pk.payloadlen);\r
+           \r
+                       /*\r
+                         if(pk.rcount == 62) {\r
+                         printf("62\n");\r
+                         }\r
+\r
+                         if(pk.rcount == 63) {\r
+                         printf("63\n");\r
+                         }\r
+                       */\r
+           \r
+                       // 興味のあるpidか確認\r
+                       for ( int i=0; i<size; i++ ) {\r
+                               if ( secs.get(i).pid == pk.pid ) {\r
+                                       secs.get(i).cur = pk;\r
+                                       /* 途中処理中か最初か? */\r
+                                       if ( secs.get(i).cont == 0 ) {\r
+                                               /* 最初 セクション長を調べる */\r
+                                               boff[0]= 12;\r
+                                               secs.get(i).seclen = Util.getBit(secs.get(i).cur.payload, 0, boff, 12) + 3; // ヘッダ;\r
+                                               /*\r
+                                                 if(secs.get(i).seclen == 2334) {\r
+                                                 printf("aa");\r
+                                                 }\r
+                                               */\r
+\r
+                                               if ( secs.get(i).seclen > secs.get(i).cur.payloadlen ) {\r
+                                                       System.arraycopy(secs.get(i).cur.payload, 0, secs.get(i).buf, 0, secs.get(i).cur.payloadlen);\r
+                                                       //memcpy(secs.get(i).buf, secs.get(i).cur.payload, secs.get(i).cur.payloadlen);\r
+                                                       secs.get(i).setlen = secs.get(i).cur.payloadlen;\r
+                                                       secs.get(i).cont = 1;\r
+                                                       continue;\r
+                                               }\r
+                                               System.arraycopy(secs.get(i).cur.payload, 0, secs.get(i).buf, 0, secs.get(i).seclen);\r
+                                               //memcpy(secs.get(i).buf, secs.get(i).cur.payload, secs.get(i).seclen);\r
+                                               secs.get(i).setlen = secs.get(i).seclen;\r
+                                               secs.get(i).curlen = secs.get(i).seclen;\r
+                                               secs.get(i).cont = 1;\r
+                                               ridx = i;\r
+                                               /* CRCのチェック */\r
+                                               if ( checkcrc(secs.get(ridx)) ) {\r
+                                                       return i; /* 取り合えず戻る */\r
+                                               }\r
+                                               return RET_GOTO_RETRY; /* 残り処理へ */\r
+                                       }\r
+                                       /* セクション長-設定済み長 */\r
+                                       len = secs.get(i).seclen - secs.get(i).setlen;\r
+                                       if ( len > secs.get(i).cur.payloadlen ) {\r
+                                               /* 全体転送 */\r
+                                               System.arraycopy(secs.get(i).cur.payload, 0, secs.get(i).buf, secs.get(i).setlen, secs.get(i).cur.payloadlen);\r
+                                               //memcpy(&secs.get(i).buf[secs.get(i).setlen], secs.get(i).cur.payload, secs.get(i).cur.payloadlen);\r
+                                               secs.get(i).setlen += secs.get(i).cur.payloadlen;\r
+                                               continue;\r
+                                       }\r
+                                       /* セクション長の残りを設定 */\r
+                                       System.arraycopy(secs.get(i).cur.payload, 0, secs.get(i).buf, secs.get(i).setlen, len);\r
+                                       //memcpy(&secs.get(i).buf[secs.get(i).setlen], secs.get(i).cur.payload, len);\r
+                                       secs.get(i).setlen  = secs.get(i).seclen;\r
+                                       secs.get(i).curlen += len;\r
+                                       secs.get(i).cont    = 1;\r
+                                       ridx = i;\r
+                                       /* CRCのチェック */\r
+                                       if( checkcrc(secs.get(ridx)) ) {\r
+                                               return i;\r
+                                       }\r
+                                       return RET_GOTO_RETRY; /* 残り処理へ */\r
+                               }\r
+                       }\r
+               }\r
+       }\r
+       \r
+       // BonTest/TsStream.cppからのパクリ\r
+       private static final int[] CrcTable = {\r
+               0x00000000, 0x04C11DB7, 0x09823B6E, 0x0D4326D9, 0x130476DC, 0x17C56B6B, 0x1A864DB2, 0x1E475005, 0x2608EDB8, 0x22C9F00F, 0x2F8AD6D6, 0x2B4BCB61, 0x350C9B64, 0x31CD86D3, 0x3C8EA00A, 0x384FBDBD,\r
+               0x4C11DB70, 0x48D0C6C7, 0x4593E01E, 0x4152FDA9, 0x5F15ADAC, 0x5BD4B01B, 0x569796C2, 0x52568B75, 0x6A1936C8, 0x6ED82B7F, 0x639B0DA6, 0x675A1011, 0x791D4014, 0x7DDC5DA3, 0x709F7B7A, 0x745E66CD,\r
+               0x9823B6E0, 0x9CE2AB57, 0x91A18D8E, 0x95609039, 0x8B27C03C, 0x8FE6DD8B, 0x82A5FB52, 0x8664E6E5, 0xBE2B5B58, 0xBAEA46EF, 0xB7A96036, 0xB3687D81, 0xAD2F2D84, 0xA9EE3033, 0xA4AD16EA, 0xA06C0B5D,\r
+               0xD4326D90, 0xD0F37027, 0xDDB056FE, 0xD9714B49, 0xC7361B4C, 0xC3F706FB, 0xCEB42022, 0xCA753D95, 0xF23A8028, 0xF6FB9D9F, 0xFBB8BB46, 0xFF79A6F1, 0xE13EF6F4, 0xE5FFEB43, 0xE8BCCD9A, 0xEC7DD02D,\r
+               0x34867077, 0x30476DC0, 0x3D044B19, 0x39C556AE, 0x278206AB, 0x23431B1C, 0x2E003DC5, 0x2AC12072, 0x128E9DCF, 0x164F8078, 0x1B0CA6A1, 0x1FCDBB16, 0x018AEB13, 0x054BF6A4, 0x0808D07D, 0x0CC9CDCA,\r
+               0x7897AB07, 0x7C56B6B0, 0x71159069, 0x75D48DDE, 0x6B93DDDB, 0x6F52C06C, 0x6211E6B5, 0x66D0FB02, 0x5E9F46BF, 0x5A5E5B08, 0x571D7DD1, 0x53DC6066, 0x4D9B3063, 0x495A2DD4, 0x44190B0D, 0x40D816BA,\r
+               0xACA5C697, 0xA864DB20, 0xA527FDF9, 0xA1E6E04E, 0xBFA1B04B, 0xBB60ADFC, 0xB6238B25, 0xB2E29692, 0x8AAD2B2F, 0x8E6C3698, 0x832F1041, 0x87EE0DF6, 0x99A95DF3, 0x9D684044, 0x902B669D, 0x94EA7B2A,\r
+               0xE0B41DE7, 0xE4750050, 0xE9362689, 0xEDF73B3E, 0xF3B06B3B, 0xF771768C, 0xFA325055, 0xFEF34DE2, 0xC6BCF05F, 0xC27DEDE8, 0xCF3ECB31, 0xCBFFD686, 0xD5B88683, 0xD1799B34, 0xDC3ABDED, 0xD8FBA05A,\r
+               0x690CE0EE, 0x6DCDFD59, 0x608EDB80, 0x644FC637, 0x7A089632, 0x7EC98B85, 0x738AAD5C, 0x774BB0EB, 0x4F040D56, 0x4BC510E1, 0x46863638, 0x42472B8F, 0x5C007B8A, 0x58C1663D, 0x558240E4, 0x51435D53,\r
+               0x251D3B9E, 0x21DC2629, 0x2C9F00F0, 0x285E1D47, 0x36194D42, 0x32D850F5, 0x3F9B762C, 0x3B5A6B9B, 0x0315D626, 0x07D4CB91, 0x0A97ED48, 0x0E56F0FF, 0x1011A0FA, 0x14D0BD4D, 0x19939B94, 0x1D528623,\r
+               0xF12F560E, 0xF5EE4BB9, 0xF8AD6D60, 0xFC6C70D7, 0xE22B20D2, 0xE6EA3D65, 0xEBA91BBC, 0xEF68060B, 0xD727BBB6, 0xD3E6A601, 0xDEA580D8, 0xDA649D6F, 0xC423CD6A, 0xC0E2D0DD, 0xCDA1F604, 0xC960EBB3,\r
+               0xBD3E8D7E, 0xB9FF90C9, 0xB4BCB610, 0xB07DABA7, 0xAE3AFBA2, 0xAAFBE615, 0xA7B8C0CC, 0xA379DD7B, 0x9B3660C6, 0x9FF77D71, 0x92B45BA8, 0x9675461F, 0x8832161A, 0x8CF30BAD, 0x81B02D74, 0x857130C3,\r
+               0x5D8A9099, 0x594B8D2E, 0x5408ABF7, 0x50C9B640, 0x4E8EE645, 0x4A4FFBF2, 0x470CDD2B, 0x43CDC09C, 0x7B827D21, 0x7F436096, 0x7200464F, 0x76C15BF8, 0x68860BFD, 0x6C47164A, 0x61043093, 0x65C52D24,\r
+               0x119B4BE9, 0x155A565E, 0x18197087, 0x1CD86D30, 0x029F3D35, 0x065E2082, 0x0B1D065B, 0x0FDC1BEC, 0x3793A651, 0x3352BBE6, 0x3E119D3F, 0x3AD08088, 0x2497D08D, 0x2056CD3A, 0x2D15EBE3, 0x29D4F654,\r
+               0xC5A92679, 0xC1683BCE, 0xCC2B1D17, 0xC8EA00A0, 0xD6AD50A5, 0xD26C4D12, 0xDF2F6BCB, 0xDBEE767C, 0xE3A1CBC1, 0xE760D676, 0xEA23F0AF, 0xEEE2ED18, 0xF0A5BD1D, 0xF464A0AA, 0xF9278673, 0xFDE69BC4,\r
+               0x89B8FD09, 0x8D79E0BE, 0x803AC667, 0x84FBDBD0, 0x9ABC8BD5, 0x9E7D9662, 0x933EB0BB, 0x97FFAD0C, 0xAFB010B1, 0xAB710D06, 0xA6322BDF, 0xA2F33668, 0xBCB4666D, 0xB8757BDA, 0xB5365D03, 0xB1F740B4\r
+       };              \r
+       private int CalcCrc(int crc, byte[] buf, int len) {\r
+               int c = crc;\r
+               int n;\r
+\r
+               for (n = 0; n < len; n++) {\r
+                       c = (c << 8) ^ CrcTable[(((c >> 24) & 0xFF) ^ buf[n]) & 0xFF];\r
+               }\r
+\r
+               return c;\r
+       }\r
+       \r
+       //\r
+       private boolean checkcrc(SECcache secs) {\r
+               /* セクションの終りに置かれる4バイトのCRC32は、\r
+                  CRC計算の結果0になるように設定される。\r
+                  値が発生した場合は、エラーなので対象外にする */\r
+               if ( CalcCrc(0xFFFFFFFF, secs.buf, secs.seclen) != 0 ) {\r
+                       if (Util.debug) System.err.println(String.format("tblid:0x%x CRC error", secs.buf[0]));\r
+                       return false;\r
+               }\r
+               return true;\r
+       }\r
+}\r
diff --git a/TinyBannavi/src/epgdump/Util.java b/TinyBannavi/src/epgdump/Util.java
new file mode 100644 (file)
index 0000000..2f93ed9
--- /dev/null
@@ -0,0 +1,124 @@
+package epgdump;\r
+\r
+\r
+public class Util {\r
+\r
+       public static boolean debug = false;\r
+       \r
+       public static final String thisEncoding = "UTF-8";\r
+\r
+       public static final int MAXSECLEN = 4096;\r
+\r
+       //\r
+       public static int getBit(byte[] buf, int ptr, int[] pbit, int gbit) {\r
+               int pbyte = pbit[0] / 8;\r
+               //unsigned char *fbyte = byte + pbyte;\r
+\r
+               int cutbit = pbit[0] - (pbyte * 8);\r
+               int lcutbit = 32 - (cutbit + gbit);\r
+\r
+               byte[] tbuf = new byte[4]; /* intの最大32bit */\r
+               int tnum;\r
+\r
+               // [鯛ナビ] rが4固定だとbufをoverrunするバグがある(TSpacket.payload) ↓の修正でいいのかどうかは、不明\r
+               int r = buf.length - (ptr+pbyte);\r
+               r =  (r>=4)?(4):(r);\r
+               if ( Util.debug && r<4 ) System.err.println("# "+ptr+" "+r+" "+cutbit+" "+lcutbit);\r
+               System.arraycopy(buf, ptr+pbyte, tbuf, 0, r);\r
+               //memcpy(tbuf, fbyte, sizeof(unsigned char) * 4);\r
+\r
+               /* 先頭バイトから不要bitをカット */\r
+               tbuf[0] = (byte) (tbuf[0] & (0x00FF >>> cutbit));\r
+\r
+               /* intにしてしまう */\r
+               \r
+               tnum = (tbuf[0]&0xFF) << 24 | (tbuf[1]&0xFF) << 16 | (tbuf[2]&0xFF) << 8 | (tbuf[3]&0xFF);\r
+\r
+               /* 後ろの不要バイトをカット */\r
+               tnum = tnum >>> lcutbit;\r
+\r
+               pbit[0] += gbit;\r
+\r
+               return tnum;\r
+       }\r
+       \r
+       //\r
+       public static int getStr(byte[] tostr, byte[] buf, int ptr, int[] pbit, int len) {\r
+               byte[] str = new byte[len];\r
+               int pbyte = pbit[0] / 8;\r
+               //unsigned char *fbuf = buf + pbyte;\r
+\r
+               //Arrays.fill(str, (byte) 0x00);\r
+               System.arraycopy(buf, ptr+pbyte, str, 0, len);\r
+               //memset(str, 0, sizeof(char) * MAXSECLEN);\r
+               //memcpy(str, fbuf, len);\r
+\r
+               pbit[0] += (len * 8);\r
+         \r
+               return Aribstr.AribToString(tostr, str, len);\r
+       }\r
+       \r
+       //\r
+       public static int parseOTHERdesc(byte[] data, int ptr) {\r
+               int[] boff = {0};\r
+               //int descriptor_tag;\r
+               int descriptor_length;\r
+\r
+               /*descriptor_tag =*/ Util.getBit(data, ptr, boff, 8);\r
+               descriptor_length = Util.getBit(data, ptr, boff, 8);\r
+\r
+               /* printf("other desc_tag:0x%x\n", descriptor_tag); */\r
+\r
+               return descriptor_length + 2;\r
+       }\r
+\r
+       // Cのstrlenもどき\r
+       public static int strlen(byte[] b) {\r
+               for ( int i=0; i<b.length; i++ ) {\r
+                       if ( b[i] == (byte) 0x00 ) {\r
+                               return i;\r
+                       }\r
+               }\r
+               return b.length;\r
+       }\r
+       \r
+       // Cのmemcmp()もどき\r
+       public static int memcmp(byte[] src, byte[] dst, int len) {\r
+               int x=0,y=0;\r
+               for ( ; x<src.length && y<dst.length && x<len; x++,y++ ) {\r
+                       int d =  src[x]&0xFF - dst[y]&0xFF;\r
+                       if ( d < 0 ) {\r
+                               return -1;\r
+                       }\r
+                       else if ( d > 0 ) {\r
+                               return 1;\r
+                       }\r
+               }\r
+               if ( x >= len && src.length < dst.length ) {\r
+                       return -1;\r
+               }\r
+               else if ( x >= len && src.length > dst.length ) {\r
+                       return 1;\r
+               }\r
+               return 0;\r
+       }\r
+       \r
+       // byteを16進文字列に変換する\r
+       public static Integer hex2dec(byte b) throws NumberFormatException {\r
+               return Integer.decode(String.format("%x",b));\r
+       }\r
+       \r
+       // 文字列バイナリをStringに変換する\r
+       public static String getText(byte[] b, int len, String enc) {\r
+               try {\r
+                       byte[] buf = new byte[len];\r
+                       System.arraycopy(b, 0, buf, 0, len);\r
+                       return new String(buf,Util.thisEncoding);\r
+               }\r
+               catch ( Exception e ) {\r
+                       e.printStackTrace();\r
+               }\r
+               return null;\r
+       }\r
+\r
+}\r
diff --git a/TinyBannavi/src/httpDump/Main.java b/TinyBannavi/src/httpDump/Main.java
new file mode 100644 (file)
index 0000000..ded29b3
--- /dev/null
@@ -0,0 +1,42 @@
+package httpDump;\r
+\r
+import java.io.IOException;\r
+import java.net.ServerSocket;\r
+import java.net.Socket;\r
+\r
+public class Main extends Thread {\r
+       private String id = "";\r
+       private String password = "";\r
+\r
+       public Main(String id, String password) {\r
+               this.id = id;\r
+               this.password = password;\r
+               //\r
+               this.start();\r
+       }\r
+\r
+       @Override\r
+       public void run() {\r
+               Thread tr = new Thread();\r
+               \r
+               int port = 8080;\r
+               ServerSocket svsock = null;\r
+               Socket sock = null;\r
+               \r
+               //\r
+               try {\r
+                       System.out.println("ブラウザからの接続をポート"+port+"で待ちます");\r
+                       svsock = new ServerSocket(port);\r
+                       \r
+                       while (true) {\r
+                               sock = svsock.accept();\r
+                       \r
+                               new Server(sock, id, password);\r
+                       }\r
+               } catch (IOException e1) {\r
+                       // TODO Auto-generated catch block\r
+                       e1.printStackTrace();\r
+               }\r
+       }\r
+\r
+}\r
diff --git a/TinyBannavi/src/httpDump/Server.java b/TinyBannavi/src/httpDump/Server.java
new file mode 100644 (file)
index 0000000..9edde92
--- /dev/null
@@ -0,0 +1,241 @@
+package httpDump;\r
+\r
+import java.io.BufferedOutputStream;\r
+import java.io.BufferedReader;\r
+import java.io.BufferedWriter;\r
+import java.io.File;\r
+import java.io.FileOutputStream;\r
+import java.io.IOException;\r
+import java.io.InputStream;\r
+import java.io.InputStreamReader;\r
+import java.io.OutputStream;\r
+import java.io.OutputStreamWriter;\r
+import java.io.PrintStream;\r
+import java.net.Authenticator;\r
+import java.net.CookieHandler;\r
+import java.net.CookieManager;\r
+import java.net.CookiePolicy;\r
+import java.net.CookieStore;\r
+import java.net.HttpCookie;\r
+import java.net.HttpURLConnection;\r
+import java.net.PasswordAuthentication;\r
+import java.net.Socket;\r
+import java.net.URL;\r
+import java.util.ArrayList;\r
+import java.util.Iterator;\r
+import java.util.List;\r
+import java.util.Map;\r
+import java.util.regex.Matcher;\r
+import java.util.regex.Pattern;\r
+\r
+\r
+public class Server extends Thread {\r
+       // 共有変数\r
+       private Socket sock = null;\r
+       private String id = "";\r
+       private String password = "";\r
+\r
+       // コンストラクタ\r
+       public Server(Socket sock, String id, String password) {\r
+               this.sock = sock;\r
+               this.id = id;\r
+               this.password = password;\r
+               \r
+               // スレッド実行\r
+               this.start();\r
+       }\r
+       \r
+       // 認証をどうこうするクラス\r
+       public class MyAuthenticator extends Authenticator {\r
+               private String username;\r
+               private String password;\r
+\r
+               public MyAuthenticator(String username, String password) {\r
+                       this.username = username;\r
+                       this.password = password;\r
+               }\r
+               protected PasswordAuthentication getPasswordAuthentication() {\r
+                       return new PasswordAuthentication(username, password.toCharArray());\r
+               }\r
+       }\r
+       \r
+       // スレッド\r
+       @Override\r
+       public void run() {\r
+\r
+               byte[] b = new byte[65536];\r
+               char[] c = new char[65536];\r
+               \r
+               try {\r
+                       // 認証のためのおまじない\r
+                       Authenticator.setDefault(new MyAuthenticator(id, password));\r
+                       \r
+                       // Cookie\r
+                       CookieManager manager = new CookieManager();\r
+                       manager.setCookiePolicy(CookiePolicy.ACCEPT_ALL);\r
+                       CookieHandler.setDefault(manager);\r
+                       \r
+                       // ブラウザからリクエストを受け取る\r
+                       ArrayList<String> reqHeaders = new ArrayList<String>();\r
+                       String str = null;\r
+                       \r
+                       OutputStream w = sock.getOutputStream(); \r
+                       BufferedWriter out = new BufferedWriter(new OutputStreamWriter(w));\r
+                       InputStream r = sock.getInputStream();\r
+                       BufferedReader in = new BufferedReader(new InputStreamReader(r));\r
+                       \r
+                       int clen = -1;\r
+                       String method = null;\r
+                       String location = null;\r
+                       String protocol = null;\r
+                       boolean findHeader = true;\r
+                       while ((str = in.readLine()) != null) {\r
+                               if (findHeader) {\r
+                                       Matcher ma = Pattern.compile("^(POST|GET) (.+?) (HTTP/.+?)$").matcher(str);\r
+                                       if (ma.find()) {\r
+                                               method = ma.group(1);\r
+                                               location = ma.group(2);\r
+                                               protocol = ma.group(3);\r
+                                       }\r
+                                       if (method.equals("GET")) {\r
+                                               break;\r
+                                       }\r
+                                       findHeader = false;\r
+                                       continue;\r
+                               }\r
+\r
+                               // ヘッダの終了\r
+                               Matcher ma = null;\r
+                               ma = Pattern.compile("^\\s*$").matcher(str);\r
+                               if (ma.find()) {\r
+                                       break;\r
+                               }\r
+\r
+                               // Content-Lengthの取得\r
+                               ma = Pattern.compile("Content-Length: (\\d+)").matcher(str);\r
+                               if (ma.find()) {\r
+                                       clen = Integer.valueOf(ma.group(1));\r
+                               }\r
+                               \r
+                               reqHeaders.add(str);\r
+                       }\r
+                       if (clen > 0) {\r
+                               in.read(c, 0, clen);\r
+                       }\r
+\r
+                       // ログに出力してみよう(1)\r
+                       PrintStream ps = new PrintStream(new BufferedOutputStream(new FileOutputStream(new File("dump.txt"),true)));\r
+                       ps.println("\n\n### Request for RECORDER");\r
+                       ps.println(method+" "+location+" "+protocol);\r
+                       for ( String s : reqHeaders ) {\r
+                               ps.println(s);\r
+                       }\r
+                       ps.println("");\r
+                       for (int i=0; i<clen; i++) {\r
+                               ps.print(c[i]);\r
+                       }\r
+                       ps.flush();\r
+\r
+                       // メッセージも出力してみよう(1)\r
+                       System.out.println("\n### Request for RECORDER");\r
+                       System.out.println(method+" "+location+" "+protocol);\r
+                       for ( String s : reqHeaders ) {\r
+                               System.out.println(s);\r
+                       }\r
+                       System.out.println("");\r
+                       for (int i=0; i<clen; i++) {\r
+                               System.out.print(c[i]);\r
+                       }\r
+\r
+                       // Proxy先にリクエストを送る\r
+                       URL url = new URL(location);\r
+                       HttpURLConnection conn = (HttpURLConnection)url.openConnection();\r
+                       if (method.equals("POST")) {\r
+                               conn.setDoOutput(true);\r
+                               for ( String s : reqHeaders ) {\r
+                                       Matcher ma = Pattern.compile("^(.+?): (.+?)$").matcher(s);\r
+                                       if (ma.find()) {\r
+                                               conn.setRequestProperty(ma.group(1), ma.group(2));\r
+                                       }\r
+                               }\r
+                               conn.setRequestMethod("POST");\r
+                               conn.connect();\r
+                               \r
+                               BufferedWriter writer = new BufferedWriter(new OutputStreamWriter(conn.getOutputStream(),"MS932"));\r
+                               for (int i=0; i<clen; i++) {\r
+                                       writer.write(c[i]);\r
+                               }\r
+                               writer.close();\r
+                       }\r
+                       else {\r
+                               conn.setRequestMethod("GET");\r
+                               conn.connect();\r
+                       }\r
+                       \r
+                       // Proxy先からもらったヘッダを処理する\r
+                       String resCode = String.valueOf(conn.getResponseCode());\r
+                       Matcher ma = Pattern.compile("text").matcher(conn.getContentType());\r
+                       boolean isText = ma.find();\r
+                       //\r
+                       Map<String, List<String>> h = conn.getHeaderFields();\r
+                       Iterator<String> it = h.keySet().iterator();\r
+                       String header = "";\r
+                       while (it.hasNext()){\r
+                               String key = (String)it.next();\r
+                               header += ((key != null)?(key+": "):(""))+h.get(key).get(0)+"\r\n";\r
+                       }\r
+                       out.write(header);\r
+                       out.write("\r\n");\r
+                       out.flush();\r
+                       \r
+                       // 出力してみよう(2)\r
+                       ps.println("\n\n### Response from RECORDER");\r
+                       ps.println(header);\r
+               \r
+                       System.out.println("\n### Response from RECORDER");\r
+                       System.out.println(header);\r
+\r
+                       if (resCode.equals("200")) {\r
+                               int len;\r
+                               InputStream rr = conn.getInputStream();\r
+                               while ((len = rr.read(b, 0, b.length)) > 0) {\r
+                                       if (isText) {\r
+                                               ps.write(b, 0, len);\r
+                                               ps.flush();\r
+                                       }\r
+                                       if ( true ) {\r
+                                               int m = 4096;\r
+                                               for (int n=0; n<len; n+=m) {\r
+                                                       if ((n+m) < len) {\r
+                                                               System.out.print("*"+len+"("+m+")");\r
+                                                               w.write(b, n, m);\r
+                                                       }\r
+                                                       else {\r
+                                                               int s = len-n;\r
+                                                               System.out.print("#"+len+"("+s+")");\r
+                                                               w.write(b, n, s);\r
+                                                       }\r
+                                               }\r
+                                               System.out.println("");\r
+                                       }\r
+                                       else {\r
+                                               w.write(b, 0, len);\r
+                                       }\r
+                               }\r
+                               rr.close();\r
+                       }\r
+                       \r
+                       ps.close();\r
+                       out.close();\r
+                       in.close();\r
+                       \r
+                   conn.disconnect();\r
+                   \r
+                   sock.close();\r
+                   \r
+               } catch (IOException e) {\r
+                       // TODO Auto-generated catch block\r
+                       e.printStackTrace();\r
+               }\r
+       }\r
+}\r
diff --git a/TinyBannavi/src/httpDump/Viewer.java b/TinyBannavi/src/httpDump/Viewer.java
new file mode 100644 (file)
index 0000000..21d680a
--- /dev/null
@@ -0,0 +1,125 @@
+package httpDump;\r
+\r
+import javax.swing.SwingUtilities;\r
+import javax.swing.JPanel;\r
+import javax.swing.JFrame;\r
+import javax.swing.JLabel;\r
+import javax.swing.BoxLayout;\r
+import javax.swing.JTextField;\r
+import javax.swing.JButton;\r
+import java.io.IOException;\r
+import java.net.ServerSocket;\r
+import java.net.Socket;\r
+\r
+public class Viewer extends JFrame {\r
+\r
+       private static final long serialVersionUID = 1L;\r
+       private JPanel jContentPane = null;\r
+       private JLabel jLabel2 = null;\r
+       private JTextField jTextField2 = null;\r
+       private JLabel jLabel3 = null;\r
+       private JTextField jTextField3 = null;\r
+       private JButton jButton = null;\r
+\r
+       /**\r
+        * This method initializes jTextField2  \r
+        *      \r
+        * @return javax.swing.JTextField       \r
+        */\r
+       private JTextField getJTextField2() {\r
+               if (jTextField2 == null) {\r
+                       jTextField2 = new JTextField();\r
+                       jTextField2.setText("");\r
+               }\r
+               return jTextField2;\r
+       }\r
+\r
+       /**\r
+        * This method initializes jTextField3  \r
+        *      \r
+        * @return javax.swing.JTextField       \r
+        */\r
+       private JTextField getJTextField3() {\r
+               if (jTextField3 == null) {\r
+                       jTextField3 = new JTextField();\r
+                       jTextField3.setText("");\r
+               }\r
+               return jTextField3;\r
+       }\r
+\r
+       /**\r
+        * This method initializes jButton      \r
+        *      \r
+        * @return javax.swing.JButton  \r
+        */\r
+       private JButton getJButton() {\r
+               if (jButton == null) {\r
+                       jButton = new JButton();\r
+                       jButton.setText("実行する");\r
+                       jButton.addMouseListener(new java.awt.event.MouseAdapter() {\r
+                               public void mouseClicked(java.awt.event.MouseEvent e) {\r
+                                       new Main(jTextField2.getText(), jTextField3.getText());\r
+                                       ((JButton)e.getSource()).removeMouseListener(null);\r
+                                       ((JButton)e.getSource()).setEnabled(false);\r
+                               }\r
+                       });\r
+               }\r
+               return jButton;\r
+       }\r
+\r
+       /**\r
+        * @param args\r
+        */\r
+       public static void main(String[] args) {\r
+               // TODO Auto-generated method stub\r
+               SwingUtilities.invokeLater(new Runnable() {\r
+                       public void run() {\r
+                               Viewer thisClass = new Viewer();\r
+                               thisClass.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);\r
+                               thisClass.setVisible(true);\r
+                       }\r
+               });\r
+       }\r
+\r
+       /**\r
+        * This is the default constructor\r
+        */\r
+       public Viewer() {\r
+               super();\r
+               initialize();\r
+       }\r
+\r
+       /**\r
+        * This method initializes this\r
+        * \r
+        * @return void\r
+        */\r
+       private void initialize() {\r
+               this.setSize(320, 160);\r
+               this.setContentPane(getJContentPane());\r
+               this.setTitle("JFrame");\r
+       }\r
+\r
+       /**\r
+        * This method initializes jContentPane\r
+        * \r
+        * @return javax.swing.JPanel\r
+        */\r
+       private JPanel getJContentPane() {\r
+               if (jContentPane == null) {\r
+                       jLabel3 = new JLabel();\r
+                       jLabel3.setText("レコーダのパスワード");\r
+                       jLabel2 = new JLabel();\r
+                       jLabel2.setText("レコーダのID");\r
+                       jContentPane = new JPanel();\r
+                       jContentPane.setLayout(new BoxLayout(getJContentPane(), BoxLayout.Y_AXIS));\r
+                       jContentPane.add(jLabel2, null);\r
+                       jContentPane.add(getJTextField2(), null);\r
+                       jContentPane.add(jLabel3, null);\r
+                       jContentPane.add(getJTextField3(), null);\r
+                       jContentPane.add(getJButton(), null);\r
+               }\r
+               return jContentPane;\r
+       }\r
+\r
+}  //  @jve:decl-index=0:visual-constraint="10,10"\r
diff --git a/TinyBannavi/src/niseRD/Main.java b/TinyBannavi/src/niseRD/Main.java
new file mode 100644 (file)
index 0000000..a12735b
--- /dev/null
@@ -0,0 +1,38 @@
+package niseRD;\r
+\r
+import java.io.IOException;\r
+import java.net.ServerSocket;\r
+import java.net.Socket;\r
+\r
+public class Main extends Thread {\r
+       private String folder = null;\r
+\r
+       public Main(String folder) {\r
+               this.folder = folder;\r
+               //\r
+               this.start();\r
+       }\r
+\r
+       @Override\r
+       public void run() {\r
+               int port = 8080;\r
+               ServerSocket svsock = null;\r
+               Socket sock = null;\r
+               \r
+               //\r
+               try {\r
+                       System.out.println("鯛ナビからの接続をポート"+port+"で待ちます");\r
+                       svsock = new ServerSocket(port);\r
+                       \r
+                       while (true) {\r
+                               sock = svsock.accept();\r
+                       \r
+                               new Server(sock, folder);\r
+                       }\r
+               } catch (IOException e1) {\r
+                       // TODO Auto-generated catch block\r
+                       e1.printStackTrace();\r
+               }\r
+       }\r
+\r
+}\r
diff --git a/TinyBannavi/src/niseRD/Server.java b/TinyBannavi/src/niseRD/Server.java
new file mode 100644 (file)
index 0000000..a17fab4
--- /dev/null
@@ -0,0 +1,280 @@
+package niseRD;\r
+\r
+import java.io.BufferedReader;\r
+import java.io.BufferedWriter;\r
+import java.io.File;\r
+import java.io.FileInputStream;\r
+import java.io.FileReader;\r
+import java.io.IOException;\r
+import java.io.InputStream;\r
+import java.io.InputStreamReader;\r
+import java.io.OutputStream;\r
+import java.io.OutputStreamWriter;\r
+import java.net.Socket;\r
+import java.net.SocketException;\r
+import java.util.ArrayList;\r
+import java.util.regex.Matcher;\r
+import java.util.regex.Pattern;\r
+\r
+import tainavi.CommonUtils;\r
+\r
+\r
+public class Server extends Thread {\r
+       // 共有変数\r
+       private Socket sock = null;\r
+       private String folder = null;\r
+\r
+       // コンストラクタ\r
+       public Server(Socket sock, String folder) {\r
+               this.sock = sock;\r
+               this.folder = folder;\r
+               \r
+               // スレッド実行\r
+               this.start();\r
+       }\r
+       \r
+       // スレッド\r
+       @Override\r
+       public void run() {\r
+\r
+               byte[] b = new byte[1024*1024];\r
+               char[] c = new char[65536];\r
+               \r
+               OutputStream w = null;\r
+               BufferedWriter out = null;\r
+               InputStream r = null;\r
+               BufferedReader in = null;\r
+               FileInputStream reader = null;\r
+               try {\r
+                       \r
+                       // ブラウザからリクエストを受け取る\r
+                       ArrayList<String> reqHeaders = new ArrayList<String>();\r
+                       String str = null;\r
+                       \r
+                       w = sock.getOutputStream(); \r
+                       out = new BufferedWriter(new OutputStreamWriter(w));\r
+                       r = sock.getInputStream();\r
+                       in = new BufferedReader(new InputStreamReader(r));\r
+                       \r
+                       int clen = -1;\r
+                       String method = null;\r
+                       String location = null;\r
+                       String protocol = null;\r
+                       String poststr = null;\r
+                       boolean findHeader = true;\r
+                       while ((str = in.readLine()) != null) {\r
+                               \r
+                               if (findHeader) {\r
+                                       Matcher ma = Pattern.compile("^(POST|GET) (.+?) (HTTP/.+?)$").matcher(str);\r
+                                       if (ma.find()) {\r
+                                               method = ma.group(1);\r
+                                               location = ma.group(2);\r
+                                               protocol = ma.group(3);\r
+                                       }\r
+                                       if (method.equals("GET")) {\r
+                                               break;\r
+                                       }\r
+                                       findHeader = false;\r
+                                       continue;\r
+                               }\r
+\r
+                               // ヘッダの終了\r
+                               Matcher ma = Pattern.compile("^\\s*$").matcher(str);\r
+                               if (ma.find()) {\r
+                                       break;\r
+                               }\r
+                               \r
+                               // Content-Lengthの取得\r
+                               ma = Pattern.compile("Content-Length: (\\d+)").matcher(str);\r
+                               if (ma.find()) {\r
+                                       clen = Integer.valueOf(ma.group(1));\r
+                               }\r
+                               reqHeaders.add(str);\r
+                       }\r
+                       if (clen > 0) {\r
+                               in.read(c, 0, clen);\r
+                               System.err.println("PSTR: "+String.valueOf(c));\r
+                       }\r
+\r
+                       if (method == null)\r
+                               return; // 謎だ\r
+                       \r
+                       // ファイルを探そう\r
+                       {\r
+                               ArrayList<String> header = new ArrayList<String>();\r
+                               \r
+                               int body_length = 0;\r
+                               int len = -1;\r
+                               int m = 4096;\r
+                               \r
+                               Matcher ma = null;\r
+                               String filename = "";\r
+                               String file = null;\r
+                               String command = null;\r
+                               if ( method.equals("GET") ) {\r
+                                       ma = Pattern.compile("/([^/]+\\.(htm|cgi|css|png|js|do|css))\\??(.*?)$").matcher(location);\r
+                                       if (ma.find()) {\r
+                                               file = ma.group(1);\r
+                                               if ( ma.groupCount() >= 3 && (ma.group(1).equals("prevLogin.cgi") || ma.group(1).equals("dispframe.cgi")) ) {\r
+                                                       filename = folder+File.separator+ma.group(1)+"."+ma.group(3);\r
+                                               }\r
+                                               else if ( ma.groupCount() >= 3 && ma.group(1).equals("dvdr_ctrl.cgi") ) {\r
+                                                       String ps = ma.group(3);\r
+                                                       filename = folder+File.separator+ma.group(1)+"."+ps;\r
+                                                       if ( ps.contains("cCMD_") ) {\r
+                                                               int a = ps.indexOf("cCMD_");\r
+                                                               int z = ps.indexOf(".",a);\r
+                                                               command = ps.substring(a,z+1);\r
+                                                               System.out.println("command: "+command);\r
+                                                       }\r
+                                                       else {\r
+                                                       }\r
+                                               }\r
+                                               else if ( ma.groupCount() >= 3 && ma.group(1).equals("reserve_list.cgi") ) {\r
+                                                       String ps = ma.group(3);\r
+                                                       filename = folder+File.separator+ma.group(1)+"."+ps;\r
+                                                       int a = ps.indexOf("ANC_RSVLSTNO");\r
+                                                       if ( a != -1 ) {\r
+                                                               int z = ps.indexOf("&",a);\r
+                                                               if ( z == -1 ) {\r
+                                                                       z = ps.length();\r
+                                                               }\r
+                                                               command = ps.substring(a,z+1);\r
+                                                               System.out.println("command: "+command);\r
+                                                       }\r
+                                               }\r
+                                               else {\r
+                                                       filename = folder+File.separator+ma.group(1);\r
+                                               }\r
+                                       }\r
+                                       else if (location.endsWith("/")){\r
+                                               file = "";\r
+                                               filename = folder+File.separator+"index.htm";\r
+                                       }\r
+                                       else {\r
+                                               filename = folder+File.separator+"404.htm";\r
+                                       }\r
+                               }\r
+                               else {\r
+                                       ma = Pattern.compile("/([^/]+\\.(cgi|htm|css|png))").matcher(location);\r
+                                       if (ma.find()) {\r
+                                               file = ma.group(1);\r
+                                               if ( ma.group(1).equals("dvdr_ctrl.cgi") || /*ma.group(1).equals("reserve_list.cgi") ||*/ ma.group(1).equals("dispframe.cgi") ) {\r
+                                                       String ps = new String(c);\r
+                                                       filename = folder+File.separator+ma.group(1)+"."+ps;\r
+                                                       if ( ps.contains("cCMD_") ) {\r
+                                                               int a = ps.indexOf("cCMD_");\r
+                                                               int z = ps.indexOf(".",a);\r
+                                                               command = ps.substring(a,z);\r
+                                                               System.out.println("command: "+command);\r
+                                                       }\r
+                                                       else {\r
+                                                       }\r
+                                               }\r
+                                               else {\r
+                                                       filename = folder+File.separator+ma.group(1);\r
+                                               }\r
+                                       }\r
+                                       else if (location.endsWith("/")){\r
+                                               filename = folder+File.separator+"index.htm";\r
+                                       }\r
+                                       else {\r
+                                               filename = folder+File.separator+"404.htm";\r
+                                       }\r
+                               }\r
+                               \r
+                               System.out.println("size: "+body_length+", "+location+", "+method);\r
+                               \r
+                               File f = null;\r
+                               \r
+                               // 新デバッグログ対応\r
+                               File dbg = new File(folder+File.separator+"debug");\r
+                               if ( file != null && dbg.exists() ) {\r
+                                       for ( File fd : dbg.listFiles() ) {\r
+                                               if ( fd.isFile() && fd.getName().endsWith(".htm") ) {\r
+                                                       BufferedReader br = new BufferedReader(new FileReader(fd));\r
+                                                       String s = null;\r
+                                                       while ( (s = br.readLine()) != null ) {\r
+                                                               if ( s.startsWith("# GET:") || s.startsWith("# POST:") ) {\r
+                                                                       //System.err.println(File.separator+file+", "+s);\r
+                                                                       if ( s.contains("/"+file) ) {\r
+                                                                               if ( command == null || s.contains(command) ) {\r
+                                                                                       f = fd;\r
+                                                                               }\r
+                                                                       }\r
+                                                                       break;\r
+                                                               }\r
+                                                       }\r
+                                                       br.close();\r
+                                                       br = null;\r
+                                               }\r
+                                               if ( f != null ) {\r
+                                                       break;\r
+                                               }\r
+                                       }\r
+                               }\r
+                               \r
+                               if ( f == null && ! (f = new File(filename+".txt")).exists() && ! (f = new File(filename)).exists() ) {\r
+                                       //System.out.println("Not exist: "+f.getName());\r
+                                       \r
+                                       header.add("HTTP/1.0 404 Not Found\r\n");\r
+                                       header.add("\r\n");\r
+                               }\r
+                               else {\r
+                                       System.out.println("Exist: "+f.getName());\r
+                                       \r
+                                       header.add("HTTP/1.0 200 OK\r\n");\r
+                                       header.add("Connection: close\r\n");\r
+                                       header.add("Content-Type: text/html\r\n");\r
+                                       header.add("Pragma: no-cache\r\n");\r
+                                       header.add("Cache-Control: no-cache\r\n");\r
+                                       header.add("Content-Length: #CSIZE#\r\n");\r
+                                       header.add("\r\n");\r
+                                       \r
+                                       reader = new FileInputStream(f);\r
+                                       \r
+                                       while ((len = reader.read(b, body_length, m)) > 0 ) {\r
+                                               body_length += len;\r
+                                       }\r
+                                       reader.close();\r
+                                       reader = null;\r
+                               }\r
+                               \r
+                               for (String s : header) {\r
+                                       ma = Pattern.compile("#CSIZE#").matcher(s);\r
+                                       if (ma.find()) {\r
+                                               s = ma.replaceFirst(String.valueOf(body_length+1));\r
+                                       }\r
+                                       out.write(s);\r
+                                       out.flush();\r
+                               }\r
+                               \r
+                               for (int n=0; n<body_length; n+=m) {\r
+                                       if ((n+m) < body_length) {\r
+                                               w.write(b, n, m);\r
+                                       }\r
+                                       else {\r
+                                               int s = body_length-n;\r
+                                               w.write(b, n, s);\r
+                                       }\r
+                               }\r
+                               w.flush();\r
+                       }\r
+                               \r
+               }\r
+               catch (SocketException e) {\r
+                       System.err.println(e.toString());\r
+               }\r
+               catch (IOException e) {\r
+                       e.printStackTrace();\r
+               }\r
+               finally {\r
+                       CommonUtils.closing(reader);\r
+                       CommonUtils.closing(in);\r
+                       CommonUtils.closing(r);\r
+                       CommonUtils.closing(out);\r
+                       CommonUtils.closing(w);\r
+                       CommonUtils.closing(sock);\r
+               }\r
+       }\r
+}\r
diff --git a/TinyBannavi/src/niseRD/Viewer.java b/TinyBannavi/src/niseRD/Viewer.java
new file mode 100644 (file)
index 0000000..5cfa3da
--- /dev/null
@@ -0,0 +1,115 @@
+package niseRD;\r
+\r
+import javax.swing.DefaultComboBoxModel;\r
+import javax.swing.SwingUtilities;\r
+import javax.swing.JPanel;\r
+import javax.swing.JFrame;\r
+import javax.swing.JLabel;\r
+import javax.swing.BoxLayout;\r
+import javax.swing.JTextField;\r
+import javax.swing.JButton;\r
+\r
+import java.io.File;\r
+import java.io.IOException;\r
+import java.net.ServerSocket;\r
+import java.net.Socket;\r
+import java.awt.BorderLayout;\r
+import javax.swing.JComboBox;\r
+\r
+public class Viewer extends JFrame {\r
+\r
+       private static final long serialVersionUID = 1L;\r
+       private JPanel jContentPane = null;\r
+       private JButton jButton = null;\r
+       private JComboBox jComboBox = null;\r
+\r
+       /**\r
+        * This method initializes jButton      \r
+        *      \r
+        * @return javax.swing.JButton  \r
+        */\r
+       private JButton getJButton() {\r
+               if (jButton == null) {\r
+                       jButton = new JButton();\r
+                       jButton.setText("実行する");\r
+                       jButton.addMouseListener(new java.awt.event.MouseAdapter() {\r
+                               public void mouseClicked(java.awt.event.MouseEvent e) {\r
+                                       new Main((String)jComboBox.getSelectedItem());\r
+                                       ((JButton)e.getSource()).removeMouseListener(null);\r
+                                       ((JButton)e.getSource()).setEnabled(false);\r
+                               }\r
+                       });\r
+               }\r
+               return jButton;\r
+       }\r
+\r
+       /**\r
+        * This method initializes jComboBox    \r
+        *      \r
+        * @return javax.swing.JComboBox        \r
+        */\r
+       private JComboBox getJComboBox() {\r
+               if (jComboBox == null) {\r
+                       jComboBox = new JComboBox();\r
+                       DefaultComboBoxModel model = new DefaultComboBoxModel();\r
+                       jComboBox.setModel(model);\r
+                       String root = "D:\\workspace\\Data";\r
+                       File f = new File(root);\r
+                       for ( File fc : f.listFiles()) {\r
+                               if (fc.isDirectory()) {\r
+                                       model.addElement(root+"\\"+fc.getName());\r
+                               }\r
+                       }\r
+               }\r
+               return jComboBox;\r
+       }\r
+\r
+       /**\r
+        * @param args\r
+        */\r
+       public static void main(String[] args) {\r
+               // TODO Auto-generated method stub\r
+               SwingUtilities.invokeLater(new Runnable() {\r
+                       public void run() {\r
+                               Viewer thisClass = new Viewer();\r
+                               thisClass.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);\r
+                               thisClass.setVisible(true);\r
+                       }\r
+               });\r
+       }\r
+\r
+       /**\r
+        * This is the default constructor\r
+        */\r
+       public Viewer() {\r
+               super();\r
+               initialize();\r
+       }\r
+\r
+       /**\r
+        * This method initializes this\r
+        * \r
+        * @return void\r
+        */\r
+       private void initialize() {\r
+               this.setSize(160, 100);\r
+               this.setContentPane(getJContentPane());\r
+               this.setTitle("JFrame");\r
+       }\r
+\r
+       /**\r
+        * This method initializes jContentPane\r
+        * \r
+        * @return javax.swing.JPanel\r
+        */\r
+       private JPanel getJContentPane() {\r
+               if (jContentPane == null) {\r
+                       jContentPane = new JPanel();\r
+                       jContentPane.setLayout(new BoxLayout(getJContentPane(), BoxLayout.Y_AXIS));\r
+                       jContentPane.add(getJComboBox(), null);\r
+                       jContentPane.add(getJButton(), null);\r
+               }\r
+               return jContentPane;\r
+       }\r
+\r
+}  //  @jve:decl-index=0:visual-constraint="10,10"\r
diff --git a/TinyBannavi/src/remoteCtrl/Viewer.java b/TinyBannavi/src/remoteCtrl/Viewer.java
new file mode 100644 (file)
index 0000000..5caadbe
--- /dev/null
@@ -0,0 +1,1104 @@
+\r
+package remoteCtrl;\r
+\r
+import javax.imageio.ImageIO;\r
+import javax.swing.SwingUtilities;\r
+import java.awt.BorderLayout;\r
+import java.awt.Color;\r
+import java.awt.Component;\r
+import java.awt.Dimension;\r
+import java.awt.Font;\r
+import java.awt.Image;\r
+import java.awt.Point;\r
+import java.awt.Rectangle;\r
+import java.awt.event.ActionEvent;\r
+import java.awt.event.ActionListener;\r
+import java.awt.event.ComponentEvent;\r
+import java.awt.event.ComponentListener;\r
+import java.awt.event.KeyAdapter;\r
+import java.awt.event.KeyEvent;\r
+import java.awt.event.MouseAdapter;\r
+import java.awt.event.MouseEvent;\r
+import java.awt.event.MouseListener;\r
+import java.awt.event.WindowEvent;\r
+import java.awt.event.WindowListener;\r
+import java.io.BufferedReader;\r
+import java.io.BufferedWriter;\r
+import java.io.File;\r
+import java.io.FileReader;\r
+import java.io.FileWriter;\r
+import java.io.IOException;\r
+import java.net.Authenticator;\r
+import java.util.ArrayList;\r
+import java.util.regex.Matcher;\r
+import java.util.regex.Pattern;\r
+\r
+import javax.swing.ActionMap;\r
+import javax.swing.DefaultCellEditor;\r
+import javax.swing.DefaultComboBoxModel;\r
+import javax.swing.JButton;\r
+import javax.swing.JPanel;\r
+import javax.swing.JFrame;\r
+import javax.swing.JScrollPane;\r
+import javax.swing.JTabbedPane;\r
+import javax.swing.JTable;\r
+import javax.swing.SpringLayout;\r
+import javax.swing.UIDefaults;\r
+import javax.swing.UIManager;\r
+import javax.swing.plaf.FontUIResource;\r
+import javax.swing.table.AbstractTableModel;\r
+import javax.swing.table.DefaultTableColumnModel;\r
+import javax.swing.table.DefaultTableModel;\r
+import javax.swing.table.TableCellRenderer;\r
+import javax.swing.table.TableColumn;\r
+import javax.swing.JComboBox;\r
+\r
+import javax.swing.JLabel;\r
+\r
+import tainavi.CommonSwingUtils;\r
+import tainavi.CommonUtils;\r
+import tainavi.RecorderInfo;\r
+import tainavi.RecorderInfoList;\r
+import tainavi.VWColorCellRenderer;\r
+import tainavi.VWColorChooserDialog;\r
+\r
+public class Viewer extends JFrame {\r
+\r
+       private static final long serialVersionUID = 1L;\r
+       \r
+       private JTabbedPane jTabbedPane = null;\r
+       \r
+       private JPanel jContentPane = null;\r
+       private JComboBox jComboBox = null;\r
+       private JPanel jPanel = null;\r
+       private JLabel jLabel = null;\r
+       \r
+       private SpringLayout layout = null;\r
+    private final int wBK = 100;\r
+    private final int hBK = 30;\r
+       \r
+       private JPanel jSettingPane = null;\r
+       private JScrollPane jScrollPane = null;\r
+       private JTable jTable = null;\r
+       private JButton jButton = null;\r
+       \r
+       private VWColorChooserDialog ccwin = null;\r
+\r
+       private final String defFile = "env/RemoteCtrlIni.def";\r
+       private final String defFileBK = "env/RemoteCtrl.def";\r
+       \r
+       final private ArrayList<BKSet> bkset = new ArrayList<BKSet>();\r
+       final private ArrayList<String[]> rowData = new ArrayList<String[]>();\r
+       \r
+       private RecorderInfo recinfo = null;\r
+       \r
+       /**\r
+        * 鯛ナビのレコーダ一覧の中で"RD-"を含むもののみ抽出したリスト. \r
+        */\r
+       private RecorderInfoList recorderList = new RecorderInfoList();\r
+       \r
+       final int maxCol = 5;\r
+       final int maxRow = 20;\r
+\r
+       final String[][] keydefs = {\r
+                       {"08","BACK SPACE"},\r
+                       {"0A","ENTER"},\r
+                       {"10","SHIFT"},\r
+                       {"11","CTRL"},\r
+                       {"12","ALT"},\r
+                       {"13","PAUSE"},\r
+                       {"1B","ESC"},\r
+                       {"20","SPCAE"},\r
+                       {"21","PAGE UP"},\r
+                       {"22","PAGE DOWN"},\r
+                       {"23","END"},\r
+                       {"24","HOME"},\r
+                       {"25","左"},\r
+                       {"26","上"},\r
+                       {"27","右"},\r
+                       {"28","下"},\r
+                       {"2C",","},\r
+                       {"2D","-"},\r
+                       {"2E","."},\r
+                       {"2F","/"},\r
+                       {"30","0"},\r
+                       {"31","1"},\r
+                       {"32","2"},\r
+                       {"33","3"},\r
+                       {"34","4"},\r
+                       {"35","5"},\r
+                       {"36","6"},\r
+                       {"37","7"},\r
+                       {"38","8"},\r
+                       {"39","9"},\r
+                       {"3B",";"},\r
+                       {"3D","="},\r
+                       {"41","A"},\r
+                       {"42","B"},\r
+                       {"43","C"},\r
+                       {"44","D"},\r
+                       {"45","E"},\r
+                       {"46","F"},\r
+                       {"47","G"},\r
+                       {"48","H"},\r
+                       {"49","I"},\r
+                       {"4A","J"},\r
+                       {"4B","K"},\r
+                       {"4C","L"},\r
+                       {"4D","M"},\r
+                       {"4E","N"},\r
+                       {"4F","O"},\r
+                       {"50","P"},\r
+                       {"51","Q"},\r
+                       {"52","R"},\r
+                       {"53","S"},\r
+                       {"54","T"},\r
+                       {"55","U"},\r
+                       {"56","V"},\r
+                       {"57","W"},\r
+                       {"58","X"},\r
+                       {"59","Y"},\r
+                       {"5A","Z"},\r
+                       {"5B","["},\r
+                       {"5C","\\"},\r
+                       {"5D","]"},\r
+                       {"70","F1"},\r
+                       {"71","F2"},\r
+                       {"72","F3"},\r
+                       {"73","F4"},\r
+                       {"74","F5"},\r
+                       {"75","F6"},\r
+                       {"76","F7"},\r
+                       {"77","F8"},\r
+                       {"78","F9"},\r
+                       {"79","F10"},\r
+                       {"7A","F11"},\r
+                       {"7B","F12"},\r
+                       {"7F","DELETE"},\r
+                       {"9B","INS"},\r
+                       {"C0","`"},\r
+                       {"DE","'"},\r
+       };\r
+       \r
+       final String[] codedefs = {\r
+                       "00(11)",\r
+                       "01(1)",\r
+                       "02(2)",\r
+                       "03(3)",\r
+                       "04(4)",\r
+                       "05(5)",\r
+                       "06(6)",\r
+                       "07(7)",\r
+                       "08(8)",\r
+                       "09(9)",\r
+                       "0A",\r
+                       "0B",\r
+                       "0C(12)",\r
+                       "0D(10)",\r
+                       "0E(サーチ/文字)",\r
+                       "0F(入力切替)",\r
+                       "10",\r
+                       "11(トレイ開閉)",\r
+                       "12(電源切)",\r
+                       "13(再生)",\r
+                       "14",\r
+                       "15(録画)",\r
+                       "16(停止)",\r
+                       "17(一時停止)",\r
+                       "18(DVD/USB)",\r
+                       "19(HDD)",\r
+                       "1A(タイムスリップ)",\r
+                       "1B",\r
+                       "1C",\r
+                       "1D",\r
+                       "1E(CH上)",\r
+                       "1F(CH下)",\r
+                       "20",\r
+                       "21(放送切替)",\r
+                       "22(メディア)",\r
+                       "23(d データ)",\r
+                       "24(入力1スルー)",\r
+                       "25(CH番号入力",\r
+                       "26",\r
+                       "27",\r
+                       "28",\r
+                       "29(青)",\r
+                       "2A(赤)",\r
+                       "2B(緑)",\r
+                       "2C(黄)",\r
+                       "2D",\r
+                       "2E(ごみ箱へ)",\r
+                       "2F",\r
+                       "30",\r
+                       "31",\r
+                       "32",\r
+                       "33",\r
+                       "34",\r
+                       "35",\r
+                       "36",\r
+                       "37(iLINK)",\r
+                       "38(アングル)",\r
+                       "39(字幕)",\r
+                       "3A(タイムバー)",\r
+                       "3B(おまかせ)",\r
+                       "3C",\r
+                       "3D",\r
+                       "3E",\r
+                       "3F",\r
+                       "40(録画予約一覧)",\r
+                       "41(編集ナビ)",\r
+                       "42(見るナビ)",\r
+                       "43(見ながら)",\r
+                       "44(決定)",\r
+                       "45(クイックメニュー)",\r
+                       "46(スタートメニュー)",\r
+                       "47",\r
+                       "48",\r
+                       "49",\r
+                       "4A(モード)",\r
+                       "4B(戻る)",\r
+                       "4C",\r
+                       "4D",\r
+                       "4E",\r
+                       "4F",\r
+                       "50",\r
+                       "51",\r
+                       "52(設定)",\r
+                       "53(クリア/先頭)",\r
+                       "54",\r
+                       "55(ワンタッチリプレイ)",\r
+                       "56",\r
+                       "57(全削除)",\r
+                       "58",\r
+                       "59",\r
+                       "5A(表示/残量)",\r
+                       "5B(ワンタッチスキップ)",\r
+                       "5C",\r
+                       "5D",\r
+                       "5E(ズーム)",\r
+                       "5F",\r
+                       "60(終了)",\r
+                       "61",\r
+                       "62(ヘルプ)",\r
+                       "63(W録)",\r
+                       "64",\r
+                       "65",\r
+                       "66",\r
+                       "67",\r
+                       "68",\r
+                       "69",\r
+                       "6A",\r
+                       "6B",\r
+                       "6C",\r
+                       "6D(番組表)",\r
+                       "6E",\r
+                       "6F",\r
+                       "70",\r
+                       "71",\r
+                       "72",\r
+                       "73",\r
+                       "74(XDE)",\r
+                       "75",\r
+                       "76",\r
+                       "77",\r
+                       "78",\r
+                       "79",\r
+                       "7A",\r
+                       "7B",\r
+                       "7C",\r
+                       "7D",\r
+                       "7E",\r
+                       "7F",\r
+                       "80(逆スキップ)",\r
+                       "81",\r
+                       "82",\r
+                       "83",\r
+                       "84(戻スキップ)",\r
+                       "85",\r
+                       "86",\r
+                       "87",\r
+                       "88(逆スロー)",\r
+                       "89",\r
+                       "8A",\r
+                       "8B",\r
+                       "8C(戻スロー)",\r
+                       "8D",\r
+                       "8E",\r
+                       "8F",\r
+                       "90",\r
+                       "91",\r
+                       "92",\r
+                       "93",\r
+                       "94",\r
+                       "95",\r
+                       "96",\r
+                       "97",\r
+                       "98(早送り)",\r
+                       "99",\r
+                       "9A(早戻し)",\r
+                       "9B",\r
+                       "9C",\r
+                       "9D(コマ送り)",\r
+                       "9E(コマ戻し)",\r
+                       "9F",\r
+                       "A0",\r
+                       "A1",\r
+                       "A2(ドライブ切換)",\r
+                       "A3",\r
+                       "A4",\r
+                       "A5",\r
+                       "A6",\r
+                       "A7(録画モード)",\r
+                       "A8",\r
+                       "A9",\r
+                       "AA",\r
+                       "AB",\r
+                       "AC",\r
+                       "AD(ワンタッチリプレイ)",\r
+                       "AE(ワンタッチスキップ)",\r
+                       "AF",\r
+                       "B0(解像度)",\r
+                       "B1",\r
+                       "B2",\r
+                       "B3",\r
+                       "B4(番組説明)",\r
+                       "B5(番組ナビ)",\r
+                       "B6",\r
+                       "B7",\r
+                       "B8",\r
+                       "B9",\r
+                       "BA",\r
+                       "BB",\r
+                       "BC",\r
+                       "BD",\r
+                       "BE",\r
+                       "BF",\r
+                       "C0(上)",\r
+                       "C1",\r
+                       "C2",\r
+                       "C3",\r
+                       "C4(右)",\r
+                       "C5",\r
+                       "C6",\r
+                       "C7",\r
+                       "C8(下)",\r
+                       "C9",\r
+                       "CA",\r
+                       "CB",\r
+                       "CC(左)",\r
+                       "CD",\r
+                       "CE",\r
+                       "CF",\r
+                       "D0(トップメニュー)",\r
+                       "D1(メニュー)",\r
+                       "D2(リターン)",\r
+                       "D3(音声/音多)",\r
+                       "D4(アングル)",\r
+                       "D5(字幕)",\r
+                       "D6",\r
+                       "D7",\r
+                       "D8",\r
+                       "D9(チャプター分割/結合)",\r
+                       "DA(タイムバー)",\r
+                       "DB",\r
+                       "DC",\r
+                       "DD",\r
+                       "DE(番組ナビ)",\r
+                       "DF(かんたんダビング)",\r
+                       "E0",\r
+                       "E1",\r
+                       "E2",\r
+                       "E3",\r
+                       "E4",\r
+                       "E5",\r
+                       "E6",\r
+                       "E7",\r
+                       "E8",\r
+                       "E9",\r
+                       "EA",\r
+                       "EB",\r
+                       "EC",\r
+                       "ED",\r
+                       "EE",\r
+                       "EF",\r
+                       "F0",\r
+                       "F1",\r
+                       "F2",\r
+                       "F3",\r
+                       "F4",\r
+                       "F5",\r
+                       "F6",\r
+                       "F7",\r
+                       "F8",\r
+                       "F9",\r
+                       "FA",\r
+                       "FB",\r
+                       "FC",\r
+                       "FD",\r
+                       "FE",\r
+                       "FF(電源入)",\r
+       };\r
+       \r
+       \r
+       \r
+       private String getCode2Key(String key) {\r
+               for (String[] s : keydefs ) {\r
+                       if (s[0].equals(key)) {\r
+                               return s[1];\r
+                       }\r
+               }\r
+               return null;\r
+       }\r
+       private String getKey2Code(String code) {\r
+               for (String[] s : keydefs ) {\r
+                       if (s[1].equals(code)) {\r
+                               return s[0];\r
+                       }\r
+               }\r
+               return null;\r
+       }\r
+       \r
+       private String getCode2Code(String code) {\r
+               if (code.length() > 0) {\r
+                       for (String cStr : codedefs) {\r
+                               if (cStr.startsWith(code)) {\r
+                                       return cStr;\r
+                               }\r
+                       }\r
+               }\r
+               return null;\r
+       }\r
+       \r
+       /*\r
+        * 操作タブのコンポーネント\r
+        */\r
+       \r
+       private JPanel getJContentPane() {\r
+               if (jContentPane == null) {\r
+                       jContentPane = new JPanel();\r
+                       jContentPane.setLayout(new BorderLayout());\r
+                       jContentPane.add(getJComboBox(), BorderLayout.NORTH);\r
+                       jContentPane.add(getJPanel(), BorderLayout.CENTER);\r
+                       jContentPane.add(getJLabel(), BorderLayout.SOUTH);\r
+\r
+                       this.addComponentListener(new ComponentListener() {\r
+                               @Override\r
+                               public void componentHidden(ComponentEvent arg0) {\r
+                               }\r
+                               @Override\r
+                               public void componentMoved(ComponentEvent arg0) {\r
+                               }\r
+                               @Override\r
+                               public void componentResized(ComponentEvent arg0) {\r
+                               }\r
+                               @Override\r
+                               public void componentShown(ComponentEvent arg0) {\r
+                                       jComboBox.requestFocusInWindow();       // フォーカスを移動する\r
+                               }\r
+                       });\r
+               }\r
+               return jContentPane;\r
+       }\r
+       \r
+       private JComboBox getJComboBox() {\r
+               if (jComboBox == null) {\r
+                       jComboBox = new JComboBox();\r
+                       \r
+                       /*\r
+                       jComboBox.getInputMap().put(KeyStroke.getKeyStroke(KeyEvent.VK_DOWN,0),"none");\r
+                       jComboBox.getActionMap().put("none", null);\r
+                       */\r
+\r
+                       // キー入力を受け付ける\r
+                       /*\r
+                       InputMap m = jComboBox.getInputMap();\r
+                       while (m != null){\r
+                               m.clear();\r
+                               m = m.getParent();\r
+                       }\r
+                       */\r
+                       ActionMap n = jComboBox.getActionMap();\r
+                       while (n != null){\r
+                               n.clear();\r
+                               n = n.getParent();\r
+                       }\r
+                       jComboBox.addKeyListener(new KeyAdapter() {\r
+                               @Override\r
+                               public void keyPressed(KeyEvent arg0) {\r
+                                       keyPressedAction(arg0.getKeyCode());\r
+                               }\r
+                       });\r
+\r
+            DefaultComboBoxModel model = (DefaultComboBoxModel) jComboBox.getModel();\r
+            for (RecorderInfo r : recorderList) {\r
+               model.addElement(r.getRecorderIPAddr()+":"+r.getRecorderPortNo()+":"+r.getRecorderId());\r
+            }\r
+            \r
+            setSelectedRecorder();\r
+            \r
+            jComboBox.addActionListener(new ActionListener() {\r
+                               @Override\r
+                               public void actionPerformed(ActionEvent e) {\r
+                           setSelectedRecorder();\r
+                               }\r
+            });\r
+               }\r
+               return jComboBox;\r
+       }\r
+       \r
+       private JPanel getJPanel() {\r
+               if (jPanel == null) {\r
+                       jPanel = new JPanel();\r
+                       \r
+                       jPanel.setPreferredSize(new Dimension(maxCol*wBK, maxRow*hBK));\r
+                       jPanel.setLayout(new SpringLayout());\r
+                       layout = (SpringLayout) jPanel.getLayout();\r
+                       \r
+                       genBKSet();\r
+               }\r
+               return jPanel;\r
+       }\r
+       \r
+       private JLabel getJLabel() {\r
+               if (jLabel == null) {\r
+                       jLabel = new JLabel();\r
+                       jLabel.setText(" ");\r
+               }\r
+               return jLabel;\r
+       }\r
+       \r
+       // キーアクション付きのボタンのクラス\r
+       private class BKSet extends JButton {\r
+               public String button;\r
+               public String key;\r
+               public int row;\r
+               public int column;\r
+               public int kcode;\r
+               public Color color;\r
+               \r
+               public int getKcode() {\r
+                       return kcode;\r
+               }\r
+               \r
+               public void action() {\r
+                       String key = this.key.substring(0,2);\r
+                       jLabel.setText(this.button+"("+key+")");\r
+                       if ( ! key.equals("FF")) {\r
+                               tainavi.HDDRecorderUtils ru = new tainavi.HDDRecorderUtils();\r
+                               Authenticator.setDefault(ru.new MyAuthenticator(recinfo.getRecorderUser(), recinfo.getRecorderPasswd()));\r
+                               ru.reqGET("http://"+recinfo.getRecorderIPAddr()+":"+recinfo.getRecorderPortNo()+"/remote/remote.htm?key="+key, null);\r
+                               System.out.println(this.button+"->"+key);\r
+                       }\r
+                       else {\r
+                               tainavi.HDDRecorderUtils ru = new tainavi.HDDRecorderUtils();\r
+                               ru.setMacAddr(recinfo.getRecorderMacAddr());\r
+                               ru.setBroadcast(recinfo.getRecorderBroadcast());\r
+                               ru.wakeup();\r
+                       }\r
+               }\r
+               \r
+               public BKSet(String button, String key, int row, int column, String kcode, Color c) {\r
+                       super();\r
+                       this.setText(button);\r
+                       this.button = button;\r
+                       this.key = key;\r
+                       this.row = row;\r
+                       this.column = column;\r
+                       this.kcode = -1;\r
+                       this.color = new Color(0,0,0);\r
+                       \r
+                       if ( ! button.equals("") ) {\r
+                               if ( ! kcode.equals("")) {\r
+                                       this.kcode = Integer.parseInt(kcode,16);\r
+                               }\r
+                               \r
+                               this.setForeground(this.color = c);\r
+                               this.addMouseListener(new MouseAdapter() {\r
+                                       @Override\r
+                                       public void mousePressed(MouseEvent e) {\r
+                                               action();\r
+                                       }\r
+                               });\r
+                       }\r
+                       else {\r
+                               this.setEnabled(false);\r
+                       }\r
+                       \r
+                       this.addKeyListener(new KeyAdapter() {\r
+                               @Override\r
+                               public void keyPressed(KeyEvent arg0) {\r
+                                       keyPressedAction(arg0.getKeyCode());\r
+                               }\r
+                       });\r
+\r
+               }\r
+       }\r
+       \r
+       // コンボボックスの操作\r
+       private void setSelectedRecorder() {\r
+        DefaultComboBoxModel model = (DefaultComboBoxModel) jComboBox.getModel();\r
+        for (RecorderInfo r : recorderList) {\r
+               if (model.getSelectedItem().equals(r.MySelf())) {\r
+                   recinfo = r;\r
+                   System.out.println(model.getSelectedItem());\r
+                       break;\r
+               }\r
+        }\r
+       }\r
+       \r
+       // キーを押したときの動作\r
+       private void keyPressedAction(int kcode) {\r
+               for (BKSet bk : bkset) {\r
+                       if (bk.getKcode() == kcode) {\r
+                               bk.requestFocusInWindow();      // フォーカスを移動する\r
+                               bk.action();                            // アクションを実行する\r
+                               return;\r
+                       }\r
+               }\r
+               jLabel.setText("unknown keycode("+String.format("%02X",kcode)+")");\r
+       }\r
+       \r
+       /*\r
+        * 設定タブのコンポーネント\r
+        */\r
+       \r
+       private JPanel getJSettingPane() {\r
+               if (jSettingPane == null) {\r
+                       jSettingPane = new JPanel();\r
+                       jSettingPane.setLayout(new BorderLayout());\r
+                       jSettingPane.add(getJScrollPane(), BorderLayout.CENTER);\r
+                       jSettingPane.add(getJButton("設定"), BorderLayout.SOUTH);\r
+                       \r
+                       setBK4Table();\r
+               }\r
+               return jSettingPane;\r
+       }\r
+       \r
+       private JScrollPane getJScrollPane() {\r
+               if (jScrollPane == null) {\r
+                       jScrollPane = new JScrollPane();\r
+                       jScrollPane.setViewportView(getJTable());\r
+               }\r
+               return jScrollPane;\r
+       }\r
+       \r
+       private JTable getJTable() {\r
+               if (jTable == null) {\r
+                       final String[] colname = { "配置", "ラベル","コード","キー","色" };\r
+                       final int[] colwidth = { 25,150,100,100,50 };\r
+                       \r
+                       DefaultTableModel model = new DefaultTableModel(colname, 0) {\r
+                               @Override\r
+                               public Object getValueAt(int row, int column) {\r
+                                       return rowData.get(row)[column];\r
+                               }\r
+                               @Override\r
+                               public void setValueAt(Object aValue, int row, int column) {\r
+                                       rowData.get(row)[column] = (String) aValue;\r
+                               }\r
+                               @Override\r
+                               public int getRowCount() {\r
+                                       return rowData.size();\r
+                               }\r
+                               @Override\r
+                               public boolean isCellEditable(int row, int column) {\r
+                                       if (column == 0 || column == 4) {\r
+                                               return false;\r
+                                       }\r
+                                       return true;\r
+                               }\r
+                       };\r
+                       jTable = new JTable(model) {\r
+                               private final Color evenColor = new Color(240,240,255);\r
+                               @Override\r
+                               public Component prepareRenderer(TableCellRenderer tcr, int row, int column) {\r
+                                       Component c = super.prepareRenderer(tcr, row, column);\r
+                                       if (column != 4) {\r
+                                               if(isRowSelected(row)) {\r
+                                                       c.setForeground(getSelectionForeground());\r
+                                                       c.setBackground(getSelectionBackground());\r
+                                           }\r
+                                               else {\r
+                                                       c.setForeground(getForeground());\r
+                                                       c.setBackground(((row/maxCol)%2==0)?evenColor:getBackground());\r
+                                               }\r
+                                       }\r
+                                       return c;\r
+                          }\r
+                       };\r
+                       \r
+                       DefaultTableColumnModel colModel = (DefaultTableColumnModel) jTable.getColumnModel();\r
+                       TableColumn column = null;\r
+                       for (int i=0; i<colModel.getColumnCount() && i<colwidth.length ; i++){\r
+                               column = colModel.getColumn(i);\r
+                               column.setPreferredWidth(colwidth[i]);\r
+                       }\r
+                       \r
+                       JComboBox jcbcode = new JComboBox();\r
+                       jcbcode.setEditable(false);\r
+                       jcbcode.addItem("");\r
+                       for (String cd : codedefs) {\r
+                               jcbcode.addItem(cd);\r
+                       }\r
+                       column = jTable.getColumn("コード");\r
+                       column.setCellEditor(new DefaultCellEditor(jcbcode));\r
+                       \r
+                       JComboBox jcbkey = new JComboBox();\r
+                       jcbkey.setEditable(false);\r
+                       jcbkey.addItem("");\r
+                       for (int i=1; i<255; i++) {\r
+                               String key = getCode2Key(String.format("%02X", i));\r
+                               if (key != null) {\r
+                                       jcbkey.addItem(key);\r
+                               }\r
+                       }\r
+                       column = jTable.getColumn("キー");\r
+                       column.setCellEditor(new DefaultCellEditor(jcbkey));\r
+                       \r
+                       TableCellRenderer colorCellRenderer = new VWColorCellRenderer();\r
+                       jTable.getColumn("色").setCellRenderer(colorCellRenderer);\r
+                       \r
+                       jTable.addMouseListener(new MouseListener() {\r
+                               @Override\r
+                               public void mouseReleased(MouseEvent e) {}\r
+                               @Override\r
+                               public void mousePressed(MouseEvent e) {}\r
+                               @Override\r
+                               public void mouseExited(MouseEvent e) {}\r
+                               @Override\r
+                               public void mouseEntered(MouseEvent e) {}\r
+                               @Override\r
+                               public void mouseClicked(MouseEvent e) {\r
+                                       JTable t = (JTable) e.getSource();\r
+                                       Point p = e.getPoint();\r
+                                       Point p2 = jTabbedPane.getLocationOnScreen();\r
+                                       \r
+                                       int col = t.convertColumnIndexToModel(t.columnAtPoint(p));\r
+                                       if (col == 4) {\r
+                                               int row = t.convertRowIndexToModel(t.rowAtPoint(p));\r
+                                               ccwin.setColor(CommonUtils.str2color(rowData.get(row)[4].substring(0,7)));\r
+                                               Rectangle r = jTable.getBounds();\r
+                                               //ccwin.setPosition(p2.x,p2.y);\r
+                                               CommonSwingUtils.setLocationCenter(Viewer.this, ccwin);\r
+                                               ccwin.setVisible(true);\r
+                                               Color c = ccwin.getSelectedColor();\r
+                                               if (c != null) {\r
+                                                       String cs = CommonUtils.color2str(c);\r
+                                                       rowData.get(row)[4] = cs+";"+cs;\r
+                                                       ((AbstractTableModel) jTable.getModel()).fireTableDataChanged();\r
+                                               }\r
+                                       }\r
+                               }\r
+                       });\r
+               }\r
+               return jTable;\r
+       }\r
+       \r
+       private JButton getJButton(String s) {\r
+               if (jButton == null) {\r
+                       jButton = new JButton(s);\r
+                       \r
+                       jButton.addActionListener(new ActionListener() {\r
+                               @Override\r
+                               public void actionPerformed(ActionEvent e) {\r
+                                       saveBKSet();\r
+                                       genBKSet();\r
+                               }\r
+                       });\r
+               }\r
+               return jButton;\r
+       }\r
+       \r
+       private void setBK4Table() {\r
+               final String cStr = "ABCDEFGHIJKLMNOPQRSTUVWXYZ";\r
+               int n = 0;\r
+               for (int row=0; row<maxRow; row++) {\r
+                       for (int col=0; col<maxCol; col++) {\r
+                               n = maxCol*row+col;\r
+                               String[] dat = new String[] {\r
+                                       String.format("%s%d",cStr.substring(col,col+1),row+1),\r
+                                       bkset.get(n).button,\r
+                                       bkset.get(n).key,\r
+                                       (bkset.get(n).kcode != -1)?(getCode2Key(String.format("%02X",bkset.get(n).kcode))):(""),\r
+                                       CommonSwingUtils.getColoredString(bkset.get(n).color,CommonUtils.color2str(bkset.get(n).color))\r
+                               };\r
+                               rowData.add(dat);\r
+                       }\r
+               }\r
+               ((AbstractTableModel) jTable.getModel()).fireTableDataChanged();\r
+       }\r
+       \r
+       /*\r
+        * その他のメソッド\r
+        */\r
+       \r
+       // フォントを鯛ナビの設定におきかえる\r
+    private void updateFont(final String font, final int fontSize) {\r
+       FontUIResource fontUIResource = null;\r
+       UIDefaults defaultTable = UIManager.getLookAndFeelDefaults();\r
+       for(Object o: defaultTable.keySet()) {\r
+                       if(o.toString().toLowerCase().endsWith("font")) {\r
+                               //System.out.println("set font to "+o.toString());\r
+                               fontUIResource = (FontUIResource) UIManager.get(o);\r
+                               fontUIResource = new FontUIResource(new Font(font, fontUIResource.getStyle(), fontSize));\r
+                               UIManager.put(o, fontUIResource);\r
+                       }\r
+       }\r
+       SwingUtilities.updateComponentTreeUI(this);\r
+    }\r
+       \r
+    // ボタンの設定ファイルを読み出す\r
+       private void genBKSet() {\r
+               File f = new File(defFileBK);\r
+               if ( ! f.exists() ) {\r
+                       return;\r
+               }\r
+               try {\r
+                       bkset.removeAll(bkset);\r
+                       \r
+                       BufferedReader reader = new BufferedReader(new FileReader(f));\r
+                       String str = null;\r
+                       boolean isEOF = false;\r
+                       for (int row=0; row<maxRow; row++) {\r
+                               for (int col=0; col<maxCol; col++) {\r
+                                       if ( ! isEOF) {\r
+                                               if  ((str = reader.readLine()) != null) {\r
+                                                       Matcher ma = Pattern.compile("^\\s*\"(.*?)\"\\s*,\\s*\"(.*?)\"\\s*,\\s*\"(.*?)\"\\s*(,\\s*\"(..)(..)(..)\")?").matcher(str);\r
+                                                       if (ma.find()) {\r
+                                                               Color c = new Color(Integer.parseInt(ma.group(5),16),Integer.parseInt(ma.group(6),16),Integer.parseInt(ma.group(7),16));\r
+                                                               String cd = getCode2Code(ma.group(2));\r
+                                                               bkset.add(new BKSet(ma.group(1),(cd==null)?(""):(cd),row,col,ma.group(3),c));\r
+                                                       }\r
+                                               }\r
+                                               else {\r
+                                                       isEOF = true;\r
+                                               }\r
+                                       }\r
+                                       if (isEOF) {\r
+                                               bkset.add(new BKSet("","",row,col,"",new Color(0,0,0)));\r
+                                       }\r
+                               }\r
+                       }\r
+                       reader.close();\r
+                       \r
+                       // パネルに追加\r
+                       for (BKSet bk : bkset) {\r
+                               bk.setPreferredSize(new Dimension(wBK,hBK));\r
+                           layout.putConstraint(SpringLayout.NORTH, bk, bk.row*hBK, SpringLayout.NORTH, jPanel);\r
+                           layout.putConstraint(SpringLayout.WEST, bk, bk.column*wBK, SpringLayout.WEST, jPanel);\r
+                           jPanel.add(bk);\r
+                       }\r
+               }\r
+               catch (Exception e) {\r
+                       e.printStackTrace();\r
+               }\r
+       }\r
+       \r
+       private void saveBKSet() {\r
+               File f = new File(defFileBK);\r
+               try {\r
+                       BufferedWriter writer = new BufferedWriter(new FileWriter(f));\r
+                       for (int row=0; row<maxRow; row++) {\r
+                               for (int col=0; col<maxCol; col++) {\r
+                                       int n = row*maxCol+col;\r
+                                       String code = rowData.get(n)[2];\r
+                                       String key = getKey2Code(rowData.get(n)[3]);\r
+                                       writer.write(String.format("\"%s\", \"%s\", \"%s\", \"%s\"\n",\r
+                                                       rowData.get(n)[1],\r
+                                                       (code == null || code.length() == 0)?(""):(code.substring(0,2)),\r
+                                                       (key == null)?(""):(key),\r
+                                                       rowData.get(n)[4].substring(1,7)));\r
+                               }\r
+                       }\r
+                       writer.close();\r
+                       \r
+                       // パネルから全部削除\r
+                       jPanel.removeAll();\r
+               }\r
+               catch (Exception e) {\r
+                       e.printStackTrace();\r
+               }\r
+       }\r
+    \r
+       \r
+       /**\r
+        * @param args\r
+        */\r
+       public static void main(String[] args) {\r
+               SwingUtilities.invokeLater(new Runnable() {\r
+                       public void run() {\r
+                               Viewer thisClass = new Viewer();\r
+                               thisClass.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);\r
+                               thisClass.setVisible(true);\r
+                       }\r
+               });\r
+       }\r
+\r
+       /**\r
+        * This is the default constructor\r
+        */\r
+       public Viewer() {\r
+               super();\r
+               initialize();\r
+       }\r
+\r
+       /**\r
+        * This method initializes this\r
+        * \r
+        * @return void\r
+        */\r
+       private void initialize() {\r
+               \r
+               // 設定ファイルを読み込む\r
+               String recip = "";\r
+               Rectangle ra = new Rectangle();\r
+               File f = new File(defFile);\r
+               if ( f.exists() ) {\r
+                       try {\r
+                               String str = null;\r
+                               int i = 0;\r
+                               BufferedReader reader = new BufferedReader(new FileReader(f));\r
+                               while ((str = reader.readLine()) != null) {\r
+                                       switch (i++) {\r
+                                       case 0:\r
+                                               recip = str;\r
+                                               break;\r
+                                       case 1:\r
+                                               ra.x = Integer.valueOf(str);\r
+                                               break;\r
+                                       case 2:\r
+                                               ra.y = Integer.valueOf(str);\r
+                                               break;\r
+                                       }\r
+                               }\r
+                       }\r
+                       catch (Exception e) {\r
+                               e.printStackTrace();\r
+                       }\r
+               }\r
+               \r
+               // 鯛ナビと タイニーシンクの設定から\r
+               tainavi.Env env = new tainavi.Env();\r
+               env.load();\r
+               \r
+               RecorderInfoList recInfos = new RecorderInfoList();\r
+               recInfos.load();\r
+               \r
+               ArrayList<taiSync.RecorderInfo> syncrl = taiSync.RecorderInfo.load();\r
+               \r
+               for (RecorderInfo r : recInfos) {\r
+               if (r.getRecorderIPAddr().equals("") == true || r.getRecorderId().indexOf("RD-") == -1) {\r
+                       continue;\r
+               }\r
+               \r
+               RecorderInfo rn = new RecorderInfo();\r
+               if (r.getRecorderIPAddr().equals("127.0.0.1") || r.getRecorderIPAddr().equals("localhost")) {\r
+                       for (taiSync.RecorderInfo sr : syncrl) {\r
+                               if (sr.getLocalPort() == Integer.valueOf(r.getRecorderPortNo())) {\r
+                               rn.setRecorderIPAddr(sr.getRecorderIPAddr());\r
+                               rn.setRecorderPortNo(sr.getRecorderPortNo());\r
+                                       rn.setRecorderUser(sr.getRecorderUser());\r
+                                       rn.setRecorderPasswd(sr.getRecorderPasswd());\r
+                               }\r
+                       }\r
+               }\r
+               if (rn.getRecorderIPAddr().equals("")) {\r
+                       rn.setRecorderIPAddr(r.getRecorderIPAddr());\r
+                       rn.setRecorderPortNo(r.getRecorderPortNo());\r
+                       rn.setRecorderUser(r.getRecorderUser());\r
+                       rn.setRecorderPasswd(r.getRecorderPasswd());\r
+               }\r
+               rn.setRecorderMacAddr(r.getRecorderMacAddr());\r
+               rn.setRecorderBroadcast(r.getRecorderBroadcast());\r
+               rn.setRecorderId(r.getRecorderId());\r
+               \r
+               recorderList.add(rn);\r
+               }\r
+               \r
+               if (env.getFontName() != null) {\r
+                       updateFont(env.getFontName(), env.getFontSize());\r
+               }\r
+               \r
+               try {\r
+                       final Image image = ImageIO.read(new File("icon/remocon.png"));\r
+                       this.setIconImage(image);\r
+               } catch (IOException e) {\r
+                       e.printStackTrace();\r
+               }\r
+\r
+               this.setResizable(false);\r
+               this.setContentPane(getJTabbedPane());\r
+               this.setTitle("RDリモコン");\r
+               this.pack();\r
+               \r
+               ccwin = new VWColorChooserDialog();\r
+               \r
+               Rectangle ws = this.getBounds();\r
+               ws.x = ra.x;\r
+               ws.y = ra.y;\r
+               this.setBounds(ws);\r
+               \r
+               // 前回選択していたレコーダーを再選択\r
+               if ( ! recip.equals("")) {\r
+                       for (int idx=0; idx<recorderList.size(); idx++) {\r
+                               if (recorderList.get(idx).getRecorderIPAddr().equals(recip)) {\r
+                                       jComboBox.setSelectedIndex(idx);\r
+                                       break;\r
+                               }\r
+                       }\r
+               }\r
+               \r
+               // ウィンドウを閉じたときの処理\r
+               this.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);\r
+               this.addWindowListener(new WindowListener() {\r
+\r
+                       public void windowActivated(WindowEvent e) {}\r
+                       public void windowClosed(WindowEvent e) {}\r
+                       public void windowDeactivated(WindowEvent e) {}\r
+                       public void windowDeiconified(WindowEvent e) {}\r
+                       public void windowIconified(WindowEvent e) {}\r
+                       public void windowOpened(WindowEvent e) {}\r
+\r
+                       @Override\r
+                       public void windowClosing(WindowEvent e) {\r
+                               final Viewer thisClass = (Viewer) e.getSource();\r
+                               Rectangle r = thisClass.getBounds();\r
+                               \r
+                               File f = new File(defFile);\r
+                               try {\r
+                                       BufferedWriter writer = new BufferedWriter(new FileWriter(f));\r
+                                       if (recinfo != null) {\r
+                                               writer.write(recinfo.getRecorderIPAddr()+"\n");\r
+                                       }\r
+                                       else {\r
+                                               writer.write("\n");\r
+                                       }\r
+                                       writer.write(r.x+"\n");\r
+                                       writer.write(r.y+"\n");\r
+                                       writer.write(r.width+"\n");\r
+                                       writer.write(r.height+"\n");\r
+                                       writer.close();\r
+                               }\r
+                               catch (Exception e1) {\r
+                                       e1.printStackTrace();\r
+                               }\r
+                       }\r
+               });\r
+       }\r
+\r
+       /**\r
+        * This method initializes jContentPane\r
+        * \r
+        * @return javax.swing.JPanel\r
+        */\r
+       \r
+       private JTabbedPane getJTabbedPane() {\r
+               if (jTabbedPane == null) {\r
+                       jTabbedPane = new JTabbedPane();\r
+                       \r
+                       jTabbedPane.addTab("操作", getJContentPane());\r
+                       jTabbedPane.addTab("設定", getJSettingPane());\r
+               }\r
+               return jTabbedPane;\r
+       }\r
+}\r
diff --git a/TinyBannavi/src/statusView/Viewer.java b/TinyBannavi/src/statusView/Viewer.java
new file mode 100644 (file)
index 0000000..0e06e1b
--- /dev/null
@@ -0,0 +1,432 @@
+\r
+package statusView;\r
+\r
+import javax.imageio.ImageIO;\r
+import javax.swing.SwingUtilities;\r
+import java.awt.BorderLayout;\r
+import java.awt.Image;\r
+import java.awt.Rectangle;\r
+import java.awt.event.ActionEvent;\r
+import java.awt.event.ActionListener;\r
+import java.awt.event.WindowEvent;\r
+import java.awt.event.WindowListener;\r
+import java.io.BufferedReader;\r
+import java.io.BufferedWriter;\r
+import java.io.File;\r
+import java.io.FileReader;\r
+import java.io.FileWriter;\r
+import java.io.IOException;\r
+import java.net.Authenticator;\r
+import java.util.ArrayList;\r
+\r
+import javax.swing.JPanel;\r
+import javax.swing.JFrame;\r
+import javax.swing.JSlider;\r
+import javax.swing.JTable;\r
+import javax.swing.Timer;\r
+import javax.swing.event.ChangeEvent;\r
+import javax.swing.event.ChangeListener;\r
+import javax.swing.table.DefaultTableColumnModel;\r
+import javax.swing.table.TableColumn;\r
+import javax.swing.JComboBox;\r
+\r
+import tainavi.RecorderInfo;\r
+import tainavi.RecorderInfoList;\r
+\r
+public class Viewer extends JFrame {\r
+\r
+       private static final long serialVersionUID = 1L;\r
+       private JPanel jContentPane = null;\r
+       private JPanel jPanel1 = null;\r
+       private JComboBox jComboBox = null;\r
+       private JPanel jPanel2 = null;\r
+       private JTable jTable = null;\r
+       private JPanel jPanel3 = null;\r
+       private JSlider jSlider = null;\r
+       private Timer jTimer = null;\r
+       \r
+       private final String defFile = "env/StatusView.def";\r
+       \r
+       private RecorderInfo recorder = null;\r
+       \r
+       /**\r
+        * 鯛ナビのレコーダ一覧の中で"RD-"を含むもののみ抽出したリスト. \r
+        */\r
+       private RecorderInfoList recorderList = new RecorderInfoList();\r
+       \r
+       private tainavi.GetRDStatus gs = new tainavi.GetRDStatus();\r
+       \r
+       \r
+       \r
+       /**\r
+        * This method initializes jComboBox    \r
+        *      \r
+        * @return javax.swing.JComboBox        \r
+        */\r
+       \r
+       private JPanel getJPanel1() {\r
+               if (jPanel1 == null) {\r
+                       jPanel1 = new JPanel();\r
+                       jPanel1.setLayout(new BorderLayout());\r
+                       jPanel1.add(jComboBox = new JComboBox(), BorderLayout.CENTER);\r
+                       \r
+                       jComboBox.addItem("");\r
+                       for (RecorderInfo re : recorderList) {\r
+                               jComboBox.addItem(re.getRecorderIPAddr());\r
+                       }\r
+                       \r
+                       jComboBox.addActionListener(new ActionListener() {\r
+                               @Override\r
+                               public void actionPerformed(ActionEvent e) {\r
+                                       if (jTimer != null) {\r
+                                               jTimer.stop();\r
+                                       }\r
+                                       \r
+                                       jTable.setValueAt("", 0, 1);\r
+                                       jTable.setValueAt("", 1, 1);\r
+                                       jTable.setValueAt("", 2, 1);\r
+                                       jTable.setValueAt("", 3, 1);\r
+                                       jTable.setValueAt("", 4, 1);\r
+                                       jTable.setValueAt("", 5, 1);\r
+                                       jTable.setValueAt("", 6, 1);\r
+                                       jTable.setValueAt("", 7, 1);\r
+                                       jTable.setValueAt("", 8, 1);\r
+                                       jTable.setValueAt("", 9, 1);\r
+                                       jTable.setValueAt("", 10, 1);\r
+                                       jTable.setValueAt("", 11, 1);\r
+                                       jTable.setValueAt("", 12, 1);\r
+                                       jTable.setValueAt("", 13, 1);\r
+                                       jTable.setValueAt("", 14, 1);\r
+                                       jTable.setValueAt("", 15, 1);\r
+                                       jTable.setValueAt("", 16, 1);\r
+                                       jTable.setValueAt("", 17, 1);\r
+                                       jTable.setValueAt("", 18, 1);\r
+                                       jTable.setValueAt("", 19, 1);\r
+                                       jTable.setValueAt("", 20, 1);\r
+                                       \r
+                                       if (jComboBox.getSelectedItem().equals("")) {\r
+                                               recorder = null;\r
+                                               return;\r
+                                       }\r
+                                       recorder = recorderList.get(jComboBox.getSelectedIndex()-1);\r
+                                       \r
+                                       jTimer = new Timer(1*1000, new ActionListener() {\r
+                                               @Override\r
+                                               public void actionPerformed(ActionEvent e) {\r
+                                                       String s = gs.getCurChannel(recorder.getRecorderIPAddr());\r
+                                                       if (s != null) {\r
+                                                               jTable.setValueAt(gs.mod, 0, 1);\r
+                                                               jTable.setValueAt(gs.enc, 1, 1);\r
+                                                               jTable.setValueAt(gs.ch, 2, 1);\r
+                                                               jTable.setValueAt(gs.typ, 3, 1);\r
+                                                               jTable.setValueAt(gs.unk, 4, 1);\r
+                                                               jTable.setValueAt(gs.dvd, 5, 1);\r
+                                                               jTable.setValueAt(gs.opn, 6, 1);\r
+                                                               jTable.setValueAt(gs.title_no, 7, 1);\r
+                                                               jTable.setValueAt(gs.title, 8, 1);\r
+                                                               jTable.setValueAt(gs.title_len_s, 9, 1);\r
+                                                               jTable.setValueAt(gs.chapter, 10, 1);\r
+                                                               jTable.setValueAt(gs.chapter_name, 11, 1);\r
+                                                               jTable.setValueAt(gs.time_all_s, 12, 1);\r
+                                                               jTable.setValueAt(gs.time_chap_s, 13, 1);\r
+                                                               jTable.setValueAt(gs.title_chno, 14, 1);\r
+                                                               jTable.setValueAt(gs.title_chname, 15, 1);\r
+                                                               jTable.setValueAt(gs.title_date + " " + gs.title_time, 16, 1);\r
+                                                               jTable.setValueAt(gs.title_gnr, 17, 1);\r
+                                                               jTable.setValueAt(gs.title_chcode, 18, 1);\r
+                                                               jTable.setValueAt(gs.okk, 19, 1);\r
+                                                               jTable.setValueAt(gs.ply, 20, 1);\r
+                                                               \r
+                                                               if (jSlider.getMaximum() != gs.title_len) {\r
+                                                                       jSlider.setMaximum(gs.title_len); \r
+                                                               }\r
+                                                               jSlider.setValue((gs.time_all>=0)?(gs.time_all):(gs.title_len+gs.time_all)); \r
+                                                               jSlider.updateUI();\r
+                                                       }\r
+                                                       else {\r
+                                                               jTable.setValueAt("切断", 20, 1);\r
+                                                       }\r
+                                               }\r
+                                       });\r
+                                       \r
+                                       jTimer.start();\r
+                               }\r
+                       });\r
+               }\r
+               return jPanel1;\r
+       }\r
+\r
+       private JPanel getJPanel2() {\r
+               if (jPanel2 == null) {\r
+                       jPanel2 = new JPanel();\r
+                       \r
+                       int row = 21;\r
+                       int col = 2;\r
+                       \r
+                       jPanel2.setLayout(new BorderLayout());\r
+                       jPanel2.add(jTable = new JTable(row,col), BorderLayout.CENTER);\r
+\r
+                       DefaultTableColumnModel columnModel = (DefaultTableColumnModel)jTable.getColumnModel();\r
+                       TableColumn column = null;\r
+                       column = columnModel.getColumn(0);\r
+                       column.setPreferredWidth(100);\r
+                       column.setMinWidth(100);\r
+                       column.setMaxWidth(100);\r
+                       column = columnModel.getColumn(1);\r
+                       column.setPreferredWidth(200);\r
+\r
+                       jTable.setValueAt("表示メニュー", 0, 0);\r
+                       jTable.setValueAt("W録", 1, 0);\r
+                       jTable.setValueAt("チャンネル", 2, 0);\r
+                       jTable.setValueAt("HDD/DVD", 3, 0);\r
+                       jTable.setValueAt("HDD", 4, 0);\r
+                       jTable.setValueAt("DVD", 5, 0);\r
+                       jTable.setValueAt("トレイ", 6, 0);\r
+                       jTable.setValueAt("タイトル番号", 7, 0);\r
+                       jTable.setValueAt("タイトル名", 8, 0);\r
+                       jTable.setValueAt("長さ", 9, 0);\r
+                       jTable.setValueAt("チャプタ番号", 10, 0);\r
+                       jTable.setValueAt("チャプタ名", 11, 0);\r
+                       jTable.setValueAt("現在位置", 12, 0);\r
+                       jTable.setValueAt("チャプタ内位置", 13, 0);\r
+                       jTable.setValueAt("CH", 14, 0);\r
+                       jTable.setValueAt("チャンネル名", 15, 0);\r
+                       jTable.setValueAt("録画日時", 16, 0);\r
+                       jTable.setValueAt("ジャンル", 17, 0);\r
+                       jTable.setValueAt("放送局コード", 18, 0);\r
+                       jTable.setValueAt("追っかけ", 19, 0);\r
+                       jTable.setValueAt("動作", 20, 0);\r
+               }\r
+               return jPanel2;\r
+       }\r
+       \r
+       private JPanel getJPanel3() {\r
+               if (jPanel3 == null) {\r
+                       jPanel3 = new JPanel();\r
+                       \r
+                       jPanel3.setLayout(new BorderLayout());\r
+                       jPanel3.add(jSlider = new JSlider(0,0,0), BorderLayout.CENTER);\r
+                       \r
+                       jSlider.addChangeListener(new ChangeListener() {\r
+                               @Override\r
+                               public void stateChanged(ChangeEvent e) {\r
+                                       if (jSlider.getValueIsAdjusting()) {\r
+                                               jTimer.stop();\r
+                                               jTable.setValueAt(gs.getHMS(jSlider.getValue()), 12, 1);\r
+                                       }\r
+                                       else {\r
+                                               if ( ! jTimer.isRunning() && ! gs.ply.equals("") && ! gs.ply.equals("録画") && gs.mod.equals("なし")) {\r
+                                                       String HMS = gs.getHMS(jSlider.getValue());\r
+                                                       \r
+                                                       tainavi.HDDRecorderUtils ru = new tainavi.HDDRecorderUtils();\r
+                                                       Authenticator.setDefault(ru.new MyAuthenticator(recorder.getRecorderUser(),recorder.getRecorderPasswd()));\r
+                                                       \r
+                                                       ru.reqGET("http://"+recorder.getRecorderIPAddr()+":"+recorder.getRecorderPortNo()+"/remote/ts.htm?"+jSlider.getValue(), null);\r
+                                                       /*\r
+                                                       ru.reqGET("http://"+recorder.getRecorderIPAddr()+":"+recorder.getRecorderPortNo()+"/remote/remote.htm?key=0E", null);\r
+                                                       tainavi.CommonUtils.milSleep(500);\r
+                                                       ru.reqGET("http://"+recorder.getRecorderIPAddr()+":"+recorder.getRecorderPortNo()+"/remote/remote.htm?key=0E", null);\r
+                                                       tainavi.CommonUtils.milSleep(100);\r
+                                                       ru.reqGET("http://"+recorder.getRecorderIPAddr()+":"+recorder.getRecorderPortNo()+"/remote/remote.htm?key=0"+HMS.substring(0,1), null);\r
+                                                       tainavi.CommonUtils.milSleep(100);\r
+                                                       ru.reqGET("http://"+recorder.getRecorderIPAddr()+":"+recorder.getRecorderPortNo()+"/remote/remote.htm?key=0"+HMS.substring(1,2), null);\r
+                                                       tainavi.CommonUtils.milSleep(100);\r
+                                                       ru.reqGET("http://"+recorder.getRecorderIPAddr()+":"+recorder.getRecorderPortNo()+"/remote/remote.htm?key=C4", null);\r
+                                                       tainavi.CommonUtils.milSleep(100);\r
+                                                       ru.reqGET("http://"+recorder.getRecorderIPAddr()+":"+recorder.getRecorderPortNo()+"/remote/remote.htm?key=0"+HMS.substring(3,4), null);\r
+                                                       tainavi.CommonUtils.milSleep(100);\r
+                                                       ru.reqGET("http://"+recorder.getRecorderIPAddr()+":"+recorder.getRecorderPortNo()+"/remote/remote.htm?key=0"+HMS.substring(4,5), null);\r
+                                                       tainavi.CommonUtils.milSleep(100);\r
+                                                       ru.reqGET("http://"+recorder.getRecorderIPAddr()+":"+recorder.getRecorderPortNo()+"/remote/remote.htm?key=C4", null);\r
+                                                       tainavi.CommonUtils.milSleep(100);\r
+                                                       ru.reqGET("http://"+recorder.getRecorderIPAddr()+":"+recorder.getRecorderPortNo()+"/remote/remote.htm?key=0"+HMS.substring(6,7), null);\r
+                                                       tainavi.CommonUtils.milSleep(100);\r
+                                                       ru.reqGET("http://"+recorder.getRecorderIPAddr()+":"+recorder.getRecorderPortNo()+"/remote/remote.htm?key=0"+HMS.substring(7,8), null);\r
+                                                       tainavi.CommonUtils.milSleep(100);\r
+                                                       ru.reqGET("http://"+recorder.getRecorderIPAddr()+":"+recorder.getRecorderPortNo()+"/remote/remote.htm?key=44", null);\r
+                                                       */\r
+                                               }\r
+                                               jTimer.start();\r
+                                       }\r
+                               }\r
+                       });\r
+               }\r
+               return jPanel3;\r
+       }\r
+       \r
+       /**\r
+        * @param args\r
+        */\r
+       public static void main(String[] args) {\r
+               SwingUtilities.invokeLater(new Runnable() {\r
+                       public void run() {\r
+                               Viewer thisClass = new Viewer();\r
+                               thisClass.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);\r
+                               thisClass.setVisible(true);\r
+                       }\r
+               });\r
+       }\r
+\r
+       /**\r
+        * This is the default constructor\r
+        */\r
+       public Viewer() {\r
+               super();\r
+               initialize();\r
+       }\r
+\r
+       /**\r
+        * This method initializes this\r
+        * \r
+        * @return void\r
+        */\r
+       private void initialize() {\r
+               //this.setSize(300, 200);\r
+               \r
+               try {\r
+                       final Image image = ImageIO.read(new File("icon/remostat.png"));\r
+                       this.setIconImage(image);\r
+               } catch (IOException e) {\r
+                       e.printStackTrace();\r
+               }\r
+\r
+               // 設定ファイルを読み込む\r
+               String recip = "";\r
+               Rectangle ra = new Rectangle();\r
+               File f = new File(defFile);\r
+               if ( f.exists() ) {\r
+                       try {\r
+                               String str = null;\r
+                               int i = 0;\r
+                               BufferedReader reader = new BufferedReader(new FileReader(f));\r
+                               while ((str = reader.readLine()) != null) {\r
+                                       switch (i++) {\r
+                                       case 0:\r
+                                               recip = str;\r
+                                               break;\r
+                                       case 1:\r
+                                               ra.x = Integer.valueOf(str);\r
+                                               break;\r
+                                       case 2:\r
+                                               ra.y = Integer.valueOf(str);\r
+                                               break;\r
+                                       case 3:\r
+                                               ra.width = Integer.valueOf(str);\r
+                                               break;\r
+                                       }\r
+                               }\r
+                       }\r
+                       catch (Exception e) {\r
+                               e.printStackTrace();\r
+                       }\r
+               }\r
+               \r
+               // 鯛ナビと タイニーシンクの設定から\r
+               //tainavi.Env env = tainavi.Env.load();\r
+               RecorderInfoList recInfos = new RecorderInfoList();\r
+               recInfos.load();\r
+               \r
+               ArrayList<taiSync.RecorderInfo> syncrl = taiSync.RecorderInfo.load();\r
+               \r
+               // 必要なレコーダのみ抽出する\r
+               for (RecorderInfo r : recInfos) {\r
+               if (r.getRecorderIPAddr().equals("") == true || r.getRecorderId().indexOf("RD-") == -1) {\r
+                       continue;\r
+               }\r
+               \r
+               RecorderInfo rn = new RecorderInfo();\r
+               if (r.getRecorderIPAddr().equals("127.0.0.1") || r.getRecorderIPAddr().equals("localhost")) {\r
+                       for (taiSync.RecorderInfo sr : syncrl) {\r
+                               if (sr.getLocalPort() == Integer.valueOf(r.getRecorderPortNo())) {\r
+                               rn.setRecorderIPAddr(sr.getRecorderIPAddr());\r
+                               rn.setRecorderPortNo(sr.getRecorderPortNo());\r
+                                       rn.setRecorderUser(sr.getRecorderUser());\r
+                                       rn.setRecorderPasswd(sr.getRecorderPasswd());\r
+                               }\r
+                       }\r
+               }\r
+               if (rn.getRecorderIPAddr().equals("")) {\r
+                       rn.setRecorderIPAddr(r.getRecorderIPAddr());\r
+                       rn.setRecorderPortNo(r.getRecorderPortNo());\r
+                       rn.setRecorderUser(r.getRecorderUser());\r
+                       rn.setRecorderPasswd(r.getRecorderPasswd());\r
+               }\r
+               rn.setRecorderMacAddr(r.getRecorderMacAddr());\r
+               rn.setRecorderBroadcast(r.getRecorderBroadcast());\r
+               rn.setRecorderId(r.getRecorderId());\r
+               \r
+               recorderList.add(rn);\r
+               }\r
+               \r
+               //this.setResizable(false);\r
+               this.setContentPane(getJContentPane());\r
+               this.setTitle("RDリモートステータスビューア");\r
+               this.pack();\r
+               \r
+               Rectangle ws = this.getBounds();\r
+               ws.x = ra.x;\r
+               ws.y = ra.y;\r
+               if (ra.width > ws.width) {\r
+                       ws.width = ra.width;\r
+               }\r
+               this.setBounds(ws);\r
+               \r
+               // 前回選択していたレコーダーを再選択\r
+               if ( ! recip.equals("")) {\r
+                       jComboBox.setSelectedItem(recip);\r
+               }\r
+               \r
+               // ウィンドウを閉じたときの処理\r
+               this.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);\r
+               this.addWindowListener(new WindowListener() {\r
+\r
+                       public void windowActivated(WindowEvent e) {}\r
+                       public void windowClosed(WindowEvent e) {}\r
+                       public void windowDeactivated(WindowEvent e) {}\r
+                       public void windowDeiconified(WindowEvent e) {}\r
+                       public void windowIconified(WindowEvent e) {}\r
+                       public void windowOpened(WindowEvent e) {}\r
+\r
+                       @Override\r
+                       public void windowClosing(WindowEvent e) {\r
+                               final Viewer thisClass = (Viewer) e.getSource();\r
+                               Rectangle r = thisClass.getBounds();\r
+                               \r
+                               File f = new File(defFile);\r
+                               try {\r
+                                       BufferedWriter writer = new BufferedWriter(new FileWriter(f));\r
+                                       if (recorder != null) {\r
+                                               writer.write(recorder.getRecorderIPAddr()+"\n");\r
+                                       }\r
+                                       else {\r
+                                               writer.write("\n");\r
+                                       }\r
+                                       writer.write(r.x+"\n");\r
+                                       writer.write(r.y+"\n");\r
+                                       writer.write(r.width+"\n");\r
+                                       writer.write(r.height+"\n");\r
+                                       writer.close();\r
+                               }\r
+                               catch (Exception e1) {\r
+                                       e1.printStackTrace();\r
+                               }\r
+                       }\r
+               });\r
+       }\r
+\r
+       /**\r
+        * This method initializes jContentPane\r
+        * \r
+        * @return javax.swing.JPanel\r
+        */\r
+       private JPanel getJContentPane() {\r
+               if (jContentPane == null) {\r
+                       jContentPane = new JPanel();\r
+                       jContentPane.setLayout(new BorderLayout());\r
+                       jContentPane.add(getJPanel1(), BorderLayout.NORTH);\r
+                       jContentPane.add(getJPanel2(), BorderLayout.CENTER);\r
+                       jContentPane.add(getJPanel3(), BorderLayout.SOUTH);\r
+               }\r
+               return jContentPane;\r
+       }\r
+}\r
diff --git a/TinyBannavi/src/taiSync/Env.java b/TinyBannavi/src/taiSync/Env.java
new file mode 100644 (file)
index 0000000..c8fd565
--- /dev/null
@@ -0,0 +1,76 @@
+package taiSync;\r
+\r
+import java.beans.XMLDecoder;\r
+import java.beans.XMLEncoder;\r
+import java.io.BufferedInputStream;\r
+import java.io.BufferedOutputStream;\r
+import java.io.FileInputStream;\r
+import java.io.FileNotFoundException;\r
+import java.io.FileOutputStream;\r
+\r
+public class Env {\r
+\r
+       // 定数\r
+       private static final String envFile = "env/taiSyncEnv.xml";\r
+       \r
+       \r
+       // メンバ変数\r
+       private boolean debug;\r
+       private boolean appendDT;\r
+       private int period;\r
+       private int keeping;\r
+       private int keepingCheckInterval;\r
+       private int popPort;\r
+       private int smtpPort;\r
+       public void setDebug(boolean b) { debug = b; }\r
+       public boolean getDebug() { return debug; }\r
+       public void setAppendDT(boolean b) { appendDT = b; }\r
+       public boolean getAppendDT() { return appendDT; }\r
+       public void setPeriod(int n) { period = n; }\r
+       public int getPeriod() { return period; }\r
+       public void setKeeping(int n) { keeping = n; }\r
+       public int getKeeping() { return keeping; }\r
+       public void setKeepingCheckInterval(int n) { keepingCheckInterval = n; }\r
+       public int getKeepingCheckInterval() { return keepingCheckInterval; }\r
+       public void setPopPort(int n) { popPort = n; }\r
+       public int getPopPort() { return popPort; }\r
+       public void setSmtpPort(int n) { smtpPort = n; }\r
+       public int getSmtpPort() { return smtpPort; }\r
+       \r
+       \r
+       // 操作系メソッド\r
+       public void save() {\r
+               try {\r
+                       XMLEncoder enc = new XMLEncoder(new BufferedOutputStream(new FileOutputStream(envFile)));\r
+            enc.writeObject(this);\r
+            enc.close();\r
+               } catch (FileNotFoundException e) {\r
+                       e.printStackTrace();\r
+               }\r
+       }\r
+\r
+       public static Env load() {\r
+               Env b = null;\r
+        try {\r
+            XMLDecoder dec = new XMLDecoder(new BufferedInputStream(new FileInputStream(envFile)));\r
+            b = (Env)dec.readObject();\r
+            dec.close();\r
+        } catch(FileNotFoundException e) {\r
+               System.out.println("Exception: load()"+e);\r
+               b = new Env();\r
+        }\r
+       return(b);\r
+       }\r
+       \r
+       \r
+       // コンストラクタ\r
+       public Env() {\r
+               this.setDebug(true);\r
+               this.setAppendDT(true);\r
+               this.setPeriod(7);\r
+               this.setKeeping(5);\r
+               this.setKeepingCheckInterval(3);\r
+               this.setPopPort(110);\r
+               this.setSmtpPort(25);\r
+       }\r
+}\r
diff --git a/TinyBannavi/src/taiSync/HTTPServer.java b/TinyBannavi/src/taiSync/HTTPServer.java
new file mode 100644 (file)
index 0000000..30ca301
--- /dev/null
@@ -0,0 +1,232 @@
+package taiSync;\r
+\r
+import java.awt.Toolkit;\r
+import java.io.BufferedReader;\r
+import java.io.BufferedWriter;\r
+import java.io.File;\r
+import java.io.FileInputStream;\r
+import java.io.IOException;\r
+import java.io.InputStream;\r
+import java.io.InputStreamReader;\r
+import java.io.OutputStream;\r
+import java.io.OutputStreamWriter;\r
+import java.net.ServerSocket;\r
+import java.net.Socket;\r
+import java.util.ArrayList;\r
+import java.util.Date;\r
+import java.util.regex.Matcher;\r
+import java.util.regex.Pattern;\r
+\r
+import tainavi.CommonUtils;\r
+import tainavi.ReserveList;\r
+import tainavi.RecorderList;\r
+\r
+\r
+\r
+public class HTTPServer extends Thread {\r
+       \r
+       private ServerSocket svsock = null;\r
+       private RecorderList rec = null;\r
+       private ArrayList<ReserveInfo> rsvInfo = null;\r
+       private ReserveCtrl rCtrl = null;\r
+       private tainavi.GetRDStatus cCtrl = null;\r
+       \r
+       \r
+       // コンストラクタ\r
+       public HTTPServer(ServerSocket sock, RecorderList rec, ArrayList<ReserveInfo> rsvInfo) {\r
+               this.svsock = sock;\r
+               this.rCtrl = new ReserveCtrl();\r
+               this.cCtrl = new tainavi.GetRDStatus();\r
+               \r
+               this.rec = rec;                 // 共有オブジェクト\r
+               this.rsvInfo = rsvInfo; // 共有オブジェクト\r
+               \r
+               this.start();\r
+       }\r
+       \r
+       \r
+       //\r
+       @Override\r
+       public void run() {\r
+               Socket sock = null;\r
+               \r
+               while (true) {\r
+                       try {\r
+                               sock = this.svsock.accept();\r
+                       } catch (IOException e) {\r
+                               e.printStackTrace();\r
+                       }\r
+                       \r
+                       synchronized(rsvInfo) {\r
+                               try {\r
+                                       recvRequest(sock);\r
+                               } catch (IOException e) {\r
+                                       e.printStackTrace();\r
+                               }\r
+                               \r
+                               try {\r
+                                       sock.close();\r
+                               } catch (IOException e) {\r
+                                       e.printStackTrace();\r
+                               }\r
+                       }\r
+               }\r
+       }\r
+       \r
+       \r
+       //\r
+       private void recvRequest(Socket sock) throws IOException {\r
+               \r
+               Date dt = new Date();\r
+               System.out.println("鯛ナビからの接続がありました("+dt.toString()+")");\r
+               \r
+               // ブラウザからリクエストを受け取る\r
+               ArrayList<String> reqHeaders = new ArrayList<String>();\r
+               String str = null;\r
+               \r
+               OutputStream w = sock.getOutputStream(); \r
+               InputStream r = sock.getInputStream();\r
+               BufferedWriter out = new BufferedWriter(new OutputStreamWriter(w));\r
+               BufferedReader in = new BufferedReader(new InputStreamReader(r));\r
+               \r
+               // リクエスト処理\r
+               int clen = -1;\r
+               String method = null;\r
+               String location = null;\r
+               String protocol = null;\r
+               String poststr = null;\r
+               /*\r
+               char[] buff = new char[65536];\r
+               int len = in.read(buff,0,buff.length);\r
+               String buf = new String(buff,0,len);\r
+               Matcher mh = Pattern.compile("^(POST|GET) (.+?) (HTTP/.+?)").matcher(buf);\r
+               if (mh.find()) {\r
+                       method = mh.group(1);\r
+                       location = mh.group(2);\r
+                       protocol = mh.group(3);\r
+                       \r
+                       if (method.equals("POST")) {\r
+                               clen = in.read(buff,0,buff.length);\r
+                               poststr = new String(buff,0,clen);\r
+                       }\r
+               }\r
+               else {\r
+                       System.out.println("不正なリクエストです。");\r
+               }\r
+               */\r
+               \r
+               boolean findHeader = true;\r
+               while ((str = in.readLine()) != null) {\r
+                       //\r
+                       System.err.println("# Header: "+str);\r
+                       \r
+                       if (findHeader) {\r
+                               Matcher ma = Pattern.compile("^(POST|GET) (.+?) (HTTP/.+?)$").matcher(str);\r
+                               if (ma.find()) {\r
+                                       method = ma.group(1);\r
+                                       location = ma.group(2);\r
+                                       protocol = ma.group(3);\r
+                               }\r
+                               //if (method.equals("GET")) {\r
+                               //      break;\r
+                               //}\r
+                               findHeader = false;\r
+                               continue;\r
+                       }\r
+                       \r
+                       // ヘッダの終了\r
+                       Matcher ma = Pattern.compile("^\\s*$").matcher(str);\r
+                       if (ma.find()) {\r
+                               break;\r
+                       }\r
+                       \r
+                       // Content-Lengthの取得\r
+                       if (method != null && method.equals("POST")) {\r
+                               ma = Pattern.compile("Content-Length: (\\d+)").matcher(str);\r
+                               if (ma.find()) {\r
+                                       clen = Integer.valueOf(ma.group(1));\r
+                               }\r
+                       }\r
+                       \r
+                       reqHeaders.add(str);\r
+               }\r
+               if (clen > 0) {\r
+                       poststr = in.readLine();\r
+                       System.err.println("# Body: "+poststr);\r
+               }\r
+               if (location == null) {\r
+                       System.out.println("エラー:鯛ナビが空のリクエストを送ってきました");\r
+                       in.close();\r
+                       out.close();\r
+                       sock.close();\r
+                       return;\r
+               }\r
+               \r
+               // レスポンス処理\r
+               {\r
+                       // データ部の生成\r
+                       StringBuilder bsb = new StringBuilder();\r
+                       \r
+                       if (location.indexOf("/reserve.htm") >= 0) {\r
+                               bsb.append(rCtrl.getRsvStr(rec, rsvInfo));\r
+                               ReserveInfo.save(rsvInfo);\r
+                               ReserveInfo.showReserveInfo(rsvInfo,rec.getRecorderIPAddr());\r
+                       }\r
+                       else if (location.indexOf("/entry.htm") >= 0) {\r
+                               String addId = rCtrl.addReservedInfo(rec, poststr, rsvInfo);\r
+                               bsb.append(addId);\r
+                               ReserveInfo.save(rsvInfo);\r
+                               ReserveInfo.showReserveInfo(rsvInfo,rec.getRecorderIPAddr());\r
+                       }\r
+                       else if (location.indexOf("/update.htm") >= 0) {\r
+                               String updId = rCtrl.updReservedInfo(rec, poststr, rsvInfo);\r
+                               bsb.append(updId);\r
+                               ReserveInfo.save(rsvInfo);\r
+                               ReserveInfo.showReserveInfo(rsvInfo,rec.getRecorderIPAddr());\r
+                       }\r
+                       else if (location.indexOf("/delete.htm") >= 0) {\r
+                               Matcher ma = Pattern.compile("/delete\\.htm\\?id=(\\d+)").matcher(location);\r
+                               if (ma.find()) {\r
+                                       rCtrl.delReservedInfo(rec, ma.group(1), rsvInfo);\r
+                                       bsb.append(ma.group(1));\r
+                                       ReserveInfo.save(rsvInfo);\r
+                                       ReserveInfo.showReserveInfo(rsvInfo,rec.getRecorderIPAddr());\r
+                               }\r
+                       }\r
+                       else if (location.indexOf("/remote.htm") >= 0) {\r
+                               rCtrl.remoteCtrl(rec, location);\r
+                       }\r
+                       else if (location.indexOf("/getcurchannel.htm") >= 0) {\r
+                               bsb.append(cCtrl.getCurChannel(rec.getRecorderIPAddr()));\r
+                       }\r
+                       \r
+                       byte[] body = bsb.toString().getBytes("MS932");\r
+                       \r
+                       // ヘッダ部の生成\r
+                       StringBuilder hsb = new StringBuilder();\r
+                       \r
+                       hsb.append("HTTP/1.0 200 OK\r\n");\r
+                       hsb.append("Connection: close\r\n");\r
+                       hsb.append("Content-Type: text/html\r\n");\r
+                       hsb.append("Pragma: no-cache\r\n");\r
+                       hsb.append("Cache-Control: no-cache\r\n");\r
+                       hsb.append("Content-Length: "+body.length+"\r\n");\r
+                       hsb.append("\r\n");\r
+                       \r
+                       String header = hsb.toString();\r
+                       \r
+                       // ヘッダ・データの送出\r
+                       out.write(header);\r
+                       out.flush();\r
+                       w.write(body, 0, body.length);\r
+                       w.flush();\r
+               }\r
+               \r
+               out.close();\r
+               in.close();\r
+               sock.close();\r
+               \r
+               System.out.println("鯛ナビからの接続を切断しました");\r
+       }\r
+\r
+}\r
diff --git a/TinyBannavi/src/taiSync/POPServer.java b/TinyBannavi/src/taiSync/POPServer.java
new file mode 100644 (file)
index 0000000..a62143f
--- /dev/null
@@ -0,0 +1,234 @@
+package taiSync;\r
+\r
+import java.io.BufferedReader;\r
+import java.io.BufferedWriter;\r
+import java.io.File;\r
+import java.io.FileInputStream;\r
+import java.io.IOException;\r
+import java.io.InputStream;\r
+import java.io.InputStreamReader;\r
+import java.io.OutputStream;\r
+import java.io.OutputStreamWriter;\r
+import java.net.ServerSocket;\r
+import java.net.Socket;\r
+import java.util.ArrayList;\r
+import java.util.Date;\r
+import java.util.regex.Matcher;\r
+import java.util.regex.Pattern;\r
+\r
+import tainavi.CommonUtils;\r
+import tainavi.RecorderList;\r
+import tainavi.TextValueSet;\r
+\r
+public class POPServer extends Thread {\r
+       \r
+       Env env = null;\r
+       private ServerSocket svsock = null;\r
+       private ArrayList<RecorderInfo> recInfo = null;\r
+       private ArrayList<ReserveInfo> rsvInfo = null;\r
+       private ReserveCtrl rCtrl = null;\r
+       private boolean fatal = false;\r
+       private boolean fault = false;\r
+       \r
+       \r
+       // コンストラクタ\r
+       public POPServer(ServerSocket sock, ArrayList<RecorderInfo> recInfo, ArrayList<ReserveInfo> rsvInfo, Env env) {\r
+               this.env = env;\r
+               this.svsock = sock;\r
+               this.rCtrl = new ReserveCtrl();\r
+               this.fatal = false;\r
+               this.fault = false;\r
+               \r
+               this.recInfo = recInfo;\r
+               this.rsvInfo = rsvInfo;\r
+               \r
+               this.start();\r
+       }\r
+       \r
+       //\r
+       public boolean isFatal() { return fatal; }\r
+       public boolean isFault() { return fault; }\r
+       \r
+       \r
+       // スレッド\r
+       @Override\r
+       public void run() {\r
+               Socket sock = null;\r
+               \r
+               while (true) {\r
+                       try {\r
+                               sock = this.svsock.accept();\r
+                       } catch (IOException e) {\r
+                               e.printStackTrace();\r
+                       }\r
+                       \r
+                       synchronized(rsvInfo) {\r
+                               try {\r
+                                       recvRequest(sock);\r
+                               } catch (IOException e) {\r
+                                       e.printStackTrace();\r
+                               }\r
+                               \r
+                               try {\r
+                                       sock.close();\r
+                               } catch (IOException e) {\r
+                                       e.printStackTrace();\r
+                               }\r
+                       }\r
+               }\r
+       }\r
+       \r
+       private ArrayList<String> reqQueue = new ArrayList<String>();\r
+       private RecorderInfo curRec = null;\r
+       \r
+       //\r
+       private void recvRequest(Socket sock) throws IOException {\r
+               \r
+               String ipaddr = sock.getInetAddress().getHostAddress().toString();\r
+               \r
+               Date dt = new Date();\r
+               System.out.println("RD["+ipaddr+"]からのPOP接続がありました("+dt.toString()+")");\r
+               \r
+               boolean drop = false;\r
+               \r
+               // 溜まっているリクエストの整理\r
+               if (curRec == null) {\r
+                       for (RecorderInfo rec : recInfo) {\r
+                               if (rec.getRecorderIPAddr().equals(ipaddr)) {\r
+                                       curRec = rec;\r
+                                       // リクエストキューを作成する\r
+                                       drop = rCtrl.getReqQueue(curRec, rsvInfo, reqQueue, env.getAppendDT(), env.getPeriod());\r
+                                       if (drop) {\r
+                                               fault = true;\r
+                                       }\r
+                                       break;\r
+                               }\r
+                       }\r
+                       if (curRec == null) {\r
+                               System.out.println(ipaddr+"は不正なIPアドレスです");\r
+                               return;\r
+                       }\r
+                       if (reqQueue.size() == 0) {\r
+                               curRec = null;\r
+                       }\r
+               }\r
+               else {\r
+                       if ( ! ipaddr.equals(curRec.getRecorderIPAddr())) {\r
+                               System.out.println(curRec.getRecorderIPAddr()+"の処理中のため"+ipaddr+"からの接続をキャンセルしました");\r
+                               return;\r
+                       }\r
+               }\r
+               \r
+               OutputStream w = sock.getOutputStream(); \r
+               InputStream r = sock.getInputStream();\r
+               BufferedWriter out = new BufferedWriter(new OutputStreamWriter(w,"MS932"));\r
+               BufferedReader in = new BufferedReader(new InputStreamReader(r));\r
+               \r
+               // RDからリクエストを受け取る\r
+               String cmd = null;\r
+               String res = null;\r
+\r
+               // 接続OK\r
+               String myAddr = sock.getLocalAddress().getHostAddress().toString();\r
+               res = "+OK TaiSync at "+myAddr+" starting.\r\n";\r
+               out.write(res);\r
+               out.flush();\r
+               System.err.print(res);\r
+\r
+               String reqStr = "";\r
+               boolean delivered = false;\r
+               int topidx = -1;\r
+               while ((cmd = in.readLine()) != null) {\r
+                       // コマンド\r
+                       System.err.println(cmd);\r
+                       \r
+                       if (delivered == true && cmd.indexOf("DELE ") != 0) {\r
+                               System.out.println("【警告】リクエストが受け付けられませんでした");\r
+                               System.out.println("====");\r
+                               System.out.print(reqStr);\r
+                               System.out.println("====");\r
+                               \r
+                               fault = true;   // 異常発生\r
+                               delivered = false;      // 回答受信済み\r
+                       }\r
+                       \r
+                       // リターン\r
+                       if (cmd.equals("STAT")) {\r
+                               res = "+OK "+reqQueue.size()+" 0\r\n";\r
+                       }\r
+                       else if (cmd.indexOf("TOP ") == 0) {\r
+                               topidx = -1;\r
+                               Matcher ma = Pattern.compile("^TOP (\\d+) ").matcher(cmd);\r
+                               if (ma.find()) {\r
+                                       topidx = Integer.valueOf(ma.group(1));\r
+                               }\r
+                               \r
+                               reqStr = reqQueue.get(reqQueue.size()-topidx);\r
+                               \r
+                               StringBuilder sb = new StringBuilder();\r
+                               sb.append("+OK\r\n");\r
+                               sb.append("From: tainavi\r\n");\r
+                               sb.append("To: rdstyle\r\n");\r
+                               sb.append("Subject: reserve program\r\n");\r
+                               sb.append("Content-Type: text/plain; charset=Shift_JIS\r\n");\r
+                               sb.append("\r\n");\r
+                               sb.append(reqStr);\r
+                               sb.append(".\r\n");\r
+                               \r
+                               res = sb.toString();\r
+                               \r
+                               delivered = true;       // リクエスト送信済み、回答(DELE)待ち\r
+                       }\r
+                       else if (cmd.indexOf("DELE ") == 0) {\r
+                               int delidx = -1;\r
+                               Matcher ma = Pattern.compile("^DELE (\\d+)").matcher(cmd);\r
+                               if (ma.find()) {\r
+                                       delidx = Integer.valueOf(ma.group(1));\r
+                               }\r
+                               \r
+                               reqQueue.remove(reqQueue.size()-delidx);\r
+                               \r
+                               System.out.println("リクエストが受け付けられました");\r
+                               System.out.println("====");\r
+                               System.out.print(reqStr);\r
+                               System.out.println("====");\r
+                               \r
+                               res = "+OK Marked to be deleted.\r\n";\r
+                               \r
+                               delivered = false;      // 回答受信済み\r
+                       }\r
+                       else if (cmd.equals("QUIT")) {\r
+                               res = "+OK Logging out.\r\n";\r
+                       }\r
+                       else {\r
+                               res = "+OK\r\n";\r
+                       }\r
+                       \r
+                       out.write(res);\r
+                       out.flush();\r
+                       System.err.print(res);\r
+                       \r
+                       // 終了\r
+                       if (cmd.equals("QUIT")) {\r
+                               break;\r
+                       }\r
+               }\r
+               \r
+               if ( topidx == 1 ) {\r
+                       //System.out.println("キューが全部チェックされたのでリセットします");\r
+                       \r
+                       if ( reqQueue.size() == 0 && ! drop ) {\r
+                               fault = false;  // 復帰\r
+                       }\r
+                       \r
+                       curRec = null;\r
+                       reqQueue.clear();\r
+               }\r
+               \r
+               out.close();\r
+               in.close();\r
+           \r
+               System.out.println("RDからのPOP接続を切断しました");\r
+       }\r
+       \r
+}\r
diff --git a/TinyBannavi/src/taiSync/RecorderInfo.java b/TinyBannavi/src/taiSync/RecorderInfo.java
new file mode 100644 (file)
index 0000000..8474bd8
--- /dev/null
@@ -0,0 +1,56 @@
+package taiSync;\r
+\r
+import java.beans.XMLDecoder;\r
+import java.beans.XMLEncoder;\r
+import java.io.BufferedInputStream;\r
+import java.io.BufferedOutputStream;\r
+import java.io.File;\r
+import java.io.FileInputStream;\r
+import java.io.FileNotFoundException;\r
+import java.io.FileOutputStream;\r
+import java.util.ArrayList;\r
+\r
+import tainavi.EncryptPassword;\r
+import tainavi.RecorderList;\r
+import tainavi.b64;\r
+\r
+public class RecorderInfo extends RecorderList {\r
+       \r
+       private int localPort = 80;\r
+       public void setLocalPort(int p) { localPort = p; }\r
+       public int getLocalPort() { return localPort; }\r
+       \r
+       private static String saveFile = "env/taiSyncRecInfo.xml";\r
+       \r
+       public static ArrayList<RecorderInfo> load() {\r
+               File f = new File(saveFile);\r
+               if (f.exists()) {\r
+                       try {\r
+                               XMLDecoder dec = new XMLDecoder(new BufferedInputStream(new FileInputStream(saveFile)));\r
+                   ArrayList<RecorderInfo> a = (ArrayList<RecorderInfo>)dec.readObject();\r
+                   dec.close();\r
+                   for (RecorderInfo rec : a) {\r
+                       rec.setRecorderPasswd(EncryptPassword.dec(b64.dec(rec.getRecorderPasswd())));\r
+                   }\r
+                   return(a);\r
+               } catch(Exception e) {\r
+                       System.out.println("Exception: load recorder="+saveFile+"("+e.toString()+")");\r
+               }\r
+               }\r
+        return(new ArrayList<RecorderInfo>());\r
+       }\r
+       \r
+       public static void save(ArrayList<RecorderInfo> a) {\r
+        try {\r
+                       for (RecorderInfo rec : a) {\r
+                               rec.setRecorderPasswd(b64.enc(EncryptPassword.enc(rec.getRecorderPasswd())));\r
+                       }\r
+            XMLEncoder enc = new XMLEncoder(new BufferedOutputStream(new FileOutputStream(saveFile)));\r
+            enc.writeObject(a);\r
+            enc.close();\r
+        } catch(FileNotFoundException e) {\r
+               System.out.println("Exception: save recorder="+saveFile);\r
+        }\r
+       }\r
+\r
+}\r
diff --git a/TinyBannavi/src/taiSync/ReqCtrl.java b/TinyBannavi/src/taiSync/ReqCtrl.java
new file mode 100644 (file)
index 0000000..e780016
--- /dev/null
@@ -0,0 +1,205 @@
+package taiSync;\r
+\r
+import java.io.BufferedReader;\r
+import java.io.FileNotFoundException;\r
+import java.io.FileReader;\r
+import java.io.IOException;\r
+import java.util.ArrayList;\r
+import java.util.regex.Matcher;\r
+import java.util.regex.Pattern;\r
+\r
+import tainavi.RecorderList;\r
+import tainavi.TextValueSet;\r
+\r
+public class ReqCtrl {\r
+\r
+       private ArrayList<TextValueSet> encoder = new ArrayList<TextValueSet>(); \r
+       private ArrayList<TextValueSet> vrate = new ArrayList<TextValueSet>(); \r
+       private ArrayList<TextValueSet> arate = new ArrayList<TextValueSet>(); \r
+       private ArrayList<TextValueSet> device = new ArrayList<TextValueSet>(); \r
+       private ArrayList<TextValueSet> dvdcompat = new ArrayList<TextValueSet>(); \r
+       private ArrayList<TextValueSet> xchapter = new ArrayList<TextValueSet>(); \r
+       private ArrayList<TextValueSet> mschapter = new ArrayList<TextValueSet>(); \r
+       private ArrayList<TextValueSet> mvchapter = new ArrayList<TextValueSet>(); \r
+       private ArrayList<TextValueSet> autodel = new ArrayList<TextValueSet>(); \r
+       private ArrayList<TextValueSet> aspect = new ArrayList<TextValueSet>(); \r
+       private ArrayList<TextValueSet> lvoice = new ArrayList<TextValueSet>(); \r
+       \r
+       private String defFile = "";\r
+       \r
+       private String version = "";\r
+       public String getVersion() { return version; }\r
+       \r
+       // コンストラクタ\r
+       public ReqCtrl(String defPath) {\r
+               \r
+               //\r
+               defFile = defPath;\r
+               \r
+               //\r
+               Matcher ma = Pattern.compile("mail_(.+?)\\.def").matcher(defFile);\r
+               if (ma.find()) {\r
+                       version = ma.group(1);\r
+               }\r
+               \r
+               try {\r
+                       encoder.clear();\r
+                       vrate.clear();\r
+                       arate.clear();\r
+                       device.clear();\r
+                       dvdcompat.clear();\r
+                       xchapter.clear();\r
+                       mschapter.clear();\r
+                       mvchapter.clear();\r
+                       autodel.clear();\r
+                       aspect.clear();\r
+                       lvoice.clear();\r
+                       \r
+                       BufferedReader r = new BufferedReader(new FileReader(defFile));\r
+                       String s ;\r
+                       while ( (s = r.readLine()) != null ) {\r
+                               String[] b = s.split(",");\r
+                               if ( b.length >= 3 ) {\r
+                                       TextValueSet t = new TextValueSet() ;\r
+                                       t.setText(b[1]) ;\r
+                                       t.setValue(b[2]) ;\r
+                                       \r
+                                       if ( b[0].equals("7") ) {\r
+                                               encoder.add(t) ;\r
+                                       }\r
+                                       else if ( b[0].equals("10") ) {\r
+                                               vrate.add(t) ;\r
+                                       }\r
+                                       else if ( b[0].equals("11") ) {\r
+                                               arate.add(t) ;\r
+                                       }\r
+                                       else if ( b[0].equals("12") ) {\r
+                                               device.add(t) ;\r
+                                       }\r
+                                       else if ( b[0].equals("14") ) {\r
+                                               dvdcompat.add(t) ;\r
+                                       }\r
+                                       else if ( b[0].equals("17") ) {\r
+                                               xchapter.add(t) ;\r
+                                       }\r
+                                       else if ( b[0].equals("18") ) {\r
+                                               mschapter.add(t) ;\r
+                                       }\r
+                                       else if ( b[0].equals("19") ) {\r
+                                               mvchapter.add(t) ;\r
+                                       }\r
+                                       else if ( b[0].equals("13") ) {\r
+                                               autodel.add(t) ;\r
+                                       }\r
+                                       else if ( b[0].equals("15") ) {\r
+                                               lvoice.add(t) ;\r
+                                       }\r
+                                       else if ( b[0].equals("16") ) {\r
+                                               aspect.add(t) ;\r
+                                       }\r
+                               }\r
+                       }\r
+               } catch (FileNotFoundException e) {\r
+                       e.printStackTrace();\r
+               } catch (IOException e) {\r
+                       e.printStackTrace();\r
+               }\r
+       }\r
+       \r
+       // リクエスト文字列を生成する\r
+       public String getReqStr(RecorderList rec, ReserveInfo r, int idx, boolean appendDT) {\r
+               \r
+               StringBuilder sb = new StringBuilder();\r
+               \r
+               sb.append("open");\r
+               sb.append(" ");\r
+               sb.append(rec.getRecorderBroadcast());\r
+               sb.append(" ");\r
+               sb.append("prog ");\r
+               sb.append("add ");\r
+               sb.append(r.getStarts().get(idx).replaceAll("/", "").substring(0,8));\r
+               sb.append(" ");\r
+               sb.append(r.getAhh()+r.getAmm());\r
+               sb.append(" ");\r
+               sb.append(r.getZhh()+r.getZmm());\r
+               sb.append(" ");\r
+               sb.append(r.getCh_code());\r
+               sb.append(" ");\r
+               sb.append(_text2val(encoder,r.getTuner()));\r
+               sb.append(" ");\r
+               String rec_mode = _text2val(vrate,r.getRec_mode()) ;\r
+               if ( ! rec_mode.equals("-")) {\r
+                       sb.append(rec_mode);\r
+                       sb.append(" ");\r
+                       String audio = _text2val(arate,r.getRec_audio());\r
+                       if ( audio != null ) {\r
+                               sb.append(audio);\r
+                               sb.append(" ");\r
+                       }\r
+               }\r
+               sb.append(_text2val(device,r.getRec_device()));\r
+               sb.append(" ");\r
+               sb.append(_text2val(dvdcompat,r.getRec_dvdcompat()));\r
+               String chapter_mode = _text2val(xchapter,r.getRec_xchapter());\r
+               if (chapter_mode != null) {\r
+                       sb.append(" ");\r
+                       sb.append(chapter_mode);\r
+               }\r
+               chapter_mode = _text2val(mvchapter,r.getRec_mvchapter());\r
+               if (chapter_mode != null) {\r
+                       sb.append(" ");\r
+                       sb.append(chapter_mode);\r
+               }\r
+               else {\r
+                       sb.append(" ");\r
+                       sb.append("CPN");       // マジックチャプターOFFをつけないとエラー発生\r
+               }\r
+               chapter_mode = _text2val(mschapter,r.getRec_mschapter());\r
+               if (chapter_mode != null) {\r
+                       sb.append(" ");\r
+                       sb.append(chapter_mode);\r
+               }\r
+               \r
+               String val;\r
+               \r
+               val = _text2val(autodel, r.getRec_autodel());\r
+               if (val != null) {\r
+                       sb.append(" ");\r
+                       sb.append(val);\r
+               }\r
+               val = _text2val(aspect, r.getRec_aspect());\r
+               if (val != null) {\r
+                       sb.append(" ");\r
+                       sb.append(val);\r
+               }\r
+               val = _text2val(lvoice, r.getRec_lvoice());\r
+               if (val != null) {\r
+                       sb.append(" ");\r
+                       sb.append(val);\r
+               }\r
+               \r
+               sb.append(" ");\r
+               sb.append((r.getExec())?("RY"):("RN"));\r
+               sb.append("\r\n");\r
+               if ( ! r.getAutocomplete()) {\r
+                       sb.append(r.getTitle());\r
+                       if (r.getRec_pattern_id() < 11 && appendDT == true) {\r
+                               sb.append(" ");\r
+                               sb.append(r.getStarts().get(idx).substring(5,10).replace("/", "_"));\r
+                       }\r
+                       sb.append("\r\n");\r
+               }\r
+               \r
+               return(sb.toString());\r
+       }\r
+       \r
+       //\r
+       private String _text2val(ArrayList<TextValueSet> a, String text) {\r
+               for ( TextValueSet t : a ) {\r
+                       if (t.getText().equals(text)) {\r
+                               return(t.getValue());\r
+                       }\r
+               }\r
+               return(null);\r
+       }\r
+}\r
diff --git a/TinyBannavi/src/taiSync/ReserveCtrl.java b/TinyBannavi/src/taiSync/ReserveCtrl.java
new file mode 100644 (file)
index 0000000..98f4494
--- /dev/null
@@ -0,0 +1,835 @@
+package taiSync;\r
+\r
+import java.beans.XMLDecoder;\r
+import java.beans.XMLEncoder;\r
+import java.io.BufferedInputStream;\r
+import java.io.BufferedOutputStream;\r
+import java.io.File;\r
+import java.io.FileInputStream;\r
+import java.io.FileNotFoundException;\r
+import java.io.FileOutputStream;\r
+import java.net.Authenticator;\r
+import java.util.ArrayList;\r
+import java.util.Calendar;\r
+import java.util.Date;\r
+import java.util.GregorianCalendar;\r
+import java.util.regex.Matcher;\r
+import java.util.regex.Pattern;\r
+\r
+import tainavi.CommonUtils;\r
+import tainavi.RecorderList;\r
+import tainavi.HDDRecorderUtils;\r
+import tainavi.ReserveList;\r
+\r
+public class ReserveCtrl extends HDDRecorderUtils {\r
+\r
+       private ArrayList<ReqCtrl> reqCtrl = null;\r
+       \r
+       // コンストラクタ\r
+       public ReserveCtrl() {\r
+               this.reqCtrl = new ArrayList<ReqCtrl>();\r
+               File f = new File("env");\r
+               if (f.exists() && f.isDirectory()) {\r
+                       for (String fn : f.list()) {\r
+                               if (fn.matches("^mail.*\\.def$")) {\r
+                                       this.reqCtrl.add(new ReqCtrl(String.format("env%s%s",File.separator,fn)));\r
+                               }\r
+                       }\r
+               }\r
+       }\r
+       \r
+       \r
+       \r
+       // 不要になりました予約エントリを削除する\r
+       public void removeEmptyReserveInfo(RecorderList rec, ArrayList<ReserveInfo> rsvInfo) {\r
+               for (int i=rsvInfo.size()-1; i>=0; i--) {\r
+                       ReserveInfo rInfo = rsvInfo.get(i);\r
+                       if ( ! rInfo.getIpaddr().equals(rec.getRecorderIPAddr())) {\r
+                               continue;\r
+                       }\r
+                       \r
+                       if (rInfo.getId().equals("")) {\r
+                               rsvInfo.remove(i);\r
+                       }\r
+               }\r
+       }\r
+       \r
+       // レコーダーからの予約済み一覧情報を反映するに当たって既存の日時情報をクリアする\r
+       private void clearReserveStartEnd(RecorderList rec, ArrayList<ReserveInfo> rsvInfo) {\r
+               for (ReserveInfo rInfo : rsvInfo) {\r
+                       if ( ! rInfo.getIpaddr().equals(rec.getRecorderIPAddr())) {\r
+                               continue;\r
+                       }\r
+                       \r
+                       rInfo.getStarts().clear();\r
+                       rInfo.getEnds().clear();\r
+                       rInfo.getRecIds().clear();\r
+                       \r
+                       CommonUtils.getStartEndList(rInfo.getStarts(), rInfo.getEnds(), rInfo);\r
+                       for (int i=0; i<rInfo.getStarts().size(); i++) {\r
+                               rInfo.getRecIds().add(null);\r
+                       }\r
+               }\r
+       }\r
+       \r
+       // 予約実行日時が近づいているものを警告する\r
+       public boolean isThereUnprocessing(RecorderList rec, ArrayList<ReserveInfo> rsvInfo, int keeping) {\r
+               String currentDateTime = CommonUtils.getDateTime(0);\r
+               String borderDateTime = CommonUtils.getDateTime(16*60);\r
+               String keepingDateTime = CommonUtils.getDateTime(keeping*86400);\r
+               boolean flag = false;\r
+               for (ReserveInfo rInfo : rsvInfo) {\r
+                       if ( rec != null && ! rInfo.getIpaddr().equals(rec.getRecorderIPAddr())) {\r
+                               continue;\r
+                       }\r
+                       \r
+                       if (rInfo.getId().equals("")) {\r
+                               // 削除系\r
+                               for (int i=0; i<rInfo.getRecIds().size(); i++) {\r
+                                       if (rInfo.getRecIds().get(i) != null) {\r
+                                               if (rInfo.getStarts().get(i).compareTo(borderDateTime) < 0) {\r
+                                                       if (rInfo.getStarts().get(i).compareTo(currentDateTime) < 0) {\r
+                                                               System.out.println("削除の期限が過ぎてしまったため削除できませんでした:"+rInfo.getStarts().get(i)+" "+rInfo.getTitle());\r
+                                                               rInfo.getRecIds().set(i, null);\r
+                                                       }\r
+                                                       else {\r
+                                                               System.out.println("録画開始時刻まで15分を切っているため削除できません.:"+rInfo.getStarts().get(i)+" "+rInfo.getTitle());\r
+                                                       }\r
+                                               }\r
+                                               else if (rInfo.getStarts().get(i).compareTo(keepingDateTime) < 0) {\r
+                                                       System.out.println("削除の期限が近づいています:"+rInfo.getStarts().get(i)+" "+rInfo.getTitle());\r
+                                                       flag = true;\r
+                                               }\r
+                                               //\r
+                                       }\r
+                               }\r
+                       }\r
+                       else {\r
+                               // 登録系\r
+                               if (rInfo.getExec() && rInfo.getByTainavi()) {\r
+                                       for (int i=0; i<rInfo.getRecIds().size(); i++) {\r
+                                               if (rInfo.getRecIds().get(i) == null) {\r
+                                                       if (rInfo.getStarts().get(i).compareTo(borderDateTime) < 0) {\r
+                                                               if (rInfo.getStarts().get(i).compareTo(currentDateTime) < 0) {\r
+                                                                       System.out.println("予約の期限が過ぎてしまったため予約できませんでした:"+rInfo.getStarts().get(i)+" "+rInfo.getTitle());\r
+                                                                       rInfo.getRecIds().set(i, "-1");\r
+                                                               }\r
+                                                               else {\r
+                                                                       System.out.println("録画開始時刻まで15分を切っているため予約できません.:"+rInfo.getStarts().get(i)+" "+rInfo.getTitle());\r
+                                                               }\r
+                                                       }\r
+                                                       else if (rInfo.getStarts().get(i).compareTo(keepingDateTime) < 0) {\r
+                                                               System.out.println("予約の期限が近づいています:"+rInfo.getStarts().get(i)+" "+rInfo.getTitle());\r
+                                                               flag = true;\r
+                                                       }\r
+                                               }\r
+                                       }\r
+                               }\r
+                       }\r
+               }\r
+               return flag;\r
+       }\r
+       \r
+       // clearReserveStartEnd()と同様だがレコーダーと同期済み(Idあり)のものは引き継ぐ\r
+       public void refreshReserveStartEnd(RecorderList rec, ArrayList<ReserveInfo> rsvInfo) {\r
+               for (int i=rsvInfo.size()-1; i>=0; i--) {\r
+                       ReserveInfo rInfo = rsvInfo.get(i);\r
+                       if ( rec != null && ! rInfo.getIpaddr().equals(rec.getRecorderIPAddr())) {\r
+                               continue;\r
+                       }\r
+                       \r
+               String cur = CommonUtils.getDateTime(0);\r
+                       if (rInfo.getRec_pattern_id() == 11) {\r
+                               if (rInfo.getStartDateTime().compareTo(cur) < 0) {\r
+                                       // 単日終了エントリの削除\r
+                                       rsvInfo.remove(i);\r
+                               }\r
+                       }\r
+                       else if (rInfo.getId() != null && rInfo.getId().equals("")) {\r
+                               // レコーダに存在しないエントリの削除\r
+                               int cnt = 0;\r
+                               for (int j=0; j<rInfo.getRecIds().size(); j++) {\r
+                                       if (rInfo.getEnds().get(j).compareTo(cur) < 0) {\r
+                                               // 終了日時が過去のものは無効エントリにする\r
+                                               rInfo.getRecIds().set(j,"-1");\r
+                                       }\r
+                                       String s = rInfo.getRecIds().get(j);\r
+                                       if (s != null && ! s.equals("-1")) {\r
+                                               cnt++;\r
+                                       }\r
+                               }\r
+                               if (cnt == 0) {\r
+                                       rsvInfo.remove(i);\r
+                               }\r
+                       }\r
+                       else {\r
+                               // 同期済み(Idあり)を引き継ぎつつ情報リセット\r
+                               ArrayList<String> starts = new ArrayList<String>();\r
+                               ArrayList<String> ends = new ArrayList<String>();\r
+                               ArrayList<String> ids = new ArrayList<String>();\r
+                               CommonUtils.getStartEndList(starts, ends, rInfo);\r
+                               \r
+                               if ( starts.size() == 0 ) {\r
+                                       // 2012/11/10 Out of boundsへの臨時対処 -> なぜここに来るのかはちょっと原因がわからない -> getStartEndListのバグだった\r
+                                       System.out.println("【致命的エラー】 開始/終了日時が判定できませんでした!このエントリは削除されます! 現在日時="+cur+" パターン="+rInfo.getRec_pattern()+"("+rInfo.getRec_pattern_id()+") "+rInfo.getAhh()+":"+rInfo.getAmm()+"-"+rInfo.getZhh()+":"+rInfo.getZmm()+" タイトル="+rInfo.getTitle());\r
+                                       rsvInfo.remove(i);\r
+                                       continue;\r
+                               }\r
+                               \r
+                               if (rInfo.getByTainavi()) {\r
+                                       // 鯛ナビからなされた予約エントリの場合\r
+                                       for (int j=0; j<starts.size(); j++) {\r
+                                               ids.add(null);\r
+                                               for (int k=0; k<rInfo.getStarts().size(); k++) {\r
+                                                       if (starts.get(j).equals(rInfo.getStarts().get(k)) && ends.get(j).equals(rInfo.getEnds().get(k))) {\r
+                                                               ids.set(j,rInfo.getRecIds().get(k));\r
+                                                               break;\r
+                                                       }\r
+                                               }\r
+                                       }\r
+                               }\r
+                               else {\r
+                                       // 本体からなされた予約エントリの場合\r
+                                       for (int j=0; j<starts.size(); j++) {\r
+                                               String id = (j==0)?(rInfo.getRecIds().get(0)):(null); \r
+                                               ids.add(id);\r
+                                       }\r
+                               }\r
+                               rInfo.setStarts(starts);\r
+                               rInfo.setEnds(ends);\r
+                               rInfo.setRecIds(ids);\r
+                               // まあ特に使わない値だけど一応更新\r
+                               rInfo.setStartDateTime(rInfo.getStarts().get(0));\r
+                               rInfo.setEndDateTime(rInfo.getEnds().get(0));\r
+                       }\r
+               }\r
+               \r
+               // ほぞんするお\r
+               ReserveInfo.save(rsvInfo);\r
+               ReserveInfo.showReserveInfo(rsvInfo,(rec!=null)?(rec.getRecorderIPAddr()):(null));\r
+       }\r
+       \r
+       // ローカル予約IDを更新する\r
+       private void setNewRsvId(RecorderList rec, ArrayList<ReserveInfo> rsvInfo) {\r
+               long rsvId = 0; // オーバーフローは考慮しない\r
+               \r
+               for (ReserveInfo rInfo : rsvInfo) {\r
+                       if ( ! rInfo.getIpaddr().equals(rec.getRecorderIPAddr())) {\r
+                               continue;\r
+                       }\r
+                       if (rInfo.getId() != null && ! rInfo.getId().equals("") ) {\r
+                               if (Long.valueOf(rInfo.getId()) > rsvId) {\r
+                                       rsvId = Long.valueOf(rInfo.getId());\r
+                               }\r
+                       }\r
+               }\r
+               for (ReserveInfo rInfo : rsvInfo) {\r
+                       if ( ! rInfo.getIpaddr().equals(rec.getRecorderIPAddr())) {\r
+                               continue;\r
+                       }\r
+                       if (rInfo.getId() == null) {\r
+                               rInfo.setId(String.valueOf(++rsvId));\r
+                       }\r
+               }\r
+       }\r
+       \r
+       // レコーダーから予約済み一覧を取得する\r
+       private ArrayList<ReserveList> getReservedList(RecorderList rec) {\r
+               \r
+               System.out.println("RD("+rec.getRecorderIPAddr()+")に接続します");\r
+               \r
+               // おまじない\r
+               Authenticator.setDefault(new MyAuthenticator(rec.getRecorderUser(), rec.getRecorderPasswd()));\r
+               \r
+               // RDから予約一覧を取り出す\r
+               Matcher ma = null;\r
+               String idx = "";\r
+               String header="";\r
+               String response="";\r
+               {\r
+                       String[] d = reqGET("http://"+rec.getRecorderIPAddr()+":"+rec.getRecorderPortNo()+"/reserve/b_rsv.htm",null);\r
+                       header = d[0];\r
+                       response = d[1];\r
+                       \r
+                       if (response == null) {\r
+                               System.out.println("レコーダーが反応しません.1("+rec.getRecorderIPAddr()+")");\r
+                               return(null);\r
+                       }\r
+               }\r
+               ma = Pattern.compile("/reserve/(\\d+?)/reserve.htm").matcher(response);\r
+               if (ma.find()) {\r
+                       idx = ma.group(1);\r
+                       String[] d = reqGET("http://"+rec.getRecorderIPAddr()+":"+rec.getRecorderPortNo()+"/reserve/"+idx+"/reserve.htm",null);\r
+                       header = d[0];\r
+                       response = d[1];\r
+                       \r
+                       if (response == null) {\r
+                               System.out.println("レコーダーが反応しません.2("+rec.getRecorderIPAddr()+")");\r
+                               return(null);\r
+                       }\r
+               }\r
+               \r
+               // 予約一覧データの分析\r
+               return(decodeReservedList(response));\r
+       }\r
+       \r
+       // レコーダーからの予約済み一覧情報と自前の一覧をマージする\r
+       public void margeReservedInfo(RecorderList rec, ArrayList<ReserveInfo> rsvInfo) {\r
+               \r
+               // レコーダーから情報取得\r
+               ArrayList<ReserveList> rsvList = getReservedList(rec);\r
+               if (rsvList == null) {\r
+                       return;\r
+               }\r
+               \r
+               // 予約一覧の日時情報をクリアする\r
+               clearReserveStartEnd(rec, rsvInfo);\r
+               \r
+               // マージするよ?\r
+               ArrayList<ReserveList> tmpList = new ArrayList<ReserveList>();\r
+               \r
+               for (ReserveList rList : rsvList) {\r
+                       if (rList.getRec_pattern_id() == 11) {\r
+                               // レコーダ情報が単日予約の場合\r
+                               boolean reserved = false;\r
+                               for (ReserveInfo rInfo : rsvInfo) {\r
+                                       if ( ! rInfo.getIpaddr().equals(rec.getRecorderIPAddr())) {\r
+                                               continue;\r
+                                       }\r
+                                       \r
+                                       if (rInfo.getChannel().equals(rList.getChannel())) {\r
+                                               if (rInfo.getRec_pattern_id() == 11) {\r
+                                                       // 単日予約\r
+                                                       if (rInfo.getRec_pattern().equals(rList.getRec_pattern())) {\r
+                                                               if (rInfo.getStarts().get(0).equals(rList.getStartDateTime()) && rInfo.getEnds().get(0).equals(rList.getEndDateTime())) {\r
+                                                                       if (rInfo.getRecIds().get(0) == null) {\r
+                                                                               rInfo.getRecIds().set(0, rList.getId());\r
+                                                                               reserved = true;\r
+                                                                               break;\r
+                                                                       }\r
+                                                               }\r
+                                                       }\r
+                                               }\r
+                                               else {\r
+                                                       // 繰り返し予約\r
+                                                       for (int i=0; i<rInfo.getStarts().size(); i++) {\r
+                                                               if (rInfo.getStarts().get(i).equals(rList.getStartDateTime()) && rInfo.getEnds().get(i).equals(rList.getEndDateTime())) {\r
+                                                                       if (rInfo.getRecIds().get(i) == null) {\r
+                                                                               rInfo.getRecIds().set(i, rList.getId());\r
+                                                                               reserved = true;\r
+                                                                               break;\r
+                                                                       }\r
+                                                               }\r
+                                                       }\r
+                                               }\r
+                                       }\r
+                                       if (reserved) {\r
+                                               break;\r
+                                       }\r
+                               }\r
+                               if ( ! reserved) {\r
+                                       tmpList.add(rList);\r
+                               }\r
+                       }\r
+                       else {\r
+                               // レコーダ情報が繰り返し予約の場合\r
+                               boolean reserved = false;\r
+                               for (ReserveInfo rInfo : rsvInfo) {\r
+                                       if ( ! rInfo.getIpaddr().equals(rec.getRecorderIPAddr())) {\r
+                                               continue;\r
+                                       }\r
+                                       \r
+                                       if (rInfo.getChannel().equals(rList.getChannel())) {\r
+                                               if (rInfo.getRec_pattern_id() == rList.getRec_pattern_id()) {\r
+                                                       if (rInfo.getAhh().equals(rList.getAhh()) && rInfo.getAmm().equals(rList.getAmm()) && rInfo.getZhh().equals(rList.getZhh()) && rInfo.getZmm().equals(rList.getZmm())) {\r
+                                                               if (rInfo.getRecIds().get(0) == null) {\r
+                                                                       rInfo.getRecIds().set(0, rList.getId());\r
+                                                                       reserved = true;\r
+                                                                       break;\r
+                                                               }\r
+                                                       }\r
+                                               }\r
+                                       }\r
+                               }\r
+                               if ( ! reserved) {\r
+                                       tmpList.add(rList);\r
+                               }\r
+                       }\r
+               }\r
+               for (ReserveList rList : tmpList) {\r
+                       ReserveInfo rInfo = new ReserveInfo(rList);\r
+                       rInfo.setByTainavi(false);\r
+                       rInfo.setIpaddr(rec.getRecorderIPAddr());\r
+                       rInfo.setPortno(rec.getRecorderPortNo());\r
+                       \r
+                       rsvInfo.add(rInfo);\r
+                       \r
+                       System.err.println("new rsvInfo: "+rInfo.getTitle());   // デバッグ\r
+               }\r
+               \r
+               // 本体から予約したもので移動しちゃったものは削除しますよ\r
+               for (int i=rsvInfo.size()-1; i>=0; i--) {\r
+                       ReserveInfo rInfo = rsvInfo.get(i);\r
+                       if (rInfo.getByTainavi()) {\r
+                               continue;\r
+                       }\r
+                       if ( ! rInfo.getIpaddr().equals(rec.getRecorderIPAddr())) {\r
+                               continue;\r
+                       }\r
+                       int cnt = 0;\r
+                       for (String s : rInfo.getRecIds()) {\r
+                               if (s != null) {\r
+                                       cnt++;\r
+                               }\r
+                       }\r
+                       if (cnt == 0) {\r
+                               rsvInfo.remove(i);\r
+                       }\r
+               }\r
+               \r
+               // 新規エントリにtaiSyncローカルの予約IDを割り当てる\r
+               if (tmpList.size() > 0) {\r
+                       setNewRsvId(rec, rsvInfo);\r
+               }\r
+       }\r
+       \r
+       // 予約一覧データを作成する\r
+       public String getRsvStr(RecorderList rec, ArrayList<ReserveInfo> rsvInfo) {\r
+               \r
+               margeReservedInfo(rec, rsvInfo);\r
+               \r
+               StringBuilder sb = new StringBuilder();\r
+               sb.append("<HTML>\n");\r
+               sb.append("<HEAD><TITLE>タイニーシンク - "+rec.getRecorderIPAddr()+"</TITLE></HEAD>\n");\r
+               sb.append("<BODY>\n");\r
+               \r
+               {\r
+                       sb.append("<!--\n");\r
+                       \r
+                       int i = 0;\r
+                       for (ReserveInfo rInfo : rsvInfo) {\r
+                               \r
+                               // 自分の分だけ\r
+                               if ( ! rInfo.getIpaddr().equals(rec.getRecorderIPAddr())) {\r
+                                       continue;\r
+                               }\r
+                               \r
+                               // 削除済みとかね\r
+                               if (rInfo.getId() == null || rInfo.getId().equals("")) {\r
+                                       continue;\r
+                               }\r
+                               \r
+                               Matcher ma = null;\r
+                               String str = null;\r
+                               sb.append("c1["+i+"]=\""+rInfo.getId()+"\";");\r
+                               sb.append("c2["+i+"]=\""+((rInfo.getExec())?("3"):("2"))+"\";");\r
+                               sb.append("c3["+i+"]=\""+rInfo.getTitle().replaceAll("\"", "&quot;")+"\";");\r
+                               if (rInfo.getTuner().equals("RE") || rInfo.getTuner().equals("RE1")) {\r
+                                       str = "7";\r
+                               }\r
+                               else if (rInfo.getTuner().equals("TS2") || rInfo.getTuner().equals("RE2") || rInfo.getTuner().equals("R2")) {\r
+                                       str = "9";\r
+                               }\r
+                               else {\r
+                                       str = "11";     // TS1\r
+                               }\r
+                               sb.append("c4["+i+"]=\""+str+"\";");\r
+                               sb.append("c5["+i+"]=\""+rInfo.getChannel()+"\";");\r
+                               sb.append("c6["+i+"]=\""+rInfo.getRec_pattern()+"\";");\r
+                               sb.append("c7["+i+"]=\""+rInfo.getAhh()+":"+rInfo.getAmm()+"\";");\r
+                               sb.append("c8["+i+"]=\""+rInfo.getZhh()+":"+rInfo.getZmm()+"\";");\r
+                               sb.append("c9["+i+"]=\""+rInfo.getRec_device()+"\";");\r
+                               str = null;\r
+                               if (str == null) {\r
+                                       ma = Pattern.compile("^\\[TSE\\] ").matcher(rInfo.getRec_mode());\r
+                                       if (ma.find()) str = ma.replaceFirst("MN");\r
+                               }\r
+                               if (str == null) {\r
+                                       ma = Pattern.compile("^\\[AVC\\] ").matcher(rInfo.getRec_mode());\r
+                                       if (ma.find()) str = ma.replaceFirst("MN");\r
+                               }\r
+                               if (str == null) {\r
+                                       ma = Pattern.compile("^\\[VR\\] 2\\.0/2\\.2$").matcher(rInfo.getRec_mode());\r
+                                       if (ma.find()) str = ma.replaceFirst("LP2.0/2.2");\r
+                               }\r
+                               if (str == null) {\r
+                                       ma = Pattern.compile("^\\[VR\\] 4\\.4/4\\.6$").matcher(rInfo.getRec_mode());\r
+                                       if (ma.find()) str = ma.replaceFirst("SP4.4/4.6");\r
+                               }\r
+                               if (str == null) {\r
+                                       ma = Pattern.compile("^\\[VR\\] ").matcher(rInfo.getRec_mode());\r
+                                       if (ma.find()) str = ma.replaceFirst("MN");\r
+                               }\r
+                               if (str == null) {\r
+                                       //System.err.println(rInfo.getRec_mode());\r
+                                       str = "TS";\r
+                               }\r
+                               sb.append("c10["+i+"]=\""+str+"\";");\r
+                               sb.append("c11["+i+"]=\""+rInfo.getRec_audio()+"\";");\r
+                               sb.append("c14["+i+"]=\"0\";");\r
+                               sb.append("c15["+i+"]=\""+((rInfo.getPursues())?("4"):("1"))+"\";");\r
+                               sb.append("c16["+i+"]=\"1\";");\r
+                               sb.append("c17["+i+"]=\"2\";");\r
+                               sb.append("\n");\r
+                               i++;\r
+                       }\r
+                       sb.append("-->\n");\r
+               }\r
+               \r
+               sb.append("<H2>タイニーシンク - "+rec.getRecorderIPAddr()+"</H2>");\r
+               sb.append("<TABLE border=\"3\">\n");\r
+               sb.append("<TR bgcolor=\"#DD3300\">\n");\r
+               sb.append("<TH STYLE=\"width: 25px\">元</TH>\n");\r
+               sb.append("<TH STYLE=\"width: 150px\">録画パターン</TH>\n");\r
+               sb.append("<TH STYLE=\"width: 225px\">開始~終了(赤字は未反映、斜体は削除待ち)</TH>\n");\r
+               sb.append("<TH STYLE=\"width: 50px\">長さ</TH>\n");\r
+               sb.append("<TH STYLE=\"width: 50px\">CH</TH>\n");\r
+               sb.append("<TH>タイトル</TH>\n");\r
+               sb.append("</TR>\n");\r
+               \r
+               {\r
+                       // ソートする\r
+                       ArrayList<ReserveInfo> b = new ArrayList<ReserveInfo>();\r
+                       for (int i=0; i<rsvInfo.size(); i++) {\r
+                               // 自分の分だけ\r
+                               if ( ! rsvInfo.get(i).getIpaddr().equals(rec.getRecorderIPAddr())) {\r
+                                       continue;\r
+                               }\r
+                               boolean f = true;\r
+                               for (int j=0; j<b.size(); j++) {\r
+                                       if (b.get(j).getStartDateTime().compareTo(rsvInfo.get(i).getStartDateTime()) > 0) {\r
+                                               b.add(j, rsvInfo.get(i));\r
+                                               f = false;\r
+                                               break;\r
+                                       }\r
+                               }\r
+                               if (f) {\r
+                                       b.add(rsvInfo.get(i));\r
+                               }\r
+                       }\r
+                       \r
+                       // 出力する\r
+                       for ( int i = 0; i<b.size(); i++ ) {\r
+                               // 詳細情報の取得\r
+                               ReserveInfo e = b.get(i);\r
+                               \r
+                               int wlen = 0;\r
+                               String ptn = e.getRec_pattern();\r
+                               Matcher ma = Pattern.compile("[^ -~。-゚]").matcher(ptn);\r
+                               while (ma.find()) {\r
+                                       wlen++;\r
+                               }\r
+                               int ptnlen = 14-wlen;\r
+                               ptn = String.format(String.format("%%-%ds",ptnlen),ptn);\r
+                               \r
+                               String bgc = "bgcolor=\"#"+((i % 2 == 0)?("CCCCCC"):("FFFFFF"))+"\"";\r
+                               \r
+                               int row = e.getStarts().size();\r
+                               sb.append("<TR "+bgc+">");\r
+                               sb.append("<TD rowspan=\""+row+"\">"+((e.getByTainavi())?("鯛"):("本"))+"</TD>");\r
+                               sb.append("<TD rowspan=\""+row+"\">"+ptn+"</TD>");\r
+                               sb.append("<TD>"+getHTMLStartEnd(e.getExec(),e.getId(),e.getRecIds().get(0),e.getStarts().get(0),e.getEnds().get(0))+"</TD>");\r
+                               sb.append("<TD rowspan=\""+row+"\">"+e.getRec_min()+"m</TD>");\r
+                               sb.append("<TD rowspan=\""+row+"\">"+e.getChannel()+"</TD>");\r
+                               sb.append("<TD rowspan=\""+row+"\">"+e.getTitle()+"</TD>");\r
+                               sb.append("</TR>\n");\r
+\r
+                               for (int j=1; j<e.getStarts().size(); j++) {\r
+                                       sb.append("<TR "+bgc+">");\r
+                                       sb.append("<TD>"+getHTMLStartEnd(e.getExec(),e.getId(),e.getRecIds().get(j),e.getStarts().get(j),e.getEnds().get(j))+"</TD>");\r
+                                       sb.append("</TR>\n");\r
+                               }\r
+                       }\r
+               }\r
+               \r
+               sb.append("</BODY>\n");\r
+               sb.append("</HTML>\n");\r
+               \r
+               return(sb.toString());\r
+       }\r
+       private String getHTMLStartEnd(boolean exec, String id, String rsvId, String start, String end) {\r
+               if ( ! id.equals("")) {\r
+                       if (rsvId == null) {\r
+                               if ( ! exec) {\r
+                                       return ("<font color=\"red\"><s>"+start+"~"+end.substring(11)+"</s></font>");\r
+                               }\r
+                               else {\r
+                                       return ("<font color=\"red\">"+start+"~"+end.substring(11)+"</font>");\r
+                               }\r
+                       }\r
+                       else {\r
+                               return (start+"~"+end.substring(11));\r
+                       }\r
+               }\r
+               else {\r
+                       if (rsvId == null) {\r
+                               return ("<font color=\"blue\"><s><i>"+start+"~"+end.substring(11)+"</i></s></font>");\r
+                       }\r
+                       else {\r
+                               return ("<font color=\"blue\"><i>"+start+"~"+end.substring(11)+"</i></font>");\r
+                       }\r
+               }\r
+       }\r
+       \r
+       // 鯛ナビからの予約リクエストを処理する\r
+       public String addReservedInfo(RecorderList rec, String pstr, ArrayList<ReserveInfo> rsvInfo) {\r
+               \r
+               // POST文字列から予約情報を生成する\r
+               ReserveInfo newInfo = new ReserveInfo(pstr);\r
+               newInfo.setByTainavi(true);\r
+               newInfo.setIpaddr(rec.getRecorderIPAddr());\r
+               newInfo.setPortno(rec.getRecorderPortNo());\r
+               newInfo.setRec_pattern_id(getRec_pattern_Id(newInfo.getRec_pattern()));\r
+               newInfo.setRec_nextdate(CommonUtils.getNextDate(newInfo));\r
+               newInfo.setRec_min(CommonUtils.getRecMin(newInfo.getAhh(),newInfo.getAmm(),newInfo.getZhh(),newInfo.getZmm()));\r
+               getStartEndDateTime(newInfo);\r
+               \r
+               String cur = CommonUtils.getDateTime(0);\r
+               CommonUtils.getStartEndList(newInfo.getStarts(), newInfo.getEnds(), newInfo);\r
+               for (int i=0; i<newInfo.getStarts().size(); i++) {\r
+                       if (newInfo.getStarts().get(i).compareTo(cur) < 0) {\r
+                               newInfo.getRecIds().add("-1");\r
+                       }\r
+                       else {\r
+                               newInfo.getRecIds().add(null);\r
+                       }\r
+               }\r
+               \r
+               newInfo.setId(null);\r
+               rsvInfo.add(newInfo);\r
+               setNewRsvId(rec, rsvInfo);\r
+               \r
+               System.out.println("予約を受け付けました:"+newInfo.getRec_pattern()+" "+newInfo.getTitle());        // デバッグ\r
+               return(newInfo.getId());\r
+       }\r
+       \r
+       // 鯛ナビからの更新リクエストを処理する\r
+       public String updReservedInfo(RecorderList rec, String pstr, ArrayList<ReserveInfo> rsvInfo) {\r
+               \r
+               // POST文字列から予約情報を生成する\r
+               ReserveInfo newInfo = new ReserveInfo(pstr);\r
+               newInfo.setByTainavi(true);\r
+               newInfo.setIpaddr(rec.getRecorderIPAddr());\r
+               newInfo.setPortno(rec.getRecorderPortNo());\r
+               newInfo.setRec_pattern_id(getRec_pattern_Id(newInfo.getRec_pattern()));\r
+               newInfo.setRec_nextdate(CommonUtils.getNextDate(newInfo));\r
+               newInfo.setRec_min(CommonUtils.getRecMin(newInfo.getAhh(),newInfo.getAmm(),newInfo.getZhh(),newInfo.getZmm()));\r
+               getStartEndDateTime(newInfo);\r
+               \r
+               String cur = CommonUtils.getDateTime(0);\r
+               CommonUtils.getStartEndList(newInfo.getStarts(), newInfo.getEnds(), newInfo);\r
+               for (int i=0; i<newInfo.getStarts().size(); i++) {\r
+                       if (newInfo.getStarts().get(i).compareTo(cur) < 0) {\r
+                               newInfo.getRecIds().add("-1");\r
+                       }\r
+                       else {\r
+                               newInfo.getRecIds().add(null);\r
+                       }\r
+               }\r
+               \r
+               for (ReserveInfo rInfo : rsvInfo) {\r
+                       if ( ! rInfo.getIpaddr().equals(rec.getRecorderIPAddr())) {\r
+                               continue;\r
+                       }\r
+                       \r
+                       if (rInfo.getId().equals(newInfo.getId())) {\r
+                               rInfo.setId("");        // 実際の削除はPOP実行時\r
+                               break;\r
+                       }\r
+               }\r
+               rsvInfo.add(newInfo);\r
+               \r
+               System.out.println("更新を受け付けました:"+newInfo.getRec_pattern()+" "+newInfo.getTitle());        // デバッグ\r
+               return(newInfo.getId());\r
+       }\r
+       \r
+       // 鯛ナビからの削除リクエストを処理する\r
+       public void delReservedInfo(RecorderList rec, String targetRsvId, ArrayList<ReserveInfo> rsvInfo) {\r
+               \r
+               // \r
+               for (ReserveInfo rInfo : rsvInfo) {\r
+                       if ( ! rInfo.getIpaddr().equals(rec.getRecorderIPAddr())) {\r
+                               continue;\r
+                       }\r
+                       \r
+                       if (rInfo.getId().equals(targetRsvId)) {\r
+                               rInfo.setId("");        // 実際の削除はPOP実行時\r
+                               \r
+                               // 無効な予約の場合は実際の削除リクエストは不要\r
+                               for ( int i=0; i<rInfo.getRecIds().size(); i++ ) {\r
+                                       if (rInfo.getRecIds().get(i) != null && rInfo.getRecIds().get(i).equals("-1")) {\r
+                                               rInfo.getRecIds().set(i, null);\r
+                                       }\r
+                               }\r
+                               \r
+                               System.out.println("更新を受け付けました:"+rInfo.getRec_pattern()+" "+rInfo.getTitle());    // デバッグ\r
+                               break;\r
+                       }\r
+               }\r
+       }\r
+       \r
+       //\r
+       public void remoteCtrl(RecorderList rec, String location) {\r
+               // おまじない\r
+               Authenticator.setDefault(new MyAuthenticator(rec.getRecorderUser(), rec.getRecorderPasswd()));\r
+               \r
+               String uri = "http://"+rec.getRecorderIPAddr()+":"+rec.getRecorderPortNo()+location;\r
+               System.err.println("GET "+uri);\r
+               reqGET(uri, null);\r
+       }\r
+       \r
+       \r
+       \r
+       // リクエストキューの作成\r
+       public boolean getReqQueue(RecorderInfo rec, ArrayList<ReserveInfo> rsvInfo, ArrayList<String> reqQueue, boolean appendDT, int period) {\r
+               \r
+               // 無効エントリの削除と新規エントリの追加\r
+               refreshReserveStartEnd(rec, rsvInfo);\r
+\r
+               // 更新期限\r
+               String cur = CommonUtils.getDateTime(16*60);\r
+               \r
+               boolean drop = false;\r
+               \r
+               // 削除リクエスト\r
+               for (ReserveInfo rInfo : rsvInfo) {\r
+                       if ( ! rInfo.getIpaddr().equals(rec.getRecorderIPAddr())) {\r
+                               continue;\r
+                       }\r
+                       \r
+                       if (rInfo.getId() != null && rInfo.getId().equals("")) {\r
+                               for (int i=0; i<rInfo.getRecIds().size(); i++) {\r
+                                       if (rInfo.getRecIds().get(i) != null && ! rInfo.getRecIds().get(i).equals("-1")) {\r
+                                               if (rInfo.getStarts().get(i).compareTo(cur) > 0) {\r
+                                                       String req = "open "+rec.getRecorderBroadcast()+" prog del "+rInfo.getRecIds().get(i)+"\r\n";\r
+                                                       reqQueue.add(req);\r
+                                               }\r
+                                               else {\r
+                                                       System.out.println("【警告】録画開始時刻まで15分を切っているため削除できません. id="+rInfo.getRecIds().get(i));\r
+                                                       drop = true;\r
+                                               }\r
+                                       }\r
+                               }\r
+                       }\r
+               }\r
+               \r
+               // 登録リクエスト\r
+               String periodDate = CommonUtils.getDateTime(period*86400);\r
+               for (ReserveInfo rInfo : rsvInfo) {\r
+                       if ( ! rInfo.getIpaddr().equals(rec.getRecorderIPAddr())) {\r
+                               continue;\r
+                       }\r
+                       \r
+                       if ( ! rInfo.getExec()) {\r
+                               // 予約実行OFFのエントリはRDに登録しない\r
+                               continue;\r
+                       }\r
+\r
+                       if (rInfo.getId() != null && ! rInfo.getId().equals("")) {\r
+                               if (rInfo.getByTainavi()) {\r
+                                       for (int i=0; i<rInfo.getRecIds().size(); i++) {\r
+                                               boolean periodFlag = (rInfo.getStarts().get(i).compareTo(periodDate) < 0);\r
+                                               if (rInfo.getRec_pattern_id() == 11) {\r
+                                                       periodFlag = true;\r
+                                               }\r
+                                               if (rInfo.getRecIds().get(i) == null && periodFlag) {\r
+                                                       for (ReqCtrl rc : reqCtrl) {\r
+                                                               if (rc.getVersion().equals(rInfo.getVersion())) {\r
+                                                                       if (rInfo.getStarts().get(i).compareTo(cur) > 0) {\r
+                                                                               String req = rc.getReqStr(rec, rInfo, i, appendDT);\r
+                                                                               reqQueue.add(req);\r
+                                                                       }\r
+                                                                       else {\r
+                                                                               System.out.println("【警告】録画開始時刻まで15分を切っているため予約できません. start="+rInfo.getStarts().get(i));\r
+                                                                               drop = true;\r
+                                                                       }\r
+                                                                       break;\r
+                                                               }\r
+                                                       }\r
+                                               }\r
+                                       }\r
+                               }\r
+                       }\r
+               }\r
+               \r
+               return drop;\r
+       }\r
+       \r
+       // 予約一覧にIdを埋め込む\r
+       public void addRsvId(String ipaddr, ArrayList<ReserveInfo> rsvInfo, String start, String end, String channel, String title, String id) {\r
+               for (ReserveInfo rInfo : rsvInfo) {\r
+                       if ( ! rInfo.getIpaddr().equals(ipaddr)) {\r
+                               continue;\r
+                       }\r
+                       \r
+                       if ( ! rInfo.getByTainavi()) {\r
+                               continue;\r
+                       }\r
+                       \r
+                       // RDが間違ったCHを返してくることがあるのでタイトル一致でもOKとする\r
+                       boolean matchCh = (rInfo.getChannel().equals(channel));\r
+                       boolean matchTi = (rInfo.getTitle().replaceAll(" \\d\\d_\\d\\d$", "").equals(title.replaceAll(" \\d\\d_\\d\\d$", "")));\r
+                       if (matchCh || matchTi) {\r
+                               System.err.println("DEBUG: matchCh="+matchCh+", matchTi="+matchTi);\r
+                               for (int i=0; i<rInfo.getStarts().size(); i++) {\r
+                                       if (rInfo.getRecIds().get(i) == null) {\r
+                                               if (rInfo.getStarts().get(i).equals(start) && rInfo.getEnds().get(i).equals(end)) {\r
+                                                       rInfo.getRecIds().set(i, id);\r
+                                                       \r
+                                                       System.out.println("予約が確定しました:"+rInfo.getStarts().get(i)+" "+rInfo.getTitle());\r
+                                                       \r
+                                                       ReserveInfo.save(rsvInfo);      // 保存しよう\r
+                                                       ReserveInfo.showReserveInfo(rsvInfo,ipaddr);\r
+                                                       return;\r
+                                               }\r
+                                       }\r
+                               }\r
+                       }\r
+               }\r
+               System.err.println("不正な完了通知です.");\r
+       }\r
+       \r
+       // 予約一覧からIdを削除\r
+       public void delRsvId(String ipaddr, ArrayList<ReserveInfo> rsvInfo, String id) {\r
+               for (ReserveInfo rInfo : rsvInfo) {\r
+                       if ( ! rInfo.getIpaddr().equals(ipaddr)) {\r
+                               continue;\r
+                       }\r
+                       \r
+                       /* == 削除の場合は、エントリが鯛ナビからのか本体からのかを気にしなくてよい ==\r
+                       if ( ! rInfo.getByTainavi()) {\r
+                               continue;\r
+                       }\r
+                       */\r
+                       \r
+                       // 削除対象でなければスルー\r
+                       if ( ! rInfo.getId().equals("")) {\r
+                               continue;\r
+                       }\r
+                       \r
+                       int cnt = 0;\r
+                       for (int i=0; i<rInfo.getRecIds().size(); i++) {\r
+                               if (rInfo.getRecIds().get(i) == null) {\r
+                                       cnt++;\r
+                               }\r
+                               else {\r
+                                       if (rInfo.getRecIds().get(i).equals(id)) {\r
+                                               rInfo.getRecIds().set(i, null);\r
+                                               cnt++;\r
+                                               \r
+                                               System.out.println("削除が確定しました:"+rInfo.getStarts().get(i)+" "+rInfo.getTitle());\r
+                                       }\r
+                               }\r
+                       }\r
+                       // 全Idがnullになったらエントリから削除\r
+                       if (cnt == rInfo.getRecIds().size()) {\r
+                               rsvInfo.remove(rInfo);\r
+                               \r
+                               ReserveInfo.save(rsvInfo);      // 保存しよう\r
+                               ReserveInfo.showReserveInfo(rsvInfo,ipaddr);\r
+                               return;\r
+                       }\r
+               }\r
+       }\r
+\r
+}\r
diff --git a/TinyBannavi/src/taiSync/ReserveInfo.java b/TinyBannavi/src/taiSync/ReserveInfo.java
new file mode 100644 (file)
index 0000000..a29509b
--- /dev/null
@@ -0,0 +1,272 @@
+package taiSync;\r
+\r
+import java.beans.XMLDecoder;\r
+import java.beans.XMLEncoder;\r
+import java.io.BufferedInputStream;\r
+import java.io.BufferedOutputStream;\r
+import java.io.File;\r
+import java.io.FileInputStream;\r
+import java.io.FileNotFoundException;\r
+import java.io.FileOutputStream;\r
+import java.io.UnsupportedEncodingException;\r
+import java.net.URLDecoder;\r
+import java.net.URLEncoder;\r
+import java.util.ArrayList;\r
+import java.util.regex.Matcher;\r
+import java.util.regex.Pattern;\r
+\r
+import tainavi.RecorderList;\r
+import tainavi.ReserveList;\r
+\r
+\r
+\r
+public class ReserveInfo extends ReserveList {\r
+\r
+       private boolean byTainavi = true;\r
+       public void setByTainavi(boolean b) { byTainavi = b; }\r
+       public boolean getByTainavi() { return byTainavi; }\r
+       \r
+       private String ipaddr = "";\r
+       private String portno = "";\r
+       public void setIpaddr(String s) { ipaddr = s; }\r
+       public String getIpaddr() { return ipaddr; }\r
+       public void setPortno(String s) { portno = s; }\r
+       public String getPortno() { return portno; }\r
+       \r
+       private String ch_code = "";\r
+       public void setCh_code(String s) { ch_code = s; }\r
+       public String getCh_code() { return ch_code; }\r
+       \r
+       private ArrayList<String> starts = new ArrayList<String>();\r
+       private ArrayList<String> ends = new ArrayList<String>();\r
+       private ArrayList<String> recIds = new ArrayList<String>();\r
+       public void setStarts(ArrayList<String> a) { starts = a; }\r
+       public ArrayList<String> getStarts() { return starts; }\r
+       public void setEnds(ArrayList<String> a) { ends = a; }\r
+       public ArrayList<String> getEnds() { return ends; }\r
+       public void setRecIds(ArrayList<String> a) { recIds = a; }\r
+       public ArrayList<String> getRecIds() { return recIds; }\r
+       \r
+       private String version = "";\r
+       public void setVersion(String s) { version = s; }\r
+       public String getVersion() { return version; }\r
+       \r
+       // 鯛ナビのリクエストから生成\r
+       public ReserveInfo(String pstr) {\r
+               \r
+               this.setId(null);\r
+               \r
+               Matcher ma = Pattern.compile("(.+?)=(.+?)(&|$)").matcher(pstr);\r
+               while (ma.find()) {\r
+                       try {\r
+                               String key = ma.group(1);\r
+                               String val = URLDecoder.decode(ma.group(2), "UTF-8");\r
+                               \r
+                               if (key.equals("ver")) {\r
+                                       this.setVersion(val);\r
+                               }\r
+                               else if (key.equals("id")) {\r
+                                       this.setId(val);\r
+                               }\r
+                               else if (key.equals("exec")) {\r
+                                       this.setExec(val.equals("ON"));\r
+                               }\r
+                               else if (key.equals("title")) {\r
+                                       //System.out.println(ma.group(2)+ "->" +val);\r
+                                       this.setTitle(val);\r
+                               }\r
+                               else if (key.equals("ch_code")) {\r
+                                       this.setCh_code(val);\r
+                               }\r
+                               else if (key.equals("channel")) {\r
+                                       this.setChannel(val);\r
+                               }\r
+                               else if (key.equals("pattern")) {\r
+                                       this.setRec_pattern(val);\r
+                               }\r
+                               else if (key.equals("ahh")) {\r
+                                       this.setAhh(val);\r
+                               }\r
+                               else if (key.equals("amm")) {\r
+                                       this.setAmm(val);\r
+                               }\r
+                               else if (key.equals("zhh")) {\r
+                                       this.setZhh(val);\r
+                               }\r
+                               else if (key.equals("zmm")) {\r
+                                       this.setZmm(val);\r
+                               }\r
+                               else if (key.equals("tuner")) {\r
+                                       this.setTuner(val);\r
+                               }\r
+                               else if (key.equals("video")) {\r
+                                       this.setRec_mode(val);\r
+                               }\r
+                               else if (key.equals("audio")) {\r
+                                       this.setRec_audio(val);\r
+                               }\r
+                               else if (key.equals("disc")) {\r
+                                       this.setRec_device(val);\r
+                               }\r
+                               else if (key.equals("dvdr")) {\r
+                                       this.setRec_dvdcompat(val);\r
+                               }\r
+                               else if (key.equals("xchapter")) {\r
+                                       this.setRec_xchapter(val);\r
+                               }\r
+                               else if (key.equals("mvchapter")) {\r
+                                       this.setRec_mvchapter(val);\r
+                               }\r
+                               else if (key.equals("mschapter")) {\r
+                                       this.setRec_mschapter(val);\r
+                               }\r
+                               else if (key.equals("auto_delete")) {\r
+                                       this.setRec_autodel(val);\r
+                               }\r
+                               else if (key.equals("lVoice")) {\r
+                                       this.setRec_lvoice(val);\r
+                               }\r
+                               else if (key.equals("edge_left")) {\r
+                                       this.setRec_aspect(val);\r
+                               }\r
+                               else if (key.equals("autocomp")) {\r
+                                       this.setAutocomplete(val.equals("ON"));\r
+                               }\r
+                       } catch (UnsupportedEncodingException e) {\r
+                               // TODO Auto-generated catch block\r
+                               e.printStackTrace();\r
+                       }\r
+               }\r
+               if (this.getRec_mode() != null && (this.getRec_mode().startsWith("[TS") || this.getRec_mode().startsWith("[DR]") || this.getRec_mode().startsWith("[AVC] "))) {\r
+                       this.setRec_audio("  ");\r
+               }\r
+       }\r
+       \r
+       // 親クラスからコピー\r
+       public ReserveInfo(ReserveList o) {\r
+               \r
+               //this.setId(o.getId());        // 独自のIDをふる\r
+               this.setRec_pattern(o.getRec_pattern());\r
+               this.setRec_pattern_id(o.getRec_pattern_id());\r
+               this.setRec_nextdate(o.getRec_nextdate());\r
+               this.setAhh(o.getAhh());\r
+               this.setAmm(o.getAmm());\r
+               this.setZhh(o.getZhh());\r
+               this.setZmm(o.getZmm());\r
+               this.setRec_min(o.getRec_min());\r
+               this.setTuner(o.getTuner());\r
+               this.setRec_mode(o.getRec_mode());\r
+               this.setTitle(o.getTitle());\r
+               //this.setTitlePop(o.getTitlePop());\r
+               this.setChannel(o.getChannel());\r
+               //this.setCh_name(o.getCh_name());\r
+\r
+               //this.setDetail(o.getDetail());\r
+\r
+               this.setRec_audio(o.getRec_audio());\r
+               //this.setRec_folder(o.getRec_folder());\r
+               //this.setRec_genre(o.getRec_genre());\r
+               //this.setRec_dvdcompat(o.getRec_dvdcompat());\r
+               this.setRec_device(o.getRec_device());\r
+               \r
+               this.setRec_xchapter(o.getRec_xchapter());\r
+               this.setRec_mvchapter(o.getRec_mvchapter());\r
+               this.setRec_mschapter(o.getRec_mschapter());\r
+\r
+               this.setRec_autodel(o.getRec_autodel());\r
+               this.setRec_lvoice(o.getRec_lvoice());\r
+               this.setRec_aspect(o.getRec_aspect());\r
+\r
+               this.setStartDateTime(o.getStartDateTime());\r
+               this.setEndDateTime(o.getEndDateTime());\r
+               \r
+               this.setExec(o.getExec());\r
+               this.setPursues(o.getPursues());\r
+               this.setAutocomplete(o.getAutocomplete());\r
+               \r
+               // 独立項目\r
+               this.starts.add(o.getStartDateTime());\r
+               this.ends.add(o.getEndDateTime());\r
+               this.recIds.add(o.getId());\r
+       }\r
+\r
+       public ReserveInfo() {\r
+       }\r
+       \r
+       \r
+       // 情報のファイルの読み込みと書き出し\r
+       private static String saveFile = "env/taiSyncRsvInfo.xml";\r
+       \r
+       public static ArrayList<ReserveInfo> load() {\r
+               File f = new File(saveFile);\r
+               if (f.exists()) {\r
+            XMLDecoder dec;\r
+                       try {\r
+                               dec = new XMLDecoder(new BufferedInputStream(new FileInputStream(saveFile)));\r
+                   ArrayList<ReserveInfo> a = (ArrayList<ReserveInfo>)dec.readObject();\r
+                   dec.close();\r
+                       return(a);\r
+               } catch(Exception e) {\r
+                       System.out.println("Exception: load recorder="+saveFile+"("+e.toString()+")");\r
+               }\r
+               }\r
+        return(new ArrayList<ReserveInfo>());\r
+       }\r
+       \r
+       public static void save(ArrayList<ReserveInfo> a) {\r
+        try {\r
+            XMLEncoder enc = new XMLEncoder(new BufferedOutputStream(new FileOutputStream(saveFile)));\r
+            enc.writeObject(a);\r
+            enc.close();\r
+        } catch(FileNotFoundException e) {\r
+               System.out.println("Exception: save recorder="+saveFile);\r
+        }\r
+       }\r
+       \r
+       public static void showReserveInfo(ArrayList<ReserveInfo> a, String ipaddr) {\r
+               // ソートする\r
+               ArrayList<ReserveInfo> b = new ArrayList<ReserveInfo>();\r
+               for (int i=0; i<a.size(); i++) {\r
+                       boolean f = true;\r
+                       // 自分の分だけ\r
+                       if (ipaddr != null && ! a.get(i).ipaddr.equals(ipaddr)) {\r
+                               continue;\r
+                       }\r
+                       for (int j=0; j<b.size(); j++) {\r
+                               if (b.get(j).getStartDateTime().compareTo(a.get(i).getStartDateTime()) > 0) {\r
+                                       b.add(j, a.get(i));\r
+                                       f = false;\r
+                                       break;\r
+                               }\r
+                       }\r
+                       if (f) {\r
+                               b.add(a.get(i));\r
+                       }\r
+               }\r
+               \r
+               // 出力する\r
+               System.err.println("---Reserved List Start---");\r
+               for ( int i = 0; i<b.size(); i++ ) {\r
+                       // 詳細情報の取得\r
+                       ReserveInfo e = b.get(i);\r
+                       \r
+                       int wlen = 0;\r
+                       String ptn = e.getRec_pattern();\r
+                       Matcher ma = Pattern.compile("[^ -~。-゚]").matcher(ptn);\r
+                       while (ma.find()) {\r
+                               wlen++;\r
+                       }\r
+                       int ptnlen = 14-wlen;\r
+                       ptn = String.format(String.format("%%-%ds",ptnlen),ptn);\r
+                       \r
+                       System.err.println(String.format("[%3d] <%s> (%3s) (%2s)%s    %s-%s %sm    %-12s    %-8s    %s",\r
+                                       (i+1), (e.getByTainavi())?("鯛"):("本"), e.getId(), e.getRec_pattern_id(), ptn, e.getStartDateTime(), e.getEndDateTime(), e.getRec_min(), e.getRec_mode(), e.getChannel(), e.getTitle()));\r
+                       for (int j=0; j<e.getStarts().size(); j++) {\r
+                               System.err.println(String.format("                                +(%3s) %s-%s",\r
+                                               e.getRecIds().get(j), e.getStarts().get(j), e.getEnds().get(j)));\r
+                               \r
+                       }\r
+               }\r
+               System.err.println("---Reserved List End---");\r
+       }\r
+}\r
diff --git a/TinyBannavi/src/taiSync/SMTPServer.java b/TinyBannavi/src/taiSync/SMTPServer.java
new file mode 100644 (file)
index 0000000..d90d1ae
--- /dev/null
@@ -0,0 +1,249 @@
+package taiSync;\r
+\r
+import java.io.BufferedReader;\r
+import java.io.BufferedWriter;\r
+import java.io.File;\r
+import java.io.FileInputStream;\r
+import java.io.IOException;\r
+import java.io.InputStream;\r
+import java.io.InputStreamReader;\r
+import java.io.OutputStream;\r
+import java.io.OutputStreamWriter;\r
+import java.net.ServerSocket;\r
+import java.net.Socket;\r
+import java.util.ArrayList;\r
+import java.util.Calendar;\r
+import java.util.Date;\r
+import java.util.GregorianCalendar;\r
+import java.util.regex.Matcher;\r
+import java.util.regex.Pattern;\r
+\r
+import tainavi.RecorderList;\r
+import tainavi.TextValueSet;\r
+\r
+public class SMTPServer extends Thread {\r
+       \r
+       private ServerSocket svsock = null;\r
+       private ArrayList<RecorderInfo> recInfo = null;\r
+       private ArrayList<ReserveInfo> rsvInfo = null;\r
+       private ReserveCtrl rCtrl = null;\r
+       private boolean fault = false;\r
+       \r
+       \r
+       // コンストラクタ\r
+       public SMTPServer(ServerSocket sock, ArrayList<RecorderInfo> recInfo, ArrayList<ReserveInfo> rsvInfo) {\r
+               this.svsock = sock;\r
+               this.rCtrl = new ReserveCtrl();\r
+               \r
+               this.recInfo = recInfo;\r
+               this.rsvInfo = rsvInfo;\r
+               \r
+               this.start();\r
+       }\r
+       \r
+       \r
+       //\r
+       public boolean isFault() { return fault; }\r
+       public void clearFault() { this.fault=false; }\r
+       \r
+       // スレッド\r
+       @Override\r
+       public void run() {\r
+               Socket sock = null;\r
+               \r
+               while (true) {\r
+                       try {\r
+                               sock = this.svsock.accept();\r
+                       } catch (IOException e) {\r
+                               e.printStackTrace();\r
+                       }\r
+                       \r
+                       synchronized(rsvInfo) {\r
+                               try {\r
+                                       recvRequest(sock);\r
+                               } catch (IOException e) {\r
+                                       e.printStackTrace();\r
+                               }\r
+                               \r
+                               try {\r
+                                       sock.close();\r
+                               } catch (IOException e) {\r
+                                       e.printStackTrace();\r
+                               }\r
+                       }\r
+               }\r
+       }\r
+       \r
+       //private ArrayList<String> reqQueue = new ArrayList<String>();\r
+       //private RecorderInfo curRec = null;\r
+       \r
+       //\r
+       private void recvRequest(Socket sock) throws IOException {\r
+               \r
+               String ipaddr = sock.getInetAddress().getHostAddress().toString();\r
+               \r
+               Date dt = new Date();\r
+               System.out.println("RD["+ipaddr+"]からのSMTP接続がありました("+dt.toString()+")");\r
+               \r
+               OutputStream w = sock.getOutputStream(); \r
+               InputStream r = sock.getInputStream();\r
+               BufferedWriter out = new BufferedWriter(new OutputStreamWriter(w,"MS932"));\r
+               BufferedReader in = new BufferedReader(new InputStreamReader(r,"ISO2022JP"));\r
+               \r
+               // RDからリクエストを受け取る\r
+               String cmd = null;\r
+               String res = null;\r
+\r
+               // 接続OK\r
+               String myAddr = sock.getLocalAddress().getHostAddress().toString();\r
+               res = "220 TaiSync ESMTP\r\n";\r
+               out.write(res);\r
+               out.flush();\r
+               System.err.print(res);\r
+\r
+               while ((cmd = in.readLine()) != null) {\r
+                       // コマンド\r
+                       System.err.println(cmd);\r
+                       \r
+                       // リターン\r
+                       if (cmd.indexOf("HELO ") == 0) {\r
+                               res = "250 TaiSync.com\r\n";\r
+                       }\r
+                       else if (cmd.indexOf("MAIL FROM:") == 0) {\r
+                               res = "250 OK\r\n";\r
+                       }\r
+                       else if (cmd.indexOf("RCPT TO:") == 0) {\r
+                               res = "250 OK\r\n";\r
+                       }\r
+                       else if (cmd.indexOf("DATA") == 0) {\r
+                               res = "354 End data with .\r\n";\r
+                       }\r
+                       else if (cmd.indexOf("QUIT") == 0) {\r
+                               res = "221\r\n";\r
+                       }\r
+                       else {\r
+                               res = "250\r\n";\r
+                       }\r
+                       \r
+                       out.write(res);\r
+                       out.flush();\r
+                       System.err.print(res);\r
+                       \r
+                       if (cmd.indexOf("DATA") == 0) {\r
+                               ArrayList<String> sa = new ArrayList<String>();\r
+                               String dat = null;\r
+                               while ((dat = in.readLine()) != null) {\r
+                                       sa.add(dat);\r
+                                       System.err.println(dat);\r
+                                       if (dat.equals(".")) {\r
+                                               break;\r
+                                       }\r
+                               }\r
+                               \r
+                               // 予約情報の更新\r
+                               updRsvInfo(ipaddr, sa);\r
+                               \r
+                               res = "250 OK\r\n";\r
+                               out.write(res);\r
+                               out.flush();\r
+                               System.err.print(res);\r
+                       }\r
+                       \r
+                       // 終了\r
+                       if (cmd.equals("QUIT")) {\r
+                               break;\r
+                       }\r
+               }\r
+               \r
+               out.close();\r
+               in.close();\r
+           \r
+               System.out.println("RDからのSMTP接続を切断しました");\r
+       }\r
+\r
+       //\r
+       private void updRsvInfo(String ipaddr, ArrayList<String> sa) {\r
+               // SMTP本文から予約情報を取得\r
+               GregorianCalendar c = new GregorianCalendar();\r
+               Matcher ma = null;\r
+               String start = null;\r
+               String end = null;\r
+               String channel = null;\r
+               String tit = null;\r
+               String id = null;\r
+               String delId = null;\r
+               for (String dat : sa) {\r
+                       if (dat != null) {\r
+                               ma = Pattern.compile("^録画日 (\\d\\d\\d\\d)/(\\d\\d)/(\\d\\d)").matcher(dat);\r
+                               if (ma.find()) {\r
+                                       c.set(Calendar.YEAR, Integer.valueOf(ma.group(1)));\r
+                                       c.set(Calendar.MONTH, Integer.valueOf(ma.group(2))-1);\r
+                                       c.set(Calendar.DAY_OF_MONTH, Integer.valueOf(ma.group(3)));\r
+                                       dat = null;\r
+                               }\r
+                       }\r
+                       if (dat != null) {\r
+                               ma = Pattern.compile("^録画開始時刻 (\\d\\d:\\d\\d)").matcher(dat);\r
+                               if (ma.find()) {\r
+                                       start = ma.group(1);\r
+                                       dat = null;\r
+                               }\r
+                       }\r
+                       if (dat != null) {\r
+                               ma = Pattern.compile("^録画終了時刻 (\\d\\d:\\d\\d)").matcher(dat);\r
+                               if (ma.find()) {\r
+                                       dat = null;\r
+                                       \r
+                                       start = String.format("%04d/%02d/%02d %s", c.get(Calendar.YEAR), c.get(Calendar.MONTH)+1, c.get(Calendar.DAY_OF_MONTH), start);\r
+                                       end = String.format("%04d/%02d/%02d %s", c.get(Calendar.YEAR), c.get(Calendar.MONTH)+1, c.get(Calendar.DAY_OF_MONTH), ma.group(1));\r
+                                       if (start.compareTo(end) > 0) {\r
+                                               c.add(Calendar.DAY_OF_MONTH,1);\r
+                                               end = String.format("%04d/%02d/%02d %s", c.get(Calendar.YEAR), c.get(Calendar.MONTH)+1, c.get(Calendar.DAY_OF_MONTH), ma.group(1));\r
+                                       }\r
+                               }\r
+                       }\r
+                       if (dat != null) {\r
+                               ma = Pattern.compile("^チャンネル (.+?)$").matcher(dat);\r
+                               if (ma.find()) {\r
+                                       channel = ma.group(1);\r
+                                       dat = null;\r
+                               }\r
+                       }\r
+                       if (dat != null) {\r
+                               ma = Pattern.compile("%20del%20(.+?)$").matcher(dat);\r
+                               if (ma.find()) {\r
+                                       id = ma.group(1);\r
+                                       dat = null;\r
+                               }\r
+                       }\r
+                       if (dat != null) {\r
+                               ma = Pattern.compile("^予約を削除しました。予約ID:\\[(.+?)\\]$").matcher(dat);\r
+                               if (ma.find()) {\r
+                                       delId = ma.group(1);\r
+                                       dat = null;\r
+                               }\r
+                       }\r
+                       if (id == null) {\r
+                               tit = dat;\r
+                       }\r
+               }\r
+               \r
+               //System.out.println(start+", "+end+", "+channel+", "+id);\r
+               if (start != null && end != null && channel != null && tit != null && id != null) {\r
+                       // これは予約完了通知\r
+                       rCtrl.addRsvId(ipaddr, rsvInfo, start, end, channel, tit, id);\r
+                       fault = false;\r
+               }\r
+               else if (delId != null) {\r
+                       // これは削除完了通知\r
+                       rCtrl.delRsvId(ipaddr, rsvInfo, delId);\r
+                       fault = false;\r
+               }\r
+               else {\r
+                       // 予約完了通知でも削除完了通知でもない\r
+                       System.out.println("【警告】リクエストが受け付けられませんでした");\r
+                       fault = true;\r
+               }\r
+       }\r
+       \r
+}\r
diff --git a/TinyBannavi/src/taiSync/VersionInfo.java b/TinyBannavi/src/taiSync/VersionInfo.java
new file mode 100644 (file)
index 0000000..a1a4c88
--- /dev/null
@@ -0,0 +1,9 @@
+package taiSync;\r
+\r
+public class VersionInfo {\r
+       private static final String Version = "タイニーシンク 1.5.12";\r
+       \r
+       public static String getVersion() {\r
+               return(Version);\r
+       }\r
+}\r
diff --git a/TinyBannavi/src/taiSync/Viewer.java b/TinyBannavi/src/taiSync/Viewer.java
new file mode 100644 (file)
index 0000000..ff9a9a3
--- /dev/null
@@ -0,0 +1,638 @@
+package taiSync;\r
+\r
+import javax.imageio.ImageIO;\r
+import javax.swing.BoxLayout;\r
+import javax.swing.DefaultCellEditor;\r
+import javax.swing.JCheckBox;\r
+import javax.swing.JComponent;\r
+import javax.swing.JFrame;\r
+import javax.swing.JLabel;\r
+import javax.swing.JOptionPane;\r
+import javax.swing.JPasswordField;\r
+import javax.swing.JScrollPane;\r
+import javax.swing.JSlider;\r
+import javax.swing.JTable;\r
+import javax.swing.ListSelectionModel;\r
+import javax.swing.ScrollPaneConstants;\r
+import javax.swing.SwingUtilities;\r
+import javax.swing.JPanel;\r
+import javax.swing.JButton;\r
+import javax.swing.Timer;\r
+\r
+import java.io.File;\r
+import java.io.IOException;\r
+import java.net.InetAddress;\r
+import java.net.ServerSocket;\r
+import java.net.URI;\r
+import java.net.URISyntaxException;\r
+import java.net.UnknownHostException;\r
+import java.util.ArrayList;\r
+import java.util.Date;\r
+import java.awt.AWTException;\r
+import java.awt.Component;\r
+import java.awt.Desktop;\r
+import java.awt.Dimension;\r
+import java.awt.Image;\r
+import java.awt.MenuItem;\r
+import java.awt.PopupMenu;\r
+import java.awt.SystemTray;\r
+import java.awt.TrayIcon;\r
+import java.awt.TrayIcon.MessageType;\r
+import java.awt.event.ActionEvent;\r
+import java.awt.event.ActionListener;\r
+import java.awt.event.WindowAdapter;\r
+import java.awt.event.WindowEvent;\r
+\r
+import javax.swing.event.ChangeEvent;\r
+import javax.swing.event.ChangeListener;\r
+import javax.swing.table.DefaultTableCellRenderer;\r
+import javax.swing.table.DefaultTableColumnModel;\r
+import javax.swing.table.DefaultTableModel;\r
+import javax.swing.table.TableCellEditor;\r
+import javax.swing.table.TableCellRenderer;\r
+import javax.swing.table.TableColumn;\r
+\r
+import tainavi.DebugPrintStream;\r
+import tainavi.LogViewer;\r
+\r
+\r
+\r
+public class Viewer extends JFrame {\r
+\r
+       private static final long serialVersionUID = 1L;\r
+       \r
+       private static Env env = null;\r
+       \r
+       private static String iconf = "icon/taisync.png";\r
+       private static String iconf_alert = "icon/taisync-alert.png";\r
+       private static String iconf_alarm = "icon/taisync-alarm.png";\r
+       \r
+       private JPanel jContentPane = null;\r
+       private JScrollPane jScrollPane = null;\r
+       private JTable jTable = null;\r
+       private JCheckBoxPanel jCBPanel_DebugMode = null;\r
+       private JCheckBoxPanel jCBPanel_AppendDT = null;\r
+       private JSliderPanel jSPanel_Period = null;\r
+       private JSliderPanel jSPanel_Keeping = null;\r
+       private JSliderPanel jSPanel_KeepingCheckInterval = null;\r
+       private JSliderPanel jSPanel_PopPort = null;\r
+       private JSliderPanel jSPanel_SmtpPort = null;\r
+       private JButton jButton = null;\r
+\r
+       /**\r
+        *      \r
+        */\r
+       private JScrollPane getJScrollPane() {\r
+               if (jScrollPane == null) {\r
+                       jScrollPane = new JScrollPane();\r
+                       jScrollPane.setVerticalScrollBarPolicy(ScrollPaneConstants.VERTICAL_SCROLLBAR_NEVER);\r
+                       jScrollPane.setHorizontalScrollBarPolicy(ScrollPaneConstants.HORIZONTAL_SCROLLBAR_NEVER);\r
+                       jScrollPane.setViewportView(getJTable());\r
+                       \r
+                       Dimension dh = getJTable().getTableHeader().getPreferredSize();\r
+                       Dimension db = getJTable().getPreferredSize();\r
+                       jScrollPane.setPreferredSize(new Dimension(dh.width,dh.height+db.height));\r
+               }\r
+               return(jScrollPane);\r
+       }\r
+       \r
+       private JTable getJTable() {\r
+               if (jTable == null) {\r
+                       String[] colname = {"IP", "PORT", "RDPASS", "ID", "PASS", "WAIT"};\r
+                       int[] colwidth = {100, 50, 100, 100, 100, 50};\r
+                       \r
+                       DefaultTableModel model = new DefaultTableModel(colname, 5);\r
+                       jTable = new JTable(model);\r
+                       jTable.setAutoResizeMode(JTable.AUTO_RESIZE_OFF);\r
+                       jTable.setSelectionMode(ListSelectionModel.SINGLE_SELECTION);\r
+                       \r
+               DefaultTableColumnModel columnModel = (DefaultTableColumnModel)jTable.getColumnModel();\r
+               for (int i = 0 ; i < columnModel.getColumnCount() ; i++){\r
+                       TableColumn column = columnModel.getColumn(i);\r
+                       column.setPreferredWidth(colwidth[i]);\r
+               }\r
+               \r
+               JPasswordField password = new JPasswordField();\r
+               TableCellEditor editor = new DefaultCellEditor( password );\r
+               jTable.getColumn("PASS").setCellEditor( editor );\r
+               TableCellRenderer renderer = new DefaultTableCellRenderer() {\r
+\r
+                               private static final long serialVersionUID = 1L;\r
+\r
+                               @Override\r
+                       protected void setValue(Object value) {\r
+                               if (value != null && ((String)value).length() > 0) {\r
+                                       setText( "●●●●●●●●" );\r
+                               }\r
+                               else {\r
+                                       setText( "" );\r
+                               }\r
+                       }\r
+               };\r
+               jTable.getColumn("PASS").setCellRenderer( renderer );\r
+               \r
+               ArrayList<RecorderInfo> tmpInfo = RecorderInfo.load();  // まあ、いいか…\r
+               int row = 0;\r
+               for (RecorderInfo rec : tmpInfo) {\r
+                       jTable.setValueAt(rec.getRecorderIPAddr(), row, 0);\r
+                       jTable.setValueAt(rec.getRecorderPortNo(), row, 1);\r
+                       jTable.setValueAt(rec.getRecorderBroadcast(), row, 2);\r
+                       jTable.setValueAt(rec.getRecorderUser(), row, 3);\r
+                       jTable.setValueAt(rec.getRecorderPasswd(), row, 4);\r
+                       jTable.setValueAt(String.valueOf(rec.getLocalPort()), row, 5);\r
+                       ++row;\r
+               }\r
+               }\r
+               return jTable;\r
+       }\r
+\r
+       private JButton getJButton() {\r
+               if (jButton == null) {\r
+                       jButton = new JButton("設定");\r
+                       \r
+                       jButton.addActionListener(new ActionListener() {\r
+                               \r
+                               @Override\r
+                               public void actionPerformed(ActionEvent e) {\r
+                                       jButton.setEnabled(false);\r
+                                       \r
+                                       ArrayList<RecorderInfo> rl = new ArrayList<RecorderInfo>();\r
+                                       for (int row=0; row<jTable.getRowCount(); row++) {\r
+                                               RecorderInfo rec = new RecorderInfo();\r
+                                               int cnt = 0;\r
+                                               for (int col=0; col<jTable.getColumnCount(); col++) {\r
+                                                       if ( jTable.getValueAt(row, col) != null && ! jTable.getValueAt(row, col).equals("")) {\r
+                                                               ++cnt;\r
+                                                       }\r
+                                               }\r
+                                               if (cnt == 0) {\r
+                                                       continue;\r
+                                               }\r
+                                               if (cnt != jTable.getColumnCount()) {\r
+                                                       JOptionPane.showMessageDialog(null, "枠は全部埋めてください");\r
+                                                       jButton.setEnabled(true);\r
+                                                       return;\r
+                                               }\r
+                                               try {\r
+                                                       rec.setRecorderIPAddr((String) jTable.getValueAt(row, 0));\r
+                                                       InetAddress.getByName(rec.getRecorderIPAddr());\r
+                                                       \r
+                                                       rec.setRecorderPortNo((String) jTable.getValueAt(row, 1));\r
+                                                       Integer.valueOf(rec.getRecorderPortNo());\r
+                                                       \r
+                                                       rec.setRecorderBroadcast((String) jTable.getValueAt(row, 2));\r
+                                                       \r
+                                                       rec.setRecorderUser((String) jTable.getValueAt(row, 3));\r
+                                                       \r
+                                                       rec.setRecorderPasswd((String) jTable.getValueAt(row, 4));\r
+                                                       \r
+                                                       rec.setLocalPort(Integer.valueOf((String) jTable.getValueAt(row, 5)));\r
+                                                       \r
+                                                       rl.add(rec);\r
+                                                       \r
+                                               } catch (UnknownHostException e1) {\r
+                                                       JOptionPane.showMessageDialog(null, "IPが不正です");\r
+                                                       jButton.setEnabled(true);\r
+                                                       return;\r
+                                               } catch (NumberFormatException e1) {\r
+                                                       JOptionPane.showMessageDialog(null, "数値の形式が不正です");\r
+                                                       jButton.setEnabled(true);\r
+                                                       return;\r
+                                               }\r
+                                       }\r
+                                       \r
+                                       RecorderInfo.save(rl);\r
+                                       \r
+                                       //\r
+                                       Env n = new Env();\r
+                                       n.setDebug(jCBPanel_DebugMode.isSelected());\r
+                                       n.setAppendDT(jCBPanel_AppendDT.isSelected());\r
+                                       n.setPeriod(jSPanel_Period.getValue());\r
+                                       n.setKeeping(jSPanel_Keeping.getValue());\r
+                                       n.setKeepingCheckInterval(jSPanel_KeepingCheckInterval.getValue());\r
+                                       n.setPopPort(jSPanel_PopPort.getValue());\r
+                                       n.setSmtpPort(jSPanel_SmtpPort.getValue());\r
+                                       n.save();\r
+                                       \r
+                                       //\r
+                                       JOptionPane.showMessageDialog(null, "設定を保存したので再起動して下さい");\r
+                                       return;\r
+                               }\r
+                       });\r
+               }\r
+               return jButton;\r
+       }\r
+\r
+       /**\r
+        * @param args\r
+        */\r
+       \r
+       private static final String logfile = "log_taiSync.txt";\r
+       \r
+       public static void main(String[] args) {\r
+               \r
+               // \r
+               String serveraddr = null;\r
+               \r
+               // コマンドラインオプションを処理する\r
+               int flag = 0;\r
+               for (String arg : args) {\r
+                       switch (flag) {\r
+                       case 0:\r
+                               if (arg.compareTo("-serveraddr") == 0) {\r
+                                       flag = 1;\r
+                               }\r
+                               break;\r
+                       case 1:\r
+                               serveraddr = arg;\r
+                               flag = 0;\r
+                               break;\r
+                       }\r
+               }\r
+               \r
+               // 環境設定をする\r
+               env = Env.load();\r
+               \r
+               // 標準出力・エラーをリダイレクトする\r
+               System.setOut(new DebugPrintStream(System.out,logfile,true));\r
+               System.setErr(new DebugPrintStream(System.err,logfile,env.getDebug()));\r
+               \r
+               // 起動ログ\r
+               System.out.println("タイニーシンクが起動しました。(VersionInfo:"+VersionInfo.getVersion()+")");\r
+               \r
+               // アイコンファイルはあるかな?\r
+               if ( ! new File(iconf).exists() || ! new File(iconf_alert).exists() || ! new File(iconf_alarm).exists()) {\r
+                       String msg = "アイコンファイルがみつかりません。タイニーシンクを終了します。";\r
+                       System.out.println(msg);\r
+                       JOptionPane.showConfirmDialog(null, msg, "タイニーシンク", JOptionPane.CLOSED_OPTION);\r
+                       return;\r
+               }\r
+               \r
+               // サーバースレッドを起動する\r
+               final ArrayList<ReserveInfo> rsvInfo = ReserveInfo.load();      // スレッド間同期用オブジェクトにするよ\r
+               final ArrayList<RecorderInfo> recInfo = RecorderInfo.load();\r
+               final ReserveCtrl rCtrl = new ReserveCtrl();\r
+               \r
+               ServerSocket sock = null;\r
+\r
+               // お知らせ\r
+               if (serveraddr != null) {\r
+                       System.out.println("POP/SMTP接続待ち受けポートをアドレス"+serveraddr+"にバインドします");\r
+               }\r
+               \r
+               // POPサーバを立ち上げる\r
+               try {\r
+                       System.out.println("RDからのPOP接続をポート"+env.getPopPort()+"で待ちます");\r
+                       if (serveraddr == null) {\r
+                               sock = new ServerSocket(env.getPopPort());\r
+                       }\r
+                       else {\r
+                               sock = new ServerSocket(env.getPopPort(), 0, InetAddress.getByName(serveraddr));\r
+                       }\r
+               } catch (IOException e) {\r
+                       e.printStackTrace();\r
+                       JOptionPane.showConfirmDialog(null, "POPポートが開けません。タイニーシンクを終了します。", "タイニーシンク", JOptionPane.CLOSED_OPTION);\r
+                       return;\r
+               }\r
+               final POPServer popserver = new POPServer(sock, recInfo, rsvInfo, env);\r
+               \r
+               // SMTPサーバを立ち上げる\r
+               try {\r
+                       System.out.println("RDからのSMTP接続をポート"+env.getSmtpPort()+"で待ちます");\r
+                       if (serveraddr == null) {\r
+                               sock = new ServerSocket(env.getSmtpPort());\r
+                       }\r
+                       else {\r
+                               sock = new ServerSocket(env.getSmtpPort(), 0, InetAddress.getByName(serveraddr));\r
+                       }\r
+               } catch (IOException e) {\r
+                       e.printStackTrace();\r
+                       JOptionPane.showConfirmDialog(null, "SMTPポートが開けません。タイニーシンクを終了します。", "タイニーシンク", JOptionPane.CLOSED_OPTION);\r
+                       return;\r
+               }\r
+               final SMTPServer smtpserver = new SMTPServer(sock, recInfo, rsvInfo);\r
+               \r
+               // HTTPサーバを立ち上げる\r
+               for (RecorderInfo rec : recInfo) {\r
+                       try {\r
+                               System.out.println("鯛ナビからのHTTP接続をポート"+rec.getLocalPort()+"で待ちます");\r
+                               sock = new ServerSocket(rec.getLocalPort());\r
+                       } catch (IOException e) {\r
+                               e.printStackTrace();\r
+                               JOptionPane.showConfirmDialog(null, "HTTPポートが開けません。タイニーシンクを終了します。", "タイニーシンク", JOptionPane.CLOSED_OPTION);\r
+                               return;\r
+                       }\r
+                       new HTTPServer(sock, rec, rsvInfo);\r
+               }\r
+               \r
+               // GUIを起動する\r
+               SwingUtilities.invokeLater(new Runnable() {\r
+                       public void run() {\r
+                               final Viewer thisClass = new Viewer();\r
+                               \r
+                               try {\r
+                                       final Image image = ImageIO.read(new File(iconf));\r
+                                       final Image image2 = ImageIO.read(new File(iconf_alert));\r
+                                       final Image image3 = ImageIO.read(new File(iconf_alarm));\r
+                                       thisClass.setIconImage(image);\r
+       \r
+                                       final boolean isSupportedSystemTray = SystemTray.isSupported();\r
+                                       final SystemTray tray = (isSupportedSystemTray)?(SystemTray.getSystemTray()):(null);\r
+                                       final TrayIcon icon = new TrayIcon(image,"taiSync");\r
+                                       \r
+                                       if (isSupportedSystemTray) {\r
+                                               // システムトレイが有効なら「閉じる」ボタンで隠れる\r
+                                               thisClass.setDefaultCloseOperation(JFrame.HIDE_ON_CLOSE);\r
+                                                       \r
+                                               PopupMenu popup = new PopupMenu();\r
+                                               {\r
+                                                       MenuItem item = new MenuItem("設定ウィンドウを開く");\r
+                                                       item.addActionListener(new ActionListener() {\r
+                                                               @Override\r
+                                                               public void actionPerformed(ActionEvent e) {\r
+                                                                       thisClass.setVisible(true);\r
+                                                               }\r
+                                                       });\r
+                                                       popup.add(item);\r
+                                               }\r
+                                               if (Desktop.isDesktopSupported()) {\r
+                                                       MenuItem item = new MenuItem("ログファイルを開く");\r
+                                                       item.addActionListener(new ActionListener() {\r
+                                                               @Override\r
+                                                               public void actionPerformed(ActionEvent e) {\r
+                                                                       LogViewer lv = new LogViewer(logfile);\r
+                                                                       lv.setVisible(true);\r
+                                                                       /*\r
+                                                                       Desktop desktop = Desktop.getDesktop();\r
+                                                                       try {\r
+                                                                               desktop.browse(new URI(logfile));\r
+                                                                       } catch (UnsupportedOperationException e1) {\r
+                                                                               e1.printStackTrace();\r
+                                                                       } catch (URISyntaxException e1) {\r
+                                                                               e1.printStackTrace();\r
+                                                                       } catch (IOException e1) {\r
+                                                                               e1.printStackTrace();\r
+                                                                       }\r
+                                                                       */\r
+                                                               }\r
+                                                       });\r
+                                                       popup.add(item);\r
+                                               }\r
+                                               {\r
+                                                       MenuItem item = new MenuItem("終了する");\r
+                                                       item.addActionListener(new ActionListener() {\r
+                                                               @Override\r
+                                                               public void actionPerformed(ActionEvent e) {\r
+                                                                       System.out.println("タイニーシンクを終了します");\r
+                                                                       // スレッド同期待ち\r
+                                                                       synchronized(rsvInfo) {\r
+                                                                               tray.remove(icon);\r
+                                                                               System.exit(0);\r
+                                                                       }\r
+                                                               }\r
+                                                       });\r
+                                                       popup.add(item);\r
+                                               }\r
+                                               icon.setPopupMenu(popup);\r
+                                               tray.add(icon);\r
+                                       }\r
+                                       else {\r
+                                               // システムトレイが無効なら「閉じる」ボタンで終了\r
+                                               thisClass.addWindowListener(new WindowAdapter() {\r
+                                                       @Override\r
+                                                       public void windowClosing(WindowEvent e) {\r
+                                                               System.out.println("タイニーシンクを終了します");\r
+                                                               // スレッド同期待ち\r
+                                                               synchronized(rsvInfo) {\r
+                                                                       System.exit(0);\r
+                                                               }\r
+                                                       }\r
+                                               });\r
+                                       }\r
+                                       \r
+                                       /*\r
+                                        *  定期的にRDとの同期状況を確認する\r
+                                        */\r
+                                       Timer timer = new Timer(1*1000, new ActionListener() {\r
+                                               private int iconMode = 1;\r
+                                               private boolean isTriggered = false;\r
+                                               private boolean isPopFaulted = false;\r
+                                               private Date prevRefreshed = null;\r
+                                               private Date prevChecked = null;\r
+                                               private Date curDate = null;\r
+                                               \r
+                                               @Override\r
+                                               public void actionPerformed(ActionEvent e) {\r
+                                                       \r
+                                                       // 定期的な予約リストのリフレッシュ(n時間に一回)\r
+                                                       if (curDate == null || (curDate.getTime()-prevRefreshed.getTime()) >= env.getKeepingCheckInterval()*3600*1000) {\r
+                                                               curDate = new Date();\r
+                                                               synchronized(rsvInfo) {\r
+                                                                       System.out.println("予約リストをリフレッシュします("+curDate.toString()+")");\r
+                                                                       rCtrl.refreshReserveStartEnd(null,rsvInfo);\r
+                                                                       isTriggered = rCtrl.isThereUnprocessing(null,rsvInfo,env.getKeeping());\r
+                                                                       if (isTriggered) {\r
+                                                                               String msg = "予約の滞留が発生しています";\r
+                                                                               System.out.println(msg);\r
+                                                                               if (isSupportedSystemTray) icon.displayMessage(null, msg, MessageType.WARNING);\r
+                                                                       }\r
+                                                               }\r
+                                                               prevRefreshed = (Date) curDate.clone();\r
+                                                               prevChecked = (Date) curDate.clone();\r
+                                                       }\r
+                                                       else {\r
+                                                               curDate = new Date();\r
+                                                       }\r
+                                                       \r
+                                                       //\r
+                                                       if (popserver.isFatal()) {\r
+                                                               // bindエラー\r
+                                                               System.out.println("【警告】タイニーシンクを強制終了します");\r
+                                                               if (isSupportedSystemTray) tray.remove(icon);\r
+                                                               synchronized(rsvInfo) {\r
+                                                                       System.exit(0);\r
+                                                               }\r
+                                                       }\r
+                                                       else if (popserver.isFault() || smtpserver.isFault()) {\r
+                                                               // RDがリクエストを受け付けない\r
+                                                               if (iconMode == 1) {\r
+                                                                       thisClass.setIconImage(image2);\r
+                                                                       if (isSupportedSystemTray) icon.setImage(image2);\r
+                                                                       iconMode = 2;\r
+                                                               }\r
+                                                               else {\r
+                                                                       thisClass.setIconImage(image);\r
+                                                                       if (isSupportedSystemTray) icon.setImage(image);\r
+                                                                       iconMode = 1;\r
+                                                               }\r
+                                                               //\r
+                                                               if ( ! isPopFaulted && ! popserver.isFault()) {\r
+                                                                       isPopFaulted = true;\r
+                                                                       if (isSupportedSystemTray) icon.displayMessage(null, "予約が受け付けられませんでした", MessageType.WARNING);\r
+                                                               }\r
+                                                       }\r
+                                                       else if (isTriggered){\r
+                                                               // RDと情報の同期がとれていない\r
+                                                               if (iconMode == 1) {\r
+                                                                       thisClass.setIconImage(image3);\r
+                                                                       if (isSupportedSystemTray) icon.setImage(image3);\r
+                                                                       iconMode = 2;\r
+                                                               }\r
+                                                               else {\r
+                                                                       thisClass.setIconImage(image);\r
+                                                                       if (isSupportedSystemTray) icon.setImage(image);\r
+                                                                       iconMode = 1;\r
+                                                               }\r
+\r
+                                                               // 予約エントリが全部はけたかどうか定期的に確認(30秒に一回)\r
+                                                               if ((curDate.getTime()-prevChecked.getTime()) >= 30*1000) {\r
+                                                                       synchronized(rsvInfo) {\r
+                                                                               //System.out.println("予約が滞留していないか確認します");\r
+                                                                               isTriggered = rCtrl.isThereUnprocessing(null,rsvInfo,env.getKeeping());\r
+                                                                               if ( ! isTriggered) {\r
+                                                                                       String msg = "予約の滞留が解消しました";\r
+                                                                                       System.out.println(msg);\r
+                                                                                       //if (isSupportedSystemTray) icon.displayMessage(null, msg, MessageType.INFO);\r
+                                                                               }\r
+                                                                       }\r
+                                                                       prevChecked = (Date) curDate.clone();\r
+                                                               }\r
+                                                       }\r
+                                                       else {\r
+                                                               // 普通の状態にもどった\r
+                                                               if (iconMode != 1) {\r
+                                                                       thisClass.setIconImage(image);\r
+                                                                       if (isSupportedSystemTray) icon.setImage(image);\r
+                                                                       iconMode = 1;\r
+                                                               }\r
+                                                               //\r
+                                                               isPopFaulted = true;\r
+                                                       }\r
+                                               }\r
+                                       });\r
+                                       \r
+                                       timer.start();\r
+                                       \r
+                               } catch (IOException e) {\r
+                                       e.printStackTrace();\r
+                               } catch (AWTException e) {\r
+                                       e.printStackTrace();\r
+                               }\r
+                               \r
+                               thisClass.setResizable(false);\r
+                               thisClass.setVisible(true);\r
+                               thisClass.pack();\r
+                       }\r
+               });\r
+       }\r
+\r
+       /**\r
+        * This is the default constructor\r
+        */\r
+       public Viewer() {\r
+               super();\r
+               initialize();\r
+       }\r
+\r
+       /**\r
+        * This method initializes this\r
+        * \r
+        * @return void\r
+        */\r
+       private void initialize() {\r
+               this.setTitle(VersionInfo.getVersion());\r
+               this.setContentPane(getJContentPane());\r
+       }\r
+\r
+       /**\r
+        * This method initializes jContentPane\r
+        * \r
+        * @return javax.swing.JPanel\r
+        */\r
+       private JPanel getJContentPane() {\r
+               if (jContentPane == null) {\r
+                       jContentPane = new JPanel();\r
+\r
+                       jContentPane.setLayout(new BoxLayout(jContentPane,BoxLayout.Y_AXIS));\r
+                       jContentPane.add(getJScrollPane());\r
+                       jContentPane.add(jSPanel_PopPort = new JSliderPanel("番のポートでRDからのPOP接続を待ちます", 1, 1024));\r
+                       jContentPane.add(jSPanel_SmtpPort = new JSliderPanel("番のポートでRDからのSMTP接続を待ちます", 1, 1024));\r
+                       jContentPane.add(jSPanel_KeepingCheckInterval = new JSliderPanel("時間ごとに未同期予約の滞留チェックを行います", 1, 24));\r
+                       jContentPane.add(jSPanel_Keeping = new JSliderPanel("日以内に開始する未同期の予約があると警告します", 1, 8));\r
+                       jContentPane.add(jSPanel_Period = new JSliderPanel("日先の予約まで自動登録します", 1, 8));\r
+                       jContentPane.add(jCBPanel_AppendDT = new JCheckBoxPanel("繰り返し予約の予約名の末尾に日付を付加します"));\r
+                       jContentPane.add(jCBPanel_DebugMode = new JCheckBoxPanel("デバッグログを出力します"));\r
+                       jContentPane.add(getJButton());\r
+\r
+                       // 配置の設定\r
+                       for (Component comp : jContentPane.getComponents()) {\r
+                               ((JComponent)comp).setAlignmentX(JComponent.LEFT_ALIGNMENT);\r
+                       }\r
+                       \r
+                       // 初期値の設定\r
+                       jCBPanel_DebugMode.setSelected(env.getDebug());\r
+                       jCBPanel_AppendDT.setSelected(env.getAppendDT());\r
+                       jSPanel_Period.setValue(env.getPeriod());\r
+                       jSPanel_Keeping.setValue(env.getKeeping());\r
+                       jSPanel_KeepingCheckInterval.setValue(env.getKeepingCheckInterval());\r
+                       jSPanel_PopPort.setValue(env.getPopPort());\r
+                       jSPanel_SmtpPort.setValue(env.getSmtpPort());\r
+               }\r
+               return jContentPane;\r
+       }\r
+       \r
+       \r
+       \r
+       // 独自のクラス\r
+       private class JCheckBoxPanel extends JPanel {\r
+\r
+               private static final long serialVersionUID = 1L;\r
+\r
+               private JCheckBox jcheckbox = null;\r
+               \r
+               public JCheckBoxPanel(String s) {\r
+                       this.setLayout(new BoxLayout(this,BoxLayout.LINE_AXIS));\r
+                       this.add(jcheckbox = new JCheckBox());\r
+                       this.add(new JLabel(s));\r
+               }\r
+               \r
+               public boolean isSelected() {\r
+                       return jcheckbox.isSelected();\r
+               }\r
+               public void setSelected(boolean b) {\r
+                       jcheckbox.setSelected(b);\r
+               }\r
+       }\r
+       \r
+       private class JSliderPanel extends JPanel {\r
+               private JSlider jslider = null;\r
+               private JLabel jlabel = null;\r
+               private String lstr = null;\r
+               \r
+               public JSliderPanel(String s, int min, int max) {\r
+                       this.setLayout(new BoxLayout(this,BoxLayout.LINE_AXIS));\r
+                       this.add(jslider = new JSlider(min,max));\r
+                       this.add(jlabel = new JLabel(lstr = s));\r
+                       \r
+                       // スライダーを短くする\r
+                       Dimension d = jslider.getPreferredSize();\r
+                       d.width = 100;\r
+                       jslider.setMaximumSize(d);\r
+                       \r
+                       jslider.addChangeListener(new ChangeListener() {\r
+                               @Override\r
+                               public void stateChanged(ChangeEvent e) {\r
+                                       jlabel.setText(String.format("%d %s",jslider.getValue(),lstr));\r
+                               }\r
+                       });\r
+               }\r
+               \r
+               public int getValue() {\r
+                       return jslider.getValue();\r
+               }\r
+               public void setValue(int n) {\r
+                       jslider.setValue(n);\r
+               }\r
+       }\r
+\r
+}  //  @jve:decl-index=0:visual-constraint="10,10"\r
diff --git a/TinyBannavi/src/tainavi/AVSetting.java b/TinyBannavi/src/tainavi/AVSetting.java
new file mode 100644 (file)
index 0000000..e12d6ff
--- /dev/null
@@ -0,0 +1,63 @@
+package tainavi;\r
+\r
+import java.io.File;\r
+import java.util.ArrayList;\r
+\r
+public class AVSetting {\r
+       \r
+       //\r
+       protected String avsettingFile = "";\r
+       protected ArrayList<AVs> avs = new ArrayList<AVs>();\r
+       \r
+       // コンストラクタ\r
+       public AVSetting() {\r
+               avsettingFile = "env"+File.separator+"avsetting.xml";\r
+       }\r
+       \r
+       // 公開メソッド\r
+       public void save() {\r
+       System.out.println("画質・音質既定値設定を保存します: "+avsettingFile);\r
+       if ( ! CommonUtils.writeXML(avsettingFile, avs) ) {\r
+               System.err.println("画質・音質既定値設定の保存に失敗しました: "+avsettingFile);\r
+       }\r
+       }\r
+       \r
+       public void load() {\r
+       if ( ! new File(avsettingFile).exists()) {\r
+               System.out.println("画質・音質既定値設定はありません: "+avsettingFile);\r
+               return;\r
+       }\r
+       \r
+       System.out.println("画質・音質既定値設定を読み込みます: "+avsettingFile);\r
+       \r
+       @SuppressWarnings("unchecked")\r
+               ArrayList<AVs> tmp = (ArrayList<AVs>) CommonUtils.readXML(avsettingFile);\r
+       if ( tmp == null ) {\r
+               System.out.println("画質・音質既定値設定が読み込めなかったので登録なしで起動します.");\r
+               return;\r
+       }\r
+       \r
+       avs = tmp;\r
+       }\r
+       \r
+       public AVs get(String key_recorderId, String key_genre) {\r
+               for ( AVs a : avs ) {\r
+                       if ( a.getRecorderId().equals(key_recorderId) ) {\r
+                               if ( (a.getGenre() != null && a.getGenre().equals(key_genre)) ||\r
+                                               (a.getGenre() == null && key_genre == null)) {\r
+                                       return(a);\r
+                               }\r
+                       }\r
+               }\r
+               return(null);\r
+       }\r
+       \r
+       public void add(String key_recorderId, String key_genre, AVs c) {\r
+               \r
+               AVs a;\r
+               while ( (a = this.get(key_recorderId,key_genre)) != null ) {\r
+                       avs.remove(a);\r
+               }\r
+               avs.add(c);\r
+       }\r
+}\r
diff --git a/TinyBannavi/src/tainavi/AVs.java b/TinyBannavi/src/tainavi/AVs.java
new file mode 100644 (file)
index 0000000..b5915fb
--- /dev/null
@@ -0,0 +1,58 @@
+package tainavi;\r
+\r
+public class AVs {\r
+       private String recorderId = "";\r
+       private String genre = "";\r
+       private String videorate = "";\r
+       private String audiorate = "";\r
+       private String dvdcompat = "";\r
+       \r
+       private String device = "";\r
+       private String xchapter = "";\r
+       private String mschapter = "";\r
+       private String mvchapter = "";\r
+       \r
+       private String aspect = "";\r
+       private String bvperf = "";\r
+       private String lvoice = "";\r
+       private String autodel = "";\r
+       \r
+       private String folder = "";\r
+       \r
+       private Boolean pursues = true;\r
+       \r
+       public void setRecorderId(String s) { recorderId = s; }\r
+       public String getRecorderId() { return recorderId; }\r
+       public void setGenre(String s) { genre = s; }\r
+       public String getGenre() { return genre; }\r
+       public void setVideorate(String s) { videorate = s; }\r
+       public String getVideorate() { return videorate; }\r
+       public void setAudiorate(String s) { audiorate = s; }\r
+       public String getAudiorate() { return audiorate; }\r
+       public void setDVDCompat(String s) { dvdcompat = s; }\r
+       public String getDVDCompat() { return dvdcompat; }\r
+\r
+       public void setDevice(String s) { device = s; }\r
+       public String getDevice() { return device; }\r
+       public void setXChapter(String s) { xchapter = s; }\r
+       public String getXChapter() { return xchapter; }\r
+       public void setMsChapter(String s) { mschapter = s; }\r
+       public String getMsChapter() { return mschapter; }\r
+       public void setMvChapter(String s) { mvchapter = s; }\r
+       public String getMvChapter() { return mvchapter; }\r
+\r
+       public void setAspect(String s) { aspect = s; }\r
+       public String getAspect() { return aspect; }\r
+       public void setBvperf(String s) { bvperf = s; }\r
+       public String getBvperf() { return bvperf; }\r
+       public void setLvoice(String s) { lvoice = s; }\r
+       public String getLvoice() { return lvoice; }\r
+       public void setAutodel(String s) { autodel = s; }\r
+       public String getAutodel() { return autodel; }\r
+       \r
+       public void setFolder(String s) { folder = s; }\r
+       public String getFolder() { return folder; }\r
+       \r
+       public void setPursues(Boolean b) { pursues = b; }\r
+       public Boolean getPursues() { return pursues; }\r
+}\r
diff --git a/TinyBannavi/src/tainavi/AbsChannelConvertView.java b/TinyBannavi/src/tainavi/AbsChannelConvertView.java
new file mode 100644 (file)
index 0000000..da432cf
--- /dev/null
@@ -0,0 +1,878 @@
+package tainavi;\r
+\r
+import java.awt.Color;\r
+import java.awt.Component;\r
+import java.awt.Dimension;\r
+import java.awt.event.ActionEvent;\r
+import java.awt.event.ActionListener;\r
+import java.awt.event.ItemEvent;\r
+import java.awt.event.ItemListener;\r
+\r
+import javax.swing.JButton;\r
+import javax.swing.JComboBox;\r
+import javax.swing.JLabel;\r
+import javax.swing.JPanel;\r
+import javax.swing.JScrollPane;\r
+import javax.swing.JTable;\r
+import javax.swing.ListSelectionModel;\r
+import javax.swing.ScrollPaneConstants;\r
+import javax.swing.SpringLayout;\r
+import javax.swing.border.LineBorder;\r
+import javax.swing.event.CellEditorListener;\r
+import javax.swing.event.ChangeEvent;\r
+import javax.swing.event.ListSelectionEvent;\r
+import javax.swing.event.ListSelectionListener;\r
+import javax.swing.table.DefaultTableColumnModel;\r
+import javax.swing.table.DefaultTableModel;\r
+import javax.swing.table.TableCellRenderer;\r
+import javax.swing.table.TableColumn;\r
+\r
+import tainavi.TVProgram.ProgSubtype;\r
+import tainavi.TVProgram.ProgType;\r
+\r
+/**\r
+ * ChannelConver.datを編集するView\r
+ */\r
+public abstract class AbsChannelConvertView extends JScrollPane {\r
+\r
+       private static final long serialVersionUID = 1L;\r
+       \r
+       public static String getViewName() { return "CHコンバート設定"; } \r
+\r
+       public void setDebug(boolean b) { debug = b; }\r
+       private static boolean debug = false;\r
+\r
+       /*******************************************************************************\r
+        * 抽象メソッド\r
+        ******************************************************************************/\r
+       \r
+       protected abstract Env getEnv();\r
+       protected abstract TVProgramList getProgPlugins();\r
+       protected abstract ChannelConvert getChannelConvert();\r
+\r
+       /*******************************************************************************\r
+        * 呼び出し元から引き継いだもの\r
+        ******************************************************************************/\r
+       \r
+       private final Env env = getEnv();\r
+       private final TVProgramList progPlugins = getProgPlugins();\r
+       private final ChannelConvert chconv = getChannelConvert();\r
+       \r
+       /*******************************************************************************\r
+        * 定数\r
+        ******************************************************************************/\r
+\r
+       // レイアウト関連\r
+       \r
+       private static final int PARTS_HEIGHT = 30;\r
+       private static final int SEP_WIDTH = 10;\r
+       private static final int SEP_HEIGHT = 10;\r
+       \r
+       private static final int UPDATE_WIDTH = 250;\r
+       private static final int HINT_WIDTH = 750;\r
+       \r
+       private static final int LABEL_WIDTH = 0;\r
+\r
+       private static final int TABLE_WIDTH = 950;\r
+       private static final int TABLE_ORIG_WIDTH = 200;\r
+       private static final int TABLE_RELATED_WIDTH = 250;\r
+       private static final int TABLE_OPTION_WIDTH = 75;\r
+       private static final int TABLE_EDIT_WIDTH = TABLE_WIDTH-TABLE_ORIG_WIDTH-TABLE_RELATED_WIDTH-TABLE_OPTION_WIDTH*2;\r
+       private static final int TABLE_HEIGHT = 450;\r
+\r
+       private static final int SELECTOR_WIDTH = 250;\r
+       private static final int AREA_WIDTH = 100;\r
+       private static final int RELATION_WIDTH = TABLE_WIDTH-SELECTOR_WIDTH-AREA_WIDTH-SEP_WIDTH*2;\r
+       \r
+       private static final int PANEL_WIDTH = SEP_WIDTH+UPDATE_WIDTH+SEP_HEIGHT*4+HINT_WIDTH+SEP_WIDTH;\r
+       \r
+       // テキスト\r
+       \r
+       private static final String TEXT_HINT =\r
+                       "鯛ナビで表示される放送局名を設定します。各番組表の放送局名を同じ名前に統一すると、番組表を切り替えても検索条件を変える必要がなくなります。"+\r
+                       "またしょぼかる連携が必要な場合は「Syobocal」の放送局名とも一致させてください。有効な場合は「しょぼかる」欄が「連携有効」となります。";\r
+       private static final String TEXT_NOTE =\r
+                       "3.16以前から継続してご利用の場合は先にCH設定タブで各番組表の放送局リストの再取得を行ってください。"+\r
+                       "これは、3.16以前の版では「変換前の放送局名」の情報が正しくないためです。";\r
+\r
+       //private static final String TEXT_UPDATE = "更新を確定する";\r
+       private static final String TEXT_UPDATE = "(更新はまだ実装してない)";\r
+\r
+       private static final String TEXT_CONVERTED = "あり";\r
+\r
+       private static final String TEXT_SYOBO_EN = "連携有効";\r
+\r
+       private static final String TEXT_AREASELECT_UNSUPPORTED = "(エリア非対応)";\r
+       \r
+       private static final String TEXT_RELATEDCOLUMN = "放送局名が変わらない他の番組表(→CH設定)";\r
+       private static final String TEXT_RELATEDCOLUMN_SYOBO = "しょぼかる連携が有効な番組表";\r
+       private static final String TEXT_MATCHEDCOLUMN = "しょぼかる";\r
+       private static final String TEXT_MATCHEDCOLUMN_SYOBO = "-";\r
+       \r
+       private static final String UNAVAILABLE_CENTER = "(選択できません)";\r
+\r
+       // ログ関連\r
+       \r
+       private static final String MSGID = "["+getViewName()+"] ";\r
+       private static final String ERRID = "[ERROR]"+MSGID;\r
+       private static final String DBGID = "[DEBUG]"+MSGID;\r
+       \r
+       // カラム設定\r
+       \r
+       private static enum ChConvColumn {\r
+               ORIG            ("[a]番組表サイト上での放送局名",                                                  TABLE_ORIG_WIDTH),\r
+               EDIT            ("[b]鯛ナビでの放送局名(CH設定での「Web番組表の放送局名」)",     TABLE_EDIT_WIDTH),\r
+               CONVERTED       ("a→b変換",                                                                                         TABLE_OPTION_WIDTH),\r
+               RELATED         (TEXT_RELATEDCOLUMN,                                                                    TABLE_RELATED_WIDTH),\r
+               MATCHED         (TEXT_MATCHEDCOLUMN,                                                                    TABLE_OPTION_WIDTH),\r
+               ;\r
+               \r
+               private String name;\r
+               private int width;\r
+               \r
+               private ChConvColumn(String name, int width) {\r
+                       this.name = name;\r
+                       this.width = width;\r
+               }\r
+               \r
+               String getName() { return name; }\r
+               \r
+               int getIniWidth() { return width; }\r
+               \r
+               int getColumn() { return ordinal(); }\r
+       }\r
+\r
+       /*******************************************************************************\r
+        * 部品\r
+        ******************************************************************************/\r
+       \r
+       // コンポーネント\r
+       \r
+       private JPanel jp_update = null;\r
+       private JButton jbtn_update = null;\r
+       private JTextAreaWithPopup jta_hint = null;\r
+\r
+       private JPanel jp_setting = null;\r
+       private JTextAreaWithPopup jta_note = null;\r
+       private JComboBox jcb_progplugin = null;\r
+       private JLabel jl_area = null;\r
+       private JLabel jl_rel = null;\r
+       private JScrollPane jscr_chconv = null;\r
+       private ChConvTable jtbl_chconv = null;\r
+       \r
+       // コンポーネント以外\r
+       \r
+       // しょぼーん\r
+       private Syobocal syoboplugin = null;\r
+       \r
+       // テーブルのデータの入れ物\r
+       private final RowItemList<ChConvItem> rowData = new RowItemList<ChConvItem>();\r
+       \r
+       /*******************************************************************************\r
+        * コンストラクタ\r
+        ******************************************************************************/\r
+\r
+       public AbsChannelConvertView() {\r
+               \r
+               super();\r
+               \r
+               this.setVerticalScrollBarPolicy(ScrollPaneConstants.VERTICAL_SCROLLBAR_ALWAYS);\r
+               this.getVerticalScrollBar().setUnitIncrement(25);\r
+               this.setHorizontalScrollBarPolicy(ScrollPaneConstants.HORIZONTAL_SCROLLBAR_AS_NEEDED);\r
+               \r
+               this.setColumnHeaderView(getJP_update());\r
+               this.setViewportView(getJP_setting());\r
+               \r
+               // テーブルの初期化\r
+               initialize();\r
+               \r
+               if (debug) System.out.println(DBGID+"構築完了");\r
+               \r
+       }\r
+       \r
+       /*******************************************************************************\r
+        * アクション\r
+        ******************************************************************************/\r
+       \r
+       // 外部向け\r
+       \r
+       /**\r
+        * テーブルの書き換えイベントを起こす\r
+        */\r
+       public void updateChannelConvertTable() {\r
+               int selected = jcb_progplugin.getSelectedIndex();\r
+               jcb_progplugin.setSelectedIndex(-1);\r
+               jcb_progplugin.setSelectedIndex(selected);\r
+       }\r
+       \r
+       // 内部向け\r
+       \r
+       /**\r
+        *  初期化\r
+        */\r
+       private void initialize() {\r
+               \r
+               // しょぼーん\r
+               setSyobo();\r
+               \r
+               // コンボボックスの設定\r
+               setPluginsForComboBox();\r
+\r
+               jcb_progplugin.setSelectedIndex(-1);\r
+               jcb_progplugin.setSelectedIndex(0);\r
+               \r
+               jbtn_update.setEnabled(false);\r
+\r
+       }\r
+       \r
+       /**\r
+        *  しょぼーん\r
+        */\r
+       private void setSyobo() {\r
+               \r
+               if ( env.getUseSyobocal() )\r
+               {\r
+                       syoboplugin = new Syobocal();\r
+\r
+                       syoboplugin.loadCenter(syoboplugin.getSelectedCode(), false);\r
+                       syoboplugin.setSortedCRlist();\r
+                       \r
+                       if (debug) System.out.println(DBGID+"しょぼかるを利用しています: "+syoboplugin.getCRlist().size());\r
+               }\r
+                               \r
+       }\r
+       \r
+       /**\r
+        *  コンボボックスの設定\r
+        */\r
+       private void setPluginsForComboBox() {\r
+               \r
+               ProgSubtype[] subs = { ProgSubtype.TERRA, ProgSubtype.CS, ProgSubtype.CS2 };\r
+               \r
+               jcb_progplugin.removeItemListener(il_pluginselected);   // リスナー停止\r
+               \r
+               for ( ProgSubtype sub : subs )\r
+               {\r
+                       for ( TVProgram tp : progPlugins )\r
+                       {\r
+                               if ( tp.getType() == ProgType.PROG && tp.getSubtype() == sub )\r
+                               {\r
+                                       tp.loadAreaCode();\r
+                                       tp.loadCenter(tp.getSelectedCode(), false);\r
+                                       tp.setSortedCRlist();\r
+\r
+                                       jcb_progplugin.addItem(tp.getTVProgramId());\r
+                               }\r
+                       }\r
+               }\r
+               if ( env.getUseSyobocal() )\r
+               {\r
+                       jcb_progplugin.addItem(syoboplugin.getTVProgramId());\r
+               }\r
+               \r
+               jcb_progplugin.addItemListener(il_pluginselected);              // リスナー再開\r
+\r
+       }\r
+       \r
+       /**\r
+        *  テーブルのデータを更新する\r
+        * @param tp\r
+        */\r
+       private void setCentersForTable(TVProgram tp) {\r
+               \r
+               rowData.clear();\r
+               \r
+               // 有効局が優先\r
+               for ( Center cr : tp.getSortedCRlist() )\r
+               {\r
+                       ChConvItem c = getChConvItem(cr,tp);\r
+                       if ( c == null )\r
+                       {\r
+                               continue;\r
+                       }\r
+                       \r
+                       rowData.add(c);\r
+                       \r
+                       //if (debug) System.out.println(DBGID+"有効局を追加しました: "+c.centername);\r
+               }\r
+               \r
+               // 無効局はおまけ\r
+               for ( Center cr : tp.getCRlist() )\r
+               {\r
+                       if ( cr.getOrder() > 0 )\r
+                       {\r
+                               continue;\r
+                       }\r
+                       \r
+                       ChConvItem c = getChConvItem(cr,tp);\r
+                       if ( c == null )\r
+                       {\r
+                               continue;\r
+                       }\r
+                       \r
+                       rowData.add(c);\r
+                       \r
+                       //if (debug) System.out.println(DBGID+"無効局を追加しました: "+c.centername);\r
+               }\r
+               \r
+               // 更新して!\r
+               ((DefaultTableModel) jtbl_chconv.getModel()).fireTableDataChanged();\r
+               \r
+               if (debug) System.out.println(DBGID+"rowData count="+rowData.size());\r
+               \r
+       }\r
+       \r
+       /**\r
+        *  テーブルの行データの作成\r
+        * @param cr\r
+        * @return\r
+        */\r
+       private ChConvItem getChConvItem(Center cr, TVProgram tp) {\r
+               \r
+               if ( cr.getCenterOrig().equals(UNAVAILABLE_CENTER) )\r
+               {\r
+                       return null;\r
+               }\r
+               \r
+               boolean issyobo = (tp.getType() == ProgType.SYOBO);\r
+               \r
+               ChConvItem c = new ChConvItem();\r
+               \r
+               c.original = cr.getCenterOrig();\r
+               c.centername = chconv.get(c.original);\r
+               \r
+               c.hide_enabled = (cr.getOrder() > 0);\r
+               \r
+               c.hide_converted = ( ! c.original.equals(c.centername));\r
+               if ( c.hide_converted )\r
+               {\r
+                       c.converted = TEXT_CONVERTED;\r
+               }\r
+               \r
+               // 他サイトとの連携が行われているか確認する\r
+               //if ( ! issyobo )\r
+               {\r
+                       c.related = "";\r
+                       for ( TVProgram p : progPlugins ) {\r
+                               if ( p.getTVProgramId().equals(tp.getTVProgramId()) )\r
+                               {\r
+                                       continue;\r
+                               }\r
+                               if ( p.getType() == ProgType.SYOBO )\r
+                               {\r
+                                       continue;\r
+                               }\r
+                               \r
+                               for ( Center crp : p.getCRlist() )\r
+                               {\r
+                                       if ( crp.getCenter().equals(c.centername) )\r
+                                       {\r
+                                               c.related += p.getTVProgramId()+", ";\r
+                                               break;\r
+                                       }\r
+                               }\r
+                               \r
+                       }\r
+                       c.related = c.related.replaceFirst(", $","");\r
+               }\r
+       \r
+               // しょぼかる連携が行われているか確認する\r
+               if ( env.getUseSyobocal() && ! issyobo )\r
+               {\r
+                       for ( Center crsy : syoboplugin.getCRlist() )\r
+                       {\r
+                               if ( c.centername.equals(chconv.get(crsy.getCenterOrig())) )\r
+                               {\r
+                                       //if (debug) System.out.println(DBGID+"しょぼかる連携が有効です: "+c.centername+"<->"+chconv.get(crsy.getCenterOrig()));\r
+                                       \r
+                                       c.syobo = TEXT_SYOBO_EN;\r
+                                       c.hide_syoborelated = true;\r
+                                       break;\r
+                               }\r
+                       }\r
+               }\r
+               \r
+               c.fireChanged();\r
+               \r
+               return c;\r
+               \r
+       }\r
+       \r
+       //\r
+\r
+       /*******************************************************************************\r
+        * リスナー\r
+        ******************************************************************************/\r
+\r
+       // 更新確定ボタンを押したイベントを拾うリスナー\r
+       private final ActionListener al_update = new ActionListener() {\r
+               \r
+               @Override\r
+               public void actionPerformed(ActionEvent e) {\r
+                       if (debug) System.out.println(DBGID+"al_update "+e.toString());\r
+                       \r
+                       JButton btn = (JButton) e.getSource();\r
+                       btn.setEnabled(false);\r
+               }\r
+               \r
+       };\r
+       \r
+       // 番組表が選択された\r
+       private final ItemListener il_pluginselected = new ItemListener() {\r
+\r
+               @Override\r
+               public void itemStateChanged(ItemEvent e) {\r
+                       if (debug) System.out.println(DBGID+"il_pluginselected "+e.toString());\r
+                       if ( e.getStateChange() == ItemEvent.SELECTED )\r
+                       {\r
+                               String selected = (String) ((JComboBox) e.getSource()).getSelectedItem();\r
+                               TVProgram tp = null;\r
+                               if ( syoboplugin != null && syoboplugin.getTVProgramId().equals(selected) )\r
+                               {\r
+                                       tp = syoboplugin;\r
+                                       jl_area.setText(TEXT_AREASELECT_UNSUPPORTED);\r
+                                       setCentersForTable(syoboplugin);\r
+                               }\r
+                               else {\r
+                                       tp = progPlugins.getProgPlugin(selected);\r
+                                       if ( tp != null )\r
+                                       {\r
+                                               tp.loadAreaCode();\r
+                                               tp.loadCenter(tp.getSelectedCode(), false);\r
+                                               tp.setSortedCRlist();\r
+                                               \r
+                                               setCentersForTable(tp);\r
+                                               \r
+                                               if ( tp.isAreaSelectSupported() )\r
+                                               {\r
+                                                       jl_area.setText(tp.getSelectedArea());\r
+                                               }\r
+                                               else\r
+                                               {\r
+                                                       jl_area.setText(TEXT_AREASELECT_UNSUPPORTED);\r
+                                               }\r
+                                       }\r
+                               }\r
+                               \r
+                               if ( tp != null ) {\r
+                                       TableColumn colrel = jtbl_chconv.getColumnModel().getColumn(ChConvColumn.RELATED.getColumn());\r
+                                       TableColumn colsyo = jtbl_chconv.getColumnModel().getColumn(ChConvColumn.MATCHED.getColumn());\r
+                                       if ( tp.getType() == ProgType.SYOBO )\r
+                                       {\r
+                                               colrel.setHeaderValue(TEXT_RELATEDCOLUMN_SYOBO);\r
+                                               colsyo.setHeaderValue(TEXT_MATCHEDCOLUMN_SYOBO);\r
+                                       }\r
+                                       else\r
+                                       {\r
+                                               colrel.setHeaderValue(TEXT_RELATEDCOLUMN);\r
+                                               colsyo.setHeaderValue(TEXT_MATCHEDCOLUMN);\r
+                                       }\r
+                                       //jtbl_chconv.getTableHeader().resizeAndRepaint();\r
+                               }\r
+                               \r
+                               if (debug) System.out.println(DBGID+"il_pluginselected selected="+selected);\r
+                       }\r
+               }\r
+               \r
+       };\r
+       \r
+       // 行が選択されて\r
+       private final ListSelectionListener lsl_selected = new ListSelectionListener() {\r
+               \r
+               @Override\r
+               public void valueChanged(ListSelectionEvent e) {\r
+                       if (debug) System.out.println(DBGID+"lsl_selected "+e.toString());\r
+                       if ( ! e.getValueIsAdjusting() )\r
+                       {\r
+                               ListSelectionModel model = (ListSelectionModel) e.getSource();\r
+                               if ( ! model.isSelectionEmpty() )\r
+                               {\r
+                                       int row = model.getMinSelectionIndex();\r
+                                       ChConvItem c = rowData.get(row);\r
+                                       jl_rel.setText(c.related);\r
+                               }\r
+                       }\r
+               }\r
+               \r
+       };\r
+       \r
+       // セルが編集された\r
+       private final CellEditorListener cel_edited = new CellEditorListener() {\r
+               \r
+               @Override\r
+               public void editingStopped(ChangeEvent e) {\r
+                       if (debug) System.out.println(DBGID+"cel_edited "+e.toString());\r
+                       jbtn_update.setEnabled(true);\r
+               }\r
+               \r
+               @Override\r
+               public void editingCanceled(ChangeEvent e) {\r
+                       if (debug) System.out.println(DBGID+"cel_edited "+e.toString());\r
+               }\r
+               \r
+       };\r
+       \r
+       /*******************************************************************************\r
+        * コンポーネント\r
+        ******************************************************************************/\r
+       \r
+       // 更新確定ボタンの部分\r
+       \r
+       public JPanel getJP_update() {\r
+               \r
+               if (jp_update == null)\r
+               {\r
+                       jp_update = new JPanel();\r
+                       jp_update.setLayout(new SpringLayout());\r
+                       \r
+                       jp_update.setBorder(new LineBorder(Color.GRAY));\r
+                       \r
+                       int y = SEP_HEIGHT;\r
+                       int x = SEP_WIDTH;\r
+                       \r
+                       CommonSwingUtils.putComponentOn(jp_update, getJBtn_update(TEXT_UPDATE), UPDATE_WIDTH, PARTS_HEIGHT, SEP_WIDTH, y);\r
+                       \r
+                       int y2 = SEP_HEIGHT/2;\r
+                       int x2 = x + UPDATE_WIDTH+SEP_WIDTH*4;\r
+                       CommonSwingUtils.putComponentOn(jp_update, getJTa_hint(), HINT_WIDTH, PARTS_HEIGHT+SEP_HEIGHT, x2, y2);\r
+                       \r
+                       y += (PARTS_HEIGHT + SEP_HEIGHT);\r
+                       \r
+                       jp_update.setPreferredSize(new Dimension(PANEL_WIDTH,y));\r
+               }\r
+               return jp_update;\r
+               \r
+       }\r
+       \r
+       // 「更新を確定する」ボタン\r
+       private JButton getJBtn_update(String s) {\r
+               \r
+               if ( jbtn_update == null )\r
+               {\r
+                       jbtn_update = new JButton(s);\r
+                       jbtn_update.addActionListener(al_update);\r
+               }\r
+               return jbtn_update;\r
+               \r
+       }\r
+       \r
+       // ヒント表示部分\r
+       private JTextAreaWithPopup getJTa_hint() {\r
+               \r
+               if ( jta_hint == null )\r
+               {\r
+                       jta_hint = CommonSwingUtils.getJta(this,2,0);\r
+                       jta_hint.append(TEXT_HINT);\r
+               }\r
+               return jta_hint;\r
+               \r
+       }\r
+       \r
+       // 設定画面の部分\r
+       \r
+       // パネル\r
+       public JPanel getJP_setting() {\r
+               \r
+               if (jp_setting == null)\r
+               {\r
+                       jp_setting = new JPanel();\r
+                       jp_setting.setLayout(new SpringLayout());\r
+                       \r
+                       int y = SEP_HEIGHT;\r
+                       int x = SEP_WIDTH;\r
+                       int x2 = x+LABEL_WIDTH+SEP_WIDTH;\r
+                       \r
+                       CommonSwingUtils.putComponentOn(jp_setting, getJTa_note(), TABLE_WIDTH, PARTS_HEIGHT, x2, y);\r
+                       y += PARTS_HEIGHT/*+SEP_HEIGHT*/;\r
+                       \r
+                       CommonSwingUtils.putComponentOn(jp_setting, getJCb_progplugin(), SELECTOR_WIDTH, PARTS_HEIGHT, x2, y);\r
+                       CommonSwingUtils.putComponentOn(jp_setting, getJL_area(), AREA_WIDTH, PARTS_HEIGHT, x2+SELECTOR_WIDTH+SEP_WIDTH, y);\r
+                       CommonSwingUtils.putComponentOn(jp_setting, getJL_rel(), RELATION_WIDTH, PARTS_HEIGHT, x2+SELECTOR_WIDTH+AREA_WIDTH+SEP_WIDTH*2, y);\r
+                       y += PARTS_HEIGHT+SEP_HEIGHT;\r
+                       \r
+                       getJScr_chconv();\r
+                       int sb_w = jscr_chconv.getVerticalScrollBar().getPreferredSize().width;\r
+                       CommonSwingUtils.putComponentOn(jp_setting, jscr_chconv, TABLE_WIDTH+sb_w+5, TABLE_HEIGHT, x2, y);\r
+                       y += TABLE_HEIGHT+SEP_HEIGHT;\r
+                       \r
+                       jp_setting.setPreferredSize(new Dimension(PANEL_WIDTH,y));\r
+               }\r
+               return jp_setting;\r
+               \r
+       }\r
+       \r
+       // 通知\r
+       private JTextAreaWithPopup getJTa_note() {\r
+               \r
+               if ( jta_note == null )\r
+               {\r
+                       jta_note = CommonSwingUtils.getJta(this,1,0);\r
+                       jta_note.append(TEXT_NOTE);\r
+               }\r
+               return jta_note;\r
+               \r
+       }\r
+       \r
+       // 番組表プラグインの選択\r
+       private JComboBox getJCb_progplugin() {\r
+               \r
+               if ( jcb_progplugin == null )\r
+               {\r
+                       jcb_progplugin = new JComboBox();\r
+                       \r
+                       jcb_progplugin.addItemListener(il_pluginselected);\r
+               }\r
+               return jcb_progplugin;\r
+               \r
+       }\r
+       \r
+       // 番組表のエリア\r
+       private JLabel getJL_area() {\r
+               \r
+               if ( jl_area == null )\r
+               {\r
+                       jl_area = new JLabel();\r
+                       jl_area.setBorder(new LineBorder(Color.BLACK));\r
+               }\r
+               return jl_area;\r
+               \r
+       }\r
+       \r
+       // 他サイト連携\r
+       private JLabel getJL_rel() {\r
+               \r
+               if ( jl_rel == null )\r
+               {\r
+                       jl_rel = new JLabel();\r
+                       jl_rel.setBorder(new LineBorder(Color.BLACK));\r
+               }\r
+               return jl_rel;\r
+               \r
+       }\r
+       \r
+       // テーブルの入れ物\r
+       private JScrollPane getJScr_chconv() {\r
+               \r
+               if (jscr_chconv == null)\r
+               {\r
+                       jscr_chconv = new JScrollPane();\r
+                       jscr_chconv.setVerticalScrollBarPolicy(ScrollPaneConstants.VERTICAL_SCROLLBAR_ALWAYS);\r
+                       jscr_chconv.getVerticalScrollBar().setUnitIncrement(25);\r
+                       jscr_chconv.setHorizontalScrollBarPolicy(ScrollPaneConstants.HORIZONTAL_SCROLLBAR_AS_NEEDED);\r
+\r
+                       jscr_chconv.setViewportView(getJTbl_chconv());\r
+               }\r
+               return jscr_chconv;\r
+               \r
+       }\r
+       \r
+       // テーブル\r
+       private ChConvTable getJTbl_chconv() {\r
+               \r
+               if ( jtbl_chconv == null )\r
+               {\r
+                       jtbl_chconv = new ChConvTable(rowData);\r
+                       \r
+                       DefaultTableModel model = new DefaultTableModel();\r
+                       for ( ChConvColumn ccc : ChConvColumn.values() )\r
+                       {\r
+                               model.addColumn(ccc.getName()); // カラム名\r
+                       }\r
+                       jtbl_chconv.setModel(model);\r
+                       \r
+                       DefaultTableColumnModel columnModel = (DefaultTableColumnModel) jtbl_chconv.getColumnModel();\r
+                       for ( ChConvColumn rc : ChConvColumn.values() )\r
+                       {\r
+                               if ( rc.getIniWidth() >= 0 )\r
+                               {\r
+                                       columnModel.getColumn(rc.ordinal()).setPreferredWidth(rc.getIniWidth());;       // カラムの幅\r
+                               }\r
+                       }\r
+                       \r
+                       // 属性\r
+                       jtbl_chconv.setAutoResizeMode(JNETable.AUTO_RESIZE_OFF);\r
+                       jtbl_chconv.getTableHeader().setReorderingAllowed(false);\r
+                       jtbl_chconv.setSelectionMode(ListSelectionModel.SINGLE_SELECTION);\r
+                       jtbl_chconv.putClientProperty("terminateEditOnFocusLost", true);        // これやらないと、編集が確定したように見えて確定しない。ヤバイ。\r
+                       jtbl_chconv.setRowHeight(jtbl_chconv.getRowHeight()+4);\r
+                       \r
+                       // 行を選択したら\r
+                       jtbl_chconv.getSelectionModel().addListSelectionListener(lsl_selected);\r
+                       \r
+                       // 編集セルにリスナーを付ける\r
+                       TableColumn tc = jtbl_chconv.getColumn(ChConvColumn.EDIT.getName());\r
+                       EditorColumn ec = new EditorColumn(); \r
+                       ec.addCellEditorListener(cel_edited);\r
+                       tc.setCellEditor(ec);\r
+               }\r
+               return jtbl_chconv;\r
+               \r
+       }\r
+       \r
+       /*******************************************************************************\r
+        * 独自部品\r
+        ******************************************************************************/\r
+       \r
+       // テーブルの行データの構造\r
+       private class ChConvItem extends RowItem implements Cloneable {\r
+               \r
+               // 表示メンバ\r
+               String original;\r
+               String centername;\r
+               String related;\r
+               String converted;\r
+               String syobo;\r
+               \r
+               // 非表示メンバ\r
+               boolean hide_enabled;\r
+               boolean hide_converted;\r
+               boolean hide_syoborelated;\r
+               \r
+               @Override\r
+               protected void myrefresh(RowItem o) {\r
+                       ChConvItem c = (ChConvItem) o;\r
+                       c.addData(original);\r
+                       c.addData(centername);\r
+                       c.addData(converted);\r
+                       c.addData(related);\r
+                       c.addData(syobo);\r
+               }\r
+               \r
+               @Override\r
+               public ChConvItem clone() {\r
+                       return (ChConvItem) super.clone();\r
+               }\r
+               \r
+       }\r
+       \r
+       // ChConvItemを使ったJTable拡張\r
+       private class ChConvTable extends JTable {\r
+\r
+               private static final long serialVersionUID = 1L;\r
+               \r
+               private final Color evenColor = new Color(240,240,255);\r
+               private final Color oddColor = super.getBackground();\r
+               \r
+               private final Color unmappedOddColor = new Color(153,153,153);\r
+               private final Color unmappedEvenColor = new Color(133,133,133);\r
+               \r
+               private final Color syoboOddColor = new Color(255,204,153);\r
+               private final Color syoboEvenColor = new Color(235,184,133);\r
+               \r
+               private final Color disabledOddColor = new Color(200,200,200);\r
+               private final Color disabledEvenColor = new Color(180,180,180);\r
+               \r
+               private RowItemList<ChConvItem> rowdata = null;\r
+               \r
+               public ChConvTable(RowItemList<ChConvItem> rowdata) {\r
+                       this.rowdata = rowdata;\r
+                       \r
+                       // フォントサイズ変更にあわせて行の高さを変える\r
+                       this.addPropertyChangeListener("font", new RowHeightChangeListener(8));\r
+\r
+                       // 行の高さの初期値の設定\r
+                       this.firePropertyChange("font", "old", "new");\r
+               }\r
+               \r
+               @Override\r
+               public Object getValueAt(int row, int column) {\r
+                       \r
+                       int vrow = this.convertRowIndexToModel(row);\r
+                       ChConvItem c = rowdata.get(vrow);\r
+                       return c.get(column);\r
+                       \r
+               }\r
+               \r
+               @Override\r
+               public void setValueAt(Object aValue, int row, int column) {\r
+                       \r
+                       int vrow = this.convertRowIndexToModel(row);\r
+                       ChConvItem c = rowdata.get(vrow); \r
+                       if ( column == ChConvColumn.EDIT.getColumn() )\r
+                       {\r
+                               c.centername = (String) aValue;\r
+                               c.fireChanged();\r
+                       }\r
+                       \r
+               }\r
+               \r
+               @Override\r
+               public int getRowCount() {\r
+                       \r
+                       return rowdata.size();\r
+                       \r
+               }\r
+\r
+               @Override\r
+               public boolean isCellEditable(int row, int column) {\r
+                       \r
+                       if ( column != ChConvColumn.EDIT.getColumn() )\r
+                       {\r
+                               return false;   // 編集欄以外は編集できない\r
+                       }\r
+                       return true;\r
+                       \r
+               }\r
+               \r
+               @Override\r
+               public Component prepareRenderer(TableCellRenderer tcr, int row, int column) {\r
+                       \r
+                       Component comp = super.prepareRenderer(tcr, row, column);\r
+                       \r
+                       int vrow = this.convertRowIndexToModel(row);\r
+                       ChConvItem c = rowdata.get(vrow);\r
+                       \r
+                       Color fg = null;\r
+                       Color bg = null;\r
+                       \r
+                       boolean evenline = (row%2 == 1);\r
+                       \r
+                       if ( c.hide_converted && column == ChConvColumn.EDIT.getColumn() )\r
+                       {\r
+                               fg = Color.RED;\r
+                       }\r
+                       else if ( column == ChConvColumn.ORIG.getColumn() )\r
+                       {\r
+                               fg = Color.BLUE;\r
+                       }\r
+                       else\r
+                       {\r
+                               if ( isRowSelected(row) )\r
+                               {\r
+                                       fg = this.getSelectionForeground();\r
+                               }\r
+                       }\r
+                       \r
+                       if ( isRowSelected(row) )\r
+                       {\r
+                               bg = this.getSelectionBackground();\r
+                       }\r
+                       else {\r
+                               if ( column == ChConvColumn.EDIT.getColumn() ) {\r
+                                       if ( ! c.hide_enabled )\r
+                                       {\r
+                                               bg = (evenline)?(unmappedEvenColor):(unmappedOddColor);\r
+                                       }\r
+                                       else if ( c.hide_syoborelated )\r
+                                       {\r
+                                               bg = (evenline)?(syoboEvenColor):(syoboOddColor);\r
+                                       }\r
+                               }\r
+                               else {\r
+                                       bg = (evenline)?(disabledEvenColor):(disabledOddColor);\r
+                               }\r
+                       }\r
+\r
+                       if (fg==null) fg = this.getForeground();\r
+                       if (bg==null) bg = (evenline)?(evenColor):(oddColor);\r
+                       \r
+                       comp.setForeground(fg);\r
+                       comp.setBackground(bg);\r
+                       \r
+                       return comp;\r
+                       \r
+               }\r
+               \r
+       }\r
+\r
+}\r
diff --git a/TinyBannavi/src/tainavi/AbsChannelDatSettingView.java b/TinyBannavi/src/tainavi/AbsChannelDatSettingView.java
new file mode 100644 (file)
index 0000000..42857c1
--- /dev/null
@@ -0,0 +1,1205 @@
+package tainavi;\r
+\r
+import java.awt.Color;\r
+import java.awt.Component;\r
+import java.awt.Dimension;\r
+import java.awt.event.ActionEvent;\r
+import java.awt.event.ActionListener;\r
+import java.awt.event.ItemEvent;\r
+import java.awt.event.ItemListener;\r
+import java.awt.event.MouseAdapter;\r
+import java.awt.event.MouseEvent;\r
+import java.awt.event.MouseListener;\r
+import java.util.ArrayList;\r
+import java.util.Arrays;\r
+\r
+import javax.swing.AbstractAction;\r
+import javax.swing.AbstractCellEditor;\r
+import javax.swing.DefaultCellEditor;\r
+import javax.swing.JButton;\r
+import javax.swing.JComboBox;\r
+import javax.swing.JLabel;\r
+import javax.swing.JOptionPane;\r
+import javax.swing.JPanel;\r
+import javax.swing.JScrollPane;\r
+import javax.swing.JTable;\r
+import javax.swing.JTextField;\r
+import javax.swing.ListSelectionModel;\r
+import javax.swing.ScrollPaneConstants;\r
+import javax.swing.SpringLayout;\r
+import javax.swing.border.LineBorder;\r
+import javax.swing.event.AncestorEvent;\r
+import javax.swing.event.AncestorListener;\r
+import javax.swing.table.DefaultTableColumnModel;\r
+import javax.swing.table.DefaultTableModel;\r
+import javax.swing.table.TableCellEditor;\r
+import javax.swing.table.TableCellRenderer;\r
+import javax.swing.table.TableColumn;\r
+\r
+import tainavi.TVProgramIterator.IterationType;\r
+\r
+/**\r
+ * CHコード設定のタブ\r
+ * @since 3.15.4β {@link Viewer}から分離\r
+ */\r
+public abstract class AbsChannelDatSettingView extends JScrollPane {\r
+\r
+       private static final long serialVersionUID = 1L;\r
+\r
+       public static String getViewName() { return "CHコード設定"; } \r
+\r
+       public static void setDebug(boolean b) {debug = b; }\r
+       private static boolean debug = false;\r
+\r
+       /*******************************************************************************\r
+        * 抽象メソッド\r
+        ******************************************************************************/\r
+       \r
+       protected abstract Env getEnv();\r
+       protected abstract TVProgramList getTVProgramList();\r
+       protected abstract ChannelSort getChannelSort();\r
+       protected abstract HDDRecorderList getHDDRecorderList();\r
+       \r
+       protected abstract StatusWindow getStWin(); \r
+       protected abstract StatusTextArea getMWin();\r
+       \r
+       protected abstract Component getParentComponent();\r
+       \r
+       protected abstract void ringBeep();\r
+       \r
+       \r
+       /*******************************************************************************\r
+        * 呼び出し元から引き継いだもの\r
+        ******************************************************************************/\r
+       \r
+       // オブジェクト\r
+       //private final Env env = getEnv();\r
+       private final TVProgramList tvprograms = getTVProgramList();\r
+       private final ChannelSort chsort = getChannelSort();\r
+       private final HDDRecorderList recorders = getHDDRecorderList();\r
+       \r
+       private final StatusWindow StWin = getStWin();                  // これは起動時に作成されたまま変更されないオブジェクト\r
+       private final StatusTextArea MWin = getMWin();                  // これは起動時に作成されたまま変更されないオブジェクト\r
+       \r
+       private final Component parent = getParentComponent();  // これは起動時に作成されたまま変更されないオブジェクト\r
+       \r
+       // メソッド\r
+       //private void StdAppendMessage(String message) { System.out.println(message); }\r
+       //private void StdAppendError(String message) { System.err.println(message); }\r
+       private void StWinSetVisible(boolean b) { StWin.setVisible(b); }\r
+       private void StWinSetLocationCenter(Component frame) { CommonSwingUtils.setLocationCenter(frame, (VWStatusWindow)StWin); }\r
+\r
+       /*******************************************************************************\r
+        * 定数\r
+        ******************************************************************************/\r
+       \r
+       // レイアウト関連\r
+\r
+       //private static final int PARTS_WIDTH = 900;\r
+       private static final int PARTS_HEIGHT = 30;\r
+       private static final int SEP_WIDTH = 10;\r
+       private static final int SEP_HEIGHT = 10;\r
+       //private static final int BLOCK_SEP_HEIGHT = 75;\r
+\r
+       private static final int LABEL_WIDTH = 200;\r
+       private static final int BUTTON_WIDTH = 75;\r
+       private static final int BUTTON_WIDTH_LONG = 200;\r
+       \r
+       private static final int TABLE_WIDTH = ChDatColumn.WEBCHNAME.getIniWidth()+ChDatColumn.RECCHNAME.getIniWidth()+ChDatColumn.CHCODE.getIniWidth()+ChDatColumn.BTYPE.getIniWidth()+ChDatColumn.AUTO.getIniWidth()+20;\r
+       \r
+       private static final int UPDATE_WIDTH = 250;\r
+       private static final int HINT_WIDTH = 750;\r
+\r
+       private static final int PANEL_WIDTH = SEP_WIDTH+LABEL_WIDTH+SEP_WIDTH+TABLE_WIDTH+SEP_WIDTH;\r
+       \r
+       // テキスト\r
+       \r
+       private static final String TEXT_HINT =\r
+                       "「Web番組表の放送局名」と「レコーダの放送局名」を関連付けて予約情報のやりとりができるようにしてください。"+\r
+                       "詳細はwikiを参照してください(http://sourceforge.jp/projects/tainavi/wiki/FAQ#CHCODE)";\r
+       \r
+       // ログ関連\r
+       \r
+       private static final String MSGID = "["+getViewName()+"] ";\r
+       private static final String ERRID = "[ERROR]"+MSGID;\r
+       private static final String DBGID = "[DEBUG]"+MSGID;\r
+       \r
+       // カラム設定\r
+       \r
+       private static enum ChDatColumn {\r
+               WEBCHNAME       ("Web番組表の放送局名", 250),\r
+               RECCHNAME       ("レコーダの放送局名",         175),\r
+               CHCODE          ("放送局コード",                  175),\r
+               BTYPE           ("放送波の種類",                  100),\r
+               AUTO            ("AUTO",                                75),\r
+               ;\r
+               \r
+               private String name;\r
+               private int iniWidth;\r
+\r
+               private ChDatColumn(String name, int iniWidth) {\r
+                       this.name = name;\r
+                       this.iniWidth = iniWidth;\r
+               }\r
+\r
+               public String getName() {\r
+                       return name;\r
+               }\r
+\r
+               public int getIniWidth() {\r
+                       return iniWidth;\r
+               }\r
+               \r
+               public int getColumn() {\r
+                       return ordinal();\r
+               }\r
+       }\r
+       \r
+       /*******************************************************************************\r
+        * 部品\r
+        ******************************************************************************/\r
+       \r
+       // コンポーネント\r
+       \r
+       private JPanel jPanel_chDatSetting = null;\r
+       private JPanel jPanel_update = null;\r
+       \r
+       private JButton jButton_update = null;\r
+       \r
+       private JLabel jLabel_recorderId = null;\r
+       private JComboBox jComboBox_recorderId = null;\r
+       \r
+       private JScrollPane jScrollPane_entries = null;\r
+       private ChDatTable jTable_entries = null;\r
+       private DefaultCellEditor editorCombo_recchname = null;\r
+       private DefaultCellEditor editorField_recchname = null;\r
+       \r
+       private JButton jButton_upCenter = null;\r
+       private JButton jButton_downCenter = null;\r
+       \r
+       private JButton jButton_removeCenter = null;\r
+       \r
+       private JButton jButton_addCenter = null;\r
+       private JTextFieldWithPopup jTextField_addCenter = null;\r
+       \r
+       private JTextAreaWithPopup jta_help = null;\r
+       private JTextAreaWithPopup jta_chdathelp = null;\r
+       \r
+       // コンポーネント以外\r
+       \r
+       private HDDRecorder selectedRecorder = null;\r
+       \r
+       private final RowItemList<ChDatItem> rowData = new RowItemList<ChDatItem>();\r
+       \r
+       /*******************************************************************************\r
+        * コンストラクタ\r
+        ******************************************************************************/\r
+       \r
+       public AbsChannelDatSettingView() {\r
+               \r
+               super();\r
+               \r
+               this.setVerticalScrollBarPolicy(ScrollPaneConstants.VERTICAL_SCROLLBAR_ALWAYS);\r
+               this.getVerticalScrollBar().setUnitIncrement(25);\r
+               this.setColumnHeaderView(getJPanel_update());\r
+               this.setViewportView(getJPanel_chDatSetting());\r
+               \r
+       }\r
+       \r
+       /*******************************************************************************\r
+        * アクション\r
+        ******************************************************************************/\r
+       \r
+       /**\r
+        * 更新を確定する本体\r
+        */\r
+       private void updateChDatSetting() {\r
+               \r
+               TatCount tc = new TatCount();\r
+               \r
+               StWin.clear();\r
+               \r
+               new SwingBackgroundWorker(false) {\r
+                       \r
+                       @Override\r
+                       protected Object doWorks() throws Exception {\r
+                               \r
+                               StWin.appendMessage(MSGID+"設定を保存します");\r
+                               \r
+                               String recId = selectedRecorder.getRecorderId();\r
+                               \r
+                               //DefaultTableModel model = (DefaultTableModel) jTable_entries.getModel();\r
+                               ArrayList<String> webChName = new ArrayList<String>();\r
+                               ArrayList<String> recChName = new ArrayList<String>();\r
+                               ArrayList<String> chCode = new ArrayList<String>();\r
+                               for ( ChDatItem c: rowData ) {\r
+                                       if ( c.recChName.equals("") && c.chCode.equals("") ) {\r
+                                               continue;       // 無効なエントリ\r
+                                       }\r
+                                       if ( selectedRecorder.isChCodeNeeded() && c.chCode.equals("") ) {\r
+                                               MWin.appendError(ERRID+"放送局コードを指定してください: "+recId+", "+c.recChName);\r
+                                               ringBeep();\r
+                                               return null;\r
+                                       }\r
+                                       \r
+                                       webChName.add(c.webChName);\r
+                                       \r
+                                       recChName.add(c.recChName);\r
+                                       \r
+                                       String chcode = null;\r
+                                       if ( ! selectedRecorder.isRecChNameNeeded() ) {\r
+                                               // NULL\r
+                                               if ( c.recChName.equals("-") ) {\r
+                                                       chcode = c.chCode;\r
+                                               }\r
+                                               else {\r
+                                                       chcode = c.recChName;\r
+                                               }\r
+                                       }\r
+                                       else if ( selectedRecorder.isChValueAvailable() || selectedRecorder.isChCodeNeeded() ) {\r
+                                               // EDCB, TvRock, REGZA || RD\r
+                                               chcode = c.chCode;\r
+                                       }\r
+                                       else {\r
+                                               // DIGA\r
+                                               chcode = c.recChName;\r
+                                       }\r
+                                       if ( selectedRecorder.isBroadcastTypeNeeded() ) {\r
+                                               switch (c.bType) {\r
+                                               case NONE:\r
+                                                       break;\r
+                                               default:\r
+                                                       // TvRock対応\r
+                                                       chcode = c.bType.getName()+":"+chcode;\r
+                                                       break;\r
+                                               }\r
+                                       }\r
+                                       chCode.add(chcode);\r
+                               }\r
+                               \r
+                               if (debug) {\r
+                                       for ( int n=0; n<webChName.size(); n++ ) {\r
+                                               System.out.println(String.format(DBGID+"[SAVE] %s,%s,%s",webChName.get(n),recChName.get(n),chCode.get(n)));\r
+                                       }\r
+                               }\r
+                               \r
+                               ChannelCode cc = new ChannelCode(recId);\r
+                               cc.save(webChName, recChName, chCode);\r
+                               \r
+                               // ここがあるのでSwingBackgroundWorkerを使わざるをえない\r
+                               for ( HDDRecorder rec : recorders.get(recId) ) {\r
+                                       // 関連するプラグインを全部リロードする\r
+                                       rec.getChCode().load(false);    // ログ出力あり\r
+                                       rec.GetRdReserve(false);\r
+                               }\r
+                               return null;\r
+                       }\r
+                       \r
+                       @Override\r
+                       protected void doFinally() {\r
+                               StWinSetVisible(false);\r
+                       }\r
+               }.execute();\r
+\r
+               StWinSetLocationCenter(parent);\r
+               StWinSetVisible(true);\r
+\r
+               MWin.appendMessage(String.format(MSGID+"更新が完了しました。所要時間: %.2f秒",tc.end()));\r
+               //MWin.appendError(MSGID+"【重要】 再起動してCHコード設定の変更をレコーダプラグインに反映してください。");\r
+               //ringBeep();\r
+       }\r
+       \r
+       /**\r
+        * <P>RdChannelCode.datをテーブルに展開する(CH設定からも呼ばれる)\r
+        * <P>ここで取得した selectedRecorder は各所で使いまわす。レコーダ設定の変更には注意が必要\r
+        */\r
+       public void updateChannelDatTable()     {\r
+               //\r
+               String recId = (String) jComboBox_recorderId.getSelectedItem();\r
+               \r
+               if ( recId == null ) {\r
+                       MWin.appendError(ERRID+"レコーダが選択できません");\r
+                       return;\r
+               }\r
+               \r
+               ArrayList<HDDRecorder> ra = recorders.get(recId);\r
+               if ( ra.size() == 0 ) {\r
+                       MWin.appendError(ERRID+"指定のレコーダのプラグインがみつかりません: "+recId);\r
+                       return;\r
+               }\r
+               \r
+               // 選択したレコーダを保存する\r
+               selectedRecorder = ra.get(0);\r
+               \r
+               ChannelCode cc = new ChannelCode(recId);\r
+               cc.load(false); // ログ出力なし\r
+\r
+               Boolean availableauto = (selectedRecorder.getChValue().size() != 0) && selectedRecorder.isChCodeNeeded();\r
+               \r
+               jTable_entries.setChValueAvailable(selectedRecorder.isChValueAvailable());\r
+               jTable_entries.setChCodeEnabled(selectedRecorder.isChCodeNeeded());\r
+               jTable_entries.setBroadcastTypeEnabled(selectedRecorder.isBroadcastTypeNeeded());\r
+\r
+               rowData.clear();\r
+               \r
+               /*\r
+                *  従来のフォーマットとの互換を保つため、いろいろ加工が入る。表示と保存も気を付けなければならない。\r
+                */\r
+               \r
+               // 定義にある放送局をリストアップする\r
+               for ( String webChName : cc.getChNames() ) {\r
+                       ChDatItem sa = new ChDatItem();\r
+                       sa.webChName = webChName;\r
+                       sa.chCode = cc.getCH_WEB2CODE(webChName);\r
+                       sa.recChName = cc.getCH_CODE2REC(sa.chCode);\r
+                       \r
+                       if ( selectedRecorder.isChValueAvailable() ) {\r
+                               String s = selectedRecorder.value2text(selectedRecorder.getChValue(), sa.chCode);\r
+                               if ( s != null && s.length() > 0 ) {\r
+                                       sa.recChName = s;\r
+                               }\r
+                       }\r
+                       \r
+                       sa.bType = null;\r
+                       if ( selectedRecorder.isBroadcastTypeNeeded() ) {\r
+                               // TvRock対応\r
+                               if ( sa.chCode != null ) {\r
+                                       String[] d = sa.chCode.split(":",2);\r
+                                       if ( d.length == 2 ) {\r
+                                               sa.chCode = d[1];\r
+                                               sa.bType = BroadcastType.get(d[0]);\r
+                                       }\r
+                               }\r
+                       }\r
+                       if ( sa.bType == null ) {\r
+                               sa.bType = BroadcastType.NONE;\r
+                       }\r
+\r
+                       if ( ! selectedRecorder.isRecChNameNeeded() ) {\r
+                               //\r
+                       }\r
+                       else if ( ! selectedRecorder.isChCodeNeeded() && ! selectedRecorder.isChValueAvailable() ) {\r
+                               sa.chCode = "";\r
+                       }\r
+\r
+                       sa.availableAuto = availableauto;\r
+                       sa.fireChanged();\r
+                       \r
+                       rowData.add(sa);\r
+               }\r
+               \r
+               // 定義にない放送局をリストアップする\r
+               {\r
+                       TVProgramIterator pli = tvprograms.getIterator().build(chsort.getClst(), IterationType.ALL);\r
+                       for ( ProgList pl : pli ) {\r
+                               boolean exists = false;\r
+                               for ( ChDatItem c : rowData ) {\r
+                                       if ( pl.Center.equals(c.webChName) ) {\r
+                                               exists = true;\r
+                                               break;\r
+                                       }\r
+                               }\r
+                               if ( ! exists ) {\r
+                                       ChDatItem sa = new ChDatItem();\r
+                                       sa.webChName = pl.Center;\r
+                                       if ( selectedRecorder.isRecChNameNeeded() ) {\r
+                                               sa.recChName = "";\r
+                                       }\r
+                                       else {\r
+                                               sa.recChName = sa.webChName;\r
+                                       }\r
+                                       sa.chCode = "";\r
+                                       sa.bType = BroadcastType.NONE;\r
+                                       sa.availableAuto = availableauto;\r
+                                       sa.fireChanged();\r
+                                       rowData.add(sa);\r
+                               }\r
+                       }\r
+               }\r
+               \r
+               if ( selectedRecorder.isChValueAvailable() ) {\r
+                       JComboBox combo = ((JComboBox) editorCombo_recchname.getComponent());\r
+                       combo.removeAllItems();\r
+                       combo.addItem("");\r
+                       for ( TextValueSet t : selectedRecorder.getChValue() ) {\r
+                               combo.addItem(t.getText());\r
+                       }\r
+                       jTable_entries.getColumn(ChDatColumn.RECCHNAME.getName()).setCellEditor(editorCombo_recchname);\r
+               }\r
+               else {\r
+                       //popupを無効にできないかなー\r
+                       jTable_entries.getColumn(ChDatColumn.RECCHNAME.getName()).setCellEditor(editorField_recchname);\r
+               }\r
+               \r
+               ((DefaultTableModel) jTable_entries.getModel()).fireTableDataChanged();\r
+               \r
+               // ヘルプの表示\r
+               jta_chdathelp.setText(selectedRecorder.getChDatHelp());\r
+       }\r
+\r
+       /**\r
+        * レコーダコンボボックスを設定しなおす(レコーダ設定タブからも呼ばれる)\r
+        */\r
+       public void updateRecorderComboBox() {\r
+               \r
+               jComboBox_recorderId.removeItemListener(il_recorderChanged);    // 停止\r
+               \r
+               ArrayList<String> ra = new ArrayList<String>();\r
+               for ( HDDRecorder r : recorders ) {\r
+                       if ( r.isBackgroundOnly() ) {\r
+                               continue;\r
+                       }\r
+                       \r
+                       if ( ! ra.contains(r.getRecorderId()) ) {\r
+                               ra.add(r.getRecorderId());\r
+                       }\r
+               }\r
+               \r
+               // 初期値を設定\r
+               jComboBox_recorderId.removeAllItems();;\r
+               for ( String s : ra ) {\r
+                       jComboBox_recorderId.addItem(s);\r
+               }\r
+               \r
+               jComboBox_recorderId.addItemListener(il_recorderChanged);       // 再開\r
+       }\r
+       \r
+       /*******************************************************************************\r
+        * リスナー\r
+        ******************************************************************************/\r
+       \r
+       /**\r
+        * 更新を確定する\r
+        */\r
+       private final ActionListener al_update = new ActionListener() {\r
+               @Override\r
+               public void actionPerformed(ActionEvent e) {\r
+                       updateChDatSetting();\r
+               }\r
+       };\r
+       \r
+       /**\r
+        * レコーダが選択された\r
+        */\r
+       private final ItemListener il_recorderChanged = new ItemListener() {\r
+               @Override\r
+               public void itemStateChanged(ItemEvent e) {\r
+                       if (debug) System.out.println(DBGID+"il_recorderChanged itemStateChanged "+e.paramString());\r
+                       if ( e.getStateChange() == ItemEvent.SELECTED ) {\r
+                               updateChannelDatTable();\r
+                       }\r
+               }\r
+       };\r
+       \r
+       private final ActionListener al_removeCenter = new ActionListener() {\r
+               @Override\r
+               public void actionPerformed(ActionEvent e) {\r
+                       int[] vrows = jTable_entries.getSelectedRows();\r
+                       Arrays.sort(vrows);\r
+                       for ( int i=vrows.length-1; i>=0; i-- ) {\r
+                               int vrow = vrows[i];\r
+                               if (vrow >=0 && vrow <= jTable_entries.getRowCount()-1) {\r
+                                       rowData.remove(vrow);\r
+                               }\r
+                       }\r
+                       ((DefaultTableModel) jTable_entries.getModel()).fireTableDataChanged();\r
+               }\r
+       };\r
+       \r
+       private final ActionListener al_upCenter = new ActionListener() {\r
+               @Override\r
+               public void actionPerformed(ActionEvent e) {\r
+                       int vrow = jTable_entries.getSelectedRow();\r
+                       int cnt = jTable_entries.getSelectedRowCount();\r
+                       if ( rowData.up(vrow, cnt) ) {\r
+                               ((DefaultTableModel) jTable_entries.getModel()).fireTableDataChanged();\r
+                               jTable_entries.setRowSelectionInterval(vrow-1, vrow-1+(cnt-1));\r
+                       }\r
+               }\r
+       };\r
+       \r
+       private final ActionListener al_downCenter = new ActionListener() {\r
+               @Override\r
+               public void actionPerformed(ActionEvent e) {\r
+                       int vrow = jTable_entries.getSelectedRow();\r
+                       int cnt = jTable_entries.getSelectedRowCount();\r
+                       if ( rowData.down(vrow, cnt) ) {\r
+                               ((DefaultTableModel) jTable_entries.getModel()).fireTableDataChanged();\r
+                               jTable_entries.setRowSelectionInterval(vrow+1, vrow+1+(cnt-1));\r
+                       }\r
+               }\r
+       };\r
+\r
+       /**\r
+        * 放送局の強制追加\r
+        */\r
+       private final MouseListener ml_addCenter = new MouseAdapter() {\r
+               @Override\r
+               public void mouseClicked(MouseEvent e) {\r
+                       // 空文字列は対象外\r
+                       String newCenter = jTextField_addCenter.getText().trim();\r
+                       if (newCenter.length() == 0) {\r
+                               return;\r
+                       }\r
+                       \r
+                       // 重複チェック\r
+                       for ( ChDatItem c : rowData ) {\r
+                               if (c.webChName.equals(newCenter)) {\r
+                                       JOptionPane.showConfirmDialog(null, "放送局名が重複しています。", "警告", JOptionPane.CLOSED_OPTION);\r
+                                       return;\r
+                               }\r
+                       }\r
+                       \r
+                       // 表へ追加\r
+                       Boolean availableauto = false;\r
+                       for ( HDDRecorder recorder : recorders ) {\r
+                               if ( recorder.getRecorderId().equals(selectedRecorder.getRecorderId())) {\r
+                                       availableauto = (recorder.getChValue().size() != 0) && recorder.isChCodeNeeded();\r
+                               }\r
+                       }\r
+                       \r
+                       ChDatItem sa = new ChDatItem();\r
+                       sa.webChName = newCenter;\r
+                       sa.chCode = "";\r
+                       sa.recChName = "";\r
+                       sa.bType = BroadcastType.NONE;\r
+                       sa.availableAuto = availableauto;\r
+                       sa.fireChanged();\r
+                       rowData.add(sa);\r
+                       \r
+                       jTextField_addCenter.setText("");\r
+                       \r
+                       ((DefaultTableModel) jTable_entries.getModel()).fireTableDataChanged();\r
+               }\r
+       };\r
+       \r
+       \r
+       /*******************************************************************************\r
+        * コンポーネント\r
+        ******************************************************************************/\r
+       \r
+       private JPanel getJPanel_update() {\r
+               if (jPanel_update == null)\r
+               {\r
+                       jPanel_update = new JPanel();\r
+                       jPanel_update.setLayout(new SpringLayout());\r
+                       \r
+                       jPanel_update.setBorder(new LineBorder(Color.GRAY));\r
+                       \r
+                       int y = SEP_HEIGHT;\r
+                       CommonSwingUtils.putComponentOn(jPanel_update, getJButton_update("更新を確定する"), UPDATE_WIDTH, PARTS_HEIGHT, SEP_WIDTH, y);\r
+                       \r
+                       int yz = SEP_HEIGHT/2;\r
+                       int x = UPDATE_WIDTH+50;\r
+                       CommonSwingUtils.putComponentOn(jPanel_update, getJta_help(), HINT_WIDTH, PARTS_HEIGHT+SEP_HEIGHT, x, yz);\r
+                       \r
+                       y += (PARTS_HEIGHT + SEP_HEIGHT);\r
+                       \r
+                       // 画面の全体サイズを決める\r
+                       Dimension d = new Dimension(PANEL_WIDTH,y);\r
+                       jPanel_update.setPreferredSize(d);\r
+               }\r
+               return jPanel_update;\r
+       }\r
+       \r
+       private JPanel getJPanel_chDatSetting() {\r
+               if (jPanel_chDatSetting == null)\r
+               {\r
+                       jPanel_chDatSetting = new JPanel();\r
+                       jPanel_chDatSetting.setLayout(new SpringLayout());\r
+                       \r
+                       //\r
+                       int y = SEP_HEIGHT;\r
+                       int x = SEP_WIDTH;\r
+                       CommonSwingUtils.putComponentOn(jPanel_chDatSetting, getJLabel_recorderId("レコーダ種別"), LABEL_WIDTH, PARTS_HEIGHT, x, y);\r
+                       CommonSwingUtils.putComponentOn(jPanel_chDatSetting, getJComboBox_recorderId(), LABEL_WIDTH, PARTS_HEIGHT, x+=LABEL_WIDTH+SEP_WIDTH, y);\r
+                       CommonSwingUtils.putComponentOn(jPanel_chDatSetting, getJta_chdathelp(), 550, PARTS_HEIGHT*2, x+LABEL_WIDTH+SEP_WIDTH, y);\r
+\r
+                       y+=(PARTS_HEIGHT*2+SEP_HEIGHT);\r
+                       x = SEP_WIDTH+LABEL_WIDTH+SEP_WIDTH;\r
+                       int table_h = 450;\r
+                       CommonSwingUtils.putComponentOn(jPanel_chDatSetting, getJScrollPane_entries(), TABLE_WIDTH, table_h, x, y);\r
+                       \r
+                       if ( jComboBox_recorderId.getItemCount() > 0 ) {\r
+                               updateChannelDatTable();\r
+                       }\r
+\r
+                       int yz = y+table_h/2;\r
+                       int xz = x - (BUTTON_WIDTH+SEP_WIDTH);\r
+                       CommonSwingUtils.putComponentOn(jPanel_chDatSetting, getJButton_upCenter("上へ"), BUTTON_WIDTH, PARTS_HEIGHT, xz, yz-50);\r
+                       CommonSwingUtils.putComponentOn(jPanel_chDatSetting, getJButton_downCenter("下へ"), BUTTON_WIDTH, PARTS_HEIGHT, xz, yz+50);\r
+\r
+                       y+=(table_h-PARTS_HEIGHT);\r
+                       xz = x - (BUTTON_WIDTH_LONG+SEP_WIDTH); \r
+                       CommonSwingUtils.putComponentOn(jPanel_chDatSetting, getJButton_removeCenter("放送局名を削除する"), BUTTON_WIDTH_LONG, PARTS_HEIGHT, xz, y);\r
+                       \r
+                       y+=(PARTS_HEIGHT+SEP_HEIGHT);\r
+                       CommonSwingUtils.putComponentOn(jPanel_chDatSetting, getJButton_addCenter("放送局名を追加する"), BUTTON_WIDTH_LONG, PARTS_HEIGHT, xz, y);\r
+                       CommonSwingUtils.putComponentOn(jPanel_chDatSetting, getJTextField_addCenter(), ChDatColumn.WEBCHNAME.getIniWidth(), PARTS_HEIGHT, x, y);\r
+                       \r
+                       y+=(PARTS_HEIGHT+SEP_HEIGHT);\r
+                       \r
+                       Dimension d = new Dimension(PANEL_WIDTH,y);\r
+                       jPanel_chDatSetting.setPreferredSize(d);\r
+               }\r
+                       \r
+               return jPanel_chDatSetting;\r
+       }\r
+       \r
+       private JButton getJButton_update(String s)\r
+       {\r
+               if (jButton_update == null) {\r
+                       jButton_update = new JButton(s);\r
+                       \r
+                       jButton_update.addActionListener(al_update);\r
+               }\r
+               return(jButton_update);\r
+       }\r
+       \r
+       // 放送局を上へ・下へ\r
+       private JButton getJButton_upCenter(String s) {\r
+               if (jButton_upCenter == null) {\r
+                       jButton_upCenter = new JButton(s);\r
+                       jButton_upCenter.addActionListener(al_upCenter);\r
+               }\r
+               return(jButton_upCenter);\r
+       }\r
+       \r
+       private JButton getJButton_downCenter(String s) {\r
+               if (jButton_downCenter == null) {\r
+                       jButton_downCenter = new JButton(s);\r
+                       jButton_downCenter.addActionListener(al_downCenter);\r
+               }\r
+               return(jButton_downCenter);\r
+       }\r
+       \r
+       // 削除しちゃうよ\r
+       private JButton getJButton_removeCenter(String s) {\r
+               if (jButton_removeCenter == null) {\r
+                       jButton_removeCenter = new JButton(s);\r
+                       jButton_removeCenter.addActionListener(al_removeCenter);\r
+               }\r
+               return jButton_removeCenter;\r
+       }\r
+       \r
+       // Web番組表に存在しない放送局の強制追加\r
+       private JButton getJButton_addCenter(String s) {\r
+               if (jButton_addCenter == null) {\r
+                       jButton_addCenter = new JButton(s);\r
+                       \r
+                       jButton_addCenter.addMouseListener(ml_addCenter);\r
+               }\r
+               return(jButton_addCenter);\r
+       }\r
+       private JTextField getJTextField_addCenter() {\r
+               if (jTextField_addCenter == null) {\r
+                       jTextField_addCenter = new JTextFieldWithPopup();\r
+               }\r
+               return jTextField_addCenter;\r
+       }\r
+       \r
+       //\r
+       private JLabel getJLabel_recorderId(String s) {\r
+               if (jLabel_recorderId == null) {\r
+                       jLabel_recorderId = new JLabel();\r
+                       jLabel_recorderId.setText(s);\r
+               }\r
+               return jLabel_recorderId;\r
+       }\r
+       \r
+       private JComboBox getJComboBox_recorderId() {\r
+               if (jComboBox_recorderId == null) {\r
+                       jComboBox_recorderId = new JComboBox();\r
+                       jComboBox_recorderId.setEditable(false);\r
+\r
+                       updateRecorderComboBox();       // 初期化\r
+                       \r
+                       jComboBox_recorderId.addItemListener(il_recorderChanged);\r
+               }\r
+               return jComboBox_recorderId;\r
+       }\r
+       \r
+       //\r
+       private JScrollPane getJScrollPane_entries() {\r
+               if (jScrollPane_entries == null) {\r
+                       jScrollPane_entries = new JScrollPane();\r
+                       jScrollPane_entries.setViewportView(getJTable_entries());\r
+                       jScrollPane_entries.setVerticalScrollBarPolicy(ScrollPaneConstants.VERTICAL_SCROLLBAR_ALWAYS);\r
+               }\r
+               return(jScrollPane_entries);\r
+       }\r
+       \r
+       private JTable getJTable_entries() {\r
+               if (jTable_entries == null) {\r
+                       \r
+                       jTable_entries = new ChDatTable(rowData) ;\r
+                       \r
+                       // テーブルの基本的な設定\r
+                       ArrayList<String> cola = new ArrayList<String>();\r
+                       for ( ChDatColumn rc : ChDatColumn.values() ) {\r
+                               if ( rc.getIniWidth() >= 0 ) {\r
+                                       cola.add(rc.getName());\r
+                               }\r
+                       }\r
+                       DefaultTableModel model = new DefaultTableModel(cola.toArray(new String[0]), 0);\r
+                       jTable_entries.setModel(model);\r
+                       \r
+                       jTable_entries.setAutoResizeMode(JNETable.AUTO_RESIZE_OFF);\r
+                       jTable_entries.setSelectionMode(ListSelectionModel.SINGLE_INTERVAL_SELECTION);\r
+                       jTable_entries.getTableHeader().setReorderingAllowed(false);\r
+                       jTable_entries.putClientProperty("terminateEditOnFocusLost", true);     // これやらないと、編集が確定したように見えて確定しない\r
+                       //jTable_entries.setRowHeight(jTable_entries.getRowHeight()+4);\r
+                       \r
+                       // 各カラムの幅\r
+                       DefaultTableColumnModel columnModel = (DefaultTableColumnModel)jTable_entries.getColumnModel();\r
+                       TableColumn column = null;\r
+                       for ( ChDatColumn rc : ChDatColumn.values() ) {\r
+                               if ( rc.getIniWidth() < 0 ) {\r
+                                       continue;\r
+                               }\r
+                               columnModel.getColumn(rc.ordinal()).setPreferredWidth(rc.getIniWidth());;\r
+                       }\r
+                       \r
+                       // レコーダの放送局名\r
+                       editorCombo_recchname = new DefaultCellEditor(new RecorderChannelNameComboBox());\r
+                       editorCombo_recchname.setClickCountToStart(0);\r
+                       editorField_recchname = new DefaultCellEditor(new RecoderCnannelNameTextField());\r
+                       editorField_recchname.setClickCountToStart(1);\r
+                       jTable_entries.getColumn(ChDatColumn.RECCHNAME.getName()).setCellEditor(editorField_recchname);\r
+                       \r
+                       // 放送局コード\r
+                       jTable_entries.getColumn(ChDatColumn.CHCODE.getName()).setCellEditor(new EditorColumn());\r
+\r
+                       // 放送局コードコンボボックス\r
+                       BroadcastTypeComboBox bTypeBox = new BroadcastTypeComboBox();\r
+                       jTable_entries.getColumn(ChDatColumn.BTYPE.getName()).setCellEditor(new DefaultCellEditor(bTypeBox));\r
+                       \r
+                       // 自動設定ボタン\r
+                       ButtonColumn buttonColumn = new ButtonColumn();\r
+                       column = jTable_entries.getColumn(ChDatColumn.AUTO.getName());\r
+                       column.setCellRenderer(buttonColumn);\r
+                       column.setCellEditor(buttonColumn);\r
+                       column.setResizable(false);\r
+               }\r
+               return jTable_entries;\r
+       }\r
+       \r
+       //\r
+       private JTextAreaWithPopup getJta_chdathelp() {\r
+               jta_chdathelp = CommonSwingUtils.getJta(this,2,0);\r
+               jta_chdathelp.setForeground(Color.BLUE);\r
+               return jta_chdathelp;\r
+       }\r
+\r
+       //\r
+       private JTextAreaWithPopup getJta_help() {\r
+               if ( jta_help == null ) {\r
+                       jta_help = CommonSwingUtils.getJta(this,2,0);\r
+                       jta_help.setText(TEXT_HINT);\r
+               }\r
+               return jta_help;\r
+       }\r
+       \r
+       \r
+       \r
+       /*******************************************************************************\r
+        * 独自部品\r
+        ******************************************************************************/\r
+\r
+       private class ChDatItem extends RowItem implements Cloneable {\r
+               String webChName;\r
+               String recChName;\r
+               String chCode;\r
+               BroadcastType bType;\r
+               Boolean availableAuto;\r
+               \r
+               @Override\r
+               protected void myrefresh(RowItem o) {\r
+                       ChDatItem c = (ChDatItem) o;\r
+                       c.addData(webChName);\r
+                       c.addData(recChName);\r
+                       c.addData(chCode);\r
+                       c.addData(bType);\r
+                       c.addData(availableAuto);\r
+               }\r
+               \r
+               @Override\r
+               public ChDatItem clone() {\r
+                       return (ChDatItem) super.clone();\r
+               }\r
+       }\r
+\r
+       private class ChDatTable extends JTable {\r
+\r
+               private static final long serialVersionUID = 1L;\r
+               \r
+               private final Color evenColor = new Color(240,240,255);\r
+               private final Color oddColor = super.getBackground();\r
+               \r
+               private final Color disabledOddColor = new Color(200,200,200);\r
+               private final Color disabledEvenColor = new Color(180,180,180);\r
+               \r
+               private RowItemList<ChDatItem> rowdata = null;\r
+               \r
+               public ChDatTable(RowItemList<ChDatItem> rowdata) {\r
+                       super();\r
+                       this.rowdata = rowdata;\r
+                       \r
+                       // フォントサイズ変更にあわせて行の高さを変える\r
+                       this.addPropertyChangeListener("font", new RowHeightChangeListener(8));\r
+\r
+                       // 行の高さの初期値の設定\r
+                       this.firePropertyChange("font", "old", "new");\r
+               }\r
+\r
+               public void setChValueAvailable(boolean b) { chvalueavailable = b; }\r
+               private boolean chvalueavailable = true;\r
+\r
+               public void setChCodeEnabled(boolean b) { chcodeenabled = b; }\r
+               private boolean chcodeenabled = true;\r
+               \r
+               public void setBroadcastTypeEnabled(boolean b) { broadcasttypeenabled = b; }\r
+               private boolean broadcasttypeenabled = false;\r
+                       \r
+               @Override\r
+               public boolean isCellEditable(int row, int column) {\r
+                       if ( column == ChDatColumn.WEBCHNAME.getColumn() ) {\r
+                               return false;   // Web番組表の放送局名は編集できない\r
+                       }\r
+                       else if ( ! chcodeenabled && column == ChDatColumn.CHCODE.getColumn() ) {\r
+                               return false;\r
+                       }\r
+                       else if ( ! broadcasttypeenabled && column == ChDatColumn.BTYPE.getColumn() ) {\r
+                               return false;\r
+                       }\r
+                       return true;\r
+               }\r
+               \r
+               @Override\r
+               public Component prepareRenderer(TableCellRenderer tcr, int row, int column) {\r
+                       Component comp = super.prepareRenderer(tcr, row, column);\r
+                       \r
+                       Color fg = null;\r
+                       Color bg = null;\r
+                       \r
+                       boolean evenline = (row%2 == 1);\r
+                       \r
+                       if ( column == ChDatColumn.WEBCHNAME.getColumn() ) {\r
+                               fg = Color.BLUE;\r
+                               bg = (evenline)?(disabledEvenColor):(disabledOddColor);\r
+                       }\r
+                       else if ( column == ChDatColumn.RECCHNAME.getColumn() ) {\r
+                               //\r
+                       }\r
+                       else if ( column == ChDatColumn.CHCODE.getColumn() ) {\r
+                               if ( ! chcodeenabled ) bg = (evenline)?(disabledEvenColor):(disabledOddColor);\r
+                       }\r
+                       else if ( column == ChDatColumn.BTYPE.getColumn() ) {\r
+                               if ( ! broadcasttypeenabled ) bg = (evenline)?(disabledEvenColor):(disabledOddColor);\r
+                       }\r
+                       if(isRowSelected(row)) {\r
+                               if (fg==null) fg = this.getSelectionForeground();\r
+                               bg = this.getSelectionBackground();\r
+                       }\r
+                       else {\r
+                               if (fg==null) fg = this.getForeground();\r
+                               if (bg==null) bg = (evenline)?(evenColor):(oddColor);\r
+                       }\r
+                       \r
+                       comp.setForeground(fg);\r
+                       comp.setBackground(bg);\r
+                                       \r
+                       return comp;\r
+               }\r
+               \r
+               @Override\r
+               public int getRowCount() { return rowdata.size(); }\r
+               \r
+               @Override\r
+               public Object getValueAt(int row, int column) {\r
+                       ChDatItem c = rowdata.get(row);\r
+                       if ( column == ChDatColumn.CHCODE.getColumn() ) {\r
+                               String chcode = null;\r
+                               if ( chcodeenabled || chvalueavailable || ( ! chcodeenabled && c.recChName.equals("-")) ) {\r
+                                       chcode = c.chCode;\r
+                               }\r
+                               else {\r
+                                       chcode = c.recChName;\r
+                               }\r
+                               if ( broadcasttypeenabled ) {\r
+                                       if ( c.bType != BroadcastType.NONE ) {\r
+                                               chcode = c.bType.getName()+":"+chcode;\r
+                                       }\r
+                               }\r
+                               return chcode;\r
+                       }\r
+                       else if ( column == ChDatColumn.BTYPE.getColumn() ) {\r
+                               \r
+                               // 設定値から放送波の種別を判断できるレコーダもある\r
+                               String selected = selectedRecorder.getRecorderId();\r
+                               if ( selected != null ) {\r
+                                       BroadcastType bt = BroadcastType.get(selected, c.recChName, c.chCode);\r
+                                       if ( bt != null ) {\r
+                                               return bt.getName();\r
+                                       }\r
+                               }\r
+                               \r
+                               return c.bType.getName();\r
+                       }\r
+                       return rowdata.get(row).get(column);\r
+               }\r
+               \r
+               @Override\r
+               public void setValueAt(Object aValue, int row, int column) {\r
+                       ChDatItem c = rowdata.get(row);\r
+                       if ( column == ChDatColumn.WEBCHNAME.getColumn() ) {\r
+                               c.webChName = (String) aValue;\r
+                       }\r
+                       else if ( column == ChDatColumn.RECCHNAME.getColumn() ) {\r
+                               c.recChName = (String) aValue;\r
+                               if ( chvalueavailable ) {\r
+                                       // EDCB対応\r
+                                       if ( c.recChName == null || c.recChName.length() == 0 ) {\r
+                                               // 消したいらしいよ\r
+                                               c.chCode = "";\r
+                                       }\r
+                                       else {\r
+                                               String chcode = selectedRecorder.text2value(selectedRecorder.getChValue(), c.recChName);\r
+                                               if ( chcode != null && chcode.length() > 0 ) {\r
+                                                       // chvalueに情報があったよ\r
+                                                       c.chCode = chcode;\r
+                                               }\r
+                                               else {\r
+                                                       if ( c.chCode == null || c.chCode.length() == 0 ) {\r
+                                                               // chvalueに情報がないよ\r
+                                                               c.chCode = c.recChName;\r
+                                                       }\r
+                                               }\r
+                                       }\r
+                               }\r
+                       }\r
+                       else if ( column == ChDatColumn.CHCODE.getColumn() ) {\r
+                               c.chCode = (String) aValue;\r
+                       }\r
+                       else if ( column == ChDatColumn.BTYPE.getColumn() ) {\r
+                               c.bType = BroadcastType.get((String) aValue);\r
+                       }\r
+                       else if ( column == ChDatColumn.AUTO.getColumn() ) {\r
+                               c.availableAuto = (Boolean) aValue;\r
+                       }\r
+                       c.fireChanged();\r
+                       return;\r
+               }\r
+       }\r
+       \r
+       /**\r
+        * レコーダの放送局名入力フィールド\r
+        */\r
+       private class RecoderCnannelNameTextField extends JTextFieldWithPopup {\r
+\r
+               private static final long serialVersionUID = 1L;\r
+\r
+               private final AncestorListener al_recChNameChanged = new AncestorListener() {\r
+                       @Override\r
+                       public void ancestorRemoved(AncestorEvent e) {\r
+                               if (debug) System.out.println(DBGID+"al_recChNameChanged/ancestorRemoved "+e.toString());\r
+                               int row = jTable_entries.getSelectedRow();\r
+                               if ( row >= 0 ) {\r
+                                       // 一行まるごと更新\r
+                                       jTable_entries.clearSelection();\r
+                                       jTable_entries.setRowSelectionInterval(row, row);\r
+                               }\r
+                       }\r
+                       @Override\r
+                       public void ancestorMoved(AncestorEvent e) {\r
+                               if (debug) System.out.println(DBGID+"al_recChNameChanged/ancestorMoved "+e.toString());\r
+                       }\r
+                       @Override\r
+                       public void ancestorAdded(AncestorEvent e) {\r
+                               if (debug) System.out.println(DBGID+"al_recChNameChanged/ancestorAdded "+e.toString());\r
+                       }\r
+               };\r
+               \r
+               public RecoderCnannelNameTextField() {\r
+                       super();\r
+                       this.setEditable(true);\r
+                       \r
+                       this.addAncestorListener(al_recChNameChanged);\r
+               }\r
+               \r
+       }\r
+       \r
+       /**\r
+        * レコーダの放送局名選択コンボボックス\r
+        */\r
+       private class RecorderChannelNameComboBox extends JComboBoxWithPopup {\r
+\r
+               private static final long serialVersionUID = 1L;\r
+\r
+               private final ItemListener il_recChNameChanged = new ItemListener() {\r
+                       @Override\r
+                       public void itemStateChanged(ItemEvent e) {\r
+                               if (debug) System.out.println(DBGID+"il_recChNameChanged "+e.paramString());\r
+                               if ( e.getStateChange() == ItemEvent.DESELECTED ) {\r
+                                       int row = jTable_entries.getSelectedRow();\r
+                                       if ( row >= 0 ) {\r
+                                               // 一行まるごと更新\r
+                                               jTable_entries.clearSelection();\r
+                                               jTable_entries.setRowSelectionInterval(row, row);\r
+                                       }\r
+                               }\r
+                       }\r
+               };\r
+               \r
+               @Override\r
+               public void setSelectedItem(Object anObject) {\r
+                       for ( int i=0; i<getItemCount(); i++ ) {\r
+                               if ( getItemAt(i).toString().equals(anObject.toString()) ) {\r
+                                       super.setSelectedItem(anObject);\r
+                                       return;\r
+                               }\r
+                       }\r
+                       if (anObject != null && ((String) anObject).length() > 0) {\r
+                               addItem(anObject);\r
+                               if (debug) System.out.println(DBGID+"選択肢を追加: "+anObject.toString());\r
+                       }\r
+                       super.setSelectedItem(anObject);\r
+               }\r
+               \r
+               public RecorderChannelNameComboBox() {\r
+                       super();\r
+                       this.setEditable(false);\r
+                       this.setMaximumRowCount(15);\r
+                       \r
+                       this.addItemListener(il_recChNameChanged);\r
+               }\r
+       }\r
+       \r
+       /**\r
+        * 地上波/BS/CS選択コンボボックス\r
+        */\r
+       private class BroadcastTypeComboBox extends JComboBox {\r
+\r
+               private static final long serialVersionUID = 1L;\r
+\r
+               private final ItemListener il_btypeChanged = new ItemListener() {\r
+                       @Override\r
+                       public void itemStateChanged(ItemEvent e) {\r
+                               if (debug) System.out.println(DBGID+"il_btypeChanged "+e.paramString());\r
+                               if ( e.getStateChange() == ItemEvent.SELECTED ) {\r
+                                       int row = jTable_entries.getSelectedRow();\r
+                                       if ( row >= 0 ) {\r
+                                               // 一行まるごと更新\r
+                                               jTable_entries.clearSelection();\r
+                                               jTable_entries.setRowSelectionInterval(row, row);\r
+                                       }\r
+                               }\r
+                       }\r
+               };\r
+               \r
+               public BroadcastTypeComboBox() {\r
+                       super();\r
+                       this.setEditable(false);\r
+                       for ( BroadcastType b : BroadcastType.values() ) {\r
+                               this.addItem(b.getName());\r
+                       }\r
+                       \r
+                       this.addItemListener(il_btypeChanged);\r
+               }\r
+       }\r
+       \r
+       \r
+       /**\r
+        * AUTOボタン\r
+        */\r
+       private class ButtonColumn extends AbstractCellEditor implements TableCellRenderer, TableCellEditor { \r
+\r
+               private static final long serialVersionUID = 1L;\r
+\r
+               private static final String LABEL = "SET";\r
+               private final JButton renderButton = new JButton(LABEL);\r
+               private final JButton editorButton;\r
+               private Boolean cellValue = Boolean.FALSE;\r
+               \r
+               public ButtonColumn() {\r
+                       super();\r
+                       editorButton = new JButton(new AbstractAction(LABEL) {\r
+\r
+                               private static final long serialVersionUID = 1L;\r
+\r
+                               public void actionPerformed(ActionEvent e) {\r
+                                       fireEditingStopped();\r
+                                       \r
+                                       int row = jTable_entries.getSelectedRow();\r
+                                       \r
+                                       ChDatItem c = rowData.get(row);\r
+                                       \r
+                                       String chCode = c.chCode;\r
+                                       if ( chCode.length() == 0 ) {\r
+                                               \r
+                                               // レコーダプラグインが持っているチャンネルコードバリューを検索する\r
+                                               if ( selectedRecorder.isChValueAvailable() ) {\r
+                                                       // 新バージョン\r
+                                                       String recChName = c.recChName;\r
+                                                       for ( TextValueSet t : selectedRecorder.getChValue() ) {\r
+                                                               if ( recChName.equals(t.getText()) ) {\r
+                                                                       c.chCode = t.getValue();\r
+                                                                       break;\r
+                                                               }\r
+                                                       }\r
+                                               }\r
+                                               else if ( selectedRecorder.getChValue().size() > 0 ) {\r
+                                                       // 旧バージョン(置き換えていきたい)\r
+                                                       String recChName = c.recChName;\r
+                                                       if ( selectedRecorder.getRecorderId().startsWith("DIGA ") ) {\r
+                                                               // for DIGA ONLY\r
+                                                               for ( TextValueSet t : selectedRecorder.getChValue() ) {\r
+                                                                       if ( recChName.startsWith(t.getText()+" ") ) {\r
+                                                                               String val = t.getValue();\r
+                                                                               if ( val != null ) {\r
+                                                                                       c.chCode = recChName.replaceFirst(t.getText()+" ","")+":"+val;\r
+                                                                               }\r
+                                                                               break;\r
+                                                                       }\r
+                                                               }\r
+                                                       }\r
+                                                       else {\r
+                                                               // for RD ONLY\r
+                                                               for ( TextValueSet t : selectedRecorder.getChValue() ) {\r
+                                                                       if ( t.getText().equals(recChName) ) {\r
+                                                                               String val = t.getValue();\r
+                                                                               if ( val != null ) {\r
+                                                                                       c.chCode = val;\r
+                                                                               }\r
+                                                                               break;\r
+                                                                       }\r
+                                                               }\r
+                                                       }\r
+                                               }\r
+                                               \r
+                                               c.fireChanged();\r
+                                               jTable_entries.clearSelection();\r
+                                               jTable_entries.setRowSelectionInterval(row, row);\r
+                                       }\r
+                               }\r
+                       });\r
+               }\r
+               \r
+               public Component getTableCellRendererComponent(JTable table, Object value, boolean isSelected, boolean hasFocus, int row, int column) {\r
+                       ChDatItem c = rowData.get(row);\r
+                       renderButton.setEnabled(c.availableAuto);\r
+                       return renderButton;\r
+               }\r
+               \r
+               public Component getTableCellEditorComponent(JTable table, Object value, boolean isSelected, int row, int column) { \r
+                       ChDatItem c = rowData.get(row);\r
+                       editorButton.setEnabled(c.availableAuto);\r
+                       cellValue = c.availableAuto;\r
+                       return editorButton;\r
+               }\r
+               \r
+               public Object getCellEditorValue() {\r
+                       return cellValue; \r
+               }\r
+       }\r
+}\r
diff --git a/TinyBannavi/src/tainavi/AbsChannelSettingView.java b/TinyBannavi/src/tainavi/AbsChannelSettingView.java
new file mode 100644 (file)
index 0000000..6e571a3
--- /dev/null
@@ -0,0 +1,266 @@
+package tainavi;\r
+\r
+import java.awt.Color;\r
+import java.awt.Component;\r
+import java.awt.Dimension;\r
+import java.awt.event.ActionEvent;\r
+import java.awt.event.ActionListener;\r
+\r
+import javax.swing.JButton;\r
+import javax.swing.JPanel;\r
+import javax.swing.JScrollPane;\r
+import javax.swing.ScrollPaneConstants;\r
+import javax.swing.SpringLayout;\r
+import javax.swing.border.LineBorder;\r
+\r
+/**\r
+ * CH設定のタブ\r
+ * @since 3.15.4β {@link Viewer}から分離\r
+ */\r
+public abstract class AbsChannelSettingView extends JScrollPane {\r
+\r
+       private static final long serialVersionUID = 1L;\r
+\r
+       public static void setDebug(boolean b) {debug = b; }\r
+       private static boolean debug = false;\r
+\r
+       \r
+       /*******************************************************************************\r
+        * 抽象メソッド\r
+        ******************************************************************************/\r
+       \r
+       protected abstract Env getEnv();\r
+       protected abstract TVProgramList getProgPlugins();\r
+       \r
+       protected abstract StatusWindow getStWin(); \r
+       protected abstract StatusTextArea getMWin();\r
+       \r
+       protected abstract Component getParentComponent();\r
+       protected abstract VWColorChooserDialog getCcWin(); \r
+       \r
+       protected abstract void ringBeep();\r
+       \r
+       /**\r
+        * 放送局の選択を変更したので反映してほしい\r
+        */\r
+       protected abstract void updateProgPlugin();\r
+\r
+       \r
+       /*******************************************************************************\r
+        * 呼び出し元から引き継いだもの\r
+        ******************************************************************************/\r
+       \r
+       private final Env env = getEnv();\r
+       private final TVProgramList progPlugins = getProgPlugins();\r
+       \r
+       private final StatusWindow StWin = getStWin();                  // これは起動時に作成されたまま変更されないオブジェクト\r
+       private final StatusTextArea MWin = getMWin();                  // これは起動時に作成されたまま変更されないオブジェクト\r
+       \r
+       private final Component parent = getParentComponent();  // これは起動時に作成されたまま変更されないオブジェクト\r
+       private final VWColorChooserDialog ccwin = getCcWin();  // これは起動時に作成されたまま変更されないオブジェクト\r
+       \r
+       // メソッド\r
+       //private void StdAppendMessage(String message) { System.out.println(message); }\r
+       //private void StdAppendError(String message) { System.err.println(message); }\r
+       private void StWinSetVisible(boolean b) { StWin.setVisible(b); }\r
+       private void StWinSetLocationCenter(Component frame) { CommonSwingUtils.setLocationCenter(frame, (VWStatusWindow)StWin); }\r
+\r
+       \r
+       /*******************************************************************************\r
+        * 定数\r
+        ******************************************************************************/\r
+\r
+       private static final int PARTS_WIDTH = 900;\r
+       private static final int PARTS_HEIGHT = 30;\r
+       private static final int SEP_WIDTH = 10;\r
+       private static final int SEP_HEIGHT = 10;\r
+       //private static final int BLOCK_SEP_HEIGHT = 75;\r
+\r
+       private static final int UPDATE_WIDTH = 250;\r
+       private static final int HINT_WIDTH = 750;\r
+       \r
+       private static final int PANEL_WIDTH = PARTS_WIDTH+100;\r
+\r
+       private static final String TEXT_HINT =\r
+                       "[CHソート設定] 放送局の並べ替えはあちらを利用してください。ここでも多少できなくはないですが、番組表をまたげません。\n"+\r
+                       "[CHコンバート設定] 番組表を切り替えても検索条件の放送局名を変えたくない方、しょぼかる連携したい方は変換規則を設定してください。";\r
+       \r
+       /*******************************************************************************\r
+        * 部品\r
+        ******************************************************************************/\r
+\r
+       // コンポーネント\r
+       \r
+       private JPanel jPanel_chSetting = null;\r
+       \r
+       private JPanel jPanel_update = null;\r
+       private JButton jButton_update = null;\r
+       \r
+       private ChannelSettingPanel tvp = null;\r
+       private ChannelSettingPanel csp = null;\r
+       private ChannelSettingPanel cs2p = null;\r
+       //private ChannelSettingPanel radp = null;\r
+       //private ChannelSettingPanel syobo = null;\r
+\r
+       private JTextAreaWithPopup jta_help = null;\r
+\r
+       \r
+       /*******************************************************************************\r
+        * コンストラクタ\r
+        ******************************************************************************/\r
+       \r
+       public AbsChannelSettingView() {\r
+               \r
+               super();\r
+               \r
+               this.setVerticalScrollBarPolicy(ScrollPaneConstants.VERTICAL_SCROLLBAR_ALWAYS);\r
+               this.getVerticalScrollBar().setUnitIncrement(25);\r
+               this.setHorizontalScrollBarPolicy(ScrollPaneConstants.HORIZONTAL_SCROLLBAR_AS_NEEDED);\r
+               this.setColumnHeaderView(getJPanel_update());\r
+               this.setViewportView(getJPanel_chSetting());\r
+               \r
+       }\r
+       \r
+       private JPanel getJPanel_update() {\r
+               if (jPanel_update == null)\r
+               {\r
+                       jPanel_update = new JPanel();\r
+                       jPanel_update.setLayout(new SpringLayout());\r
+                       \r
+                       jPanel_update.setBorder(new LineBorder(Color.GRAY));\r
+                       \r
+                       int y = SEP_HEIGHT;\r
+                       CommonSwingUtils.putComponentOn(jPanel_update, getJButton_update("更新を確定する"), UPDATE_WIDTH, PARTS_HEIGHT, SEP_WIDTH, y);\r
+                       \r
+                       int yz = SEP_HEIGHT/2;\r
+                       int x = UPDATE_WIDTH+50;\r
+                       CommonSwingUtils.putComponentOn(jPanel_update, getJta_help(), HINT_WIDTH, PARTS_HEIGHT+SEP_HEIGHT, x, yz);\r
+                       \r
+                       y += (PARTS_HEIGHT + SEP_HEIGHT);\r
+                       \r
+                       // 画面の全体サイズを決める\r
+                       Dimension d = new Dimension(PANEL_WIDTH,y);\r
+                       jPanel_update.setPreferredSize(d);\r
+               }\r
+               return jPanel_update;\r
+       }\r
+       \r
+       private JPanel getJPanel_chSetting() {\r
+               if (jPanel_chSetting == null) {\r
+                       \r
+                       jPanel_chSetting = new JPanel();\r
+                       jPanel_chSetting.setLayout(new SpringLayout());\r
+                       \r
+                       //\r
+                       Dimension pd;\r
+                       \r
+                       int y = SEP_HEIGHT;\r
+                       \r
+                       tvp = new ChannelSettingPanel("地上波&BS番組表",progPlugins.getTvProgPlugins(),env.getTVProgramSite(),true,ccwin,StWin,parent);\r
+                       pd = tvp.getPreferredSize();\r
+                       CommonSwingUtils.putComponentOn(jPanel_chSetting, tvp, pd.width, pd.height, SEP_WIDTH, y);\r
+                       y+=(pd.height+SEP_HEIGHT);\r
+                       \r
+                       csp = new ChannelSettingPanel("CS[プライマリ]番組表",progPlugins.getCsProgPlugins(),env.getCSProgramSite(),false,ccwin,StWin,parent);\r
+                       pd = csp.getPreferredSize();\r
+                       CommonSwingUtils.putComponentOn(jPanel_chSetting, csp, pd.width, pd.height, SEP_WIDTH, y);\r
+                       y+=(pd.height+SEP_HEIGHT);\r
+                       \r
+                       cs2p = new ChannelSettingPanel("CS[セカンダリ]番組表",progPlugins.getCs2ProgPlugins(),env.getCS2ProgramSite(),false,ccwin,StWin,parent);\r
+                       pd = cs2p.getPreferredSize();\r
+                       CommonSwingUtils.putComponentOn(jPanel_chSetting, cs2p, pd.width, pd.height, SEP_WIDTH, y);\r
+                       y+=(pd.height+SEP_HEIGHT);\r
+\r
+                       /*\r
+                       if ( progPlugins.getRadioProgPlugins().size() > 0 ) {\r
+                               y+=(pd.height+SEP_HEIGHT);\r
+                               radp = new ChannelSettingPanel("ラジオ番組表",progPlugins.getRadioProgPlugins(),env.getRadioProgramSite(),"",true,ccwin,StWin);\r
+                               pd = radp.getPreferredSize();\r
+                               CommonSwingUtils.putComponentOn(jPanel_chSetting, radp, pd.width, pd.height, SEP_WIDTH, y);\r
+                               y+=(pd.height+SEP_HEIGHT);\r
+                       }\r
+                       */\r
+                       \r
+                       y += SEP_HEIGHT;\r
+                       \r
+                       Dimension d = tvp.getPreferredSize();\r
+                       d.width += SEP_WIDTH*2;\r
+                       d.height = y;\r
+                       \r
+                       jPanel_chSetting.setPreferredSize(d);\r
+               }\r
+                       \r
+               return jPanel_chSetting;\r
+       }\r
+\r
+       \r
+       /*******************************************************************************\r
+        * アクション\r
+        ******************************************************************************/\r
+       \r
+       private void updateChSetting() {\r
+               \r
+               TatCount tc = new TatCount();\r
+               \r
+               MWin.appendMessage("【CH設定】設定を保存します");\r
+               StWin.clear();\r
+               \r
+               new SwingBackgroundWorker(false) {\r
+                       \r
+                       @Override\r
+                       protected Object doWorks() throws Exception {\r
+                               env.setTVProgramSite((String) tvp.getSelectedCenter());\r
+                               env.setCSProgramSite((String) csp.getSelectedCenter());\r
+                               env.setCS2ProgramSite((String) cs2p.getSelectedCenter());\r
+                               //if (radp!=null) env.setRadioProgramSite((String) radp.getSelectedCenter());\r
+\r
+                               tvp.saveChannelSetting();\r
+                               csp.saveChannelSetting();\r
+                               cs2p.saveChannelSetting();\r
+\r
+                               updateProgPlugin();\r
+\r
+                               return null;\r
+                       }\r
+\r
+                       @Override\r
+                       protected void doFinally() {\r
+                               StWinSetVisible(false);\r
+                       }\r
+                       \r
+               }.execute();\r
+                       \r
+               StWinSetLocationCenter(parent);\r
+               StWinSetVisible(true);\r
+               \r
+               MWin.appendMessage(String.format("【CH設定】更新が完了しました。所要時間: %.2f秒",tc.end()));\r
+       }\r
+\r
+       \r
+       /*******************************************************************************\r
+        * コンポーネント\r
+        ******************************************************************************/\r
+       \r
+       private JButton getJButton_update(String s) {\r
+               if (jButton_update == null) {\r
+                       \r
+                       jButton_update = new JButton(s);\r
+                       \r
+                       jButton_update.addActionListener(new ActionListener() {\r
+                               public void actionPerformed(ActionEvent e) {\r
+                                       updateChSetting();\r
+                               }\r
+                       });\r
+               }\r
+               return(jButton_update);\r
+       }\r
+       \r
+       //\r
+       private JTextAreaWithPopup getJta_help() {\r
+               if ( jta_help == null ) {\r
+                       jta_help = CommonSwingUtils.getJta(this,2,0);\r
+                       jta_help.append(TEXT_HINT);\r
+               }\r
+               return jta_help;\r
+       }\r
+}\r
diff --git a/TinyBannavi/src/tainavi/AbsChannelSortView.java b/TinyBannavi/src/tainavi/AbsChannelSortView.java
new file mode 100644 (file)
index 0000000..ff443dc
--- /dev/null
@@ -0,0 +1,643 @@
+package tainavi;\r
+\r
+import java.awt.Color;\r
+import java.awt.Dimension;\r
+import java.awt.event.ActionEvent;\r
+import java.awt.event.ActionListener;\r
+import java.awt.event.ItemEvent;\r
+import java.awt.event.ItemListener;\r
+import java.beans.PropertyChangeEvent;\r
+import java.beans.PropertyChangeListener;\r
+import java.util.ArrayList;\r
+\r
+import javax.swing.JButton;\r
+import javax.swing.JPanel;\r
+import javax.swing.JScrollPane;\r
+import javax.swing.JToggleButton;\r
+import javax.swing.ListSelectionModel;\r
+import javax.swing.ScrollPaneConstants;\r
+import javax.swing.SpringLayout;\r
+import javax.swing.border.LineBorder;\r
+import javax.swing.table.DefaultTableColumnModel;\r
+import javax.swing.table.DefaultTableModel;\r
+import javax.swing.table.TableCellRenderer;\r
+import javax.swing.table.TableColumn;\r
+import javax.swing.table.TableModel;\r
+\r
+/**\r
+ * CHソート設定のタブ\r
+ * @since 3.15.4β VWChannelSortからクラス名変更\r
+ * @version 3.16 全面リライト\r
+ */\r
+public abstract class AbsChannelSortView extends JScrollPane {\r
+\r
+       private static final long serialVersionUID = 1L;\r
+\r
+       public void setDebug(boolean b) { debug = b; }\r
+       private boolean debug = false;\r
+       \r
+       \r
+       /*******************************************************************************\r
+        * 抽象メソッド\r
+        ******************************************************************************/\r
+       \r
+       protected abstract Env getEnv();\r
+       protected abstract TVProgramList getTVProgramList();\r
+       protected abstract ChannelSort getChannelSort();\r
+\r
+       protected abstract StatusTextArea getMWin();\r
+\r
+       /**\r
+        * ソート設定の更新を反映してください\r
+        */\r
+       protected abstract void updProc();\r
+       \r
+       \r
+       \r
+       /*******************************************************************************\r
+        * 呼び出し元から引き継いだもの\r
+        ******************************************************************************/\r
+       \r
+       private final Env env = getEnv();\r
+       private final TVProgramList tvprograms = getTVProgramList();\r
+       private final ChannelSort chsort = getChannelSort();\r
+\r
+       private final StatusTextArea MWin = getMWin();                  // これは起動時に作成されたまま変更されないオブジェクト\r
+\r
+       \r
+       \r
+       /*******************************************************************************\r
+        * 定数\r
+        ******************************************************************************/\r
+       \r
+       private static final String MSGID = "[CHソート設定] ";\r
+       //private static final String ERRID = "[ERROR]"+MSGID;\r
+       private static final String DBGID = "[DEBUG]"+MSGID;\r
+       \r
+       //private static final int PARTS_WIDTH = 900;\r
+       private static final int PARTS_HEIGHT = 30;\r
+       private static final int SEP_WIDTH = 10;\r
+       private static final int SEP_HEIGHT = 10;\r
+       //private static final int BLOCK_SEP_HEIGHT = 75;\r
+\r
+       private static final int LABEL_WIDTH = 250;\r
+       private static final int BUTTON_WIDTH = 75;\r
+       //private static final int BUTTON_WIDTH_LONG = 200;\r
+       \r
+       private static final int TABLE_WIDTH = 450;\r
+       private static final int TABLE_HEIGHT = 475;\r
+       \r
+       private static final int UPDATE_WIDTH = 250;\r
+       private static final int HINT_WIDTH = 750;\r
+\r
+       private static final int PANEL_WIDTH = SEP_WIDTH+LABEL_WIDTH+SEP_WIDTH+TABLE_WIDTH+SEP_WIDTH;\r
+       \r
+       private static final String PAGEDIS_COLOR = "#888888";\r
+       private static final String PAGEEN_ODD_COLOR = "#FFA0A0";\r
+       private static final String PAGEEN_EVEN_COLOR = "#FF6060";\r
+       \r
+       private static final String LABEL_UPDATE = "更新を確定する";\r
+       \r
+       private static final String LABEL_ENABLED = "新聞形式の放送局表示順並べ替えは有効です";\r
+       private static final String LABEL_DISABLED = "新聞形式の放送局表示順並べ替えは無効です";\r
+       private static final String LABEL_ENABLED_U = LABEL_ENABLED+"(要更新確定)";\r
+       private static final String LABEL_DISABLED_U = LABEL_DISABLED+"(要更新確定)";\r
+       \r
+       public static enum ChSortColumn {\r
+               PAGE            ("ページ",                           50),\r
+               WEBCHNAME       ("Web番組表の放送局名", TABLE_WIDTH-50),\r
+               ;\r
+\r
+               private String name;\r
+               private int iniWidth;\r
+\r
+               private ChSortColumn(String name, int iniWidth) {\r
+                       this.name = name;\r
+                       this.iniWidth = iniWidth;\r
+               }\r
+\r
+               public String getName() {\r
+                       return name;\r
+               }\r
+\r
+               public int getIniWidth() {\r
+                       return iniWidth;\r
+               }\r
+               \r
+               public int getColumn() {\r
+                       return ordinal();\r
+               }\r
+       };\r
+       \r
+       \r
+       /*******************************************************************************\r
+        * 部品\r
+        ******************************************************************************/\r
+       \r
+       // コンポーネント\r
+       \r
+       private JPanel jpan_update = null;\r
+       private JPanel jpan_chsort = null;\r
+       \r
+       private JButton jbtn_update = null;\r
+       //private JButton jbtn_refresh = null;\r
+       private JToggleButton jtgl_chsort = null;\r
+       private JButton jbtn_up = null;\r
+       private JButton jbtn_down = null;\r
+       \r
+       private JScrollPane jscr_entries = null;\r
+       private JNETable jtbl_entries = null;\r
+       \r
+       private JTextAreaWithPopup jta_help = null;\r
+       \r
+       // コンポーネント以外\r
+       \r
+       RowItemList<ChSortItem> rowData = null;\r
+       \r
+       ChannelSort chsortbak = null;\r
+       \r
+       \r
+       /*******************************************************************************\r
+        * コンストラクタ\r
+        ******************************************************************************/\r
+       \r
+       public AbsChannelSortView() {\r
+               \r
+               super();\r
+               \r
+               rowData = new RowItemList<ChSortItem>();\r
+               \r
+               this.setVerticalScrollBarPolicy(ScrollPaneConstants.VERTICAL_SCROLLBAR_ALWAYS);\r
+               this.getVerticalScrollBar().setUnitIncrement(25);\r
+               this.setViewportView(getJPan_chsort());\r
+               this.setColumnHeaderView(getJPan_update());\r
+               \r
+               // 無効だろうが読む\r
+               //chsort.load();\r
+               \r
+               // 初期値は?\r
+               if ( env.getChSortEnabled() ) {\r
+                       chsortbak = null;\r
+                       \r
+                       jtgl_chsort.setSelected(true);\r
+                       \r
+                       updateChannelSortList(false);   // 保存はしない\r
+               }\r
+               else {\r
+                       backupChSort();\r
+                       \r
+                       _updateChannelSortTable();      // テーブルならべる\r
+               }\r
+               \r
+               setUpdateButtonEnabled(false);  // 設定を変えないと押せない\r
+       }\r
+       \r
+       private JPanel getJPan_update() {\r
+               if (jpan_update == null)\r
+               {\r
+                       jpan_update = new JPanel();\r
+                       jpan_update.setLayout(new SpringLayout());\r
+                       \r
+                       jpan_update.setBorder(new LineBorder(Color.GRAY));\r
+                       \r
+                       int y = SEP_HEIGHT;\r
+                       CommonSwingUtils.putComponentOn(jpan_update, getJBtn_update(LABEL_UPDATE), UPDATE_WIDTH, PARTS_HEIGHT, SEP_WIDTH, y);\r
+                       \r
+                       int yz = SEP_HEIGHT/2;\r
+                       int x = UPDATE_WIDTH+50;\r
+                       CommonSwingUtils.putComponentOn(jpan_update, getJta_help(), HINT_WIDTH, PARTS_HEIGHT+SEP_HEIGHT, x, yz);\r
+                       \r
+                       y += (PARTS_HEIGHT + SEP_HEIGHT);\r
+                       \r
+                       // 画面の全体サイズを決める\r
+                       Dimension d = new Dimension(PANEL_WIDTH,y);\r
+                       jpan_update.setPreferredSize(d);\r
+               }\r
+               return jpan_update;\r
+       }\r
+       \r
+       private JPanel getJPan_chsort() {\r
+               if (jpan_chsort == null) {\r
+                       jpan_chsort = new JPanel();\r
+                       jpan_chsort.setLayout(new SpringLayout());\r
+                       \r
+                       int y = SEP_HEIGHT;\r
+                       int x = SEP_WIDTH+LABEL_WIDTH+SEP_WIDTH;\r
+                       CommonSwingUtils.putComponentOn(jpan_chsort, getJTgl_chsort(), TABLE_WIDTH, PARTS_HEIGHT, x, y);\r
+                       \r
+                       //y+=(PARTS_HEIGHT+SEP_HEIGHT);\r
+                       //CommonSwingUtils.putComponentOn(jpan_chsort, getJBtn_refresh("CH設定を変更したら放送局リストをリフレッシュしてください"), TABLE_WIDTH, PARTS_HEIGHT, x, y);\r
+                       \r
+                       y+=(PARTS_HEIGHT+SEP_HEIGHT);\r
+                       CommonSwingUtils.putComponentOn(jpan_chsort, getJScr_entries(), TABLE_WIDTH, TABLE_HEIGHT, x, y);\r
+                       \r
+                       int yz = y + TABLE_HEIGHT/2;\r
+                       x -= (BUTTON_WIDTH+SEP_WIDTH);\r
+                       CommonSwingUtils.putComponentOn(jpan_chsort, getJBtn_up("上へ"), BUTTON_WIDTH, PARTS_HEIGHT, x, yz-PARTS_HEIGHT-10);\r
+                       CommonSwingUtils.putComponentOn(jpan_chsort, getJBtn_down("下へ"), BUTTON_WIDTH, PARTS_HEIGHT, x, yz+10);\r
+                       \r
+                       y+=(TABLE_HEIGHT+SEP_HEIGHT+SEP_HEIGHT);\r
+                       \r
+                       Dimension d = new Dimension(PANEL_WIDTH,y);\r
+                       jpan_chsort.setPreferredSize(d);\r
+               }\r
+               return jpan_chsort;\r
+       }\r
+       \r
+       \r
+       \r
+       /*******************************************************************************\r
+        * アクション\r
+        ******************************************************************************/\r
+       \r
+       /**\r
+        *  既存にマッチしたものは順番を維持する・しないものは末尾に追加。こちらは外部(CH設定タブ)からの呼び出し用\r
+        */\r
+       public void updateChannelSortTable() {\r
+               \r
+               if (debug) System.out.println(DBGID+"REFRESH KICKED BY CHSETTING");\r
+\r
+               // 順番を引き継ぐ\r
+               _updateChannelSortTable();\r
+               // 結果を保存して\r
+               updateChannelSortList(env.getChSortEnabled());  // 状態によって保存したりしなかったり\r
+       }\r
+       \r
+       /**\r
+        * 既存にマッチしたものは順番を維持する・しないものは末尾に追加。こちらは内部からの呼び出し用\r
+        */\r
+       private void _updateChannelSortTable() {\r
+               \r
+               if (debug) System.out.println(DBGID+"REFRESH");\r
+               \r
+               // 番組表プラグインの放送局リストをなめて、現在のリストとつきあわせて引き継げるものは引き継げるようによりわける\r
+               ArrayList<ChSortItem> en = new ArrayList<ChSortItem>();\r
+               ArrayList<ChSortItem> dis = new ArrayList<ChSortItem>();\r
+               for (TVProgram p : tvprograms.getProgPlugins()) {\r
+                       for (Center cr : p.getSortedCRlist()) {\r
+                               ChSortItem si = new ChSortItem();\r
+                               si.page = "";\r
+                               si.webChName = cr.getCenter();\r
+                               si.areaCode = cr.getAreaCode();\r
+                               si.fireChanged();\r
+                               for (Center cs : chsort.getClst()) {\r
+                                       if (cs.getCenter().equals(si.webChName) && cs.getAreaCode().equals(si.areaCode)) {\r
+                                               en.add(si);\r
+                                               si = null;\r
+                                               break;\r
+                                       }\r
+                               }\r
+                               if (si != null) {\r
+                                       dis.add(si);\r
+                               }\r
+                       }\r
+               }\r
+               \r
+               // よりわけたものをもとの順序にならべかえる\r
+               RowItemList<ChSortItem> newRowData = new RowItemList<ChSortItem>();\r
+               for (Center cs : chsort.getClst()) {\r
+                       // chsortの順序を引き継ぐ\r
+                       for (ChSortItem a : en) {\r
+                               if (cs.getCenter().equals(a.webChName) && cs.getAreaCode().equals(a.areaCode)) {\r
+                                       newRowData.add(a);\r
+                               }\r
+                       }\r
+               }\r
+               \r
+               // 新規追加分は入ってきた順番に追加\r
+               for (ChSortItem a : dis) {\r
+                       newRowData.add(a);\r
+               }\r
+               \r
+               // テーブル入れ替え\r
+               rowData = newRowData;\r
+\r
+               // Fire!\r
+               ((DefaultTableModel) jtbl_entries.getModel()).fireTableDataChanged();\r
+               \r
+       }\r
+       \r
+       private void updateChannelSortList(boolean save) {\r
+               chsort.clear();\r
+               for ( RowItem ra : rowData ) {\r
+                       ChSortItem a = (ChSortItem) ra;\r
+                       Center cr = new Center();\r
+                       cr.setCenter(a.webChName);\r
+                       cr.setAreaCode(a.areaCode);\r
+                       chsort.add(cr);\r
+               }\r
+               if (save) chsort.save();\r
+       }\r
+       \r
+       private void setUpdateButtonEnabled(boolean b) {\r
+               if ( b ) {\r
+                       jbtn_update.setForeground(Color.RED);\r
+                       jbtn_update.setEnabled(true);\r
+                       \r
+                       if ( jtgl_chsort.isSelected() ) {\r
+                               jtgl_chsort.setText(LABEL_ENABLED_U);\r
+                               jtgl_chsort.setForeground(Color.RED);\r
+                       }\r
+                       else {\r
+                               jtgl_chsort.setText(LABEL_DISABLED_U);\r
+                               jtgl_chsort.setForeground(Color.RED);\r
+                       }\r
+               }\r
+               else {\r
+                       jbtn_update.setForeground(Color.BLACK);\r
+                       jbtn_update.setEnabled(false);\r
+                       \r
+                       if ( jtgl_chsort.isSelected() ) {\r
+                               jtgl_chsort.setText(LABEL_ENABLED);\r
+                               jtgl_chsort.setForeground(Color.BLUE);\r
+                       }\r
+                       else {\r
+                               jtgl_chsort.setText(LABEL_DISABLED);\r
+                               jtgl_chsort.setForeground(Color.BLACK);\r
+                       }\r
+               }\r
+       }\r
+       \r
+       private void backupChSort() {\r
+               chsortbak = new ChannelSort();\r
+               copyChSort(chsortbak, chsort);\r
+               chsort.clear();\r
+       }\r
+       private void undoChSort() {\r
+               if ( chsortbak != null ) {\r
+                       chsort.clear();\r
+                       copyChSort(chsort, chsortbak);\r
+                       chsortbak = null;\r
+               }\r
+       }\r
+       \r
+       private void copyChSort(ChannelSort to, ChannelSort from) {\r
+               to.clear();\r
+               for ( Center cr : from.getClst() ) {\r
+                       to.add(cr);\r
+               }\r
+               \r
+       }\r
+       \r
+       \r
+       /*******************************************************************************\r
+        * リスナー\r
+        ******************************************************************************/\r
+       \r
+       /**\r
+        * 更新を確定したいなあ\r
+        */\r
+       private final ActionListener al_update = new ActionListener() {\r
+               @Override\r
+               public void actionPerformed(ActionEvent e) {\r
+                       \r
+                       env.setChSortEnabled(jtgl_chsort.isSelected());\r
+                       \r
+                       if ( env.getChSortEnabled() ) {\r
+                               // 放送局リストを作成する\r
+                               updateChannelSortList(true);\r
+                       }\r
+                       else {\r
+                               // 放送局リストをCH設定通りの並べに戻す\r
+                               _updateChannelSortTable();\r
+                       }\r
+\r
+                       // 更新確定ボタンは無効に戻す\r
+                       setUpdateButtonEnabled(false);\r
+                       \r
+                       // 本体側でなにか処理があるかなー?\r
+                       updProc();\r
+                       \r
+                       MWin.appendMessage(MSGID+"設定を保存しました");\r
+               }\r
+       };\r
+       \r
+       /**\r
+        * トグルしたいなー\r
+        */\r
+       private final ItemListener il_chsort = new ItemListener() {\r
+               @Override\r
+               public void itemStateChanged(ItemEvent e) {\r
+                       \r
+                       if (debug) System.out.println(DBGID+"chsort "+e.paramString());\r
+                       \r
+                       JToggleButton source = (JToggleButton) e.getSource();\r
+                       \r
+                       // コンポーネントの状態を変える\r
+                       {\r
+                               jtbl_entries.setEnabled(source.isSelected());\r
+                               jbtn_up.setEnabled(source.isSelected());\r
+                               jbtn_down.setEnabled(source.isSelected());\r
+                       }\r
+                       \r
+                       if (source.isSelected()) {\r
+                               // 有効にした時\r
+                               undoChSort();\r
+                               \r
+                               _updateChannelSortTable();\r
+                       }\r
+                       else {\r
+                               // 無効にした時\r
+                               backupChSort();\r
+                               \r
+                               _updateChannelSortTable();\r
+                       }\r
+                       \r
+                       if ( ! jbtn_update.isSelected() ) setUpdateButtonEnabled(true); // 変更されちゃった\r
+               }\r
+       };\r
+       \r
+       private final PropertyChangeListener pcl_up = new PropertyChangeListener() {\r
+               @Override\r
+               public void propertyChange(PropertyChangeEvent e) {\r
+                       if ( "BUTTON.BP_PUSHBUTTON".equals(e.getPropertyName()) && e.getNewValue() != null && "PRESSED".equals(e.getNewValue().toString()) ) {\r
+                               if (debug) System.out.println(DBGID+"up "+e.getPropertyName()+" "+e.getNewValue());\r
+                               \r
+                               int vrow = jtbl_entries.getSelectedRow();\r
+                               int cnt = jtbl_entries.getSelectedRowCount();\r
+                               if ( rowData.up(vrow, cnt) ) {\r
+                                       ((DefaultTableModel) jtbl_entries.getModel()).fireTableDataChanged();\r
+                                       jtbl_entries.setRowSelectionInterval(vrow-1, vrow-1+(cnt-1));\r
+                               }\r
+                               \r
+                               if ( ! jbtn_update.isSelected() ) setUpdateButtonEnabled(true); // 変更されちゃった\r
+                       }\r
+               }\r
+       };\r
+       \r
+       private final PropertyChangeListener pcl_down = new PropertyChangeListener() {\r
+               @Override\r
+               public void propertyChange(PropertyChangeEvent e) {\r
+                       if ( "BUTTON.BP_PUSHBUTTON".equals(e.getPropertyName()) && e.getNewValue() != null && "PRESSED".equals(e.getNewValue().toString()) ) {\r
+                               \r
+                               int vrow = jtbl_entries.getSelectedRow();\r
+                               int cnt = jtbl_entries.getSelectedRowCount();\r
+                               if ( rowData.down(vrow, cnt) ) {\r
+                                       ((DefaultTableModel) jtbl_entries.getModel()).fireTableDataChanged();\r
+                                       jtbl_entries.setRowSelectionInterval(vrow+1, vrow+1+(cnt-1));\r
+                               }\r
+                               \r
+                               if ( ! jbtn_update.isSelected() ) setUpdateButtonEnabled(true); // 変更されちゃった\r
+                       }\r
+               }\r
+       };\r
+       \r
+       \r
+       \r
+       /*******************************************************************************\r
+        * コンポーネント\r
+        ******************************************************************************/\r
+       \r
+       private JButton getJBtn_update(String s) {\r
+               if (jbtn_update == null) {\r
+                       jbtn_update = new JButton(s);\r
+                       \r
+                       jbtn_update.addActionListener(al_update);\r
+               }\r
+               return jbtn_update;\r
+       }\r
+       \r
+       private JToggleButton getJTgl_chsort() {\r
+               if (jtgl_chsort == null) {\r
+                       jtgl_chsort = new JToggleButton();\r
+                       jtgl_chsort.setSelected(false);\r
+                       jtgl_chsort.addItemListener(il_chsort);\r
+               }\r
+               return jtgl_chsort;\r
+       }\r
+       \r
+       private JScrollPane getJScr_entries() {\r
+               if (jscr_entries == null) {\r
+                       jscr_entries = new JScrollPane();\r
+                       jscr_entries.setVerticalScrollBarPolicy(ScrollPaneConstants.VERTICAL_SCROLLBAR_ALWAYS);\r
+                       jscr_entries.getVerticalScrollBar().setUnitIncrement(25);\r
+                       jscr_entries.setViewportView(getJTbl_entries());\r
+               }\r
+               return jscr_entries;\r
+       }\r
+       \r
+       private JNETable getJTbl_entries() {\r
+               if (jtbl_entries == null) {\r
+                       \r
+                       // カラム名の初期化\r
+                       ArrayList<String> cola = new ArrayList<String>();\r
+                       for ( ChSortColumn lc : ChSortColumn.values() ) {\r
+                               if ( lc.getIniWidth() >= 0 ) {\r
+                                       cola.add(lc.getName());\r
+                               }\r
+                       }\r
+                       final String[] colname = cola.toArray(new String[0]);\r
+                       \r
+                       DefaultTableModel model = new DefaultTableModel(colname, 0);\r
+                       jtbl_entries = new JChSortTable(model,false);\r
+                       jtbl_entries.getTableHeader().setReorderingAllowed(false);\r
+                       \r
+                       jtbl_entries.setSelectionMode(ListSelectionModel.SINGLE_INTERVAL_SELECTION);\r
+                       \r
+                       // 各カラムの幅を設定する\r
+                       DefaultTableColumnModel columnModel = (DefaultTableColumnModel) jtbl_entries.getColumnModel();\r
+                       TableColumn column = null;\r
+                       for ( ChSortColumn lc : ChSortColumn.values() ) {\r
+                               if ( lc.getIniWidth() < 0 ) {\r
+                                       continue;\r
+                               }\r
+                               column = columnModel.getColumn(lc.ordinal());\r
+                               column.setPreferredWidth(lc.getIniWidth());\r
+                       }\r
+                       \r
+                       // ページ欄は色つき&センタリング\r
+                       column = columnModel.getColumn(ChSortColumn.PAGE.ordinal());\r
+                       TableCellRenderer renderer = new VWColorCellRenderer();\r
+                       column.setCellRenderer(renderer);\r
+               }\r
+               return jtbl_entries;\r
+       }\r
+       \r
+       // 放送局を上へ・下へ\r
+       private JButton getJBtn_up(String s) {\r
+               if (jbtn_up == null) {\r
+                       jbtn_up = new JButton(s);\r
+                       jbtn_up.addPropertyChangeListener(pcl_up);\r
+               }\r
+               return jbtn_up;\r
+       }\r
+       private JButton getJBtn_down(String s) {\r
+               if (jbtn_down == null) {\r
+                       jbtn_down = new JButton(s);\r
+                       jbtn_down.addPropertyChangeListener(pcl_down);\r
+               }\r
+               return jbtn_down;\r
+       }\r
+       \r
+       //\r
+       private JTextAreaWithPopup getJta_help() {\r
+               if ( jta_help == null ) {\r
+                       jta_help = CommonSwingUtils.getJta(this,2,0);\r
+                       jta_help.append(\r
+                                       "Web番組表をまたいで放送局を並べ替えるための機能です。先にCH設定を完了させる必要があります。\n"+\r
+                                       "(この機能の有効無効は処理速度に影響しません)");\r
+               }\r
+               return jta_help;\r
+       }\r
+       \r
+       \r
+       /*******************************************************************************\r
+        * 独自部品\r
+        ******************************************************************************/\r
+\r
+       // テーブルの行データの構造\r
+       private class ChSortItem extends RowItem implements Cloneable {\r
+               String page;\r
+               String webChName;\r
+               String areaCode;\r
+               \r
+               @Override\r
+               protected void myrefresh(RowItem o) {\r
+                       ChSortItem c = (ChSortItem) o;\r
+                       c.addData(page);\r
+                       c.addData(webChName);\r
+                       c.addData(areaCode);\r
+               }\r
+               \r
+               public ChSortItem clone() {\r
+                       return (ChSortItem) super.clone();\r
+               }\r
+       }\r
+       \r
+       // ChSortItemを使ったJTable拡張\r
+       private class JChSortTable extends JNETable {\r
+\r
+               private static final long serialVersionUID = 1L;\r
+\r
+               @Override\r
+               public Object getValueAt(int row, int column) {\r
+                       \r
+                       if ( column != 0 ) {\r
+                               // 1カラム目以外はそのまま\r
+                               return rowData.get(row).get(column);\r
+                       }\r
+                       \r
+                       if ( ! env.isPagerEnabled() ) {\r
+                               // ページャーが無効ならグレーアウト\r
+                               return CommonSwingUtils.getColoredString(PAGEDIS_COLOR,"-");\r
+                       }\r
+                       \r
+                       // 1行ごとに背景色を互い違いにする。またソートが無効な場合はグレーアウト\r
+                       int page = 1 + env.getPageIndex(1+this.convertRowIndexToView(row));\r
+                       int pagemax = 1 + env.getPageIndex(rowData.size());\r
+                       String color = ( ! jtgl_chsort.isSelected())?(PAGEDIS_COLOR):((page % 2 == 0)?(PAGEEN_ODD_COLOR):(PAGEEN_EVEN_COLOR));\r
+                       String text = String.format("%d / %d", page, pagemax);\r
+                       return CommonSwingUtils.getColoredString(color, text);\r
+                                       \r
+               }\r
+               \r
+               @Override\r
+               public int getRowCount() {\r
+                       return rowData.size();\r
+               }\r
+               \r
+               public JChSortTable(TableModel d, boolean b) {\r
+                       super(d,b);\r
+               }\r
+       }\r
+}\r
diff --git a/TinyBannavi/src/tainavi/AbsExtensionDialog.java b/TinyBannavi/src/tainavi/AbsExtensionDialog.java
new file mode 100644 (file)
index 0000000..4e2825a
--- /dev/null
@@ -0,0 +1,57 @@
+package tainavi;\r
+\r
+import tainavi.SearchKey.TargetId;\r
+\r
+\r
+/***\r
+ * \r
+ * キーワード検索の設定のクラス\r
+ * \r
+ */\r
+abstract class AbsExtensionDialog extends AbsKeywordDialog {\r
+\r
+       private static final long serialVersionUID = 1L;\r
+\r
+       //JComboBox jComboBox_infection = null;\r
+       \r
+       // コンストラクタ\r
+       @Override\r
+       protected void setItems() {\r
+               setWindowTitle("延長警告キーワードの設定");\r
+               \r
+               clean_target_items();\r
+               add_target_item(TargetId.TITLEANDDETAIL);\r
+               add_target_item(TargetId.TITLE);\r
+               add_target_item(TargetId.DETAIL);\r
+               add_target_item(TargetId.CHANNEL);\r
+               add_target_item(TargetId.GENRE);\r
+               add_target_item(TargetId.SUBGENRE);\r
+               add_target_item(TargetId.NEW);\r
+               add_target_item(TargetId.LAST);\r
+               add_target_item(TargetId.REPEAT);\r
+               add_target_item(TargetId.FIRST);\r
+               add_target_item(TargetId.SPECIAL);\r
+               add_target_item(TargetId.NOSCRUMBLE);\r
+               add_target_item(TargetId.LIVE);\r
+               add_target_item(TargetId.LENGTH);       \r
+               add_target_item(TargetId.STARTA);\r
+               add_target_item(TargetId.STARTZ);\r
+               add_target_item(TargetId.STARTDATETIME);\r
+               \r
+               clean_contain_items();\r
+               add_contain_item("を含む番組");\r
+               add_contain_item("を含む番組を除く");\r
+               \r
+               clean_condition_items();\r
+               add_condition_item("次のすべての条件に一致");\r
+               add_condition_item("次のいずれかの条件に一致");\r
+       \r
+               clean_infection_items();\r
+       add_infection_item("延長感染源にする");\r
+       add_infection_item("延長感染源にしない");\r
+       }\r
+       \r
+       public AbsExtensionDialog() {\r
+               super();\r
+       }\r
+}
\ No newline at end of file
diff --git a/TinyBannavi/src/tainavi/AbsKeywordDialog.java b/TinyBannavi/src/tainavi/AbsKeywordDialog.java
new file mode 100644 (file)
index 0000000..f4d230d
--- /dev/null
@@ -0,0 +1,1091 @@
+package tainavi;\r
+\r
+import java.awt.Dimension;\r
+import java.awt.event.ActionEvent;\r
+import java.awt.event.ActionListener;\r
+import java.awt.event.ItemEvent;\r
+import java.awt.event.ItemListener;\r
+import java.util.ArrayList;\r
+import java.util.regex.Matcher;\r
+import java.util.regex.Pattern;\r
+import java.util.regex.PatternSyntaxException;\r
+\r
+import javax.swing.DefaultComboBoxModel;\r
+import javax.swing.JButton;\r
+import javax.swing.JComboBox;\r
+import javax.swing.JDialog;\r
+import javax.swing.JLabel;\r
+import javax.swing.JOptionPane;\r
+import javax.swing.JPanel;\r
+import javax.swing.JScrollPane;\r
+import javax.swing.JTextField;\r
+import javax.swing.ListSelectionModel;\r
+import javax.swing.SpringLayout;\r
+import javax.swing.SwingConstants;\r
+import javax.swing.table.DefaultTableColumnModel;\r
+import javax.swing.table.DefaultTableModel;\r
+\r
+import tainavi.SearchKey.TargetId;\r
+import tainavi.TVProgram.ProgGenre;\r
+import tainavi.TVProgram.ProgSubgenre;\r
+\r
+\r
+/**\r
+ * キーワード検索の設定のクラス(延長警告管理でも流用する)\r
+ */\r
+abstract class AbsKeywordDialog extends JDialog {\r
+\r
+       private static final long serialVersionUID = 1L;\r
+\r
+       public void setWindowTitle(String s) { windowTitle = s; }\r
+\r
+       /*******************************************************************************\r
+        * 抽象メソッド\r
+        ******************************************************************************/\r
+       \r
+       abstract void preview(SearchKey search);\r
+\r
+       /*******************************************************************************\r
+        * 定数\r
+        ******************************************************************************/\r
+\r
+       // レイアウト関連\r
+       \r
+       private static final int PARTS_HEIGHT = 30;\r
+       private static final int SEP_WIDTH = 10;\r
+       private static final int SEP_HEIGHT = 10;\r
+       private static final int SEP_HEIGHT_NARROW = 3;\r
+\r
+       private static final int LABEL_WIDTH_S = 50;\r
+       private static final int LABEL_WIDTH_L = 75;\r
+       \r
+       private static final int TEXT_WIDTH = 300;\r
+\r
+       private static final int UPDOWN_WIDTH = 50;\r
+       private static final int BUTTON_WIDTH_S = 75;\r
+       private static final int BUTTON_WIDTH_L = 100;\r
+\r
+       private static final int TARGET_WIDTH = 175;\r
+       private static final int REGEX_WIDTH = 300;\r
+       \r
+       private static final int CHECKBOX_WIDTH = 200;\r
+       private static final int CHECKLABEL_WIDTH = CHECKBOX_WIDTH-25;\r
+       \r
+       private static final int CONDITION_WIDTH = 220;\r
+       \r
+       private static final int SELECTION_WIDTH = 150;\r
+\r
+       private static final int TABLE_WIDTH = REGEX_WIDTH+TARGET_WIDTH*2+SEP_WIDTH*2;  // スクロールバー分\r
+       private static final int TABLEPANE_WIDTH = TABLE_WIDTH+25;      // スクロールバー分\r
+       private static final int TABLE_HEIGHT = 160;\r
+       \r
+       private static final int PANEL_WIDTH = TABLEPANE_WIDTH+UPDOWN_WIDTH+SEP_WIDTH*2;\r
+\r
+       // カラム関連\r
+       \r
+       private static enum KDColumn {\r
+               TARGET          ("",    TARGET_WIDTH+SEP_WIDTH/2),\r
+               REGEX           ("",    REGEX_WIDTH+SEP_WIDTH),\r
+               CONTAIN         ("",    TARGET_WIDTH+SEP_WIDTH/2)\r
+               ;\r
+               \r
+               private String name;\r
+               private int iniWidth;\r
+               \r
+               private KDColumn(String name, int iniWidth) {\r
+                       this.name = name;\r
+                       this.iniWidth = iniWidth;\r
+               }\r
+\r
+               public String getName() {\r
+                       return name;\r
+               }\r
+\r
+               public int getIniWidth() {\r
+                       return iniWidth;\r
+               }\r
+               \r
+               public int getColumn() {\r
+                       return ordinal();\r
+               }\r
+       }\r
+       \r
+       /*******************************************************************************\r
+        * 部品\r
+        ******************************************************************************/\r
+       \r
+       // コンポーネント\r
+       \r
+       private JPanel jPanel = null;\r
+       private JLabel jLabel_label = null;\r
+       private JTextField jTextField_label = null;\r
+       private JButton jButton_label = null;\r
+       private JButton jButton_cancel = null;\r
+       private JButton jButton_preview = null;\r
+       private JComboBox jComboBox_target = null;\r
+       private JComboBox jComboBox_regex = null;\r
+       private JComboBox jComboBox_contain = null;\r
+       private JButton jButton_add = null;\r
+       private JButton jButton_remove = null;\r
+       private JComboBox jComboBox_condition = null;\r
+       private JComboBox jComboBox_infection = null;\r
+       private JScrollPane jScrollPane_keywords = null;\r
+       private KDTable jTable_keywords = null;\r
+       private JLabel jLabel_okiniiri = null;\r
+       private JComboBox jComboBox_okiniiri = null;\r
+       private JLabel jLabel_group = null;\r
+       private JComboBox jComboBox_group = null;\r
+       private JCheckBoxPanel jCheckBox_caseSensitive = null;\r
+       private JCheckBoxPanel jCheckBox_showInStandby = null;\r
+       private JButton jButton_up = null;\r
+       private JButton jButton_down = null;\r
+\r
+       // コンポーネント以外\r
+       \r
+       private String windowTitle = "";\r
+       private String originalLabel = "";\r
+       private SearchProgram xKeys = null;\r
+       private SearchGroupList xGroups = null;\r
+       \r
+       private boolean reg = false;\r
+       \r
+       private ArrayList<TargetId> target_items = new ArrayList<TargetId>();\r
+       private ArrayList<String> contain_items = new ArrayList<String>(); \r
+       private ArrayList<String> condition_items = new ArrayList<String>(); \r
+       private ArrayList<String> infection_items = new ArrayList<String>();\r
+       private ArrayList<String> okiniiri_items = new ArrayList<String>(); \r
+       public void clean_target_items() { target_items.clear(); }\r
+       public void add_target_item(TargetId ti) { target_items.add(ti); }\r
+       public void clean_contain_items() { contain_items.clear(); }\r
+       public void add_contain_item(String s) { contain_items.add(s); }\r
+       public void clean_condition_items() { condition_items.clear(); }\r
+       public void add_condition_item(String s) { condition_items.add(s); }\r
+       public void clean_infection_items() { infection_items.clear(); }\r
+       public void add_infection_item(String s) { infection_items.add(s); }\r
+       public void clean_okiniiri_items() { okiniiri_items.clear(); }\r
+       public void add_okiniiri_item(String s) { okiniiri_items.add(s); }\r
+       \r
+       // 分類的にここでないような\r
+       \r
+       public String getNewLabel() { return jTextField_label.getText(); }\r
+       public String getNewGroup() { return (String)jComboBox_group.getSelectedItem(); }\r
+\r
+       /*******************************************************************************\r
+        * コンストラクタ\r
+        ******************************************************************************/\r
+       \r
+       public AbsKeywordDialog() {\r
+               \r
+               super();\r
+\r
+               //\r
+               reg = false;\r
+               \r
+               //\r
+               setItems();\r
+               \r
+               //\r
+               this.setModal(true);\r
+               this.setContentPane(getJPanel());\r
+               // タイトルバーの高さも考慮する必要がある\r
+               Dimension d = getJPanel().getPreferredSize();\r
+               this.pack();\r
+               this.setPreferredSize(new Dimension(d.width, d.height+this.getInsets().top));\r
+               this.setResizable(false);\r
+               //\r
+               this.setTitle(windowTitle);\r
+       }\r
+       \r
+       /*******************************************************************************\r
+        * アクション\r
+        ******************************************************************************/\r
+       \r
+       // 公開メソッド\r
+       \r
+       /**\r
+        *  検索条件が登録が実行されたかな?\r
+        */\r
+       public boolean isRegistered() { return reg; } \r
+       \r
+       // 使っている人いないね…\r
+       /*\r
+       private void open(String s, SearchProgram sKeys) {\r
+               //\r
+               xKeys = sKeys;\r
+               //\r
+               jComboBox_target.setSelectedIndex(0);\r
+               jComboBox_contain.setSelectedIndex(0);\r
+               jComboBox_condition.setSelectedIndex(0);\r
+               for (int i=jTable_keywords.getRowCount()-1; i>=0; i--) {\r
+                       jTable_keywords.getRowItemList().remove(i);\r
+               }\r
+               //\r
+               jTextField_label.setText(s);\r
+               jTextField_label.setCaretPosition(0);\r
+               jComboBox_regex.addItem(s);\r
+       }\r
+       */\r
+       \r
+       // キーワード検索管理としてのオープン(新規)\r
+       public void open(String label, SearchKey sK, SearchProgram sKeys, SearchGroupList gList) {\r
+               //\r
+               xKeys = sKeys;\r
+               xGroups = gList; \r
+               //\r
+               jComboBox_target.setSelectedIndex(0);\r
+               jComboBox_contain.setSelectedIndex(0);\r
+               jComboBox_condition.setSelectedIndex(0);\r
+               for (int i=jTable_keywords.getRowCount()-1; i>=0; i--) {\r
+                       jTable_keywords.getRowItemList().remove(i);\r
+               }\r
+               //\r
+               for (int i=0; i<sK.alTarget.size(); i++) {\r
+                       \r
+                       KDItem data = new KDItem();\r
+                       data.target = sK.alTarget.get(i);\r
+                       data.regex = sK.alKeyword.get(i);\r
+                       data.contain = contain_items.get(Integer.valueOf(sK.alContain.get(i)));\r
+                       data.fireChanged();\r
+                       jTable_keywords.getRowItemList().add(data);\r
+               }\r
+               jTable_keywords.fireChanged();\r
+               \r
+               jTextField_label.setText(label);\r
+               jTextField_label.setCaretPosition(0);\r
+               //\r
+               if ( xGroups != null && xGroups.size() != 0 ) {\r
+                       jComboBox_group.setEnabled(true);\r
+                       jComboBox_group.addItem("");\r
+                       for ( SearchGroup gr : xGroups ) {\r
+                               jComboBox_group.addItem(gr.getName());\r
+                       }\r
+               }\r
+\r
+               jCheckBox_caseSensitive.setSelected(sK.getCaseSensitive());\r
+               jCheckBox_showInStandby.setSelected(sK.getShowInStandby());\r
+       }\r
+       \r
+       // 延長警告管理としてのオープン(新規)\r
+       public void open(String title, String center, boolean isInfection, SearchProgram sKeys) {\r
+               //\r
+               xKeys = sKeys;\r
+               //\r
+               if (isInfection) {\r
+                       jTextField_label.setText("●"+title);\r
+               }\r
+               else {\r
+                       jTextField_label.setText("○"+title);\r
+               }\r
+               jTextField_label.setCaretPosition(0);\r
+               \r
+               jComboBox_regex.addItem("");\r
+               jComboBox_target.setSelectedIndex(0);\r
+               jComboBox_contain.setSelectedIndex(0);\r
+               jComboBox_condition.setSelectedIndex(0);\r
+               for (int i=jTable_keywords.getRowCount()-1; i>=0; i--) {\r
+                       jTable_keywords.getRowItemList().remove(i);\r
+               }\r
+               //\r
+               KDItem d1 = new KDItem();\r
+               d1.target = TargetId.TITLE;\r
+               d1.regex = title;\r
+               d1.contain = (String) jComboBox_contain.getItemAt(0);\r
+               d1.fireChanged();\r
+               jTable_keywords.getRowItemList().add(d1);\r
+               \r
+               KDItem d2 = new KDItem();\r
+               d2.target = TargetId.CHANNEL;\r
+               d2.regex = center;\r
+               d2.contain = (String) jComboBox_contain.getItemAt(0);\r
+               d2.fireChanged();\r
+               if (isInfection) {\r
+                       jComboBox_infection.setSelectedItem(infection_items.get(0));\r
+               }\r
+               else {\r
+                       jComboBox_infection.setSelectedItem(infection_items.get(1));\r
+               }\r
+               jTable_keywords.getRowItemList().add(d2);\r
+               \r
+               jTable_keywords.fireChanged();\r
+       }\r
+       \r
+       // キーワード検索管理としてのオープン(更新)\r
+       public void reopen(String s, SearchProgram sKeys) {\r
+               //\r
+               xKeys = sKeys;\r
+               originalLabel = s;\r
+               //\r
+               jTextField_label.setText(s);\r
+               jTextField_label.setCaretPosition(0);\r
+\r
+               jComboBox_regex.addItem("");\r
+               jComboBox_target.setSelectedIndex(0);\r
+               jComboBox_contain.setSelectedIndex(0);\r
+               jComboBox_condition.setSelectedIndex(0);\r
+               for (int i=jTable_keywords.getRowCount()-1; i>=0; i--) {\r
+                       jTable_keywords.getRowItemList().remove(i);\r
+               }\r
+               //\r
+               for (SearchKey k : xKeys.getSearchKeys()) {\r
+                       if (k.getLabel().equals(s)) {\r
+                       jComboBox_condition.setSelectedIndex(Integer.valueOf(k.getCondition()));\r
+                       \r
+                               Matcher ma = Pattern.compile("(.*?)\t").matcher(k.getTarget());\r
+                               Matcher mb = Pattern.compile("(.*?)\t").matcher(k.getKeyword());\r
+                               Matcher mc = Pattern.compile("(.*?)\t").matcher(k.getContain());\r
+                               while (ma.find()) {\r
+                                       mb.find();\r
+                                       mc.find();\r
+                                       KDItem data = new KDItem();\r
+                                       data.target = TargetId.getTargetId(ma.group(1));\r
+                                       data.regex = mb.group(1);\r
+                                       data.contain = contain_items.get(Integer.valueOf(mc.group(1)));\r
+                                       data.fireChanged();\r
+                                       jTable_keywords.getRowItemList().add(data);\r
+                               }\r
+                               \r
+                               jComboBox_okiniiri.setSelectedItem(k.getOkiniiri());\r
+                               \r
+                               jCheckBox_caseSensitive.setSelected(k.getCaseSensitive());\r
+                               jCheckBox_showInStandby.setSelected(k.getShowInStandby());\r
+                       }\r
+               }\r
+               \r
+               jTable_keywords.fireChanged();\r
+       }\r
+       \r
+       // 延長警告管理としてのオープン(更新)\r
+       public void reopen(String s, ExtProgram sKeys) {\r
+               //\r
+               for (SearchKey k : sKeys.getSearchKeys()) {\r
+                       if (k.getLabel().equals(s)) {\r
+                               int idx = Integer.valueOf(k.getInfection());\r
+                               if (jComboBox_infection.getItemCount() >= idx) {\r
+                                       jComboBox_infection.setSelectedIndex(idx);\r
+                               }\r
+                       }\r
+               }\r
+               reopen(s,(SearchProgram)sKeys);\r
+       }\r
+       \r
+       /**\r
+        *  コンパイル部分を分離してみました\r
+        */\r
+       protected SearchKey skCompile() {\r
+               \r
+               SearchKey sk = new SearchKey();\r
+               \r
+               String tStr = "";\r
+               String rStr = "";\r
+               String cStr = "";\r
+               for (int row=0; row<jTable_keywords.getRowCount(); row++) {\r
+                       \r
+                       KDItem c = jTable_keywords.getRowItemList().get(row);\r
+                       TargetId ti = c.target;\r
+                       tStr += ti.getId()+"\t";\r
+                       \r
+                       if ( ti.getUseRegexpr() ) {\r
+                               try {\r
+                                       Pattern.compile(TraceProgram.replacePop(c.regex));\r
+                               }\r
+                               catch (PatternSyntaxException ex) {\r
+                                       showExprFmtWarn(c.contain);\r
+                                       return null;\r
+                               }\r
+                       }\r
+                       rStr += c.regex+"\t";\r
+                       \r
+                       for (int i=0; i<contain_items.size(); i++) {\r
+                               if (contain_items.get(i).equals(c.contain)) {\r
+                                       cStr += i+"\t";\r
+                                       break;\r
+                               }\r
+                       }\r
+               }\r
+               sk.setTarget(tStr);             // compile後は順番が変わるので残すことにする\r
+               sk.setKeyword(rStr);    // 同上\r
+               sk.setContain(cStr);    // 同上\r
+               \r
+               for (int i=0; i<condition_items.size(); i++) {\r
+                       if (condition_items.get(i).equals((String) jComboBox_condition.getSelectedItem())) {\r
+                               sk.setCondition(String.valueOf(i));\r
+                               break;\r
+                       }\r
+               }\r
+               for (int i=0; i<infection_items.size(); i++) {\r
+                       if (infection_items.get(i).equals((String) jComboBox_infection.getSelectedItem())) {\r
+                               sk.setInfection(String.valueOf(i));\r
+                               break;\r
+                       }\r
+               }\r
+               \r
+               sk.setLabel(jTextField_label.getText());\r
+               sk.setOkiniiri((String) jComboBox_okiniiri.getSelectedItem());\r
+               sk.setCaseSensitive(jCheckBox_caseSensitive.isSelected());\r
+               sk.setShowInStandby(jCheckBox_showInStandby.isSelected());\r
+\r
+               new SearchProgram().compile(sk);\r
+               \r
+               return sk;\r
+       }\r
+\r
+       /**\r
+        * 正規表現がおかしかったら警告したい\r
+        * @param expr\r
+        */\r
+       private void showExprFmtWarn(String expr) {\r
+               JOptionPane.showMessageDialog(this, "正規表現の文法に則っていません: "+expr, "エラー", JOptionPane.ERROR_MESSAGE);\r
+       }\r
+       \r
+       /*******************************************************************************\r
+        * リスナー\r
+        ******************************************************************************/\r
+       \r
+       /**\r
+        * 条件を保存する\r
+        */\r
+       private final ActionListener al_save = new ActionListener() {\r
+               @Override\r
+               public void actionPerformed(ActionEvent e) {\r
+                       if (jTextField_label.getText().equals("") || jTable_keywords.getRowCount()<=0) {\r
+                               return;\r
+                       }\r
+                       \r
+                       SearchKey sk = skCompile();\r
+                       if (sk == null) {\r
+                               return;\r
+                       }\r
+                       \r
+                       // 検索キーワードを保存する\r
+                       if (xKeys.replace(originalLabel, sk) == null) {\r
+                               xKeys.add(sk);\r
+                       }\r
+                       xKeys.save();\r
+                       \r
+                       // グループに登録するかも\r
+                       if ( jComboBox_group.getSelectedItem() != null && ! ((String)jComboBox_group.getSelectedItem()).equals("")) {\r
+                               xGroups.add((String)jComboBox_group.getSelectedItem(),sk.getLabel());\r
+                               xGroups.save();\r
+                       }\r
+                       \r
+                       // 登録したお!\r
+                       reg = true;\r
+                       \r
+                       // ウィンドウを閉じる\r
+                       dispose();\r
+               }\r
+       };\r
+       \r
+       /**\r
+        * キャンセルしたい\r
+        */\r
+       private final ActionListener al_cancel = new ActionListener() {\r
+               @Override\r
+               public void actionPerformed(ActionEvent e) {\r
+                       dispose();\r
+               }\r
+       };\r
+       \r
+       /**\r
+        * プレビューしたい\r
+        */\r
+       private final ActionListener al_preview = new ActionListener() {\r
+               @Override\r
+               public void actionPerformed(ActionEvent e) {\r
+                       SearchKey sk = skCompile(); \r
+                       if (jTable_keywords.getRowCount() > 0 && sk != null) {\r
+                               preview(sk);\r
+                       }\r
+               }\r
+       };\r
+       \r
+       /**\r
+        * 条件選択コンボボックスが選択された\r
+        */\r
+       private final ItemListener il_targetChanged = new ItemListener() {\r
+               @Override\r
+               public void itemStateChanged(ItemEvent e) {\r
+                       //\r
+                       if ( e.getStateChange() != ItemEvent.SELECTED ) {\r
+                               return;\r
+                       }\r
+                       //\r
+                       TargetId target = (TargetId) jComboBox_target.getSelectedItem();\r
+                       if (target == TargetId.NEW || target == TargetId.LAST || target == TargetId.REPEAT || target == TargetId.FIRST || target == TargetId.REPEAT || target == TargetId.NOSCRUMBLE) {\r
+                               jComboBox_regex.setEnabled(false);\r
+                               jComboBox_regex.setEditable(false);\r
+                               jComboBox_regex.removeAllItems();\r
+                               jComboBox_regex.addItem("");\r
+                       }\r
+                       else if (target == TargetId.LENGTH) {\r
+                               jComboBox_regex.removeAllItems();\r
+                               jComboBox_regex.setEnabled(true);\r
+                               jComboBox_regex.setEditable(false);\r
+                               for (int i=1; i <= 120; i++) {\r
+                                       jComboBox_regex.addItem(i+" 分以上である");\r
+                               }\r
+                               jComboBox_regex.setSelectedIndex(29);\r
+                       }\r
+                       else if (target == TargetId.STARTA) {\r
+                               jComboBox_regex.removeAllItems();\r
+                               jComboBox_regex.setEnabled(true);\r
+                               jComboBox_regex.setEditable(false);\r
+                               for (int i=0; i < 24; i++) {\r
+                                       jComboBox_regex.addItem(String.format("%02d:00以降である", (i+5)%24));\r
+                               }\r
+                               jComboBox_regex.setSelectedIndex(18-5);\r
+                       }\r
+                       else if (target == TargetId.STARTZ) {\r
+                               jComboBox_regex.removeAllItems();\r
+                               jComboBox_regex.setEnabled(true);\r
+                               jComboBox_regex.setEditable(false);\r
+                               for (int i=0; i < 24; i++) {\r
+                                       jComboBox_regex.addItem(String.format("%02d:00以前である", (i+5)%24));\r
+                               }\r
+                               jComboBox_regex.setSelectedIndex(23-5);\r
+                       }\r
+                       else if (target == TargetId.GENRE) {\r
+                               jComboBox_regex.removeAllItems();\r
+                               jComboBox_regex.setEnabled(true);\r
+                               jComboBox_regex.setEditable(false);\r
+                               for ( ProgGenre genre : ProgGenre.values()) {\r
+                                       jComboBox_regex.addItem(genre.toString());\r
+                               }\r
+                       }\r
+                       else if (target == TargetId.SUBGENRE) {\r
+                               jComboBox_regex.removeAllItems();\r
+                               jComboBox_regex.setEnabled(true);\r
+                               jComboBox_regex.setEditable(false);\r
+                               for ( ProgSubgenre subgenre : ProgSubgenre.values()) {\r
+                                       jComboBox_regex.addItem(subgenre.toString());\r
+                               }\r
+                       }\r
+                       else {\r
+                               // 番組名・番組詳細・放送局・開始日時\r
+                               jComboBox_regex.setEnabled(true);\r
+                               jComboBox_regex.setEditable(true);\r
+                               if (jComboBox_regex.getItemCount() > 1) {\r
+                                       jComboBox_regex.removeAllItems();\r
+                                       jComboBox_regex.addItem("");\r
+                               }\r
+                       }\r
+               }\r
+       };\r
+       \r
+       /**\r
+        * 条件を追加する\r
+        */\r
+       private final ActionListener al_addTarget = new ActionListener() {\r
+               @Override\r
+               public void actionPerformed(ActionEvent e) {\r
+                       TargetId ti = (TargetId) jComboBox_target.getSelectedItem();\r
+                       String re = (String) jComboBox_regex.getSelectedItem();\r
+                       \r
+                       if ( ti.getUseKeyword() && re.length() == 0 ) {\r
+                               // キーワードが必要なのに入力されていなければNG\r
+                               return;\r
+                       }\r
+                       \r
+                       KDItem data = new KDItem();\r
+                       data.target = ti;\r
+                       data.regex = re;\r
+                       data.contain = (String) jComboBox_contain.getSelectedItem();\r
+                       data.fireChanged();\r
+                       jTable_keywords.getRowItemList().add(data);\r
+                       jTable_keywords.fireChanged();\r
+                       \r
+                       if (jComboBox_regex.getItemCount() <= 1) {\r
+                               jComboBox_regex.removeAllItems();\r
+                               jComboBox_regex.addItem("");\r
+                       }\r
+               }\r
+       };\r
+       \r
+       /**\r
+        * 条件を削除する(コンボボックスに戻す)\r
+        */\r
+       private final ActionListener al_removeTarget = new ActionListener() {\r
+               @Override\r
+               public void actionPerformed(ActionEvent e) {\r
+                       int row = -1;\r
+                       if ((row = jTable_keywords.getSelectedRow()) == -1) {\r
+                               return;\r
+                       }\r
+                       \r
+                       KDItem c = jTable_keywords.getRowItemList().get(row);\r
+                       jComboBox_target.setSelectedItem(c.target);\r
+                       if (jComboBox_target.getSelectedItem() ==  TargetId.GENRE ||\r
+                                       jComboBox_target.getSelectedItem() ==  TargetId.SUBGENRE ||\r
+                                       jComboBox_target.getSelectedItem() ==  TargetId.LENGTH ||\r
+                                       jComboBox_target.getSelectedItem() ==  TargetId.STARTA ||\r
+                                       jComboBox_target.getSelectedItem() ==  TargetId.STARTZ ) {\r
+                               jComboBox_regex.setSelectedItem(c.regex);\r
+                       }\r
+                       else {\r
+                               jComboBox_regex.removeAllItems();\r
+                               jComboBox_regex.addItem(c.regex);\r
+                       }\r
+                       jComboBox_contain.setSelectedItem(c.contain);\r
+                       \r
+                       jTable_keywords.getRowItemList().remove(jTable_keywords.getSelectedRow());\r
+                       jTable_keywords.fireChanged();\r
+               }\r
+       };\r
+       \r
+       /**\r
+        * \r
+        */\r
+       private final ActionListener al_upTarget = new ActionListener() {\r
+               @Override\r
+               public void actionPerformed(ActionEvent e) {\r
+                       int row = jTable_keywords.getSelectedRow();\r
+                       if ( jTable_keywords.getRowItemList().up(row, 1) ) {\r
+                               jTable_keywords.fireChanged();\r
+                               jTable_keywords.setRowSelectionInterval(row-1,row-1);\r
+                       }\r
+               }\r
+       };\r
+       \r
+       /**\r
+        * \r
+        */\r
+       private final ActionListener al_downTarget = new ActionListener() {\r
+               @Override\r
+               public void actionPerformed(ActionEvent e) {\r
+                       int row = jTable_keywords.getSelectedRow();\r
+                       if ( jTable_keywords.getRowItemList().down(row, 1) ) {\r
+                               jTable_keywords.fireChanged();\r
+                               jTable_keywords.setRowSelectionInterval(row+1,row+1);\r
+                       }\r
+               }\r
+       };\r
+\r
+       /*******************************************************************************\r
+        * コンポーネント\r
+        ******************************************************************************/\r
+       \r
+       private JPanel getJPanel() {\r
+               if (jPanel == null) {\r
+                       jPanel = new JPanel();\r
+\r
+                       jPanel.setLayout(new SpringLayout());\r
+                       \r
+                       int y = SEP_HEIGHT;\r
+                       int x = SEP_WIDTH;\r
+                       \r
+                       x = SEP_WIDTH;\r
+                       CommonSwingUtils.putComponentOn(jPanel, getJLabel_label("設定名"), LABEL_WIDTH_S, PARTS_HEIGHT, x, y);\r
+                       CommonSwingUtils.putComponentOn(jPanel, getJTextField_label(), TEXT_WIDTH, PARTS_HEIGHT, x+=LABEL_WIDTH_S, y);\r
+                       CommonSwingUtils.putComponentOn(jPanel, getJButton_label("登録"), BUTTON_WIDTH_S, PARTS_HEIGHT, x+=TEXT_WIDTH+SEP_WIDTH, y);\r
+                       CommonSwingUtils.putComponentOn(jPanel, getJButton_cancel("キャンセル"), BUTTON_WIDTH_S, PARTS_HEIGHT, x+=BUTTON_WIDTH_S+SEP_WIDTH, y);\r
+                       CommonSwingUtils.putComponentOn(jPanel, getJButton_preview("プレビュー"), BUTTON_WIDTH_S, PARTS_HEIGHT, PANEL_WIDTH-BUTTON_WIDTH_S-SEP_WIDTH, y);\r
+                       \r
+                       y += PARTS_HEIGHT+SEP_HEIGHT;\r
+                       x = SEP_WIDTH;\r
+                       CommonSwingUtils.putComponentOn(jPanel, getJComboBox_target(), TARGET_WIDTH, PARTS_HEIGHT, x, y);\r
+                       CommonSwingUtils.putComponentOn(jPanel, getJTextField_regex(), REGEX_WIDTH, PARTS_HEIGHT, x+=TARGET_WIDTH+SEP_WIDTH, y);\r
+                       CommonSwingUtils.putComponentOn(jPanel, getJComboBox_contain(), TARGET_WIDTH, PARTS_HEIGHT, x+=REGEX_WIDTH+SEP_WIDTH, y);\r
+                       \r
+                       y += PARTS_HEIGHT+SEP_HEIGHT_NARROW;\r
+                       x = (TABLE_WIDTH/2-(BUTTON_WIDTH_L+SEP_WIDTH/2))+SEP_WIDTH;\r
+                       CommonSwingUtils.putComponentOn(jPanel, getJButton_add("追加↓"), BUTTON_WIDTH_L, PARTS_HEIGHT, x , y);\r
+                       CommonSwingUtils.putComponentOn(jPanel, getJButton_remove("↑削除"), BUTTON_WIDTH_L, PARTS_HEIGHT, x+=BUTTON_WIDTH_L+SEP_WIDTH, y);\r
+                       \r
+                       CommonSwingUtils.putComponentOn(jPanel, getJCheckBox_caseSensitive("文字列比較は完全一致で",CHECKLABEL_WIDTH,false), CHECKBOX_WIDTH, PARTS_HEIGHT, PANEL_WIDTH-CHECKBOX_WIDTH-SEP_WIDTH, y);\r
+                       \r
+                       y += PARTS_HEIGHT+SEP_HEIGHT_NARROW;\r
+                       CommonSwingUtils.putComponentOn(jPanel, getJComboBox_condition(), CONDITION_WIDTH, PARTS_HEIGHT, SEP_WIDTH, y);\r
+                       CommonSwingUtils.putComponentOn(jPanel, getJComboBox_infection(), CONDITION_WIDTH, PARTS_HEIGHT, TABLE_WIDTH-CONDITION_WIDTH+SEP_WIDTH, y);\r
+                       \r
+                       y += PARTS_HEIGHT+SEP_HEIGHT_NARROW;\r
+                       CommonSwingUtils.putComponentOn(jPanel, getJScrollPane_keywords(), TABLEPANE_WIDTH, TABLE_HEIGHT, SEP_WIDTH, y);\r
+                       int yz = y + (TABLE_HEIGHT/2-(PARTS_HEIGHT+SEP_HEIGHT/2));\r
+                       CommonSwingUtils.putComponentOn(jPanel, getJButton_up("↑"), UPDOWN_WIDTH, PARTS_HEIGHT, TABLEPANE_WIDTH+SEP_WIDTH*2, yz);\r
+                       CommonSwingUtils.putComponentOn(jPanel, getJButton_down("↓"), UPDOWN_WIDTH, PARTS_HEIGHT, TABLEPANE_WIDTH+SEP_WIDTH*2, yz+=SEP_HEIGHT+PARTS_HEIGHT);\r
+\r
+                       y += TABLE_HEIGHT+SEP_HEIGHT;\r
+                       x = SEP_WIDTH;\r
+                       CommonSwingUtils.putComponentOn(jPanel, getJLabel_okiniiri("お気に入り度"), LABEL_WIDTH_L, PARTS_HEIGHT, x, y);\r
+                       CommonSwingUtils.putComponentOn(jPanel, getJComboBox_okiniiri(), SELECTION_WIDTH, PARTS_HEIGHT, x+=LABEL_WIDTH_L+SEP_WIDTH, y);\r
+                       CommonSwingUtils.putComponentOn(jPanel, getJLabel_group("グループ"), LABEL_WIDTH_L, PARTS_HEIGHT, x+=SELECTION_WIDTH+SEP_WIDTH, y);\r
+                       CommonSwingUtils.putComponentOn(jPanel, getJComboBox_group(), SELECTION_WIDTH, PARTS_HEIGHT, x+=LABEL_WIDTH_L+SEP_WIDTH, y);\r
+                       \r
+                       CommonSwingUtils.putComponentOn(jPanel, getJCheckBox_showInStandby("予約待機に表示する",CHECKLABEL_WIDTH,true), CHECKBOX_WIDTH, PARTS_HEIGHT, PANEL_WIDTH-CHECKBOX_WIDTH-SEP_WIDTH, y);\r
+\r
+                       y += PARTS_HEIGHT+SEP_HEIGHT*2;\r
+                       \r
+                       jPanel.setPreferredSize(new Dimension(PANEL_WIDTH, y));\r
+               }\r
+               return jPanel;\r
+       }\r
+       \r
+       //\r
+       private JLabel getJLabel_label(String s) {\r
+               if (jLabel_label == null) {\r
+                       jLabel_label = new JLabel(s);\r
+               }\r
+               return(jLabel_label);\r
+       }\r
+       \r
+       //\r
+       private JTextField getJTextField_label() {\r
+               if (jTextField_label == null) {\r
+                       jTextField_label = new JTextFieldWithPopup();\r
+               }\r
+               return(jTextField_label);\r
+       }\r
+       \r
+       //\r
+       private JButton getJButton_label(String s) {\r
+               if (jButton_label == null) {\r
+                       jButton_label = new JButton();\r
+                       jButton_label.setText(s);\r
+                       \r
+                       jButton_label.addActionListener(al_save);\r
+               }\r
+               return(jButton_label);\r
+       }\r
+       \r
+       //\r
+       private JButton getJButton_cancel(String s) {\r
+               if (jButton_cancel == null) {\r
+                       jButton_cancel = new JButton();\r
+                       jButton_cancel.setText(s);\r
+                       \r
+                       jButton_cancel.addActionListener(al_cancel);\r
+               }\r
+               return jButton_cancel;\r
+       }\r
+       \r
+       //\r
+       private JButton getJButton_preview(String s) {\r
+               if (jButton_preview == null) {\r
+                       jButton_preview = new JButton();\r
+                       jButton_preview.setText(s);\r
+                       \r
+                       jButton_preview.addActionListener(al_preview);\r
+               }\r
+               return jButton_preview;\r
+       }\r
+                       \r
+       \r
+       //\r
+       private JComboBox getJComboBox_target() {\r
+               if (jComboBox_target == null) {\r
+                       jComboBox_target = new JComboBox();\r
+                       \r
+                       DefaultComboBoxModel aModel = new DefaultComboBoxModel();\r
+                       jComboBox_target.setModel(aModel);\r
+                       for (TargetId k : target_items) {\r
+                               aModel.addElement(k);\r
+                       }\r
+                       \r
+                       jComboBox_target.addItemListener(il_targetChanged);\r
+               }\r
+               return(jComboBox_target);\r
+       }\r
+       \r
+       //\r
+       private JComboBox getJTextField_regex() {\r
+               if (jComboBox_regex == null) {\r
+                       jComboBox_regex = new JComboBoxWithPopup();\r
+                       jComboBox_regex.setEditable(true);\r
+               }\r
+               return(jComboBox_regex);\r
+       }\r
+       \r
+       //\r
+       private JComboBox getJComboBox_contain() {\r
+               if (jComboBox_contain == null) {\r
+                       jComboBox_contain = new JComboBox();\r
+                       \r
+                       DefaultComboBoxModel aModel = new DefaultComboBoxModel();\r
+                       jComboBox_contain.setModel(aModel);\r
+                       for (String k : contain_items) {\r
+                               aModel.addElement(k);\r
+                       }\r
+               }\r
+               return(jComboBox_contain);\r
+       }\r
+       \r
+       //\r
+       private JComboBox getJComboBox_condition() {\r
+               if (jComboBox_condition == null) {\r
+                       jComboBox_condition = new JComboBox();\r
+                       \r
+                       DefaultComboBoxModel aModel = new DefaultComboBoxModel();\r
+                       jComboBox_condition.setModel(aModel);\r
+                       for (String k : condition_items) {\r
+                               aModel.addElement(k);\r
+                       }\r
+               }\r
+               return(jComboBox_condition);\r
+       }\r
+       \r
+       //\r
+       private JComboBox getJComboBox_infection() {\r
+               if (jComboBox_infection == null) {\r
+                       jComboBox_infection = new JComboBox();\r
+                       \r
+                       if (infection_items.size() > 0) {\r
+                       DefaultComboBoxModel aModel = new DefaultComboBoxModel();\r
+                       jComboBox_infection.setModel(aModel);\r
+                       for (String k : infection_items) {\r
+                               aModel.addElement(k);\r
+                       }\r
+                       }\r
+                       else {\r
+                               jComboBox_infection.setEnabled(false);\r
+                       }\r
+               }\r
+               return(jComboBox_infection);\r
+       }\r
+       \r
+       //\r
+       private JLabel getJLabel_okiniiri(String s) {\r
+               if (jLabel_okiniiri == null) {\r
+                       jLabel_okiniiri = new JLabel(s,SwingConstants.RIGHT);\r
+               }\r
+               return(jLabel_okiniiri);\r
+       }\r
+       private JComboBox getJComboBox_okiniiri() {\r
+               if (jComboBox_okiniiri == null) {\r
+                       jComboBox_okiniiri = new JComboBox();\r
+                       \r
+                       DefaultComboBoxModel aModel = new DefaultComboBoxModel();\r
+                       jComboBox_okiniiri.setModel(aModel);\r
+                       for (String k : okiniiri_items) {\r
+                               aModel.addElement(k);\r
+                       }\r
+               }\r
+               return(jComboBox_okiniiri);\r
+       }\r
+\r
+       \r
+       //\r
+       private JLabel getJLabel_group(String s) {\r
+               if (jLabel_group == null) {\r
+                       jLabel_group = new JLabel(s,SwingConstants.RIGHT);\r
+               }\r
+               return(jLabel_group);\r
+       }\r
+       private JComboBox getJComboBox_group() {\r
+               if (jComboBox_group == null) {\r
+                       jComboBox_group = new JComboBox();\r
+                       \r
+                       jComboBox_group.setEnabled(false);\r
+               }\r
+               return(jComboBox_group);\r
+       }\r
+       \r
+       //\r
+       private JCheckBoxPanel getJCheckBox_caseSensitive(String s,int labelWidth, boolean b) {\r
+               if (jCheckBox_caseSensitive == null) {\r
+                       jCheckBox_caseSensitive = new JCheckBoxPanel(s,labelWidth,true);\r
+                       jCheckBox_caseSensitive.setSelected(b);\r
+               }\r
+               return(jCheckBox_caseSensitive);\r
+       }\r
+       \r
+       //\r
+       private JCheckBoxPanel getJCheckBox_showInStandby(String s,int labelWidth, boolean b) {\r
+               if (jCheckBox_showInStandby == null) {\r
+                       jCheckBox_showInStandby = new JCheckBoxPanel(s,labelWidth,true);\r
+                       jCheckBox_showInStandby.setSelected(b);\r
+               }\r
+               return(jCheckBox_showInStandby);\r
+       }\r
+       \r
+       // 条件追加のボタン\r
+       private JButton getJButton_add(String s) {\r
+               if (jButton_add == null) {\r
+                       jButton_add = new JButton();\r
+                       jButton_add.setText(s);\r
+                       \r
+                       jButton_add.addActionListener(al_addTarget);\r
+               }\r
+               return(jButton_add);\r
+       }\r
+       \r
+       // 条件削除のボタン\r
+       private JButton getJButton_remove(String s) {\r
+               if (jButton_remove == null) {\r
+                       jButton_remove = new JButton();\r
+                       jButton_remove.setText(s);\r
+                       \r
+                       jButton_remove.addActionListener(al_removeTarget);\r
+               }\r
+               return(jButton_remove);\r
+       }\r
+       \r
+       private JButton getJButton_up(String s) {\r
+               if (jButton_up == null) {\r
+                       jButton_up = new JButton();\r
+                       jButton_up.setText(s);\r
+                       \r
+                       jButton_up.addActionListener(al_upTarget);\r
+               }\r
+               return(jButton_up);\r
+       }\r
+       \r
+       private JButton getJButton_down(String s) {\r
+               if (jButton_down == null) {\r
+                       jButton_down = new JButton();\r
+                       jButton_down.setText(s);\r
+                       \r
+                       jButton_down.addActionListener(al_downTarget);\r
+               }\r
+               return(jButton_down);\r
+       }\r
+       \r
+       //\r
+       private JScrollPane getJScrollPane_keywords() {\r
+               if (jScrollPane_keywords == null) {\r
+                       jScrollPane_keywords = new JScrollPane(JScrollPane.VERTICAL_SCROLLBAR_ALWAYS, JScrollPane.HORIZONTAL_SCROLLBAR_AS_NEEDED);\r
+                       jScrollPane_keywords.setViewportView(getJTable_keywords());\r
+               }\r
+               return jScrollPane_keywords;\r
+       }\r
+       \r
+       //\r
+       private KDTable getJTable_keywords() {\r
+               if (jTable_keywords == null) {\r
+                       \r
+                       jTable_keywords = new KDTable(false, new RowItemList<KDItem>());\r
+                       \r
+                       // テーブルの基本的な設定\r
+                       ArrayList<String> cola = new ArrayList<String>();\r
+                       for ( KDColumn rc : KDColumn.values() ) {\r
+                               if ( rc.getIniWidth() >= 0 ) {\r
+                                       cola.add(rc.getName());\r
+                               }\r
+                       }\r
+                       jTable_keywords.setModel(new DefaultTableModel(cola.toArray(new String[0]), 0));\r
+                       \r
+                       jTable_keywords.setAutoResizeMode(JNETable.AUTO_RESIZE_OFF);\r
+                       jTable_keywords.setSelectionMode(ListSelectionModel.SINGLE_SELECTION);\r
+                       jTable_keywords.getTableHeader().setReorderingAllowed(false);\r
+                       \r
+                       // 各カラムの幅\r
+                       DefaultTableColumnModel columnModel = (DefaultTableColumnModel)jTable_keywords.getColumnModel();\r
+                       //TableColumn column = null;\r
+                       for ( KDColumn rc : KDColumn.values() ) {\r
+                               if ( rc.getIniWidth() < 0 ) {\r
+                                       continue;\r
+                               }\r
+                               columnModel.getColumn(rc.ordinal()).setPreferredWidth(rc.getIniWidth());;\r
+                       }\r
+\r
+               }\r
+               return(jTable_keywords);\r
+       }\r
+       \r
+       /*******************************************************************************\r
+        * 独自コンポーネント\r
+        ******************************************************************************/\r
+       \r
+       private class KDItem extends RowItem implements Cloneable {\r
+               TargetId target;\r
+               String regex;\r
+               String contain;\r
+               \r
+               @Override\r
+               protected void myrefresh(RowItem o) {\r
+                       KDItem c = (KDItem) o;\r
+                       c.addData(target);\r
+                       c.addData(regex);\r
+                       c.addData(contain);\r
+               }\r
+               \r
+               @Override\r
+               public KDItem clone() {\r
+                       return (KDItem) super.clone();\r
+               }\r
+       }\r
+       \r
+       private class KDTable extends JNETable {\r
+\r
+               private static final long serialVersionUID = 1L;\r
+               \r
+               RowItemList<KDItem> rowdata = null;\r
+               \r
+               public KDTable(boolean b, RowItemList<KDItem> rowdata) {\r
+                       super(b);\r
+                       this.rowdata = rowdata;\r
+               }\r
+               \r
+               public RowItemList<KDItem> getRowItemList() { return rowdata; }\r
+               \r
+               public void fireChanged() { ((DefaultTableModel) this.getModel()).fireTableDataChanged(); }\r
+               \r
+               @Override\r
+               public int getRowCount() { return rowdata.size(); }\r
+               \r
+               @Override\r
+               public Object getValueAt(int row, int column) {\r
+                       return rowdata.get(row).get(column);\r
+               }\r
+               \r
+               @Override\r
+               public void setValueAt(Object aValue, int row, int column) {\r
+                       KDItem c = rowdata.get(row);\r
+                       if ( column == KDColumn.TARGET.getColumn() ) {\r
+                               c.target = (TargetId) aValue;\r
+                       }\r
+                       else if ( column == KDColumn.REGEX.getColumn() ) {\r
+                               c.regex = (String) aValue;\r
+                       }\r
+                       else if ( column == KDColumn.CONTAIN.getColumn() ) {\r
+                               c.contain = (String) aValue;\r
+                       }\r
+                       c.fireChanged();\r
+               }\r
+               \r
+       }\r
+\r
+       /*******************************************************************************\r
+        * 延長警告管理では以下をオーバーライドしてください\r
+        ******************************************************************************/\r
+       \r
+       protected void setItems() {\r
+               setWindowTitle("キーワード検索の設定");\r
+               \r
+               clean_target_items();\r
+               add_target_item(TargetId.TITLEANDDETAIL);\r
+               add_target_item(TargetId.TITLE);\r
+               add_target_item(TargetId.DETAIL);\r
+               add_target_item(TargetId.CHANNEL);\r
+               add_target_item(TargetId.GENRE);\r
+               add_target_item(TargetId.SUBGENRE);\r
+               add_target_item(TargetId.NEW);\r
+               add_target_item(TargetId.LAST);\r
+               add_target_item(TargetId.REPEAT);\r
+               add_target_item(TargetId.FIRST);\r
+               add_target_item(TargetId.SPECIAL);\r
+               add_target_item(TargetId.RATING);\r
+               add_target_item(TargetId.NOSCRUMBLE);\r
+               add_target_item(TargetId.LIVE);\r
+               add_target_item(TargetId.BILINGUAL);\r
+               add_target_item(TargetId.STANDIN);\r
+               add_target_item(TargetId.LENGTH);\r
+               add_target_item(TargetId.STARTA);\r
+               add_target_item(TargetId.STARTZ);\r
+               add_target_item(TargetId.STARTDATETIME);\r
+\r
+               clean_contain_items();\r
+               add_contain_item("を含む番組");\r
+               add_contain_item("を含む番組を除く");\r
+               \r
+               clean_condition_items();\r
+               add_condition_item("次のすべての条件に一致");\r
+               add_condition_item("次のいずれかの条件に一致");\r
+       \r
+               clean_infection_items();\r
+       //add_infection_item();\r
+\r
+               clean_okiniiri_items();\r
+               for (String okini : TVProgram.OKINIIRI) {\r
+                       add_okiniiri_item(okini);\r
+               }\r
+       }\r
+}\r
diff --git a/TinyBannavi/src/tainavi/AbsListedView.java b/TinyBannavi/src/tainavi/AbsListedView.java
new file mode 100644 (file)
index 0000000..0e9da88
--- /dev/null
@@ -0,0 +1,4140 @@
+package tainavi;\r
+\r
+import java.awt.BorderLayout;\r
+import java.awt.Color;\r
+import java.awt.Component;\r
+import java.awt.Desktop;\r
+import java.awt.Dimension;\r
+import java.awt.Point;\r
+import java.awt.event.ActionEvent;\r
+import java.awt.event.ActionListener;\r
+import java.awt.event.ComponentAdapter;\r
+import java.awt.event.ComponentEvent;\r
+import java.awt.event.ComponentListener;\r
+import java.awt.event.MouseAdapter;\r
+import java.awt.event.MouseEvent;\r
+import java.awt.event.MouseListener;\r
+import java.io.File;\r
+import java.io.IOException;\r
+import java.net.URI;\r
+import java.net.URISyntaxException;\r
+import java.util.ArrayList;\r
+import java.util.Calendar;\r
+import java.util.Comparator;\r
+import java.util.GregorianCalendar;\r
+import java.util.HashMap;\r
+import java.util.Map.Entry;\r
+import java.util.regex.Matcher;\r
+import java.util.regex.Pattern;\r
+\r
+import javax.swing.ButtonGroup;\r
+import javax.swing.JComponent;\r
+import javax.swing.JMenuItem;\r
+import javax.swing.JOptionPane;\r
+import javax.swing.JPanel;\r
+import javax.swing.JPopupMenu;\r
+import javax.swing.JRadioButtonMenuItem;\r
+import javax.swing.JScrollPane;\r
+import javax.swing.JSplitPane;\r
+import javax.swing.JTable;\r
+import javax.swing.JTree;\r
+import javax.swing.SwingConstants;\r
+import javax.swing.SwingUtilities;\r
+import javax.swing.event.ListSelectionEvent;\r
+import javax.swing.event.ListSelectionListener;\r
+import javax.swing.event.RowSorterEvent;\r
+import javax.swing.event.RowSorterEvent.Type;\r
+import javax.swing.event.RowSorterListener;\r
+import javax.swing.event.TableModelEvent;\r
+import javax.swing.event.TreeExpansionEvent;\r
+import javax.swing.event.TreeExpansionListener;\r
+import javax.swing.event.TreeSelectionEvent;\r
+import javax.swing.event.TreeSelectionListener;\r
+import javax.swing.table.DefaultTableCellRenderer;\r
+import javax.swing.table.DefaultTableColumnModel;\r
+import javax.swing.table.DefaultTableModel;\r
+import javax.swing.table.TableCellRenderer;\r
+import javax.swing.table.TableColumn;\r
+import javax.swing.table.TableModel;\r
+import javax.swing.table.TableRowSorter;\r
+import javax.swing.tree.DefaultMutableTreeNode;\r
+import javax.swing.tree.DefaultTreeModel;\r
+import javax.swing.tree.TreeNode;\r
+import javax.swing.tree.TreePath;\r
+import javax.swing.tree.TreeSelectionModel;\r
+\r
+import tainavi.TVProgram.ProgFlags;\r
+import tainavi.TVProgram.ProgGenre;\r
+import tainavi.TVProgram.ProgSubgenre;\r
+import tainavi.TVProgram.ProgSubtype;\r
+import tainavi.TVProgram.ProgType;\r
+import tainavi.TVProgramIterator.IterationType;\r
+import tainavi.VWMainWindow.MWinTab;\r
+\r
+\r
+/**\r
+ * リスト形式タブのクラス\r
+ * @since 3.15.4β {@link Viewer}から分離\r
+ */\r
+public abstract class AbsListedView extends JPanel implements VWTimerRiseListener {\r
+\r
+       private static final long serialVersionUID = 1L;\r
+       \r
+       public static String getViewName() { return "リスト形式"; } \r
+       \r
+       public void setDebug(boolean b) { debug = b; }\r
+       private static boolean debug = false;\r
+       \r
+       \r
+       /*******************************************************************************\r
+        * 抽象メソッド\r
+        ******************************************************************************/\r
+       \r
+       protected abstract Env getEnv();\r
+       protected abstract Bounds getBoundsEnv();\r
+       protected abstract ChannelSort getChannelSort();\r
+       \r
+       protected abstract MarkedProgramList getMarkedProgramList();\r
+       protected abstract TraceProgram getTraceProgram();\r
+       protected abstract SearchProgram getSearchProgram();\r
+       protected abstract SearchGroupList getSearchGroupList();\r
+       protected abstract ExtProgram getExtProgram();\r
+       \r
+       protected abstract TVProgramList getTVProgramList();\r
+       protected abstract HDDRecorderList getRecorderList();\r
+       \r
+       protected abstract StatusWindow getStWin(); \r
+       protected abstract StatusTextArea getMWin();\r
+       protected abstract AbsReserveDialog getReserveDialog();\r
+       \r
+       protected abstract Component getParentComponent();\r
+       \r
+       protected abstract void ringBeep();\r
+       \r
+       // クラス内のイベントから呼び出されるもの\r
+       \r
+       /**\r
+        * タブが開いた\r
+        */\r
+       protected abstract void onShown();\r
+       /**\r
+        * タブが閉じた\r
+        */\r
+       protected abstract void onHidden();\r
+       \r
+       /**\r
+        * マウス右クリックメニューを表示する\r
+        */\r
+       protected abstract void showPopupForTraceProgram(\r
+                       final JComponent comp,\r
+                       final ProgDetailList tvd, final String keyword, final int threshold,\r
+                       final int x, final int y, final int h\r
+                       );\r
+\r
+       /**\r
+        * 予約マーク・予約枠を更新してほしい\r
+        */\r
+       protected abstract void updateReserveDisplay(String chname);\r
+\r
+       /**\r
+        * 番組枠を更新してほしい\r
+        */\r
+       protected abstract void updateBangumiColumns();\r
+\r
+       \r
+       /**\r
+        * 開けてくれ!\r
+        */\r
+       protected abstract void clearPaper();\r
+       \r
+       /**\r
+        * ぷれびゅりたいお\r
+        */\r
+       protected abstract void previewKeywordSearch(SearchKey search);\r
+       //protected abstract void previewExtensionSearch(SearchKey search);\r
+       /**\r
+        * 新聞形式にジャンプしてほしい\r
+        */\r
+       protected abstract void jumpToPaper(String Center, String StartDateTime);\r
+       \r
+       /**\r
+        * ピックアップに追加してほしい\r
+        */\r
+       protected abstract boolean addToPickup(final ProgDetailList tvd);\r
+       \r
+       protected abstract boolean isTabSelected(MWinTab tab);\r
+       protected abstract void setSelectedTab(MWinTab tab);\r
+\r
+       /**\r
+        * @see Viewer.VWToolBar#getSelectedRecorder()\r
+        */\r
+       protected abstract String getSelectedRecorderOnToolbar();\r
+       protected abstract boolean isFullScreen();\r
+       //protected abstract void setPagerItems(int total_page, int idx);\r
+       protected abstract void setPagerEnabled(boolean b);\r
+       protected abstract int getPagerCount();\r
+       protected abstract int getSelectedPagerIndex();\r
+\r
+       /**\r
+        * ツリーペーンの幅の変更を保存してほしい\r
+        */\r
+       protected abstract void setDividerEnvs(int loc);\r
+\r
+       \r
+       /*******************************************************************************\r
+        * 呼び出し元から引き継いだもの\r
+        ******************************************************************************/\r
+       \r
+       // オブジェクト\r
+       private final Env env = getEnv();\r
+       private final Bounds bounds = getBoundsEnv();\r
+       private final ChannelSort chsort = getChannelSort();\r
+       \r
+       private final MarkedProgramList mpList = getMarkedProgramList();;\r
+       private final TraceProgram trKeys = getTraceProgram();\r
+       private final SearchProgram srKeys = getSearchProgram();\r
+       private final SearchGroupList srGrps = getSearchGroupList();\r
+       private final ExtProgram extKeys = getExtProgram();\r
+       \r
+       private final TVProgramList tvprograms = getTVProgramList();\r
+       private final HDDRecorderList recorders = getRecorderList();\r
+\r
+       private final StatusWindow StWin = getStWin();                  // これは起動時に作成されたまま変更されないオブジェクト\r
+       private final StatusTextArea MWin = getMWin();                  // これは起動時に作成されたまま変更されないオブジェクト\r
+       private final AbsReserveDialog rD = getReserveDialog(); // これは起動時に作成されたまま変更されないオブジェクト\r
+       \r
+       private final Component parent = getParentComponent();  // これは起動時に作成されたまま変更されないオブジェクト\r
+\r
+       // メソッド\r
+       private void StdAppendMessage(String message) { System.out.println(message); }\r
+       private void StdAppendError(String message) { System.err.println(message); }\r
+       //private void StWinSetVisible(boolean b) { StWin.setVisible(b); }\r
+       //private void StWinSetLocationCenter(Component frame) { CommonSwingUtils.setLocationCenter(frame, (VWStatusWindow)StWin); }\r
+       \r
+       \r
+       /*******************************************************************************\r
+        * 定数\r
+        ******************************************************************************/\r
+       \r
+       private static final String MSGID = "["+getViewName()+"] ";\r
+       private static final String ERRID = "[ERROR]"+MSGID;\r
+       private static final String DBGID = "[DEBUG]"+MSGID;\r
+       \r
+       \r
+       /**\r
+        * メインテーブルのカラムの定義\r
+        */\r
+       private static final HashMap<String,Integer> lcmap = new HashMap<String, Integer>();\r
+       public static HashMap<String,Integer> getColumnIniWidthMap() {\r
+               if (lcmap.size() == 0 ) {\r
+                       for ( ListedColumn lc : ListedColumn.values() ) {\r
+                               lcmap.put(lc.toString(),lc.getIniWidth());      // toString()!\r
+                       }\r
+               }\r
+               return lcmap;\r
+       }\r
+       \r
+       /**\r
+        * テーブルのカラムの設定(名前と幅の初期値)。できれば@{link {@link ListedItem}と一体化させたかったのだが無理っぽい\r
+        * @see ListedItem\r
+        */\r
+       public static enum ListedColumn {\r
+               RSVMARK         ("予約",                      35),\r
+               DUPMARK         ("重複",                      35),\r
+               CHNAME          ("チャンネル名",  100),\r
+               OPTIONS         ("オプション",             100),\r
+               TITLE           ("番組タイトル",  300),\r
+               DETAIL          ("番組詳細",                200),\r
+               START           ("開始時刻",                150),\r
+               END                     ("終了",                      50),\r
+               LENGTH          ("長さ",                      50),\r
+               GENRE           ("ジャンル",                85),\r
+               SITEM           ("検索アイテム名",       100),\r
+               STAR            ("お気に入り度",  100),\r
+               SCORE           ("スコア",                   35),\r
+               THRESHOLD       ("閾値",                      35),\r
+               ;\r
+\r
+               private String name;\r
+               private int iniWidth;\r
+\r
+               private ListedColumn(String name, int iniWidth) {\r
+                       this.name = name;\r
+                       this.iniWidth = iniWidth;\r
+               }\r
+\r
+               public String getName() {\r
+                       return name;\r
+               }\r
+\r
+               public int getIniWidth() {\r
+                       return iniWidth;\r
+               }\r
+               \r
+               public int getColumn() {\r
+                       return ordinal();\r
+               }\r
+               \r
+               public boolean equals(String s) {\r
+                       return name.equals(s);\r
+               }\r
+       };\r
+\r
+       /**\r
+        * 検索範囲(番組追跡か、キーワード検索か、予約待機か)\r
+        */\r
+       private static enum SearchBy { TRACE, KEYWORD, BOTH } ;\r
+       \r
+       public static final String RSVMARK_NOEXEC               = "×"; // 予約無効\r
+       public static final String RSVMARK_NORMAL               = "●";        // ぴったり\r
+       public static final String RSVMARK_OVERRUN              = "◎";        // のりしろより大きく予約時間がとられている\r
+       public static final String RSVMARK_UNDERRUN             = "○";        // 時間延長が考慮されていない\r
+       public static final String RSVMARK_DELAYED              = "◇";        // 開始時刻が一致しない\r
+       public static final String RSVMARK_CLIPPED              = "▲";        // 1分短縮済み\r
+       public static final String RSVMARK_CLIPPED_E    = "△";        // 1分短縮済み(延長警告あり)\r
+       public static final String RSVMARK_SHORTAGE             = "▼";        // 2分以上短かい\r
+       public static final String RSVMARK_SHORTAGE_E   = "▽";        // 2分以上短かい(延長警告あり)\r
+       \r
+       public static final String RSVMARK_PICKUP               = "★";        // ピックアップ\r
+       public static final String RSVMARK_URABAN               = "■";        // 裏番組\r
+       private static final String PICKUP_COLOR                = CommonUtils.color2str(Color.BLACK);\r
+       private static final String URABAN_COLOR                = CommonUtils.color2str(Color.BLACK);\r
+\r
+       private static final String DUPMARK_NORMAL              = "■";\r
+       private static final String DUPMARK_REP                 = "□";\r
+       private static final String DUPMARK_COLOR               = "#FFB6C1";\r
+       \r
+       private static final String TreeExpRegFile_Listed = "env"+File.separator+"tree_expand_listed.xml";\r
+       \r
+       private final String SearchItemLabel_Passed = JTreeLabel.Nodes.PASSED.getLabel();       // 過去ログの名前\r
+       \r
+       // 定数ではないが\r
+       \r
+       private int vrowInFocus = -1;   // マウスクリックした時のフォーカス行\r
+       \r
+       \r
+       /*******************************************************************************\r
+        * 部品\r
+        ******************************************************************************/\r
+       \r
+       // コンポーネント\r
+       \r
+       private JDetailPanel jTextPane_detail = null;\r
+       private JSplitPane jSplitPane_view = null;\r
+       private JPanel jPanel_tree = null;\r
+       private JScrollPane jScrollPane_tree_top = null;\r
+       private JTreeLabel jLabel_tree = null;\r
+       private JScrollPane jScrollPane_tree = null;\r
+       private JTree jTree_tree = null;\r
+       private JScrollPane jScrollPane_listed = null;\r
+       private JTableRowHeader jTable_rowheader = null;\r
+       private ListedTable jTable_listed = null;\r
+       private VWColorCharCellRenderer2 titleCellRenderer = null;\r
+       \r
+       private VWListedTreeNode listRootNode = null;           // リスト形式のツリー\r
+       \r
+       VWListedTreeNode searchedNode   = null;\r
+       VWListedTreeNode startNode              = null;\r
+       VWListedTreeNode endNode                = null;\r
+       VWListedTreeNode nowNode                = null;\r
+       VWListedTreeNode syobocalNode   = null;\r
+       VWListedTreeNode standbyNode    = null;\r
+       VWListedTreeNode traceNode              = null;\r
+       VWListedTreeNode keywordNode    = null;\r
+       VWListedTreeNode keywordGrpNode = null;\r
+       VWListedTreeNode genreNode              = null;\r
+       VWListedTreeNode centerListNode = null;\r
+       VWListedTreeNode extensionNode  = null;\r
+       \r
+       VWListedTreeNode defaultNode    = null;\r
+       \r
+       private ListedTableModel tableModel_listed = null;\r
+       \r
+       private DefaultTableModel rowheaderModel_listed = null;\r
+       \r
+       // コンポーネント以外\r
+       \r
+       // テーブルの実態\r
+       private final RowItemList<ListedItem> rowData = new RowItemList<ListedItem>();\r
+       \r
+       // ツリーの展開状態の保存場所\r
+       TreeExpansionReg ter = null;\r
+       \r
+       // 現在放送中のタイマー\r
+       private boolean timer_now_enabled = false;\r
+       \r
+       \r
+       /*******************************************************************************\r
+        * コンストラクタ\r
+        ******************************************************************************/\r
+       \r
+       public AbsListedView() {\r
+               \r
+               super();\r
+               \r
+               // コンポーネントを追加\r
+               this.setLayout(new BorderLayout());\r
+               this.add(getJTextPane_detail(), BorderLayout.PAGE_START);\r
+               this.add(getJSplitPane_view(), BorderLayout.CENTER);\r
+               \r
+               // バグ対応\r
+               if ( bounds.getListedColumnSize() == null ) {\r
+                       StWin.appendError(ERRID+"なんらかの不具合によりテーブルのカラム幅設定が取得できませんでした。設定はリセットされました。申し訳ありません。");\r
+                       bounds.setListedColumnSize(lcmap);\r
+               }\r
+               else {\r
+                       for ( Entry<String, Integer> en : lcmap.entrySet() ) {\r
+                               try {\r
+                                       bounds.getListedColumnSize().get(en.getKey());\r
+                               }\r
+                               catch (NullPointerException e) {\r
+                                       System.err.println(ERRID+en.getKey()+", "+e.toString());\r
+                                       bounds.getListedColumnSize().put(en.getKey(),en.getValue());\r
+                               }\r
+                       }\r
+               }\r
+               \r
+               // タブが開いたり閉じたりしたときの処理\r
+               this.addComponentListener(cl_tabshownhidden);\r
+       }\r
+       \r
+       \r
+       /*******************************************************************************\r
+        * アクション\r
+        ******************************************************************************/\r
+       \r
+       // このクラスの肝、リスト表示について。\r
+       \r
+       /*\r
+        *  検索総当り版\r
+        */\r
+       \r
+       /**\r
+        * 検索条件\r
+        */\r
+       private class RedrawCond {\r
+               ProgType progtype = null;\r
+               SearchKey searchkeyword = null;\r
+               ProgFlags flag = null;\r
+               ProgGenre genre = null;\r
+               ProgSubgenre subgenre = null;\r
+               String center = null;\r
+               String targetdate = null;\r
+       }\r
+       \r
+       /**\r
+        *  プレビュー表示とか(親から呼ばれるよ!)\r
+        * @see #redrawListByKeywordDyn(SearchKey, String)\r
+        * @see #redrawListByPassed(SearchKey, String)\r
+        * @see #redrawListByKeywordFilter(SearchKey, String)\r
+        */\r
+       public void redrawListByPreview(SearchKey sKey) {\r
+               stopTimer(true);\r
+               jLabel_tree.setView(JTreeLabel.Nodes.SEARCHED, JTreeLabel.PREVIEW);\r
+               redrawListByKeywordDyn(sKey);\r
+       }\r
+       \r
+       /**\r
+        *  キーワード検索ボックスとか(親から呼ばれるよ!)\r
+        */\r
+       public void redrawListByKeywordDyn(SearchKey sKey, String target) {\r
+               stopTimer(true);\r
+               jLabel_tree.setView(JTreeLabel.Nodes.SEARCHED, target);\r
+               redrawListByKeywordDyn(sKey);\r
+       }       \r
+       \r
+       \r
+       // キーワード検索結果に基づきリストを作成(動的)\r
+       // (1)延長警告管理、(2)ツールバーからの検索\r
+       private void redrawListByKeywordDyn(SearchKey sKey) {\r
+               rowData.clear();\r
+               RedrawCond c = new RedrawCond();\r
+               c.progtype = ProgType.PROG;\r
+               c.searchkeyword = sKey;\r
+               _redrawListBy(c);\r
+               //_redrawListBy(ProgType.RADIO,sKey,null,null,null,null);\r
+               setReservedMarks();\r
+               tableModel_listed.fireTableDataChanged();\r
+               rowheaderModel_listed.fireTableDataChanged();\r
+               \r
+               setOverlapMark();\r
+       }\r
+       private void redrawListByExtkeywordAll(ArrayList<SearchKey> sKeys) {\r
+               rowData.clear();\r
+               RedrawCond c = new RedrawCond();\r
+               c.progtype = ProgType.PROG;\r
+               for (SearchKey sKey : sKeys) { \r
+                       c.searchkeyword = sKey;\r
+                       _redrawListBy(c);\r
+               }\r
+               setReservedMarks();\r
+               tableModel_listed.fireTableDataChanged();\r
+               rowheaderModel_listed.fireTableDataChanged();\r
+               \r
+               setOverlapMark();\r
+       }\r
+       // フラグに基づきリストを作成\r
+       private void redrawListByFlag(ProgFlags flag) {\r
+               rowData.clear();\r
+               RedrawCond c = new RedrawCond();\r
+               c.progtype = ProgType.PROG;\r
+               c.flag = flag;\r
+               _redrawListBy(c);\r
+               setReservedMarks();\r
+               tableModel_listed.fireTableDataChanged();\r
+               rowheaderModel_listed.fireTableDataChanged();\r
+               \r
+               setOverlapMark();\r
+       }\r
+       // フラグ+ジャンルでリストを作成\r
+       private void redrawListByFlag(ProgFlags flag, ProgGenre genre) {\r
+               rowData.clear();\r
+               RedrawCond c = new RedrawCond();\r
+               c.progtype = ProgType.PROG;\r
+               c.genre = genre;\r
+               c.flag = flag;\r
+               _redrawListBy(c);\r
+               setReservedMarks();\r
+               tableModel_listed.fireTableDataChanged();\r
+               rowheaderModel_listed.fireTableDataChanged();\r
+               \r
+               setOverlapMark();\r
+       }\r
+       // 現在時刻で絞り込み\r
+       private void redrawListByNow(ProgGenre genre) {\r
+               jTable_listed.getRowSorter().setSortKeys(null); // ソーターをリセットする\r
+               rowData.clear();\r
+               RedrawCond c = new RedrawCond();\r
+               c.progtype = ProgType.PROG;\r
+               c.genre = genre;\r
+               c.targetdate = "";\r
+               _redrawListBy(c);       // target == ""は現在日時しぼりこみ\r
+               setReservedMarks();\r
+               tableModel_listed.fireTableDataChanged();\r
+               rowheaderModel_listed.fireTableDataChanged();\r
+               \r
+               setOverlapMark();\r
+       }\r
+       // ジャンル検索結果に基づきリストを作成\r
+       private void redrawListByGenre(ProgGenre genre, ProgSubgenre subgenre) {\r
+               rowData.clear();\r
+               RedrawCond c = new RedrawCond();\r
+               c.progtype = ProgType.PROG;\r
+               c.genre = genre;\r
+               c.subgenre = subgenre;\r
+               _redrawListBy(c);\r
+               setReservedMarks();\r
+               tableModel_listed.fireTableDataChanged();\r
+               rowheaderModel_listed.fireTableDataChanged();\r
+               \r
+               setOverlapMark();\r
+       }\r
+       // 放送局ごとにリストを作成\r
+       private void redrawListByCenterList(String center) {\r
+               rowData.clear();\r
+               RedrawCond c = new RedrawCond();\r
+               c.progtype = ProgType.PROG;\r
+               c.center = center;\r
+               _redrawListBy(c);\r
+               setReservedMarks();\r
+               tableModel_listed.fireTableDataChanged();\r
+               rowheaderModel_listed.fireTableDataChanged();\r
+               \r
+               //setOverlapMark();\r
+       }\r
+       \r
+       /**\r
+        *  しょぼーん\r
+        */\r
+       private void redrawSyobocalAll() {\r
+               rowData.clear();\r
+               RedrawCond c = new RedrawCond();\r
+               c.progtype = ProgType.SYOBO;\r
+               _redrawListBy(c);\r
+               tableModel_listed.fireTableDataChanged();\r
+               rowheaderModel_listed.fireTableDataChanged();\r
+               setReservedMarks();\r
+       }\r
+\r
+       /**\r
+        * 検索総当り版の本体(全件に対して検索処理をかける)\r
+        */\r
+       private void _redrawListBy(RedrawCond cond) {\r
+               \r
+               String curDateTime = CommonUtils.getDateTime(0);\r
+               String critDateTime = CommonUtils.getCritDateTime();\r
+               \r
+               for ( TVProgram tvp : tvprograms ) {\r
+                       if (tvp.getType() != cond.progtype) {\r
+                               continue;\r
+                       }\r
+                       \r
+                       for ( ProgList tvpl : tvp.getCenters() ) {\r
+                               if ( ! tvpl.enabled ) {\r
+                                       // 有効局のみだよ\r
+                                       continue;\r
+                               }\r
+                               if (cond.center != null && ! cond.center.equals(tvpl.Center)) {\r
+                                       // 放送局指定があれば\r
+                                       continue;\r
+                               }\r
+                               \r
+                               // キーワード検索用\r
+                               String centerPop = TraceProgram.replacePop(tvpl.Center);\r
+                               \r
+                               for ( ProgDateList tvc : tvpl.pdate ) {\r
+\r
+                                       for ( ProgDetailList tvd : tvc.pdetail ) {\r
+                                               \r
+                                               // 過去日のものは表示しない\r
+                                               if (tvp.getType() != ProgType.PASSED) {\r
+                                                       if (tvd.endDateTime.compareTo(critDateTime) < 0) {\r
+                                                               continue;\r
+                                                       }\r
+                                               }\r
+                                               // 当日過去分は表示しない(オプション)\r
+                                               if ( ! env.getDisplayPassedEntry()) {\r
+                                                       if (tvp.getType() != ProgType.PASSED) {\r
+                                                               if (tvd.endDateTime.compareTo(curDateTime) <= 0) {\r
+                                                                       continue;\r
+                                                               }\r
+                                                       }\r
+                                               }\r
+                                               \r
+                                               // 番組情報がありませんは表示しない\r
+                                               if (tvd.start.equals("")) {\r
+                                                       continue;\r
+                                               }\r
+                                               \r
+                                               // 放送休止は表示しない\r
+                                               if (tvd.title.equals("放送休止") || tvd.title.equals("休止") || tvd.title.contains("放送を休止")) {\r
+                                                       continue;\r
+                                               }\r
+                                               \r
+                                               //マッチング\r
+                                               String label = "";\r
+                                               String okini = "";\r
+                                               String matched = null;\r
+                                               long cur_remain = -1;\r
+                                               long cur_wait = 0;\r
+                                               boolean isFind = false;\r
+                                               if (cond.center != null) {\r
+                                                       isFind = true;\r
+                                               }\r
+                                               else if (cond.targetdate != null) {\r
+                                                       // 現在放送中!\r
+                                                       if ( cond.targetdate.length() == 0 ) {\r
+                                                               String cridt = CommonUtils.getDateTime(-env.getCurrentAfter());\r
+                                                               String curdt = CommonUtils.getDateTime(0);\r
+                                                               String nextdt = CommonUtils.getDateTime(env.getCurrentBefore());\r
+                                                               if ( tvd.endDateTime.compareTo(cridt) <= 0 ) {\r
+                                                                       continue;       // オワタ\r
+                                                               }\r
+                                                               if ( tvd.startDateTime.compareTo(cridt) <= 0 && cridt.compareTo(tvd.endDateTime) <= 0 ||\r
+                                                                               tvd.startDateTime.compareTo(curdt) <= 0 && curdt.compareTo(tvd.endDateTime) <= 0) {\r
+                                                                       cur_remain = CommonUtils.getCompareDateTime(tvd.endDateTime, curdt);\r
+                                                                       isFind = true;\r
+                                                               }\r
+                                                               else if ( tvd.startDateTime.compareTo(cridt) > 0 && tvd.startDateTime.compareTo(nextdt) <= 0 ) {\r
+                                                                       cur_wait = CommonUtils.getCompareDateTime(tvd.startDateTime, curdt);\r
+                                                                       isFind = true;  // 今後一時間以内に開始予定のものも表示\r
+                                                               }\r
+                                                               else if ( tvd.startDateTime.compareTo(nextdt) < 0 ) {\r
+                                                                       continue;       // これ以上みても無駄\r
+                                                               }\r
+                                                               \r
+                                                               if ( isFind && cond.genre != null && ! tvd.isEqualsGenre(cond.genre, null) ) {\r
+                                                                       continue;\r
+                                                               }\r
+                                                       }\r
+                                               }\r
+                                               else {\r
+                                                       if (cond.searchkeyword != null) {\r
+                                                               isFind = SearchProgram.isMatchKeyword(cond.searchkeyword, ((cond.searchkeyword.getCaseSensitive()==false)?(centerPop):(tvpl.Center)), tvd);\r
+                                                               label = ((cond.progtype == ProgType.PASSED)?(SearchItemLabel_Passed):(cond.searchkeyword.getLabel()));\r
+                                                               okini = cond.searchkeyword.getOkiniiri();\r
+                                                               matched = SearchProgram.getMatchedString();\r
+                                                       }\r
+                                                       else if (cond.flag != null) {\r
+                                                               if (tvd.flag == cond.flag) {\r
+                                                                       isFind = true;\r
+                                                                       label = cond.flag.toString();\r
+                                                               }\r
+                                                               \r
+                                                               if ( isFind && cond.genre != null && ! tvd.isEqualsGenre(cond.genre, null) ) {\r
+                                                                       continue;\r
+                                                               }\r
+                                                       }\r
+                                                       else if (cond.genre != null) {\r
+                                                               isFind = tvd.isEqualsGenre(cond.genre, cond.subgenre);\r
+                                                       }\r
+                                                       else if ( cond.progtype == ProgType.SYOBO && cond.searchkeyword == null ) {\r
+                                                               // 全しょぼかる\r
+                                                               isFind = true;\r
+                                                       }\r
+                                               }\r
+                                               \r
+                                               if (isFind) {\r
+                                                       String[] tStr = new String[3];\r
+                                                       if (matched != null) {\r
+                                                               int a = tvd.title.indexOf(matched);\r
+                                                               tStr[0] = tvd.title.substring(0,a);\r
+                                                               tStr[1] = matched;\r
+                                                               tStr[2] = tvd.title.substring(a+matched.length());\r
+                                                       }\r
+                                                       else {\r
+                                                               tStr[0] = tvd.title;\r
+                                                               tStr[1] = "";\r
+                                                               tStr[2] = "";\r
+                                                       }\r
+                                                       \r
+                                                       String prefixMark = "";\r
+                                                       if ( cond.targetdate == "" ) {\r
+                                                               if ( cur_remain > 0 ) {\r
+                                                                       prefixMark = String.format("\0終了まで%3d分",cur_remain/60000);\r
+                                                               }\r
+                                                               else if ( cur_wait > 0 ){\r
+                                                                       prefixMark = String.format("\0開始まで%3d分",cur_wait/60000);\r
+                                                               }\r
+                                                       }\r
+                                                       else {\r
+                                                               prefixMark = tvd.extension_mark+tvd.prefix_mark;\r
+                                                       }\r
+                                                       \r
+                                                       ListedItem sa = new ListedItem();\r
+                                                       \r
+                                                       sa.tvd = tvd;\r
+                                                       \r
+                                                       sa.rsvmark              = "";\r
+                                                       sa.dupmark              = "";\r
+                                                       sa.prefix               = prefixMark;\r
+                                                       sa.title                = tvd.newlast_mark+"\0"+tStr[0]+"\0"+tStr[1]+"\0"+tStr[2]+tvd.postfix_mark;\r
+                                                       sa.searchlabel  = label;\r
+                                                       sa.okiniiri             = okini;\r
+                                                       sa.score                = "";\r
+                                                       sa.threshold    = "";\r
+                                                       \r
+                                                       sa.hide_rsvmarkcolor    = "";\r
+                                                       \r
+                                                       sa.fireChanged();\r
+                                                       \r
+                                                       addRow(sa);\r
+                                               }\r
+                                       }\r
+                               }\r
+                       }\r
+               }\r
+               \r
+               if ( cond.targetdate == "" ) {\r
+                       // 現在放送中の終了済み番組をリストの先頭に移動する\r
+                       RowItemList<ListedItem> passed = new RowItemList<ListedItem>();\r
+                       RowItemList<ListedItem> cur = new RowItemList<ListedItem>();\r
+                       RowItemList<ListedItem> future = new RowItemList<ListedItem>();\r
+                       for ( ListedItem c : rowData ) {\r
+                               if ( c.prefix == "" ) {\r
+                                       passed.add(c);\r
+                               }\r
+                               else if ( c.prefix.startsWith("\0終了まで") ){\r
+                                       cur.add(c);\r
+                               }\r
+                               else {\r
+                                       future.add(c);\r
+                               }\r
+                       }\r
+                       \r
+                       rowData.clear();\r
+                       for ( ListedItem c : passed ) {\r
+                               rowData.add(c);\r
+                       }\r
+                       \r
+                       int toprow = rowData.size();\r
+                       for ( ListedItem c : cur ) {\r
+                               int row = toprow;\r
+                               for ( ; row<rowData.size(); row++ ) {\r
+                                       ListedItem d = rowData.get(row);\r
+                                       if ( c.tvd.endDateTime.compareTo(d.tvd.endDateTime) < 0 ) {\r
+                                               break;\r
+                                       }\r
+                               }\r
+                               rowData.add(row,c);\r
+                       }\r
+                       \r
+                       for ( ListedItem c : future ) {\r
+                               rowData.add(c);\r
+                       }\r
+               }\r
+       }\r
+       \r
+       /**\r
+        * 検索総当り版の本体(全件に対して検索処理をかける)\r
+        * <P><B>★将来的には、動的検索結果の表示はすべてこちらに移行する \r
+        */\r
+       public boolean redrawListBySearched(ProgType typ, int index) {\r
+               \r
+               stopTimer(true);\r
+               \r
+               // 検索結果が入っているところ\r
+               SearchResult searched = tvprograms.getSearched();\r
+               \r
+               // 検索結果の履歴数より大きい番号を指定された場合はエラー\r
+               if ( searched.getResultBufferSize() < index ) {\r
+                       return false;\r
+               }\r
+               \r
+               JTreeLabel.Nodes desc = ((typ == ProgType.PASSED) ? (JTreeLabel.Nodes.SEARCHHIST):(JTreeLabel.Nodes.SEARCHED));\r
+               String label = searched.getLabel(index);\r
+               \r
+               jLabel_tree.setView(desc, label);\r
+\r
+               rowData.clear();\r
+               \r
+               for ( ProgDetailList tvd : searched.getResult(index) ) {\r
+                       \r
+                       String[] tStr = new String[3];\r
+                       \r
+                       if (tvd.dynMatched != null) {\r
+                               int a = tvd.title.indexOf(tvd.dynMatched);\r
+                               tStr[0] = tvd.title.substring(0,a);\r
+                               tStr[1] = tvd.dynMatched;\r
+                               tStr[2] = tvd.title.substring(a+tvd.dynMatched.length());\r
+                       }\r
+                       else {\r
+                               tStr[0] = tvd.title;\r
+                               tStr[1] = "";\r
+                               tStr[2] = "";\r
+                       }\r
+                       \r
+                       String prefixMark = tvd.extension_mark+tvd.prefix_mark;\r
+                       \r
+                       ListedItem sa = new ListedItem();\r
+                       \r
+                       sa.tvd = tvd;\r
+                       \r
+                       sa.rsvmark              = "";\r
+                       sa.dupmark              = "";\r
+                       sa.prefix               = prefixMark;\r
+                       sa.title                = tvd.newlast_mark+"\0"+tStr[0]+"\0"+tStr[1]+"\0"+tStr[2]+tvd.postfix_mark;\r
+                       sa.searchlabel  = tvd.dynKey.getLabel();\r
+                       sa.okiniiri             = tvd.dynKey.getOkiniiri();\r
+                       sa.score                = "";\r
+                       sa.threshold    = "";\r
+                       \r
+                       sa.hide_rsvmarkcolor    = "";\r
+                       \r
+                       sa.fireChanged();\r
+                       \r
+                       addRow(sa);\r
+               }\r
+               \r
+               tableModel_listed.fireTableDataChanged();\r
+               rowheaderModel_listed.fireTableDataChanged();\r
+               \r
+               return true;\r
+       }\r
+       \r
+       /*\r
+        *  検索高速化版\r
+        */\r
+       \r
+       // しょぼかるリストを作成\r
+       private void redrawSyobocalListByTrace() {\r
+               _redrawListByTraceAndKeyword(ProgType.SYOBO,SearchBy.TRACE,null,null,null);\r
+       }\r
+       private void redrawSyobocalListByKeyword() {\r
+               _redrawListByTraceAndKeyword(ProgType.SYOBO,SearchBy.KEYWORD,null,null,null);\r
+       }\r
+\r
+       // しょぼかるのみに存在\r
+       private void redrawSyobocalListByOnly() {\r
+               _redrawListByTraceAndKeyword(ProgType.SYOBO,SearchBy.BOTH,null,null,null,true);\r
+       }\r
+       \r
+       private void redrawSyobocalListByTraceAndKeyword() {\r
+               _redrawListByTraceAndKeyword(ProgType.SYOBO,SearchBy.BOTH,null,null,null);\r
+       }\r
+\r
+       // 番組追跡に基づきリストを作成\r
+       private void redrawListByTrace(TraceKey tKey) {\r
+               _redrawListByTraceAndKeyword(ProgType.PROG,SearchBy.TRACE,tKey,null,null);\r
+       }\r
+       \r
+       // キーワード検索結果に基づきリストを作成(静的)\r
+       private void redrawListByKeyword(SearchKey sKey) {\r
+               _redrawListByTraceAndKeyword(ProgType.PROG,SearchBy.KEYWORD,null,sKey,null);\r
+       }\r
+       // キーワードグループに基づきリストを作成(静的)\r
+       private void redrawListByKeywordGrp(SearchGroup gr) {\r
+               _redrawListByTraceAndKeyword(ProgType.PROG,SearchBy.KEYWORD,null,null,null,false,gr,false,false);\r
+       }\r
+       // ピックアップに基づきリストを作成(静的)\r
+       private void redrawListByPickup() {\r
+               _redrawListByTraceAndKeyword(ProgType.PICKED,SearchBy.BOTH,null,null,null);\r
+       }\r
+       \r
+       // 番組追跡&キーワード検索結果に基づきリストを作成\r
+       private void redrawListByTraceAndKeywordOkini(String oKey) {\r
+               _redrawListByTraceAndKeyword(ProgType.PROG,SearchBy.BOTH,null,null,oKey);\r
+               selectBatchTarget();\r
+       }\r
+       private void redrawListByTraceAndKeyword() {\r
+               _redrawListByTraceAndKeyword(ProgType.PROG,SearchBy.BOTH,null,null,null);\r
+               selectBatchTarget();\r
+       }\r
+\r
+       private void redrawListByTraceAndKeywordNewArrival() {\r
+               _redrawListByTraceAndKeyword(ProgType.PROG,SearchBy.BOTH,null,null,null,false,null,true,false);\r
+               selectBatchTarget();\r
+       }\r
+       private void redrawListByTraceAndKeywordModified() {\r
+               _redrawListByTraceAndKeyword(ProgType.PROG,SearchBy.BOTH,null,null,null,false,null,false,true);\r
+               selectBatchTarget();\r
+       }\r
+\r
+       private void _redrawListByTraceAndKeyword(ProgType typ, SearchBy opt, TraceKey tKey, SearchKey sKey, String oKey) {\r
+               _redrawListByTraceAndKeyword(typ, opt, tKey, sKey, oKey, false, null, false, false); \r
+       }\r
+       private void _redrawListByTraceAndKeyword(ProgType typ, SearchBy opt, TraceKey tKey, SearchKey sKey, String oKey, boolean only) {\r
+               _redrawListByTraceAndKeyword(typ, opt, tKey, sKey, oKey, only, null, false, false); \r
+       }\r
+       \r
+       /**\r
+        * 検索高速化版の本体(作成済み検索結果から必要なものを選ぶだけ、検索処理は行わない)\r
+        */\r
+       private void _redrawListByTraceAndKeyword(ProgType typ, SearchBy opt, TraceKey tKey, SearchKey sKey, String oKey, boolean only, SearchGroup gr, boolean doChkNewArr, boolean doChkModify) {\r
+               \r
+               rowData.clear();\r
+               \r
+               String curDateTime = CommonUtils.getDateTime(0);\r
+               String critDateTime = CommonUtils.getCritDateTime();\r
+               \r
+               for (int n=0; n<mpList.size(); n++) {\r
+                       \r
+                       // Web番組表・しょぼかる分岐点\r
+                       if (mpList.getProg(n).type != typ) {\r
+                               continue;\r
+                       }\r
+                       \r
+                       // 番組追跡・キーワード検索\r
+                       if (opt == SearchBy.TRACE && mpList.getTKey(n) == null) {\r
+                               continue;\r
+                       }\r
+                       if (opt == SearchBy.KEYWORD && mpList.getSKey(n) == null) {\r
+                               continue;\r
+                       }\r
+                       \r
+                       ProgDetailList tvd = mpList.getProg(n);\r
+\r
+                       // 過去日のものは表示しない\r
+                       if (tvd.endDateTime.compareTo(critDateTime) < 0) {\r
+                               continue;\r
+                       }\r
+                       // 当日過去分は表示しない\r
+                       if ( ! env.getDisplayPassedEntry()) {\r
+                               if (tvd.endDateTime.compareTo(curDateTime) <= 0) {\r
+                                       continue;\r
+                               }\r
+                       }\r
+                       \r
+                       // 検索キーワード関連の値算出\r
+                       ArrayList<TraceKey>  td = mpList.getTKey(n);\r
+                       ArrayList<SearchKey> sd = mpList.getSKey(n);\r
+                       \r
+                       String label = "";\r
+                       String okini = "";\r
+                       String matched = null;\r
+                       String fazScore = "";\r
+                       String threshold = "";\r
+                       \r
+                       if ((opt == SearchBy.BOTH  || opt == SearchBy.TRACE) && td.size() > 0) {\r
+                               int i=0;\r
+                               if (tKey != null) {\r
+                                       for (i=0; i<td.size(); i++) {\r
+                                               if (td.get(i) == tKey) {\r
+                                                       break;\r
+                                               }\r
+                                       }\r
+                                       if (i >= td.size()) {\r
+                                               continue;       // キー指定で見つからなかったもの\r
+                                       }\r
+                               }\r
+                               \r
+                               label = td.get(i).getLabel();\r
+                               okini = td.get(i).getOkiniiri();\r
+                               threshold = String.valueOf(td.get(i).getFazzyThreshold());\r
+                               fazScore = String.valueOf(mpList.getTScore(n).get(i));\r
+                       }\r
+                       else if ((opt == SearchBy.BOTH  || opt == SearchBy.KEYWORD) && sd.size() > 0) {\r
+                               int i=0;\r
+                               if (sKey != null) {\r
+                                       for (i=0; i<sd.size(); i++) {\r
+                                               if (sd.get(i) == sKey) {\r
+                                                       break;\r
+                                               }\r
+                                       }\r
+                                       if (i >= sd.size()) {\r
+                                               continue;       // キー指定で見つからなかったもの\r
+                                       }\r
+                               }\r
+                               else if (gr != null) {\r
+                                       for (i=0; i<sd.size(); i++) {\r
+                                               boolean f = false;\r
+                                               for ( String gmember : gr ) {\r
+                                                       if (sd.get(i).getLabel().equals(gmember)) {\r
+                                                               f = true;\r
+                                                               break;\r
+                                                       }\r
+                                               }\r
+                                               if (f) {\r
+                                                       break;\r
+                                               }\r
+                                       }\r
+                                       if (i >= sd.size()) {\r
+                                               continue;       // キー指定で見つからなかったもの\r
+                                       }\r
+                               }\r
+                               \r
+                               if ( opt == SearchBy.BOTH && ! sd.get(i).getShowInStandby() ) {\r
+                                       continue;       // 予約待機への表示はYA・DA・YO\r
+                               }\r
+                               \r
+                               label = sd.get(i).getLabel();\r
+                               okini = sd.get(i).getOkiniiri();\r
+                               matched = mpList.getSStr(n).get(i);\r
+                       }\r
+                       else {\r
+                               continue;\r
+                       }\r
+                       \r
+                       if ( doChkNewArr ) {\r
+                               if ( ! tvd.newarrival ) {\r
+                                       // 新着フィルタリング\r
+                                       continue;\r
+                               }\r
+                       }\r
+                       else if ( doChkModify ) {\r
+                               if ( ! tvd.modified ) {\r
+                                       // 更新フィルタリング\r
+                                       continue;\r
+                               }\r
+                       }\r
+                       else if (oKey != null && oKey.compareTo(okini) > 0) {\r
+                               // お気に入り度フィルタリング\r
+                               continue;\r
+                       }\r
+                       \r
+                       // しょぼかるのみに存在\r
+                       if ( typ == ProgType.SYOBO && (env.getSyoboFilterByCenters() || only) ) {\r
+                               if ( only && tvd.nosyobo ) {\r
+                                       // nosyoboって名前と内容が一致していないよね…\r
+                                       continue;\r
+                               }\r
+                               // 有効局のみに限定する\r
+                               boolean encr = false;\r
+                               for ( int x=0; x<tvprograms.size() && encr == false; x++ ) {\r
+                                       TVProgram tvp = tvprograms.get(x);\r
+                                       if ( tvp.getType() != ProgType.PROG ) {\r
+                                               continue;\r
+                                       }\r
+                                       for ( Center cr : tvp.getSortedCRlist() ) {\r
+                                               if ( mpList.getProg(n).center.equals(cr.getCenter()) ) {\r
+                                                       encr = true;\r
+                                                       break;\r
+                                               }\r
+                                       }\r
+                               }\r
+                               if ( ! encr ) {\r
+                                       continue;\r
+                               }\r
+                       }\r
+                       \r
+                       // リストに追加\r
+                       String[] tStr = new String[3];\r
+                       if (matched != null) {\r
+                               int a = tvd.title.indexOf(matched);\r
+                               tStr[0] = tvd.title.substring(0,a);\r
+                               tStr[1] = matched;\r
+                               tStr[2] = tvd.title.substring(a+matched.length());\r
+                       }\r
+                       else {\r
+                               tStr[0] = tvd.title;\r
+                               tStr[1] = "";\r
+                               tStr[2] = "";\r
+                       }\r
+                       \r
+                       ListedItem sa = new ListedItem();\r
+\r
+                       sa.tvd = tvd;\r
+                       \r
+                       sa.rsvmark              = "";\r
+                       sa.dupmark              = "";\r
+                       sa.prefix               = tvd.extension_mark+tvd.prefix_mark;\r
+                       sa.title                = tvd.newlast_mark+"\0"+tStr[0]+"\0"+tStr[1]+"\0"+tStr[2]+tvd.postfix_mark;\r
+                       sa.searchlabel  = label;\r
+                       sa.okiniiri             = okini;\r
+                       sa.score                = fazScore;\r
+                       sa.threshold    = threshold;\r
+                       \r
+                       sa.hide_rsvmarkcolor    = "";\r
+                       \r
+                       sa.fireChanged();\r
+                       \r
+                       addRow(sa);\r
+               }\r
+               \r
+               // ピックアップ(予約待機(親)のみで表示)\r
+               if ((typ == ProgType.PROG || typ == ProgType.PICKED) && opt == SearchBy.BOTH && oKey == null) {\r
+                       if ( ! doChkNewArr && ! doChkModify ) {\r
+                               addPickedPrograms(curDateTime);\r
+                       }\r
+               }\r
+               \r
+               // 予約マーク\r
+               setReservedMarks();\r
+               // テーブルに反映\r
+               tableModel_listed.fireTableDataChanged();\r
+               rowheaderModel_listed.fireTableDataChanged();\r
+               \r
+               // 時間重複マーク\r
+               setOverlapMark();\r
+       }\r
+       \r
+       private void addPickedPrograms(String curDateTime) {\r
+               TVProgram tvp = tvprograms.getPickup();\r
+               if ( tvp != null ) {\r
+                       for ( ProgList tPl : tvp.getCenters() ) {\r
+                               for ( ProgDateList tPcl : tPl.pdate ) {\r
+                                       for ( ProgDetailList tvd : tPcl.pdetail ) {\r
+                                               \r
+                                               // すでに過去になっているものは表示しない\r
+                                               if ( ! env.getDisplayPassedEntry()) {\r
+                                                       if (tvd.endDateTime.compareTo(curDateTime) <= 0) {\r
+                                                               continue;\r
+                                                       }\r
+                                               }\r
+                                               \r
+                                               // リストに追加\r
+                                               ListedItem sa = new ListedItem();\r
+                                               \r
+                                               sa.tvd = tvd;\r
+                                               \r
+                                               sa.rsvmark              = "";\r
+                                               sa.dupmark              = "";\r
+                                               sa.prefix               = tvd.extension_mark+tvd.prefix_mark;\r
+                                               sa.title                = tvd.newlast_mark+"\0"+tvd.title+tvd.postfix_mark;\r
+                                               sa.searchlabel  = "ピックアップ";\r
+                                               sa.okiniiri             = "";\r
+                                               sa.score                = "";\r
+                                               sa.threshold    = "";\r
+                                               \r
+                                               sa.hide_rsvmarkcolor    = "";\r
+                                               \r
+                                               sa.fireChanged();\r
+                                               \r
+                                               addRow(sa);\r
+                                       }\r
+                               }\r
+                       }\r
+               }\r
+       }\r
+       \r
+       /*\r
+        * 絞り込み検索\r
+        */\r
+       \r
+       /**\r
+        * 絞り込み検索の本体(現在リストアップされているものから絞り込みを行う)(親から呼ばれるよ!)\r
+        */\r
+       public void redrawListByKeywordFilter(SearchKey keyword, String target) {\r
+               \r
+               stopTimer(true);\r
+\r
+               jLabel_tree.setView(JTreeLabel.Nodes.FILTERED, target);\r
+               \r
+               ArrayList<ListedItem> tmpRowData = new ArrayList<ListedItem>();\r
+               \r
+               for ( ListedItem c : rowData ) {\r
+                       \r
+                       // 表示中の情報を一行ずつチェックする\r
+                       ProgDetailList tvd = c.tvd;\r
+                       \r
+                       // タイトルを整形しなおす\r
+                       boolean isFind = SearchProgram.isMatchKeyword(keyword, "", tvd);\r
+                       if ( isFind ) {\r
+                               String matched = SearchProgram.getMatchedString();\r
+                               String[] tStr = new String[3];\r
+                               if (matched != null) {\r
+                                       int a = tvd.title.indexOf(matched);\r
+                                       tStr[0] = tvd.title.substring(0,a);\r
+                                       tStr[1] = matched;\r
+                                       tStr[2] = tvd.title.substring(a+matched.length());\r
+                               }\r
+                               else {\r
+                                       tStr[0] = tvd.title;\r
+                                       tStr[1] = "";\r
+                                       tStr[2] = "";\r
+                               }\r
+                               \r
+                               // 修正して仮置き場に入れる\r
+                               c.prefix = tvd.extension_mark+tvd.prefix_mark;\r
+                               c.title = tvd.newlast_mark+"\0"+tStr[0]+"\0"+tStr[1]+"\0"+tStr[2]+tvd.postfix_mark;\r
+                               c.fireChanged();\r
+                               tmpRowData.add(c);\r
+                       }\r
+               }\r
+               \r
+               // 表示データを置き換える\r
+               rowData.clear();\r
+               for ( ListedItem a : tmpRowData ) {\r
+                       addRow(a);\r
+               }\r
+               \r
+               // fire!\r
+               tableModel_listed.fireTableDataChanged();\r
+               rowheaderModel_listed.fireTableDataChanged();\r
+       }\r
+       \r
+       public boolean addRow(ListedItem data) {\r
+               // 開始日時でソート\r
+               int i=0;\r
+               for (; i<rowData.size(); i++) {\r
+                       ListedItem c = rowData.get(i);\r
+                       ProgDetailList tvd = c.tvd;\r
+                       int x = tvd.startDateTime.compareTo(data.tvd.startDateTime);\r
+                       int y = tvd.endDateTime.compareTo(data.tvd.endDateTime);\r
+                       boolean isChMatched = c.tvd.center.equals(data.tvd.center);\r
+                       boolean isTitleMatched = c.tvd.title.equals(data.tvd.title);\r
+                       if (x == 0 && y == 0 && isChMatched ) {\r
+                               // 日またがりで発生した重複エントリを整理 → ピックアップとかも重複するよ\r
+                               ProgType typ = tvd.type;\r
+                               if ( debug ) {\r
+                                       if ( isTitleMatched ) {\r
+                                               StdAppendMessage("[重複エントリ] 省略しました: "+typ+" "+data.tvd.center+" "+data.tvd.title+" "+data.tvd.startDateTime+" "+data.tvd.endDateTime);\r
+                                       }\r
+                                       else {\r
+                                               StdAppendMessage("[重複エントリ] 放送局と開始終了日時が同がじでタイトルの異なる情報がありました: "+typ+" "+data.tvd.center+" "+data.tvd.startDateTime+" "+data.tvd.title+" -> "+c.title);\r
+                                       }\r
+                               }\r
+                               return false;\r
+                       }\r
+                       else if (x > 0) {\r
+                               break;  // 挿入位置確定\r
+                       }\r
+               }\r
+               \r
+               // 有効データ\r
+               rowData.add(i, data);\r
+               return true;\r
+       }\r
+       \r
+       public void setReservedMarks() {\r
+               \r
+               for ( ListedItem data : rowData ) {\r
+                       \r
+                       // 予約済みマーク\r
+                       Marker rm = getReservedMarkChar(data);\r
+                       \r
+                       if (rm != null) {\r
+                               data.rsvmark = rm.mark;\r
+                               data.hide_rsvmarkcolor = rm.color;\r
+                               data.fireChanged();\r
+                       }\r
+                       else {\r
+                               data.rsvmark = "";\r
+                               data.hide_rsvmarkcolor = "";\r
+                               data.fireChanged();\r
+                       }\r
+               }\r
+       }\r
+       \r
+       /**\r
+        * 重複マークつけてください\r
+        */\r
+       private void setOverlapMark() {\r
+               \r
+               if ( rowData.size() <= 1 ) {\r
+                       // 1個以下ならソートの意味ないよね\r
+                       return;\r
+               }\r
+               \r
+               // リセット\r
+               for (int vrow=0; vrow<rowData.size(); vrow++) {\r
+                       ListedItem rf = rowData.get(vrow); \r
+                       rf.dupmark = "";\r
+                       rf.fireChanged();\r
+               }\r
+               \r
+               // 時間重複のマーキング\r
+               String sDT = "";\r
+               String eDT = "";\r
+               String sDT2 = "";\r
+               String eDT2 = "";\r
+               for (int vrow=0; vrow<rowData.size()-1; vrow++) {\r
+                       ListedItem ra = rowData.get(vrow);\r
+                       \r
+                       for ( int vrow2=vrow+1; vrow2<rowData.size(); vrow2++ ) {\r
+                               ListedItem rb = rowData.get(vrow2);\r
+                               \r
+                               if ( CommonUtils.getCompareDateTime(ra.tvd.endDateTime, rb.tvd.startDateTime) < 0) {\r
+                                       // もう見なくていい\r
+                                       break;\r
+                               }\r
+                               \r
+                               /*\r
+                               if ( ! sDT2.equals("")) {\r
+                                       sDT = sDT2;\r
+                                       eDT = eDT2;\r
+                               }\r
+                               else\r
+                               */\r
+                               {\r
+                                       sDT = ra.tvd.startDateTime;\r
+                                       eDT = ra.tvd.endDateTime;\r
+                               }\r
+                               \r
+                               {\r
+                                       sDT2 = rb.tvd.startDateTime;\r
+                                       eDT2 = rb.tvd.endDateTime;\r
+                               }\r
+                               \r
+                               if ( eDT.equals(sDT2) ) {\r
+                                       if ( ra.dupmark.length() == 0 ) {\r
+                                               ra.dupmark = DUPMARK_REP;\r
+                                               ra.fireChanged();\r
+                                       }\r
+                                       if ( rb.dupmark.length() == 0 ) {\r
+                                               rb.dupmark = DUPMARK_REP;\r
+                                               rb.fireChanged();\r
+                                       }\r
+                               }\r
+                               else if ( CommonUtils.isOverlap(sDT, eDT, sDT2, eDT2, false) ) {\r
+                                       ra.dupmark = rb.dupmark = DUPMARK_NORMAL;\r
+                                       ra.fireChanged();\r
+                                       rb.fireChanged();\r
+                               }\r
+                       }\r
+               }\r
+       }\r
+       \r
+       /**\r
+        * 現在時刻追従スクロールを開始する\r
+        * @see #stopTimer\r
+        * @see #pauseTimer\r
+        */\r
+       private void startTimer() {\r
+               timer_now_enabled = true;\r
+       }\r
+       \r
+       /**\r
+        * 現在時刻追従スクロールを停止する\r
+        */\r
+       private boolean stopTimer(boolean showmsg) {\r
+               return (timer_now_enabled = false);\r
+       }\r
+       \r
+       // 主に他のクラスから呼び出されるメソッド\r
+       \r
+       /**\r
+        * サイドツリーの「予約待機」を選択する\r
+        */\r
+       public void selectTreeDefault() {\r
+               if ( defaultNode != null ) jTree_tree.setSelectionPath(new TreePath(defaultNode.getPath()));\r
+       }\r
+       \r
+       /**\r
+        * サイドツリーの現在選択中のノードを再度選択して描画しなおす\r
+        */\r
+       public void reselectTree() {\r
+               JTreeLabel.Nodes node = jLabel_tree.getNode();\r
+               String value = jLabel_tree.getValue();\r
+               String[] names = new String[] { node.getLabel(), value };\r
+               TreeNode[] nodes = ter.getSelectedPath(listRootNode, names, 0);\r
+               if (nodes != null) {\r
+                       TreePath tp = new TreePath(nodes);\r
+                       if ( tp != null ) {\r
+                               jTree_tree.setSelectionPath(null);\r
+                               jTree_tree.setSelectionPath(tp);\r
+                       }\r
+               }\r
+       }\r
+       \r
+       /**\r
+        * 他から検索を実行される時にツリーの選択をはずす\r
+        */\r
+       public void clearSelection() {\r
+               jTree_tree.clearSelection();\r
+       }\r
+       \r
+       /**\r
+        * 特定のノードを選択しているか?\r
+        */\r
+       public boolean isNodeSelected(JTreeLabel.Nodes node) {\r
+               return(node == jLabel_tree.getNode());\r
+       }\r
+\r
+       /**\r
+        * サイドツリーを開く\r
+        */\r
+       public void setExpandTree() {\r
+               jSplitPane_view.setDividerLocation(bounds.getTreeWidth());\r
+               jScrollPane_tree.setVerticalScrollBarPolicy(JScrollPane.VERTICAL_SCROLLBAR_AS_NEEDED);\r
+               jScrollPane_tree.setHorizontalScrollBarPolicy(JScrollPane.HORIZONTAL_SCROLLBAR_AS_NEEDED);\r
+       }\r
+       \r
+       /**\r
+        * サイドツリーを閉じる\r
+        */\r
+       public void setCollapseTree() {\r
+               jSplitPane_view.setDividerLocation(bounds.getMinDivLoc());\r
+               jScrollPane_tree.setVerticalScrollBarPolicy(JScrollPane.VERTICAL_SCROLLBAR_NEVER);\r
+               jScrollPane_tree.setHorizontalScrollBarPolicy(JScrollPane.HORIZONTAL_SCROLLBAR_NEVER);\r
+       }\r
+       \r
+       /**\r
+        * サイドツリーの展開状態を設定ファイルに保存(鯛ナビ終了時に呼び出される)\r
+        */\r
+       public void saveTreeExpansion() {\r
+               ter.save();\r
+       }\r
+       \r
+       /**\r
+        * いまはどんな条件で表示しているのかな?\r
+        */\r
+       public String getCurrentView() {\r
+               return jLabel_tree.getView();\r
+       }\r
+       \r
+       /**\r
+        * 画面上部の番組詳細領域の表示のON/OFF\r
+        */\r
+       public void setDetailVisible(boolean aFlag) {\r
+               jTextPane_detail.setVisible(aFlag);\r
+       }\r
+\r
+       /**\r
+        * テーブルの行番号の表示のON/OFF\r
+        */\r
+       public void setRowHeaderVisible(boolean b) {\r
+               jScrollPane_listed.getRowHeader().setVisible(b);\r
+       }\r
+       \r
+       /**\r
+        * スクリーンショット用\r
+        */\r
+       public Component getTableHeader() {\r
+               return jTable_listed.getTableHeader();\r
+       }\r
+       \r
+       /**\r
+        * スクリーンショット用\r
+        */\r
+       public Component getTableBody() {\r
+               return jTable_listed;\r
+       }\r
+       \r
+       /*******************************************************************************\r
+        * リスナー\r
+        ******************************************************************************/\r
+       \r
+       /**\r
+        * 現在時刻追従スクロール\r
+        */\r
+       @Override\r
+       public void timerRised(VWTimerRiseEvent e) {\r
+               \r
+               if ( ! timer_now_enabled ) {\r
+                       return;\r
+               }\r
+                       \r
+               stopTimer(false);\r
+               \r
+               ProgDetailList tvd = null;\r
+               \r
+               // 更新前に選択していた行を確認する\r
+               {\r
+                       int row = jTable_listed.getSelectedRow();\r
+                       if ( row >= 0 ) {\r
+                               int vrow = jTable_listed.convertRowIndexToModel(row);\r
+                               tvd = rowData.get(vrow).tvd;\r
+                       }\r
+               }\r
+               \r
+               reselectTree(); // タイマーはこの中で再開される\r
+\r
+               // 更新前に選択していた行を再度選択する\r
+               if ( tvd != null ) {\r
+                       int vrow = -1;\r
+                       for ( ListedItem c : rowData ) {\r
+                               vrow++;\r
+                               if ( c.tvd == tvd ) {\r
+                                       int row = jTable_listed.convertRowIndexToView(vrow);\r
+                                       jTable_listed.setRowSelectionInterval(row,row);\r
+                                       break; \r
+                               }\r
+                       }\r
+               }\r
+       }\r
+\r
+       /**\r
+        * タブを開いたり閉じたりしたときに動くリスナー\r
+        * ★synchronized(rowData)★\r
+        * @see #updateReserveMark()\r
+        */\r
+       private ComponentListener cl_tabshownhidden = new ComponentAdapter() {\r
+               @Override\r
+               public void componentShown(ComponentEvent e) {\r
+                       \r
+                       // ★★★ イベントにトリガーされた処理がかちあわないように synchronized() ★★★\r
+                       synchronized ( rowData ) {\r
+                               // 終了した予約を整理する\r
+                               for (HDDRecorder recorder : recorders) {\r
+                                       recorder.refreshReserves();\r
+                               }\r
+                               \r
+                               // 他のコンポーネントと連動\r
+                               onShown();\r
+                       }\r
+               }\r
+               @Override\r
+               public void componentHidden(ComponentEvent e) {\r
+                       // フォーカスは無効にする\r
+                       vrowInFocus = -1;\r
+                       \r
+                       // 他のコンポーネントと連動\r
+                       onHidden();\r
+               }\r
+       };\r
+       \r
+       /**\r
+        *  行を選択すると詳細が表示されるようにする\r
+        */\r
+       private ListSelectionListener lsSelectListner = new ListSelectionListener() {\r
+               public void valueChanged(ListSelectionEvent e) {\r
+                       if(e.getValueIsAdjusting()) return;\r
+                       if (jTable_listed.getSelectedRow() >= 0) {\r
+                               int row = jTable_listed.convertRowIndexToModel(jTable_listed.getSelectedRow());\r
+                               ListedItem c = rowData.get(row);\r
+                               jTextPane_detail.setLabel(\r
+                                               c.tvd.start,\r
+                                               c.tvd.end,\r
+                                               c.tvd.title);\r
+                               jTextPane_detail.setText(c.tvd.detail);\r
+                       }\r
+                       else {\r
+                               jTextPane_detail.setLabel("","","");\r
+                               jTextPane_detail.setText("");\r
+                       }\r
+               }\r
+       };\r
+       \r
+       /**\r
+        * マウスクリックでメニュー表示\r
+        */\r
+       private MouseAdapter lsClickAdapter = new MouseAdapter() {\r
+               public void mouseClicked(MouseEvent e) {\r
+                       \r
+                       JTable t = (JTable) e.getSource();\r
+                       Point p = e.getPoint();\r
+                       int vrow = t.rowAtPoint(p);\r
+                       \r
+                       // カーソル位置を記憶する\r
+                       vrowInFocus = vrow;\r
+                       \r
+                       //t.getSelectionModel().setSelectionInterval(vrow,vrow);\r
+                       int row = t.convertRowIndexToModel(vrow);\r
+                       ListedItem c = rowData.get(row);\r
+                       \r
+                       ProgDetailList tvd = c.tvd;\r
+                       \r
+                       // 番組表リストの挿入位置を決める\r
+                       GregorianCalendar cal = CommonUtils.getCalendar(c.tvd.startDateTime);\r
+                       if (CommonUtils.isLateNight(cal)) {\r
+                               cal.add(Calendar.DATE,-1);\r
+                       }\r
+                       \r
+                       switch ( tvd.type ) {\r
+                       case PROG:\r
+                       case SYOBO:\r
+                       case PASSED:\r
+                       case PICKED:\r
+                       case OTHERS:\r
+                               break;\r
+                       default:\r
+                               MWin.appendError(ERRID+"未定義の番組表種別です: "+tvd.type);\r
+                               return;\r
+                       };\r
+                       \r
+                       if (e.getButton() == MouseEvent.BUTTON3) {\r
+                               if (e.getClickCount() == 1) {\r
+                                       // 右シングルクリックでメニューの表示\r
+                                       t.getSelectionModel().setSelectionInterval(vrow,vrow);\r
+                                       \r
+                                       int threshold = getThrValByRow(row);\r
+                                       String keyword = (threshold > 0) ? (getKeyValByRow(row)) : (tvd.title);\r
+                                       showPopupForTraceProgram(t, tvd, keyword, threshold, p.x, p.y, -1);\r
+                               }\r
+                       }\r
+                       else if (e.getButton() == MouseEvent.BUTTON1) {\r
+                               if (e.getClickCount() == 2) {\r
+                                       // RADIOは閲覧のみ \r
+                                       if (tvd.type == ProgType.PROG && tvd.subtype == ProgSubtype.RADIO) {\r
+                                               return;\r
+                                       }\r
+                                       // レコーダが選択されていない場合はなにもしない\r
+                                       if (recorders.size() == 0) {\r
+                                               return;\r
+                                       }\r
+\r
+                                       // 左ダブルクリックで\r
+                                       switch ( env.getDblClkCmd() ) {\r
+                                       \r
+                                       // 予約ウィンドウを開く\r
+                                       case SHOWRSVDIALOG:\r
+                                               if (tvd.type == ProgType.PASSED) {\r
+                                                       // 過去ログは閲覧のみ \r
+                                                       return;\r
+                                               }\r
+                                               \r
+                                               // 類似予約抽出条件の設定\r
+                                               String keyword = "";\r
+                                               int threshold = getThrValByRow(row);\r
+                                               if (threshold > 0) {\r
+                                                       keyword = getKeyValByRow(row);\r
+                                               }\r
+                                               \r
+                                               // ダイアログを開く\r
+                                               //rD.clear();\r
+                                               CommonSwingUtils.setLocationCenter(parent,rD);\r
+                                               if (rD.open(tvd, keyword, threshold)) {\r
+                                                       rD.setVisible(true);\r
+                                               }\r
+                                               else {\r
+                                                       rD.setVisible(false);\r
+                                               }\r
+                                               \r
+                                               // 予約マークを更新する\r
+                                               if (rD.isReserved()) {\r
+                                                       // 自分\r
+                                                       setReservedMarks();\r
+                                                       tableModel_listed.fireTableDataChanged();\r
+                                                       rowheaderModel_listed.fireTableDataChanged();\r
+                                                       refocus();\r
+                                                       // 他人\r
+                                                       updateReserveDisplay(tvd.center);\r
+                                               }\r
+                                               break;\r
+                                        \r
+                                       // 番組欄にジャンプ\r
+                                       case JUMPTOPAPER:\r
+                                               jumpToPaper(tvd.center,tvd.startDateTime);\r
+                                               break;\r
+                                               \r
+                                       // ブラウザで番組詳細を開く\r
+                                       case JUMPTOWEB:\r
+                                               if ( tvd.link.startsWith("http") ) {\r
+                                                       try {\r
+                                                               Desktop desktop = Desktop.getDesktop();\r
+                                                               desktop.browse(new URI(tvd.link));\r
+                                                       } catch (IOException e1) {\r
+                                                               e1.printStackTrace();\r
+                                                       } catch (URISyntaxException e1) {\r
+                                                               e1.printStackTrace();\r
+                                                       }\r
+                                               }\r
+                                               break;\r
+                                       }\r
+                               }\r
+                       }\r
+                       else if (e.getButton() == MouseEvent.BUTTON2) {\r
+                               // ピックアップに追加or削除\r
+                               addToPickup(tvd);\r
+                               setReservedMarks();\r
+                               tableModel_listed.fireTableDataChanged();\r
+                               rowheaderModel_listed.fireTableDataChanged();\r
+                               refocus();\r
+                       }\r
+                       \r
+                       // カーソル位置をリセットする\r
+                       vrowInFocus = -1;\r
+               }\r
+       };\r
+       \r
+       /**\r
+        * サイドツリーのノードをさわると実行されるリスナー\r
+        */\r
+       private final MouseListener ml_nodeselected = new MouseAdapter() {\r
+               @Override\r
+               public void mouseClicked(MouseEvent e) {\r
+                       if (SwingUtilities.isRightMouseButton(e)) {\r
+                               int selRow = jTree_tree.getRowForLocation(e.getX(), e.getY());\r
+                               if (selRow != -1) {\r
+                                       jTree_tree.setSelectionRow(selRow);\r
+                               }\r
+                               \r
+                               TreePath path = jTree_tree.getPathForLocation(e.getX(), e.getY());\r
+                               \r
+                               if ( path != null ) {\r
+                                       \r
+                                       JTreeLabel.Nodes node = (path.getPathCount() < 2) ? null : JTreeLabel.Nodes.getNode(path.getPathComponent(1).toString());\r
+                                       String value = path.getLastPathComponent().toString();\r
+                                       \r
+                                       switch ( path.getPathCount() ) {\r
+                                       case 2:\r
+                                               switch ( node ) {\r
+                                               case TRACE:\r
+                                                       showPopupForSortTraceKey(e.getX(), e.getY());\r
+                                                       break;\r
+                                               case KEYWORD:\r
+                                                       showPopupForSortSearchKey(e.getX(), e.getY());\r
+                                                       break;\r
+                                               case KEYWORDGROUP:\r
+                                                       showPopupForRemoveKeywordGrp(e.getX(), e.getY());\r
+                                                       break;\r
+                                               case EXTENTION:\r
+                                                       showPopupForSortExtension(e.getX(), e.getY());\r
+                                                       break;\r
+                                               default:\r
+                                                       break;\r
+                                               }\r
+                                               break;\r
+                                               \r
+                                       case 3:\r
+                                               switch ( node ) {\r
+                                               case TRACE:\r
+                                                       showPopupForRemoveTraceKey(e.getX(), e.getY(), value);\r
+                                                       break;\r
+                                               case KEYWORD:\r
+                                                       showPopupForRemoveKeyword(e.getX(), e.getY(), value);\r
+                                                       break;\r
+                                               case KEYWORDGROUP:\r
+                                                       showPopupForRemoveKeywordGrpName(e.getX(), e.getY(), value);\r
+                                                       break;\r
+                                               case EXTENTION:\r
+                                                       showPopupForRemoveExtension(e.getX(), e.getY(), value);\r
+                                                       break;\r
+                                               default:\r
+                                                       break;\r
+                                               }\r
+                                               break;\r
+                                               \r
+                                       case 4:\r
+                                               switch ( node ) {\r
+                                               case KEYWORDGROUP:\r
+                                                       showPopupForRemoveKeywordGrpEntry(e.getX(), e.getY(), path.getPathComponent(2).toString(), value);\r
+                                                       break;\r
+                                               default:\r
+                                                       break;\r
+                                               }\r
+                                               break;\r
+                                       }\r
+                               }\r
+                       }\r
+               }\r
+       };\r
+       \r
+       /**\r
+        * サイドツリーにつけるリスナー(ツリーの展開状態を記憶する)\r
+        */\r
+       private final TreeExpansionListener tel_nodeexpansion = new TreeExpansionListener() {\r
+               \r
+               @Override\r
+               public void treeExpanded(TreeExpansionEvent event) {\r
+                       ter.reg();\r
+               }\r
+               \r
+               @Override\r
+               public void treeCollapsed(TreeExpansionEvent event) {\r
+                       ter.reg();\r
+               }\r
+       };\r
+       \r
+       /**\r
+        * サイドツリーにつけるリスナー(クリックで描画実行)\r
+        */\r
+       private final TreeSelectionListener tsl_nodeselected = new TreeSelectionListener() {\r
+               @Override\r
+               public void valueChanged(TreeSelectionEvent e) {\r
+                       \r
+                       TreePath path = jTree_tree.getSelectionPath();\r
+                       \r
+                       if (path != null) {\r
+                               \r
+                               // 本当は排他するべきかな\r
+                               stopTimer(false);\r
+                               boolean stop_timer = true;\r
+                               \r
+                               JTreeLabel.Nodes node = (path.getPathCount() < 2) ? null : JTreeLabel.Nodes.getNode(path.getPathComponent(1).toString());\r
+\r
+                               switch ( path.getPathCount() ) {\r
+                               \r
+                               // 親ノード\r
+                               case 2:\r
+                                       switch ( node ) {\r
+                                       case START:\r
+                                               redrawListByFlag(ProgFlags.NEW);\r
+                                               break;\r
+                                       case END:\r
+                                               redrawListByFlag(ProgFlags.LAST);\r
+                                               break;\r
+                                       case NOW:\r
+                                               redrawListByNow(null);\r
+                                               stop_timer = false;\r
+                                               break;\r
+                                       case SYOBOCAL:\r
+                                               redrawSyobocalListByTraceAndKeyword();\r
+                                               break;\r
+                                       case STANDBY:\r
+                                               redrawListByTraceAndKeyword();\r
+                                               break;\r
+                                       case TRACE:\r
+                                               redrawListByTrace(null);\r
+                                               break;\r
+                                       case KEYWORD:\r
+                                               redrawListByKeyword(null);\r
+                                               break;\r
+                                       case EXTENTION:\r
+                                               redrawListByExtkeywordAll(extKeys.getSearchKeys());\r
+                                               break;\r
+                                       default:\r
+                                               break;\r
+                                       }\r
+                                       jLabel_tree.setView(node, null);\r
+                                       break;\r
+                                       \r
+                               // 子ノード\r
+                               case 3:\r
+                                       switch ( node ) {\r
+                                       case SEARCHHIST:\r
+                                               VWListedTreeNode inode = (VWListedTreeNode) path.getLastPathComponent();\r
+                                               VWListedTreeNode parent = (VWListedTreeNode) inode.getParent();\r
+                                               redrawListBySearched(ProgType.PASSED,parent.getIndex(inode));\r
+                                               break;\r
+                                       case START:\r
+                                               {\r
+                                                       ProgGenre genre = ProgGenre.get(path.getLastPathComponent().toString());\r
+                                                       if ( genre != null ) {\r
+                                                               redrawListByFlag(ProgFlags.NEW,genre);\r
+                                                       }\r
+                                               }\r
+                                               break;\r
+                                       case END:\r
+                                               {\r
+                                                       ProgGenre genre = ProgGenre.get(path.getLastPathComponent().toString());\r
+                                                       if ( genre != null ) {\r
+                                                               redrawListByFlag(ProgFlags.LAST,genre);\r
+                                                       }\r
+                                               }\r
+                                               break;\r
+                                       case SYOBOCAL:\r
+                                               {\r
+                                                       JTreeLabel.Nodes subnode = JTreeLabel.Nodes.getNode(path.getLastPathComponent().toString());\r
+                                                       switch ( subnode ) {\r
+                                                       case TRACE:\r
+                                                               redrawSyobocalListByTrace();\r
+                                                               break;\r
+                                                       case KEYWORD:\r
+                                                               redrawSyobocalListByKeyword();\r
+                                                               break;\r
+                                                       case SYOBOALL:\r
+                                                               redrawSyobocalAll();\r
+                                                               break;\r
+                                                       default:\r
+                                                               break;\r
+                                                       }\r
+                                               }\r
+                                               break;\r
+                                       case NOW:\r
+                                               {\r
+                                                       ProgGenre genre = ProgGenre.get(path.getLastPathComponent().toString());\r
+                                                       if ( genre != null ) {\r
+                                                               redrawListByNow(genre);\r
+                                                               stop_timer = false;\r
+                                                       }\r
+                                               }\r
+                                               break;\r
+                                       case STANDBY:\r
+                                               {\r
+                                                       JTreeLabel.Nodes subnode = JTreeLabel.Nodes.getNode(path.getLastPathComponent().toString());\r
+                                                       switch ( subnode ) {\r
+                                                       case NEWARRIVAL:\r
+                                                               redrawListByTraceAndKeywordNewArrival();\r
+                                                               break;\r
+                                                       case MODIFIED:\r
+                                                               redrawListByTraceAndKeywordModified();\r
+                                                               break;\r
+                                                       case SYOBOONLY:\r
+                                                               redrawSyobocalListByOnly();\r
+                                                               break;\r
+                                                       case PICKUP:\r
+                                                               redrawListByPickup();\r
+                                                               break;\r
+                                                       default:\r
+                                                               redrawListByTraceAndKeywordOkini(path.getLastPathComponent().toString());\r
+                                                               break;\r
+                                                       }\r
+                                               }\r
+                                               break;\r
+                                       case TRACE:\r
+                                               for (TraceKey trace : trKeys.getTraceKeys()) {\r
+                                                       if (path.getLastPathComponent().toString().equals(trace.getLabel())) {\r
+                                                               redrawListByTrace(trace);\r
+                                                               break;\r
+                                                       }\r
+                                               }\r
+                                               break;\r
+                                       case KEYWORD:\r
+                                               for (SearchKey search : srKeys.getSearchKeys()) {\r
+                                                       if (path.getLastPathComponent().toString().equals(search.getLabel())) {\r
+                                                               redrawListByKeyword(search);\r
+                                                               break;\r
+                                                       }\r
+                                               }\r
+                                               break;\r
+                                       case KEYWORDGROUP:\r
+                                               for (SearchGroup gr : srGrps) {\r
+                                                       if (gr.getName().equals(path.getLastPathComponent().toString())) {\r
+                                                               redrawListByKeywordGrp(gr);\r
+                                                               break;\r
+                                                       }\r
+                                               }\r
+                                               break;\r
+                                       case GENRE:\r
+                                               for (ProgGenre genre : ProgGenre.values()) {\r
+                                                       if (path.getLastPathComponent().toString().equals(genre.toString())) {\r
+                                                               redrawListByGenre(genre, null);\r
+                                                               break;\r
+                                                       }\r
+                                               }\r
+                                               break;\r
+                                       case BCASTLIST:\r
+                                               redrawListByCenterList(path.getLastPathComponent().toString());\r
+                                               break;\r
+                                       case EXTENTION:\r
+                                               for (SearchKey search : extKeys.getSearchKeys()) {\r
+                                                       if (path.getLastPathComponent().toString().equals(search.getLabel())) {\r
+                                                               redrawListByKeywordDyn(search);\r
+                                                               break;\r
+                                                       }\r
+                                               }\r
+                                               break;\r
+                                       default:\r
+                                               break;\r
+                                       }\r
+                                       jLabel_tree.setView(node, path.getLastPathComponent().toString());\r
+                                       System.out.println(jLabel_tree.getView());\r
+                                       break;\r
+                                       \r
+                               // 孫ノード\r
+                               case 4:\r
+                                       switch ( node ) {\r
+                                       case KEYWORDGROUP:\r
+                                               for (SearchKey search : srKeys.getSearchKeys()) {\r
+                                                       if (path.getLastPathComponent().toString().equals(search.getLabel())) {\r
+                                                               redrawListByKeyword(search);\r
+                                                               break;\r
+                                                       }\r
+                                               }\r
+                                               break;\r
+                                       case GENRE:\r
+                                               ProgGenre genre = ProgGenre.get(path.getPathComponent(2).toString());\r
+                                               if ( genre != null ) {\r
+                                                       ProgSubgenre subgenre = ProgSubgenre.get(path.getLastPathComponent().toString());\r
+                                                       if ( subgenre != null ) {\r
+                                                               redrawListByGenre(genre, subgenre);\r
+                                                       }\r
+                                               }\r
+                                               break;\r
+                                       default:\r
+                                               break;\r
+                                       }\r
+                                       jLabel_tree.setView(node, path.getLastPathComponent().toString());\r
+                                       break;\r
+                               }\r
+                               \r
+                               if (stop_timer) {\r
+                                       stopTimer( ! (path.getPathCount() >= 2 && path.getPathComponent(1).toString().equals(JTreeLabel.Nodes.NOW.toString())));\r
+                               }\r
+                               else {\r
+                                       startTimer();\r
+                               }\r
+                       }\r
+               }\r
+       };\r
+       \r
+       private final MouseListener ml_treehide = new MouseAdapter() {\r
+               public void mouseEntered(MouseEvent e) {\r
+                       if (isFullScreen()) {\r
+                               setExpandTree();\r
+                               //StdAppendMessage("Show tree (L)");\r
+                       }\r
+               }\r
+               public void mouseExited(MouseEvent e) {\r
+                       if (isFullScreen()) {\r
+                               setCollapseTree();\r
+                               //StdAppendMessage("Hide tree (L)");\r
+                       }\r
+               }\r
+       };\r
+       \r
+       \r
+       /**\r
+        * ツリーのリスナーを止める\r
+        */\r
+       private void stopTreeListener() {\r
+               jTree_tree.removeMouseListener(ml_nodeselected);\r
+               jTree_tree.removeTreeSelectionListener(tsl_nodeselected);\r
+       }\r
+       \r
+       /**\r
+        * ツリーのリスナーを動かす\r
+        */\r
+       private void startTreeListener() {\r
+               jTree_tree.addMouseListener(ml_nodeselected);\r
+               jTree_tree.addTreeSelectionListener(tsl_nodeselected);\r
+       }\r
+\r
+       /**\r
+        * 検索履歴でサブノード作成\r
+        */\r
+       public void redrawTreeByHistory() {\r
+               \r
+               stopTreeListener();\r
+               TreePath tp = jTree_tree.getSelectionPath();\r
+               \r
+               SearchResult searched = tvprograms.getSearched();\r
+               \r
+               searchedNode.removeAllChildren();\r
+               for ( int i=0; i<searched.getResultBufferSize(); i++) {\r
+                       searchedNode.add(new VWListedTreeNode(searched.getLabel(i)));\r
+               }\r
+               \r
+               jTree_tree.setSelectionPath(tp);\r
+               jTree_tree.updateUI();\r
+               startTreeListener();\r
+       }\r
+\r
+       /**\r
+        * 新番組/最終回でサブノード作成\r
+        */\r
+       private void redrawTreeByGenre() {\r
+               \r
+               stopTreeListener();\r
+               TreePath tp = jTree_tree.getSelectionPath();\r
+               \r
+               _redrawTreeByGenre(nowNode);\r
+               _redrawTreeByGenre(startNode);\r
+               _redrawTreeByGenre(endNode);\r
+               \r
+               jTree_tree.setSelectionPath(tp);\r
+               jTree_tree.updateUI();\r
+               startTreeListener();\r
+       }\r
+       \r
+       private void _redrawTreeByGenre(DefaultMutableTreeNode parent) {\r
+               parent.removeAllChildren();\r
+               for ( ProgGenre genre : ProgGenre.values() ) {\r
+                       parent.add(new VWListedTreeNode(genre.toString()));\r
+               }\r
+       }\r
+\r
+       /**\r
+        * しょぼかるでサブノード作成\r
+        */\r
+       private void redrawTreeBySyobo() {\r
+               \r
+               stopTreeListener();\r
+               TreePath tp = jTree_tree.getSelectionPath();\r
+               \r
+               syobocalNode.removeAllChildren();\r
+               syobocalNode.add(new VWListedTreeNode(JTreeLabel.Nodes.TRACE.getLabel()));\r
+               syobocalNode.add(new VWListedTreeNode(JTreeLabel.Nodes.KEYWORD.getLabel()));\r
+               syobocalNode.add(new VWListedTreeNode(JTreeLabel.Nodes.SYOBOALL.getLabel()));\r
+               \r
+               jTree_tree.setSelectionPath(tp);\r
+               jTree_tree.updateUI();\r
+               startTreeListener();\r
+       }\r
+       \r
+       /**\r
+        * 予約待機でサブノード作成\r
+        */\r
+       private void redrawTreeByStandby() {\r
+               \r
+               stopTreeListener();\r
+               TreePath tp = jTree_tree.getSelectionPath();\r
+               \r
+               standbyNode.removeAllChildren();\r
+               for ( String okini : TVProgram.OKINIIRI ) {\r
+                       if ( ! "".equals(okini) ) {\r
+                               standbyNode.add(new VWListedTreeNode(okini));\r
+                       }\r
+               }\r
+               standbyNode.add(new VWListedTreeNode(JTreeLabel.Nodes.PICKUP.getLabel()));\r
+               standbyNode.add(new VWListedTreeNode(JTreeLabel.Nodes.NEWARRIVAL.getLabel()));\r
+               standbyNode.add(new VWListedTreeNode(JTreeLabel.Nodes.MODIFIED.getLabel()));\r
+               if ( env.getUseSyobocal() ) {\r
+                       standbyNode.add(new VWListedTreeNode(JTreeLabel.Nodes.SYOBOONLY.getLabel()));\r
+               }\r
+               \r
+               jTree_tree.setSelectionPath(tp);\r
+               jTree_tree.updateUI();\r
+               startTreeListener();\r
+       }\r
+       \r
+       /**\r
+        * 番組追跡でサブノード作成\r
+        */\r
+       public void redrawTreeByTrace() {\r
+               \r
+               stopTreeListener();\r
+               TreePath tp = jTree_tree.getSelectionPath();\r
+               \r
+               traceNode.removeAllChildren();\r
+               for ( TraceKey key : trKeys.getTraceKeys() ) {\r
+                       traceNode.add(new VWListedTreeNode(key.getLabel(), mpList.isTrKeyUsed(key.getLabel())));\r
+               }\r
+               \r
+               jTree_tree.setSelectionPath(tp);\r
+               jTree_tree.updateUI();\r
+               startTreeListener();\r
+       }\r
+       \r
+       /**\r
+        * キーワード検索でサブノード作成\r
+        */\r
+       public void redrawTreeByKeyword() {\r
+               \r
+               stopTreeListener();\r
+               TreePath tp = jTree_tree.getSelectionPath();\r
+               \r
+               keywordNode.removeAllChildren();\r
+               for ( SearchKey key : srKeys.getSearchKeys() ) {\r
+                       keywordNode.add(new VWListedTreeNode(key.getLabel(), mpList.isSrKeyUsed(key.getLabel())));\r
+               }\r
+               \r
+               jTree_tree.setSelectionPath(tp);\r
+               jTree_tree.updateUI();\r
+               startTreeListener();\r
+       }\r
+\r
+       /**\r
+        * サブジャンルでサブノード作成\r
+        */\r
+       public void redrawTreeBySubGenre() {\r
+               \r
+               stopTreeListener();\r
+               TreePath tp = jTree_tree.getSelectionPath();\r
+               \r
+               genreNode.removeAllChildren();\r
+               for ( ProgGenre genre : ProgGenre.values() ) {\r
+                       VWListedTreeNode g = new VWListedTreeNode(genre.toString());\r
+                       genreNode.add(g);\r
+                       for ( ProgSubgenre subgenre : ProgSubgenre.values(genre) ) {\r
+                               VWListedTreeNode sg = new VWListedTreeNode(subgenre.toString()); \r
+                               g.add(sg);\r
+                       }\r
+               }\r
+               \r
+               jTree_tree.setSelectionPath(tp);\r
+               jTree_tree.updateUI();\r
+               startTreeListener();\r
+       }\r
+\r
+       /**\r
+        * 放送局でサブノード作成\r
+        */\r
+       public void redrawTreeByCenter() {\r
+               \r
+               stopTreeListener();\r
+               TreePath tp = jTree_tree.getSelectionPath();\r
+               \r
+               centerListNode.removeAllChildren();\r
+               TVProgramIterator pli = tvprograms.getIterator().build(chsort.getClst(), IterationType.ALL);\r
+               for ( ProgList pl : pli ) {\r
+                       centerListNode.add(new VWListedTreeNode(pl.Center));\r
+               }\r
+               \r
+               jTree_tree.setSelectionPath(tp);\r
+               jTree_tree.updateUI();\r
+               startTreeListener();\r
+       }\r
+\r
+       /**\r
+        * キーワードグループでサブノード作成\r
+        */\r
+       public void redrawTreeByKeywordGroup() {\r
+               \r
+               stopTreeListener();\r
+               TreePath tp = jTree_tree.getSelectionPath();\r
+               \r
+               keywordGrpNode.removeAllChildren();\r
+               for ( SearchGroup gr : srGrps ) {\r
+                       VWListedTreeNode gn = new VWListedTreeNode(gr.getName());\r
+                       keywordGrpNode.add(gn);\r
+                       for ( String kw : gr ) {\r
+                               gn.add(new VWListedTreeNode(kw));\r
+                       }\r
+               }\r
+               \r
+               jTree_tree.setSelectionPath(tp);\r
+               jTree_tree.updateUI();\r
+               startTreeListener();\r
+       }\r
+\r
+       /**\r
+        * 放送局でサブノード作成\r
+        */\r
+       public void redrawTreeByExtension() {\r
+               \r
+               stopTreeListener();\r
+               TreePath tp = jTree_tree.getSelectionPath();\r
+               \r
+               extensionNode.removeAllChildren();\r
+               for ( SearchKey key : extKeys.getSearchKeys() ) {\r
+                       extensionNode.add(new VWListedTreeNode(key.getLabel()));\r
+               }\r
+               \r
+               jTree_tree.setSelectionPath(tp);\r
+               jTree_tree.updateUI();\r
+               startTreeListener();\r
+       }\r
+       \r
+       \r
+       \r
+       /**\r
+        * 現時点でまだ開始していない番組を上から順に選択する。\r
+        */\r
+       public void selectBatchTarget() {\r
+               \r
+               String dt = CommonUtils.getDateTime(0);\r
+               \r
+               int cnt=1;\r
+               for (int row=0; row<rowData.size(); row++) {\r
+                       ListedItem c = rowData.get(row);\r
+                       if (dt.compareTo(c.tvd.startDateTime) > 0) {\r
+                               // 開始日時が過去のものは対象外\r
+                               continue;\r
+                       }\r
+                       if ( c.rsvmark.length() == 0 || c.rsvmark.equals(RSVMARK_URABAN) ) {\r
+                               int vrow = jTable_listed.convertRowIndexToView(row);\r
+                               jTable_listed.getSelectionModel().addSelectionInterval(vrow, vrow);\r
+                               if (cnt++ >= env.getRsvTargets()) {\r
+                                       return;\r
+                               }\r
+                       }\r
+               }\r
+       }\r
+       \r
+       /**\r
+        * ツールバーの一括予約ボタンを押して実行される一括予約処理\r
+        */\r
+       public void doBatchReserve() {\r
+               //\r
+               boolean mod = false;\r
+               for (int vrow : jTable_listed.getSelectedRows()) {\r
+                       \r
+                       int row = jTable_listed.convertRowIndexToModel(vrow);\r
+                       ProgDetailList tvd = rowData.get(row).tvd;\r
+                       \r
+                       //VWReserveDialog rD = new VWReserveDialog(0, 0, env, tvprograms, recorders, avs, chavs, stwin);\r
+                       //rD.clear();\r
+\r
+                       if (rD.open(tvd)) {\r
+                               rD.doRecord();\r
+                       }\r
+                       \r
+                       // 予約ダイアログは見せないまま更新を実行する\r
+                       \r
+                       if ( ! rD.isReserved()) {\r
+                               StdAppendError("【警告】予約の登録に失敗しました: "+rowData.get(row).tvd.title);\r
+                               break;\r
+                       }\r
+                       else {\r
+                               mod = true;\r
+                       }\r
+               }\r
+               if (mod) {\r
+                       // 自分\r
+                       setReservedMarks();\r
+                       tableModel_listed.fireTableDataChanged();\r
+                       rowheaderModel_listed.fireTableDataChanged();\r
+                       refocus();\r
+                       // 他人\r
+                       updateReserveDisplay(null);\r
+               }\r
+       }\r
+       \r
+       /**\r
+        * 他のクラスで発生したイベント中に呼び出されてリスト形式の予約マーク表示を更新するためのメソッド。\r
+        * ★synchronized(rowData)★\r
+        * @see #cl_tabshownhidden\r
+        */\r
+       public void updateReserveMark() {\r
+               // ★★★ イベントにトリガーされた処理がかちあわないように synchronized() ★★★\r
+               synchronized ( rowData ) {\r
+                       setReservedMarks();\r
+                       tableModel_listed.fireTableDataChanged();\r
+                       rowheaderModel_listed.fireTableDataChanged();\r
+               }\r
+       }\r
+       \r
+       /**\r
+        * テーブルを更新した後、セレクション状態が解除されるので再度セレクションする。\r
+        */\r
+       public void refocus() {\r
+               if (vrowInFocus >= 0) {\r
+                       jTable_listed.getSelectionModel().addSelectionInterval(vrowInFocus, vrowInFocus);\r
+                       vrowInFocus = -1;\r
+               }\r
+       }\r
+\r
+       /**\r
+        * カラム幅を保存する(鯛ナビ終了時に呼び出されるメソッド)\r
+        */\r
+       public void copyColumnWidth() {\r
+               //DefaultTableColumnModel columnModel = (DefaultTableColumnModel)jTable_listed.getColumnModel();\r
+               TableColumn column = null;\r
+               for ( ListedColumn lc : ListedColumn.values() ) {\r
+                       if ( lc.getIniWidth() < 0 ) {\r
+                               continue;\r
+                       }\r
+                       try {\r
+                               column = jTable_listed.getColumn(lc.getName());\r
+                               bounds.getListedColumnSize().put(lc.toString(), column.getPreferredWidth());    // toString()!\r
+                       }\r
+                       catch (IllegalArgumentException e) {\r
+                               // 非表示のカラムは操作できない\r
+                       }\r
+               }\r
+       }\r
+       \r
+       // キーワードにマッチした箇所の強調色\r
+       public void setMatchedKeywordColor(Color c) {\r
+               titleCellRenderer.setMatchedKeywordColor(c);\r
+       }\r
+       \r
+       // 予約行の強調表示\r
+       public void setRsvdLineColor(Color c) {\r
+               jTable_listed.setReservedColor(c);\r
+       }\r
+       \r
+       // ピックアップ行の強調表示\r
+       public void setPickedLineColor(Color c) {\r
+               jTable_listed.setPickedColor(c);\r
+       }\r
+       \r
+       // 予約行の強調表示\r
+       public void setCurrentLineColor(Color c) {\r
+               jTable_listed.setCurrentColor(c);\r
+       }\r
+       \r
+       // 番組詳細の表示・非表示\r
+       public void setMarkColumnVisible(boolean b) {\r
+               jTable_listed.setColumnVisible(ListedColumn.OPTIONS.getName(), b);\r
+       }\r
+       \r
+       // 番組詳細の表示・非表示\r
+       public void setDetailColumnVisible(boolean b) {\r
+               jTable_listed.setColumnVisible(ListedColumn.DETAIL.getName(), b);\r
+       }\r
+\r
+       /*\r
+        * 特定の項目を取得しやすくした感じ?\r
+        */\r
+       \r
+       // 検索結果一覧上のIdNumをintに戻す\r
+       private int getThrValByRow(int row) { \r
+               try {\r
+                       return Integer.valueOf(rowData.get(row).threshold);\r
+               } catch (NumberFormatException e2) {\r
+                       // No proc.\r
+               }\r
+               return 0;\r
+       }\r
+       private String getKeyValByRow(int row) {\r
+               Matcher ma = Pattern.compile("^(.+)\\s*\\([^\\)]+?\\)$").matcher(rowData.get(row).searchlabel);\r
+               if (ma.find()) {\r
+                       return ma.group(1);\r
+               }\r
+               return "";\r
+       }\r
+       \r
+       \r
+       \r
+       \r
+       \r
+       /*\r
+        * 予約マークの取得だお!\r
+        */\r
+       \r
+       private class Marker {\r
+               String mark; \r
+               String myself;\r
+               String color;\r
+               \r
+               public Marker(String mark, String myself, String color) {\r
+                       this.mark = mark;\r
+                       this.myself = myself;\r
+                       this.color = color;\r
+               }\r
+       }\r
+       \r
+       /**\r
+        * 引数で指定した番組を予約している、または予約に一部時間が重なっている場合に表示する予約マークを取得する。\r
+        * @param Center チェックする番組の放送局名\r
+        * @param startDateTime チェックする番組の開始日時\r
+        * @param endDateTime チェックする番組の終了日時\r
+        * @param title チェックする番組のタイトル\r
+        * @param extention 延長警告がなされている番組の場合はtrueを指定。\r
+        * @return String [0]マーク [1]予約しているレコーダのユニークID({@link HDDRecorder#Myself()}) [2]色({@link CommonUtils#str2color(String)})\r
+        */\r
+       private Marker getReservedMarkChar(ListedItem data) {\r
+               \r
+               //\r
+               HDDRecorder recorder = null;                                            // その番組の予約がみつかった最初のレコーダ\r
+               ReserveList reserve = null;                                                     // 見つかった予約情報\r
+               String start = null;                                                            // 実行予定が複数あったら一番近いのを選ぶ\r
+               String end = "";                                                                        // 同上\r
+               long diff = 86400L*30L;\r
+               \r
+               String myself = getSelectedRecorderOnToolbar();\r
+               HDDRecorderList recs = recorders.getMyself(myself);\r
+\r
+               // コンボボックスの指定はピックアップである\r
+               boolean isPickupOnly = ( myself != null && myself.length() == 0 ) ;\r
+               \r
+               if ( ! isPickupOnly ) {\r
+                       \r
+                       // 「ピックアップ」が選択されていればここは通らない\r
+                       \r
+                       // 基準日時\r
+                       String critDateTime = CommonUtils.getCritDateTime(env.getDisplayPassedReserve());\r
+                       \r
+                       // 全予約をなめて、一番近い予約を探さなければならない\r
+                       for ( HDDRecorder rec : recs )\r
+                       {\r
+                               if (diff == 0) break;\r
+                               \r
+                               for ( ReserveList res : rec.getReserves() )\r
+                               {\r
+                                       if (diff == 0) break;\r
+                                       \r
+                                       // Exec == ON ?\r
+                                       if ( env.getDisplayOnlyExecOnEntry() && ! res.getExec()) {\r
+                                               continue;\r
+                                       }\r
+                                       \r
+                                       if (res.getCh_name() == null) {\r
+                                               // 警告したい!\r
+                                               continue;\r
+                                       }\r
+                                       \r
+                                       // 局が一致して\r
+                                       if ( ! data.tvd.center.equals(res.getCh_name())) {\r
+                                               continue;\r
+                                       }\r
+                                               \r
+                                       // 開始終了日時リストを生成する\r
+                                       ArrayList<String> starts = new ArrayList<String>();\r
+                                       ArrayList<String> ends = new ArrayList<String>();\r
+                                       CommonUtils.getStartEndList(starts, ends, res);\r
+                                       \r
+                                       for (int j=0; j<starts.size(); j++) {\r
+                                               if (critDateTime.compareTo(ends.get(j)) > 0) {\r
+                                                       // 終了済みは対象外\r
+                                                       continue;\r
+                                               }\r
+                                               if ( CommonUtils.isOverlap(data.tvd.startDateTime, data.tvd.endDateTime, starts.get(j), ends.get(j), true) ) {\r
+                                                       // より開始時刻が近いものを選ぶ\r
+                                                       if ( start == null ) {\r
+                                                               start = starts.get(j);\r
+                                                       }\r
+                                                       long df = CommonUtils.getDiffDateTime(starts.get(j), data.tvd.startDateTime);\r
+                                                       if ( diff > df ) {\r
+                                                               recorder = rec;\r
+                                                               reserve = res;\r
+                                                               start = starts.get(j);\r
+                                                               end = ends.get(j);\r
+                                                               diff = df;\r
+                                                       }\r
+                                               }\r
+                                       }\r
+                               }\r
+                       }\r
+               }\r
+               \r
+               // 予約されている\r
+               \r
+               Marker mark = null;\r
+               Marker pickmark = null;\r
+               Marker uramark = null;\r
+               \r
+               // 予約マーク\r
+               if (recorder != null) {\r
+                       mark = _getReservedMarkCharNormal(data, recorder, reserve, start, end);\r
+               }\r
+               // ピックアップマーク\r
+               if (env.getShowRsvPickup()) {\r
+                       pickmark = _getReservedMarkCharPickup(data);\r
+               }\r
+               // 裏番組予約マーク\r
+               if (mark == null && pickmark == null && env.getShowRsvUra()) {\r
+                       uramark = _getReservedMarkCharUra(data);\r
+               }\r
+               \r
+               if ( mark != null ) {\r
+                       return mark;\r
+               }\r
+               if ( pickmark != null || isPickupOnly ) {\r
+                       return pickmark;\r
+               }\r
+               if ( uramark != null ) {\r
+                       return uramark;\r
+               }\r
+               \r
+               // 予約されていない\r
+               return(null);\r
+       }\r
+       /**\r
+        * @see #getReservedMarkChar(String, String, String, String, boolean) \r
+        */\r
+       private Marker _getReservedMarkCharNormal(ListedItem data, HDDRecorder recorder, ReserveList reserve, String start, String end) {\r
+               \r
+               // ここに入ってくる場合は時間の重なりが確認できているものだけである\r
+               \r
+               RSVMARK_COND cond = getReservedMarkCond(data, start, end);\r
+               \r
+               if (debug) System.err.println(DBGID+data.tvd.title+" "+data.tvd.startDateTime+" "+data.tvd.endDateTime+" "+start+" "+end+" "+cond);\r
+               \r
+               String mark = null;\r
+               \r
+               switch (cond) {\r
+               case PREV:\r
+                       return null;\r
+               case DELAY:\r
+                       mark = RSVMARK_DELAYED;\r
+                       break;\r
+               case UNDER:\r
+                       mark = RSVMARK_UNDERRUN;\r
+                       break;\r
+               case OVER:\r
+                       mark = RSVMARK_OVERRUN;\r
+                       break;\r
+               case CLIP:\r
+                       mark = (data.tvd.extension) ? (RSVMARK_CLIPPED_E) : (RSVMARK_CLIPPED);\r
+                       break;\r
+               case SHORT:\r
+                       mark = (data.tvd.extension) ? (RSVMARK_SHORTAGE_E) : (RSVMARK_SHORTAGE);\r
+                       break;\r
+               default:\r
+                       mark = RSVMARK_NORMAL;\r
+                       break;\r
+               }\r
+\r
+               return(new Marker((reserve.getExec())?(mark):(RSVMARK_NOEXEC), recorder.Myself(), recorder.getColor(reserve.getTuner())));\r
+       }\r
+       private RSVMARK_COND getReservedMarkCond(ListedItem data, String start, String end) {\r
+               {\r
+                       // 番組の終了日時と予約の開始日時(1分は想定内)\r
+                       int overlap = (int) (CommonUtils.getCompareDateTime(data.tvd.endDateTime,start)/60000L);\r
+                       if ( env.getOverlapUp() && overlap == 1 )\r
+                               return RSVMARK_COND.PREV;\r
+               }\r
+               {\r
+                       // 番組の開始日時と予約の開始日時(1分でも遅れちゃだめ)\r
+                       int overlap = (int) (CommonUtils.getCompareDateTime(data.tvd.startDateTime,start)/60000L);\r
+                       if ( overlap <= -1 )\r
+                               return RSVMARK_COND.DELAY;\r
+               }\r
+               \r
+               // 延長警告がある場合はこんだけ延びる\r
+               int spoex_length = (data.tvd.extension)?(Integer.valueOf(env.getSpoexLength())):(0);\r
+               \r
+               {\r
+                       // 番組の終了日時と予約の終了日時\r
+                       int overlap = (int) (CommonUtils.getCompareDateTime(data.tvd.endDateTime,end)/60000L);\r
+                       \r
+                       if (data.tvd.extension) {\r
+                               // ここは、延長警告で時間が延びるはずが微妙に延びてない感じの予約を探すためのもの\r
+                               \r
+                               // 通常1分短縮から~延長2分短縮まで\r
+                               // 通常1分延長から~延長0分延長まで\r
+                               // どちらでもなければ通常0分短縮から~延長1分短縮まで\r
+                               if (\r
+                                               (env.getOverlapDown2() && ! data.tvd.dontoverlapdown && (overlap <= 1  && (overlap+spoex_length) >= 2)) ||\r
+                                               (env.getOverlapDown()  && (overlap <= -1 && (overlap+spoex_length) >= 0)) ||\r
+                                               ( ( ! env.getOverlapDown2() || env.getOverlapDown2() && data.tvd.dontoverlapdown) && ! env.getOverlapDown() && (overlap <= 0 && (overlap+spoex_length) >= 1))\r
+                                               )\r
+                                       return RSVMARK_COND.UNDER;\r
+                       }\r
+                       \r
+                       // ケツ短縮で0分以上進んでたらだめ\r
+                       // ケツ延長で2分以上進んでたらだめ\r
+                       // どちらでもなければ1分以上進んでたらだめ\r
+                       if (\r
+                                       (env.getOverlapDown2() && ! data.tvd.dontoverlapdown && (overlap+spoex_length) <= 0) ||\r
+                                       (env.getOverlapDown() && (overlap+spoex_length) <= -2) ||\r
+                                       ( ( ! env.getOverlapDown2() || env.getOverlapDown2() && data.tvd.dontoverlapdown) && ! env.getOverlapDown() && (overlap+spoex_length) <= -1)\r
+                                       )\r
+                               return RSVMARK_COND.OVER;\r
+                       \r
+                       if ( env.getOverlapDown2() ) {\r
+                               // 1分短縮時に1分なら予定通り\r
+                               if ( (overlap+spoex_length) == 1 )\r
+                                       return RSVMARK_COND.CLIP;\r
+                               // 2分はどうかな\r
+                               if ( (overlap+spoex_length) >= 2 )\r
+                                       return RSVMARK_COND.SHORT;\r
+                       }\r
+                       else {\r
+                               // 1分でもだめ\r
+                               if ( (overlap+spoex_length) >= 2 )\r
+                                       return RSVMARK_COND.SHORT;\r
+                       }\r
+                       \r
+                       if ( env.getOverlapDown() ) {\r
+                               // 1分延長時に1分なら予定通り\r
+                               if ( (overlap+spoex_length) == -1 )\r
+                                       return RSVMARK_COND.CLIP;\r
+                               // ぴったりはだめでしょ\r
+                               if ( (overlap+spoex_length) <= 0 )\r
+                                       return RSVMARK_COND.SHORT;\r
+                       }\r
+                       else {\r
+                               // 1分でもだめ\r
+                               if ( (overlap+spoex_length) <= -1 )\r
+                                       return RSVMARK_COND.SHORT;\r
+                       }\r
+                       \r
+               }\r
+               \r
+               // それら以外の場合(正常予約)\r
+               return RSVMARK_COND.NORMAL;\r
+       }\r
+       private static enum RSVMARK_COND { PREV, DELAY, UNDER, OVER, CLIP, SHORT, NORMAL };\r
+       \r
+       private Marker _getReservedMarkCharPickup(ListedItem data) {\r
+               //return (data.hide_ispickup)?(new Marker(RSVMARK_PICKUP,"",PICKUP_COLOR)):(null);\r
+               //\r
+               PickedProgram picktvp = tvprograms.getPickup();\r
+               \r
+               // みつかるかな?\r
+               ProgDetailList picktvd = picktvp.find(data.tvd);\r
+               if ( picktvd == null ) {\r
+                       // みつかんねーよ\r
+                       return null;\r
+               }\r
+               \r
+               return new Marker(RSVMARK_PICKUP,"",PICKUP_COLOR);\r
+       }\r
+       \r
+       private Marker _getReservedMarkCharUra(ListedItem data) {\r
+               //\r
+               String myself = getSelectedRecorderOnToolbar();\r
+               HDDRecorderList recs = recorders.getMyself(myself);\r
+               \r
+               for ( HDDRecorder rec : recs )\r
+               {\r
+                       for ( ReserveList res : rec.getReserves() ) {\r
+                               // Exec == ON ?\r
+                               if ( env.getDisplayOnlyExecOnEntry() && ! res.getExec() ) {\r
+                                       // 無効状態はしらねーよ\r
+                                       continue;\r
+                               }\r
+                               if ( data.tvd.center.equals(res.getCh_name()) ) {\r
+                                       // 局が違うならいらねーよ → 裏番組だろ、逆だろJK\r
+                                       continue;\r
+                               }\r
+                                       \r
+                               // 開始終了日時リストを生成する\r
+                               ArrayList<String> starts = new ArrayList<String>();\r
+                               ArrayList<String> ends = new ArrayList<String>();\r
+                               CommonUtils.getStartEndList(starts, ends, res);\r
+                               for (int j=0; j<starts.size(); j++) {\r
+                                       if ( CommonUtils.isOverlap(data.tvd.startDateTime, data.tvd.endDateTime, starts.get(j), ends.get(j), env.getAdjoiningNotRepetition()) ) {\r
+                                               return new Marker(RSVMARK_URABAN,"",URABAN_COLOR);\r
+                                       }\r
+                               }\r
+                       }\r
+               }\r
+               return null;\r
+       }\r
+       \r
+       \r
+       \r
+       \r
+       \r
+       /*\r
+        *  ここからノード編集系がいっぱいならんでるお!\r
+        */\r
+       \r
+       /**\r
+        * 番組追跡を編集したい\r
+        */\r
+       private void editTraceKey(String keyword) {\r
+               \r
+               VWTraceKeyDialog tD = new VWTraceKeyDialog(0,0);\r
+               CommonSwingUtils.setLocationCenter(parent,tD);\r
+               \r
+               tD.reopen(keyword, trKeys);\r
+               tD.setVisible(true);\r
+               \r
+               if (tD.isRegistered()) { \r
+                       // 検索結果の再構築\r
+                       mpList.clear(env.getDisableFazzySearch(), env.getDisableFazzySearchReverse());\r
+                       mpList.build(tvprograms, trKeys.getTraceKeys(), srKeys.getSearchKeys());\r
+                       \r
+                       //trKeys.save();        // 保存はtDの中でやってるよ\r
+                       \r
+                       // 変更したノードを選択するようにしたい\r
+                       jLabel_tree.setView(JTreeLabel.Nodes.TRACE, tD.getNewLabel());\r
+                       \r
+                       // 新聞形式を修正してほしい\r
+                       updateBangumiColumns();\r
+\r
+                       // ツリーを更新\r
+                       redrawTreeByTrace();\r
+                       \r
+                       // ツリーを再選択\r
+                       reselectTree();\r
+               }\r
+       }\r
+       \r
+       /**\r
+        * 番組追跡を削除したい\r
+        */\r
+       private void removeTraceKey(String keyword) {\r
+               \r
+               if (env.getShowWarnDialog()) {\r
+                       //Container cp = frame.getContentPane();\r
+                       int ret = JOptionPane.showConfirmDialog(parent, "削除しますか?【"+keyword+"】", "確認", JOptionPane.YES_NO_OPTION);\r
+                       if (ret != JOptionPane.YES_OPTION) {\r
+                               return;\r
+                       }\r
+               }\r
+               \r
+               MWin.appendMessage("番組追跡が削除されました【"+keyword+"】");\r
+\r
+               // 保存\r
+               trKeys.remove(keyword);\r
+               trKeys.save();\r
+               \r
+               // 検索結果の再構築\r
+               mpList.clear(env.getDisableFazzySearch(), env.getDisableFazzySearchReverse());\r
+               mpList.build(tvprograms, trKeys.getTraceKeys(), srKeys.getSearchKeys());\r
+               //mpList.debug();\r
+               \r
+               // 新聞形式を修正してほしい\r
+               updateBangumiColumns();\r
+               \r
+               // ツリーを更新\r
+               redrawTreeByTrace();\r
+       }\r
+       \r
+       /**\r
+        * 番組追跡のお気に入りを変更したい\r
+        */\r
+       private void setTraceKeyOkiniiri(TraceKey tk, String okini) {\r
+               \r
+               // 保存\r
+               tk.setOkiniiri(okini);\r
+               trKeys.save();\r
+               \r
+               // 検索結果の再構築\r
+               mpList.clear(env.getDisableFazzySearch(), env.getDisableFazzySearchReverse());\r
+               mpList.build(tvprograms, trKeys.getTraceKeys(), srKeys.getSearchKeys());\r
+               \r
+               // ツリーに反映する\r
+               reselectTree();\r
+       }\r
+       \r
+       /**\r
+        * 番組追跡を並べ替えたい\r
+        */\r
+       private void sortTraceKey() {\r
+               //\r
+               ArrayList<String> oList = new ArrayList<String>();\r
+               for ( TraceKey key : trKeys.getTraceKeys() ) {\r
+                       oList.add(key.getLabel());\r
+               }\r
+               \r
+               // 編集前のリストサイズ\r
+               int oCnt = oList.size();\r
+               \r
+               //\r
+               JListSortDialog lsD = new JListSortDialog("番組追跡の並べ替え", oList);\r
+               CommonSwingUtils.setLocationCenter(parent,lsD);\r
+               \r
+               lsD.setVisible(true);\r
+\r
+               if (lsD.isRegistered()) {\r
+                       TraceProgram newTrKeys = new TraceProgram();\r
+                       for ( String label : oList ) {\r
+                               for ( TraceKey key : trKeys.getTraceKeys() ) {\r
+                                       if ( key.getLabel().equals(label) ) {\r
+                                               newTrKeys.add(key);\r
+                                               break;\r
+                                       }\r
+                               }\r
+                       }\r
+                       //trKeys = newTrKeys;\r
+                       CommonUtils.FieldCopy(trKeys, newTrKeys);\r
+                       trKeys.save();\r
+                       \r
+                       if ( oList.size() < oCnt ) {\r
+                               // 削除があった場合のみ検索結果の再構築\r
+                               mpList.clear(env.getDisableFazzySearch(), env.getDisableFazzySearchReverse());\r
+                               mpList.build(tvprograms, trKeys.getTraceKeys(), srKeys.getSearchKeys());\r
+                       }\r
+                       \r
+                       // ツリーを更新\r
+                       redrawTreeByTrace();\r
+               }\r
+       }\r
+\r
+       /**\r
+        * キーワード検索を編集したい\r
+        */\r
+       private void editSearchKey(String keyword) {\r
+               //\r
+               AbsKeywordDialog kD = new VWKeywordDialog();\r
+               CommonSwingUtils.setLocationCenter(parent,kD);\r
+               \r
+               kD.reopen(keyword, srKeys);\r
+               kD.setVisible(true);\r
+\r
+               if (kD.isRegistered()) {\r
+                       // 検索結果の再構築\r
+                       mpList.clear(env.getDisableFazzySearch(), env.getDisableFazzySearchReverse());\r
+                       mpList.build(tvprograms, trKeys.getTraceKeys(), srKeys.getSearchKeys());\r
+                       //mpList.debug();\r
+                       \r
+                       // キーワードグループにも反映\r
+                       if ( ! kD.getNewLabel().equals(keyword) ) {\r
+                               if ( srGrps.rename(null, keyword, kD.getNewLabel()) ) {\r
+                                       srGrps.save();\r
+                               }\r
+                       }\r
+                       \r
+                       // srKeys.save();       // 保存はkDの中でやってるよ\r
+\r
+                       // 変更したノードを選択するようにしたい\r
+                       jLabel_tree.setView(JTreeLabel.Nodes.KEYWORD, kD.getNewLabel());\r
+                       \r
+                       // 新聞形式を修正してほしい\r
+                       updateBangumiColumns();\r
+                       \r
+                       // ツリーを更新\r
+                       redrawTreeByKeyword();\r
+                       redrawTreeByKeywordGroup();\r
+                       \r
+                       // ツリーを再選択\r
+                       reselectTree();\r
+               }\r
+       }\r
+\r
+       /**\r
+        * キーワード検索を削除したい\r
+        */\r
+       private void removeSearchKey(String keyword) {\r
+               \r
+               if (env.getShowWarnDialog()) {\r
+                       //Container cp = getContentPane();\r
+                       int ret = JOptionPane.showConfirmDialog(parent, "削除しますか?【"+keyword+"】", "確認", JOptionPane.YES_NO_OPTION);\r
+                       if (ret != JOptionPane.YES_OPTION) {\r
+                               return;\r
+                       }\r
+               }\r
+               \r
+               // 保存\r
+               srKeys.remove(keyword);\r
+               srKeys.save();\r
+\r
+               // 検索結果の再構築\r
+               mpList.clear(env.getDisableFazzySearch(), env.getDisableFazzySearchReverse());\r
+               mpList.build(tvprograms, trKeys.getTraceKeys(), srKeys.getSearchKeys());\r
+               //mpList.debug();\r
+               \r
+               // キーワードグループにも反映\r
+               if ( srGrps.remove(null,keyword) ) {\r
+                       srGrps.save();\r
+               }\r
+               \r
+               // 新聞形式を修正してほしい\r
+               updateBangumiColumns();\r
+               \r
+               // ツリーを更新\r
+               redrawTreeByKeyword();\r
+               redrawTreeByKeywordGroup();\r
+       }\r
+       \r
+       /**\r
+        * キーワード検索のお気に入りを変更したい\r
+        */\r
+       private void setSearchKeyOkiniiri(SearchKey sr, String okini) {\r
+               // 保存\r
+               sr.setOkiniiri(okini);\r
+               srKeys.save();\r
+               \r
+               // 検索結果の再構築\r
+               mpList.clear(env.getDisableFazzySearch(), env.getDisableFazzySearchReverse());\r
+               mpList.build(tvprograms, trKeys.getTraceKeys(), srKeys.getSearchKeys());\r
+               \r
+               // ツリーに反映する\r
+               reselectTree();\r
+       }\r
+       \r
+       /**\r
+        * キーワード検索を並べ替えたい\r
+        */\r
+       private void sortSearchKey() {\r
+               //\r
+               ArrayList<String> oList = new ArrayList<String>();\r
+               for ( SearchKey key : srKeys.getSearchKeys() ) {\r
+                       oList.add(key.getLabel());\r
+               }\r
+               //\r
+               JListSortDialog lsD = new JListSortDialog("キーワード検索の並べ替え", oList);\r
+               CommonSwingUtils.setLocationCenter(parent,lsD);\r
+               \r
+               lsD.setVisible(true);\r
+\r
+               if (lsD.isRegistered()) {\r
+                       SearchProgram newSrKeys = new SearchProgram();\r
+                       for ( String label : oList ) {\r
+                               for ( SearchKey key : srKeys.getSearchKeys() ) {\r
+                                       if ( key.getLabel().equals(label) ) {\r
+                                               newSrKeys.add(key);\r
+                                               break;\r
+                                       }\r
+                               }\r
+                       }\r
+                       //srKeys = newSrKeys;\r
+                       CommonUtils.FieldCopy(srKeys, newSrKeys);\r
+                       srKeys.save();\r
+                       \r
+                       // ツリーを更新\r
+                       redrawTreeByKeyword();\r
+               }\r
+       }\r
+       \r
+       /**\r
+        * キーワードグループを追加したい\r
+        */\r
+       private void addSearchKeyGroup() {\r
+               //\r
+               VWKeywordGroupDialog kD = new VWKeywordGroupDialog();\r
+               CommonSwingUtils.setLocationCenter(parent,kD);\r
+               \r
+               kD.reopen("");\r
+               kD.setVisible(true);\r
+\r
+               if (kD.isRegistered()) {\r
+                       // グループ名を変更する\r
+                       srGrps.add(kD.getNewName());\r
+                       srGrps.save();\r
+                       \r
+                       // ツリーを更新\r
+                       redrawTreeByKeywordGroup();\r
+                       \r
+                       // 変更したノードを選択するようにしたい\r
+                       jLabel_tree.setView(JTreeLabel.Nodes.KEYWORDGROUP, kD.getNewName());\r
+                       \r
+                       // ツリーを再選択\r
+                       reselectTree();\r
+               }\r
+       }\r
+       \r
+       /**\r
+        * キーワードグループを編集したい\r
+        */\r
+       private void editSeachkeyGroup(String name) {\r
+               //\r
+               VWKeywordGroupDialog kD = new VWKeywordGroupDialog();\r
+               CommonSwingUtils.setLocationCenter(parent,kD);\r
+               \r
+               kD.reopen(name);\r
+               kD.setVisible(true);\r
+\r
+               if (kD.isRegistered()) {\r
+                       // グループ名を変更する\r
+                       srGrps.rename(name, kD.getNewName());\r
+                       srGrps.save();\r
+                       \r
+                       // ツリーを更新\r
+                       redrawTreeByKeywordGroup();\r
+                       \r
+                       // 変更したノードを選択するようにしたい\r
+                       jLabel_tree.setView(JTreeLabel.Nodes.KEYWORDGROUP, kD.getNewName());\r
+                       \r
+                       // ツリーを再選択\r
+                       reselectTree();\r
+               }\r
+       }\r
+\r
+       /**\r
+        * キーワードグループを削除したい\r
+        */\r
+       private void removeSearchKeyGroup(String name) {\r
+               if (env.getShowWarnDialog()) {\r
+                       //Container cp = getContentPane();\r
+                       int ret = JOptionPane.showConfirmDialog(parent, "キーワードグループを削除しますか?【"+name+"】", "確認", JOptionPane.YES_NO_OPTION);\r
+                       if (ret != JOptionPane.YES_OPTION) {\r
+                               return;\r
+                       }\r
+               }\r
+               for ( SearchGroup gr : srGrps ) {\r
+                       if ( gr.getName().equals(name) ) {\r
+                               srGrps.remove();\r
+                               srGrps.save();\r
+                               redrawTreeByKeywordGroup();\r
+                               return;\r
+                       }\r
+               }\r
+       }\r
+       \r
+       /**\r
+        * キーワードグループのアイテムを削除したい\r
+        * @param groupName : nullならグループ登録だけでなくキーワード検索アイテム自体を削除する\r
+        */\r
+       private void removeSearchKeyGroupItem(String groupName, String keyword) {\r
+               if (env.getShowWarnDialog()) {\r
+                       //Container cp = getContentPane();\r
+                       String warn; \r
+                       if ( groupName == null ) {\r
+                               warn = "削除しますか?【"+keyword+"】 ※グループ登録の解除だけでなくアイテム自体が削除されます。";\r
+                       }\r
+                       else {\r
+                               warn = "削除しますか?【"+keyword+"】 ※グループ登録の解除のみ行います。";\r
+                       }\r
+                       int ret = JOptionPane.showConfirmDialog(parent, warn, "確認", JOptionPane.YES_NO_OPTION);\r
+                       if (ret != JOptionPane.YES_OPTION) {\r
+                               return;\r
+                       }\r
+               }\r
+               \r
+               if ( groupName == null ) {\r
+                       // nullなら全消し\r
+                       srKeys.remove(keyword);\r
+                       srKeys.save();\r
+               }\r
+\r
+               // 検索結果の再構築\r
+               mpList.clear(env.getDisableFazzySearch(), env.getDisableFazzySearchReverse());\r
+               mpList.build(tvprograms, trKeys.getTraceKeys(), srKeys.getSearchKeys());\r
+               //mpList.debug();\r
+               \r
+               // キーワードグループにも反映\r
+               if ( srGrps.remove(groupName,keyword) ) {\r
+                       srGrps.save();\r
+               }\r
+               \r
+               // ツリーを更新\r
+               if ( groupName == null ) {\r
+                       redrawTreeByKeyword();\r
+               }\r
+               // ツリーを更新\r
+               redrawTreeByKeywordGroup();\r
+       }\r
+       \r
+       /**\r
+        * キーワードグループのアイテムを追加したい\r
+        */\r
+       private void addSearchKeyGroupItem(String groupName, String keyword) {\r
+               if ( srGrps.add(groupName,keyword) ) {\r
+                       srGrps.save();\r
+                       redrawTreeByKeywordGroup();\r
+               }\r
+       }\r
+       \r
+       /**\r
+        * キーワードグループのアイテムを編集したい\r
+        */\r
+       private void editSearchKeyGroupItem(String name, String member) {\r
+               //\r
+               AbsKeywordDialog kD = new VWKeywordDialog();\r
+               CommonSwingUtils.setLocationCenter(parent,kD);\r
+               \r
+               kD.reopen(member, srKeys);\r
+               kD.setVisible(true);\r
+\r
+               if (kD.isRegistered()) {\r
+                       // 検索結果の再構築\r
+                       mpList.clear(env.getDisableFazzySearch(), env.getDisableFazzySearchReverse());\r
+                       mpList.build(tvprograms, trKeys.getTraceKeys(), srKeys.getSearchKeys());\r
+                       //mpList.debug();\r
+                       \r
+                       // キーワードグループにも反映\r
+                       if ( ! kD.getNewLabel().equals(member) ) {\r
+                               if ( srGrps.rename(null, member, kD.getNewLabel()) ) {\r
+                                       srGrps.save();\r
+                               }\r
+                       }\r
+                       \r
+                       // ツリーを更新\r
+                       redrawTreeByKeywordGroup();\r
+                       \r
+                       // 変更したノードを選択するようにしたい\r
+                       jLabel_tree.setView(JTreeLabel.Nodes.KEYWORDGROUP, name);\r
+                       \r
+                       // ツリーを再選択\r
+                       reselectTree();\r
+               }\r
+       }\r
+       \r
+       /**\r
+        * 延長警告管理を削除したい\r
+        */\r
+       private void removeExtension(String keyword) {\r
+               if (env.getShowWarnDialog()) {\r
+                       //Container cp = getContentPane();\r
+                       int ret = JOptionPane.showConfirmDialog(parent, "削除しますか?【"+keyword+"】", "確認", JOptionPane.YES_NO_OPTION);\r
+                       if (ret != JOptionPane.YES_OPTION) {\r
+                               return;\r
+                       }\r
+               }\r
+                       \r
+               extKeys.remove(keyword);\r
+               extKeys.save();\r
+               \r
+               // ツリーを更新\r
+               redrawTreeByExtension();\r
+                       \r
+               // 番組表の状態を更新する\r
+               for (TVProgram tvp : tvprograms) {\r
+                       if (tvp.getType() == ProgType.PROG) {\r
+                               tvp.setExtension(null, null, false, extKeys.getSearchKeys());\r
+                       }\r
+               }\r
+       }\r
+       \r
+       /**\r
+        * 延長警告管理を追加したい\r
+        */\r
+       private void editExtension(String keyword) {\r
+               //\r
+               AbsExtensionDialog eD = new VWExtensionDialog();\r
+               CommonSwingUtils.setLocationCenter(parent,eD);\r
+               \r
+               eD.reopen(keyword, extKeys);\r
+               eD.setVisible(true);\r
+               \r
+               if (eD.isRegistered()) {\r
+                       // 番組表の状態を更新する\r
+                       for (TVProgram tvp : tvprograms) {\r
+                               if (tvp.getType() == ProgType.PROG) {\r
+                                       tvp.setExtension(null, null, false, extKeys.getSearchKeys());\r
+                               }\r
+                       }\r
+                       \r
+                       // ツリーを更新\r
+                       redrawTreeByExtension();\r
+               }\r
+       }\r
+       \r
+       /**\r
+        * 延長警告管理を並べ替えたい\r
+        */\r
+       private void sortExtension() {\r
+               //\r
+               ArrayList<String> oList = new ArrayList<String>();\r
+               for ( SearchKey key : extKeys.getSearchKeys() ) {\r
+                       oList.add(key.getLabel());\r
+               }\r
+               //\r
+               JListSortDialog lsD = new JListSortDialog("延長警告の並べ替え", oList);\r
+               CommonSwingUtils.setLocationCenter(parent,lsD);\r
+               \r
+               lsD.setVisible(true);\r
+\r
+               if (lsD.isRegistered()) {\r
+                       ExtProgram newExtKeys = new ExtProgram();\r
+                       for ( String label : oList ) {\r
+                               for ( SearchKey key : extKeys.getSearchKeys() ) {\r
+                                       if ( key.getLabel().equals(label) ) {\r
+                                               newExtKeys.add(key);\r
+                                               break;\r
+                                       }\r
+                               }\r
+                       }\r
+                       //extKeys = newExtKeys;\r
+                       CommonUtils.FieldCopy(extKeys, newExtKeys);\r
+                       extKeys.save();\r
+                       \r
+                       // ツリーを更新\r
+                       redrawTreeByExtension();\r
+               }\r
+       }\r
+       \r
+       // キーワード削除のポップアップ\r
+       private void showPopupForRemoveTraceKey(int x, int y, final String keyword)\r
+       {\r
+               JPopupMenu pop = new JPopupMenu();\r
+               \r
+               {\r
+                       JMenuItem menuItem = new JMenuItem("番組追跡の編集【"+keyword+"】");\r
+                       menuItem.addActionListener(new ActionListener() {\r
+                               public void actionPerformed(ActionEvent e) {\r
+                                       editTraceKey(keyword);\r
+                               }\r
+                       });\r
+                       pop.add(menuItem);\r
+               }\r
+               \r
+               {\r
+                       JMenuItem menuItem = new JMenuItem("番組追跡の削除【"+keyword+"】");\r
+                       menuItem.addActionListener(new ActionListener() {\r
+                               public void actionPerformed(ActionEvent e) {\r
+                                       removeTraceKey(keyword);\r
+                               }\r
+                       });\r
+                       pop.add(menuItem);\r
+               }\r
+               \r
+               pop.addSeparator();\r
+               \r
+               {\r
+                       ButtonGroup bg = new ButtonGroup();\r
+                       \r
+                       for ( TraceKey t : trKeys.getTraceKeys()) {\r
+                               if (t.getLabel().equals(keyword)) {\r
+                                       final TraceKey tk = t;\r
+                                       for (String o : TVProgram.OKINIIRI) {\r
+                                               final String okini = o; \r
+                                               JRadioButtonMenuItem menuItem = new JRadioButtonMenuItem(okini, okini.equals(tk.getOkiniiri()));\r
+                                               bg.add(menuItem);\r
+                                               menuItem.addActionListener(new ActionListener() {\r
+                                                       public void actionPerformed(ActionEvent e) {\r
+                                                               setTraceKeyOkiniiri(tk,okini);\r
+                                                       }\r
+                                               });\r
+                                               pop.add(menuItem);\r
+                                       }\r
+                                       break;\r
+                               }\r
+                       }\r
+               }\r
+               \r
+               pop.show(jTree_tree, x, y);\r
+       }\r
+\r
+       // キーワード検索アイテムの処理\r
+       private void showPopupForRemoveKeyword(int x, int y, final String keyword)\r
+       {\r
+               JPopupMenu pop = new JPopupMenu();\r
+               \r
+               {\r
+                       JMenuItem menuItem = new JMenuItem("キーワードの編集【"+keyword+"】");\r
+                       menuItem.addActionListener(new ActionListener() {\r
+                               public void actionPerformed(ActionEvent e) {\r
+                                       editSearchKey(keyword);\r
+                               }\r
+                       });\r
+                       pop.add(menuItem);\r
+               }\r
+               {\r
+                       JMenuItem menuItem = new JMenuItem("キーワードの削除【"+keyword+"】");\r
+                       menuItem.addActionListener(new ActionListener() {\r
+                               public void actionPerformed(ActionEvent e) {\r
+                                       removeSearchKey(keyword);\r
+                               }\r
+                       });\r
+                       pop.add(menuItem);\r
+               }\r
+               \r
+               pop.addSeparator();\r
+               \r
+               {\r
+                       for ( SearchGroup gr : srGrps ) {\r
+                               final String groupName = gr.getName();\r
+                               if (srGrps.isFind(groupName,keyword) ) {\r
+                                       JMenuItem menuItem = new JMenuItem("キーワードグループから登録解除【"+groupName+"】");\r
+                                       menuItem.setForeground(Color.RED);\r
+                                       menuItem.addActionListener(new ActionListener() {\r
+                                               public void actionPerformed(ActionEvent e) {\r
+                                                       removeSearchKeyGroupItem(groupName,keyword);\r
+                                               }\r
+                                       });\r
+                                       pop.add(menuItem);\r
+                               }\r
+                               else {\r
+                                       JMenuItem menuItem = new JMenuItem("キーワードグループに追加【"+groupName+"】");\r
+                                       menuItem.addActionListener(new ActionListener() {\r
+                                               public void actionPerformed(ActionEvent e) {\r
+                                                       addSearchKeyGroupItem(groupName,keyword);\r
+                                               }\r
+                                       });\r
+                                       pop.add(menuItem);\r
+                               }\r
+                       }\r
+               }\r
+               \r
+               pop.addSeparator();\r
+               \r
+               {\r
+                       ButtonGroup bg = new ButtonGroup();\r
+                       \r
+                       for ( SearchKey s : srKeys.getSearchKeys()) {\r
+                               if (s.getLabel().equals(keyword)) {\r
+                                       final SearchKey sr = s;\r
+                                       for (String o : TVProgram.OKINIIRI) {\r
+                                               final String okini = o; \r
+                                               JRadioButtonMenuItem menuItem = new JRadioButtonMenuItem(okini, okini.equals(sr.getOkiniiri()));\r
+                                               bg.add(menuItem);\r
+                                               menuItem.addActionListener(new ActionListener() {\r
+                                                       public void actionPerformed(ActionEvent e) {\r
+                                                               setSearchKeyOkiniiri(sr,okini);\r
+                                                       }\r
+                                               });\r
+                                               pop.add(menuItem);\r
+                                       }\r
+                                       break;\r
+                               }\r
+                       }\r
+               }\r
+               pop.show(jTree_tree, x, y);\r
+       }\r
+\r
+       // 番組追跡の並べ替え\r
+       private void showPopupForSortTraceKey(int x, int y) {\r
+               JPopupMenu pop = new JPopupMenu();\r
+               {\r
+                       JMenuItem menuItem = new JMenuItem("番組追跡の並べ替え");\r
+                       menuItem.addActionListener(new ActionListener() {\r
+                               public void actionPerformed(ActionEvent e) {\r
+                                       sortTraceKey();\r
+                               }\r
+                       });\r
+                       pop.add(menuItem);\r
+               }\r
+               pop.show(jTree_tree, x, y);\r
+       }\r
+\r
+       // キーワード検索の並べ替え\r
+       private void showPopupForSortSearchKey(int x, int y) {\r
+               JPopupMenu pop = new JPopupMenu();\r
+               {\r
+                       JMenuItem menuItem = new JMenuItem("キーワード検索の並べ替え");\r
+                       menuItem.addActionListener(new ActionListener() {\r
+                               public void actionPerformed(ActionEvent e) {\r
+                                       sortSearchKey();\r
+                               }\r
+                       });\r
+                       pop.add(menuItem);\r
+               }\r
+               pop.show(jTree_tree, x, y);\r
+       }\r
+\r
+       // 延長警告の並べ替え\r
+       private void showPopupForSortExtension(int x, int y) {\r
+               JPopupMenu pop = new JPopupMenu();\r
+               {\r
+                       JMenuItem menuItem = new JMenuItem("延長警告の並べ替え");\r
+                       menuItem.addActionListener(new ActionListener() {\r
+                               public void actionPerformed(ActionEvent e) {\r
+                                       sortExtension();\r
+                               }\r
+                       });\r
+                       pop.add(menuItem);\r
+               }\r
+               pop.show(jTree_tree, x, y);\r
+       }\r
+       \r
+       // キーワードグループの処理\r
+       private void showPopupForRemoveKeywordGrp(int x, int y)\r
+       {\r
+               JPopupMenu pop = new JPopupMenu();\r
+               \r
+               {\r
+                       JMenuItem menuItem = new JMenuItem("キーワードグループの追加");\r
+                       menuItem.addActionListener(new ActionListener() {\r
+                               public void actionPerformed(ActionEvent e) {\r
+                                       addSearchKeyGroup();\r
+                               }\r
+                       });\r
+                       pop.add(menuItem);\r
+               }\r
+               pop.show(jTree_tree, x, y);\r
+       }\r
+       \r
+       private void showPopupForRemoveKeywordGrpName(int x, int y, final String name)\r
+       {\r
+               JPopupMenu pop = new JPopupMenu();\r
+               \r
+               {\r
+                       JMenuItem menuItem = new JMenuItem("キーワードグループの編集【"+name+"】");\r
+                       menuItem.addActionListener(new ActionListener() {\r
+                               public void actionPerformed(ActionEvent e) {\r
+                                       editSeachkeyGroup(name);\r
+                               }\r
+                       });\r
+                       pop.add(menuItem);\r
+               }\r
+               \r
+               pop.addSeparator();\r
+               \r
+               {\r
+                       JMenuItem menuItem = new JMenuItem("キーワードグループの削除【"+name+"】");\r
+                       menuItem.addActionListener(new ActionListener() {\r
+                               public void actionPerformed(ActionEvent e) {\r
+                                       removeSearchKeyGroup(name);\r
+                               }\r
+                       });\r
+                       pop.add(menuItem);\r
+               }\r
+               pop.show(jTree_tree, x, y);\r
+       }\r
+       \r
+       private void showPopupForRemoveKeywordGrpEntry(int x, int y, final String name, final String member)\r
+       {\r
+               JPopupMenu pop = new JPopupMenu();\r
+               \r
+               {\r
+                       JMenuItem menuItem = new JMenuItem("キーワードの編集【"+member+"】");\r
+                       menuItem.addActionListener(new ActionListener() {\r
+                               public void actionPerformed(ActionEvent e) {\r
+                                       editSearchKeyGroupItem(name,member);\r
+                               }\r
+                       });\r
+                       pop.add(menuItem);\r
+               }\r
+               {\r
+                       JMenuItem menuItem = new JMenuItem("キーワードの削除【"+member+"】");\r
+                       menuItem.addActionListener(new ActionListener() {\r
+                               public void actionPerformed(ActionEvent e) {\r
+                                       removeSearchKeyGroupItem(null,member);\r
+                               }\r
+                       });\r
+                       pop.add(menuItem);\r
+               }\r
+               \r
+               pop.addSeparator();\r
+               \r
+               {\r
+                       for ( SearchGroup gr : srGrps ) {\r
+                               final String groupName = gr.getName();\r
+                               if (srGrps.isFind(groupName,member) ) {\r
+                                       JMenuItem menuItem = new JMenuItem("キーワードグループから登録解除【"+groupName+"】");\r
+                                       menuItem.setForeground(Color.RED);\r
+                                       menuItem.addActionListener(new ActionListener() {\r
+                                               public void actionPerformed(ActionEvent e) {\r
+                                                       removeSearchKeyGroupItem(groupName, member);\r
+                                               }\r
+                                       });\r
+                                       pop.add(menuItem);\r
+                               }\r
+                               else {\r
+                                       JMenuItem menuItem = new JMenuItem("キーワードグループに追加【"+groupName+"】");\r
+                                       menuItem.addActionListener(new ActionListener() {\r
+                                               public void actionPerformed(ActionEvent e) {\r
+                                                       addSearchKeyGroupItem(groupName,member);\r
+                                               }\r
+                                       });\r
+                                       pop.add(menuItem);\r
+                               }\r
+                       }\r
+               }\r
+               \r
+               pop.show(jTree_tree, x, y);\r
+       }\r
+\r
+       // 延長警告アイテムの処理\r
+       private void showPopupForRemoveExtension(int x, int y, final String keyword)\r
+       {\r
+               JPopupMenu pop = new JPopupMenu();\r
+               \r
+               {\r
+                       JMenuItem menuItem = new JMenuItem("延長警告の編集【"+keyword+"】");\r
+                       menuItem.addActionListener(new ActionListener() {\r
+                               public void actionPerformed(ActionEvent e) {\r
+                                       editExtension(keyword);\r
+                               }\r
+                       });\r
+                       pop.add(menuItem);\r
+               }\r
+               {\r
+                       JMenuItem menuItem = new JMenuItem("延長警告の削除【"+keyword+"】");\r
+                       menuItem.addActionListener(new ActionListener() {\r
+                               public void actionPerformed(ActionEvent e) {\r
+                                       removeExtension(keyword);\r
+                               }\r
+                       });\r
+                       pop.add(menuItem);\r
+               }\r
+               \r
+               pop.show(jTree_tree, x, y);\r
+       }\r
+       \r
+       \r
+       \r
+       /*******************************************************************************\r
+        * コンポーネント\r
+        ******************************************************************************/\r
+       \r
+       /*\r
+        * 特殊部品\r
+        */\r
+       \r
+       /**\r
+        * キーワード検索ウィンドウの内部クラス\r
+        */\r
+       private class VWKeywordDialog extends AbsKeywordDialog {\r
+               \r
+               private static final long serialVersionUID = 1L;\r
+\r
+               @Override\r
+               void preview(SearchKey search) {\r
+                       previewKeywordSearch(search);\r
+               }\r
+       }\r
+\r
+       /**\r
+        * 延長警告管理ウィンドウの内部クラス\r
+        */\r
+       private class VWExtensionDialog extends AbsExtensionDialog {\r
+\r
+               private static final long serialVersionUID = 1L;\r
+\r
+               @Override\r
+               void preview(SearchKey search) {\r
+                       previewKeywordSearch(search);\r
+               }\r
+       }\r
+       \r
+       /*\r
+        * 部品\r
+        */\r
+       \r
+       private JSplitPane getJSplitPane_view() {\r
+               if ( jSplitPane_view == null ) {\r
+                       jSplitPane_view = new JSplitPane() {\r
+\r
+                               private static final long serialVersionUID = 1L;\r
+\r
+                               @Override\r
+                               public void setDividerLocation(int loc) {\r
+                                       setDividerEnvs(loc);\r
+                                       super.setDividerLocation(loc);\r
+                               }\r
+                       };\r
+                       \r
+                       jSplitPane_view.setLeftComponent(getJPanel_tree());\r
+                       jSplitPane_view.setRightComponent(getJScrollPane_listed());\r
+                       setExpandTree();\r
+               }\r
+               return jSplitPane_view;\r
+       }\r
+       \r
+       private JPanel getJPanel_tree() {\r
+               if (jPanel_tree == null) {\r
+                       jPanel_tree = new JPanel();\r
+                       \r
+                       jPanel_tree.setLayout(new BorderLayout());\r
+                       jPanel_tree.add(getJScrollPane_tree_top(), BorderLayout.PAGE_START);\r
+                       jPanel_tree.add(getJScrollPane_tree(), BorderLayout.CENTER);\r
+               }\r
+               return jPanel_tree;\r
+       }\r
+       \r
+       private JScrollPane getJScrollPane_tree_top() {\r
+               if (jScrollPane_tree_top == null) {\r
+                       jScrollPane_tree_top = new JScrollPane();\r
+                       jScrollPane_tree_top.setViewportView(getJLabel_tree());\r
+                       jScrollPane_tree_top.setVerticalScrollBarPolicy(JScrollPane.VERTICAL_SCROLLBAR_NEVER);\r
+                       jScrollPane_tree_top.setHorizontalScrollBarPolicy(JScrollPane.HORIZONTAL_SCROLLBAR_NEVER);\r
+               }\r
+               return jScrollPane_tree_top;\r
+       }\r
+       \r
+       private JTreeLabel getJLabel_tree() {\r
+               if (jLabel_tree == null) {\r
+                       jLabel_tree = new JTreeLabel();\r
+                       \r
+                       Dimension d = jLabel_tree.getMaximumSize();\r
+                       d.height = bounds.getBangumiColumnHeight();\r
+                       jLabel_tree.setPreferredSize(d);\r
+                       //jLabel_tree.setBorder(new LineBorder(Color.BLACK));\r
+                       jLabel_tree.setOpaque(true);\r
+                       jLabel_tree.setBackground(Color.WHITE);\r
+               }\r
+               return jLabel_tree;\r
+       }\r
+       \r
+       private JScrollPane getJScrollPane_tree() {\r
+               if (jScrollPane_tree == null) {\r
+                       jScrollPane_tree = new JScrollPane();\r
+\r
+                       jScrollPane_tree.setViewportView(getJTree_tree());\r
+               }\r
+               return jScrollPane_tree;\r
+       }\r
+       \r
+       private JDetailPanel getJTextPane_detail() {\r
+               if (jTextPane_detail == null) {\r
+                       jTextPane_detail = new JDetailPanel();\r
+                       jTextPane_detail.setRows(bounds.getDetailRows());\r
+                       //Dimension d = jTextPane_detail.getMaximumSize();\r
+                       //d.height = bounds.getDetailAreaHeight();\r
+                       //jTextPane_detail.setPreferredSize(d);\r
+                       //jTextPane_detail.setVerticalAlignment(JLabel.TOP);\r
+                       //jTextPane_detail.setHorizontalAlignment(JLabel.LEFT);\r
+               }\r
+               return jTextPane_detail;\r
+       }\r
+       \r
+       /**\r
+        * ツリーの作成\r
+        */\r
+       private JTree getJTree_tree() {\r
+               if (jTree_tree == null) {\r
+                       \r
+                       // ツリーの作成\r
+                       jTree_tree = new JTree();\r
+                       jTree_tree.getSelectionModel().setSelectionMode(TreeSelectionModel.SINGLE_TREE_SELECTION);\r
+                       jTree_tree.setRootVisible(env.getRootNodeVisible());\r
+                       jTree_tree.setCellRenderer(new VWTreeCellRenderer());   // 検索結果が存在するノードの色を変える\r
+                       \r
+                       // ノードの作成\r
+                       jTree_tree.setModel(new DefaultTreeModel(getTreeNodes()));\r
+                       \r
+                       // ツリーの展開状態の復帰\r
+                       undoTreeExpansion();\r
+                       \r
+                       // ツリーの開閉時に状態を保存する\r
+                       jTree_tree.addTreeExpansionListener(tel_nodeexpansion);\r
+                       \r
+                       // フルスクリーンの時に使う(新聞形式のツリーを自動的に隠す)\r
+                       jTree_tree.addMouseListener(ml_treehide);\r
+               }\r
+               return jTree_tree;\r
+       }\r
+       \r
+       /**\r
+        * ツリーのノード作成\r
+        */\r
+       private DefaultMutableTreeNode getTreeNodes() {\r
+               \r
+               listRootNode = new VWListedTreeNode(JTreeLabel.Nodes.ROOT.getLabel());\r
+\r
+               searchedNode    = new VWListedTreeNode(JTreeLabel.Nodes.SEARCHHIST.getLabel());\r
+               startNode               = new VWListedTreeNode(JTreeLabel.Nodes.START.getLabel());\r
+               endNode                 = new VWListedTreeNode(JTreeLabel.Nodes.END.getLabel());\r
+               nowNode                 = new VWListedTreeNode(JTreeLabel.Nodes.NOW.getLabel());\r
+               syobocalNode    = new VWListedTreeNode(JTreeLabel.Nodes.SYOBOCAL.getLabel());\r
+               standbyNode             = new VWListedTreeNode(JTreeLabel.Nodes.STANDBY.getLabel());\r
+               traceNode               = new VWListedTreeNode(JTreeLabel.Nodes.TRACE.getLabel());\r
+               keywordNode             = new VWListedTreeNode(JTreeLabel.Nodes.KEYWORD.getLabel());\r
+               keywordGrpNode  = new VWListedTreeNode(JTreeLabel.Nodes.KEYWORDGROUP.getLabel());\r
+               genreNode               = new VWListedTreeNode(JTreeLabel.Nodes.GENRE.getLabel());\r
+               centerListNode  = new VWListedTreeNode(JTreeLabel.Nodes.BCASTLIST.getLabel());\r
+               extensionNode   = new VWListedTreeNode(JTreeLabel.Nodes.EXTENTION.getLabel());\r
+\r
+               // ★★★ でふぉるとのーど ★★★\r
+               defaultNode = nowNode;\r
+\r
+               listRootNode.add(searchedNode);\r
+               listRootNode.add(startNode);\r
+               listRootNode.add(endNode);\r
+               listRootNode.add(nowNode);\r
+               if ( env.getUseSyobocal() ) {\r
+                       listRootNode.add(syobocalNode);\r
+               }\r
+               listRootNode.add(standbyNode);\r
+               listRootNode.add(traceNode);\r
+               listRootNode.add(keywordNode);\r
+               listRootNode.add(keywordGrpNode);\r
+               listRootNode.add(genreNode);\r
+               listRootNode.add(centerListNode);\r
+               listRootNode.add(extensionNode);\r
+               \r
+               // 子の描画\r
+               redrawTreeByGenre();\r
+               redrawTreeBySyobo();\r
+               redrawTreeByStandby();\r
+               redrawTreeByTrace();\r
+               redrawTreeByKeyword();\r
+               redrawTreeByKeywordGroup();\r
+               redrawTreeBySubGenre();\r
+               redrawTreeByCenter();\r
+               redrawTreeByExtension();\r
+               \r
+               return listRootNode;\r
+       }\r
+\r
+       private void undoTreeExpansion() {\r
+               \r
+               // 展開状態の復帰\r
+               stopTreeListener();\r
+               \r
+               // ツリーの展開状態の保存場所\r
+               ter = new TreeExpansionReg(jTree_tree, TreeExpRegFile_Listed);\r
+               try {\r
+                       ter.load();\r
+               }\r
+               catch (Exception e) {\r
+                       MWin.appendMessage(ERRID+"ツリー展開情報の解析で問題が発生しました");\r
+                       e.printStackTrace();\r
+               }\r
+               \r
+               // 状態を復元する\r
+               ArrayList<TreePath> tpa = ter.get();\r
+               for ( TreePath path : tpa ) {\r
+                       jTree_tree.expandPath(path);\r
+               }\r
+               \r
+               startTreeListener();\r
+       }\r
+       \r
+       private JScrollPane getJScrollPane_listed() {\r
+               if (jScrollPane_listed == null) {\r
+                       jScrollPane_listed = new JScrollPane();\r
+                       jScrollPane_listed.setRowHeaderView(jTable_rowheader = new JTableRowHeader(rowData));\r
+                       jScrollPane_listed.setViewportView(getNETable_listed());\r
+                       \r
+                       Dimension d = new Dimension(jTable_rowheader.getPreferredSize().width,0);\r
+                       jScrollPane_listed.getRowHeader().setPreferredSize(d);\r
+                       \r
+                       setRowHeaderVisible(env.getRowHeaderVisible());\r
+               }\r
+               return jScrollPane_listed;\r
+       }\r
+       \r
+       private JNETable getNETable_listed() {\r
+               if (jTable_listed == null) {\r
+                       \r
+                       // カラム名の初期化\r
+                       ArrayList<String> cola = new ArrayList<String>();\r
+                       for ( ListedColumn lc : ListedColumn.values() ) {\r
+                               if ( lc.getIniWidth() >= 0 ) {\r
+                                       cola.add(lc.getName());\r
+                               }\r
+                       }\r
+                       final String[] colname = cola.toArray(new String[0]);\r
+\r
+                       // テーブルの基本的な設定\r
+                       tableModel_listed = new ListedTableModel(colname, 0);\r
+                       \r
+                       jTable_listed = new ListedTable(tableModel_listed, true);\r
+                       jTable_listed.setAutoResizeMode(JNETable.AUTO_RESIZE_OFF);\r
+                       //jTable_listed.setSelectionMode(ListSelectionModel.SINGLE_SELECTION);\r
+\r
+                       // ヘッダのモデル\r
+                       rowheaderModel_listed = (DefaultTableModel) jTable_rowheader.getModel();\r
+               \r
+                       // ソーターをつける\r
+                       final TableRowSorter<TableModel> sorter = new TableRowSorter<TableModel>(tableModel_listed);\r
+                       jTable_listed.setRowSorter(sorter);\r
+                       //sorter.toggleSortOrder(listedTableColumn_Sorter);\r
+                       \r
+                       sorter.addRowSorterListener(new RowSorterListener() {\r
+                               @Override\r
+                               public void sorterChanged(RowSorterEvent e) {\r
+                                       if ( e.getType() == Type.SORTED ) {\r
+                                               if (rowData.size()>2) setOverlapMark();\r
+                                       }\r
+                               }\r
+                       });\r
+                       \r
+                       // 数値でソートする項目用の計算式(番組長とか)\r
+                       final Comparator<String> numcomp = new Comparator<String>() {\r
+                               @Override\r
+                               public int compare(String o1, String o2) {\r
+                                       int n1 = -1;\r
+                                       int n2 = -1;\r
+                                       if ( o1 != null ) {\r
+                                               Matcher ma = Pattern.compile("^(\\d+)").matcher(o1);\r
+                                               if ( ma.find() ) {\r
+                                                       n1 = Integer.valueOf(ma.group(1));\r
+                                               }\r
+                                       }\r
+                                       if ( o2 != null ) {\r
+                                               Matcher ma = Pattern.compile("^(\\d+)").matcher(o2);\r
+                                               if ( ma.find() ) {\r
+                                                       n2 = Integer.valueOf(ma.group(1));\r
+                                               }\r
+                                       }\r
+                                       return n1-n2;\r
+                               }\r
+                       };\r
+                       \r
+                       // ソーターの効かない項目用の計算式(重複マーク)\r
+                       final Comparator<String> noncomp = new Comparator<String>() {\r
+                               @Override\r
+                               public int compare(String o1, String o2) {\r
+                                       return 0;\r
+                               }\r
+                       };\r
+                       \r
+                       // 数値比較を行う列\r
+                       sorter.setComparator(jTable_listed.getColumn(ListedColumn.LENGTH.getName()).getModelIndex(),numcomp);\r
+                       sorter.setComparator(jTable_listed.getColumn(ListedColumn.SCORE.getName()).getModelIndex(),numcomp);\r
+                       sorter.setComparator(jTable_listed.getColumn(ListedColumn.THRESHOLD.getName()).getModelIndex(),numcomp);\r
+\r
+                       sorter.setComparator(jTable_listed.getColumn(ListedColumn.DUPMARK.getName()).getModelIndex(),noncomp);\r
+\r
+                       // 予約済みマーク/重複マークはちょっとだけ表示の仕方が違う\r
+                       VWColorCharCellRenderer renderer = new VWColorCharCellRenderer();\r
+                       if ( CommonUtils.isMac() ) renderer.setMacMarkFont();\r
+                       jTable_listed.getColumn(ListedColumn.RSVMARK.getName()).setCellRenderer(renderer);\r
+                       jTable_listed.getColumn(ListedColumn.DUPMARK.getName()).setCellRenderer(renderer);\r
+                       \r
+                       // 強調色関連\r
+                       titleCellRenderer = new VWColorCharCellRenderer2();\r
+                       jTable_listed.getColumn(ListedColumn.OPTIONS.getName()).setCellRenderer(titleCellRenderer);\r
+                       jTable_listed.getColumn(ListedColumn.TITLE.getName()).setCellRenderer(titleCellRenderer);\r
+                       \r
+                       this.setMatchedKeywordColor(env.getMatchedKeywordColor());\r
+                       this.setRsvdLineColor((env.getRsvdLineEnhance())?(env.getRsvdLineColor()):(null));\r
+                       this.setPickedLineColor((env.getRsvdLineEnhance())?(env.getPickedLineColor()):(null));\r
+                       this.setCurrentLineColor((env.getCurrentLineEnhance())?(env.getCurrentLineColor()):(null));\r
+\r
+                       // スコア・閾値はちょっとだけ表示の仕方が違う\r
+                       DefaultTableCellRenderer renderer3 = new DefaultTableCellRenderer();\r
+                       renderer3.setHorizontalAlignment(SwingConstants.RIGHT);\r
+                       jTable_listed.getColumn(ListedColumn.SCORE.getName()).setCellRenderer(renderer3);\r
+                       jTable_listed.getColumn(ListedColumn.THRESHOLD.getName()).setCellRenderer(renderer3);\r
+                       \r
+                       // 詳細表示はうんぬんかんぬん\r
+                       VWDetailCellRenderer renderer4 = new VWDetailCellRenderer();\r
+                       jTable_listed.getColumn(ListedColumn.DETAIL.getName()).setCellRenderer(renderer4);\r
+\r
+                       // 各カラムの幅を設定する\r
+                       DefaultTableColumnModel columnModel = (DefaultTableColumnModel)jTable_listed.getColumnModel();\r
+                       TableColumn column = null;\r
+                       for ( ListedColumn lc : ListedColumn.values() ) {\r
+                               if ( lc.getIniWidth() < 0 ) {\r
+                                       continue;\r
+                               }\r
+                               column = columnModel.getColumn(lc.ordinal());\r
+                               column.setPreferredWidth(bounds.getListedColumnSize().get(lc.toString()));      // toString()!\r
+                       }\r
+                       \r
+                       // マーク表示分離の有無\r
+                       setMarkColumnVisible(env.getSplitMarkAndTitle());\r
+                       \r
+                       // 番組詳細の表示・非表示\r
+                       setDetailColumnVisible(env.getShowDetailOnList());\r
+                       \r
+                       // 行を選択すると詳細が表示されるようにする\r
+                       jTable_listed.getSelectionModel().addListSelectionListener(lsSelectListner);\r
+                       \r
+                       // マウスクリックでメニュー表示\r
+                       jTable_listed.addMouseListener(lsClickAdapter);\r
+               }\r
+               return jTable_listed;\r
+       }\r
+       \r
+       \r
+       \r
+       \r
+       /*******************************************************************************\r
+        * 独自部品\r
+        ******************************************************************************/\r
+       \r
+       /**\r
+        *  テーブルの行データの構造\r
+        * @see ListedColumn\r
+        */\r
+       private class ListedItem extends RowItem implements Cloneable {\r
+               String rsvmark;\r
+               String dupmark;\r
+               String prefix;\r
+               String title;\r
+               String searchlabel;\r
+               String okiniiri;\r
+               String score;\r
+               String threshold;\r
+               \r
+               String hide_rsvmarkcolor;\r
+\r
+               ProgDetailList tvd;\r
+\r
+               @Override\r
+               protected void myrefresh(RowItem o) {\r
+                       ListedItem c = (ListedItem) o;\r
+                       c.addData(rsvmark);\r
+                       c.addData(dupmark);\r
+                       c.addData(tvd.center);\r
+                       c.addData(prefix);\r
+                       c.addData(title);               // "\0"+title or "\0"+titlebefore+"\0"+matchedkeyword+"\0"+titleafter みたいな感じで\r
+                       c.addData(tvd.detail);\r
+                       c.addData(tvd.start);\r
+                       c.addData(tvd.end);\r
+                       c.addData(tvd.recmin);\r
+                       c.addData(tvd.genre.toString());\r
+                       c.addData(searchlabel);\r
+                       c.addData(okiniiri);\r
+                       c.addData(score);\r
+                       c.addData(threshold);\r
+               }\r
+               \r
+               public ListedItem clone() {\r
+                       return (ListedItem) super.clone();\r
+               }\r
+       }\r
+       \r
+       /**\r
+        * {@link ListedItem}を使ったJTable拡張\r
+        */\r
+       private class ListedTable extends JNETable {\r
+\r
+               private static final long serialVersionUID = 1L;\r
+\r
+               private Color passedColor = new Color(180,180,180);\r
+               \r
+               // futuer use.\r
+               public void setPassedColor(Color c) { passedColor = c; }\r
+\r
+               private Color currentColorEven = new Color(240,120,120);\r
+               private Color currentColorOdd = new Color(248,128,128);\r
+\r
+               public void setCurrentColor(Color c) {\r
+                       if ( c == null ) {\r
+                               currentColorEven = null;\r
+                               currentColorOdd = null;\r
+                       }\r
+                       else {\r
+                               currentColorOdd = c;\r
+                               currentColorEven = new Color(\r
+                                               ((c.getRed()>=247)?(255):(c.getRed()+8)),\r
+                                               ((c.getGreen()>=247)?(255):(c.getGreen()+8)),\r
+                                               ((c.getBlue()>=247)?(255):(c.getBlue()+8))\r
+                                               );\r
+                       }\r
+               }\r
+\r
+               private Color nextweekFgColor = new Color(120,120,120);\r
+               \r
+               // futuer use.\r
+               public void setNextweekFgColor(Color c) { nextweekFgColor = c; }\r
+\r
+               private Color reservedColorEven = new Color(255,247,204);\r
+               private Color reservedColorOdd = new Color(255,255,212);\r
+\r
+               public void setReservedColor(Color c) {\r
+                       if ( c == null ) {\r
+                               reservedColorEven = null;\r
+                               reservedColorOdd = null;\r
+                       }\r
+                       else {\r
+                               reservedColorOdd = c;\r
+                               reservedColorEven = new Color(\r
+                                               ((c.getRed()>=247)?(255):(c.getRed()+8)),\r
+                                               ((c.getGreen()>=247)?(255):(c.getGreen()+8)),\r
+                                               ((c.getBlue()>=247)?(255):(c.getBlue()+8))\r
+                                               );\r
+                       }\r
+               }\r
+               \r
+               private Color pickedColorEven = new Color(51,255,0);\r
+               private Color pickedColorOdd = new Color(59,255,8);\r
+\r
+               public void setPickedColor(Color c) {\r
+                       if ( c == null ) {\r
+                               pickedColorEven = null;\r
+                               pickedColorOdd = null;\r
+                       }\r
+                       else {\r
+                               pickedColorEven = c;\r
+                               pickedColorOdd = new Color(\r
+                                               ((c.getRed()>=247)?(255):(c.getRed()+8)),\r
+                                               ((c.getGreen()>=247)?(255):(c.getGreen()+8)),\r
+                                               ((c.getBlue()>=247)?(255):(c.getBlue()+8))\r
+                                               );\r
+                       }\r
+               }\r
+               \r
+               private int prechkrow = -1;\r
+               private boolean prechkreserved = false;\r
+               private boolean prechkpicked = false;\r
+               private boolean prechkpassed = false;\r
+               private boolean prechkcurrent = false;\r
+               private boolean prechknextweek = false;\r
+               \r
+               @Override\r
+               public Component prepareRenderer(TableCellRenderer tcr, int row, int column) {\r
+                       Component comp = super.prepareRenderer(tcr, row, column);\r
+                       Color fgColor = null;\r
+                       Color bgColor = null;\r
+                       if(isRowSelected(row)) {\r
+                               fgColor = this.getSelectionForeground();\r
+                               bgColor = this.getSelectionBackground();\r
+                       }\r
+                       else {\r
+                               isRowPassed(row);\r
+                               \r
+                               fgColor = (prechknextweek)?(nextweekFgColor):(this.getForeground());\r
+                               \r
+                               if( prechkpassed && passedColor != null ) {\r
+                                       bgColor = passedColor;\r
+                               }\r
+                               else if( prechkreserved && reservedColorEven != null ) {\r
+                                       bgColor = (isSepRowColor && row%2 == 1)?(reservedColorEven):(reservedColorOdd);\r
+                               }\r
+                               else if( prechkpicked && pickedColorEven != null ) {\r
+                                       bgColor = (isSepRowColor && row%2 == 1)?(pickedColorEven):(pickedColorOdd);\r
+                               }\r
+                               else if( prechkcurrent && currentColorEven != null ) {\r
+                                       bgColor = (isSepRowColor && row%2 == 1)?(currentColorEven):(currentColorOdd);\r
+                               }\r
+                               else {\r
+                                       bgColor = (isSepRowColor && row%2 == 1)?(evenColor):(super.getBackground());\r
+                               }\r
+                       }\r
+                       if ( tcr instanceof VWColorCharCellRenderer2 ) {\r
+                               ((VWColorCharCellRenderer2) tcr).setForeground(fgColor);\r
+                       }\r
+                       else if ( ! (tcr instanceof VWColorCharCellRenderer) && ! (tcr instanceof VWColorCellRenderer)) {\r
+                               // マーク類は除外\r
+                               comp.setForeground(fgColor);\r
+                       }               \r
+                       if ( ! (tcr instanceof VWColorCellRenderer)) {\r
+                               comp.setBackground(bgColor);\r
+                       }\r
+                       return comp;\r
+               }\r
+               \r
+               // 直接rowDataを見に行くようになったから、このisRowPassed()はもういらないんじゃ…\r
+               \r
+               // 連続して同じ行へのアクセスがあったら計算を行わず前回のままにする\r
+               private boolean isRowPassed(int prow) {\r
+                       \r
+                       if(prechkrow == prow) {\r
+                               return true;\r
+                       }\r
+\r
+                       int row = this.convertRowIndexToModel(prow);\r
+                       ListedItem c = rowData.get(row);\r
+\r
+                       prechkrow = prow;\r
+                       \r
+                       prechkreserved  = false;\r
+                       prechkpicked = false;\r
+                       prechkpassed = false;\r
+                       prechkcurrent = false;\r
+                       prechknextweek = false;\r
+                       \r
+                       {\r
+                               // 予約が入っているか否か\r
+                               if ( c.rsvmark == null || c.rsvmark.length() == 0 ) {\r
+                                       //\r
+                               }\r
+                               else if ( c.rsvmark.equals(RSVMARK_NOEXEC)  ) {\r
+                                       //\r
+                               }\r
+                               else if ( c.rsvmark.equals(RSVMARK_PICKUP) ) {\r
+                                       prechkpicked = true;\r
+                               }\r
+                               else {\r
+                                       prechkreserved = true;\r
+                               }\r
+                       }\r
+                       {\r
+                               // 終了済みの番組か否か\r
+                               String cDT = CommonUtils.getDateTime(0);\r
+                               prechkpassed = (cDT.compareTo(c.tvd.endDateTime) >= 0);\r
+                               if ( ! prechkpassed ) {\r
+                                       // 現在放送中\r
+                                       prechkcurrent = (cDT.compareTo(c.tvd.startDateTime) >= 0);\r
+                               }\r
+                               if ( ! prechkcurrent ) {\r
+                                       // 来週かな\r
+                                       String critDT = CommonUtils.getCritDateTime(7);\r
+                                       prechknextweek = (critDT.compareTo(c.tvd.startDateTime) <= 0);\r
+                               }\r
+                       }\r
+                       \r
+                       return true;\r
+               }\r
+               \r
+               //\r
+               @Override\r
+               public void tableChanged(TableModelEvent e) {\r
+                       reset();\r
+                       super.tableChanged(e);\r
+               }\r
+               \r
+               private void reset() {\r
+                       prechkrow = -1;\r
+                       prechkreserved = false;\r
+                       prechkpicked = false;\r
+                       prechkpassed = false;\r
+               }\r
+               \r
+               /*\r
+                * コンストラクタ\r
+                */\r
+               public ListedTable(boolean b) {\r
+                       super(b);\r
+                       reset();\r
+               }\r
+               public ListedTable(TableModel d, boolean b) {\r
+                       super(d,b);\r
+                       reset();\r
+               }\r
+       }\r
+       \r
+       /**\r
+        *  ソートが必要な場合はTableModelを作る。ただし、その場合Viewのrowがわからないので行の入れ替えが行えない\r
+        * @see ListedTable\r
+        */\r
+       private class ListedTableModel extends DefaultTableModel {\r
+               \r
+               private static final long serialVersionUID = 1L;\r
+\r
+               @Override\r
+               public Object getValueAt(int row, int column) {\r
+                       // 多少負荷があがるがこっちの方が見通しがいいだろう\r
+                       ListedItem c = rowData.get(row);\r
+                       if ( c.size()>column ) {\r
+                               // 特殊なカラム\r
+                               if ( column == ListedColumn.RSVMARK.getColumn() ) {\r
+                                       if ( c.rsvmark.length() > 0 ) {\r
+                                               return c.rsvmark+"\0"+c.hide_rsvmarkcolor;\r
+                                       }\r
+                                       else {\r
+                                               return "";\r
+                                       }\r
+                               }\r
+                               else if ( column == ListedColumn.DUPMARK.getColumn() ) {\r
+                                       return c.dupmark+"\0"+DUPMARK_COLOR;\r
+                               }\r
+                               else if ( column == ListedColumn.START.getColumn() ) {\r
+                                       return c.tvd.accurateDate+" "+c.tvd.start;\r
+                               }\r
+                               else if ( column == ListedColumn.LENGTH.getColumn() ) {\r
+                                       return c.tvd.recmin+"m";\r
+                               }\r
+                               else if ( column == ListedColumn.GENRE.getColumn() ) {\r
+                                       if ( c.tvd.subgenre != null ) {\r
+                                               return c.tvd.genre.toString()+" - "+c.tvd.subgenre.toString();\r
+                                       }\r
+                                       else {\r
+                                               // サブジャンルに非対応な番組表の場合\r
+                                               return c.tvd.genre.toString();\r
+                                       }\r
+                               }\r
+                               else if ( column == ListedColumn.OPTIONS.getColumn() && ! env.getSplitMarkAndTitle() ) {\r
+                                       // オプション分離がOFFです\r
+                                       return "";\r
+                               }\r
+                               else if ( column == ListedColumn.TITLE.getColumn() && ! env.getSplitMarkAndTitle() ) {\r
+                                       // オプション分離がOFFです\r
+                                       return c.prefix+c.title;\r
+                               }\r
+                               return c.get(column);\r
+                       }\r
+                       return null;\r
+               }\r
+               \r
+               @Override\r
+               public int getRowCount() {\r
+                       return rowData.size();\r
+               }\r
+\r
+               public ListedTableModel(String[] colname, int i) {\r
+                       super(colname,i);\r
+               }\r
+               \r
+       }\r
+\r
+}\r
diff --git a/TinyBannavi/src/tainavi/AbsPaperColorsDialog.java b/TinyBannavi/src/tainavi/AbsPaperColorsDialog.java
new file mode 100644 (file)
index 0000000..dea440f
--- /dev/null
@@ -0,0 +1,841 @@
+package tainavi;\r
+\r
+import java.awt.BorderLayout;\r
+import java.awt.Color;\r
+import java.awt.Component;\r
+import java.awt.Dimension;\r
+import java.awt.Point;\r
+import java.awt.event.ActionEvent;\r
+import java.awt.event.ActionListener;\r
+import java.awt.event.MouseAdapter;\r
+import java.awt.event.MouseEvent;\r
+import java.awt.event.WindowAdapter;\r
+import java.awt.event.WindowEvent;\r
+import java.util.ArrayList;\r
+\r
+import javax.swing.DefaultCellEditor;\r
+import javax.swing.JButton;\r
+import javax.swing.JCheckBox;\r
+import javax.swing.JDialog;\r
+import javax.swing.JLabel;\r
+import javax.swing.JPanel;\r
+import javax.swing.JScrollPane;\r
+import javax.swing.JTabbedPane;\r
+import javax.swing.JTable;\r
+import javax.swing.ListSelectionModel;\r
+import javax.swing.ScrollPaneConstants;\r
+import javax.swing.SpringLayout;\r
+import javax.swing.SwingUtilities;\r
+import javax.swing.table.DefaultTableCellRenderer;\r
+import javax.swing.table.DefaultTableColumnModel;\r
+import javax.swing.table.DefaultTableModel;\r
+import javax.swing.table.TableCellRenderer;\r
+import javax.swing.table.TableColumn;\r
+\r
+import tainavi.Env.AAMode;\r
+import tainavi.JTXTButton.FontStyle;\r
+import tainavi.TVProgram.ProgGenre;\r
+\r
+\r
+abstract class AbsPaperColorsDialog extends JDialog {\r
+\r
+       private static final long serialVersionUID = 1L;\r
+       \r
+       private static boolean debug = false;\r
+\r
+       \r
+       /*******************************************************************************\r
+        * 抽象メソッド\r
+        ******************************************************************************/\r
+       \r
+       protected abstract Env getEnv();\r
+       protected abstract Bounds getBoundsEnv();\r
+       protected abstract PaperColorsMap getPaperColorMap();\r
+\r
+       protected abstract VWColorChooserDialog getCCWin();\r
+\r
+       protected abstract void updatePaperColors(Env ec, PaperColorsMap pc);\r
+       protected abstract void updatePaperFonts(Env ec);\r
+       protected abstract void updatePaperBounds(Env ec, Bounds bc);\r
+       protected abstract void updatePaperRepaint();\r
+\r
+       \r
+       /*******************************************************************************\r
+        * 呼び出し元から引き継いだもの\r
+        ******************************************************************************/\r
+       \r
+       private final Env origenv = getEnv();\r
+       private final Bounds origbnd = getBoundsEnv();\r
+       private final PaperColorsMap origpc = getPaperColorMap();\r
+       \r
+       private final VWColorChooserDialog ccwin = getCCWin();\r
+       \r
+       private final Env tmpenv = new Env();\r
+       private final Bounds tmpbnd = new Bounds();\r
+       private final PaperColorsMap tmppc = new PaperColorsMap();\r
+       \r
+       \r
+       /*******************************************************************************\r
+        * 定数\r
+        ******************************************************************************/\r
+\r
+       private static final int STEPBY = 10;\r
+       \r
+       private static final int SEP_WIDTH = 10;\r
+       private static final int SEP_WIDTH_NARROW = 2;\r
+       private static final int SEP_HEIGHT = 5;\r
+       private static final int SEP_HEIGHT_NARROW = 2;\r
+       \r
+       //private static final int PARTS_WIDTH = 900;\r
+       private static final int PARTS_HEIGHT = 30;\r
+       \r
+       private static final int LABEL_WIDTH = 125;\r
+       private static final int ITEM_WIDTH = 250;\r
+       private static final int TITLE_WIDTH = LABEL_WIDTH+ITEM_WIDTH;\r
+       \r
+       private static final int BUTTON_WIDTH = 100;\r
+       \r
+       private static final int PANEL_WIDTH = LABEL_WIDTH+ITEM_WIDTH+SEP_WIDTH*2;\r
+       private static int PANEL_HEIGHT = 0;\r
+       \r
+       private static final int TABLE_WIDTH = PANEL_WIDTH-SEP_WIDTH*2;\r
+       private static final int TABLE_HEIGHT = 260;\r
+       \r
+       private static final int STYLETABLE_HEIGHT = 80;\r
+       \r
+       private static final int TIMEBAR_WIDTH = TABLE_WIDTH/4;\r
+       \r
+       \r
+       /*******************************************************************************\r
+        * 部品\r
+        ******************************************************************************/\r
+       \r
+       private JPanel jPanel = null;\r
+       \r
+       private JTabbedPane jTabbedPane = null;\r
+       \r
+       private JPanel jPanel_buttons = null;\r
+       private JButton jButton_preview = null;\r
+       private JButton jButton_update = null;\r
+       private JButton jButton_cancel = null;\r
+       \r
+       // ジャンル別背景色のタブ\r
+       private JPanel jPanel_pColors = null;\r
+       private JScrollPane jScrollPane_list = null;\r
+       private JNETable jTable_list = null;\r
+       private DefaultTableModel jTableModel_list = null;\r
+       private JCCLabel jLabel_timebar = null;\r
+       private JCCLabel jLabel_timebar2 = null;\r
+       private JCCLabel jLabel_timebar3 = null;\r
+       private JCCLabel jLabel_timebar4 = null;\r
+       private JCheckBoxPanel jCBP_highlight = null;\r
+       private JCCLabel jLabel_highlight = null;\r
+       \r
+       // フォント設定のタブ\r
+       private JPanel jPanel_fonts = null;\r
+       private JCheckBoxPanel jCBP_showStart = null;\r
+       private JComboBoxPanel jCBX_titleFont = null;\r
+       private JSliderPanel jSP_titleFontSize = null;\r
+       private JCCLabel jLabel_titleFontColor = null;\r
+       private JScrollPane jScrollPane_titleFontStyle = null;\r
+       private JCheckBoxPanel jCBP_showDetail = null;\r
+       private JSliderPanel jSP_detailRows = null;\r
+       private JComboBoxPanel jCBX_detailFont = null;\r
+       private JSliderPanel jSP_detailFontSize = null;\r
+       private JCCLabel jLabel_detailFontColor = null;\r
+       private JScrollPane jScrollPane_detailFontStyle = null;\r
+       private JSliderPanel jSP_detailTab = null;\r
+       private JComboBoxPanel jCBX_aaMode = null;\r
+       \r
+       // サイズのタブ\r
+       private JPanel jPanel_bounds = null;\r
+       private JSliderPanel jSP_width = null;\r
+       private JSliderPanel jSP_height = null;\r
+       private JSliderPanel jSP_timebarPosition = null;\r
+       private JCCLabel jLabel_execon = null;\r
+       private JCCLabel jLabel_execoff = null;\r
+       private JCCLabel jLabel_pickup = null;\r
+       private JCCLabel jLabel_pickupFont = null;\r
+       private JCCLabel jLabel_matchedBorderColor = null;\r
+       private JSliderPanel jSP_matchedBorderThickness = null;\r
+       //private JCheckBoxPanel jCBP_lightProgramView = null;\r
+       \r
+       \r
+       /*******************************************************************************\r
+        * コンストラクタ\r
+        ******************************************************************************/\r
+       \r
+       public AbsPaperColorsDialog() {\r
+               //\r
+               super();\r
+               //\r
+               this.setModal(true);\r
+               //\r
+               this.addWindowListener(new WindowAdapter() {\r
+                       @Override\r
+                       public void windowClosing(WindowEvent e) {\r
+                               doCancel();\r
+                       }\r
+               });\r
+\r
+               this.setContentPane(getJPanel());\r
+               this.setDefaultCloseOperation(DO_NOTHING_ON_CLOSE);     // 閉じるときはキャンセルボタンを使ってクレ\r
+               \r
+               this.pack();\r
+               this.setResizable(false);\r
+               \r
+               this.setTitle("新聞形式の表示設定");\r
+       }\r
+\r
+       \r
+       /*******************************************************************************\r
+        * アクション\r
+        ******************************************************************************/\r
+       \r
+       private void doPreview() {\r
+               getColors(tmpenv,tmppc);\r
+               getFonts(tmpenv);\r
+               getBounds(tmpenv,tmpbnd);\r
+               \r
+               updatePaperColors(tmpenv,tmppc);\r
+               updatePaperFonts(tmpenv);\r
+               updatePaperBounds(tmpenv,tmpbnd);\r
+               \r
+               updatePaperRepaint();\r
+       }\r
+       \r
+       private void doUpdate() {\r
+               getColors(origenv,origpc);\r
+               getFonts(origenv);\r
+               getBounds(origenv,origbnd);\r
+               \r
+               updatePaperFonts(origenv);\r
+               updatePaperColors(origenv,origpc);\r
+               updatePaperBounds(origenv,origbnd);\r
+               \r
+               updatePaperRepaint();\r
+               \r
+               origpc.save();\r
+               origenv.save();\r
+               origbnd.save();\r
+\r
+               setVisible(false);\r
+       }\r
+       \r
+       private void doCancel() {\r
+               updatePaperColors(origenv,origpc);\r
+               updatePaperFonts(origenv);\r
+               updatePaperBounds(origenv,origbnd);\r
+               \r
+               updatePaperRepaint();\r
+               \r
+               setVisible(false);\r
+       }\r
+\r
+       /*\r
+        * メソッド\r
+        */\r
+\r
+       //\r
+       @Override\r
+       public void setVisible(boolean b) {\r
+               if (b) {\r
+                       if (debug) {\r
+                               for ( ProgGenre key : origpc.keySet() ) {\r
+                                       System.err.println("[DEBUG] before orig papercolorsmap "+key+"="+origpc.get(key));\r
+                               }\r
+                       }\r
+                       CommonUtils.FieldCopy(tmpenv, origenv);\r
+                       CommonUtils.FieldCopy(tmpbnd, origbnd);\r
+                       CommonUtils.FieldCopy(tmppc, origpc);\r
+                       setColors();\r
+                       setFonts();\r
+                       setBounds();\r
+               }\r
+               else {\r
+                       if (debug) {\r
+                               for ( ProgGenre key : origpc.keySet() ) {\r
+                                       System.err.println("[DEBUG] after orig papercolorsmap "+key+"="+origpc.get(key));\r
+                               }\r
+                       }\r
+               }\r
+               super.setVisible(b);\r
+       }\r
+       \r
+       //\r
+       private void getColors(Env toe, PaperColorsMap top) {\r
+               for ( int row=0; row<jTable_list.getRowCount(); row++ ) {\r
+                       TVProgram.ProgGenre g = (ProgGenre) jTable_list.getValueAt(row, 0);\r
+                       Color c = CommonUtils.str2color((String) jTable_list.getValueAt(row, 1));\r
+                       top.put(g, c);\r
+               }\r
+               toe.setTimebarColor(jLabel_timebar.getChoosed());\r
+               toe.setTimebarColor2(jLabel_timebar2.getChoosed());\r
+               toe.setTimebarColor3(jLabel_timebar3.getChoosed());\r
+               toe.setTimebarColor4(jLabel_timebar4.getChoosed());\r
+               toe.setEnableHighlight(jCBP_highlight.isSelected());\r
+               toe.setHighlightColor(jLabel_highlight.getChoosed());\r
+       }\r
+       \r
+       //\r
+       private void getFonts(Env to) {\r
+               to.setShowStart(jCBP_showStart.isSelected());\r
+               to.setTitleFont((String) jCBX_titleFont.getSelectedItem());\r
+               to.setTitleFontSize(jSP_titleFontSize.getValue());\r
+               to.setTitleFontColor(jLabel_titleFontColor.getChoosed());\r
+               to.setTitleFontStyle(getFontStyles((JNETable) jScrollPane_titleFontStyle.getViewport().getView()));\r
+               to.setShowDetail(jCBP_showDetail.isSelected());\r
+               to.setDetailRows(jSP_detailRows.getValue());\r
+               to.setDetailFont((String) jCBX_detailFont.getSelectedItem());\r
+               to.setDetailFontSize(jSP_detailFontSize.getValue());\r
+               to.setDetailFontColor(jLabel_detailFontColor.getChoosed());\r
+               to.setDetailFontStyle(getFontStyles((JNETable) jScrollPane_detailFontStyle.getViewport().getView()));\r
+               to.setDetailTab(jSP_detailTab.getValue());\r
+               to.setPaperAAMode((AAMode) jCBX_aaMode.getSelectedItem());\r
+       }\r
+       private ArrayList<JTXTButton.FontStyle> getFontStyles(JNETable jt) {\r
+               ArrayList<JTXTButton.FontStyle> fsa = new ArrayList<JTXTButton.FontStyle>();\r
+               for ( int row=0; row<jt.getRowCount(); row++ ) {\r
+                       if ( (Boolean)jt.getValueAt(row, 0) ) {\r
+                               fsa.add((FontStyle) jt.getValueAt(row, 1));\r
+                       }\r
+               }\r
+               return fsa;\r
+       }\r
+       \r
+       //\r
+       private void getBounds(Env toe, Bounds tob) {\r
+               tob.setBangumiColumnWidth(jSP_width.getValue());\r
+               tob.setPaperHeightMultiplier(jSP_height.getValue()*(float)STEPBY/(float)60);\r
+               tob.setTimelinePosition(jSP_timebarPosition.getValue());\r
+               toe.setExecOnFontColor(jLabel_execon.getChoosed());\r
+               toe.setExecOffFontColor(jLabel_execoff.getChoosed());\r
+               toe.setPickedColor(jLabel_pickup.getChoosed());\r
+               toe.setPickedFontColor(jLabel_pickupFont.getChoosed());\r
+               toe.setMatchedBorderColor(jLabel_matchedBorderColor.getChoosed());\r
+               toe.setMatchedBorderThickness(jSP_matchedBorderThickness.getValue());\r
+               //\r
+               tob.setShowMatchedBorder(origbnd.getShowMatchedBorder());\r
+       }\r
+       \r
+       \r
+       \r
+       /*******************************************************************************\r
+        * コンポーネント\r
+        ******************************************************************************/\r
+\r
+       private JPanel getJPanel() {\r
+               if (jPanel == null) {\r
+                       jPanel = new JPanel();\r
+                       jPanel.setLayout(new BorderLayout());\r
+                       jPanel.add(getJTabbedPane(), BorderLayout.CENTER);\r
+                       jPanel.add(getJPanel_buttons(), BorderLayout.PAGE_END);\r
+               }\r
+               return jPanel;\r
+       }\r
+       \r
+       //\r
+       private JTabbedPane getJTabbedPane() {\r
+               if (jTabbedPane == null) {\r
+                       jTabbedPane = new JTabbedPane();\r
+                       jTabbedPane.add(getJPanel_pColors(),"背景色",0);\r
+                       jTabbedPane.add(getJPanel_fonts(),"テキスト",1);\r
+                       jTabbedPane.add(getJPanel_bounds(),"その他",2);\r
+               }\r
+               return jTabbedPane;\r
+       }\r
+       \r
+       //\r
+       private JPanel getJPanel_buttons() {\r
+               if (jPanel_buttons == null) {\r
+                       jPanel_buttons = new JPanel();\r
+\r
+                       jPanel_buttons.setLayout(new SpringLayout());\r
+                       \r
+                       int y = SEP_HEIGHT;\r
+                       int x = (PANEL_WIDTH - (BUTTON_WIDTH*3+SEP_WIDTH*2))/2;\r
+                       CommonSwingUtils.putComponentOn(jPanel_buttons, getJButton_preview("プレビュー"), BUTTON_WIDTH, PARTS_HEIGHT, x, y);\r
+                       CommonSwingUtils.putComponentOn(jPanel_buttons, getJButton_update("登録"), BUTTON_WIDTH, PARTS_HEIGHT, x+=BUTTON_WIDTH+SEP_WIDTH, y);\r
+                       CommonSwingUtils.putComponentOn(jPanel_buttons, getJButton_cancel("キャンセル"), BUTTON_WIDTH, PARTS_HEIGHT, x+=BUTTON_WIDTH+SEP_WIDTH, y);\r
+                       \r
+                       y += PARTS_HEIGHT+SEP_HEIGHT;\r
+                       \r
+                       jPanel_buttons.setPreferredSize(new Dimension(PANEL_WIDTH, y));\r
+               }\r
+               return jPanel_buttons;\r
+       }\r
+       private JButton getJButton_preview(String s) {\r
+               if (jButton_preview == null) {\r
+                       jButton_preview = new JButton();\r
+                       jButton_preview.setText(s);\r
+                       \r
+                       jButton_preview.addActionListener(new ActionListener() {\r
+                               @Override\r
+                               public void actionPerformed(ActionEvent e) {\r
+                                       doPreview();\r
+                               }\r
+                       });\r
+               }\r
+               return jButton_preview;\r
+       }\r
+       private JButton getJButton_update(String s) {\r
+               if (jButton_update == null) {\r
+                       jButton_update = new JButton();\r
+                       jButton_update.setText(s);\r
+                       \r
+                       jButton_update.addActionListener(new ActionListener() {\r
+                               @Override\r
+                               public void actionPerformed(ActionEvent e) {\r
+                                       doUpdate();\r
+                               }\r
+                       });\r
+               }\r
+               return jButton_update;\r
+       }\r
+       private JButton getJButton_cancel(String s) {\r
+               if (jButton_cancel == null) {\r
+                       jButton_cancel = new JButton();\r
+                       jButton_cancel.setText(s);\r
+                       \r
+                       jButton_cancel.addActionListener(new ActionListener() {\r
+                               @Override\r
+                               public void actionPerformed(ActionEvent e) {\r
+                                       doCancel();\r
+                               }\r
+                       });\r
+               }\r
+               return jButton_cancel;\r
+       }\r
+       \r
+       \r
+       /*\r
+        * ジャンル別背景色のタブ \r
+        */\r
+       \r
+       private JPanel getJPanel_pColors() {\r
+               if (jPanel_pColors == null) {\r
+                       jPanel_pColors = new JPanel();\r
+\r
+                       jPanel_pColors.setLayout(new SpringLayout());\r
+                       \r
+                       int y = SEP_HEIGHT_NARROW;\r
+                       int x = SEP_WIDTH;\r
+                       \r
+                       CommonSwingUtils.putComponentOn(jPanel_pColors, new JTitleLabel("ジャンル別背景色"), TITLE_WIDTH, PARTS_HEIGHT, SEP_WIDTH_NARROW, y);\r
+                       y += (PARTS_HEIGHT+SEP_HEIGHT_NARROW);\r
+                       CommonSwingUtils.putComponentOn(jPanel_pColors, getJScrollPane_list(), TABLE_WIDTH, TABLE_HEIGHT, SEP_WIDTH, y);\r
+                       \r
+                       y += (TABLE_HEIGHT+SEP_HEIGHT);\r
+                       CommonSwingUtils.putComponentOn(jPanel_pColors, new JTitleLabel("タイムバーの色"), TITLE_WIDTH, PARTS_HEIGHT, SEP_WIDTH_NARROW, y);\r
+                       y += (PARTS_HEIGHT+SEP_HEIGHT_NARROW);\r
+                       CommonSwingUtils.putComponentOn(jPanel_pColors, jLabel_timebar = new JCCLabel("6~11", origenv.getTimebarColor(),true,this,ccwin), TIMEBAR_WIDTH, PARTS_HEIGHT, x, y);\r
+                       CommonSwingUtils.putComponentOn(jPanel_pColors, jLabel_timebar2 = new JCCLabel("12~17", origenv.getTimebarColor2(),true,this,ccwin), TIMEBAR_WIDTH, PARTS_HEIGHT, x+=TIMEBAR_WIDTH, y);\r
+                       CommonSwingUtils.putComponentOn(jPanel_pColors, jLabel_timebar3 = new JCCLabel("18~23", origenv.getTimebarColor3(),true,this,ccwin), TIMEBAR_WIDTH, PARTS_HEIGHT, x+=TIMEBAR_WIDTH, y);\r
+                       CommonSwingUtils.putComponentOn(jPanel_pColors, jLabel_timebar4 = new JCCLabel("24~5", origenv.getTimebarColor4(),true,this,ccwin), TIMEBAR_WIDTH, PARTS_HEIGHT, x+=TIMEBAR_WIDTH, y);\r
+                       \r
+                       y += (PARTS_HEIGHT+SEP_HEIGHT);\r
+                       CommonSwingUtils.putComponentOn(jPanel_pColors, new JTitleLabel("マウスオーバー時のハイライト色"), TITLE_WIDTH, PARTS_HEIGHT, SEP_WIDTH_NARROW, y);\r
+                       y += (PARTS_HEIGHT+SEP_HEIGHT_NARROW);\r
+                       CommonSwingUtils.putComponentOn(jPanel_pColors, jCBP_highlight = new JCheckBoxPanel("有効",LABEL_WIDTH/2), LABEL_WIDTH, PARTS_HEIGHT, SEP_WIDTH, y);\r
+                       CommonSwingUtils.putComponentOn(jPanel_pColors, jLabel_highlight = new JCCLabel("ハイライト",origenv.getHighlightColor(),true,this,ccwin), ITEM_WIDTH, PARTS_HEIGHT, LABEL_WIDTH+SEP_WIDTH, y);\r
+                       \r
+                       y += (PARTS_HEIGHT+SEP_HEIGHT*2);\r
+                       \r
+                       if (PANEL_HEIGHT < y) PANEL_HEIGHT = y;\r
+                       \r
+                       jPanel_pColors.setPreferredSize(new Dimension(PANEL_WIDTH, PANEL_HEIGHT));\r
+               }\r
+               return jPanel_pColors;\r
+       }\r
+       private void setColors() {\r
+               //\r
+               for (int i=jTableModel_list.getRowCount()-1; i>=0; i--) {\r
+                       jTableModel_list.removeRow(i);\r
+               }\r
+               for (TVProgram.ProgGenre g : TVProgram.ProgGenre.values()) {\r
+                       Object[] data = {\r
+                                       g,\r
+                                       CommonSwingUtils.getColoredString(origpc.get(g),"色見本")\r
+                       };\r
+                       jTableModel_list.addRow(data);\r
+               }\r
+               jTable_list.updateUI();\r
+               //\r
+               jLabel_timebar.setChoosed(origenv.getTimebarColor());\r
+               jLabel_timebar2.setChoosed(origenv.getTimebarColor2());\r
+               jLabel_timebar3.setChoosed(origenv.getTimebarColor3());\r
+               jLabel_timebar4.setChoosed(origenv.getTimebarColor4());\r
+               jCBP_highlight.setSelected(origenv.getEnableHighlight());\r
+               jLabel_highlight.setChoosed(origenv.getHighlightColor());\r
+       }\r
+       \r
+       private JScrollPane getJScrollPane_list() {\r
+               if (jScrollPane_list == null) {\r
+                       jScrollPane_list = new JScrollPane();\r
+                       jScrollPane_list.setViewportView(getJTable_list());\r
+                       jScrollPane_list.setVerticalScrollBarPolicy(ScrollPaneConstants.VERTICAL_SCROLLBAR_NEVER);\r
+                       jScrollPane_list.setHorizontalScrollBarPolicy(ScrollPaneConstants.HORIZONTAL_SCROLLBAR_NEVER);\r
+               }\r
+               return(jScrollPane_list);\r
+       }\r
+       private JNETable getJTable_list() {\r
+               if (jTable_list == null) {\r
+                       //\r
+                       String[] colname = {"ジャンル", "色"};\r
+                       int[] colwidth = {TABLE_WIDTH-100,100};\r
+                       //\r
+                       jTableModel_list = new DefaultTableModel(colname, 0);\r
+                       jTable_list = new JNETable(jTableModel_list, false);\r
+                       jTable_list.setAutoResizeMode(JNETable.AUTO_RESIZE_OFF);\r
+                       DefaultTableColumnModel columnModel = (DefaultTableColumnModel)jTable_list.getColumnModel();\r
+                       TableColumn column = null;\r
+                       for (int i = 0 ; i < columnModel.getColumnCount() ; i++){\r
+                               column = columnModel.getColumn(i);\r
+                               column.setPreferredWidth(colwidth[i]);\r
+                       }\r
+                       //\r
+                       TableCellRenderer colorCellRenderer = new VWColorCellRenderer();\r
+                       jTable_list.getColumn("色").setCellRenderer(colorCellRenderer);\r
+                       //\r
+                       final JDialog jd = this;\r
+                       jTable_list.addMouseListener(new MouseAdapter() {\r
+                               @Override\r
+                               public void mouseClicked(MouseEvent e) {\r
+                                       if (SwingUtilities.isLeftMouseButton(e)) {\r
+                                               //\r
+                                               JTable t = (JTable) e.getSource();\r
+                                               Point p = e.getPoint();\r
+                                               \r
+                                               int col = t.convertColumnIndexToModel(t.columnAtPoint(p));\r
+                                               if (col == 1) {\r
+                                                       int row = t.convertRowIndexToModel(t.rowAtPoint(p));\r
+                                                       \r
+                                                       ccwin.setColor(CommonUtils.str2color((String) t.getValueAt(row,1)));\r
+                                                       CommonSwingUtils.setLocationCenter(jd,ccwin);\r
+                                                       ccwin.setVisible(true);\r
+                                                       \r
+                                                       if (ccwin.getSelectedColor() != null ) {\r
+                                                               //\r
+                                                               tmppc.put((TVProgram.ProgGenre) t.getValueAt(row,0), ccwin.getSelectedColor());\r
+                                                               //\r
+                                                               t.setValueAt(CommonSwingUtils.getColoredString(ccwin.getSelectedColor(),"色見本"), row, 1);\r
+                                                       }\r
+                                               }\r
+                                       }\r
+                               }\r
+                       });\r
+               }\r
+               return(jTable_list);\r
+       }\r
+       \r
+       \r
+       \r
+       /*\r
+        * フォントのタブ \r
+        */\r
+       \r
+       /**\r
+        * フォントの選択肢を設定\r
+        */\r
+       public void setFontList(VWFont vwfont) {\r
+               jCBX_titleFont.removeAllItems();\r
+               jCBX_detailFont.removeAllItems();\r
+               for ( String fn : vwfont.getNames() ) {\r
+                       jCBX_titleFont.addItem(fn);\r
+                       jCBX_detailFont.addItem(fn);\r
+                       \r
+                       //if (debug) System.err.println("[DEBUG] font name="+fn);\r
+               }\r
+       }\r
+       private JPanel getJPanel_fonts() {\r
+               if (jPanel_fonts == null) {\r
+                       jPanel_fonts = new JPanel();\r
+\r
+                       jPanel_fonts.setLayout(new SpringLayout());\r
+                       \r
+                       int y = SEP_HEIGHT_NARROW;\r
+                       \r
+                       CommonSwingUtils.putComponentOn(jPanel_fonts, new JTitleLabel("開始時刻欄の設定"), TITLE_WIDTH, PARTS_HEIGHT, SEP_HEIGHT_NARROW, y);\r
+                       \r
+                       y += (PARTS_HEIGHT+SEP_HEIGHT_NARROW);\r
+                       CommonSwingUtils.putComponentOn(jPanel_fonts, jCBP_showStart = new JCheckBoxPanel("表示する",LABEL_WIDTH), TITLE_WIDTH, PARTS_HEIGHT, SEP_WIDTH, y);\r
+                       //jCBP_showStart.addActionListener(fal);\r
+                       \r
+                       y += (PARTS_HEIGHT+SEP_HEIGHT);\r
+                       CommonSwingUtils.putComponentOn(jPanel_fonts, new JTitleLabel("番組名のフォント設定"), TITLE_WIDTH, PARTS_HEIGHT, SEP_WIDTH_NARROW, y);\r
+                       \r
+                       y += (PARTS_HEIGHT+SEP_HEIGHT_NARROW);\r
+                       CommonSwingUtils.putComponentOn(jPanel_fonts, jCBX_titleFont = new JComboBoxPanel("フォント",LABEL_WIDTH,ITEM_WIDTH,true), LABEL_WIDTH+ITEM_WIDTH, PARTS_HEIGHT, SEP_WIDTH, y);\r
+                       \r
+                       y += (PARTS_HEIGHT+SEP_HEIGHT_NARROW);\r
+                       CommonSwingUtils.putComponentOn(jPanel_fonts, jSP_titleFontSize = new JSliderPanel("サイズ",LABEL_WIDTH,6,24,ITEM_WIDTH), LABEL_WIDTH+ITEM_WIDTH, PARTS_HEIGHT, SEP_WIDTH, y);\r
+                       \r
+                       y += (PARTS_HEIGHT+SEP_HEIGHT_NARROW);\r
+                       CommonSwingUtils.putComponentOn(jPanel_fonts, new JLabel("文字色"), LABEL_WIDTH, PARTS_HEIGHT, SEP_WIDTH, y);\r
+                       CommonSwingUtils.putComponentOn(jPanel_fonts, jLabel_titleFontColor = new JCCLabel("番組名", origenv.getTitleFontColor(),false,this,ccwin), ITEM_WIDTH, PARTS_HEIGHT, SEP_WIDTH+LABEL_WIDTH, y);\r
+                       \r
+                       y += (PARTS_HEIGHT+SEP_HEIGHT_NARROW);\r
+                       CommonSwingUtils.putComponentOn(jPanel_fonts, new JLabel("スタイル"), LABEL_WIDTH, PARTS_HEIGHT, SEP_WIDTH, y);\r
+                       CommonSwingUtils.putComponentOn(jPanel_fonts, jScrollPane_titleFontStyle = getJScrollPane_fontstyle(), ITEM_WIDTH, STYLETABLE_HEIGHT, SEP_WIDTH+LABEL_WIDTH, y);\r
+                       \r
+                       y += (STYLETABLE_HEIGHT+10);\r
+                       CommonSwingUtils.putComponentOn(jPanel_fonts, new JTitleLabel("番組詳細のフォント設定"), TITLE_WIDTH, PARTS_HEIGHT, SEP_WIDTH_NARROW, y);\r
+                       \r
+                       y += (PARTS_HEIGHT+SEP_HEIGHT_NARROW);\r
+                       CommonSwingUtils.putComponentOn(jPanel_fonts, jCBP_showDetail = new JCheckBoxPanel("表示する",LABEL_WIDTH), TITLE_WIDTH+ITEM_WIDTH, PARTS_HEIGHT, SEP_WIDTH, y);\r
+                       \r
+                       y += (PARTS_HEIGHT+SEP_HEIGHT_NARROW);\r
+                       CommonSwingUtils.putComponentOn(jPanel_fonts, jSP_detailRows = new JSliderPanel("最大行数",LABEL_WIDTH,1,50,ITEM_WIDTH), TITLE_WIDTH+ITEM_WIDTH, PARTS_HEIGHT, SEP_WIDTH, y);\r
+\r
+                       y += (PARTS_HEIGHT+SEP_HEIGHT_NARROW);\r
+                       CommonSwingUtils.putComponentOn(jPanel_fonts, jCBX_detailFont = new JComboBoxPanel("フォント",LABEL_WIDTH,ITEM_WIDTH,true), LABEL_WIDTH+ITEM_WIDTH, PARTS_HEIGHT, SEP_WIDTH, y);\r
+                       \r
+                       y += (PARTS_HEIGHT+SEP_HEIGHT_NARROW);\r
+                       CommonSwingUtils.putComponentOn(jPanel_fonts, jSP_detailFontSize = new JSliderPanel("サイズ",LABEL_WIDTH,6,24,ITEM_WIDTH), LABEL_WIDTH+ITEM_WIDTH, PARTS_HEIGHT, SEP_WIDTH, y);\r
+                       \r
+                       y += (PARTS_HEIGHT+SEP_HEIGHT_NARROW);\r
+                       CommonSwingUtils.putComponentOn(jPanel_fonts, new JLabel("文字色"), LABEL_WIDTH, PARTS_HEIGHT, SEP_WIDTH, y);\r
+                       CommonSwingUtils.putComponentOn(jPanel_fonts, jLabel_detailFontColor = new JCCLabel("番組詳細", origenv.getDetailFontColor(),false,this,ccwin), ITEM_WIDTH, PARTS_HEIGHT, SEP_WIDTH+LABEL_WIDTH, y);\r
+                       \r
+                       y += (PARTS_HEIGHT+SEP_HEIGHT_NARROW);\r
+                       CommonSwingUtils.putComponentOn(jPanel_fonts, new JLabel("スタイル"), LABEL_WIDTH, PARTS_HEIGHT, SEP_WIDTH, y);\r
+                       CommonSwingUtils.putComponentOn(jPanel_fonts, jScrollPane_detailFontStyle = getJScrollPane_fontstyle(), ITEM_WIDTH, STYLETABLE_HEIGHT, SEP_WIDTH+LABEL_WIDTH, y);\r
+                       \r
+                       y += (STYLETABLE_HEIGHT+SEP_HEIGHT_NARROW);\r
+                       CommonSwingUtils.putComponentOn(jPanel_fonts, jSP_detailTab = new JSliderPanel("左余白",LABEL_WIDTH,0,24,ITEM_WIDTH), LABEL_WIDTH+ITEM_WIDTH, PARTS_HEIGHT, SEP_WIDTH, y);\r
+                       \r
+                       y += (PARTS_HEIGHT+SEP_HEIGHT*3);\r
+                       \r
+                       if (PANEL_HEIGHT < y) PANEL_HEIGHT = y;\r
+                       \r
+                       jPanel_fonts.setPreferredSize(new Dimension(PANEL_WIDTH, PANEL_HEIGHT));\r
+               }\r
+               return jPanel_fonts;\r
+       }\r
+       private void setFonts() {\r
+               //\r
+               jCBP_showStart.setSelected(origenv.getShowStart());\r
+               //\r
+               if ( ! origenv.getTitleFont().equals("") ) {\r
+                       jCBX_titleFont.setSelectedItem(origenv.getTitleFont());\r
+               }\r
+               else if ( ! origenv.getFontName().equals("") ) {\r
+                       jCBX_titleFont.setSelectedItem(origenv.getFontName());\r
+               }\r
+               jSP_titleFontSize.setValue(origenv.getTitleFontSize());\r
+               jLabel_titleFontColor.setChoosed(origenv.getTitleFontColor());\r
+               setFontStyles((JNETable) jScrollPane_titleFontStyle.getViewport().getView(), origenv.getTitleFontStyle());\r
+               //\r
+               jCBP_showDetail.setSelected(origenv.getShowDetail());\r
+               jSP_detailRows.setValue(origenv.getDetailRows());\r
+               if ( ! origenv.getDetailFont().equals("") ) {\r
+                       jCBX_detailFont.setSelectedItem(origenv.getDetailFont());\r
+               }\r
+               else if ( ! origenv.getFontName().equals("") ) {\r
+                       jCBX_detailFont.setSelectedItem(origenv.getFontName());\r
+               }\r
+               jSP_detailFontSize.setValue(origenv.getDetailFontSize());\r
+               jLabel_detailFontColor.setChoosed(origenv.getDetailFontColor());\r
+               setFontStyles((JNETable) jScrollPane_detailFontStyle.getViewport().getView(), origenv.getDetailFontStyle());\r
+               jSP_detailTab.setValue(origenv.getDetailTab());\r
+               jCBX_aaMode.setSelectedItem(origenv.getPaperAAMode());\r
+       }\r
+       private void setFontStyles(JNETable jt, ArrayList<JTXTButton.FontStyle> fsa) {\r
+               for ( int row=0; row<jt.getRowCount(); row++ ) {\r
+                       jt.setValueAt(false, row, 0);\r
+                       for ( JTXTButton.FontStyle fs : fsa ) {\r
+                               if ( fs == jt.getValueAt(row, 1) ) {\r
+                                       jt.setValueAt(true, row, 0);\r
+                                       break;\r
+                               }\r
+                       }\r
+               }\r
+       }\r
+       \r
+       private JScrollPane getJScrollPane_fontstyle() {\r
+               JScrollPane jScrollPane = new JScrollPane();\r
+               jScrollPane.setViewportView(getJTable_fontstyle());\r
+               jScrollPane.setVerticalScrollBarPolicy(ScrollPaneConstants.VERTICAL_SCROLLBAR_NEVER);\r
+               jScrollPane.setHorizontalScrollBarPolicy(ScrollPaneConstants.HORIZONTAL_SCROLLBAR_NEVER);\r
+               return jScrollPane;\r
+       }\r
+       private JNETable getJTable_fontstyle() {\r
+               \r
+               // ヘッダの設定\r
+               String[] colname = {"チェック", "スタイル"};\r
+               int[] colwidth = {50,ITEM_WIDTH-50};\r
+                       \r
+               //\r
+               DefaultTableModel model = new DefaultTableModel(colname, 0);\r
+               JNETable jTable = new JNETable(model, false) {\r
+\r
+                       private static final long serialVersionUID = 1L;\r
+\r
+                       @Override\r
+                       public boolean isCellEditable(int row, int column) {\r
+                                       return (column == 0);\r
+                       }\r
+               };\r
+               jTable.setAutoResizeMode(JNETable.AUTO_RESIZE_OFF);\r
+               DefaultTableColumnModel columnModel = (DefaultTableColumnModel)jTable.getColumnModel();\r
+               TableColumn column = null;\r
+               for (int i = 0 ; i < columnModel.getColumnCount() ; i++){\r
+                       column = columnModel.getColumn(i);\r
+                       column.setPreferredWidth(colwidth[i]);\r
+               }\r
+               \r
+               // にゃーん\r
+               jTable.setSelectionMode(ListSelectionModel.SINGLE_SELECTION);\r
+   \r
+               // エディタに手を入れる\r
+               DefaultCellEditor editor = new DefaultCellEditor(new JCheckBox() {\r
+\r
+                       private static final long serialVersionUID = 1L;\r
+\r
+                       @Override\r
+                       public int getHorizontalAlignment() {\r
+                               return JCheckBox.CENTER;\r
+                       }\r
+               });\r
+               jTable.getColumn("チェック").setCellEditor(editor);\r
+               // レンダラに手を入れる\r
+               DefaultTableCellRenderer renderer = new DefaultTableCellRenderer() {\r
+\r
+                       private static final long serialVersionUID = 1L;\r
+\r
+                       @Override\r
+                       public Component getTableCellRendererComponent(JTable table, Object value,\r
+                                       boolean isSelected, boolean hasFocus, int row, int column) {\r
+                               //\r
+                               JCheckBox cBox = new JCheckBox();\r
+                               cBox.setHorizontalAlignment(JCheckBox.CENTER);\r
+                               //\r
+                               Boolean b = (Boolean)value;\r
+                               cBox.setSelected(b.booleanValue());\r
+                               //\r
+                               if (isSelected) {\r
+                                       cBox.setBackground(table.getSelectionBackground());\r
+                               }\r
+                               else {\r
+                                       cBox.setBackground(table.getBackground());\r
+                               }\r
+                               return cBox;\r
+                       }\r
+               };\r
+               jTable.getColumn("チェック").setCellRenderer(renderer);\r
+               \r
+               //\r
+               for ( JTXTButton.FontStyle fs : JTXTButton.FontStyle.values() ) {\r
+                       Object[] data = { false,fs };\r
+                       model.addRow(data);\r
+               }\r
+               return jTable;\r
+       }\r
+       \r
+       /*\r
+        * サイズのタブ \r
+        */\r
+       \r
+       private JPanel getJPanel_bounds() {\r
+               if (jPanel_bounds == null) {\r
+                       jPanel_bounds = new JPanel();\r
+\r
+                       jPanel_bounds.setLayout(new SpringLayout());\r
+                       \r
+                       int y = SEP_HEIGHT_NARROW;\r
+                       \r
+                       CommonSwingUtils.putComponentOn(jPanel_bounds, new JTitleLabel("サイズの設定"), TITLE_WIDTH, PARTS_HEIGHT, SEP_WIDTH_NARROW, y);\r
+                       \r
+                       y += (PARTS_HEIGHT+SEP_HEIGHT_NARROW);\r
+                       CommonSwingUtils.putComponentOn(jPanel_bounds, jSP_width = new JSliderPanel("幅",LABEL_WIDTH,50,300,ITEM_WIDTH), LABEL_WIDTH+ITEM_WIDTH, PARTS_HEIGHT, SEP_WIDTH, y);\r
+                       \r
+                       y += (PARTS_HEIGHT+SEP_HEIGHT_NARROW);\r
+                       CommonSwingUtils.putComponentOn(jPanel_bounds, jSP_height = new JSliderPanel("高さ(pt/H)",LABEL_WIDTH,30,600,STEPBY,ITEM_WIDTH), LABEL_WIDTH+ITEM_WIDTH, PARTS_HEIGHT, SEP_WIDTH, y);\r
+                       \r
+                       y += (PARTS_HEIGHT+SEP_HEIGHT_NARROW);\r
+                       CommonSwingUtils.putComponentOn(jPanel_bounds, jSP_timebarPosition = new JSliderPanel("現在時刻線(分)",LABEL_WIDTH,1,180,ITEM_WIDTH), LABEL_WIDTH+ITEM_WIDTH, PARTS_HEIGHT, SEP_WIDTH, y);\r
+\r
+                       y += (PARTS_HEIGHT+SEP_HEIGHT);\r
+                       CommonSwingUtils.putComponentOn(jPanel_bounds, new JTitleLabel("予約枠の設定"), TITLE_WIDTH, PARTS_HEIGHT, SEP_WIDTH_NARROW, y);\r
+                       \r
+                       y += (PARTS_HEIGHT+SEP_HEIGHT_NARROW);\r
+                       CommonSwingUtils.putComponentOn(jPanel_bounds, jLabel_execon = new JCCLabel("実行ONの文字色",origenv.getExecOnFontColor(),false,this,ccwin), ITEM_WIDTH, PARTS_HEIGHT, SEP_WIDTH+LABEL_WIDTH, y);\r
+                       \r
+                       y += (PARTS_HEIGHT+SEP_HEIGHT_NARROW);\r
+                       CommonSwingUtils.putComponentOn(jPanel_bounds, jLabel_execoff = new JCCLabel("実行OFFの文字色",origenv.getExecOffFontColor(),false,this,ccwin), ITEM_WIDTH, PARTS_HEIGHT, SEP_WIDTH+LABEL_WIDTH, y);\r
+\r
+                       y += (PARTS_HEIGHT+SEP_HEIGHT);\r
+                       CommonSwingUtils.putComponentOn(jPanel_bounds, new JTitleLabel("ピックアップ枠の設定"), TITLE_WIDTH, PARTS_HEIGHT, SEP_WIDTH_NARROW, y);\r
+                       \r
+                       y += (PARTS_HEIGHT+SEP_HEIGHT_NARROW);\r
+                       CommonSwingUtils.putComponentOn(jPanel_bounds, jLabel_pickup = new JCCLabel("ピックアップの枠色",origenv.getPickedColor(),true,this,ccwin), ITEM_WIDTH, PARTS_HEIGHT, SEP_WIDTH+LABEL_WIDTH, y);\r
+                       \r
+                       y += (PARTS_HEIGHT+SEP_HEIGHT_NARROW);\r
+                       CommonSwingUtils.putComponentOn(jPanel_bounds, jLabel_pickupFont = new JCCLabel("ピックアップの文字色",origenv.getPickedFontColor(),false,this,ccwin), ITEM_WIDTH, PARTS_HEIGHT, SEP_WIDTH+LABEL_WIDTH, y);\r
+                       \r
+                       y += (PARTS_HEIGHT+SEP_HEIGHT);\r
+                       CommonSwingUtils.putComponentOn(jPanel_bounds, new JTitleLabel("予約待機枠の設定"), TITLE_WIDTH, PARTS_HEIGHT, SEP_WIDTH_NARROW, y);\r
+                       \r
+                       y += (PARTS_HEIGHT+SEP_HEIGHT_NARROW);\r
+                       CommonSwingUtils.putComponentOn(jPanel_bounds, jSP_matchedBorderThickness = new JSliderPanel("太さ",LABEL_WIDTH,1,16,ITEM_WIDTH), LABEL_WIDTH+ITEM_WIDTH, PARTS_HEIGHT, SEP_WIDTH, y);\r
+                       \r
+                       y += (PARTS_HEIGHT+SEP_HEIGHT_NARROW);\r
+                       CommonSwingUtils.putComponentOn(jPanel_bounds, jLabel_matchedBorderColor = new JCCLabel("予約待機の枠色",origenv.getMatchedBorderColor(),true,this,ccwin), ITEM_WIDTH, PARTS_HEIGHT, SEP_WIDTH+LABEL_WIDTH, y);\r
+                       \r
+                       y += (PARTS_HEIGHT+SEP_HEIGHT);\r
+                       CommonSwingUtils.putComponentOn(jPanel_bounds, new JTitleLabel("フォントのアンチエイリアス設定"), TITLE_WIDTH, PARTS_HEIGHT, SEP_WIDTH_NARROW, y);\r
+                       \r
+                       y += (PARTS_HEIGHT+SEP_HEIGHT_NARROW);\r
+                       CommonSwingUtils.putComponentOn(jPanel_bounds, jCBX_aaMode = new JComboBoxPanel("アンチエイリアス",LABEL_WIDTH,ITEM_WIDTH,true), LABEL_WIDTH+ITEM_WIDTH, PARTS_HEIGHT, SEP_WIDTH, y);\r
+                       for ( AAMode aam : AAMode.values() ) {\r
+                               jCBX_aaMode.addItem(aam);\r
+                       }\r
+                       \r
+                       y += (PARTS_HEIGHT+SEP_HEIGHT*2);\r
+                       \r
+                       if (PANEL_HEIGHT < y) PANEL_HEIGHT = y;\r
+                       \r
+                       jPanel_bounds.setPreferredSize(new Dimension(PANEL_WIDTH, PANEL_HEIGHT));\r
+               }\r
+               return jPanel_bounds;\r
+       }\r
+\r
+       private void setBounds() {\r
+               jSP_width.setValue(origbnd.getBangumiColumnWidth());\r
+               jSP_height.setValue(Math.round(origbnd.getPaperHeightMultiplier()*(float)60/(float)STEPBY));\r
+               jSP_timebarPosition.setValue(origbnd.getTimelinePosition());\r
+               jLabel_execon.setChoosed(origenv.getExecOnFontColor());\r
+               jLabel_execon.setBackground(Color.RED);\r
+               jLabel_execoff.setChoosed(origenv.getExecOffFontColor());\r
+               jLabel_execoff.setBackground(Color.RED);\r
+               jLabel_pickup.setChoosed(origenv.getPickedColor());\r
+               jLabel_pickupFont.setChoosed(origenv.getPickedFontColor());\r
+               jLabel_pickupFont.setBackground(Color.RED);\r
+               jLabel_matchedBorderColor.setChoosed(origenv.getMatchedBorderColor());\r
+               jSP_matchedBorderThickness.setValue(origenv.getMatchedBorderThickness());\r
+               /*\r
+               if ( ! origenv.getShowStart() && ! origenv.getShowDetail() ) {\r
+                       jCBP_lightProgramView.setSelected(true);\r
+               }\r
+               else {\r
+                       jCBP_lightProgramView.setSelected(false);\r
+               }\r
+               */\r
+       }\r
+       \r
+       /*******************************************************************************\r
+        * 独自部品\r
+        ******************************************************************************/\r
+       \r
+       private class JTitleLabel extends JLabel {\r
+               \r
+               private static final long serialVersionUID = 1L;\r
+\r
+               public JTitleLabel(String s) {\r
+                       super(s);\r
+                       this.setForeground(Color.RED);\r
+                       //this.setFont(this.getFont().deriveFont(this.getFont().getStyle()|Font.BOLD));\r
+               }\r
+       }\r
+       \r
+}\r
diff --git a/TinyBannavi/src/tainavi/AbsPaperView.java b/TinyBannavi/src/tainavi/AbsPaperView.java
new file mode 100644 (file)
index 0000000..75bdcbb
--- /dev/null
@@ -0,0 +1,2473 @@
+package tainavi;\r
+\r
+import java.awt.AWTException;\r
+import java.awt.BorderLayout;\r
+import java.awt.Color;\r
+import java.awt.Component;\r
+import java.awt.Cursor;\r
+import java.awt.Dimension;\r
+import java.awt.Point;\r
+import java.awt.Rectangle;\r
+import java.awt.Robot;\r
+import java.awt.event.ComponentAdapter;\r
+import java.awt.event.ComponentEvent;\r
+import java.awt.event.ComponentListener;\r
+import java.awt.event.MouseAdapter;\r
+import java.awt.event.MouseEvent;\r
+import java.awt.event.MouseListener;\r
+import java.io.File;\r
+import java.util.ArrayList;\r
+import java.util.Calendar;\r
+import java.util.GregorianCalendar;\r
+import java.util.HashMap;\r
+import java.util.regex.Matcher;\r
+import java.util.regex.Pattern;\r
+\r
+import javax.swing.JComponent;\r
+import javax.swing.JLabel;\r
+import javax.swing.JLayeredPane;\r
+import javax.swing.JPanel;\r
+import javax.swing.JScrollPane;\r
+import javax.swing.JSplitPane;\r
+import javax.swing.JTree;\r
+import javax.swing.JViewport;\r
+import javax.swing.SpringLayout;\r
+import javax.swing.border.LineBorder;\r
+import javax.swing.event.MouseInputListener;\r
+import javax.swing.event.TreeExpansionEvent;\r
+import javax.swing.event.TreeExpansionListener;\r
+import javax.swing.event.TreeSelectionEvent;\r
+import javax.swing.event.TreeSelectionListener;\r
+import javax.swing.tree.DefaultMutableTreeNode;\r
+import javax.swing.tree.TreeNode;\r
+import javax.swing.tree.TreePath;\r
+import javax.swing.tree.TreeSelectionModel;\r
+import javax.swing.tree.DefaultTreeModel;\r
+\r
+import tainavi.TVProgram.ProgSubtype;\r
+import tainavi.TVProgram.ProgType;\r
+import tainavi.TVProgramIterator.IterationType;\r
+import tainavi.VWMainWindow.MWinTab;\r
+\r
+/**\r
+ * 新聞形式タブのクラス\r
+ * @since 3.15.4β {@link Viewer}から分離\r
+ */\r
+public abstract class AbsPaperView extends JPanel implements VWTimerRiseListener {\r
+\r
+       private static final long serialVersionUID = 1L;\r
+\r
+       public static String getViewName() { return "新聞形式"; }\r
+       \r
+       //public void setDebug(boolean b) { debug = b; }\r
+       //private static boolean debug = false;\r
+       \r
+       \r
+       /*******************************************************************************\r
+        * 抽象メソッド\r
+        ******************************************************************************/\r
+       \r
+       protected abstract Env getEnv();\r
+       protected abstract Bounds getBoundsEnv();\r
+       protected abstract PaperColorsMap getPaperColorMap();\r
+       protected abstract ChannelSort getChannelSort();\r
+       \r
+       protected abstract TVProgramList getTVProgramList();\r
+       protected abstract HDDRecorderList getRecorderList();\r
+       \r
+       protected abstract StatusWindow getStWin(); \r
+       protected abstract StatusTextArea getMWin();\r
+       \r
+       protected abstract AbsReserveDialog getReserveDialog();\r
+       protected abstract Component getParentComponent();\r
+       \r
+       protected abstract void ringBeep();\r
+       \r
+       // クラス内のイベントから呼び出されるもの\r
+       \r
+       /**\r
+        * タブが開いた\r
+        */\r
+       protected abstract void onShown();\r
+       /**\r
+        * タブが閉じた\r
+        */\r
+       protected abstract void onHidden();\r
+       \r
+       /**\r
+        * マウス右クリックメニューを表示する\r
+        */\r
+       protected abstract void showPopupForTraceProgram(\r
+                       final JComponent comp,\r
+                       final ProgDetailList tvd, final String keyword, final int threshold,\r
+                       final int x, final int y, final int h\r
+                       );\r
+\r
+       /**\r
+        * 予約マーク・予約枠を更新してほしい\r
+        */\r
+       protected abstract void updateReserveDisplay();\r
+       \r
+       /**\r
+        * ピックアップに追加してほしい\r
+        */\r
+       protected abstract void addToPickup(final ProgDetailList tvd);\r
+       \r
+       protected abstract boolean isTabSelected(MWinTab tab);\r
+       protected abstract void setSelectedTab(MWinTab tab);\r
+\r
+       protected abstract String getSelectedRecorderOnToolbar();\r
+       protected abstract boolean isFullScreen();\r
+       /**\r
+        * ページャーコンボボックスを更新してほしい\r
+        */\r
+       protected abstract void setPagerEnabled(boolean b);\r
+       protected abstract int getPagerCount();\r
+       protected abstract int getSelectedPagerIndex();\r
+       protected abstract void setSelectedPagerIndex(int idx);\r
+       protected abstract void setPagerItems(TVProgramIterator pli, int curindex);\r
+\r
+       protected abstract String getExtensionMark(ProgDetailList tvd);\r
+       protected abstract String getOptionMark(ProgDetailList tvd);\r
+       protected abstract String getPostfixMark(ProgDetailList tvd);\r
+\r
+       /**\r
+        * ツリーペーンの幅の変更を保存してほしい\r
+        */\r
+       protected abstract void setDividerEnvs(int loc);\r
+       \r
+       \r
+       \r
+       /*******************************************************************************\r
+        * 呼び出し元から引き継いだもの\r
+        ******************************************************************************/\r
+       \r
+       // オブジェクト\r
+       private final Env env = getEnv();\r
+       private final Bounds bounds = getBoundsEnv();\r
+       private final PaperColorsMap pColors = getPaperColorMap();\r
+       private final ChannelSort chsort = getChannelSort();\r
+       \r
+       private final TVProgramList tvprograms = getTVProgramList();\r
+       private final HDDRecorderList recorders = getRecorderList();\r
+\r
+       private final StatusWindow StWin = getStWin();                  // これは起動時に作成されたまま変更されないオブジェクト\r
+       private final StatusTextArea MWin = getMWin();                  // これは起動時に作成されたまま変更されないオブジェクト\r
+       private final AbsReserveDialog rD = getReserveDialog(); // これは起動時に作成されたまま変更されないオブジェクト\r
+       \r
+       private final Component parent = getParentComponent();  // これは起動時に作成されたまま変更されないオブジェクト\r
+\r
+       // メソッド\r
+       private void StdAppendMessage(String message) { System.out.println(message); }\r
+       private void StdAppendError(String message) { System.err.println(message); }\r
+       //private void StWinSetVisible(boolean b) { StWin.setVisible(b); }\r
+       //private void StWinSetLocationCenter(Component frame) { CommonSwingUtils.setLocationCenter(frame, (VWStatusWindow)StWin); }\r
+\r
+       \r
+       \r
+       /*******************************************************************************\r
+        * 定数\r
+        ******************************************************************************/\r
+\r
+       private final String MSGID = "["+getViewName()+"] ";\r
+       private final String ERRID = "[ERROR]"+MSGID;\r
+       private final String DBGID = "[DEBUG]"+MSGID;\r
+\r
+       private final int DASHBORDER_LENGTH = 6;        // ダッシュの長さ\r
+       private final int DASHBORDER_SPACE = 4;         // ダッシュの間隔\r
+       \r
+       private static final String TreeExpRegFile_Paper = "env"+File.separator+"tree_expand_paper.xml";\r
+       \r
+       private static final int TIMEBAR_START = Viewer.TIMEBAR_START;\r
+       \r
+       // 定数ではないが\r
+       \r
+       /**\r
+        * 現在時刻追従スクロールで日付がかわったかどうかを確認するための情報を保持する\r
+        */\r
+       private String prevDT4Now = CommonUtils.getDate529(0,true);\r
+       private String prevDT4Tree = prevDT4Now;\r
+       \r
+       /**\r
+        * 番組枠フレームバッファのサイズ\r
+        */\r
+       private int framebuffersize = 512;\r
+       \r
+       \r
+       /*******************************************************************************\r
+        * 部品\r
+        ******************************************************************************/\r
+       \r
+       // コンポーネント\r
+       \r
+       private JDetailPanel jTextPane_detail = null;\r
+       private JSplitPane jSplitPane_view = null;\r
+       private JPanel jPanel_tree = null;\r
+       private JScrollPane jScrollPane_tree_top = null;\r
+       private JTreeLabel jLabel_tree = null;\r
+       private JScrollPane jScrollPane_tree = null;\r
+       private JTree jTree_tree = null;\r
+       private JScrollPane jScrollPane_space_main = null;\r
+       private JLayeredPane jLayeredPane_space_main_view = null;\r
+       private ArrayList<JTaggedLayeredPane> jLayeredPane_space_main_view_byDate = null;\r
+       private JLayeredPane jLayeredPane_space_main_view_byMakeshift = null; \r
+       private JPanel jPanel_space_top_view = null;\r
+       private JPanel jPanel_space_side_view = null;\r
+       private JViewport vport = null;\r
+       \r
+       private final JTimeline jLabel_timeline = new JTimeline();\r
+       \r
+       private DefaultMutableTreeNode paperRootNode = null;    // 新聞形式のツリー\r
+       private DefaultMutableTreeNode dateNode = null;\r
+       private DefaultMutableTreeNode dgNode = null;\r
+       private DefaultMutableTreeNode bsNode = null;\r
+       private DefaultMutableTreeNode csNode = null;\r
+       private DefaultMutableTreeNode centerNode = null;\r
+       private DefaultMutableTreeNode passedNode = null;\r
+\r
+       private DefaultMutableTreeNode defaultNode = null;\r
+\r
+       // コンポーネント以外\r
+       \r
+       // 番組枠をしまっておくバッファ(newが遅いので一回作ったら捨てない)\r
+       private ArrayList<JTXTButton> frameUsed = new ArrayList<JTXTButton>();                  // 画面に表示されている番組枠\r
+       private ArrayList<JTXTButton> frameUnused = new ArrayList<JTXTButton>();                // 未使用の予備\r
+       private ArrayList<JTXTButton> frameUsedByDate = new ArrayList<JTXTButton>();    // 高速描画時の日付別ペーンに表示されている番組枠。高速描画時も、過去ログはframeUsedが使われる\r
+\r
+       // 予約枠をしまっておくバッファ(検索用)\r
+       private ArrayList<JRMLabel> reserveBorders = new ArrayList<JRMLabel>();\r
+       \r
+       // ツリーの展開状態の保存場所\r
+       TreeExpansionReg ter = null;\r
+       \r
+       DefaultMutableTreeNode nowNode = null;\r
+       \r
+       // 現在放送中のタイマー\r
+       private boolean timer_now_enabled = false;\r
+       \r
+       private IterationType cur_tuner = null;\r
+       \r
+       \r
+       // 予約待機枠と番組枠\r
+       private final DashBorder dborder = new DashBorder(Color.RED,env.getMatchedBorderThickness(),DASHBORDER_LENGTH,DASHBORDER_SPACE);\r
+       private final LineBorder lborder = new ChippedBorder(Color.BLACK,1);\r
+       \r
+       private float paperHeightZoom = 1.0F;\r
+       \r
+       /**\r
+        * 現在時刻線のオブジェクト\r
+        */\r
+       private class JTimeline extends JLabel {\r
+               \r
+               private static final long serialVersionUID = 1L;\r
+               \r
+               private int minpos = 0;\r
+               \r
+               public int setMinpos(int x, int minpos, float multiplier) {\r
+                       if ( minpos >= 0 ) {\r
+                               this.minpos = minpos;\r
+                       }\r
+                       \r
+                       int timeline = Math.round(this.minpos*multiplier);\r
+                       this.setLocation(x,timeline);\r
+                       \r
+                       return timeline;\r
+               }\r
+       }\r
+       \r
+       /*******************************************************************************\r
+        * コンストラクタ\r
+        ******************************************************************************/\r
+       \r
+       public AbsPaperView() {\r
+               \r
+               super();\r
+               \r
+               this.setLayout(new BorderLayout());\r
+               this.add(getJTextPane_detail(), BorderLayout.PAGE_START);\r
+               this.add(getJSplitPane_view(), BorderLayout.CENTER);\r
+               \r
+               // タブが開いたり閉じたりしたときの処理\r
+               this.addComponentListener(cl_shownhidden);\r
+       }\r
+       \r
+       \r
+       \r
+       /*******************************************************************************\r
+        * アクション\r
+        ******************************************************************************/\r
+       \r
+       // 主に他のクラスから呼び出されるメソッド\r
+       \r
+       public String getFrameBufferStatus() { return String.format("%d/%d",frameUsed.size(),framebuffersize); }\r
+\r
+       /**\r
+        * 現在日時表示にリセット\r
+        */\r
+       public void jumpToNow() {\r
+               if ( nowNode != null ) {\r
+                       TreePath tp = new TreePath(nowNode.getPath());\r
+                       jTree_tree.setSelectionPath(null);\r
+                       jTree_tree.setSelectionPath(tp);\r
+               }\r
+       }\r
+       \r
+       /**\r
+        * リスト形式・本体予約一覧からの目的の番組へジャンプ\r
+        */\r
+       public boolean jumpToBangumi(String center, String startdt) {\r
+               \r
+               // ページャーは効くよ\r
+               if ( env.isPagerEnabled() ) {\r
+                       setPagerEnabled(true);\r
+               }\r
+               \r
+               // タイマーは止める\r
+               stopTimer();\r
+\r
+               // 日付群\r
+               GregorianCalendar c = CommonUtils.getCalendar(startdt);\r
+               String adate = CommonUtils.getDate(c);\r
+               String atime = CommonUtils.getTime(c);\r
+               String adate529 = CommonUtils.getDate529(c,true);\r
+               \r
+               // 指定日付に移動して放送局の位置を確認する\r
+               TVProgramIterator pli = redrawByDateWithCenter(center,adate529);\r
+               if ( pli == null ) {\r
+                       // どちらにもない\r
+                       MWin.appendError(ERRID+"ジャンプ先の日付がみつかりません: "+adate529);\r
+                       ringBeep();\r
+                       return false;\r
+               }\r
+               \r
+               // 新聞形式に移動\r
+               if ( ! isTabSelected(MWinTab.PAPER) ) {\r
+                       setSelectedTab(MWinTab.PAPER);\r
+               }\r
+                       \r
+               // 横の列\r
+               int crindex = pli.getIndex(center);\r
+               if ( crindex == -1 ) {\r
+                       MWin.appendError(ERRID+"「"+center+"」は有効な放送局ではありません");\r
+                       ringBeep();\r
+                       return false;\r
+               }\r
+               \r
+               int x = 0;\r
+               if ( env.isPagerEnabled() ) {\r
+                       int idx = env.getPageIndex(1+crindex);\r
+                       x = (crindex - idx*env.getCenterPerPage()) * bounds.getBangumiColumnWidth();\r
+               }\r
+               else {\r
+                       x = crindex * bounds.getBangumiColumnWidth();\r
+               }\r
+               \r
+               // 縦の列\r
+               int h = 0;\r
+               int m = 0;\r
+               int y = 0;\r
+               Matcher ma = Pattern.compile("^(\\d\\d):(\\d\\d)$").matcher(atime);\r
+               if (ma.find()) {\r
+                       h = Integer.valueOf(ma.group(1));\r
+                       m = Integer.valueOf(ma.group(2));\r
+               }\r
+               if (adate529.equals(adate)) {\r
+                       if (h < TIMEBAR_START) {\r
+                               h = TIMEBAR_START;\r
+                               m = 0;\r
+                       }\r
+               }\r
+               else {\r
+                       h += 24;\r
+               }\r
+               y = Math.round((float)((h-TIMEBAR_START)*60+m)*bounds.getPaperHeightMultiplier()*paperHeightZoom);\r
+               \r
+               // 新聞面を移動する\r
+               {\r
+                       // Viewのサイズ変更をJavaまかせにすると実際に表示されるまで変更されないので明示的に変更しておく\r
+                       Dimension dm = vport.getView().getPreferredSize();\r
+                       vport.setViewSize(dm);\r
+                       \r
+                       // 一旦位置情報をリセットする\r
+                       Point pos = new Point(0, 0);\r
+                       //vport.setViewPosition(pos);\r
+                       \r
+                       Rectangle ra = vport.getViewRect();\r
+                       pos.x = x + bounds.getBangumiColumnWidth()/2 - ra.width/2;\r
+                       pos.y = y - ra.height/4;\r
+                       \r
+                       // ViewのサイズがViewPortのサイズより小さい場合はsetViewPosition()が正しく動作しないので0にする\r
+                       if (pos.x < 0 || dm.width < ra.width) {\r
+                               pos.x=0;\r
+                       }\r
+                       else if ((dm.width - ra.width) < pos.x) {\r
+                               pos.x = dm.width - ra.width;\r
+                       }\r
+                       \r
+                       if (pos.y < 0 || dm.height < ra.height)  {\r
+                               pos.y=0;\r
+                       }\r
+                       else if ((dm.height - ra.height) < pos.y) {\r
+                               pos.y = dm.height - ra.height;\r
+                       }\r
+                       \r
+                       vport.setViewPosition(pos);\r
+               }\r
+               \r
+               // マウスカーソルを移動する\r
+               {\r
+                       Point sc = vport.getLocationOnScreen();\r
+                       Point pos = vport.getViewPosition();\r
+                       \r
+                       Point loc = new Point();\r
+                       loc.x = sc.x + (x + bounds.getBangumiColumnWidth()/2) - pos.x;\r
+                       loc.y = sc.y + (y + Math.round(5*bounds.getPaperHeightMultiplier()*paperHeightZoom)) - pos.y;\r
+                       \r
+                       try {\r
+                               Robot robo = new Robot();\r
+                               robo.mouseMove(loc.x,loc.y);\r
+                               robo = null;\r
+                       } catch (AWTException e) {\r
+                               e.printStackTrace();\r
+                       }\r
+               }\r
+               \r
+               return true;\r
+       }\r
+       \r
+       /**\r
+        * ページャーによる再描画\r
+        */\r
+       public boolean redrawByPager() {\r
+                \r
+               //JTreeLabel.Nodes node = jLabel_tree.getNode();\r
+               String value = jLabel_tree.getValue();\r
+\r
+               if ( value != null ) {\r
+                       if ( JTreeLabel.Nodes.NOW.getLabel().equals(value) ) {\r
+                               redrawByNow(cur_tuner);\r
+                       }\r
+                       else {\r
+                               redrawByDate(value, cur_tuner);\r
+                       }\r
+\r
+                       return true;\r
+               }\r
+               \r
+               return false;\r
+       }\r
+       \r
+       /**\r
+        * 予約待機赤枠の描画(全部)\r
+        * @see #putReserveBorder(String, String, int)\r
+        */\r
+       public void updateReserveBorder(String center) {\r
+               \r
+               // 予約の赤枠を表示する(上:日付別表示中、下:放送局別表示中)\r
+               \r
+               JTreeLabel.Nodes node = jLabel_tree.getNode();\r
+               String value = jLabel_tree.getValue();\r
+               \r
+               switch ( node ) {\r
+               case DATE:\r
+               case TERRA:\r
+               case BS:\r
+               case CS:\r
+                       {\r
+                               // 日付別は4種類ある\r
+                               IterationType sTyp;\r
+                               switch ( node ) {\r
+                               case TERRA:\r
+                                       sTyp = IterationType.TERRA;\r
+                                       break;\r
+                               case BS:\r
+                                       sTyp = IterationType.BS;\r
+                                       break;\r
+                               case CS:\r
+                                       sTyp = IterationType.CS;\r
+                                       break;\r
+                               default:\r
+                                       sTyp = IterationType.ALL;\r
+                                       break;\r
+                               }\r
+                               \r
+                               // "現在日付"を現在日付にする\r
+                               String dt = value;\r
+                               if ( JTreeLabel.Nodes.NOW.getLabel().equals(dt) ) {\r
+                                       dt = CommonUtils.getDate529(0,true);\r
+                               }\r
+                               \r
+                               TVProgramIterator pli = tvprograms.getIterator().build(chsort.getClst(), sTyp);\r
+                               \r
+                               // ページャーが有効なら表示すべきページ番号を取得する\r
+                               int colmin = 0;\r
+                               int colmax = pli.size();\r
+                               int divider = 0;\r
+                               if ( env.isPagerEnabled() ) {\r
+                                       int selectedpage = getSelectedPagerIndex();     // 予約枠の描画なのだから、ページ移動の必要はないはずだ\r
+                                       if ( selectedpage >= 0 ) {\r
+                                               colmin = env.getCenterPerPage() * selectedpage;\r
+                                               colmax = colmin + env.getCenterPerPage()-1;\r
+                                               divider = env.getCenterPerPage();\r
+                                       }\r
+                                       else {\r
+                                               StWin.appendError(ERRID+"ページャーコンボボックスが不正です: "+selectedpage);\r
+                                               return;\r
+                                       }\r
+                               }\r
+       \r
+                               if ( center != null ) {\r
+                                       // 特定の放送局のみ更新\r
+                                       int cnt = pli.getIndex(center);\r
+                                       if ( colmin <= cnt && cnt <= colmax ) {\r
+                                               int col = (divider==0) ? (cnt) : (cnt % divider);\r
+                                               putReserveBorder(dt, center, col);\r
+                                       }\r
+                               }\r
+                               else {\r
+                                       // すべての放送局を更新\r
+                                       int cnt = -1;\r
+                                       for ( ProgList pl : pli ) {\r
+                                               ++cnt;\r
+                                               if ( cnt < colmin ) {\r
+                                                       continue;\r
+                                               }\r
+                                               else if ( cnt > colmax ) {\r
+                                                       break;\r
+                                               }\r
+                                               \r
+                                               int col = (divider==0) ? (cnt) : (cnt % divider);\r
+                                               \r
+                                               putReserveBorder(dt, pl.Center, col);\r
+                                       }\r
+                               }\r
+                       }\r
+                       break;\r
+                       \r
+               case BCAST:\r
+                       {\r
+                               \r
+                               if (center != null && ! center.equals(value)) {\r
+                                       // 更新の必要はない\r
+                                       return;\r
+                               }\r
+                               if (center == null) {\r
+                                       // 選択中の放送局で更新する\r
+                                       center = value;\r
+                               }\r
+                               \r
+                               TVProgramIterator pli = tvprograms.getIterator().build(chsort.getClst(), IterationType.ALL);\r
+                               int cnt = tvprograms.getIterator().getIndex(center);\r
+                               if ( cnt == -1 ) {\r
+                                       MWin.appendError(ERRID+"「"+center+"」は有効な放送局ではありません");\r
+                               }\r
+                               else {\r
+                                       ProgList pl = pli.getP();\r
+                                       for (int col=0; col<pl.pdate.size(); col++) {\r
+                                               // 予約の赤枠を一週間分表示する\r
+                                               putReserveBorder(pl.pdate.get(col).Date, center, col);\r
+                                       }\r
+                               }\r
+                       }\r
+                       break;\r
+               \r
+               default:\r
+                       break;\r
+               }\r
+       }\r
+       \r
+       /**\r
+        * \r
+        */\r
+       public void updateBangumiColumns() {\r
+               for (JTXTButton b : frameUsed ) {\r
+                       ProgDetailList tvd = b.getInfo();\r
+                       if ( tvd.type == ProgType.PROG ) {\r
+                               if (bounds.getShowMatchedBorder() && b.isStandby()) {\r
+                                       if ( b.getBorder() != dborder )\r
+                                               b.setBorder(dborder);\r
+                               }\r
+                               else {\r
+                                       if ( b.getBorder() != lborder )\r
+                                               b.setBorder(lborder);\r
+                               }\r
+                       }\r
+               }\r
+       }\r
+       \r
+       /**\r
+        * 予約待機赤枠の描画(ツールバーからのトグル操作)\r
+        */\r
+       public boolean toggleMatchBorder() {\r
+               \r
+               // 状態を保存\r
+               bounds.setShowMatchedBorder( ! bounds.getShowMatchedBorder());\r
+               \r
+               _updPBorders(env, bounds, frameUsed);\r
+               \r
+               if ( env.getDrawcacheEnable() ) {\r
+                       _updPBorders(env, bounds, frameUsedByDate);\r
+               }\r
+               \r
+               return bounds.getShowMatchedBorder();\r
+       }\r
+\r
+       \r
+       /**\r
+        * 新聞枠の拡縮(ツールバーからの操作)\r
+        */\r
+       public void setZoom(int n) {\r
+               paperHeightZoom = n * 0.01F;\r
+               updateBounds(env, bounds);\r
+               updateRepaint();\r
+       }\r
+\r
+       /**\r
+        * 新聞ペーンをクリアする。\r
+        */\r
+       public void clearPanel() {\r
+               \r
+               // 番組枠の初期化\r
+               /* 移動しました */\r
+               \r
+               // 予約枠の初期化\r
+               for ( JRMLabel b : reserveBorders) {\r
+                       jLayeredPane_space_main_view.remove(b);\r
+               }\r
+               reserveBorders.clear();\r
+               \r
+               // タイムラインの削除\r
+               if (jLabel_timeline != null && jLayeredPane_space_main_view != null) {\r
+                       jLayeredPane_space_main_view.remove(jLabel_timeline);\r
+               }\r
+               \r
+               // 時間枠・日付枠・放送局枠の初期化\r
+               jPanel_space_top_view.removeAll();\r
+               redrawTimebar(jPanel_space_side_view);\r
+               \r
+               // 選択してないことにする\r
+               //paper.jLabel_tree.setText("");\r
+       }\r
+       \r
+       /**\r
+        * <P>新聞ペーンを選択する。\r
+        * <P>高速描画ONの場合は、主ペーンのほかに複数の日付別ペーンが作成されるのでどれを利用するか選択する。\r
+        * @param pane : nullの場合、主ペーンを選択する。過去ログは常にnullで。\r
+        * @see #jLayeredPane_space_main_view_byMakeshift 主ペーン\r
+        * @see #jLayeredPane_space_main_view_byDate 日付別ペーン\r
+        */\r
+       private void selectMainView(JLayeredPane pane) {\r
+               \r
+               // 表示開始位置を記憶する\r
+               Point p = vport.getViewPosition();\r
+               \r
+               if (pane == null) {\r
+                       // 番組枠の初期化\r
+                       StdAppendMessage(MSGID+"番組枠描画バッファをリセット: "+frameUsed.size()+"/"+framebuffersize);\r
+                       for (int i=frameUsed.size()-1; i>=0; i--) {\r
+                               JTXTButton b = frameUsed.remove(i);\r
+                               b.setToolTipText(null);\r
+                               b.clean();\r
+                               frameUnused.add(b);\r
+                               //jLayeredPane_space_main_view_byMakeshift.remove(b);   // 削除しちゃダメよ?\r
+                       }\r
+                       \r
+                       if (jLayeredPane_space_main_view == jLayeredPane_space_main_view_byMakeshift) {\r
+                               return;\r
+                       }\r
+                       jScrollPane_space_main.setViewportView(jLayeredPane_space_main_view = jLayeredPane_space_main_view_byMakeshift);\r
+               }\r
+               else {\r
+                       if (jLayeredPane_space_main_view == pane) {\r
+                               return;\r
+                       }\r
+                       jScrollPane_space_main.setViewportView(jLayeredPane_space_main_view = pane);\r
+               }\r
+               \r
+               // 表示開始位置を戻す\r
+               vport.setViewPosition(p);\r
+       }\r
+       \r
+       /**\r
+        * 現在時刻追従スクロールを開始する\r
+        */\r
+       private void startTimer() {\r
+               timer_now_enabled = true;\r
+       }\r
+       \r
+       /**\r
+        * 現在時刻追従スクロールを停止する\r
+        */\r
+       private boolean stopTimer() {\r
+               prevDT4Now = null;\r
+               jLabel_timeline.setVisible(false);\r
+               return (timer_now_enabled = false);\r
+       }\r
+       \r
+       /**\r
+        * サイドツリーの「現在日時」を選択する\r
+        */\r
+       public void selectTreeDefault() {\r
+               if ( defaultNode != null ) jTree_tree.setSelectionPath(new TreePath(defaultNode.getPath()));\r
+       }\r
+       \r
+       /**\r
+        * サイドツリーの現在選択中のノードを再度選択して描画しなおす\r
+        */\r
+       public void reselectTree() {\r
+               String[] names = new String[] { jLabel_tree.getNode().getLabel(), jLabel_tree.getValue() };\r
+               TreeNode[] nodes = ter.getSelectedPath(paperRootNode, names, 0);\r
+               if (nodes != null) {\r
+                       TreePath tp = new TreePath(nodes);\r
+                       if ( tp != null ) {\r
+                               // 表示位置を記憶\r
+                               Point vp = vport.getViewPosition(); //= SwingUtilities.convertPoint(vport,0,0,label);\r
+                               // ツリー再選択\r
+                               jTree_tree.setSelectionPath(null);\r
+                               jTree_tree.setSelectionPath(tp);\r
+                               // 表示位置を復帰\r
+                               if (vp.x != 0 && vp.y != 0) {\r
+                                       jLayeredPane_space_main_view.scrollRectToVisible(new Rectangle(vp, vport.getSize()));\r
+                               }\r
+                       }\r
+               }\r
+       }\r
+       \r
+       /**\r
+        * サイドツリーを開く\r
+        */\r
+       public void setExpandTree() {\r
+               jSplitPane_view.setDividerLocation(bounds.getTreeWidthPaper());\r
+               jScrollPane_tree.setVerticalScrollBarPolicy(JScrollPane.VERTICAL_SCROLLBAR_ALWAYS);\r
+               jScrollPane_tree.setHorizontalScrollBarPolicy(JScrollPane.HORIZONTAL_SCROLLBAR_ALWAYS);\r
+       }\r
+       \r
+       /**\r
+        * サイドツリーを閉じる\r
+        */\r
+       public void setCollapseTree() {\r
+               jSplitPane_view.setDividerLocation(bounds.getMinDivLoc());\r
+               jScrollPane_tree.setVerticalScrollBarPolicy(JScrollPane.VERTICAL_SCROLLBAR_NEVER);\r
+               jScrollPane_tree.setHorizontalScrollBarPolicy(JScrollPane.HORIZONTAL_SCROLLBAR_NEVER);\r
+       }\r
+       \r
+       /**\r
+        * サイドツリーの展開状態を設定ファイルに保存(鯛ナビ終了時に呼び出される)\r
+        */\r
+       public void saveTreeExpansion() {\r
+               ter.save();\r
+       }\r
+       \r
+       /**\r
+        * 画面上部の番組詳細領域の表示のON/OFF\r
+        */\r
+       public void setDetailVisible(boolean aFlag) {\r
+               jTextPane_detail.setVisible(aFlag);\r
+       }\r
+       \r
+       /**\r
+        * スクリーンショット用\r
+        */\r
+       public Component getCenterPane() {\r
+               return jPanel_space_top_view;\r
+       }\r
+       \r
+       /**\r
+        * スクリーンショット用\r
+        */\r
+       public Component getTimebarPane() {\r
+               return jPanel_space_side_view;\r
+       }\r
+       \r
+       /**\r
+        * スクリーンショット用\r
+        */\r
+       public Component getCurrentPane() {\r
+               return jLayeredPane_space_main_view;\r
+       }\r
+       \r
+       /**\r
+        * スクリーンショット用\r
+        */\r
+       public String getCurrentView() {\r
+               return jLabel_tree.getView();\r
+       }\r
+       \r
+       /**\r
+        * 高速描画ONの場合に日付別ペーンを一気に全部描画する\r
+        */\r
+       public void buildMainViewByDate() {\r
+               \r
+               if (env.getDebug()) System.out.println(DBGID+"CALLED buildMainViewByDate()");\r
+               \r
+               if (jLayeredPane_space_main_view_byMakeshift == null) {\r
+                       jLayeredPane_space_main_view_byMakeshift = new JLayeredPane();\r
+                       for (int i=0; i<framebuffersize; i++) {\r
+                               JTXTButton b2 = new JTXTButton();\r
+                               b2.clean();\r
+                               jLayeredPane_space_main_view_byMakeshift.add(b2);\r
+                               jLayeredPane_space_main_view_byMakeshift.setLayer(b2, 0);\r
+                               \r
+                               // リスナーを設定する\r
+                               b2.addMouseListener(mListner);\r
+                               b2.addMouseMotionListener(mListner);\r
+                               frameUnused.add(b2);\r
+                       }\r
+                       StdAppendMessage(MSGID+"番組枠描画バッファを初期化: "+framebuffersize);\r
+               }\r
+               \r
+               // ページャー機能とは排他\r
+               if (env.isPagerEnabled() || ! env.getDrawcacheEnable()) {\r
+                       jLayeredPane_space_main_view_byDate = null;\r
+                       return;\r
+               }\r
+               \r
+               jLayeredPane_space_main_view_byDate = new ArrayList<JTaggedLayeredPane>();\r
+               frameUsedByDate = new ArrayList<JTXTButton>();\r
+               new SwingBackgroundWorker(true) {\r
+                       \r
+                       @Override\r
+                       protected Object doWorks() throws Exception {\r
+                               //\r
+                               int dogDays = (env.getExpandTo8())?(8):(7);\r
+                               //\r
+                               for ( int y=0; y < dogDays; y++ ) {\r
+                                       jLayeredPane_space_main_view_byDate.add(new JTaggedLayeredPane());\r
+                               }\r
+                               for ( int y=0; y < dogDays; y++ ) {\r
+                                       String day = CommonUtils.getDate529(y*86400,true);\r
+                                       \r
+                                       jLayeredPane_space_main_view_byDate.get(y).setTagstr(day);\r
+                                       \r
+                                       StWin.appendMessage(MSGID+"番組表を構築します:"+day);\r
+                                       redrawByDate(day,IterationType.ALL);\r
+                               }\r
+                               return null;\r
+                       }\r
+                       \r
+                       @Override\r
+                       protected void doFinally() {\r
+                       }\r
+               }.execute();\r
+       }\r
+\r
+       /**\r
+        * ツリーのリスナーを止める\r
+        */\r
+       private void stopTreeListener() {\r
+               jTree_tree.removeTreeSelectionListener(tsl_nodeselected);\r
+       }\r
+       \r
+       /**\r
+        * ツリーのリスナーを動かす\r
+        */\r
+       private void startTreeListener() {\r
+               jTree_tree.addTreeSelectionListener(tsl_nodeselected);\r
+       }\r
+       \r
+       /**\r
+        * 日付でサブノード作成\r
+        */\r
+       public  void redrawTreeByDate() {\r
+               \r
+               stopTreeListener();\r
+               TreePath tp = jTree_tree.getSelectionPath();\r
+               \r
+               _redrawTreeByDate(dateNode);\r
+               _redrawTreeByDate(dgNode);\r
+               _redrawTreeByDate(bsNode);\r
+               _redrawTreeByDate(csNode);\r
+               \r
+               jTree_tree.setSelectionPath(tp);\r
+               jTree_tree.updateUI();\r
+               startTreeListener();\r
+       }\r
+       \r
+       private void _redrawTreeByDate(DefaultMutableTreeNode parent) {\r
+               \r
+               // ★★★ でふぉるとのーど ★★★\r
+               DefaultMutableTreeNode nNode = new DefaultMutableTreeNode(JTreeLabel.Nodes.NOW.getLabel());\r
+               if ( parent == dateNode ) {\r
+                       nowNode = nNode;\r
+                       defaultNode = nNode;\r
+               }\r
+               \r
+               parent.removeAllChildren();\r
+               parent.add(nNode);\r
+               int dogDays = (env.getExpandTo8())?(8):(7);\r
+               for ( int i=0; i<dogDays; i++ ) {\r
+                       parent.add(new DefaultMutableTreeNode(CommonUtils.getDate529(i*86400, true)));\r
+               }\r
+       }\r
+\r
+       /**\r
+        * 放送局名でサブノード作成\r
+        */\r
+       public void redrawTreeByCenter() {\r
+\r
+               stopTreeListener();\r
+               TreePath tp = jTree_tree.getSelectionPath();\r
+               \r
+               centerNode.removeAllChildren();\r
+               TVProgramIterator pli = tvprograms.getIterator();\r
+               pli.build(chsort.getClst(), IterationType.ALL);\r
+               for ( ProgList pl : pli ) {\r
+                       centerNode.add(new DefaultMutableTreeNode(pl.Center));\r
+               }\r
+               \r
+               jTree_tree.setSelectionPath(tp);\r
+               jTree_tree.updateUI();\r
+               startTreeListener();\r
+       }\r
+\r
+       /**\r
+        * 過去ログ日付でサブノード作成\r
+        */\r
+       public void redrawTreeByPassed() {\r
+\r
+               stopTreeListener();\r
+               TreePath tp = jTree_tree.getSelectionPath();\r
+               \r
+               passedNode.removeAllChildren();\r
+               if ( env.getUsePassedProgram() ) {\r
+                       String[] dd = new PassedProgram().getDateList(env.getPassedLogLimit());\r
+                       for ( int i=1; i<dd.length && i<=env.getPassedLogLimit(); i++ ) {\r
+                               passedNode.add(new DefaultMutableTreeNode(dd[i]));\r
+                       }\r
+               }\r
+               \r
+               jTree_tree.setSelectionPath(tp);\r
+               jTree_tree.updateUI();\r
+               startTreeListener();\r
+       }\r
+\r
+       /**\r
+        *  時刻の列を生成\r
+        */\r
+       private void redrawTimebar(JPanel jp)\r
+       {\r
+               jp.removeAll();\r
+               \r
+               float phm60 = 60.0F * bounds.getPaperHeightMultiplier() * paperHeightZoom;\r
+               \r
+               for (int row=0; row<24; row++) {\r
+                       \r
+                       int hour = row+TIMEBAR_START;\r
+                       \r
+                       JTimebarLabel b0 = new JTimebarLabel(Integer.toString(hour));\r
+                       \r
+                       if ( hour >=6 && hour <= 11 ) {\r
+                               b0.setBackground(env.getTimebarColor());\r
+                       }\r
+                       else if ( hour >=12 && hour <= 17 ) {\r
+                               b0.setBackground(env.getTimebarColor2());\r
+                       }\r
+                       else if ( hour >=18 && hour <= 23 ) {\r
+                               b0.setBackground(env.getTimebarColor3());\r
+                       }\r
+                       else {\r
+                               b0.setBackground(env.getTimebarColor4());\r
+                       }\r
+                       b0.setOpaque(true);\r
+                       b0.setBorder(lborder);\r
+                       b0.setHorizontalAlignment(JLabel.CENTER);\r
+                       \r
+                       b0.setBounds(\r
+                                       0,\r
+                                       (int) Math.ceil((float)row*phm60),\r
+                                       bounds.getTimebarColumnWidth(),\r
+                                       (int) Math.ceil(phm60));\r
+                       \r
+                       jp.add(b0);\r
+               }\r
+               \r
+               Dimension d = jp.getMaximumSize();\r
+               d.width = bounds.getTimebarColumnWidth();\r
+               d.height = (int) Math.ceil(24*phm60);\r
+               jp.setPreferredSize(d);\r
+               jp.updateUI();\r
+       }\r
+       \r
+\r
+       /**\r
+        * 現在日時に移動する\r
+        * @see #redrawByDate(String, IterationType)\r
+        */\r
+       private void redrawByNow(final IterationType tuner) {\r
+               \r
+               // 古いタイマーの削除\r
+               stopTimer();\r
+               \r
+               // 移動汁!!\r
+               redrawByDate(CommonUtils.getDate529(0,true),tuner);\r
+               \r
+               // 時間線をひく\r
+               jLabel_timeline.setVisible(true);\r
+               Dimension dm = jLayeredPane_space_main_view.getPreferredSize();\r
+               //dm.height = Math.round(1*bounds.getPaperHeightMultiplier()*paperHeightZoom);\r
+               dm.height = 3;\r
+               jLabel_timeline.setBorder(new LineBorder(Color.RED,2));\r
+               jLabel_timeline.setBackground(Color.RED);\r
+               jLabel_timeline.setOpaque(true);\r
+               jLabel_timeline.setBounds(0, 0, dm.width, dm.height);\r
+               jLayeredPane_space_main_view.add(jLabel_timeline);\r
+               jLayeredPane_space_main_view.setLayer(jLabel_timeline, 2);\r
+               \r
+               // 現在時刻線のリセット\r
+               setTimelinePos(true);\r
+               \r
+               // 新しいタイマーの作成(1分ごとに線を移動する)\r
+               startTimer();\r
+       }\r
+       \r
+       /**\r
+        * 日付別に表を作成する\r
+        * @see #_redrawByDateWithCenter(String, String, IterationType)\r
+        */\r
+       private TVProgramIterator redrawByDate(String date, IterationType tuner) {\r
+               return _redrawByDateWithCenter(null,date,tuner);\r
+       }\r
+       \r
+       /**\r
+        * 日付別に表を作成する(ページャーが有効な場合は指定の放送局のあるページを開く)\r
+        * @see #_redrawByDateWithCenter(String, String, IterationType)\r
+        */\r
+       private TVProgramIterator redrawByDateWithCenter(String center, String date) {\r
+               \r
+               // 今日は?\r
+               String ndate529 = CommonUtils.getDate529(0, true);\r
+               \r
+               // 過去ログかどうか\r
+               IterationType tuner;\r
+               JTreeLabel.Nodes node;\r
+               if ( ndate529.compareTo(date) > 0 ) {\r
+                       tuner = IterationType.PASSED;\r
+                       node = JTreeLabel.Nodes.PASSED;\r
+               }\r
+               else {\r
+                       tuner = IterationType.ALL;\r
+                       node = JTreeLabel.Nodes.DATE;\r
+               }\r
+               \r
+               if ( tuner == IterationType.PASSED ) {\r
+                       // 過去ログの取得\r
+                       PassedProgram passed = tvprograms.getPassed();\r
+                       if ( ! passed.loadAllCenters(date) ) {\r
+                               System.err.println(ERRID+"過去ログの取得に失敗しました: "+date);\r
+                               return null;\r
+                       }\r
+               }\r
+               \r
+               // 番組枠描画\r
+               TVProgramIterator pli = _redrawByDateWithCenter(center, date, tuner);\r
+               \r
+               getJTree_tree().getSelectionModel().setSelectionPath(null); // 選択は解除する\r
+               \r
+               jLabel_tree.setView(node, date);\r
+               \r
+               return pli;\r
+       }\r
+       \r
+       /**\r
+        * 日付別に表を作成する、の本体\r
+        * @see #redrawByDate(String, IterationType)\r
+        * @see #redrawByDateWithCenter(String, String)\r
+        */\r
+       private TVProgramIterator _redrawByDateWithCenter(String center, String date, IterationType tuner) {\r
+               \r
+               if (env.getDebug()) System.out.println(DBGID+"CALLED redrawByDate() date="+date+" IterationType="+tuner);\r
+               \r
+               // 古いタイマーの削除\r
+               stopTimer();\r
+               \r
+               cur_tuner = tuner;\r
+               \r
+               if (date == null) {\r
+                       return null;\r
+               }\r
+\r
+               // イテレータ-\r
+               TVProgramIterator pli = tvprograms.getIterator().build(chsort.getClst(),tuner);\r
+               \r
+               // パネルの初期化\r
+               clearPanel();\r
+               \r
+               // 新聞ペーンの選択\r
+               boolean drawPrograms = true;\r
+               if ( tuner != IterationType.ALL || env.isPagerEnabled() || ! env.getDrawcacheEnable() ) {\r
+                       selectMainView(null);\r
+               }\r
+               else {\r
+                       // 描画速度優先の場合\r
+                       boolean nopane = true;\r
+                       for ( JTaggedLayeredPane tlp : jLayeredPane_space_main_view_byDate ) {\r
+                               if ( tlp.getTagstr().equals(date) ) {\r
+                                       selectMainView(tlp);\r
+                                       if ( tlp.getComponentCountInLayer(0) > 0 ) {\r
+                                               // 描画済みなら再度の描画は不要\r
+                                               drawPrograms = false;\r
+                                       }\r
+                                       nopane = false;\r
+                                       break;\r
+                               }\r
+                       }\r
+                       if ( nopane ) {\r
+                               // 該当日付のPaneがない場合\r
+                               selectMainView(null);\r
+                       }\r
+               }\r
+               \r
+               // ページ制御\r
+               int colmin = 0;\r
+               int colmax = pli.size();\r
+               int divider = 0;\r
+               if ( env.isPagerEnabled() ) {\r
+                       \r
+                       int selectedpage = getSelectedPagerIndex();\r
+                       \r
+                       if ( center == null ) {\r
+                               // とび先の指定がないのでもともと選択されていたページを再度選択したい\r
+                               if ( selectedpage == -1 ) {\r
+                                       // なんか、選択されてないよ?\r
+                                       selectedpage = 0;\r
+                               }\r
+                               else {\r
+                                       int maxindex = env.getPageIndex(pli.size());\r
+                                       if ( selectedpage > maxindex ) {\r
+                                               // ページ数かわったら、インデックスがはみだしちゃった\r
+                                               selectedpage = 0;\r
+                                       }\r
+                               }\r
+                       }\r
+                       else {\r
+                               // 特定の日付の特定の放送局を表示したい\r
+                               int crindex = pli.getIndex(center);\r
+                               if ( crindex ==  -1 ) {\r
+                                       // ここに入ったらバグ\r
+                                       MWin.appendError(ERRID+"「"+center+"」は有効な放送局ではありません");\r
+                                       ringBeep();\r
+                                       crindex = 0;\r
+                               }\r
+                               selectedpage = env.getPageIndex(1+crindex);\r
+                       }\r
+\r
+                       // 開始位置・終了位置・局数\r
+                       colmin = env.getCenterPerPage() * selectedpage;\r
+                       colmax = colmin + (env.getCenterPerPage()-1);\r
+                       divider = env.getCenterPerPage();\r
+                       \r
+                       // ページャーコンボボックスの書き換え\r
+                       setPagerItems(pli,env.getPageIndex(1+colmin));\r
+                       pli.rewind();\r
+                       \r
+                       // ページャーは有効だよ\r
+                       //setPagerEnabled(true);\r
+               }\r
+               \r
+               if (env.getDebug()) System.out.println(DBGID+"[描画開始] ch_start="+colmin+" ch_end="+colmax+" ch_size="+pli.size());\r
+               \r
+               // 番組の枠表示用\r
+               dborder.setDashColor(env.getMatchedBorderColor());\r
+               dborder.setThickness(env.getMatchedBorderThickness());\r
+               \r
+               // 番組表時の共通設定\r
+               updateFonts(env);\r
+               \r
+               // 局列・番組表を作成\r
+               jPanel_space_top_view.setLayout(null);\r
+               int cnt = -1;\r
+               int col = -1;\r
+               for ( ProgList pl : pli ) {\r
+\r
+                       ++cnt;\r
+                       \r
+                       if ( cnt < colmin ) {\r
+                               continue;\r
+                       }\r
+                       else if ( cnt > colmax ) {\r
+                               break;\r
+                       }\r
+                       \r
+                       col = (divider==0) ? (cnt) : (cnt % divider);\r
+                       \r
+                       //TVProgram tvp = tvprograms.get(pli.getSiteId());\r
+                       \r
+                       //if (env.getDebug()) System.out.println(DBGID+"[描画中] "+pl.Center+" min="+colmin+" max="+colmax+" cnt="+cnt+" col="+col+" siteid="+siteid);\r
+                       \r
+                       // 局列\r
+                       JLabel b1 = new JLabel(pl.Center);\r
+                       b1.setOpaque(true);\r
+                       b1.setBackground(pl.BgColor);\r
+                       b1.setBorder(lborder);\r
+                       b1.setHorizontalAlignment(JLabel.CENTER);\r
+                       b1.setBounds(bounds.getBangumiColumnWidth()*col, 0, bounds.getBangumiColumnWidth(), bounds.getBangumiColumnHeight());\r
+                       b1.addMouseListener(cnMouseAdapter);\r
+                       jPanel_space_top_view.add(b1);\r
+                       \r
+                       // 予約の赤枠を表示する\r
+                       if (tuner != IterationType.PASSED) {\r
+                               putReserveBorder(date, pl.Center, col);\r
+                       }\r
+\r
+                       // 番組表\r
+                       if (drawPrograms == true) {\r
+                               putBangumiColumns(pl, col, date);\r
+                       }\r
+               }\r
+               \r
+               ++col; // 描画後にパネルサイズの変更にも使う\r
+               \r
+               if ( ! env.getDrawcacheEnable()) {\r
+                       // 番組枠描画バッファサイズの上限を確認する\r
+                       if (framebuffersize < frameUsed.size()) {\r
+                               framebuffersize = frameUsed.size();\r
+                               StdAppendMessage(MSGID+"番組枠描画バッファの上限を変更: "+frameUsed.size()+"/"+framebuffersize);\r
+                       }\r
+               }\r
+               \r
+               // ページサイズを変更する\r
+               //jPanel_space_top_view.setPreferredSize(new Dimension(bounds.getTimebarColumnWidth()+cnt*bounds.getBangumiColumnWidth(),bounds.getBangumiColumnHeight()));\r
+               jPanel_space_top_view.setPreferredSize(new Dimension(col*bounds.getBangumiColumnWidth(),bounds.getBangumiColumnHeight()));\r
+               jPanel_space_top_view.updateUI();\r
+               \r
+               jLayeredPane_space_main_view.setPreferredSize(new Dimension(bounds.getBangumiColumnWidth()*col,Math.round(24*60*bounds.getPaperHeightMultiplier()*paperHeightZoom)));\r
+               \r
+               if (env.getDebug()) System.out.println(DBGID+"END redrawByDate() date="+date+" IterationType="+tuner);\r
+               \r
+               pli.rewind();\r
+               return pli;\r
+       }\r
+\r
+       /**\r
+        * 放送局別に表を作成する\r
+        */\r
+       private void redrawByCenter(String center)\r
+       {\r
+               // 古いタイマーの削除\r
+               stopTimer();\r
+               \r
+               // ページャーは効かないよ\r
+               if ( env.isPagerEnabled() ) {\r
+                       setPagerEnabled(false);\r
+               }\r
+               \r
+               // パネルの初期化\r
+               clearPanel();\r
+               selectMainView(null);\r
+               \r
+               jPanel_space_top_view.setLayout(null);\r
+               \r
+               for (int a=0; a<tvprograms.size(); a++) {\r
+                       //\r
+                       TVProgram tvp = tvprograms.get(a);\r
+                       //\r
+                       if (tvp.getType() != ProgType.PROG) {\r
+                               continue;\r
+                       }\r
+                       //\r
+                       for (ProgList pl : tvp.getCenters()) {\r
+                               if (pl.enabled == true && pl.Center.equals(center)) {\r
+                                       // 日付ヘッダを描画する\r
+                                       for (int centerid=0; centerid<pl.pdate.size(); centerid++)\r
+                                       {\r
+                                               ProgDateList pcl = pl.pdate.get(centerid);\r
+                                               \r
+                                               JTXTLabel b1 = new JTXTLabel();\r
+                                               GregorianCalendar c = CommonUtils.getCalendar(pcl.Date);\r
+                                               if ( c != null ) {\r
+                                                       String date = CommonUtils.getDate(c);\r
+                                                       b1.setValue(date);\r
+                                                       b1.setText(date.substring(5));\r
+                                                       b1.setOpaque(true);\r
+                                                       if ( c.get(Calendar.DAY_OF_WEEK) == Calendar.SATURDAY ) {\r
+                                                               b1.setBackground(new Color(90,90,255));\r
+                                                       }\r
+                                                       else if ( c.get(Calendar.DAY_OF_WEEK) == Calendar.SUNDAY ) {\r
+                                                               b1.setBackground(new Color(255,90,90));\r
+                                                       }\r
+                                                       else {\r
+                                                               b1.setBackground(new Color(180,180,180));\r
+                                                       }\r
+                                               }\r
+                                               b1.setBorder(lborder);\r
+                                               b1.setHorizontalAlignment(JLabel.CENTER);\r
+                                               b1.setBounds(bounds.getBangumiColumnWidth()*centerid, 0, bounds.getBangumiColumnWidth(), bounds.getBangumiColumnHeight());\r
+                                               b1.addMouseListener(tbMouseAdapter);\r
+                                               jPanel_space_top_view.add(b1);\r
+                                       }\r
+                                       //jPanel_space_top_view.setPreferredSize(new Dimension(bounds.getTimebarColumnWidth()+bounds.getBangumiColumnWidth()*tvprogram.getPlist().get(x).pcenter.size(),bounds.getBangumiColumnHeight()));\r
+                                       jPanel_space_top_view.setPreferredSize(new Dimension(bounds.getBangumiColumnWidth()*pl.pdate.size(),bounds.getBangumiColumnHeight()));\r
+                                       jPanel_space_top_view.updateUI();\r
+                                       \r
+\r
+                                       // 番組枠の表示\r
+                                       {\r
+                                               putBangumiColumns(pl, -1, null);\r
+                                       }\r
+                                       \r
+                                       // 予約枠の表示\r
+                                       for (int progid=0; progid<pl.pdate.size(); progid++) {\r
+                                               putReserveBorder(pl.pdate.get(progid).Date, pl.Center, progid);\r
+                                       }\r
+                                       \r
+                                       //\r
+                                       jLayeredPane_space_main_view.setPreferredSize(new Dimension(tvp.getCenters().get(0).pdate.size()*bounds.getBangumiColumnWidth(),Math.round(24*60*bounds.getPaperHeightMultiplier()*paperHeightZoom)));\r
+                                       //jScrollPane_space_main.updateUI();\r
+\r
+                                       break;\r
+                               }\r
+                       }\r
+               }\r
+               \r
+               // 番組枠描画バッファサイズの上限を確認する\r
+               if (framebuffersize < frameUsed.size()) {\r
+                       framebuffersize = frameUsed.size();\r
+                       StdAppendMessage(MSGID+"番組枠描画バッファの上限を変更: "+frameUsed.size()+"/"+framebuffersize);\r
+               }\r
+       }\r
+       \r
+       \r
+       /**\r
+        * 予約待機赤枠の描画(個々の枠)\r
+        * @see #updateReserveBorder(String)\r
+        */\r
+       private void putReserveBorder(String date, String Center, int q) {\r
+               \r
+               // 古いマークの削除(一見取りこぼしがあるように見えるが無問題)\r
+               for (int i=reserveBorders.size()-1; i>=0; i--) {\r
+                       JRMLabel rb = reserveBorders.get(i);\r
+                       if ( rb.getDate().equals(date) && rb.getCenter().equals(Center) ) {\r
+                               rb.setVisible(false);\r
+                               jLayeredPane_space_main_view.remove(rb);\r
+                               reserveBorders.remove(i);\r
+                       }\r
+               }\r
+               \r
+               // 座標系\r
+               JRMLabel.setColumnWidth(bounds.getBangumiColumnWidth());\r
+               JRMLabel.setHeightMultiplier(bounds.getPaperHeightMultiplier() * paperHeightZoom);\r
+               \r
+               // 表示範囲\r
+               GregorianCalendar cal = CommonUtils.getCalendar(String.format("%s %02d:00",date.substring(0,10),TIMEBAR_START));\r
+               String startDateTime = CommonUtils.getDateTime(cal);\r
+               cal.add(Calendar.HOUR_OF_DAY, 24);\r
+               String endDateTime = CommonUtils.getDateTime(cal);\r
+               \r
+               // 基準日\r
+               String critDateTime = CommonUtils.getCritDateTime(env.getDisplayPassedReserve());\r
+               \r
+               // 予約枠\r
+               HashMap<String,Boolean> misCN = new HashMap<String, Boolean>();\r
+               \r
+               // ツールバーで選択されている実レコーダ\r
+               String myself = ( env.getEffectComboToPaper() ) ? (getSelectedRecorderOnToolbar()) : (null);\r
+               \r
+               if ( myself == null || myself.length() > 0 ) {\r
+                       \r
+                       // ピックアップはここに入らない\r
+                       \r
+                       HDDRecorderList recs = recorders.getMyself(myself);\r
+                       \r
+                       for ( HDDRecorder recorder : recs )\r
+                       {\r
+                               //System.err.println(DBGID+recorder.Myself());\r
+                               \r
+                               for ( ReserveList r : recorder.getReserves()) {\r
+                                       // Exec == ON ?\r
+                                       if (env.getDisplayOnlyExecOnEntry() && ! r.getExec()) {\r
+                                               //StdAppendMessage("@Exec = OFF : "+r.getTitle());\r
+                                               continue;\r
+                                       }\r
+                                       // 局が一致して\r
+                                       if (r.getCh_name() == null) {\r
+                                               if (r.getChannel().length() > 0) {\r
+                                                       misCN.put(r.getChannel(),true);\r
+                                               }\r
+                                               continue;\r
+                                       }\r
+                                       \r
+                                       if (r.getCh_name().equals(Center)) {\r
+                                               \r
+                                               // 開始終了日時リストを生成する\r
+                                               ArrayList<String> starts = new ArrayList<String>();\r
+                                               ArrayList<String> ends = new ArrayList<String>();\r
+                                               CommonUtils.getStartEndList(starts, ends, r);\r
+                                               \r
+                                               // 予約枠を描画する\r
+                                               for (int j=0; j<starts.size(); j++) {\r
+                                                       if (critDateTime.compareTo(ends.get(j)) <= 0) {\r
+                                                               putReserveBorderSub(date,Center,startDateTime,endDateTime,starts.get(j),ends.get(j),r.getAhh(),r.getAmm(),r.getRec_min(),r.getTuner(),recorder.getColor(r.getTuner()),r.getExec(),q);\r
+                                                       }\r
+                                               }\r
+                                       }\r
+                               }\r
+                       }\r
+               }\r
+               \r
+               // ピックアップ枠\r
+               TVProgram tvp = tvprograms.getPickup();\r
+               for ( ProgList pl : tvp.getCenters() ) {\r
+                       if ( pl.Center.equals(Center) ) {\r
+                               for ( ProgDateList pcl : pl.pdate ) {\r
+                                       for ( ProgDetailList tvd : pcl.pdetail ) {\r
+                                               putReserveBorderSub(date,Center,startDateTime,endDateTime,tvd.startDateTime,tvd.endDateTime,tvd.start.substring(0,2),tvd.start.substring(3,5),String.valueOf(tvd.length),"PICKUP",CommonUtils.color2str(env.getPickedColor()),false,q);\r
+                                       }\r
+                               }\r
+                       }\r
+               }\r
+       }\r
+       private void putReserveBorderSub(String date, String Center, String startDateTime, String endDateTime, String start, String end, String ahh, String amm, String recmin, String tuner, String bordercol,boolean exec,int col) {\r
+               //\r
+               int row = 0;\r
+               int length = 0;\r
+               if (startDateTime.compareTo(start) <= 0 && start.compareTo(endDateTime) < 0) {\r
+                       //\r
+                       row = Integer.valueOf(ahh) - TIMEBAR_START;\r
+                       if (row < 0) {\r
+                               row += 24;\r
+                       }\r
+                       row = row*60 + Integer.valueOf(amm);\r
+                       length = Integer.valueOf(recmin);\r
+               }\r
+               else if (start.compareTo(startDateTime) < 0 && startDateTime.compareTo(end) < 0) {\r
+                       //\r
+                       row = 0;\r
+                       length = Integer.valueOf(recmin) - (TIMEBAR_START*60 - Integer.valueOf(ahh)*60 - Integer.valueOf(amm));\r
+               }\r
+               else {\r
+                       return;\r
+               }\r
+               \r
+               {\r
+                       // 枠の色\r
+                       Color rbc = CommonUtils.str2color(bordercol);\r
+                       \r
+                       // 重複予約の場合のエンコーダマーク表示位置の調整\r
+                       int rc = 0;\r
+                       //int rw = 0;\r
+                       for (int k=0; k<reserveBorders.size(); k++) {\r
+                               JRMLabel rb = reserveBorders.get(k);\r
+                               if ( rb.getDate().equals(date) && rb.getCenter().equals(Center) ) {\r
+                                       int drow = rb.getVRow() - row;\r
+                                       int dlen = rb.getVHeight() - length;\r
+                                       if ( rb.getVColumn() == col && ((drow == 0 && dlen == 0) || ((drow == 1 || drow == -1) && (dlen == 0 || dlen == -1 || dlen == 1))) ) {\r
+                                               rc++;\r
+                                       }\r
+                               }\r
+                       }\r
+                       \r
+                       // 予約マーク追加\r
+                       JRMLabel rb = new JRMLabel();\r
+                       \r
+                       if (rc == 0) {\r
+                               rb.setVerticalAlignment(JLabel.BOTTOM);\r
+                               rb.setHorizontalAlignment(JLabel.RIGHT);\r
+                       }\r
+                       else if (rc == 1) {\r
+                               rb.setVerticalAlignment(JLabel.BOTTOM);\r
+                               rb.setHorizontalAlignment(JLabel.LEFT);\r
+                       }\r
+                       else {\r
+                               rb.setVerticalAlignment(JLabel.TOP);\r
+                               rb.setHorizontalAlignment(JLabel.RIGHT);\r
+                       }\r
+                       \r
+                       // エンコーダの区別がないものは"■"を表示する\r
+                       rb.setEncBackground(rbc);\r
+                       rb.setBorder(new LineBorder(rbc,4));\r
+                       if ( tuner != null && tuner.equals("PICKUP") ) {\r
+                               rb.setEncForeground(env.getPickedFontColor());\r
+                       }\r
+                       else if ( exec ) {\r
+                               rb.setEncForeground(env.getExecOnFontColor());\r
+                       }\r
+                       else {\r
+                               rb.setEncForeground(env.getExecOffFontColor());\r
+                       }\r
+                       if (tuner == null || tuner.equals("")) {\r
+                               rb.setEncoder("■");\r
+                       }\r
+                       else {\r
+                               rb.setEncoder(tuner);\r
+                       }\r
+\r
+                       // 検索用情報\r
+                       rb.setDate(date);\r
+                       rb.setCenter(Center);\r
+                       rb.setExec(exec);\r
+                       \r
+                       jLayeredPane_space_main_view.add(rb);\r
+                       jLayeredPane_space_main_view.setLayer(rb,1);\r
+                       rb.setVBounds(col, row, 1, length);\r
+                       rb.setVisible(true);\r
+                       \r
+                       reserveBorders.add(rb);\r
+               }\r
+       }\r
+       \r
+       /**\r
+        * 番組枠の表示\r
+        * @param cnt -1:放送局別表示、>=0:日付表示\r
+        */\r
+       private void putBangumiColumns(ProgList pl, int cnt, String date) {\r
+               int ymax = pl.pdate.size();\r
+               int col = -1;\r
+               for ( int dateid=0; dateid < ymax; dateid++ ) {\r
+                       ProgDateList pcl = pl.pdate.get(dateid);\r
+                       \r
+                       if ( cnt >= 0 ) {\r
+                               if ( ! pcl.Date.equals(date) ) {\r
+                                       // 日付表示の場合は1列のみ描画\r
+                                       continue;\r
+                               }\r
+                               \r
+                               col = cnt;\r
+                       }\r
+                       else if ( cnt == -1 ) {\r
+                               col++;\r
+                       }\r
+                       \r
+                       int row = 0;\r
+                       int pEnd = 0;\r
+                       int zmax = pcl.pdetail.size();\r
+                       for ( int progid=0; progid<zmax; progid++ ) {\r
+                               ProgDetailList tvd = pcl.pdetail.get(progid);\r
+                               if ( progid != 0 ) {\r
+                                       // 2つめ以降は開始時刻から計算\r
+                                       String[] st = tvd.start.split(":",2);\r
+                                       if ( st.length == 2 ) {\r
+                                               int ahh = Integer.valueOf(st[0]);\r
+                                               int amm = Integer.valueOf(st[1]);\r
+                                               if ( CommonUtils.isLateNight(ahh) ) {\r
+                                                       ahh += 24;\r
+                                               }\r
+                                               row = (ahh-TIMEBAR_START)*60+amm;\r
+                                       }\r
+                                       else {\r
+                                               // 「番組情報がありません」は前の番組枠のお尻に\r
+                                               row = pEnd;\r
+                                       }\r
+                               }\r
+                               else {\r
+                                       // その日の最初のエントリは5:00以前の場合もあるので強制0スタート\r
+                                       row = 0;\r
+                               }\r
+                               \r
+                               // 番組枠描画\r
+                               putBangumiColumnSub(tvd, row, col);\r
+                               \r
+                               // 「番組情報がありません」用に保存\r
+                               pEnd = row + tvd.length;\r
+                       }\r
+               }\r
+       }\r
+       private void putBangumiColumnSub(ProgDetailList tvd, int row, int col) {\r
+               \r
+               // 新規生成か既存流用かを決める\r
+               JTXTButton b2 = null;\r
+               if (jLayeredPane_space_main_view == jLayeredPane_space_main_view_byMakeshift && ! frameUnused.isEmpty()) {\r
+                       b2 = frameUnused.remove(frameUnused.size()-1);\r
+                       //b2.setVisible(true);  // JTXTButton.clear()内でsetVisible(false)しているので\r
+               }\r
+               else {\r
+                       // 生成する\r
+                       b2 = new JTXTButton();\r
+                       jLayeredPane_space_main_view.add(b2);\r
+                       jLayeredPane_space_main_view.setLayer(b2, 0);\r
+                       \r
+                       // リスナーを設定する\r
+                       b2.addMouseListener(mListner);\r
+                       b2.addMouseMotionListener(mListner);\r
+               }\r
+               if (jLayeredPane_space_main_view == jLayeredPane_space_main_view_byMakeshift) {\r
+                       frameUsed.add(b2);\r
+               }\r
+               else {\r
+                       // 裏描画は十分遅いのでb2をUnusedキャッシュには入れず都度生成で構わない\r
+                       frameUsedByDate.add(b2);\r
+               }\r
+               \r
+               // 情報設定\r
+               b2.setInfo(tvd);\r
+               \r
+               JTXTButton.setColumnWidth(bounds.getBangumiColumnWidth());\r
+               JTXTButton.setHeightMultiplier(bounds.getPaperHeightMultiplier() * paperHeightZoom);\r
+               \r
+               b2.setBackground(pColors.get(tvd.genre));\r
+               if (bounds.getShowMatchedBorder() && b2.isStandby() ) {\r
+                       b2.setBorder(dborder);\r
+               }\r
+               else {\r
+                       b2.setBorder(lborder);\r
+               }\r
+               \r
+               // 配置を決定する\r
+               b2.setVBounds(col,row,1,tvd.length);\r
+               \r
+               // ツールチップを付加する\r
+               if ( env.getTooltipEnable() == true && ! tvd.title.equals("") && ! tvd.start.equals("") ) {\r
+                       String t = "";\r
+                       int tlen = bounds.getTooltipWidth();\r
+                       for (int i=0; i<tvd.title.length(); i+=tlen) {\r
+                               t += tvd.title.substring(i, (i+tlen<tvd.title.length())?(i+tlen):(tvd.title.length()))+"<BR>";\r
+                       }\r
+                       String d = "";\r
+                       int dlen = tlen+2;\r
+                       for (int i=0; i<tvd.detail.length(); i+=dlen) {\r
+                               d += "&nbsp;&nbsp;&nbsp;&nbsp;"+tvd.detail.substring(i, (i+dlen<tvd.detail.length())?(i+dlen):(tvd.detail.length()))+"<BR>";\r
+                       }\r
+                       String e = getExtensionMark(tvd);\r
+                       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
+               }\r
+       }\r
+       \r
+       /**\r
+        * 現在時刻線の位置を変える\r
+        * @param minpos : MINPOS.RESET=初回、MINPOS.UPDATE=自動更新時\r
+        */\r
+       private int setTimelinePos(boolean reset) {\r
+               if ( vport != null && jLabel_timeline != null && jLabel_timeline.isVisible() ) {\r
+                       \r
+                       int correct = 0; // 24:00-28:59迄は前日の日付になる\r
+                       GregorianCalendar c = CommonUtils.getCalendar(0);\r
+                       if ( CommonUtils.isLateNight(c) ) {\r
+                               c.add(Calendar.DATE, -1);\r
+                               correct += 24;\r
+                       }\r
+                       \r
+                       Point vp = vport.getViewPosition();\r
+                       Point tp = jLabel_timeline.getLocation();\r
+                       \r
+                       // ビュー上の位置\r
+                       int minpos_new = (c.get(Calendar.HOUR_OF_DAY)-TIMEBAR_START+correct)*60+c.get(Calendar.MINUTE);\r
+                       int timeline_vpos = jLabel_timeline.setMinpos(0, minpos_new, bounds.getPaperHeightMultiplier()*paperHeightZoom);\r
+                       \r
+                       // ビューポートの位置(05:30まではスクロールしないよ)\r
+                       if ( env.getTimerbarScrollEnable() && minpos_new >= 30 ) {\r
+                               if ( reset ) {\r
+                                       // 初回描画\r
+                                       Rectangle ra = vport.getViewRect();\r
+                                       ra.y =  Math.round(timeline_vpos - (float)bounds.getTimelinePosition() * bounds.getPaperHeightMultiplier() * paperHeightZoom);\r
+                                       vport.setViewPosition(new Point(ra.x, ra.y));\r
+                               }\r
+                               else {\r
+                                       // 自動更新\r
+                                       vp.y += (timeline_vpos - tp.y);\r
+                                       vport.setViewPosition(vp);\r
+                               }\r
+                       }\r
+                       \r
+                       jLabel_timeline.updateUI();\r
+                       \r
+                       return minpos_new;\r
+               }\r
+               \r
+               return -1;\r
+       }\r
+       \r
+       /*******************************************************************************\r
+        * リスナー\r
+        ******************************************************************************/\r
+\r
+       /**\r
+        * 現在時刻追従スクロール\r
+        */\r
+       @Override\r
+       public void timerRised(VWTimerRiseEvent e) {\r
+               \r
+               String curDT = CommonUtils.getDate529(0,true);\r
+               \r
+               if ( prevDT4Tree != null && ! prevDT4Tree.equals(curDT) ) {\r
+                       // 日付が変わったらツリーを書き換える\r
+                       redrawTreeByDate();\r
+                       redrawTreeByPassed();\r
+                       prevDT4Tree = curDT;\r
+               }\r
+               \r
+               if ( timer_now_enabled ) {\r
+\r
+                       if (prevDT4Now != null && ! prevDT4Now.equals(curDT)) {\r
+                               // 日付切り替え\r
+                               StdAppendError(MSGID+"日付が変わったので番組表を切り替えます("+CommonUtils.getDateTime(0)+")");\r
+                               redrawByNow(cur_tuner);\r
+                       }\r
+                       else {\r
+                               // 現在時刻線の移動\r
+                               setTimelinePos(false);\r
+                       }\r
+                       \r
+                       // 前回実行日\r
+                       prevDT4Now = curDT;\r
+               }\r
+       }\r
+       \r
+       /**\r
+        * タブを開いたり閉じたりしたときに動くリスナー\r
+        */\r
+       private ComponentListener cl_shownhidden = new ComponentAdapter() {\r
+               @Override\r
+               public void componentShown(ComponentEvent e) {\r
+                       \r
+                       // 前日以前の番組情報を削除する\r
+                       for ( TVProgram tvp : tvprograms ) {\r
+                               tvp.refresh();\r
+                       }\r
+                       \r
+                       // 終了した予約を整理する\r
+                       for ( HDDRecorder recorder : recorders ) {\r
+                               recorder.refreshReserves();\r
+                       }\r
+                       \r
+                       // 他のコンポーネントと連動\r
+                       onShown();\r
+                       \r
+                       setPagerEnabled(true);\r
+               }\r
+               \r
+               @Override\r
+               public void componentHidden(ComponentEvent e) {\r
+                       \r
+                       onHidden();\r
+                       \r
+                       setPagerEnabled(false);\r
+               }\r
+       };\r
+       \r
+       /**\r
+        * 番組枠につけるマウス操作のリスナー\r
+        */\r
+       private final MouseInputListener mListner = new MouseInputListener() {\r
+               //\r
+               private final Cursor defCursor = Cursor.getPredefinedCursor(Cursor.DEFAULT_CURSOR);\r
+               private final Cursor hndCursor = Cursor.getPredefinedCursor(Cursor.HAND_CURSOR);\r
+               private final Point pp = new Point();\r
+               \r
+               private Color bgcolor = null;\r
+\r
+               @Override\r
+               public void mouseClicked(MouseEvent e) {\r
+                       \r
+                       // ポインタの位置\r
+                       Point p = e.getPoint();\r
+                       \r
+                       // ポインタの乗っている番組\r
+                       JTXTButton b = (JTXTButton) e.getSource();\r
+                       ProgDetailList tvd = b.getInfo();\r
+                       \r
+                       if (e.getButton() == MouseEvent.BUTTON3) {\r
+                               if (e.getClickCount() == 1) {\r
+                                       // 右シングルクリックでメニューの表示\r
+                                       showPopupForTraceProgram(b, tvd, tvd.title, TraceKey.noFazzyThreshold, p.x, p.y, e.getY());\r
+                               }\r
+                       }\r
+                       else if (e.getButton() == MouseEvent.BUTTON1) {\r
+                               // 過去ログは閲覧のみ\r
+                               if (tvd.type == ProgType.PASSED) {\r
+                                       if (e.getClickCount() == 2) {\r
+                                               MWin.appendMessage(MSGID+"過去ログでダブルクリックは利用できません");\r
+                                               ringBeep();\r
+                                       }\r
+                                       return;\r
+                               }\r
+                               if (e.getClickCount() == 2) {\r
+                                       // RADIOは閲覧のみ\r
+                                       if (tvd.type == ProgType.PROG && tvd.subtype == ProgSubtype.RADIO) {\r
+                                               return;\r
+                                       }\r
+                                       // レコーダが選択されていない場合はなにもしない\r
+                                       if (recorders.size() == 0) {\r
+                                               return;\r
+                                       }\r
+                                       \r
+                                       // 左ダブルクリックで予約ウィンドウを開く\r
+                                       \r
+                                       //VWReserveDialog rD = new VWReserveDialog(0, 0, env, tvprograms, recorders, avs, chavs, stwin);\r
+                                       CommonSwingUtils.setLocationCenter(parent,rD);\r
+                                       //rD.clear();\r
+                                       \r
+                                       // サブタイトルを番組追跡の対象から外す\r
+                                       boolean succeeded = false;\r
+                                       if ( ! env.getSplitEpno() && env.getTraceOnlyTitle() ) {\r
+                                               //String[] d = tvp.doSplitEpno(tvd.genre, tvd.title);\r
+                                               succeeded = rD.open(tvd,tvd.title,TraceKey.defaultFazzyThreshold);\r
+                                       }\r
+                                       else {\r
+                                               succeeded = rD.open(tvd);\r
+                                       }\r
+                                       \r
+                                       if (succeeded) {\r
+                                               rD.setVisible(true);\r
+                                       }\r
+                                       else {\r
+                                               rD.dispose();\r
+                                       }\r
+                                       \r
+                                       if (rD.isReserved()) {\r
+                                               updateReserveDisplay();\r
+                                               updateReserveBorder(tvd.center);\r
+                                       }\r
+                               }\r
+                       }\r
+                       else if (e.getButton() == MouseEvent.BUTTON2) {\r
+                               // ピックアップに追加\r
+                               addToPickup(tvd);\r
+                       }\r
+               }\r
+\r
+               /**\r
+                * 詳細情報の自動表示\r
+                */\r
+               @Override\r
+               public void mouseEntered(MouseEvent e) {\r
+                       \r
+                       JTXTButton b = (JTXTButton) e.getSource();\r
+                       ProgDetailList tvd = b.getInfo();\r
+                       \r
+                       if ( env.getEnableHighlight() ) {\r
+                               bgcolor = ((JTXTButton)e.getSource()).getBackground();\r
+                               ((JTXTButton)e.getSource()).setBackground(env.getHighlightColor());\r
+                       }\r
+                       jTextPane_detail.setLabel(tvd.start,tvd.end,tvd.title);\r
+                       jTextPane_detail.setText(tvd.detail+"\n"+tvd.getAddedDetail());\r
+               }\r
+\r
+               @Override\r
+               public void mouseExited(MouseEvent e) {\r
+                       if ( env.getEnableHighlight() ) {\r
+                               ((JTXTButton)e.getSource()).setBackground(bgcolor);\r
+                       }\r
+               }\r
+\r
+               @Override\r
+               public void mouseDragged(final MouseEvent e) {\r
+                       Point cp = e.getLocationOnScreen();\r
+                       Point vp = vport.getViewPosition(); //= SwingUtilities.convertPoint(vport,0,0,label);\r
+                       vp.translate(pp.x-cp.x, pp.y-cp.y);\r
+                       jLayeredPane_space_main_view.scrollRectToVisible(new Rectangle(vp, vport.getSize()));\r
+                       pp.setLocation(cp);\r
+               }\r
+\r
+               @Override\r
+               public void mousePressed(MouseEvent e) {\r
+                       pp.setLocation(e.getLocationOnScreen());\r
+                       jLayeredPane_space_main_view.setCursor(hndCursor);\r
+               }\r
+\r
+               @Override\r
+               public void mouseReleased(MouseEvent e) {\r
+                       jLayeredPane_space_main_view.setCursor(defCursor);\r
+               }\r
+\r
+               @Override\r
+               public void mouseMoved(MouseEvent e) {\r
+               }\r
+       };\r
+       \r
+       /**\r
+        * サイドツリーにつけるリスナー(ツリーの展開状態を記憶する)\r
+        */\r
+       private final TreeExpansionListener tel_nodeexpansion = new TreeExpansionListener() {\r
+               \r
+               @Override\r
+               public void treeExpanded(TreeExpansionEvent event) {\r
+                       ter.reg();\r
+               }\r
+               \r
+               @Override\r
+               public void treeCollapsed(TreeExpansionEvent event) {\r
+                       ter.reg();\r
+               }\r
+       };\r
+\r
+       /**\r
+        * サイドツリーにつけるリスナー(クリックで描画実行)\r
+        */\r
+       private final TreeSelectionListener tsl_nodeselected = new TreeSelectionListener() {\r
+               @Override\r
+               public void valueChanged(TreeSelectionEvent e){\r
+                       \r
+                       TreePath path = jTree_tree.getSelectionPath();\r
+\r
+                       if ( path != null && path.getPathCount() == 2 ) {\r
+                               // 親ノードとか触られても…\r
+                               return;\r
+                       }\r
+                       \r
+                       if ( path != null && path.getPathCount() == 3 ) {\r
+                               \r
+                               if (env.getDebug()) System.out.println(DBGID+"SELECTED treeSelListner "+path);\r
+\r
+                               stopTimer();\r
+\r
+                               JTreeLabel.Nodes node = JTreeLabel.Nodes.getNode(path.getPathComponent(1).toString());\r
+                               String value = path.getLastPathComponent().toString();\r
+                       \r
+                               switch ( node ) {\r
+                               case DATE:\r
+                                       if ( JTreeLabel.Nodes.NOW.getLabel().equals(value) ) {\r
+                                               // 現在日時に移動する\r
+                                               redrawByNow(IterationType.ALL);\r
+                                       }\r
+                                       else {\r
+                                               redrawByDate(value,IterationType.ALL);\r
+                                       }\r
+                                       if ( env.isPagerEnabled() ) {\r
+                                               setPagerEnabled(true);\r
+                                       }\r
+                                       break;\r
+                               case TERRA:\r
+                                       if ( JTreeLabel.Nodes.NOW.getLabel().equals(value) ) {\r
+                                               redrawByNow(IterationType.TERRA);\r
+                                       }\r
+                                       else {\r
+                                               redrawByDate(value,IterationType.TERRA);\r
+                                       }\r
+                                       if ( env.isPagerEnabled() ) {\r
+                                               setPagerEnabled(true);\r
+                                       }\r
+                                       break;\r
+                               case BS:\r
+                                       if ( JTreeLabel.Nodes.NOW.getLabel().equals(value) ) {\r
+                                               redrawByNow(IterationType.BS);\r
+                                       }\r
+                                       else {\r
+                                               redrawByDate(path.getLastPathComponent().toString(),IterationType.BS);\r
+                                       }\r
+                                       if ( env.isPagerEnabled() ) {\r
+                                               setPagerEnabled(true);\r
+                                       }\r
+                                       break;\r
+                               case CS:\r
+                                       if ( JTreeLabel.Nodes.NOW.getLabel().equals(value) ) {\r
+                                               redrawByNow(IterationType.CS);\r
+                                       }\r
+                                       else {\r
+                                               redrawByDate(path.getLastPathComponent().toString(),IterationType.CS);\r
+                                       }\r
+                                       if ( env.isPagerEnabled() ) {\r
+                                               setPagerEnabled(true);\r
+                                       }\r
+                                       break;\r
+                               case BCAST:\r
+                                       redrawByCenter(value);\r
+                                       if ( env.isPagerEnabled() ) {\r
+                                               setPagerEnabled(false);\r
+                                       }\r
+                                       break;\r
+                               case PASSED:\r
+                                       PassedProgram passed = tvprograms.getPassed();\r
+                                       if ( passed.loadAllCenters(value) ) {\r
+                                               redrawByDate(value, IterationType.PASSED);\r
+                                       }\r
+                                       else {\r
+                                               MWin.appendError(ERRID+"過去ログが存在しません: "+value);\r
+                                               ringBeep();\r
+                                       }\r
+                                       if ( env.isPagerEnabled() ) {\r
+                                               setPagerEnabled(true);\r
+                                       }\r
+                                       break;\r
+                               default:\r
+                                       break;\r
+                               }\r
+                               \r
+                               jLabel_tree.setView(node, value);\r
+                               return;\r
+                       }\r
+                       \r
+                       // なんかおかしいのでデフォルト選択にまわす\r
+                       CommonUtils.printStackTrace();\r
+                       MWin.appendError(ERRID+"バグの可能性あり");\r
+                       //redrawByNow(IterationType.ALL);\r
+                       //jLabel_tree.setView(JTreeLabel.Nodes.DATE, JTreeLabel.Nodes.NOW.getLabel());\r
+               }\r
+       };\r
+       \r
+       /**\r
+        * フルスクリーン時にツリーを隠したりするの\r
+        */\r
+       private final MouseListener ml_treehide = new MouseAdapter() {\r
+               public void mouseEntered(MouseEvent e) {\r
+                       if (isFullScreen()) {\r
+                               setExpandTree();\r
+                               //StdAppendMessage("Show tree (N)");\r
+                       }\r
+               }\r
+               public void mouseExited(MouseEvent e) {\r
+                       if (isFullScreen()) {\r
+                               setCollapseTree();\r
+                               //StdAppendMessage("Hide tree (N)");\r
+                       }\r
+               }\r
+       };\r
+       \r
+       /**\r
+        * 放送局名につけるリスナー(ダブルクリックで一週間表示にジャンプ)\r
+        */\r
+       private final MouseAdapter cnMouseAdapter = new MouseAdapter() {\r
+               \r
+               private Color bgcolor = null;\r
+               \r
+               public void mouseExited(MouseEvent e) {\r
+                       ((JLabel)e.getSource()).setBackground(bgcolor);\r
+               }\r
+               public void mouseEntered(MouseEvent e) {\r
+                       bgcolor = ((JLabel)e.getSource()).getBackground();\r
+                       ((JLabel)e.getSource()).setBackground(new Color(180,180,255));\r
+               }\r
+               \r
+               public void mouseClicked(MouseEvent e) {\r
+                       if (e.getButton() == MouseEvent.BUTTON1) {\r
+                               if (e.getClickCount() == 2) {\r
+                                       if ( cur_tuner == IterationType.PASSED ) {\r
+                                               MWin.appendMessage(MSGID+"過去ログでは一局表示に切り替えられません");\r
+                                               return;\r
+                                       }\r
+                                       \r
+                                       // 右ダブルクリックで局表示に切り替え\r
+                                       String center = ((JLabel)e.getSource()).getText();\r
+                                       StdAppendMessage(MSGID+"一局表示に切り替え:"+center);\r
+                                       //redrawByCenter(center);\r
+                                       jLabel_tree.setView(JTreeLabel.Nodes.BCAST, center);\r
+                                       reselectTree();\r
+                               }\r
+                       }\r
+               }\r
+       };\r
+       \r
+       /**\r
+        * 日付枠につけるリスナー(ダブルクリックで放送局別表示にジャンプ)\r
+        */\r
+       private final MouseAdapter tbMouseAdapter = new MouseAdapter() {\r
+               private Color bgcolor = null;\r
+               //\r
+               public void mouseExited(MouseEvent e) {\r
+                       ((JTXTLabel)e.getSource()).setBackground(bgcolor);\r
+               }\r
+               public void mouseEntered(MouseEvent e) {\r
+                       bgcolor = ((JTXTLabel)e.getSource()).getBackground();\r
+                       ((JTXTLabel)e.getSource()).setBackground(new Color(180,180,255));\r
+               }\r
+               \r
+               //\r
+               public void mouseClicked(MouseEvent e) {\r
+                       if (e.getButton() == MouseEvent.BUTTON1) {\r
+                               if (e.getClickCount() == 2) {\r
+                                       // 右ダブルクリックで日付表示に切り替え\r
+                                       String date = ((JTXTLabel)e.getSource()).getValue();\r
+                                       StdAppendMessage(MSGID+"日付表示に切り替え:"+date);\r
+                                       //redrawByDate(date, -1);\r
+                                       jLabel_tree.setView(JTreeLabel.Nodes.DATE, date);\r
+                                       reselectTree();\r
+                               }\r
+                       }\r
+               }\r
+       };\r
+       \r
+       \r
+       \r
+       /*******************************************************************************\r
+        * コンポーネント\r
+        ******************************************************************************/\r
+\r
+       private JDetailPanel getJTextPane_detail() {\r
+               if (jTextPane_detail == null) {\r
+                       jTextPane_detail = new JDetailPanel();\r
+                       jTextPane_detail.setRows(bounds.getDetailRows());\r
+                       //Dimension d = jTextPane_detail.getMaximumSize();\r
+                       //d.height = bounds.getDetailAreaHeight();\r
+                       //jTextPane_detail.setPreferredSize(d);\r
+                       //jTextPane_detail.setVerticalAlignment(JLabel.TOP);\r
+                       //jTextPane_detail.setHorizontalAlignment(JLabel.LEFT);\r
+               }\r
+               return jTextPane_detail;\r
+       }\r
+       \r
+       private JSplitPane getJSplitPane_view() {\r
+               if ( jSplitPane_view == null ) {\r
+                       \r
+                       jSplitPane_view = new JSplitPane() {\r
+\r
+                               private static final long serialVersionUID = 1L;\r
+\r
+                               @Override\r
+                               public void setDividerLocation(int loc) {\r
+                                       setDividerEnvs(loc);\r
+                                       super.setDividerLocation(loc);\r
+                               }\r
+                       };\r
+                       \r
+                       jSplitPane_view.setLeftComponent(getJPanel_tree());\r
+                       jSplitPane_view.setRightComponent(getJScrollPane_space_main());\r
+                       setExpandTree();\r
+               }\r
+               return jSplitPane_view;\r
+       }\r
+\r
+       private JPanel getJPanel_tree() {\r
+               if (jPanel_tree == null) {\r
+                       jPanel_tree = new JPanel();\r
+\r
+                       jPanel_tree.setLayout(new BorderLayout());\r
+                       jPanel_tree.add(getJScrollPane_tree_top(), BorderLayout.PAGE_START);\r
+                       jPanel_tree.add(getJScrollPane_tree(), BorderLayout.CENTER);\r
+               }\r
+               return jPanel_tree;\r
+       }\r
+       \r
+       //\r
+       private JScrollPane getJScrollPane_tree_top() {\r
+               if (jScrollPane_tree_top == null) {\r
+                       jScrollPane_tree_top = new JScrollPane();\r
+                       jScrollPane_tree_top.setViewportView(getJLabel_tree());\r
+                       jScrollPane_tree_top.setVerticalScrollBarPolicy(JScrollPane.VERTICAL_SCROLLBAR_NEVER);\r
+                       jScrollPane_tree_top.setHorizontalScrollBarPolicy(JScrollPane.HORIZONTAL_SCROLLBAR_NEVER);\r
+               }\r
+               return jScrollPane_tree_top;\r
+       }\r
+       private JTreeLabel getJLabel_tree() {\r
+               if (jLabel_tree == null) {\r
+                       jLabel_tree = new JTreeLabel();\r
+                       \r
+                       Dimension d = jLabel_tree.getMaximumSize();\r
+                       d.height = bounds.getBangumiColumnHeight();\r
+                       jLabel_tree.setPreferredSize(d);\r
+                       jLabel_tree.setOpaque(true);\r
+                       jLabel_tree.setBackground(Color.WHITE);\r
+               }\r
+               return jLabel_tree;\r
+       }\r
+       \r
+       private JScrollPane getJScrollPane_tree() {\r
+               if (jScrollPane_tree == null) {\r
+                       jScrollPane_tree = new JScrollPane();\r
+\r
+                       jScrollPane_tree.setViewportView(getJTree_tree());\r
+               }\r
+               return jScrollPane_tree;\r
+       }\r
+       \r
+       /**\r
+        * ツリーの作成\r
+        */\r
+       private JTree getJTree_tree() {\r
+               if (jTree_tree == null) {\r
+\r
+                       // ツリーの作成\r
+                       jTree_tree = new JTree();\r
+                       jTree_tree.getSelectionModel().setSelectionMode(TreeSelectionModel.SINGLE_TREE_SELECTION);\r
+                       jTree_tree.setRootVisible(env.getRootNodeVisible());\r
+                       \r
+                       // ノードの作成\r
+                       jTree_tree.setModel(new DefaultTreeModel(getTreeNodes()));\r
+                       \r
+                       // ツリーの展開状態の復帰\r
+                       undoTreeExpansion();\r
+                       \r
+                       // ツリーの開閉時に状態を保存する\r
+                       jTree_tree.addTreeExpansionListener(tel_nodeexpansion);\r
+                       \r
+                       // フルスクリーンの時に使う(新聞形式のツリーを自動的に隠す)\r
+                       jTree_tree.addMouseListener(ml_treehide);\r
+               }\r
+               return jTree_tree;\r
+       }\r
+\r
+       /**\r
+        * ツリーのノード作成\r
+        */\r
+       private DefaultMutableTreeNode getTreeNodes() {\r
+               \r
+               paperRootNode = new DefaultMutableTreeNode(JTreeLabel.Nodes.ROOT.getLabel());\r
+               \r
+               dateNode        = new DefaultMutableTreeNode(JTreeLabel.Nodes.DATE.getLabel());\r
+               dgNode          = new DefaultMutableTreeNode(JTreeLabel.Nodes.TERRA.getLabel());\r
+               bsNode          = new DefaultMutableTreeNode(JTreeLabel.Nodes.BS.getLabel());\r
+               csNode          = new DefaultMutableTreeNode(JTreeLabel.Nodes.CS.getLabel());\r
+               centerNode      = new DefaultMutableTreeNode(JTreeLabel.Nodes.BCAST.getLabel());\r
+               passedNode      = new DefaultMutableTreeNode(JTreeLabel.Nodes.PASSED.getLabel());\r
+               \r
+               paperRootNode.add(dateNode);\r
+               paperRootNode.add(dgNode);\r
+               paperRootNode.add(bsNode);\r
+               paperRootNode.add(csNode);\r
+               paperRootNode.add(centerNode);\r
+               paperRootNode.add(passedNode);\r
+\r
+               // 子の描画\r
+               redrawTreeByDate();\r
+               redrawTreeByCenter();\r
+               redrawTreeByPassed();\r
+               \r
+               return paperRootNode;\r
+       }\r
+       \r
+       private void undoTreeExpansion() {\r
+                       \r
+               // 展開状態の復帰\r
+               stopTreeListener();\r
+               \r
+               // 展開状態の記憶域の初期化\r
+               ter = new TreeExpansionReg(jTree_tree, TreeExpRegFile_Paper);\r
+               try {\r
+                       ter.load();\r
+               }\r
+               catch (Exception e) {\r
+                       MWin.appendMessage(ERRID+"ツリー展開情報の解析で問題が発生しました");\r
+                       e.printStackTrace();\r
+               }\r
+\r
+               // 状態を復元する\r
+               ArrayList<TreePath> tpa = ter.get();\r
+               for ( TreePath path : tpa ) {\r
+                       jTree_tree.expandPath(path);\r
+               }\r
+               \r
+               startTreeListener();\r
+       }\r
+\r
+       private JScrollPane getJScrollPane_space_main() {\r
+               if (jScrollPane_space_main == null) {\r
+                       jScrollPane_space_main = new JScrollPane(JScrollPane.VERTICAL_SCROLLBAR_ALWAYS, JScrollPane.HORIZONTAL_SCROLLBAR_ALWAYS);\r
+                       jScrollPane_space_main.getVerticalScrollBar().setUnitIncrement(bounds.getBangumiColumnHeight());\r
+                       jScrollPane_space_main.getHorizontalScrollBar().setUnitIncrement(bounds.getBangumiColumnWidth());\r
+                       \r
+                       //jScrollPane_space_main.setViewportView(getJLayeredPane_space_main_view());\r
+                       jScrollPane_space_main.setColumnHeaderView(getJPanel_space_top_view());\r
+                       jScrollPane_space_main.setRowHeaderView(getJPanel_space_side_view());\r
+                       \r
+                       vport = jScrollPane_space_main.getViewport();\r
+               }\r
+               return jScrollPane_space_main;\r
+       }\r
+       \r
+       private JPanel getJPanel_space_top_view() {\r
+               if (jPanel_space_top_view == null) {\r
+                       jPanel_space_top_view = new JPanel();\r
+                       jPanel_space_top_view.setLayout(new SpringLayout());\r
+               }\r
+               return jPanel_space_top_view;\r
+       }\r
+       \r
+       private JPanel getJPanel_space_side_view() {\r
+               if (jPanel_space_side_view == null) {\r
+                       jPanel_space_side_view = new JPanel();\r
+                       jPanel_space_side_view.setLayout(null);\r
+               }\r
+               return jPanel_space_side_view;\r
+       }\r
+       \r
+       \r
+       /*\r
+        * 以下は、pcwinから呼び出されるメソッドをまとめたもの \r
+        */\r
+       \r
+       // 時間枠のコンポーネント\r
+       public Component[] getTimebarComponents() {\r
+               return jPanel_space_side_view.getComponents();\r
+       }\r
+\r
+       // 背景色ほかの変更\r
+       public void updateColors(Env ec,PaperColorsMap pc) {\r
+               _updPColors(ec, pc, frameUsed);\r
+               \r
+               if ( env.getDrawcacheEnable() ) {\r
+                       _updPColors(ec, pc, frameUsedByDate);\r
+               }\r
+               \r
+               // マウスオーバー時のハイライト\r
+               /* no proc. */\r
+               \r
+               // タイムバーの色\r
+               for ( Component c : getTimebarComponents() ) {\r
+                       if ( c instanceof JTimebarLabel ) {\r
+                               int j = Integer.valueOf(((JTimebarLabel) c).getTs());\r
+                               if ( j >=6 && j <= 11 ) {\r
+                                       c.setBackground(ec.getTimebarColor());\r
+                               }\r
+                               else if ( j >=12 && j <= 17 ) {\r
+                                       c.setBackground(ec.getTimebarColor2());\r
+                               }\r
+                               else if ( j >=18 && j <= 23 ) {\r
+                                       c.setBackground(ec.getTimebarColor3());\r
+                               }\r
+                               else {\r
+                                       c.setBackground(ec.getTimebarColor4());\r
+                               }\r
+                       }\r
+               }\r
+       }\r
+       \r
+       // サイズの変更\r
+       public void updateBounds(Env ec, Bounds bc) {\r
+               \r
+               int maxCol = jPanel_space_top_view.getComponentCount();\r
+               float maxRow = 24*60;\r
+               \r
+               float phm = bc.getPaperHeightMultiplier() * paperHeightZoom ;\r
+\r
+               int vieww = maxCol * bc.getBangumiColumnWidth();\r
+               int viewh = (int) Math.ceil(maxRow * phm);\r
+\r
+               // 変更前のビューの位置\r
+               Point vp = vport.getViewPosition();\r
+               float vh = vport.getView().getPreferredSize().height;\r
+               \r
+               // タイムバーのサイズ変更\r
+               {\r
+                       int h = (int) Math.ceil(60.0F*phm);\r
+                       int row = 0;\r
+                       for ( Component b0 : jPanel_space_side_view.getComponents() ) {\r
+                               b0.setBounds(0,(int) Math.ceil((float)row*phm),bc.getTimebarColumnWidth(),h);\r
+                               row += 60;\r
+                       }\r
+                       \r
+                       Dimension d = jPanel_space_side_view.getPreferredSize();\r
+                       d.height = viewh;\r
+                       jPanel_space_side_view.setPreferredSize(d);\r
+               }\r
+               \r
+               // 放送局名(or日付)のサイズ変更\r
+               {\r
+                       for ( int col=0; col<jPanel_space_top_view.getComponentCount(); col++ ) {\r
+                               Component b1 = jPanel_space_top_view.getComponent(col);\r
+                               b1.setBounds(\r
+                                               bc.getBangumiColumnWidth() * col,\r
+                                               0,\r
+                                               bc.getBangumiColumnWidth(),\r
+                                               bc.getBangumiColumnHeight());\r
+                       }\r
+                       Dimension d = jPanel_space_top_view.getPreferredSize();\r
+                       d.width = vieww;\r
+                       jPanel_space_top_view.setPreferredSize(d);\r
+               }\r
+               \r
+               // 各番組枠のサイズ変更・検索マッチ枠の表示変更\r
+               {\r
+                       {\r
+                               _updPBounds(bc, frameUsed);\r
+                               _updPBorders(ec, bc, frameUsed);\r
+                               \r
+                               Dimension d = jLayeredPane_space_main_view.getPreferredSize();\r
+                               d.width = vieww;\r
+                               d.height = viewh;\r
+                               jLayeredPane_space_main_view.setPreferredSize(d);\r
+                       }\r
+                       \r
+                       if ( ec.getDrawcacheEnable() ) {\r
+                               _updPBounds(bc, frameUsedByDate);\r
+                               _updPBorders(ec, bc, frameUsedByDate);\r
+                               \r
+                               for ( JLayeredPane pane : jLayeredPane_space_main_view_byDate ) {\r
+                                       Dimension d = pane.getPreferredSize();\r
+                                       d.width = vieww;\r
+                                       d.height = viewh;\r
+                                       pane.setPreferredSize(d);\r
+                               }\r
+                       }\r
+               }\r
+               \r
+               // 予約枠・ピックアップ枠のサイズ変更&色変更\r
+               {\r
+                       JRMLabel.setColumnWidth(bc.getBangumiColumnWidth());\r
+                       JRMLabel.setHeightMultiplier(phm);\r
+                       \r
+                       for ( JRMLabel rb : reserveBorders ) {\r
+                               \r
+                               rb.reVBounds();\r
+                               \r
+                               if ( rb.getEncoder().equals("PICKUP") ) {\r
+                                       rb.setEncBackground(ec.getPickedColor());\r
+                                       rb.setEncForeground(ec.getPickedFontColor());\r
+                                       rb.setBorder(new LineBorder(ec.getPickedColor(),4));\r
+                               }\r
+                               else if ( rb.getExec() ) {\r
+                                       rb.setEncForeground(ec.getExecOnFontColor());\r
+                               }\r
+                               else {\r
+                                       rb.setEncForeground(ec.getExecOffFontColor());\r
+                               }\r
+                               rb.repaint();\r
+                       }\r
+               }\r
+               \r
+               // 現在時刻線の位置変更\r
+               setTimelinePos(false);\r
+               \r
+               // 枠のサイズを更新したのでupdateUI()\r
+               jScrollPane_space_main.updateUI();\r
+               \r
+               // ビューの位置調整\r
+               vp.y = (int)Math.ceil(maxRow * (float)vp.y * phm / vh);\r
+               vport.setViewPosition(vp);\r
+               \r
+       }\r
+       \r
+       // フォントの変更\r
+       public void updateFonts(Env ec) {\r
+               JTXTButton.setShowStart(ec.getShowStart());\r
+               JTXTButton.setSplitEpno(ec.getSplitEpno());\r
+               JTXTButton.setShowDetail(ec.getShowDetail());\r
+               JTXTButton.setDetailTab(ec.getDetailTab());\r
+               JTXTButton.setDetailRows(ec.getDetailRows());\r
+               \r
+               JTXTButton.setTitleFont(ec.getTitleFont());\r
+               JTXTButton.setTitleFontStyle(ec.getTitleFontStyle());\r
+               JTXTButton.setDetailFont(ec.getDetailFont());\r
+               JTXTButton.setDetailFontStyle(ec.getDetailFontStyle());\r
+               JTXTButton.setTitleFontSize(ec.getTitleFontSize());\r
+               JTXTButton.setTitleFontColor(ec.getTitleFontColor());\r
+               JTXTButton.setDetailFontSize(ec.getDetailFontSize());\r
+               JTXTButton.setDetailFontColor(ec.getDetailFontColor());\r
+               JTXTButton.setAAHint(ec.getPaperAAMode().getHint());\r
+       }\r
+       \r
+       // 再描画?\r
+       public void updateRepaint() {\r
+               _updPRepaint(frameUsed);\r
+               \r
+               if ( env.getDrawcacheEnable() ) {\r
+                       _updPRepaint(frameUsedByDate);\r
+               }\r
+       }\r
+       \r
+       // 以下共通部品\r
+       \r
+       private void _updPColors(Env ec, PaperColorsMap pc, ArrayList<JTXTButton> fa) {\r
+               for ( JTXTButton b2 : fa ) {\r
+                       b2.setBackground(pc.get(b2.getInfo().genre));\r
+               }\r
+       }\r
+\r
+       private void _updPBounds(Bounds bc, ArrayList<JTXTButton> fa) {\r
+               \r
+               JTXTButton.setColumnWidth(bc.getBangumiColumnWidth());\r
+               JTXTButton.setHeightMultiplier(bc.getPaperHeightMultiplier() * paperHeightZoom);\r
+               \r
+               for ( JTXTButton b2 :  fa ) {\r
+                       b2.reVBounds();\r
+               }\r
+       }\r
+       \r
+       private void _updPBorders(Env ec, Bounds bc, ArrayList<JTXTButton> fa) {\r
+               dborder.setDashColor(ec.getMatchedBorderColor());\r
+               dborder.setThickness(ec.getMatchedBorderThickness());\r
+               for ( JTXTButton b2 :  fa ) {\r
+                       if ( bc.getShowMatchedBorder() && b2.isStandby() ) {\r
+                               if ( b2.getBorder() != dborder )\r
+                                       b2.setBorder(dborder);\r
+                       }\r
+                       else {\r
+                               if ( b2.getBorder() != lborder )\r
+                                       b2.setBorder(lborder);\r
+                       }\r
+               }\r
+       }\r
+       \r
+       private void _updPRepaint(ArrayList<JTXTButton> fa) {\r
+               for ( JTXTButton b2 :  fa ) {\r
+                       b2.forceRepaint();\r
+               }\r
+       }\r
+\r
+}\r
diff --git a/TinyBannavi/src/tainavi/AbsRecordedListView.java b/TinyBannavi/src/tainavi/AbsRecordedListView.java
new file mode 100644 (file)
index 0000000..975aac1
--- /dev/null
@@ -0,0 +1,624 @@
+package tainavi;\r
+\r
+import java.awt.BorderLayout;\r
+import java.awt.Color;\r
+import java.awt.Component;\r
+import java.awt.Dimension;\r
+import java.awt.event.ComponentAdapter;\r
+import java.awt.event.ComponentEvent;\r
+import java.util.ArrayList;\r
+import java.util.Comparator;\r
+import java.util.HashMap;\r
+\r
+import javax.swing.JPanel;\r
+import javax.swing.JScrollPane;\r
+import javax.swing.JTable;\r
+import javax.swing.event.ListSelectionEvent;\r
+import javax.swing.event.ListSelectionListener;\r
+import javax.swing.event.TableModelEvent;\r
+import javax.swing.table.DefaultTableColumnModel;\r
+import javax.swing.table.DefaultTableModel;\r
+import javax.swing.table.TableCellRenderer;\r
+import javax.swing.table.TableColumn;\r
+import javax.swing.table.TableModel;\r
+import javax.swing.table.TableRowSorter;\r
+\r
+\r
+/**\r
+ * 録画結果一覧タブのクラス\r
+ */\r
+public abstract class AbsRecordedListView extends JPanel {\r
+\r
+       private static final long serialVersionUID = 1L;\r
+\r
+       public static void setDebug(boolean b) {debug = b; }\r
+       private static boolean debug = false;\r
+       \r
+\r
+       /*******************************************************************************\r
+        * 抽象メソッド\r
+        ******************************************************************************/\r
+       \r
+       protected abstract Env getEnv();\r
+       protected abstract Bounds getBoundsEnv();\r
+       \r
+       protected abstract HDDRecorderList getRecorderList();\r
+       \r
+       //protected abstract StatusWindow getStWin(); \r
+       //protected abstract StatusTextArea getMWin();\r
+       //protected abstract AbsReserveDialog getReserveDialog();\r
+       \r
+       protected abstract Component getParentComponent();\r
+       \r
+       protected abstract void ringBeep();\r
+       \r
+       /**\r
+        * @see Viewer.VWToolBar#getSelectedRecorder()\r
+        */\r
+       protected abstract String getSelectedRecorderOnToolbar();\r
+       \r
+\r
+       /*******************************************************************************\r
+        * 定数\r
+        ******************************************************************************/\r
+       \r
+       private static final String MSGID = "[録画結果一覧] ";\r
+       private static final String ERRID = "[ERROR]"+MSGID;\r
+       private static final String DBGID = "[DEBUG]"+MSGID;\r
+       \r
+       \r
+       /*******************************************************************************\r
+        * 部品\r
+        ******************************************************************************/\r
+       \r
+       // オブジェクト\r
+       private final Env env = getEnv();\r
+       private final Bounds bounds = getBoundsEnv();\r
+       private final HDDRecorderList recorders = getRecorderList();\r
+\r
+       //private final StatusWindow StWin = getStWin();                        // これは起動時に作成されたまま変更されないオブジェクト\r
+       //private final StatusTextArea MWin = getMWin();                        // これは起動時に作成されたまま変更されないオブジェクト\r
+       \r
+       private final Component parent = getParentComponent();  // これは起動時に作成されたまま変更されないオブジェクト\r
+       \r
+       // メソッド\r
+       //private void StdAppendMessage(String message) { System.out.println(message); }\r
+       //private void StdAppendError(String message) { System.err.println(message); }\r
+       //private void StWinSetVisible(boolean b) { StWin.setVisible(b); }\r
+       //private void StWinSetLocationCenter(Component frame) { CommonSwingUtils.setLocationCenter(frame, (VWStatusWindow)StWin); }\r
+       \r
+       /**\r
+        * カラム定義\r
+        */\r
+       \r
+       public static HashMap<String,Integer> getColumnIniWidthMap() {\r
+               if (rcmap.size() == 0 ) {\r
+                       for ( RecedColumn rc : RecedColumn.values() ) {\r
+                               rcmap.put(rc.toString(),rc.getIniWidth());      // toString()!\r
+                       }\r
+               }\r
+               return rcmap;\r
+       }\r
+       \r
+       private static final HashMap<String,Integer> rcmap = new HashMap<String, Integer>();\r
+       \r
+       public static enum RecedColumn {\r
+               START           ("開始",                      150),\r
+               END                     ("終了",                      50),\r
+               LENGTH          ("長さ",                      50),\r
+               DROP            ("ドロップ",                50),\r
+               DROPMPEG        ("MPEG",                50),\r
+               TITLE           ("番組タイトル",  300),\r
+               CHNAME          ("チャンネル名",  150),\r
+               RESULT          ("録画結果",                200),\r
+               RECORDER        ("レコーダ",                200),\r
+               ;\r
+\r
+               private String name;\r
+               private int iniWidth;\r
+\r
+               private RecedColumn(String name, int iniWidth) {\r
+                       this.name = name;\r
+                       this.iniWidth = iniWidth;\r
+               }\r
+\r
+               public String getName() {\r
+                       return name;\r
+               }\r
+\r
+               public int getIniWidth() {\r
+                       return iniWidth;\r
+               }\r
+               \r
+               public int getColumn() {\r
+                       return ordinal();\r
+               }\r
+       };\r
+       \r
+\r
+       /*******************************************************************************\r
+        * コンポーネント\r
+        ******************************************************************************/\r
+\r
+       private class RecordedItem extends RowItem implements Cloneable {\r
+       \r
+               String start;   // YYYY/MM/DD(WD) hh:mm\r
+               String end;                     // hh:mm\r
+               Integer length;\r
+               Integer drop;\r
+               Integer dropmpeg;\r
+               String title;\r
+               String chname;\r
+               String result;\r
+               String recorder;\r
+\r
+               String hide_detail;\r
+               Boolean hide_succeeded;\r
+               \r
+               @Override\r
+               protected void myrefresh(RowItem o) {\r
+                       RecordedItem c = (RecordedItem) o;\r
+                       \r
+                       c.addData(start);\r
+                       c.addData(end);\r
+                       c.addData(length);\r
+                       c.addData(drop);\r
+                       c.addData(dropmpeg);\r
+                       c.addData(title);\r
+                       c.addData(chname);\r
+                       c.addData(result);\r
+                       c.addData(recorder);\r
+               }\r
+               \r
+               public RecordedItem clone() {\r
+                       return (RecordedItem) super.clone();\r
+               }\r
+       }\r
+\r
+       // ソートが必要な場合はTableModelを作る。ただし、その場合Viewのrowがわからないので行の入れ替えが行えない\r
+       private class ReservedTableModel extends DefaultTableModel {\r
+\r
+               private static final long serialVersionUID = 1L;\r
+               \r
+               @Override\r
+               public Object getValueAt(int row, int column) {\r
+                       RecordedItem c = rowView.get(row); \r
+                       if ( c.getColumnCount() > column ) {\r
+                               if ( column == RecedColumn.LENGTH.getColumn() ) {\r
+                                       return String.valueOf(c.length)+"m";\r
+                               }\r
+                               return c.get(column);\r
+                       }\r
+                       return null;\r
+               }\r
+               \r
+               @Override\r
+               public int getRowCount() {\r
+                       return rowView.size();\r
+               }\r
+\r
+               public ReservedTableModel(String[] colname, int i) {\r
+                       super(colname,i);\r
+               }\r
+               \r
+       }\r
+       \r
+       //private final RecordedItem sa = new RecordedItem();\r
+       \r
+       private JScrollPane jsc_list = null;\r
+       private JScrollPane jsc_detail = null;\r
+       private JTextAreaWithPopup jta_detail = null;\r
+       private JNETable jTable_reced = null;\r
+       private JTable jTable_rowheader = null;\r
+\r
+       private DefaultTableModel tableModel_reced = null;\r
+       \r
+       private DefaultTableModel rowheaderModel_reced = null;\r
+\r
+       // 表示用のテーブル\r
+       private final RowItemList<RecordedItem> rowView = new RowItemList<RecordedItem>();\r
+       \r
+       // テーブルの実体\r
+       private final RowItemList<RecordedItem> rowData = new RowItemList<RecordedItem>();\r
+       \r
+       /*******************************************************************************\r
+        * コンストラクタ\r
+        ******************************************************************************/\r
+       \r
+       public AbsRecordedListView() {\r
+               \r
+               super();\r
+               \r
+               this.setLayout(new BorderLayout());\r
+               this.add(getJScrollPane_list(), BorderLayout.CENTER);\r
+               this.add(getJTextPane_detail(), BorderLayout.PAGE_END);\r
+               \r
+               // バグ対応\r
+               /*\r
+               if ( bounds.getRecordedColumnSize() == null ) {\r
+                       System.err.println(ERRID+"なんらかの不具合によりテーブルのカラム幅設定が取得できませんでした。設定はリセットされました。申し訳ありません。");\r
+                       bounds.setRecordedColumnSize(rcmap);\r
+               }\r
+               else {\r
+                       for ( Entry<String, Integer> en : rcmap.entrySet() ) {\r
+                               try {\r
+                                       bounds.getListedColumnSize().get(en.getKey());\r
+                               }\r
+                               catch (NullPointerException e) {\r
+                                       System.err.println(ERRID+en.getKey()+", "+e.toString());\r
+                                       bounds.getListedColumnSize().put(en.getKey(),en.getValue());\r
+                               }\r
+                       }\r
+               }\r
+               */\r
+               \r
+               \r
+               //\r
+               this.addComponentListener(cl_tabShown);\r
+       }\r
+\r
+       /*******************************************************************************\r
+        * アクション\r
+        ******************************************************************************/\r
+       \r
+       // 対外的な\r
+       \r
+       /**\r
+        * 予約一覧を描画してほしいかなって\r
+        * ★synchronized(rowData)★\r
+        * @see #cl_tabShown\r
+        */\r
+       public void redrawRecordedList() {\r
+               // ★★★ イベントにトリガーされた処理がかちあわないように synchronized() ★★★\r
+               synchronized ( rowView ) {\r
+                       _redrawRecordedList();\r
+               }\r
+       }\r
+       \r
+       private void _redrawRecordedList() {\r
+               //\r
+               rowData.clear();\r
+               \r
+               // 選択されたレコーダ\r
+               String myself = getSelectedRecorderOnToolbar();\r
+               HDDRecorderList recs = recorders.getMyself(myself);\r
+\r
+               for ( HDDRecorder recorder : recs )\r
+               {\r
+                       if ( recorder.isBackgroundOnly() ) {\r
+                               continue;\r
+                       }\r
+                       \r
+                       // 並べ替えるために新しいリストを作成する\r
+                       for ( RecordedInfo ro : recorder.getRecorded() ) {\r
+                                                               \r
+                               RecordedItem sa = new RecordedItem();\r
+                               \r
+                               sa.start = ro.getDate()+" "+ro.getAhh()+":"+ro.getAmm();        // YYYY/MM/DD(WD) hh:mm\r
+                               sa.end = ro.getZhh()+":"+ro.getZmm();\r
+                               sa.length = ro.getLength();\r
+                               sa.drop = ro.getDrop();\r
+                               sa.dropmpeg = ro.getDrop_mpeg();\r
+                               sa.title = ro.getTitle();\r
+                               sa.chname = ro.getCh_name();\r
+                               sa.result = ro.getResult();\r
+                               sa.recorder = recorder.Myself();\r
+                               \r
+                               sa.hide_detail = ro.getDetail();\r
+                               sa.hide_succeeded = ro.getSucceeded();\r
+                               \r
+                               sa.fireChanged();\r
+                               \r
+                               addRow(sa);\r
+                       }\r
+               }\r
+               \r
+               // 表示用\r
+               rowView.clear();\r
+               for ( RecordedItem a : rowData ) {\r
+                       rowView.add(a);\r
+               }\r
+\r
+               tableModel_reced.fireTableDataChanged();\r
+               ((DefaultTableModel)jTable_rowheader.getModel()).fireTableDataChanged();\r
+               \r
+               jta_detail.setText(null);\r
+\r
+               //jta_detail.setText("レコーダから予約結果の一覧を取得して表示します。現在の対応レコーダはTvRock/EpgDataCap_Bonのみです。");\r
+       }\r
+       \r
+       \r
+       /**\r
+        * 絞り込み検索の本体(現在リストアップされているものから絞り込みを行う)(親から呼ばれるよ!)\r
+        */\r
+       public void redrawListByKeywordFilter(SearchKey keyword, String target) {\r
+               \r
+               rowView.clear();\r
+               \r
+               // 情報を一行ずつチェックする\r
+               if ( keyword != null ) {\r
+                       for ( RecordedItem a : rowData ) {\r
+                               \r
+                               ProgDetailList tvd = new ProgDetailList();\r
+                               tvd.title = a.title;\r
+                               tvd.titlePop = TraceProgram.replacePop(tvd.title);\r
+                               \r
+                               // タイトルを整形しなおす\r
+                               boolean isFind = SearchProgram.isMatchKeyword(keyword, "", tvd);\r
+                               \r
+                               if ( isFind ) {\r
+                                       rowView.add(a);\r
+                               }\r
+                       }\r
+               }\r
+               else {\r
+                       for ( RecordedItem a : rowData ) {\r
+                               rowView.add(a);\r
+                       }\r
+               }\r
+               \r
+               // fire!\r
+               tableModel_reced.fireTableDataChanged();\r
+               rowheaderModel_reced.fireTableDataChanged();\r
+       }\r
+       \r
+       /**\r
+        * カラム幅を保存する(鯛ナビ終了時に呼び出されるメソッド)\r
+        */\r
+       public void copyColumnWidth() {\r
+               DefaultTableColumnModel columnModel = (DefaultTableColumnModel)jTable_reced.getColumnModel();\r
+               TableColumn column = null;\r
+               for ( RecedColumn rc : RecedColumn.values() ) {\r
+                       if ( rc.getIniWidth() < 0 ) {\r
+                               continue;\r
+                       }\r
+                       column = columnModel.getColumn(rc.ordinal());\r
+                       //bounds.getRecordedColumnSize().put(rc.toString(), column.getPreferredWidth());\r
+               }\r
+       }\r
+       \r
+       /**\r
+        * テーブルの行番号の表示のON/OFF\r
+        */\r
+       public void setRowHeaderVisible(boolean b) {\r
+               jsc_list.getRowHeader().setVisible(b);\r
+       }\r
+       \r
+       // 内部的な\r
+       \r
+       /**\r
+        * テーブル(の中の人)に追加\r
+        */\r
+       private void addRow(RecordedItem data) {\r
+               // 有効データ\r
+               int n=0;\r
+               for ( ; n<rowData.size(); n++ ) {\r
+                       RecordedItem c = rowData.get(n);\r
+                       if ( c.start.compareTo(data.start) < 0 ) {\r
+                               break;\r
+                       }\r
+               }\r
+               rowData.add(n,data);\r
+       }\r
+\r
+       /*******************************************************************************\r
+        * リスナー\r
+        ******************************************************************************/\r
+       \r
+       /**\r
+        * タブが開かれたら表を書き換える\r
+        * ★synchronized(rowData)★\r
+        * @see #redrawRecordedList()\r
+        */\r
+       private final ComponentAdapter cl_tabShown = new ComponentAdapter() {\r
+               @Override\r
+               public void componentShown(ComponentEvent e) {\r
+                       // ★★★ イベントにトリガーされた処理がかちあわないように synchronized() ★★★\r
+                       synchronized ( rowView ) {\r
+                               _redrawRecordedList();\r
+                       }\r
+               }\r
+       };\r
+\r
+       /**\r
+        *  行を選択すると詳細が表示されるようにする\r
+        */\r
+       private final ListSelectionListener lsSelectListner = new ListSelectionListener() {\r
+               @Override\r
+               public void valueChanged(ListSelectionEvent e) {\r
+                       if(e.getValueIsAdjusting()) return;\r
+                       if (jTable_reced.getSelectedRow() >= 0) {\r
+                               int row = jTable_reced.convertRowIndexToModel(jTable_reced.getSelectedRow());\r
+                               RecordedItem c = rowView.get(row);\r
+                               jta_detail.setText(c.hide_detail);\r
+                               jta_detail.setCaretPosition(0);\r
+                       }\r
+               }\r
+       };\r
+       \r
+       /*******************************************************************************\r
+        * コンポーネント\r
+        ******************************************************************************/\r
+\r
+       private JScrollPane getJScrollPane_list() {\r
+               \r
+               if ( jsc_list == null ) {\r
+                       jsc_list = new JScrollPane();\r
+                       \r
+                       jsc_list.setRowHeaderView(jTable_rowheader = new JTableRowHeader(rowView));\r
+                       jsc_list.setViewportView(getNETable_reced());\r
+                       \r
+                       Dimension d = new Dimension(jTable_rowheader.getPreferredSize().width,0);\r
+                       jsc_list.getRowHeader().setPreferredSize(d);\r
+                       \r
+                       this.setRowHeaderVisible(env.getRowHeaderVisible());\r
+               }\r
+               \r
+               return jsc_list;\r
+       }\r
+\r
+       private JScrollPane getJTextPane_detail() {\r
+               if ( jsc_detail == null ) {\r
+                       jsc_detail = new JScrollPane(jta_detail = new JTextAreaWithPopup(),JScrollPane.VERTICAL_SCROLLBAR_ALWAYS,JScrollPane.HORIZONTAL_SCROLLBAR_ALWAYS);\r
+                       jta_detail.setRows(8);\r
+                       jta_detail.setEditable(false);\r
+                       jta_detail.setBackground(Color.LIGHT_GRAY);\r
+               }\r
+               return jsc_detail;\r
+       }\r
+       \r
+       private JNETable getNETable_reced() {\r
+               if (jTable_reced == null) {\r
+                       \r
+                       ArrayList<String> cola = new ArrayList<String>();\r
+                       for ( RecedColumn rc : RecedColumn.values() ) {\r
+                               if ( rc.getIniWidth() >= 0 ) {\r
+                                       cola.add(rc.getName());\r
+                               }\r
+                       }\r
+                       String[] colname = cola.toArray(new String[0]);\r
+                       \r
+                       tableModel_reced = new ReservedTableModel(colname, 0);\r
+                       jTable_reced = new JNETableRecorded(tableModel_reced, false);\r
+                       jTable_reced.setAutoResizeMode(JNETable.AUTO_RESIZE_OFF);\r
+                       \r
+                       // ヘッダのモデル\r
+                       rowheaderModel_reced = (DefaultTableModel) jTable_rowheader.getModel();\r
+                       \r
+                       // ソータを付ける\r
+                       TableRowSorter<TableModel> sorter = new TableRowSorter<TableModel>(tableModel_reced);\r
+                       jTable_reced.setRowSorter(sorter);\r
+                       \r
+                       // 数値でソートする項目用の計算式(番組長とか)\r
+                       final Comparator<String> titlecomp = new Comparator<String>() {\r
+                               \r
+                               @Override\r
+                               public int compare(String o1, String o2) {\r
+                                       String t1 = TraceProgram.replacePop(o1.replaceAll(TVProgram.titlePrefixRemoveExpr, "")).replaceFirst(TVProgram.epnoNormalizeExpr, "$1\\0$2");\r
+                                       String t2 = TraceProgram.replacePop(o2.replaceAll(TVProgram.titlePrefixRemoveExpr, "")).replaceFirst(TVProgram.epnoNormalizeExpr, "$1\\0$2");\r
+                                       return t1.compareTo(t2);\r
+                               }\r
+                       };\r
+                       \r
+                       // ソーターの効かない項目用の計算式(重複マーク)\r
+                       final Comparator<String> noncomp = new Comparator<String>() {\r
+                               @Override\r
+                               public int compare(String o1, String o2) {\r
+                                       return 0;\r
+                               }\r
+                       };\r
+                       \r
+                       sorter.setComparator(jTable_reced.getColumn(RecedColumn.TITLE.getName()).getModelIndex(),titlecomp);\r
+                       sorter.setComparator(jTable_reced.getColumn(RecedColumn.END.getName()).getModelIndex(),noncomp);\r
+                       \r
+                       // 各カラムの幅\r
+                       DefaultTableColumnModel columnModel = (DefaultTableColumnModel)jTable_reced.getColumnModel();\r
+                       TableColumn column = null;\r
+                       for ( RecedColumn rc : RecedColumn.values() ) {\r
+                               if ( rc.getIniWidth() < 0 ) {\r
+                                       continue;\r
+                               }\r
+                               column = columnModel.getColumn(rc.ordinal());\r
+                               //column.setPreferredWidth(bounds.getRecordedColumnSize().get(rc.toString()));\r
+                               column.setPreferredWidth(rc.getIniWidth());\r
+                       }\r
+                       \r
+                       // 詳細表示\r
+                       jTable_reced.getSelectionModel().addListSelectionListener(lsSelectListner);\r
+               }\r
+               return jTable_reced;\r
+       }\r
+\r
+       \r
+       \r
+       \r
+       /*******************************************************************************\r
+        * 表表示\r
+        ******************************************************************************/\r
+       \r
+       private class JNETableRecorded extends JNETable {\r
+\r
+               private static final long serialVersionUID = 1L;\r
+\r
+               private Color failedColor = new Color(255,255,0);\r
+               private Color dropMpegColor = new Color(255,0,0);\r
+               private Color dropColor = new Color(255,204,204);\r
+               \r
+               private int prechkrow = -1;\r
+               private boolean prechksucceeded = false;\r
+               \r
+               @Override\r
+               public Component prepareRenderer(TableCellRenderer tcr, int row, int column) {\r
+                       Component c = super.prepareRenderer(tcr, row, column);\r
+                       Color fgColor = null;\r
+                       Color bgColor = null;\r
+                       if(isRowSelected(row)) {\r
+                               fgColor = this.getSelectionForeground();\r
+                               bgColor = this.getSelectionBackground();\r
+                       }\r
+                       else {\r
+                               fgColor = this.getForeground();\r
+                               \r
+                               int xrow = this.convertRowIndexToModel(row);\r
+                               RecordedItem item = rowView.get(xrow);\r
+                               \r
+                               if ( ! item.hide_succeeded ) {\r
+                                       bgColor = failedColor;\r
+                               }\r
+                               else if ( item.dropmpeg > 0 ) {\r
+                                       bgColor = dropMpegColor;\r
+                               }\r
+                               else if ( item.drop > 0 ) {\r
+                                       bgColor = dropColor;\r
+                               }\r
+                               else {\r
+                                       bgColor = (isSepRowColor && row%2 == 1)?(evenColor):(super.getBackground());\r
+                               }\r
+                       }\r
+                       c.setForeground(fgColor);\r
+                       c.setBackground(bgColor);\r
+                       return c;\r
+               }\r
+               \r
+               // 連続して同じ行へのアクセスがあったら計算を行わず前回のままにする\r
+               private boolean isRowPassed(int prow) {\r
+                       \r
+                       if(prechkrow == prow) {\r
+                               return prechksucceeded;\r
+                       }\r
+\r
+                       int row = this.convertRowIndexToModel(prow);\r
+                       RecordedItem c = rowView.get(row);\r
+\r
+                       {\r
+                               // 実行可能かどうか\r
+                               prechkrow = prow;\r
+                               prechksucceeded = c.hide_succeeded;\r
+                               return prechksucceeded;\r
+                       }\r
+               }\r
+               \r
+               //\r
+               @Override\r
+               public void tableChanged(TableModelEvent e) {\r
+                       reset();\r
+                       super.tableChanged(e);\r
+               }\r
+               \r
+               private void reset() {\r
+                       prechkrow = -1;\r
+                       prechksucceeded = true;\r
+               }\r
+               \r
+               /*\r
+                * コンストラクタ\r
+                */\r
+               public JNETableRecorded(boolean b) {\r
+                       super(b);\r
+               }\r
+               public JNETableRecorded(TableModel d, boolean b) {\r
+                       super(d,b);\r
+               }\r
+       }\r
+\r
+}\r
diff --git a/TinyBannavi/src/tainavi/AbsRecorderSettingView.java b/TinyBannavi/src/tainavi/AbsRecorderSettingView.java
new file mode 100644 (file)
index 0000000..9ac0bb1
--- /dev/null
@@ -0,0 +1,1563 @@
+package tainavi;\r
+\r
+import java.awt.BorderLayout;\r
+import java.awt.Color;\r
+import java.awt.Component;\r
+import java.awt.Dimension;\r
+import java.awt.Point;\r
+import java.awt.Toolkit;\r
+import java.awt.datatransfer.Clipboard;\r
+import java.awt.datatransfer.StringSelection;\r
+import java.awt.event.ActionEvent;\r
+import java.awt.event.ActionListener;\r
+import java.awt.event.ItemEvent;\r
+import java.awt.event.ItemListener;\r
+import java.awt.event.KeyAdapter;\r
+import java.awt.event.KeyEvent;\r
+import java.awt.event.KeyListener;\r
+import java.awt.event.MouseAdapter;\r
+import java.awt.event.MouseEvent;\r
+import java.awt.event.MouseListener;\r
+import java.net.InetAddress;\r
+import java.net.UnknownHostException;\r
+import java.util.ArrayList;\r
+\r
+import javax.swing.DefaultComboBoxModel;\r
+import javax.swing.JButton;\r
+import javax.swing.JComboBox;\r
+import javax.swing.JLabel;\r
+import javax.swing.JPanel;\r
+import javax.swing.JPasswordField;\r
+import javax.swing.JScrollPane;\r
+import javax.swing.JTable;\r
+import javax.swing.JTextField;\r
+import javax.swing.ListSelectionModel;\r
+import javax.swing.ScrollPaneConstants;\r
+import javax.swing.SpringLayout;\r
+import javax.swing.SwingUtilities;\r
+import javax.swing.border.LineBorder;\r
+import javax.swing.table.DefaultTableColumnModel;\r
+import javax.swing.table.DefaultTableModel;\r
+import javax.swing.table.TableCellRenderer;\r
+import javax.swing.table.TableColumn;\r
+\r
+import tainavi.HDDRecorder.RecType;\r
+\r
+/**\r
+ * レコーダ設定のタブ\r
+ * @since 3.15.4β {@link Viewer}から分離\r
+ */\r
+public abstract class AbsRecorderSettingView extends JScrollPane {\r
+\r
+       private static final long serialVersionUID = 1L;\r
+\r
+       public static String getViewName() { return "レコーダ設定"; } \r
+\r
+       public void setDebug(boolean b) { debug = b; }\r
+       private static boolean debug = false;\r
+\r
+       /*******************************************************************************\r
+        * 抽象メソッド\r
+        ******************************************************************************/\r
+       \r
+       protected abstract Env getEnv();\r
+       protected abstract RecorderInfoList getRecInfos();\r
+       protected abstract HDDRecorderList getRecPlugins();\r
+       \r
+       protected abstract StatusWindow getStWin(); \r
+       protected abstract StatusTextArea getMWin();\r
+       \r
+       protected abstract Component getParentComponent();\r
+       protected abstract VWColorChooserDialog getCcWin(); \r
+       \r
+       protected abstract void ringBeep();\r
+       \r
+       // クラス内のイベントから呼び出されるもの\r
+       \r
+       /**\r
+        * レコーダ一覧の更新を反映してもらう\r
+        */\r
+       protected abstract void setRecInfos();\r
+\r
+       /*******************************************************************************\r
+        * 呼び出し元から引き継いだもの\r
+        ******************************************************************************/\r
+       \r
+       // オブジェクト\r
+       //private final Env env = getEnv();\r
+       private final RecorderInfoList recInfoList = getRecInfos();\r
+       private final HDDRecorderList recPlugins = getRecPlugins();\r
+       \r
+       private final StatusWindow StWin = getStWin();                  // これは起動時に作成されたまま変更されないオブジェクト\r
+       private final StatusTextArea MWin = getMWin();                  // これは起動時に作成されたまま変更されないオブジェクト\r
+       \r
+       private final Component parent = getParentComponent();  // これは起動時に作成されたまま変更されないオブジェクト\r
+       private final VWColorChooserDialog ccwin = getCcWin();  // これは起動時に作成されたまま変更されないオブジェクト\r
+       \r
+       // メソッド\r
+       //private void StdAppendMessage(String message) { System.out.println(message); }\r
+       //private void StdAppendError(String message) { System.err.println(message); }\r
+       private void StWinSetVisible(boolean b) { StWin.setVisible(b); }\r
+       private void StWinSetLocationCenter(Component frame) { CommonSwingUtils.setLocationCenter(frame, (VWStatusWindow)StWin); }\r
+\r
+       /*******************************************************************************\r
+        * 定数\r
+        ******************************************************************************/\r
+       \r
+       // レイアウト関連\r
+       \r
+       private static final int PARTS_WIDTH = 900;\r
+       private static final int PARTS_HEIGHT = 30;\r
+       private static final int SEP_WIDTH = 10;\r
+       private static final int SEP_HEIGHT = 10;\r
+       private static final int SEP_HEIGHT_NALLOW = 5;\r
+       \r
+       private static final int LABEL_WIDTH = 250;\r
+       private static final int CCLABEL_WIDTH = 140;\r
+       private static final int TEXT_WIDTH = CCLABEL_WIDTH*2+SEP_WIDTH;\r
+\r
+       private static final int BUTTON_WIDTH = 75;\r
+\r
+       private static final int UPDATE_WIDTH = 250;\r
+       private static final int HINT_WIDTH = 750;\r
+       \r
+       private static final int PANEL_WIDTH = LABEL_WIDTH+PARTS_WIDTH+SEP_WIDTH*3;\r
+       \r
+       //\r
+       private static final String ITEM_SCOPEDISABLE = "チェックしない";\r
+       \r
+       private static final String VALUE_CALENDAR_ENABLED = "C";\r
+       \r
+       // テキスト\r
+       \r
+       private static final String TEXT_HINT =\r
+                       "IPアドレス欄はx.x.x.x形式だけでなくホスト名でも指定が可能です。名前解決にはhostsファイルなどを利用してください。"+\r
+                       "フォルダの追加やプロファイルの変更など、レコーダの設定を変更した際は予約一覧取得を行って設定を取得してください。";\r
+\r
+       // ログ関連\r
+       \r
+       private static final String MSGID = "["+getViewName()+"] ";\r
+       private static final String ERRID = "[ERROR]"+MSGID;\r
+       private static final String DBGID = "[DEBUG]"+MSGID;\r
+       \r
+       // カラム設定\r
+       \r
+       /**\r
+        * テーブル上に"色"ではじまる項目名がいくつあるか=エンコーダを何個まで許容するか。\r
+        */\r
+       private static final ArrayList<RecorderColumn> colorColumns = new ArrayList<RecorderColumn>();\r
+\r
+       private static enum RecorderColumn {\r
+               IP                      ("IP",                  100),\r
+               PORT            ("PORT",                50),\r
+               MAC                     ("MAC",                 100),\r
+               BCAST           ("ブロードキャスト",      100),\r
+               RECID           ("機種",                      150),\r
+               USER            ("ID",                  80),\r
+               PW                      ("PW",                  80),\r
+               CALENDAR        ("連",                 25),\r
+               SCOPE           ("範",                 25),\r
+               TUNUM           ("数",                 25),\r
+               C1                      ("色1",                        40),\r
+               C2                      ("色2",                        40),\r
+               C3                      ("色3",                        40),\r
+               C4                      ("色4",                        40),\r
+               C5                      ("色5",                        40),\r
+               C6                      ("色6",                        40),\r
+               C7                      ("色7",                        40),\r
+               C8                      ("色8",                        40),\r
+               ;\r
+               \r
+               private String name;\r
+               private int iniWidth;\r
+\r
+               private RecorderColumn(String name, int iniWidth) {\r
+                       this.name = name;\r
+                       this.iniWidth = iniWidth;\r
+               }\r
+\r
+               public String getName() {\r
+                       return name;\r
+               }\r
+\r
+               public int getIniWidth() {\r
+                       return iniWidth;\r
+               }\r
+               \r
+               public int getColumn() {\r
+                       return ordinal();\r
+               }\r
+       }\r
+       \r
+       /*******************************************************************************\r
+        * 部品\r
+        ******************************************************************************/\r
+       \r
+       // コンポーネント\r
+       \r
+       private JPanel jPanel_recorder = null;\r
+       private JPanel jPanel_update = null;\r
+       \r
+       private JButton jButton_update = null;\r
+\r
+       private JLabel jLabel_recordertype = null;\r
+       private JComboBox jComboBox_recordertype = null;\r
+       \r
+       private JLabel jLabel_recorder = null;\r
+       private JTextFieldWithPopup jTextField_ipaddr = null;\r
+       private JTextFieldWithPopup jTextField_port = null;\r
+       \r
+       private JLabel jLabel_user = null;\r
+       private JTextFieldWithPopup jTextField_user = null;\r
+       private JPanel jPanel_passwd = null;\r
+       private JPasswordField jTextField_passwd = null;\r
+       \r
+       private JLabel jLabel_wakeup = null;\r
+       private JTextFieldWithPopup jTextField_macaddr = null;\r
+       private JTextFieldWithPopup jTextField_broadcast = null;\r
+       private JButton jButton_getmac = null;\r
+       \r
+       private JLabel jLabel_color = null;\r
+       private JCCLabel jLabel_colorcells[] = null;\r
+       \r
+       private JButton jButton_recorderadd = null;\r
+       private JButton jButton_recorderupd = null;\r
+       private JButton jButton_recorderdel = null;\r
+       private JButton jButton_recorderup = null;\r
+       private JButton jButton_recorderdown = null;\r
+       \r
+       private JScrollPane jScrollPane_recorders = null;\r
+       private RecorderTable jTable_recorders = null;\r
+\r
+       private JTextAreaWithPopup jta_help = null;\r
+       \r
+       private JCheckBoxPanel jCBP_calendar = null;\r
+\r
+       private JCheckBoxPanel jCBP_chchange = null;\r
+\r
+       // 主にTvRock/EDCB用\r
+       private JComboBoxPanel jCBX_scope = null;\r
+       \r
+       // 主にDIGA用\r
+       private JComboBoxPanel jCBX_tunernum = null;\r
+       \r
+       // 主にTvRock用 - 使わなくなった\r
+       private JTextFieldWithPopup jtf_opt = null;\r
+       \r
+       //private DefaultTableModel tableModel_recoders = new DefaultTableModel();\r
+       \r
+       // コンポーネント以外\r
+       \r
+       // テーブルの実態\r
+       private final RowItemList<RecorderItem> rowData = new RowItemList<RecorderItem>();\r
+       \r
+       \r
+       /*******************************************************************************\r
+        * コンストラクタ\r
+        ******************************************************************************/\r
+       \r
+       public AbsRecorderSettingView() {\r
+               \r
+               super();\r
+\r
+               // 色設定は何個あるかな?\r
+               for ( RecorderColumn rc : RecorderColumn.values() ) {\r
+                       if ( rc.getName().startsWith("色") ) {\r
+                               colorColumns.add(rc);\r
+                       }\r
+               }\r
+               \r
+               this.setVerticalScrollBarPolicy(ScrollPaneConstants.VERTICAL_SCROLLBAR_ALWAYS);\r
+               this.getVerticalScrollBar().setUnitIncrement(25);\r
+               this.setHorizontalScrollBarPolicy(ScrollPaneConstants.HORIZONTAL_SCROLLBAR_AS_NEEDED);\r
+               this.setColumnHeaderView(getJPanel_update());\r
+               this.setViewportView(getJPanel_recorder());\r
+               \r
+               // テーブルを初期化\r
+               updateTable();\r
+               \r
+               // 初期値\r
+               setSelectedRecoderTypeDefault();\r
+               \r
+               // 更新ボタンは使えない\r
+               jButton_recorderupd.setEnabled(false);\r
+               \r
+               setUpdateButtonEnhanced(false);\r
+       }\r
+       \r
+       /*******************************************************************************\r
+        * アクション\r
+        ******************************************************************************/\r
+       \r
+       private void updateTable() {\r
+               \r
+               rowData.clear();\r
+               \r
+               // エンコーダ別色分けのどーたらこーたら\r
+               for ( RecorderInfo ri : recInfoList ) {\r
+                       RecorderItem sa = new RecorderItem();\r
+                       sa.ipaddr = ri.getRecorderIPAddr();\r
+                       sa.portno = ri.getRecorderPortNo();\r
+                       sa.macaddr = ri.getRecorderMacAddr();\r
+                       sa.broadcast = ri.getRecorderBroadcast();\r
+                       sa.recorderid = ri.getRecorderId();\r
+                       sa.user = ri.getRecorderUser();\r
+                       sa.password = ri.getRecorderPasswd();\r
+                       sa.calendar = ri.getUseCalendar();\r
+                       sa.hide_chchange = ri.getUseChChange(); // 隠し\r
+                       sa.scope = ri.getRecordedCheckScope();\r
+                       sa.tunernum = ri.getTunerNum();\r
+                       for ( int n=0; n<colorColumns.size(); n++ ) {\r
+                               sa.setCx(n, CommonSwingUtils.getColoredString(ri.getEncoderColor(n),ri.getEncoder(n)));\r
+                       }\r
+\r
+                       sa.fireChanged();\r
+                       \r
+                       rowData.add(sa);\r
+               }\r
+\r
+               ((DefaultTableModel) jTable_recorders.getModel()).fireTableDataChanged();\r
+       }\r
+       \r
+       private void setSelectedRecoderTypeDefault() {\r
+               jComboBox_recordertype.setSelectedItem(null);\r
+               jComboBox_recordertype.setSelectedItem("NULL");\r
+               if ( ! "NULL".equals((String) jComboBox_recordertype.getSelectedItem()) && jComboBox_recordertype.getItemCount() >= 1 ) {\r
+                       jComboBox_recordertype.setSelectedIndex(0);\r
+               }\r
+       }\r
+       \r
+       /*******************************************************************************\r
+        * リスナー\r
+        ******************************************************************************/\r
+\r
+       private void setUpdateButtonEnhanced(boolean b) {\r
+               if (b) {\r
+                       ///jButton_update.setText("更新を確定する");\r
+                       jButton_update.setForeground(Color.RED);\r
+               }\r
+               else {\r
+                       //jButton_update.setText("更新を確定する");\r
+                       jButton_update.setForeground(Color.BLACK);\r
+               }\r
+               jButton_update.setEnabled(b);\r
+       }\r
+\r
+       /**\r
+        * 更新を確定する\r
+        */\r
+       private final ActionListener al_update = new ActionListener() {\r
+               @Override\r
+               public void actionPerformed(ActionEvent e) {\r
+                       TatCount tc = new TatCount();\r
+                       \r
+                       MWin.appendMessage(MSGID+"設定を保存します");\r
+                       StWin.clear();\r
+                       \r
+                       new SwingBackgroundWorker(false) {\r
+                               \r
+                               @Override\r
+                               protected Object doWorks() throws Exception {\r
+                                       \r
+\r
+                                       if (debug) System.err.println(DBGID+"追加するレコーダ情報の数: "+rowData.size());\r
+                                       \r
+                                       if ( rowData.size() == 0 ) {\r
+                                                       // からっぽだ\r
+                                                       recInfoList.clear();\r
+                                                       setRecInfos();\r
+                                       }\r
+                                       else {\r
+                                               try {\r
+                                                       RecorderInfoList newRIL = new RecorderInfoList();\r
+                                                       \r
+                                                       for ( RecorderItem c : rowData ) {\r
+                                                               RecorderInfo ri = new RecorderInfo();\r
+                                                               ri.setRecorderIPAddr(c.ipaddr);\r
+                                                               ri.setRecorderPortNo(c.portno);\r
+                                                               ri.setRecorderMacAddr(c.macaddr);\r
+                                                               ri.setRecorderBroadcast(c.broadcast);\r
+                                                               ri.setRecorderId(c.recorderid);\r
+                                                               ri.setRecorderUser(c.user);\r
+                                                               ri.setRecorderPasswd(c.password);\r
+                                                               ri.setUseCalendar(c.calendar);\r
+                                                               ri.setUseChChange(c.hide_chchange);\r
+                                                               ri.setRecordedCheckScope(c.scope);\r
+                                                               ri.setTunerNum(c.tunernum);\r
+                                                               //\r
+                                                               ri.clearEncoders();\r
+                                                               ri.clearEncoderColors();\r
+                                                               for (int n=0; n<colorColumns.size(); n++) {\r
+                                                                       String[] d = CommonSwingUtils.splitColorString(c.getCx(n));\r
+                                                                       if (d != null && d.length >= 2) {\r
+                                                                               ri.addEncoder(d[0]);\r
+                                                                               ri.addEncoderColor(d[1]);\r
+                                                                       }\r
+                                                               }\r
+                                                               \r
+                                                               if (debug) System.err.println(DBGID+"レコーダ情報を追加: "+ri.getRecorderIPAddr()+":"+ri.getRecorderPortNo()+":"+ri.getRecorderId());\r
+                                                               \r
+                                                               newRIL.add(ri);\r
+                                                       }\r
+                                                       \r
+                                                       // 置き換え\r
+                                                       recInfoList.clear();\r
+                                                       for ( RecorderInfo ri : newRIL ) {\r
+                                                               recInfoList.add(ri);\r
+                                                       }\r
+                                                       \r
+                                                       // 設定保存\r
+                                                       setRecInfos();\r
+                                               }\r
+                                               catch ( Exception e ) {\r
+                                                       e.printStackTrace();\r
+                                               }\r
+                                       }\r
+                                       \r
+                                       return null;\r
+                               }\r
+\r
+                               @Override\r
+                               protected void doFinally() {\r
+                                       StWinSetVisible(false);\r
+                               }\r
+                       }.execute();\r
+                       \r
+                       StWinSetLocationCenter(parent);\r
+                       StWinSetVisible(true);\r
+                       \r
+                       MWin.appendMessage(String.format(MSGID+"更新が完了しました。所要時間: %.2f秒",tc.end()));\r
+                       \r
+                       setUpdateButtonEnhanced(false);\r
+               }\r
+       };\r
+       \r
+       /**\r
+        * レコーダを選択する\r
+        */\r
+       private final ItemListener il_recorderSelected = new ItemListener() {\r
+               @Override\r
+               public void itemStateChanged(ItemEvent e) {\r
+                       if (e.getStateChange() == ItemEvent.SELECTED) {\r
+                               \r
+                               String recId = (String) ((JComboBox)e.getSource()).getSelectedItem();\r
+                                       \r
+                               // 値をクリアする\r
+                               clearInputs();\r
+                               \r
+                               // ラベルを適切な値に変更する\r
+                               jTextField_ipaddr.setEnabled(true);\r
+                               jTextField_port.setEnabled(true);\r
+                               jTextField_user.setEnabled(true);\r
+                               jTextField_passwd.setEnabled(true);\r
+                               jTextField_macaddr.setEnabled(true);\r
+                               jTextField_broadcast.setEnabled(true);\r
+                               jButton_getmac.setEnabled(false);\r
+                               jCBP_calendar.setEnabled(true);\r
+                               jCBX_scope.setEnabled(true);\r
+                               jCBX_tunernum.setEnabled(false);\r
+                               \r
+                               // とりあえずIDの一致するものを全部拾う\r
+                               HDDRecorderList rl = recPlugins.get(recId);\r
+                               if ( rl.size() <= 0) {\r
+                                       MWin.appendError(ERRID+"選択されたプラグインのインスタンスが存在しない: "+recId);\r
+                                       return;\r
+                               }\r
+                               \r
+                               HDDRecorder rec = rl.get(0);\r
+                               RecType recTyp = rl.getRecId2Type(recId);\r
+                               switch (recTyp) {\r
+                               case MAIL:\r
+                                       jLabel_recorder.setText("SMTPサーバ/SMTPのポート番号");\r
+                                       jLabel_user.setText("Fromアドレス/SMTPのパスワード");\r
+                                       jLabel_wakeup.setText("Toアドレス/RDに設定したパスワード");\r
+                                       break;\r
+                                       \r
+                               case CALENDAR:\r
+                                       jLabel_recorder.setText("-");\r
+                                       jLabel_user.setText("GoogleのID/パスワード");\r
+                                       jLabel_wakeup.setText("リマインダ(分)");\r
+                                       \r
+                                       jTextField_ipaddr.setEnabled(false);\r
+                                       jTextField_port.setEnabled(false);\r
+                                       jTextField_broadcast.setEnabled(false);\r
+                                       jCBP_calendar.setEnabled(false);\r
+                                       jCBX_scope.setEnabled(false);\r
+                                       \r
+                                       if (rec.getUser().length() > 0)         jTextField_user.setText(rec.getUser());\r
+                                       if (rec.getPasswd().length() > 0)       jTextField_passwd.setText(rec.getPasswd());\r
+                                       if (rec.getMacAddr().length() > 0)      jTextField_macaddr.setText(rec.getMacAddr());\r
+                                       break;\r
+                                       \r
+                               case TUNER:\r
+                                       jLabel_recorder.setText("-");\r
+                                       jLabel_user.setText("インストールディレクトリ");\r
+                                       jLabel_wakeup.setText("-");\r
+                                       \r
+                                       jTextField_user.setText(rl.get(recId).get(0).getUser());\r
+                                       jTextField_passwd.setText("dummy");\r
+                                       \r
+                                       jTextField_ipaddr.setEnabled(false);\r
+                                       jTextField_port.setEnabled(false);\r
+                                       jTextField_passwd.setEnabled(false);\r
+                                       jTextField_macaddr.setEnabled(false);\r
+                                       jTextField_broadcast.setEnabled(false);\r
+                                       break;\r
+                                       \r
+                               case EPG:\r
+                               case NULL:\r
+                                       jLabel_recorder.setText("-");\r
+                                       jLabel_user.setText("-");\r
+                                       jLabel_wakeup.setText("-");\r
+                                       \r
+                                       jTextField_ipaddr.setText("EPG");\r
+                                       jTextField_port.setText("dummy");\r
+                                       jTextField_user.setText("dummy");\r
+                                       jTextField_passwd.setText("dummy");\r
+                                       \r
+                                       jTextField_ipaddr.setEnabled(false);\r
+                                       jTextField_port.setEnabled(false);\r
+                                       jTextField_user.setEnabled(false);\r
+                                       jTextField_passwd.setEnabled(false);\r
+                                       jTextField_macaddr.setEnabled(false);\r
+                                       jTextField_broadcast.setEnabled(false);\r
+                                       jCBX_scope.setEnabled(false);\r
+                                       break;\r
+                                       \r
+                               case RECORDER:\r
+                               default:\r
+                                       jLabel_recorder.setText("レコーダIP/PORT");\r
+                                       jLabel_user.setText("ID/PASS");\r
+                                       jLabel_wakeup.setText("レコーダMAC/ブロードキャスト(任意)");\r
+                                       \r
+                                       if (rec.getIPAddr().length() > 0)       jTextField_ipaddr.setText(rec.getIPAddr());\r
+                                       if (rec.getPortNo().length() > 0)       jTextField_port.setText(rec.getPortNo());\r
+                                       if (rec.getUser().length() > 0)         jTextField_user.setText(rec.getUser());\r
+                                       if (rec.getPasswd().length() > 0)       jTextField_passwd.setText(rec.getPasswd());\r
+                                       \r
+                                       jButton_getmac.setEnabled(true);\r
+\r
+                                       break;\r
+                               }\r
+\r
+                               jCBP_chchange.setEnabled(rec.isChangeChannelSupported());\r
+\r
+                               jCBP_calendar.setSelected(rec.getUseCalendar());\r
+                               \r
+                               // 主にDIGA用\r
+                               jCBX_tunernum.setText("-");\r
+                               if ( rl.get(0).getTunerNum() > 0 ) {\r
+                                       jCBX_tunernum.setText("最大同時録画数の手動設定");\r
+                                       jCBX_tunernum.setEnabled(true);\r
+                                       jCBX_tunernum.setSelectedItem(String.valueOf(rl.get(0).getTunerNum()));\r
+                               }\r
+                               \r
+                               // 主にTvRock用\r
+                               String opt = rl.get(0).getOptString();\r
+                               if ( opt == null ) {\r
+                                       jtf_opt.setEnabled(false);\r
+                                       jtf_opt.setText("");\r
+                               }\r
+                               else {\r
+                                       jtf_opt.setEnabled(true);\r
+                                       jtf_opt.setText(opt);\r
+                               }\r
+                       }\r
+               }\r
+       };\r
+       \r
+       /**\r
+        * レコーダを追加する\r
+        */\r
+       private final ActionListener al_addRecorder = new ActionListener() {\r
+               @Override\r
+               public void actionPerformed(ActionEvent e) {\r
+                       if (CommonUtils.isNGChar(jTextField_ipaddr.getText()) || CommonUtils.isNGChar(jTextField_port.getText())) {\r
+                               MWin.appendError(ERRID+"【警告】レコーダIPまたはPORTに次の文字は利用できません "+CommonUtils.getNGCharList());\r
+                               ringBeep();\r
+                               return;\r
+                       }\r
+                       //\r
+                       if ( jTextField_user.getText().length() == 0 || jTextField_passwd.getPassword().length == 0 ) {\r
+                               MWin.appendError(ERRID+"【警告】IDとパスワードの入力は必須です");\r
+                               ringBeep();\r
+                               return;\r
+                       }\r
+                       \r
+                       // 重複チェック\r
+                       for ( int row=0; row<rowData.size(); row++ ) {\r
+                               RecorderItem c = rowData.get(row);\r
+                               String ip = c.ipaddr;\r
+                               String port = c.portno;\r
+                               String recid = c.recorderid;\r
+                               if ( ip.equals(jTextField_ipaddr.getText()) &&\r
+                                               port.equals(jTextField_port.getText()) &&\r
+                                               recid.equals((String)jComboBox_recordertype.getSelectedItem())\r
+                                               ) {\r
+                                       MWin.appendError(ERRID+"レコーダ情報が重複しています: "+ip+":"+port+":"+recid);\r
+                                       ringBeep();\r
+                                       return;\r
+                               }\r
+                       }\r
+                       \r
+                       // 入力からテーブルへ\r
+                       RecorderItem sa = copyInputs2Table();\r
+                       rowData.add(sa);\r
+                       \r
+                       // 入力欄をクリアする\r
+                       clearInputs();\r
+                       \r
+                       ((DefaultTableModel) jTable_recorders.getModel()).fireTableDataChanged();\r
+                       //jTable_recorders.updateUI();\r
+                       \r
+                       setSelectedRecoderTypeDefault();\r
+                       \r
+                       // 必要ないはずだけど\r
+                       jButton_recorderupd.setEnabled(false);\r
+\r
+                       setUpdateButtonEnhanced(true);\r
+\r
+                       return;\r
+               }\r
+       };\r
+\r
+       \r
+       /**\r
+        * レコーダを更新する\r
+        */\r
+       private final ActionListener al_updRecorder = new ActionListener() {\r
+               @Override\r
+               public void actionPerformed(ActionEvent e) {\r
+                       if (CommonUtils.isNGChar(jTextField_ipaddr.getText()) || CommonUtils.isNGChar(jTextField_port.getText())) {\r
+                               MWin.appendError(ERRID+"【警告】レコーダIPまたはPORTに次の文字は利用できません "+CommonUtils.getNGCharList());\r
+                               ringBeep();\r
+                               return;\r
+                       }\r
+                       //\r
+                       if ( jTextField_user.getText().length() == 0 || jTextField_passwd.getPassword().length == 0 ) {\r
+                               MWin.appendError(ERRID+"【警告】IDとパスワードの入力は必須です");\r
+                               ringBeep();\r
+                               return;\r
+                       }\r
+                       \r
+                       // 重複チェックはないよ\r
+                       \r
+                       // 入力からテーブルへ\r
+                       int row =  jTable_recorders.convertRowIndexToModel(jTable_recorders.getSelectedRow());\r
+                       RecorderItem sa = copyInputs2Table();\r
+                       rowData.set(row, sa);\r
+                       \r
+                       // 入力欄をクリアする\r
+                       clearInputs();\r
+                       \r
+                       ((DefaultTableModel) jTable_recorders.getModel()).fireTableDataChanged();\r
+                       //jTable_recorders.updateUI();\r
+                       \r
+                       setSelectedRecoderTypeDefault();\r
+                       \r
+                       // 更新ボタンは無効に\r
+                       jButton_recorderupd.setEnabled(false);\r
+\r
+                       setUpdateButtonEnhanced(true);\r
+\r
+                       return;\r
+               }\r
+       };\r
+\r
+       /**\r
+        * レコーダを削除する\r
+        */\r
+       private final ActionListener al_delRecorder = new ActionListener() {\r
+               @Override\r
+               public void actionPerformed(ActionEvent e) {\r
+                       \r
+                       int row = jTable_recorders.getSelectedRow();\r
+                       \r
+                       if ( row < rowData.size() ) {\r
+\r
+                               // テーブルの情報を入力にコピー\r
+                               copyTable2Inputs(rowData.remove(row));\r
+                               \r
+                       }\r
+                       \r
+                       ((DefaultTableModel) jTable_recorders.getModel()).fireTableDataChanged();\r
+                       //jTable_recorders.updateUI();\r
+                       \r
+                       // 更新ボタンは無効に\r
+                       jButton_recorderupd.setEnabled(false);\r
+                       \r
+                       setUpdateButtonEnhanced(true);\r
+\r
+               }\r
+       };\r
+       \r
+       /**\r
+        * レコーダを一個上に上げる\r
+        */\r
+       private final MouseListener ml_upRecorder = new MouseAdapter() {\r
+               @Override\r
+               public void mouseClicked(MouseEvent e) {\r
+                       int row = jTable_recorders.getSelectedRow();\r
+                       rowData.up(row, 1);\r
+                       \r
+                       if ( row > 0 ) {\r
+                               ((DefaultTableModel) jTable_recorders.getModel()).fireTableDataChanged();\r
+                               jTable_recorders.setRowSelectionInterval(row-1,row-1);\r
+                       }\r
+                       \r
+                       setUpdateButtonEnhanced(true);\r
+               }\r
+       };\r
+       \r
+       /**\r
+        * レコーダを一個下に下げる\r
+        */\r
+       private final MouseListener ml_downRecorder = new MouseAdapter() {\r
+               @Override\r
+               public void mouseClicked(MouseEvent e) {\r
+                       int row = jTable_recorders.getSelectedRow();\r
+                       rowData.down(row, 1);\r
+                       \r
+                       if ( row < (rowData.size()-1) ) {\r
+                               ((DefaultTableModel) jTable_recorders.getModel()).fireTableDataChanged();\r
+                               jTable_recorders.setRowSelectionInterval(row+1,row+1);\r
+                       }\r
+                       \r
+                       setUpdateButtonEnhanced(true);\r
+               }\r
+       };\r
+       \r
+       /**\r
+        * MACアドレスを取得する\r
+        */\r
+       private final MouseListener ml_getmac = new MouseAdapter() {\r
+               \r
+               @Override\r
+               public void mouseClicked(MouseEvent e) {\r
+                       \r
+                       Component comp = (Component) e.getSource();\r
+                       \r
+                       comp.setEnabled(false);\r
+                       \r
+                       if ( ! _getmac() ) {\r
+                               ringBeep();\r
+                       }\r
+                       \r
+                       comp.setEnabled(true);\r
+\r
+               }\r
+               \r
+       };\r
+       \r
+       private boolean _getmac() {\r
+               \r
+               String mac = null;\r
+               String host = jTextField_ipaddr.getText();\r
+               String ip = null;\r
+               \r
+               try {\r
+                       ip = InetAddress.getByName(host).getHostAddress();\r
+               }\r
+               catch (UnknownHostException e1) {\r
+                       MWin.appendError(ERRID+"レコーダの指定をIPアドレスに変換できません: "+host);\r
+                       e1.printStackTrace();\r
+                       return false;\r
+               }\r
+               \r
+               mac = ArpCommand.getMac(ip);\r
+               if ( mac != null && mac.length() == 12 ) {\r
+                       jTextField_macaddr.setText(mac);\r
+                       jTextField_broadcast.setText("255.255.255.255");\r
+               }\r
+               \r
+               if ( mac == null ) {\r
+                       MWin.appendError(ERRID+"MACアドレスの取得に失敗しました: "+host+"("+ip+")");\r
+                       return false;\r
+               }\r
+\r
+               MWin.appendError(MSGID+"MACアドレスを取得しました: "+host+"("+ip+") -> "+mac);\r
+               return true;\r
+               \r
+       }\r
+       \r
+       /**\r
+        * テーブルはコピーさせないよ!\r
+        */\r
+       private final KeyListener kl_cannnotcopy = new KeyAdapter() {\r
+               @Override\r
+               public void keyTyped(KeyEvent e) {\r
+                       // クリップボードに残さない\r
+                       Clipboard cb = Toolkit.getDefaultToolkit().getSystemClipboard();\r
+                       StringSelection s = new StringSelection("きさま! 見ているなッ!");\r
+                       cb.setContents(s, null);\r
+                       // beep!メガドライブ\r
+                       ringBeep();\r
+               }\r
+       };\r
+\r
+       /**\r
+        * テーブルで行が選択された\r
+        */\r
+       private final MouseListener ml_tableSelected = new MouseAdapter() {\r
+               @Override\r
+               public void mouseClicked(MouseEvent e) {\r
+                       if (SwingUtilities.isLeftMouseButton(e)) {\r
+                               //\r
+                               JTable t = (JTable) e.getSource();\r
+                               Point p = e.getPoint();\r
+                               \r
+                               int row = t.convertRowIndexToModel(t.rowAtPoint(p));\r
+\r
+                               // 入力に戻す\r
+                               copyTable2Inputs(rowData.get(row));\r
+                               \r
+                               // 更新ボタンを有効にする\r
+                               jButton_recorderupd.setEnabled(true);\r
+                               \r
+                               //setUpdateButtonEnhanced(true);        // 新規・更新・削除・上・下ボタンを押した時だけ復活すればいい\r
+                       }\r
+               }\r
+       };\r
+       \r
+       private void clearInputs() {\r
+               jTextField_ipaddr.setText("");\r
+               jTextField_port.setText("");\r
+               jTextField_user.setText("");\r
+               jTextField_passwd.setText("");\r
+               jTextField_macaddr.setText("");\r
+               jTextField_broadcast.setText("");\r
+               for (int c=0; c<colorColumns.size(); c++) {\r
+                       jLabel_colorcells[c].setChoosed(new Color(0xff,0x00,0x00));\r
+                       jLabel_colorcells[c].setText(colorColumns.get(c).getName());\r
+               }\r
+               jCBP_calendar.setSelected(true);\r
+               jCBX_scope.setSelectedIndex(0);\r
+               jCBX_tunernum.setSelectedIndex(0);\r
+       }\r
+       \r
+       /**\r
+        * 入力をテーブルにコピーするためにオブジェクトを作成\r
+        */\r
+       private RecorderItem copyInputs2Table() {\r
+               \r
+               RecorderItem sa = new RecorderItem();\r
+               \r
+               sa.ipaddr = jTextField_ipaddr.getText();\r
+               sa.portno = jTextField_port.getText();\r
+               if ( jTextField_macaddr.getText().matches("^[ 0-9a-zA-Z:-]+$") ) {\r
+                       sa.macaddr = jTextField_macaddr.getText().replaceAll("[:-]", "").toUpperCase();\r
+               }\r
+               else {\r
+                       sa.macaddr = jTextField_macaddr.getText();\r
+               }\r
+               sa.broadcast = jTextField_broadcast.getText();\r
+               sa.recorderid = (String)jComboBox_recordertype.getSelectedItem();\r
+               sa.user = jTextField_user.getText();\r
+               sa.password = new String(jTextField_passwd.getPassword());\r
+               \r
+               sa.calendar = jCBP_calendar.isSelected();\r
+               sa.hide_chchange = jCBP_chchange.isSelected();\r
+               if ( ITEM_SCOPEDISABLE.equals((String) jCBX_scope.getSelectedItem()) ) {\r
+                       sa.scope = 0;\r
+               }\r
+               else {\r
+                       sa.scope = jCBX_scope.getSelectedIndex();\r
+               }\r
+               \r
+               // 主にDIGA用\r
+               if ( jCBX_tunernum.isEnabled() ) {\r
+                       sa.tunernum = Integer.valueOf((String) jCBX_tunernum.getSelectedItem());\r
+               }\r
+               else {\r
+                       sa.tunernum = 0;\r
+               }\r
+               \r
+               for (int n=0; n<colorColumns.size(); n++) {\r
+                       String cs = CommonUtils.color2str(jLabel_colorcells[n].getChoosed());\r
+                       String es = jLabel_colorcells[n].getText(); \r
+                       if ( n == 0 ) {\r
+                               es = (es.length() != 0) ? (jLabel_colorcells[n].getText()):("■");\r
+                       }\r
+                       sa.setCx(n, CommonSwingUtils.getColoredString(cs,es));\r
+               }\r
+               \r
+               sa.fireChanged();\r
+               \r
+               return sa;\r
+               \r
+       }\r
+       \r
+       /**\r
+        * テーブルの情報を入力に書き戻す\r
+        */\r
+       private void copyTable2Inputs(RecorderItem c) {\r
+               \r
+               jComboBox_recordertype.setSelectedItem(c.recorderid);\r
+               //\r
+               jTextField_ipaddr.setText(c.ipaddr);\r
+               jTextField_port.setText(c.portno);\r
+               jTextField_user.setText(c.user);\r
+               jTextField_passwd.setText(c.password);\r
+               jTextField_macaddr.setText(c.macaddr);\r
+               jTextField_broadcast.setText(c.broadcast);\r
+               \r
+               jCBP_calendar.setSelected(c.calendar);\r
+               jCBP_chchange.setSelected(c.hide_chchange);\r
+               jCBX_scope.setSelectedIndex(c.scope);\r
+               \r
+               // 主にDIGA用\r
+               if ( c.tunernum > 0 ) {\r
+                       jCBX_tunernum.setSelectedItem(String.valueOf(c.tunernum));\r
+               }\r
+               \r
+               // イロイッカイヅツ\r
+               for (int n=0; n<colorColumns.size(); n++) {\r
+                       String[] d = CommonSwingUtils.splitColorString(c.getCx(n));\r
+                       jLabel_colorcells[n].setChoosed(CommonUtils.str2color(d[1]));\r
+                       jLabel_colorcells[n].setText(d[0]);\r
+               }\r
+               \r
+       }\r
+\r
+       \r
+       /*******************************************************************************\r
+        * コンポーネント\r
+        ******************************************************************************/\r
+\r
+       private JPanel getJPanel_update() {\r
+               if (jPanel_update == null)\r
+               {\r
+                       jPanel_update = new JPanel();\r
+                       jPanel_update.setLayout(new SpringLayout());\r
+                       \r
+                       jPanel_update.setBorder(new LineBorder(Color.GRAY));\r
+                       \r
+                       int y = SEP_HEIGHT;\r
+                       CommonSwingUtils.putComponentOn(jPanel_update, getJButton_update("更新を確定する"), UPDATE_WIDTH, PARTS_HEIGHT, SEP_WIDTH, y);\r
+                       \r
+                       int yz = SEP_HEIGHT/2;\r
+                       int x = UPDATE_WIDTH+50;\r
+                       CommonSwingUtils.putComponentOn(jPanel_update, getJta_help(), HINT_WIDTH, PARTS_HEIGHT+SEP_HEIGHT, x, yz);\r
+                       \r
+                       y += (PARTS_HEIGHT + SEP_HEIGHT);\r
+                       \r
+                       // 画面の全体サイズを決める\r
+                       Dimension d = new Dimension(PANEL_WIDTH,y);\r
+                       jPanel_update.setPreferredSize(d);\r
+               }\r
+               return jPanel_update;\r
+       }\r
+       \r
+       private JPanel getJPanel_recorder() {\r
+               if (jPanel_recorder == null)\r
+               {\r
+                       jPanel_recorder = new JPanel();\r
+                       jPanel_recorder.setLayout(new SpringLayout());\r
+                       \r
+                       /*\r
+                        * レコーダ関連 \r
+                        */\r
+                       int table_w = 25;\r
+                       for ( RecorderColumn rc : RecorderColumn.values() ) {\r
+                               table_w += (rc.getIniWidth()>0)?(rc.getIniWidth()):(0);\r
+                       }\r
+                       \r
+                       int y = SEP_HEIGHT;\r
+                       int x = SEP_WIDTH+LABEL_WIDTH+SEP_WIDTH;\r
+                       CommonSwingUtils.putComponentOn(jPanel_recorder, new JLabel("登録済みレコーダ一覧"), LABEL_WIDTH, PARTS_HEIGHT, SEP_WIDTH, y);\r
+                       \r
+                       y+=(PARTS_HEIGHT+SEP_HEIGHT);\r
+                       int table_h = PARTS_HEIGHT*5;\r
+                       CommonSwingUtils.putComponentOn(jPanel_recorder, getJScrollPane_recorders(), table_w, table_h, SEP_WIDTH, y);\r
+                       \r
+                       y+=(table_h+SEP_HEIGHT);\r
+                       int yz = y;\r
+                       CommonSwingUtils.putComponentOn(jPanel_recorder, getJButton_recorderup("上へ"), BUTTON_WIDTH, PARTS_HEIGHT, table_w-BUTTON_WIDTH, yz);\r
+                       CommonSwingUtils.putComponentOn(jPanel_recorder, getJButton_recorderdown("下へ"), BUTTON_WIDTH, PARTS_HEIGHT, table_w-BUTTON_WIDTH, yz+=(PARTS_HEIGHT+SEP_HEIGHT_NALLOW));\r
+                       \r
+                       yz += (PARTS_HEIGHT+SEP_HEIGHT_NALLOW)*3;\r
+                       \r
+                       CommonSwingUtils.putComponentOn(jPanel_recorder, getJButton_recorderadd("登録"), BUTTON_WIDTH, PARTS_HEIGHT, table_w-BUTTON_WIDTH, yz);\r
+                       CommonSwingUtils.putComponentOn(jPanel_recorder, getJButton_recorderupd("置換"), BUTTON_WIDTH, PARTS_HEIGHT, table_w-BUTTON_WIDTH, yz+=(PARTS_HEIGHT+SEP_HEIGHT_NALLOW));\r
+                       CommonSwingUtils.putComponentOn(jPanel_recorder, getJButton_recorderdel("削除"), BUTTON_WIDTH, PARTS_HEIGHT, table_w-BUTTON_WIDTH, yz+=(PARTS_HEIGHT+SEP_HEIGHT_NALLOW));\r
+\r
+                       y+=(PARTS_HEIGHT+SEP_HEIGHT);\r
+                       CommonSwingUtils.putComponentOn(jPanel_recorder, getJLabel_recordertype("レコーダ機種"), LABEL_WIDTH, PARTS_HEIGHT, SEP_WIDTH, y);\r
+                       CommonSwingUtils.putComponentOn(jPanel_recorder, getJComboBox_recordertype(), TEXT_WIDTH, PARTS_HEIGHT, x, y);\r
+\r
+                       y+=(PARTS_HEIGHT+SEP_HEIGHT);\r
+                       CommonSwingUtils.putComponentOn(jPanel_recorder, getJLabel_recorder("レコーダIP/PORT"), LABEL_WIDTH, PARTS_HEIGHT, SEP_WIDTH, y);\r
+                       CommonSwingUtils.putComponentOn(jPanel_recorder, getJTextField_ipaddr(""), TEXT_WIDTH, PARTS_HEIGHT, x, y);\r
+                       CommonSwingUtils.putComponentOn(jPanel_recorder, getJTextField_port(""), TEXT_WIDTH, PARTS_HEIGHT, x+TEXT_WIDTH+SEP_WIDTH, y);\r
+                       \r
+                       y+=(PARTS_HEIGHT+SEP_HEIGHT);\r
+                       CommonSwingUtils.putComponentOn(jPanel_recorder, getJLabel_user("ID/PASS"), LABEL_WIDTH, PARTS_HEIGHT, SEP_WIDTH, y);\r
+                       CommonSwingUtils.putComponentOn(jPanel_recorder, getJTextField_user(""), TEXT_WIDTH, PARTS_HEIGHT, x, y);\r
+                       if ( ! CommonUtils.isLinux() ) {\r
+                               CommonSwingUtils.putComponentOn(jPanel_recorder, getJTextField_passwd(""), TEXT_WIDTH, PARTS_HEIGHT, x+TEXT_WIDTH+SEP_WIDTH, y);\r
+                       }\r
+                       else {\r
+                               if (debug) System.out.println(DBGID+"fixies JPasswordField problem");\r
+                               CommonSwingUtils.putComponentOn(jPanel_recorder, getJPanel_passwd(), TEXT_WIDTH, PARTS_HEIGHT, x+TEXT_WIDTH+SEP_WIDTH, y);\r
+                       }\r
+                       \r
+                       y+=(PARTS_HEIGHT+SEP_HEIGHT);\r
+                       CommonSwingUtils.putComponentOn(jPanel_recorder, getJLabel_wakeup("レコーダMAC/ブロードキャスト(任意)"), LABEL_WIDTH, PARTS_HEIGHT, SEP_WIDTH, y);\r
+                       CommonSwingUtils.putComponentOn(jPanel_recorder, getJTextField_macaddr(""), TEXT_WIDTH, PARTS_HEIGHT, x, y);\r
+                       CommonSwingUtils.putComponentOn(jPanel_recorder, getJTextField_broadcast(""), TEXT_WIDTH, PARTS_HEIGHT, x+TEXT_WIDTH+SEP_WIDTH, y);\r
+                       CommonSwingUtils.putComponentOn(jPanel_recorder, getJButton_getMac("Get MAC"), BUTTON_WIDTH, PARTS_HEIGHT, x+(TEXT_WIDTH+SEP_WIDTH)*2, y);\r
+                       \r
+                       y+=(PARTS_HEIGHT+SEP_HEIGHT);\r
+                       CommonSwingUtils.putComponentOn(jPanel_recorder, getJLabel_color("配色"), LABEL_WIDTH, PARTS_HEIGHT, SEP_WIDTH, y);\r
+                       \r
+                       int ny = 0;\r
+                       jLabel_colorcells = new JCCLabel[colorColumns.size()];\r
+                       for (int c=0; c<colorColumns.size(); c++) {\r
+                               jLabel_colorcells[c] = new JCCLabel(colorColumns.get(c).getName(), Color.RED, true, jPanel_recorder, ccwin);\r
+                               CommonSwingUtils.putComponentOn(jPanel_recorder, jLabel_colorcells[c], CCLABEL_WIDTH, PARTS_HEIGHT, x+(c%4)*(CCLABEL_WIDTH+SEP_WIDTH), (ny = y+((c-c%4)/4)*(PARTS_HEIGHT+SEP_HEIGHT)));\r
+                       }\r
+                       y = ny;\r
+                       \r
+                       y+=(PARTS_HEIGHT+SEP_HEIGHT);\r
+                       CommonSwingUtils.putComponentOn(jPanel_recorder, jCBP_calendar = new JCheckBoxPanel("カレンダー連携する",LABEL_WIDTH+SEP_WIDTH), LABEL_WIDTH+SEP_WIDTH+CCLABEL_WIDTH, PARTS_HEIGHT, SEP_WIDTH, y);\r
+\r
+                       y+=(PARTS_HEIGHT+SEP_HEIGHT);\r
+                       CommonSwingUtils.putComponentOn(jPanel_recorder, getJCBX_scope("録画に成功した記録をチェックする範囲(日)",LABEL_WIDTH+SEP_WIDTH,CCLABEL_WIDTH), LABEL_WIDTH+SEP_WIDTH+CCLABEL_WIDTH, PARTS_HEIGHT, SEP_WIDTH, y);\r
+                       \r
+                       y+=(PARTS_HEIGHT+SEP_HEIGHT);\r
+                       CommonSwingUtils.putComponentOn(jPanel_recorder, getJComboBox_TunerNum("最大同時録画数の手動設定",LABEL_WIDTH+SEP_WIDTH,CCLABEL_WIDTH), LABEL_WIDTH+SEP_WIDTH+CCLABEL_WIDTH, PARTS_HEIGHT, SEP_WIDTH, y);\r
+                       \r
+                       y+=(PARTS_HEIGHT+SEP_HEIGHT);\r
+                       CommonSwingUtils.putComponentOn(jPanel_recorder, jCBP_chchange = new JCheckBoxPanel("チャンネル操作を有効にする",LABEL_WIDTH+SEP_WIDTH), LABEL_WIDTH+SEP_WIDTH+CCLABEL_WIDTH, PARTS_HEIGHT, SEP_WIDTH, y);\r
+                       \r
+                       y+=(PARTS_HEIGHT+SEP_HEIGHT);\r
+                       CommonSwingUtils.putComponentOn(jPanel_recorder, new JLabel("オプション指定(形式:KEY=VAL;)"), LABEL_WIDTH, PARTS_HEIGHT, SEP_WIDTH, y);\r
+                       CommonSwingUtils.putComponentOn(jPanel_recorder, getJtf_opt(), TEXT_WIDTH*2+SEP_WIDTH, PARTS_HEIGHT, x, y);\r
+                       \r
+                       y+=(PARTS_HEIGHT+SEP_HEIGHT);\r
+                       \r
+                       // 画面の全体サイズを決める\r
+                       Dimension d = new Dimension(PANEL_WIDTH,y);\r
+                       jPanel_recorder.setPreferredSize(d);\r
+               }\r
+               \r
+               return jPanel_recorder;\r
+       }\r
+       \r
+       \r
+       // 更新確定ボタン\r
+       private JButton getJButton_update(String s) {\r
+               if (jButton_update == null) {\r
+                       jButton_update = new JButton(s);\r
+                       \r
+                       jButton_update.addActionListener(al_update);\r
+               }\r
+               return(jButton_update);\r
+       }\r
+\r
+       \r
+       /*\r
+        *      レコーダ関連 \r
+        */\r
+       \r
+       // レコーダからエンコーダ情報をもらったら一覧に反映する\r
+       public void redrawRecorderEncoderEntry() {\r
+               int row = 0;\r
+               for ( RecorderInfo ri : recInfoList ) {\r
+                       for (int n=0; n<colorColumns.size(); n++) {\r
+                               RecorderItem c = rowData.get(row);\r
+                               String[] d = CommonSwingUtils.splitColorString(c.getCx(n));\r
+                               String e = ri.getEncoder(n);\r
+                               c.setCx(n,CommonSwingUtils.getColoredString(d[1],e));\r
+                               c.fireChanged();\r
+                       }\r
+                       row++;\r
+               }\r
+               \r
+               ((DefaultTableModel) jTable_recorders.getModel()).fireTableDataChanged();\r
+       }\r
+\r
+       private JButton getJButton_recorderadd(String s) {\r
+               if (jButton_recorderadd == null) {\r
+                       jButton_recorderadd = new JButton(s);\r
+                       jButton_recorderadd.setForeground(Color.RED);\r
+                       jButton_recorderadd.addActionListener(al_addRecorder);\r
+               }\r
+               return(jButton_recorderadd);\r
+       }\r
+\r
+       private JButton getJButton_recorderupd(String s) {\r
+               if (jButton_recorderupd == null) {\r
+                       jButton_recorderupd = new JButton(s);\r
+                       jButton_recorderupd.setForeground(Color.RED);\r
+                       jButton_recorderupd.addActionListener(al_updRecorder);\r
+               }\r
+               return(jButton_recorderupd);\r
+       }\r
+       \r
+       private JButton getJButton_recorderdel(String s) {\r
+               if (jButton_recorderdel == null) {\r
+                       jButton_recorderdel = new JButton(s);\r
+                       jButton_recorderdel.setForeground(Color.BLUE);\r
+                       jButton_recorderdel.addActionListener(al_delRecorder);\r
+               }\r
+               return(jButton_recorderdel);\r
+       }\r
+       \r
+       private JButton getJButton_recorderup(String s) {\r
+               if (jButton_recorderup == null) {\r
+                       jButton_recorderup = new JButton(s);\r
+                       jButton_recorderup.addMouseListener(ml_upRecorder);\r
+               }\r
+               return(jButton_recorderup);\r
+       }\r
+       \r
+       private JButton getJButton_recorderdown(String s) {\r
+               if (jButton_recorderdown == null) {\r
+                       jButton_recorderdown = new JButton(s);\r
+                       jButton_recorderdown.addMouseListener(ml_downRecorder);\r
+               }\r
+               return(jButton_recorderdown);\r
+       }\r
+       \r
+       /*\r
+        * 機種 \r
+        */\r
+       \r
+       private JLabel getJLabel_recordertype(String s) {\r
+               if (jLabel_recordertype == null) {\r
+                       jLabel_recordertype = new JLabel();\r
+                       jLabel_recordertype.setText(s);\r
+               }\r
+               return(jLabel_recordertype);\r
+       }\r
+\r
+       private JComboBox getJComboBox_recordertype() {\r
+               if (jComboBox_recordertype == null) {\r
+                       jComboBox_recordertype = new JComboBox();\r
+                       \r
+                       DefaultComboBoxModel model = new DefaultComboBoxModel();\r
+                       jComboBox_recordertype.setModel(model);\r
+                       for ( HDDRecorder r : recPlugins ) {\r
+                               int idx = 0;\r
+                               for (int i=0; i<model.getSize(); i++) {\r
+                                       if (((String)model.getElementAt(i)).compareToIgnoreCase(r.getRecorderId()) < 0) {\r
+                                               // ID昇順でリストアップする\r
+                                               idx = i+1;\r
+                                       }\r
+                               }\r
+                               \r
+                               model.insertElementAt(r.getRecorderId(),idx);\r
+                       }\r
+                       \r
+                       jComboBox_recordertype.addItemListener(il_recorderSelected);\r
+               }\r
+               return(jComboBox_recordertype);\r
+       }\r
+       \r
+       /*\r
+        * IP・PORT \r
+        */\r
+       \r
+       private JLabel getJLabel_recorder(String s) {\r
+               if (jLabel_recorder == null) {\r
+                       jLabel_recorder = new JLabel();\r
+                       jLabel_recorder.setText(s);\r
+               }\r
+               return(jLabel_recorder);\r
+       }\r
+\r
+       private JTextField getJTextField_ipaddr(String s) {\r
+               if (jTextField_ipaddr == null) {\r
+                       jTextField_ipaddr = new JTextFieldWithPopup();\r
+                       jTextField_ipaddr.setText(s);\r
+               }\r
+               return jTextField_ipaddr;\r
+       }\r
+\r
+       private JTextField getJTextField_port(String s) {\r
+               if (jTextField_port == null) {\r
+                       jTextField_port = new JTextFieldWithPopup();\r
+                       jTextField_port.setText(s);\r
+               }\r
+               return jTextField_port;\r
+       }\r
+       \r
+       /*\r
+        * ユーザ \r
+        */\r
+       \r
+       private JLabel getJLabel_user(String s) {\r
+               if (jLabel_user == null) {\r
+                       jLabel_user = new JLabel();\r
+                       jLabel_user.setText(s);\r
+               }\r
+               return(jLabel_user);\r
+       }\r
+       \r
+       private JTextField getJTextField_user(String s) {\r
+               if (jTextField_user == null) {\r
+                       jTextField_user = new JTextFieldWithPopup();\r
+                       jTextField_user.setText(s);\r
+               }\r
+               return jTextField_user;\r
+       }\r
+       \r
+       private JPanel getJPanel_passwd() {\r
+               if (jPanel_passwd == null) {\r
+                       jPanel_passwd = new JPanel();\r
+                       jPanel_passwd.setLayout(new BorderLayout());\r
+                       jPanel_passwd.add(getJTextField_passwd(""), BorderLayout.CENTER);\r
+               }\r
+               return jPanel_passwd;\r
+       }\r
+       \r
+       private JPasswordField getJTextField_passwd(String s) {\r
+               if (jTextField_passwd == null) {\r
+                       jTextField_passwd = new JPasswordField(s);\r
+               }\r
+               return jTextField_passwd;\r
+       }\r
+       \r
+       /*\r
+        * MACアドレス \r
+        */\r
+       \r
+       private JLabel getJLabel_wakeup(String s) {\r
+               if (jLabel_wakeup == null) {\r
+                       jLabel_wakeup = new JLabel();\r
+                       jLabel_wakeup.setText(s);\r
+               }\r
+               return(jLabel_wakeup);\r
+       }\r
+       \r
+       private JTextField getJTextField_macaddr(String s) {\r
+               if (jTextField_macaddr == null) {\r
+                       jTextField_macaddr = new JTextFieldWithPopup();\r
+                       jTextField_macaddr.setText(s);\r
+               }\r
+               return jTextField_macaddr;\r
+       }\r
+       \r
+       private JTextField getJTextField_broadcast(String s) {\r
+               if (jTextField_broadcast == null) {\r
+                       jTextField_broadcast = new JTextFieldWithPopup();\r
+                       jTextField_broadcast.setText(s);\r
+               }\r
+               return jTextField_broadcast;\r
+       }\r
+\r
+       private JButton getJButton_getMac(String s) {\r
+               if (jButton_getmac == null) {\r
+                       jButton_getmac = new JButton(s);\r
+                       jButton_getmac.addMouseListener(ml_getmac);\r
+               }\r
+               return(jButton_getmac);\r
+       }\r
+\r
+       /*\r
+        * チューナ・エンコーダカラー \r
+        */\r
+       \r
+       private JLabel getJLabel_color(String s) {\r
+               if (jLabel_color == null) {\r
+                       jLabel_color = new JLabel();\r
+                       jLabel_color.setText(s);\r
+               }\r
+               return(jLabel_color);\r
+       }\r
+       \r
+       private JComboBoxPanel getJCBX_scope(String s, int labelWidth, int comboboxWidth) {\r
+               if ( jCBX_scope == null ) {\r
+                       jCBX_scope = new JComboBoxPanel(s, labelWidth, comboboxWidth, true);\r
+                       jCBX_scope.addItem(ITEM_SCOPEDISABLE);\r
+                       for ( int n=1; n<=HDDRecorder.SCOPEMAX; n++ ) {\r
+                               jCBX_scope.addItem(String.format("%d日 (%d週)",n,(n/7)+1));\r
+                       }\r
+               }\r
+               return jCBX_scope;\r
+       }\r
+       \r
+       private JComboBoxPanel getJComboBox_TunerNum(String s, int labelWidth, int comboboxWidth) {\r
+               if (jCBX_tunernum == null) {\r
+                       jCBX_tunernum = new JComboBoxPanel(s, labelWidth, comboboxWidth, true);\r
+                       for ( int n=1; n<=colorColumns.size(); n++ ) {\r
+                               jCBX_tunernum.addItem(String.valueOf(n));\r
+                       }\r
+               }\r
+               return jCBX_tunernum;\r
+       }\r
+\r
+       /**\r
+        * フリーオプション\r
+        */\r
+       private JTextFieldWithPopup getJtf_opt() {\r
+               if ( jtf_opt == null ) {\r
+                       jtf_opt = new JTextFieldWithPopup();\r
+               }\r
+               return jtf_opt;\r
+       }\r
+       \r
+       /*\r
+        * レコーダテーブル\r
+        */\r
+\r
+       private JScrollPane getJScrollPane_recorders() {\r
+               if (jScrollPane_recorders == null) {\r
+                       jScrollPane_recorders = new JScrollPane();\r
+                       jScrollPane_recorders.setViewportView(getJTable_recorders());\r
+                       jScrollPane_recorders.setVerticalScrollBarPolicy(ScrollPaneConstants.VERTICAL_SCROLLBAR_ALWAYS);\r
+               }\r
+               return(jScrollPane_recorders);\r
+       }\r
+       \r
+       private RecorderTable getJTable_recorders() {\r
+               if (jTable_recorders == null) {\r
+                       \r
+                       jTable_recorders = new RecorderTable(rowData);\r
+                                       \r
+                       DefaultTableModel model = new DefaultTableModel();\r
+                       for ( RecorderColumn rc : RecorderColumn.values() ) {\r
+                               if ( rc.getIniWidth() >= 0 ) {\r
+                                       model.addColumn(rc.getName());\r
+                               }\r
+                       }\r
+                       jTable_recorders.setModel(model);\r
+\r
+                       //\r
+                       jTable_recorders.setAutoResizeMode(JNETable.AUTO_RESIZE_OFF);\r
+                       jTable_recorders.setSelectionMode(ListSelectionModel.SINGLE_SELECTION);\r
+                       \r
+                       // 各カラムの幅を設定する\r
+                       DefaultTableColumnModel columnModel = (DefaultTableColumnModel)jTable_recorders.getColumnModel();\r
+                       TableColumn column = null;\r
+                       for ( RecorderColumn rc : RecorderColumn.values() ) {\r
+                               if ( rc.getIniWidth() < 0 ) {\r
+                                       continue;\r
+                               }\r
+                               column = columnModel.getColumn(rc.ordinal());\r
+                               column.setPreferredWidth(rc.getIniWidth());\r
+                       }\r
+\r
+                       //\r
+                       TableCellRenderer colorCellRenderer = new VWColorCellRenderer();\r
+                       \r
+                       for ( RecorderColumn rc : colorColumns ) {\r
+                               jTable_recorders.getColumn(rc.getName()).setCellRenderer(colorCellRenderer);\r
+                       }\r
+\r
+                       // 内容をコピーさせない\r
+                       jTable_recorders.addKeyListener(kl_cannnotcopy);\r
+                       \r
+                       // 行を選択したら入力に値を戻す\r
+                       jTable_recorders.addMouseListener(ml_tableSelected);\r
+               }\r
+               return jTable_recorders;\r
+       }\r
+\r
+       private JTextAreaWithPopup getJta_help() {\r
+               if ( jta_help == null ) {\r
+                       jta_help = CommonSwingUtils.getJta(this,2,0);\r
+                       jta_help.append(TEXT_HINT);\r
+               }\r
+               return jta_help;\r
+       }\r
+       \r
+       /*******************************************************************************\r
+        * 独自部品\r
+        ******************************************************************************/\r
+       /**\r
+        *  テーブルの行データの構造\r
+        * @see RecorderColumn\r
+        */\r
+       private class RecorderItem extends RowItem implements Cloneable {\r
+               String ipaddr;\r
+               String portno;\r
+               String macaddr;\r
+               String broadcast;\r
+               String recorderid;\r
+               String user;\r
+               String password;\r
+               Boolean calendar;\r
+               Integer scope;\r
+               Integer tunernum;\r
+               String c1;\r
+               String c2;\r
+               String c3;\r
+               String c4;\r
+               String c5;\r
+               String c6;\r
+               String c7;\r
+               String c8;\r
+               \r
+               Boolean hide_chchange;\r
+\r
+               @Override\r
+               protected void myrefresh(RowItem o) {\r
+                       RecorderItem c = (RecorderItem) o;\r
+                       c.addData(ipaddr);\r
+                       c.addData(portno);\r
+                       c.addData(macaddr);\r
+                       c.addData(broadcast);\r
+                       c.addData(recorderid);\r
+                       c.addData(user);\r
+                       c.addData(password);\r
+                       c.addData(calendar);\r
+                       c.addData(scope);\r
+                       c.addData(tunernum);\r
+                       c.addData(c1);\r
+                       c.addData(c2);\r
+                       c.addData(c3);\r
+                       c.addData(c4);\r
+                       c.addData(c5);\r
+                       c.addData(c6);\r
+                       c.addData(c7);\r
+                       c.addData(c8);\r
+               }\r
+               \r
+               public String getCx(int n) {\r
+                       switch (n) {\r
+                       case 0:\r
+                               return c1;\r
+                       case 1:\r
+                               return c2;\r
+                       case 2:\r
+                               return c3;\r
+                       case 3:\r
+                               return c4;\r
+                       case 4:\r
+                               return c5;\r
+                       case 5:\r
+                               return c6;\r
+                       case 6:\r
+                               return c7;\r
+                       case 7:\r
+                               return c8;\r
+                       }\r
+                       return null;\r
+               }\r
+               \r
+               public void setCx(int n, String c) {\r
+                       switch (n) {\r
+                       case 0:\r
+                               c1 = c;\r
+                               break;\r
+                       case 1:\r
+                               c2 = c;\r
+                               break;\r
+                       case 2:\r
+                               c3 = c;\r
+                               break;\r
+                       case 3:\r
+                               c4 = c;\r
+                               break;\r
+                       case 4:\r
+                               c5 = c;\r
+                               break;\r
+                       case 5:\r
+                               c6 = c;\r
+                               break;\r
+                       case 6:\r
+                               c7 = c;\r
+                               break;\r
+                       case 7:\r
+                               c8 = c;\r
+                               break;\r
+                       }\r
+               }\r
+               \r
+               public RecorderItem clone() {\r
+                       return (RecorderItem) super.clone();\r
+               }\r
+       }\r
+       \r
+       private class RecorderTable extends JTable {\r
+               \r
+               private static final long serialVersionUID = 1L;\r
+\r
+               private final Color evenColor = new Color(240,240,255);\r
+               private final Color oddColor = super.getBackground();\r
+\r
+               private RowItemList<RecorderItem> rowdata = null;\r
+\r
+               public RecorderTable(RowItemList<RecorderItem> rowdata) {\r
+                       \r
+                       super();\r
+                       \r
+                       this.rowdata = rowdata;\r
+                       \r
+                       // フォントサイズ変更にあわせて行の高さを変える\r
+                       this.addPropertyChangeListener("font", new RowHeightChangeListener(8));\r
+\r
+                       // 行の高さの初期値の設定\r
+                       this.firePropertyChange("font", "old", "new");\r
+               }\r
+               \r
+               @Override\r
+               public Object getValueAt(int row, int column) {\r
+                       \r
+                       int vrow = this.convertRowIndexToModel(row);\r
+                       RecorderItem c = rowdata.get(vrow);\r
+                       if ( column == RecorderColumn.PW.getColumn() ) {\r
+                               return "ミンナニハナイショダヨ";\r
+                       }\r
+                       else if ( column == RecorderColumn.CALENDAR.getColumn() ) {\r
+                               return ((c.calendar)?(VALUE_CALENDAR_ENABLED):(""));\r
+                       }\r
+                       return (c.get(column) instanceof String)?(c.get(column)):(String.valueOf(c.get(column)));\r
+                       \r
+               }\r
+               \r
+               @Override\r
+               public void setValueAt(Object aValue, int row, int column) {\r
+                       // 編集はないよ\r
+               }\r
+               \r
+               @Override\r
+               public int getRowCount() {\r
+                       \r
+                       return rowdata.size();\r
+                       \r
+               }\r
+               \r
+               // 編集禁止\r
+               @Override\r
+               public boolean isCellEditable(int row, int column) {\r
+                       return false;\r
+               }\r
+\r
+               @Override\r
+               public Component prepareRenderer(TableCellRenderer tcr, int row, int column) {\r
+                       \r
+                       Component comp = super.prepareRenderer(tcr, row, column);\r
+                       \r
+                       if ( column >= RecorderColumn.C1.getColumn() ) {\r
+                               return comp;\r
+                       }\r
+                       \r
+                       //int vrow = this.convertRowIndexToModel(row);\r
+                       //RecorderItem c = rowdata.get(vrow);\r
+                       \r
+                       Color fg = null;\r
+                       Color bg = null;\r
+                       \r
+                       boolean evenline = (row%2 == 1);\r
+                       \r
+                       if ( isRowSelected(row) )\r
+                       {\r
+                               fg = this.getSelectionForeground();\r
+                               bg = this.getSelectionBackground();\r
+                       }\r
+\r
+                       if (fg==null) fg = this.getForeground();\r
+                       if (bg==null) bg = (evenline)?(evenColor):(oddColor);\r
+                       \r
+                       comp.setForeground(fg);\r
+                       comp.setBackground(bg);\r
+                       \r
+                       return comp;\r
+               }\r
+\r
+       }\r
+\r
+}\r
diff --git a/TinyBannavi/src/tainavi/AbsReserveDialog.java b/TinyBannavi/src/tainavi/AbsReserveDialog.java
new file mode 100644 (file)
index 0000000..f7ed430
--- /dev/null
@@ -0,0 +1,3370 @@
+package tainavi;\r
+\r
+import java.awt.Color;\r
+import java.awt.Component;\r
+import java.awt.Dimension;\r
+import java.awt.event.ActionEvent;\r
+import java.awt.event.ActionListener;\r
+import java.awt.event.ItemEvent;\r
+import java.awt.event.ItemListener;\r
+import java.awt.event.MouseAdapter;\r
+import java.awt.event.MouseEvent;\r
+import java.awt.event.MouseListener;\r
+import java.awt.event.WindowAdapter;\r
+import java.awt.event.WindowEvent;\r
+import java.awt.event.WindowListener;\r
+import java.util.ArrayList;\r
+import java.util.Calendar;\r
+import java.util.GregorianCalendar;\r
+import java.util.HashMap;\r
+import java.util.regex.Matcher;\r
+import java.util.regex.Pattern;\r
+\r
+import javax.swing.JButton;\r
+import javax.swing.JComboBox;\r
+import javax.swing.JDialog;\r
+import javax.swing.JLabel;\r
+import javax.swing.JPanel;\r
+import javax.swing.JScrollPane;\r
+import javax.swing.JTable;\r
+import javax.swing.JTextArea;\r
+import javax.swing.JTextField;\r
+import javax.swing.SpringLayout;\r
+import javax.swing.SwingUtilities;\r
+import javax.swing.border.LineBorder;\r
+import javax.swing.table.DefaultTableCellRenderer;\r
+import javax.swing.table.DefaultTableColumnModel;\r
+import javax.swing.table.DefaultTableModel;\r
+import javax.swing.table.TableColumn;\r
+\r
+import tainavi.HDDRecorder.RecType;\r
+import tainavi.TVProgram.ProgGenre;\r
+import tainavi.TVProgram.ProgOption;\r
+import tainavi.TVProgram.ProgSubgenre;\r
+\r
+\r
+/**\r
+ * 予約ダイアログのクラス\r
+ * @since 3.15.4β ReserveDialogからクラス名変更\r
+ */\r
+abstract class AbsReserveDialog extends JDialog {\r
+\r
+       private static final long serialVersionUID = 1L;\r
+\r
+       public static String getViewName() { return "予約ダイアログ"; }\r
+       \r
+       public void setDebug(boolean b) { debug = b; }\r
+       private static boolean debug = false;\r
+       \r
+       /*******************************************************************************\r
+        * 抽象メソッド\r
+        ******************************************************************************/\r
+\r
+       protected abstract Env getEnv();\r
+       protected abstract TVProgramList getTVProgramList();\r
+       protected abstract HDDRecorderList getRecorderList();\r
+\r
+       protected abstract AVSetting getAVSetting();\r
+       protected abstract CHAVSetting getCHAVSetting();\r
+\r
+       protected abstract StatusWindow getStWin(); \r
+       protected abstract StatusTextArea getMWin();\r
+\r
+       protected abstract Component getParentComponent();\r
+       \r
+       protected abstract void ringBeep();\r
+\r
+       // クラス内のイベントから呼び出されるもの\r
+       protected abstract void searchLikeRsv(LikeReserveList likeRsvList, ProgDetailList tvd, String keyword, int threshold);\r
+       protected abstract String getSelectedRecorderOnToolbar();\r
+       \r
+       /*******************************************************************************\r
+        * 呼び出し元から引き継いだもの\r
+        ******************************************************************************/\r
+       \r
+       private final Env env = getEnv();\r
+       private final HDDRecorderList recorders = getRecorderList();\r
+       \r
+       private final AVSetting avs = getAVSetting();\r
+       private final CHAVSetting chavs = getCHAVSetting();\r
+       \r
+       private final StatusWindow StWin = getStWin();                  // これは起動時に作成されたまま変更されないオブジェクト\r
+       private final StatusTextArea MWin = getMWin();                  // これは起動時に作成されたまま変更されないオブジェクト\r
+       \r
+       private final Component parent = getParentComponent();  // これは起動時に作成されたまま変更されないオブジェクト\r
+       \r
+       private final GetEventId geteventid = new GetEventId(); // 番組IDの取得\r
+       \r
+       /*******************************************************************************\r
+        * 定数\r
+        ******************************************************************************/\r
+       \r
+       private enum ChangedSelector { ALL, RECORDER, GENRE, LIKELIST };\r
+       \r
+       private static final String ITEM_YES = "する";\r
+       private static final String ITEM_NO = "しない";\r
+       \r
+       private static final String ITEM_EVIDNEEDED = "番組ID取得";\r
+       \r
+       private static final int LIKERSVTABLE_NOTSELECTED = -1;\r
+       \r
+       // レイアウト関連\r
+       \r
+       private static final int TITLE_WIDTH = 370;\r
+       private static final int CHNAME_WIDTH = 240;\r
+       private static final int RECORDER_WIDTH = 300;\r
+       private static final int ENCODER_WIDTH = 150;\r
+       private static final int BOX_WIDTH = 730;\r
+       private static final int LIKELIST_WIDTH = BOX_WIDTH;\r
+       private static final int LIKELIST_ROWS = 4;\r
+       \r
+       private static final int PARTS_HEIGHT = 25;\r
+       private static final int SEP_WIDTH = 10;\r
+       private static final int SEP_WIDTH_NARROW = 5;\r
+       private static final int SEP_HEIGHT = 10;\r
+       private static final int SEP_HEIGHT_NALLOW = 5;\r
+\r
+       private static final int LABEL_WIDTH = 150;\r
+       private static final int COMBO_WIDTH = 115;\r
+       private static final int COMBO_WIDTH_WIDE = 155;\r
+       private static final int COMBO_HEIGHT = 50;\r
+\r
+       private static final int PANEL_WIDTH = 760;\r
+       \r
+       private static final int LRT_HEADER_WIDTH = 20;\r
+       private static final int LRT_TITLE_WIDTH = 325;\r
+       private static final int LRT_START_WIDTH = 115;\r
+       private static final int LRT_RECORDER_WIDTH = 185;\r
+       private static final int LRT_ENCODER_WIDTH = 60;\r
+       \r
+       public static enum LikeRsvColumn {\r
+               TITLE           ("予約名",   LRT_TITLE_WIDTH),\r
+               START           ("開始日時",        LRT_START_WIDTH),\r
+               RECORDER        ("レコーダ",        LRT_RECORDER_WIDTH),\r
+               TUNER           ("エンコーダ",  LRT_ENCODER_WIDTH),\r
+               ;\r
+\r
+               private String name;\r
+               private int iniWidth;\r
+\r
+               private LikeRsvColumn(String name, int iniWidth) {\r
+                       this.name = name;\r
+                       this.iniWidth = iniWidth;\r
+               }\r
+\r
+               public String getName() {\r
+                       return name;\r
+               }\r
+\r
+               public int getIniWidth() {\r
+                       return iniWidth;\r
+               }\r
+               \r
+               public int getColumn() {\r
+                       return ordinal();\r
+               }\r
+               \r
+               public boolean equals(String s) {\r
+                       return name.equals(s);\r
+               }\r
+       };\r
+       \r
+       //\r
+       \r
+       private static final String TEXT_SAVEDEFAULT = "<HTML>録画設定を開いた時の枠内のデフォルト値として<BR>現在の値を使用するようにします。<BR><FONT COLOR=#FF0000>※ジャンル別AV設定があればそちらが優先されます。</FONT></HTML>";\r
+       \r
+       // ログ関連\r
+       \r
+       private static final String MSGID = "["+getViewName()+"] ";\r
+       private static final String ERRID = "[ERROR]"+MSGID;\r
+       private static final String DBGID = "[DEBUG]"+MSGID;\r
+       \r
+       /*******************************************************************************\r
+        * 部品\r
+        ******************************************************************************/\r
+       \r
+       // コンポーネント\r
+       \r
+       private JPanel jContentPane_rsv = null;\r
+\r
+       private JLabel jLabel_title = null;\r
+       private JComboBox jComboBox_ch = null;\r
+       private JLabel jLabel_ch= null;\r
+       private JLabel jLabel_encoder = null;\r
+       private JComboBoxWithPopup jComboBox_title = null;\r
+       private JWideComboBox jComboBox_encoder = null;\r
+       private JButton jButton_getEventId = null;\r
+       private JLabel jLabel_encoderemptywarn = null;\r
+       private JLabel jLabel_date = null;\r
+       private JLabel jLabel_ahh = null;\r
+       private JLabel jLabel_zhh = null;\r
+       private JLabel jLabel_recorders = null;\r
+       private JComboBox jComboBox_recorder = null;\r
+       private JComboBox jComboBox_date = null;\r
+       private JTextField jTextField_ahh = null;\r
+       private JTextField jTextField_amm = null;\r
+       private JLabel jLabel_asep = null;\r
+       private JButton jButton_amm_up = null;\r
+       private JButton jButton_amm_down = null;\r
+       private JTextField jTextField_Xahh = null;\r
+       private JTextField jTextField_Xamm = null;\r
+       private JLabel jLabel_Xasep = null;\r
+       private JTextField jTextField_zhh = null;\r
+       private JTextField jTextField_zmm = null;\r
+       private JLabel jLabel_zsep = null;\r
+       private JButton jButton_zmm_up = null;\r
+       private JButton jButton_zmm_down = null;\r
+       private JTextField jTextField_Xzhh = null;\r
+       private JTextField jTextField_Xzmm = null;\r
+       private JLabel jLabel_Xzsep = null;\r
+       private JButton jButton_Xreset = null;\r
+       private JLabel jLabel_detail = null;\r
+       private JScrollPane jScrollPane_detail = null;\r
+       private JTextAreaWithPopup jTextArea_detail = null;\r
+       private JCheckBoxPanel jCheckBox_OverlapDown2 = null;\r
+       private JCheckBoxPanel jCheckBox_spoex_extend = null;\r
+       private JLabel jLabel_rectype = null;\r
+       private JButton jButton_load = null;\r
+       private JButton jButton_save = null;\r
+       private JButton jButton_savedefault = null;\r
+       private JComboBoxPanel jCBXPanel_genre = null;\r
+       private JComboBoxPanel jCBXPanel_subgenre = null;\r
+       private JComboBoxPanel jCBXPanel_videorate = null;\r
+       private JComboBoxPanel jCBXPanel_audiorate = null;\r
+       private JComboBoxPanel jCBXPanel_folder = null;\r
+       private JComboBoxPanel jCBXPanel_dvdcompat = null;\r
+       private JComboBoxPanel jCBXPanel_device = null;\r
+       private JComboBoxPanel jCBXPanel_aspect = null;\r
+       private JComboBoxPanel jCBXPanel_bvperf = null;\r
+       private JComboBoxPanel jCBXPanel_lvoice = null;\r
+       private JComboBoxPanel jCBXPanel_autodel = null;\r
+       private JComboBoxPanel jCBXPanel_pursues = null;\r
+       private JButton jButton_update = null;\r
+       private JButton jButton_record = null;\r
+       private JButton jButton_cancel = null;\r
+       private JCheckBoxPanel jCheckBox_Exec = null;\r
+       private JCheckBoxPanel jCheckBox_Autocomplete = null;\r
+       private JButton jButton_addDate = null;\r
+       \r
+       private LikeRsvRowHeader likersvrowheader = null;\r
+       private JScrollPane likersvpane = null;\r
+       private LikeRsvTable likersvtable = null;\r
+       \r
+       private JComboBoxPanel jCBXPanel_xChapter = null;\r
+       private JComboBoxPanel jCBXPanel_msChapter = null;\r
+       private JComboBoxPanel jCBXPanel_mvChapter = null;\r
+       \r
+       /*\r
+        * その他\r
+        */\r
+       \r
+       /**\r
+        * 初期化漏れが怖いのでまとめて内部クラスとした。\r
+        */\r
+       private class Vals {\r
+               \r
+               // 検索した類似予約を保持する\r
+               final LikeReserveList likeRsvList = new LikeReserveList();\r
+               LikeReserveItem selectedLikeRsv = null; \r
+               \r
+               // 類似予約抽出条件(タイトル)\r
+               String keyword = "";\r
+               // 類似予約抽出条件(あいまい度)\r
+               int threshold = 0;\r
+               \r
+               // オープン時の単日指定の値(状態リセット用)\r
+               String byDateIni = "";\r
+               // オープン時の週次予約の値(状態リセット用)\r
+               String byWeeklyIni = "";\r
+               \r
+               // 延長警告分のばすかどうか\r
+               boolean isExtended = false;\r
+               // おしりを1分削るかどうか\r
+               boolean isClipped = false;\r
+               // 実行のON/OFFのみの更新かどうか\r
+               boolean isUpdateOnlyExec = false;\r
+       \r
+               // 予約する番組情報\r
+               ProgDetailList hide_tvd = null;\r
+               // 番組IDはUI上には表示されない隠し項目\r
+               String hide_content_id = null;\r
+               // 未編集の番組開始日時\r
+               String hide_startdatetime = null;\r
+               \r
+               // 開いたときの選択レコーダ\r
+               HDDRecorder hide_default_recorder = null;\r
+               \r
+               // 本体予約一覧から開かれたかどうか\r
+               boolean hide_atreservedlist = false;\r
+               \r
+       }\r
+       \r
+       private Vals vals = null;\r
+       \r
+       /**\r
+        * 予約操作が成功したかどうかを返す。\r
+        */\r
+       public boolean isReserved() { return doneReserve; }\r
+\r
+       private boolean doneReserve = false;\r
+\r
+       /*******************************************************************************\r
+        * コンストラクタ\r
+        ******************************************************************************/\r
+       \r
+       public AbsReserveDialog(int x, int y) {\r
+               \r
+               super();\r
+               \r
+               this.setModal(true);\r
+               this.setContentPane(getJContentPane_rsv());\r
+               \r
+               // タイトルバーの高さも考慮する必要がある\r
+               Dimension d = getJContentPane_rsv().getPreferredSize();\r
+               this.pack();\r
+               this.setBounds(x, y, d.width, d.height+this.getInsets().top);\r
+               this.setResizable(false);\r
+               //\r
+               this.setTitle("録画設定");\r
+               \r
+               this.addWindowListener(wl_opened);\r
+       }\r
+       \r
+       /*******************************************************************************\r
+        * アクション\r
+        ******************************************************************************/\r
+\r
+       /**\r
+        * 新規登録\r
+        * @see #doUpdate()\r
+        */\r
+       public void doRecord() {\r
+               \r
+               if (debug) System.out.println(DBGID+"doRecord "+vals.toString());\r
+               \r
+               // 新規処理\r
+               final ReserveList r = new ReserveList();\r
+               //r.setNo(-1);                          // PostRdEntry()中で取得するのでここはダミー(旧)\r
+               r.setId(null);                          // PostRdEntry()中で取得するのでここはダミー(新)\r
+               r.setRec_pattern((String)jComboBox_date.getSelectedItem());\r
+               r.setRec_pattern_id(-1);        // PostRdEntry()中で取得するのでここはダミー\r
+               r.setRec_nextdate((String)jComboBox_date.getItemAt(0)); // PostRdEntry()中で取得するのでここはダミー(日付を入れるのはDIGA用)\r
+               r.setAhh(String.format("%02d", Integer.valueOf(getJTextField_ahh().getText())));\r
+               r.setAmm(String.format("%02d", Integer.valueOf(getJTextField_amm().getText())));\r
+               r.setZhh(String.format("%02d", Integer.valueOf(getJTextField_zhh().getText())));\r
+               r.setZmm(String.format("%02d", Integer.valueOf(getJTextField_zmm().getText())));\r
+               r.setRec_min("");                       // PostRdEntry()中で取得するのでここはダミー\r
+               r.setTuner((String)jComboBox_encoder.getSelectedItem());\r
+               r.setRec_mode((String)jCBXPanel_videorate.getSelectedItem());\r
+               r.setRec_audio((String)jCBXPanel_audiorate.getSelectedItem());\r
+               r.setRec_folder((String)jCBXPanel_folder.getSelectedItem());\r
+               r.setRec_genre((String)jCBXPanel_genre.getSelectedItem());\r
+               r.setRec_subgenre((String)jCBXPanel_subgenre.getSelectedItem());\r
+               r.setRec_dvdcompat((String)jCBXPanel_dvdcompat.getSelectedItem());\r
+               r.setRec_device((String)jCBXPanel_device.getSelectedItem());\r
+               // 自動チャプタ関連\r
+               r.setRec_xchapter((String)jCBXPanel_xChapter.getSelectedItem());\r
+               r.setRec_mschapter((String)jCBXPanel_msChapter.getSelectedItem());\r
+               r.setRec_mvchapter((String)jCBXPanel_mvChapter.getSelectedItem());\r
+               // その他\r
+               r.setRec_aspect((String)jCBXPanel_aspect.getSelectedItem());\r
+               r.setRec_bvperf((String)jCBXPanel_bvperf.getSelectedItem());\r
+               r.setRec_lvoice((String)jCBXPanel_lvoice.getSelectedItem());\r
+               r.setRec_autodel((String)jCBXPanel_autodel.getSelectedItem());\r
+               \r
+               //r.setPursues(jCheckBox_Pursues.isSelected());\r
+               r.setPursues(ITEM_YES.equals((String) jCBXPanel_pursues.getSelectedItem()));\r
+               \r
+               r.setTitle((String)jComboBox_title.getSelectedItem());\r
+               r.setTitlePop(TraceProgram.replacePop(r.getTitle()));\r
+               r.setDetail(jTextArea_detail.getText());\r
+               r.setChannel("");                       // PostRdEntry()中で取得するのでここはダミー\r
+               r.setCh_name((String)jComboBox_ch.getSelectedItem());\r
+               r.setStartDateTime("");         // PostRdEntry()中で取得するのでここはダミー\r
+               r.setEndDateTime("");           // PostRdEntry()中で取得するのでここはダミー\r
+               r.setExec(jCheckBox_Exec.isSelected());\r
+               r.setAutocomplete(jCheckBox_Autocomplete.isSelected());\r
+               \r
+               r.setContentId(vals.hide_content_id);\r
+               \r
+               // 予約実行\r
+               StWin.clear();\r
+               new SwingBackgroundWorker(false) {\r
+                       \r
+                       @Override\r
+                       protected Object doWorks() throws Exception {\r
+                               for ( HDDRecorder recorder : recorders ) {\r
+                                       if (recorder.isMyself((String)jComboBox_recorder.getSelectedItem()) == true) {\r
+                                               StWin.appendMessage(MSGID+"予約を登録します:"+r.getTitle());\r
+                                               //recorder.setProgressArea(StWin);\r
+                                               if (recorder.PostRdEntry(r)) {\r
+                                                       \r
+                                                       // 成功したよ\r
+                                                       MWin.appendMessage(MSGID+"正常に登録できました:"+r.getTitle()+"("+r.getCh_name()+")");\r
+                                                       doneReserve = true;\r
+                                                       \r
+                                                       // カレンダーに登録する\r
+                                                       if ( recorder.getUseCalendar()) {\r
+                                                               if ( jCheckBox_Exec.isSelected() ) {\r
+                                                                       for ( HDDRecorder calendar : recorders ) {\r
+                                                                               if (calendar.getType() == RecType.CALENDAR) {\r
+                                                                                       StWin.appendMessage(MSGID+"カレンダーに予約情報を登録します");\r
+                                                                                       //calendar.setProgressArea(StWin);\r
+                                                                                       if ( ! calendar.PostRdEntry(r)) {\r
+                                                                                               MWin.appendError(ERRID+"[カレンダー] "+calendar.getErrmsg());\r
+                                                                                               ringBeep();\r
+                                                                                       }\r
+                                                                               }\r
+                                                                       }\r
+                                                               }\r
+                                                       }\r
+                                               }\r
+                                               else {\r
+                                                       MWin.appendError(ERRID+"登録に失敗しました:"+r.getTitle()+"("+r.getCh_name()+")");\r
+                                               }\r
+                                               //\r
+                                               if ( ! recorder.getErrmsg().equals("")) {\r
+                                                       MWin.appendMessage(MSGID+"[追加情報] "+recorder.getErrmsg());\r
+                                                       ringBeep();\r
+                                               }\r
+                                               break;\r
+                                       }\r
+                               }\r
+                               return null;\r
+                       }\r
+                       \r
+                       @Override\r
+                       protected void doFinally() {\r
+                               //CommonUtils.milSleep(0);\r
+                               StWin.setVisible(false);\r
+                       }\r
+               }.execute();\r
+               \r
+               CommonSwingUtils.setLocationCenter(parent, (Component)StWin);\r
+               StWin.setVisible(true);\r
+               \r
+               //setVisible(false);\r
+               resetWhenWindowClosed();\r
+               dispose();\r
+       }\r
+       \r
+       /**\r
+        * 更新処理\r
+        * @see #doRecord()\r
+        */\r
+       public void doUpdate() {\r
+               if (vals.likeRsvList.size() <= 0) {\r
+                       return;\r
+               }\r
+               \r
+               // 更新処理\r
+               final ReserveList newRsv = vals.selectedLikeRsv.getRsv().clone();\r
+               //newRsv.setNo(-1);\r
+               //newRsv.setId(null);\r
+               newRsv.setRec_pattern((String)jComboBox_date.getSelectedItem());\r
+               newRsv.setRec_pattern_id(-1);           // UpdateRdEntry()中で取得するのでここはダミー\r
+               newRsv.setRec_nextdate("");             // UpdateRdEntry()中で取得するのでここはダミー\r
+               newRsv.setAhh(String.format("%02d", Integer.valueOf(getJTextField_ahh().getText())));\r
+               newRsv.setAmm(String.format("%02d", Integer.valueOf(getJTextField_amm().getText())));\r
+               newRsv.setZhh(String.format("%02d", Integer.valueOf(getJTextField_zhh().getText())));\r
+               newRsv.setZmm(String.format("%02d", Integer.valueOf(getJTextField_zmm().getText())));\r
+               newRsv.setRec_min("");                  // UpdateRdEntry()中で取得するのでここはダミー\r
+               newRsv.setTuner((String)jComboBox_encoder.getSelectedItem());\r
+               newRsv.setRec_mode((String)jCBXPanel_videorate.getSelectedItem());;\r
+               newRsv.setRec_audio((String)jCBXPanel_audiorate.getSelectedItem());\r
+               newRsv.setRec_folder((String)jCBXPanel_folder.getSelectedItem());\r
+               newRsv.setRec_genre((String)jCBXPanel_genre.getSelectedItem());\r
+               newRsv.setRec_subgenre((String)jCBXPanel_subgenre.getSelectedItem());\r
+               newRsv.setRec_dvdcompat((String)jCBXPanel_dvdcompat.getSelectedItem());\r
+               newRsv.setRec_device((String)jCBXPanel_device.getSelectedItem());\r
+               // 自動チャプタ関連\r
+               newRsv.setRec_xchapter((String)jCBXPanel_xChapter.getSelectedItem());\r
+               newRsv.setRec_mschapter((String)jCBXPanel_msChapter.getSelectedItem());\r
+               newRsv.setRec_mvchapter((String)jCBXPanel_mvChapter.getSelectedItem());\r
+               // その他\r
+               newRsv.setRec_aspect((String)jCBXPanel_aspect.getSelectedItem());\r
+               newRsv.setRec_bvperf((String)jCBXPanel_bvperf.getSelectedItem());\r
+               newRsv.setRec_lvoice((String)jCBXPanel_lvoice.getSelectedItem());\r
+               newRsv.setRec_autodel((String)jCBXPanel_autodel.getSelectedItem());\r
+               \r
+               //newRsv.setPursues(jCheckBox_Pursues.isSelected());\r
+               newRsv.setPursues(ITEM_YES.equals((String) jCBXPanel_pursues.getSelectedItem()));\r
+               \r
+               newRsv.setTitle((String)jComboBox_title.getSelectedItem());\r
+               newRsv.setTitlePop(TraceProgram.replacePop(newRsv.getTitle()));\r
+               newRsv.setDetail(jTextArea_detail.getText());\r
+               newRsv.setChannel("");                  // UpdateRdEntry()中で取得するのでここはダミー\r
+               newRsv.setCh_name((String)jComboBox_ch.getSelectedItem());\r
+               newRsv.setStartDateTime("");            // UpdateRdEntry()中で取得するのでここはダミー\r
+               newRsv.setEndDateTime("");              // UpdateRdEntry()中で取得するのでここはダミー\r
+               newRsv.setExec(jCheckBox_Exec.isSelected());\r
+               newRsv.setAutocomplete(jCheckBox_Autocomplete.isSelected());\r
+               newRsv.setUpdateOnlyExec(vals.isUpdateOnlyExec);\r
+               \r
+               newRsv.setContentId(vals.hide_content_id);\r
+\r
+               // 更新実行\r
+               StWin.clear();\r
+               new SwingBackgroundWorker(false) {\r
+                       \r
+                       @Override\r
+                       protected Object doWorks() throws Exception {\r
+                               StWin.appendMessage(MSGID+"予約を更新します:"+newRsv.getTitle());\r
+                               //likeRsvRecorder.setProgressArea(StWin);\r
+                               if (vals.selectedLikeRsv.getRec().UpdateRdEntry(vals.selectedLikeRsv.getRsv(), newRsv)) {\r
+                                       \r
+                                       // 成功したよ\r
+                                       MWin.appendMessage(MSGID+"正常に更新できました:"+vals.selectedLikeRsv.getRsv().getTitle()+"("+vals.selectedLikeRsv.getRsv().getCh_name()+")");\r
+                                       doneReserve = true;\r
+                                       \r
+                                       // カレンダーを更新する\r
+                                       if ( vals.selectedLikeRsv.getRec().getUseCalendar() ) {\r
+                                               for ( HDDRecorder calendar : recorders ) {\r
+                                                       if (calendar.getType() == RecType.CALENDAR) {\r
+                                                               StWin.appendMessage(MSGID+"カレンダーの予約情報を更新します");\r
+                                                               //calendar.setProgressArea(StWin);\r
+                                                               if ( ! calendar.UpdateRdEntry(vals.selectedLikeRsv.getRsv(), (jCheckBox_Exec.isSelected())?(newRsv):(null))) {\r
+                                                                       MWin.appendError(ERRID+"[カレンダー] "+calendar.getErrmsg());\r
+                                                                       ringBeep();\r
+                                                               }\r
+                                                       }\r
+                                               }\r
+                                       }\r
+                               }\r
+                               else {\r
+                                       MWin.appendError(ERRID+"更新に失敗しました:"+vals.selectedLikeRsv.getRsv().getTitle()+"("+vals.selectedLikeRsv.getRsv().getCh_name()+")");\r
+                               }\r
+                               //\r
+                               if ( ! vals.selectedLikeRsv.getRec().getErrmsg().equals("")) {\r
+                                       MWin.appendMessage(MSGID+"[追加情報] "+vals.selectedLikeRsv.getRec().getErrmsg());\r
+                                       ringBeep();\r
+                               }\r
+                               return null;\r
+                       }\r
+                       \r
+                       @Override\r
+                       protected void doFinally() {\r
+                               StWin.setVisible(false);\r
+                       }\r
+               }.execute();\r
+\r
+               StWin.setVisible(true);\r
+\r
+               vals.selectedLikeRsv = null;\r
+               \r
+               //setVisible(false);\r
+               resetWhenWindowClosed();\r
+               dispose();\r
+       }\r
+       \r
+       /*******************************************************************************\r
+        * ダイアログオープン\r
+        ******************************************************************************/\r
+\r
+       /**\r
+        * 実行のON/OFFだけしか操作しない場合に呼び出す(画面にウィンドウは表示しない)\r
+        * ※これがあるので、各openでは vals != null チェックの必要がある\r
+        */\r
+       public void setOnlyUpdateExec(boolean b) {\r
+               if (vals == null) vals = new Vals();\r
+               vals.isUpdateOnlyExec = true;\r
+               jCheckBox_Exec.setSelected(b);\r
+       }\r
+       \r
+       /**\r
+        *  類似予約抽出条件ありオープン\r
+        */\r
+       public boolean open(ProgDetailList tvd, String keywordVal, int thresholdVal) {\r
+               if (thresholdVal > 0) {\r
+                       if (vals == null) vals = new Vals();\r
+                       vals.keyword = keywordVal;\r
+                       vals.threshold = thresholdVal;\r
+               }\r
+               return open(tvd);\r
+       }\r
+       \r
+       /**\r
+        *  類似予約抽出条件なしオープン\r
+        */\r
+       public boolean open(ProgDetailList tvd) {\r
+               \r
+               if (recorders.size() == 0) {\r
+                       return false;   // レコーダがひとつもないのはやばい\r
+               }\r
+               if (tvd.start.equals("")) {\r
+                       return false;   // これは「番組情報がありません」だろう\r
+               }\r
+               \r
+               // 隠しパラメータ\r
+               if (vals == null) vals = new Vals();\r
+               vals.hide_tvd = tvd;\r
+               vals.hide_content_id = tvd.progid;\r
+               vals.hide_startdatetime = tvd.startDateTime;\r
+               vals.hide_atreservedlist = false;\r
+               \r
+               // 番組ID取得ボタン\r
+               getEventIdOnOpen(tvd);\r
+               setGetEventIdButton(vals.hide_content_id,true);\r
+\r
+               return _open(null, null);\r
+       }\r
+       \r
+       /**\r
+        *  本体予約一覧からのオープン、または予約ON/OFFメニュー\r
+        */\r
+       public boolean open(String myself, String rsvId) {\r
+               \r
+               HDDRecorderList recs = recorders.getMyself(myself);\r
+               if ( recs.size() == 0 ) {\r
+                       return false;   // ここに来たらバグ\r
+               }\r
+               HDDRecorder myrec = recs.get(0);\r
+\r
+               ReserveList myrsv = myrec.getReserveList(rsvId);\r
+               if ( myrsv == null ) {\r
+                       return false;   // ここに来たらバグ\r
+               }\r
+\r
+               // 番組ID取得ボタンを無効にする\r
+               setGetEventIdButton(null,false);\r
+\r
+               ProgDetailList tvd = new ProgDetailList();\r
+               CommonUtils.getNextDate(myrsv);\r
+               tvd.center = myrsv.getCh_name();\r
+               tvd.title = myrsv.getTitle();\r
+               tvd.detail = myrsv.getDetail();\r
+               tvd.start = myrsv.getAhh()+":"+myrsv.getAmm();\r
+               tvd.end = myrsv.getZhh()+":"+myrsv.getZmm();\r
+               //tvd.accurateDate = myrsv.getRec_pattern();            // これは特殊\r
+               tvd.progid = myrsv.getContentId();\r
+               tvd.genre = ProgGenre.get(myrsv.getRec_genre());\r
+               tvd.subgenre = ProgSubgenre.get(tvd.genre,myrsv.getRec_subgenre());\r
+               \r
+               // 隠しパラメータ\r
+               if (vals == null) vals = new Vals();\r
+               vals.hide_tvd = tvd;\r
+               vals.hide_content_id = null;\r
+               vals.hide_startdatetime = null; // 予約一覧からは番組IDの取得はできないので開始日時は保存しない\r
+               vals.hide_atreservedlist = true;\r
+\r
+               return _open(myrec, myrsv);\r
+       }\r
+       \r
+       /**\r
+        * ダイアログオープン(共通処理)\r
+        */\r
+       private boolean _open(HDDRecorder rsvdrec, ReserveList rsvdrsv) {\r
+               \r
+               // 予約は行われてないよー\r
+               doneReserve = false;\r
+               \r
+               String myself = null;\r
+               HDDRecorder myrec = null;\r
+               ReserveList myrsv = null;\r
+               AVs myavs = null;\r
+               if ( rsvdrec == null ) {\r
+                       // レコーダの初期値の確認\r
+                       myself = getSelectedRecorderOnToolbar();                // ツールバーで選択されているのはどれかな?\r
+                       if ( myself != null && myself.length() > 0 ) {\r
+                               myrec = recorders.getMyself(myself).get(0);             // "すべて"と"ピックアップ"以外\r
+                       }\r
+                       else {\r
+                               myrec = recorders.get(0);                                               // "すべて"と"ピックアップ"なら先頭を選んでおけばいい\r
+                       }\r
+                       \r
+                       // リセット用データ収集(1)\r
+                       {\r
+                               vals.hide_default_recorder = myrec;\r
+                       }\r
+                       \r
+                       // 類似予約の確認\r
+                       searchLikeRsv(vals.likeRsvList, vals.hide_tvd, vals.keyword, vals.threshold);\r
+                       \r
+                       if ( env.getGivePriorityToReserved() && vals.likeRsvList.size() > 0 ) {\r
+                               // 類似予約が有効かつ存在しているならば\r
+                               for ( int i=0; i<vals.likeRsvList.size(); i++ ) {\r
+                                       HDDRecorder rec = vals.likeRsvList.getRec(i);\r
+                                       if ( rec.isMyself(myself) ) {\r
+                                               myrec = rec;    // 類似予約の中にコンボボックスと一致するものがあったわ\r
+                                               myself = myrec.Myself();\r
+                                               myrsv = vals.likeRsvList.getRsv(i);\r
+                                               vals.selectedLikeRsv = new LikeReserveItem(myrec, myrsv);\r
+                                               break;\r
+                                       }\r
+                               }\r
+                               if ( myrsv == null && myself != null && myself.length() != 0 ) {\r
+                                       // 類似予約があってもコンボボックスで選択したレコーダのものがない場合は無視\r
+                                       if (debug) System.out.println(DBGID+"類似予約に選択中のレコーダのものはなかった: "+myself);\r
+                               }\r
+                               else {\r
+                                       if ( myrsv == null ) {\r
+                                               // "すべて"と"ピックアップのみ"なら選択できるものはないね\r
+                                               myrec = vals.likeRsvList.getRec(0);\r
+                                               myself = myrec.Myself();\r
+                                               myrsv = vals.likeRsvList.getRsv(0);\r
+                                               vals.selectedLikeRsv = new LikeReserveItem(myrec, myrsv);\r
+                                               if (debug) System.out.println(DBGID+"選択中のレコーダがないので先頭の類似予約を使う: "+myself);\r
+                                       }\r
+                               }\r
+                       }\r
+                       \r
+                       // ジャンル別AV設定の確認\r
+                       if ( myrsv == null || ! env.getGivePriorityToReserved() ) {\r
+                               myavs = getSelectedAVs(vals.hide_tvd.genre, vals.hide_tvd.center, myrec.getRecorderId());\r
+                       }\r
+                       else {\r
+                               MWin.appendMessage(MSGID+"画質・音質は類似予約の設定が継承されます");\r
+                       }\r
+               }\r
+               else {\r
+                       myrec = rsvdrec;\r
+                       myrsv = rsvdrsv;\r
+                       myself = myrec.Myself();\r
+                       vals.selectedLikeRsv = new LikeReserveItem(myrec,myrsv);\r
+                       vals.likeRsvList.add(vals.selectedLikeRsv);\r
+                       \r
+                       // リセット用データ収集(1)\r
+                       {\r
+                               vals.hide_default_recorder = myrec;\r
+                       }\r
+               }\r
+               \r
+               if ( vals.hide_content_id == null || ! ContentIdEDCB.isValid(vals.hide_content_id) ) {\r
+                       if ( myrsv != null && ContentIdEDCB.isValid(myrsv.getContentId()) ) {\r
+                               vals.hide_content_id = myrsv.getContentId();\r
+                               setGetEventIdButton(vals.hide_content_id,false);\r
+                       }\r
+               }\r
+\r
+               // 一旦選択系リスナーは全部止めてしまう\r
+               setEnabledSelectionListeners(false);\r
+               \r
+               // 初期値を設定(固定部分)\r
+               setInitFixies(myrec, myrsv);\r
+\r
+               // 初期値を設定(可変部分)\r
+               setInitVariables(myrec);\r
+\r
+               // 初期値を選択\r
+               setSelectedVariables(myrec, myrsv, myavs, null, null);\r
+               \r
+               // 項目ラベルのオーバーライド\r
+               setLabels(myrec);\r
+\r
+               // リスナーを全部戻す\r
+               setEnabledSelectionListeners(true);\r
+               \r
+               // リセット用データ収集\r
+               {\r
+                       vals.isExtended = jCheckBox_spoex_extend.isSelected();\r
+                       vals.isClipped = jCheckBox_OverlapDown2.isSelected();\r
+                       vals.byDateIni = (String) jComboBox_date.getItemAt(0);\r
+                       vals.byWeeklyIni = (String) jComboBox_date.getItemAt(1);\r
+                       jTextField_Xahh.setText(jTextField_ahh.getText());\r
+                       jTextField_Xamm.setText(jTextField_amm.getText());\r
+                       jTextField_Xzhh.setText(jTextField_zhh.getText());\r
+                       jTextField_Xzmm.setText(jTextField_zmm.getText());\r
+               }\r
+               \r
+               return(true);\r
+       }\r
+       \r
+       /**\r
+        * open()時に設定したら内容の変更のないコンボボックスの選択肢の設定\r
+        * @see #setInitVariables\r
+        */\r
+       private void setInitFixies(HDDRecorder myrec, ReserveList myrsv) {\r
+               // レコーダコンボボックスの設定\r
+               {\r
+                       jComboBox_recorder.removeAllItems();\r
+                       for ( HDDRecorder rec : recorders ) {\r
+                               if ( rec.isBackgroundOnly() ) {\r
+                                       continue;       // Googleカレンダープラグインとかははずす\r
+                               }\r
+                               jComboBox_recorder.addItem(rec.Myself());\r
+                       }\r
+                       jComboBox_recorder.setEnabled( jComboBox_recorder.getItemCount() > 0 );\r
+                       \r
+                       if ( vals.hide_atreservedlist ) {\r
+                               jComboBox_recorder.setEnabled(false);\r
+                       }\r
+                       else {\r
+                               jComboBox_recorder.setEnabled(true);\r
+                       }\r
+               }\r
+               \r
+               // 予約名\r
+               {\r
+                       jComboBox_title.removeAllItems();\r
+                       jComboBox_title.addItem(vals.hide_tvd.title);\r
+                       for ( LikeReserveItem ll : vals.likeRsvList ) {\r
+                               for ( int i=0; i<jComboBox_title.getItemCount(); i++ ) {\r
+                                       String t = (String) jComboBox_title.getItemAt(i);\r
+                                       if ( t != null && ! t.equals(ll.getRsv().getTitle()) ) {\r
+                                               jComboBox_title.addItem(ll.getRsv().getTitle());\r
+                                               break;\r
+                                       }\r
+                               }\r
+                       }\r
+                       \r
+                       // 予約名の初期選択\r
+                       if ( env.getGivePriorityToReserved() && env.getGivePriorityToReservedTitle() && myrsv != null ) {\r
+                               jComboBox_title.setSelectedItem(myrsv.getTitle());\r
+                       }\r
+                       ((JTextField)jComboBox_title.getEditor().getEditorComponent()).setCaretPosition(0);\r
+               }\r
+               \r
+               // 詳細\r
+               {\r
+                       jTextArea_detail.setText(vals.hide_tvd.detail+"\n"+vals.hide_tvd.getAddedDetail());\r
+                       jTextArea_detail.setCaretPosition(0);\r
+               }\r
+\r
+               // ジャンル\r
+               {\r
+                       jCBXPanel_genre.removeAllItems();\r
+                       jCBXPanel_subgenre.removeAllItems();\r
+                       \r
+                       for ( ProgGenre g : ProgGenre.values() ) {\r
+                               jCBXPanel_genre.addItem(g.toString());\r
+                       }\r
+                       jCBXPanel_genre.setEnabled( jCBXPanel_genre.getItemCount() > 0 );\r
+                       \r
+                       if ( vals.hide_tvd.subgenre == null ) {\r
+                               jCBXPanel_subgenre.setEnabled(false);\r
+                       }\r
+                       else {\r
+                               for ( ProgSubgenre sg : ProgSubgenre.values(vals.hide_tvd.genre)) {\r
+                                       jCBXPanel_subgenre.addItem(sg.toString());\r
+                               }\r
+                               jCBXPanel_subgenre.setEnabled( jCBXPanel_subgenre.getItemCount() > 0 );\r
+                       }\r
+                       \r
+                       // ジャンルの初期選択\r
+                       if ( vals.hide_tvd.genre != null ) {\r
+                               jCBXPanel_genre.setSelectedItem(vals.hide_tvd.genre.toString());\r
+                               if ( vals.hide_tvd.subgenre != null ) {\r
+                                       jCBXPanel_subgenre.setSelectedItem(vals.hide_tvd.subgenre.toString());\r
+                               }\r
+                       }\r
+               }\r
+               \r
+               boolean OverlapException = false;       // 日付コンボボックスでも使うよ\r
+               \r
+               // 開始・終了時刻は長いよ\r
+               {\r
+                       int ahh = Integer.valueOf(vals.hide_tvd.start.substring(0,vals.hide_tvd.start.indexOf(":")));\r
+                       int amm = Integer.valueOf(vals.hide_tvd.start.substring(vals.hide_tvd.start.indexOf(":")+1,vals.hide_tvd.start.length()));\r
+                       int zhh = Integer.valueOf(vals.hide_tvd.end.substring(0,vals.hide_tvd.end.indexOf(":")));\r
+                       int zmm = Integer.valueOf(vals.hide_tvd.end.substring(vals.hide_tvd.end.indexOf(":")+1,vals.hide_tvd.end.length()));\r
+                       \r
+                       if ( ! vals.hide_atreservedlist ) {\r
+                               // のりしろ処理\r
+                               if (env.getOverlapUp() == true) {\r
+                                       int a = ahh*60+amm-1;\r
+                                       if (a<0) {\r
+                                               a+=24*60;\r
+                                               OverlapException = true;\r
+                                       }\r
+                                       amm = a % 60;\r
+                                       ahh = (a-amm)/60;\r
+                               }\r
+                               if (env.getOverlapDown() == true) {\r
+                                       int z = zhh*60+zmm+1;\r
+                                       zmm = z % 60;\r
+                                       zhh = (z-zmm)/60%24;\r
+                                       jCheckBox_OverlapDown2.setSelected(false);\r
+                                       jCheckBox_OverlapDown2.setForeground(Color.BLACK);\r
+                               }\r
+                               else if (\r
+                                               env.getOverlapDown2() == true &&\r
+                                               ! vals.hide_tvd.dontoverlapdown &&                      // NHKは縮めない\r
+                                               ! (env.getNoOverlapDown2Sp() && vals.hide_tvd.option.contains(ProgOption.SPECIAL))      // OVAとかは縮めない \r
+                                               ) {\r
+                                       jCheckBox_OverlapDown2.setSelected(true);\r
+                                       jCheckBox_OverlapDown2.setForeground(Color.RED);\r
+                                       int z = zhh*60+zmm-1;\r
+                                       if (z<0) {\r
+                                               z+=24*60;\r
+                                       }\r
+                                       zmm = z % 60;\r
+                                       zhh = (z-zmm)/60%24;\r
+                               }\r
+                               else {\r
+                                       jCheckBox_OverlapDown2.setSelected(false);\r
+                                       jCheckBox_OverlapDown2.setForeground(Color.BLACK);\r
+                               }\r
+                               \r
+                               // 延長警告処理\r
+                               int spoexLength = Integer.valueOf(env.getSpoexLength());\r
+                               if (vals.hide_tvd.extension == true && spoexLength>0) {\r
+                                       jCheckBox_spoex_extend.setSelected(true);\r
+                                       jCheckBox_spoex_extend.setForeground(Color.RED);\r
+                                       int z = zhh*60+zmm+spoexLength;\r
+                                       zmm = z % 60;\r
+                                       zhh = (z-zmm)/60%24;\r
+                               }\r
+                               else {\r
+                                       jCheckBox_spoex_extend.setSelected(false);\r
+                                       jCheckBox_spoex_extend.setForeground(Color.BLACK);\r
+                               }\r
+                       }\r
+                       else {\r
+                               jCheckBox_spoex_extend.setEnabled(false);\r
+                               jCheckBox_spoex_extend.setSelected(false);\r
+                               jCheckBox_spoex_extend.setForeground(Color.BLACK);\r
+                               jCheckBox_OverlapDown2.setEnabled(false);\r
+                               jCheckBox_OverlapDown2.setSelected(false);\r
+                               jCheckBox_OverlapDown2.setForeground(Color.BLACK);\r
+                       }\r
+                       \r
+                       jTextField_ahh.setText(String.format("%02d", ahh));\r
+                       jTextField_amm.setText(String.format("%02d", amm));\r
+                       jTextField_zhh.setText(String.format("%02d", zhh));\r
+                       jTextField_zmm.setText(String.format("%02d", zmm));\r
+               }\r
+\r
+               // 録画日付(のりしろ処理が必要な場合あり)\r
+               {\r
+                       jComboBox_date.removeAllItems();\r
+                       if ( ! vals.hide_atreservedlist ) {\r
+                               // リスト/新聞形式からの呼び出しなら番組情報をもとに\r
+                               GregorianCalendar c = CommonUtils.getCalendar(vals.hide_tvd.accurateDate);\r
+                               if ( c != null ) {\r
+                                       if (OverlapException == true) {\r
+                                               c.add(Calendar.DATE, -1);\r
+                                       }\r
+                                       jComboBox_date.addItem(CommonUtils.getDate(c));\r
+                                       jComboBox_date.addItem(HDDRecorder.RPTPTN[c.get(Calendar.DAY_OF_WEEK)-Calendar.SUNDAY]);\r
+                               }\r
+                       }\r
+                       else {\r
+                               // 本体予約一覧からの呼び出しなら既存の予約情報からの引継ぎ\r
+                               GregorianCalendar c = CommonUtils.getCalendar(myrsv.getRec_pattern());\r
+                               if ( c != null ) {\r
+                                       jComboBox_date.addItem(myrsv.getRec_pattern());\r
+                                       jComboBox_date.addItem(HDDRecorder.RPTPTN[c.get(Calendar.DAY_OF_WEEK)-Calendar.SUNDAY]);\r
+                               }\r
+                               else {\r
+                                       jComboBox_date.addItem(myrsv.getRec_nextdate());\r
+                                       jComboBox_date.addItem(myrsv.getRec_pattern());\r
+                               }\r
+                       }\r
+                       jComboBox_date.addItem(HDDRecorder.RPTPTN[7]);\r
+                       jComboBox_date.addItem(HDDRecorder.RPTPTN[8]);\r
+                       jComboBox_date.addItem(HDDRecorder.RPTPTN[9]);\r
+                       jComboBox_date.addItem(HDDRecorder.RPTPTN[10]);\r
+               }\r
+               \r
+               // 類似予約コンボボックスと抽出条件、その関連\r
+               {\r
+                       //vals.likeRsvList.clear();\r
+                       int selectedrow = LIKERSVTABLE_NOTSELECTED;\r
+                       if ( ! vals.hide_atreservedlist ) {\r
+                               jButton_record.setForeground(Color.RED);\r
+                               jButton_record.setEnabled(true);\r
+                               if ( vals.likeRsvList.size() > 0 ) {\r
+                                       //jButton_update.setForeground(Color.RED);\r
+                                       //jButton_update.setEnabled(true);\r
+                                       likersvtable.setEnabled(true);\r
+                                       \r
+                                       // 類似予約中の一番近い予約を探す\r
+                                       long score = 86400;\r
+                                       if ( env.getGivePriorityToReserved() ) {\r
+                                               for ( int i=0; i<vals.likeRsvList.size(); i++) {\r
+                                                       if ( vals.likeRsvList.getRec(i).isMyself(myrec.Myself()) ) {\r
+                                                               long sc = CommonUtils.getDiffDateTime(vals.likeRsvList.getRsv(i).getStartDateTime(), vals.hide_tvd.startDateTime);\r
+                                                               if ( selectedrow == -1 || score > sc ) {\r
+                                                                       selectedrow = i;\r
+                                                                       score = sc;\r
+                                                               }\r
+                                                       }\r
+                                               }\r
+                                       }\r
+\r
+                               }\r
+                               else {\r
+                                       likersvtable.setEnabled(false);\r
+                               }\r
+                       }\r
+                       else {\r
+                               // 本体予約一覧の場合は1個だけ追加すればよい\r
+                               jButton_record.setForeground(Color.GRAY);\r
+                               jButton_record.setEnabled(false);\r
+                               likersvtable.setEnabled(false);\r
+                               selectedrow = 0;\r
+                               //vals.likeRsvList.add(new LikeReserveItem(myrec,myrsv));\r
+                       }\r
+\r
+                       ((DefaultTableModel)likersvtable.getModel()).fireTableDataChanged();\r
+                       ((DefaultTableModel)likersvrowheader.getModel()).fireTableDataChanged();\r
+                       \r
+                       likersvtable.setRowSelectionInterval(selectedrow,selectedrow);\r
+\r
+                       setEnabledUpdateButton(likersvtable.getSelectedRow());\r
+               }\r
+       }\r
+       \r
+       /**\r
+        * 更新ボタンの有効無効\r
+        */\r
+       private void setEnabledUpdateButton(int selectedrow) {\r
+               if ( selectedrow == LIKERSVTABLE_NOTSELECTED ) {\r
+                       jButton_update.setForeground(Color.BLACK);\r
+                       jButton_update.setEnabled(false);\r
+               }\r
+               else {\r
+                       jButton_update.setForeground(Color.RED);\r
+                       jButton_update.setEnabled(true);\r
+               }\r
+       }\r
+       \r
+       /**\r
+        * 他のコンボボックスの操作によって内容が変わるコンボボックスの選択肢の入れ替え\r
+        * @see #setInitFixies\r
+        */\r
+       private void setInitVariables(HDDRecorder myrec) {\r
+               \r
+               // 番組追従\r
+               {\r
+                       if ( myrec.isPursuesEditable() ) {\r
+                               jCBXPanel_pursues.setEnabled( ! ITEM_EVIDNEEDED.equals(jButton_getEventId.getText()));\r
+                       }\r
+                       else {\r
+                               jCBXPanel_pursues.setEnabled(false);\r
+                       }\r
+               }\r
+               \r
+               // CH\r
+               {\r
+                       jComboBox_ch.removeAllItems();\r
+                       jComboBox_ch.addItem(vals.hide_tvd.center);\r
+                       for ( TextValueSet t : myrec.getChValue() ) {\r
+                               if ( t.getText().startsWith("外部") ) {\r
+                                       jComboBox_ch.addItem(t.getText());\r
+                               }\r
+                       }\r
+                       jComboBox_ch.setEnabled( jComboBox_ch.getItemCount() > 0 );\r
+               }\r
+               \r
+               // エンコーダ\r
+               {\r
+                       jComboBox_encoder.removeAllItems();\r
+                       for ( String enc : getFilteredEncoders(myrec, vals.hide_tvd.center) ) {\r
+                               jComboBox_encoder.addItem(enc);\r
+                       }\r
+                       jComboBox_encoder.setEnabled( jComboBox_encoder.getItemCount() > 0 );\r
+               }\r
+               \r
+               // 日付\r
+               {\r
+                       if ( myrec.isRepeatReserveSupported() ) {\r
+                               jComboBox_date.setEnabled(true);\r
+                       }\r
+                       else {\r
+                               jComboBox_date.setEnabled(false);\r
+                               jComboBox_date.setSelectedIndex(0);\r
+                       }\r
+               }\r
+               \r
+               // AV設定\r
+               refCBX(jCBXPanel_videorate, myrec.getVideoRateList());\r
+               refCBX(jCBXPanel_folder, myrec.getFolderList());\r
+               refCBX(jCBXPanel_audiorate, myrec.getAudioRateList());\r
+               \r
+               refCBX(jCBXPanel_device, myrec.getDeviceList());\r
+               refCBX(jCBXPanel_bvperf, myrec.getBVperf());\r
+               refCBX(jCBXPanel_dvdcompat, myrec.getDVDCompatList());\r
+               \r
+               refCBX(jCBXPanel_autodel, myrec.getAutodel());\r
+               refCBX(jCBXPanel_lvoice, myrec.getLVoice());\r
+               refCBX(jCBXPanel_aspect, myrec.getAspect());\r
+               \r
+               refCBX(jCBXPanel_msChapter, myrec.getMsChapter());\r
+               refCBX(jCBXPanel_mvChapter, myrec.getMvChapter());\r
+               refCBX(jCBXPanel_xChapter, myrec.getXChapter());\r
+\r
+               {\r
+                       jCBXPanel_pursues.removeAllItems();\r
+                       jCBXPanel_pursues.addItem(ITEM_YES);\r
+                       jCBXPanel_pursues.addItem(ITEM_NO);\r
+               }\r
+       }\r
+       \r
+       /**\r
+        * コンボボックス操作によって連動して選択しなおし\r
+        */\r
+       private void setSelectedVariables(HDDRecorder myrec, ReserveList myrsv, AVs myavs, String mychname, String myenc) {\r
+\r
+               // 予約実行\r
+               if ( myrsv == null ) {\r
+                       jCheckBox_Exec.setSelected(true);\r
+                       jCheckBox_Exec.setForeground(Color.BLACK);\r
+               }\r
+               else {\r
+                       jCheckBox_Exec.setSelected(myrsv.getExec());\r
+                       jCheckBox_Exec.setForeground((myrsv.getExec())?(Color.BLACK):(Color.RED));\r
+               }\r
+\r
+               // タイトル自動補完\r
+               jCheckBox_Autocomplete.setEnabled(myrec.isAutocompleteSupported());\r
+               if ( myrec.isAutocompleteSupported() ) {\r
+                       if ( myrsv == null ) {\r
+                               jCheckBox_Autocomplete.setSelected(env.getUseAutocomplete());\r
+                               jCheckBox_Autocomplete.setForeground(Color.BLACK);\r
+                       }\r
+                       else {\r
+                               jCheckBox_Autocomplete.setSelected(myrsv.getAutocomplete());\r
+                               jCheckBox_Autocomplete.setForeground((myrsv.getAutocomplete())?(Color.BLACK):(Color.RED));\r
+                       }\r
+               }\r
+               else {\r
+                       jCheckBox_Autocomplete.setSelected(false);\r
+               }\r
+               \r
+               // 番組追従\r
+               jCBXPanel_pursues.setEnabled(myrec.isPursuesEditable());\r
+               if ( ! vals.hide_atreservedlist ) {\r
+                       if ( myrec.isPursuesEditable() ) {\r
+                               jCBXPanel_pursues.setEnabled(true);\r
+                               if ( myrsv != null ) {\r
+                                       // 類似予約からの継承\r
+                                       jCBXPanel_pursues.setSelectedItem((myrsv.getPursues())?(ITEM_YES):(ITEM_NO));\r
+                               }\r
+                               else {\r
+                                       // デフォルトはON\r
+                                       if ( ITEM_EVIDNEEDED.equals(jButton_getEventId.getText()) ) {\r
+                                               jCBXPanel_pursues.setEnabled(false);\r
+                                               jCBXPanel_pursues.setSelectedItem(ITEM_NO);\r
+                                       }\r
+                                       else {\r
+                                               jCBXPanel_pursues.setSelectedItem(ITEM_YES);\r
+                                       }\r
+                               }\r
+                       }\r
+                       else {\r
+                               jCBXPanel_pursues.setEnabled(false);\r
+                               jCBXPanel_pursues.setSelectedItem(ITEM_NO);\r
+                       }\r
+               }\r
+               else {\r
+                       if ( myrec.isPursuesEditable() ) { \r
+                               // 既存予約の継承(番組IDが取得できるまではdisable)\r
+                               jCBXPanel_pursues.setEnabled( myrsv.getContentId() != null && myrsv.getContentId().length() > 0 );\r
+                               jCBXPanel_pursues.setSelectedItem((myrsv.getPursues())?(ITEM_YES):(ITEM_NO));\r
+                       }\r
+                       else {\r
+                               jCBXPanel_pursues.setEnabled(false);\r
+                               jCBXPanel_pursues.setSelectedItem((myrsv.getPursues())?(ITEM_YES):(ITEM_NO));\r
+                       }\r
+               }\r
+\r
+               // レコーダ\r
+               jComboBox_recorder.setSelectedItem(myrec.Myself());\r
+               \r
+               // エンコーダ(移動しました)\r
+               \r
+               // サブジャンル\r
+               {\r
+                       ProgGenre mygenre = ProgGenre.get((String) jCBXPanel_genre.getSelectedItem());\r
+                       jCBXPanel_subgenre.removeAllItems();\r
+                       for ( ProgSubgenre sg : ProgSubgenre.values(mygenre) ) {\r
+                               jCBXPanel_subgenre.addItem(sg.toString());\r
+                       }\r
+                       if ( myrsv != null && mygenre == ProgGenre.get(myrsv.getRec_genre()) ) {\r
+                               jCBXPanel_subgenre.setSelectedItem(myrsv.getRec_subgenre());\r
+                       }\r
+                       else if ( mygenre == vals.hide_tvd.genre ) {\r
+                               jCBXPanel_subgenre.setSelectedItem(vals.hide_tvd.subgenre.toString());\r
+                       }\r
+               }\r
+               \r
+               // 連動するAV設定\r
+               setSelectedAVItems(myrec.getRecorderId(), myrsv, myavs);\r
+               \r
+               // エンコーダ(旧RDデジは画質によって利用できるエンコーダが制限される)\r
+               {\r
+                       \r
+                       if ( vals.hide_atreservedlist ) {\r
+                               jComboBox_encoder.setSelectedItem(myrsv.getTuner());\r
+                       }\r
+                       else if ( jComboBox_encoder.getItemCount() > 0 ) {\r
+                               // 裏番組チェックとかやるよ\r
+                               String vrate = ( isVARDIA(myrec.getRecorderId()) ) ? ((String) jCBXPanel_videorate.getSelectedItem()) : (null);\r
+                               String starttm = vals.hide_tvd.start;\r
+                               String endtm = vals.hide_tvd.end;\r
+                               try {\r
+                                       GregorianCalendar cal = CommonUtils.getCalendar((String) jComboBox_date.getItemAt(0));\r
+                                       cal.set(Calendar.HOUR_OF_DAY, Integer.valueOf(jTextField_ahh.getText()));\r
+                                       cal.set(Calendar.MINUTE, Integer.valueOf(jTextField_amm.getText()));\r
+                                       starttm = CommonUtils.getTime(cal);\r
+                                       \r
+                                       cal.set(Calendar.HOUR_OF_DAY, Integer.valueOf(jTextField_zhh.getText()));\r
+                                       cal.set(Calendar.MINUTE, Integer.valueOf(jTextField_zmm.getText()));\r
+                                       endtm = CommonUtils.getTime(cal);\r
+                               }\r
+                               catch ( NumberFormatException e) {\r
+                                       System.err.println(ERRID+"時刻の指定が数値ではありません");\r
+                               }\r
+                               String enc = getEmptyEncorder(myrec, vals.hide_tvd.center, vals.hide_tvd.accurateDate, starttm, endtm, vrate);\r
+                               \r
+                               if ( myrsv != null ) {\r
+                                       // 類似予約最優先\r
+                                       jComboBox_encoder.setSelectedItem(myrsv.getTuner());\r
+                               }\r
+                               else if ( myrec.isAutoEncSelectEnabled() && ! vals.hide_atreservedlist ) {\r
+                                       // 番組情報に近い予約を探してエンコーダを絞り込む\r
+                                       jComboBox_encoder.setSelectedItem(enc);\r
+                                       showUraList(myrec.Myself());\r
+                               }\r
+                               else if ( jComboBox_encoder.getItemCount() > 0 ) {\r
+                                       // 類似予約や自動選択がない場合は極力もとのエンコーダを選択したい\r
+                                       if ( myenc != null ) {\r
+                                               jComboBox_encoder.setSelectedItem(myenc);\r
+                                               myenc = (String) jComboBox_encoder.getSelectedItem();\r
+                                       }\r
+                                       if ( myenc == null ) {\r
+                                               jComboBox_encoder.setSelectedIndex(0);\r
+                                       }\r
+                                       showUraList(myrec.Myself());\r
+                               }\r
+                       }\r
+               }\r
+               \r
+               // パターン\r
+               if ( myrsv == null ) {\r
+                       jComboBox_date.setSelectedIndex(0);\r
+               }\r
+               else {\r
+                       int dateid = 0;\r
+                       if ( myrsv.getRec_pattern_id() <= HDDRecorder.RPTPTN_ID_SAT ) {\r
+                               dateid = 1;\r
+                       }\r
+                       else if ( myrsv.getRec_pattern_id() <= HDDRecorder.RPTPTN_ID_EVERYDAY ) {\r
+                               dateid = 1+myrsv.getRec_pattern_id()-HDDRecorder.RPTPTN_ID_SAT;\r
+                       }\r
+                       jComboBox_date.setSelectedIndex(dateid);\r
+               }\r
+       }\r
+       \r
+       private void setSelectedAVItems(String myrecid, ReserveList myrsv, AVs myavs) {\r
+               if ( myrsv != null ) {\r
+                       selCBX(jCBXPanel_videorate, myrsv.getRec_mode());\r
+                       selCBX(jCBXPanel_folder, myrsv.getRec_folder());\r
+                       selCBX(jCBXPanel_audiorate, myrsv.getRec_audio());\r
+                       \r
+                       selCBX(jCBXPanel_device, myrsv.getRec_device());\r
+                       selCBX(jCBXPanel_bvperf, myrsv.getRec_bvperf());\r
+                       selCBX(jCBXPanel_dvdcompat, myrsv.getRec_dvdcompat());\r
+                       \r
+                       selCBX(jCBXPanel_autodel, myrsv.getRec_autodel());\r
+                       selCBX(jCBXPanel_lvoice, myrsv.getRec_lvoice());\r
+                       selCBX(jCBXPanel_aspect, myrsv.getRec_aspect());\r
+                       \r
+                       selCBX(jCBXPanel_msChapter, myrsv.getRec_mschapter());\r
+                       selCBX(jCBXPanel_mvChapter, myrsv.getRec_mvchapter());\r
+                       selCBX(jCBXPanel_xChapter, myrsv.getRec_xchapter());\r
+               }\r
+               else if ( myavs != null ) {\r
+                       selCBX(jCBXPanel_videorate, myavs.getVideorate());\r
+                       selCBX(jCBXPanel_folder, myavs.getFolder());\r
+                       selCBX(jCBXPanel_audiorate, myavs.getAudiorate());\r
+                       \r
+                       selCBX(jCBXPanel_device, myavs.getDevice());\r
+                       selCBX(jCBXPanel_bvperf, myavs.getBvperf());\r
+                       selCBX(jCBXPanel_dvdcompat, myavs.getDVDCompat());\r
+                       \r
+                       selCBX(jCBXPanel_autodel, myavs.getAutodel());\r
+                       selCBX(jCBXPanel_lvoice, myavs.getLvoice());\r
+                       selCBX(jCBXPanel_aspect, myavs.getAspect());\r
+                       \r
+                       selCBX(jCBXPanel_msChapter, myavs.getMsChapter());\r
+                       selCBX(jCBXPanel_mvChapter, myavs.getMvChapter());\r
+                       selCBX(jCBXPanel_xChapter, myavs.getXChapter());\r
+                       \r
+                       {\r
+                               jCBXPanel_pursues.setSelectedItem(myavs.getPursues()?ITEM_YES:ITEM_NO);\r
+                       }\r
+               }\r
+               else {\r
+                       // 特殊アイテム\r
+                       if ( isRD(myrecid) ) {\r
+                               setSelectedFolder();\r
+                       }\r
+               }\r
+       }\r
+       \r
+       /**\r
+        *  <P>指定したレコーダによってフォルダを変える\r
+        *  <P>うーん、folderを他の用途に転用してるけど問題おきないかな?\r
+        */\r
+       private void setSelectedFolder() {\r
+\r
+               // タイトルに連動\r
+               if ( env.getAutoFolderSelect() ) {\r
+                       String titlePop = TraceProgram.replacePop((String) jComboBox_title.getSelectedItem());\r
+                       for (int i=0; i<jCBXPanel_folder.getItemCount(); i++) {\r
+                               String folderPop = TraceProgram.replacePop(((String) jCBXPanel_folder.getItemAt(i)).replaceFirst("^\\[(HDD|USB)\\] ",""));\r
+                               if (folderPop.equals(titlePop)) {\r
+                                       jCBXPanel_folder.setSelectedIndex(i);\r
+                                       return;\r
+                               }\r
+                       }\r
+               }\r
+               \r
+               // デバイス名に連動\r
+               int defaultFolderIdx = -1;\r
+               int defaultHDDFolderIdx = -1;\r
+               int defaultDVDFolderIdx = -1;\r
+               for (int i=0; i<jCBXPanel_folder.getItemCount(); i++ ) {\r
+                       String folderName = (String) jCBXPanel_folder.getItemAt(i);\r
+                       if (folderName.indexOf("指定なし") != -1) {\r
+                               if (defaultFolderIdx == -1) {\r
+                                       defaultFolderIdx = i;\r
+                               }\r
+                               if (folderName.startsWith("[HDD] ")) {\r
+                                       defaultHDDFolderIdx = i;\r
+                               }\r
+                               else if (folderName.startsWith("[USB] ")) {\r
+                                       defaultDVDFolderIdx = i;\r
+                               }\r
+                       }\r
+               }\r
+               if (jCBXPanel_device.getItemCount() > 0) {\r
+                       if (((String) jCBXPanel_device.getSelectedItem()).equals("HDD")) {\r
+                               if (defaultHDDFolderIdx != -1) {\r
+                                       jCBXPanel_folder.setSelectedIndex(defaultHDDFolderIdx);\r
+                                       return;\r
+                               }\r
+                       }\r
+                       else {\r
+                               if (defaultDVDFolderIdx != -1) {\r
+                                       jCBXPanel_folder.setSelectedIndex(defaultDVDFolderIdx);\r
+                                       return;\r
+                               }\r
+                       }\r
+               }\r
+               if (defaultFolderIdx != -1) {\r
+                       jCBXPanel_folder.setSelectedIndex(defaultFolderIdx);\r
+               }\r
+       }\r
+       \r
+       private void setLabels(HDDRecorder recorder) {\r
+               String val;\r
+               \r
+               val = recorder.getLabel_Videorate();\r
+               jCBXPanel_videorate.setText((val!=null)?(val):("画質"));\r
+               val = recorder.getLabel_Audiorate();\r
+               jCBXPanel_audiorate.setText((val!=null)?(val):("音質"));\r
+               val = recorder.getLabel_Folder();\r
+               jCBXPanel_folder.setText((val!=null)?(val):("記録先フォルダ"));\r
+               val = recorder.getLabel_Device();\r
+               jCBXPanel_device.setText((val!=null)?(val):("記録先デバイス"));\r
+               val = recorder.getLabel_DVDCompat();\r
+               jCBXPanel_dvdcompat.setText((val!=null)?(val):("BD/DVD互換モード"));\r
+               val = recorder.getLabel_XChapter();\r
+               jCBXPanel_xChapter.setText((val!=null)?(val):("無音部分チャプタ分割"));\r
+               val = recorder.getLabel_MsChapter();\r
+               jCBXPanel_msChapter.setText((val!=null)?(val):("マジックチャプタ(シーン)"));\r
+               val = recorder.getLabel_MvChapter();\r
+               jCBXPanel_mvChapter.setText((val!=null)?(val):("マジックチャプタ(本編)"));\r
+               val = recorder.getLabel_Aspect();\r
+               jCBXPanel_aspect.setText((val!=null)?(val):("録画のりしろ"));\r
+               val = recorder.getLabel_BVperf();\r
+               jCBXPanel_bvperf.setText((val!=null)?(val):("録画優先度"));\r
+               val = recorder.getLabel_LVoice();\r
+               jCBXPanel_lvoice.setText((val!=null)?(val):("ライン音声選択"));\r
+               val = recorder.getLabel_Autodel();\r
+               jCBXPanel_autodel.setText((val!=null)?(val):("自動削除"));\r
+       }\r
+\r
+       /*******************************************************************************\r
+        * 共通部品的な\r
+        ******************************************************************************/\r
+       \r
+       /**\r
+        * コンボボックスのアイテムのリフレッシュ\r
+        */\r
+       private void refCBX(JComboBoxPanel combo, ArrayList<TextValueSet> tvs) {\r
+               combo.removeAllItems();\r
+               for ( TextValueSet t : tvs ) {\r
+                       combo.addItem(t.getText());\r
+                       if (t.getDefval()) combo.setSelectedIndex(combo.getItemCount()-1);      // デフォルト値があるならば\r
+               }\r
+               \r
+               combo.setEnabled( combo.getItemCount() > 0 );\r
+       }\r
+       \r
+       /**\r
+        * コンボボックスのアイテム選択\r
+        */\r
+       private void selCBX(JComboBoxPanel combo, String selected) {\r
+               if ( selected != null ) combo.setSelectedItem(selected);\r
+       }\r
+\r
+       /**\r
+        * コンボボックス系のリスナーの設定/解除\r
+        */\r
+       private void setEnabledSelectionListeners(boolean b) {\r
+               // 重複呼び出しがこわいので一回全部削除してしまう\r
+               jComboBox_encoder.removeItemListener(il_encoderChanged);\r
+               jCBXPanel_videorate.removeItemListener(il_videorateChanged);\r
+               jComboBox_recorder.removeItemListener(il_recorderChanged);\r
+               jCBXPanel_genre.removeItemListener(il_genreChanged);\r
+               likersvtable.removeMouseListener(ml_likelistSelected);\r
+               if ( b ) {\r
+                       // 必要なら追加する\r
+                       jComboBox_encoder.addItemListener(il_encoderChanged);\r
+                       jCBXPanel_videorate.addItemListener(il_videorateChanged);\r
+                       jComboBox_recorder.addItemListener(il_recorderChanged);\r
+                       jCBXPanel_genre.addItemListener(il_genreChanged);\r
+                       likersvtable.addMouseListener(ml_likelistSelected);\r
+               }\r
+       }\r
+       \r
+       private void removeAllSelectionItems() {\r
+               jComboBox_encoder.removeAllItems();\r
+               jCBXPanel_videorate.removeAllItems();\r
+               jComboBox_recorder.removeAllItems();\r
+               jCBXPanel_genre.removeAllItems();\r
+               //likersvtable.removeAllItems();\r
+       }\r
+       \r
+       /*******************************************************************************\r
+        * ネットから番組IDを取得する\r
+        ******************************************************************************/\r
+       \r
+       /**\r
+        * ジャンル別AV設定の取得\r
+        */\r
+       private AVs getSelectedAVs(ProgGenre key_genre, String key_webChName, String recId) {\r
+               \r
+               AVs myavs = new AVs();\r
+               \r
+               String selected_key = null;\r
+               AVSetting xavs = null;\r
+               if ( env.getEnableCHAVsetting() ) {\r
+                       selected_key = key_webChName;\r
+                       xavs = chavs;\r
+               }\r
+               else {\r
+                       selected_key = key_genre.toString();\r
+                       xavs = avs;\r
+               }\r
+               \r
+               jButton_load.setEnabled(true);\r
+               myavs = xavs.get(recId, selected_key);\r
+               if (myavs == null) {                    \r
+                       jButton_load.setEnabled(false);\r
+                       \r
+                       // デフォルトの設定があれば再チャレンジ!\r
+                       myavs = xavs.get(recId, null);\r
+                       if ( myavs != null ) {\r
+                               selected_key = "デフォルト";\r
+                       }\r
+               }\r
+       \r
+               if ( myavs != null ) {\r
+                       MWin.appendMessage(MSGID+"画質・音質を自動設定します: "+recId+" & "+selected_key);\r
+               }\r
+               else {\r
+                       MWin.appendMessage(MSGID+"画質・音質の自動設定候補がありません: "+recId+" & "+selected_key);\r
+               }\r
+               \r
+               return myavs;\r
+       }\r
+       \r
+       /*******************************************************************************\r
+        * ネットから番組IDを取得する\r
+        ******************************************************************************/\r
+       \r
+       /**\r
+        * ダイアログオープン時の番組ID取得処理\r
+        */\r
+       private void getEventIdOnOpen(final ProgDetailList tvd) {\r
+               \r
+               if (debug) System.err.println(DBGID+vals.hide_content_id+" "+ContentIdEDCB.isValid(vals.hide_content_id)+" "+vals.hide_content_id.length());\r
+               \r
+               if ( ContentIdEDCB.isValid(vals.hide_content_id) || ! env.getAutoEventIdComplete() ) {\r
+                       return; // すでに番組ID取得ずみか、自動取得がOFFか\r
+               }\r
+                       \r
+               Integer evid = null;\r
+               if ( ContentIdDIMORA.isValid(vals.hide_content_id) ) {\r
+                       ContentIdDIMORA.decodeContentId(vals.hide_content_id);\r
+                       String chid = ContentIdDIMORA.getChId();\r
+                       evid = doGetEventIdById(chid, false);\r
+               }\r
+               else {\r
+                       evid = doGetEventIdByName(tvd.center, false);\r
+               }\r
+               if ( evid != null ) {\r
+                       // on cache\r
+                       tvd.progid = vals.hide_content_id;\r
+                       tvd.setContentIdStr();\r
+               }\r
+               else {\r
+                       // online\r
+                       StWin.clear();\r
+                       \r
+                       new SwingBackgroundWorker(false) {\r
+                               @Override\r
+                               protected Object doWorks() throws Exception {\r
+                                       StWin.appendMessage(MSGID+"番組IDを取得します");\r
+                                       \r
+                                       // 番組IDを探したい\r
+                                       Integer evid = null;\r
+                                       if ( ContentIdDIMORA.isValid(vals.hide_content_id) ) {\r
+                                               ContentIdDIMORA.decodeContentId(vals.hide_content_id);\r
+                                               String chid = ContentIdDIMORA.getChId();\r
+                                               evid = doGetEventIdById(chid, true);\r
+                                       }\r
+                                       else {\r
+                                               evid = doGetEventIdByName(tvd.center, true);\r
+                                       }\r
+                                       if ( evid != null ) {\r
+                                               tvd.progid = vals.hide_content_id;\r
+                                               tvd.setContentIdStr();\r
+                                       }\r
+                                       return null;\r
+                               }\r
+                               @Override\r
+                               protected void doFinally() {\r
+                                       StWin.setVisible(false);\r
+                               }\r
+                       }.execute();\r
+                       \r
+                       CommonSwingUtils.setLocationCenter(parent, (Component) StWin);\r
+                       StWin.setVisible(true);\r
+               }\r
+       }\r
+\r
+       /**\r
+        * 番組表に存在する放送局IDで\r
+        */\r
+       private Integer doGetEventIdById(String chid, boolean force) {\r
+               \r
+               Integer evid = geteventid.getEvId(chid,vals.hide_startdatetime, force);\r
+               \r
+               if ( force && evid == null ) {\r
+                       MWin.appendError(ERRID+"番組ID取得でエラーが発生しました: "+chid+", "+vals.hide_startdatetime);\r
+                       ringBeep();\r
+                       return null;    // 一発死に\r
+               }\r
+               else if ( ! force && evid == null ) {\r
+                       System.out.println(MSGID+"キャッシュにヒットしませんでした: "+chid+", "+vals.hide_startdatetime);\r
+                       return null;\r
+               }\r
+               else if ( evid == -1 ) {\r
+                       MWin.appendError(ERRID+"番組IDが取得できませんでした: "+chid+", "+vals.hide_startdatetime);\r
+                       return null;\r
+               }\r
+               \r
+               ContentIdDIMORA.decodeContentId(vals.hide_content_id);\r
+               vals.hide_content_id = ContentIdDIMORA.getContentId(evid);\r
+\r
+               MWin.appendMessage(MSGID+"番組IDを取得しました(byId): "+vals.hide_content_id);\r
+               setGetEventIdButton(vals.hide_content_id, true);\r
+               \r
+               return evid;\r
+       }\r
+       \r
+       /**\r
+        * レコーダに登録された放送局IDで\r
+        */\r
+       private Integer doGetEventIdByName(String centername, boolean force) {\r
+               \r
+               String chid = null;\r
+               String chidEDCB = null;\r
+               String chidREGZA = null;\r
+               \r
+               Integer evid = null;\r
+               \r
+               // 登録済みのレコーダプラグインを全部チェックしてみる\r
+               for ( HDDRecorder rec : recorders ) {\r
+                       if ( rec.isBackgroundOnly() ) {\r
+                               continue;\r
+                       }\r
+\r
+                       chidEDCB = chidREGZA = chid = null;\r
+                       Integer tmpEvid = null;\r
+                       \r
+                       String chcode = rec.getChCode().getCH_WEB2CODE(centername);\r
+                       if ( chcode == null ) {\r
+                               System.err.println(ERRID+"「Web番組表の放送局名」を「放送局コード」に変換できません: "+rec.getRecorderId()+" "+centername);\r
+                               continue;\r
+                       }\r
+                       \r
+                       chidEDCB = chid = ContentIdEDCB.getChId(chcode);\r
+                       if ( chid == null ) {\r
+                               chidREGZA = chid = ContentIdREGZA.getChId(chcode);\r
+                               if ( chid == null ) {\r
+                                       System.err.println(ERRID+"番組IDの取得に未対応のレコーダです: "+rec.getRecorderId());\r
+                                       continue;\r
+                               }\r
+                       }\r
+                       \r
+                       if (debug) System.out.println(MSGID+"番組IDを取得します: "+rec.getRecorderId());\r
+                       \r
+                       tmpEvid = geteventid.getEvId(chid,vals.hide_startdatetime, force);\r
+                       \r
+                       if (evid == null) evid = tmpEvid;\r
+                       \r
+                       if ( force && tmpEvid == null ) {\r
+                               MWin.appendError(ERRID+"番組ID取得でエラーが発生しました: "+chid+", "+vals.hide_startdatetime);\r
+                               ringBeep();\r
+                               return null;    // 一発死に\r
+                       }\r
+                       else if ( ! force && tmpEvid == null ) {\r
+                               System.out.println(MSGID+"キャッシュにヒットしませんでした: "+chid+", "+vals.hide_startdatetime);\r
+                               return null;\r
+                       }\r
+                       else if ( tmpEvid == -1 ) {\r
+                               System.err.println(ERRID+"番組IDが取得できませんでした: "+chid+", "+vals.hide_startdatetime);\r
+                               continue;\r
+                       }\r
+                       \r
+                       break;\r
+               }\r
+               \r
+               if ( evid == null ) {\r
+                       MWin.appendError(ERRID+"【致命的エラー】放送局IDを持つレコーダプラグインが存在しません");\r
+                       ringBeep();\r
+                       return null;\r
+               }\r
+               else if ( evid == -1 ) {\r
+                       MWin.appendError(ERRID+"【警告】番組IDの取得に失敗しました。開始時刻の移動や、まだ番組表サイトに情報が用意されていない場合などが考えられます。");\r
+                       ringBeep();\r
+                       return null;\r
+               }\r
+               \r
+               if ( chidREGZA != null ) {\r
+                       vals.hide_content_id = ContentIdREGZA.getContentId(chidREGZA, evid);\r
+               }\r
+               else {\r
+                       vals.hide_content_id = ContentIdEDCB.getContentId(chidEDCB, evid);\r
+               }               \r
+               \r
+               MWin.appendMessage(MSGID+"番組IDを取得しました(byName): "+vals.hide_content_id);\r
+               setGetEventIdButton(vals.hide_content_id, true);\r
+               \r
+               return evid;\r
+       }\r
+\r
+       /**\r
+        * @param enabled 番組IDの取得ができないシチュエーション(予約一覧から開くとか)では取得ボタンをfalseに。\r
+        */\r
+       private boolean setGetEventIdButton(String cId, boolean enabled) {\r
+               Integer evid = null;\r
+               if ( ContentIdEDCB.decodeContentId(cId) ) {\r
+                       evid = ContentIdEDCB.getEvId();\r
+               }\r
+               else if ( ContentIdREGZA.decodeContentId(cId) && ContentIdREGZA.getEvId() != 0x0000 ) {\r
+                       evid = ContentIdREGZA.getEvId();\r
+               }\r
+               else if ( ContentIdDIMORA.decodeContentId(cId) && ContentIdDIMORA.getEvId() != 0x0000 ) {\r
+                       evid = ContentIdDIMORA.getEvId();\r
+               }\r
+               if ( evid == null ) {\r
+                       jButton_getEventId.setText(ITEM_EVIDNEEDED);\r
+                       if (enabled) {\r
+                               jButton_getEventId.setForeground(Color.BLUE);\r
+                               jButton_getEventId.setEnabled(true);\r
+                       }\r
+                       else {\r
+                               jButton_getEventId.setForeground(Color.GRAY);\r
+                               jButton_getEventId.setEnabled(false);\r
+                       }\r
+                       // 番組IDが取得できるまではdisable\r
+                       return false;\r
+               }\r
+               else {\r
+                       jButton_getEventId.setText(String.format("番組ID:%04X",evid));\r
+                       jButton_getEventId.setForeground(Color.GRAY);\r
+                       jButton_getEventId.setEnabled(false);\r
+                       // 番組IDが取得できるまではdisable\r
+                       return true;\r
+               }\r
+       }\r
+\r
+       /*******************************************************************************\r
+        * 自動エンコーダ選択と裏番組抽出\r
+        ******************************************************************************/\r
+\r
+       /**\r
+        * <P>使用されていないエンコーダをリストアップする\r
+        * <P>裏番組リストも一緒に作成する\r
+        */\r
+       private String getEmptyEncorder(HDDRecorder recorder, String ch, String date, String stime, String etime, String vrate) {\r
+               // 予約の開始・終了日時を算出する\r
+               GregorianCalendar c = CommonUtils.getCalendar(date);\r
+               String start = String.format("%s %s", CommonUtils.getDate(c,false),stime);\r
+               if (stime.compareTo(etime) > 0) {\r
+                       c.add(Calendar.DATE, 1);\r
+               }\r
+               String end = String.format("%s %s", CommonUtils.getDate(c,false),etime);\r
+               \r
+               // エンコーダの一覧を作成する\r
+               ArrayList<String> encs = getFilteredEncoders(recorder, ch);\r
+               if ( encs.size() == 0 ) {\r
+                       if ( recorder.getEncoderList().size() > 0 ) {\r
+                               MWin.appendError(ERRID+"エンコーダ候補が見つからない: "+recorder.Myself());\r
+                       }\r
+                       return null;\r
+               }\r
+               \r
+               // 空きエンコーダがみつからなかった場合の選択肢\r
+               String firstenc = encs.get(0);\r
+               \r
+               // 予約リストをなめて予約済みエンコーダーをさがす\r
+               \r
+               String rsvedTuner = null;\r
+               uraban.clear();\r
+               for ( ReserveList r : recorder.getReserves() ) {\r
+                       // 実行予定のもののみ\r
+                       if (r.getExec()) {\r
+                               // 予約時間が重なるものを抽出する\r
+                               ArrayList<String> starts = new ArrayList<String>();\r
+                               ArrayList<String> ends = new ArrayList<String>();\r
+                               CommonUtils.getStartEndList(starts, ends, r);\r
+                               for (int i=0;i<starts.size(); i++) {\r
+                                       // 既に予約済みの場合\r
+                                       if (starts.get(i).equals(start) && ends.get(i).equals(end) && ch.equals(r.getCh_name())) {\r
+                                               rsvedTuner = r.getTuner();\r
+                                               continue;\r
+                                       }\r
+                                       \r
+                                       // 時間の重なる番組\r
+                                       if ( CommonUtils.isOverlap(start, end, starts.get(i), ends.get(i), env.getAdjoiningNotRepetition()) ) {\r
+                                               \r
+                                               String msg = starts.get(i)+", "+r.getTuner()+", "+r.getTitle();\r
+                                               for ( String ura : uraban ) {\r
+                                                       if ( ura.equals(msg)) {\r
+                                                               msg = "";\r
+                                                               break;\r
+                                                       }\r
+                                               }\r
+                                               if ( ! msg.equals("")) {\r
+                                                       uraban.add(msg);\r
+                                               }\r
+                                               \r
+                                               // 予約時間が重なるものはエンコーダーの一覧から削除する\r
+                                               HashMap<String,Boolean> rems = new HashMap<String,Boolean>();\r
+                                               for ( String enc : encs ) {\r
+                                                       if (enc.equals(r.getTuner())) {\r
+                                                               rems.put(enc, true);\r
+                                                               \r
+                                                               // ---- RDデジタルW録向け暫定コード ----\r
+                                                               if (enc.equals("TS1") || enc.equals("DR1")) {\r
+                                                                       // TS1が埋まっていればREは使えない\r
+                                                                       rems.put("RE", true);\r
+                                                               }\r
+                                                               else if (enc.equals("RE")) {\r
+                                                                       // REが埋まっていればTS1は使えない\r
+                                                                       rems.put("TS1", true);\r
+                                                                       rems.put("DR1", true);\r
+                                                               }\r
+                                                               // ---- RDデジタルW録向け暫定コード ----\r
+                                                               \r
+                                                               break;\r
+                                                       }\r
+                                               }\r
+                                               for ( String key : rems.keySet() ) {\r
+                                                       encs.remove(key);\r
+                                               }\r
+                                       }\r
+                               }\r
+                       }\r
+               }\r
+               if ( recorder.isAutoEncSelectEnabled() ) {\r
+                       \r
+                       // 旧RDデジ系 - ここから\r
+                       if ( vrate != null ) {\r
+                               if ( ! vrate.equals("[TS]") && ! vrate.equals("[DR]")) {\r
+                                       if ( ! encs.contains("RE") ) {\r
+                                               // 空きエンコーダはなかった\r
+                                               jLabel_encoderemptywarn.setText("空きエンコーダ不足");\r
+                                               jLabel_encoderemptywarn.setForeground(Color.RED);\r
+                                       }\r
+                                       return "RE";\r
+                               }\r
+\r
+                               encs.remove("RE");\r
+                       }\r
+                       // 旧RDデジ系 - ここまで\r
+                       \r
+                       if ( encs.size() == 0  ) {\r
+                               // 空きエンコーダはなかった\r
+                               jLabel_encoderemptywarn.setText("空きエンコーダ不足");\r
+                               jLabel_encoderemptywarn.setForeground(Color.RED);\r
+                               return firstenc;\r
+                       }\r
+                       \r
+                       jLabel_encoderemptywarn.setText("");\r
+                       if (rsvedTuner != null) {\r
+                               // 予約済みなら同じのでいいよね\r
+                               return rsvedTuner;\r
+                       }\r
+                       if ( encs.size() > 0 ) {\r
+                               // エンコーダーが残っていればそれらの先頭を返す(裏番組がない場合は除く)\r
+                               return encs.get(0);\r
+                       }\r
+               }\r
+               else {\r
+                       jLabel_encoderemptywarn.setText("空きエンコーダ検索無効");\r
+                       jLabel_encoderemptywarn.setForeground(Color.BLACK);\r
+                       return firstenc;\r
+               }\r
+               return null;\r
+       }\r
+       \r
+       ArrayList<String> uraban = new ArrayList<String>();     // 裏番組の一覧\r
+       \r
+       private void showUraList(String myself) {\r
+               if (uraban.size() > 0) {\r
+                       for (String ura : uraban) {\r
+                               String msg = MSGID+"[裏番組チェック] "+((String) jComboBox_title.getItemAt(0))+" の裏番組: "+ura;\r
+                               MWin.appendMessage(msg);\r
+                       }\r
+               }\r
+               else {\r
+                       // 裏番組がない場合に分かりにくかったので追加\r
+                       String msg = MSGID+"[裏番組チェック] "+((String) jComboBox_title.getItemAt(0))+" の裏番組はありません";\r
+                       MWin.appendMessage(msg);\r
+               }\r
+       }\r
+       \r
+       /*******************************************************************************\r
+        * 放送波種別によって利用できるエンコーダを絞り込んでみる\r
+        ******************************************************************************/\r
+       \r
+       /**\r
+        * エンコーダを絞る\r
+        */\r
+       private ArrayList<String> getFilteredEncoders(HDDRecorder recorder, String webChName) {\r
+               \r
+               ArrayList<String> encs = new ArrayList<String>();\r
+               \r
+               if ( recorder.getEncoderList().size() == 0 ) {\r
+                       encs.add("■");\r
+                       return encs;\r
+               }\r
+               \r
+               if ( recorder.isBroadcastTypeNeeded() ) {\r
+                       \r
+                       // エンコーダーに地上波・BS/CSの区別のあるとき\r
+                       \r
+                       String code = recorder.getChCode().getCH_WEB2CODE(webChName);\r
+                       if (code != null) {\r
+                               for ( TextValueSet enc : recorder.getEncoderList() ) {\r
+                                       if ((code.startsWith(BroadcastType.TERRA.getName()+":") && enc.getText().startsWith("地上")) ||\r
+                                                       ((code.startsWith(BroadcastType.BS.getName()+":")||code.startsWith(BroadcastType.CS.getName()+":")) && enc.getText().startsWith("BS")) ||\r
+                                                       (code.startsWith(BroadcastType.CAPTURE.getName()+":") && enc.getText().startsWith("キャプチャ"))) {\r
+                                               encs.add(enc.getText());\r
+                                       }\r
+                               }\r
+                       }\r
+                       if ( encs.size() > 0 ) {\r
+                               return encs;\r
+                       }\r
+               }\r
+\r
+               // エンコーダーに地上波・BS/CSの区別のないとき、フィルタ結果が0件のとき\r
+               \r
+               for ( TextValueSet enc : recorder.getEncoderList() ) {\r
+                       encs.add(enc.getText());\r
+               }\r
+               \r
+               return encs;\r
+       }\r
+       \r
+       \r
+       /**\r
+        * 当初はRD専用だったのですが\r
+        */\r
+       \r
+       private boolean isRD( String myrecid ) {\r
+               return ( myrecid.startsWith("RD-") || myrecid.startsWith("VARDIA RD-") || myrecid.startsWith("REGZA RD-") || myrecid.startsWith("REGZA DBR-Z") ) ;\r
+       }\r
+       \r
+       private boolean isVARDIA( String myrecid ) {\r
+               return ( myrecid.startsWith("VARDIA RD-") || myrecid.startsWith("REGZA RD-") ) ;\r
+       }\r
+       \r
+       \r
+       /*******************************************************************************\r
+        * ここから下は古いコード\r
+        ******************************************************************************/\r
+\r
+       \r
+       \r
+       \r
+       \r
+       /*******************************************************************************\r
+        * エンコーダコンボボックスのリフレッシュ\r
+        ******************************************************************************/\r
+       \r
+       /**\r
+        * コンボボックスの内容をリフレッシュする(日付専用)\r
+        */\r
+       private void refreshComboBox_date(int updown) {\r
+               GregorianCalendar cal = CommonUtils.getCalendar((String) jComboBox_date.getItemAt(0));\r
+               if ( cal == null ) {\r
+                       return;\r
+               }\r
+               cal.add(Calendar.DAY_OF_MONTH, updown);\r
+               String date = CommonUtils.getDate(cal);\r
+               String ptrn = String.format("毎%s曜日",date.substring(11,12));\r
+               Integer idx = jComboBox_date.getSelectedIndex();\r
+               jComboBox_date.removeItemAt(0);\r
+               jComboBox_date.insertItemAt(date, 0);\r
+               jComboBox_date.removeItemAt(1);\r
+               jComboBox_date.insertItemAt(ptrn, 1);\r
+               jComboBox_date.setSelectedIndex(idx);\r
+       }\r
+\r
+       /*\r
+        * 項目連動のためのメソッド群\r
+        */\r
+\r
+       /**\r
+        * エンコーダに連動して画質を変える(RD系)\r
+        */\r
+       private void setVrateComboBoxByEncoder(String vrate1, String vrate2) {\r
+               int index = -1;\r
+               for (int i=0; i<jCBXPanel_videorate.getItemCount(); i++) {\r
+                       if (vrate1 != null && ((String)jCBXPanel_videorate.getItemAt(i)).startsWith(vrate1)) {\r
+                               index = i;\r
+                               break;\r
+                       }\r
+                       if (vrate2 != null && ((String)jCBXPanel_videorate.getItemAt(i)).startsWith(vrate2)) {\r
+                               index = i;\r
+                               break;\r
+                       }\r
+               }\r
+               if (index >= 0) {\r
+                       jCBXPanel_videorate.setSelectedIndex(index);\r
+               }\r
+       }\r
+       \r
+       /**\r
+        * 画質に連動してエンコーダを変える(RD系)\r
+        */\r
+       private void setEncoderComboBoxByVrate(String enc1, String enc2) {\r
+               int index = -1;\r
+               for (int i=0; i<jComboBox_encoder.getItemCount(); i++) {\r
+                       if (enc1 != null && jComboBox_encoder.getItemAt(i).equals(enc1)) {\r
+                               index = i;\r
+                               break;\r
+                       }\r
+                       if (enc2 != null && jComboBox_encoder.getItemAt(i).equals(enc2)) {\r
+                               index = i;\r
+                               break;\r
+                       }\r
+               }\r
+               if (index >= 0) {\r
+                       jComboBox_encoder.setSelectedIndex(index);\r
+               }\r
+       }\r
+       \r
+       /**\r
+        * 自分のかツールバーのかわかりにくいので名前にMyって付け足した\r
+        */\r
+       private HDDRecorder getMySelectedRecorder() {\r
+               String myself = (String) jComboBox_recorder.getSelectedItem();\r
+               if ( myself == null ) {\r
+                       return null;\r
+               }\r
+               HDDRecorderList recs = recorders.getMyself(myself);\r
+               if ( recs.size() == 0 ) {\r
+                       return null;\r
+               }\r
+               return recs.get(0);\r
+       }\r
+       \r
+       private ProgGenre getMySelectedGenre() {\r
+               String mygenre = (String) jCBXPanel_genre.getSelectedItem();\r
+               if ( mygenre == null ) {\r
+                       return null;\r
+               }\r
+               return ProgGenre.get(mygenre);\r
+       }\r
+       \r
+       /*******************************************************************************\r
+        * リスナー\r
+        ******************************************************************************/\r
+       \r
+       /**\r
+        * ダイアログを開いたときは\r
+        */\r
+       private final WindowListener wl_opened = new WindowAdapter() {\r
+               @Override\r
+               public void windowClosing(WindowEvent e) {\r
+                       \r
+                       if (debug) System.out.println(DBGID+"wl_opened/windowClosing "+((vals!=null)?(vals.toString()):("")));\r
+                       \r
+                       resetWhenWindowClosed();\r
+                       \r
+                       ((AbsReserveDialog) e.getSource()).dispose();\r
+               }\r
+               \r
+               @Override\r
+               public void windowOpened(WindowEvent e) {\r
+                       if (debug) System.out.println(DBGID+"wl_opened/windowOpened");\r
+                       // 開いたときは、タイトル入力エリアにフォーカスを移します\r
+                       jComboBox_title.requestFocusInWindow();\r
+               }\r
+       };\r
+       \r
+       private void resetWhenWindowClosed() {\r
+               \r
+               if (debug) System.out.println(DBGID+"resetWhenWindowClosed "+((vals!=null)?(vals.toString()):("")));\r
+               \r
+               if (vals == null) return;\r
+               \r
+               // リセット\r
+               vals = null;\r
+               \r
+               // リスナー停止\r
+               setEnabledSelectionListeners(false);\r
+               \r
+               // アイテム削除\r
+               removeAllSelectionItems();\r
+               \r
+               // クラス内のコンポーネントを全部setEnabeld(true)にする。個別に変更するのは面倒なので…\r
+               CommonSwingUtils.setEnabledAllComponents(this, AbsReserveDialog.class, true);\r
+       }\r
+       \r
+       /*\r
+        * 項目連動のためのリスナー群\r
+        */\r
+       \r
+       /**\r
+        *  レコーダー/ジャンル/類似予約の変更にAV設定他が連動\r
+        */\r
+       private final ItemListener il_recorderChanged = new ItemListener() {\r
+               @Override\r
+               public void itemStateChanged(ItemEvent e) {\r
+                       selectionChangedComm(e.getStateChange(), ChangedSelector.RECORDER);\r
+               }\r
+       };\r
+       \r
+       private final ItemListener il_genreChanged = new ItemListener() {\r
+               @Override\r
+               public void itemStateChanged(ItemEvent e) {\r
+                       selectionChangedComm(e.getStateChange(), ChangedSelector.GENRE);\r
+               }\r
+       };\r
+\r
+       private final MouseListener ml_likelistSelected = new MouseAdapter() {\r
+               @Override\r
+               public void mouseClicked(MouseEvent e) {\r
+                       if ( ! likersvtable.isEnabled() ) {\r
+                               return;\r
+                       }\r
+                       if (SwingUtilities.isLeftMouseButton(e)) {\r
+                               selectionChangedComm(ItemEvent.SELECTED, ChangedSelector.LIKELIST);\r
+                       }\r
+               }\r
+       };\r
+       \r
+       private void selectionChangedComm(int stateChange, ChangedSelector changed) {\r
+               \r
+               if ( stateChange != ItemEvent.SELECTED ) {\r
+                       return;\r
+               }\r
+               \r
+               // 選択中の放送局\r
+               String mychname = (String) jComboBox_ch.getSelectedItem();\r
+               \r
+               HDDRecorder myrec = null;\r
+               String myenc = null;\r
+               ReserveList myrsv = null;\r
+               AVs myavs = null;\r
+               \r
+               if ( changed == ChangedSelector.LIKELIST ) {\r
+                       // 類似予約の選択の場合\r
+                       \r
+                       int row = likersvtable.getSelectedRow();\r
+                       \r
+                       if ( row != LIKERSVTABLE_NOTSELECTED ) {\r
+                               LikeReserveItem ll = (LikeReserveItem) vals.likeRsvList.get(row);\r
+                               vals.selectedLikeRsv = ll;\r
+                               // 選択中のレコーダ\r
+                               myrec = ll.getRec();\r
+                               // 選択中のエンコーダ\r
+                               myenc = ll.getRsv().getTuner();\r
+                               // 類似予約の選択\r
+                               myrsv = ll.getRsv();\r
+                               // ジャンル別AV設定\r
+                               myavs = null;\r
+                               MWin.appendMessage(MSGID+"画質・音質は類似予約の設定が継承されます");\r
+                               \r
+                               if ( ContentIdEDCB.isValid(myrsv.getContentId()) ) {\r
+                                       // 類似予約の番組IDを利用する\r
+                                       vals.hide_content_id = myrsv.getContentId();\r
+                                       setGetEventIdButton(vals.hide_content_id,false);\r
+                               }\r
+                               else {\r
+                                       // 番組表の番組IDを利用する\r
+                                       vals.hide_content_id = null;\r
+                                       setGetEventIdButton(vals.hide_content_id,true);\r
+                               }\r
+                       }\r
+                       else {\r
+                               myrec = vals.hide_default_recorder;\r
+                               myenc = null;\r
+                               myrsv = null;\r
+                               myavs = getSelectedAVs(vals.hide_tvd.genre, mychname, myrec.getRecorderId());\r
+                               \r
+                               if ( ContentIdEDCB.isValid(vals.hide_tvd.progid) ) {\r
+                                       vals.hide_content_id = vals.hide_tvd.progid;\r
+                                       setGetEventIdButton(vals.hide_content_id,false);\r
+                               }\r
+                       }\r
+                       \r
+                       // 状況に応じて"更新"ボタンの有効無効を変更する\r
+                       setEnabledUpdateButton(row);\r
+               }\r
+               else {\r
+                       // レコーダ、ジャンルの選択の場合\r
+                       \r
+                       // 選択中のレコーダ\r
+                       myrec = getMySelectedRecorder();\r
+                       if ( myrec == null ) {\r
+                               System.err.println(ERRID+"選択したレコーダの情報が登録されていません: "+(String) jComboBox_recorder.getSelectedItem());\r
+                               return;\r
+                       }\r
+               \r
+                       // 選択中のエンコーダ\r
+                       myenc = (String) jComboBox_encoder.getSelectedItem();\r
+                       \r
+                       if ( env.getGivePriorityToReserved() ) {\r
+                               // 類似予約の選択(選択中のレコーダに一致するものがあれば)\r
+                               for ( int i=0; i<vals.likeRsvList.size(); i++ ) {\r
+                                       HDDRecorder rec = vals.likeRsvList.getRec(i);\r
+                                       if ( rec.isMyself(myrec.Myself()) ) {\r
+                                               myrsv = vals.likeRsvList.getRsv(i);\r
+                                               vals.selectedLikeRsv = new LikeReserveItem(rec, myrsv);\r
+                                               break;\r
+                                       }\r
+                               }\r
+                       }\r
+                       else {\r
+                               myrsv = null;\r
+                               vals.selectedLikeRsv = null;\r
+                       }\r
+                       \r
+                       ProgGenre mygenre = getMySelectedGenre();\r
+                       if ( (myrsv != null && mygenre == ProgGenre.get(myrsv.getRec_genre())) && env.getGivePriorityToReserved() ) {\r
+                               // 類似予約からのAV設定継承\r
+                               myavs = null;\r
+                               MWin.appendMessage(MSGID+"画質・音質は類似予約の設定が継承されます");\r
+                       }\r
+                       else {\r
+                               // 一致する類似予約があないか、あってもジャンルが違う(ジャンル別AV設定)\r
+                               myavs = getSelectedAVs(mygenre, mychname, myrec.getRecorderId());\r
+                               // メッセージはgetSelectedAVs()で出力\r
+                       }\r
+               }\r
+               \r
+               // 設定変更\r
+               \r
+               setEnabledSelectionListeners(false);\r
+               \r
+               setInitVariables(myrec);\r
+               \r
+               setSelectedVariables(myrec, myrsv, myavs, mychname, myenc);\r
+               \r
+               setLabels(myrec);\r
+               \r
+               if ( changed != ChangedSelector.LIKELIST ) {\r
+                       // ********\r
+                       //jComboBox_likelist.setSelectedIndex(vals.likeRsvList.indexOf(myrsv));\r
+               }\r
+               \r
+               setEnabledSelectionListeners(true);\r
+       }\r
+       \r
+       /**\r
+        *  エンコーダに画質が連動\r
+        */\r
+       private final ItemListener il_encoderChanged = new ItemListener() {\r
+               @Override\r
+               public void itemStateChanged(ItemEvent e) {\r
+                       if (e.getStateChange() != ItemEvent.SELECTED) {\r
+                               return;\r
+                       }\r
+\r
+                       String myself = (String) jComboBox_recorder.getSelectedItem();\r
+                       if ( myself == null ) {\r
+                               return;\r
+                       }\r
+                       String myrecId = recorders.getMyself(myself).get(0).getRecorderId();\r
+                       if ( ! isVARDIA(myrecId) ) {\r
+                               return;\r
+                       }\r
+                       \r
+                       String encoder = (String) jComboBox_encoder.getSelectedItem();\r
+                       if ( encoder == null ) {\r
+                               return;\r
+                       }\r
+\r
+                       setEnabledSelectionListeners(false);\r
+\r
+                       // TS1/2では画質に[TS]、DR1/2では[DR]を選ぶ\r
+                       if (encoder.startsWith("TS")) {\r
+                               setVrateComboBoxByEncoder("[TS]",null);\r
+                       }\r
+                       else if (encoder.startsWith("DR")) {\r
+                               setVrateComboBoxByEncoder("[DR]",null);\r
+                       }\r
+                       else if (encoder.startsWith("RE")) {\r
+                               setVrateComboBoxByEncoder("[TSE] ","[AVC] ");\r
+                       }\r
+               \r
+                       setEnabledSelectionListeners(true);\r
+               }\r
+       };\r
+       \r
+       /**\r
+        *  画質にエンコーダと音質が連動\r
+        */\r
+       private final ItemListener il_videorateChanged = new ItemListener() {\r
+               @Override\r
+               public void itemStateChanged(ItemEvent e) {\r
+                       if (e.getStateChange() != ItemEvent.SELECTED) {\r
+                               return;\r
+                       }\r
+\r
+                       String myself = (String) jComboBox_recorder.getSelectedItem();\r
+                       if ( myself == null ) {\r
+                               return;\r
+                       }\r
+                       String myrecId = recorders.getMyself(myself).get(0).getRecorderId();\r
+                       if ( ! isVARDIA(myrecId) ) {\r
+                               return;\r
+                       }\r
+                       \r
+                       String vrate = (String)jCBXPanel_videorate.getSelectedItem(); \r
+                       if (vrate == null) {\r
+                               return;\r
+                       }\r
+\r
+                       setEnabledSelectionListeners(false);\r
+                       \r
+                       // レコーダに連動する画質・音質・エンコーダ・フォルダコンボボックスの設定\r
+                       if (vrate.equals("[TS]")) {\r
+                               setEncoderComboBoxByVrate("TS1", null);\r
+                       }\r
+                       else if (vrate.equals("[DR]")) {\r
+                               setEncoderComboBoxByVrate("DR1", null);\r
+                       }\r
+                       else {\r
+                               setEncoderComboBoxByVrate("RE", "VR");\r
+                       }\r
+                       \r
+                       // RDのデジタル系では音質は選択不可\r
+                       if (jCBXPanel_audiorate.getItemCount() > 0) {\r
+                               if (vrate.startsWith("[TS") || vrate.startsWith("[DR]") || vrate.startsWith("[AVC]")) {\r
+                                       jCBXPanel_audiorate.setEnabled(false);\r
+                               }\r
+                               else {\r
+                                       jCBXPanel_audiorate.setEnabled(true);\r
+                               }\r
+                       }\r
+                       \r
+                       setEnabledSelectionListeners(true);\r
+               }\r
+       };\r
+       \r
+       /**\r
+        * 延長警告のON/OFF\r
+        */\r
+       private final ActionListener al_spoexClicked = new ActionListener() {\r
+               public void actionPerformed(ActionEvent e) {\r
+                       if (jCheckBox_spoex_extend.isSelected() == true) {\r
+                               int spoexLength = Integer.valueOf(env.getSpoexLength());\r
+                               if (spoexLength>0) {\r
+                                       int zhh = Integer.valueOf(jTextField_zhh.getText());\r
+                                       int zmm = Integer.valueOf(jTextField_zmm.getText());\r
+                                       \r
+                                       int z = zhh*60+zmm+spoexLength;\r
+                                       zmm = z % 60;\r
+                                       zhh = (z-zmm)/60%24;\r
+                                       \r
+                                       jTextField_zhh.setText(String.format("%02d", zhh));\r
+                                       jTextField_zmm.setText(String.format("%02d", zmm));\r
+                               }\r
+                       }\r
+                       else {\r
+                               int spoexLength = Integer.valueOf(env.getSpoexLength());\r
+                               if (spoexLength>0) {\r
+                                       int zhh = Integer.valueOf(jTextField_zhh.getText());\r
+                                       int zmm = Integer.valueOf(jTextField_zmm.getText());\r
+                                       \r
+                                       int z = zhh*60+zmm-spoexLength;\r
+                                       if (z < 0) {\r
+                                               z +=24*60;\r
+                                       }\r
+                                       zmm = z % 60;\r
+                                       zhh = (z-zmm)/60%24;\r
+                                       \r
+                                       jTextField_zhh.setText(String.format("%02d", zhh));\r
+                                       jTextField_zmm.setText(String.format("%02d", zmm));\r
+                               }\r
+                       }\r
+               }\r
+       };\r
+       \r
+       /**\r
+        * 1分短縮のON/OFF\r
+        */\r
+       private final ActionListener al_overlapClipClicked = new ActionListener() {\r
+               public void actionPerformed(ActionEvent e) {\r
+                       if (jCheckBox_OverlapDown2.isSelected() == true) {\r
+                               int zhh = Integer.valueOf(jTextField_zhh.getText());\r
+                               int zmm = Integer.valueOf(jTextField_zmm.getText());\r
+                               \r
+                               int z = zhh*60+zmm-1;\r
+                               if (z < 0) {\r
+                                       z +=24*60;\r
+                               }\r
+                               zmm = z % 60;\r
+                               zhh = (z-zmm)/60%24;\r
+                               \r
+                               jTextField_zhh.setText(String.format("%02d", zhh));\r
+                               jTextField_zmm.setText(String.format("%02d", zmm));\r
+                       }\r
+                       else {\r
+                               int zhh = Integer.valueOf(jTextField_zhh.getText());\r
+                               int zmm = Integer.valueOf(jTextField_zmm.getText());\r
+                               \r
+                               int z = zhh*60+zmm+1;\r
+                               zmm = z % 60;\r
+                               zhh = (z-zmm)/60%24;\r
+                               \r
+                               jTextField_zhh.setText(String.format("%02d", zhh));\r
+                               jTextField_zmm.setText(String.format("%02d", zmm));\r
+                       }\r
+               }\r
+       };\r
+       \r
+       /**\r
+        * ジャンル別AV設定のロード\r
+        */\r
+       private final ActionListener al_loadAction = new ActionListener() {\r
+               public void actionPerformed(ActionEvent e) {\r
+                       ProgGenre key_genre = ProgGenre.get((String) jCBXPanel_genre.getSelectedItem());\r
+                       String key_webChName = (String) jComboBox_ch.getSelectedItem();\r
+                       String recId = recorders.getMyself((String) jComboBox_recorder.getSelectedItem()).get(0).getRecorderId();\r
+                       setSelectedAVItems(recId, null, getSelectedAVs(key_genre, key_webChName, recId));\r
+                       MWin.appendMessage(MSGID+"画質・音質等の設定を取得しました");\r
+               }\r
+       };\r
+       \r
+       /**\r
+        * ジャンル別AV設定のセーブ\r
+        */\r
+       private final ActionListener al_saveAction = new ActionListener() {\r
+               public void actionPerformed(ActionEvent e) {\r
+                       Matcher ma = Pattern.compile("^.+?:.+?:(.+?)$").matcher((String)jComboBox_recorder.getSelectedItem());\r
+                       if (ma.find()) {\r
+                               if (env.getEnableCHAVsetting()) {\r
+                                       String key_webChName = (String) jComboBox_ch.getSelectedItem();\r
+                                       _save_avsettings(ma.group(1),key_webChName);\r
+                               }\r
+                               else {\r
+                                       String key_genre = (String) jCBXPanel_genre.getSelectedItem();\r
+                                       _save_avsettings(ma.group(1),key_genre);\r
+                               }\r
+                       }\r
+               }\r
+       };\r
+       \r
+       /**\r
+        * 既定AV設定のセーブ\r
+        */\r
+       private final ActionListener al_saveDefault = new ActionListener() {\r
+               public void actionPerformed(ActionEvent e) {\r
+                       Matcher ma = Pattern.compile("^.+?:.+?:(.+?)$").matcher((String)jComboBox_recorder.getSelectedItem());\r
+                       if (ma.find()) {\r
+                               _save_avsettings(ma.group(1),null);\r
+                       }\r
+               }\r
+       };\r
+       \r
+       /**\r
+        *  ジャンル別の画質・音質等の設定の保存の本体\r
+        */\r
+       private void _save_avsettings(String key_recorderId, String key_genre) {\r
+               AVSetting xavs = avs;\r
+               if (env.getEnableCHAVsetting()) {\r
+                       xavs = chavs;   // CHをキーに\r
+               }\r
+               \r
+               AVs c = new AVs();\r
+               \r
+               c.setRecorderId(key_recorderId);\r
+               c.setGenre(key_genre);\r
+               \r
+               c.setVideorate((String)jCBXPanel_videorate.getSelectedItem());\r
+               c.setAudiorate((String)jCBXPanel_audiorate.getSelectedItem());\r
+               c.setDVDCompat((String)jCBXPanel_dvdcompat.getSelectedItem());\r
+               c.setDevice((String)jCBXPanel_device.getSelectedItem());\r
+               c.setXChapter((String)jCBXPanel_xChapter.getSelectedItem());\r
+               c.setMsChapter((String)jCBXPanel_msChapter.getSelectedItem());\r
+               c.setMvChapter((String)jCBXPanel_mvChapter.getSelectedItem());\r
+               c.setAspect((String)jCBXPanel_aspect.getSelectedItem());\r
+               c.setBvperf((String)jCBXPanel_bvperf.getSelectedItem());\r
+               c.setLvoice((String)jCBXPanel_lvoice.getSelectedItem());\r
+               c.setAutodel((String)jCBXPanel_autodel.getSelectedItem());\r
+               c.setFolder((String)jCBXPanel_folder.getSelectedItem());\r
+               c.setPursues(ITEM_YES.equals((String) jCBXPanel_pursues.getSelectedItem()));\r
+               \r
+               xavs.add(key_recorderId, key_genre, c);\r
+               xavs.save();\r
+               \r
+               MWin.appendMessage(MSGID+"画質・音質等の設定を保存しました:"+key_recorderId+" & "+((key_genre!=null)?(key_genre):("デフォルト")));\r
+       }\r
+\r
+       /**\r
+        * タイトルが入力されたよ\r
+        */\r
+       private final ItemListener il_titleEntered = new ItemListener() {\r
+               @Override\r
+               public void itemStateChanged(ItemEvent e) {\r
+                       if (e.getStateChange() == ItemEvent.SELECTED) {\r
+                               // キャレットを先頭へ\r
+                               ((JTextField)jComboBox_title.getEditor().getEditorComponent()).setCaretPosition(0);\r
+                       }\r
+               }\r
+       };\r
+       \r
+       /**\r
+        * 番組IDを取得する\r
+        */\r
+       private final ActionListener al_getEventId = new ActionListener() {\r
+               @Override\r
+               public void actionPerformed(ActionEvent e) {\r
+                       \r
+                       StWin.clear();\r
+                       \r
+                       new SwingBackgroundWorker(false) {\r
+                               \r
+                               @Override\r
+                               protected Object doWorks() throws Exception {\r
+                                       TatCount tc = new TatCount();\r
+                                       StWin.appendMessage(MSGID+"番組IDを取得します");\r
+                                       Integer evid = null;\r
+                                       if ( ContentIdDIMORA.isValid(vals.hide_content_id) ) {\r
+                                               ContentIdDIMORA.decodeContentId(vals.hide_content_id);\r
+                                               String chid = ContentIdDIMORA.getChId();\r
+                                               evid = doGetEventIdById(chid, true);\r
+                                       }\r
+                                       else {\r
+                                               evid = doGetEventIdByName((String) jComboBox_ch.getSelectedItem(), true);\r
+                                       }\r
+                                       if ( evid == null ) {\r
+                                               StWin.appendError(ERRID+String.format("番組IDの取得に失敗しました。所要時間: %.2f秒",tc.end()));\r
+                                       }\r
+                                       else {\r
+                                               StWin.appendMessage(MSGID+String.format("番組IDを取得しました。所要時間: %.2f秒",tc.end()));\r
+                                               if ( vals.hide_tvd != null ) {\r
+                                                       vals.hide_tvd.progid = vals.hide_content_id;\r
+                                                       vals.hide_tvd.setContentIdStr();\r
+                                               }\r
+                                               HDDRecorderList recs = recorders.getMyself((String) jComboBox_recorder.getSelectedItem());\r
+                                               if ( recs.size() > 0 ) {\r
+                                                       jCBXPanel_pursues.setEnabled(recs.get(0).isPursuesEditable());\r
+                                               }\r
+                                       }\r
+                                       return null;\r
+                               }\r
+                               \r
+                               @Override\r
+                               protected void doFinally() {\r
+                                       StWin.setVisible(false);\r
+                               }\r
+                       }.execute();\r
+                       \r
+                       CommonSwingUtils.setLocationCenter(AbsReserveDialog.this, (Component) StWin);\r
+                       StWin.setVisible(true);\r
+               }\r
+       };\r
+       \r
+       /**\r
+        * タイトルに日付を追加する\r
+        */\r
+       private ActionListener al_addDate = new ActionListener() {\r
+               @Override\r
+               public void actionPerformed(ActionEvent e) {\r
+                       String date = (String) jComboBox_date.getSelectedItem();\r
+                       if ( ! date.matches("^\\d\\d\\d\\d/\\d\\d/\\d\\d.*$") ) {\r
+                               // 日付指定でなければ\r
+                               return;\r
+                       }\r
+                       date = date.substring(5,10).replaceAll("/","_");\r
+                       \r
+                       String title = (String) jComboBox_title.getSelectedItem();\r
+                       if ( title.matches("^.* \\d\\d_\\d\\d$") ) {\r
+                               // 日付更新\r
+                               title = title.substring(0,title.length()-6);\r
+                       }\r
+                       title = title + " " + date;\r
+                       if ( title.equals((String) jComboBox_title.getSelectedItem()) ) {\r
+                               // 変化なければ\r
+                               return;\r
+                       }\r
+                       \r
+                       int index = jComboBox_title.getSelectedIndex();\r
+                       jComboBox_title.insertItemAt(title,index);\r
+                       jComboBox_title.setSelectedIndex(index);\r
+                       \r
+                       // キャレットを末尾へ\r
+                       ((JTextField)jComboBox_title.getEditor().getEditorComponent()).setCaretPosition(title.length());\r
+               }\r
+       };\r
+\r
+       /**\r
+        * 開始時刻を1分進める\r
+        */\r
+       private final ActionListener al_upAmm = new ActionListener() {\r
+               @Override\r
+               public void actionPerformed(ActionEvent e) {\r
+                       if ( _upmm(jTextField_ahh,jTextField_amm) ) {\r
+                               refreshComboBox_date(+1);\r
+                       }\r
+               }\r
+       };\r
+       \r
+       /**\r
+        * 開始時刻を1分戻す\r
+        */\r
+       private final ActionListener al_downAmm = new ActionListener() {\r
+               @Override\r
+               public void actionPerformed(ActionEvent e) {\r
+                       if ( _downmm(jTextField_ahh,jTextField_amm) ) {\r
+                               refreshComboBox_date(-1);\r
+                       }\r
+               }\r
+       };\r
+       \r
+       /**\r
+        * 終了時刻を1分進める\r
+        */\r
+       private final ActionListener al_upZmm = new ActionListener() {\r
+               @Override\r
+               public void actionPerformed(ActionEvent e) {\r
+                       _upmm(jTextField_zhh,jTextField_zmm);\r
+               }\r
+       };\r
+       \r
+       /**\r
+        * 終了時刻を1分戻す\r
+        */\r
+       private final ActionListener al_downZmm = new ActionListener() {\r
+               @Override\r
+               public void actionPerformed(ActionEvent e) {\r
+                       _downmm(jTextField_zhh,jTextField_zmm);\r
+               }\r
+       };\r
+       \r
+       private boolean _upmm(JTextField hh, JTextField mm) {\r
+               boolean b = false;\r
+               Integer amm = Integer.valueOf(mm.getText()) + 1;\r
+               Integer ahh = amm / 60;\r
+               if (ahh > 0) {\r
+                       ahh += Integer.valueOf(hh.getText());\r
+                       ahh %= 24;\r
+                       hh.setText(String.format("%02d",ahh));\r
+                       // 日付変更線を超えた\r
+                       if (ahh == 0) {\r
+                               b = true;\r
+                       }\r
+               }\r
+               amm = amm % 60;\r
+               mm.setText(String.format("%02d",amm));\r
+               return b;\r
+       }\r
+       \r
+       private boolean _downmm(JTextField hh, JTextField mm) {\r
+               boolean b = false;\r
+               Integer amm = Integer.valueOf(mm.getText()) + 59;\r
+               amm %= 60;\r
+               if (amm == 59) {\r
+                       Integer ahh = Integer.valueOf(hh.getText()) + 23;\r
+                       ahh %= 24;\r
+                       hh.setText(String.format("%02d",ahh));\r
+                       // 日付変更線を超えた\r
+                       if (ahh == 23) {\r
+                               b = true;\r
+                       }\r
+               }\r
+               mm.setText(String.format("%02d",amm));\r
+               return b;\r
+       }\r
+       \r
+       /**\r
+        * 開始終了日時をリセットする\r
+        */\r
+       private MouseAdapter ml_resetStartEnd = new MouseAdapter() {\r
+               @Override\r
+               public void mouseClicked(MouseEvent e) {\r
+                       int n = jComboBox_date.getSelectedIndex();\r
+                       jCheckBox_spoex_extend.setSelected(vals.isExtended);\r
+                       jCheckBox_OverlapDown2.setSelected(vals.isClipped);\r
+                       jComboBox_date.removeItemAt(0);\r
+                       jComboBox_date.insertItemAt(vals.byDateIni, 0);\r
+                       jComboBox_date.removeItemAt(1);\r
+                       jComboBox_date.insertItemAt(vals.byWeeklyIni, 1);\r
+                       jComboBox_date.setSelectedIndex(n);\r
+                       jTextField_ahh.setText(jTextField_Xahh.getText());\r
+                       jTextField_amm.setText(jTextField_Xamm.getText());\r
+                       jTextField_zhh.setText(jTextField_Xzhh.getText());\r
+                       jTextField_zmm.setText(jTextField_Xzmm.getText());\r
+               }\r
+       };\r
+\r
+       /**\r
+        * 登録実行\r
+        */\r
+       private final ActionListener al_doRecord = new ActionListener() {\r
+               @Override\r
+               public void actionPerformed(ActionEvent e) {\r
+                       doRecord();\r
+               }\r
+       };\r
+\r
+       /**\r
+        * 更新実行\r
+        */\r
+       private final ActionListener al_doUpdate = new ActionListener() {\r
+               public void actionPerformed(ActionEvent e) {\r
+                       doUpdate();\r
+               }\r
+       };\r
+       \r
+       /**\r
+        * キャンセル\r
+        */\r
+       private final ActionListener al_doCancel = new ActionListener() {\r
+               @Override\r
+               public void actionPerformed(ActionEvent e) {\r
+                       //setVisible(false);\r
+                       resetWhenWindowClosed();\r
+                       dispose();\r
+               }\r
+       };\r
+       \r
+       /*******************************************************************************\r
+        * コンポーネント\r
+        ******************************************************************************/\r
+       \r
+       private JPanel getJContentPane_rsv() {\r
+               if (jContentPane_rsv == null) {\r
+                       jContentPane_rsv = new JPanel();\r
+                       jContentPane_rsv.setLayout(new SpringLayout());\r
+                       \r
+                       int y = 0;\r
+                       CommonSwingUtils.putComponentOn(jContentPane_rsv, getJLabel_title("予約名"), 40, PARTS_HEIGHT, 5, y);\r
+                       CommonSwingUtils.putComponentOn(jContentPane_rsv, getJButton_getEventId("番組ID取得"), LABEL_WIDTH, PARTS_HEIGHT, 55, y);\r
+                       \r
+                       CommonSwingUtils.putComponentOn(jContentPane_rsv, getJButton_addDate("日付追加"), 100, PARTS_HEIGHT, 270, y);\r
+                       CommonSwingUtils.putComponentOn(jContentPane_rsv, getJLabel_ch("CH"), LABEL_WIDTH, PARTS_HEIGHT, 335+60, y);\r
+                       \r
+                       int spHeight2 = y;\r
+                       \r
+                       y += PARTS_HEIGHT;\r
+                       int x = 10;\r
+                       CommonSwingUtils.putComponentOn(jContentPane_rsv, getJComboBox_title(), TITLE_WIDTH, PARTS_HEIGHT, x, y);\r
+                       CommonSwingUtils.putComponentOn(jContentPane_rsv, getJComboBox_ch(), CHNAME_WIDTH, PARTS_HEIGHT, x+=TITLE_WIDTH+SEP_WIDTH, y);\r
+\r
+                       y += PARTS_HEIGHT;\r
+                       x = 35;\r
+                       CommonSwingUtils.putComponentOn(jContentPane_rsv, getJLabel_recorders("レコーダ"), LABEL_WIDTH, PARTS_HEIGHT, x, y);\r
+                       CommonSwingUtils.putComponentOn(jContentPane_rsv, getJLabel_encoder("エンコーダ"), LABEL_WIDTH, PARTS_HEIGHT, x+=RECORDER_WIDTH, y);\r
+                       \r
+                       y += PARTS_HEIGHT;\r
+                       x = 35+SEP_WIDTH_NARROW;\r
+                       CommonSwingUtils.putComponentOn(jContentPane_rsv, getJComboBox_recorder(), RECORDER_WIDTH, PARTS_HEIGHT, x, y);\r
+                       CommonSwingUtils.putComponentOn(jContentPane_rsv, getJComboBox_encoder(), ENCODER_WIDTH, PARTS_HEIGHT, x+=RECORDER_WIDTH+SEP_WIDTH, y);\r
+                       CommonSwingUtils.putComponentOn(jContentPane_rsv, getJLabel_encoderemptywarn(""),LABEL_WIDTH,PARTS_HEIGHT,x+=ENCODER_WIDTH+SEP_WIDTH,y);\r
+\r
+                       y += PARTS_HEIGHT;\r
+                       x = 35;\r
+                       CommonSwingUtils.putComponentOn(jContentPane_rsv, getJLabel_date("録画日付"), LABEL_WIDTH, PARTS_HEIGHT, x, y);\r
+                       \r
+                       int hmx = 210;\r
+                       CommonSwingUtils.putComponentOn(jContentPane_rsv, getJLabel_ahh("開始時刻"), 75, PARTS_HEIGHT, hmx-SEP_WIDTH_NARROW, y);\r
+                       CommonSwingUtils.putComponentOn(jContentPane_rsv, getJLabel_zhh("終了時刻"), 75, PARTS_HEIGHT, hmx+120-SEP_WIDTH_NARROW, y);\r
+               \r
+                       y += PARTS_HEIGHT;\r
+                       x = 35+SEP_WIDTH_NARROW;\r
+                       CommonSwingUtils.putComponentOn(jContentPane_rsv, getJComboBox_date(), LABEL_WIDTH, PARTS_HEIGHT, x, y);\r
+                       \r
+                       hmx = 210;\r
+                       CommonSwingUtils.putComponentOn(jContentPane_rsv, getJTextField_ahh(), 40, PARTS_HEIGHT, hmx, y);\r
+                       CommonSwingUtils.putComponentOn(jContentPane_rsv, getJLabel_asep(":"), 10, PARTS_HEIGHT, hmx+=40, y);\r
+                       CommonSwingUtils.putComponentOn(jContentPane_rsv, getJTextField_amm(), 40, PARTS_HEIGHT, hmx+=10, y);\r
+                       CommonSwingUtils.putComponentOn(jContentPane_rsv, getJButton_amm_up(), 20, 12, hmx+=42, y);\r
+                       CommonSwingUtils.putComponentOn(jContentPane_rsv, getJButton_amm_down(), 20, 12, hmx, y+13);\r
+                       \r
+                       CommonSwingUtils.putComponentOn(jContentPane_rsv, getJTextField_zhh(), 40, PARTS_HEIGHT, hmx+=28, y);\r
+                       CommonSwingUtils.putComponentOn(jContentPane_rsv, getJLabel_zsep(":"), 10, PARTS_HEIGHT, hmx+=40, y);\r
+                       CommonSwingUtils.putComponentOn(jContentPane_rsv, getJTextField_zmm(), 40, PARTS_HEIGHT, hmx+=10, y);\r
+                       CommonSwingUtils.putComponentOn(jContentPane_rsv, getJButton_zmm_up(), 20, 12, hmx+=42, y);\r
+                       CommonSwingUtils.putComponentOn(jContentPane_rsv, getJButton_zmm_down(), 20, 12, hmx, y+13);\r
+\r
+                       int exy = y-12;\r
+                       CommonSwingUtils.putComponentOn(jContentPane_rsv, getJCheckBox_spoex_extend("スポーツ延長",LABEL_WIDTH,true), 200, PARTS_HEIGHT, hmx+=28, exy);\r
+                       CommonSwingUtils.putComponentOn(jContentPane_rsv, getJCheckBox_OverlapDown2("終了時刻1分短縮",LABEL_WIDTH,true), 200, PARTS_HEIGHT, hmx, exy+PARTS_HEIGHT);\r
+\r
+                       y += PARTS_HEIGHT+2;\r
+                       hmx = 210;\r
+                       CommonSwingUtils.putComponentOn(jContentPane_rsv, getJTextField_Xahh(), 40, 21, hmx, y+2);\r
+                       CommonSwingUtils.putComponentOn(jContentPane_rsv, getJLabel_Xasep(":"), 10, 21, hmx+=40, y+2);\r
+                       CommonSwingUtils.putComponentOn(jContentPane_rsv, getJTextField_Xamm(), 40, 21, hmx+=10, y+2);\r
+                       CommonSwingUtils.putComponentOn(jContentPane_rsv, getJTextField_Xzhh(), 40, 21, hmx+=70, y+2);\r
+                       CommonSwingUtils.putComponentOn(jContentPane_rsv, getJLabel_Xzsep(":"), 10, 21, hmx+=40, y+2);\r
+                       CommonSwingUtils.putComponentOn(jContentPane_rsv, getJTextField_Xzmm(), 40, 21, hmx+=10, y+2);\r
+                       CommonSwingUtils.putComponentOn(jContentPane_rsv, getJButton_Xreset(""), 20, 15, hmx+=42, y+5);\r
+                       \r
+                       CommonSwingUtils.putComponentOn(jContentPane_rsv, getJLabel_detail("番組詳細"), 100, PARTS_HEIGHT, 35, y);\r
+                       \r
+                       y += PARTS_HEIGHT;\r
+                       CommonSwingUtils.putComponentOn(jContentPane_rsv, getJScrollPane_detail(), 600, 95, 40, y);\r
+                       \r
+                       CommonSwingUtils.putComponentOn(jContentPane_rsv, jCheckBox_Exec = new JCheckBoxPanel("予約実行",LABEL_WIDTH,true), 200, PARTS_HEIGHT, 650, y);\r
+                       jCheckBox_Exec.setSelected(true);\r
+                       CommonSwingUtils.putComponentOn(jContentPane_rsv, jCheckBox_Autocomplete = new JCheckBoxPanel("タイトル自動補完",LABEL_WIDTH,true), 200, PARTS_HEIGHT, 650, y+PARTS_HEIGHT);\r
+                       jCheckBox_Autocomplete.setSelected(env.getUseAutocomplete());\r
+                       //CommonSwingUtils.putComponentOn(jContentPane_rsv, jCheckBox_Pursues = new JCheckBoxPanel("番組追従",LABEL_WIDTH,true), 200, PARTS_HEIGHT, 650, y+50);\r
+                       //jCheckBox_Pursues.setSelected(false);\r
+                       //jCheckBox_Pursues.setEnabled(false);\r
+                       \r
+                       y += 95;\r
+                       int BoxTop = y+5;\r
+                       \r
+                       y += 10;\r
+                       CommonSwingUtils.putComponentOn(jContentPane_rsv, jCBXPanel_genre = new JComboBoxPanel("ジャンル",110,150), COMBO_WIDTH_WIDE, COMBO_HEIGHT, 25, y);\r
+                       CommonSwingUtils.putComponentOn(jContentPane_rsv, jCBXPanel_subgenre = new JComboBoxPanel("サブジャンル",110,150), COMBO_WIDTH_WIDE, COMBO_HEIGHT, 190, y);\r
+                       CommonSwingUtils.putComponentOn(jContentPane_rsv, jCBXPanel_autodel = new JComboBoxPanel("自動削除",110,100), COMBO_WIDTH, COMBO_HEIGHT, 420, y);\r
+                       CommonSwingUtils.putComponentOn(jContentPane_rsv, jCBXPanel_xChapter = new JComboBoxPanel("無音部分チャプタ分割",110,100), COMBO_WIDTH, COMBO_HEIGHT, 535, y);\r
+                       \r
+                       y += PARTS_HEIGHT+PARTS_HEIGHT;\r
+                       CommonSwingUtils.putComponentOn(jContentPane_rsv, jCBXPanel_videorate = new JComboBoxPanel("画質",110,150), COMBO_WIDTH_WIDE, COMBO_HEIGHT, 25, y);\r
+                       CommonSwingUtils.putComponentOn(jContentPane_rsv, jCBXPanel_audiorate = new JComboBoxPanel("音質",110,100), COMBO_WIDTH, COMBO_HEIGHT, 190, y);\r
+                       CommonSwingUtils.putComponentOn(jContentPane_rsv, jCBXPanel_bvperf = new JComboBoxPanel("高レート節約",110,100), COMBO_WIDTH, COMBO_HEIGHT, 305, y);\r
+                       CommonSwingUtils.putComponentOn(jContentPane_rsv, jCBXPanel_lvoice = new JComboBoxPanel("ライン音声選択",110,100), COMBO_WIDTH, COMBO_HEIGHT, 420, y);\r
+                       CommonSwingUtils.putComponentOn(jContentPane_rsv, jCBXPanel_msChapter = new JComboBoxPanel("DVD/シーンチャプタ分割",110,100), COMBO_WIDTH, COMBO_HEIGHT, 535, y);\r
+                       \r
+                       int spHeight1 = y;\r
+                       \r
+                       y += PARTS_HEIGHT+PARTS_HEIGHT;\r
+                       CommonSwingUtils.putComponentOn(jContentPane_rsv, jCBXPanel_folder = new JComboBoxPanel("記録先フォルダ",100,150), COMBO_WIDTH_WIDE, COMBO_HEIGHT, 25, y);\r
+                       CommonSwingUtils.putComponentOn(jContentPane_rsv, jCBXPanel_device = new JComboBoxPanel("記録先デバイス",110,100), COMBO_WIDTH, COMBO_HEIGHT, 190, y);\r
+                       CommonSwingUtils.putComponentOn(jContentPane_rsv, jCBXPanel_dvdcompat = new JComboBoxPanel("BD/DVD互換モード",110,100), COMBO_WIDTH, COMBO_HEIGHT, 305, y);\r
+                       CommonSwingUtils.putComponentOn(jContentPane_rsv, jCBXPanel_aspect = new JComboBoxPanel("DVD記録時画面比",110,100), COMBO_WIDTH, COMBO_HEIGHT, 420, y);\r
+                       CommonSwingUtils.putComponentOn(jContentPane_rsv, jCBXPanel_mvChapter = new JComboBoxPanel("音多/本編チャプタ分割",110,100), COMBO_WIDTH, COMBO_HEIGHT, 535, y);\r
+\r
+                       y += PARTS_HEIGHT+PARTS_HEIGHT;\r
+                       CommonSwingUtils.putComponentOn(jContentPane_rsv, jCBXPanel_pursues = new JComboBoxPanel("番組追従",110,100), COMBO_WIDTH, COMBO_HEIGHT, 535, y);\r
+\r
+                       y += PARTS_HEIGHT+PARTS_HEIGHT;\r
+\r
+                       // 録画設定を囲む枠線\r
+                       JLabel jl = null;\r
+                       int BoxBottom = y+SEP_HEIGHT;\r
+                       CommonSwingUtils.putComponentOn(jContentPane_rsv, jl = new JLabel(), BOX_WIDTH, BoxBottom-BoxTop, 15, BoxTop);\r
+                       jl.setBorder(new LineBorder(Color.GRAY,1));\r
+\r
+                       y = BoxBottom+SEP_HEIGHT_NALLOW;\r
+                       CommonSwingUtils.putComponentOn(jContentPane_rsv, getLikeRsvPane(), LIKELIST_WIDTH, PARTS_HEIGHT*LIKELIST_ROWS, 15, y);\r
+                       \r
+                       y += PARTS_HEIGHT*LIKELIST_ROWS+SEP_HEIGHT;\r
+                       Dimension d = new Dimension(PANEL_WIDTH,y);\r
+                       jContentPane_rsv.setPreferredSize(d);\r
+\r
+                       // 特殊配置(1)\r
+                       y = spHeight1+8;\r
+                       if (env.getEnableCHAVsetting()) {\r
+                               CommonSwingUtils.putComponentOn(jContentPane_rsv, new JLabel("放送局別"), LABEL_WIDTH, PARTS_HEIGHT, 655, y);\r
+                       }\r
+                       else {\r
+                               CommonSwingUtils.putComponentOn(jContentPane_rsv, new JLabel("ジャンル別"), LABEL_WIDTH, PARTS_HEIGHT, 655, y);\r
+                       }\r
+                       y+=17;\r
+                       CommonSwingUtils.putComponentOn(jContentPane_rsv, getJLabel_rectype("録画設定の選択"), LABEL_WIDTH, PARTS_HEIGHT, 655, y);\r
+                       y += PARTS_HEIGHT;\r
+                       CommonSwingUtils.putComponentOn(jContentPane_rsv, getJButton_load("開く"), 75, PARTS_HEIGHT, 660, y);\r
+                       y += 30;\r
+                       CommonSwingUtils.putComponentOn(jContentPane_rsv, getJButton_save("保存"), 75, PARTS_HEIGHT, 660, y);\r
+                       y += 40;\r
+                       CommonSwingUtils.putComponentOn(jContentPane_rsv, getJButton_savedefault("既定化"), 75, PARTS_HEIGHT, 660, y);\r
+                       \r
+                       // 特殊配置(2)\r
+                       y = spHeight2+20;\r
+                       CommonSwingUtils.putComponentOn(jContentPane_rsv, new JLabel("予約"), LABEL_WIDTH, PARTS_HEIGHT, 655, y);\r
+                       y += PARTS_HEIGHT;\r
+                       CommonSwingUtils.putComponentOn(jContentPane_rsv, getJButton_record("新規"), 75, PARTS_HEIGHT, 660, y);\r
+                       y += 30;\r
+                       CommonSwingUtils.putComponentOn(jContentPane_rsv, getJButton_update("更新"), 75, PARTS_HEIGHT, 660, y);\r
+                       y += 30;\r
+                       CommonSwingUtils.putComponentOn(jContentPane_rsv, getJButton_cancel("キャンセル"), 75, PARTS_HEIGHT, 660, y);\r
+               }\r
+               return jContentPane_rsv;\r
+       }\r
+       \r
+       private JLabel getJLabel_date(String s)\r
+       {\r
+               if (jLabel_date == null) {\r
+                       jLabel_date = new JLabel();\r
+                       jLabel_date.setText(s);\r
+               }\r
+               return(jLabel_date);\r
+       }\r
+       \r
+       private JComboBox getJComboBox_date() {\r
+               if (jComboBox_date == null) {\r
+                       jComboBox_date = new JComboBox();\r
+               }\r
+               return jComboBox_date;\r
+       }\r
+       \r
+       private JLabel getJLabel_detail(String s)\r
+       {\r
+               if (jLabel_detail == null) {\r
+                       jLabel_detail = new JLabel();\r
+                       jLabel_detail.setText(s);\r
+               }\r
+               return(jLabel_detail);\r
+       }\r
+\r
+       private JCheckBoxPanel getJCheckBox_OverlapDown2(String s, int labelWidth, boolean rev) {\r
+               if (jCheckBox_OverlapDown2 == null) {\r
+                       jCheckBox_OverlapDown2 = new JCheckBoxPanel(s,labelWidth,rev);\r
+\r
+                       jCheckBox_OverlapDown2.addActionListener(al_overlapClipClicked);\r
+               }\r
+               return(jCheckBox_OverlapDown2);\r
+       }\r
+       \r
+       private JCheckBoxPanel getJCheckBox_spoex_extend(String s, int labelWidth, boolean rev) {\r
+               \r
+               if (jCheckBox_spoex_extend == null) {\r
+                       jCheckBox_spoex_extend = new JCheckBoxPanel(s,labelWidth,rev);\r
+\r
+                       jCheckBox_spoex_extend.addActionListener(al_spoexClicked);\r
+               }\r
+               return(jCheckBox_spoex_extend);\r
+       }\r
+       \r
+       private JScrollPane getJScrollPane_detail()\r
+       {\r
+               if (jScrollPane_detail == null) {\r
+                       jScrollPane_detail = new JScrollPane(getJTextArea_detail(),JScrollPane.VERTICAL_SCROLLBAR_AS_NEEDED,JScrollPane.HORIZONTAL_SCROLLBAR_NEVER);\r
+                       jScrollPane_detail.setBorder(new LineBorder(Color.BLACK));\r
+               }\r
+               return(jScrollPane_detail);\r
+       }\r
+       private JTextArea getJTextArea_detail()\r
+       {\r
+               if (jTextArea_detail == null) {\r
+                       jTextArea_detail = new JTextAreaWithPopup();\r
+                       jTextArea_detail.setLineWrap(true);\r
+               }\r
+               return(jTextArea_detail);\r
+       }\r
+       \r
+       private JLabel getJLabel_rectype(String s)\r
+       {\r
+               if (jLabel_rectype == null) {\r
+                       jLabel_rectype = new JLabel();\r
+                       jLabel_rectype.setText(s);\r
+               }\r
+               return(jLabel_rectype);\r
+       }\r
+       \r
+       private JButton getJButton_load(String s) {\r
+               if (jButton_load == null) {\r
+                       jButton_load = new JButton(s);\r
+                       jButton_load.addActionListener(al_loadAction);\r
+               }\r
+               return jButton_load;\r
+       }\r
+       \r
+       private JButton getJButton_save(String s) {\r
+               if (jButton_save == null) {\r
+                       jButton_save = new JButton(s);\r
+                       jButton_save.addActionListener(al_saveAction);\r
+               }\r
+               return jButton_save;\r
+       }\r
+       // デフォルトの画質・音質等の設定の保存\r
+       private JButton getJButton_savedefault(String s) {\r
+               if (jButton_savedefault == null) {\r
+                       jButton_savedefault = new JButton(s);\r
+                       jButton_savedefault.setForeground(Color.BLUE);\r
+                       jButton_savedefault.addActionListener(al_saveDefault);\r
+                       \r
+                       jButton_savedefault.setToolTipText(TEXT_SAVEDEFAULT);\r
+               }\r
+               return jButton_savedefault;\r
+       }\r
+\r
+       private JLabel getJLabel_title(String s)\r
+       {\r
+               if (jLabel_title == null) {\r
+                       jLabel_title = new JLabel();\r
+                       jLabel_title.setText(s);\r
+               }\r
+               return(jLabel_title);\r
+       }\r
+       \r
+       private JLabel getJLabel_ch(String s)\r
+       {\r
+               if (jLabel_ch== null) {\r
+                       jLabel_ch= new JLabel();\r
+                       jLabel_ch.setText(s);\r
+               }\r
+               return(jLabel_ch);\r
+       }\r
+       \r
+       private JLabel getJLabel_encoder(String s)\r
+       {\r
+               if (jLabel_encoder == null) {\r
+                       jLabel_encoder = new JLabel();\r
+                       jLabel_encoder.setText(s);\r
+               }\r
+               return(jLabel_encoder);\r
+       }\r
+       \r
+       private JLabel getJLabel_encoderemptywarn(String s)\r
+       {\r
+               if (jLabel_encoderemptywarn == null) {\r
+                       jLabel_encoderemptywarn = new JLabel();\r
+                       jLabel_encoderemptywarn.setText(s);\r
+                       jLabel_encoderemptywarn.setForeground(Color.RED);\r
+                       //Font f= jLabel_encoderemptywarn.getFont();\r
+                       //jLabel_encoderemptywarn.setFont(f.deriveFont(f.getStyle() | Font.BOLD));\r
+               }\r
+               return(jLabel_encoderemptywarn);\r
+       }\r
+\r
+       private JLabel getJLabel_recorders(String s)\r
+       {\r
+               if (jLabel_recorders == null) {\r
+                       jLabel_recorders = new JLabel();\r
+                       jLabel_recorders.setText(s);\r
+               }\r
+               return(jLabel_recorders);\r
+       }\r
+       \r
+       private JLabel getJLabel_ahh(String s)\r
+       {\r
+               if (jLabel_ahh == null) {\r
+                       jLabel_ahh = new JLabel();\r
+                       jLabel_ahh.setText(s);\r
+               }\r
+               return(jLabel_ahh);\r
+       }\r
+       \r
+       private JLabel getJLabel_zhh(String s)\r
+       {\r
+               if (jLabel_zhh == null) {\r
+                       jLabel_zhh = new JLabel();\r
+                       jLabel_zhh.setText(s);\r
+               }\r
+               return(jLabel_zhh);\r
+       }\r
+\r
+       private JComboBox getJComboBox_title() {\r
+               if (jComboBox_title == null) {\r
+                       jComboBox_title = new JComboBoxWithPopup();\r
+                       jComboBox_title.addPopupWidth(150);\r
+                       jComboBox_title.setEditable(true);\r
+                       \r
+                       jComboBox_title.addItemListener(il_titleEntered);\r
+               }\r
+               return jComboBox_title;\r
+       }\r
+       \r
+       /**\r
+        * EpgDataCap_BonプラグインのCHコードとを利用してテレビ王国から番組IDを取得します\r
+        * @since 3.14.5β\r
+        */\r
+       private JButton getJButton_getEventId(String s) {\r
+               if (jButton_getEventId == null) {\r
+                       jButton_getEventId = new JButton(s);\r
+                       \r
+                       jButton_getEventId.addActionListener(al_getEventId);\r
+               }\r
+               return jButton_getEventId;\r
+       }\r
+       \r
+       private JButton getJButton_addDate(String s) {\r
+               if (jButton_addDate == null) {\r
+                       jButton_addDate = new JButton(s);\r
+                       \r
+                       jButton_addDate.addActionListener(al_addDate);\r
+               }\r
+               return jButton_addDate;\r
+       }\r
+\r
+       private JComboBox getJComboBox_ch() {\r
+               if (jComboBox_ch== null) {\r
+                       jComboBox_ch = new JComboBox();\r
+               }\r
+               return jComboBox_ch;\r
+       }\r
+       \r
+       private JComboBox getJComboBox_encoder() {\r
+               if (jComboBox_encoder == null) {\r
+                       jComboBox_encoder = new JWideComboBox();\r
+                       jComboBox_encoder.addPopupWidth(100);\r
+               }\r
+               return jComboBox_encoder;\r
+       }\r
+       \r
+       private JComboBox getJComboBox_recorder() {\r
+               if (jComboBox_recorder == null) {\r
+                       jComboBox_recorder = new JComboBox();\r
+               }\r
+               return jComboBox_recorder;\r
+       }\r
+\r
+       // 開始時刻\r
+       private JTextField getJTextField_Xahh() {\r
+               if (jTextField_Xahh == null) {\r
+                       jTextField_Xahh = new JTextField();\r
+                       jTextField_Xahh.setEditable(false);\r
+                       jTextField_Xahh.setForeground(Color.PINK);\r
+                       jTextField_Xahh.setBorder(null);\r
+                       jTextField_Xahh.setHorizontalAlignment(JTextField.CENTER);\r
+               }\r
+               return jTextField_Xahh;\r
+       }\r
+       private JTextField getJTextField_Xamm() {\r
+               if (jTextField_Xamm == null) {\r
+                       jTextField_Xamm = new JTextField();\r
+                       jTextField_Xamm.setEditable(false);\r
+                       jTextField_Xamm.setForeground(Color.PINK);\r
+                       jTextField_Xamm.setBorder(null);\r
+                       jTextField_Xamm.setHorizontalAlignment(JTextField.CENTER);\r
+               }\r
+               return jTextField_Xamm;\r
+       }\r
+       private JLabel getJLabel_Xasep(String s)\r
+       {\r
+               if (jLabel_Xasep == null) {\r
+                       jLabel_Xasep = new JLabel(s);\r
+               }\r
+               return(jLabel_Xasep);\r
+       }\r
+       // 開始時刻\r
+       private JTextField getJTextField_ahh() {\r
+               if (jTextField_ahh == null) {\r
+                       jTextField_ahh = new JTextField();\r
+                       jTextField_ahh.setHorizontalAlignment(JTextField.CENTER);\r
+               }\r
+               return jTextField_ahh;\r
+       }\r
+       private JTextField getJTextField_amm() {\r
+               if (jTextField_amm == null) {\r
+                       jTextField_amm = new JTextField();\r
+                       jTextField_amm.setHorizontalAlignment(JTextField.CENTER);\r
+               }\r
+               return jTextField_amm;\r
+       }\r
+       private JLabel getJLabel_asep(String s)\r
+       {\r
+               if (jLabel_asep == null) {\r
+                       jLabel_asep = new JLabel(s);\r
+               }\r
+               return(jLabel_asep);\r
+       }\r
+       // 開始時刻上げ下げ\r
+       private JButton getJButton_amm_up() {\r
+               if (jButton_amm_up == null) {\r
+                       jButton_amm_up = new JButton();\r
+                       //\r
+                       jButton_amm_up.addActionListener(al_upAmm);\r
+               }\r
+               return jButton_amm_up;\r
+       }\r
+       private JButton getJButton_amm_down() {\r
+               if (jButton_amm_down == null) {\r
+                       jButton_amm_down = new JButton();\r
+                       //\r
+                       jButton_amm_down.addActionListener(al_downAmm);\r
+               }\r
+               return jButton_amm_down;\r
+       }\r
+       \r
+       //\r
+       private JTextField getJTextField_Xzhh() {\r
+               if (jTextField_Xzhh == null) {\r
+                       jTextField_Xzhh = new JTextField();\r
+                       jTextField_Xzhh.setEditable(false);\r
+                       jTextField_Xzhh.setForeground(Color.PINK);\r
+                       jTextField_Xzhh.setBorder(null);\r
+                       jTextField_Xzhh.setHorizontalAlignment(JTextField.CENTER);\r
+               }\r
+               return jTextField_Xzhh;\r
+       }\r
+       private JTextField getJTextField_Xzmm() {\r
+               if (jTextField_Xzmm == null) {\r
+                       jTextField_Xzmm = new JTextField();\r
+                       jTextField_Xzmm.setEditable(false);\r
+                       jTextField_Xzmm.setForeground(Color.PINK);\r
+                       jTextField_Xzmm.setBorder(null);\r
+                       jTextField_Xzmm.setHorizontalAlignment(JTextField.CENTER);\r
+               }\r
+               return jTextField_Xzmm;\r
+       }\r
+       private JLabel getJLabel_Xzsep(String s)\r
+       {\r
+               if (jLabel_Xzsep == null) {\r
+                       jLabel_Xzsep = new JLabel(s);\r
+               }\r
+               return(jLabel_Xzsep);\r
+       }\r
+       //\r
+       private JTextField getJTextField_zhh() {\r
+               if (jTextField_zhh == null) {\r
+                       jTextField_zhh = new JTextField();\r
+                       jTextField_zhh.setHorizontalAlignment(JTextField.CENTER);\r
+               }\r
+               return jTextField_zhh;\r
+       }\r
+       private JTextField getJTextField_zmm() {\r
+               if (jTextField_zmm == null) {\r
+                       jTextField_zmm = new JTextField();\r
+                       jTextField_zmm.setHorizontalAlignment(JTextField.CENTER);\r
+               }\r
+               return jTextField_zmm;\r
+       }\r
+       private JLabel getJLabel_zsep(String s)\r
+       {\r
+               if (jLabel_zsep == null) {\r
+                       jLabel_zsep = new JLabel(s);\r
+               }\r
+               return(jLabel_zsep);\r
+       }\r
+       // 終了時刻上げ下げ\r
+       private JButton getJButton_zmm_up() {\r
+               if (jButton_zmm_up == null) {\r
+                       jButton_zmm_up = new JButton();\r
+                       //\r
+                       jButton_zmm_up.addActionListener(al_upZmm);\r
+               }\r
+               return jButton_zmm_up;\r
+       }\r
+       private JButton getJButton_zmm_down() {\r
+               if (jButton_zmm_down == null) {\r
+                       jButton_zmm_down = new JButton();\r
+                       //\r
+                       jButton_zmm_down.addActionListener(al_downZmm);\r
+               }\r
+               return jButton_zmm_down;\r
+       }\r
+       \r
+       //\r
+       private JButton getJButton_Xreset(String s) {\r
+               if (jButton_Xreset == null) {\r
+                       jButton_Xreset = new JButton(s);\r
+                       //\r
+                       jButton_Xreset.addMouseListener(ml_resetStartEnd);\r
+               }\r
+               return(jButton_Xreset);\r
+       }\r
+       \r
+       private JScrollPane getLikeRsvPane() {\r
+               if (likersvpane == null ) {\r
+                       likersvpane = new JScrollPane(JScrollPane.VERTICAL_SCROLLBAR_ALWAYS, JScrollPane.HORIZONTAL_SCROLLBAR_AS_NEEDED);\r
+                       likersvpane.setRowHeaderView(likersvrowheader = new LikeRsvRowHeader());\r
+                       likersvpane.setViewportView(getLikeRsvTable());\r
+                       \r
+                       Dimension d = new Dimension(LRT_HEADER_WIDTH,0);\r
+                       likersvpane.getRowHeader().setPreferredSize(d);\r
+                       \r
+                       likersvpane.getRowHeader().setVisible(true);\r
+               }\r
+               return likersvpane;\r
+       }\r
+       \r
+       private LikeRsvTable getLikeRsvTable() {\r
+               if (likersvtable == null) {\r
+                       \r
+                       // カラム名の初期化\r
+                       ArrayList<String> cola = new ArrayList<String>();\r
+                       for ( LikeRsvColumn lc : LikeRsvColumn.values() ) {\r
+                               if ( lc.getIniWidth() >= 0 ) {\r
+                                       cola.add(lc.getName());\r
+                               }\r
+                       }\r
+                       final String[] colname = cola.toArray(new String[0]);\r
+                       \r
+                       // テーブルの基本的な設定\r
+                       DefaultTableModel model = new DefaultTableModel(colname, 0);\r
+                       \r
+                       likersvtable = new LikeRsvTable(model);\r
+                       likersvtable.setAutoResizeMode(JNETable.AUTO_RESIZE_OFF);\r
+                       \r
+                       // 各カラムの幅を設定する\r
+                       DefaultTableColumnModel columnModel = (DefaultTableColumnModel)likersvtable.getColumnModel();\r
+                       TableColumn column = null;\r
+                       for ( LikeRsvColumn lc : LikeRsvColumn.values() ) {\r
+                               if ( lc.getIniWidth() < 0 ) {\r
+                                       continue;\r
+                               }\r
+                               column = columnModel.getColumn(lc.ordinal());\r
+                               column.setPreferredWidth(lc.getIniWidth());\r
+                       }\r
+               }\r
+               \r
+               return likersvtable;\r
+       }\r
+       \r
+       private JButton getJButton_record(String s) {\r
+               if (jButton_record == null) {\r
+                       jButton_record = new JButton(s);\r
+                       jButton_record.setForeground(Color.RED);\r
+                       jButton_record.addActionListener(al_doRecord);\r
+               }\r
+               return jButton_record;\r
+       }\r
+       \r
+       private JButton getJButton_update(String s) {\r
+               if (jButton_update == null) {\r
+                       jButton_update = new JButton();\r
+                       jButton_update.setText(s);\r
+\r
+                       jButton_update.addActionListener(al_doUpdate);\r
+               }\r
+               \r
+               return jButton_update;\r
+       }\r
+\r
+       private JButton getJButton_cancel(String s) {\r
+               if (jButton_cancel == null) {\r
+                       jButton_cancel = new JButton(s);\r
+                       jButton_cancel.addActionListener(al_doCancel);\r
+               }\r
+               return jButton_cancel;\r
+       }\r
+       \r
+       \r
+       \r
+       \r
+       /*******************************************************************************\r
+        * 独自部品\r
+        ******************************************************************************/\r
+       \r
+       private class LikeRsvTable extends JNETable {\r
+\r
+               private static final long serialVersionUID = 1L;\r
+\r
+               public LikeRsvTable(DefaultTableModel model) {\r
+                       super(model,true);\r
+               }\r
+               \r
+               @Override\r
+               public Object getValueAt(int row, int column) {\r
+                       if ( row == 0 ) {\r
+                               if ( column == LikeRsvColumn.TITLE.ordinal() ) {\r
+                                       if ( vals.likeRsvList.size() == 0 ) {\r
+                                               return "(類似予約なし)";\r
+                                       }\r
+                                       else {\r
+                                               return "類似予約を選択しない";\r
+                                       }\r
+                               }\r
+                               else if ( column == LikeRsvColumn.START.ordinal() ) {\r
+                                       return "-";\r
+                               }\r
+                               else if ( column == LikeRsvColumn.RECORDER.ordinal() ) {\r
+                                       return vals.hide_default_recorder.Myself();\r
+                               }\r
+                               return null;\r
+                       }\r
+                       if ( column == LikeRsvColumn.TITLE.ordinal() ) {\r
+                               return vals.likeRsvList.get(row-1).getRsv().getTitle();\r
+                       }\r
+                       else if ( column == LikeRsvColumn.START.ordinal() ) {\r
+                               return vals.likeRsvList.get(row-1).getRsv().getStartDateTime();\r
+                       }\r
+                       else if ( column == LikeRsvColumn.RECORDER.ordinal() ) {\r
+                               return vals.likeRsvList.get(row-1).getRec().Myself();\r
+                       }\r
+                       else if ( column == LikeRsvColumn.TUNER.ordinal() ) {\r
+                               return vals.likeRsvList.get(row-1).getRsv().getTuner();\r
+                       }\r
+                       return null;\r
+               }\r
+               \r
+               @Override\r
+               public int getSelectedRow() {\r
+                       return super.getSelectedRow()-1;\r
+               }\r
+               \r
+               @Override\r
+               public void setRowSelectionInterval(int index0, int index1) {\r
+                       super.setRowSelectionInterval(index0+1, index1+1);\r
+               }\r
+               \r
+               @Override\r
+               public int getRowCount() {\r
+                       if ( vals == null || vals.likeRsvList == null ) {\r
+                               return 1;\r
+                       }\r
+                       return vals.likeRsvList.size()+1;\r
+               }\r
+       }\r
+       \r
+       private class LikeRsvRowHeader extends JTable {\r
+               \r
+               private static final long serialVersionUID = 1L;\r
+               \r
+               public LikeRsvRowHeader() {\r
+                       super();\r
+                       \r
+                       String[] colname = {""};\r
+                       DefaultTableModel model = new DefaultTableModel(colname,0);\r
+                       this.setModel(model);\r
+\r
+                       //this.setAutoResizeMode(JTable.AUTO_RESIZE_OFF);\r
+                       //this.setRowSelectionAllowed(false);\r
+                       this.setEnabled(false);\r
+\r
+                       DefaultTableCellRenderer cr = new DefaultTableCellRenderer() {\r
+                               \r
+                               private static final long serialVersionUID = 1L;\r
+                               \r
+                               @Override\r
+                               public Component getTableCellRendererComponent(JTable table, Object value, boolean isSelected, boolean hasFocus, int row, int column) {\r
+                                       Component c = super.getTableCellRendererComponent(table, value, isSelected, hasFocus, row, column);\r
+                                       c.setBackground(table.getTableHeader().getBackground());\r
+                                       return c;\r
+                               };\r
+                       };\r
+                       cr.setHorizontalAlignment(JLabel.CENTER);\r
+                       cr.setOpaque(true);\r
+                       this.getColumnModel().getColumn(0).setCellRenderer(cr);\r
+               }\r
+               \r
+               @Override\r
+               public Object getValueAt(int row, int column) {\r
+                       return (row==0) ? "-" : String.valueOf(row);\r
+               }\r
+               \r
+               @Override\r
+               public int getRowCount() {\r
+                       if ( vals == null || vals.likeRsvList == null ) {\r
+                               return 1;\r
+                       }\r
+                       return vals.likeRsvList.size()+1;\r
+               }\r
+       }\r
+}\r
diff --git a/TinyBannavi/src/tainavi/AbsReserveListView.java b/TinyBannavi/src/tainavi/AbsReserveListView.java
new file mode 100644 (file)
index 0000000..b5b3529
--- /dev/null
@@ -0,0 +1,1008 @@
+package tainavi;\r
+\r
+import java.awt.Color;\r
+import java.awt.Component;\r
+import java.awt.Dimension;\r
+import java.awt.Insets;\r
+import java.awt.Point;\r
+import java.awt.event.ActionEvent;\r
+import java.awt.event.ActionListener;\r
+import java.awt.event.ComponentAdapter;\r
+import java.awt.event.ComponentEvent;\r
+import java.awt.event.MouseAdapter;\r
+import java.awt.event.MouseEvent;\r
+import java.util.ArrayList;\r
+import java.util.Calendar;\r
+import java.util.Comparator;\r
+import java.util.GregorianCalendar;\r
+import java.util.HashMap;\r
+import java.util.Map.Entry;\r
+\r
+import javax.swing.AbstractAction;\r
+import javax.swing.AbstractCellEditor;\r
+import javax.swing.ImageIcon;\r
+import javax.swing.JButton;\r
+import javax.swing.JLabel;\r
+import javax.swing.JMenuItem;\r
+import javax.swing.JPopupMenu;\r
+import javax.swing.JScrollPane;\r
+import javax.swing.JTable;\r
+import javax.swing.event.RowSorterEvent;\r
+import javax.swing.event.TableModelEvent;\r
+import javax.swing.event.RowSorterEvent.Type;\r
+import javax.swing.event.RowSorterListener;\r
+import javax.swing.table.DefaultTableColumnModel;\r
+import javax.swing.table.DefaultTableModel;\r
+import javax.swing.table.TableCellEditor;\r
+import javax.swing.table.TableCellRenderer;\r
+import javax.swing.table.TableColumn;\r
+import javax.swing.table.TableModel;\r
+import javax.swing.table.TableRowSorter;\r
+\r
+\r
+/**\r
+ * 本体予約一覧タブのクラス\r
+ * @since 3.15.4β {@link Viewer}から分離\r
+ */\r
+public abstract class AbsReserveListView extends JScrollPane {\r
+\r
+       private static final long serialVersionUID = 1L;\r
+\r
+       public static void setDebug(boolean b) {debug = b; }\r
+       private static boolean debug = false;\r
+       \r
+\r
+       /*******************************************************************************\r
+        * 抽象メソッド\r
+        ******************************************************************************/\r
+       \r
+       protected abstract Env getEnv();\r
+       protected abstract Bounds getBoundsEnv();\r
+       \r
+       protected abstract HDDRecorderList getRecorderList();\r
+       \r
+       //protected abstract StatusWindow getStWin(); \r
+       //protected abstract StatusTextArea getMWin();\r
+       protected abstract AbsReserveDialog getReserveDialog();\r
+       \r
+       protected abstract Component getParentComponent();\r
+       \r
+       protected abstract void ringBeep();\r
+       \r
+       /**\r
+        * 予約マーク・予約枠を更新してほしい\r
+        */\r
+       protected abstract void updateReserveDisplay(String chname);\r
+\r
+       /**\r
+        * 予約実行を更新してほしい\r
+        */\r
+       protected abstract boolean doExecOnOff(boolean fexec, String title, String chnam, String rsvId, String recId);\r
+\r
+       /**\r
+        *  予約実行をONOFFするメニューアイテム\r
+        */\r
+       protected abstract JMenuItem getExecOnOffMenuItem(final boolean fexec, final String title, final String chnam, final String rsvId, final String recId);\r
+       \r
+       /**\r
+        *  予約を削除するメニューアイテム\r
+        */\r
+       protected abstract JMenuItem getRemoveRsvMenuItem(final String title, final String chnam, final String rsvId, final String recId);\r
+       \r
+       /**\r
+        *  新聞形式へジャンプするメニューアイテム\r
+        */\r
+       protected abstract JMenuItem getJumpMenuItem(final String title, final String chnam, final String startDT);\r
+       protected abstract JMenuItem getJumpToLastWeekMenuItem(final String title, final String chnam, final String startDT);\r
+       \r
+       /**\r
+        * @see Viewer.VWToolBar#getSelectedRecorder()\r
+        */\r
+       protected abstract String getSelectedRecorderOnToolbar();\r
+       \r
+\r
+       /*******************************************************************************\r
+        * 定数\r
+        ******************************************************************************/\r
+       \r
+       private static final String MSGID = "[本体予約一覧] ";\r
+       private static final String ERRID = "[ERROR]"+MSGID;\r
+       private static final String DBGID = "[DEBUG]"+MSGID;\r
+       \r
+       private static final String DUPMARK_NORMAL = "■";\r
+       private static final String DUPMARK_REP = "□";\r
+       private static final String DUPMARK_COLOR = "#FFB6C1";\r
+\r
+       private static final String VALID_CHNAME_COLOR = "#0000ff";\r
+       private static final String INVALID_CHNAME_COLOR = "#ff0000";\r
+\r
+       private static final String ICONFILE_EXEC                       = "icon/media-record-3.png";\r
+       \r
+       /*******************************************************************************\r
+        * 部品\r
+        ******************************************************************************/\r
+       \r
+       // オブジェクト\r
+       private final Env env = getEnv();\r
+       private final Bounds bounds = getBoundsEnv();\r
+       private final HDDRecorderList recorders = getRecorderList();\r
+\r
+       //private final StatusWindow StWin = getStWin();                        // これは起動時に作成されたまま変更されないオブジェクト\r
+       //private final StatusTextArea MWin = getMWin();                        // これは起動時に作成されたまま変更されないオブジェクト\r
+       private final AbsReserveDialog rD = getReserveDialog(); // これは起動時に作成されたまま変更されないオブジェクト\r
+       \r
+       private final Component parent = getParentComponent();  // これは起動時に作成されたまま変更されないオブジェクト\r
+       \r
+       // メソッド\r
+       //private void StdAppendMessage(String message) { System.out.println(message); }\r
+       //private void StdAppendError(String message) { System.err.println(message); }\r
+       //private void StWinSetVisible(boolean b) { StWin.setVisible(b); }\r
+       //private void StWinSetLocationCenter(Component frame) { CommonSwingUtils.setLocationCenter(frame, (VWStatusWindow)StWin); }\r
+\r
+       ImageIcon execicon = new ImageIcon(ICONFILE_EXEC);\r
+                       \r
+       /**\r
+        * カラム定義\r
+        */\r
+       \r
+       public static HashMap<String,Integer> getColumnIniWidthMap() {\r
+               if (rcmap.size() == 0 ) {\r
+                       for ( RsvedColumn rc : RsvedColumn.values() ) {\r
+                               rcmap.put(rc.toString(),rc.getIniWidth());      // toString()!\r
+                       }\r
+               }\r
+               return rcmap;\r
+       }\r
+       \r
+       private static final HashMap<String,Integer> rcmap = new HashMap<String, Integer>();\r
+       \r
+       public static enum RsvedColumn {\r
+               PATTERN         ("パタン",                   110),\r
+               DUPMARK         ("重複",                      35),\r
+               EXEC            ("実行",                      35),\r
+               TRACE           ("追跡",                      35),\r
+               AUTO            ("自動",                      35),\r
+               NEXTSTART       ("次回実行予定",  150),\r
+               END                     ("終了",                      50),\r
+               LENGTH          ("長さ",                      50),\r
+               ENCODER         ("エンコーダ",          50),\r
+               VRATE           ("画質",                      100),\r
+               ARATE           ("音質",                      50),\r
+               TITLE           ("番組タイトル",  300),\r
+               CHNAME          ("チャンネル名",  150),\r
+               RECORDER        ("レコーダ",                200),\r
+               \r
+               /*\r
+               HID_INDEX       ("INDEX",               -1),\r
+               HID_RSVID       ("RSVID",               -1),\r
+               */\r
+               ;\r
+\r
+               private String name;\r
+               private int iniWidth;\r
+\r
+               private RsvedColumn(String name, int iniWidth) {\r
+                       this.name = name;\r
+                       this.iniWidth = iniWidth;\r
+               }\r
+\r
+               @Override\r
+               public String toString() {\r
+                       return name;\r
+               }\r
+\r
+               public int getIniWidth() {\r
+                       return iniWidth;\r
+               }\r
+               \r
+               public int getColumn() {\r
+                       return ordinal();\r
+               }\r
+       };\r
+       \r
+\r
+       /*******************************************************************************\r
+        * コンポーネント\r
+        ******************************************************************************/\r
+\r
+       private class ReservedItem extends RowItem implements Cloneable {\r
+       \r
+               String pattern;\r
+               String dupmark;\r
+               Boolean exec;\r
+               String trace;\r
+               String auto;\r
+               String nextstart;       // YYYY/MM/DD(WD) hh:mm\r
+               String end;                     // hh:mm\r
+               String length;\r
+               String encoder;\r
+               String vrate;\r
+               String arate;\r
+               String title;\r
+               String chname;\r
+               String recorder;\r
+               \r
+               String hide_chname;\r
+               String hide_rsvid;\r
+               String hide_centercolor;\r
+               String hide_encodercolor;\r
+               String hide_itecolor;\r
+               Boolean hide_tunershort;\r
+               Boolean hide_recorded;\r
+               \r
+               @Override\r
+               protected void myrefresh(RowItem o) {\r
+                       ReservedItem c = (ReservedItem) o;\r
+                       \r
+                       c.addData(pattern);\r
+                       c.addData(dupmark);\r
+                       c.addData(exec);\r
+                       c.addData(trace);\r
+                       c.addData(auto);\r
+                       c.addData(nextstart);\r
+                       c.addData(end);\r
+                       c.addData(length);\r
+                       c.addData(encoder);\r
+                       c.addData(vrate);\r
+                       c.addData(arate);\r
+                       c.addData(title);\r
+                       c.addData(chname);\r
+                       c.addData(recorder);\r
+               }\r
+               \r
+               public ReservedItem clone() {\r
+                       return (ReservedItem) super.clone();\r
+               }\r
+       }\r
+\r
+       // ソートが必要な場合はTableModelを作る。ただし、その場合Viewのrowがわからないので行の入れ替えが行えない\r
+       private class ReservedTableModel extends DefaultTableModel {\r
+\r
+               private static final long serialVersionUID = 1L;\r
+               \r
+               @Override\r
+               public Object getValueAt(int row, int column) {\r
+                       ReservedItem c = rowView.get(row); \r
+                       if ( c.getColumnCount() > column ) {\r
+                               if ( column == RsvedColumn.DUPMARK.getColumn() ) {\r
+                                       return CommonSwingUtils.getColoredString(DUPMARK_COLOR,c.dupmark);\r
+                               }\r
+                               else if ( column == RsvedColumn.ENCODER.getColumn() ) {\r
+                                       return CommonSwingUtils.getColoredString(c.hide_encodercolor,c.encoder);\r
+                               }\r
+                               else if ( column == RsvedColumn.TITLE.getColumn() ) {\r
+                                       if ( c.hide_itecolor!=null ) {\r
+                                               return CommonSwingUtils.getColoredString(c.hide_itecolor,c.title);\r
+                                       }\r
+                                       else {\r
+                                               return c.title;\r
+                                       }\r
+                               }\r
+                               else if ( column == RsvedColumn.CHNAME.getColumn() ) {\r
+                                       return CommonSwingUtils.getColoredString(c.hide_centercolor,c.chname);\r
+                               }\r
+                               else if ( column == RsvedColumn.LENGTH.getColumn() ) {\r
+                                       return c.length+"m";\r
+                               }\r
+                               return c.get(column);\r
+                       }\r
+                       return null;\r
+               }\r
+               \r
+               @Override\r
+               public void setValueAt(Object aValue, int row, int column) {\r
+                       /*\r
+                       ReservedItem c = rowView.get(row);\r
+                       if ( column == RsvedColumn.EXEC.getColumn() ) {\r
+                               //c.exec = (Boolean) aValue;\r
+                               //c.fireChanged();\r
+                       }\r
+                       */\r
+               }\r
+               \r
+               @Override\r
+               public int getRowCount() {\r
+                       return rowView.size();\r
+               }\r
+\r
+               public ReservedTableModel(String[] colname, int i) {\r
+                       super(colname,i);\r
+               }\r
+               \r
+       }\r
+       \r
+       //private final ReservedItem sa = new ReservedItem();\r
+       \r
+       private JNETableReserved jTable_rsved = null;\r
+       private JTable jTable_rowheader = null;\r
+\r
+       private DefaultTableModel tableModel_rsved = null;\r
+       \r
+       private DefaultTableModel rowheaderModel_rsved = null;\r
+       \r
+       // 表示用のテーブル\r
+       private final RowItemList<ReservedItem> rowView = new RowItemList<ReservedItem>();\r
+       \r
+       // テーブルの実体\r
+       private final RowItemList<ReservedItem> rowData = new RowItemList<ReservedItem>();\r
+       \r
+       /*******************************************************************************\r
+        * コンストラクタ\r
+        ******************************************************************************/\r
+       \r
+       public AbsReserveListView() {\r
+               \r
+               super();\r
+               \r
+               this.setRowHeaderView(jTable_rowheader = new JTableRowHeader(rowView));\r
+               this.setViewportView(getNETable_rsved());\r
+               \r
+               Dimension d = new Dimension(jTable_rowheader.getPreferredSize().width,0);\r
+               this.getRowHeader().setPreferredSize(d);\r
+               \r
+               this.setRowHeaderVisible(env.getRowHeaderVisible());\r
+               \r
+               // バグ対応\r
+               if ( bounds.getRsvedColumnSize() == null ) {\r
+                       System.err.println(ERRID+"なんらかの不具合によりテーブルのカラム幅設定が取得できませんでした。設定はリセットされました。申し訳ありません。");\r
+                       bounds.setRsvedColumnSize(rcmap);\r
+               }\r
+               else {\r
+                       for ( Entry<String, Integer> en : rcmap.entrySet() ) {\r
+                               try {\r
+                                       bounds.getListedColumnSize().get(en.getKey());\r
+                               }\r
+                               catch (NullPointerException e) {\r
+                                       System.err.println(ERRID+en.getKey()+", "+e.toString());\r
+                                       bounds.getListedColumnSize().put(en.getKey(),en.getValue());\r
+                               }\r
+                       }\r
+               }\r
+               \r
+               //\r
+               this.addComponentListener(cl_tabshown);\r
+       }\r
+       \r
+\r
+       /*******************************************************************************\r
+        * アクション\r
+        ******************************************************************************/\r
+       \r
+       // 対外的な\r
+       \r
+       /**\r
+        * 予約一覧を描画してほしいかなって\r
+        * ★synchronized(rowData)★\r
+        * @see #cl_tabshown\r
+        */\r
+       public void redrawReservedList() {\r
+               // ★★★ イベントにトリガーされた処理がかちあわないように synchronized() ★★★\r
+               synchronized ( rowView ) {\r
+                       _redrawReservedList();\r
+               }\r
+       }\r
+               \r
+       private void _redrawReservedList() {\r
+               \r
+               //\r
+               rowData.clear();\r
+               \r
+               // 選択されたレコーダ\r
+               String myself = getSelectedRecorderOnToolbar();\r
+               HDDRecorderList recs = recorders.getMyself(myself);\r
+\r
+               // 現在日時\r
+               String curDateTime = CommonUtils.getDateTime(0);\r
+               \r
+               // 繰り返し予約関連\r
+               String itecolor = CommonUtils.color2str(env.getIterationItemForeground());\r
+               int maxn = ( env.getShowAllIterationItem() ) ? (env.getDogDays()) : (1);\r
+\r
+               // 背景色\r
+               jTable_rsved.setTunerShortColor(env.getTunerShortColor());\r
+               jTable_rsved.setRecordedColor(env.getRecordedColor());\r
+\r
+               for ( HDDRecorder recorder : recs )\r
+               {\r
+                       if ( recorder.isBackgroundOnly() ) {\r
+                               continue;\r
+                       }\r
+                       \r
+                       // 終了した番組があれば整理\r
+                       recorder.refreshReserves();\r
+                       \r
+                       // 並べ替えるために新しいリストを作成する\r
+                       for ( ReserveList ro : recorder.getReserves() ) {\r
+                               ArrayList<String> starts = new ArrayList<String>();\r
+                               ArrayList<String> ends = new ArrayList<String>();\r
+                               CommonUtils.getStartEndList(starts, ends, ro);\r
+                               for ( int n=0; n<starts.size() && n<maxn; n++ ) {\r
+                                       if ( ! env.getDisplayPassedReserve() ) {\r
+                                               if ( ends.get(n).compareTo(curDateTime) < 0 ) {\r
+                                                       continue;\r
+                                               }\r
+                                       }\r
+                                       \r
+                                       ReservedItem sa = new ReservedItem();\r
+                                       \r
+                                       sa.pattern = ro.getRec_pattern();\r
+                                       sa.dupmark = "";\r
+                                       sa.exec = ro.getExec();\r
+                                       sa.trace = ((ro.getPursues())?("追"):(""));\r
+                                       sa.auto = ((ro.getAutoreserved())?("○"):(""));\r
+                                       sa.nextstart = CommonUtils.getDate(CommonUtils.getCalendar(starts.get(n)))+" "+ro.getAhh()+":"+ro.getAmm();     // YYYY/MM/DD(WD) hh:mm\r
+                                       sa.end = ro.getZhh()+":"+ro.getZmm();                   // hh:mm\r
+                                       sa.length = ro.getRec_min();\r
+                                       sa.encoder = (ro.getTuner() != null)?(ro.getTuner()):("★エンコーダ不正");\r
+                                       sa.vrate = getRec_mode(ro);\r
+                                       sa.arate = ro.getRec_audio();\r
+                                       sa.title = ro.getTitle();\r
+                                       sa.chname = (ro.getCh_name()!=null && ro.getCh_name().length()>0)?(ro.getCh_name()):("★放送局名不正("+ro.getChannel()+")");\r
+                                       sa.recorder = recorder.Myself();\r
+                                       \r
+                                       sa.hide_chname = (ro.getCh_name()!=null)?(ro.getCh_name()):("");\r
+                                       sa.hide_rsvid = ro.getId();\r
+                                       sa.hide_centercolor = (ro.getCh_name()!=null && ro.getCh_name().length()>0)?(VALID_CHNAME_COLOR):(INVALID_CHNAME_COLOR);\r
+                                       sa.hide_encodercolor = recorder.getColor(ro.getTuner());\r
+                                       sa.hide_itecolor = (n>0)?(itecolor):(null);\r
+                                       sa.hide_tunershort = ro.getTunershort();\r
+                                       sa.hide_recorded = ro.getRecorded();\r
+\r
+                                       sa.fireChanged();\r
+                                       \r
+                                       addRow(sa);\r
+                               }\r
+                       }\r
+               }\r
+               \r
+               // 表示用\r
+               rowView.clear();\r
+               for ( ReservedItem a : rowData ) {\r
+                       rowView.add(a);\r
+               }\r
+               \r
+               tableModel_rsved.fireTableDataChanged();\r
+               ((DefaultTableModel)jTable_rowheader.getModel()).fireTableDataChanged();\r
+               \r
+               setOverlapMark();\r
+       }\r
+       \r
+       private String getRec_mode(ReserveList reserve) { String s = ((reserve.getAppsRsv())?(reserve.getRec_mvchapter()):(reserve.getRec_mode())); return (s==null)?(""):(s); }\r
+       \r
+       /**\r
+        * 絞り込み検索の本体(現在リストアップされているものから絞り込みを行う)(親から呼ばれるよ!)\r
+        */\r
+       public void redrawListByKeywordFilter(SearchKey keyword, String target) {\r
+               \r
+               rowView.clear();\r
+               \r
+               // 情報を一行ずつチェックする\r
+               if ( keyword != null ) {\r
+                       for ( ReservedItem a : rowData ) {\r
+                               \r
+                               ProgDetailList tvd = new ProgDetailList();\r
+                               tvd.title = a.title;\r
+                               tvd.titlePop = TraceProgram.replacePop(tvd.title);\r
+                               \r
+                               // タイトルを整形しなおす\r
+                               boolean isFind = SearchProgram.isMatchKeyword(keyword, "", tvd);\r
+                               \r
+                               if ( isFind ) {\r
+                                       rowView.add(a);\r
+                               }\r
+                       }\r
+               }\r
+               else {\r
+                       for ( ReservedItem a : rowData ) {\r
+                               rowView.add(a);\r
+                       }\r
+               }\r
+               \r
+               // fire!\r
+               tableModel_rsved.fireTableDataChanged();\r
+               rowheaderModel_rsved.fireTableDataChanged();\r
+       }\r
+       \r
+       /**\r
+        * カラム幅を保存する(鯛ナビ終了時に呼び出されるメソッド)\r
+        */\r
+       public void copyColumnWidth() {\r
+               DefaultTableColumnModel columnModel = (DefaultTableColumnModel)jTable_rsved.getColumnModel();\r
+               TableColumn column = null;\r
+               for ( RsvedColumn rc : RsvedColumn.values() ) {\r
+                       if ( rc.getIniWidth() < 0 ) {\r
+                               continue;\r
+                       }\r
+                       column = columnModel.getColumn(rc.ordinal());\r
+                       bounds.getRsvedColumnSize().put(rc.toString(), column.getPreferredWidth());\r
+               }\r
+       }\r
+       \r
+       /**\r
+        * テーブルの行番号の表示のON/OFF\r
+        */\r
+       public void setRowHeaderVisible(boolean b) {\r
+               this.getRowHeader().setVisible(b);\r
+       }\r
+       \r
+       // 内部的な\r
+       \r
+       /**\r
+        * テーブル(の中の人)に追加\r
+        */\r
+       private void addRow(ReservedItem data) {\r
+               // 開始日時+放送局でソート\r
+               int i=0;\r
+               for (; i<rowData.size(); i++) {\r
+                       ReservedItem ra = rowData.get(i);\r
+                       int x = ra.nextstart.compareTo(data.nextstart);\r
+                       int y = ra.hide_chname.compareTo(data.hide_chname);\r
+                       int z = ra.encoder.compareTo(data.encoder);\r
+                       if (x == 0 && y == 0 && z > 0) {\r
+                               break;  // 挿入位置確定\r
+                       }\r
+                       if (x == 0 && y > 0) {\r
+                               break;  // 挿入位置確定\r
+                       }\r
+                       else if (x > 0) {\r
+                               break;  // 挿入位置確定\r
+                       }\r
+               }\r
+               \r
+               // 有効データ\r
+               rowData.add(i, data);\r
+       }\r
+\r
+       /**\r
+        * 重複マークつけてください\r
+        */\r
+       private void setOverlapMark() {\r
+               \r
+               if ( rowView.size() < 2 ) {\r
+                       return;\r
+               }\r
+               \r
+               // 最初の一行はリセットしておかないとなんの処理も行われない場合がある\r
+               ReservedItem fr = rowView.get(jTable_rsved.convertRowIndexToModel(0));\r
+               fr.dupmark = "";\r
+               fr.fireChanged();\r
+               \r
+               // 時間重複のマーキング\r
+               String sDT = "";\r
+               String eDT = "";\r
+               String sDT2 = "";\r
+               String eDT2 = "";\r
+               for (int i=0; i<jTable_rsved.getRowCount()-1; i++) {\r
+                       int vrow = jTable_rsved.convertRowIndexToModel(i);\r
+                       int vrow2 = jTable_rsved.convertRowIndexToModel(i+1);\r
+\r
+                       ReservedItem ra = rowView.get(vrow);\r
+                       ReservedItem rb = rowView.get(vrow2);\r
+\r
+                       if ( ! sDT2.equals("")) {\r
+                               sDT = sDT2;\r
+                               eDT = eDT2;\r
+                       }\r
+                       else {\r
+                               GregorianCalendar ca = CommonUtils.getCalendar(ra.nextstart);\r
+                               if ( ca != null ) {\r
+                                       sDT = CommonUtils.getDateTime(ca);\r
+                                       \r
+                                       int len = Integer.valueOf(ra.length);\r
+                                       ca.add(Calendar.MINUTE, len);\r
+                                       eDT = CommonUtils.getDateTime(ca);\r
+                               }\r
+                       }\r
+                       \r
+                       {\r
+                               GregorianCalendar ca = CommonUtils.getCalendar(rb.nextstart);\r
+                               if ( ca != null ) {\r
+                                       sDT2 = CommonUtils.getDateTime(ca);\r
+                                       \r
+                                       int len = Integer.valueOf(rb.length);\r
+                                       ca.add(Calendar.MINUTE, len);\r
+                                       eDT2 = CommonUtils.getDateTime(ca);\r
+                               }\r
+                       }\r
+                       \r
+                       if ( eDT.equals(sDT2) ) {\r
+                               ra.dupmark = rb.dupmark = DUPMARK_REP;\r
+                               ra.fireChanged();\r
+                               rb.fireChanged();\r
+                       }\r
+                       else if ( CommonUtils.isOverlap(sDT, eDT, sDT2, eDT2, false) ) {\r
+                               ra.dupmark = rb.dupmark = DUPMARK_NORMAL;\r
+                               ra.fireChanged();\r
+                               rb.fireChanged();\r
+                       }\r
+                       else {\r
+                               //raは操作しない\r
+                               rb.dupmark = "";\r
+                               rb.fireChanged();\r
+                       }\r
+               }\r
+       }\r
+       \r
+       /**\r
+        * 予約を編集したい\r
+        */\r
+       private void editReserve(String recId,String rsvId,String chnam,int vrow) {\r
+\r
+               //VWReserveDialog rD = new VWReserveDialog(0, 0, env, tvprograms, recorders, avs, chavs, stwin);\r
+               //rD.clear();\r
+               CommonSwingUtils.setLocationCenter(parent,rD);\r
+               \r
+               if (rD.open(recId,rsvId)) {\r
+                       rD.setVisible(true);\r
+               }\r
+               else {\r
+                       rD.setVisible(false);\r
+               }\r
+               \r
+               if (rD.isReserved()) {\r
+                       // よそさま\r
+                       updateReserveDisplay(chnam);\r
+                       // じぶん\r
+                       _redrawReservedList();\r
+                       // フォーカスを戻す\r
+                       jTable_rsved.getSelectionModel().setSelectionInterval(vrow,vrow);\r
+               }\r
+       }\r
+       \r
+\r
+       /*******************************************************************************\r
+        * リスナー\r
+        ******************************************************************************/\r
+       \r
+       /**\r
+        * タブが開かれたら表を書き換える\r
+        * ★synchronized(rowData)★\r
+        * @see #redrawReservedList()\r
+        */\r
+       private final ComponentAdapter cl_tabshown = new ComponentAdapter() {\r
+               @Override\r
+               public void componentShown(ComponentEvent e) {\r
+                       // ★★★ イベントにトリガーされた処理がかちあわないように synchronized() ★★★\r
+                       synchronized ( rowView ) {\r
+                               // 終了した予約を整理する\r
+                               for (HDDRecorder recorder : recorders) {\r
+                                       recorder.refreshReserves();\r
+                               }\r
+                               \r
+                               // 予約一覧を再構築する\r
+                               _redrawReservedList();\r
+                       }\r
+               }\r
+       };\r
+       \r
+       private final MouseAdapter ma_showpopup = new MouseAdapter() {\r
+               @Override\r
+               public void mouseClicked(MouseEvent e) {\r
+                       //\r
+                       Point p = e.getPoint();\r
+                       final int vrow = jTable_rsved.rowAtPoint(p);\r
+                       jTable_rsved.getSelectionModel().setSelectionInterval(vrow,vrow);\r
+                       //\r
+                       final int row = jTable_rsved.convertRowIndexToModel(vrow);\r
+                       ReservedItem ra = rowView.get(row);\r
+                       final boolean fexec = ra.exec;\r
+                       final String start = ra.nextstart;\r
+                       final String title = ra.title;\r
+                       final String chnam = ra.hide_chname;\r
+                       final String recId = ra.recorder;\r
+                       final String rsvId = ra.hide_rsvid;\r
+                       //\r
+                       if (e.getButton() == MouseEvent.BUTTON3) {\r
+                               if (e.getClickCount() == 1) {\r
+                                       // 右クリックで予約削除メニュー表示\r
+                                       JPopupMenu pop = new JPopupMenu();\r
+                                       //\r
+                                       {\r
+                                               JMenuItem menuItem = new JMenuItem("予約を編集する");\r
+                                               menuItem.addActionListener(new ActionListener() {\r
+                                                       public void actionPerformed(ActionEvent e) {\r
+                                                               editReserve(recId,rsvId,chnam,vrow);\r
+                                                       }\r
+                                               });\r
+                                               pop.add(menuItem);\r
+                                       }\r
+\r
+                                       pop.addSeparator();\r
+\r
+                                       // 予約実行ON・OFF\r
+                                       {\r
+                                               pop.add(getExecOnOffMenuItem(fexec,title,chnam,rsvId,recId));\r
+                                       }\r
+                                       \r
+                                       pop.addSeparator();\r
+                                       \r
+                                       {\r
+                                               pop.add(getRemoveRsvMenuItem(title,chnam,rsvId,recId));\r
+                                       }\r
+                                       \r
+                                       pop.addSeparator();\r
+                                       \r
+                                       {\r
+                                               pop.add(getJumpMenuItem(title,chnam,start));\r
+                                               pop.add(getJumpToLastWeekMenuItem(title,chnam,start));\r
+                                       }\r
+                                       \r
+                                       pop.show(jTable_rsved, e.getX(), e.getY());\r
+                               }\r
+                       }\r
+                       else if (e.getButton() == MouseEvent.BUTTON1) {\r
+                               if (e.getClickCount() == 1) {\r
+                                       \r
+                               }\r
+                               else if (e.getClickCount() == 2) {\r
+                                       // 左ダブルクリックで予約ウィンドウを開く\r
+                                       editReserve(recId,rsvId,chnam,vrow);\r
+                               }\r
+                       }\r
+               }\r
+       };\r
+       \r
+       private final RowSorterListener rsl_sorterchanged = new RowSorterListener() {\r
+               @Override\r
+               public void sorterChanged(RowSorterEvent e) {\r
+                       if ( e.getType() == Type.SORTED ) {\r
+                               if (rowView.size()>=2) setOverlapMark();\r
+                       }\r
+               }\r
+       }; \r
+       \r
+       /*******************************************************************************\r
+        * コンポーネント\r
+        ******************************************************************************/\r
+       \r
+       private JNETableReserved getNETable_rsved() {\r
+               if (jTable_rsved == null) {\r
+                       \r
+                       ArrayList<String> cola = new ArrayList<String>();\r
+                       for ( RsvedColumn rc : RsvedColumn.values() ) {\r
+                               if ( rc.getIniWidth() >= 0 ) {\r
+                                       cola.add(rc.toString());\r
+                               }\r
+                       }\r
+                       String[] colname = cola.toArray(new String[0]);\r
+                       \r
+                       tableModel_rsved = new ReservedTableModel(colname, 0);\r
+                       jTable_rsved = new JNETableReserved(tableModel_rsved, false);\r
+                       jTable_rsved.setAutoResizeMode(JNETable.AUTO_RESIZE_OFF);\r
+                       \r
+                       // ヘッダのモデル\r
+                       rowheaderModel_rsved = (DefaultTableModel) jTable_rowheader.getModel();\r
+                       \r
+                       // ソータを付ける\r
+                       TableRowSorter<TableModel> sorter = new TableRowSorter<TableModel>(tableModel_rsved);\r
+                       jTable_rsved.setRowSorter(sorter);\r
+                       \r
+                       sorter.addRowSorterListener(rsl_sorterchanged);\r
+                       \r
+                       // 数値でソートする項目用の計算式(番組長とか)\r
+                       final Comparator<String> titlecomp = new Comparator<String>() {\r
+                               \r
+                               @Override\r
+                               public int compare(String o1, String o2) {\r
+                                       String t1 = TraceProgram.replacePop(o1.replaceAll(TVProgram.titlePrefixRemoveExpr, "")).replaceFirst(TVProgram.epnoNormalizeExpr, "$1\\0$2");\r
+                                       String t2 = TraceProgram.replacePop(o2.replaceAll(TVProgram.titlePrefixRemoveExpr, "")).replaceFirst(TVProgram.epnoNormalizeExpr, "$1\\0$2");\r
+                                       return t1.compareTo(t2);\r
+                               }\r
+                       };\r
+                       \r
+                       // ソーターの効かない項目用の計算式(重複マーク)\r
+                       final Comparator<String> noncomp = new Comparator<String>() {\r
+                               @Override\r
+                               public int compare(String o1, String o2) {\r
+                                       return 0;\r
+                               }\r
+                       };\r
+                       \r
+                       sorter.setComparator(jTable_rsved.getColumn(RsvedColumn.TITLE.toString()).getModelIndex(),titlecomp);\r
+                       sorter.setComparator(jTable_rsved.getColumn(RsvedColumn.DUPMARK.toString()).getModelIndex(),noncomp);\r
+                       \r
+                       // 各カラムの幅\r
+                       DefaultTableColumnModel columnModel = (DefaultTableColumnModel)jTable_rsved.getColumnModel();\r
+                       TableColumn column = null;\r
+                       for ( RsvedColumn rc : RsvedColumn.values() ) {\r
+                               if ( rc.getIniWidth() < 0 ) {\r
+                                       continue;\r
+                               }\r
+                               column = columnModel.getColumn(rc.ordinal());\r
+                               column.setPreferredWidth(bounds.getRsvedColumnSize().get(rc.toString()));\r
+                       }\r
+                       \r
+                       // 重複マーク・実行マークはちょっとだけ表示の仕方が違う\r
+                       VWColorCharCellRenderer renderer = new VWColorCharCellRenderer(JLabel.CENTER); \r
+                       if ( CommonUtils.isMac() ) renderer.setMacMarkFont();\r
+                       jTable_rsved.getColumn(RsvedColumn.DUPMARK.toString()).setCellRenderer(renderer);\r
+                       ButtonColumn buttonColumn = new ButtonColumn();\r
+                       jTable_rsved.getColumn(RsvedColumn.EXEC.toString()).setCellRenderer(buttonColumn);\r
+                       jTable_rsved.getColumn(RsvedColumn.EXEC.toString()).setCellEditor(buttonColumn);\r
+                       jTable_rsved.getColumn(RsvedColumn.EXEC.toString()).setResizable(false);\r
+                       jTable_rsved.getColumn(RsvedColumn.TRACE.toString()).setCellRenderer(renderer);\r
+                       jTable_rsved.getColumn(RsvedColumn.AUTO.toString()).setCellRenderer(renderer);\r
+\r
+                       VWColorCharCellRenderer renderer2 = new VWColorCharCellRenderer(JLabel.LEFT); \r
+                       jTable_rsved.getColumn(RsvedColumn.TITLE.toString()).setCellRenderer(renderer2);\r
+                       jTable_rsved.getColumn(RsvedColumn.CHNAME.toString()).setCellRenderer(renderer2);\r
+                       jTable_rsved.getColumn(RsvedColumn.VRATE.toString()).setCellRenderer(renderer2);\r
+                       \r
+                       VWColorCharCellRenderer renderer3 = new VWColorCharCellRenderer(JLabel.LEFT); \r
+                       jTable_rsved.getColumn(RsvedColumn.ENCODER.toString()).setCellRenderer(renderer3);\r
+\r
+                       // 一覧表クリックで削除メニュー出現\r
+                       jTable_rsved.addMouseListener(ma_showpopup);\r
+               }\r
+               return jTable_rsved;\r
+       }\r
+\r
+       \r
+       \r
+       \r
+       /*******************************************************************************\r
+        * 表表示\r
+        ******************************************************************************/\r
+       \r
+       private class JNETableReserved extends JNETable {\r
+\r
+               private static final long serialVersionUID = 1L;\r
+\r
+               // futuer use.\r
+               public void setDisabledColor(Color c) { disabledColor = c; }\r
+               private Color disabledColor = new Color(180,180,180);\r
+\r
+               public void setTunerShortColor(Color c) { tunershortColor = c; }\r
+               private Color tunershortColor = new Color(255,255,0);\r
+               \r
+               public void setRecordedColor(Color c) { recordedColor = c; }\r
+               private Color recordedColor = new Color(204,153,255);\r
+               \r
+               private int prechkrow = -1;\r
+               private boolean prechkdisabled = false;\r
+               private boolean prechktunershort = false;\r
+               private boolean prechkrecorded = false;\r
+               \r
+               @Override\r
+               public Component prepareRenderer(TableCellRenderer tcr, int row, int column) {\r
+                       Component c = super.prepareRenderer(tcr, row, column);\r
+                       Color fgColor = null;\r
+                       Color bgColor = null;\r
+                       if(isRowSelected(row)) {\r
+                               fgColor = this.getSelectionForeground();\r
+                               bgColor = this.getSelectionBackground();\r
+                       }\r
+                       else {\r
+                               fgColor = this.getForeground();\r
+                               isRowPassed(row);\r
+                               if ( prechkdisabled ) {\r
+                                       bgColor = disabledColor;\r
+                               }\r
+                               else if ( prechktunershort ) {\r
+                                       bgColor = tunershortColor;\r
+                               }\r
+                               else if ( prechkrecorded ) {\r
+                                       bgColor = recordedColor;\r
+                               }\r
+                               else {\r
+                                       bgColor = (isSepRowColor && row%2 == 1)?(evenColor):(super.getBackground());\r
+                               }\r
+                       }\r
+                       if ( ! (tcr instanceof VWColorCharCellRenderer) && ! (tcr instanceof VWColorCharCellRenderer2) && ! (tcr instanceof VWColorCellRenderer)) {\r
+                               c.setForeground(fgColor);\r
+                       }               \r
+                       if ( ! (tcr instanceof VWColorCellRenderer)) {\r
+                               c.setBackground(bgColor);\r
+                       }\r
+                       return c;\r
+               }\r
+               \r
+               // 連続して同じ行へのアクセスがあったら計算を行わず前回のままにする\r
+               private boolean isRowPassed(int prow) {\r
+                       \r
+                       if(prechkrow == prow) {\r
+                               return prechkdisabled;\r
+                       }\r
+\r
+                       int row = this.convertRowIndexToModel(prow);\r
+                       ReservedItem c = rowView.get(row);\r
+\r
+                       {\r
+                               // 実行可能かどうか\r
+                               prechkrow = prow;\r
+                               prechkdisabled = ! c.exec;\r
+                               prechktunershort = c.hide_tunershort;\r
+                               prechkrecorded = c.hide_recorded;\r
+                       }\r
+                       \r
+                       return true;\r
+               }\r
+               \r
+               //\r
+               @Override\r
+               public void tableChanged(TableModelEvent e) {\r
+                       reset();\r
+                       super.tableChanged(e);\r
+               }\r
+               \r
+               private void reset() {\r
+                       prechkrow = -1;\r
+                       prechkdisabled = false;\r
+                       prechktunershort = false;\r
+                       prechkrecorded = false;\r
+               }\r
+               \r
+               @Override\r
+               public boolean isCellEditable(int row, int column) {\r
+                       if ( column == RsvedColumn.EXEC.getColumn() ) {\r
+                               return true;\r
+                       }\r
+                       return false;\r
+               }\r
+               \r
+               // コンストラクタ\r
+               public JNETableReserved(boolean b) {\r
+                       super(b);\r
+               }\r
+               public JNETableReserved(TableModel d, boolean b) {\r
+                       super(d,b);\r
+               }\r
+       }\r
+       \r
+       \r
+       /**\r
+        * EXECボタン\r
+        */\r
+       private class ButtonColumn extends AbstractCellEditor implements TableCellRenderer, TableCellEditor { \r
+\r
+               private static final long serialVersionUID = 1L;\r
+\r
+               private final JButton renderButton = new JButton();\r
+               private final JButton editorButton = new JButton();\r
+               \r
+               public ButtonColumn() {\r
+                       super();\r
+                       renderButton.setMargin(new Insets(1, 0, 0, 0));\r
+                       renderButton.setHorizontalAlignment(JLabel.CENTER);\r
+                       renderButton.setVerticalAlignment(JLabel.CENTER);\r
+                       editorButton.setAction(act);\r
+               }\r
+               \r
+               private final AbstractAction act = new AbstractAction("") {\r
+\r
+                       private static final long serialVersionUID = 1L;\r
+\r
+                       public void actionPerformed(ActionEvent e) {\r
+                               fireEditingStopped();\r
+                               \r
+                               int vrow = jTable_rsved.getSelectedRow();\r
+                               int row = jTable_rsved.convertRowIndexToModel(vrow);\r
+                               \r
+                               ReservedItem c = rowData.get(row);\r
+                               if ( doExecOnOff( ! c.exec, c.title, c.chname, c.hide_rsvid, c.recorder) ) {\r
+                                       c.exec = ! c.exec;\r
+                                       c.fireChanged();\r
+                               }\r
+                               \r
+                               jTable_rsved.clearSelection();\r
+                               jTable_rsved.setRowSelectionInterval(vrow, vrow);\r
+                       }\r
+               };\r
+               \r
+               public Component getTableCellRendererComponent(JTable table, Object value, boolean isSelected, boolean hasFocus, int row, int column) {\r
+                       renderButton.setIcon((Boolean) value ? execicon : null);\r
+                       renderButton.setPressedIcon((Boolean) value ? execicon : null);\r
+                       return renderButton;\r
+               }\r
+               \r
+               public Component getTableCellEditorComponent(JTable table, Object value, boolean isSelected, int row, int column) { \r
+                       editorButton.setIcon((Boolean) value ? execicon : null);\r
+                       editorButton.setPressedIcon((Boolean) value ? execicon : null);\r
+                       return editorButton;\r
+               }\r
+               \r
+               public Object getCellEditorValue() {\r
+                       return renderButton.getIcon() != null; \r
+               }\r
+       }\r
+}\r
diff --git a/TinyBannavi/src/tainavi/AbsSettingView.java b/TinyBannavi/src/tainavi/AbsSettingView.java
new file mode 100644 (file)
index 0000000..b93ad48
--- /dev/null
@@ -0,0 +1,2197 @@
+package tainavi;\r
+\r
+import java.awt.Color;\r
+import java.awt.Component;\r
+import java.awt.Dimension;\r
+import java.awt.Font;\r
+import java.awt.event.ActionEvent;\r
+import java.awt.event.ActionListener;\r
+import java.awt.event.ItemEvent;\r
+import java.awt.event.ItemListener;\r
+import java.awt.event.MouseAdapter;\r
+import java.awt.event.MouseEvent;\r
+import java.util.Map.Entry;\r
+import java.util.regex.Matcher;\r
+import java.util.regex.Pattern;\r
+\r
+import javax.swing.DefaultCellEditor;\r
+import javax.swing.DefaultComboBoxModel;\r
+import javax.swing.JButton;\r
+import javax.swing.JCheckBox;\r
+import javax.swing.JComboBox;\r
+import javax.swing.JComponent;\r
+import javax.swing.JLabel;\r
+import javax.swing.JPanel;\r
+import javax.swing.JRadioButton;\r
+import javax.swing.JScrollPane;\r
+import javax.swing.JTable;\r
+import javax.swing.JTextField;\r
+import javax.swing.ListSelectionModel;\r
+import javax.swing.ScrollPaneConstants;\r
+import javax.swing.SpringLayout;\r
+import javax.swing.UIManager;\r
+import javax.swing.border.LineBorder;\r
+import javax.swing.event.ChangeEvent;\r
+import javax.swing.event.ChangeListener;\r
+import javax.swing.event.DocumentEvent;\r
+import javax.swing.event.DocumentListener;\r
+import javax.swing.table.DefaultTableCellRenderer;\r
+import javax.swing.table.DefaultTableColumnModel;\r
+import javax.swing.table.DefaultTableModel;\r
+import javax.swing.table.TableColumn;\r
+\r
+import tainavi.Env.DblClkCmd;\r
+import tainavi.Env.SnapshotFmt;\r
+import tainavi.Env.UpdateOn;\r
+import tainavi.TVProgram.ProgOption;\r
+\r
+\r
+/**\r
+ * 各種設定タブのクラス\r
+ * @since 3.15.4β {@link Viewer}から分離\r
+ */\r
+public abstract class AbsSettingView extends JScrollPane {\r
+\r
+       private static final long serialVersionUID = 1L;\r
+\r
+       public static String getViewName() { return "各種設定"; } \r
+\r
+       public void setDebug(boolean b) { debug = b; }\r
+       private static boolean debug = false;\r
+\r
+       /*******************************************************************************\r
+        * 抽象メソッド\r
+        ******************************************************************************/\r
+       \r
+       protected abstract Env getEnv();\r
+       protected abstract ClipboardInfoList getCbItemEnv();\r
+       \r
+       protected abstract VWLookAndFeel getLAFEnv();\r
+       protected abstract VWFont getFontEnv();\r
+       \r
+       protected abstract StatusWindow getStWin(); \r
+       protected abstract StatusTextArea getMWin();\r
+       \r
+       protected abstract Component getParentComponent();\r
+       protected abstract VWColorChooserDialog getCcWin(); \r
+       \r
+       protected abstract void lafChanged(String lafname);\r
+       protected abstract void fontChanged(String fn, int fontSize);\r
+       protected abstract void setEnv(boolean reload_prog);\r
+\r
+       /*******************************************************************************\r
+        * 呼び出し元から引き継いだもの\r
+        ******************************************************************************/\r
+       \r
+       // オブジェクト\r
+       private final Env env = getEnv();\r
+       private final ClipboardInfoList cbitems = getCbItemEnv();\r
+       \r
+       private final StatusWindow StWin = getStWin();                  // これは起動時に作成されたまま変更されないオブジェクト\r
+       private final StatusTextArea MWin = getMWin();                  // これは起動時に作成されたまま変更されないオブジェクト\r
+       \r
+       private final Component parent = getParentComponent();  // これは起動時に作成されたまま変更されないオブジェクト\r
+       private final VWColorChooserDialog ccwin = getCcWin();  // これは起動時に作成されたまま変更されないオブジェクト\r
+       \r
+       // 雑多なメソッド\r
+       private void StdAppendMessage(String message) { System.out.println(message); }\r
+       //private void StdAppendError(String message) { System.err.println(message); }\r
+       private void StWinSetVisible(boolean b) { StWin.setVisible(b); }\r
+       private void StWinSetLocationCenter(Component frame) { CommonSwingUtils.setLocationCenter(frame, (VWStatusWindow)StWin); }\r
+       \r
+       /*******************************************************************************\r
+        * 定数\r
+        ******************************************************************************/\r
+\r
+       // レイアウト関連\r
+\r
+       private static final int PARTS_WIDTH = 900;\r
+       private static final int PARTS_HEIGHT = 30;\r
+       private static final int SEP_WIDTH = 10;\r
+       private static final int SEP_HEIGHT = 10;\r
+       private static final int SEP_HEIGHT_NALLOW = 5;\r
+       private static final int BLOCK_SEP_HEIGHT = 75;\r
+       \r
+       private static final int LABEL_WIDTH = 350;\r
+       private static final int CCLABEL_WIDTH = 250;\r
+       private static final int DESCRIPTION_WIDTH = LABEL_WIDTH+PARTS_WIDTH;\r
+\r
+       private static final int UPDATE_WIDTH = 250;\r
+       private static final int HINT_WIDTH = 700;\r
+       \r
+       private static final int PANEL_WIDTH = LABEL_WIDTH+PARTS_WIDTH+SEP_WIDTH*3;\r
+       \r
+       //\r
+       \r
+       private static final Color NOTICEMSG_COLOR = new Color(0,153,153);\r
+       // テキスト\r
+       \r
+       private static final String TEXT_HINT =\r
+                       "各項目の詳細はプロジェクトWikiに説明があります(http://sourceforge.jp/projects/tainavi/wiki/)。"+\r
+                       "ツールバーのヘルプアイコンをクリックするとブラウザで開きます。";\r
+\r
+       private static final String PARER_REDRAW_NORMAL = "通常";\r
+       private static final String PARER_REDRAW_CACHE = "再描画時に一週間分をまとめて描画して日付切り替えを高速化する(メモリ消費大)";\r
+       private static final String PARER_REDRAW_PAGER = "ページャーを有効して一度に描画する放送局数を抑える(メモリ消費抑制、切替時間短縮)";\r
+\r
+       // ログ関連\r
+\r
+       private static final String MSGID = "["+getViewName()+"] ";\r
+       private static final String ERRID = "[ERROR]"+MSGID;\r
+       private static final String DBGID = "[DEBUG]"+MSGID;\r
+       \r
+       /*******************************************************************************\r
+        * 部品\r
+        ******************************************************************************/\r
+       \r
+       // コンポーネント\r
+       \r
+       private JPanel jPanel_setting = null;\r
+       \r
+       private JPanel jPanel_update = null;\r
+       private JButton jButton_update = null;\r
+       \r
+       // リスト形式\r
+       private JCheckBoxPanel jCBP_disableFazzySearch = null;\r
+       private JCheckBoxPanel jCBP_disableFazzySearchReverse = null;\r
+       private JSliderPanel jSP_defaultFazzyThreshold = null;\r
+       private JCheckBoxPanel jCBP_syoboFilterByCenters = null;\r
+       private JCheckBoxPanel jCBP_displayPassedEntry = null;\r
+       private JCheckBoxPanel jCBP_showRsvPickup = null;\r
+       private JCheckBoxPanel jCBP_showRsvUra = null;\r
+       private JCheckBoxPanel jCBP_rsvdLineEnhance = null;\r
+       private JLabel jLabel_rsvdLineColor = null;\r
+       private JCCLabel jCCL_rsvdLineColor = null;\r
+       private JLabel jLabel_pickedLineColor = null;\r
+       private JCCLabel jCCL_pickedLineColor = null;\r
+       private JCCLabel jCCL_matchedKeywordColor = null;\r
+       private JCheckBoxPanel jCBP_currentLineEnhance = null;\r
+       private JLabel jLabel_currentLineColor = null;\r
+       private JCCLabel jCCL_currentLineColor = null;\r
+       private JSliderPanel jSP_currentAfter = null;\r
+       private JSliderPanel jSP_currentBefore = null;\r
+       private JCheckBoxPanel jCBP_showWarnDialog = null;\r
+       private JCheckBoxPanel jCBP_splitMarkAndTitle = null;\r
+       private JCheckBoxPanel jCBP_showDetailOnList = null;\r
+       private JSliderPanel jSP_rsvTargets = null;\r
+       private JCheckBoxPanel jCBP_rowHeaderVisible = null;\r
+       private JComboBoxPanel jCBX_dblClkCmd = null;\r
+       private JSliderPanel jSP_searchResultMax = null;\r
+       private JSliderPanel jSP_searchResultBufferMax = null;\r
+       \r
+       // 新聞形式\r
+       private JRadioButtonPanel jRBP_getPaperRedrawType = null;\r
+       private JSliderPanel jSP_centerPerPage = null;\r
+       private JCheckBoxPanel jCBP_allPageSnapshot = null;\r
+       private JCheckBoxPanel jCBP_tooltipEnable = null;\r
+       private JSliderPanel jSP_tooltipInitialDelay = null;\r
+       private JSliderPanel jSP_tooltipDismissDelay = null;\r
+       private JCheckBoxPanel jCBP_timerbarScrollEnable = null;\r
+       private JSliderPanel jSP_passedLogLimit = null;\r
+       private JCheckBoxPanel jCBP_effectComboToPaper = null;\r
+       private JComboBoxPanel jCBX_snapshotFmt = null;\r
+       private JCheckBoxPanel jCBP_printSnapshot = null;\r
+       \r
+       // リスト・新聞共通\r
+       private JCheckBoxPanel jCBP_displayOnlyExecOnEntry = null;\r
+       private JCheckBoxPanel jCBP_displayPassedReserve = null;\r
+       private JCheckBoxPanel jCBP_showOnlyNonrepeated = null;\r
+       private JCheckBoxPanel jCBP_adjLateNight = null;\r
+       private JCheckBoxPanel jCBP_rootNodeVisible = null;\r
+       private JCheckBoxPanel jCBP_syncTreeWidth = null;\r
+       private JCheckBoxPanel jCBP_shortExtMark = null;\r
+       private JLabel jLabel_showmarks = null;\r
+       private JScrollPane jScrollPane_showmarks = null;\r
+       private JNETable jTable_showmarks = null;\r
+       private JLabel jLabel_clipboard = null;\r
+       private JScrollPane jScrollPane_clipboard = null;\r
+       private JNETable jTable_clipboard = null;\r
+       private JButton jButton_clipboard_up = null;\r
+       private JButton jButton_clipboard_down = null;\r
+       private JLabel jLabel_menuitem = null;\r
+       private JTextFieldWithPopup jTextField_mikey = null;\r
+       private JTextFieldWithPopup jTextField_mival = null;\r
+       private JScrollPane jScrollPane_mitable = null;\r
+       private JNETable jTable_mitable = null;\r
+       private JButton jButton_miadd = null;\r
+       private JButton jButton_midel = null;\r
+       private JButton jButton_miup = null;\r
+       private JButton jButton_midown = null;\r
+       \r
+       // Web番組表対応\r
+       private JCheckBoxPanel jCBP_continueTomorrow = null;\r
+       private JSliderPanel jSP_cacheTimeLimit = null;\r
+       private JComboBoxPanel jCBX_shutdownCmd = null;\r
+       private JCheckBoxPanel jCBP_expandTo8 = null;\r
+       //private JCheckBoxPanel jCBP_useDetailCache = null;\r
+       private JCheckBoxPanel jCBP_autoEventIdComplete = null;\r
+       private JCheckBoxPanel jCBP_splitEpno = null;\r
+       private JCheckBoxPanel jCBP_traceOnlyTitle = null;\r
+       private JCheckBoxPanel jCBP_fixTitle = null;\r
+       private JLabel jLabel_ngword = null;\r
+       private JTextFieldWithPopup jTextField_ngword = null;\r
+       private JLabel jLabel_userAgent = null;\r
+       private JTextFieldWithPopup jTextField_userAgent = null;\r
+       private JCheckBoxPanel jCBP_useProxy = null;\r
+       private JLabel jLabel_proxy = null;\r
+       private JTextFieldWithPopup jTextField_proxyAddr = null;\r
+       private JTextFieldWithPopup jTextField_proxyPort = null;\r
+       private JCheckBoxPanel jCBP_useSyobocal = null;\r
+       private JCheckBoxPanel jCBP_historyOnlyUpdateOnce = null;\r
+       private JCheckBoxPanel jCBP_usePassedProgram = null;\r
+       private JSliderPanel jSP_prepPassedProgramCount = null;\r
+       \r
+       // レコーダ対応\r
+       private JRadioButtonPanel jRBP_getRdReserveDetails = null;\r
+       private JCheckBoxPanel jCBP_skipGetRdRecorded = null;\r
+       private JComboBoxPanel jCBX_recordedSaveScope = null;\r
+       \r
+       // 予約\r
+       private JSliderPanel jSP_spoex_extend = null;\r
+       private JRadioButtonPanel jRBP_overlapUp = null;\r
+       private JRadioButtonPanel jRBP_overlapDown = null;\r
+       private JLabel jLabel_autoFolderSelect = null;\r
+       private JCheckBox jCheckBox_autoFolderSelect = null;\r
+       private JLabel jLabel_enableCHAVsetting = null;\r
+       private JCheckBox jCheckBox_enableCHAVsetting = null;\r
+       private JSliderPanel jSP_rangeLikeRsv = null;\r
+       private JCheckBoxPanel jCBP_givePriorityToReserved = null;\r
+       private JCheckBoxPanel jCBP_givePriorityToReservedTitle = null;\r
+       private JCheckBoxPanel jCBP_adjoiningNotRepetition = null;\r
+       private JCheckBoxPanel jCBP_rsv_showallite = null;\r
+       private JLabel jLabel_rsv_itecolor = null;\r
+       private JCCLabel jCCL_rsv_itecolor = null;\r
+       private JLabel jLabel_rsv_tunshortcolor = null;\r
+       private JCCLabel jCCL_rsv_tunshortcolor = null;\r
+       private JLabel jLabel_rsv_recedcolor = null;\r
+       private JCCLabel jCCL_rsv_recedcolor = null;\r
+       private JCheckBoxPanel jCBP_useAutocomplete = null;\r
+       \r
+       // その他\r
+       private JComboBoxPanel jCBX_updateMethod = null;\r
+       private JCheckBoxPanel jCBP_disableBeep = null;\r
+       private JCheckBoxPanel jCBP_showSysTray = null;\r
+       private JCheckBoxPanel jCBP_hideToTray = null;\r
+       private JCheckBoxPanel jCBP_onlyOneInstance = null;\r
+       private JLabel jLabel_lookAndFeel = null;\r
+       private JComboBox jComboBox_lookAndFeel = null;\r
+       private JLabel jLabel_font = null;\r
+       private JComboBox jComboBox_font = null;\r
+       private JComboBox jComboBox_fontSize = null;\r
+       private JLabel jLabel_fontSample = null;\r
+       private JCheckBoxPanel jCBP_useGTKRC = null;\r
+       private JCheckBoxPanel jCBP_useRundll32 = null;\r
+       private JCheckBoxPanel jCBP_debug = null;\r
+\r
+       private JTextAreaWithPopup jta_help = null;\r
+\r
+       // コンポーネント以外\r
+       \r
+       /**\r
+        * 特定のコンポーネントを操作した時だけ番組表を再取得してほしい\r
+        */\r
+       private boolean reload_prog_needed = false;\r
+       \r
+       /*******************************************************************************\r
+        * コンストラクタ\r
+        ******************************************************************************/\r
+       \r
+       public AbsSettingView() {\r
+               \r
+               super();\r
+               \r
+               this.setVerticalScrollBarPolicy(ScrollPaneConstants.VERTICAL_SCROLLBAR_ALWAYS);\r
+               this.getVerticalScrollBar().setUnitIncrement(25);\r
+               this.setColumnHeaderView(getJPanel_update());\r
+               this.setViewportView(getJPanel_setting());\r
+               \r
+               setUpdateButtonEnhanced(false);\r
+       }\r
+       \r
+       private JPanel getJPanel_update() {\r
+               if (jPanel_update == null)\r
+               {\r
+                       jPanel_update = new JPanel();\r
+                       jPanel_update.setLayout(new SpringLayout());\r
+                       \r
+                       jPanel_update.setBorder(new LineBorder(Color.GRAY));\r
+                       \r
+                       int y = SEP_HEIGHT;\r
+                       CommonSwingUtils.putComponentOn(jPanel_update, getJButton_update("更新を確定する"), UPDATE_WIDTH, PARTS_HEIGHT, SEP_WIDTH, y);\r
+                       \r
+                       int yz = SEP_HEIGHT/2;\r
+                       int x = UPDATE_WIDTH+50;\r
+                       CommonSwingUtils.putComponentOn(jPanel_update, getJta_help(), HINT_WIDTH, PARTS_HEIGHT+SEP_HEIGHT, x, yz);\r
+                       \r
+                       y += (PARTS_HEIGHT + SEP_HEIGHT);\r
+                       \r
+                       // 画面の全体サイズを決める\r
+                       Dimension d = new Dimension(PANEL_WIDTH,y);\r
+                       jPanel_update.setPreferredSize(d);\r
+               }\r
+               return jPanel_update;\r
+       }\r
+\r
+       /**\r
+        * ActionListenerはGUIの操作では動くがsetSelected()での変更では動かない<BR>\r
+        * ChangeListenerもItemListenerも同じ値のセットしなおしだと動作しない<BR>\r
+        * 以下ではFire!するために涙ぐましい努力を行っている<BR>\r
+        * <BR>\r
+        * ex.<BR>\r
+        * jcheckbox.setSelected( ! env.isSelected())<BR>\r
+        * jchackbox.addItemListener()<BR>\r
+        * jcheckbox.setSelected( ! jcheckbox.isSelected())<BR>\r
+        * <BR>\r
+        * …あほか!\r
+        */\r
+       private JPanel getJPanel_setting() {\r
+               if (jPanel_setting == null)\r
+               {\r
+                       jPanel_setting = new JPanel();\r
+                       jPanel_setting.setLayout(new SpringLayout());\r
+                       \r
+                       //\r
+                       int y = SEP_HEIGHT;\r
+                       \r
+                       /*\r
+                        * リスト形式 \r
+                        */\r
+                       \r
+                       y+=0;\r
+                       CommonSwingUtils.putComponentOn(jPanel_setting, new JLabel("<<<リスト形式>>>"), LABEL_WIDTH, PARTS_HEIGHT, SEP_WIDTH, y);\r
+\r
+                       {\r
+                               y+=(PARTS_HEIGHT+SEP_HEIGHT);\r
+                               CommonSwingUtils.putComponentOn(jPanel_setting, jCBP_disableFazzySearch = new JCheckBoxPanel("番組追跡であいまい検索をしない",LABEL_WIDTH), PARTS_WIDTH, PARTS_HEIGHT, SEP_WIDTH, y);\r
+                               jCBP_disableFazzySearch.setSelected( ! env.getDisableFazzySearch());\r
+                               // RELOADリスナー不要\r
+                               \r
+                               y+=(PARTS_HEIGHT+SEP_HEIGHT);\r
+                               CommonSwingUtils.putComponentOn(jPanel_setting, jCBP_disableFazzySearchReverse = new JCheckBoxPanel("┗ あいまい検索の逆引きを省略(非推奨)",LABEL_WIDTH), PARTS_WIDTH, PARTS_HEIGHT, SEP_WIDTH, y);\r
+                               jCBP_disableFazzySearchReverse.setSelected(env.getDisableFazzySearchReverse());\r
+                               // RELOADリスナー不要\r
+\r
+                               y+=(PARTS_HEIGHT+SEP_HEIGHT);\r
+                               CommonSwingUtils.putComponentOn(jPanel_setting, jSP_defaultFazzyThreshold = new JSliderPanel("┗ あいまい検索のデフォルト閾値",LABEL_WIDTH,1,99,200), PARTS_WIDTH, PARTS_HEIGHT, SEP_WIDTH, y);\r
+                               jSP_defaultFazzyThreshold.setValue(env.getDefaultFazzyThreshold());\r
+                               // RELOADリスナー不要\r
+                               \r
+                               // 連動設定(Fire!がキモイ…)\r
+                               \r
+                               jCBP_disableFazzySearch.addItemListener(al_fazzysearch);\r
+                               \r
+                               jCBP_disableFazzySearch.setSelected( ! jCBP_disableFazzySearch.isSelected());\r
+                       }\r
+\r
+                       y+=(PARTS_HEIGHT+SEP_HEIGHT);\r
+                       CommonSwingUtils.putComponentOn(jPanel_setting, getNoticeMsg("※あいまい検索のアルゴリズムにはバイグラムを使用しています(TraceProgram.java参照)"), DESCRIPTION_WIDTH, PARTS_HEIGHT, SEP_WIDTH*2, y);\r
+                       y+=(PARTS_HEIGHT);\r
+                       CommonSwingUtils.putComponentOn(jPanel_setting, getNoticeMsg(" 正順では「検索キーワード」の成分が「番組表のタイトル」にどれくらい含まれているかを判定しています。"), DESCRIPTION_WIDTH, PARTS_HEIGHT, SEP_WIDTH*2, y);\r
+                       y+=(PARTS_HEIGHT);\r
+                       CommonSwingUtils.putComponentOn(jPanel_setting, getNoticeMsg(" 逆順を有効にすると、正順でNG判定になった場合に前者と後者を入れ替えて再判定を行います。取りこぼしが減る場合がある反面、検索ノイズが増える場合もあります。"), DESCRIPTION_WIDTH, PARTS_HEIGHT, SEP_WIDTH*2, y);\r
+                       y+=(PARTS_HEIGHT);\r
+                       CommonSwingUtils.putComponentOn(jPanel_setting, getNoticeMsg(" 閾値を大きくすると判定が厳しくなります。キーワードが短いためにヒットしまくりで検索ノイズが多くなった場合に、値を大きくしてみてください。"), DESCRIPTION_WIDTH, PARTS_HEIGHT, SEP_WIDTH*2, y);\r
+                       \r
+                       y+=(PARTS_HEIGHT+SEP_HEIGHT);\r
+                       CommonSwingUtils.putComponentOn(jPanel_setting, jCBP_syoboFilterByCenters = new JCheckBoxPanel("しょぼかるの検索結果も有効な放送局のみに絞る",LABEL_WIDTH), PARTS_WIDTH, PARTS_HEIGHT, SEP_WIDTH, y);\r
+                       jCBP_syoboFilterByCenters.setSelected(env.getSyoboFilterByCenters());\r
+                       // RELOADリスナー不要\r
+\r
+                       y+=(PARTS_HEIGHT+SEP_HEIGHT);\r
+                       CommonSwingUtils.putComponentOn(jPanel_setting, jCBP_displayPassedEntry = new JCheckBoxPanel("当日の終了済み番組も表示する",LABEL_WIDTH), PARTS_WIDTH, PARTS_HEIGHT, SEP_WIDTH, y);\r
+                       jCBP_displayPassedEntry.setSelected(env.getDisplayPassedEntry());\r
+                       // RELOADリスナー不要\r
+\r
+                       y+=(PARTS_HEIGHT+SEP_HEIGHT);\r
+                       CommonSwingUtils.putComponentOn(jPanel_setting, jCBP_showRsvPickup = new JCheckBoxPanel("ピックアップマーク(★)を表示する",LABEL_WIDTH), PARTS_WIDTH, PARTS_HEIGHT, SEP_WIDTH, y);\r
+                       jCBP_showRsvPickup.setSelected(env.getShowRsvPickup());\r
+                       // RELOADリスナー不要\r
+\r
+                       y+=(PARTS_HEIGHT+SEP_HEIGHT);\r
+                       CommonSwingUtils.putComponentOn(jPanel_setting, jCBP_showRsvUra = new JCheckBoxPanel("裏番組予約マーク(■)を表示する",LABEL_WIDTH), PARTS_WIDTH, PARTS_HEIGHT, SEP_WIDTH, y);\r
+                       jCBP_showRsvUra.setSelected(env.getShowRsvUra());\r
+                       // RELOADリスナー不要\r
+\r
+                       {\r
+                               y+=(PARTS_HEIGHT+SEP_HEIGHT);\r
+                               CommonSwingUtils.putComponentOn(jPanel_setting, jCBP_rsvdLineEnhance = new JCheckBoxPanel("予約行の背景色を変えて強調する",LABEL_WIDTH), LABEL_WIDTH+PARTS_HEIGHT, PARTS_HEIGHT, SEP_WIDTH, y);\r
+                               jCBP_rsvdLineEnhance.setSelected( ! env.getRsvdLineEnhance());\r
+                               // RELOADリスナー不要\r
+\r
+                               y+=(PARTS_HEIGHT+SEP_HEIGHT);\r
+                               CommonSwingUtils.putComponentOn(jPanel_setting, jLabel_rsvdLineColor = new JLabel("┗ 予約行の背景色"), LABEL_WIDTH, PARTS_HEIGHT, SEP_WIDTH, y);\r
+                               CommonSwingUtils.putComponentOn(jPanel_setting, jCCL_rsvdLineColor = new JCCLabel("予約行の背景色",env.getRsvdLineColor(),true,parent,ccwin), CCLABEL_WIDTH, PARTS_HEIGHT, LABEL_WIDTH+SEP_WIDTH, y);\r
+                               // RELOADリスナー不要\r
+\r
+                               y+=(PARTS_HEIGHT+SEP_HEIGHT);\r
+                               CommonSwingUtils.putComponentOn(jPanel_setting, jLabel_pickedLineColor = new JLabel("┗ ピックアップ行の背景色"), LABEL_WIDTH, PARTS_HEIGHT, SEP_WIDTH, y);\r
+                               CommonSwingUtils.putComponentOn(jPanel_setting, jCCL_pickedLineColor = new JCCLabel("ピックアップ行の背景色",env.getPickedLineColor(),true,parent,ccwin), CCLABEL_WIDTH, PARTS_HEIGHT, LABEL_WIDTH+SEP_WIDTH, y);\r
+                               // RELOADリスナー不要\r
+                               \r
+                               // 連動設定\r
+                               \r
+                               jCBP_rsvdLineEnhance.addItemListener(al_rsvdlineenhance);\r
+                               \r
+                               jCBP_rsvdLineEnhance.setSelected( ! jCBP_rsvdLineEnhance.isSelected());\r
+                       }\r
+                       \r
+                       {\r
+                               y+=(PARTS_HEIGHT+SEP_HEIGHT);\r
+                               CommonSwingUtils.putComponentOn(jPanel_setting, jCBP_currentLineEnhance = new JCheckBoxPanel("現在放送中の行の背景色を変えて強調する",LABEL_WIDTH), LABEL_WIDTH+PARTS_HEIGHT, PARTS_HEIGHT, SEP_WIDTH, y);\r
+                               jCBP_currentLineEnhance.setSelected( ! env.getCurrentLineEnhance());\r
+                               // RELOADリスナー不要\r
+\r
+                               y+=(PARTS_HEIGHT+SEP_HEIGHT);\r
+                               CommonSwingUtils.putComponentOn(jPanel_setting, jLabel_currentLineColor = new JLabel("┗ 現在放送中行の背景色"), LABEL_WIDTH, PARTS_HEIGHT, SEP_WIDTH, y);\r
+                               CommonSwingUtils.putComponentOn(jPanel_setting, jCCL_currentLineColor = new JCCLabel("現在放送中行の背景色",env.getCurrentLineColor(),true,parent,ccwin), CCLABEL_WIDTH, PARTS_HEIGHT, LABEL_WIDTH+SEP_WIDTH, y);\r
+                               // RELOADリスナー不要\r
+                               \r
+                               // 連動設定\r
+                               \r
+                               jCBP_currentLineEnhance.addItemListener(al_currentlineenhance);\r
+                               \r
+                               jCBP_currentLineEnhance.setSelected( ! jCBP_currentLineEnhance.isSelected());\r
+                       }\r
+                       \r
+                       y+=(PARTS_HEIGHT+SEP_HEIGHT);\r
+                       CommonSwingUtils.putComponentOn(jPanel_setting, jSP_currentAfter = new JSliderPanel("現在放送中ノードに終了後何分までの番組を表示するか",LABEL_WIDTH,0,60,200), PARTS_WIDTH, PARTS_HEIGHT, SEP_WIDTH, y);\r
+                       jSP_currentAfter.setValue(env.getCurrentAfter()/60);\r
+                       // RELOADリスナー不要\r
+                       \r
+                       y+=(PARTS_HEIGHT+SEP_HEIGHT);\r
+                       CommonSwingUtils.putComponentOn(jPanel_setting, jSP_currentBefore = new JSliderPanel("現在放送中ノードに開始前何分までの番組を表示するか",LABEL_WIDTH,0,120,200), PARTS_WIDTH, PARTS_HEIGHT, SEP_WIDTH, y);\r
+                       jSP_currentBefore.setValue(env.getCurrentBefore()/60);\r
+                       // RELOADリスナー不要\r
+                       \r
+                       //\r
+                       y+=(PARTS_HEIGHT+SEP_HEIGHT);\r
+                       CommonSwingUtils.putComponentOn(jPanel_setting, new JLabel("タイトル中のキーワードにマッチした箇所の強調色"), LABEL_WIDTH, PARTS_HEIGHT, SEP_WIDTH, y);\r
+                       CommonSwingUtils.putComponentOn(jPanel_setting, jCCL_matchedKeywordColor = new JCCLabel("強調色",env.getMatchedKeywordColor(),false,parent,ccwin), CCLABEL_WIDTH, PARTS_HEIGHT, LABEL_WIDTH+SEP_WIDTH, y);\r
+                       // RELOADリスナー不要\r
+                       \r
+                       y+=(PARTS_HEIGHT+SEP_HEIGHT);\r
+                       CommonSwingUtils.putComponentOn(jPanel_setting, jCBP_showWarnDialog = new JCheckBoxPanel("キーワード削除時に確認ダイアログを表示",LABEL_WIDTH), PARTS_WIDTH, PARTS_HEIGHT, SEP_WIDTH, y);\r
+                       jCBP_showWarnDialog.setSelected(env.getShowWarnDialog());\r
+                       // RELOADリスナー不要\r
+                       \r
+                       y+=(PARTS_HEIGHT+SEP_HEIGHT);\r
+                       CommonSwingUtils.putComponentOn(jPanel_setting, jCBP_splitMarkAndTitle = new JCheckBoxPanel("オプション表示を個別欄に分離表示",LABEL_WIDTH), PARTS_WIDTH, PARTS_HEIGHT, SEP_WIDTH, y);\r
+                       jCBP_splitMarkAndTitle.setSelected(env.getSplitMarkAndTitle());\r
+                       // RELOADリスナー不要\r
+                       \r
+                       y+=(PARTS_HEIGHT+SEP_HEIGHT);\r
+                       CommonSwingUtils.putComponentOn(jPanel_setting, jCBP_showDetailOnList = new JCheckBoxPanel("番組詳細列を表示",LABEL_WIDTH), PARTS_WIDTH, PARTS_HEIGHT, SEP_WIDTH, y);\r
+                       jCBP_showDetailOnList.setSelected(env.getShowDetailOnList());\r
+                       // RELOADリスナー不要\r
+                       \r
+                       y+=(PARTS_HEIGHT+SEP_HEIGHT);\r
+                       CommonSwingUtils.putComponentOn(jPanel_setting, jCBP_rowHeaderVisible = new JCheckBoxPanel("行番号を表示",LABEL_WIDTH), PARTS_WIDTH, PARTS_HEIGHT, SEP_WIDTH, y);\r
+                       jCBP_rowHeaderVisible.setSelected(env.getRowHeaderVisible());\r
+                       // RELOADリスナー不要\r
+                       \r
+                       y+=(PARTS_HEIGHT+SEP_HEIGHT);\r
+                       CommonSwingUtils.putComponentOn(jPanel_setting, jSP_rsvTargets = new JSliderPanel("予約待機の予約番組自動選択数",LABEL_WIDTH,1,99,200), PARTS_WIDTH, PARTS_HEIGHT, SEP_WIDTH, y);\r
+                       jSP_rsvTargets.setValue(env.getRsvTargets());\r
+                       // RELOADリスナー不要\r
+\r
+                       y+=(PARTS_HEIGHT+SEP_HEIGHT);\r
+                       CommonSwingUtils.putComponentOn(jPanel_setting, jCBX_dblClkCmd = new JComboBoxPanel("ダブルクリック時の動作",LABEL_WIDTH,250,true), PARTS_WIDTH, PARTS_HEIGHT, SEP_WIDTH, y);\r
+                       for ( DblClkCmd c : DblClkCmd.values() ) {\r
+                               jCBX_dblClkCmd.addItem(c);\r
+                       }\r
+                       jCBX_dblClkCmd.setSelectedItem(env.getDblClkCmd());\r
+                       // RELOADリスナー不要\r
+                       \r
+                       y+=(PARTS_HEIGHT+SEP_HEIGHT);\r
+                       CommonSwingUtils.putComponentOn(jPanel_setting, jSP_searchResultMax = new JSliderPanel("過去ログ検索件数の上限",LABEL_WIDTH,10,500,200), PARTS_WIDTH, PARTS_HEIGHT, SEP_WIDTH, y);\r
+                       jSP_searchResultMax.setValue(env.getSearchResultMax());\r
+                       // RELOADリスナー不要\r
+\r
+                       y+=(PARTS_HEIGHT+SEP_HEIGHT);\r
+                       CommonSwingUtils.putComponentOn(jPanel_setting, jSP_searchResultBufferMax = new JSliderPanel("過去ログ検索履歴の上限",LABEL_WIDTH,1,10,200), PARTS_WIDTH, PARTS_HEIGHT, SEP_WIDTH, y);\r
+                       jSP_searchResultBufferMax.setValue(env.getSearchResultBufferMax());\r
+                       // RELOADリスナー不要\r
+\r
+                       /*\r
+                        * 新聞形式 \r
+                        */\r
+                       \r
+                       y+=(PARTS_HEIGHT+BLOCK_SEP_HEIGHT);\r
+                       CommonSwingUtils.putComponentOn(jPanel_setting, new JLabel("<<<新聞形式>>>"), LABEL_WIDTH, PARTS_HEIGHT, SEP_WIDTH, y);\r
+                       \r
+                       {\r
+                               y+=(PARTS_HEIGHT+SEP_HEIGHT);\r
+                               int paperredraw_h = PARTS_HEIGHT*3+SEP_HEIGHT_NALLOW*2;\r
+                               CommonSwingUtils.putComponentOn(jPanel_setting, jRBP_getPaperRedrawType = new JRadioButtonPanel("描画方式",LABEL_WIDTH), PARTS_WIDTH, paperredraw_h, SEP_WIDTH, y);\r
+                               jRBP_getPaperRedrawType.add(PARER_REDRAW_NORMAL, false);\r
+                               jRBP_getPaperRedrawType.add(PARER_REDRAW_CACHE, false);\r
+                               jRBP_getPaperRedrawType.add(PARER_REDRAW_PAGER, false);\r
+                               // RELOADリスナー不要\r
+                               \r
+                               y+=(paperredraw_h+SEP_HEIGHT);\r
+                               CommonSwingUtils.putComponentOn(jPanel_setting, jSP_centerPerPage = new JSliderPanel("┗ ページあたりの放送局数",LABEL_WIDTH,1,99,200), PARTS_WIDTH, PARTS_HEIGHT, SEP_WIDTH, y);\r
+                               jSP_centerPerPage.setValue(env.getCenterPerPage());\r
+                               // RELOADリスナー不要\r
+                               \r
+                               y+=(PARTS_HEIGHT+SEP_HEIGHT);\r
+                               CommonSwingUtils.putComponentOn(jPanel_setting, jCBP_allPageSnapshot = new JCheckBoxPanel("┗ スナップショットを全ページ連続で実行",LABEL_WIDTH), PARTS_WIDTH, PARTS_HEIGHT, SEP_WIDTH, y);\r
+                               jCBP_allPageSnapshot.setSelected(env.getAllPageSnapshot());\r
+                               // RELOADリスナー不要\r
+                               \r
+                               // 連動設定(1)\r
+                               jRBP_getPaperRedrawType.addItemListener(il_paperredrawtype);\r
+                               \r
+                               if ( env.isPagerEnabled() ) {\r
+                                       jRBP_getPaperRedrawType.setSelectedItem(PARER_REDRAW_PAGER);\r
+                               }\r
+                               else if ( env.getDrawcacheEnable() ) {\r
+                                       jRBP_getPaperRedrawType.setSelectedItem(PARER_REDRAW_CACHE);\r
+                               }\r
+                               else {\r
+                                       jRBP_getPaperRedrawType.setSelectedItem(PARER_REDRAW_NORMAL);\r
+                               }\r
+                       }\r
+\r
+                       y+=(PARTS_HEIGHT+SEP_HEIGHT);\r
+                       CommonSwingUtils.putComponentOn(jPanel_setting, getNoticeMsg("※切り替えがおっせーよ!忍耐の限界だよ!という場合は新聞形式の設定変更(ツールバーのパレットアイコン)で「番組詳細のフォント設定>表示する」のチェックを外してください。"), DESCRIPTION_WIDTH, PARTS_HEIGHT, SEP_WIDTH*2, y);\r
+\r
+                       {\r
+                               y+=(PARTS_HEIGHT+SEP_HEIGHT);\r
+                               CommonSwingUtils.putComponentOn(jPanel_setting, jCBP_tooltipEnable = new JCheckBoxPanel("番組表でツールチップを表示する",LABEL_WIDTH), PARTS_WIDTH, PARTS_HEIGHT, SEP_WIDTH, y);\r
+                               jCBP_tooltipEnable.setSelected( ! env.getTooltipEnable());\r
+                               // RELOADリスナー不要\r
+\r
+                               y+=(PARTS_HEIGHT+SEP_HEIGHT);\r
+                               CommonSwingUtils.putComponentOn(jPanel_setting, jSP_tooltipInitialDelay = new JSliderPanel("┗ 表示までの遅延(ミリ秒)",LABEL_WIDTH,0,3000,100,200), PARTS_WIDTH, PARTS_HEIGHT, SEP_WIDTH, y);\r
+                               jSP_tooltipInitialDelay.setValue(env.getTooltipInitialDelay());\r
+                               // RELOADリスナー不要\r
+\r
+                               y+=(PARTS_HEIGHT+SEP_HEIGHT);\r
+                               CommonSwingUtils.putComponentOn(jPanel_setting, jSP_tooltipDismissDelay = new JSliderPanel("┗ 消去までの遅延(ミリ秒)",LABEL_WIDTH,1000,60000,100,200), PARTS_WIDTH, PARTS_HEIGHT, SEP_WIDTH, y);\r
+                               jSP_tooltipDismissDelay.setValue(env.getTooltipDismissDelay());\r
+                               // RELOADリスナー不要\r
+                               \r
+                               // 連動設定\r
+\r
+                               jCBP_tooltipEnable.addItemListener(al_tooltipenable);\r
+                               \r
+                               jCBP_tooltipEnable.setSelected( ! jCBP_tooltipEnable.isSelected());\r
+                       }\r
+                       \r
+                       y+=(PARTS_HEIGHT+SEP_HEIGHT);\r
+                       CommonSwingUtils.putComponentOn(jPanel_setting, jCBP_timerbarScrollEnable = new JCheckBoxPanel("現在時刻線を固定し番組表側をスクロール",LABEL_WIDTH), PARTS_WIDTH, PARTS_HEIGHT, SEP_WIDTH, y);\r
+                       jCBP_timerbarScrollEnable.setSelected(env.getTimerbarScrollEnable());\r
+                       // RELOADリスナー不要\r
+                       \r
+                       y+=(PARTS_HEIGHT+SEP_HEIGHT);\r
+                       CommonSwingUtils.putComponentOn(jPanel_setting, jSP_passedLogLimit = new JSliderPanel("過去ログの日付ノードの表示数",LABEL_WIDTH,7,180,200), PARTS_WIDTH, PARTS_HEIGHT, SEP_WIDTH, y);\r
+                       jSP_passedLogLimit.setValue(env.getPassedLogLimit());\r
+                       // RELOADリスナー不要\r
+\r
+                       y+=(PARTS_HEIGHT+SEP_HEIGHT);\r
+                       CommonSwingUtils.putComponentOn(jPanel_setting, jCBP_effectComboToPaper = new JCheckBoxPanel("レコーダコンボボックスを新聞形式でも有効に",LABEL_WIDTH), PARTS_WIDTH, PARTS_HEIGHT, SEP_WIDTH, y);\r
+                       jCBP_effectComboToPaper.setSelected(env.getEffectComboToPaper());\r
+                       // RELOADリスナー不要\r
+                       \r
+                       \r
+                       /*\r
+                        * リスト・新聞形式共通 \r
+                        */\r
+                       \r
+                       y+=(PARTS_HEIGHT+BLOCK_SEP_HEIGHT);\r
+                       CommonSwingUtils.putComponentOn(jPanel_setting, new JLabel("<<<リスト・新聞形式共通>>>"), LABEL_WIDTH, PARTS_HEIGHT, SEP_WIDTH, y);\r
+\r
+                       y+=(PARTS_HEIGHT+SEP_HEIGHT);\r
+                       CommonSwingUtils.putComponentOn(jPanel_setting, jCBP_displayOnlyExecOnEntry = new JCheckBoxPanel("実行ONの予約のみ予約マークを表示する",LABEL_WIDTH), PARTS_WIDTH, PARTS_HEIGHT, SEP_WIDTH, y);\r
+                       jCBP_displayOnlyExecOnEntry.setSelected(env.getDisplayOnlyExecOnEntry());\r
+                       // RELOADリスナー不要\r
+                       \r
+                       y+=(PARTS_HEIGHT+SEP_HEIGHT);\r
+                       CommonSwingUtils.putComponentOn(jPanel_setting, jCBP_displayPassedReserve = new JCheckBoxPanel("当日の終了済み予約も表示する",LABEL_WIDTH), PARTS_WIDTH, PARTS_HEIGHT, SEP_WIDTH, y);\r
+                       jCBP_displayPassedReserve.setSelected(env.getDisplayPassedReserve());\r
+                       // RELOADリスナー不要\r
+\r
+                       y+=(PARTS_HEIGHT+SEP_HEIGHT);\r
+                       CommonSwingUtils.putComponentOn(jPanel_setting, jCBP_showOnlyNonrepeated = new JCheckBoxPanel("リピート放送と判定されたら番組追跡に表示しない",LABEL_WIDTH), PARTS_WIDTH, PARTS_HEIGHT, SEP_WIDTH, y);\r
+                       jCBP_showOnlyNonrepeated.setSelected(env.getShowOnlyNonrepeated());\r
+                       // RELOADリスナー不要\r
+\r
+                       y+=(PARTS_HEIGHT+SEP_HEIGHT);\r
+                       CommonSwingUtils.putComponentOn(jPanel_setting, jCBP_adjLateNight = new JCheckBoxPanel("【RD】深夜の帯予約を前にずらす(火~土→月~金)",LABEL_WIDTH), PARTS_WIDTH, PARTS_HEIGHT, SEP_WIDTH, y);\r
+                       jCBP_adjLateNight.setSelected(env.getAdjLateNight());\r
+                       // RELOADリスナー不要\r
+                       \r
+                       y+=(PARTS_HEIGHT+SEP_HEIGHT);\r
+                       CommonSwingUtils.putComponentOn(jPanel_setting, getNoticeMsg("※月~金26:00の帯番組は、実際には火~土AM2:00に放送されますので鯛ナビでもそのように帯予約を処理しています。"), DESCRIPTION_WIDTH, PARTS_HEIGHT, SEP_WIDTH*2, y);\r
+                       y+=(PARTS_HEIGHT);\r
+                       CommonSwingUtils.putComponentOn(jPanel_setting, getNoticeMsg(" しかし、RDのように月~金AM2:00で録画が実行されてしまうような場合にはこれをチェックしてください。帯予約の予約枠を月~金AM2:00で表示するようにします。"), DESCRIPTION_WIDTH, PARTS_HEIGHT, SEP_WIDTH*2, y);\r
+\r
+                       y+=(PARTS_HEIGHT+SEP_HEIGHT);\r
+                       CommonSwingUtils.putComponentOn(jPanel_setting, jCBP_rootNodeVisible = new JCheckBoxPanel("ツリーペーンにrootノードを表示させる",LABEL_WIDTH), PARTS_WIDTH, PARTS_HEIGHT, SEP_WIDTH, y);\r
+                       jCBP_rootNodeVisible.setSelected(env.getRootNodeVisible());\r
+                       // RELOADリスナー不要\r
+                       \r
+                       y+=(PARTS_HEIGHT+SEP_HEIGHT);\r
+                       CommonSwingUtils.putComponentOn(jPanel_setting, jCBP_syncTreeWidth = new JCheckBoxPanel("リスト形式と新聞形式のツリーペーンの幅を同期する",LABEL_WIDTH), PARTS_WIDTH, PARTS_HEIGHT, SEP_WIDTH, y);\r
+                       jCBP_syncTreeWidth.setSelected(env.getSyncTreeWidth());\r
+                       // RELOADリスナー不要\r
+                       \r
+                       y+=(PARTS_HEIGHT+SEP_HEIGHT);\r
+                       CommonSwingUtils.putComponentOn(jPanel_setting, jCBP_shortExtMark = new JCheckBoxPanel("「★延長注意★」を「(延)」に短縮表示",LABEL_WIDTH), PARTS_WIDTH, PARTS_HEIGHT, SEP_WIDTH, y);\r
+                       jCBP_shortExtMark.setSelected(env.getShortExtMark());\r
+                       // RELOADリスナー不要\r
+                       \r
+                       y+=(PARTS_HEIGHT+SEP_HEIGHT);\r
+                       CommonSwingUtils.putComponentOn(jPanel_setting, jCBX_snapshotFmt = new JComboBoxPanel("スナップショットの画像形式",LABEL_WIDTH,250,true), PARTS_WIDTH, PARTS_HEIGHT, SEP_WIDTH, y);\r
+                       for ( SnapshotFmt s : SnapshotFmt.values() ) {\r
+                               jCBX_snapshotFmt.addItem(s);\r
+                       }\r
+                       jCBX_snapshotFmt.setSelectedItem(env.getSnapshotFmt());\r
+                       // RELOADリスナー不要\r
+                       \r
+                       y+=(PARTS_HEIGHT+SEP_HEIGHT);\r
+                       CommonSwingUtils.putComponentOn(jPanel_setting, jCBP_printSnapshot = new JCheckBoxPanel("スナップショットボタンで印刷を実行する",LABEL_WIDTH), PARTS_WIDTH, PARTS_HEIGHT, SEP_WIDTH, y);\r
+                       jCBP_printSnapshot.setSelected(env.getPrintSnapshot());\r
+                       // RELOADリスナー不要\r
+                       \r
+                       y+=(PARTS_HEIGHT+SEP_HEIGHT);\r
+                       int marks_h = PARTS_HEIGHT*12; \r
+                       CommonSwingUtils.putComponentOn(jPanel_setting, getJLabel_showmarks("表示マークの選択"), LABEL_WIDTH, PARTS_HEIGHT, SEP_WIDTH, y);\r
+                       CommonSwingUtils.putComponentOn(jPanel_setting, getJScrollPane_showmarks(), 320, marks_h, LABEL_WIDTH+SEP_WIDTH, y);\r
+                       // RELOADリスナー不要\r
+                       \r
+                       y+=(marks_h+SEP_HEIGHT);\r
+                       int cbitems_w = 320;\r
+                       int cbitems_h = PARTS_HEIGHT*8;\r
+                       CommonSwingUtils.putComponentOn(jPanel_setting, getJLabel_clipboard("クリップボードアイテムの選択"), LABEL_WIDTH, PARTS_HEIGHT, SEP_WIDTH, y);\r
+                       CommonSwingUtils.putComponentOn(jPanel_setting, getJButton_clipboard_up("↑"), 50, PARTS_HEIGHT, LABEL_WIDTH+SEP_WIDTH+10+cbitems_w, y);\r
+                       CommonSwingUtils.putComponentOn(jPanel_setting, getJButton_clipboard_down("↓"), 50, PARTS_HEIGHT, LABEL_WIDTH+SEP_WIDTH+10+cbitems_w, y+PARTS_HEIGHT+SEP_WIDTH);\r
+                       CommonSwingUtils.putComponentOn(jPanel_setting, getJScrollPane_clipboard(), cbitems_w, cbitems_h, LABEL_WIDTH+SEP_WIDTH, y);\r
+                       // RELOADリスナー不要\r
+  \r
+                       y += (cbitems_h + SEP_HEIGHT);\r
+                       int mitable_h = PARTS_HEIGHT*8;\r
+                       {\r
+                               int col1_w = 150;\r
+                               int col2_w = 400;\r
+                               int mitable_w = col1_w+col2_w;\r
+                               CommonSwingUtils.putComponentOn(jPanel_setting, getJLabel_menuitem("右クリックメニューの実行アイテム"), LABEL_WIDTH,PARTS_HEIGHT, SEP_WIDTH, y);\r
+                               CommonSwingUtils.putComponentOn(jPanel_setting, getJTextField_mikey(),col1_w, PARTS_HEIGHT, LABEL_WIDTH+SEP_WIDTH, y);\r
+                               CommonSwingUtils.putComponentOn(jPanel_setting, getJTextField_mival(),col2_w, PARTS_HEIGHT, LABEL_WIDTH+SEP_WIDTH+col1_w, y);\r
+                               int yz = y;\r
+                               CommonSwingUtils.putComponentOn(jPanel_setting, getJButton_miadd("登録"),75, PARTS_HEIGHT, LABEL_WIDTH+SEP_WIDTH+mitable_w+SEP_WIDTH, yz);\r
+                               CommonSwingUtils.putComponentOn(jPanel_setting, getJButton_midel("削除"),75, PARTS_HEIGHT, LABEL_WIDTH+SEP_WIDTH+mitable_w+SEP_WIDTH, yz += PARTS_HEIGHT+SEP_HEIGHT_NALLOW);\r
+                               CommonSwingUtils.putComponentOn(jPanel_setting, getJButton_miup("↑"), 50, PARTS_HEIGHT, LABEL_WIDTH+SEP_WIDTH+mitable_w+SEP_WIDTH, yz += PARTS_HEIGHT+SEP_HEIGHT);\r
+                               CommonSwingUtils.putComponentOn(jPanel_setting, getJButton_midown("↓"), 50, PARTS_HEIGHT, LABEL_WIDTH+SEP_WIDTH+mitable_w+SEP_WIDTH, yz += PARTS_HEIGHT+SEP_HEIGHT_NALLOW);\r
+                               y += (PARTS_HEIGHT + SEP_HEIGHT);\r
+                               CommonSwingUtils.putComponentOn(jPanel_setting,getJScrollPane_mitable(col1_w,col2_w), mitable_w, mitable_h, LABEL_WIDTH+SEP_WIDTH, y);\r
+                               // RELOADリスナー不要\r
+                       }\r
+\r
+                       /*\r
+                        * Web番組表対応 \r
+                        */\r
+                       \r
+                       y+=(mitable_h+BLOCK_SEP_HEIGHT);\r
+                       CommonSwingUtils.putComponentOn(jPanel_setting, new JLabel("<<<Web番組表対応>>>"), LABEL_WIDTH, PARTS_HEIGHT, SEP_WIDTH, y);\r
+                       \r
+                       y+=(PARTS_HEIGHT+SEP_HEIGHT);\r
+                       CommonSwingUtils.putComponentOn(jPanel_setting, jCBP_continueTomorrow = new JCheckBoxPanel("29時をまたぐ番組を検出し同一視する",LABEL_WIDTH), PARTS_WIDTH, PARTS_HEIGHT, SEP_WIDTH, y);\r
+                       jCBP_continueTomorrow.setSelected(env.getContinueTomorrow());\r
+                       jCBP_continueTomorrow.addItemListener(IL_RELOAD_PROG_NEEDED);\r
+                       \r
+                       {\r
+                               y+=(PARTS_HEIGHT+SEP_HEIGHT);\r
+                               CommonSwingUtils.putComponentOn(jPanel_setting, jSP_cacheTimeLimit = new JSliderPanel("番組表キャッシュの有効時間(0:無制限)",LABEL_WIDTH,0,72,200), PARTS_WIDTH, PARTS_HEIGHT, SEP_WIDTH, y);\r
+                               jSP_cacheTimeLimit.setValue((env.getCacheTimeLimit()+1)%73);\r
+                               // RELOADリスナー不要\r
+\r
+                               y+=(PARTS_HEIGHT+SEP_HEIGHT);\r
+                               CommonSwingUtils.putComponentOn(jPanel_setting, jCBX_shutdownCmd = new JComboBoxPanel("┗ シャットダウンコマンド",LABEL_WIDTH,250,true), PARTS_WIDTH, PARTS_HEIGHT, SEP_WIDTH, y);\r
+                               jCBX_shutdownCmd.setEditable(true);\r
+                               jCBX_shutdownCmd.addItem(env.getShutdownCmd());\r
+                               for ( String cmd : Env.SHUTDOWN_COMMANDS ) {\r
+                                       jCBX_shutdownCmd.addItem(cmd);\r
+                               }\r
+                               // RELOADリスナー不要\r
+\r
+                               // 連動設定\r
+                               \r
+                               jSP_cacheTimeLimit.addChangeListener(cl_cachetimelimit);\r
+                               \r
+                               jSP_cacheTimeLimit.setValue(env.getCacheTimeLimit());\r
+                       }\r
+                       \r
+                       y+=(PARTS_HEIGHT+SEP_HEIGHT);\r
+                       CommonSwingUtils.putComponentOn(jPanel_setting, getNoticeMsg("※起動時に、Web番組表の再取得を自動で「実行させたくない」場合は0にしてください。"), DESCRIPTION_WIDTH, PARTS_HEIGHT, SEP_WIDTH*2, y);\r
+                       \r
+                       y+=(PARTS_HEIGHT+SEP_HEIGHT);\r
+                       CommonSwingUtils.putComponentOn(jPanel_setting, jCBP_expandTo8 = new JCheckBoxPanel("可能なら番組表を8日分取得する",LABEL_WIDTH), PARTS_WIDTH, PARTS_HEIGHT, SEP_WIDTH, y);\r
+                       jCBP_expandTo8.setSelected(env.getExpandTo8());\r
+                       jCBP_expandTo8.addItemListener(IL_RELOAD_PROG_NEEDED);\r
+\r
+                       y+=(PARTS_HEIGHT+SEP_HEIGHT);\r
+                       CommonSwingUtils.putComponentOn(jPanel_setting, jCBP_autoEventIdComplete = new JCheckBoxPanel("予約ダイアログを開いたときに自動で番組IDを取得する",LABEL_WIDTH), PARTS_WIDTH, PARTS_HEIGHT, SEP_WIDTH, y);\r
+                       jCBP_autoEventIdComplete.setSelected(env.getAutoEventIdComplete());\r
+                       // RELOADリスナー不要\r
+\r
+                       {\r
+                               y+=(PARTS_HEIGHT+SEP_HEIGHT);\r
+                               CommonSwingUtils.putComponentOn(jPanel_setting, jCBP_splitEpno = new JCheckBoxPanel("タイトルに話数が含まれる場合に以降を分離する",LABEL_WIDTH), PARTS_WIDTH, PARTS_HEIGHT, SEP_WIDTH, y);\r
+                               jCBP_splitEpno.setSelected( ! env.getSplitEpno());\r
+                               jCBP_splitEpno.addItemListener(IL_RELOAD_PROG_NEEDED);\r
+                               \r
+                               y+=(PARTS_HEIGHT+SEP_HEIGHT);\r
+                               CommonSwingUtils.putComponentOn(jPanel_setting, jCBP_traceOnlyTitle = new JCheckBoxPanel("┗ サブタイトルを番組追跡の対象から除外する",LABEL_WIDTH), PARTS_WIDTH, PARTS_HEIGHT, SEP_WIDTH, y);\r
+                               jCBP_traceOnlyTitle.setSelected(env.getTraceOnlyTitle());\r
+                               jCBP_traceOnlyTitle.addItemListener(IL_RELOAD_PROG_NEEDED);\r
+                               \r
+                               // 連動設定\r
+\r
+                               jCBP_splitEpno.addItemListener(al_splitepno);\r
+\r
+                               jCBP_splitEpno.setSelected( ! jCBP_splitEpno.isSelected());\r
+                       }\r
+                       \r
+                       y+=(PARTS_HEIGHT+SEP_HEIGHT);\r
+                       CommonSwingUtils.putComponentOn(jPanel_setting, jCBP_fixTitle = new JCheckBoxPanel("タイトル先頭の「アニメ 」を削除(NHKのみ)",LABEL_WIDTH), PARTS_WIDTH, PARTS_HEIGHT, SEP_WIDTH, y);\r
+                       jCBP_fixTitle.setSelected(env.getFixTitle());\r
+                       jCBP_fixTitle.addItemListener(IL_RELOAD_PROG_NEEDED);\r
+                       \r
+                       y +=(PARTS_HEIGHT+SEP_HEIGHT);\r
+                       CommonSwingUtils.putComponentOn(jPanel_setting, getJLabel_ngword("NGワード(;区切りで複数指定可)"), LABEL_WIDTH,PARTS_HEIGHT, SEP_WIDTH, y);\r
+                       CommonSwingUtils.putComponentOn(jPanel_setting, getJTextField_ngword(CommonUtils.joinStr(";", env.getNgword())),600, PARTS_HEIGHT, LABEL_WIDTH+SEP_WIDTH, y);\r
+                       jTextField_ngword.getDocument().addDocumentListener(DL_RELOAD_PROG_NEEDED);\r
+                       \r
+                       \r
+                       y+=(PARTS_HEIGHT+SEP_HEIGHT);\r
+                       CommonSwingUtils.putComponentOn(jPanel_setting, getJLabel_userAgent("User-Agent"), LABEL_WIDTH, PARTS_HEIGHT, SEP_WIDTH, y);\r
+                       CommonSwingUtils.putComponentOn(jPanel_setting, getJTextField_userAgent(env.getUserAgent()), 600, PARTS_HEIGHT, LABEL_WIDTH+SEP_WIDTH, y);\r
+                       // RELOADリスナー不要\r
+                       \r
+                       {\r
+                               y+=(PARTS_HEIGHT+SEP_HEIGHT);\r
+                               CommonSwingUtils.putComponentOn(jPanel_setting, jCBP_useProxy = new JCheckBoxPanel("HTTPプロキシを有効にする",LABEL_WIDTH), PARTS_WIDTH, PARTS_HEIGHT, SEP_WIDTH, y);\r
+                               jCBP_useProxy.setSelected( ! env.getUseProxy());\r
+                               // RELOADリスナー不要\r
+                       \r
+                               y+=(PARTS_HEIGHT+SEP_HEIGHT);\r
+                               CommonSwingUtils.putComponentOn(jPanel_setting, getJLabel_proxy("┗ HTTPプロキシ/ポート"), LABEL_WIDTH, PARTS_HEIGHT, SEP_WIDTH, y);\r
+                               CommonSwingUtils.putComponentOn(jPanel_setting, getJTextField_proxyAddr(env.getProxyAddr()), 200, PARTS_HEIGHT, LABEL_WIDTH+SEP_WIDTH, y);\r
+                               CommonSwingUtils.putComponentOn(jPanel_setting, getJTextField_proxyPort(env.getProxyPort()), 100, PARTS_HEIGHT, LABEL_WIDTH+SEP_WIDTH+210, y);\r
+                               // RELOADリスナー不要\r
+                               \r
+                               // 連動設定\r
+\r
+                               jCBP_useProxy.addItemListener(al_useproxy);\r
+\r
+                               jCBP_useProxy.setSelected( ! jCBP_useProxy.isSelected());\r
+                       }\r
+                       \r
+                       y+=(PARTS_HEIGHT+SEP_HEIGHT);\r
+                       CommonSwingUtils.putComponentOn(jPanel_setting, jCBP_useSyobocal = new JCheckBoxPanel("【アニメ】しょぼいカレンダーを利用する",LABEL_WIDTH), PARTS_WIDTH, PARTS_HEIGHT, SEP_WIDTH, y);\r
+                       jCBP_useSyobocal.setSelected(env.getUseSyobocal());\r
+                       jCBP_useSyobocal.addItemListener(IL_RELOAD_PROG_NEEDED);\r
+                       \r
+                       y+=(PARTS_HEIGHT+SEP_HEIGHT);\r
+                       CommonSwingUtils.putComponentOn(jPanel_setting, getNoticeMsg("※アニメなんか興味ないよ!という場合はチェックを外して再起動してください。"), DESCRIPTION_WIDTH, PARTS_HEIGHT, SEP_WIDTH*2, y);\r
+                       \r
+                       y+=(PARTS_HEIGHT+SEP_HEIGHT);\r
+                       CommonSwingUtils.putComponentOn(jPanel_setting, jCBP_historyOnlyUpdateOnce = new JCheckBoxPanel("日に一回しか新着履歴を更新しない",LABEL_WIDTH), PARTS_WIDTH, PARTS_HEIGHT, SEP_WIDTH, y);\r
+                       jCBP_historyOnlyUpdateOnce.setSelected(env.getHistoryOnlyUpdateOnce());\r
+                       // RELOADリスナー不要\r
+                       \r
+                       {\r
+                               y+=(PARTS_HEIGHT+SEP_HEIGHT);\r
+                               CommonSwingUtils.putComponentOn(jPanel_setting, jCBP_usePassedProgram = new JCheckBoxPanel("過去ログを記録する",LABEL_WIDTH), PARTS_WIDTH, PARTS_HEIGHT, SEP_WIDTH, y);\r
+                               jCBP_usePassedProgram.setSelected( ! env.getUsePassedProgram());\r
+                               // RELOADリスナー不要\r
+                               \r
+                               y+=(PARTS_HEIGHT+SEP_HEIGHT);\r
+                               CommonSwingUtils.putComponentOn(jPanel_setting, jSP_prepPassedProgramCount = new JSliderPanel("┗ 何日先のログまで過去ログ用に保存するか",LABEL_WIDTH,1,8,200), PARTS_WIDTH, PARTS_HEIGHT, SEP_WIDTH, y);\r
+                               jSP_prepPassedProgramCount.setValue(env.getPrepPassedProgramCount());\r
+                               // RELOADリスナー不要\r
+                               \r
+                               y+=(PARTS_HEIGHT+SEP_HEIGHT);\r
+                               CommonSwingUtils.putComponentOn(jPanel_setting, getNoticeMsg("※保存期間を4日先までにして1週間旅行に出かけると7日-4日=3日分の過去ログがロストすることになります。"), DESCRIPTION_WIDTH, PARTS_HEIGHT, SEP_WIDTH*2, y);\r
+                               \r
+                               // 連動設定\r
+                               \r
+                               jCBP_usePassedProgram.addItemListener(al_usepassedprogram);\r
+                               \r
+                               jCBP_usePassedProgram.setSelected( ! jCBP_usePassedProgram.isSelected());\r
+                       }\r
+                       \r
+                       /*\r
+                        * レコーダ対応 \r
+                        */\r
+\r
+                       y+=(PARTS_HEIGHT+BLOCK_SEP_HEIGHT);\r
+                       CommonSwingUtils.putComponentOn(jPanel_setting, new JLabel("<<<レコーダ対応>>>"), LABEL_WIDTH, PARTS_HEIGHT, SEP_WIDTH, y);\r
+                       \r
+                       y+=(PARTS_HEIGHT+SEP_HEIGHT);\r
+                       int getdetail_h = PARTS_HEIGHT*3+SEP_HEIGHT_NALLOW*2;\r
+                       CommonSwingUtils.putComponentOn(jPanel_setting, jRBP_getRdReserveDetails = new JRadioButtonPanel("予約詳細情報の取得",LABEL_WIDTH), PARTS_WIDTH, getdetail_h, SEP_WIDTH, y);\r
+                       jRBP_getRdReserveDetails.add("毎回確認する",! (env.getForceGetRdReserveDetails() || env.getNeverGetRdReserveDetails()));\r
+                       jRBP_getRdReserveDetails.add("常に取得する",(env.getForceGetRdReserveDetails()));\r
+                       jRBP_getRdReserveDetails.add("常に取得しない",(env.getNeverGetRdReserveDetails()));\r
+                       jRBP_getRdReserveDetails.addItemListener(IL_RELOAD_PROG_NEEDED);\r
+                       \r
+                       y+=(getdetail_h+SEP_HEIGHT);\r
+                       CommonSwingUtils.putComponentOn(jPanel_setting, jCBP_skipGetRdRecorded = new JCheckBoxPanel("予約取得ボタンでは録画結果を取得しない",LABEL_WIDTH), PARTS_WIDTH, PARTS_HEIGHT, SEP_WIDTH, y);\r
+                       jCBP_skipGetRdRecorded.setSelected(env.getSkipGetRdRecorded());\r
+                       \r
+                       y+=(PARTS_HEIGHT+SEP_HEIGHT);\r
+                       CommonSwingUtils.putComponentOn(jPanel_setting, jCBX_recordedSaveScope = new JComboBoxPanel("録画結果一覧の保存期間",LABEL_WIDTH,250,true), PARTS_WIDTH, PARTS_HEIGHT, SEP_WIDTH, y);\r
+                       jCBX_recordedSaveScope.addItem("保存しない");\r
+                       for ( int n=1; n<=HDDRecorder.SCOPEMAX; n++ ) {\r
+                               jCBX_recordedSaveScope.addItem(String.format("%d日 (%d週)",n,(n/7)+1));\r
+                       }\r
+                       jCBX_recordedSaveScope.setSelectedIndex(env.getRecordedSaveScope());\r
+                       \r
+                       // RELOADリスナー不要\r
+                       y+=(getdetail_h+SEP_HEIGHT);\r
+                       CommonSwingUtils.putComponentOn(jPanel_setting, new JLabel("NULLプラグインでのカレンダ連携設定はレコーダ設定に移動しました"), PARTS_WIDTH, PARTS_HEIGHT, SEP_WIDTH, y);\r
+                       \r
+                       /*\r
+                        * 予約\r
+                        */\r
+\r
+                       y+=(75+BLOCK_SEP_HEIGHT);\r
+                       CommonSwingUtils.putComponentOn(jPanel_setting, new JLabel("<<<予約>>>"), LABEL_WIDTH, PARTS_HEIGHT, SEP_WIDTH, y);\r
+                       \r
+                       y+=(PARTS_HEIGHT+SEP_HEIGHT);\r
+                       CommonSwingUtils.putComponentOn(jPanel_setting, jSP_spoex_extend = new JSliderPanel("延長警告の録画時間延長幅(分)",LABEL_WIDTH,0,180,200), PARTS_WIDTH, PARTS_HEIGHT, SEP_WIDTH, y);\r
+                       jSP_spoex_extend.setValue(Integer.valueOf(env.getSpoexLength()));\r
+                       // RELOADリスナー不要\r
+                       \r
+                       y+=(PARTS_HEIGHT+SEP_HEIGHT);\r
+                       int ovarlap_h = PARTS_HEIGHT*2+SEP_HEIGHT_NALLOW*1;\r
+                       CommonSwingUtils.putComponentOn(jPanel_setting, jRBP_overlapUp = new JRadioButtonPanel("録画時間の前",LABEL_WIDTH), PARTS_WIDTH, ovarlap_h, SEP_WIDTH, y);\r
+                       jRBP_overlapUp.add("なにもしない",! (env.getOverlapUp()));\r
+                       jRBP_overlapUp.add("1分前倒し",(env.getOverlapUp()));\r
+                       // RELOADリスナー不要\r
+\r
+                       y+=(ovarlap_h+SEP_HEIGHT);\r
+                       int ovarlap2_h = PARTS_HEIGHT*3+SEP_HEIGHT_NALLOW*2;\r
+                       CommonSwingUtils.putComponentOn(jPanel_setting, jRBP_overlapDown = new JRadioButtonPanel("録画時間の後ろ",LABEL_WIDTH), PARTS_WIDTH, ovarlap2_h, SEP_WIDTH, y);\r
+                       jRBP_overlapDown.add("なにもしない",! (env.getOverlapDown() || env.getOverlapDown2()));\r
+                       jRBP_overlapDown.add("1分延ばす",(env.getOverlapDown()));\r
+                       jRBP_overlapDown.add("1分短縮(NHK以外)",(env.getOverlapDown2()));\r
+                       // RELOADリスナー不要\r
+\r
+                       y+=(ovarlap2_h+SEP_HEIGHT);\r
+                       CommonSwingUtils.putComponentOn(jPanel_setting, getJLabel_autoFolderSelect("自動フォルダ選択を有効にする"), LABEL_WIDTH, PARTS_HEIGHT, SEP_WIDTH, y);\r
+                       CommonSwingUtils.putComponentOn(jPanel_setting, getJCheckBox_autoFolderSelect(env.getAutoFolderSelect()), 100, PARTS_HEIGHT, LABEL_WIDTH+SEP_WIDTH, y);\r
+                       // RELOADリスナー不要\r
+\r
+                       y+=(PARTS_HEIGHT+SEP_HEIGHT);\r
+                       CommonSwingUtils.putComponentOn(jPanel_setting, getJLabel_enableCHAVsetting("AV自動設定キーをジャンルからCHに"), LABEL_WIDTH, PARTS_HEIGHT, SEP_WIDTH, y);\r
+                       CommonSwingUtils.putComponentOn(jPanel_setting, getJCheckBox_enableCHAVsetting(env.getEnableCHAVsetting()), 100, PARTS_HEIGHT, LABEL_WIDTH+SEP_WIDTH, y);\r
+                       // RELOADリスナー不要\r
+                       \r
+                       y+=(PARTS_HEIGHT+SEP_HEIGHT);\r
+                       CommonSwingUtils.putComponentOn(jPanel_setting, jSP_rangeLikeRsv = new JSliderPanel("類似予約の検索時間範囲(0:無制限)",LABEL_WIDTH,0,24,200), PARTS_WIDTH, PARTS_HEIGHT, SEP_WIDTH, y);\r
+                       jSP_rangeLikeRsv.setValue(env.getRangeLikeRsv());\r
+                       // RELOADリスナー不要\r
+                       \r
+                       {\r
+                               y+=(PARTS_HEIGHT+SEP_HEIGHT);\r
+                               CommonSwingUtils.putComponentOn(jPanel_setting, jCBP_givePriorityToReserved = new JCheckBoxPanel("類似予約がある場合は情報を引き継ぐ",LABEL_WIDTH), PARTS_WIDTH, PARTS_HEIGHT, SEP_WIDTH, y);\r
+                               jCBP_givePriorityToReserved.setSelected( ! env.getGivePriorityToReserved());\r
+                               // RELOADリスナー不要\r
+\r
+                               y+=(PARTS_HEIGHT+SEP_HEIGHT);\r
+                               CommonSwingUtils.putComponentOn(jPanel_setting, jCBP_givePriorityToReservedTitle = new JCheckBoxPanel("┗ 類似予約のタイトルを引き継ぐ",LABEL_WIDTH), PARTS_WIDTH, PARTS_HEIGHT, SEP_WIDTH, y);\r
+                               jCBP_givePriorityToReservedTitle.setSelected(env.getGivePriorityToReservedTitle());\r
+                               // RELOADリスナー不要\r
+                               \r
+                               // 連動設定\r
+\r
+                               jCBP_givePriorityToReserved.addItemListener(al_giveprioritytoreserved);\r
+                               \r
+                               jCBP_givePriorityToReserved.setSelected( ! jCBP_givePriorityToReserved.isSelected());\r
+                       }\r
+                       \r
+                       y+=(PARTS_HEIGHT+SEP_HEIGHT);\r
+                       CommonSwingUtils.putComponentOn(jPanel_setting, jCBP_adjoiningNotRepetition = new JCheckBoxPanel("終了時刻と開始時刻が重なる番組でも重複扱いしない",LABEL_WIDTH), PARTS_WIDTH, PARTS_HEIGHT, SEP_WIDTH, y);\r
+                       jCBP_adjoiningNotRepetition.setSelected(env.getAdjoiningNotRepetition());\r
+                       // RELOADリスナー不要\r
+                       \r
+                       {\r
+                               y+=(PARTS_HEIGHT+SEP_HEIGHT);\r
+                               CommonSwingUtils.putComponentOn(jPanel_setting, jCBP_rsv_showallite = new JCheckBoxPanel("予約一覧で繰り返し予約を展開する",LABEL_WIDTH), PARTS_WIDTH, PARTS_HEIGHT, SEP_WIDTH, y);\r
+                               jCBP_rsv_showallite.setSelected( ! env.getShowAllIterationItem());\r
+                               // RELOADリスナー不要\r
+                               \r
+                               y+=(PARTS_HEIGHT+SEP_HEIGHT);\r
+                               CommonSwingUtils.putComponentOn(jPanel_setting, jLabel_rsv_itecolor = new JLabel("┗ 展開した繰り返し予約の2回目以降の文字色"), LABEL_WIDTH, PARTS_HEIGHT, SEP_WIDTH, y);\r
+                               CommonSwingUtils.putComponentOn(jPanel_setting, jCCL_rsv_itecolor = new JCCLabel("文字色",env.getIterationItemForeground(),false,parent,ccwin), CCLABEL_WIDTH, PARTS_HEIGHT, LABEL_WIDTH+SEP_WIDTH, y);\r
+                               // RELOADリスナー不要\r
+                               \r
+                               jCBP_rsv_showallite.addItemListener(al_showallite);\r
+                               \r
+                               // Fire!\r
+                               jCBP_rsv_showallite.setSelected( ! jCBP_rsv_showallite.isSelected());\r
+                       }\r
+                       \r
+                       y+=(PARTS_HEIGHT+SEP_HEIGHT);\r
+                       CommonSwingUtils.putComponentOn(jPanel_setting, jLabel_rsv_tunshortcolor = new JLabel("チューナー不足警告の背景色"), LABEL_WIDTH, PARTS_HEIGHT, SEP_WIDTH, y);\r
+                       CommonSwingUtils.putComponentOn(jPanel_setting, jCCL_rsv_tunshortcolor = new JCCLabel("チューナー不足警告の背景色",env.getTunerShortColor(),true,parent,ccwin), CCLABEL_WIDTH, PARTS_HEIGHT, LABEL_WIDTH+SEP_WIDTH, y);\r
+                       // RELOADリスナー不要\r
+                       \r
+                       y+=(PARTS_HEIGHT+SEP_HEIGHT);\r
+                       CommonSwingUtils.putComponentOn(jPanel_setting, getNoticeMsg("※チューナー不足警告は、レコーダの予約一覧上に表示される警告情報を反映しています。"), DESCRIPTION_WIDTH, PARTS_HEIGHT, SEP_WIDTH*2, y);\r
+                       y+=(PARTS_HEIGHT);\r
+                       CommonSwingUtils.putComponentOn(jPanel_setting, getNoticeMsg("※EDCBの場合、チューナー不足警告は鯛ナビからの予約アクションでは更新されませんので、必要に応じて予約一覧の再取得を行って更新してください。"), DESCRIPTION_WIDTH, PARTS_HEIGHT, SEP_WIDTH*2, y);\r
+                       \r
+                       y+=(PARTS_HEIGHT+SEP_HEIGHT);\r
+                       CommonSwingUtils.putComponentOn(jPanel_setting, jLabel_rsv_recedcolor = new JLabel("正常録画済み(と思われる)予約の背景色"), LABEL_WIDTH, PARTS_HEIGHT, SEP_WIDTH, y);\r
+                       CommonSwingUtils.putComponentOn(jPanel_setting, jCCL_rsv_recedcolor = new JCCLabel("正常録画済み(と思われる)予約の背景色",env.getRecordedColor(),true,parent,ccwin), CCLABEL_WIDTH, PARTS_HEIGHT, LABEL_WIDTH+SEP_WIDTH, y);\r
+                       // RELOADリスナー不要\r
+                       \r
+                       y+=(PARTS_HEIGHT+SEP_HEIGHT);\r
+                       CommonSwingUtils.putComponentOn(jPanel_setting, jCBP_useAutocomplete = new JCheckBoxPanel("【RD】タイトル自動補完機能を使用する",LABEL_WIDTH), PARTS_WIDTH, PARTS_HEIGHT, SEP_WIDTH, y);\r
+                       jCBP_useAutocomplete.setSelected(env.getUseAutocomplete());\r
+                       // RELOADリスナー不要\r
+                       \r
+                       /*\r
+                        * その他 \r
+                        */\r
+\r
+                       y+=(PARTS_HEIGHT+BLOCK_SEP_HEIGHT);\r
+                       CommonSwingUtils.putComponentOn(jPanel_setting, new JLabel("<<<その他>>>"), LABEL_WIDTH, PARTS_HEIGHT, SEP_WIDTH, y);\r
+\r
+                       y+=(PARTS_HEIGHT+SEP_HEIGHT);\r
+                       CommonSwingUtils.putComponentOn(jPanel_setting, jCBX_updateMethod = new JComboBoxPanel("起動時にアップデートを確認する",LABEL_WIDTH,250,true), PARTS_WIDTH, PARTS_HEIGHT, SEP_WIDTH, y);\r
+                       for ( UpdateOn u : UpdateOn.values() ) {\r
+                               jCBX_updateMethod.addItem(u);\r
+                       }\r
+                       jCBX_updateMethod.setSelectedItem(env.getUpdateMethod());\r
+                       // RELOADリスナー不要\r
+                       \r
+                       y+=(PARTS_HEIGHT+SEP_HEIGHT);\r
+                       CommonSwingUtils.putComponentOn(jPanel_setting, jCBP_disableBeep = new JCheckBoxPanel("beep禁止",LABEL_WIDTH), PARTS_WIDTH, PARTS_HEIGHT, SEP_WIDTH, y);\r
+                       jCBP_disableBeep.setSelected(env.getDisableBeep());\r
+                       // RELOADリスナー不要\r
+\r
+                       {\r
+                               y+=(PARTS_HEIGHT+SEP_HEIGHT);\r
+                               CommonSwingUtils.putComponentOn(jPanel_setting, jCBP_showSysTray = new JCheckBoxPanel("システムトレイにアイコンを表示",LABEL_WIDTH), PARTS_WIDTH, PARTS_HEIGHT, SEP_WIDTH, y);\r
+                               jCBP_showSysTray.setSelected( ! env.getShowSysTray());\r
+                               // RELOADリスナー不要\r
+\r
+                               y+=(PARTS_HEIGHT+SEP_HEIGHT);\r
+                               CommonSwingUtils.putComponentOn(jPanel_setting, jCBP_hideToTray = new JCheckBoxPanel("┗ 最小化時はシステムトレイに隠す",LABEL_WIDTH), PARTS_WIDTH, PARTS_HEIGHT, SEP_WIDTH, y);\r
+                               jCBP_hideToTray.setSelected(env.getHideToTray());\r
+                               // RELOADリスナー不要\r
+                               \r
+                               // 連動設定\r
+\r
+                               jCBP_showSysTray.addItemListener(al_showsystray);\r
+                               \r
+                               jCBP_showSysTray.setSelected( ! jCBP_showSysTray.isSelected());\r
+                       }\r
+\r
+                       y+=(PARTS_HEIGHT+SEP_HEIGHT);\r
+                       CommonSwingUtils.putComponentOn(jPanel_setting, jCBP_onlyOneInstance = new JCheckBoxPanel("多重起動禁止(要再起動)",LABEL_WIDTH), PARTS_WIDTH, PARTS_HEIGHT, SEP_WIDTH, y);\r
+                       jCBP_onlyOneInstance.setSelected(env.getOnlyOneInstance());\r
+                       // RELOADリスナー不要\r
+                       \r
+                       y+=(PARTS_HEIGHT+SEP_HEIGHT);\r
+                       CommonSwingUtils.putComponentOn(jPanel_setting, getJLabel_lookAndFeel("ルック&フィール"), LABEL_WIDTH, PARTS_HEIGHT, SEP_WIDTH, y);\r
+                       CommonSwingUtils.putComponentOn(jPanel_setting, getJComboBox_lookAndFeel(), 250, PARTS_HEIGHT, LABEL_WIDTH+SEP_WIDTH, y);\r
+                       // RELOADリスナー不要\r
+                       \r
+                       y+=(PARTS_HEIGHT+SEP_HEIGHT);\r
+                       CommonSwingUtils.putComponentOn(jPanel_setting, getJLabel_font("表示フォント"), LABEL_WIDTH, PARTS_HEIGHT, SEP_WIDTH, y);\r
+                       CommonSwingUtils.putComponentOn(jPanel_setting, getJComboBox_font(), 250, PARTS_HEIGHT, LABEL_WIDTH+SEP_WIDTH, y);\r
+                       CommonSwingUtils.putComponentOn(jPanel_setting, getJComboBox_fontSize(), 100, PARTS_HEIGHT, LABEL_WIDTH+SEP_WIDTH+260, y);\r
+                       // RELOADリスナー不要\r
+                       \r
+                       y+=(PARTS_HEIGHT+SEP_HEIGHT);\r
+                       CommonSwingUtils.putComponentOn(jPanel_setting, new JLabel("┗ 表示サンプル"), LABEL_WIDTH, PARTS_HEIGHT, SEP_WIDTH, y);\r
+                       CommonSwingUtils.putComponentOn(jPanel_setting, getJLabel_fontSample(""), 360, PARTS_HEIGHT, LABEL_WIDTH+SEP_WIDTH, y);\r
+                       \r
+                       y+=(PARTS_HEIGHT+SEP_HEIGHT);\r
+                       CommonSwingUtils.putComponentOn(jPanel_setting, jCBP_useGTKRC = new JCheckBoxPanel("鯛ナビ専用のgtkrcを使う",LABEL_WIDTH), PARTS_WIDTH, PARTS_HEIGHT, SEP_WIDTH, y);\r
+                       jCBP_useGTKRC.setSelected(env.getUseGTKRC());\r
+                       \r
+                       y+=(PARTS_HEIGHT+SEP_HEIGHT);\r
+                       CommonSwingUtils.putComponentOn(jPanel_setting, getNoticeMsg("※ルック&フィールがGTKの場合は再起動するまで表示フォントの設定は反映されません(@see env/_gtkrc-2.0)"), DESCRIPTION_WIDTH, PARTS_HEIGHT, SEP_WIDTH*2, y);\r
+                       \r
+                       y+=(PARTS_HEIGHT+SEP_HEIGHT);\r
+                       CommonSwingUtils.putComponentOn(jPanel_setting, jCBP_useRundll32 = new JCheckBoxPanel("【Win】ファイルオープンにrundll32を使用する",LABEL_WIDTH), PARTS_WIDTH, PARTS_HEIGHT, SEP_WIDTH, y);\r
+                       jCBP_useRundll32.setSelected(env.getUseRundll32());\r
+                       // RELOADリスナー不要\r
+                       \r
+                       y+=(PARTS_HEIGHT+SEP_HEIGHT);\r
+                       CommonSwingUtils.putComponentOn(jPanel_setting, jCBP_debug = new JCheckBoxPanel("【注意】デバッグログ出力を有効にする",LABEL_WIDTH), PARTS_WIDTH, PARTS_HEIGHT, SEP_WIDTH, y);\r
+                       jCBP_debug.setSelected(env.getDebug());\r
+                       // RELOADリスナー不要\r
+\r
+                       y += (PARTS_HEIGHT + 50);\r
+                       \r
+                       // 画面の全体サイズを決める\r
+                       Dimension d = new Dimension(PANEL_WIDTH,y);\r
+                       jPanel_setting.setPreferredSize(d);\r
+               }\r
+               \r
+               return jPanel_setting;\r
+       }\r
+       \r
+       /*******************************************************************************\r
+        * アクション\r
+        ******************************************************************************/\r
+       \r
+       // 更新確定ボタン押下時の処理\r
+       private void updateEnvs() {\r
+               \r
+               TatCount tc = new TatCount();\r
+               \r
+               StWin.clear();\r
+               \r
+               new SwingBackgroundWorker(false) {\r
+                       \r
+                       @Override\r
+                       protected Object doWorks() throws Exception {\r
+                               \r
+                               StWin.appendMessage(MSGID+"設定を保存します");\r
+                               \r
+                               int idx;\r
+       \r
+                               // リスト形式\r
+                               env.setDisableFazzySearch(jCBP_disableFazzySearch.isSelected());\r
+                               env.setDisableFazzySearchReverse(jCBP_disableFazzySearchReverse.isSelected());\r
+                               env.setDefaultFazzyThreshold(jSP_defaultFazzyThreshold.getValue());\r
+                               env.setSyoboFilterByCenters(jCBP_syoboFilterByCenters.isSelected());\r
+                               env.setDisplayPassedEntry(jCBP_displayPassedEntry.isSelected());\r
+                               env.setShowRsvPickup(jCBP_showRsvPickup.isSelected());\r
+                               env.setShowRsvUra(jCBP_showRsvUra.isSelected());\r
+                               env.setRsvdLineEnhance(jCBP_rsvdLineEnhance.isSelected());\r
+                               env.setRsvdLineColor(jCCL_rsvdLineColor.getChoosed());\r
+                               env.setPickedLineColor(jCCL_pickedLineColor.getChoosed());\r
+                               env.setCurrentLineEnhance(jCBP_currentLineEnhance.isSelected());\r
+                               env.setCurrentLineColor(jCCL_currentLineColor.getChoosed());\r
+                               env.setCurrentAfter(jSP_currentAfter.getValue()*60);\r
+                               env.setCurrentBefore(jSP_currentBefore.getValue()*60);\r
+                               env.setMatchedKeywordColor(jCCL_matchedKeywordColor.getChoosed());\r
+                               env.setShowWarnDialog(jCBP_showWarnDialog.isSelected());\r
+                               env.setSplitMarkAndTitle(jCBP_splitMarkAndTitle.isSelected());\r
+                               env.setShowDetailOnList(jCBP_showDetailOnList.isSelected());\r
+                               env.setRsvTargets(jSP_rsvTargets.getValue());\r
+                               env.setRowHeaderVisible(jCBP_rowHeaderVisible.isSelected());\r
+                               env.setDblClkCmd((DblClkCmd) jCBX_dblClkCmd.getSelectedItem());\r
+                               env.setSearchResultMax(jSP_searchResultMax.getValue());\r
+                               env.setSearchResultBufferMax(jSP_searchResultBufferMax.getValue());\r
+                               \r
+                               // 新聞形式関連\r
+                               {\r
+                                       String selected = jRBP_getPaperRedrawType.getSelectedItem().getText();\r
+                                       if ( PARER_REDRAW_PAGER.equals(selected) ) {\r
+                                               env.setDrawcacheEnable(false);\r
+                                               env.setCenterPerPage(jSP_centerPerPage.getValue());\r
+                                       }\r
+                                       else if ( PARER_REDRAW_CACHE.equals(selected) ) {\r
+                                               env.setDrawcacheEnable(true);\r
+                                               env.setCenterPerPage(0);\r
+                                       }\r
+                                       else if ( PARER_REDRAW_NORMAL.equals(selected) ) {\r
+                                               env.setDrawcacheEnable(false);\r
+                                               env.setCenterPerPage(0);\r
+                                       }\r
+                               }\r
+                               env.setAllPageSnapshot(jCBP_allPageSnapshot.isSelected());\r
+                               env.setTooltipEnable(jCBP_tooltipEnable.isSelected());\r
+                               env.setTooltipInitialDelay(jSP_tooltipInitialDelay.getValue());\r
+                               env.setTooltipDismissDelay(jSP_tooltipDismissDelay.getValue());\r
+                               env.setTimerbarScrollEnable(jCBP_timerbarScrollEnable.isSelected());\r
+                               env.setPassedLogLimit(jSP_passedLogLimit.getValue());\r
+                               env.setEffectComboToPaper(jCBP_effectComboToPaper.isSelected());\r
+                               env.setSnapshotFmt((SnapshotFmt) jCBX_snapshotFmt.getSelectedItem());\r
+                               env.setPrintSnapshot(jCBP_printSnapshot.isSelected());\r
+                               \r
+                               // リスト・新聞形式共通\r
+                               env.setDisplayOnlyExecOnEntry(jCBP_displayOnlyExecOnEntry.isSelected());\r
+                               env.setDisplayPassedReserve(jCBP_displayPassedReserve.isSelected());\r
+                               env.setShowOnlyNonrepeated(jCBP_showOnlyNonrepeated.isSelected());\r
+                               env.setAdjLateNight(jCBP_adjLateNight.isSelected());\r
+                               env.setRootNodeVisible(jCBP_rootNodeVisible.isSelected());\r
+                               env.setSyncTreeWidth(jCBP_syncTreeWidth.isSelected());\r
+                               env.setShortExtMark(jCBP_shortExtMark.isSelected());\r
+                               for (int row=0; row<jTable_showmarks.getRowCount(); row++) {\r
+                                       env.getOptMarks().put((TVProgram.ProgOption) jTable_showmarks.getValueAt(row, 2), (Boolean) jTable_showmarks.getValueAt(row, 0));\r
+                               }\r
+                               for (int row=0; row<jTable_clipboard.getRowCount(); row++) {\r
+                                       cbitems.get(row).setB((Boolean)jTable_clipboard.getValueAt(row, 0));\r
+                                       cbitems.get(row).setItem((String)jTable_clipboard.getValueAt(row, 1));\r
+                                       cbitems.get(row).setId((Integer)jTable_clipboard.getValueAt(row, 2));\r
+                               }\r
+                               env.getTvCommand().removeAll(env.getTvCommand());\r
+                               for (int row = 0; row < jTable_mitable.getRowCount(); row++) {\r
+                                       TextValueSet tv = new TextValueSet();\r
+                                       tv.setText((String) jTable_mitable.getValueAt(row, 0));\r
+                                       tv.setValue((String) jTable_mitable.getValueAt(row, 1));\r
+                                       env.getTvCommand().add(tv);\r
+                               }\r
+                               \r
+                               // Web番組表対応\r
+                               env.setContinueTomorrow(jCBP_continueTomorrow.isSelected());\r
+                               env.setCacheTimeLimit(jSP_cacheTimeLimit.getValue());\r
+                               if ( env.getCacheTimeLimit() == 0 ) {\r
+                                       env.setShutdownCmd((String) jCBX_shutdownCmd.getSelectedItem());\r
+                               }\r
+                               else {\r
+                                       env.setShutdownCmd("");\r
+                               }\r
+                               env.setExpandTo8(jCBP_expandTo8.isSelected());\r
+                               //env.setUseDetailCache(jCBP_useDetailCache.isSelected());\r
+                               env.setUseDetailCache(false);\r
+                               env.setAutoEventIdComplete(jCBP_autoEventIdComplete.isSelected());\r
+                               env.setSplitEpno(jCBP_splitEpno.isSelected());\r
+                               env.setTraceOnlyTitle(jCBP_traceOnlyTitle.isSelected());\r
+                               env.setFixTitle(jCBP_fixTitle.isSelected());\r
+                               env.setNgword(jTextField_ngword.getText());\r
+                               env.setUserAgent(jTextField_userAgent.getText());\r
+                               env.setUseProxy(jCBP_useProxy.isSelected());\r
+                               env.setProxyAddr((String)jTextField_proxyAddr.getText());\r
+                               env.setProxyPort((String)jTextField_proxyPort.getText());\r
+                               env.setUseSyobocal(jCBP_useSyobocal.isSelected());\r
+                               env.setHistoryOnlyUpdateOnce(jCBP_historyOnlyUpdateOnce.isSelected());\r
+                               env.setUsePassedProgram(jCBP_usePassedProgram.isSelected());\r
+                               env.setPrepPassedProgramCount(jSP_prepPassedProgramCount.getValue());\r
+                               \r
+                               // レコーダ対応\r
+                               idx = jRBP_getRdReserveDetails.getSelectedIndex();\r
+                               switch (idx) {\r
+                               case 1:\r
+                                       env.setForceGetRdReserveDetails(true);\r
+                                       env.setNeverGetRdReserveDetails(false);\r
+                                       break;\r
+                               case 2:\r
+                                       env.setForceGetRdReserveDetails(false);\r
+                                       env.setNeverGetRdReserveDetails(true);\r
+                                       break;\r
+                               default:\r
+                                       env.setForceGetRdReserveDetails(false);\r
+                                       env.setNeverGetRdReserveDetails(false);\r
+                                       break;\r
+                               }\r
+                               env.setSkipGetRdRecorded(jCBP_skipGetRdRecorded.isSelected());\r
+                               env.setRecordedSaveScope(jCBX_recordedSaveScope.getSelectedIndex());\r
+       \r
+                               // 予約\r
+                               env.setSpoexLength(String.format("%d",jSP_spoex_extend.getValue()));\r
+                               idx = jRBP_overlapUp.getSelectedIndex();\r
+                               switch (idx) {\r
+                               case 1:\r
+                                       env.setOverlapUp(true);\r
+                                       break;\r
+                               default:\r
+                                       env.setOverlapUp(false);\r
+                                       break;\r
+                               }\r
+                               idx = jRBP_overlapDown.getSelectedIndex();\r
+                               switch (idx) {\r
+                               case 1:\r
+                                       env.setOverlapDown(true);\r
+                                       env.setOverlapDown2(false);\r
+                                       break;\r
+                               case 2:\r
+                                       env.setOverlapDown(false);\r
+                                       env.setOverlapDown2(true);\r
+                                       break;\r
+                               default:\r
+                                       env.setOverlapDown(false);\r
+                                       env.setOverlapDown2(false);\r
+                                       break;\r
+                               }\r
+                               env.setAutoFolderSelect(jCheckBox_autoFolderSelect.isSelected());\r
+                               env.setEnableCHAVsetting(jCheckBox_enableCHAVsetting.isSelected());\r
+                               env.setRangeLikeRsv(jSP_rangeLikeRsv.getValue());\r
+                               env.setGivePriorityToReserved(jCBP_givePriorityToReserved.isSelected());\r
+                               env.setGivePriorityToReservedTitle(jCBP_givePriorityToReservedTitle.isSelected());\r
+                               env.setAdjoiningNotRepetition(jCBP_adjoiningNotRepetition.isSelected());\r
+                               env.setShowAllIterationItem(jCBP_rsv_showallite.isSelected());\r
+                               env.setIterationItemForeground(jCCL_rsv_itecolor.getChoosed());\r
+                               env.setTunerShortColor(jCCL_rsv_tunshortcolor.getChoosed());\r
+                               env.setRecordedColor(jCCL_rsv_recedcolor.getChoosed());\r
+                               env.setUseAutocomplete(jCBP_useAutocomplete.isSelected());\r
+                               \r
+                               // その他の設定\r
+                               env.setUpdateMethod((UpdateOn) jCBX_updateMethod.getSelectedItem());\r
+                               env.setDisableBeep(jCBP_disableBeep.isSelected());\r
+                               env.setShowSysTray(jCBP_showSysTray.isSelected());\r
+                               env.setHideToTray(jCBP_hideToTray.isSelected());\r
+                               env.setOnlyOneInstance(jCBP_onlyOneInstance.isSelected());\r
+                               env.setLookAndFeel((String) jComboBox_lookAndFeel.getSelectedItem());\r
+                               env.setFontName((String) jComboBox_font.getSelectedItem());\r
+                               env.setFontSize(Integer.valueOf((String) jComboBox_fontSize.getSelectedItem()));\r
+                               env.setUseGTKRC(jCBP_useGTKRC.isSelected());\r
+                               env.setUseRundll32(jCBP_useRundll32.isSelected());\r
+                               env.setDebug(jCBP_debug.isSelected());\r
+       \r
+                               // 設定保存\r
+                               setEnv(reload_prog_needed);\r
+                               setUpdateButtonEnhanced(false);\r
+       \r
+                               return null;\r
+                       }\r
+                       \r
+                       @Override\r
+                       protected void doFinally() {\r
+                               StWinSetVisible(false);\r
+                       }\r
+               }.execute();\r
+\r
+               StWinSetLocationCenter(parent);\r
+               StWinSetVisible(true);\r
+               \r
+               MWin.appendMessage(String.format(MSGID+"更新が完了しました。所要時間: %.2f秒",tc.end()));\r
+       }\r
+       \r
+       /**\r
+        * 各種設定タブ以外で変更したenvの内容をタブに反映する\r
+        */\r
+       public void updateSelections() {\r
+               if ( ! env.getForceGetRdReserveDetails() && ! env.getNeverGetRdReserveDetails() ) {\r
+                       jRBP_getRdReserveDetails.setSelectedIndex(0);\r
+               }\r
+               else if ( env.getForceGetRdReserveDetails() ) {\r
+                       jRBP_getRdReserveDetails.setSelectedIndex(1);\r
+               }\r
+               else if ( env.getNeverGetRdReserveDetails() ) {\r
+                       jRBP_getRdReserveDetails.setSelectedIndex(2);\r
+               }\r
+       }\r
+       \r
+       /*******************************************************************************\r
+        * リスナー\r
+        ******************************************************************************/\r
+       \r
+       /*\r
+        * 連動\r
+        */\r
+       \r
+       /**\r
+        * 変更があった場合に番組表のリロードを要求するコンポーネントにつけるリスナー\r
+        */\r
+       \r
+       private void setUpdateButtonEnhanced(boolean b) {\r
+               if (b) {\r
+                       jButton_update.setText("更新時番組表再取得あり");\r
+                       jButton_update.setForeground(Color.RED);\r
+               }\r
+               else {\r
+                       jButton_update.setText("更新を確定する");\r
+                       jButton_update.setForeground(Color.BLACK);\r
+               }\r
+               reload_prog_needed = b;\r
+       }\r
+       \r
+       private final ItemListener IL_RELOAD_PROG_NEEDED = new ItemListener() {\r
+               @Override\r
+               public void itemStateChanged(ItemEvent e) {\r
+                       if (debug) System.err.println(DBGID+"MODIFIED");\r
+                       setUpdateButtonEnhanced(true);\r
+               }\r
+       };\r
+       private final DocumentListener DL_RELOAD_PROG_NEEDED = new DocumentListener() {\r
+               @Override\r
+               public void removeUpdate(DocumentEvent e) {\r
+                       if (debug) System.err.println(DBGID+"MODIFIED");\r
+                       setUpdateButtonEnhanced(true);\r
+               }\r
+               @Override\r
+               public void insertUpdate(DocumentEvent e) {\r
+                       if (debug) System.err.println(DBGID+"MODIFIED");\r
+                       setUpdateButtonEnhanced(true);\r
+               }\r
+               @Override\r
+               public void changedUpdate(DocumentEvent e) {\r
+                       if (debug) System.err.println(DBGID+"MODIFIED");\r
+                       setUpdateButtonEnhanced(true);\r
+               }\r
+       };\r
+       \r
+       // あいまい検索\r
+       ItemListener al_fazzysearch = new ItemListener() {\r
+               @Override\r
+               public void itemStateChanged(ItemEvent e) {\r
+                       if (debug) System.out.println("Fire! al_fazzysearch");\r
+                       if (jCBP_disableFazzySearch.isSelected()) {\r
+                               jCBP_disableFazzySearchReverse.setEnabled(false);\r
+                               jSP_defaultFazzyThreshold.setEnabled(false);\r
+                       }\r
+                       else {\r
+                               jCBP_disableFazzySearchReverse.setEnabled(true);\r
+                               jSP_defaultFazzyThreshold.setEnabled(true);\r
+                       }\r
+               }\r
+       };\r
+\r
+       // 予約行の背景色\r
+       ItemListener al_rsvdlineenhance = new ItemListener() {\r
+               @Override\r
+               public void itemStateChanged(ItemEvent e) {\r
+                       if (debug) System.out.println("Fire! al_rsvdlineenhance");\r
+                       if (jCBP_rsvdLineEnhance.isSelected()) {\r
+                               jLabel_rsvdLineColor.setEnabled(true);\r
+                               jCCL_rsvdLineColor.setEnabled(true);\r
+                               jLabel_pickedLineColor.setEnabled(true);\r
+                               jCCL_pickedLineColor.setEnabled(true);\r
+                               \r
+                       }\r
+                       else {\r
+                               jLabel_rsvdLineColor.setEnabled(false);\r
+                               jCCL_rsvdLineColor.setEnabled(false);\r
+                               jLabel_pickedLineColor.setEnabled(false);\r
+                               jCCL_pickedLineColor.setEnabled(false);\r
+                       }\r
+               }\r
+       };\r
+       \r
+       // 現在放送中行の背景色\r
+       ItemListener al_currentlineenhance = new ItemListener() {\r
+               @Override\r
+               public void itemStateChanged(ItemEvent e) {\r
+                       if (debug) System.out.println("Fire! al_currentlineenhance");\r
+                       if (jCBP_currentLineEnhance.isSelected()) {\r
+                               jLabel_currentLineColor.setEnabled(true);\r
+                               jCCL_currentLineColor.setEnabled(true);\r
+                               \r
+                       }\r
+                       else {\r
+                               jLabel_currentLineColor.setEnabled(false);\r
+                               jCCL_currentLineColor.setEnabled(false);\r
+                       }\r
+               }\r
+       };\r
+\r
+       ItemListener il_paperredrawtype = new ItemListener() {\r
+               @Override\r
+               public void itemStateChanged(ItemEvent e) {\r
+                       if (debug) System.out.println("Fire! il_paperredrawtype "+e.toString());\r
+                       if ( e.getStateChange() == ItemEvent.SELECTED ) {\r
+                               String selected = ((JRadioButton)e.getItem()).getText();\r
+                               if ( selected.equals(PARER_REDRAW_NORMAL) ) {\r
+                                       jSP_centerPerPage.setEnabled(false);\r
+                                       jCBP_allPageSnapshot.setEnabled(false);\r
+                               }\r
+                               else if ( selected.equals(PARER_REDRAW_CACHE) ) {\r
+                                       jSP_centerPerPage.setEnabled(false);\r
+                                       jCBP_allPageSnapshot.setEnabled(false);\r
+                               }\r
+                               else if ( selected.equals(PARER_REDRAW_PAGER) ) {\r
+                                       jSP_centerPerPage.setEnabled(true);\r
+                                       jCBP_allPageSnapshot.setEnabled(true);\r
+                                       jSP_centerPerPage.setValue(env.getCenterPerPage()>0?env.getCenterPerPage():7);\r
+                               }\r
+                       }\r
+               }\r
+       };\r
+\r
+       /*\r
+       ItemListener al_drawcacheenable = new ItemListener() {\r
+               @Override\r
+               public void itemStateChanged(ItemEvent e) {\r
+                       if (debug) System.out.println("Fire! al_drawcacheenable");\r
+                       if (jCBP_drawcacheEnable.isSelected()) {\r
+                               jCBP_pagerEnable.removeItemListener(il_pagerenable);\r
+                               jCBP_pagerEnable.setEnabled(false);\r
+                               jCBP_pagerEnable.addItemListener(il_pagerenable);\r
+                               \r
+                               jSP_centerPerPage.setEnabled(false);\r
+                               jCBP_allPageSnapshot.setEnabled(false);\r
+                       }\r
+                       else {\r
+                               jCBP_pagerEnable.removeItemListener(il_pagerenable);\r
+                               jCBP_pagerEnable.setEnabled(true);\r
+                               jCBP_pagerEnable.addItemListener(il_pagerenable);\r
+                               \r
+                               if (jCBP_pagerEnable.isSelected()) {\r
+                                       jSP_centerPerPage.setEnabled(true);\r
+                                       jCBP_allPageSnapshot.setEnabled(true);\r
+                               }\r
+                               else {\r
+                                       jSP_centerPerPage.setEnabled(false);\r
+                                       jCBP_allPageSnapshot.setEnabled(false);\r
+                               }\r
+                       }\r
+               }\r
+       };\r
+\r
+       ChangeListener cl_centerperpage = new ChangeListener() {\r
+               @Override\r
+               public void stateChanged(ChangeEvent e) {\r
+                       if (debug) System.out.println("Fire! cl_centerperpage");\r
+                       if (jSP_centerPerPage.getValue() == 0) {\r
+                               jCBP_drawcacheEnable.removeItemListener(al_drawcacheenable);\r
+                               jCBP_drawcacheEnable.setEnabled(true);\r
+                               jCBP_allPageSnapshot.setEnabled(false);\r
+                               jCBP_drawcacheEnable.addItemListener(al_drawcacheenable);\r
+                       }\r
+                       else {\r
+                               jCBP_drawcacheEnable.removeItemListener(al_drawcacheenable);\r
+                               jCBP_drawcacheEnable.setSelected(false);\r
+                               jCBP_drawcacheEnable.setEnabled(false);\r
+                               jCBP_allPageSnapshot.setEnabled(true);\r
+                               jCBP_drawcacheEnable.addItemListener(al_drawcacheenable);\r
+                       }\r
+               }\r
+       };\r
+       */\r
+       \r
+       ItemListener al_tooltipenable = new ItemListener() {\r
+               @Override\r
+               public void itemStateChanged(ItemEvent e) {\r
+                       if (debug) System.out.println("Fire! al_tooltipenable");\r
+                       if (jCBP_tooltipEnable.isSelected()) {\r
+                               jSP_tooltipInitialDelay.setEnabled(true);\r
+                               jSP_tooltipDismissDelay.setEnabled(true);\r
+                       }\r
+                       else {\r
+                               jSP_tooltipInitialDelay.setEnabled(false);\r
+                               jSP_tooltipDismissDelay.setEnabled(false);\r
+                       }\r
+               }\r
+       };\r
+\r
+       ChangeListener cl_cachetimelimit = new ChangeListener() {\r
+               @Override\r
+               public void stateChanged(ChangeEvent e) {\r
+                       if (debug) System.out.println("Fire! cl_cachetimelimit");\r
+                       if (jSP_cacheTimeLimit.getValue() == 0) {\r
+                               jCBX_shutdownCmd.setEnabled(true);\r
+                       }\r
+                       else {\r
+                               jCBX_shutdownCmd.setEnabled(false);\r
+                       }\r
+               }\r
+       };\r
+\r
+       ItemListener al_splitepno = new ItemListener() {\r
+               @Override\r
+               public void itemStateChanged(ItemEvent e) {\r
+                       if (debug) System.out.println("Fire! al_splitepno");\r
+                       if (jCBP_splitEpno.isSelected()) {\r
+                               jCBP_traceOnlyTitle.setEnabled(false);\r
+                       }\r
+                       else {\r
+                               jCBP_traceOnlyTitle.setEnabled(true);\r
+                       }\r
+               }\r
+       };\r
+\r
+       ItemListener al_useproxy = new ItemListener() {\r
+               @Override\r
+               public void itemStateChanged(ItemEvent e) {\r
+                       if (debug) System.out.println("Fire! al_useproxy");\r
+                       if (jCBP_useProxy.isSelected()) {\r
+                               jLabel_proxy.setEnabled(true);\r
+                               jTextField_proxyAddr.setEnabled(true);\r
+                               jTextField_proxyPort.setEnabled(true);\r
+                       }\r
+                       else {\r
+                               jLabel_proxy.setEnabled(false);\r
+                               jTextField_proxyAddr.setEnabled(false);\r
+                               jTextField_proxyPort.setEnabled(false);\r
+                       }\r
+               }\r
+       };\r
+\r
+       ItemListener al_usepassedprogram = new ItemListener() {\r
+               @Override\r
+               public void itemStateChanged(ItemEvent e) {\r
+                       if (debug) System.out.println("Fire! al_usepassedprogram");\r
+                       if (jCBP_usePassedProgram.isSelected()) {\r
+                               jSP_prepPassedProgramCount.setEnabled(true);\r
+                       }\r
+                       else {\r
+                               jSP_prepPassedProgramCount.setEnabled(false);\r
+                       }\r
+               }\r
+       };\r
+\r
+       ItemListener al_giveprioritytoreserved = new ItemListener() {\r
+               @Override\r
+               public void itemStateChanged(ItemEvent e) {\r
+                       if (debug) System.out.println("Fire! al_giveprioritytoreserved");\r
+                       if (jCBP_givePriorityToReserved.isSelected()) {\r
+                               jCBP_givePriorityToReservedTitle.setEnabled(true);\r
+                               jCBP_givePriorityToReservedTitle.setSelected(true);\r
+                       }\r
+                       else {\r
+                               jCBP_givePriorityToReservedTitle.setEnabled(false);\r
+                               jCBP_givePriorityToReservedTitle.setSelected(false);\r
+                       }\r
+               }\r
+       };\r
+\r
+       ItemListener al_showallite = new ItemListener() {\r
+               @Override\r
+               public void itemStateChanged(ItemEvent e) {\r
+                       if ( jCBP_rsv_showallite.isSelected() ) {\r
+                               jLabel_rsv_itecolor.setEnabled(true);\r
+                               jCCL_rsv_itecolor.setEnabled(true);\r
+                       }\r
+                       else {\r
+                               jLabel_rsv_itecolor.setEnabled(false);\r
+                               jCCL_rsv_itecolor.setEnabled(false);\r
+                       }\r
+               }\r
+       };\r
+       \r
+       ItemListener al_showsystray = new ItemListener() {\r
+               @Override\r
+               public void itemStateChanged(ItemEvent e) {\r
+                       if (debug) System.out.println(DBGID+"Fire! al_showsystray");\r
+                       if (jCBP_showSysTray.isSelected()) {\r
+                               jCBP_hideToTray.setEnabled(true);\r
+                       }\r
+                       else {\r
+                               jCBP_hideToTray.setEnabled(false);\r
+                       }\r
+               }\r
+       };\r
+       \r
+       ActionListener al_fontChanged = new ActionListener() {\r
+               @Override\r
+               public void actionPerformed(ActionEvent e) {\r
+                       \r
+                       String fn = (String) jComboBox_font.getSelectedItem();\r
+                       int fs = Integer.valueOf((String) jComboBox_fontSize.getSelectedItem());\r
+                       \r
+                       fontChanged(fn, fs);\r
+                       \r
+                       if ( jLabel_fontSample != null ) {\r
+                               Font f = jLabel_fontSample.getFont();\r
+                               jLabel_fontSample.setFont(new Font(fn,f.getStyle(),fs));\r
+                       }\r
+               }\r
+       };\r
+       \r
+       \r
+       /*******************************************************************************\r
+        * コンポーネント\r
+        ******************************************************************************/\r
+       \r
+       private JLabel getNoticeMsg(String text) {\r
+               JLabel l = new JLabel(text);\r
+               l.setForeground(NOTICEMSG_COLOR);\r
+               return l;\r
+       }\r
+       \r
+       // 更新確定ボタン\r
+       private JButton getJButton_update(String s) {\r
+               if (jButton_update == null) {\r
+                       jButton_update = new JButton(s);\r
+                       \r
+                       jButton_update.addActionListener(new ActionListener() {\r
+                               public void actionPerformed(ActionEvent e) {\r
+                                       updateEnvs();\r
+                               }\r
+                       });\r
+               }\r
+               return(jButton_update);\r
+       }\r
+       \r
+       //\r
+       private JLabel getJLabel_userAgent(String s)\r
+       {\r
+               if (jLabel_userAgent == null) {\r
+                       jLabel_userAgent = new JLabel(s);\r
+               }\r
+               return(jLabel_userAgent);\r
+       }\r
+       private JTextField getJTextField_userAgent(String s) {\r
+               if (jTextField_userAgent == null) {\r
+                       jTextField_userAgent = new JTextFieldWithPopup();\r
+                       jTextField_userAgent.setText(s);\r
+                       jTextField_userAgent.setCaretPosition(0);\r
+               }\r
+               return jTextField_userAgent;\r
+       }\r
+       \r
+       //\r
+       private JLabel getJLabel_proxy(String s)\r
+       {\r
+               if (jLabel_proxy == null) {\r
+                       jLabel_proxy = new JLabel(s);\r
+               }\r
+               return(jLabel_proxy);\r
+       }\r
+       private JTextField getJTextField_proxyAddr(String s) {\r
+               if (jTextField_proxyAddr == null) {\r
+                       jTextField_proxyAddr = new JTextFieldWithPopup();\r
+                       jTextField_proxyAddr.setText(s);\r
+                       jTextField_proxyAddr.setCaretPosition(0);\r
+               }\r
+               return jTextField_proxyAddr;\r
+       }\r
+       private JTextField getJTextField_proxyPort(String s) {\r
+               if (jTextField_proxyPort == null) {\r
+                       jTextField_proxyPort = new JTextFieldWithPopup();\r
+                       jTextField_proxyPort.setText(s);\r
+                       jTextField_proxyPort.setCaretPosition(0);\r
+               }\r
+               return jTextField_proxyPort;\r
+       }\r
+       \r
+       // 表示マークの選択\r
+       private JLabel getJLabel_showmarks(String s) {\r
+               if (jLabel_showmarks == null) {\r
+                       jLabel_showmarks = new JLabel(s);\r
+               }\r
+               return jLabel_showmarks;\r
+       }\r
+       private JScrollPane getJScrollPane_showmarks() {\r
+               if (jScrollPane_showmarks == null) {\r
+                       jScrollPane_showmarks = new JScrollPane();\r
+                       jScrollPane_showmarks.setViewportView(getJTable_showmarks());\r
+                       jScrollPane_showmarks.setVerticalScrollBarPolicy(ScrollPaneConstants.VERTICAL_SCROLLBAR_ALWAYS);\r
+               }\r
+               return(jScrollPane_showmarks);\r
+       }\r
+       private JNETable getJTable_showmarks() {\r
+               if (jTable_showmarks == null) {\r
+                       // ヘッダの設定\r
+                       String[] colname = {"チェック", "マーク", "ID"};\r
+                       int[] colwidth = {50,250,0};\r
+                       \r
+                       //\r
+                       DefaultTableModel model = new DefaultTableModel(colname, 0);\r
+                       jTable_showmarks = new JNETable(model, false) {\r
+\r
+                               private static final long serialVersionUID = 1L;\r
+\r
+                               @Override\r
+                               public boolean isCellEditable(int row, int column) {\r
+                                       return (column == 0);\r
+                               }\r
+                       };\r
+                       jTable_showmarks.setAutoResizeMode(JNETable.AUTO_RESIZE_OFF);\r
+                       DefaultTableColumnModel columnModel = (DefaultTableColumnModel)jTable_showmarks.getColumnModel();\r
+                       TableColumn column = null;\r
+                       for (int i = 0 ; i < columnModel.getColumnCount() ; i++){\r
+                               column = columnModel.getColumn(i);\r
+                               column.setPreferredWidth(colwidth[i]);\r
+                       }\r
+                       \r
+                       // にゃーん\r
+                       jTable_showmarks.setSelectionMode(ListSelectionModel.SINGLE_SELECTION);\r
+   \r
+                       // エディタに手を入れる\r
+                       DefaultCellEditor editor = new DefaultCellEditor(new JCheckBox() {\r
+\r
+                               private static final long serialVersionUID = 1L;\r
+\r
+                               @Override\r
+                               public int getHorizontalAlignment() {\r
+                                       return JCheckBox.CENTER;\r
+                               }\r
+                       });\r
+                       jTable_showmarks.getColumn("チェック").setCellEditor(editor);\r
+                       // レンダラに手を入れる\r
+                       DefaultTableCellRenderer renderer = new DefaultTableCellRenderer() {\r
+\r
+                               private static final long serialVersionUID = 1L;\r
+\r
+                               @Override\r
+                               public Component getTableCellRendererComponent(JTable table, Object value,\r
+                                               boolean isSelected, boolean hasFocus, int row, int column) {\r
+                                       //\r
+                                       JCheckBox cBox = new JCheckBox();\r
+                                       cBox.setHorizontalAlignment(JCheckBox.CENTER);\r
+                                       //\r
+                                       Boolean b = (Boolean)value;\r
+                                       cBox.setSelected(b.booleanValue());\r
+                                       //\r
+                                       if (isSelected) {\r
+                                               cBox.setBackground(table.getSelectionBackground());\r
+                                       }\r
+                                       else {\r
+                                               cBox.setBackground(table.getBackground());\r
+                                       }\r
+                                       return cBox;\r
+                               }\r
+                       };\r
+                       jTable_showmarks.getColumn("チェック").setCellRenderer(renderer);\r
+                       \r
+                       //\r
+                       for (Object[] obj : TVProgram.optMarks) {\r
+                               Entry<ProgOption,Boolean> entry = null;\r
+                               for (Entry<ProgOption,Boolean> e : env.getOptMarks().entrySet()) {\r
+                                       if (e.getKey() == obj[0]) {\r
+                                               entry = e;\r
+                                               break;\r
+                                       }\r
+                               }\r
+                               if ( entry != null ) {\r
+                                       Object[] data = { entry.getValue(),obj[1],obj[0] };\r
+                                       model.addRow(data);\r
+                               }\r
+                               else {\r
+                                       Object[] data = { Boolean.TRUE,obj[1],obj[0] };\r
+                                       model.addRow(data);\r
+                               }\r
+                       }\r
+               }\r
+               return(jTable_showmarks);\r
+       }\r
+\r
+       // クリップボードアイテムの選択\r
+       private JLabel getJLabel_clipboard(String s) {\r
+               if (jLabel_clipboard == null) {\r
+                       jLabel_clipboard = new JLabel(s);\r
+               }\r
+               return jLabel_clipboard;\r
+       }\r
+       private JButton getJButton_clipboard_up(String s) {\r
+               if (jButton_clipboard_up == null) {\r
+                       jButton_clipboard_up = new JButton(s);\r
+                       jButton_clipboard_up.addMouseListener(new MouseAdapter() {\r
+                               @Override\r
+                               public void mouseClicked(MouseEvent e) {\r
+                                       int row = 0;\r
+                                       if ((row = jTable_clipboard.getSelectedRow()) <= 0) {\r
+                                               return;\r
+                                       }\r
+                                       Object b = jTable_clipboard.getValueAt(row, 0);\r
+                                       Object item = jTable_clipboard.getValueAt(row, 1);\r
+                                       Object id = jTable_clipboard.getValueAt(row, 2);\r
+                                       \r
+                                       jTable_clipboard.setValueAt(jTable_clipboard.getValueAt(row-1, 0), row, 0);\r
+                                       jTable_clipboard.setValueAt(jTable_clipboard.getValueAt(row-1, 1), row, 1);\r
+                                       jTable_clipboard.setValueAt(jTable_clipboard.getValueAt(row-1, 2), row, 2);\r
+                                       \r
+                                       jTable_clipboard.setValueAt(b, row-1, 0);\r
+                                       jTable_clipboard.setValueAt(item, row-1, 1);\r
+                                       jTable_clipboard.setValueAt(id, row-1, 2);\r
+                                       \r
+                                       jTable_clipboard.setRowSelectionInterval(row-1, row-1);\r
+                               }\r
+                       });\r
+               }\r
+               return jButton_clipboard_up;\r
+       }\r
+       private JButton getJButton_clipboard_down(String s) {\r
+               if (jButton_clipboard_down == null) {\r
+                       jButton_clipboard_down = new JButton(s);\r
+                       jButton_clipboard_down.addMouseListener(new MouseAdapter() {\r
+                               @Override\r
+                               public void mouseClicked(MouseEvent e) {\r
+                                       int row = 0;\r
+                                       if ((row = jTable_clipboard.getSelectedRow()) >= jTable_clipboard.getRowCount()-1) {\r
+                                               return;\r
+                                       }\r
+                                       Object b = jTable_clipboard.getValueAt(row, 0);\r
+                                       Object item = jTable_clipboard.getValueAt(row, 1);\r
+                                       Object id = jTable_clipboard.getValueAt(row, 2);\r
+                                       \r
+                                       jTable_clipboard.setValueAt(jTable_clipboard.getValueAt(row+1, 0), row, 0);\r
+                                       jTable_clipboard.setValueAt(jTable_clipboard.getValueAt(row+1, 1), row, 1);\r
+                                       jTable_clipboard.setValueAt(jTable_clipboard.getValueAt(row+1, 2), row, 2);\r
+                                       \r
+                                       jTable_clipboard.setValueAt(b, row+1, 0);\r
+                                       jTable_clipboard.setValueAt(item, row+1, 1);\r
+                                       jTable_clipboard.setValueAt(id, row+1, 2);\r
+                                       \r
+                                       jTable_clipboard.setRowSelectionInterval(row+1, row+1);\r
+                               }\r
+                       });\r
+               }\r
+               return jButton_clipboard_down;\r
+       }\r
+       private JScrollPane getJScrollPane_clipboard() {\r
+               if (jScrollPane_clipboard == null) {\r
+                       jScrollPane_clipboard = new JScrollPane();\r
+                       jScrollPane_clipboard.setViewportView(getJTable_clipboard());\r
+                       jScrollPane_clipboard.setVerticalScrollBarPolicy(ScrollPaneConstants.VERTICAL_SCROLLBAR_ALWAYS);\r
+               }\r
+               return(jScrollPane_clipboard);\r
+       }\r
+       private JNETable getJTable_clipboard() {\r
+               if (jTable_clipboard == null) {\r
+                       // ヘッダの設定\r
+                       String[] colname = {"チェック", "アイテム", "ID"};\r
+                       int[] colwidth = {50,250,0};\r
+                       \r
+                       //\r
+                       DefaultTableModel model = new DefaultTableModel(colname, 0);\r
+                       jTable_clipboard = new JNETable(model, false) {\r
+\r
+                               private static final long serialVersionUID = 1L;\r
+\r
+                               @Override\r
+                               public boolean isCellEditable(int row, int column) {\r
+                                       return (column == 0);\r
+                               }\r
+                       };\r
+                       jTable_clipboard.setAutoResizeMode(JNETable.AUTO_RESIZE_OFF);\r
+                       DefaultTableColumnModel columnModel = (DefaultTableColumnModel)jTable_clipboard.getColumnModel();\r
+                       TableColumn column = null;\r
+                       for (int i = 0 ; i < columnModel.getColumnCount() ; i++){\r
+                               column = columnModel.getColumn(i);\r
+                               column.setPreferredWidth(colwidth[i]);\r
+                       }\r
+                       \r
+                       // にゃーん\r
+                       jTable_clipboard.setSelectionMode(ListSelectionModel.SINGLE_SELECTION);\r
+   \r
+                       // エディタに手を入れる\r
+                       DefaultCellEditor editor = new DefaultCellEditor(new JCheckBox() {\r
+\r
+                               private static final long serialVersionUID = 1L;\r
+\r
+                               @Override\r
+                               public int getHorizontalAlignment() {\r
+                                       return JCheckBox.CENTER;\r
+                               }\r
+                       });\r
+                       jTable_clipboard.getColumn("チェック").setCellEditor(editor);\r
+                       // レンダラに手を入れる\r
+                       DefaultTableCellRenderer renderer = new DefaultTableCellRenderer() {\r
+\r
+                               private static final long serialVersionUID = 1L;\r
+\r
+                               @Override\r
+                               public Component getTableCellRendererComponent(JTable table, Object value,\r
+                                               boolean isSelected, boolean hasFocus, int row, int column) {\r
+                                       //\r
+                                       JCheckBox cBox = new JCheckBox();\r
+                                       cBox.setHorizontalAlignment(JCheckBox.CENTER);\r
+                                       //\r
+                                       Boolean b = (Boolean)value;\r
+                                       cBox.setSelected(b.booleanValue());\r
+                                       //\r
+                                       if (isSelected) {\r
+                                               cBox.setBackground(table.getSelectionBackground());\r
+                                       }\r
+                                       else {\r
+                                               cBox.setBackground(table.getBackground());\r
+                                       }\r
+                                       return cBox;\r
+                               }\r
+                       };\r
+                       jTable_clipboard.getColumn("チェック").setCellRenderer(renderer);\r
+                       \r
+                       //\r
+                       for (ClipboardInfo cb : getCbItemEnv()) {\r
+                               Object[] data = { cb.getB(), cb.getItem(), cb.getId() };\r
+                               model.addRow(data);\r
+                       }\r
+               }\r
+               return(jTable_clipboard);\r
+       }\r
+       \r
+       // 右クリックメニューの実行コマンドの追加\r
+       private JLabel getJLabel_menuitem(String s) {\r
+               if (jLabel_menuitem == null) {\r
+                       jLabel_menuitem = new JLabel(s);\r
+               }\r
+               return jLabel_menuitem;\r
+       }\r
+       private JTextField getJTextField_mikey() {\r
+               if (jTextField_mikey == null) {\r
+                       jTextField_mikey = new JTextFieldWithPopup();\r
+               }\r
+               return (jTextField_mikey);\r
+       }\r
+       private JTextField getJTextField_mival() {\r
+               if (jTextField_mival == null) {\r
+                       jTextField_mival = new JTextFieldWithPopup();\r
+               }\r
+               return (jTextField_mival);\r
+       }\r
+       private JComponent getJButton_miadd(String s) {\r
+               if (jButton_miadd == null) {\r
+                       jButton_miadd = new JButton(s);\r
+                       jButton_miadd.addMouseListener(new MouseAdapter() {\r
+                               @Override\r
+                               public void mouseClicked(MouseEvent e) {\r
+                                       if (jTextField_mikey.getText().length() > 0 && jTextField_mival.getText().length() > 0) {\r
+                                               DefaultTableModel model = (DefaultTableModel) jTable_mitable.getModel();\r
+                                               Object[] data = { jTextField_mikey.getText(),jTextField_mival.getText() };\r
+                                               model.addRow(data);\r
+                                               jTextField_mikey.setText("");\r
+                                               jTextField_mival.setText("");\r
+                                       }\r
+                               }\r
+                       });\r
+               }\r
+               return (jButton_miadd);\r
+       }\r
+       private JComponent getJButton_midel(String s) {\r
+               if (jButton_midel == null) {\r
+                       jButton_midel = new JButton(s);\r
+                       jButton_midel.addMouseListener(new MouseAdapter() {\r
+                               @Override\r
+                               public void mouseClicked(MouseEvent e) {\r
+                                       DefaultTableModel model = (DefaultTableModel) jTable_mitable.getModel();\r
+                                       int row = 0;\r
+                                       if ((row = jTable_mitable.getSelectedRow()) >= 0) {\r
+                                               jTextField_mikey.setText((String) model.getValueAt(row, 0));\r
+                                               jTextField_mival.setText((String) model.getValueAt(row, 1));\r
+                                               model.removeRow(row);\r
+                                       }\r
+                               }\r
+                       });\r
+               }\r
+               return (jButton_midel);\r
+       }\r
+       private JButton getJButton_miup(String s) {\r
+               if (jButton_miup == null) {\r
+                       jButton_miup = new JButton(s);\r
+                       jButton_miup.addMouseListener(new MouseAdapter() {\r
+                               @Override\r
+                               public void mouseClicked(MouseEvent e) {\r
+                                       int row = 0;\r
+                                       if ((row = jTable_mitable.getSelectedRow()) <= 0) {\r
+                                               return;\r
+                                       }\r
+                                       Object name = jTable_mitable.getValueAt(row, 0);\r
+                                       Object cmd = jTable_mitable.getValueAt(row, 1);\r
+                                       \r
+                                       jTable_mitable.setValueAt(jTable_mitable.getValueAt(row-1, 0), row, 0);\r
+                                       jTable_mitable.setValueAt(jTable_mitable.getValueAt(row-1, 1), row, 1);\r
+                                       \r
+                                       jTable_mitable.setValueAt(name, row-1, 0);\r
+                                       jTable_mitable.setValueAt(cmd, row-1, 1);\r
+                                       \r
+                                       jTable_mitable.setRowSelectionInterval(row-1, row-1);\r
+                               }\r
+                       });\r
+               }\r
+               return jButton_miup;\r
+       }\r
+       private JButton getJButton_midown(String s) {\r
+               if (jButton_midown == null) {\r
+                       jButton_midown = new JButton(s);\r
+                       jButton_midown.addMouseListener(new MouseAdapter() {\r
+                               @Override\r
+                               public void mouseClicked(MouseEvent e) {\r
+                                       int row = 0;\r
+                                       if ((row = jTable_mitable.getSelectedRow()) >= jTable_mitable.getRowCount()-1) {\r
+                                               return;\r
+                                       }\r
+                                       Object name = jTable_mitable.getValueAt(row, 0);\r
+                                       Object cmd = jTable_mitable.getValueAt(row, 1);\r
+                                       \r
+                                       jTable_mitable.setValueAt(jTable_mitable.getValueAt(row+1, 0), row, 0);\r
+                                       jTable_mitable.setValueAt(jTable_mitable.getValueAt(row+1, 1), row, 1);\r
+                                       \r
+                                       jTable_mitable.setValueAt(name, row+1, 0);\r
+                                       jTable_mitable.setValueAt(cmd, row+1, 1);\r
+                                       \r
+                                       jTable_mitable.setRowSelectionInterval(row+1, row+1);\r
+                               }\r
+                       });\r
+               }\r
+               return jButton_midown;\r
+       }\r
+       private JScrollPane getJScrollPane_mitable(int col1_w, int col2_w) {\r
+               if (jScrollPane_mitable == null) {\r
+                       jScrollPane_mitable = new JScrollPane();\r
+                       jScrollPane_mitable.setViewportView(getJTable_mitable(col1_w, col2_w));\r
+                       jScrollPane_mitable.setVerticalScrollBarPolicy(ScrollPaneConstants.VERTICAL_SCROLLBAR_ALWAYS);\r
+               }\r
+               return (jScrollPane_mitable);\r
+       }\r
+       private JNETable getJTable_mitable(int col1_w, int col2_w) {\r
+               if (jTable_mitable == null) {\r
+                       // ヘッダの設定\r
+                       String[] colname = {"コマンド名", "実行するコマンド"};\r
+                       int[] colwidth = {col1_w,col2_w};\r
+                       \r
+                       //\r
+                       DefaultTableModel model = new DefaultTableModel(colname, 0);\r
+                       jTable_mitable = new JNETable(model, false) {\r
+\r
+                               private static final long serialVersionUID = 1L;\r
+\r
+                               @Override\r
+                               public boolean isCellEditable(int row, int column) {\r
+                                       return (column == 0);\r
+                               }\r
+                       };\r
+                       jTable_mitable.setAutoResizeMode(JNETable.AUTO_RESIZE_OFF);\r
+                       DefaultTableColumnModel columnModel = (DefaultTableColumnModel)jTable_mitable.getColumnModel();\r
+                       TableColumn column = null;\r
+                       for (int i = 0 ; i < columnModel.getColumnCount() ; i++){\r
+                               column = columnModel.getColumn(i);\r
+                               column.setPreferredWidth(colwidth[i]);\r
+                       }\r
+                       \r
+                       // にゃーん\r
+                       jTable_mitable.setSelectionMode(ListSelectionModel.SINGLE_SELECTION);\r
+   \r
+                       //\r
+                       for (TextValueSet tv : env.getTvCommand()) {\r
+                               Object[] data = { tv.getText(), tv.getValue() };\r
+                               model.addRow(data);\r
+                       }\r
+               }\r
+               return(jTable_mitable);\r
+       }\r
+       \r
+       //\r
+       private JLabel getJLabel_ngword(String s) {\r
+               if (jLabel_ngword == null) {\r
+                       jLabel_ngword = new JLabel(s);\r
+               }\r
+               return(jLabel_ngword);\r
+       }\r
+       private JTextField getJTextField_ngword(String s) {\r
+               if (jTextField_ngword == null) {\r
+                       jTextField_ngword = new JTextFieldWithPopup();\r
+                       jTextField_ngword.setText(s);\r
+                       jTextField_ngword.setCaretPosition(0);\r
+               }\r
+               return jTextField_ngword;\r
+       }\r
+\r
+       private JLabel getJLabel_lookAndFeel(String s) {\r
+               if (jLabel_lookAndFeel == null) {\r
+                       jLabel_lookAndFeel = new JLabel();\r
+                       jLabel_lookAndFeel.setText(s);\r
+               }\r
+               return jLabel_lookAndFeel;\r
+       }\r
+       private JComboBox getJComboBox_lookAndFeel() {\r
+               if (jComboBox_lookAndFeel == null) {\r
+                       jComboBox_lookAndFeel = new JComboBox();\r
+\r
+                       // 初期値を設定\r
+                       DefaultComboBoxModel model = new DefaultComboBoxModel();\r
+                       jComboBox_lookAndFeel.setModel(model);\r
+                       for ( String className : getLAFEnv().getNames() ) {\r
+                               Matcher ma = Pattern.compile("\\.([^\\.]+?)LookAndFeel$").matcher(className);\r
+                               if ( ma.find() ) {\r
+                                       model.addElement(ma.group(1));\r
+                               }\r
+                       }\r
+                       if ( ! env.getLookAndFeel().equals("")) {\r
+                               model.setSelectedItem(env.getLookAndFeel());\r
+                               //updateFont(env.getFontName(), env.getFontSize());\r
+                               StdAppendMessage("Set lookandfeel="+env.getLookAndFeel());\r
+                       }\r
+                       else {\r
+                               model.setSelectedItem(UIManager.getLookAndFeel().getName());\r
+                       }\r
+                       \r
+                       //\r
+                       jComboBox_lookAndFeel.addActionListener(new ActionListener() {\r
+                               public void actionPerformed(ActionEvent e) {\r
+                                       DefaultComboBoxModel model = (DefaultComboBoxModel)((JComboBox)e.getSource()).getModel();\r
+                                       lafChanged((String)model.getSelectedItem());\r
+                               }\r
+                       });\r
+\r
+               }\r
+               return jComboBox_lookAndFeel;\r
+       }\r
+       \r
+       private JLabel getJLabel_font(String s) {\r
+               if (jLabel_font == null) {\r
+                       jLabel_font = new JLabel();\r
+                       jLabel_font.setText(s);\r
+               }\r
+               return jLabel_font;\r
+       }\r
+       private JComboBox getJComboBox_font() {\r
+               if (jComboBox_font == null) {\r
+                       jComboBox_font = new JComboBox();\r
+\r
+                       // 初期値を設定\r
+                       DefaultComboBoxModel model = new DefaultComboBoxModel();\r
+                       jComboBox_font.setModel(model);\r
+                       for ( String f : getFontEnv().getNames() ) {\r
+                               model.addElement(f);\r
+                       }\r
+                       if ( ! env.getFontName().equals("")) {\r
+                               model.setSelectedItem(env.getFontName());\r
+                               //updateFont(env.getFontName(), env.getFontSize());\r
+                               //StdAppendMessage("システムのフォント: "+env.getFontName());\r
+                       }\r
+                       else {\r
+                               model.setSelectedItem(jComboBox_font.getFont().getFontName());\r
+                       }\r
+                       \r
+                       //\r
+                       jComboBox_font.addActionListener(al_fontChanged);\r
+\r
+               }\r
+               return jComboBox_font;\r
+       }\r
+       private JComboBox getJComboBox_fontSize() {\r
+               if (jComboBox_fontSize == null) {\r
+                       jComboBox_fontSize = new JComboBox();\r
+                       DefaultComboBoxModel model = new DefaultComboBoxModel();\r
+                       jComboBox_fontSize.setModel(model);\r
+                       for ( int i=6; i<=20; i++ ) {\r
+                               model.addElement(String.valueOf(i));\r
+                       }\r
+                       if ( env.getFontSize() > 0) {\r
+                               jComboBox_fontSize.setSelectedItem(String.valueOf(env.getFontSize()));\r
+                       }\r
+                       \r
+                       jComboBox_fontSize.addActionListener(al_fontChanged);\r
+               }\r
+               return(jComboBox_fontSize);\r
+       }\r
+       private JLabel getJLabel_fontSample(String s) {\r
+               if (jLabel_fontSample == null) {\r
+                       jLabel_fontSample = new JLabel();\r
+                       jLabel_fontSample.setText("0120123abcABCあいうアイウ阿伊宇○×?");\r
+                       jLabel_fontSample.setBackground(Color.WHITE);\r
+                       jLabel_fontSample.setBorder(new LineBorder(Color.BLACK));\r
+                       jLabel_fontSample.setOpaque(true);\r
+                       Font f = jLabel_fontSample.getFont();\r
+                       jLabel_fontSample.setFont(new Font(env.getFontName(),f.getStyle(),env.getFontSize()));\r
+               }\r
+               return jLabel_fontSample;\r
+       }\r
+       \r
+       private JLabel getJLabel_enableCHAVsetting(String s)\r
+       {\r
+               if (jLabel_enableCHAVsetting == null) {\r
+                       jLabel_enableCHAVsetting = new JLabel();\r
+                       jLabel_enableCHAVsetting.setText(s);\r
+               }\r
+               return(jLabel_enableCHAVsetting);\r
+       }\r
+       private JCheckBox getJCheckBox_enableCHAVsetting(boolean b) {\r
+               if (jCheckBox_enableCHAVsetting == null) {\r
+                       jCheckBox_enableCHAVsetting = new JCheckBox();\r
+                       jCheckBox_enableCHAVsetting.setSelected(b);\r
+               }\r
+               return(jCheckBox_enableCHAVsetting);\r
+       }\r
+       \r
+       private JLabel getJLabel_autoFolderSelect(String s)\r
+       {\r
+               if (jLabel_autoFolderSelect == null) {\r
+                       jLabel_autoFolderSelect = new JLabel();\r
+                       jLabel_autoFolderSelect.setText(s);\r
+               }\r
+               return(jLabel_autoFolderSelect);\r
+       }\r
+       private JCheckBox getJCheckBox_autoFolderSelect(boolean b) {\r
+               if (jCheckBox_autoFolderSelect == null) {\r
+                       jCheckBox_autoFolderSelect = new JCheckBox();\r
+                       jCheckBox_autoFolderSelect.setSelected(b);\r
+               }\r
+               return(jCheckBox_autoFolderSelect);\r
+       }\r
+\r
+\r
+       //\r
+       private JTextAreaWithPopup getJta_help() {\r
+               if ( jta_help == null ) {\r
+                       jta_help = CommonSwingUtils.getJta(this,2,0);\r
+                       jta_help.setText(TEXT_HINT);\r
+               }\r
+               return jta_help;\r
+       }\r
+}\r
diff --git a/TinyBannavi/src/tainavi/AbsToolBar.java b/TinyBannavi/src/tainavi/AbsToolBar.java
new file mode 100644 (file)
index 0000000..3d270b1
--- /dev/null
@@ -0,0 +1,1440 @@
+package tainavi;\r
+\r
+import java.awt.Component;\r
+import java.awt.Desktop;\r
+import java.awt.Dimension;\r
+import java.awt.event.ActionEvent;\r
+import java.awt.event.ActionListener;\r
+import java.awt.event.ItemEvent;\r
+import java.awt.event.ItemListener;\r
+import java.awt.event.MouseAdapter;\r
+import java.awt.event.MouseEvent;\r
+import java.awt.event.MouseListener;\r
+import java.io.IOException;\r
+import java.net.URI;\r
+import java.net.URISyntaxException;\r
+import java.util.Calendar;\r
+import java.util.GregorianCalendar;\r
+import java.util.regex.Matcher;\r
+import java.util.regex.Pattern;\r
+import java.util.regex.PatternSyntaxException;\r
+\r
+import javax.swing.ImageIcon;\r
+import javax.swing.JButton;\r
+import javax.swing.JComboBox;\r
+import javax.swing.JMenuItem;\r
+import javax.swing.JOptionPane;\r
+import javax.swing.JPopupMenu;\r
+import javax.swing.JSlider;\r
+import javax.swing.JTextField;\r
+import javax.swing.JToggleButton;\r
+import javax.swing.JToolBar;\r
+import javax.swing.event.ChangeEvent;\r
+import javax.swing.event.ChangeListener;\r
+\r
+import tainavi.HDDRecorder.RecType;\r
+import tainavi.SearchKey.TargetId;\r
+import tainavi.TVProgramIterator.IterationType;\r
+import tainavi.VWMainWindow.MWinTab;\r
+import tainavi.VWUpdate.UpdateResult;\r
+import tainavi.Viewer.LoadFor;\r
+import tainavi.Viewer.LoadRsvedFor;;\r
+\r
+/**\r
+ * ツールバーのクラス\r
+ * @version 3.16.3β Viewer.classから分離\r
+ */\r
+public abstract class AbsToolBar extends JToolBar {\r
+\r
+       private static final long serialVersionUID = 1L;\r
+\r
+       public static String getViewName() { return "ツールバー"; } \r
+\r
+       public static void setDebug(boolean b) {debug = b; }\r
+       private static boolean debug = false;\r
+       \r
+       /*******************************************************************************\r
+        * 抽象メソッド\r
+        ******************************************************************************/\r
+\r
+       // 共用部品群\r
+       \r
+       protected abstract Env getEnv();\r
+       protected abstract Bounds getBoundsEnv();\r
+       protected abstract TVProgramList getTVPrograms();\r
+       protected abstract ChannelSort getChannelSort();\r
+       protected abstract HDDRecorderList getHDDRecorders();\r
+       \r
+       protected abstract StatusWindow getStWin(); \r
+       protected abstract StatusTextArea getMWin();\r
+\r
+       protected abstract Component getParentComponent();\r
+       \r
+       protected abstract void ringBeep();\r
+       \r
+       // 親に依頼\r
+       \r
+       // リスト形式\r
+       protected abstract boolean doKeywordSerach(SearchKey search, String kStr, String sStr, boolean doFilter);\r
+       protected abstract boolean doBatchReserve();\r
+       // 新聞形式\r
+       protected abstract boolean jumpToNow();\r
+       protected abstract boolean jumpToPassed(String passed);\r
+       protected abstract boolean redrawByPager();\r
+       protected abstract void toggleMatchBorder();\r
+       protected abstract void setPaperColorDialogVisible(boolean b);\r
+       protected abstract void setPaperZoom(int n);\r
+       // 共通\r
+       protected abstract boolean recorderSelectorChanged();\r
+       protected abstract void takeSnapShot();\r
+       // メインウィンドウ\r
+       /**\r
+        * 親から呼ばないでくださいね!\r
+        * @see #setToggleShowStatusButton(boolean)\r
+        */\r
+       protected abstract void setStatusVisible(boolean b);\r
+       /**\r
+        * 親から呼ばないでくださいね!\r
+        * @see #setToggleFullScreenButton(boolean)\r
+        */\r
+       protected abstract void setFullScreen(boolean b);\r
+       protected abstract void toggleSettingTabVisible();\r
+       protected abstract boolean isTabSelected(MWinTab tab);\r
+       // 部品\r
+       protected abstract boolean addKeywordSearch(SearchKey search);\r
+       protected abstract boolean reLoadTVProgram(LoadFor lf);\r
+       protected abstract boolean reLoadRdReserve(String myself);\r
+       protected abstract boolean reLoadRdRecorded(String myself);\r
+\r
+       /*******************************************************************************\r
+        * 呼び出し元から引き継いだもの\r
+        ******************************************************************************/\r
+\r
+       private final Env env = getEnv();\r
+       private final Bounds bounds = getBoundsEnv();\r
+       private final TVProgramList tvprograms = getTVPrograms();\r
+       private final ChannelSort chsort = getChannelSort();\r
+       private final HDDRecorderList recorders = getHDDRecorders();\r
+       \r
+       private final StatusWindow StWin = getStWin();                  // これは起動時に作成されたまま変更されないオブジェクト\r
+       private final StatusTextArea MWin = getMWin();                  // これは起動時に作成されたまま変更されないオブジェクト\r
+       \r
+       private final Component parent = getParentComponent();  // これは起動時に作成されたまま変更されないオブジェクト\r
+       \r
+       /*******************************************************************************\r
+        * 定数\r
+        ******************************************************************************/\r
+       \r
+       // アイコンファイル\r
+       \r
+       private static final String ICONFILE_SEARCH                     = "icon/system-search-2.png";\r
+       private static final String ICONFILE_ADDKEYWORD         = "icon/bookmark-new-list-4.png";\r
+       private static final String ICONFILE_RELOADPROG         = "icon/internet-news-reader.png";\r
+       private static final String ICONFILE_BATCHCMD           = "icon/checkbox.png";\r
+       private static final String ICONFILE_RELOADRSV          = "icon/video-television.png";\r
+       private static final String ICONFILE_WAKEUP                     = "icon/system-shutdown-2.png";\r
+       private static final String ICONFILE_SHUTDOWN           = "icon/user-offline.png";\r
+       private static final String ICONFILE_SHOWSETTING        = "icon/system.png";\r
+       private static final String ICONFILE_STATUSHIDDEN       = "icon/view-split-top-bottom-3.png";\r
+       private static final String ICONFILE_STATUSSHOWN        = "icon/view-close.png";\r
+       private static final String ICONFILE_TOFULL                     = "icon/view-fullscreen-5.png";\r
+       private static final String ICONFILE_TOWIN                      = "icon/view-nofullscreen-3.png";\r
+       private static final String ICONFILE_SHOWMATCHBORDER= "icon/view-calendar-timeline.png";\r
+       private static final String ICONFILE_JUMPTONOW          = "icon/view-calendar-time-spent.png";\r
+       private static final String ICONFILE_PALLET                     = "icon/colorize-2.png";\r
+       private static final String ICONFILE_TIMER                      = "icon/tool_timer.png";\r
+       private static final String ICONFILE_SCREENSHOT         = "icon/camera.png";\r
+       private static final String ICONFILE_SHOWLOG            = "icon/utilities-log_viewer.png";\r
+       private static final String ICONFILE_UPDATE                     = "icon/system-software-update-2.png";\r
+       private static final String ICONFILE_HELP                       = "icon/help-browser-2.png";\r
+\r
+       private static final String ICONFILE_PULLDOWNMENU       = "icon/down-arrow.png";\r
+\r
+       // ページャーに追加する特殊選択肢\r
+       \r
+       private final String SELECTED_ALL = "すべて";\r
+       private final String SELECTED_PICKUP = "ピックアップのみ";\r
+       \r
+       // ツールチップ関連\r
+       \r
+       private static final String TIPS_KEYWORD                        = "<HTML><B>検索ボックスの書式</B><BR>検索:(オプション1) (オプション2) キーワード <BR>過去ログ検索:開始日[(YYYY/)MM/DD] 終了日[(YYYY/)MM/DD] (オプション2) キーワード<BR>過去ログ閲覧:日付[YYYY/MM/DD]<BR>※オプション1:@filter..絞込検索(過去ログは対象外)<BR>※オプション2:#title..番組名一致、#detail..番組詳細一致、なし..番組名&番組詳細一致<BR></HTML>";\r
+       private static final String TIPS_SEARCH                         = "キーワード検索 or 過去ログ閲覧";\r
+       private static final String TIPS_ADDKEYWORD                     = "キーワードリストに登録";\r
+       private static final String TIPS_PAGER                          = "ページャー";\r
+       private static final String TIPS_RELOADPROG                     = "Webから番組情報を再取得";\r
+       private static final String TIPS_BATCHRESERVATION       = "一括予約";\r
+       private static final String TIPS_RECORDERSEL            = "操作するレコーダを選択する";\r
+       private static final String TIPS_RELOADRSVED            = "レコーダから予約情報を再取得&レコーダの各種設定情報の収集";\r
+       private static final String TIPS_WAKEUP                         = "レコーダの電源を入れる";\r
+       private static final String TIPS_DOWN                           = "レコーダの電源を落とす";\r
+       private static final String TIPS_SHOWSETTING            = "設定タブを表示する";\r
+       private static final String TIPS_STATUSHIDDEN           = "ステータスエリアを表示する";\r
+       private static final String TIPS_STATUSSHOWN            = "ステータスエリアを隠す";\r
+       private static final String TIPS_TOFULL                         = "フルスクリーンモードへ";\r
+       private static final String TIPS_TOWIN                          = "ウィンドウモードへ";\r
+       private static final String TIPS_SHOWBORDER                     = "予約待機一覧を重ね合わせ表示する";\r
+       private static final String TIPS_JUMPTO                         = "新聞形式の現在日時までジャンプ";\r
+       private static final String TIPS_PAPERCOLOR                     = "新聞形式のジャンル別背景色を設定する";\r
+       private static final String TIPS_PAPERZOOM                      = "新聞形式の番組枠の高さを拡大する";\r
+       private static final String TIPS_SNAPSHOT                       = "<HTML><P>スナップショットをとる<P><B>★メモリの使用量が大きめなので、スナップショット作成後は再起動をおすすめします</B></HTML>";\r
+       private static final String TIPS_LOGVIEW                        = "ログをビューアで開く";\r
+       private static final String TIPS_UPDATE                         = "オンラインアップデートを行う";\r
+       private static final String TIPS_OPENHELP                       = "ブラウザでヘルプを開く";\r
+\r
+       // その他\r
+       \r
+       private static final String HELP_URL = "http://sourceforge.jp/projects/tainavi/wiki/FrontPage";\r
+       \r
+       private static final int OPENING_WIAT = 500;                            // まあ起動時しか使わないんですけども\r
+\r
+       // ログ関連\r
+       \r
+       private static final String MSGID = "["+getViewName()+"] ";\r
+       private static final String ERRID = "[ERROR]"+MSGID;\r
+       private static final String DBGID = "[DEBUG]"+MSGID;\r
+\r
+       /*******************************************************************************\r
+        * 部品\r
+        ******************************************************************************/\r
+\r
+       // ツールバーのコンポーネント\r
+       \r
+       private JTextFieldWithPopup jTextField_keyword = null;\r
+       private JButton jButton_search = null;\r
+       private JButton jButton_addkeyword = null;\r
+       private JWideComboBox jComboBox_select_recorder = null;\r
+       private JWideComboBox jComboBox_pager = null;\r
+       private JButton jButton_reloadprogs = null;\r
+       private JButton jButton_reloadprogmenu = null;\r
+       private JPopupMenu jPopupMenu_reloadprogmenu = null;\r
+       private JButton jButton_reloadrsved = null;\r
+       private JButton jButton_reloadrsvedmenu = null;\r
+       private JPopupMenu jPopupMenu_reloadrsvedmenu = null;\r
+       //private JButton jButton_reloadreced = null;\r
+       private JButton jButton_batchreservation = null;\r
+       private JToggleButton jToggleButton_showmatchborder = null;\r
+       private JButton jButton_moveToNow = null;\r
+       private JButton jButton_wakeup = null;\r
+       private JButton jButton_shutdown = null;\r
+       private JButton jButton_snapshot = null;\r
+       private JButton jButton_paperColors = null;\r
+       private JSlider jSlider_paperZoom = null;\r
+       private JToggleButton jToggleButton_timer = null;\r
+       private JButton jButton_logviewer = null;\r
+       private JToggleButton jToggleButton_showsetting = null;\r
+       private JToggleButton jToggleButton_showstatus = null;\r
+       private JToggleButton jToggleButton_fullScreen = null;\r
+       private JButton jButton_update = null;\r
+       private JButton jButton_help = null;\r
+       \r
+       // その他\r
+       \r
+       private boolean statusarea_shown = bounds.getShowStatus();\r
+       \r
+       /*******************************************************************************\r
+        * コンストラクタ\r
+        ******************************************************************************/\r
+       \r
+       public AbsToolBar() {\r
+               \r
+               super();\r
+               \r
+               this.add(getJTextField_keyword());\r
+               this.add(getJButton_search("キーワード検索"));\r
+               this.add(getJButton_addkeyword("キーワード一覧に登録"));\r
+               this.addSeparator(new Dimension(4,0));\r
+               this.add(getJButton_reloadprogs("番組情報再取得"));\r
+               this.add(getJButton_reloadprogmenu("番組情報再取得メニュー"));\r
+               this.add(getJToggleButton_showmatchborder("予約待機一覧を重ね合わせ表示する"));\r
+               this.add(getJButton_moveToNow("現在日時"));\r
+               //this.addSeparator(new Dimension(4,0));\r
+               this.addSeparator(new Dimension(4,0));\r
+               this.add(getJComboBox_pager());\r
+               this.addSeparator(new Dimension(6,0));\r
+               this.add(getJButton_batchreservation("一括予約"));\r
+               this.add(getJButton_reloadrsved("レコ情報再取得"));\r
+               this.add(getJButton_reloadrsvedmenu("レコ情報再取得メニュー"));\r
+               this.addSeparator(new Dimension(4,0));\r
+               this.add(getJButton_wakeup("入"));\r
+               this.add(getJButton_shutdown("切"));\r
+               this.add(getJComboBox_select_recorder());\r
+               this.addSeparator(new Dimension(6,0));\r
+               this.add(getJButton_snapshot("スナップショット"));\r
+               this.add(getJButton_paperColors("ジャンル別背景色設定"));\r
+               this.add(getJSlider_paperZoom("番組枠表示拡大"));\r
+               this.add(getJButton_logviewer("ログビューア"));\r
+               this.add(getJToggleButton_timer("タイマー"));\r
+               this.add(getJToggleButton_showsetting("設定タブを開く"));\r
+               this.addSeparator(new Dimension(4,0));\r
+               this.add(getJToggleButton_showstatus("ステータス領域"));\r
+               this.add(getJToggleButton_fullScreen("全"));\r
+               this.addSeparator(new Dimension(4,0));\r
+               this.add(getJButton_update("オンラインアップデート"));\r
+               this.add(getJButton_help("ヘルプ"));\r
+       }\r
+       \r
+       /*******************************************************************************\r
+        * アクション\r
+        ******************************************************************************/\r
+       \r
+       /**\r
+        * キーワード検索ボックスからの検索の実行\r
+        */\r
+       private void toolbarSearch() {\r
+               // 入力形式による分岐\r
+               boolean doFilter = false;\r
+               String sStr = null;\r
+               String kStr = null;\r
+               Matcher ma = Pattern.compile("^(@(.+?)[  ]+)").matcher(jTextField_keyword.getText());\r
+               if ( ma.find() ) {\r
+                       if ( ma.group(2).matches("^f(ilter)?$") ) {\r
+                               // 絞込検索\r
+                               kStr = jTextField_keyword.getText();\r
+                               kStr = kStr.substring(ma.group(1).length()-1,kStr.length()).trim();\r
+                               doFilter = true;\r
+                       }\r
+               }\r
+               else {\r
+                       ma = Pattern.compile("^(\\d\\d\\d\\d/)?(\\d\\d/\\d\\d)([  ]+((\\d\\d\\d\\d/)?\\d\\d/\\d\\d))?[   ]+").matcher(jTextField_keyword.getText());\r
+                       if (ma.find()) {\r
+                               // 過去ログ検索(範囲指定あり)\r
+                               String sD;\r
+                               if (ma.group(1) == null || ma.group(1).length() == 0) {\r
+                                       GregorianCalendar c = CommonUtils.getCalendar(0);\r
+                                       sD = String.format("%04d/%s", c.get(Calendar.YEAR), ma.group(2));\r
+                               }\r
+                               else {\r
+                                       sD = ma.group(1)+ma.group(2);\r
+                               }\r
+                               String cD = CommonUtils.getDate529(0,false);\r
+                               String pD = CommonUtils.getDate529(-86400,false);\r
+                               //String sD = ma.group(2);\r
+                               String eD;\r
+                               if (ma.group(4) == null) {\r
+                                       eD = pD;\r
+                               }\r
+                               else {\r
+                                       if (ma.group(5) == null) {\r
+                                               GregorianCalendar c = CommonUtils.getCalendar(0);\r
+                                               eD = String.format("%04d/%s", c.get(Calendar.YEAR), ma.group(4));\r
+                                       }\r
+                                       else {\r
+                                               eD = ma.group(4);\r
+                                       }\r
+                               }\r
+                               if (sD.compareTo(eD) > 0) {\r
+                                       // 開始日と終了日が逆転していたら入れ替える\r
+                                       String tD = sD;\r
+                                       sD = eD;\r
+                                       eD = tD;\r
+                               }\r
+                               if (eD.compareTo(cD) >= 0) {\r
+                                       MWin.appendError(ERRID+"[過去ログ検索] 終了日付には前日以前の日付("+pD+")を指定してください");\r
+                                       ringBeep();\r
+                                       return;\r
+                               }\r
+                               else {\r
+                                       sStr = String.format("%s-%s", sD, eD);\r
+                               }\r
+                               kStr = ma.replaceFirst("");\r
+                       }\r
+                       else {\r
+                               // 通常ログ検索\r
+                               kStr = jTextField_keyword.getText().trim();\r
+                       }\r
+               }\r
+               if ( kStr == null || kStr.matches("^[  ]*$") ) {\r
+                       // 検索キーワードがない\r
+                       doKeywordSerach(null,null,null,false);\r
+                       return;\r
+               }\r
+               \r
+               // 検索キーワードの解析\r
+               SearchKey search = decSearchKeyText(kStr);\r
+               if ( search == null ) {\r
+                       return;\r
+               }\r
+               \r
+               // 検索実行\r
+               if (search.alTarget.size() > 0) {\r
+                       doKeywordSerach(search,kStr,sStr,doFilter);\r
+               }\r
+       }\r
+       \r
+       /**\r
+        * キーワード検索ボックスに入力された字句の解析\r
+        * @param kStr キーワード検索ボックスに入力された字句\r
+        * @return\r
+        */\r
+       private SearchKey decSearchKeyText(String kStr) {\r
+               \r
+               // オプション\r
+               TargetId tId = TargetId.TITLEANDDETAIL;\r
+               while ( true ) {\r
+                       Matcher ma = Pattern.compile("^#(.+?)\\s+").matcher(kStr);\r
+                       if ( ! ma.find() ) {\r
+                               break;\r
+                       }\r
+                       \r
+                       if ( ma.group(1).matches("^t(itle)?$") ) {\r
+                               tId = TargetId.TITLE;\r
+                       }\r
+                       else if ( ma.group(1).matches("^d(etail)?$") ) {\r
+                               tId = TargetId.DETAIL;\r
+                       }\r
+                       kStr = ma.replaceFirst("");\r
+               }\r
+               \r
+               SearchKey search = new SearchKey();\r
+               \r
+               search.setLabel(kStr);\r
+               \r
+               // 完全一致\r
+               if ( kStr.matches("^\".+\"$") ) {\r
+                       search.setCondition("0");\r
+                       search.alTarget.add(tId);\r
+                       search.alContain.add("0");\r
+                       search.alKeyword_plane.add(kStr.substring(1,kStr.length()-1));\r
+                       search.alKeyword.add(kStr.substring(1,kStr.length()-1));\r
+                       search.setCaseSensitive(true);\r
+                       return search;\r
+               }\r
+               \r
+               // 正規表現\r
+               {\r
+                       search.setCondition("0");\r
+                       for (String s : kStr.split("[  ]")) {\r
+                               if ( ! s.equals("")) {\r
+                                       search.alTarget.add(tId);\r
+                                       search.alContain.add("0");\r
+                                       try {\r
+                                               search.alKeyword_regex.add(Pattern.compile("("+TraceProgram.replacePop(s)+")"));\r
+                                               search.alKeyword.add(s);\r
+                                       }\r
+                                       catch (PatternSyntaxException e) {\r
+                                               MWin.appendError(ERRID+"正規表現の文法に則っていません: "+s);\r
+                                               ringBeep();\r
+                                               return null;\r
+                                       }\r
+                               }\r
+                       }\r
+                       search.setCaseSensitive(false);\r
+                       return search;\r
+               }\r
+       }\r
+       \r
+       /**\r
+        * \r
+        */\r
+       public void setAddkeywordEnabled(boolean b) {\r
+               jButton_addkeyword.setEnabled(b);\r
+       }\r
+       \r
+       /**\r
+        * ばちー\r
+        */\r
+       public void setBatchReservationEnabled(boolean b) {\r
+               jButton_batchreservation.setEnabled(b);\r
+       }\r
+       \r
+       /**\r
+        * すなー\r
+        */\r
+       public void setSnapShotEnabled(boolean b) {\r
+               jButton_snapshot.setEnabled(b);\r
+       }\r
+       \r
+       /**\r
+        * ぺぱー\r
+        */\r
+       public void setPaperColorDialogEnabled(boolean b) {\r
+               jButton_paperColors.setEnabled(b);\r
+               jSlider_paperZoom.setEnabled(b);\r
+       }\r
+       \r
+       /**\r
+        * ぼだー\r
+        */\r
+       public void setBorderToggleEnabled(boolean b) {\r
+               jToggleButton_showmatchborder.setEnabled(b);\r
+       }\r
+       \r
+       /**\r
+        * ページャーコンボボックスの有効無効。新聞形式を開いている時以外は有効にならないよ\r
+        * @param b\r
+        */\r
+       public void setPagerEnabled(boolean b) {// 新聞形式を開いてないとだめだよ\r
+               jComboBox_pager.removeItemListener(il_pagerSelected);   // そんな…\r
+               \r
+               jComboBox_pager.setEnabled(b && isTabSelected(MWinTab.PAPER) && env.isPagerEnabled());\r
+               \r
+               jComboBox_pager.addItemListener(il_pagerSelected);\r
+       }\r
+       \r
+       /**\r
+        * ページャーコンボボックスの書き換え(汎用版)\r
+        */\r
+       public void setPagerItems() {\r
+               \r
+               if ( env.isPagerEnabled() ) {\r
+                       TVProgramIterator pli = tvprograms.getIterator().build(chsort.getClst(), IterationType.ALL);\r
+                       setPagerItems(pli,null);\r
+               }\r
+               else {\r
+                       jComboBox_pager.removeItemListener(il_pagerSelected);\r
+                       jComboBox_pager.removeAllItems();\r
+                       jComboBox_pager.addItemListener(il_pagerSelected);\r
+               }\r
+               \r
+       }\r
+       \r
+       /**\r
+        * ページャーコンボボックスの書き換え(こちらはPaperViewからしか呼ばれないはずである)\r
+        */\r
+       public void setPagerItems(TVProgramIterator pli, Integer curindex) {\r
+               \r
+               if ( ! env.isPagerEnabled() ) {\r
+                       return;\r
+               }\r
+               \r
+               int total_page = 1+env.getPageIndex(pli.size());\r
+               \r
+               // イベント停止\r
+               jComboBox_pager.removeItemListener(il_pagerSelected);\r
+               \r
+               int index = jComboBox_pager.getSelectedIndex();\r
+               \r
+               // ページャー書き換え\r
+               jComboBox_pager.removeAllItems();\r
+               \r
+               // これは…\r
+               if ( total_page == 0 ) {\r
+                       // イベント再開…はしなくていいか\r
+                       return;\r
+               }\r
+\r
+               pli.rewind();   // 巻き戻してください\r
+               \r
+               for (int i=1; i<=total_page; i++) {\r
+                       String centers = "";\r
+                       for ( int j=0; j<env.getCenterPerPage() && pli.hasNext(); j++ ) {\r
+                               centers += pli.next().Center+"、";\r
+                       }\r
+                       centers = centers.replaceFirst(".$", "");\r
+                       jComboBox_pager.addItem(i+"/"+total_page+((centers.length()>0)?(" - "+centers):("")));\r
+               }\r
+\r
+               // 選択するページ番号を決定する\r
+               int newindex = 0;\r
+               if ( curindex == null ) {\r
+                       // 指定されていないなら基本は以前選択されていたもの\r
+                       newindex = (index>=0) ? (index) : (0);\r
+               }\r
+               else {\r
+                       // 指定されていればそれ\r
+                       newindex = curindex;\r
+               }\r
+               if ( newindex >= jComboBox_pager.getItemCount() ) {\r
+                       // 書き換えの結果ページの数が減ってしまう(通常ここにはこないはずなので、これは例外防止用)\r
+                       if (debug) System.out.println(DBGID+"ページ数が変更された: "+newindex+" -> "+jComboBox_pager.getItemCount());\r
+                       newindex = 0;\r
+               }\r
+               jComboBox_pager.setSelectedIndex(newindex);\r
+               \r
+               // イベント再開\r
+               jComboBox_pager.addItemListener(il_pagerSelected);\r
+       }\r
+       \r
+       /**\r
+        * ページャーコンボボックスのアイテム数\r
+        */\r
+       public int getPagerCount() {\r
+               return jComboBox_pager.getItemCount();\r
+       }\r
+       \r
+       /**\r
+        * ページャーコンボボックスの選択位置\r
+        */\r
+       public int getSelectedPagerIndex() {\r
+               return jComboBox_pager.getSelectedIndex();\r
+       }\r
+       \r
+       /**\r
+        * ページャーコンボボックスの選択\r
+        */\r
+       public void setSelectedPagerIndex(int idx) {\r
+               if (jComboBox_pager.isEnabled() ) { \r
+                       jComboBox_pager.setSelectedItem(null);\r
+                       jComboBox_pager.setSelectedIndex(idx);\r
+               }\r
+       }\r
+\r
+       /**\r
+        * 指定のレコーダは選択されているか\r
+        */\r
+       public boolean isRecorderSelected(String myself) {\r
+               String sid = getSelectedRecorder();\r
+               if ( sid == null || sid.equals(myself)) {\r
+                       return true;\r
+               }\r
+               return false;\r
+       }\r
+       \r
+       /**\r
+        * 選択されているレコーダー(のMySelf())を返す\r
+        * @return 「すべて」が選択されている場合はNULL、「ピックアップ」が選択されている場合は""を返す\r
+        */\r
+       public String getSelectedRecorder() {\r
+               if ( jComboBox_select_recorder == null ) {\r
+                       return null;\r
+               }\r
+               String recId = (String)jComboBox_select_recorder.getSelectedItem();\r
+               if ( recId.equals(SELECTED_ALL) ) {\r
+                       return null;\r
+               }\r
+               else if ( recId.equals(SELECTED_PICKUP) ) {\r
+                       return "";\r
+               }\r
+               \r
+               return recId;\r
+       }\r
+       \r
+       /**\r
+        * 選択しているレコーダを変える\r
+        * @param myself 「すべて」を選択する場合はNULLを渡す\r
+        */\r
+       public void setSelectedRecorder(String myself) {\r
+               if ( jComboBox_select_recorder != null ) {\r
+                       jComboBox_select_recorder.setSelectedItem((myself == null)?(SELECTED_ALL):(myself));\r
+               }\r
+       }\r
+       \r
+       /**\r
+        * レコーダコンボボックスを初期化する\r
+        */\r
+       public void updateRecorderComboBox() {\r
+               \r
+               jComboBox_select_recorder.removeItemListener(il_recorderSelected);\r
+               \r
+               jComboBox_select_recorder.removeAllItems();\r
+               jComboBox_select_recorder.addItem(SELECTED_ALL);\r
+               for (HDDRecorder r : recorders) {\r
+                       switch ( r.getType() ) {\r
+                       case RECORDER:\r
+                       case EPG:\r
+                       case MAIL:\r
+                       case NULL:\r
+                               jComboBox_select_recorder.addItem(r.Myself());\r
+                               break;\r
+                       default:\r
+                               break;\r
+                       }\r
+               }\r
+               jComboBox_select_recorder.addItem(SELECTED_PICKUP);\r
+\r
+               jComboBox_select_recorder.addItemListener(il_recorderSelected);\r
+\r
+       }\r
+\r
+       /**\r
+        * \r
+        */\r
+       public boolean updateReloadProgramExtention() {\r
+               \r
+               // 消して\r
+               jPopupMenu_reloadprogmenu.removeAll();\r
+               \r
+               // 追加する\r
+               for (LoadFor lf : reloadProgMenu ) {\r
+                       if ( lf == null ) {\r
+                               jPopupMenu_reloadprogmenu.addSeparator();\r
+                               continue;\r
+                       }\r
+                       \r
+                       if ( (lf == LoadFor.CSwSD && ! env.isShutdownEnabled()) ||\r
+                                       (lf == LoadFor.SYOBO && ! env.getUseSyobocal()) ) {\r
+                               // 無効なメニューがある\r
+                               continue;\r
+                       }\r
+               \r
+                       JMenuItem menuItem = new JMenuItem(lf.getName());\r
+                       jPopupMenu_reloadprogmenu.add(menuItem);\r
+                       \r
+                       menuItem.addActionListener(al_reloadProgramIndividual);\r
+               }\r
+               \r
+               return true;\r
+       }\r
+       \r
+       /**\r
+        * ステータスエリアは表示中?\r
+        */\r
+       public boolean isStatusShown() {\r
+               return jToggleButton_showstatus.isSelected();\r
+       }\r
+       \r
+       /**\r
+        * フルスクリーン化しているかな?\r
+        */\r
+       public boolean isFullScreen() {\r
+               return jToggleButton_fullScreen.isSelected();\r
+       }\r
+       \r
+       \r
+       /*******************************************************************************\r
+        * リスナー\r
+        ******************************************************************************/\r
+\r
+       // キーワード検索ボックスが確定された or キーワード検索ボタンが押された\r
+       private final ActionListener al_keywordEntered = new ActionListener() {\r
+               @Override\r
+               public void actionPerformed(ActionEvent e) {\r
+                       if (jTextField_keyword.getText().matches("^\\d\\d\\d\\d/\\d\\d/\\d\\d$")) {\r
+                               if ( ! jumpToPassed(jTextField_keyword.getText())) {\r
+                                       JOptionPane.showConfirmDialog(null, jTextField_keyword.getText()+"はみつからなかったでゲソ!", "警告", JOptionPane.CLOSED_OPTION);\r
+                               }\r
+                       }\r
+                       else {\r
+                               toolbarSearch();\r
+                       }\r
+               }\r
+       };\r
+       \r
+       // キーワード検索への登録ボタンが押された\r
+       private final ActionListener al_keywordAdded = new ActionListener(){\r
+               public void actionPerformed(ActionEvent e){\r
+                       // 「キーワード検索の設定」ウィンドウを開く\r
+                       String kStr = jTextField_keyword.getText().trim();\r
+                       \r
+                       SearchKey search = decSearchKeyText(kStr);\r
+                       if ( search == null ) {\r
+                               return;\r
+                       }\r
+                       \r
+                       jTextField_keyword.setText("");\r
+\r
+                       addKeywordSearch(search);\r
+               }\r
+       };\r
+       \r
+       // ページャーコンボボックスが選択された\r
+       private final ItemListener il_pagerSelected = new ItemListener() {\r
+               @Override\r
+               public void itemStateChanged(ItemEvent e) {\r
+                       if (e.getStateChange() == ItemEvent.SELECTED) {\r
+                               if (debug) System.out.println(DBGID+"PAGER SELECTED");\r
+                               redrawByPager();\r
+                       }\r
+               }\r
+       };\r
+       \r
+       // 番組表の再取得の実行\r
+       private final MouseListener ml_reloadProgram = new MouseAdapter() {\r
+               @Override\r
+               public void mouseClicked(MouseEvent e) {\r
+                       reLoadTVProgram(LoadFor.ALL);\r
+               }\r
+       };\r
+       \r
+       // 番組表の再取得の拡張メニュー\r
+       private final MouseAdapter ma_reloadProgramExtention = new MouseAdapter() {\r
+               @Override\r
+               public void mousePressed(MouseEvent e) {\r
+                       jPopupMenu_reloadprogmenu.show(jButton_reloadprogs,0,jButton_reloadprogs.getHeight());\r
+               }\r
+       };\r
+       \r
+       // 番組表の再取得の拡張メニューの個々のアイテムにつけるリスナー\r
+       private final ActionListener al_reloadProgramIndividual = new ActionListener() {\r
+               @Override\r
+               public void actionPerformed(ActionEvent e) {\r
+                       String selected = ((JMenuItem)e.getSource()).getText();\r
+                       LoadFor lf = LoadFor.get(selected);\r
+                       switch (lf) {\r
+                       case CSwSD: \r
+                               if ( env.isShutdownEnabled() ) {\r
+                                       reLoadTVProgram(LoadFor.CS);\r
+                                       CommonUtils.executeCommand(env.getShutdownCmd());\r
+                               }\r
+                               else {\r
+                                       JOptionPane.showMessageDialog(parent, "シャットダウンコマンドが利用できません");\r
+                               }\r
+                               break;\r
+                       default:\r
+                               reLoadTVProgram(lf);\r
+                               break;\r
+                       }\r
+               }\r
+       };\r
+       \r
+       // 一括登録の実行\r
+       private final ActionListener al_batchreservation = new ActionListener(){\r
+               public void actionPerformed(ActionEvent e){\r
+                       doBatchReserve();\r
+               }\r
+       };\r
+\r
+       // レコーダコンボボックスが選択された\r
+       private final ItemListener il_recorderSelected = new ItemListener() {\r
+               @Override\r
+               public void itemStateChanged(ItemEvent e) {\r
+                       if (e.getStateChange() == ItemEvent.SELECTED) {\r
+                               recorderSelectorChanged();\r
+                       }\r
+               }\r
+       };\r
+       \r
+       // 予約一覧の再取得\r
+       private final ActionListener al_reloadReserved = new ActionListener(){\r
+               public void actionPerformed(ActionEvent e){\r
+                       reLoadRdReserve(getSelectedRecorder());\r
+               }\r
+       };\r
+       \r
+       // 番組表の再取得の拡張メニューの個々のアイテムにつけるリスナー\r
+       private final ActionListener al_reloadReservedIndividual = new ActionListener() {\r
+               @Override\r
+               public void actionPerformed(ActionEvent e) {\r
+                       String selected = ((JMenuItem)e.getSource()).getText();\r
+                       LoadRsvedFor lrf = LoadRsvedFor.get(selected);\r
+                       switch (lrf) {\r
+                       case RECORDED:\r
+                               reLoadRdRecorded(getSelectedRecorder());\r
+                               break;\r
+                       case SETTING: \r
+                       default:\r
+                               break;\r
+                       }\r
+               }\r
+       };\r
+\r
+       // 番組表の再取得の拡張メニュー\r
+       private final MouseAdapter ma_reloadReservedExtention = new MouseAdapter() {\r
+               @Override\r
+               public void mousePressed(MouseEvent e) {\r
+                       jPopupMenu_reloadrsvedmenu.show(jButton_reloadrsved,0,jButton_reloadrsved.getHeight());\r
+               }\r
+       };\r
+       \r
+       // レコーダにWOL\r
+       private final ActionListener al_wakeup = new ActionListener(){\r
+               public void actionPerformed(ActionEvent e){\r
+                       for (HDDRecorder r : recorders) {\r
+                               if ( ! isRecorderSelected(r.Myself())) {\r
+                                       continue;\r
+                               }\r
+                               if ( r.getType() != RecType.RECORDER) {\r
+                                       continue;\r
+                               }\r
+                               if ( r.getMacAddr().equals("") || r.getBroadcast().equals("")) {\r
+                                       MWin.appendError(ERRID+"MACアドレスとブロードキャストアドレスを設定してください: "+r.Myself());\r
+                                       ringBeep();\r
+                                       continue;\r
+                               }\r
+                               r.wakeup();\r
+                               MWin.appendMessage(MSGID+"wakeupリクエストを送信しました: "+r.Myself());\r
+                       }\r
+               }\r
+       };\r
+       \r
+       // レコーダの電源を落とす\r
+       private final ActionListener al_down = new ActionListener(){\r
+               public void actionPerformed(ActionEvent e){\r
+                       for (HDDRecorder r : recorders) {\r
+                               if ( ! isRecorderSelected(r.Myself())) {\r
+                                       continue;\r
+                               }\r
+                               if ( ! r.getMacAddr().equals("") && ! r.getBroadcast().equals("")) {\r
+                                       r.shutdown();\r
+                                       MWin.appendMessage(MSGID+"shutdownリクエストを送信しました: "+r.Myself());\r
+                               }\r
+                       }\r
+               }\r
+       };\r
+       \r
+       // 設定タブを出したりしまったり\r
+       private final ActionListener al_showsetting = new ActionListener(){\r
+               public void actionPerformed(ActionEvent e){\r
+                       toggleSettingTabVisible();\r
+               }\r
+       };\r
+\r
+       // ステータスエリアを出したりしまったり\r
+       private final ActionListener al_toggleShowStatus = new ActionListener() {\r
+               \r
+               @Override\r
+               public void actionPerformed(ActionEvent e) {\r
+\r
+                       JToggleButton btn = (JToggleButton) e.getSource();\r
+                       boolean b = btn.isSelected();\r
+\r
+                       if (debug) System.out.println(DBGID+"act_toggleShowStatus "+b);\r
+                       \r
+                       setStatusVisible(b);\r
+                       \r
+                       bounds.setShowStatus(b);\r
+               }\r
+       };\r
+       \r
+       // フルスクリーンになったりウィンドウになったり\r
+       private final ActionListener al_toggleFullscreen = new ActionListener() {\r
+               \r
+               @Override\r
+               public void actionPerformed(ActionEvent e) {\r
+                       \r
+                       JToggleButton btn = (JToggleButton) e.getSource();\r
+                       boolean b = btn.isSelected();\r
+                       \r
+                       if (debug) System.out.println(DBGID+"al_toggleFullscreen "+b);\r
+                       \r
+                       // ウィンドウ化\r
+                       setFullScreen(b);\r
+                       \r
+                       // ステータスエリアの表示/非表示\r
+                       if ( b ) {\r
+                               // フルスクリーン化の際、ステータスエリアは隠す\r
+                               statusarea_shown = jToggleButton_showstatus.isSelected();\r
+                               if ( jToggleButton_showstatus.isSelected() == true ) jToggleButton_showstatus.doClick();\r
+                       }\r
+                       else {\r
+                               // ウィンドウに戻す際、もともとステータスエリアを開いていた場合だけ戻す\r
+                               if ( jToggleButton_showstatus.isSelected() != statusarea_shown ) jToggleButton_showstatus.doClick();\r
+                       }\r
+               }\r
+       };\r
+       \r
+       // 新聞形式に予約待機枠を表示させたりしなかったり\r
+       private final ActionListener al_showborder = new ActionListener(){\r
+               public void actionPerformed(ActionEvent e){\r
+                       toggleMatchBorder();\r
+               }\r
+       };\r
+       \r
+       // 新聞形式の現在日付までジャンプ\r
+       private final ActionListener al_jumpto = new ActionListener(){\r
+               public void actionPerformed(ActionEvent e){\r
+                       jumpToNow();\r
+               }\r
+       };\r
+       \r
+       // 新聞形式の背景色選択ダイアログを表示する\r
+       private final ActionListener al_showpapercolordialog = new ActionListener(){\r
+               public void actionPerformed(ActionEvent e){\r
+                       setPaperColorDialogVisible(true);\r
+               }\r
+       };\r
+       \r
+       private final MouseAdapter ml_paperzoom = new MouseAdapter() {\r
+               \r
+               @Override\r
+               public void mouseReleased(MouseEvent e) {\r
+                       JSlider sl = (JSlider) e.getSource();\r
+                       setPaperZoom(sl.getValue());\r
+               }\r
+       };\r
+       \r
+       // スナップショットをとる\r
+       private final ActionListener al_snapshot = new ActionListener(){\r
+               public void actionPerformed(ActionEvent e){\r
+                       takeSnapShot();\r
+               }\r
+       };\r
+       \r
+       // ログをビューアで開く\r
+       private final ActionListener al_logview = new ActionListener(){\r
+               public void actionPerformed(ActionEvent e){\r
+                       LogViewer lv = new LogViewer(Viewer.LOG_FILE);\r
+                       lv.setVisible(true);\r
+               }\r
+       };\r
+       \r
+       //\r
+       private final ActionListener al_update = new ActionListener(){\r
+               public void actionPerformed(ActionEvent e){\r
+                       \r
+                       StWin.clear();\r
+                       new SwingBackgroundWorker(false) {\r
+                               @Override\r
+                               protected Object doWorks() throws Exception {\r
+                                       UpdateResult res = new VWUpdate(StWin).checkUpdate(VersionInfo.getVersion()); \r
+                                       if (res == UpdateResult.DONE) {\r
+                                               LogViewer lv = new LogViewer(Viewer.HISTORY_FILE);\r
+                                               lv.setCaretPosition(0);\r
+                                               lv.setVisible(true);\r
+                                       }\r
+                                       return null;\r
+                               }\r
+                               @Override\r
+                               protected void doFinally() {\r
+                                       CommonUtils.milSleep(OPENING_WIAT);\r
+                                       StWin.setVisible(false);\r
+                               }\r
+                       }.execute();\r
+                       \r
+                       CommonSwingUtils.setLocationCenter(parent, (Component) StWin);\r
+                       StWin.setVisible(true);\r
+               }\r
+       };\r
+\r
+       // ヘルプを開く\r
+       private final ActionListener al_openhelp = new ActionListener(){\r
+               public void actionPerformed(ActionEvent e){\r
+                       try {\r
+                               Desktop desktop = Desktop.getDesktop();\r
+                               desktop.browse(new URI(HELP_URL));\r
+                       } catch (IOException e1) {\r
+                               e1.printStackTrace();\r
+                       } catch (URISyntaxException e1) {\r
+                               e1.printStackTrace();\r
+                       }\r
+               }\r
+       };\r
+       \r
+       /*******************************************************************************\r
+        * コンポーネント\r
+        ******************************************************************************/\r
+\r
+       // キーワード検索ボックス\r
+       private JTextField getJTextField_keyword() {\r
+               if (jTextField_keyword == null) {\r
+                       jTextField_keyword = new JTextFieldWithPopup(16);\r
+                       Dimension d = jTextField_keyword.getPreferredSize();\r
+\r
+                       jTextField_keyword.setMaximumSize(d);   // 固定しないと環境によってサイズがかわっちゃう\r
+                       jTextField_keyword.setMinimumSize(d);\r
+                       \r
+                       jTextField_keyword.setToolTipText(TIPS_KEYWORD);\r
+                       \r
+                       jTextField_keyword.addActionListener(al_keywordEntered);\r
+               }\r
+               return jTextField_keyword;\r
+       }\r
+\r
+       // 「検索ボタン」\r
+       private JButton getJButton_search(String s) {\r
+               if (jButton_search == null) {\r
+                       ImageIcon icon = new ImageIcon(ICONFILE_SEARCH);\r
+                       jButton_search = new JButton(icon);\r
+                       jButton_search.setToolTipText(TIPS_SEARCH);\r
+                       \r
+                       //jButton_search.addActionListener(al_searchRequested);\r
+                       jButton_search.addActionListener(al_keywordEntered);\r
+               }\r
+               return jButton_search;\r
+       }\r
+\r
+       // 「キーワード検索に登録」\r
+       private JButton getJButton_addkeyword(String s) {\r
+               if (jButton_addkeyword == null) {\r
+                       ImageIcon icon = new ImageIcon(ICONFILE_ADDKEYWORD);\r
+                       jButton_addkeyword = new JButton(icon);\r
+                       jButton_addkeyword.setToolTipText(TIPS_ADDKEYWORD);\r
+\r
+                       jButton_addkeyword.addActionListener(al_keywordAdded);\r
+               }\r
+               return jButton_addkeyword;\r
+       }\r
+       \r
+       // 「ページャー」\r
+       private JComboBox getJComboBox_pager() {\r
+               if (jComboBox_pager == null) {\r
+                       jComboBox_pager = new JWideComboBox();\r
+                       jComboBox_pager.addPopupWidth(600);\r
+                       jComboBox_pager.setToolTipText(TIPS_PAGER);\r
+                       \r
+                       int w = 16*6;\r
+                       Dimension d = jComboBox_pager.getPreferredSize();\r
+                       d.width = w;\r
+                       jComboBox_pager.setPreferredSize(d);\r
+                       \r
+                       d = jComboBox_pager.getMaximumSize();\r
+                       d.width = w;\r
+                       jComboBox_pager.setMaximumSize(d);\r
+                       jComboBox_pager.setMinimumSize(d);\r
+                       jComboBox_pager.setEnabled(false);\r
+                       \r
+                       // 選択されたっぽい\r
+                       jComboBox_pager.addItemListener(il_pagerSelected);\r
+               }\r
+               return(jComboBox_pager);\r
+       }\r
+\r
+       // 「番組情報を再取得」\r
+       private JButton getJButton_reloadprogs(String s) {\r
+               if (jButton_reloadprogs == null) {\r
+                       ImageIcon icon = new ImageIcon(ICONFILE_RELOADPROG);\r
+                       jButton_reloadprogs = new JButton(icon);\r
+                       jButton_reloadprogs.setToolTipText(TIPS_RELOADPROG);\r
+                       \r
+                       jButton_reloadprogs.addMouseListener(ml_reloadProgram);\r
+               }\r
+               return jButton_reloadprogs;\r
+       }\r
+\r
+       // 「番組情報を再取得」の拡張メニューの並び順\r
+       private final LoadFor[] reloadProgMenu =\r
+               {\r
+                       LoadFor.TERRA,\r
+                       LoadFor.CS,\r
+                       LoadFor.CSo1,\r
+                       LoadFor.CSo2,\r
+                       null,\r
+                       LoadFor.CSwSD,\r
+                       null,\r
+                       LoadFor.SYOBO\r
+               };\r
+\r
+       // 「番組情報を再取得」の拡張メニュー\r
+       private JButton getJButton_reloadprogmenu(String s) {\r
+               if (jButton_reloadprogmenu == null) {\r
+                       // メニューの作成\r
+                       jPopupMenu_reloadprogmenu = new JPopupMenu();\r
+\r
+                       // アイテムの登録\r
+                       updateReloadProgramExtention();\r
+                       \r
+                       ImageIcon arrow = new ImageIcon(ICONFILE_PULLDOWNMENU);\r
+                       jButton_reloadprogmenu = new JButton(arrow);\r
+                       jButton_reloadprogmenu.addMouseListener(ma_reloadProgramExtention);\r
+               }\r
+               return jButton_reloadprogmenu;\r
+       }\r
+       \r
+       // 「一括予約」\r
+       private JButton getJButton_batchreservation(String s) {\r
+               if (jButton_batchreservation == null) {\r
+                       ImageIcon icon = new ImageIcon(ICONFILE_BATCHCMD);\r
+                       jButton_batchreservation = new JButton(icon);\r
+                       jButton_batchreservation.setToolTipText(TIPS_BATCHRESERVATION);\r
+                       \r
+                       jButton_batchreservation.addActionListener(al_batchreservation);\r
+               }\r
+               return jButton_batchreservation;\r
+       }\r
+       \r
+       //\r
+       private JComboBox getJComboBox_select_recorder() {\r
+               if (jComboBox_select_recorder == null) {\r
+                       jComboBox_select_recorder = new JWideComboBox();\r
+                       jComboBox_select_recorder.addPopupWidth(200);\r
+                       jComboBox_select_recorder.setToolTipText(TIPS_RECORDERSEL);\r
+                       \r
+                       Dimension d = jComboBox_select_recorder.getPreferredSize();\r
+                       d.width = 16*10;\r
+                       jComboBox_select_recorder.setPreferredSize(d);\r
+                       \r
+                       d = jComboBox_select_recorder.getMaximumSize();\r
+                       d.width = 16*10;\r
+                       jComboBox_select_recorder.setMaximumSize(d);\r
+                       jComboBox_select_recorder.setMinimumSize(d);\r
+                       //jComboBox_select_recorder.setEnabled(false);\r
+                       \r
+                       // 初期値\r
+                       updateRecorderComboBox();\r
+                       \r
+                       // 選択された\r
+                       jComboBox_select_recorder.addItemListener(il_recorderSelected);\r
+               }\r
+               return(jComboBox_select_recorder);\r
+       }\r
+\r
+       // 「予約一覧の再取得」\r
+       private JButton getJButton_reloadrsved(String s) {\r
+               if (jButton_reloadrsved == null) {\r
+                       ImageIcon icon = new ImageIcon(ICONFILE_RELOADRSV);\r
+                       jButton_reloadrsved = new JButton(icon);\r
+                       jButton_reloadrsved.setToolTipText(TIPS_RELOADRSVED);\r
+                       \r
+                       jButton_reloadrsved.addActionListener(al_reloadReserved);\r
+               }\r
+               return jButton_reloadrsved;\r
+       }\r
+\r
+       // 「番組情報を再取得」の拡張メニュー\r
+       private JButton getJButton_reloadrsvedmenu(String s) {\r
+               if (jButton_reloadrsvedmenu == null) {\r
+                       // メニューの作成\r
+                       jPopupMenu_reloadrsvedmenu = new JPopupMenu();\r
+\r
+                       // アイテムの登録\r
+                       {\r
+                               for ( LoadRsvedFor lrf : LoadRsvedFor.values() ) {\r
+                                       JMenuItem menuItem = new JMenuItem(lrf.getName());\r
+                                       jPopupMenu_reloadrsvedmenu.add(menuItem);\r
+                                       \r
+                                       menuItem.addActionListener(al_reloadReservedIndividual);\r
+                               }\r
+                       }\r
+                       \r
+                       ImageIcon arrow = new ImageIcon(ICONFILE_PULLDOWNMENU);\r
+                       jButton_reloadrsvedmenu = new JButton(arrow);\r
+                       jButton_reloadrsvedmenu.addMouseListener(ma_reloadReservedExtention);\r
+               }\r
+               return jButton_reloadrsvedmenu;\r
+       }\r
+\r
+       // 「入」\r
+       private JButton getJButton_wakeup(String s) {\r
+               if (jButton_wakeup == null) {\r
+                       ImageIcon icon = new ImageIcon(ICONFILE_WAKEUP);\r
+                       jButton_wakeup = new JButton(icon);\r
+                       jButton_wakeup.setToolTipText(TIPS_WAKEUP);\r
+\r
+                       jButton_wakeup.addActionListener(al_wakeup);\r
+               }\r
+               return jButton_wakeup;\r
+       }\r
+       // 「切」\r
+       private JButton getJButton_shutdown(String s) {\r
+               if (jButton_shutdown == null) {\r
+                       ImageIcon icon = new ImageIcon(ICONFILE_SHUTDOWN);\r
+                       jButton_shutdown = new JButton(icon);\r
+                       jButton_shutdown.setToolTipText(TIPS_DOWN);\r
+                       \r
+                       jButton_shutdown.addActionListener(al_down);\r
+               }\r
+               return jButton_shutdown;\r
+       }\r
+       \r
+       // 「設定タブを表示」\r
+       private JToggleButton getJToggleButton_showsetting(String s) {\r
+               if (jToggleButton_showsetting == null) {\r
+                       final ImageIcon icon = new ImageIcon(ICONFILE_SHOWSETTING);\r
+                       jToggleButton_showsetting = new JToggleButton(icon);\r
+                       jToggleButton_showsetting.setToolTipText(TIPS_SHOWSETTING);\r
+                       \r
+                       jToggleButton_showsetting.setSelected(bounds.getShowSettingTabs());\r
+                       \r
+                       jToggleButton_showsetting.addActionListener(al_showsetting);\r
+               }\r
+               return jToggleButton_showsetting;\r
+       }\r
+       \r
+       // 「ステータス領域」\r
+       private JToggleButton getJToggleButton_showstatus(String s) {\r
+               if (jToggleButton_showstatus == null) {\r
+                       \r
+                       final ImageIcon IconHidden = new ImageIcon(ICONFILE_STATUSHIDDEN);\r
+                       final ImageIcon IconShown = new ImageIcon(ICONFILE_STATUSSHOWN);\r
+                       \r
+                       jToggleButton_showstatus = new JToggleButton(IconHidden) {\r
+                               \r
+                               private static final long serialVersionUID = 1L;\r
+                               \r
+                               @Override\r
+                               public void setSelected(boolean b) {\r
+                                       super.setSelected(b);\r
+                                       if ( b ) {\r
+                                               this.setToolTipText(TIPS_STATUSSHOWN);\r
+                                       }\r
+                                       else {\r
+                                               this.setToolTipText(TIPS_STATUSHIDDEN);\r
+                                       }\r
+                               }\r
+                       };\r
+                       jToggleButton_showstatus.setSelectedIcon(IconShown);\r
+                       jToggleButton_showstatus.setSelected(bounds.getShowStatus());\r
+                       jToggleButton_showstatus.addActionListener(al_toggleShowStatus);\r
+               }\r
+               return jToggleButton_showstatus;\r
+       }\r
+       \r
+       // 「全画面」\r
+       private JToggleButton getJToggleButton_fullScreen(String s) {\r
+               if (jToggleButton_fullScreen == null) {\r
+                       \r
+                       final ImageIcon IconToWin = new ImageIcon(ICONFILE_TOWIN);\r
+                       final ImageIcon IconToFull = new ImageIcon(ICONFILE_TOFULL);\r
+                       \r
+                       jToggleButton_fullScreen = new JToggleButton(IconToFull) {\r
+                               \r
+                               private static final long serialVersionUID = 1L;\r
+                               \r
+                               @Override\r
+                               public void setSelected(boolean b) {\r
+                                       super.setSelected(b);\r
+                                       if (b) {\r
+                                               this.setToolTipText(TIPS_TOWIN);\r
+                                       }\r
+                                       else {\r
+                                               this.setToolTipText(TIPS_TOFULL);\r
+                                       }\r
+                               }\r
+                       };\r
+                       jToggleButton_fullScreen.setSelectedIcon(IconToWin);\r
+                       jToggleButton_fullScreen.setSelected(false);\r
+                       jToggleButton_fullScreen.addActionListener(al_toggleFullscreen);\r
+               }\r
+               return jToggleButton_fullScreen;\r
+       }\r
+       \r
+       // 「設定タブを表示」\r
+       private JToggleButton getJToggleButton_showmatchborder(String s) {\r
+               if (jToggleButton_showmatchborder == null) {\r
+                       final ImageIcon icon = new ImageIcon(ICONFILE_SHOWMATCHBORDER);\r
+                       jToggleButton_showmatchborder = new JToggleButton(icon);\r
+                       jToggleButton_showmatchborder.setToolTipText(TIPS_SHOWBORDER);\r
+                       jToggleButton_showmatchborder.setSelected(bounds.getShowMatchedBorder());\r
+                       \r
+                       jToggleButton_showmatchborder.addActionListener(al_showborder);\r
+               }\r
+               return jToggleButton_showmatchborder;\r
+       }\r
+       \r
+       // 「現在日時」\r
+       private JButton getJButton_moveToNow(String s) {\r
+               if (jButton_moveToNow == null) {\r
+                       ImageIcon icon = new ImageIcon(ICONFILE_JUMPTONOW);\r
+                       jButton_moveToNow = new JButton(icon);\r
+                       jButton_moveToNow.setToolTipText(TIPS_JUMPTO);\r
+\r
+                       jButton_moveToNow.addActionListener(al_jumpto);\r
+               }\r
+               return jButton_moveToNow;\r
+       }\r
+       \r
+       // 「現在日時」\r
+       private JButton getJButton_paperColors(String s) {\r
+               if (jButton_paperColors == null) {\r
+                       ImageIcon icon = new ImageIcon(ICONFILE_PALLET);\r
+                       jButton_paperColors = new JButton(icon);\r
+                       jButton_paperColors.setToolTipText(TIPS_PAPERCOLOR);\r
+\r
+                       jButton_paperColors.addActionListener(al_showpapercolordialog);\r
+               }\r
+               return jButton_paperColors;\r
+       }\r
+       \r
+       private JSlider getJSlider_paperZoom(String s) {\r
+               if ( jSlider_paperZoom == null ) {\r
+                       jSlider_paperZoom = new JSlider(100,300,100);\r
+                       jSlider_paperZoom.setToolTipText(TIPS_PAPERZOOM);\r
+\r
+                       Dimension d = jSlider_paperZoom.getPreferredSize();\r
+                       d.width = 32;\r
+                       jSlider_paperZoom.setPreferredSize(d);\r
+                       jSlider_paperZoom.setMaximumSize(d);\r
+                       jSlider_paperZoom.setMinimumSize(d);\r
+                       \r
+                       jSlider_paperZoom.addMouseListener(ml_paperzoom);\r
+               }\r
+               return jSlider_paperZoom;\r
+       }\r
+       /*\r
+       // 「タイマー」\r
+       private String nextEventDateTime = "29991231 2359";\r
+       private void setNextEventDateTime() {\r
+               GregorianCalendar c = new GregorianCalendar();\r
+               c.setTime(new Date());\r
+               c.add(Calendar.HOUR_OF_DAY, env.getCacheTimeLimit());\r
+               nextEventDateTime = String.format("%04d%02d%02d %02d%02d", c.get(Calendar.YEAR),c.get(Calendar.MONTH)+1,c.get(Calendar.DAY_OF_MONTH),c.get(Calendar.HOUR_OF_DAY),00);\r
+               StdAppendMessage("Next Event Time: "+nextEventDateTime);\r
+       }\r
+       private ActionListener alTimer = new ActionListener() {\r
+               public void actionPerformed(ActionEvent e){\r
+                       GregorianCalendar c = new GregorianCalendar();\r
+                       c.setTime(new Date());\r
+                       String curDateTime = String.format("%04d%02d%02d %02d%02d", c.get(Calendar.YEAR),c.get(Calendar.MONTH)+1,c.get(Calendar.DAY_OF_MONTH),c.get(Calendar.HOUR_OF_DAY),c.get(Calendar.MINUTE));\r
+                       if (nextEventDateTime.compareTo(curDateTime) <= 0) {\r
+                               funcReloadProgs(LoadFor.ALL);\r
+                               setNextEventDateTime();\r
+                       }\r
+               }\r
+       };\r
+       private Timer tbTimer = new Timer(60*1000, alTimer);\r
+       private void tbTimerOn() {\r
+               String tip = "タイマーOFF";\r
+               jToggleButton_timer.setToolTipText(tip);\r
+               setNextEventDateTime();\r
+               tbTimer.start();\r
+               bounds.setEnableTimer(true);\r
+       }\r
+       private void tbTimerOff() {\r
+               String tip = "タイマーON";\r
+               jToggleButton_timer.setToolTipText(tip);\r
+               tbTimer.stop();\r
+               bounds.setEnableTimer(false);\r
+       }\r
+       */\r
+       private JToggleButton getJToggleButton_timer(String s) {\r
+               if (jToggleButton_timer == null) {\r
+                       ImageIcon icon = new ImageIcon(ICONFILE_TIMER);\r
+                       String tip = "タイマーON";\r
+                       jToggleButton_timer = new JToggleButton(icon);\r
+                       jToggleButton_timer.setToolTipText(tip);\r
+                       \r
+                       jToggleButton_timer.setEnabled(false);\r
+                       jToggleButton_timer.setToolTipText("future use");\r
+                       \r
+                       /*\r
+                       if (bounds.getEnableTimer()) {\r
+                               jToggleButton_timer.setSelected(true);\r
+                               tbTimerOn();\r
+                       }\r
+                       \r
+                       jToggleButton_timer.addChangeListener(new ChangeListener() {\r
+                               @Override\r
+                               public void stateChanged(ChangeEvent e) {\r
+                                       ButtonModel bm = jToggleButton_timer.getModel();\r
+                                       if (bm.isPressed() && bm.isSelected()) {\r
+                                               tbTimerOn();\r
+                                       }\r
+                                       else if (bm.isPressed() && ! bm.isSelected()) {\r
+                                               tbTimerOff();\r
+                                       }\r
+                               }\r
+                       });\r
+                       */\r
+               }\r
+               return jToggleButton_timer;\r
+       }\r
+       \r
+       // 「スナップショット」\r
+       private JButton getJButton_snapshot(String s) {\r
+               if (jButton_snapshot == null) {\r
+                       ImageIcon icon = new ImageIcon(ICONFILE_SCREENSHOT);\r
+                       jButton_snapshot = new JButton(icon);\r
+                       jButton_snapshot.setToolTipText(TIPS_SNAPSHOT);\r
+\r
+                       jButton_snapshot.addActionListener(al_snapshot);\r
+               }\r
+               return jButton_snapshot;\r
+       }\r
+       \r
+       // 「ログビューア」\r
+       private JButton getJButton_logviewer(String s) {\r
+               if (jButton_logviewer == null) {\r
+                       ImageIcon icon = new ImageIcon(ICONFILE_SHOWLOG);\r
+                       jButton_logviewer = new JButton(icon);\r
+                       jButton_logviewer.setToolTipText(TIPS_LOGVIEW);\r
+\r
+                       jButton_logviewer.addActionListener(al_logview);\r
+               }\r
+               return jButton_logviewer;\r
+       }\r
+       \r
+       // 「オンラインアップデート」\r
+       private JButton getJButton_update(String s) {\r
+               if (jButton_update == null) {\r
+                       ImageIcon icon = new ImageIcon(ICONFILE_UPDATE);\r
+                       jButton_update = new JButton(icon);\r
+                       jButton_update.setToolTipText(TIPS_UPDATE);\r
+\r
+                       jButton_update.addActionListener(al_update);\r
+               }\r
+               return jButton_update;\r
+       }\r
+       \r
+       // 「ヘルプ」\r
+       private JButton getJButton_help(String s) {\r
+               if (jButton_help == null) {\r
+                       ImageIcon icon = new ImageIcon(ICONFILE_HELP);\r
+                       jButton_help = new JButton(icon);\r
+                       jButton_help.setToolTipText(TIPS_OPENHELP);\r
+\r
+                       jButton_help.addActionListener(al_openhelp);\r
+               }\r
+               return jButton_help;\r
+       }\r
+       \r
+}\r
diff --git a/TinyBannavi/src/tainavi/AreaCode.java b/TinyBannavi/src/tainavi/AreaCode.java
new file mode 100644 (file)
index 0000000..be414f4
--- /dev/null
@@ -0,0 +1,33 @@
+package tainavi;\r
+\r
+\r
+\r
+public class AreaCode implements Cloneable {\r
+       private String area;\r
+       private String code;\r
+       private boolean selected;\r
+       \r
+       public AreaCode() {\r
+               this.area = "";\r
+               this.code = "";\r
+               this.selected = false; \r
+       }\r
+       \r
+       @Override\r
+       public AreaCode clone() {\r
+               try {\r
+                       return (AreaCode) super.clone();\r
+               } catch (CloneNotSupportedException e) {\r
+                       throw new InternalError(e.toString());\r
+               }\r
+       }\r
+       \r
+       public String getArea() { return this.area; }\r
+       public void setArea(String s) { this.area = s; }\r
+\r
+       public String getCode() { return this.code; }\r
+       public void setCode(String s) { this.code = s; }\r
+       \r
+       public boolean getSelected() { return this.selected; }\r
+       public void setSelected(boolean b) { this.selected = b; }\r
+}\r
diff --git a/TinyBannavi/src/tainavi/ArpCommand.java b/TinyBannavi/src/tainavi/ArpCommand.java
new file mode 100644 (file)
index 0000000..cb11305
--- /dev/null
@@ -0,0 +1,56 @@
+package tainavi;\r
+\r
+\r
+public class ArpCommand {\r
+\r
+       // キャッシュに乗るまで時間がかかる場合があるので3秒待つ\r
+       // InetAddress#isReachable()は使えない\r
+       \r
+       private static final String PINGWIN = "PING.EXE -n 3 -w 1000 #HOST#";\r
+       private static final String ARPWIN = "ARP.EXE -a #HOST#";\r
+       \r
+       private static final String PINGX = "ping -c 3 -i 1 #HOST#";\r
+       private static final String ARPX = "arp -n #HOST#";\r
+       \r
+       /**\r
+        * @return \r
+        */\r
+       public static String getMac(String host) {\r
+               \r
+               String pingcmd = null;\r
+               String arpcmd = null;\r
+               \r
+               if ( CommonUtils.isWindows() ) {\r
+                       pingcmd = PINGWIN;\r
+                       arpcmd = ARPWIN;\r
+               }\r
+               else if ( CommonUtils.isLinux() || CommonUtils.isMac() ) {\r
+                       pingcmd = PINGX;\r
+                       arpcmd = ARPX;\r
+               }\r
+               else {\r
+                       return null;\r
+               }\r
+               \r
+               {\r
+                       int retval = CommonUtils.executeCommand(pingcmd.replaceFirst("#HOST#", host));\r
+                       if ( retval != 0 ) {\r
+                               return null;\r
+                       }\r
+               }\r
+               {\r
+                       int retval = CommonUtils.executeCommand(arpcmd.replaceFirst("#HOST#", host));\r
+                       if ( retval != 0 ) {\r
+                               return null;\r
+                       }\r
+               }\r
+               \r
+               String mac = CommonUtils.getCommandResult();\r
+               if ( mac == null ) {\r
+                       return null;\r
+               }\r
+               \r
+               return mac.replaceFirst("^[\\s\\S]*(([0-9a-fA-F][0-9a-fA-F][\\-:]){5}[0-9a-fA-F][0-9a-fA-F])[\\s\\S]*$", "$1").replaceAll("[\\-:]", "").toUpperCase();\r
+       }\r
+\r
+}\r
diff --git a/TinyBannavi/src/tainavi/BackgroundWorker.java b/TinyBannavi/src/tainavi/BackgroundWorker.java
new file mode 100644 (file)
index 0000000..6f7c5a0
--- /dev/null
@@ -0,0 +1,20 @@
+package tainavi;\r
+\r
+import java.util.concurrent.ExecutionException;\r
+\r
+/**\r
+ * スレッドを使ったバックグラウンド処理について、swingの利用を隠ぺいできないかなーと思って作ったinterface\r
+ * @sinc 3.15.4β\r
+ */\r
+public abstract class BackgroundWorker {\r
+\r
+       // 抽象メソッド\r
+       protected abstract Object doWorks() throws Exception;   // 主な処理(SwingWorker#doInBackground())\r
+       protected abstract void doFinally();                                    // 終了時に1回だけ実行する処理(SwingWorker#done())\r
+       \r
+       // 必要なメソッド(SwingWorkerのパクリ)\r
+       public abstract void execute();\r
+       public abstract Object get() throws InterruptedException, ExecutionException;\r
+       public abstract boolean cancel(boolean mayInterruptIfRunning);\r
+               \r
+}\r
diff --git a/TinyBannavi/src/tainavi/Bounds.java b/TinyBannavi/src/tainavi/Bounds.java
new file mode 100644 (file)
index 0000000..e71b085
--- /dev/null
@@ -0,0 +1,306 @@
+package tainavi;\r
+\r
+import java.awt.Rectangle;\r
+import java.io.File;\r
+import java.util.HashMap;\r
+\r
+\r
+\r
+/**\r
+ * 【結論】ちゃんと設計しないとメンバの変更で簡単に例外が起きる。できるならシリアライズは自前で組もう!\r
+ */\r
+public class Bounds {\r
+       //\r
+       private static final String BOUNDS_FILE = "env"+File.separator+"bounds.xml";\r
+       private static final String BOUNDS_TEXT = "env"+File.separator+"bounds.txt";\r
+       \r
+       //\r
+       private Rectangle winRectangle;\r
+       public Rectangle getWinRectangle() { return winRectangle; }\r
+       public void setWinRectangle(Rectangle r) { winRectangle = r; }\r
+       //\r
+       private int bangumiColumnWidth;\r
+       public int getBangumiColumnWidth() { return bangumiColumnWidth; }\r
+       public void setBangumiColumnWidth(int w) { bangumiColumnWidth = w; }\r
+       private int bangumiColumnHeight;\r
+       public int getBangumiColumnHeight() { return bangumiColumnHeight; }\r
+       public void setBangumiColumnHeight(int h) { bangumiColumnHeight = h; }\r
+       // \r
+       private int treeWidth;\r
+       public int getTreeWidth() { return treeWidth; }\r
+       public void setTreeWidth(int w) { treeWidth = w; }\r
+       private int treeWidthPaper;\r
+       public int getTreeWidthPaper() { return treeWidthPaper; }\r
+       public void setTreeWidthPaper(int w) { treeWidthPaper = w; }\r
+       \r
+       // サイドツリーの最小幅(固定)\r
+       public int getMinDivLoc() { return 32; }\r
+       \r
+       //\r
+       public int tooltipWidth;\r
+       public int getTooltipWidth() { return tooltipWidth; }\r
+       public void setTooltipWidth(int w) { tooltipWidth = w; }\r
+       \r
+       //\r
+       private int timebarColumnWidth;\r
+       public int getTimebarColumnWidth() { return timebarColumnWidth; }\r
+       public void setTimebarColumnWidth(int w) { timebarColumnWidth = w; }\r
+       //\r
+       private float paperHeightMultiplier;\r
+       public float getPaperHeightMultiplier() { return paperHeightMultiplier; }\r
+       public void setPaperHeightMultiplier(float m) { paperHeightMultiplier = m; }\r
+       //\r
+       @Deprecated\r
+       private int detailAreaHeight;\r
+       @Deprecated\r
+       public int getDetailAreaHeight() { return detailAreaHeight; }\r
+       @Deprecated\r
+       public void setDetailAreaHeight(int h) { detailAreaHeight = h; }\r
+       //\r
+       @Deprecated\r
+       private int statusAreaHeight;\r
+       @Deprecated\r
+       public int getStatusAreaHeight() { return statusAreaHeight; }\r
+       @Deprecated\r
+       public void setStatusAreaHeight(int h) { statusAreaHeight = h; }\r
+       \r
+       //\r
+       private int selectedTab;\r
+       public int getSelectedTab() { return selectedTab; }\r
+       public void setSelectedTab(int t) { selectedTab = t; }\r
+       \r
+       //\r
+       private boolean showSettingTabs;\r
+       public boolean getShowSettingTabs() { return showSettingTabs; }\r
+       public void setShowSettingTabs(boolean b) { showSettingTabs = b; }\r
+       \r
+       // 検索マッチ枠を表示する\r
+       public boolean getShowMatchedBorder() { return showMatchedBorder; }\r
+       public void setShowMatchedBorder(boolean b) { showMatchedBorder = b; }\r
+       private boolean showMatchedBorder = true;\r
+       \r
+       // ステータスエリアを表示する\r
+       private boolean showStatus;\r
+       public boolean getShowStatus() { return showStatus; }\r
+       public void setShowStatus(boolean b) { showStatus = b; }\r
+       \r
+       // 番組詳細エリアの高さ\r
+       public int getDetailRows() { return detailRows; }\r
+       public void setDetailRows(int n) { detailRows = n; }\r
+       private int detailRows = 4;\r
+       \r
+       // ステータスエリアの高さ\r
+       public int getStatusRows() { return statusRows; }\r
+       public void setStatusRows(int n) { statusRows = n; }\r
+       private int statusRows = 4;\r
+       \r
+       //\r
+       private boolean enableTimer;\r
+       public boolean getEnableTimer() { return enableTimer; }\r
+       public void setEnableTimer(boolean b) { enableTimer = b; }\r
+       \r
+       //\r
+       public int getTimelinePosition() { return timelinePosition; }\r
+       public void setTimelinePosition(int n) { timelinePosition = n; }\r
+       private int timelinePosition = 30;\r
+       \r
+       // リスト形式のカラム幅\r
+       \r
+       @Deprecated\r
+       private HashMap<Viewer.ListedColumn, Integer> listedColumnWidth;\r
+       @Deprecated\r
+       public HashMap<Viewer.ListedColumn, Integer> getListedColumnWidth() { return listedColumnWidth; }\r
+       @Deprecated\r
+       public void setListedColumnWidth(HashMap<Viewer.ListedColumn, Integer> map) {\r
+               //listedColumnWidth = map;\r
+               if ( map.size() > 0 ) {\r
+                       for ( Viewer.ListedColumn lc : map.keySet() ) {\r
+                               // 新しいほうに入れなおす\r
+                               listedColumnSize.put(lc.toString(), map.get(lc));\r
+                       }\r
+                       listedColumnWidth.clear();\r
+               }\r
+       }\r
+\r
+       private HashMap<String, Integer> listedColumnSize;\r
+       public HashMap<String, Integer> getListedColumnSize() { return listedColumnSize; }\r
+       public void setListedColumnSize(HashMap<String, Integer> map) { listedColumnSize = map; }\r
+\r
+       // 本体予約一覧のカラム幅\r
+       \r
+       @Deprecated\r
+       private HashMap<Viewer.RsvedColumn, Integer> rsvedColumnWidth;\r
+       @Deprecated\r
+       public HashMap<Viewer.RsvedColumn, Integer> getRsvedColumnWidth() { return rsvedColumnWidth; }\r
+       @Deprecated\r
+       public void setRsvedColumnWidth(HashMap<Viewer.RsvedColumn, Integer> map) {\r
+               //rsvedColumnWidth = map;\r
+               if ( map.size() > 0 ) {\r
+                       for ( Viewer.RsvedColumn rc : map.keySet() ) {\r
+                               // 新しいほうに入れなおす\r
+                               rsvedColumnSize.put(rc.toString(), map.get(rc));\r
+                       }\r
+                       rsvedColumnWidth.clear();\r
+               }\r
+       }\r
+\r
+       private HashMap<String, Integer> rsvedColumnSize;\r
+       public HashMap<String, Integer> getRsvedColumnSize() { return rsvedColumnSize; }\r
+       public void setRsvedColumnSize(HashMap<String, Integer> map) { rsvedColumnSize = map; }\r
+\r
+       // 選択中のレコーダ\r
+       private String selectedRecorderId;\r
+       public String getSelectedRecorderId() { return selectedRecorderId; }\r
+       public void setSelectedRecorderId(String s) { selectedRecorderId = s; }\r
+       \r
+       //\r
+       private boolean expandedSyobocalNode;\r
+       private boolean expandedStandbyNode;\r
+       private boolean expandedTraceNode;\r
+       private boolean expandedKeywordNode;\r
+       private boolean expandedKeywordGrpNode;\r
+       private boolean expandedGenreNode;\r
+       private boolean expandedCenterListNode;\r
+       private boolean expandedExtensionNode;\r
+       public boolean getExpandedSyobocalNode() { return expandedSyobocalNode; }\r
+       public void setExpandedSyobocalNode(boolean b) { expandedSyobocalNode = b; }\r
+       public boolean getExpandedStandbyNode() { return expandedStandbyNode; }\r
+       public void setExpandedStandbyNode(boolean b) { expandedStandbyNode = b; }\r
+       public boolean getExpandedTraceNode() { return expandedTraceNode; }\r
+       public void setExpandedTraceNode(boolean b) { expandedTraceNode = b; }\r
+       public boolean getExpandedKeywordNode() { return expandedKeywordNode; }\r
+       public void setExpandedKeywordNode(boolean b) { expandedKeywordNode = b; }\r
+       public boolean getExpandedKeywordGrpNode() { return expandedKeywordGrpNode; }\r
+       public void setExpandedKeywordGrpNode(boolean b) { expandedKeywordGrpNode = b; }\r
+       public boolean getExpandedGenreNode() { return expandedGenreNode; }\r
+       public void setExpandedGenreNode(boolean b) { expandedGenreNode = b; }\r
+       public boolean getExpandedCenterListNode() { return expandedCenterListNode; }\r
+       public void setExpandedCenterListNode(boolean b) { expandedCenterListNode = b; }\r
+       public boolean getExpandedExtensionNode() { return expandedExtensionNode; }\r
+       public void setExpandedExtensionNode(boolean b) { expandedExtensionNode = b; }\r
+       \r
+       //\r
+       private boolean expandedDateNode;\r
+       private boolean expandedDgNode;\r
+       private boolean expandedCsNode;\r
+       private boolean expandedRadioNode;\r
+       private boolean expandedCenterNode;\r
+       public boolean getExpandedDateNode() { return expandedDateNode; }\r
+       public void setExpandedDateNode(boolean b) { expandedDateNode = b; }\r
+       public boolean getExpandedDgNode() { return expandedDgNode; }\r
+       public void setExpandedDgNode(boolean b) { expandedDgNode = b; }\r
+       public boolean getExpandedCsNode() { return expandedCsNode; }\r
+       public void setExpandedCsNode(boolean b) { expandedCsNode = b; }\r
+       public boolean getExpandedRadioNode() { return expandedRadioNode; }\r
+       public void setExpandedRadioNode(boolean b) { expandedRadioNode = b; }\r
+       public boolean getExpandedCenterNode() { return expandedCenterNode; }\r
+       public void setExpandedCenterNode(boolean b) { expandedCenterNode = b; }\r
+       \r
+       //\r
+       private int frameBufferSize;\r
+       public int getFrameBufferSize() { return frameBufferSize; }\r
+       public void setFrameBufferSize(int n) { frameBufferSize = n; }\r
+       \r
+       //\r
+       private boolean loaded;\r
+       public boolean isLoaded() { return loaded; }\r
+       public void setLoaded(boolean b) { loaded = b; }\r
+       \r
+       \r
+       /***\r
+        * \r
+        */\r
+       \r
+       public boolean save() {\r
+               System.out.println("ウィンドウサイズ・位置情報を保存します: "+BOUNDS_TEXT);\r
+               if ( ! FieldUtils.save(BOUNDS_TEXT, this) ) {\r
+                       System.err.println("ウィンドウサイズ・位置情報の保存に失敗しました.");\r
+                       return false;\r
+               }\r
+               return true;\r
+       }\r
+       \r
+       public boolean load() {\r
+               \r
+               File ft = new File(BOUNDS_TEXT);\r
+               if ( ft.exists() ) {\r
+                       // 移行済み\r
+                       System.out.println("@Deprecated: "+BOUNDS_FILE);\r
+                       return true;\r
+               }\r
+               \r
+               System.out.println("ウィンドウサイズ・位置情報を読み込みます: "+BOUNDS_FILE);\r
+               File fx = new File(BOUNDS_FILE);\r
+               if ( fx.exists() ) {\r
+                       Bounds b = (Bounds) CommonUtils.readXML(BOUNDS_FILE);\r
+                       if ( b != null ) {\r
+                               b.setListedColumnWidth(b.getListedColumnWidth());       // 旧型式→新形式に変換\r
+                               b.setRsvedColumnWidth(b.getRsvedColumnWidth());         // 旧型式→新形式に変換\r
+                               CommonUtils.FieldCopy(this, b);\r
+                               \r
+                               // テキスト形式がなければ作るよ\r
+                               if ( FieldUtils.save(BOUNDS_TEXT,this) ) {\r
+                                       fx.renameTo(new File(BOUNDS_FILE+".bak"));\r
+                               }\r
+                               \r
+                               return true;\r
+                       }\r
+               }\r
+\r
+               System.err.println("ウィンドウサイズ・位置情報を読み込めなかったのでデフォルトの設定値を利用します.");\r
+               return false;\r
+       }\r
+       \r
+       public boolean loadText() {\r
+               System.out.println("ウィンドウサイズ・位置情報を読み込みます: "+BOUNDS_TEXT);\r
+               if ( FieldUtils.load(BOUNDS_TEXT, this) ) {\r
+                       return true;\r
+               }\r
+               System.err.println("ウィンドウサイズ・位置情報を読み込めなかったのでデフォルトの設定値を利用します.");\r
+               return false;\r
+       }\r
+       \r
+       @SuppressWarnings("unchecked")\r
+       public Bounds() {\r
+               //\r
+               this.setWinRectangle(new Rectangle(0,0,1134,768));\r
+               this.setBangumiColumnWidth(125);\r
+               this.setBangumiColumnHeight(25);\r
+               this.setTreeWidth(180);\r
+               this.setTreeWidthPaper(this.getTreeWidth());\r
+               this.setTooltipWidth(20);\r
+               this.setTimebarColumnWidth(32);\r
+               this.setPaperHeightMultiplier(3);\r
+               this.setDetailAreaHeight(100);\r
+               this.setStatusAreaHeight(21*3);\r
+               this.setSelectedTab(0);\r
+               this.setShowSettingTabs(true);\r
+               //this.setShowMatchedBorder(false);\r
+               this.setShowStatus(true);\r
+               this.setEnableTimer(false);\r
+               this.setFrameBufferSize(900);\r
+               this.setSelectedRecorderId(null);\r
+               \r
+               this.setLoaded(false);\r
+               \r
+               // 旧版との互換部分\r
+               listedColumnWidth = new HashMap<Viewer.ListedColumn, Integer>();\r
+               /*\r
+               for ( Viewer.ListedColumn lc : Viewer.ListedColumn.values() ) {\r
+                       listedColumnWidth.put(lc,lc.getIniWidth());\r
+               }\r
+               */\r
+               \r
+               //\r
+               rsvedColumnWidth = new HashMap<Viewer.RsvedColumn, Integer>();\r
+               /*\r
+               for ( RsvedColumn rc : RsvedColumn.values() ) {\r
+                       rsvedColumnWidth.put(rc,rc.getIniWidth());\r
+               }\r
+               */\r
+               \r
+               // さむしんぐにゅー\r
+               listedColumnSize = (HashMap<String, Integer>) AbsListedView.getColumnIniWidthMap().clone();\r
+               rsvedColumnSize = (HashMap<String, Integer>) AbsReserveListView.getColumnIniWidthMap().clone();\r
+       }\r
+}\r
diff --git a/TinyBannavi/src/tainavi/BroadcastType.java b/TinyBannavi/src/tainavi/BroadcastType.java
new file mode 100644 (file)
index 0000000..30b4147
--- /dev/null
@@ -0,0 +1,123 @@
+package tainavi;\r
+\r
+/**\r
+ * CHコード設定で使ってたけど汎用的に使えそうな気がしてきたので分離してみた\r
+ */\r
+public enum BroadcastType {\r
+       \r
+       TERRA   ("地上波",   "DFS"),\r
+       BS              ("BS",          "BSDT"),\r
+       CS              ("CS",          "CSDT"),\r
+       CAPTURE ("キャプチャ",     ""),\r
+       NONE    ("指定なし",        ""),\r
+       ;\r
+       \r
+       String name;\r
+       String header;\r
+       \r
+       private BroadcastType(String name, String header) {\r
+               this.name = name;\r
+               this.header = header;\r
+       }\r
+       \r
+       public String getName() { return name; }\r
+       \r
+       public String getHeader() { return header; }\r
+       \r
+       public static BroadcastType get(String name) {\r
+               for ( BroadcastType b : BroadcastType.values() ) {\r
+                       if ( b.name.equals(name) ) {\r
+                               return b;\r
+                       }\r
+               }\r
+               return NONE;\r
+       }\r
+\r
+       /**\r
+        * 各レコーダの設定内容から放送波の種類を判別する\r
+        * @param selected\r
+        * @param recChName\r
+        * @param chCode\r
+        * @return\r
+        */\r
+       public static BroadcastType get(String selected, String recChName, String chCode) {\r
+               \r
+               if ( "EpgDataCap_Bon".equals(selected) || "TVTest".equals(selected) ) {\r
+                       if ( chCode != null ) {\r
+                               String chid = ContentIdEDCB.getChId(chCode);\r
+                               if ( chid != null ) {\r
+                                       if ( chid.startsWith("0004") ) {\r
+                                               return BS;\r
+                                       }\r
+                                       if ( chid.startsWith("0006") || chid.startsWith("0007") ) {\r
+                                               return CS;\r
+                                       }\r
+                                       else {\r
+                                               return TERRA;\r
+                                       }\r
+                               }\r
+                       }\r
+                       return NONE;\r
+               }\r
+               else if ( selected.startsWith("VARDIA RD-") || selected.startsWith("REGZA RD-")  || selected.startsWith("REGZA DBR-Z") ) {\r
+                       if ( chCode != null ) {\r
+                               String[] d = chCode.split(":");\r
+                               if ( d.length == 3 ) {\r
+                                       if ( d[1].equals("4") ) {\r
+                                               return BS;\r
+                                       }\r
+                                       else if ( d[1].equals("6") || d[1].equals("7") ) {\r
+                                               return CS;\r
+                                       }\r
+                                       else if ( ! d[1].matches("^0+$") ) {\r
+                                               return TERRA;\r
+                                       }\r
+                               }\r
+                       }\r
+                       return NONE;\r
+               }\r
+               else if ( selected.startsWith("REGZA") && chCode != null && chCode.matches("^[Gg][0-9A-Fa-f]{8}$") ) {\r
+                       if ( chCode.matches("^[Gg]0004....$") ) {\r
+                               return BS;\r
+                       }\r
+                       else if ( chCode.matches("^[Gg]000[67]....$") ) {\r
+                               return CS;\r
+                       }\r
+                       else if ( chCode.matches("^[Gg]7.......$") ) {\r
+                               return TERRA;\r
+                       }\r
+                       return NONE;\r
+               }\r
+               else if ( selected.startsWith("DIGA ") ) {\r
+                       if ( recChName != null ) {\r
+                               if ( recChName.startsWith("地上D ") ) {\r
+                                       return TERRA;\r
+                               }\r
+                               else if ( recChName.startsWith("BS ") ) {\r
+                                       return BS;\r
+                               }\r
+                               else if ( recChName.startsWith("CS ") || recChName.startsWith("CS2 ")) {\r
+                                       return CS;\r
+                               }\r
+                       }\r
+                       return NONE;\r
+               }\r
+               else if ( "iEPG2".equals(selected) ) {\r
+                       if ( recChName != null ) {\r
+                               if ( recChName.startsWith(TERRA.getHeader()) ) {\r
+                                       return TERRA;\r
+                               }\r
+                               else if ( recChName.startsWith(BS.getHeader()) ) {\r
+                                       return BS;\r
+                               }\r
+                               else if ( recChName.startsWith(CS.getHeader()) ) {\r
+                                       return CS;\r
+                               }\r
+                       }\r
+                       return NONE;\r
+               }\r
+\r
+               return null;\r
+       }\r
+\r
+}\r
diff --git a/TinyBannavi/src/tainavi/CHAVSetting.java b/TinyBannavi/src/tainavi/CHAVSetting.java
new file mode 100644 (file)
index 0000000..0f8abe3
--- /dev/null
@@ -0,0 +1,11 @@
+package tainavi;\r
+\r
+import java.io.File;\r
+\r
+public class CHAVSetting extends AVSetting {\r
+       \r
+       // コンストラクタ\r
+       public CHAVSetting() {\r
+               avsettingFile = "env"+File.separator+"chavsetting.xml";\r
+       }\r
+}\r
diff --git a/TinyBannavi/src/tainavi/Center.java b/TinyBannavi/src/tainavi/Center.java
new file mode 100644 (file)
index 0000000..a0650c1
--- /dev/null
@@ -0,0 +1,75 @@
+package tainavi;\r
+\r
+import java.awt.Color;\r
+\r
+public class Center implements Cloneable {\r
+       private String areacode;\r
+       private String center;\r
+       private String center_orig = null;\r
+       private String link;\r
+       private String type;\r
+       private boolean enabled;\r
+       private int order;\r
+       private Color bgcolor;\r
+       \r
+       public Center() {\r
+               this.areacode = "";\r
+               this.center = "";\r
+               this.link = "";\r
+               this.type = "";\r
+               this.enabled = true;\r
+               this.order = 0;\r
+               this.bgcolor = new Color(180,180,180);\r
+       }\r
+       public Center(String s, boolean b) {\r
+               this.areacode = "";\r
+               this.center = s;\r
+               this.link = "";\r
+               this.type = "";\r
+               this.enabled = b;\r
+               this.order = 0;\r
+               this.bgcolor = new Color(180,180,180);\r
+       }\r
+       public Center(String s, String l, boolean b) {\r
+               this.areacode = "";\r
+               this.center = s;\r
+               this.link = l;\r
+               this.type = "";\r
+               this.enabled = b;\r
+               this.order = 0;\r
+               this.bgcolor = new Color(180,180,180);\r
+       }\r
+       \r
+       @Override\r
+       public Center clone() {\r
+               try {\r
+                       return (Center) super.clone();\r
+               } catch (CloneNotSupportedException e) {\r
+                       throw new InternalError(e.toString());\r
+               }\r
+       }\r
+       \r
+       public String getAreaCode() { return this.areacode; }\r
+       public void setAreaCode(String s) { this.areacode = s; }\r
+       \r
+       public String getCenter() { return this.center; }\r
+       public void setCenter(String s) { this.center = s; }\r
+\r
+       public String getCenterOrig() { return this.center_orig; }\r
+       public void setCenterOrig(String s) { this.center_orig = s; }\r
+\r
+       public String getLink() { return this.link; }\r
+       public void setLink(String s) { this.link = s; }\r
+\r
+       public String getType() { return this.type; }\r
+       public void setType(String s) { this.type = s; }\r
+\r
+       public boolean getEnabled() { return this.enabled; }\r
+       public void setEnabled(boolean b) { this.enabled = b; }\r
+\r
+       public int getOrder() { return this.order; }\r
+       public void setOrder(int o) { this.order = o; }\r
+\r
+       public Color getBgColor() { return this.bgcolor; }\r
+       public void setBgColor(Color c) { this.bgcolor = c; }\r
+}\r
diff --git a/TinyBannavi/src/tainavi/ChannelCode.java b/TinyBannavi/src/tainavi/ChannelCode.java
new file mode 100644 (file)
index 0000000..7a9e95b
--- /dev/null
@@ -0,0 +1,263 @@
+package tainavi;\r
+\r
+import java.io.File;\r
+import java.util.ArrayList;\r
+import java.util.HashMap;\r
+import java.util.Map.Entry;\r
+import java.util.regex.Matcher;\r
+import java.util.regex.Pattern;\r
+\r
+/**\r
+ * <P>レコーダで利用するCHコード設定を保持するクラス。\r
+ * <P>NAME : Web番組表の放送局名\r
+ * <P>NO : レコーダの放送局名\r
+ * <P>CODE : 放送局コード\r
+ * @version 3.15.4β EDCBの旧型式→新形式自動変換追加、DIGAの旧型式→新形式自動変換廃止ほか\r
+ */\r
+public class ChannelCode extends ArrayList<String> implements Cloneable {\r
+       \r
+       private static final long serialVersionUID = 1L;\r
+       \r
+       private final String datfile = "env"+File.separator+"RdChannelCode.dat";\r
+       private String recId = null;\r
+       \r
+       private static final String MSGID = "[CHコード設定] ";\r
+       private static final String ERRID = "[ERROR]"+MSGID;\r
+       private static final String DBGID = "[DEBUG]"+MSGID;\r
+\r
+       private final HashMap<String,String> CH_REC2WEB = new HashMap<String,String>();\r
+       private final HashMap<String,String> CH_CODE2REC = new HashMap<String,String>();\r
+       private final HashMap<String,String> CH_WEB2CODE = new HashMap<String,String>();\r
+\r
+       public void setCH_REC2WEB(String key, String val) { CH_REC2WEB.put(key, val); }\r
+       public void setCH_CODE2REC(String key, String val) { CH_CODE2REC.put(key, val); }\r
+       public void setCH_WEB2CODE(String key, String val) { CH_WEB2CODE.put(key, val); }\r
+       \r
+       /**\r
+        * レコーダの放送局名 を Web番組表の放送局名 に変換する。\r
+        */\r
+       public String getCH_REC2WEB(String recChName) { return (String)CH_REC2WEB.get(recChName); }\r
+       \r
+       /**\r
+        * Web番組表の放送局名 を レコーダの放送局名 に変換する。\r
+        */\r
+       public String getCH_WEB2REC(String webChName) {\r
+               for ( Entry<String,String> e : CH_REC2WEB.entrySet() ) {\r
+                       if ( e.getValue().equals(webChName) ) {\r
+                               return e.getKey();\r
+                       }\r
+               }\r
+               return null;\r
+       }\r
+       \r
+       /**\r
+        * 放送局コード を レコーダの放送局名 に変換する。\r
+        */\r
+       public String getCH_CODE2REC(String chCode) { return (String)CH_CODE2REC.get(chCode); }\r
+       \r
+       /**\r
+        * レコーダの放送局名 を 放送局コード に変換する。\r
+        */\r
+       public String getCH_REC2CODE(String recChName) {\r
+               for ( Entry<String,String> e : CH_CODE2REC.entrySet() ) {\r
+                       if ( e.getValue().equals(recChName) ) {\r
+                               return e.getKey();\r
+                       }\r
+               }\r
+               return null;\r
+       }\r
+       \r
+       /**\r
+        * Web番組表の放送局名 を  放送局コード に変換する。\r
+        */\r
+       public String getCH_WEB2CODE(String webChName) { return (String)CH_WEB2CODE.get(webChName); }\r
+       \r
+       /**\r
+        *  放送局コード を Web番組表の放送局名 に変換する。\r
+        */\r
+       public String getCH_CODE2WEB(String chCode) {\r
+               for ( Entry<String,String> e : CH_WEB2CODE.entrySet() ) {\r
+                       if ( e.getValue().equals(chCode) ) {\r
+                               return e.getKey();\r
+                       }\r
+               }\r
+               return null;\r
+       }\r
+       \r
+       // コンストラクタ\r
+       public ChannelCode(String recId) {\r
+               this.recId = recId; \r
+       }\r
+       \r
+       /**\r
+        * Web番組表の放送局名のリストを返す \r
+        */\r
+       public ArrayList<String> getChNames() {\r
+               return this;\r
+       }\r
+       \r
+       /**\r
+        * 読み出します。\r
+        */\r
+       public boolean load(boolean logging)\r
+       {\r
+               // 領域初期化\r
+               CH_REC2WEB.clear();\r
+               CH_CODE2REC.clear();\r
+               CH_WEB2CODE.clear();\r
+               \r
+               // 読み込み\r
+               String ccFile = datfile+"."+recId;\r
+               \r
+               if ( ! new File(ccFile).exists() ) {\r
+                       System.err.println(ERRID+"設定ファイルがみつかりません: "+ccFile);\r
+                       return false;\r
+               }\r
+               \r
+               this.clear();\r
+               \r
+               String buf = CommonUtils.read4file(ccFile, false);\r
+               if ( buf == null ) {\r
+                       System.err.println(ERRID+"設定ファイルの取得に失敗しました: "+ccFile);\r
+                       return false;\r
+               }\r
+               \r
+               String[] codes = buf.split("[\r\n]+");\r
+               for ( String code : codes ) {\r
+                       Matcher ma = Pattern.compile("^\\s*\"(.+?)\",\"(.+?)\",\"(.+?)\"").matcher(code);\r
+                       if (ma.find()) {\r
+                               if (getCH_WEB2CODE(ma.group(1)) == null) setCH_WEB2CODE(ma.group(1),ma.group(3));\r
+                               if (getCH_REC2WEB(ma.group(2)) == null || ma.group(1).startsWith("外部入力")) setCH_REC2WEB(ma.group(2),ma.group(1));\r
+                               if (getCH_CODE2REC(ma.group(3)) == null) setCH_CODE2REC(ma.group(3),ma.group(2));\r
+                               \r
+                               this.add(ma.group(1));\r
+                       }\r
+               }\r
+               \r
+               boolean oldflag = false;\r
+               /* - もういらない\r
+               if ( recId.equals("EpgDataCap_Bon") ) { // 本当はプラグインからレコーダIDの文字列を取得したかったのだけど\r
+                       oldflag = ccModEDCB();\r
+               }\r
+               */\r
+               if ( recId.equals("TVTest") ) {\r
+                       oldflag = ccModTVTest();\r
+               }\r
+               if ( oldflag ) {\r
+                       System.out.println(MSGID+"CHコード設定が自動修正されました: "+ccFile);\r
+                       this.save();\r
+               }\r
+               \r
+               if (logging) {\r
+                       System.out.println(DBGID+"--- "+ccFile+" Start---");\r
+                       \r
+                       String[] keys = CH_REC2WEB.keySet().toArray(new String[0]);\r
+                       for ( String recChName : keys ) {\r
+                               String webChName = CH_REC2WEB.get(recChName);\r
+                               String chCode = CH_WEB2CODE.get(webChName);\r
+                               if ( chCode.equals("error") ) {\r
+                                       System.out.printf("CH: %7s %-32s %-8s %s\n","<Error>",webChName,recChName,chCode);\r
+                               }\r
+                               else {\r
+                                       System.out.printf("CH: %7s %-32s %-8s %s\n","",webChName,recChName,chCode);\r
+                               }\r
+                       }\r
+                       \r
+                       System.out.println(DBGID+"--- "+ccFile+" End ---");\r
+               }\r
+               \r
+               return true;\r
+       }\r
+\r
+       /**\r
+        * 古いEDCBの情報を新形式に更新する。\r
+        */\r
+       /*\r
+       private boolean ccModEDCB() {\r
+               boolean oldflag = false;\r
+               String[] keys = CH_REC2WEB.keySet().toArray(new String[0]);\r
+               for ( String recChName : keys ) {\r
+                       if ( ! recChName.matches("^\\d+$") ) {\r
+                               oldflag = true;\r
+                               String webChName = CH_REC2WEB.get(recChName);\r
+                               String chCode = CH_WEB2CODE.get(webChName);\r
+                               CH_REC2WEB.remove(recChName);\r
+                               CH_CODE2REC.remove(chCode);\r
+                               CH_REC2WEB.put(chCode,webChName);\r
+                               CH_CODE2REC.put(chCode,chCode);\r
+                       }\r
+               }\r
+               return oldflag;\r
+       }\r
+       */\r
+\r
+       /**\r
+        * 古いEDCBの情報を新形式に更新する。\r
+        */\r
+       private boolean ccModTVTest() {\r
+               boolean oldflag = false;\r
+               if (CH_REC2WEB.remove("-") != null) {\r
+                       oldflag = true;\r
+                       String[] keys = CH_CODE2REC.keySet().toArray(new String[0]);\r
+                       for ( String chCode : keys ) {\r
+                               String recChName = chCode;\r
+                               CH_CODE2REC.remove(chCode);\r
+                               CH_CODE2REC.put(chCode, recChName);\r
+                               for ( Entry<String,String> ent : CH_WEB2CODE.entrySet() ) {     // NAME2CODEは変更の必要がない\r
+                                       if ( ent.getValue().equals(chCode) ) {\r
+                                               String webChName = ent.getKey();\r
+                                               CH_REC2WEB.put(recChName,webChName);\r
+                                               break;\r
+                                       }\r
+                               }\r
+                       }\r
+               }\r
+               return oldflag;\r
+       }\r
+       \r
+       /**\r
+        * 保存します。\r
+        */\r
+       public boolean save(ArrayList<String> webChNames, ArrayList<String> recChNames, ArrayList<String> chCodes) {\r
+               \r
+               String ccFile = datfile+"."+recId;\r
+               \r
+               System.err.println(MSGID+"設定ファイルを保存します: "+ccFile);\r
+               StringBuilder sb = new StringBuilder();\r
+               sb.append("# "+CommonUtils.getDateTime(0)+"\n");\r
+               for ( int i=0; i<webChNames.size(); i++ ) {\r
+                       sb.append(String.format("\"%s\",\"%s\",\"%s\"\n", webChNames.get(i), recChNames.get(i), chCodes.get(i)));\r
+               }\r
+               if ( ! CommonUtils.write2file(ccFile, sb.toString()) ) {\r
+                       System.err.println(MSGID+"設定ファイルの保存に失敗しました: "+ccFile);\r
+                       return false;\r
+               }\r
+               return true;\r
+       }\r
+\r
+       /**\r
+        * 自動修正時に利用するよ\r
+        */\r
+       private boolean save() {\r
+               ArrayList<String> webChNames = new ArrayList<String>();\r
+               ArrayList<String> recChNames = new ArrayList<String>();\r
+               ArrayList<String> chCodes = new ArrayList<String>();\r
+               for ( String webChName : this ) {\r
+                       String chCode = CH_WEB2CODE.get(webChName);\r
+                       String recChName = CH_CODE2REC.get(chCode);\r
+                       \r
+                       webChNames.add(webChName);\r
+                       recChNames.add(recChName);\r
+                       chCodes.add(chCode);\r
+               }\r
+               return this.save(webChNames, recChNames, chCodes);\r
+       }\r
+\r
+       //\r
+       @Override\r
+       public ChannelCode clone() {\r
+               ChannelCode cc = (ChannelCode) super.clone();\r
+               CommonUtils.FieldCopy(cc, this); // ディープコピーするよ\r
+               return cc;\r
+       }\r
+}\r
diff --git a/TinyBannavi/src/tainavi/ChannelConvert.java b/TinyBannavi/src/tainavi/ChannelConvert.java
new file mode 100644 (file)
index 0000000..f389636
--- /dev/null
@@ -0,0 +1,84 @@
+package tainavi;\r
+\r
+import java.io.File;\r
+import java.util.HashMap;\r
+import java.util.regex.Matcher;\r
+import java.util.regex.Pattern;\r
+\r
+public class ChannelConvert extends HashMap<String,String> {\r
+\r
+       private static final long serialVersionUID = 1L;\r
+       \r
+       private static boolean debug = false;\r
+       \r
+       public final static String chFilterFile = "env"+File.separator+"ChannelConvert.dat";\r
+       \r
+       /**\r
+        * 一致するものがなければそのまま返す\r
+        */\r
+       @Override\r
+       public String get(Object from) {\r
+               String to = super.get(from);\r
+               return ((to!=null)?(to):((String)from));\r
+       }\r
+       \r
+       //\r
+       public boolean load()\r
+       {\r
+               if ( ! new File(chFilterFile).exists() ) {\r
+                       System.err.println("[ChannelConvert]ファイルがない: "+chFilterFile);\r
+                       return false;\r
+               }\r
+               String s = CommonUtils.read4file(chFilterFile, false);\r
+               if ( s == null ) {\r
+                       System.err.println("[ChannelConvert]ファイルの取得に失敗: "+chFilterFile);\r
+                       return false;\r
+               }\r
+               \r
+               this.clear();\r
+\r
+               int lineno = 0;\r
+               String[] d = s.split("[\\r\\n]+");\r
+               for ( String v : d ) {\r
+                       lineno++;\r
+                       v = v.trim();\r
+                       if ( v.startsWith("#") ) {\r
+                               continue;\r
+                       }\r
+                       Matcher ma = Pattern.compile("\"(.+?)\"\\s*,\\s*\"(.+?)\"",Pattern.DOTALL).matcher(v);\r
+                       if ( ma.find() ) {\r
+                               if (this.containsKey(ma.group(1))) {\r
+                                       if (this.get(ma.group(1)).equals(ma.group(2))) {\r
+                                               System.err.println("【警告】重複したエントリーが存在します("+chFilterFile+"): key="+ma.group(1)+" lineno="+lineno);\r
+                                       }\r
+                                       else {\r
+                                               System.err.println("【警告】重複したキーに異なる値が設定されています("+chFilterFile+"): key="+ma.group(1)+" lineno="+lineno);\r
+                                       }\r
+                               }\r
+                               \r
+                               this.put(ma.group(1), ma.group(2));\r
+                       }\r
+               }\r
+               for ( String val : this.values() ) {\r
+                       if ( this.containsKey(val) ) {\r
+                               System.err.println("【警告】値が他のエントリのキーと同じです("+chFilterFile+"): val="+val+" lineno=?");\r
+                       }\r
+               }\r
+               \r
+               if (debug) {\r
+                       for ( String key : this.keySet() ) {\r
+                               System.err.println(String.format("[DEBUG] channelconvert from=%s to=%s",key,this.get(key)));\r
+                       }\r
+               }\r
+               \r
+               return true;\r
+       }\r
+       \r
+       //\r
+       /* まだない\r
+       public boolean save()\r
+       {\r
+       }\r
+       */\r
+       \r
+}\r
diff --git a/TinyBannavi/src/tainavi/ChannelSettingPanel.java b/TinyBannavi/src/tainavi/ChannelSettingPanel.java
new file mode 100644 (file)
index 0000000..abe05b3
--- /dev/null
@@ -0,0 +1,1147 @@
+package tainavi;\r
+\r
+import java.awt.Color;\r
+import java.awt.Component;\r
+import java.awt.Dimension;\r
+import java.awt.Point;\r
+import java.awt.event.ItemEvent;\r
+import java.awt.event.ItemListener;\r
+import java.awt.event.MouseAdapter;\r
+import java.awt.event.MouseEvent;\r
+import java.util.ArrayList;\r
+\r
+import javax.swing.JButton;\r
+import javax.swing.JComboBox;\r
+import javax.swing.JLabel;\r
+import javax.swing.JPanel;\r
+import javax.swing.JScrollPane;\r
+import javax.swing.JTable;\r
+import javax.swing.ListSelectionModel;\r
+import javax.swing.ScrollPaneConstants;\r
+import javax.swing.SpringLayout;\r
+import javax.swing.SwingUtilities;\r
+import javax.swing.border.LineBorder;\r
+import javax.swing.event.DocumentEvent;\r
+import javax.swing.event.DocumentListener;\r
+import javax.swing.table.DefaultTableColumnModel;\r
+import javax.swing.table.DefaultTableModel;\r
+import javax.swing.table.TableColumn;\r
+import javax.swing.table.TableModel;\r
+\r
+/**\r
+ * CH設定について、放送の種類の別に独立したパネルを用意\r
+ * @version 3.15.4β \r
+ */\r
+public class ChannelSettingPanel extends JPanel {\r
+\r
+       private static final long serialVersionUID = 1L;\r
+       \r
+       public static void setDebug(boolean b) {debug = b; }\r
+       private static boolean debug = false;\r
+       \r
+       /*\r
+        * メンバ\r
+        */\r
+\r
+       private String label = "";\r
+       private TVProgramList progPlugins = null;\r
+       private boolean hasArea = true;\r
+       private VWColorChooserDialog ccwin = null;\r
+       private StatusWindow stwin = null;\r
+       private Component parent = null;\r
+       \r
+       /**\r
+        * 現在使用されているプラグイン\r
+        */\r
+       private TVProgram usingNow = null;\r
+       \r
+       /**\r
+        * <P>起動時 {@link #getUsingPluginForInit()}でclone()\r
+        * <P>番組表を切り替えたとき {@link #getEditingPluginForEdit()}でclone()\r
+        */\r
+       private TVProgram editingNow = null;\r
+       \r
+       private static final String flTextNormal = "Webサイトから放送局リストを取得し直す";\r
+       private static final String flTextWarn = "「更新を確定する」を実行してください";\r
+\r
+       \r
+       \r
+       /*******************************************************************************\r
+        * 定数\r
+        ******************************************************************************/\r
+       \r
+       //private static final int PARTS_WIDTH = 900;\r
+       private static final int PARTS_HEIGHT = 30;\r
+       private static final int SEP_WIDTH = 10;\r
+       private static final int SEP_HEIGHT = 10;\r
+       \r
+       private static final int LABEL_WIDTH = 250;\r
+       private static final int BUTTON_WIDTH = 50;\r
+       private static final int BUTTON_WIDTH_LONG = 100;\r
+       private static final int TABLE_NAME_WIDTH = 250;\r
+       private static final int TABLE_AREA_WIDTH = 75;\r
+       private static final int TABLE_COLOR_WIDTH = 25;\r
+       private static final int TABLE_WIDTH = TABLE_NAME_WIDTH+TABLE_AREA_WIDTH+TABLE_COLOR_WIDTH;\r
+       private static final int TABLE_HEIGHT = 350;\r
+       //private static final int PANEL_WIDTH = PARTS_WIDTH+100;\r
+\r
+       private static final String MSGID = "[CH設定パネル] ";\r
+       private static final String ERRID = "[ERROR]"+MSGID;\r
+       private static final String DBGID = "[DEBUG]"+MSGID;\r
+       \r
+       // カラム設定\r
+       \r
+       private static enum ChSetColumn {\r
+               CENTER          ("有効無効",        TABLE_NAME_WIDTH),\r
+               AREA            ("エリア",           TABLE_AREA_WIDTH),\r
+               COLOR           ("色",         TABLE_COLOR_WIDTH),\r
+               ;\r
+               \r
+               private String name;\r
+               private int width;\r
+               \r
+               private ChSetColumn(String name, int width) {\r
+                       this.name = name;\r
+                       this.width = width;\r
+               }\r
+               \r
+               String getName() { return name; }\r
+               \r
+               int getIniWidth() { return width; }\r
+               \r
+               int getColumn() { return ordinal(); }\r
+       }\r
+       \r
+       /*******************************************************************************\r
+        * 部品\r
+        ******************************************************************************/\r
+\r
+       // コンポーネント\r
+       \r
+       private JComboBox jComboBox_progplugin = null;\r
+       private JComboBox jComboBox_area = null;\r
+       private JScrollPane jScrollPane_enable = null;\r
+       private JScrollPane jScrollPane_disable = null;\r
+       private ChSetTable jTable_enable = null;\r
+       private ChSetTable jTable_disable = null;\r
+       private JButton jButton_d2e = null;\r
+       private JButton jButton_e2d = null;\r
+       private JButton jButton_up = null;\r
+       private JButton jButton_down = null;\r
+       private JButton jButton_forceload = null;\r
+       private JButton jButton_opt = null;\r
+       //private JTextAreaWithPopup jTextArea_opt = null;\r
+       private JTextFieldWithPopup jTextField_opt = null;\r
+       \r
+       // コンポーネント以外\r
+       \r
+       /*******************************************************************************\r
+        * コンストラクタ\r
+        ******************************************************************************/\r
+       \r
+       public ChannelSettingPanel(String label, TVProgramList progPlugins, final String selectedsite, boolean hasArea, VWColorChooserDialog ccwin, StatusWindow stwin, Component parent) {\r
+               \r
+               super();\r
+               \r
+               // 初期化\r
+               this.label = label;\r
+               this.progPlugins = progPlugins;\r
+               this.hasArea = hasArea;\r
+               this.ccwin = ccwin;\r
+               this.stwin = stwin;\r
+               this.parent = parent;\r
+               \r
+               // パーツの組み立て\r
+               this.setLayout(new SpringLayout());\r
+               \r
+               //コンポーネントの生成\r
+               getMyComponents();\r
+               \r
+               // 起動時に選択されているWeb番組表プラグインの情報を反映する\r
+               getUsingPluginForInit(selectedsite);\r
+       }\r
+       \r
+       private void getMyComponents() {\r
+               int y = SEP_HEIGHT;\r
+               \r
+               int x = SEP_WIDTH;\r
+               CommonSwingUtils.putComponentOn(this, new JLabel(label), LABEL_WIDTH, PARTS_HEIGHT, x, y);\r
+               x += LABEL_WIDTH+SEP_WIDTH;\r
+               CommonSwingUtils.putComponentOn(this, getJComboBox_progplugin(), TABLE_WIDTH, PARTS_HEIGHT, x, y);\r
+               x += TABLE_WIDTH+SEP_WIDTH+BUTTON_WIDTH+SEP_WIDTH;\r
+               CommonSwingUtils.putComponentOn(this, getJButton_forceload(flTextNormal), TABLE_WIDTH, PARTS_HEIGHT, x, y);\r
+\r
+               if (this.hasArea) {\r
+                       y+=(PARTS_HEIGHT+SEP_HEIGHT);\r
+                       CommonSwingUtils.putComponentOn(this, new JLabel("放送エリア"), LABEL_WIDTH, PARTS_HEIGHT, SEP_WIDTH, y);\r
+                       CommonSwingUtils.putComponentOn(this, new JLabel("(他県局選択は「全国」を指定)"), LABEL_WIDTH, PARTS_HEIGHT, SEP_WIDTH, y+PARTS_HEIGHT/2);\r
+                       CommonSwingUtils.putComponentOn(this, getJComboBox_area(), TABLE_WIDTH, PARTS_HEIGHT, SEP_WIDTH+LABEL_WIDTH+SEP_WIDTH, y);\r
+               }\r
+\r
+               y+=(PARTS_HEIGHT+SEP_HEIGHT);\r
+               x = SEP_WIDTH+LABEL_WIDTH+SEP_WIDTH;\r
+               CommonSwingUtils.putComponentOn(this, getJScrollPane_enable(), TABLE_WIDTH, TABLE_HEIGHT, x, y);\r
+               \r
+               int yz = y+TABLE_HEIGHT/2;\r
+               int xz = x + SEP_WIDTH+TABLE_WIDTH;\r
+               CommonSwingUtils.putComponentOn(this, getJButton_d2e("<"),    BUTTON_WIDTH, PARTS_HEIGHT, xz, yz-PARTS_HEIGHT-SEP_HEIGHT/2);\r
+               CommonSwingUtils.putComponentOn(this, getJButton_e2d(">"),    BUTTON_WIDTH, PARTS_HEIGHT, xz, yz+SEP_HEIGHT/2);\r
+\r
+               yz = y+TABLE_HEIGHT-PARTS_HEIGHT*2-SEP_HEIGHT;\r
+               CommonSwingUtils.putComponentOn(this, getJButton_up("↑"),     BUTTON_WIDTH, PARTS_HEIGHT, xz, yz);\r
+               CommonSwingUtils.putComponentOn(this, getJButton_down("↓"), BUTTON_WIDTH, PARTS_HEIGHT, xz, yz+SEP_HEIGHT+PARTS_HEIGHT);\r
+               \r
+               xz += BUTTON_WIDTH+SEP_WIDTH;\r
+               CommonSwingUtils.putComponentOn(this, getJScrollPane_disable(), TABLE_WIDTH, TABLE_HEIGHT, xz, y);\r
+               \r
+               int panel_w = xz+TABLE_WIDTH+SEP_WIDTH;\r
+               \r
+               y+=(TABLE_HEIGHT+SEP_HEIGHT);\r
+               \r
+               int tt_l = TABLE_WIDTH*2+SEP_WIDTH*2+BUTTON_WIDTH;\r
+               CommonSwingUtils.putComponentOn(this, new JLabel("オプション指定(形式:KEY=VAL;)"), LABEL_WIDTH, PARTS_HEIGHT, SEP_WIDTH, y);\r
+               CommonSwingUtils.putComponentOn(this, getJTextArea_opt(), tt_l-BUTTON_WIDTH_LONG-SEP_WIDTH, PARTS_HEIGHT, x, y);\r
+               CommonSwingUtils.putComponentOn(this, getJButton_opt("futuer use."), BUTTON_WIDTH_LONG, PARTS_HEIGHT, x+tt_l-BUTTON_WIDTH_LONG, y);\r
+\r
+               y+=(PARTS_HEIGHT+SEP_HEIGHT);\r
+               \r
+               //y += SEP_HEIGHT;\r
+               \r
+               Dimension d = new Dimension(panel_w,y);\r
+               this.setPreferredSize(d);\r
+               this.setBorder(new LineBorder(new Color(0,0,0)));\r
+       }\r
+       \r
+       /*******************************************************************************\r
+        * アクション\r
+        ******************************************************************************/\r
+       \r
+       /*\r
+        * 公開メソッド\r
+        */\r
+       \r
+       /**\r
+        *  選択中の放送局を返却する\r
+        */\r
+       public String getSelectedCenter() {\r
+               return (String)jComboBox_progplugin.getSelectedItem();\r
+       }\r
+\r
+       /**\r
+        * <P>更新の確定時に呼び出されるメソッド\r
+        * <P>プラグイン内の設定はここで保存、プラグインの選択情報は親で保存\r
+        */\r
+       public boolean saveChannelSetting() {\r
+               if ( editingNow != null ) {\r
+                       // 放送局リスト再取得ボタンをもとに戻す\r
+                       setReloadButtonEnhanced(false);\r
+                       // ワークを使って保存する\r
+                       editingNow.setSelectedAreaByName(getSelectedArea());\r
+                       editingNow.saveAreaCode();\r
+                       setChOrder(editingNow);\r
+                       setChBgColor(editingNow);\r
+                       editingNow.saveCenter();\r
+                       if ( ! editingNow.setOptString((String) jTextField_opt.getText()) ) {\r
+                               // メッセージぐらいでてもいいのでは\r
+                               stwin.appendError(ERRID+"【致命的】フリーワードオプションの設定に失敗しました: "+((String) jTextField_opt.getText()));\r
+                       }\r
+                       jTextField_opt.setText(editingNow.getOptString());\r
+                       //\r
+                       int size = progPlugins.size();\r
+                       for ( int i=0; i<size; i++ ) {\r
+                               TVProgram px = progPlugins.get(i);\r
+                               if ( px.getTVProgramId().equals(editingNow.getTVProgramId()) ) {\r
+                                       progPlugins.set(i,editingNow);\r
+                                       usingNow = editingNow;\r
+                                       return true;\r
+                               }\r
+                       }\r
+                       return false;\r
+               }\r
+               return false;\r
+       }\r
+       \r
+       \r
+       /*\r
+        * 非公開\r
+        */\r
+        \r
+       /**\r
+        * <P>インスタンス作成時に編集するプラグインを選択する\r
+        * <P>起動時一回のみの実行\r
+        * <P>コンボボックス選択によりイベントも起こす\r
+        * @see #il_progChanged\r
+        */\r
+       private boolean getUsingPluginForInit(String selectedsite) {\r
+               if ( progPlugins.size() == 0 ) {\r
+                       stwin.appendError(ERRID+"【致命的】プラグインが1つも登録されていません: "+selectedsite);\r
+                       return false;\r
+               }\r
+               \r
+               // 起動時に選択されていたプラグインはusingNowである\r
+               TVProgram p = progPlugins.getProgPlugin(selectedsite);\r
+               if ( p == null ) {\r
+                       p = progPlugins.get(0);\r
+                       stwin.appendError(ERRID+"指定のプラグインがみつからなかったので代替のプラグインを利用します: "+selectedsite+" -> "+p.getTVProgramId());\r
+               }\r
+               else {\r
+                       System.out.println(MSGID+"番組表プラグインの初期値です: "+selectedsite+" -> "+p.getTVProgramId());\r
+               }\r
+               usingNow = p;\r
+               \r
+               // 初期化\r
+               p.loadAreaCode();\r
+               p.loadCenter(p.getSelectedCode(), false);\r
+               p.setSortedCRlist();\r
+               \r
+               final String area = (p.getSelectedArea()==null)?(p.getDefaultArea()):p.getSelectedArea();\r
+               \r
+               TatCount tc = new TatCount();\r
+               stwin.appendMessage(MSGID+"選択されている番組表: "+p.getTVProgramId()+" / "+area);\r
+               \r
+               // 編集対象editingNowは、現時点ではusingNowと同じである\r
+               editingNow = p.clone();\r
+               \r
+               // MacOSXで起動時ハングアップが発生することがあるのでトラップコードを入れてみた\r
+               try {\r
+                       // プラグイン選択\r
+                       jComboBox_progplugin.removeItemListener(il_progChanged);\r
+                       jComboBox_progplugin.setSelectedItem(editingNow.getTVProgramId());\r
+                       jComboBox_progplugin.addItemListener(il_progChanged);\r
+\r
+                       // エリア選択\r
+                       if (hasArea) {\r
+                               jComboBox_area.removeItemListener(il_areaChanged);\r
+                               for ( AreaCode ac : editingNow.getAClist() ) {\r
+                                       jComboBox_area.addItem(ac.getArea());\r
+                               }\r
+                               jComboBox_area.setSelectedItem(editingNow.getSelectedArea());\r
+                               jComboBox_area.addItemListener(il_areaChanged);\r
+                       }\r
+                       \r
+                       // テーブルへ設定する\r
+                       addCentersToTable(jTable_enable, jTable_disable, editingNow);\r
+                       \r
+                       if (CommonUtils.isMac()) System.err.println(DBGID+"mac debug 1");\r
+                       \r
+                       // オプションの扱い\r
+                       String opt = editingNow.getOptString();\r
+                       setOptionFieldEnabled(opt);\r
+                       \r
+                       if (CommonUtils.isMac()) System.err.println(DBGID+"mac debug 2");\r
+               }\r
+               catch (Exception e) {\r
+                       e.printStackTrace();\r
+                       stwin.appendError(ERRID+"[致命的エラー] 異常動作です。製作元に連絡してください。");\r
+               }\r
+               \r
+               stwin.appendMessage(String.format("%s番組表が初期化されました。所要時間: %.2f秒 %s %s",MSGID,tc.end(),p.getTVProgramId(),p.getSelectedArea()));\r
+               \r
+               return true;\r
+       }\r
+       \r
+       /**\r
+        * プラグイン選択コンボボックスを操作した場合\r
+        */\r
+       private boolean getEditingPluginForEdit() {\r
+               \r
+               // コンボボックスで選択されたプラグインは?\r
+               TVProgram p = progPlugins.getProgPlugin(getSelectedPluginId());\r
+               if ( p == null ) {\r
+                       System.err.println(ERRID+"【致命的】選択されたプラグインのインスタンスがみつかりません: "+getSelectedPluginId());\r
+                       return false;\r
+               }\r
+               \r
+               stwin.clear();\r
+               \r
+               TatCount tc = new TatCount();\r
+               stwin.appendMessage(MSGID+"選択された番組表: "+p.getTVProgramId()+" / "+p.getSelectedArea());\r
+               \r
+               // これだね\r
+               editingNow = p.clone();\r
+               \r
+               new SwingBackgroundWorker(false) {\r
+                       @Override\r
+                       protected Object doWorks() throws Exception {\r
+                               // あればファイル、なければWebから\r
+                               editingNow.loadAreaCode();\r
+                               \r
+                               String code = editingNow.getCode(usingNow.getSelectedArea());\r
+                               if ( code != null ) {\r
+                                       editingNow.setSelectedAreaByName(usingNow.getSelectedArea());\r
+                                       if (debug) stwin.appendMessage(DBGID+"切り替え前のプラグインからエリアを引き継ぎました: "+usingNow.getTVProgramId()+":"+usingNow.getSelectedArea()+" -> "+editingNow.getTVProgramId()+":"+editingNow.getSelectedArea());\r
+                               }\r
+                               else {\r
+                                       if (debug) stwin.appendMessage(DBGID+"プラグインがきりかわります: "+usingNow.getTVProgramId()+":"+usingNow.getSelectedArea()+" -> "+editingNow.getTVProgramId()+":"+editingNow.getSelectedArea());\r
+                               }\r
+                               editingNow.loadCenter(editingNow.getSelectedCode(), false);\r
+                               editingNow.setSortedCRlist();\r
+                               \r
+                               // エリア選択\r
+                               if (hasArea) {\r
+                                       jComboBox_area.removeItemListener(il_areaChanged);\r
+                                       // プラグイン選択\r
+                                       jComboBox_area.removeAllItems();\r
+                                       for ( AreaCode ac : editingNow.getAClist() ) {\r
+                                               jComboBox_area.addItem(ac.getArea());\r
+                                       }\r
+                                       jComboBox_area.setSelectedItem(editingNow.getSelectedArea());\r
+                                       jComboBox_area.addItemListener(il_areaChanged);\r
+                               }\r
+                               \r
+                               if ( editingNow.getSortedCRlist().size() == 0 && usingNow.getSortedCRlist().size() > 0 ) {\r
+                                       stwin.appendMessage(MSGID+"有効局が未設定のエリアのため放送局情報の引き継ぎをこころみます ");\r
+                                       inheritEnabledCenters(editingNow, usingNow);\r
+                               }\r
+                               \r
+                               // テーブルへ設定する\r
+                               addCentersToTable(jTable_enable, jTable_disable, editingNow);\r
+                               \r
+                               // オプションの扱い\r
+                               String opt = editingNow.getOptString();\r
+                               setOptionFieldEnabled(opt);\r
+                               \r
+                               return null;\r
+                       }\r
+                       @Override\r
+                       protected void doFinally() {\r
+                               stwin.setVisible(false);\r
+                       }\r
+               }.execute();\r
+               \r
+               CommonSwingUtils.setLocationCenter(parent, (Component) stwin);\r
+               stwin.setVisible(true);\r
+               \r
+               stwin.appendMessage(String.format("%s番組表が切り替わりました。所要時間: %.2f秒 %s %s",MSGID,tc.end(),p.getTVProgramId(),p.getSelectedArea()));\r
+               \r
+               return true;\r
+       }\r
+       \r
+       /**\r
+        * エリア選択コンボボックスを操作した場合\r
+        */\r
+       private boolean setAreaForEdit() {\r
+\r
+               // コンボボックスで選択されたエリアは?\r
+               final String code = editingNow.setSelectedAreaByName(getSelectedArea());\r
+               if ( code == null ) {\r
+                       System.err.println(ERRID+"【致命的】選択されたエリア情報がみつかりません: "+getSelectedPluginId()+" "+getSelectedArea());\r
+                       return false;\r
+               }\r
+               \r
+               stwin.clear();\r
+               \r
+               TatCount tc = new TatCount();\r
+               stwin.appendMessage(MSGID+"選択されたエリア: "+editingNow.getTVProgramId()+" / "+getSelectedArea());\r
+               \r
+               new SwingBackgroundWorker(false) {\r
+                       @Override\r
+                       protected Object doWorks() throws Exception {\r
+                               // あればファイル、なければWebから\r
+                               editingNow.loadCenter(code, false);\r
+                               editingNow.setSortedCRlist();\r
+                               \r
+                               if ( editingNow.getSortedCRlist().size() == 0 && usingNow.getSortedCRlist().size() > 0 ) {\r
+                                       System.out.println(MSGID+"有効局が未設定のエリアのため情報の引き継ぎをこころみます ");\r
+                                       inheritEnabledCenters(editingNow, usingNow);\r
+                               }\r
+                               \r
+                               // テーブルへ設定する\r
+                               addCentersToTable(jTable_enable, jTable_disable, editingNow);\r
+                               \r
+                               return null;\r
+                       }\r
+                       @Override\r
+                       protected void doFinally() {\r
+                               stwin.setVisible(false);\r
+                       }\r
+               }.execute();\r
+               \r
+               CommonSwingUtils.setLocationCenter(parent, (Component) stwin);\r
+               stwin.setVisible(true);\r
+               \r
+               stwin.appendMessage(String.format("%sエリアが切り替わりました。所要時間: %.2f秒 %s %s",MSGID,tc.end(),editingNow.getTVProgramId(),editingNow.getSelectedArea()));\r
+               \r
+               return true;\r
+       }\r
+\r
+       /**\r
+        * 再読み込みしちゃいなちゃーいなちゃいなー\r
+        */\r
+       private boolean getCenterListFromWeb() {\r
+\r
+               setReloadButtonEnhanced(true);\r
+               \r
+               stwin.clear();\r
+               \r
+               TatCount tc = new TatCount();\r
+               stwin.appendMessage(MSGID+"選択されたエリア: "+editingNow.getTVProgramId()+" / "+getSelectedArea());\r
+               \r
+               final TVProgram tmp = editingNow.clone();\r
+               \r
+               new SwingBackgroundWorker(false) {\r
+                       @Override\r
+                       protected Object doWorks() throws Exception {\r
+                               editingNow.setOptString(getOptionField());\r
+                               String code = editingNow.getCode(getSelectedArea());\r
+                               editingNow.loadCenter(code, true);\r
+                               editingNow.setSortedCRlist();\r
+                               \r
+                               if ( editingNow.getSortedCRlist().size() == 0 && tmp.getSortedCRlist().size() > 0 ) {\r
+                                       // editingNowが未選択状態であれば、以前の選択状態を引き継ぎたい\r
+                                       inheritEnabledCenters(editingNow, tmp);\r
+                               }\r
+                               \r
+                               // テーブルへ設定する\r
+                               addCentersToTable(jTable_enable, jTable_disable, editingNow);\r
+                               \r
+                               // オプションの扱い\r
+                               String opt = editingNow.getOptString();\r
+                               setOptionFieldEnabled(opt);\r
+                               \r
+                               return null;\r
+                       }\r
+                       @Override\r
+                       protected void doFinally() {\r
+                               stwin.setVisible(false);\r
+                       }\r
+               }.execute();\r
+               \r
+               CommonSwingUtils.setLocationCenter(parent, (Component) stwin);\r
+               stwin.setVisible(true);\r
+               \r
+               stwin.appendMessage(String.format("%s放送局リストを再読み込みしました。所要時間: %.2f秒 %s %s",MSGID,tc.end(),editingNow.getTVProgramId(),editingNow.getSelectedArea()));\r
+               \r
+               return true;\r
+       }\r
+       \r
+       /**\r
+        * 番組表再取得ボタンを強調表示したりもどしたりする\r
+        */\r
+       private void setReloadButtonEnhanced(boolean b) {\r
+               if ( b ) {\r
+                       jButton_forceload.setText(flTextWarn);\r
+                       jButton_forceload.setForeground(Color.RED);\r
+               }\r
+               else {\r
+                       jButton_forceload.setText(flTextNormal);\r
+                       jButton_forceload.setForeground(Color.BLACK);\r
+               }\r
+       }\r
+       \r
+       // 選択中のプラグインを返却する\r
+       private String getSelectedPluginId() {\r
+               return (String) jComboBox_progplugin.getSelectedItem();\r
+       }\r
+       \r
+       // 選択中のエリアを返却する\r
+       private String getSelectedArea() {\r
+               return ((hasArea)?((String) jComboBox_area.getSelectedItem()):(null));\r
+       }\r
+\r
+       // おぷしょん\r
+       private String getOptionField() {\r
+               return jTextField_opt.getText();\r
+       }\r
+       \r
+       // 放送局リストに並び順を設定する\r
+       private void setChOrder(TVProgram progplugin) {\r
+               \r
+               RowItemList<ChSetItem> eRowdata = jTable_enable.getRowData();\r
+\r
+               // 局順クリア\r
+               for ( Center center : progplugin.getCRlist() ) {\r
+                       center.setOrder(0);\r
+               }\r
+               \r
+               // 局順・色セット\r
+               for ( int row=0; row<eRowdata.size(); row++ ) {\r
+                       ChSetItem c = eRowdata.get(row);\r
+                       String centerName = c.centername;\r
+                       String areaName = c.area;\r
+                       Color colorValue = CommonUtils.str2color(c.color);\r
+                       for ( Center cr : progplugin.getCRlist() ) {\r
+                               if ( cr.getCenter().equals(centerName) && cr.getAreaCode().equals(progplugin.getCode(areaName))) {\r
+                                       cr.setOrder(row+1);\r
+                                       cr.setBgColor(colorValue);\r
+                                       break;\r
+                               }\r
+                       }\r
+               }\r
+               \r
+               // ソート済みリスト作成\r
+               progplugin.setSortedCRlist();\r
+       }\r
+       \r
+       // 放送局リストに背景色を設定する\r
+       private void setChBgColor(TVProgram progplugin) {\r
+               \r
+               RowItemList<ChSetItem> eRowdata = jTable_enable.getRowData();\r
+\r
+               for ( int row=0; row<eRowdata.size(); row++ ) {\r
+                       progplugin.getSortedCRlist().get(row).setBgColor(CommonUtils.str2color(eRowdata.get(row).color));\r
+               }\r
+       }\r
+       \r
+       /**\r
+        *  放送局をテーブルに書き込む\r
+        */\r
+       private void addCentersToTable(ChSetTable eTable, ChSetTable dTable, TVProgram program) {\r
+               \r
+               RowItemList<ChSetItem> eRowdata = eTable.getRowData();\r
+               RowItemList<ChSetItem> dRowdata = dTable.getRowData();\r
+               \r
+               eRowdata.clear();\r
+               dRowdata.clear();\r
+               \r
+               for ( int order=1; order <= program.getCRlist().size(); order++ ) {\r
+                       Center center = null;\r
+                       for ( Center cr : program.getCRlist() ) {\r
+                               if ( cr.getOrder() == order ) {\r
+                                       center = cr;\r
+                                       break;\r
+                               }\r
+                       }\r
+                       if (center != null) {\r
+                               ChSetItem c = new ChSetItem();\r
+                               c.centername = center.getCenter();\r
+                               c.area = program.getArea(center.getAreaCode());\r
+                               c.color = CommonSwingUtils.getColoredString(center.getBgColor(),"-");\r
+                               c.fireChanged();\r
+                               eRowdata.add(c);\r
+                       }\r
+               }\r
+               for ( Center center : program.getCRlist() ) {\r
+                       if ( center.getOrder() == 0  && ! center.getCenter().equals("(選択できません)") ) {\r
+                               ChSetItem c = new ChSetItem();\r
+                               c.centername = center.getCenter();\r
+                               c.area = program.getArea(center.getAreaCode());\r
+                               c.color = CommonSwingUtils.getColoredString(center.getBgColor(),"-");\r
+                               c.fireChanged();\r
+                               dRowdata.add(c);\r
+                       }\r
+               }\r
+\r
+               ((DefaultTableModel) eTable.getModel()).fireTableDataChanged();\r
+               ((DefaultTableModel) dTable.getModel()).fireTableDataChanged();\r
+       }\r
+       \r
+       /**\r
+        * <P>有効局の引き継ぎ\r
+        * <P>有効局が空の番組表を選択した場合にカレントの番組表から放送局の順序などを引き継ぐ\r
+        */\r
+       private void inheritEnabledCenters(TVProgram editing, TVProgram using) {\r
+               \r
+               // 既存の設定のあるものは引き継ぎを行わない\r
+               if ( editing.getSortedCRlist().size() > 0 ) {\r
+                       return;\r
+               }\r
+               \r
+               int order = 1;\r
+               for ( Center ocr : using.getSortedCRlist() ) {\r
+                       Center xcr = null;\r
+                       for ( Center ncr : editing.getCRlist() ) {\r
+                               if ( ncr.getCenter().equals(ocr.getCenter()) ) {\r
+                                       xcr = ncr; \r
+                                       if (editing.getArea(ncr.getAreaCode()).equals(using.getArea(ocr.getAreaCode()))) {\r
+                                               // エリアが等しいものがあればそれを採用する\r
+                                               break;\r
+                                       }\r
+                               }\r
+                       }\r
+                       if (xcr != null) {\r
+                               xcr.setOrder(order++);\r
+                               xcr.setEnabled(true);\r
+                               xcr.setBgColor(ocr.getBgColor());\r
+                       }\r
+               }\r
+               \r
+               // 整理して終わり\r
+               editing.setSortedCRlist();\r
+       }\r
+       \r
+       // 左右\r
+       private void moveChSrcToDst(ChSetTable sTable, ChSetTable dTable)\r
+       {\r
+               RowItemList<ChSetItem> sRowData = sTable.getRowData();\r
+               RowItemList<ChSetItem> dRowData = dTable.getRowData();\r
+               \r
+               if ( sRowData.size() <= 0 ) {\r
+                       return;\r
+               }\r
+               \r
+               int top = sTable.getSelectedRow();\r
+               int length = sTable.getSelectedRowCount();\r
+               \r
+               RowItemList<ChSetItem> tmp = new RowItemList<ChSetItem>();\r
+               for ( int i=top; i<top+length; i++) {\r
+                       tmp.add(sRowData.get(i));\r
+               }\r
+               for ( int i=0; i<tmp.size(); i++) {\r
+                       dRowData.add(tmp.get(i));\r
+                       sRowData.remove(tmp.get(i));\r
+               }\r
+               \r
+               ((DefaultTableModel) sTable.getModel()).fireTableDataChanged();\r
+               ((DefaultTableModel) dTable.getModel()).fireTableDataChanged();\r
+       }\r
+       \r
+       // DISに戻したものをもとの行にかえしてあげる\r
+       private void refreshChDisOrder(ChSetTable table)\r
+       {\r
+               RowItemList<ChSetItem> rowdata = table.getRowData();\r
+               RowItemList<ChSetItem> tmp = new RowItemList<ChSetItem>();\r
+               \r
+               for ( Center cr : editingNow.getCRlist() ) {\r
+                       for ( ChSetItem c : rowdata ) {\r
+                               if ( cr.getCenter().equals(c.centername) && cr.getAreaCode().equals(editingNow.getCode(c.area))) {\r
+                                       tmp.add(c);\r
+                                       break;\r
+                               }\r
+                       }\r
+               }\r
+               \r
+               rowdata.clear();\r
+               for ( ChSetItem c : tmp ) {\r
+                       rowdata.add(c);\r
+               }\r
+               \r
+               ((DefaultTableModel) table.getModel()).fireTableDataChanged();\r
+       }\r
+       \r
+       // 上下\r
+       private void chUp(ChSetTable table)\r
+       {\r
+               int top = table.getSelectedRow();\r
+               int length = table.getSelectedRowCount();\r
+\r
+               RowItemList<ChSetItem> rowdata = table.getRowData();\r
+               \r
+               if ( top <= 0 ) {\r
+                       return;\r
+               }\r
+               \r
+               rowdata.up(top, length);\r
+               \r
+               ((DefaultTableModel) table.getModel()).fireTableDataChanged();\r
+               table.setRowSelectionInterval(top-1, top-1+(length-1));\r
+       }\r
+       private void chDown(ChSetTable table)\r
+       {\r
+               int top = table.getSelectedRow();\r
+               int length = table.getSelectedRowCount();\r
+               \r
+               RowItemList<ChSetItem> rowdata = table.getRowData();\r
+               \r
+               if ( (top+length) >= rowdata.size() ) {\r
+                       return;\r
+               }\r
+\r
+               rowdata.down(top, length);\r
+               \r
+               ((DefaultTableModel) table.getModel()).fireTableDataChanged();\r
+               table.setRowSelectionInterval(top+1, top+1+(length-1));\r
+       }\r
+       \r
+       \r
+       /*******************************************************************************\r
+        * リスナー\r
+        ******************************************************************************/\r
+       \r
+       /**\r
+        * Web番組表コンボボックスを操作することで起動されるリスナー\r
+        */\r
+       private final ItemListener il_progChanged = new ItemListener() {\r
+               @Override\r
+               public void itemStateChanged(ItemEvent e) {\r
+                       if (e.getStateChange() != ItemEvent.SELECTED) {\r
+                               return;\r
+                       }\r
+                       getEditingPluginForEdit();\r
+               }\r
+       };\r
+       \r
+       /**\r
+        *  放送エリア\r
+        */\r
+       private final ItemListener il_areaChanged = new ItemListener() {\r
+               @Override\r
+               public void itemStateChanged(ItemEvent e) {\r
+                       if (e.getStateChange() != ItemEvent.SELECTED) {\r
+                               return;\r
+                       }\r
+                       setAreaForEdit();\r
+               }\r
+       };\r
+       \r
+       /**\r
+        * 放送局一覧再取得\r
+        */\r
+       private final MouseAdapter centerlistReloadingListener = new MouseAdapter() {\r
+               @Override\r
+               public void mouseClicked(MouseEvent e) {\r
+                       getCenterListFromWeb();\r
+               }\r
+       };\r
+       \r
+       /**\r
+        * 放送局ごとの色\r
+        */\r
+       private final MouseAdapter colorSelectionAdapter = new MouseAdapter() {\r
+               @Override\r
+               public void mouseClicked(MouseEvent e) {\r
+                       if (SwingUtilities.isLeftMouseButton(e)) {\r
+                               //\r
+                               JTable t = (JTable) e.getSource();\r
+                               Point p = e.getPoint();\r
+                               \r
+                               int col = t.columnAtPoint(p);\r
+                               if (col != 2) {\r
+                                       return;\r
+                               }\r
+                               \r
+                               int row = t.rowAtPoint(p);\r
+                               //\r
+                               ccwin.setColor(CommonUtils.str2color((String) t.getValueAt(row,2)));\r
+                               ccwin.setVisible(true);\r
+                               \r
+                               if (ccwin.getSelectedColor() != null ) {\r
+                                       t.setValueAt(CommonSwingUtils.getColoredString(ccwin.getSelectedColor(),"-"), row, 2);\r
+                               }\r
+                       }\r
+               }\r
+       };\r
+       \r
+       /**\r
+        * オプションが編集されたっぽい\r
+        */\r
+       private DocumentListener optionEditedListener = new DocumentListener() {\r
+               @Override\r
+               public void removeUpdate(DocumentEvent e) {\r
+                       setOptionButtonEnabled(true);\r
+               }\r
+               @Override\r
+               public void insertUpdate(DocumentEvent e) {\r
+                       setOptionButtonEnabled(true);\r
+               }\r
+               @Override\r
+               public void changedUpdate(DocumentEvent e) {\r
+               }\r
+       };\r
+       \r
+       private void setOptionButtonEnabled(boolean b) {\r
+               if ( b ) {\r
+                       jButton_opt.setEnabled(true);\r
+                       jButton_opt.setForeground(Color.RED);\r
+               }\r
+               else {\r
+                       jButton_opt.setEnabled(false);\r
+                       jButton_opt.setForeground(Color.DARK_GRAY);\r
+               }\r
+       }\r
+       \r
+       \r
+       /**\r
+        * オプション入力エリア\r
+        */\r
+       private void setOptionFieldEnabled(String opt) {\r
+               if ( opt == null ) {\r
+                       jTextField_opt.setEnabled(false);\r
+                       jTextField_opt.setBackground(this.getBackground());\r
+                       jTextField_opt.setText("");\r
+               }\r
+               else {\r
+                       jTextField_opt.setEnabled(true);\r
+                       jTextField_opt.setBackground(Color.WHITE);\r
+                       jTextField_opt.setText(opt);\r
+               }\r
+       }\r
+       \r
+       /*\r
+        * 部品\r
+        */\r
+       \r
+       /**\r
+        * Web番組表プラグインの切り替えコンボボックス\r
+        */\r
+       private JComboBox getJComboBox_progplugin() {\r
+               if (jComboBox_progplugin == null) {\r
+                       \r
+                       jComboBox_progplugin = new JComboBox();\r
+                       \r
+                       // 選択値の初期化(ここはやめるべき?)\r
+                       for ( TVProgram p : progPlugins ) {\r
+                               jComboBox_progplugin.addItem(p.getTVProgramId());\r
+                       }\r
+                       \r
+                       // Web番組表コンボボックスにリスナーを追加\r
+                       jComboBox_progplugin.addItemListener(il_progChanged);\r
+               }\r
+               return jComboBox_progplugin;\r
+       }\r
+       \r
+       /**\r
+        * エリアの切り替えコンボボックス\r
+        */\r
+       private JComboBox getJComboBox_area() {\r
+               if (jComboBox_area == null) {\r
+                       \r
+                       jComboBox_area = new JComboBox();\r
+                       \r
+                       // エリアコンボボックスにリスナーを追加\r
+                       jComboBox_area.addItemListener(il_areaChanged);\r
+               }\r
+               return jComboBox_area;\r
+       }\r
+       \r
+       // 放送局リスト(有効)\r
+       private JScrollPane getJScrollPane_enable() {\r
+               if (jScrollPane_enable == null) {\r
+                       jScrollPane_enable = new JScrollPane();\r
+                       jScrollPane_enable.setViewportView(jTable_enable = getJTable_tvcenter(true,TABLE_NAME_WIDTH,TABLE_AREA_WIDTH,TABLE_COLOR_WIDTH));\r
+                       jScrollPane_enable.setVerticalScrollBarPolicy(ScrollPaneConstants.VERTICAL_SCROLLBAR_ALWAYS);\r
+               }\r
+               return(jScrollPane_enable);\r
+       }\r
+       \r
+       // 放送局リスト(無効)\r
+       private JScrollPane getJScrollPane_disable() {\r
+               if (jScrollPane_disable == null) {\r
+                       jScrollPane_disable = new JScrollPane();\r
+                       jScrollPane_disable.setViewportView(jTable_disable = getJTable_tvcenter(false,TABLE_NAME_WIDTH,TABLE_AREA_WIDTH,TABLE_COLOR_WIDTH));\r
+                       jScrollPane_disable.setVerticalScrollBarPolicy(ScrollPaneConstants.VERTICAL_SCROLLBAR_ALWAYS);\r
+               }\r
+               return(jScrollPane_disable);\r
+       }\r
+       \r
+       private ChSetTable getJTable_tvcenter(boolean isEnabled, int col1_w, int col2_w, int col3_w) {\r
+               \r
+               ArrayList<String> cola = new ArrayList<String>();\r
+               ArrayList<Integer> wida = new ArrayList<Integer>();\r
+               for ( ChSetColumn cs : ChSetColumn.values() ) {\r
+                       if ( cs == ChSetColumn.CENTER ) {\r
+                               cola.add((isEnabled)?("有効なWeb番組表の放送局名"):("無効"));\r
+                       }\r
+                       else {\r
+                               cola.add(cs.getName());\r
+                       }\r
+                       wida.add(cs.getIniWidth());\r
+               }\r
+               String[] colname = cola.toArray(new String[0]);\r
+               Integer[] colwidth = wida.toArray(new Integer[0]);\r
+               \r
+               ChSetTable jTable_tvcenter = getJTableWithFormat(colname, colwidth);\r
+               \r
+               // 重要!\r
+               jTable_tvcenter.setRowData(new RowItemList<ChSetItem>());\r
+               \r
+               jTable_tvcenter.getTableHeader().setReorderingAllowed(false);\r
+               jTable_tvcenter.setSelectionMode(ListSelectionModel.SINGLE_INTERVAL_SELECTION);\r
+               \r
+               // 色選択欄のみレンダラが独自\r
+               VWColorCellRenderer renderer = new VWColorCellRenderer();\r
+               jTable_tvcenter.getColumn(ChSetColumn.COLOR.getName()).setCellRenderer(renderer);\r
+               \r
+               // 有効リストのみ色選択リスナーが付く\r
+               if ( isEnabled ) {\r
+                       jTable_tvcenter.addMouseListener(colorSelectionAdapter);\r
+               }\r
+               \r
+               return(jTable_tvcenter);\r
+       }\r
+       \r
+       private ChSetTable getJTableWithFormat(String[] s, Integer[] w) {\r
+               //\r
+               DefaultTableModel model = new DefaultTableModel(s,0);\r
+               ChSetTable jTable = new ChSetTable(model,true);\r
+               // 各カラムの幅を設定する\r
+               DefaultTableColumnModel columnModel = (DefaultTableColumnModel)jTable.getColumnModel();\r
+               TableColumn column = null;\r
+               for (int i = 0 ; i < columnModel.getColumnCount() ; i++){\r
+                       column = columnModel.getColumn(i);\r
+                       column.setPreferredWidth(w[i]);\r
+               }\r
+               return(jTable);\r
+       }\r
+       \r
+       // 左右ボタン\r
+       private JButton getJButton_d2e(String s) {\r
+               if (jButton_d2e == null) {\r
+                       jButton_d2e = new JButton(s);\r
+                       jButton_d2e.addMouseListener(new MouseAdapter() {\r
+                               public void mouseClicked(MouseEvent e) {\r
+                                       moveChSrcToDst(jTable_disable,jTable_enable);\r
+                               }\r
+                       });\r
+               }\r
+               return(jButton_d2e);\r
+       }\r
+       private JButton getJButton_e2d(String s) {\r
+               if (jButton_e2d == null) {\r
+                       jButton_e2d = new JButton(s);\r
+                       jButton_e2d.addMouseListener(new MouseAdapter() {\r
+                               public void mouseClicked(MouseEvent e) {\r
+                                       moveChSrcToDst(jTable_enable,jTable_disable);\r
+                                       refreshChDisOrder(jTable_disable);\r
+                               }\r
+                       });\r
+               }\r
+               return(jButton_e2d);\r
+       }\r
+       \r
+       // 上下ボタン\r
+       private JButton getJButton_up(String s) {\r
+               if (jButton_up == null) {\r
+                       jButton_up = new JButton(s);\r
+                       jButton_up.addMouseListener(new MouseAdapter() {\r
+                               @Override\r
+                               public void mouseClicked(MouseEvent e) {\r
+                                       chUp(jTable_enable);\r
+                               }\r
+                       });\r
+               }\r
+               return(jButton_up);\r
+       }\r
+       private JButton getJButton_down(String s) {\r
+               if (jButton_down == null) {\r
+                       jButton_down = new JButton(s);\r
+                       jButton_down.addMouseListener(new MouseAdapter() {\r
+                               @Override\r
+                               public void mouseClicked(MouseEvent e) {\r
+                                       chDown(jTable_enable);\r
+                               }\r
+                       });\r
+               }\r
+               return(jButton_down);\r
+       }\r
+       \r
+       // 放送局リストを再構築する\r
+       private JButton getJButton_forceload(String s) {\r
+               if (jButton_forceload == null) {\r
+                       jButton_forceload = new JButton(s);\r
+                       jButton_forceload.addMouseListener(centerlistReloadingListener);\r
+               }\r
+               return(jButton_forceload);\r
+       }\r
+       \r
+       /**\r
+        *  フリーオプションの保存ボタン\r
+        */\r
+       @SuppressWarnings("serial")\r
+       private JButton getJButton_opt(String s) {\r
+               if (jButton_opt == null) {\r
+                       jButton_opt = new JButton(s) {\r
+                               @Override\r
+                               public void setEnabled(boolean b) {\r
+                                       // ダミー中のため常にfalse\r
+                                       super.setEnabled(false);\r
+                               }\r
+                       };\r
+                       setOptionButtonEnabled(false);\r
+               }\r
+               return(jButton_opt);\r
+       }\r
+       \r
+       /**\r
+        * フリーオプション領域\r
+        */\r
+       private JTextFieldWithPopup getJTextArea_opt() {\r
+               if (jTextField_opt == null) {\r
+                       jTextField_opt = new JTextFieldWithPopup();\r
+                       jTextField_opt.setBorder(new LineBorder(Color.BLACK));\r
+                       jTextField_opt.getDocument().addDocumentListener(optionEditedListener);\r
+                       \r
+               }\r
+               return jTextField_opt;\r
+       }\r
+       \r
+       /*******************************************************************************\r
+        * 独自部品\r
+        ******************************************************************************/\r
+\r
+       // テーブルの行データの構造\r
+       private class ChSetItem extends RowItem implements Cloneable {\r
+               \r
+               // 表示メンバ\r
+               String centername;\r
+               String area;\r
+               String color;\r
+               \r
+               // 非表示メンバ\r
+               \r
+               @Override\r
+               protected void myrefresh(RowItem o) {\r
+                       ChSetItem c = (ChSetItem) o;\r
+                       c.addData(centername);\r
+                       c.addData(area);\r
+                       c.addData(color);\r
+               }\r
+               \r
+               public ChSetItem clone() {\r
+                       return (ChSetItem) super.clone();\r
+               }\r
+               \r
+               public void set(int pos, String value) {\r
+                       if ( pos == ChSetColumn.CENTER.getColumn() )\r
+                       {\r
+                               centername = value;\r
+                       }\r
+                       else if ( pos == ChSetColumn.AREA.getColumn() )\r
+                       {\r
+                               area = value;\r
+                       }\r
+                       else if ( pos == ChSetColumn.COLOR.getColumn() )\r
+                       {\r
+                               color = value;\r
+                       }\r
+               }\r
+               \r
+       }\r
+\r
+       // ChSetItemを使ったJTable拡張\r
+       private class ChSetTable extends JNETable {\r
+\r
+               private static final long serialVersionUID = 1L;\r
+               \r
+               private RowItemList<ChSetItem> rowdata = null;\r
+\r
+               public void setRowData(RowItemList<ChSetItem> rowdata) { this.rowdata = rowdata; }\r
+               public RowItemList<ChSetItem> getRowData() { return rowdata; }\r
+               \r
+               public ChSetTable(boolean b) {\r
+                       super(b);\r
+               }\r
+               \r
+               public ChSetTable(TableModel d, boolean b) {\r
+                       super(d, b);\r
+               }\r
+               \r
+               @Override\r
+               public Object getValueAt(int row, int column) {\r
+                       \r
+                       int vrow = this.convertRowIndexToModel(row);\r
+                       ChSetItem c = rowdata.get(vrow);\r
+                       return c.get(column);\r
+                       \r
+               }\r
+               \r
+               @Override\r
+               public void setValueAt(Object aValue, int row, int column) {\r
+                       \r
+                       int vrow = this.convertRowIndexToModel(row);\r
+                       ChSetItem c = rowdata.get(vrow);\r
+                       c.set(column,(String) aValue);\r
+                       c.fireChanged();\r
+                       \r
+               }\r
+               \r
+               @Override\r
+               public int getRowCount() {\r
+                       \r
+                       return rowdata.size();\r
+                       \r
+               }\r
+               \r
+       }\r
+\r
+}\r
diff --git a/TinyBannavi/src/tainavi/ChannelSort.java b/TinyBannavi/src/tainavi/ChannelSort.java
new file mode 100644 (file)
index 0000000..f63e840
--- /dev/null
@@ -0,0 +1,75 @@
+package tainavi;\r
+\r
+import java.io.File;\r
+import java.util.ArrayList;\r
+\r
+public class ChannelSort {\r
+\r
+       /*\r
+        * 定数\r
+        */\r
+       \r
+       private final String filename = "env"+File.separator+"chsort.xml";\r
+       \r
+       private final String MSGID = "[CHソート設定] ";\r
+       private final String ERRID = "[ERROR]"+MSGID;\r
+       \r
+       /*\r
+        * 部品\r
+        */\r
+       private ArrayList<Center> clst = new ArrayList<Center>();\r
+       \r
+       //public boolean isEnabled() { return (clst != null); }\r
+       public int size() { return clst.size(); }\r
+       \r
+       public boolean add(Center cr) { return clst.add(cr); }\r
+       \r
+       /**\r
+        * ソート済みの放送局一覧を返す\r
+        */\r
+       public ArrayList<Center> getClst() {\r
+               return clst;\r
+       }\r
+       \r
+       public void clear() {\r
+               clst.clear();\r
+       }\r
+       \r
+       public boolean load() {\r
+               \r
+               //System.out.println(MSGID+"設定を読み込みます: "+filename);\r
+               \r
+               if ( ! new File(filename).exists() ) {\r
+                       System.err.println(ERRID+"設定が読み込めませんでした、CHソートは無効です: "+filename);\r
+                       return false;\r
+               }\r
+               \r
+               @SuppressWarnings("unchecked")\r
+               ArrayList<Center> tClst = (ArrayList<Center>) CommonUtils.readXML(filename);\r
+               if ( tClst == null ) {\r
+                       System.err.println(ERRID+"設定の読み込みに失敗しました、CHソートは無効です: "+filename);\r
+                       return false; \r
+               }\r
+               \r
+               System.out.println(MSGID+"設定を読み込みました: "+filename);\r
+               \r
+               clst = tClst;\r
+               return true;\r
+       }\r
+\r
+       public boolean save() {\r
+               if ( ! CommonUtils.writeXML(filename, clst) ) {\r
+                       System.err.println(ERRID+"設定の保存に失敗しました: "+filename);\r
+                       return false;\r
+               }\r
+               return true;\r
+       }\r
+       \r
+       /*\r
+       public boolean delete() {\r
+               clst = new ArrayList<Center>();\r
+               return new File(filename).delete();\r
+       }\r
+       */\r
+       \r
+}\r
diff --git a/TinyBannavi/src/tainavi/ChippedBorder.java b/TinyBannavi/src/tainavi/ChippedBorder.java
new file mode 100644 (file)
index 0000000..f99ad23
--- /dev/null
@@ -0,0 +1,25 @@
+package tainavi;\r
+\r
+import java.awt.Color;\r
+import java.awt.Component;\r
+import java.awt.Graphics;\r
+\r
+import javax.swing.border.LineBorder;\r
+\r
+public class ChippedBorder extends LineBorder {\r
+       \r
+       private static final long serialVersionUID = 1L;\r
+\r
+       public ChippedBorder(Color color, int thickness) {\r
+               super(color, thickness);\r
+       }\r
+\r
+       @Override\r
+       public void paintBorder(Component c, Graphics g, int x, int y, int width, int height) {\r
+               g.fillRect(0, 0, width, thickness);                             // 上\r
+               //g.fillRect(0, y-thickness-1, width, thickness);       // 下\r
+               g.fillRect(0, 0, thickness, height);                    // 左\r
+               //g.fillRect(x-thickness-1, 0, thickness, height);      // 右\r
+       }\r
+\r
+}\r
diff --git a/TinyBannavi/src/tainavi/ClipboardInfo.java b/TinyBannavi/src/tainavi/ClipboardInfo.java
new file mode 100644 (file)
index 0000000..eeef01b
--- /dev/null
@@ -0,0 +1,23 @@
+package tainavi;\r
+\r
+/**\r
+ * <P>クリップボードにコピーする項目名を保持するクラスです。{@link clipboardItem} から移行しました。\r
+ * @since 3.15.4β\r
+ * @see ClipboardInfoList\r
+ */\r
+public class ClipboardInfo {\r
+\r
+       private boolean b = false ;     // 有効か無効か\r
+       private String item = "" ;      // 項目名\r
+       private int id = 0 ;            // 順番\r
+       \r
+       public void setB(boolean b) { this.b = b; }\r
+       public boolean getB() { return this.b; }\r
+       \r
+       public void setItem(String item) { this.item = item; }\r
+       public String getItem() { return this.item; }\r
+       \r
+       public void setId(int id) { this.id = id; }\r
+       public int getId() { return this.id; }\r
+\r
+}\r
diff --git a/TinyBannavi/src/tainavi/ClipboardInfoList.java b/TinyBannavi/src/tainavi/ClipboardInfoList.java
new file mode 100644 (file)
index 0000000..e5ffa5d
--- /dev/null
@@ -0,0 +1,92 @@
+package tainavi;\r
+\r
+import java.io.File;\r
+import java.util.ArrayList;\r
+\r
+/**\r
+ * {@link ClipboardInfo} のリストを実現するクラスです. \r
+ * @since 3.15.4β\r
+ */\r
+public class ClipboardInfoList extends ArrayList<ClipboardInfo> {\r
+\r
+       private static final long serialVersionUID = 1L;\r
+       \r
+       private static final String cbFileOld = "env"+File.separator+"cbitems.xml";\r
+       private static final String cbFile = "env"+File.separator+"cbinfolist.xml";\r
+       \r
+       public boolean save() {\r
+               System.out.println("クリップボード設定を保存します: "+cbFile);\r
+               if ( ! CommonUtils.writeXML(cbFile, this) ) {\r
+               System.err.println("クリップボード設定の保存に失敗しました: "+cbFile);\r
+               return false;\r
+               }\r
+               \r
+               return true;\r
+       }\r
+       \r
+       @SuppressWarnings("deprecation")\r
+       public boolean load() {\r
+               \r
+               System.out.println("クリップボード設定を読み込みます: "+cbFile);\r
+\r
+               boolean isoldclass = false;\r
+               ArrayList<ClipboardInfo> cl = null;\r
+               \r
+               if ( ! new File(cbFile).exists() ) {\r
+                       // ファイルがなければデフォルトで\r
+                       if ( new File(cbFileOld).exists() ) {\r
+                               // 旧clipboardItem対策\r
+                               isoldclass = true;\r
+                               cl = new ArrayList<ClipboardInfo>();\r
+                               ArrayList<clipboardItem> clx = (ArrayList<clipboardItem>) CommonUtils.readXML(cbFileOld);\r
+                               for ( clipboardItem cx : clx ) {\r
+                                       ClipboardInfo c = new ClipboardInfo();\r
+                                       CommonUtils.FieldCopy(c, cx);\r
+                                       cl.add(c);\r
+                               }\r
+                       }\r
+               }\r
+               else {\r
+                       // ファイルがあるならロード\r
+                       cl = (ClipboardInfoList) CommonUtils.readXML(cbFile);\r
+               }\r
+               if ( cl == null || cl.size() == 0 ) {\r
+                       System.err.println("クリップボード設定が読み込めなかったのでデフォルト設定で起動します.");\r
+                       \r
+               // 初期化してみよう\r
+               this.clear();\r
+               int idx = 1;\r
+               Object[][] o = {\r
+                               {true,  "番組名",    idx++},\r
+                               {true,  "放送局",    idx++},\r
+                               {true,  "開始日",    idx++},\r
+                               {true,  "開始時刻", idx++},\r
+                               {false, "終了時刻", idx++},\r
+                               {false, "ジャンル", idx++},\r
+                               {true,  "番組詳細", idx++},\r
+               };\r
+               for (int i=0; i<o.length; i++) {\r
+                       ClipboardInfo cb = new ClipboardInfo();\r
+                       cb.setB((Boolean) o[i][0]);\r
+                       cb.setItem((String) o[i][1]);\r
+                       cb.setId((Integer) o[i][2]);\r
+                       this.add(cb);\r
+               }\r
+               \r
+                       return false;\r
+               }\r
+               \r
+               this.clear();\r
+               for (ClipboardInfo c : cl) {\r
+                       this.add(c);\r
+               }\r
+               \r
+               if ( isoldclass && this.save() ) {\r
+                       System.err.println("クリップボード設定ファイルを置き換えます: "+cbFileOld+"->"+cbFile);\r
+                       new File(cbFileOld).delete();\r
+               }\r
+               \r
+               return true;\r
+       }\r
+\r
+}\r
diff --git a/TinyBannavi/src/tainavi/CommonSwingUtils.java b/TinyBannavi/src/tainavi/CommonSwingUtils.java
new file mode 100644 (file)
index 0000000..a44e055
--- /dev/null
@@ -0,0 +1,243 @@
+package tainavi;\r
+\r
+import java.awt.Color;\r
+import java.awt.Component;\r
+import java.awt.Dimension;\r
+import java.awt.Font;\r
+import java.awt.Graphics2D;\r
+import java.awt.Insets;\r
+import java.awt.Point;\r
+import java.awt.Rectangle;\r
+import java.awt.image.BufferedImage;\r
+import java.beans.PropertyChangeEvent;\r
+import java.beans.PropertyChangeListener;\r
+import java.io.File;\r
+import java.io.IOException;\r
+import java.lang.reflect.Field;\r
+import java.lang.reflect.Modifier;\r
+import java.util.Locale;\r
+\r
+import javax.imageio.IIOImage;\r
+import javax.imageio.ImageIO;\r
+import javax.imageio.ImageWriteParam;\r
+import javax.imageio.ImageWriter;\r
+import javax.imageio.plugins.jpeg.JPEGImageWriteParam;\r
+import javax.imageio.stream.ImageOutputStream;\r
+import javax.swing.JComponent;\r
+import javax.swing.JPanel;\r
+import javax.swing.SpringLayout;\r
+\r
+import tainavi.Env.SnapshotFmt;\r
+\r
+/**\r
+ * Swingを使う上で利用できる共通のロジックをstaticメソッドとしてまとめたもの\r
+ */\r
+public class CommonSwingUtils {\r
+\r
+       public void setDebug(boolean b) { debug = b; }\r
+       private static boolean debug = false;\r
+       \r
+       private static final String MSGID = "[Swing共通] ";\r
+       private static final String ERRID = "[ERROR]"+MSGID;\r
+       private static final String DBGID = "[DEBUG]"+MSGID;\r
+       \r
+       /**\r
+        *  パネルに部品を貼り付ける\r
+        */\r
+       public static void putComponentOn(JPanel p, JComponent c, int width, int height, int x, int y) {\r
+               c.setPreferredSize(new Dimension(width, height));\r
+               ((SpringLayout)p.getLayout()).putConstraint(SpringLayout.NORTH, c, y, SpringLayout.NORTH, p);\r
+               ((SpringLayout)p.getLayout()).putConstraint(SpringLayout.WEST, c, x, SpringLayout.WEST, p);\r
+               p.add(c);\r
+       }\r
+       \r
+       /**\r
+        *  親コンポーネントの中心に移動する\r
+        */\r
+       public static void setLocationCenter(Component parent, Component c) {\r
+               Point p2 = parent.getLocationOnScreen();\r
+               Rectangle r2 = parent.getBounds();\r
+               Rectangle r = c.getBounds();\r
+               int x = p2.x+(r2.width-r.width)/2;\r
+               int y = p2.y+(r2.height-r.height)/2;\r
+               c.setLocation(x, y);\r
+       }\r
+       \r
+       /**\r
+        *  親コンポーネントの下に移動する\r
+        */\r
+       public static void setLocationUnder(Component parent, Component c) {\r
+               Point p2 = parent.getLocationOnScreen();\r
+               Rectangle r2 = parent.getBounds();\r
+               Rectangle r = c.getBounds();\r
+               int x = p2.x+(r2.width-r.width)/2;\r
+               int y = p2.y+r2.height-10;\r
+               c.setLocation(x, y);\r
+       }\r
+       \r
+       /**\r
+        * 謎のJTextArea\r
+        */\r
+       public static JTextAreaWithPopup getJta(final Component parent, int rows, int columns) {\r
+               JTextAreaWithPopup jta = new JTextAreaWithPopup(rows,columns);\r
+               jta.setLineWrap(true);\r
+               jta.setEditable(false);\r
+               jta.setBackground(parent.getBackground());\r
+               //jta.setBorder(new LineBorder(Color.BLACK));\r
+               jta.setMargin(new Insets(0,0,0,0));\r
+               jta.setAlignmentX(Component.CENTER_ALIGNMENT);\r
+               \r
+               jta.addPropertyChangeListener(new PropertyChangeListener() {\r
+                       @Override\r
+                       public void propertyChange(PropertyChangeEvent e) {\r
+                               if ( "background".equals(e.getPropertyName()) ) {\r
+                                       ((JTextAreaWithPopup) e.getSource()).setBackground(parent.getBackground());\r
+                               }\r
+                       }\r
+               });\r
+               \r
+               return jta;\r
+       }\r
+       \r
+       /**\r
+        * 色セルの書式\r
+        */\r
+       public static String getColoredString(String color, String text) {\r
+               return text+"\0"+color;\r
+       }\r
+       public static String getColoredString(Color color, String text) {\r
+               return text+"\0"+CommonUtils.color2str(color);\r
+       }\r
+       /**\r
+        * @return [0] テキスト [1] 色\r
+        */\r
+       public static String[] splitColorString(String value) {\r
+               return value.split("\0");\r
+       }\r
+       \r
+       /**\r
+        * <P>こどもをおっきする\r
+        * <P><B>こどもは"jCombobox_update"のように先頭が"j"ではじまること!</B>\r
+        */\r
+       @SuppressWarnings("rawtypes")\r
+       public static void setEnabledAllComponents(Component own, Class c, boolean b) {\r
+               Field[] fd = c.getDeclaredFields();\r
+               for ( Field fx : fd ) {\r
+                       fx.setAccessible(true);\r
+                       if ( Modifier.isFinal(fx.getModifiers()) ) {\r
+                               continue;\r
+                       }\r
+                       try {\r
+                               Object o = fx.get(own);\r
+                               if ( o == null || ! (o instanceof Component) || ! fx.getName().startsWith("j") ) {\r
+                                       continue;\r
+                               }\r
+                               if (debug) System.out.println(DBGID+"enabled class="+c.getName()+" name="+fx.getName());\r
+                               ((Component)o).setEnabled(true);\r
+                       }\r
+                       catch (Exception e) {\r
+                               e.printStackTrace();\r
+                       } \r
+               }\r
+       }\r
+       \r
+       /**\r
+        * リスト形式と新聞形式のスナップショットを作成する\r
+        */\r
+       public static void saveComponentAsJPEG(String label, Component header, Component sidebar, Component body, String filename, SnapshotFmt fmt, Component parent) {\r
+               \r
+               BufferedImage myImage = null;\r
+               {\r
+                       if ( header != null && sidebar != null && body != null ) {\r
+                               Dimension hSize = header.getPreferredSize();\r
+                               Dimension sSize = sidebar.getPreferredSize();\r
+                               Dimension bSize = body.getPreferredSize();\r
+                               \r
+                               BufferedImage hdrImage = new BufferedImage(hSize.width, hSize.height, BufferedImage.TYPE_INT_RGB);\r
+                               Graphics2D gh = hdrImage.createGraphics();\r
+                               header.paint(gh);\r
+                               \r
+                               BufferedImage sideImage = new BufferedImage(sSize.width, sSize.height, BufferedImage.TYPE_INT_RGB);\r
+                               Graphics2D gs = sideImage.createGraphics();\r
+                               sidebar.paint(gs);\r
+                               \r
+                               Font f = parent.getFont();\r
+                               int fSize = f.getSize();\r
+                               \r
+                               myImage = new BufferedImage(sSize.width*2+bSize.width, fSize*2+hSize.height*2+bSize.height, BufferedImage.TYPE_INT_RGB);\r
+                               Graphics2D g2 = myImage.createGraphics();\r
+                               g2.setColor(new Color(0,0,0));\r
+                               g2.setBackground(new Color(255,255,255));\r
+                               \r
+                               g2.clearRect(0, 0, sSize.width*2+bSize.width, fSize*2+hSize.height*2+bSize.height);\r
+                               g2.drawString(label, 0, fSize);\r
+                               \r
+                               g2.translate(0, fSize*2);\r
+                               g2.drawImage(hdrImage, sSize.width, 0, parent);\r
+                               \r
+                               g2.translate(0, hSize.height);\r
+                               g2.drawImage(sideImage, 0, 0, parent);\r
+                               \r
+                               g2.translate(sSize.width, 0);\r
+                               body.paint(g2);\r
+       \r
+                               g2.translate(bSize.width, 0);\r
+                               g2.drawImage(sideImage, 0, 0, parent);\r
+       \r
+                               g2.translate(-bSize.width, bSize.height);\r
+                               g2.drawImage(hdrImage, 0, 0, parent);\r
+                       }\r
+                       else if ( header != null  && body != null ) {\r
+                               Dimension hSize = header.getPreferredSize();\r
+                               Dimension bSize = body.getPreferredSize();\r
+                               \r
+                               Font f = parent.getFont();\r
+                               int fSize = f.getSize();\r
+                               \r
+                               BufferedImage hdrImage = new BufferedImage(hSize.width, hSize.height, BufferedImage.TYPE_INT_RGB);\r
+                               Graphics2D gh = hdrImage.createGraphics();\r
+                               header.paint(gh);\r
+                               \r
+                               myImage = new BufferedImage(bSize.width, fSize*2+hSize.height+bSize.height, BufferedImage.TYPE_INT_RGB);\r
+                               Graphics2D g2 = myImage.createGraphics();\r
+                               g2.setColor(new Color(0,0,0));\r
+                               g2.setBackground(new Color(255,255,255));\r
+                               \r
+                               g2.clearRect(0, 0, bSize.width, fSize*2+hSize.height+bSize.height);\r
+                               g2.drawString(label, 0, fSize);\r
+                               \r
+                               g2.translate(0, fSize*2);\r
+                               g2.drawImage(hdrImage, 0, 0, parent);\r
+                               \r
+                               g2.translate(0, hSize.height);\r
+                               body.paint(g2);\r
+                       }\r
+               }\r
+               \r
+               try {\r
+                       switch (fmt) {\r
+                       case JPG:\r
+                               JPEGImageWriteParam param = new JPEGImageWriteParam(Locale.getDefault());\r
+                               param.setCompressionMode(ImageWriteParam.MODE_EXPLICIT);\r
+                               param.setCompressionQuality(0.95f);\r
+                               ImageWriter iw = ImageIO.getImageWritersByFormatName("jpg").next();\r
+                               ImageOutputStream ios = ImageIO.createImageOutputStream(new File(filename));\r
+                               iw.setOutput(ios);\r
+                               iw.write(null, new IIOImage(myImage, null, null), param);\r
+                               ios.close();\r
+                               iw.dispose();\r
+                               //ImageIO.write(myImage, "jpg", new File(filename));    // 圧縮率を変更しない場合\r
+                               break;\r
+                       case BMP:\r
+                               ImageIO.write(myImage, "bmp", new File(filename));\r
+                               break;\r
+                       case PNG:\r
+                               ImageIO.write(myImage, "png", new File(filename));\r
+                               break;\r
+                       }\r
+                       myImage = null;\r
+               } catch (IOException e) {\r
+                       e.printStackTrace();\r
+               }\r
+       }\r
+}\r
diff --git a/TinyBannavi/src/tainavi/CommonUtils.java b/TinyBannavi/src/tainavi/CommonUtils.java
new file mode 100644 (file)
index 0000000..80a7262
--- /dev/null
@@ -0,0 +1,1854 @@
+\r
+package tainavi;\r
+\r
+import java.awt.Color;\r
+import java.awt.Component;\r
+import java.awt.Desktop;\r
+import java.beans.XMLDecoder;\r
+import java.beans.XMLEncoder;\r
+import java.io.BufferedInputStream;\r
+import java.io.BufferedOutputStream;\r
+import java.io.BufferedReader;\r
+import java.io.BufferedWriter;\r
+import java.io.File;\r
+import java.io.FileInputStream;\r
+import java.io.FileNotFoundException;\r
+import java.io.FileOutputStream;\r
+import java.io.FileReader;\r
+import java.io.FileWriter;\r
+import java.io.IOException;\r
+import java.io.InputStream;\r
+import java.io.InputStreamReader;\r
+import java.io.OutputStream;\r
+import java.io.OutputStreamWriter;\r
+import java.io.Reader;\r
+import java.io.Writer;\r
+import java.lang.reflect.Array;\r
+import java.lang.reflect.Field;\r
+import java.lang.reflect.InvocationTargetException;\r
+import java.lang.reflect.Method;\r
+import java.lang.reflect.Modifier;\r
+import java.net.HttpURLConnection;\r
+import java.net.Socket;\r
+import java.nio.channels.FileChannel;\r
+import java.nio.channels.FileLock;\r
+import java.text.SimpleDateFormat;\r
+import java.util.AbstractList;\r
+import java.util.AbstractMap;\r
+import java.util.ArrayList;\r
+import java.util.Calendar;\r
+import java.util.ConcurrentModificationException;\r
+import java.util.GregorianCalendar;\r
+import java.util.HashMap;\r
+import java.util.Map.Entry;\r
+import java.util.regex.Matcher;\r
+import java.util.regex.Pattern;\r
+import java.util.zip.ZipEntry;\r
+import java.util.zip.ZipFile;\r
+import java.util.zip.ZipOutputStream;\r
+\r
+/**\r
+ * <P>頻繁に使用する雑多なメソッドをstaticで提供します。\r
+ * <P>パッケージを小分けにしておけばよかった…\r
+ */\r
+public class CommonUtils {\r
+\r
+       /*******************************************************************************\r
+        * CommonUtilsの動作設定\r
+        ******************************************************************************/\r
+\r
+       /**\r
+        * デバッグログ出力するかどうか\r
+        */\r
+       public static void setDebug(boolean b) { debug = b; }\r
+       private static boolean debug = false;\r
+       private static boolean debuglv2 = false;        // これは俺用\r
+       \r
+       /**\r
+        *  深夜の帯予約補正に対応するかどうか\r
+        */\r
+       public static void setAdjLateNight(boolean b) { adjLateNight = b; }\r
+       private static boolean adjLateNight = false;\r
+       \r
+       /**\r
+        * Web番組表の8日分取得に対応するかどうか\r
+        */\r
+       public static void setExpandTo8(boolean b) { dogDays = ((b)?(8):(7)); }\r
+       private static int dogDays = 7;\r
+       \r
+       /**\r
+        * 過去予約の表示に対応するかどうか\r
+        */\r
+       public static void setDisplayPassedReserve(boolean b) { displayPassedReserve = b; }\r
+       private static boolean displayPassedReserve = false;\r
+       \r
+       /**\r
+        * Windows環境下で、アプリケーションに関連付けられたファイルを開くのにrundll32.exeを利用するかどうか\r
+        */\r
+       public static void setUseRundll32(boolean b) { useRundll32 = b; }\r
+       private static boolean useRundll32 = false;\r
+       \r
+       \r
+       /*******************************************************************************\r
+        * 日付時刻関連\r
+        ******************************************************************************/\r
+       \r
+       public static final String[] WDTPTN = {"日","月","火","水","木","金","土"};\r
+       \r
+       /**\r
+        * 曜日→wday\r
+        */\r
+       public static int getWday(String s) {\r
+               if ( s == null || s.length() == 0 ) {\r
+                       return 0;\r
+               }\r
+               \r
+               String x = s.substring(0,1);\r
+               for ( int i=0; i<WDTPTN.length; i++ ) {\r
+                       if ( WDTPTN[i].equals(x) ) {\r
+                               return i+1;\r
+                       }\r
+               }\r
+               return 0;\r
+       }\r
+       \r
+       /**\r
+        * 月日に年を補完\r
+        */\r
+       public static String getDateByMD(int m, int d, int wday, boolean addwstr) {\r
+               return getDate(getCalendarByMD(m,d,wday),addwstr);\r
+       }\r
+       \r
+       /**\r
+        * 月日に年を補完\r
+        */\r
+       public static GregorianCalendar getCalendarByMD(int m, int d, int wday) {\r
+               GregorianCalendar c = new GregorianCalendar(\r
+                               Calendar.getInstance().get(Calendar.YEAR),\r
+                               m-1,\r
+                               d\r
+                               );\r
+               \r
+               if ( c.get(Calendar.DAY_OF_WEEK) == wday ) {\r
+                       // 当年\r
+                       return c;\r
+               }\r
+               \r
+               c.add(Calendar.YEAR, 1);\r
+               if ( c.get(Calendar.DAY_OF_WEEK) == wday ) {\r
+                       // 翌年\r
+                       return c;\r
+               }\r
+               \r
+               return null;\r
+       }\r
+\r
+       \r
+       /**\r
+        * 深夜帯(24:00~28:59)かどうか判定する\r
+        */\r
+       public static boolean isLateNight(GregorianCalendar A) {\r
+               return isLateNight(A.get(Calendar.HOUR_OF_DAY));\r
+       }\r
+       \r
+       /**\r
+        * 深夜帯(24:00~28:59)かどうか判定する\r
+        */\r
+       public static boolean isLateNight(String Ahh) {\r
+               return ("00".compareTo(Ahh) <= 0 && "05".compareTo(Ahh) > 0);\r
+       }\r
+       \r
+       /**\r
+        * 深夜帯(24:00~28:59)かどうか判定する\r
+        */\r
+       public static boolean isLateNight(int Ahh) {\r
+               return (Ahh >= 0 && Ahh < 5);\r
+       }\r
+       \r
+       /**\r
+        *  開始・終了時刻から長さを算出する。引数の前後関係は意識しなくて良い。\r
+        */\r
+       public static String getRecMin(GregorianCalendar ca, GregorianCalendar cz) {\r
+               return String.valueOf(getRecMinVal(ca, cz));\r
+       }\r
+       \r
+       /**\r
+        *  開始・終了時刻から長さを算出する。引数の前後関係は意識しなくて良い。\r
+        */\r
+       public static String getRecMin(String start, String end) {\r
+               return String.valueOf(getRecMinVal(start,end));\r
+       }\r
+\r
+       public static String getRecMin(String ahhStr,String ammStr,String zhhStr,String zmmStr) {\r
+               return String.valueOf(getRecMinVal(ahhStr,ammStr,zhhStr,zmmStr));\r
+       }\r
+\r
+       /**\r
+        *  開始・終了時刻から長さを算出する。引数の前後関係は意識しなくて良い。\r
+        */\r
+       public static long getRecMinVal(GregorianCalendar ca, GregorianCalendar cz)     {\r
+               return getDiffDateTime(ca, cz)/60000L;\r
+       }\r
+       \r
+       /**\r
+        * 開始・終了時刻から長さを算出する。引数の前後関係は意識しなくて良い。\r
+        * @param start hh:mm\r
+        * @param end hh:mm\r
+        */\r
+       public static int getRecMinVal(String start, String end) {\r
+               Matcher ma = Pattern.compile("(\\d\\d):(\\d\\d)").matcher(start);\r
+               Matcher mb = Pattern.compile("(\\d\\d):(\\d\\d)").matcher(end);\r
+               if ( ! ma.find() || ! mb.find()) {\r
+                       return 0;\r
+               }\r
+               return getRecMinVal(ma.group(1),ma.group(2),mb.group(1),mb.group(2));\r
+       }\r
+       \r
+       /**\r
+        *  開始・終了時刻から長さを算出する。引数の前後関係は意識しなくて良い。\r
+        */\r
+       public static int getRecMinVal(String ahhStr,String ammStr,String zhhStr,String zmmStr) {\r
+               try {\r
+                       return getRecMinVal(Integer.valueOf(ahhStr), Integer.valueOf(ammStr), Integer.valueOf(zhhStr), Integer.valueOf(zmmStr));\r
+               }\r
+               catch ( NumberFormatException e ) {\r
+                       System.err.println("[ERROR] at getRecMin(): "+ahhStr+","+ammStr+","+zhhStr+","+zmmStr);\r
+                       e.printStackTrace();\r
+               }\r
+               return 0;\r
+       }\r
+       \r
+       /**\r
+        *  開始・終了時刻から長さを算出する。引数の前後関係は意識しなくて良い。\r
+        */\r
+       private static int getRecMinVal(int ahh, int amm, int zhh, int zmm) {\r
+               int min = (zhh*60+zmm) - (ahh*60+amm);\r
+               if (min < 0) min += 24*60;\r
+               return min;\r
+       }\r
+       \r
+       /**\r
+        * @see #getCritDateTime(int)\r
+        */\r
+       public static String getCritDateTime() {\r
+               return getCritDateTime(0);\r
+       }\r
+       /**\r
+        * <P>ここより前は「前日」分であり過去情報であると判断するための基準日時を生成する。\r
+        * <P>要するに、「当日」の "YYYY/MM/DD 05:00" (「当日」なので、日付は {@link #getDate529} の値)\r
+        * @param n : n日先の基準日時か。当日の場合は0 \r
+        */\r
+       public static String getCritDateTime(int n) {\r
+               return getDate529(n*86400,false)+" 05:00";\r
+       }\r
+       /**\r
+        * \r
+        * @param showpassed true:{@link #getCritDateTime()}と等価、false:{@link #getDateTime(int)}と等価\r
+        * @return\r
+        */\r
+       public static String getCritDateTime(boolean showpassed) {\r
+               if (showpassed) {\r
+                       return getCritDateTime(0);\r
+               }\r
+               else {\r
+                       return getDateTime(0);\r
+               }\r
+       }\r
+       \r
+       /**\r
+        * 次回実行予定日を取得する\r
+        * @see #getStartEndList(ArrayList, ArrayList, ReserveList)\r
+        */\r
+       public static String getNextDate(ReserveList r) {\r
+               \r
+               ArrayList<String> starts = new ArrayList<String>();\r
+               ArrayList<String> ends = new ArrayList<String>();\r
+               CommonUtils.getStartEndList(starts, ends, r);\r
+               if (starts.size() > 0) {\r
+                       GregorianCalendar c = getCalendar(starts.get(0));\r
+                       if (c != null) {\r
+                               return(getDate(c));\r
+                       }\r
+               }\r
+               \r
+               // エラーの場合は1970/01/01を返す\r
+               return(getDate(new GregorianCalendar(1970,0,1)));\r
+       }\r
+       \r
+       /**\r
+        * 開始・終了日時のリストを作成する\r
+        * @see #getNextDate(ReserveList)\r
+        */\r
+       public static void getStartEndList(ArrayList<String> starts, ArrayList<String> ends, ReserveList r) {\r
+               \r
+               int ptnid = r.getRec_pattern_id();\r
+               \r
+               //boolean isOverDay = (r.getAhh().compareTo(r.getZhh()) > 0);           // 日付をまたいだ\r
+               //boolean isLateNight = (adjLateNight == false && ( ptnid >= 7 && ptnid <= 10 ) && isLateNight(r.getAhh()));    // 深夜帯(毎日&帯予約のみ利用)\r
+               \r
+               int len = Integer.valueOf(getRecMin(r.getAhh(), r.getAmm(), r.getZhh(), r.getZmm()));\r
+               \r
+               // 予約パターンによりけり対象となる日付は増えたり増えたり\r
+               if (ptnid == 11) {\r
+                       // 単日\r
+                       GregorianCalendar d = getCalendar(r.getRec_pattern()+" "+r.getAhh()+":"+r.getAmm());\r
+                       if (d != null) {\r
+                               starts.add(getDateTime(d));\r
+                               d.add(Calendar.MINUTE,len);\r
+                               ends.add(getDateTime(d));\r
+                       }\r
+               }\r
+               else {\r
+                       \r
+                       // 基準日時\r
+                       GregorianCalendar cur = getCalendar(0);\r
+                       GregorianCalendar cri = getCalendar(getCritDateTime());\r
+                       \r
+                       // 切り捨て条件の選択\r
+                       GregorianCalendar cond;\r
+                       if ( displayPassedReserve ) {\r
+                               cond = (GregorianCalendar) cri.clone(); // 過去予約表示\r
+                       }\r
+                       else {\r
+                               cond = (GregorianCalendar) cur.clone(); // 現在日時以上\r
+                       }\r
+                       \r
+                       // ループ終了位置\r
+                       GregorianCalendar cz = (GregorianCalendar) cur.clone();\r
+                       cz.add(Calendar.DATE, dogDays);\r
+                       \r
+                       // ループ開始位置\r
+                       GregorianCalendar ca = (GregorianCalendar) cri.clone();\r
+                       if ( isLateNight(r.getAhh()) ) {\r
+                               ca.add(Calendar.DATE, 1);\r
+                       }\r
+                       ca.set(Calendar.HOUR_OF_DAY, Integer.valueOf(r.getAhh()));\r
+                       ca.set(Calendar.MINUTE, Integer.valueOf(r.getAmm()));\r
+                       GregorianCalendar cb = (GregorianCalendar) ca.clone();\r
+                       cb.set(Calendar.HOUR_OF_DAY, Integer.valueOf(r.getZhh()));\r
+                       cb.set(Calendar.MINUTE, Integer.valueOf(r.getZmm()));\r
+                       if ( cb.compareTo(ca) < 0 ) {\r
+                               cb.add(Calendar.DATE, 1);       // 終了日時より開始日時が大きいならば\r
+                       }\r
+                       \r
+                       // 深夜かな?\r
+                       boolean islatenight = isLateNight(r.getAhh());\r
+                       \r
+                       while (ca.compareTo(cz) < 0)\r
+                       {\r
+                               if ( cond.compareTo(cb) > 0 ) {\r
+                                       // 過去情報だにゅ\r
+                                       ca.add(Calendar.DATE, 1);\r
+                                       cb.add(Calendar.DATE, 1);\r
+                                       continue;\r
+                               }\r
+                               \r
+                               boolean isReserved = false;     // 過去の予約=false\r
+\r
+                               if (ptnid == 10) {\r
+                                       // 毎日\r
+                                       isReserved = true;\r
+                               }\r
+                               else if (9 >= ptnid && ptnid >= 7) {\r
+                                       // 帯\r
+                                       int wd = ca.get(Calendar.DAY_OF_WEEK);\r
+                                       if ( islatenight ) {\r
+                                               if ( adjLateNight ) {\r
+                                                       // RDなどの深夜時間帯(月~土)\r
+                                                       if ( Calendar.MONDAY <= wd && wd <= (r.getRec_pattern_id()-2) ) {\r
+                                                               isReserved = true;\r
+                                                       }\r
+                                               }\r
+                                               else {\r
+                                                       // 通常の深夜時間帯(火~日)\r
+                                                       if ( Calendar.TUESDAY <= wd && wd <= (r.getRec_pattern_id()-1) ) {\r
+                                                               isReserved = true;\r
+                                                       }\r
+                                                       else if ( ptnid == 9 && wd == Calendar.SUNDAY ) {\r
+                                                               isReserved = true;\r
+                                                       }\r
+                                               }\r
+                                       }\r
+                                       else {\r
+                                               // 平常時間帯\r
+                                               if ( Calendar.MONDAY <= wd && wd <= (r.getRec_pattern_id()-2) ) {\r
+                                                       isReserved = true;\r
+                                               }\r
+                                       }\r
+                               }\r
+                               else if (ptnid < 7) {\r
+                                       // 週次\r
+                                       if ( ptnid == (ca.get(Calendar.DAY_OF_WEEK)-1) ) {\r
+                                               isReserved = true;\r
+                                       }\r
+                               }\r
+                               \r
+                               if (isReserved) {\r
+                                       GregorianCalendar ct = (GregorianCalendar) ca.clone();\r
+                                       ct.add(Calendar.MINUTE,len);\r
+                                       starts.add(getDateTime(ca));\r
+                                       ends.add(getDateTime(ct));\r
+                               }\r
+                               \r
+                               ca.add(Calendar.DATE, 1);\r
+                               cb.add(Calendar.DATE, 1);\r
+                       }\r
+               }\r
+       }\r
+       \r
+       /**\r
+        * <P>aとbの差をミリ秒で返す(正/負)\r
+        * <P>秒に直すなら 1000、分に直すなら 60000で割る。\r
+        * @see #getDiffDateTime\r
+        */\r
+       public static long getCompareDateTime(String a, String b) {\r
+               GregorianCalendar d = getCalendar(a);\r
+               GregorianCalendar e = getCalendar(b);\r
+               if ( d == null || e == null ) {\r
+                       return -1;\r
+               }\r
+               return getCompareDateTime(d.getTimeInMillis(), e.getTimeInMillis());\r
+       }\r
+\r
+       /**\r
+        * <P>aとbの差をミリ秒で返す(正/負)\r
+        * <P>秒に直すなら 1000、分に直すなら 60000で割る。\r
+        * @see #getDiffDateTime\r
+        */\r
+       public static long getCompareDateTime(GregorianCalendar a, GregorianCalendar b) {\r
+               return getCompareDateTime(a.getTimeInMillis(), b.getTimeInMillis());\r
+       }\r
+\r
+       private static long getCompareDateTime(long x, long y) {\r
+               return x-y;\r
+       }\r
+       \r
+       /**\r
+        * <P>aとbの差をミリ秒で返す(絶対値)\r
+        * <P>秒に直すなら 1000、分に直すなら 60000で割る。\r
+        * @see #getCompareDateTime\r
+        */\r
+       public static long getDiffDateTime(String a, String b) {\r
+               GregorianCalendar d = getCalendar(a);\r
+               GregorianCalendar e = getCalendar(b);\r
+               if ( d == null || e == null ) {\r
+                       return -1;\r
+               }\r
+               return getDiffDateTime(d.getTimeInMillis(), e.getTimeInMillis());\r
+       }\r
+       \r
+       /**\r
+        * <P>aとbの差をミリ秒で返す\r
+        * <P>秒に直すなら 1000、分に直すなら 60000で割る。\r
+        */\r
+       public static long getDiffDateTime(GregorianCalendar a, GregorianCalendar b) {\r
+               return getDiffDateTime(a.getTimeInMillis(), b.getTimeInMillis());\r
+       }\r
+       \r
+       private static long getDiffDateTime(long x, long y) {\r
+               return ((x>y)?(x-y):(y-x));\r
+       }\r
+       \r
+\r
+       /**\r
+        * 現在時刻+n秒のCalendarを返す\r
+        */\r
+       public static GregorianCalendar getCalendar(int n) {\r
+               GregorianCalendar c = new GregorianCalendar();\r
+               //c.setTime(new Date());\r
+               c.add(Calendar.SECOND, n);\r
+               return c;\r
+       }\r
+       /**\r
+        *  日付時刻文字列をCalendarに変換\r
+        *  @param date YYYY/MM/DD[(.)][ hh:mm[:ss]] or YYYY-MM-DD[Thh:mm[:ss]] or YYYYMMDD[hhmm[ss]]\r
+        */\r
+       public static GregorianCalendar getCalendar(String date) {\r
+               Matcher ma = Pattern.compile("^(\\d\\d\\d\\d)[/-](\\d\\d)[/-](\\d\\d)(\\(.\\))?([ T](\\d\\d):(\\d\\d)(:\\d\\d)?)?$").matcher(date);\r
+               if ( ! ma.find()) {\r
+                       ma = Pattern.compile("^(\\d\\d\\d\\d)(\\d\\d)(\\d\\d)()?((\\d\\d)(\\d\\d)(\\d\\d)?)?$").matcher(date);\r
+                       if ( ! ma.find()) {\r
+                               return null;\r
+                       }\r
+               }\r
+               \r
+               GregorianCalendar c = null;\r
+               if ( ma.group(5) == null ) {\r
+                       c = new GregorianCalendar(\r
+                                       Integer.valueOf(ma.group(1)),\r
+                                       Integer.valueOf(ma.group(2))-1,\r
+                                       Integer.valueOf(ma.group(3)),\r
+                                       0,\r
+                                       0,\r
+                                       0);\r
+               }\r
+               else {\r
+                       c = new GregorianCalendar(\r
+                                       Integer.valueOf(ma.group(1)),\r
+                                       Integer.valueOf(ma.group(2))-1,\r
+                                       Integer.valueOf(ma.group(3)),\r
+                                       Integer.valueOf(ma.group(6)),\r
+                                       Integer.valueOf(ma.group(7)),\r
+                                       0);\r
+               }\r
+               return c;\r
+       }\r
+       \r
+       /**\r
+        *  現在日時+n秒を日付時刻形式に変換。\r
+        *  @param n : 負の値を許可する\r
+        *  @return YYYY/MM/DD hh:mm\r
+        */\r
+       public static String getDateTime(int n) {\r
+               GregorianCalendar c = new GregorianCalendar();\r
+               //c.setTime(new Date());\r
+               c.add(Calendar.SECOND, n);\r
+               return getDateTime(c);\r
+       }\r
+       \r
+       /**\r
+        *  日時を日付時刻形式に変換。曜日文字がつかない。\r
+        *  @see #getDateTimeW(GregorianCalendar)\r
+        *  @see #getIsoDateTime(GregorianCalendar)\r
+        *  @see #getDateTimeYMD(GregorianCalendar)\r
+        *  @return YYYY/MM/DD hh:mm\r
+        */\r
+       public static String getDateTime(GregorianCalendar c) {\r
+               return new SimpleDateFormat("yyyy/MM/dd HH:mm").format(c.getTime());\r
+       }\r
+       \r
+       /**\r
+        *  現在日時+n秒を日付時刻形式に変換。曜日文字がつく。\r
+        *  @param n : 負の値を許可する\r
+        *  @return YYYY/MM/DD hh:mm\r
+        */\r
+       public static String getDateTimeW(int n) {\r
+               GregorianCalendar c = new GregorianCalendar();\r
+               //c.setTime(new Date());\r
+               c.add(Calendar.SECOND, n);\r
+               return getDateTimeW(c);\r
+       }\r
+       \r
+       /**\r
+        *  日時を日付時刻形式に変換。曜日文字がつく。\r
+        *  @see #getDateTime(GregorianCalendar)\r
+        *  @return YYYY/MM/DD(WD) hh:mm\r
+        */\r
+       public static String getDateTimeW(GregorianCalendar c) {\r
+               return new SimpleDateFormat("yyyy/MM/dd('"+WDTPTN[c.get(Calendar.DAY_OF_WEEK)-1]+"') HH:mm").format(c.getTime());\r
+       }\r
+       \r
+       /**\r
+        *  日時を日付時刻形式に変換。ISO形式。秒まで返却。\r
+        *  @see #getDateTime(GregorianCalendar)\r
+        *  @return YYYY-MM-DDThh:mm:ss\r
+        */\r
+       public static String getIsoDateTime(GregorianCalendar c) {\r
+               return new SimpleDateFormat("yyyy-MM-dd'T'HH:mm:ss").format(c.getTime());\r
+       }\r
+\r
+       /**\r
+        *  現在日時+n秒を日付時刻形式に変換。\r
+        *  @see #getDateTime(GregorianCalendar)\r
+        *  @param n : 負の値を許可する\r
+        *  @return YYYYMMDDhhmmss\r
+        */\r
+       public static String getDateTimeYMD(int n) {\r
+               GregorianCalendar c = new GregorianCalendar();\r
+               //c.setTime(new Date());\r
+               if (n != 0) c.add(Calendar.SECOND, n);\r
+               return getDateTimeYMD(c);\r
+       }\r
+       \r
+       /**\r
+        *  日時を日付時刻形式に変換。YMD形式。秒まで返却。\r
+        *  @see #getDateTime(GregorianCalendar)\r
+        *  @return YYYYMMDDhhmmss\r
+        */\r
+       public static String getDateTimeYMD(GregorianCalendar c) {\r
+               return new SimpleDateFormat("yyyyMMddHHmmss").format(c.getTime());\r
+       }\r
+\r
+       /**\r
+        *  日時を日付時刻形式に変換。YMD形式。ミリ秒まで返却。\r
+        *  @see #getDateTime(GregorianCalendar)\r
+        *  @return YYYYMMDDhhmmssSSS\r
+        */\r
+       public static String getDateTimeYMDx(GregorianCalendar c) {\r
+               return new SimpleDateFormat("yyyyMMddHHmmssSSS").format(c.getTime());\r
+       }\r
+\r
+       /**\r
+        * <P>「当日」の日付文字列を返します。\r
+        * <P>ただし、05時~29時を当日として判断するので、<B>24時~29時に実行した場合は前日の日付が返ります</B>。\r
+        * @param n : 現在日時に対して n秒 加えた日時を返します。負の値も許可されます。\r
+        * @param addwdstr : trueの場合、日付に曜日文字[これ->(日)]を加えます。\r
+        * @return YYYY/MM/DD[(WD)] hh:mm:ss\r
+        */\r
+       public static String getDate529(int n, boolean addwdstr) {\r
+               GregorianCalendar c = getCalendar(0);\r
+               c.add(Calendar.SECOND, n);\r
+               return getDate529(c, addwdstr);\r
+       }\r
+       /**\r
+        * @see #getDate529(int, boolean)\r
+        */\r
+       public static String getDate529(String s, boolean addwdstr) {\r
+               GregorianCalendar c = getCalendar(s);\r
+               return getDate529(c, addwdstr);\r
+       }\r
+       /**\r
+        * @see #getDate529(int, boolean)\r
+        */\r
+       public static String getDate529(GregorianCalendar c, boolean addwdstr) {\r
+               // 今日の範囲は05:00~04:59まで\r
+               if ( isLateNight(c.get(Calendar.HOUR_OF_DAY)) ) {\r
+                       c.add(Calendar.DAY_OF_MONTH, -1);\r
+               }\r
+               return getDate(c, addwdstr);\r
+       }\r
+       \r
+       /**\r
+        * 日付を日付形式に変換。\r
+        * @return YYYY/MM/DD(WD)\r
+        */ \r
+       public static String getDate(GregorianCalendar c) {\r
+               return getDate(c, true);\r
+       }\r
+       \r
+       /**\r
+        * 日付を日付形式に変換。\r
+        * @param addwdstr : trueの場合、日付に曜日文字[これ->(日)]を加えます。\r
+        * @return YYYY/MM/DD[(WD)]\r
+        */ \r
+       public static String getDate(GregorianCalendar c, boolean addwdstr) {\r
+               if ( addwdstr ) {\r
+                       return new SimpleDateFormat("yyyy/MM/dd('"+WDTPTN[c.get(Calendar.DAY_OF_WEEK)-1]+"')").format(c.getTime());\r
+               }\r
+               else {\r
+                       return new SimpleDateFormat("yyyy/MM/dd").format(c.getTime());\r
+               }\r
+       }\r
+       \r
+       /**\r
+        * <P>「当日」の日付文字列を返します。\r
+        * <P>ただし、05時~29時を当日として判断するので、<B>24時~29時に実行した場合は前日の日付が返ります</B>。\r
+        * @param n : 現在日時に対して n秒 加えた日時を返します。負の値も許可されます。\r
+        * @return YYYYMMDD\r
+        */\r
+       public static String getDateYMD529(int n) {\r
+               GregorianCalendar c = new GregorianCalendar();\r
+               //c.setTime(new Date());\r
+               c.add(Calendar.SECOND, n);\r
+               \r
+               // 今日の範囲は05:00~04:59まで\r
+               if (c.get(Calendar.HOUR_OF_DAY) < 5) {\r
+                       c.add(Calendar.DAY_OF_MONTH, -1);\r
+               }\r
+               \r
+               return getDateYMD(c);\r
+       }\r
+       \r
+       /**\r
+        * 現在日付を日付形式に変換。\r
+        * @param n : 負の値を許可する。\r
+        * @return YYYYMMDD\r
+        */\r
+       public static String getDateYMD(int n) {\r
+               GregorianCalendar ca = CommonUtils.getCalendar(n);\r
+               return getDateYMD(ca);\r
+       }\r
+       \r
+       /**\r
+        * 現在日付を日付形式に変換。\r
+        * @return YYYYMMDD\r
+        */\r
+       public static String getDateYMD(GregorianCalendar c) {\r
+               return new SimpleDateFormat("yyyyMMdd").format(c.getTime());\r
+       }\r
+       \r
+       /**\r
+        *  日時を時刻形式に変換\r
+        * @return hh:mm\r
+        */\r
+       public static String getTime(int n) {\r
+               return getTime(getCalendar(n));\r
+       }\r
+       \r
+       /**\r
+        *  日時を時刻形式に変換\r
+        * @return hh:mm\r
+        */\r
+       public static String getTime(GregorianCalendar c) {\r
+               return getTime(c.get(Calendar.HOUR_OF_DAY),c.get(Calendar.MINUTE));\r
+       }\r
+       \r
+       /**\r
+        *  日時を時刻形式に変換\r
+        * @see #getTimeHM(GregorianCalendar)\r
+        * @return hh:mm\r
+        */\r
+       public static String getTime(int hh, int mm) {\r
+               return String.format("%02d:%02d",hh,mm);\r
+       }\r
+       \r
+       /**\r
+        *  日時を時刻形式に変換\r
+        *  @see #getTime(GregorianCalendar)\r
+        * @return hhmm\r
+        */\r
+       public static String getTimeHM(int n) {\r
+               return getTimeHM(getCalendar(n));\r
+       }\r
+       \r
+       /**\r
+        *  日時を時刻形式に変換\r
+        *  @see #getTime(GregorianCalendar)\r
+        * @return hhmm\r
+        */\r
+       public static String getTimeHM(GregorianCalendar c) {\r
+               return getTimeHM(c.get(Calendar.HOUR_OF_DAY),c.get(Calendar.MINUTE));\r
+       }\r
+       \r
+       /**\r
+        *  日時を時刻形式に変換\r
+        *  @see #getTime(GregorianCalendar)\r
+        * @return hhmm\r
+        */\r
+       public static String getTimeHM(int hh, int mm) {\r
+               return String.format("%02d%02d",hh,mm);\r
+       }\r
+       \r
+       /**\r
+        * 時間帯の重複があるかどうかを判定します。\r
+        * @param adjnotrep : falseの場合、終了時刻と開始時刻が重なるものを重複として扱います。\r
+        */\r
+       public static boolean isOverlap(String start1, String end1, String start2, String end2, boolean adjnotrep) {\r
+               if ( adjnotrep ) {\r
+                       return( ! (end1.compareTo(start2) <= 0 || end2.compareTo(start1) <= 0));\r
+               }\r
+               else {\r
+                       return( ! (end1.compareTo(start2) < 0 || end2.compareTo(start1) < 0));\r
+               }\r
+       }\r
+       \r
+       \r
+       /*******************************************************************************\r
+        * Color関連\r
+        ******************************************************************************/\r
+       \r
+       /**\r
+        * 文字列を色化します。\r
+        * @param s : 色化する文字列 "#xxxxxx;" (R、G、B 各16進2ケタ)\r
+        * @return {@link Color}\r
+        * @see #color2str(Color c)\r
+        */\r
+       public static Color str2color(String s) {\r
+               try {\r
+                       Matcher ma = Pattern.compile("#(..)(..)(..)").matcher(s);\r
+                       if ( ma.find() ) {\r
+                               int r = Integer.decode("0x"+ma.group(1));\r
+                               int g = Integer.decode("0x"+ma.group(2));\r
+                               int b = Integer.decode("0x"+ma.group(3));\r
+                               return(new Color(r,g,b));\r
+                       }\r
+                       return new Color(0,0,0);\r
+               }\r
+               catch (NumberFormatException e) {\r
+                       e.printStackTrace();\r
+               }\r
+               return null;\r
+       }\r
+       \r
+       /**\r
+        * 色を文字列化します。\r
+        * @param c : 文字列化する色({@link Color})\r
+        * @return "#xxxxxx;" (R、G、B 各16進2ケタ)\r
+        * @see #str2color(String) \r
+        */\r
+       public static String color2str(Color c) {\r
+               return String.format("#%02x%02x%02x", c.getRed(), c.getGreen(), c.getBlue());\r
+       }\r
+       \r
+       \r
+       /*******************************************************************************\r
+        * 文字列操作関連\r
+        ******************************************************************************/\r
+        \r
+       public static String joinPath(String... path) {\r
+               return joinStr(File.separator, path);\r
+       }\r
+\r
+       public static String joinStr(String s, ArrayList<String> a) {\r
+               return joinStr(s,a.toArray(new String[0]));\r
+       }\r
+       \r
+       public static String joinStr(String s, String... a) {\r
+               if (a.length <= 0) {\r
+                       return "";\r
+               }\r
+               StringBuffer sb = new StringBuffer();\r
+               for (int i=0; i<a.length-1; i++) {\r
+                       sb.append(a[i]);\r
+                       sb.append(s);\r
+               }\r
+               sb.append(a[a.length-1]);\r
+               return(sb.toString());\r
+       }\r
+       \r
+       /**\r
+        * 文字数ではなく、バイト数でのsubstringを行います。\r
+        * @param s\r
+        * @param length\r
+        * @return\r
+        */\r
+       public static String substringrb(String s, int length) {\r
+               StringBuilder sb = new StringBuilder();\r
+               int len = 0;\r
+               for ( Character c : s.toCharArray() ) {\r
+                       len += (c<256)?(1):(2);\r
+                       if (len > length) {\r
+                               break;\r
+                       }\r
+                       sb.append(c.toString());\r
+               }\r
+               return(sb.toString());\r
+       }\r
+       \r
+       private static final char[] NGCharList = "\\/:*?\"<>|".toCharArray();\r
+       \r
+       /**\r
+        * ファイル名に使用できない文字の一覧を返します。\r
+        */\r
+       public static String getNGCharList() {\r
+               return new String(NGCharList);\r
+       }\r
+       \r
+       /**\r
+        * ファイル名に使用できない文字が含まれているかどうか判定します。\r
+        */\r
+       public static boolean isNGChar(String s) {\r
+               for (int i=0; i<NGCharList.length; i++) {\r
+                       if (s.indexOf(NGCharList[i]) >= 0) {\r
+                               return(true);\r
+                       }\r
+               }\r
+               return(false);\r
+       }\r
+       \r
+       /**\r
+        * ファイル名に使用できない文字をHTMLエスケープ(&#ddd;)します。\r
+        * @see #unEscape(String)\r
+        */\r
+       public static String escapeFilename(String src) {\r
+               for (int i=0; i<NGCharList.length; i++) {\r
+                       src = src.replaceAll(String.format("\\%s",String.valueOf(NGCharList[i])), String.format("&#%03d;", (int)NGCharList[i]));\r
+               }\r
+               return src;\r
+       }\r
+\r
+       /**\r
+        * HTMLエスケープをデコードします。\r
+        */\r
+       public static String unEscape(String src) {\r
+               String dst = src.replaceAll("&quot;", "\"");\r
+               dst = dst.replaceAll("&lt;", "<");\r
+               dst = dst.replaceAll("&gt;", ">");\r
+               dst = dst.replaceAll("&amp;", "&");\r
+               dst = dst.replaceAll("&nbsp;", " ");\r
+               dst = dst.replaceAll("〜", "~");\r
+               dst = dst.replaceAll("−", "-");\r
+               HashMap<String, String> ek = new HashMap<String, String>();\r
+               Matcher ma = Pattern.compile("(&#(\\d+);)").matcher(src);\r
+               while (ma.find()) {\r
+                       ek.put(ma.group(1), Character.valueOf((char)(int)Integer.valueOf(ma.group(2))).toString());\r
+               }\r
+               for (Entry<String, String> kv : ek.entrySet()) {\r
+                       dst = dst.replaceAll(kv.getKey(), kv.getValue());\r
+               }\r
+               return dst;\r
+       }\r
+       \r
+       /**\r
+        *  Unicodeエスケープをデコードします。\r
+        */\r
+       public static String unUniEscape(String src) {\r
+               Matcher ma = Pattern.compile("\\\\u[0-9a-f]{4}").matcher(src);\r
+               StringBuffer sb = new StringBuffer(src.length());\r
+               while (ma.find()) {\r
+                       char[] chars = ma.group().substring(2, 6).toCharArray();\r
+                       int hex = 0;\r
+                       for (char c : chars) {\r
+                               hex = hex << 4;\r
+                               if ('a' <= c && c <= 'f') {\r
+                                       hex += c - 'a' + 10;\r
+                               }\r
+                               else {\r
+                                       hex += c - '0';\r
+                               }\r
+                       }\r
+                       ma.appendReplacement(sb, ""+(char)hex);\r
+               }\r
+               ma.appendTail(sb);\r
+               return sb.toString();\r
+       }\r
+       \r
+       \r
+       /**\r
+        * 文字列中の全角数字を半角数字に置き換えます\r
+        */\r
+       public static String toHANUM(String numTmp) {\r
+               if (numTmp == null) return null;\r
+               \r
+               StringBuilder sb = new StringBuilder();\r
+               for ( int i=0; i<numTmp.length(); i++ ) {\r
+                       char c = numTmp.charAt(i);\r
+                       if ( '0' <= c && c <= '9' ) {\r
+                               c = (char)(c -  '0' + '0' );\r
+                       }\r
+                       sb.append(c);\r
+               }\r
+               return sb.toString();\r
+       }\r
+       \r
+       public static String toHANALNUM(String alnumTmp) {\r
+               if (alnumTmp == null) return null;\r
+               \r
+               StringBuilder sb = new StringBuilder();\r
+               for ( int i=0; i<alnumTmp.length(); i++ ) {\r
+                       char c = alnumTmp.charAt(i);\r
+                       if ( 'a' <= c && c <= 'z' ) {\r
+                               c = (char)(c - 'a' + 'a' );\r
+                       }\r
+                       else if ( 'A' <= c && c <= 'Z' ) {\r
+                               c = (char)(c -  'A' + 'A' );\r
+                       }\r
+                       else if ( '0' <= c && c <= '9' ) {\r
+                               c = (char)(c -  '0' + '0' );\r
+                       }\r
+                       else if ( c == '(' ) {\r
+                               c = '(';\r
+                       }\r
+                       else if ( c == ')' ) {\r
+                               c = ')';\r
+                       }\r
+                       else if ( c == '-' ) {\r
+                               c = '-';\r
+                       }\r
+                       else if ( c == ' ' ) {\r
+                               c = ' ';\r
+                       }\r
+                       sb.append(c);\r
+               }\r
+               return sb.toString();\r
+       }\r
+\r
+       /*******************************************************************************\r
+        * オブジェクト操作関連\r
+        ******************************************************************************/\r
+\r
+       /**\r
+        * <P>オブジェクトからオブジェクトへフィールドのコピー(ディープコピー)を行います。\r
+        * <P>新しく作ったインスタンスに入れ替えたいけど、ポインタを変えたくないので中身だけコピーできないか?という時に使います。\r
+        * <P>fromにあってtoにないフィールドについては無視して最後まで処理を続行します(というか型違いとかも無視します)。\r
+        * <P>多分遅いので、設定ファイルの読み出しなど使用頻度の少ないところで利用します。\r
+        * @param to : HashMapクラスの場合は、コピー先にインスタンスが存在している必要があります(nullはだめ)\r
+        * @param from\r
+        * @version 3.15.4β~\r
+        */\r
+       public static boolean FieldCopy(final Object to, final Object from) {\r
+               try {\r
+                       return FieldCopy(to,from,null);\r
+               }\r
+               catch (Exception e) {\r
+                       e.printStackTrace();\r
+               }\r
+               return false;\r
+       }\r
+       \r
+       @SuppressWarnings("rawtypes")\r
+       private static boolean FieldCopy(final Object to, final Object from, final Field fn) throws InstantiationException, IllegalAccessException, IllegalArgumentException, InvocationTargetException, ConcurrentModificationException, InstantiationException  {\r
+               \r
+               // 継承しているクラスのリストを作成する\r
+               ArrayList<Class> fromCL = new ArrayList<Class>();\r
+               ArrayList<Class> toCL = new ArrayList<Class>();\r
+               for ( Class c=from.getClass(); c!=null && ! Object.class.equals(c); c=c.getSuperclass() ) {\r
+                       //if (debug) System.out.println("[DEBUG] FROM "+c.getName());\r
+                       fromCL.add(c);\r
+               }\r
+               for ( Class c=to.getClass(); c!=null && ! Object.class.equals(c); c=c.getSuperclass() ) {\r
+                       //if (debug) System.out.println("[DEBUG] TO "+c.getName());\r
+                       toCL.add(c);\r
+               }\r
+\r
+               // 両方の開始位置をそろえる\r
+               while ( fromCL.size() > 0 && ! toCL.contains(fromCL.get(0)) ) {\r
+                       if (debuglv2) System.out.println("[DEBUG] removed FROM "+fromCL.get(0).getName());\r
+                       fromCL.remove(0);\r
+               }\r
+               while ( toCL.size() > 0 && ! fromCL.contains(toCL.get(0)) ) {\r
+                       if (debuglv2) System.out.println("[DEBUG] removed TO "+toCL.get(0).getName());\r
+                       toCL.remove(0);\r
+               }\r
+               \r
+               int i=0;\r
+               for ( Class c : fromCL ) {\r
+                       \r
+                       i++;\r
+                       \r
+                       String ctype = (i>1)?(" (super class)"):("");\r
+                       String cname = c.getName();\r
+                       String fname = (fn!=null)?(" name="+fn.getName()):("");\r
+                       \r
+                       if ( isHashMap(to,from,c,fn) ) {\r
+                               if (debuglv2) System.err.println("[DEBUG] FieldCopy("+cname+") *** TERM *** deep copy"+ctype+fname);\r
+                               return true;\r
+                       }\r
+                       \r
+                       if ( isLeaf(to,from,c,fn) ) {\r
+                               if (debuglv2) System.err.println("[DEBUG] FieldCopy("+cname+") *** TERM *** leaf"+ctype+fname);\r
+                               return true;\r
+                       }\r
+                       \r
+                       Field[] fd = c.getDeclaredFields();\r
+                       \r
+                       // フィールドなんかねーよ\r
+                       if ( fd.length == 0 ) {\r
+                               if ( fn == null ) {\r
+                                       // 継承だけして追加のフィールドがない場合にここに入る\r
+                                       if (debug) System.err.println("[DEBUG] FieldCopy("+cname+") no field"+ctype+fname);\r
+                                       continue;\r
+                               }\r
+                               \r
+                               if (debug) System.err.println("[DEBUG] FieldCopy("+cname+") *** TERM *** no field"+ctype+fname);\r
+                               return isCopyable(to,from,c,fn);        // 終端\r
+                       }\r
+                       \r
+                       // (フィールド)たんけんぼくのまち。チョーさん生きてるよ!\r
+                       for ( Field f : fd ) {\r
+                               f.setAccessible(true);\r
+                               \r
+                               String xcname = f.getType().getName();\r
+                               String xfname = " name="+f.getName();\r
+                               \r
+                               int mod = f.getModifiers();\r
+                               \r
+                               if ( Modifier.isFinal(mod) ) {\r
+                                       if (debuglv2) System.err.println("[DEBUG] FieldCopy("+xcname+") : FINAL field "+ctype+xfname);\r
+                                       continue;\r
+                               }\r
+                               if ( Modifier.isStatic(mod) ) {\r
+                                       if (debug) System.err.println("[DEBUG] FieldCopy("+xcname+") : STATIC field "+ctype+xfname);\r
+                                       continue;\r
+                               }\r
+                               \r
+                               \r
+                               Object o = f.get(from);\r
+                               if ( o == null ) {\r
+                                       if (debug) System.err.println("[DEBUG] FieldCopy("+xcname+") *** TERM *** null value FROM"+ctype+xfname);\r
+                                       f.set(to, null);\r
+                                       continue;       // 終端\r
+                               }\r
+                               \r
+                               Class xc = o.getClass();\r
+                               xcname = xc.getName();\r
+                               \r
+                               if ( isHashMap(to,o,xc,f) ) {\r
+                                       if (debuglv2) System.err.println("[DEBUG] FieldCopy("+xcname+") *** TERM *** deep copy"+ctype+xfname);\r
+                                       continue;\r
+                               }\r
+                               \r
+                               if ( isLeaf(to,o,xc,f) ) {\r
+                                       if (debuglv2) System.err.println("[DEBUG] FieldCopy("+xcname+") *** TERM *** leaf"+ctype+xfname);\r
+                                       continue;\r
+                               }\r
+                               \r
+                               isCopyable(to,o,xc,f);  // 終端\r
+                       }\r
+               }\r
+               \r
+               return true;\r
+       }\r
+       \r
+       // HashMapとか、コピーする\r
+       @SuppressWarnings({ "unchecked", "rawtypes" })\r
+       private static boolean isHashMap(Object to, Object from, Class c, Field f) throws InstantiationException, IllegalAccessException, ConcurrentModificationException  {\r
+               \r
+               String cname = c.getName();\r
+               String fname = " name="+((f==null)?("<TOP>"):(f.getName()));\r
+               \r
+               for ( Class cx : unCloneables ) {\r
+                       if ( cx.equals(c) ) {\r
+                               if ( HashMap.class.equals(c) || ArrayList.class.equals(c) ) {\r
+               \r
+                                       final Object px = (f==null)?(to):(f.get(to));\r
+                                       if ( px == null ) {\r
+                                               // コピー先にインスタンスが存在している必要がある\r
+                                               if (debug) System.err.println("[DEBUG] FieldCopy("+cname+") / isCloneable() : must have been initialized"+fname);\r
+                                               return true;\r
+                                       }\r
+                                       \r
+                                       // 個別(キャスト!)\r
+               \r
+                                       if ( HashMap.class.equals(c) ) {\r
+                                               HashMap<Object, Object> o = (HashMap<Object, Object>) from;\r
+                                               HashMap<Object, Object> p;\r
+                                               if ( f != null ) {\r
+                                                       // フィールドならclone()では社ローコピーのせいで同じものを指してるに違いない、新しいインスタンスを作らないといけない\r
+                                                       p = (HashMap<Object, Object>) c.newInstance();\r
+                                               }\r
+                                               else {\r
+                                                       // 自身(orスーパークラス)なら別物だろうからキャストで大丈夫だろう\r
+                                                       p = (HashMap<Object, Object>) to;\r
+                                               }\r
+                                               \r
+                                               p.clear(); // とりあえず消す\r
+                                               if (debug) System.err.println("[DEBUG] FieldCopy("+cname+") / isHashMap() : HashMap "+cname+fname+" size="+o.size());\r
+                                               for ( Entry<Object, Object> entry: o.entrySet() ) {\r
+                                                       if (debuglv2) System.err.println("[DEBUG] FieldCopy("+cname+") / isHashMap() : copy"+fname+" key="+entry.getKey()+" value="+entry.getValue());\r
+                                                       p.put(entry.getKey(), entry.getValue());\r
+                                               }\r
+                                               \r
+                                               if ( f != null ) {\r
+                                                       f.set(to, p);\r
+                                               }\r
+                                       }\r
+                                       else if ( ArrayList.class.equals(c) ) {\r
+                                               ArrayList<Object> o = (ArrayList<Object>) from;\r
+                                               ArrayList<Object> p = null;\r
+                                               if ( f != null ) {\r
+                                                       p = (ArrayList<Object>) c.newInstance();\r
+                                               }\r
+                                               else {\r
+                                                       p = (ArrayList<Object>) to;\r
+                                               }\r
+                                               \r
+                                               p.clear(); // とりあえず消す\r
+                                               if (debug) System.err.println("[DEBUG] FieldCopy("+cname+") / isHashMap() : ArrayList "+cname+fname+" size="+o.size());\r
+                                               for ( Object entry: o ) {\r
+                                                       if (debuglv2) System.err.println("[DEBUG] FieldCopy("+cname+") / isHashMap() : copy"+fname+" value="+entry);\r
+                                                       p.add(entry);\r
+                                               }\r
+                                               \r
+                                               if ( f != null ) {\r
+                                                       f.set(to, p);\r
+                                               }\r
+                                       }\r
+                               }\r
+                               else {\r
+                                       if (debug) System.err.println("[DEBUG] FieldCopy("+cname+") / isHashMap() : unsupported"+fname);\r
+                               }\r
+                               return true;\r
+                       }                       \r
+               }\r
+               return false;\r
+       }\r
+       \r
+       @SuppressWarnings("rawtypes")\r
+       private static final Class[] unCloneables = { HashMap.class, AbstractMap.class, ArrayList.class, AbstractList.class };\r
+       \r
+       // Integerとか、コピーする\r
+       @SuppressWarnings("rawtypes")\r
+       private static boolean isLeaf(Object to, Object from, Class c, Field f) throws IllegalArgumentException, IllegalAccessException {\r
+               \r
+               String cname = c.getName();\r
+               String fname = " name="+((f==null)?("<TOP>"):(f.getName()));\r
+               \r
+               if ( from instanceof String || from instanceof Boolean || from instanceof Number || from instanceof Color || from instanceof Enum || from instanceof Component ) {\r
+                       if ( f == null ) {\r
+                               if (debug) System.err.println("[DEBUG] FieldCopy("+cname+") / isLeaf() : <Top> is not allowed");\r
+                               return true;\r
+                       }\r
+                               \r
+                       //if (debuglv2) System.err.println("[DEBUG] FieldCopy("+cname+") / isLeaf() : leaf field"+fname);\r
+                       f.set(to, from);\r
+                       return true;\r
+               }\r
+               \r
+               return false;\r
+       }\r
+       \r
+       //\r
+       @SuppressWarnings("rawtypes")\r
+       private static boolean isCopyable(Object to, Object from, Class c, Field f) throws IllegalArgumentException, IllegalAccessException, InvocationTargetException, InstantiationException {\r
+               \r
+               String cname = c.getName();\r
+               String fname = " name="+((f==null)?("<TOP>"):(f.getName()));\r
+               \r
+               // コピーに失敗して例外の発生したフィールドについては無視します\r
+               \r
+               if ( c.isPrimitive() ) {\r
+                       if (debug) System.err.println("[DEBUG] FieldCopy("+cname+") / isCopyable() : primitive"+fname);\r
+                       f.set(to, from);\r
+               }\r
+               else if ( c.isArray() ) {\r
+                       if ( f == null ) {\r
+                               // 自分自身だとコピーするしかないが、しない。redim()があればなんとかできたのに!\r
+                               if (debug) System.err.println("[DEBUG] FieldCopy("+cname+") / isCopyable() : not copyable array"+fname);\r
+                       }\r
+                       else {\r
+                               // フィールドなら入れ物は作る。要素はコピー\r
+                               Class comp = c.getComponentType();\r
+                               if (debug) System.err.println("[DEBUG] FieldCopy("+cname+") / isCopyable() : array of "+comp.getName()+fname+" lenth="+Array.getLength(from));\r
+                               Object[] o = (Object[]) from;\r
+                               Object[] p = (Object[]) Array.newInstance(comp, Array.getLength(from));\r
+                               \r
+                               for ( int i=o.length-1; i>=0; i-- ) {\r
+                                       if (debuglv2) System.err.println("[DEBUG] FieldCopy("+comp.getName()+") / isCopyable() : copy"+fname+" value="+o[i]);\r
+                                       p[i] = o[i];\r
+                               }\r
+                       }\r
+               }\r
+               else {\r
+                       // そのほかは可能な限りcloneする\r
+                       invokeClone(to,from,c,f);\r
+               }\r
+               \r
+               return true;\r
+       }\r
+       \r
+       //\r
+       @SuppressWarnings("rawtypes")\r
+       private static boolean invokeClone(Object to, Object from, Class c, Field f) throws IllegalArgumentException, IllegalAccessException, InvocationTargetException {\r
+               \r
+               final String cname = c.getName();\r
+               final String fname = " name="+((f==null)?("<TOP>"):(f.getName())); // よく考えたらここにf==nullで入ってくることはない\r
+               \r
+               try {\r
+                       @SuppressWarnings("unchecked")\r
+                       Method m = c.getMethod("clone");\r
+                       f.set(to, m.invoke(from));\r
+                       if (debug) System.err.println("[DEBUG] FieldCopy("+cname+") / invokeClone() : invoke clone "+cname+fname);\r
+                       return true;\r
+               }\r
+               catch (NoSuchMethodException e) {\r
+                       // ちょっとこわいのでコピーせずに無視\r
+                       if (debug) System.err.println("[DEBUG] FieldCopy("+cname+") / invokeClone() : clone unsupported and ignored "+fname);\r
+               }\r
+               return false;\r
+       }\r
+       \r
+       /*******************************************************************************\r
+        * JavaVM関連\r
+        ******************************************************************************/\r
+\r
+       /**\r
+        * デバッグ用にスタックトレースをとりたいなー\r
+        */\r
+       public static void printStackTrace() {\r
+               if ( ! debug) return;\r
+               \r
+               new Throwable().printStackTrace();\r
+       }\r
+       \r
+       /*******************************************************************************\r
+        * OS操作関連\r
+        ******************************************************************************/\r
+\r
+       // なぜJavaなのに機種別のコードをかかなければならないのか…\r
+       public static boolean isMac() { return ismac; }\r
+       public static boolean isLinux() { return islinux; }\r
+       public static boolean isWindows() { return iswindows; }\r
+       public static boolean isWindowsXP() { return iswindowsxp; }\r
+       \r
+       private static final boolean ismac              = ((String)System.getProperty("os.name")).toLowerCase().replaceAll(" ", "").startsWith("macosx");\r
+       private static final boolean islinux    = ((String)System.getProperty("os.name")).toLowerCase().replaceAll(" ", "").startsWith("linux");\r
+       private static final boolean iswindows  = ((String)System.getProperty("os.name")).toLowerCase().replaceAll(" ", "").startsWith("windows");\r
+       private static final boolean iswindowsxp= ((String)System.getProperty("os.name")).toLowerCase().replaceAll(" ", "").startsWith("windowsxp");\r
+       \r
+       // ミリ秒単位でsleep\r
+       public static void milSleep(int i) {\r
+               try {\r
+                       Thread.sleep(i);\r
+               }\r
+               catch (InterruptedException e) {\r
+                       System.err.println("[スリープ] なんかあったのか");\r
+                       e.printStackTrace();\r
+               }\r
+       }\r
+       \r
+       /**\r
+        * コマンドを実行する\r
+        * @see #getCommandResult()\r
+        * @param run : コマンドの指定\r
+        * @return -1 : 失敗、-1以外 : コマンドのexit値\r
+        */\r
+       public static int executeCommand(String run)    {\r
+               \r
+               commandResult = null;\r
+               \r
+               ArrayList<String> list = new ArrayList<String>();\r
+               Matcher ma = Pattern.compile("((\"([^\"]*?)\"|([^\"]+?))(\\s+|$))").matcher(run);\r
+               while (ma.find()) {\r
+                       if (ma.group(3) != null) {\r
+                               list.add(ma.group(3));\r
+                       }\r
+                       else {\r
+                               list.add(ma.group(4));\r
+                       }\r
+               }\r
+               if ( list.size() == 0 ) {\r
+                       System.err.println("[外部コマンド] 実行できません: "+run);\r
+                       return -1;\r
+               }\r
+               else {\r
+                       ProcessBuilder pb = null;\r
+                       Process p = null;\r
+                       \r
+                       InputStream is = null;\r
+                       InputStreamReader isr = null;\r
+                       BufferedReader br =  null;\r
+       \r
+                       try {\r
+                               pb = new ProcessBuilder(list);\r
+                               pb.redirectErrorStream(true);\r
+                               \r
+                               p = pb.start();\r
+                               p.getOutputStream().close();    // 入力はしない\r
+                               \r
+                               String encoding = (isWindows())?("MS932"):("UTF-8");\r
+                               \r
+                               StringBuilder sb = new StringBuilder();\r
+                               is = p.getInputStream();\r
+                               isr = new InputStreamReader(is,encoding);\r
+                               br =  new BufferedReader(isr);\r
+                               String s;\r
+                               while ((s=br.readLine()) != null) {\r
+                                       sb.append(s);\r
+                                       sb.append("\n");\r
+                               }\r
+                               commandResult = sb.toString();\r
+                               \r
+                               System.out.println("--- 外部コマンドを実行します ---");\r
+                               System.out.println(commandResult);\r
+                               System.out.println("--- 外部コマンドを実行しました ---");\r
+                               \r
+                               p.waitFor();    // is.read()で判定できるので不要\r
+                               \r
+                               int retval = p.exitValue();\r
+                               \r
+                               return retval;\r
+                       }\r
+                       catch ( IOException e ) {\r
+                               System.err.println("[外部コマンド] 失敗しました: "+run);\r
+                               e.printStackTrace();\r
+                       }\r
+                       catch ( InterruptedException e ) {\r
+                               System.err.println("[外部コマンド] 失敗しました: "+run);\r
+                               e.printStackTrace();\r
+                       }\r
+                       finally {\r
+                               closing(br);\r
+                               closing(isr);\r
+                               closing(is);\r
+                               \r
+                               if ( p != null ) {\r
+                                       closing(p.getInputStream());\r
+                                       closing(p.getErrorStream());\r
+                                       p.destroy();\r
+                               }\r
+                       }\r
+               }\r
+               \r
+               return -1;\r
+       }\r
+       \r
+       public static String getCommandResult() { return commandResult; }\r
+       \r
+       private static String commandResult;\r
+\r
+       /**\r
+        * 登録されたアプリケーションでファイルを開く\r
+        */\r
+       public static String openFile(String file)      {\r
+               String errmsg = null;\r
+               try {\r
+                       if ( useRundll32 && isWindows() ) {\r
+                               Runtime runtime = Runtime.getRuntime(); \r
+                               runtime.exec("rundll32 url.dll,FileProtocolHandler "+new File(file).getAbsolutePath());\r
+                       }\r
+                       else {\r
+                               Desktop desktop = Desktop.getDesktop();\r
+                               desktop.open(new File(file));\r
+                       }\r
+               }\r
+               catch (UnsupportedOperationException e) {\r
+                       System.err.println("[アプリケーションでファイルを開く] 失敗しました: "+file);\r
+                       e.printStackTrace();\r
+                       errmsg = e.toString();\r
+               }\r
+               catch (IOException e) {\r
+                       System.err.println("[アプリケーションでファイルを開く] 失敗しました: "+file);\r
+                       e.printStackTrace();\r
+                       errmsg = e.toString();\r
+               }\r
+               return errmsg;\r
+       }\r
+       \r
+       \r
+       /*******************************************************************************\r
+        * ファイルI/O関連\r
+        ******************************************************************************/\r
+       \r
+       // 古くないかどうか\r
+       public static boolean isFileAvailable(File f, int days) {\r
+               // 存在するか\r
+               if ( ! f.exists() ) {\r
+                       return false;\r
+               }\r
+               \r
+               // 最近更新されているか\r
+               long lm = f.lastModified();\r
+               GregorianCalendar cal = new GregorianCalendar();\r
+               cal.setTimeInMillis(lm);\r
+               if ( getCompareDateTime(cal, getCalendar(-days*86400)) < 0 ) {\r
+                       return false;\r
+               }\r
+                       \r
+               return true;\r
+       }\r
+       \r
+       // ディレクトリをまるっと削除\r
+       public static boolean rmdir(File f) {\r
+               if( ! f.exists()) {\r
+                       return false;\r
+               }\r
+               \r
+               if (f.isFile()){\r
+                       return f.delete();\r
+               }\r
+               else if (f.isDirectory()) {\r
+                       File[] files = f.listFiles();\r
+                       for (File file : files){\r
+                               if ( ! rmdir(file)) {\r
+                                       return false;\r
+                               }\r
+                       }\r
+                       return f.delete();\r
+               }\r
+               \r
+               return false;   // ここには来ないはず\r
+       }\r
+       \r
+       // カウンタの記録\r
+       public static boolean saveCnt(int cnt, String filename) {\r
+               if ( ! write2file(filename, String.valueOf(cnt)) ) {\r
+                       System.out.println("[カウンタファイル] 書き込めませんでした: "+filename);\r
+                       return false;\r
+               }\r
+               return true;\r
+       }\r
+       public static int loadCnt(String filename) {\r
+               if ( ! new File(filename).exists() ) {\r
+                       return -1;\r
+               }\r
+               \r
+               try {\r
+                       String str = read4file(filename, true);\r
+                       if ( str != null ) {\r
+                               return Integer.valueOf(str);\r
+                       }\r
+               }\r
+               catch ( NumberFormatException e ) {\r
+                       System.err.println("[カウンタファイル] 内容が不正です: "+filename);\r
+               }\r
+               \r
+               System.err.println("[カウンタファイル] 読み込めませんでした: "+filename);\r
+               return -1;\r
+       }\r
+       \r
+       // ロック・アンロック\r
+       \r
+       /**\r
+        *  ロック\r
+        */\r
+       public static boolean getLock() {\r
+               FileOutputStream fos = null;\r
+               FileChannel fc = null;\r
+               try {\r
+                       fos = new FileOutputStream(new File("_lock_"));\r
+                       fc = fos.getChannel();\r
+                       lock = fc.tryLock();\r
+                       if ( lock == null ) {\r
+                               System.err.println("[多重起動禁止] ロックされています.");\r
+                               return false;\r
+                       }\r
+                       else {\r
+                               return true;\r
+                       }\r
+               }\r
+               catch (FileNotFoundException e) {\r
+                       System.err.println("[多重起動禁止] ロックの取得に失敗しました.");\r
+                       e.printStackTrace();\r
+               }\r
+               catch (IOException e) {\r
+                       System.err.println("[多重起動禁止] ロックの取得に失敗しました.");\r
+                       e.printStackTrace();\r
+               }\r
+               finally {\r
+                       //closing(fos);\r
+                       //closing(fc);\r
+               }\r
+               return false;\r
+       }\r
+       \r
+       /**\r
+        *  アンロック\r
+        */\r
+       public static void getUnlock() {\r
+               try {\r
+                       if ( lock != null ) {\r
+                               lock.release();\r
+                       }\r
+               } catch (IOException e) {\r
+                       System.err.println("[多重起動禁止] ロックの解放に失敗しました.");\r
+                       e.printStackTrace();\r
+               }\r
+       }\r
+\r
+       private static FileLock lock = null;\r
+\r
+       /**\r
+        *  テキストをファイルに書き出す(エンコードはデフォルト)\r
+        */\r
+       public static boolean write2file(String fname, String dat) {\r
+               return write2file(fname, dat, null);\r
+       }\r
+       \r
+       /**\r
+        *  テキストをファイルに書き出す(エンコードは指定による)\r
+        */\r
+       public static boolean write2file(String fname, String dat, String encoding) {\r
+               if ( fname.matches(".*\\.zip$") ) {\r
+                       return _write2zip(fname, dat, encoding);\r
+               }\r
+               else {\r
+                       return _write2file(fname, dat, encoding);\r
+               }\r
+       }\r
+       private static boolean _write2zip(String fname, String dat, String encoding) {\r
+               {\r
+                       FileOutputStream os = null;\r
+                       BufferedOutputStream bos = null;\r
+                       ZipOutputStream zos = null;\r
+                       try {\r
+                               // 一時ファイルに書き出し\\r
+                               os = new FileOutputStream(fname+".tmp");\r
+                               bos = new BufferedOutputStream(os);\r
+                               zos = new ZipOutputStream(bos);\r
+                               \r
+                               byte[] buf = null;\r
+                               if ( encoding == null ) {\r
+                                       // デフォルトエンコーディングで\r
+                                       buf = dat.getBytes();\r
+                               }\r
+                               else {\r
+                                       // 指定エンコーディングで\r
+                                       buf = dat.getBytes(encoding);\r
+                               }\r
+                               \r
+                               ZipEntry ze = new ZipEntry(ZIPENTRY);\r
+                               ze.setSize(buf.length);\r
+                               \r
+                               zos.putNextEntry(ze);\r
+                               zos.write(buf);\r
+                               \r
+                       // 処理が続くので閉じないとアカン!\r
+                               zos.closeEntry();\r
+                           zos.close(); zos = null;\r
+                       if (bos!=null) { bos.close(); bos = null; }\r
+                       if (os!=null) { os.close(); os = null; }\r
+                               \r
+                               // キャッシュファイルに変換\r
+                           File o = new File(fname);\r
+                           if ( o.exists() && ! o.delete() ) {\r
+                               System.err.println("削除できないよ: "+fname);\r
+                           }\r
+                           File n = new File(fname+".tmp");\r
+                           if ( ! n.renameTo(o) ) {\r
+                               System.err.println("リネームできないよ: "+fname);\r
+                           }\r
+                       }\r
+                       catch (Exception e) {\r
+                               // 例外\r
+                               System.out.println("書けないよ: "+e.toString());\r
+                               return false;\r
+                       }\r
+                       finally {\r
+                               closing(zos);\r
+                               closing(bos);\r
+                               closing(os);\r
+                       }\r
+               }\r
+               return true;\r
+       }\r
+       private static boolean _write2file(String fname, String dat, String encoding) {\r
+               {\r
+                       Writer wr = null;\r
+                       BufferedWriter bw = null;\r
+                       FileOutputStream os = null;\r
+                       try {\r
+                               // 一時ファイルに書き出し\r
+                               if ( encoding == null ) {\r
+                                       // デフォルトエンコーディングで\r
+                                       wr = new FileWriter(fname+".tmp");\r
+                               }\r
+                               else {\r
+                                       // 指定エンコーディングで\r
+                                       os = new FileOutputStream(new File(fname+".tmp"));\r
+                                       wr = new OutputStreamWriter(os, encoding); \r
+                               }\r
+                               bw = new BufferedWriter(wr);\r
+                       bw.write(dat);\r
+                       \r
+                       // 処理が続くので閉じないとアカン!\r
+                           bw.close(); bw = null;\r
+                       wr.close(); wr = null;\r
+                       if (os!=null) { os.close(); os = null; }\r
+                               \r
+                               // キャッシュファイルに変換\r
+                           File o = new File(fname);\r
+                           if ( o.exists() && ! o.delete() ) {\r
+                               System.err.println("削除できないよ: "+fname);\r
+                           }\r
+                           File n = new File(fname+".tmp");\r
+                           if ( ! n.renameTo(o) ) {\r
+                               System.err.println("リネームできないよ: "+fname);\r
+                           }\r
+                       }\r
+                       catch (Exception e) {\r
+                               // 例外\r
+                               System.out.println("書けないよ: "+e.toString());\r
+                               return false;\r
+                       }\r
+                       finally {\r
+                               closing(bw);\r
+                               closing(wr);\r
+                               closing(os);\r
+                       }\r
+               }\r
+               return true;\r
+       }\r
+       \r
+       private static final String ZIPENTRY = "compressed.dat";\r
+       \r
+       /**\r
+        *  テキストをファイルを読み出す(エンコードはデフォルト)\r
+        */\r
+       public static String read4file(String fname, boolean nocr) {\r
+               return read4file(fname, nocr, null);\r
+       }\r
+\r
+       /**\r
+        *  テキストをファイルを読み出す(エンコードは指定による)\r
+        */\r
+       public static String read4file(String fname, boolean nocr, String encoding) {\r
+               if ( fname.matches(".*\\.zip$") ) {\r
+                       return _read4zip(fname, nocr, null);\r
+               }\r
+               else {\r
+                       return _read4file(fname, nocr, null);\r
+               }\r
+       }\r
+       private static String _read4zip(String fname, boolean nocr, String encoding) {\r
+               String response = null;\r
+               {\r
+                       ZipFile zf = null;\r
+                       Reader rd = null;\r
+                       InputStream is = null;\r
+                       BufferedReader br = null;\r
+                       try {\r
+                               // ファイルから読み出し\r
+                               zf = new ZipFile(fname);\r
+                               ZipEntry ze = zf.getEntry(ZIPENTRY);\r
+                               if ( ze == null ) {\r
+                                       System.out.println("ZIPファイルがおかしい");\r
+                                       return null;\r
+                               }\r
+                               \r
+                               is = zf.getInputStream(ze);\r
+                               \r
+                               // ファイルから読み出し\r
+                               if ( encoding == null ) {\r
+                                       // デフォルトエンコーディングで\r
+                                       rd = new InputStreamReader(is); \r
+                               }\r
+                               else {\r
+                                       // 指定エンコーディングで\r
+                                       rd = new InputStreamReader(is, encoding); \r
+                               }\r
+                               br = new BufferedReader(rd);\r
+                               \r
+                               String str;\r
+                               StringBuilder sb = new StringBuilder();\r
+                               while ((str = br.readLine()) != null) {\r
+                                       sb.append(str);\r
+                                       if ( ! nocr ) sb.append("\n");\r
+                               }\r
+                               \r
+                               response = sb.toString();\r
+                       }\r
+                       catch (Exception e) {\r
+                               // 例外\r
+                               System.out.println("読めないよ: "+e.toString());\r
+                               return null;\r
+                       }\r
+                       finally {\r
+                               closing(is);\r
+                               closing(br);\r
+                               closing(rd);\r
+                               closing(zf);\r
+                       }\r
+               }\r
+               return response;\r
+       }\r
+       private static String _read4file(String fname, boolean nocr, String encoding) {\r
+               String response = null;\r
+               {\r
+                       Reader rd = null;\r
+                       BufferedReader br = null;\r
+                       FileInputStream is = null;\r
+                       try {\r
+                               // ファイルから読み出し\r
+                               if ( encoding == null ) {\r
+                                       // デフォルトエンコーディングで\r
+                                       rd = new FileReader(fname);\r
+                               }\r
+                               else {\r
+                                       // 指定エンコーディングで\r
+                                       is = new FileInputStream(new File(fname));\r
+                                       rd = new InputStreamReader(is, encoding); \r
+                               }\r
+                               br = new BufferedReader(rd);\r
+                               \r
+                               String str;\r
+                               StringBuilder sb = new StringBuilder();\r
+                               while ((str = br.readLine()) != null) {\r
+                                       sb.append(str);\r
+                                       if ( ! nocr ) sb.append("\n");\r
+                               }\r
+                               \r
+                               response = sb.toString();\r
+                       }\r
+                       catch (Exception e) {\r
+                               // 例外\r
+                               System.out.println("読めないよ: "+e.toString());\r
+                               return null;\r
+                       }\r
+                       finally {\r
+                               closing(br);\r
+                               closing(rd);\r
+                               closing(is);\r
+                       }\r
+               }\r
+               return response;\r
+       }\r
+\r
+       /**\r
+        *  XMLEncoderでシリアライズしてファイルに出力する\r
+        */\r
+       public static boolean writeXML(String fname, Object obj) {\r
+               boolean done = true;\r
+               {\r
+                       FileOutputStream fos = null;\r
+                       BufferedOutputStream bos = null;\r
+                       XMLEncoder enc = null;\r
+                       try {\r
+                               fos = new FileOutputStream(fname+".tmp");\r
+                               bos = new BufferedOutputStream(fos);\r
+                               enc = new XMLEncoder(bos);\r
+                               enc.writeObject(obj);\r
+                               \r
+                       // 処理が続くので閉じないとアカン!\r
+                               enc.close(); enc = null;\r
+                               bos.close(); bos = null;\r
+                               fos.close(); fos = null;\r
+                               \r
+                               // キャッシュファイルに変換\r
+                           File o = new File(fname);\r
+                           if ( o.exists() && ! o.delete() ) {\r
+                               System.err.println("削除できないよ: "+fname);\r
+                           }\r
+                           File n = new File(fname+".tmp");\r
+                           if ( ! n.renameTo(o) ) {\r
+                               System.err.println("リネームできないよ: "+fname);\r
+                           }\r
+                       }\r
+                       catch (Exception e) {\r
+                               // 例外\r
+                               done = false;\r
+                               System.err.println(e.toString());\r
+                       }\r
+                       finally {\r
+                               closing(enc);\r
+                               closing(fos);\r
+                               closing(bos);\r
+                       }\r
+               }\r
+               return done;\r
+       }\r
+       \r
+       /**\r
+        *  XMLDecorderでシリアライズしたファイルを読みだす\r
+        */\r
+       public static Object readXML(String fname) {\r
+               {\r
+                       XMLDecoder dec = null;\r
+                       FileInputStream fis = null;\r
+                       BufferedInputStream bis = null;\r
+                       try {\r
+                               fis = new FileInputStream(fname);\r
+                               bis = new BufferedInputStream(fis);\r
+                               dec = new XMLDecoder(bis);\r
+                               return dec.readObject();\r
+                       }\r
+                       catch (Exception e) {\r
+                               // 例外\r
+                               System.err.println(e.toString());\r
+                       }\r
+                       finally {\r
+                               closing(dec);\r
+                               closing(bis);\r
+                               closing(fis);\r
+                       }\r
+               }\r
+               return null;\r
+       }\r
+       \r
+       /*\r
+        * finallyブロックで書き間違えそうなので\r
+        */\r
+       public static void closing(Reader r) {\r
+               if ( r != null ) try { r.close(); } catch (Exception e) {}\r
+       }\r
+       public static void closing(InputStream r) {\r
+               if ( r != null ) try { r.close(); } catch (Exception e) {}\r
+       }\r
+       public static void closing(ZipFile r) {\r
+               if ( r != null ) try { r.close(); } catch (Exception e) {}\r
+       }\r
+       public static void closing(XMLDecoder r) {\r
+               if ( r != null ) r.close();\r
+       }\r
+       \r
+       public static void closing(Writer w) {\r
+               if ( w != null ) try { w.close(); } catch (Exception e) {}\r
+       }\r
+       public static void closing(OutputStream w) {\r
+               if ( w != null ) try { w.close(); } catch (Exception e) {}\r
+       }\r
+       public static void closing(XMLEncoder w) {\r
+               if ( w != null ) w.close();\r
+       }\r
+       \r
+       public static void closing(FileChannel fc) {\r
+               if ( fc != null ) try { fc.close(); } catch (Exception e) {}\r
+       }\r
+       \r
+       public static void closing(Socket sock) {\r
+               if ( sock != null ) try { sock.close(); } catch (Exception e) {}\r
+       }\r
+       public static void closing(HttpURLConnection ucon) {\r
+               if ( ucon != null ) ucon.disconnect();\r
+       }\r
+}\r
diff --git a/TinyBannavi/src/tainavi/ContentIdDIMORA.java b/TinyBannavi/src/tainavi/ContentIdDIMORA.java
new file mode 100644 (file)
index 0000000..6dcb97c
--- /dev/null
@@ -0,0 +1,105 @@
+package tainavi;\r
+\r
+/**\r
+ * 別にContentIdEDCBを継承する必要はないような…\r
+ */\r
+public class ContentIdDIMORA extends ContentIdEDCB {\r
+\r
+       public static final String CIDMARK = "DIMORA$";\r
+       private static final int chid_len = 12;\r
+       private static final int evid_len = 4;\r
+\r
+       //protected static int original_network_id ;    // これは放送種別がわかるだけで正しい値ではない\r
+       //protected static int transport_stream_id ;    // これは常に0\r
+       //protected static int servive_id ;                             // これは正しい値のはず\r
+       //protected static int event_id ;                               // これは使えない\r
+\r
+       //private static String station_id;                             // これは使える\r
+\r
+       protected static String dimora_program_id;                              // DIMORA独自。用途が謎だがDimora経由での予約に必要になるかもしれないので保存\r
+\r
+       public static boolean isValid(String cId) {\r
+               return (cId!=null)?(cId.startsWith(CIDMARK) && cId.length()>=CIDMARK.length()+chid_len+evid_len):(false);\r
+       }\r
+       \r
+       public static String stripMark(String contentid) { return contentid.substring(CIDMARK.length()); }\r
+       \r
+       public static String getDimId() { return dimora_program_id; }\r
+       \r
+       /**\r
+        * DimoraのIDを変換する\r
+        */\r
+       public static String getChId(String dimoraid) {\r
+               try {\r
+                       int id = Integer.decode("0x"+dimoraid);\r
+                       int onid = 0;\r
+                       int sid = 0;\r
+                       if ( (id & 0x8000) != 0 ) {\r
+                               sid = ((id & 0x7F00) << 1) | (id & 0x00FF);     // これは一体どういう…?\r
+                               onid = 0x7FFF;\r
+                       }\r
+                       else if ( id >= 0x3C00 ) {\r
+                               onid = 0x0004;\r
+                               sid = id - 0x3C00;\r
+                       }\r
+                       else if ( id >= 0x3800 ) {\r
+                               onid = 0x0007;\r
+                               sid = id - 0x3800;\r
+                       }\r
+                       else {\r
+                               onid = 0x0006;\r
+                               sid = id - 0x3400;\r
+                       }\r
+                       return String.format("%04X%04X%04X", onid, 0, sid);\r
+               }\r
+               catch (NumberFormatException e) {\r
+               }\r
+               return null;\r
+       }\r
+       \r
+       public static String getChId() {\r
+               return String.format("%04X%04X%04X", original_network_id, transport_stream_id, servive_id);\r
+       }\r
+\r
+       // decodeChIdは共通\r
+       \r
+       public static String getContentId(int evid) {\r
+               event_id = evid;\r
+               return getContentId(original_network_id, transport_stream_id, servive_id, event_id, dimora_program_id);\r
+       }\r
+       \r
+       public static String getContentId(String dimoid) {\r
+               dimora_program_id = dimoid;\r
+               return getContentId(original_network_id, transport_stream_id, servive_id, event_id, dimora_program_id);\r
+       }\r
+\r
+       public static String getContentId(int evid, String dimoid) {\r
+               event_id = evid;\r
+               dimora_program_id = dimoid;\r
+               return getContentId(original_network_id, transport_stream_id, servive_id, event_id, dimora_program_id);\r
+       }\r
+\r
+       private static String getContentId(int onid, int tsid, int sid, int evid, String dimid) {\r
+               return String.format("%s%04X%04X%04X%04X%s", CIDMARK, onid, tsid, sid, evid, (dimid!=null)?(dimid):(""));\r
+       }\r
+       \r
+       public static boolean decodeContentId(String cId) {\r
+               if ( isValid(cId) ) {\r
+                       try {\r
+                               String xId = stripMark(cId);\r
+                               if ( ! decodeChId(xId) ) {\r
+                                       return false;\r
+                               }\r
+                               transport_stream_id = 0;\r
+                               event_id = Integer.decode("0x"+xId.substring(chid_len,chid_len+evid_len));\r
+                               dimora_program_id = xId.substring(chid_len+evid_len);\r
+                               return true;\r
+                       }\r
+                       catch ( Exception e ) {\r
+                               e.printStackTrace();\r
+                       }\r
+               }\r
+               return false;\r
+       }\r
+\r
+}\r
diff --git a/TinyBannavi/src/tainavi/ContentIdEDCB.java b/TinyBannavi/src/tainavi/ContentIdEDCB.java
new file mode 100644 (file)
index 0000000..5f27bef
--- /dev/null
@@ -0,0 +1,116 @@
+package tainavi;\r
+\r
+/**\r
+ * EpgDataCap_BonプラグインとEDCBプラグインで利用するコンテンツIDを操作するstaticメソッドの集合\r
+ */\r
+public class ContentIdEDCB {\r
+\r
+       public static final String CIDMARK = "EDCB$";\r
+       private static final int chid_len = 12;\r
+       private static final int evid_len = 4;\r
+       \r
+       protected static int original_network_id ;\r
+       protected static int transport_stream_id ;\r
+       protected static int servive_id ;\r
+       protected static int event_id ;\r
+       \r
+       private static String station_id;\r
+       \r
+       public static boolean isValid(String cId) {\r
+               return (cId!=null)?(cId.startsWith(CIDMARK) && cId.length()>=CIDMARK.length()+chid_len+evid_len):(false);\r
+       } \r
+       \r
+       public static String stripMark(String edcbid) { return edcbid.substring(CIDMARK.length()); }\r
+       \r
+       public static int getOnId() { return original_network_id; }\r
+       public static int getTSId() { return transport_stream_id; }\r
+       public static int getSId() { return servive_id; }\r
+       public static int getEvId() { return event_id; }\r
+       public static String getStId() { return station_id; }\r
+       \r
+       public static String getChId() {\r
+               return getChId(original_network_id,transport_stream_id,servive_id);\r
+       }\r
+       \r
+       /**\r
+        * \r
+        * @param onid\r
+        * @param tsid\r
+        * @param sid\r
+        * @return\r
+        */\r
+       public static String getChId(int onid, int tsid, int sid) {\r
+               return String.format("%04X%04X%04X",onid,tsid,sid);\r
+       }\r
+       \r
+       /**\r
+        * 10進表記のChIDを16進表記に変更\r
+        */\r
+       public static String getChId(String dec) {\r
+               try {\r
+                       long n = Long.valueOf(dec);\r
+                       if ( (n&0xFFFF00000000L) == 0 || (n&0x0000FFFF0000L) == 0 || (n&0x00000000FFFFL) == 0 ) { \r
+                               return null;\r
+                       }\r
+                       return String.format("%012X", n);       // Javaでは%lxではなく%xでいいらしい\r
+               }\r
+               catch ( NumberFormatException e ) {\r
+               }\r
+               return null;\r
+       }\r
+       \r
+       public static String getContentId(int onid, int tsid, int sid, int evid) {\r
+               return getContentId(getChId(onid,tsid,sid),evid);\r
+       }\r
+       \r
+       public static String getContentId(String chid, int evid) {\r
+               return String.format("%s%s%04X",CIDMARK,chid,evid);\r
+       }\r
+       \r
+       public static String getContentId(int evid) {\r
+               return getContentId(getChId(),evid);\r
+       }\r
+\r
+       public static boolean decodeChId(String chid) {\r
+               if ( chid != null && chid.length()>=chid_len ) {\r
+                       try {\r
+                               original_network_id = Integer.decode("0x"+chid.substring(0,4));\r
+                               transport_stream_id = Integer.decode("0x"+chid.substring(4,8));\r
+                               servive_id = Integer.decode("0x"+chid.substring(8,12));\r
+                               // iEPG2\r
+                               if ( original_network_id == 4 ) {\r
+                                       station_id = String.format("%s%3d", BroadcastType.BS.getHeader(),servive_id);\r
+                               }\r
+                               else if ( original_network_id == 6 || original_network_id == 7 ) {\r
+                                       station_id = String.format("%s%d", BroadcastType.CS.getHeader(),servive_id);\r
+                               }\r
+                               //else if ( 0x7880 <= original_network_id && original_network_id <= 0x7FE8 ) {\r
+                               else {\r
+                                       station_id = String.format("%s%05X", BroadcastType.TERRA.getHeader(),servive_id);\r
+                               }\r
+                               return true;\r
+                       }\r
+                       catch ( Exception e ) {\r
+                               e.printStackTrace();\r
+                       }\r
+               }\r
+               return false;\r
+       }\r
+       \r
+       public static boolean decodeContentId(String cId) {\r
+               if ( isValid(cId) ) {\r
+                       try {\r
+                               String xId = stripMark(cId);\r
+                               if ( ! decodeChId(xId) ) {\r
+                                       return false;\r
+                               }\r
+                               event_id = Integer.decode("0x"+xId.substring(chid_len,chid_len+evid_len));\r
+                               return true;\r
+                       }\r
+                       catch ( Exception e ) {\r
+                               e.printStackTrace();\r
+                       }\r
+               }\r
+               return false;\r
+       }\r
+}\r
diff --git a/TinyBannavi/src/tainavi/ContentIdREGZA.java b/TinyBannavi/src/tainavi/ContentIdREGZA.java
new file mode 100644 (file)
index 0000000..480d5cc
--- /dev/null
@@ -0,0 +1,52 @@
+package tainavi;\r
+\r
+import java.util.regex.Matcher;\r
+import java.util.regex.Pattern;\r
+\r
+/**\r
+ * EpgDataCap_BonプラグインとEDCBプラグインで利用するコンテンツIDを操作するstaticメソッドの集合\r
+ */\r
+public class ContentIdREGZA extends ContentIdEDCB {\r
+\r
+       public static final String CIDMARK = "REGZA$";\r
+       private static final int chid_len = 12;\r
+       private static final int evid_len = 4;\r
+\r
+       public static boolean isValid(String cId) {\r
+               return (cId!=null)?(cId.startsWith(CIDMARK) && cId.length()>=CIDMARK.length()+chid_len+evid_len):(false);\r
+       }\r
+       \r
+       public static String stripMark(String edcbid) { return edcbid.substring(CIDMARK.length()); }\r
+       \r
+       /**\r
+        * 10進表記のChIDを16進表記に変更\r
+        */\r
+       public static String getChId(String str) {\r
+               Matcher ma = Pattern.compile("^(\\d+):(\\d+):(\\d+)$").matcher(str);\r
+               if ( ma.find() ) {\r
+                       return String.format("%04X%04X%04X", Integer.valueOf(ma.group(2)), 0, Integer.valueOf(ma.group(3)));\r
+               }\r
+               return null;\r
+       }\r
+       \r
+       public static String getContentId(String chid, int evid) {\r
+               return String.format("%s%s%04X",CIDMARK,chid,evid);\r
+       }\r
+       \r
+       public static boolean decodeContentId(String cId) {\r
+               if ( isValid(cId) ) {\r
+                       try {\r
+                               String xId = stripMark(cId);\r
+                               if ( ! decodeChId(xId) ) {\r
+                                       return false;\r
+                               }\r
+                               event_id = Integer.decode("0x"+xId.substring(chid_len,chid_len+evid_len));\r
+                               return true;\r
+                       }\r
+                       catch ( Exception e ) {\r
+                               e.printStackTrace();\r
+                       }\r
+               }\r
+               return false;\r
+       }\r
+}\r
diff --git a/TinyBannavi/src/tainavi/ContentIdSyobo.java b/TinyBannavi/src/tainavi/ContentIdSyobo.java
new file mode 100644 (file)
index 0000000..2e2a4ae
--- /dev/null
@@ -0,0 +1,47 @@
+package tainavi;\r
+\r
+import java.util.regex.Matcher;\r
+import java.util.regex.Pattern;\r
+\r
+/**\r
+ * しょぼかるの。\r
+ */\r
+public class ContentIdSyobo {\r
+\r
+       public static final String CIDMARK = "SYOBO$";\r
+       \r
+       private static int tid = 0;\r
+       private static int pid = 0;\r
+       \r
+       private static String EDCBID = null;\r
+\r
+       public static boolean isValid(String link) {\r
+               return (link!=null)?(link.matches(".*/tid/\\d+#\\d+$")):(false);\r
+       } \r
+\r
+       public static int getTId() { return tid; }\r
+       public static int getPId() { return pid; }\r
+\r
+       public static String getContentId(int tid, int pid) {\r
+               return String.format("%s%d,%d",CIDMARK,tid,pid);\r
+       }\r
+\r
+       public static boolean decodeContentId(String link) {\r
+               if ( isValid(link) ) {\r
+                       Matcher ma = Pattern.compile("/tid/(\\d+)#(\\d+)$").matcher(link);\r
+                       if ( ma.find() ) {\r
+                               try {\r
+                                       tid = Integer.valueOf(ma.group(1));\r
+                                       pid = Integer.valueOf(ma.group(2));\r
+                                       return true;\r
+                               }\r
+                               catch (Exception e) {\r
+                                       e.printStackTrace();\r
+                               }\r
+                       }\r
+               }\r
+               tid = 0;\r
+               pid = 0;\r
+               return false;\r
+       }\r
+}\r
diff --git a/TinyBannavi/src/tainavi/DashBorder.java b/TinyBannavi/src/tainavi/DashBorder.java
new file mode 100644 (file)
index 0000000..2c7d093
--- /dev/null
@@ -0,0 +1,67 @@
+package tainavi;\r
+\r
+import java.awt.Color;\r
+import java.awt.Component;\r
+import java.awt.Graphics;\r
+import java.awt.Insets;\r
+\r
+import javax.swing.border.Border;\r
+\r
+public class DashBorder implements Border {\r
+       \r
+       private Color color = Color.RED;\r
+       private int thickness = 3;\r
+       private int length = 6;\r
+       private int space = 4;\r
+       \r
+       public DashBorder(Color color, int thickness, int length, int space) {\r
+               this.color = color;\r
+               this.thickness = thickness;\r
+               this.length = length;\r
+               this.space = space;\r
+       }\r
+       \r
+       @Override\r
+       public boolean isBorderOpaque() {\r
+               return false;\r
+       }\r
+       \r
+       @Override\r
+       public Insets getBorderInsets(Component c) {\r
+               return new Insets(thickness,thickness,thickness,thickness);\r
+       }\r
+\r
+       @Override\r
+       public void paintBorder(Component c, Graphics g, int x, int y, int width, int height) {\r
+               g.setColor(color);\r
+               for (int i = 0; i < width; i += length) {\r
+                       g.fillRect(i, 0, length, thickness);\r
+                       g.fillRect(i, (height-0)-thickness, length, thickness);\r
+                       i += space;\r
+               }\r
+               for (int i = 0; i < height; i += length) {\r
+                       g.fillRect(0, i, thickness, length);\r
+                       g.fillRect((width-0)-thickness, i, thickness, length);\r
+                       i += space;\r
+               }\r
+       }\r
+       \r
+       //\r
+       \r
+       public Color getDashColor() {\r
+               return color;\r
+       }\r
+       \r
+       public void setDashColor(Color color) {\r
+               this.color = color;\r
+       }\r
+       \r
+       public int getThickness() {\r
+               return thickness;\r
+       }\r
+       \r
+       public void setThickness(int thickness) {\r
+               this.thickness = thickness;\r
+       }\r
+\r
+}\r
diff --git a/TinyBannavi/src/tainavi/DebugPrintStream.java b/TinyBannavi/src/tainavi/DebugPrintStream.java
new file mode 100644 (file)
index 0000000..1d570a5
--- /dev/null
@@ -0,0 +1,108 @@
+package tainavi;\r
+\r
+import java.io.File;\r
+import java.io.FileNotFoundException;\r
+import java.io.FileOutputStream;\r
+import java.io.OutputStream;\r
+import java.io.PrintStream;\r
+\r
+public class DebugPrintStream extends PrintStream {\r
+\r
+       private PrintStream cw = null;\r
+       private String logname = null;\r
+       \r
+       private final long switchSize = 5*1024*1024;\r
+       \r
+       public DebugPrintStream(OutputStream out, String logfile, boolean fileOut) {\r
+               super(out);\r
+               /*\r
+               try {\r
+                       if ( CommonUtils.isWindows() ) {\r
+                               cw = new PrintStream(out,true,"MS932");\r
+                       }\r
+                       else {\r
+                               cw = new PrintStream(out);\r
+                       }\r
+                       if (fileOut) {\r
+                               logname = logfile;\r
+                       }\r
+               } catch (UnsupportedEncodingException e) {\r
+                       // TODO Auto-generated catch block\r
+                       e.printStackTrace();\r
+               }\r
+               */\r
+               cw = new PrintStream(out);\r
+               if (fileOut) {\r
+                       logname = logfile;\r
+               }\r
+       }\r
+       \r
+       @Override\r
+       public PrintStream printf(String format, Object ... args) {\r
+               try {\r
+                       PrintStream fw = logOpen();\r
+                       if (fw != null) {\r
+                               fw.printf(format, args);\r
+                               fw.close();\r
+                       }\r
+               } catch (FileNotFoundException e) {\r
+                       // TODO Auto-generated catch block\r
+                       cw.println(e.toString());\r
+               }\r
+               \r
+               return cw.printf(format, args);\r
+       }\r
+       \r
+       @Override\r
+       public void println(String x) {\r
+               try {\r
+                       PrintStream fw = logOpen();\r
+                       if (fw != null) {\r
+                               fw.println(x);\r
+                               fw.close();\r
+                       }\r
+               } catch (FileNotFoundException e) {\r
+                       // TODO Auto-generated catch block\r
+                       cw.println(e.toString());\r
+               }\r
+               \r
+               cw.println(x);\r
+       }\r
+       \r
+       @Override\r
+       public void write(byte[] buf, int off, int len) {\r
+               try {\r
+                       PrintStream fw = logOpen();\r
+                       if (fw != null) {\r
+                               fw.write(buf,off,len);\r
+                               fw.close();\r
+                       }\r
+               } catch (FileNotFoundException e) {\r
+                       // TODO Auto-generated catch block\r
+                       cw.println(e.toString());\r
+               }\r
+\r
+               cw.write(buf,off,len);\r
+       }\r
+       \r
+       private PrintStream logOpen() throws FileNotFoundException {\r
+               if (logname == null)\r
+                       return null;\r
+               \r
+               File f = new File(logname);\r
+               if ( f.exists() && f.length() >= switchSize ) {\r
+                       // 削除\r
+                       File o = new File(logname+".bak");\r
+                       o.delete();\r
+                       \r
+                       // 退避\r
+                       f.renameTo(o);\r
+               }\r
+               \r
+               FileOutputStream fos = new FileOutputStream(f,true);\r
+               PrintStream ps = new PrintStream(fos); \r
+               \r
+               // 追記or新規\r
+               return ps;\r
+       }\r
+}\r
diff --git a/TinyBannavi/src/tainavi/DebugShowTat.java b/TinyBannavi/src/tainavi/DebugShowTat.java
new file mode 100644 (file)
index 0000000..da3b890
--- /dev/null
@@ -0,0 +1,13 @@
+package tainavi;\r
+\r
+public class DebugShowTat {\r
+       private long start,stop;\r
+       \r
+       public void start() {\r
+               start = System.nanoTime();\r
+       }\r
+       public void stop(String s) {\r
+               stop = System.nanoTime();\r
+               System.err.println(s+" [DEBUG] tat="+(stop-start));\r
+       }\r
+}\r
diff --git a/TinyBannavi/src/tainavi/DumpHttp.java b/TinyBannavi/src/tainavi/DumpHttp.java
new file mode 100644 (file)
index 0000000..c390279
--- /dev/null
@@ -0,0 +1,96 @@
+package tainavi;\r
+\r
+import java.io.File;\r
+\r
+/**\r
+ * log.txtにダラーっと吐くとあとで分離するのにすごい苦労するのでリクエストごとに分離するようにしてみた。大したものではない。\r
+ */\r
+public class DumpHttp {\r
+\r
+       public void setCounterMax(int n) { COUNTERMAX = n; }\r
+       private int COUNTERMAX = 800;\r
+       \r
+       private static int counter = -1;        // クラス間共通である。多重アクセスはありえないので排他はしない\r
+       \r
+       private String DUMPDIR = "debug"; \r
+       private String DUMPDIRALT = DUMPDIR+"alt";\r
+       private final String cname = "counter.txt";\r
+       \r
+       private String request = null;\r
+       private String header = null;\r
+       private String body = null;\r
+\r
+       public DumpHttp() {\r
+               \r
+       }\r
+       \r
+       public DumpHttp(String s) {\r
+               DUMPDIR = s;\r
+               DUMPDIRALT = s+"alt";\r
+       }\r
+\r
+       /**\r
+        * \r
+        * @param s\r
+        */\r
+       public void request(String s) {\r
+               request = s;\r
+       }\r
+       \r
+       public void res_header(String s) {\r
+               header = s;\r
+       }\r
+       \r
+       public String res_body(String s) {\r
+               body = s;\r
+               \r
+               return close();\r
+       }\r
+       \r
+       private String close() {\r
+               \r
+               String dir = DUMPDIR;\r
+               if ( ! new File(dir).exists() ) {\r
+                       new File(dir).mkdir();\r
+               }\r
+               else {\r
+                       if ( ! new File(dir).isDirectory() ) {\r
+                               // りとらいするお\r
+                               dir = DUMPDIRALT;\r
+                               if ( ! new File(dir).exists() ) {\r
+                                       new File(dir).mkdir();\r
+                               }\r
+                               else if ( ! new File(dir).isDirectory() ) {\r
+                                       return null;    // こりゃだめだ\r
+                               }\r
+                       }\r
+               }\r
+\r
+               if ( counter == -1 ) {\r
+                       counter = CommonUtils.loadCnt(dir+File.separator+cname);\r
+               }\r
+               \r
+               counter = (counter+1) % COUNTERMAX;\r
+               \r
+               CommonUtils.saveCnt(counter, dir+File.separator+cname);\r
+               \r
+               String fname = String.format("%04d.htm", counter);\r
+               String fpath = dir+File.separator+fname;\r
+               \r
+               StringBuilder sb = new StringBuilder();\r
+               sb.append("<!-- "+CommonUtils.getDateTime(0)+" -->\n");\r
+               sb.append("<!--\n"+request+"\n-->\n");\r
+               sb.append("<!--\n"+header+"\n-->\n");\r
+               sb.append(body);\r
+               String s = sb.toString();\r
+               \r
+               request = header = body = null;\r
+               \r
+               if ( ! CommonUtils.write2file(fpath, s) ) {\r
+                       return null;\r
+               }\r
+               \r
+               return fname;\r
+       }\r
+       \r
+}\r
diff --git a/TinyBannavi/src/tainavi/EditorColumn.java b/TinyBannavi/src/tainavi/EditorColumn.java
new file mode 100644 (file)
index 0000000..ce65a23
--- /dev/null
@@ -0,0 +1,55 @@
+package tainavi;\r
+\r
+import java.awt.Component;\r
+import java.awt.event.FocusAdapter;\r
+import java.awt.event.FocusEvent;\r
+\r
+import javax.swing.AbstractCellEditor;\r
+import javax.swing.JTable;\r
+import javax.swing.JTextField;\r
+import javax.swing.table.TableCellEditor;\r
+\r
+/**\r
+ * カラムの編集\r
+ */\r
+public class EditorColumn extends AbstractCellEditor implements TableCellEditor {\r
+\r
+       private static final long serialVersionUID = 1L;\r
+\r
+       private final JTextFieldWithPopup editorField = new JTextFieldWithPopup();\r
+\r
+       public JTextField getTextField() { return editorField; }\r
+       \r
+       public EditorColumn() {\r
+               \r
+               super();\r
+               \r
+               editorField.addFocusListener(new FocusAdapter() {\r
+                       \r
+                       @Override\r
+                       public void focusGained(FocusEvent e) {\r
+                               \r
+                               editorField.selectAll();\r
+                               \r
+                       }\r
+                       \r
+               });\r
+               \r
+       }\r
+       \r
+       @Override\r
+       public Object getCellEditorValue() {\r
+               \r
+               return editorField.getText();\r
+               \r
+       }\r
+\r
+       @Override\r
+       public Component getTableCellEditorComponent(JTable arg0, Object arg1, boolean arg2, int arg3, int arg4) {\r
+               \r
+               editorField.setText((String)arg1);\r
+               return editorField;\r
+               \r
+       }\r
+       \r
+}
\ No newline at end of file
diff --git a/TinyBannavi/src/tainavi/EditorComboColumn.java b/TinyBannavi/src/tainavi/EditorComboColumn.java
new file mode 100644 (file)
index 0000000..cef1510
--- /dev/null
@@ -0,0 +1,46 @@
+package tainavi;\r
+\r
+import java.awt.Component;\r
+import java.awt.event.FocusAdapter;\r
+import java.awt.event.FocusEvent;\r
+\r
+import javax.swing.AbstractCellEditor;\r
+import javax.swing.JComboBox;\r
+import javax.swing.JTable;\r
+import javax.swing.JTextField;\r
+import javax.swing.table.TableCellEditor;\r
+\r
+public class EditorComboColumn extends AbstractCellEditor implements TableCellEditor {\r
+\r
+       private static final long serialVersionUID = 1L;\r
+\r
+       private final JComboBoxWithPopup editorCombo;\r
+\r
+       public EditorComboColumn() {\r
+               \r
+               super();\r
+               \r
+               editorCombo = new JComboBoxWithPopup();\r
+               editorCombo.setEditable(true);\r
+               editorCombo.addFocusListener(new FocusAdapter() {\r
+                       @Override\r
+                       public void focusGained(FocusEvent e) {\r
+                               ((JTextField) editorCombo.getEditor().getEditorComponent()).selectAll();        // 効かない?\r
+                       }\r
+               });\r
+       }\r
+       \r
+       public JComboBox getComboBox() { return editorCombo; }\r
+       \r
+       @Override\r
+       public Object getCellEditorValue() {\r
+               return editorCombo.getEditor().getItem();\r
+       }\r
+\r
+       @Override\r
+       public Component getTableCellEditorComponent(JTable arg0, Object arg1, boolean arg2, int arg3, int arg4) {\r
+               editorCombo.setSelectedItem((String) arg1);\r
+               return editorCombo;\r
+       }\r
+\r
+}\r
diff --git a/TinyBannavi/src/tainavi/EncryptPassword.java b/TinyBannavi/src/tainavi/EncryptPassword.java
new file mode 100644 (file)
index 0000000..40fab99
--- /dev/null
@@ -0,0 +1,35 @@
+package tainavi;\r
+\r
+import javax.crypto.Cipher;\r
+import javax.crypto.SecretKey;\r
+import javax.crypto.spec.SecretKeySpec;\r
+\r
+public class EncryptPassword {\r
+\r
+       private static final String encKey = "TheWorldofNull-A";\r
+       private static final SecretKey makeKey = new SecretKeySpec(encKey.getBytes(), "AES");\r
+       \r
+       // 暗号化\r
+       public static byte[] enc(String src) {\r
+               try {\r
+                   Cipher cipher = Cipher.getInstance(makeKey.getAlgorithm()+"/ECB/PKCS5Padding");\r
+                   cipher.init(Cipher.ENCRYPT_MODE, makeKey);\r
+                       return cipher.doFinal(src.getBytes());\r
+               } catch (Exception e) {\r
+                       e.printStackTrace();\r
+               }\r
+               return null;\r
+       }\r
+       \r
+       // 復号化\r
+       public static String dec(byte[] src) {\r
+               try {\r
+                   Cipher cipher = Cipher.getInstance(makeKey.getAlgorithm()+"/ECB/PKCS5Padding");\r
+                   cipher.init(Cipher.DECRYPT_MODE, makeKey);\r
+                       return new String(cipher.doFinal(src));\r
+               } catch (Exception e) {\r
+                       e.printStackTrace();\r
+               }\r
+               return null;\r
+       }       \r
+}\r
diff --git a/TinyBannavi/src/tainavi/Env.java b/TinyBannavi/src/tainavi/Env.java
new file mode 100644 (file)
index 0000000..ea1249b
--- /dev/null
@@ -0,0 +1,1130 @@
+package tainavi;\r
+\r
+import java.awt.Color;\r
+import java.awt.RenderingHints;\r
+import java.io.File;\r
+import java.util.ArrayList;\r
+import java.util.HashMap;\r
+import java.util.regex.Matcher;\r
+import java.util.regex.Pattern;\r
+\r
+\r
+/**\r
+ *\r
+ * @version 3.15.4β {@link #getAutoEventIdComplete()} 追加\r
+ */\r
+public class Env {\r
+\r
+       /*******************************************************************************\r
+        * 定数とか\r
+        ******************************************************************************/\r
+       \r
+       // 変更できないフォルダ\r
+       private static final String binDir = "bin";\r
+       private static final String metainfDir = binDir+File.separator+"META-INF";\r
+       private static final String services = metainfDir+File.separator+"services";\r
+       \r
+       private static final String envDir = "env";\r
+       private static final String envFile = envDir+File.separator+"envs.xml";\r
+       private static final String envText = envDir+File.separator+"envs.txt";\r
+       private static final String lafFile = envDir+File.separator+"laf.txt";\r
+       private static final String GTKRC = envDir+File.separator+"_gtkrc-2.0";\r
+       \r
+       private static final String skinDir = "skin";\r
+       \r
+       private static final int PAGER_DEFAULT = 7;\r
+       \r
+       private static final Color RSVDLINE_COLOR = new Color(204,153,255);\r
+       private static final Color PICKEDLINE_COLOR = new Color(51,255,0);\r
+       private static final Color CURRENTLINE_COLOR = new Color(240,120,120);\r
+       private static final Color MATCHEDKEYWORD_COLOR = new Color(51,102,255);\r
+       \r
+       private static final Color TIMEBAR_COLOR_06_12 = new Color(180,180,180);\r
+       private static final Color TIMEBAR_COLOR_12_18 = new Color(160,160,160);\r
+       private static final Color TIMEBAR_COLOR_18_24 = new Color(140,140,140);\r
+       private static final Color TIMEBAR_COLOR_24_06 = new Color(120,120,120);\r
+       private static final Color EXECON_TUNE_COLOR = Color.WHITE;\r
+       private static final Color EXECOFF_TUNE_COLOR = Color.GRAY;\r
+       private static final Color PICKUP_BORDER_COLOR = new Color(0,255,0);\r
+       private static final Color PICKUP_TUNER_COLOR = Color.GRAY;\r
+       private static final Color MOUSEOVER_COLOR = new Color(180,180,255);\r
+       private static final Color TITLE_COLOR = Color.BLUE;\r
+       private static final Color DETAIL_COLOR = Color.DARK_GRAY;\r
+       private static final Color MATCHBORDER_COLOR = Color.RED;\r
+       private static final Color ITERATIONITEM_COLOR = Color.GRAY;\r
+       \r
+       private static final Color TUNERSHORT_COLOR = new Color(255,255,0);\r
+       private static final Color RECORDED_COLOR = new Color(204,153,255);\r
+       \r
+       private static final String USER_AGENT = "Mozilla/5.0 (Windows NT 6.1; WOW64; rv:2.0.1) Gecko/20100101 Firefox/4.0.1";\r
+       \r
+       // この記述は…どうなの?\r
+       private static final String TV_SITE = "Dimora";\r
+       private static final String CS_SITE = "スカパー!";\r
+       private static final String CS2_SITE = "webザテレビジョン(CSデジ)";\r
+       private static final String RADIO_SITE = "";\r
+       \r
+       private static final String MSGID = "[環境] ";\r
+       private static final String ERRID = "[ERROR]"+MSGID;\r
+       \r
+       public static String[] SHUTDOWN_COMMANDS = { "shutdown /s /t 0", "sudo shutdown -h now" };\r
+       \r
+       // ダブルクリック時の動作\r
+       public static enum DblClkCmd {\r
+               \r
+               SHOWRSVDIALOG ("予約ダイアログを開く"),\r
+               JUMPTOPAPER ("番組欄へジャンプする"),\r
+               JUMPTOWEB ("ブラウザで番組詳細のページを開く");\r
+               \r
+               private String name;\r
+               \r
+               private DblClkCmd(String name) {\r
+                       this.name = name;\r
+               }\r
+               \r
+               @Override\r
+               public String toString() {\r
+                       return(name);\r
+               }\r
+               \r
+               public String getId() {\r
+                       return(super.toString());\r
+               }\r
+               \r
+               public static DblClkCmd get(String id) {\r
+                       for ( DblClkCmd dcc : DblClkCmd.values() ) {\r
+                               if ( dcc.getId().equals(id) ) {\r
+                                       return dcc;\r
+                               }\r
+                       }\r
+                       return null;\r
+               }\r
+       };\r
+\r
+       //\r
+       public static enum SnapshotFmt {\r
+               JPG ("JPEG","jpg"),\r
+               PNG ("PNG","png"),\r
+               BMP ("BMP","bmp");\r
+               \r
+               private String name;\r
+               private String ext;\r
+               \r
+               private SnapshotFmt(String name, String ext) {\r
+                       this.name = name;\r
+                       this.ext = ext;\r
+               }\r
+               \r
+               @Override\r
+               public String toString() {\r
+                       return(name);\r
+               }\r
+\r
+               public String getExtension() {\r
+                       return(ext);\r
+               }\r
+               \r
+               \r
+               public String getId() {\r
+                       return(super.toString());\r
+               }\r
+\r
+               public static SnapshotFmt get(String id) {\r
+                       for ( SnapshotFmt sf : SnapshotFmt.values() ) {\r
+                               if ( sf.getId().equals(id) ) {\r
+                                       return sf;\r
+                               }\r
+                       }\r
+                       return null;\r
+               }\r
+       }\r
+       \r
+       public static enum AAMode {\r
+               OFF             ("無効","off",RenderingHints.VALUE_TEXT_ANTIALIAS_OFF),\r
+               ON              ("有効","on",RenderingHints.VALUE_TEXT_ANTIALIAS_ON),\r
+               GASP    ("小さい文字には無効", "gasp",RenderingHints.VALUE_TEXT_ANTIALIAS_GASP),\r
+               LCD             ("サブピクセルAA", "lcd",RenderingHints.VALUE_TEXT_ANTIALIAS_LCD_HRGB),\r
+               HRGB    ("サブピク横方向RGB", "lcd_hrgb",RenderingHints.VALUE_TEXT_ANTIALIAS_LCD_HRGB),\r
+               HBGR    ("サブピク横方向BGR", "lcd_hbgr",RenderingHints.VALUE_TEXT_ANTIALIAS_LCD_HBGR),\r
+               VRGB    ("サブピク縦方向RGB", "lcd_vrgb",RenderingHints.VALUE_TEXT_ANTIALIAS_LCD_VRGB),\r
+               VBGR    ("サブピク縦方向BGR", "lcd_vbgr",RenderingHints.VALUE_TEXT_ANTIALIAS_LCD_VBGR);\r
+               \r
+               private String name;\r
+               private String mode;\r
+               private Object hint;\r
+               \r
+               private AAMode(String name, String mode, Object hint) {\r
+                       this.name = name;\r
+                       this.mode = mode;\r
+                       this.hint = hint;\r
+               }\r
+               \r
+               @Override\r
+               public String toString() {\r
+                       return name;\r
+               }\r
+               \r
+               public String getId() {\r
+                       return(super.toString());\r
+               }\r
+               \r
+               public String getMode() {\r
+                       return mode;\r
+               }\r
+\r
+               public Object getHint() {\r
+                       return hint;\r
+               }\r
+               \r
+               public static AAMode get(String id) {\r
+                       for ( AAMode aam : AAMode.values() ) {\r
+                               if ( aam.getId().equals(id) ) {\r
+                                       return aam;\r
+                               }\r
+                       }\r
+                       return null;\r
+               }\r
+       }\r
+       \r
+       // 自動アップデートの間隔\r
+       public static enum UpdateOn {\r
+               DISABLE ("無効にする", -1),\r
+               EVERYDAY ("毎日実行する", 0),\r
+               EVERY1DAY ("1日おきに実行する", 1),\r
+               EVERY2DAY ("2日おきに実行する", 2),\r
+               EVERY3DAY ("3日おきに実行する", 3),\r
+               EVERY4DAY ("4日おきに実行する", 4),\r
+               EVERY5DAY ("5日おきに実行する", 5),\r
+               EVERY6DAY ("6日おきに実行する", 6);\r
+               \r
+               private String name;\r
+               private int interval;\r
+               \r
+               private UpdateOn(String name, int interval) {\r
+                       this.name = name;\r
+                       this.interval = interval;\r
+               }\r
+               \r
+               @Override\r
+               public String toString() {\r
+                       return this.name;\r
+               }\r
+\r
+               public String getId() {\r
+                       return(super.toString());\r
+               }\r
+\r
+               public int getInterval() {\r
+                       return this.interval;\r
+               }\r
+               \r
+               public static UpdateOn get(String id) {\r
+                       for ( UpdateOn uo : UpdateOn.values() ) {\r
+                               if ( uo.getId().equals(id) ) {\r
+                                       return uo;\r
+                               }\r
+                       }\r
+                       return null;\r
+               }\r
+       }\r
+       \r
+       /*******************************************************************************\r
+        * 隠し\r
+        ******************************************************************************/\r
+       \r
+       private static boolean firstinstance = true;    // 起動後最初の呼び出しか(2インスタンス目以降は実行しない)\r
+       \r
+       // CHソートを有効にするかどうか\r
+       public boolean getChSortEnabled() { return chSortEnabled; }\r
+       public void setChSortEnabled(boolean b) { chSortEnabled = b; }\r
+       private boolean chSortEnabled = true;\r
+       \r
+       \r
+       /*******************************************************************************\r
+        * メンバ\r
+        ******************************************************************************/\r
+       \r
+       /*\r
+        * リスト形式関連の設定\r
+        */\r
+\r
+       // 番組追跡であいまい検索をしない\r
+       public boolean getDisableFazzySearch() { return disableFazzySearch; }\r
+       public void setDisableFazzySearch(boolean b) { disableFazzySearch = b; }\r
+       private boolean disableFazzySearch = false;\r
+       // あいまい検索で逆引きはしない\r
+       public boolean getDisableFazzySearchReverse() { return disableFazzySearchReverse; }\r
+       public void setDisableFazzySearchReverse(boolean b) { disableFazzySearchReverse = b; }\r
+       private boolean disableFazzySearchReverse = false;\r
+       // あいまい検索の閾値のデフォルト値\r
+       public int getDefaultFazzyThreshold() { return defaultFazzyThreshold; }\r
+       public void setDefaultFazzyThreshold(int c) { defaultFazzyThreshold = c; }\r
+       private int defaultFazzyThreshold = TraceKey.defaultFazzyThreshold;\r
+       // しょぼかるの検索結果も有効な放送局のみに絞る\r
+       public boolean getSyoboFilterByCenters() { return syoboFilterByCenters; }\r
+       public void setSyoboFilterByCenters(boolean b) { syoboFilterByCenters = b; }\r
+       private boolean syoboFilterByCenters = true;\r
+       // 終了済みエントリも表示する\r
+       public boolean getDisplayPassedEntry() { return displayPassedEntry; }\r
+       public void setDisplayPassedEntry(boolean b) { displayPassedEntry = b; }\r
+       private boolean displayPassedEntry = true;\r
+       // 裏番組予約マークの表示\r
+       public boolean getShowRsvPickup() { return showRsvPickup; }\r
+       public void setShowRsvPickup(boolean b) { showRsvPickup = b; }\r
+       private boolean showRsvPickup = true;\r
+       // 裏番組予約マークの表示\r
+       public boolean getShowRsvUra() { return showRsvUra; }\r
+       public void setShowRsvUra(boolean b) { showRsvUra = b; }\r
+       private boolean showRsvUra = false;\r
+       // 予約行の背景色を変えて強調する\r
+       public boolean getRsvdLineEnhance() { return rsvdLineEnhance; }\r
+       public void setRsvdLineEnhance(boolean b) { rsvdLineEnhance = b; }\r
+       private boolean rsvdLineEnhance = true;\r
+       // 予約行の背景色\r
+       public Color getRsvdLineColor() { return rsvdLineColor; }\r
+       public void setRsvdLineColor(Color c) { rsvdLineColor = c; }\r
+       private Color rsvdLineColor = RSVDLINE_COLOR;\r
+       // ピックアップ行の背景色\r
+       public Color getPickedLineColor() { return pickedLineColor; }\r
+       public void setPickedLineColor(Color c) { pickedLineColor = c; }\r
+       private Color pickedLineColor = PICKEDLINE_COLOR;\r
+       // 現在放送中行の背景色を変えて強調する\r
+       public boolean getCurrentLineEnhance() { return currentLineEnhance; }\r
+       public void setCurrentLineEnhance(boolean b) { currentLineEnhance = b; }\r
+       private boolean currentLineEnhance = true;\r
+       // 現在放送中行の背景色\r
+       public Color getCurrentLineColor() { return currentLineColor; }\r
+       public void setCurrentLineColor(Color c) { currentLineColor = c; }\r
+       private Color currentLineColor = CURRENTLINE_COLOR;\r
+       // 現在放送中ノードに終了後何分までの番組を表示するか\r
+       public int getCurrentAfter() { return currentAfter; }\r
+       public void setCurrentAfter(int n) { currentAfter = n; }\r
+       private int currentAfter = 10*60;\r
+       // 現在放送中ノードに開始前何分までの番組を表示するか\r
+       public int getCurrentBefore() { return currentBefore; }\r
+       public void setCurrentBefore(int n) { currentBefore = n; }\r
+       private int currentBefore = 60*60;\r
+       // キーワードにマッチした箇所の強調色\r
+       public Color getMatchedKeywordColor() { return matchedKeywordColor; }\r
+       public void setMatchedKeywordColor(Color c) { matchedKeywordColor = c; }\r
+       private Color matchedKeywordColor = MATCHEDKEYWORD_COLOR;\r
+       // 削除時に確認ダイアログを表示する\r
+       public boolean getShowWarnDialog() { return showWarnDialog; }\r
+       public void setShowWarnDialog(boolean b) { showWarnDialog = b; }\r
+       private boolean showWarnDialog = false;\r
+       // マーク表示を個別欄に分離する\r
+       public boolean getSplitMarkAndTitle() { return splitMarkAndTitle; }\r
+       public void setSplitMarkAndTitle(boolean b) { splitMarkAndTitle = b; }\r
+       private boolean splitMarkAndTitle = true;\r
+       // 番組詳細も表示する\r
+       public boolean getShowDetailOnList() { return showDetailOnList; }\r
+       public void setShowDetailOnList(boolean b) { showDetailOnList = b; }\r
+       private boolean showDetailOnList = true;\r
+       // 予約待機の実予約番組自動選択数\r
+       public int getRsvTargets() { return rsvTargets; }\r
+       public void setRsvTargets(int c) { rsvTargets = c; }\r
+       private int rsvTargets = 32;\r
+       // 行ヘッダを表示する\r
+       public boolean getRowHeaderVisible() { return rowHeaderVisible; }\r
+       public void setRowHeaderVisible(boolean b) { rowHeaderVisible = b; }\r
+       private boolean rowHeaderVisible = false;\r
+       // ダブルクリック時の動作\r
+       public DblClkCmd getDblClkCmd() { return dblClkCmd; }\r
+       public void setDblClkCmd(DblClkCmd c) { dblClkCmd = c; }\r
+       private DblClkCmd dblClkCmd = DblClkCmd.SHOWRSVDIALOG;\r
+\r
+       // 過去ログ検索件数の上限\r
+       public int getSearchResultMax() { return searchResultMax; }\r
+       public void setSearchResultMax(int c) { searchResultMax = c; }\r
+       private int searchResultMax = 50;\r
+\r
+       // 過去ログ検索履歴の上限\r
+       public int getSearchResultBufferMax() { return searchResultBufferMax; }\r
+       public void setSearchResultBufferMax(int c) { searchResultBufferMax = c; }\r
+       private int searchResultBufferMax = 5;\r
+\r
+       /*\r
+        * 新聞形式関連の設定\r
+        */\r
+       \r
+       // 描画速度優先\r
+       public boolean getDrawcacheEnable() { return drawcacheEnable; }\r
+       public void setDrawcacheEnable(boolean b) { drawcacheEnable = b; }\r
+       private boolean drawcacheEnable = false;\r
+       // 新聞形式での、1ページあたりの放送局数\r
+       public int getCenterPerPage() { return centerPerPage; }\r
+       public void setCenterPerPage(int c) { centerPerPage = c; }\r
+       public boolean isPagerEnabled() { return ( ! drawcacheEnable) && (centerPerPage > 0); }\r
+       /**\r
+        * indexなので、0から始まる\r
+        */\r
+       public int getPageIndex(int n) {\r
+               int rem = n % centerPerPage;\r
+               return (n-rem)/centerPerPage + ((rem==0)?(0):(1)) - 1;\r
+       }\r
+       private int centerPerPage = PAGER_DEFAULT;\r
+       // 全ページを連続スナップショット\r
+       public boolean getAllPageSnapshot() { return allPageSnapshot; }\r
+       public void setAllPageSnapshot(boolean b) { allPageSnapshot = b; }\r
+       private boolean allPageSnapshot = false;\r
+       // 番組表のツールチップを表示する\r
+       public boolean getTooltipEnable() { return tooltipEnable; }\r
+       public void setTooltipEnable(boolean b) { tooltipEnable = b; }\r
+       private boolean tooltipEnable = false;\r
+       // ツールチップの表示遅延(100ミリ秒)\r
+       public int getTooltipInitialDelay() { return tooltipInitialDelay; }\r
+       public void setTooltipInitialDelay(int c) { tooltipInitialDelay = c; }\r
+       private int tooltipInitialDelay = 3;\r
+       // ツールチップの消去遅延(100ミリ秒)\r
+       public int getTooltipDismissDelay() { return tooltipDismissDelay; }\r
+       public void setTooltipDismissDelay(int c) { tooltipDismissDelay = c; }\r
+       private int tooltipDismissDelay = 30*10;\r
+       // タイムバーにあわせてスクロール\r
+       public boolean getTimerbarScrollEnable() { return timerbarScrollEnable; }\r
+       public void setTimerbarScrollEnable(boolean b) { timerbarScrollEnable = b; }\r
+       private boolean timerbarScrollEnable = true;\r
+       // 過去ログの日付ノードの表示数\r
+       public int getPassedLogLimit() { return passedLogLimit; }\r
+       public void setPassedLogLimit(int c) { passedLogLimit = c; }\r
+       private int passedLogLimit = 31;\r
+       // 局ごとの幅(->bounds)\r
+       // 番組枠の高さ(->bounds)\r
+       // タイムバーの色設定\r
+       public Color getTimebarColor() { return timebarColor; }\r
+       public void setTimebarColor(Color c) { timebarColor = c; }\r
+       private Color timebarColor = TIMEBAR_COLOR_06_12;\r
+       public Color getTimebarColor2() { return timebarColor2; }\r
+       public void setTimebarColor2(Color c) { timebarColor2 = c; }\r
+       private Color timebarColor2 = TIMEBAR_COLOR_12_18;\r
+       public Color getTimebarColor3() { return timebarColor3; }\r
+       public void setTimebarColor3(Color c) { timebarColor3 = c; }\r
+       private Color timebarColor3 = TIMEBAR_COLOR_18_24;\r
+       public Color getTimebarColor4() { return timebarColor4; }\r
+       public void setTimebarColor4(Color c) { timebarColor4 = c; }\r
+       private Color timebarColor4 = TIMEBAR_COLOR_24_06;\r
+       // 予約枠の色設定\r
+       public Color getExecOnFontColor() { return execOnFontColor; }\r
+       public void setExecOnFontColor(Color c) { execOnFontColor = c; }\r
+       private Color execOnFontColor = EXECON_TUNE_COLOR;\r
+       // 無効予約のフォント色\r
+       public Color getExecOffFontColor() { return execOffFontColor; }\r
+       public void setExecOffFontColor(Color c) { execOffFontColor = c; }\r
+       private Color execOffFontColor = EXECOFF_TUNE_COLOR;\r
+       // ピックアップ枠の色設定\r
+       public Color getPickedColor() { return pickedColor; }\r
+       public void setPickedColor(Color c) { pickedColor = c; }\r
+       private Color pickedColor = PICKUP_BORDER_COLOR;\r
+       public Color getPickedFontColor() { return pickedFontColor; }\r
+       public void setPickedFontColor(Color c) { pickedFontColor = c; }\r
+       private Color pickedFontColor = PICKUP_TUNER_COLOR;\r
+       // ジャンル別色設定(ここでは設定しない。PaperColorMapクラスで)\r
+       @Deprecated\r
+       public String getPaperColors() { return paperColors; }\r
+       @Deprecated\r
+       public void setPaperColors(String s) { paperColors = s; }\r
+       @Deprecated\r
+       private String paperColors = "";\r
+       // マウスオーバー時のハイライト色設定\r
+       public boolean getEnableHighlight() { return enableHighlight; }\r
+       public void setEnableHighlight(boolean b) { enableHighlight = b; }\r
+       private boolean enableHighlight = true;\r
+       public Color getHighlightColor() { return highlightColor; }\r
+       public void setHighlightColor(Color c) { highlightColor = c; }\r
+       private Color highlightColor = MOUSEOVER_COLOR;\r
+       // 番組枠の設定(titleFontStyleの初期化はコンストラクタで)\r
+       public boolean getShowStart() { return showStart; }\r
+       public void setShowStart(boolean b) { showStart = b; }\r
+       private boolean showStart = true;\r
+       public String getTitleFont() { return titleFont; }\r
+       public void setTitleFont(String s) { titleFont = s; }\r
+       private String titleFont = "";\r
+       public int getTitleFontSize() { return titleFontSize; }\r
+       public void setTitleFontSize(int n) { titleFontSize = n; }\r
+       private int titleFontSize = 13;\r
+       public Color getTitleFontColor() { return titleFontColor; }\r
+       public void setTitleFontColor(Color c) { titleFontColor = c; }\r
+       private Color titleFontColor = TITLE_COLOR;\r
+       public ArrayList<JTXTButton.FontStyle> getTitleFontStyle() { return titleFontStyle; }\r
+       public void setTitleFontStyle(ArrayList<JTXTButton.FontStyle> a) { titleFontStyle = a; }\r
+       private ArrayList<JTXTButton.FontStyle> titleFontStyle = new ArrayList<JTXTButton.FontStyle>();\r
+       public boolean getShowDetail() { return showDetail; }\r
+       public void setShowDetail(boolean b) { showDetail = b; }\r
+       private boolean showDetail = true;\r
+       public int getDetailRows() { return detailRows; }\r
+       public void setDetailRows(int n) { detailRows = n; }\r
+       private int detailRows = 50;\r
+       public String getDetailFont() { return detailFont; }\r
+       public void setDetailFont(String s) { detailFont = s; }\r
+       private String detailFont = "";\r
+       public int getDetailFontSize() { return detailFontSize; }\r
+       public void setDetailFontSize(int n) { detailFontSize = n; }\r
+       private int detailFontSize = 11;\r
+       public Color getDetailFontColor() { return detailFontColor; }\r
+       public void setDetailFontColor(Color c) { detailFontColor = c; }\r
+       private Color detailFontColor = DETAIL_COLOR;\r
+       public ArrayList<JTXTButton.FontStyle> getDetailFontStyle() { return detailFontStyle; }\r
+       public void setDetailFontStyle(ArrayList<JTXTButton.FontStyle> a) { detailFontStyle = a; }\r
+       private ArrayList<JTXTButton.FontStyle> detailFontStyle = new ArrayList<JTXTButton.FontStyle>();\r
+       public int getDetailTab() { return detailTab; }\r
+       public void setDetailTab(int n) { detailTab = n; }\r
+       private int detailTab = 8;\r
+       public Color getMatchedBorderColor() { return matchedBorderColor; }\r
+       public void setMatchedBorderColor(Color c) { matchedBorderColor = c; }\r
+       private Color matchedBorderColor = MATCHBORDER_COLOR;\r
+       public int getMatchedBorderThickness() { return matchedBorderThickness; }\r
+       public void setMatchedBorderThickness(int n) { matchedBorderThickness = n; }\r
+       private int matchedBorderThickness = 6;\r
+       // AAモード\r
+       public AAMode getPaperAAMode() { return paperAAMode; }\r
+       public void setPaperAAMode(AAMode a) { paperAAMode = a; }\r
+       private AAMode paperAAMode = AAMode.ON;\r
+       // 新聞形式でもレコーダーコンボボックスを有効にする\r
+       public boolean getEffectComboToPaper() { return effectComboToPaper; }\r
+       public void setEffectComboToPaper(boolean b) { effectComboToPaper = b; }\r
+       private boolean effectComboToPaper = true;\r
+       // スナップショットのフォーマット\r
+       public SnapshotFmt getSnapshotFmt() { return snapshotFmt; }\r
+       public void setSnapshotFmt(SnapshotFmt s) { snapshotFmt = s; }\r
+       private SnapshotFmt snapshotFmt = SnapshotFmt.JPG;\r
+       // スナップショットを印刷する\r
+       public boolean getPrintSnapshot() { return printSnapshot; }\r
+       public void setPrintSnapshot(boolean b) { printSnapshot = b; }\r
+       private boolean printSnapshot = false;\r
+       \r
+       /*\r
+        * リスト・新聞形式共通\r
+        */\r
+       \r
+       // 実行ONのエントリのみ予約マークを表示する\r
+       public boolean getDisplayOnlyExecOnEntry() { return displayOnlyExecOnEntry; }\r
+       public void setDisplayOnlyExecOnEntry(boolean b) { displayOnlyExecOnEntry = b; }\r
+       private boolean displayOnlyExecOnEntry = false;\r
+       // 終了済み予約も表示する\r
+       public boolean getDisplayPassedReserve() { return displayPassedReserve; }\r
+       public void setDisplayPassedReserve(boolean b) { displayPassedReserve = b; }\r
+       private boolean displayPassedReserve = false;\r
+       // リピート放送を表示しない\r
+       public boolean getShowOnlyNonrepeated() { return showOnlyNonrepeated; }\r
+       public void setShowOnlyNonrepeated(boolean b) { showOnlyNonrepeated = b; }\r
+       private boolean showOnlyNonrepeated = true;\r
+       // 深夜(24:00-28:59)の帯予約を一日前にずらす\r
+       public boolean getAdjLateNight() { return adjLateNight; }\r
+       public void setAdjLateNight(boolean b) { adjLateNight = b; }\r
+       private boolean adjLateNight = false;\r
+       // ツリーペーンにrootノードを表示させる\r
+       public boolean getRootNodeVisible() { return rootNodeVisible; }\r
+       public void setRootNodeVisible(boolean b) { rootNodeVisible = b; }\r
+       private boolean rootNodeVisible = false;\r
+       // ツリーペーンの幅を同期させる\r
+       public boolean getSyncTreeWidth() { return syncTreeWidth; }\r
+       public void setSyncTreeWidth(boolean b) { syncTreeWidth = b; }\r
+       private boolean syncTreeWidth = true;\r
+       // ★延長警告★を短縮表示\r
+       public boolean getShortExtMark() { return shortExtMark; }\r
+       public void setShortExtMark(boolean b) { shortExtMark = b; }\r
+       private boolean shortExtMark = false;\r
+       // 表示マークの選択\r
+       public HashMap<TVProgram.ProgOption,Boolean> getOptMarks() { return optMarks; }\r
+       public void setOptMarks(HashMap<TVProgram.ProgOption,Boolean> h) { optMarks = h;        }\r
+       private HashMap<TVProgram.ProgOption,Boolean> optMarks = new HashMap<TVProgram.ProgOption, Boolean>();\r
+       // クリップボードアイテムの選択(->clipboardItem)\r
+       // 右クリックメニューの実行アイテム\r
+       public ArrayList<TextValueSet> getTvCommand() { return tvCommand; }\r
+       public void setTvCommand(ArrayList<TextValueSet> al) { tvCommand = al; }\r
+       private ArrayList<TextValueSet> tvCommand = new ArrayList<TextValueSet>();\r
+       \r
+       /*\r
+        * Web番組表対応\r
+        */\r
+       \r
+       // 29時をまたいで同タイトルが続いている場合は同一番組とみなす\r
+       public boolean getContinueTomorrow() { return continueTomorrow; }\r
+       public void setContinueTomorrow(boolean b) { continueTomorrow = b; }\r
+       private boolean continueTomorrow = true;\r
+       // Web番組表のキャッシュ保持時間\r
+       public int getCacheTimeLimit() { return cacheTimeLimit; }\r
+       public void setCacheTimeLimit(int w) { cacheTimeLimit = w; }\r
+       public boolean isShutdownEnabled() { return (cacheTimeLimit == 0)&&(shutdownCmd!=null&&shutdownCmd.length()>0); }\r
+       private int cacheTimeLimit = 12;\r
+       // シャットダウンコマンド\r
+       public String getShutdownCmd() { return shutdownCmd; }\r
+       public void setShutdownCmd(String s) { shutdownCmd = s; }\r
+       private String shutdownCmd = "";\r
+       // 可能なら番組表8日分を取得する\r
+       public boolean getExpandTo8() { return expandTo8; }\r
+       public void setExpandTo8(boolean b) { expandTo8 = b; dogdays = (b)?(8):(7); }\r
+       private boolean expandTo8 = false;\r
+       public int getDogDays() { return dogdays; }\r
+       private int dogdays = 7;\r
+       // 番組詳細取得を行なうかどうか(廃止)\r
+       @Deprecated\r
+       public boolean getUseDetailCache() { return false; }\r
+       @Deprecated\r
+       public void setUseDetailCache(boolean b) { useDetailCache = false; }\r
+       @Deprecated\r
+       private boolean useDetailCache = false;\r
+       /**\r
+        *  予約ダイアログを開いたときに自動で番組IDを取得する\r
+        */\r
+       public boolean getAutoEventIdComplete() { return autoEventIdComplete; }\r
+       public void setAutoEventIdComplete(boolean b) { autoEventIdComplete = b; }\r
+       private boolean autoEventIdComplete = false;\r
+       // タイトルに話数が含まれる場合に以降を分離する\r
+       public boolean getSplitEpno() { return splitEpno; }\r
+       public void setSplitEpno(boolean b) { splitEpno = b; }\r
+       private boolean splitEpno = false;\r
+       // サブタイトルを番組追跡の対象から除外する\r
+       public boolean getTraceOnlyTitle() { return traceOnlyTitle; }\r
+       public void setTraceOnlyTitle(boolean b) { traceOnlyTitle = b; }\r
+       private boolean traceOnlyTitle = true;\r
+       // タイトルを整形する\r
+       public boolean getFixTitle() { return fixTitle; }\r
+       public void setFixTitle(boolean b) { fixTitle = b; }\r
+       private boolean fixTitle = true;\r
+       // NGワード\r
+       public ArrayList<String> getNgword() { return ngword; }\r
+       public void setNgword(ArrayList<String> ngw) { ngword = ngw; }\r
+       public void setNgword(String ngw) {\r
+               ngword = new ArrayList<String>();\r
+               for (String ngs : ngw.split(";")) {\r
+                       if (ngs.trim().length() > 0) {\r
+                               ngword.add(ngs.trim());\r
+                       }\r
+               }\r
+       }\r
+       private ArrayList<String> ngword = new ArrayList<String>();\r
+       // Web番組表をアクセスする際のUser-Agentの値\r
+       public String getUserAgent() { return userAgent; }\r
+       public void setUserAgent(String s) { userAgent = s; }\r
+       private String userAgent = USER_AGENT;\r
+       // HTTP Proxy\r
+       public boolean getUseProxy() { return useProxy; }\r
+       public void setUseProxy(boolean b) { useProxy = b; }\r
+       public String getProxyAddr() { return proxyAddr; }\r
+       public void setProxyAddr(String s) { proxyAddr = s; }\r
+       public String getProxyPort() { return proxyPort; }\r
+       public void setProxyPort(String s) { proxyPort = s; }\r
+       private boolean useProxy = false;\r
+       private String proxyAddr = "";\r
+       private String proxyPort = "";\r
+       // しょぼかるを利用する\r
+       public boolean getUseSyobocal() { return useSyobocal; }\r
+       public void setUseSyobocal(boolean b) { useSyobocal = b; }\r
+       private boolean useSyobocal = true;\r
+       // 日に一回しか新着履歴を更新しない\r
+       public boolean getHistoryOnlyUpdateOnce() { return historyOnlyUpdateOnce; }\r
+       public void setHistoryOnlyUpdateOnce(boolean b) { historyOnlyUpdateOnce = b; }\r
+       private boolean historyOnlyUpdateOnce = true;\r
+       // 過去ログを利用する\r
+       public boolean getUsePassedProgram() { return usePassedProgram; }\r
+       public void setUsePassedProgram(boolean b) { usePassedProgram = b; }\r
+       private boolean usePassedProgram = true;\r
+       // 何日先までのログを過去ログ用に準備しておくか\r
+       public int getPrepPassedProgramCount() { return prepPassedProgramCount; }\r
+       public void setPrepPassedProgramCount(int w) { prepPassedProgramCount = w; }\r
+       private int prepPassedProgramCount = 4;\r
+       \r
+       \r
+       \r
+       /*\r
+        * レコーダ対応\r
+        */\r
+       \r
+       // 常に予約詳細情報を取得する\r
+       public boolean getForceGetRdReserveDetails() { return forceGetRdReserveDetails; }\r
+       public void setForceGetRdReserveDetails(boolean b) { forceGetRdReserveDetails = b; }\r
+       private boolean forceGetRdReserveDetails = false;\r
+       // 常に予約詳細情報を取得しない\r
+       public boolean getNeverGetRdReserveDetails() { return neverGetRdReserveDetails; }\r
+       public void setNeverGetRdReserveDetails(boolean b) { neverGetRdReserveDetails = b; }\r
+       private boolean neverGetRdReserveDetails = false;\r
+       // 予約取得ボタンで録画結果も同時に取得する\r
+       public boolean getSkipGetRdRecorded() { return skipGetRdRecorded; }\r
+       public void setSkipGetRdRecorded(boolean b) { skipGetRdRecorded = b; }\r
+       private boolean skipGetRdRecorded = false;\r
+       // 録画結果一覧の保存期間\r
+       public void setRecordedSaveScope(int n) { recordedSaveScope = n; }\r
+       public int getRecordedSaveScope() { return recordedSaveScope; }\r
+       private int recordedSaveScope = 90;\r
+       \r
+       \r
+       //\r
+       @Deprecated\r
+       public boolean getDontCalendarWorkWithNull() { return dontCalendarWorkWithNull; }\r
+       @Deprecated\r
+       public void setDontCalendarWorkWithNull(boolean b) { dontCalendarWorkWithNull = b; }\r
+       @Deprecated\r
+       boolean dontCalendarWorkWithNull = true;\r
+       \r
+       /*\r
+        * 予約\r
+        */\r
+       \r
+       // スポーツ延長の長さ(分) \r
+       public String getSpoexLength() { return spoexLength; }\r
+       public void setSpoexLength(String l) { spoexLength = l; }\r
+       private String spoexLength = "90";\r
+       // 前倒し\r
+       public boolean getOverlapUp() { return overlapUp; }\r
+       public void setOverlapUp(boolean b) { overlapUp = b; }\r
+       private boolean overlapUp = false;\r
+       // 先送り\r
+       public boolean getOverlapDown() { return overlapDown; }\r
+       public void setOverlapDown(boolean b) { overlapDown = b; }\r
+       private boolean overlapDown = false;\r
+       // ケツ短縮\r
+       public boolean getOverlapDown2() { return overlapDown2; }\r
+       public void setOverlapDown2(boolean b) { overlapDown2 = b; }\r
+       private boolean overlapDown2 = false;\r
+       // OVAとか縮めんといて\r
+       public boolean getNoOverlapDown2Sp() { return noOverlapDown2Sp; }\r
+       public void setNoOverlapDown2Sp(boolean b) { noOverlapDown2Sp = b; }\r
+       private boolean noOverlapDown2Sp = true;\r
+       // 自動フォルダ選択\r
+       public boolean getAutoFolderSelect() { return autoFolderSelect; }\r
+       public void setAutoFolderSelect(boolean b) { autoFolderSelect = b; }\r
+       private boolean autoFolderSelect = true;\r
+       // ジャンルではなく放送局でAV設定をする\r
+       public boolean getEnableCHAVsetting() { return enableCHAVsetting; }\r
+       public void setEnableCHAVsetting(boolean b) { enableCHAVsetting = b; }\r
+       private boolean enableCHAVsetting = false;\r
+       // 類似予約の検索範囲\r
+       public int getRangeLikeRsv() { return rangeLikeRsv; }\r
+       public void setRangeLikeRsv(int c) { rangeLikeRsv = c; }\r
+       private int rangeLikeRsv = 0;\r
+       // 類似予約の情報を引き継ぐ\r
+       public boolean getGivePriorityToReserved() { return givePriorityToReserved; }\r
+       public void setGivePriorityToReserved(boolean b) { givePriorityToReserved = b; }\r
+       private boolean givePriorityToReserved = true;\r
+       // 類似予約の予約名を優先する\r
+       public boolean getGivePriorityToReservedTitle() { return givePriorityToReservedTitle; }\r
+       public void setGivePriorityToReservedTitle(boolean b) { givePriorityToReservedTitle = b; }\r
+       private boolean givePriorityToReservedTitle = true;\r
+       // 隣接する番組を重複扱いしない\r
+       public boolean getAdjoiningNotRepetition() { return adjoiningNotRepetition; }\r
+       public void setAdjoiningNotRepetition(boolean b) { adjoiningNotRepetition = b; }\r
+       private boolean adjoiningNotRepetition = false;\r
+       // 予約一覧で繰り返し予約を展開する\r
+       public boolean getShowAllIterationItem() { return showAllIterationItem; }\r
+       public void setShowAllIterationItem(boolean b) { showAllIterationItem = b; }\r
+       private boolean showAllIterationItem = true;\r
+       // 繰り返し予約の2つ目以降の文字色\r
+       public Color getIterationItemForeground() { return iterationItemForeground; }\r
+       public void setIterationItemForeground(Color c) { iterationItemForeground = c; }\r
+       private Color iterationItemForeground = ITERATIONITEM_COLOR;\r
+       // チューナー不足警告の強調色\r
+       public Color getTunerShortColor() { return tunerShortColor; }\r
+       public void setTunerShortColor(Color c) { tunerShortColor = c; }\r
+       private Color tunerShortColor = TUNERSHORT_COLOR;\r
+       // 正常録画済み(と思われる)と思われる背景色\r
+       public Color getRecordedColor() { return recordedColor; }\r
+       public void setRecordedColor(Color c) { recordedColor = c; }\r
+       private Color recordedColor = RECORDED_COLOR;\r
+       // タイトル自動補完\r
+       public boolean getUseAutocomplete() { return useAutocomplete; }\r
+       public void setUseAutocomplete(boolean b) { useAutocomplete = b; }\r
+       private boolean useAutocomplete = false;\r
+       \r
+       /*\r
+        * その他の設定\r
+        */\r
+       \r
+       // 起動時にアップデートを確認する\r
+       public boolean getCheckUpdate() { return checkUpdate; }\r
+       public void setCheckUpdate(boolean b) { checkUpdate= b; }\r
+       private boolean checkUpdate = true;\r
+       public UpdateOn getUpdateMethod() { return updateMethod; }\r
+       public void setUpdateMethod(UpdateOn u) { updateMethod = u; }\r
+       private UpdateOn updateMethod = UpdateOn.EVERY2DAY;\r
+       // beep音無効\r
+       public boolean getDisableBeep() { return disableBeep; }\r
+       public void setDisableBeep(boolean b) { disableBeep = b; }\r
+       private boolean disableBeep = false;\r
+       // システムトレイを表示する\r
+       public boolean getShowSysTray() { return showSysTray; }\r
+       public void setShowSysTray(boolean b) { showSysTray = b; }\r
+       private boolean showSysTray = false;\r
+       // [X]ボタンでシステムトレイに隠す\r
+       public boolean getHideToTray() { return hideToTray; }\r
+       public void setHideToTray(boolean b) { hideToTray = b; }\r
+       private boolean hideToTray = false;\r
+       // 多重起動禁止\r
+       public boolean getOnlyOneInstance() { return onlyOneInstance; }\r
+       public void setOnlyOneInstance(boolean b) { onlyOneInstance = b; }\r
+       private boolean onlyOneInstance = false;\r
+       // LookAndFeel\r
+       public String getLookAndFeel() { return lookAndFeel; }\r
+       public void setLookAndFeel(String n) { lookAndFeel = n; }\r
+       private String lookAndFeel = "";\r
+       // 表示フォント \r
+       public String getFontName() { return fontName; }\r
+       public void setFontName(String f) { fontName = f; }\r
+       private String fontName = "";\r
+       public int getFontSize() { return fontSize; }\r
+       public void setFontSize(int f) { fontSize = f; }\r
+       private int fontSize = 12;\r
+       \r
+       public boolean getUseGTKRC() { return useGTKRC; }\r
+       public void setUseGTKRC(boolean b) { useGTKRC = b; }\r
+       private boolean useGTKRC = true;\r
+       \r
+       private int fontSizeAdjustGTK = 3;\r
+       \r
+       // 【Win】ファイルオープンにrundll32を使用する\r
+       public boolean getUseRundll32() { return useRundll32; }\r
+       public void setUseRundll32(boolean b) { useRundll32 = b; }\r
+       private boolean useRundll32 = false;\r
+       // 【注意】デバッグモード\r
+       public boolean getDebug() { return debug; }\r
+       public void setDebug(boolean b) { debug = b; }\r
+       private boolean debug = false;\r
+       \r
+       \r
+       /*******************************************************************************\r
+        * 作ったけど使ってないもの\r
+        ******************************************************************************/\r
+       \r
+       /*\r
+        * フォルダの設定\r
+        */\r
+       \r
+       // キャッシュ格納場所\r
+       public String getProgDir() { return progDir; }\r
+       public void setProgDir(String s) { progDir = s; }\r
+       private String progDir = "progcache";\r
+       // 過去ログ格納場所\r
+       public String getPassedDir() { return passedDir; }\r
+       public void setPassedDir(String s) { passedDir = s; }\r
+       private String passedDir = "passed";\r
+\r
+       \r
+       /*******************************************************************************\r
+        * 設定項目ではないもの\r
+        ******************************************************************************/\r
+       \r
+       /*\r
+        * Web番組表関連の設定 \r
+        */\r
+       \r
+       // Web番組表サイト(TV)\r
+       public String getTVProgramSite() { return tvProgramSite; }\r
+       public void setTVProgramSite(String s) { tvProgramSite = s; }\r
+       private String tvProgramSite = TV_SITE;\r
+       // Web番組表サイト(CS)\r
+       public String getCSProgramSite() { return csProgramSite; }\r
+       public void setCSProgramSite(String s) { csProgramSite = s; }\r
+       private String csProgramSite = CS_SITE;\r
+       // Web番組表サイト(CS2)\r
+       public String getCS2ProgramSite() { return cs2ProgramSite; }\r
+       public void setCS2ProgramSite(String s) { cs2ProgramSite = s; }\r
+       private String cs2ProgramSite = CS2_SITE;\r
+       // Web番組表サイト(Radio)\r
+       @Deprecated\r
+       public String getRadioProgramSite() { return radioProgramSite; }\r
+       @Deprecated\r
+       public void setRadioProgramSite(String s) { radioProgramSite = s; }\r
+       @Deprecated\r
+       private String radioProgramSite = RADIO_SITE;\r
+\r
+       /*******************************************************************************\r
+        * もう使われていないアイテム\r
+        ******************************************************************************/\r
+       \r
+       // 番組表の表示を簡素化する(現在は使用していない)\r
+       @Deprecated\r
+       public boolean getLightProgramView() { return lightProgramView; }\r
+       @Deprecated\r
+       public void setLightProgramView(boolean b) { lightProgramView = b; }\r
+       @Deprecated\r
+       private boolean lightProgramView = false;\r
+       \r
+       // スポーツ延長の検索範囲\r
+       @Deprecated\r
+       public String getSpoexSearchStart() { return spoexSearchStart; }\r
+       @Deprecated\r
+       public void setSpoexSearchStart(String s) { spoexSearchStart = s; }\r
+       @Deprecated\r
+       public String getSpoexSearchEnd() { return spoexSearchEnd; }\r
+       \r
+       @Deprecated\r
+       public void setSpoexSearchEnd(String s) { spoexSearchEnd = s; }\r
+       @Deprecated\r
+       private String spoexSearchStart = "00:00";\r
+       @Deprecated\r
+       private String spoexSearchEnd = "00:00";\r
+       \r
+       // 「最大延長」の記載のあるもののみスポーツ延長のトリガーにする\r
+       @Deprecated\r
+       public boolean getSpoexLimitation() { return spoexLimitation; }\r
+       @Deprecated\r
+       public void setSpoexLimitation(boolean b) { spoexLimitation = b; }\r
+       @Deprecated\r
+       private boolean spoexLimitation = false;\r
+       \r
+       \r
+       /*******************************************************************************\r
+        * 雑多なメソッド\r
+        ******************************************************************************/\r
+\r
+       // 各種ディレクトリの作成\r
+       public void makeEnvDir() {\r
+               String cause = "";\r
+               try {\r
+                       File d = null;\r
+                       \r
+                       // 設定ファイルを格納するディレクトリの作成\r
+                       d = new File(cause = envDir);\r
+                       if (d.exists() == false) {\r
+                               if (d.mkdir() == false) {\r
+                                       // 何か致命的なエラー\r
+                               }\r
+                       }\r
+\r
+                       // プラグイン一覧ファイルを格納するディレクトリの作成\r
+                       d = new File(cause = metainfDir);\r
+                       if (d.exists() == false) {\r
+                               if (d.mkdir() == false) {\r
+                                       // 何か致命的なエラー\r
+                               }\r
+                       }\r
+                       d = new File(cause = services);\r
+                       if (d.exists() == false) {\r
+                               if (d.mkdir() == false) {\r
+                                       // 何か致命的なエラー\r
+                               }\r
+                       }\r
+                       \r
+                       // 番組表をキャッシュするディレクトリの作成\r
+                       d = new File(cause = progDir);\r
+                       if (d.exists() == false) {\r
+                               if (d.mkdir() == false) {\r
+                                       // 何か致命的なエラー\r
+                               }\r
+                       }\r
+                       \r
+                       // 過去ログ\r
+                       d = new File(cause = passedDir);\r
+                       if (d.exists() == false) {\r
+                               if (d.mkdir() == false) {\r
+                                       // 何か致命的なエラー\r
+                               }\r
+                       }\r
+                       \r
+                       // skin\r
+                       d = new File(cause = skinDir);\r
+                       if (d.exists() == false) {\r
+                               if (d.mkdir() == false) {\r
+                                       // 何か致命的なエラー\r
+                               }\r
+                       }\r
+                       \r
+                       /*\r
+                       // debug\r
+                       d = new File(cause = debugDir);\r
+                       if (d.exists() == false) {\r
+                               if (d.mkdir() == false) {\r
+                                       // 何か致命的なエラー\r
+                               }\r
+                       }\r
+                       */\r
+               }\r
+               catch (Exception e) {\r
+                       System.err.println(ERRID+"フォルダの作成に失敗 "+cause);\r
+               }\r
+       }\r
+       \r
+       // 保存する・読みだす\r
+       public boolean save() {\r
+       \r
+               if ( FieldUtils.save(envText,this) ) {\r
+               // テキスト形式へ移行済み\r
+                       \r
+                       if ( useGTKRC ) {\r
+                               saveGTKRC();\r
+                       }\r
+                       else {\r
+                               File f = new File(GTKRC);\r
+                               if ( f.exists() ) {\r
+                                       f.delete();\r
+                               }\r
+                       }\r
+               \r
+                       return true;\r
+               }\r
+       \r
+       System.out.println(MSGID+"保存します: "+envFile);\r
+       if ( ! CommonUtils.writeXML(envFile, this) ) {\r
+               System.err.println(ERRID+"保存に失敗しました");\r
+               return false;\r
+       }\r
+       return true;\r
+       }\r
+       \r
+       private boolean saveGTKRC() {\r
+               StringBuilder sb = new StringBuilder();\r
+               sb.append("style \"tainavistyle\" {\n");\r
+               sb.append("font_name = \"");\r
+               sb.append(this.getFontName());\r
+               sb.append(" ");\r
+               sb.append(String.valueOf(this.getFontSize()-fontSizeAdjustGTK));\r
+               sb.append("\"\n");\r
+               sb.append("}\n");\r
+               sb.append("\n");\r
+               sb.append("class \"GtkWidget\" style \"tainavistyle\"\n");\r
+               return CommonUtils.write2file(GTKRC, sb.toString());\r
+       }\r
+       \r
+       public boolean load() {\r
+               \r
+       File ft = new File(envText);\r
+       if ( ft.exists() ) {\r
+               // テキスト形式へ移行済み\r
+                       System.out.println("@Deprecated: "+envFile);\r
+               return true;\r
+       }\r
+       \r
+       System.out.println(MSGID+"読み込みます: "+envFile);\r
+       File fx = new File(envFile);\r
+       if ( fx.exists() ) {\r
+               Env b = (Env) CommonUtils.readXML(envFile);\r
+               if ( b != null ) {\r
+                       CommonUtils.FieldCopy(this, b);\r
+                       \r
+                               // テキスト形式がなければ作るよ\r
+                               if ( FieldUtils.save(envText,this) ) {\r
+                                       fx.renameTo(new File(envFile+".bak"));\r
+                               }\r
+                       \r
+                       return true;\r
+               }\r
+       }\r
+       \r
+       System.err.println(ERRID+"環境設定が読み込めなかったのでデフォルトの設定値を利用します");\r
+       return false;\r
+       }\r
+       \r
+       \r
+       /**\r
+        * {@link Env#save()}から呼び出されるだけで公開はしない\r
+        */\r
+       @Deprecated\r
+       private boolean saveLAF() {\r
+       System.out.println(MSGID+"ルックアンドフィール設定を保存します: "+lafFile);\r
+       if ( ! CommonUtils.write2file(lafFile, String.format("%s\t%s\t%d",this.lookAndFeel,fontName,fontSize)) ) {\r
+               System.err.println(ERRID+"ルックアンドフィール設定の保存に失敗しました");\r
+               return false;\r
+       }\r
+       return true;\r
+       }\r
+       \r
+       /**\r
+        * 移行用\r
+        * @return\r
+        */\r
+       public boolean loadText() {\r
+               \r
+               boolean b = FieldUtils.load(envText,this);\r
+               \r
+               // 廃止するよ\r
+               File f = new File(lafFile); \r
+       if ( f.exists() ) {\r
+               System.out.println(MSGID+"ルックアンドフィール設定ファイルは廃止されます: "+lafFile);\r
+               String s = CommonUtils.read4file(lafFile, true);\r
+               if ( s != null ) {\r
+                       Matcher ma = Pattern.compile("^(.+?)\t(.+?)\t(\\d+?)$",Pattern.DOTALL).matcher(s);\r
+                       if ( ma.find() ) {\r
+                               this.lookAndFeel = ma.group(1);\r
+                               this.fontName = ma.group(2);\r
+                               this.fontSize = Integer.valueOf(ma.group(3));\r
+                               System.out.println("+lookandfeel="+this.lookAndFeel+", fontname="+this.fontName+", fontsize="+this.fontSize);\r
+                       }\r
+               }\r
+                       f.delete();\r
+       }\r
+               \r
+               return b;\r
+               \r
+       }\r
+       \r
+       /**\r
+        * <P>XMLDecoderに失敗すると起動できなくなる場合があるので、どうしても起動時に欲しい設定だけベタテキストで保存しおく\r
+        * <P>{@link #saveLAF()}は公開しない\r
+        */\r
+       @Deprecated\r
+       public boolean loadLAF() {\r
+       return true;\r
+       }\r
+       \r
+       /*******************************************************************************\r
+        * コンストラクタ\r
+        ******************************************************************************/\r
+       \r
+       public Env() {\r
+\r
+               // ↑の宣言時に設定できないもの\r
+               \r
+               {\r
+                       ArrayList<JTXTButton.FontStyle> fsa = new ArrayList<JTXTButton.FontStyle>();\r
+                       fsa.add(JTXTButton.FontStyle.BOLD);\r
+                       fsa.add(JTXTButton.FontStyle.UNDERLINE);\r
+                       this.setTitleFontStyle(fsa);\r
+               }\r
+               {\r
+                       ArrayList<JTXTButton.FontStyle> fsa = new ArrayList<JTXTButton.FontStyle>();\r
+                       this.setDetailFontStyle(fsa);\r
+               }\r
+               {\r
+                       HashMap<TVProgram.ProgOption, Boolean> h = new HashMap<TVProgram.ProgOption, Boolean>();\r
+                       for ( Object[] obj : TVProgram.optMarks ) {\r
+                               h.put((TVProgram.ProgOption) obj[0],true);\r
+                       }\r
+                       this.setOptMarks(h);\r
+               }\r
+               {\r
+                       ArrayList<TextValueSet> al = new ArrayList<TextValueSet>();\r
+                       \r
+                       TextValueSet tv = new TextValueSet();\r
+                       tv.setText("Googleでタイトルを検索");\r
+                       tv.setValue("http://www.google.co.jp/search?q=%ENCTITLE%");\r
+                       al.add(tv);\r
+                       \r
+                       tv = new TextValueSet();\r
+                       tv.setText("Bingでタイトルを検索");\r
+                       tv.setValue("http://www.bing.com/search?q=%ENCTITLE%");\r
+                       al.add(tv);\r
+                       \r
+                       tv = new TextValueSet();\r
+                       tv.setText("ブラウザで番組詳細のページを開く");\r
+                       tv.setValue("%DETAILURL%");\r
+                       al.add(tv);\r
+                       \r
+                       tv = new TextValueSet();\r
+                       tv.setText("ブラウザで放送局のページを探す");\r
+                       tv.setValue("http://www.google.co.jp/search?hl=ja&btnI=1&q=%ENCCHNAME%");\r
+                       al.add(tv);\r
+                       \r
+                       tv = new TextValueSet();\r
+                       tv.setText("ブラウザで番組情報のページを探す");\r
+                       tv.setValue("http://www.google.co.jp/search?hl=ja&btnI=1&q=%ENCCHNAME%+%ENCTITLE%");\r
+                       al.add(tv);\r
+\r
+                       tv = new TextValueSet();\r
+                       tv.setText("ブラウザでTweetする");\r
+                       tv.setValue("https://twitter.com/share?text=%DATE%+%START%-%END%+%ENCCHNAME%+%ENCTITLE%");\r
+                       al.add(tv);\r
+                       \r
+                       tv = new TextValueSet();\r
+                       tv.setText("CHAN-TORUの予約ページを開く");\r
+                       tv.setValue("https://tv.so-net.ne.jp/chan-toru/detail?type=bytime&cat=%TVKCAT%&area=%TVKAREACODE%&pid=%TVKPID%");\r
+                       al.add(tv);\r
+                       \r
+                       this.setTvCommand(al);\r
+               }\r
+               \r
+               // おまけ\r
+               if ( firstinstance ) {\r
+                       firstinstance = false;\r
+                       loadLAF();\r
+               }\r
+       }\r
+}\r
diff --git a/TinyBannavi/src/tainavi/ExtProgram.java b/TinyBannavi/src/tainavi/ExtProgram.java
new file mode 100644 (file)
index 0000000..bdc6fab
--- /dev/null
@@ -0,0 +1,14 @@
+package tainavi;\r
+\r
+import java.io.File;\r
+\r
+/**\r
+ * @see SearchProgram\r
+ */\r
+public class ExtProgram extends SearchProgram {\r
+       // コンストラクタ\r
+       public ExtProgram() {\r
+               setSearchKeyFile("env"+File.separator+"extkey.xml");\r
+               setSearchKeyLabel("延長警告管理");\r
+       }\r
+}\r
diff --git a/TinyBannavi/src/tainavi/FieldUtils.java b/TinyBannavi/src/tainavi/FieldUtils.java
new file mode 100644 (file)
index 0000000..348161a
--- /dev/null
@@ -0,0 +1,493 @@
+package tainavi;\r
+\r
+import java.awt.Color;\r
+import java.awt.Rectangle;\r
+import java.io.File;\r
+import java.lang.reflect.Field;\r
+import java.lang.reflect.Modifier;\r
+import java.lang.reflect.ParameterizedType;\r
+import java.util.ArrayList;\r
+import java.util.HashMap;\r
+import java.util.Map.Entry;\r
+import java.util.regex.Pattern;\r
+\r
+import tainavi.Env.AAMode;\r
+import tainavi.Env.DblClkCmd;\r
+import tainavi.Env.SnapshotFmt;\r
+import tainavi.Env.UpdateOn;\r
+import tainavi.JTXTButton.FontStyle;\r
+import tainavi.TVProgram.ProgOption;\r
+\r
+\r
+public class FieldUtils {\r
+       \r
+       /*******************************************************************************\r
+        * 定数とか\r
+        ******************************************************************************/\r
+       \r
+       private static final String SPCH_LF = "$LF$";\r
+       private static final String SPCH_NULL = "$NULL$";\r
+       \r
+       private static final String SPMK_CM = "#";\r
+       private static final String SPMK_SEP = "=";\r
+       private static final String SPMK_LF = "\n";\r
+       \r
+       private static final String SPHD_MOD = SPMK_CM+" MODIFIED : ";\r
+       private static final String SPHD_VER = SPMK_CM+" VERSION : ";\r
+       private static final String SPHD_DEP = SPMK_CM+" DEPRECATED : ";\r
+       private static final String SPHD_UNS = SPMK_CM+" UNSUPPORTED : ";\r
+       private static final String SPHD_NOE = SPMK_CM+" NOELEMENT : ";\r
+       \r
+       private static final String MSGID = "[設定保存] ";\r
+       private static final String ERRID = "[ERROR]"+MSGID;\r
+       \r
+       /*******************************************************************************\r
+        * 保存\r
+        ******************************************************************************/\r
+       \r
+       @SuppressWarnings({ "rawtypes", "unchecked" })\r
+       public static boolean save(String envText, Object root) {\r
+               \r
+               StringBuilder sb = new StringBuilder();\r
+               \r
+               sb.append(SPHD_MOD);\r
+               sb.append(CommonUtils.getDateTime(0));\r
+               sb.append(SPMK_LF);\r
+               sb.append(SPHD_VER);\r
+               sb.append(VersionInfo.getVersionNumber());\r
+               sb.append(SPMK_LF);\r
+               sb.append(SPMK_LF);\r
+               \r
+               Field[] fd = root.getClass().getDeclaredFields();\r
+               for ( Field fx : fd ) {\r
+                       fx.setAccessible(true);\r
+                       if ( Modifier.isFinal(fx.getModifiers()) ) {\r
+                               continue;\r
+                       }\r
+                       if ( Modifier.isStatic(fx.getModifiers()) ) {\r
+                               continue;\r
+                       }\r
+\r
+                       try {\r
+                               \r
+                               String key = fx.getName();\r
+                               Class cls = fx.getType();\r
+                               Object obj = fx.get(root);\r
+                               \r
+                               if ( fx.getAnnotation(Deprecated.class) != null ) {\r
+                                       sb.append(SPHD_DEP);\r
+                                       sb.append(key);\r
+                                       sb.append(" AS ");\r
+                                       sb.append(cls.getName());\r
+                                       sb.append(SPMK_LF);\r
+                                       continue;\r
+                               }\r
+                               \r
+                               int n = -1;\r
+                               ArrayList objlst = null;\r
+                               Class ocls = null;\r
+                               if ( cls == ArrayList.class ) {\r
+                                       objlst = (ArrayList) obj;\r
+                                       \r
+                                       // nullの要素がある可能性がある\r
+                                       if ( objlst.size() > 0 ) {\r
+                                               ParameterizedType paramType = (ParameterizedType) fx.getGenericType();\r
+                                               ocls = (Class) paramType.getActualTypeArguments()[0];\r
+                                       }\r
+                                       \r
+                                       n = 1;\r
+                               }\r
+                               else if ( cls == HashMap.class ) {\r
+                                       objlst = new ArrayList();\r
+                                       HashMap map = (HashMap) obj;\r
+                                       for ( Object o : map.entrySet().toArray() ) {\r
+                                               objlst.add(o);\r
+                                               \r
+                                               // これは必ずEntrySetだね\r
+                                               if ( ocls == null ) {\r
+                                                       ocls = o.getClass();\r
+                                               }\r
+                                       }\r
+                                       n = 1;\r
+                               }\r
+                               else {\r
+                                       objlst = new ArrayList();\r
+                                       objlst.add(obj);\r
+                                       \r
+                                       // obj==nullの可能性がある\r
+                                       ocls = cls;\r
+                               }\r
+                               \r
+                               if ( ! objlst.isEmpty() ) {\r
+                                       for ( Object o : objlst ) {\r
+                                               \r
+                                               //Class ocls = o.getClass();\r
+                                               String val = obj2str(o,ocls);\r
+                                               \r
+                                               if ( val != null ) {\r
+                                                       sb.append(key);\r
+                                                       if ( n >= 1 ) {\r
+                                                               // ArrayList or HashMap\r
+                                                               sb.append("[");\r
+                                                               sb.append(String.valueOf(n));\r
+                                                               sb.append("]");\r
+                                                               n++;\r
+                                                       }\r
+                                                       sb.append(SPMK_SEP);\r
+                                                       sb.append(val);\r
+                                                       sb.append(SPMK_LF);\r
+                                               }\r
+                                               else {\r
+                                                       sb.append(SPHD_UNS);\r
+                                                       sb.append(key);\r
+                                                       if ( n >= 1 ) {\r
+                                                               sb.append("[");\r
+                                                               sb.append(String.valueOf(n));\r
+                                                               sb.append("]");\r
+                                                               n++;\r
+                                                       }\r
+                                                       sb.append(" AS ");\r
+                                                       sb.append(ocls.getName());\r
+                                                       sb.append(SPMK_LF);\r
+                                               }\r
+                                       }\r
+                               }\r
+                               else {\r
+                                       sb.append(key);\r
+                                       sb.append("[0]");\r
+                                       sb.append(SPMK_SEP);\r
+                                       sb.append(SPMK_LF);\r
+                               }\r
+                       }\r
+                       catch (Exception e) {\r
+                               e.printStackTrace();\r
+                               return false;\r
+                       } \r
+               }\r
+               \r
+       System.out.println(MSGID+"テキスト形式で保存します: "+envText);\r
+       if ( ! CommonUtils.write2file(envText, sb.toString()) ) {\r
+               System.err.println(ERRID+"保存に失敗しました");\r
+               return false;\r
+       }\r
+               \r
+               return true;\r
+       }\r
+       \r
+       \r
+       /*******************************************************************************\r
+        * 取得\r
+        ******************************************************************************/\r
+       \r
+       @SuppressWarnings({ "rawtypes", "unchecked" })\r
+       public static boolean load(String envText, Object root) {\r
+               \r
+       if ( new File(envText).exists() ) {\r
+               System.out.println(MSGID+"テキスト形式で読み込みます: "+envText);\r
+               String buf = CommonUtils.read4file(envText, false);\r
+               if ( buf != null ) {\r
+                       \r
+                       Field[] fd = root.getClass().getDeclaredFields();\r
+                       \r
+                       int lineno = 0 ;\r
+                       for ( String str : buf.split(SPMK_LF) ) {\r
+                               \r
+                               ++lineno;\r
+                               \r
+                               if ( str.startsWith(SPHD_MOD) ) {\r
+                                       System.out.println(MSGID+str);\r
+                                       continue;\r
+                               }\r
+                               \r
+                               if ( str.startsWith(SPMK_CM) || str.matches("^\\s*$") ) {\r
+                                       continue;\r
+                               }\r
+                               \r
+                               String[] a = str.split(SPMK_SEP, 2);\r
+                               if ( a.length != 2 ) {\r
+                                       System.err.println(ERRID+"不正な記述: "+envText+" at "+lineno+"行目 "+str);\r
+                                       break;\r
+                               }\r
+                               \r
+                               Field fx = null;\r
+                               for ( Field f : fd ) {\r
+                                       if ( f.getName().equals(a[0].replaceFirst("\\[\\d+\\]$","")) ) {\r
+                                               fx = f;\r
+                                               break;\r
+                                       }\r
+                               }\r
+                               if ( fx == null ) {\r
+                                       System.err.println(ERRID+"不正な記述: "+envText+" at "+lineno+"行目 "+str);\r
+                                       break;\r
+                               }\r
+                               \r
+                               fx.setAccessible(true);\r
+                               \r
+                                       if ( Modifier.isFinal(fx.getModifiers()) ) {\r
+                                               continue;\r
+                                       }\r
+                                       if ( Modifier.isStatic(fx.getModifiers()) ) {\r
+                                               continue;\r
+                                       }\r
+                                       \r
+                                       if ( fx.getAnnotation(Deprecated.class) != null ) {\r
+                                               System.out.println(MSGID+SPHD_DEP+fx.getName());\r
+                                               break;\r
+                                       }\r
+                                       \r
+                                       Class cls = fx.getType();\r
+                                       \r
+                                       try {\r
+                                               if ( cls == ArrayList.class ) {\r
+                                                       ArrayList list = (ArrayList) fx.get(root);\r
+                                                       if ( fx.get(root) == null ) {\r
+                                                               System.out.println(ERRID+"初期化されていないフィールド: "+envText+" at "+lineno+"行目 ("+cls.getName()+") "+str);\r
+                                                               break;\r
+                                                       }\r
+                                                       \r
+                                                       if ( a[0].endsWith("[0]") ) {\r
+                                                               // no element.\r
+                                                               list.clear();\r
+                                                       }\r
+                                                       else {\r
+                                                               if ( a[0].endsWith("[1]") ) {\r
+                                                                       // newした時のデフォルト値が入っているからリセットじゃー\r
+                                                                       list.clear();\r
+                                                               }\r
+                                                               ParameterizedType paramType = (ParameterizedType) fx.getGenericType();\r
+                                                               Class ocls = (Class) paramType.getActualTypeArguments()[0];\r
+                                                               Object obj = str2obj(a[1],ocls);\r
+                                                               list.add(obj);\r
+                                                       }\r
+                                               }\r
+                                               else if ( cls == HashMap.class ) {\r
+                                                       HashMap map = (HashMap) fx.get(root);\r
+                                                       if ( fx.get(root) == null ) {\r
+                                                               System.out.println(ERRID+"初期化されていないフィールド: "+envText+" at "+lineno+"行目 ("+cls.getName()+") "+str);\r
+                                                               break;\r
+                                                       }\r
+                                                       String[] b = a[1].split(Pattern.quote(SPCH_LF),2);\r
+                                                       if ( b.length != 2 ) {\r
+                                                       System.err.println(ERRID+"不正な記述: "+envText+" at "+lineno+"行目 "+str);\r
+                                                       break;\r
+                                                       }\r
+                                                       \r
+                                                       if ( a[0].endsWith("[0]") ) {\r
+                                                               // no element.\r
+                                                               map.clear();\r
+                                                       }\r
+                                                       else {\r
+                                                               if ( a[0].endsWith("[1]") ) {\r
+                                                                       // newした時のデフォルト値が入っているからリセットじゃー\r
+                                                                       map.clear();\r
+                                                               }\r
+                                                               ParameterizedType paramType = (ParameterizedType) fx.getGenericType();\r
+                                                               Class kcls = (Class) paramType.getActualTypeArguments()[0];\r
+                                                               Class vcls = (Class) paramType.getActualTypeArguments()[1];\r
+                                                               Object k = str2obj(b[0],kcls);\r
+                                                               Object v = str2obj(b[1],vcls);\r
+                                                               map.put(k,v);\r
+                                                       }\r
+                                               }\r
+                                               else {\r
+                                                       Object obj = str2obj(a[1],cls);\r
+                                                       fx.set(root, obj);\r
+                                               }\r
+                                       }\r
+                                       catch (UnsupportedOperationException e) {\r
+                                               System.out.println(ERRID+e.getMessage()+": "+envText+" at "+lineno+"行目 ("+cls.getName()+") "+str);\r
+                                       }\r
+                                       catch (Exception e) {\r
+                                               // エラー項目はスキップする\r
+                                               System.out.println(ERRID+e.getMessage()+": "+envText+" at "+lineno+"行目 ("+cls.getName()+") "+str);\r
+                                               e.printStackTrace();\r
+                                       }\r
+                       }\r
+               }\r
+               \r
+               return true;\r
+       }\r
+               \r
+               return false;\r
+       }\r
+       \r
+       \r
+       /*******************************************************************************\r
+        * 保存(部品)\r
+        ******************************************************************************/\r
+       \r
+       @SuppressWarnings("rawtypes")\r
+       private static String obj2str(Object obj, Class cls) {\r
+               if ( cls == String.class ) {\r
+                       return(obj == null ? SPCH_NULL : ((String) obj).replaceAll(SPMK_LF, SPCH_LF));\r
+               }\r
+               else if ( cls == int.class || cls == Integer.class ) {\r
+                       return(obj == null ? SPCH_NULL : String.valueOf((Integer) obj));\r
+               }\r
+               else if ( cls == float.class || cls == Float.class ) {\r
+                       return(obj == null ? SPCH_NULL : String.valueOf((Float) obj));\r
+               }\r
+               else if ( cls == boolean.class || cls == Boolean.class ) {\r
+                       return(obj == null ? SPCH_NULL : String.valueOf((Boolean) obj));\r
+               }\r
+               else if ( cls == Rectangle.class ) {\r
+                       Rectangle ra = (Rectangle) obj;\r
+                       return(obj == null ? SPCH_NULL : String.format("%d,%d,%d,%d",ra.x,ra.y,ra.width,ra.height));\r
+               }\r
+               else if ( cls == Color.class ) {\r
+                       return(obj == null ? SPCH_NULL : CommonUtils.color2str((Color) obj));\r
+               }\r
+               else if ( cls == DblClkCmd.class ) {\r
+                       return(obj == null ? SPCH_NULL : String.valueOf(((DblClkCmd) obj).getId()));\r
+               }\r
+               else if ( cls == SnapshotFmt.class ) {\r
+                       return(obj == null ? SPCH_NULL : String.valueOf(((SnapshotFmt) obj).getId()));\r
+               }\r
+               else if ( cls == AAMode.class ) {\r
+                       return(obj == null ? SPCH_NULL : String.valueOf(((AAMode) obj).getId()));\r
+               }\r
+               else if ( cls == UpdateOn.class ) {\r
+                       return(obj == null ? SPCH_NULL : String.valueOf(((UpdateOn) obj).getId()));\r
+               }\r
+               else if ( cls == FontStyle.class ) {\r
+                       return(obj == null ? SPCH_NULL : String.valueOf(((FontStyle) obj).getId()));\r
+               }\r
+               else if ( cls == TextValueSet.class ) {\r
+                       TextValueSet t = (TextValueSet) obj;\r
+                       return(obj == null ? SPCH_NULL : t.getText()+SPCH_LF+t.getValue());\r
+               }\r
+               else if ( cls == ProgOption.class ) {\r
+                       return(obj == null ? SPCH_NULL : ((ProgOption) obj).toString());\r
+               }\r
+               else if ( obj instanceof Entry ) {\r
+                       Entry t = (Entry) obj;\r
+                       String k = obj2str(t.getKey(), t.getKey().getClass());\r
+                       String v = obj2str(t.getValue(), t.getValue().getClass());\r
+                       return(obj == null ? SPCH_NULL : k+SPCH_LF+v);\r
+               }\r
+               \r
+               return null;\r
+       }\r
+       \r
+       \r
+       /*******************************************************************************\r
+        * 取得(部品)\r
+        ******************************************************************************/\r
+       \r
+       @SuppressWarnings("rawtypes")\r
+       private static Object str2obj(String str, Class cls) throws UnsupportedOperationException {\r
+               if ( cls == String.class ) {\r
+                       return(str.equals(SPCH_NULL) ? null : str.replaceAll(SPCH_LF, SPMK_LF));\r
+               }\r
+               else if ( cls == int.class || cls == Integer.class ) {\r
+                       try {\r
+                               return(str.equals(SPCH_NULL) ? null : Integer.valueOf(str));\r
+                       }\r
+                       catch ( NumberFormatException e ) {\r
+                               throw new UnsupportedOperationException("数値に変換できない");\r
+                       }\r
+               }\r
+               else if ( cls == float.class || cls == Float.class ) {\r
+                       try {\r
+                               return(str.equals(SPCH_NULL) ? null : Float.valueOf(str));\r
+                       }\r
+                       catch ( NumberFormatException e ) {\r
+                               throw new UnsupportedOperationException("数値に変換できない");\r
+                       }\r
+               }\r
+               else if ( cls == boolean.class || cls == Boolean.class ) {\r
+                       return(str.equals(SPCH_NULL) ? null : Boolean.valueOf(str));\r
+               }\r
+               else if ( cls == Rectangle.class ) {\r
+                       try {\r
+                               String[] a = str.split(",");\r
+                               if ( a.length == 4 ) {\r
+                                       return(str.equals(SPCH_NULL) ? null : new Rectangle(Integer.valueOf(a[0]),Integer.valueOf(a[1]),Integer.valueOf(a[2]),Integer.valueOf(a[3])));\r
+                               }\r
+                               else {\r
+                                       throw new UnsupportedOperationException("変換できない");\r
+                               }\r
+                       }\r
+                       catch ( NumberFormatException e ) {\r
+                               throw new UnsupportedOperationException("数値に変換できない");\r
+                       }\r
+               }\r
+               else if ( cls == Color.class ) {\r
+                       Color c = CommonUtils.str2color(str);\r
+                       if ( c != null ) {\r
+                               return(c);\r
+                       }\r
+                       else {\r
+                               throw new UnsupportedOperationException("色に変換できない ");\r
+                       }\r
+               }\r
+               else if ( cls == DblClkCmd.class ) {\r
+                       DblClkCmd dcc = DblClkCmd.get(str);\r
+                       if ( dcc != null ) {\r
+                               return(dcc);\r
+                       }\r
+                       else {\r
+                               throw new UnsupportedOperationException("変換できない");\r
+                       }\r
+               }\r
+               else if ( cls == SnapshotFmt.class ) {\r
+                       SnapshotFmt sf = SnapshotFmt.get(str);\r
+                       if ( sf != null ) {\r
+                               return(sf);\r
+                       }\r
+                       else {\r
+                               throw new UnsupportedOperationException("変換できない");\r
+                       }\r
+               }\r
+               else if ( cls == AAMode.class ) {\r
+                       AAMode aam = AAMode.get(str);\r
+                       if ( aam != null ) {\r
+                               return(aam);\r
+                       }\r
+                       else {\r
+                               throw new UnsupportedOperationException("変換できない");\r
+                       }\r
+               }\r
+               else if ( cls == UpdateOn.class ) {\r
+                       UpdateOn uo = UpdateOn.get(str);\r
+                       if ( uo != null ) {\r
+                               return(uo);\r
+                       }\r
+                       else {\r
+                               throw new UnsupportedOperationException(ERRID+"変換できない");\r
+                       }\r
+               }\r
+               else if ( cls == FontStyle.class ) {\r
+                       FontStyle fs = FontStyle.get(str);\r
+                       if ( fs != null ) {\r
+                               return(fs);\r
+                       }\r
+                       else {\r
+                               throw new UnsupportedOperationException("変換できない");\r
+                       }\r
+               }\r
+               else if ( cls == TextValueSet.class ) {\r
+                       String a[] = str.split(Pattern.quote(SPCH_LF),2);\r
+                       if ( a.length == 2 ) {\r
+                               TextValueSet t = new TextValueSet();\r
+                               t.setText(a[0]);\r
+                               t.setValue(a[1]);\r
+                               return (t);\r
+                       }\r
+                       else {\r
+                               throw new UnsupportedOperationException("変換できない");\r
+                       }\r
+               }\r
+               else if ( cls == ProgOption.class ) {\r
+                       for ( ProgOption po : ProgOption.values() ) {\r
+                               if ( po.toString().equals(str) ) {\r
+                                       return po;\r
+                               }\r
+                       }\r
+                       throw new UnsupportedOperationException("変換できない");\r
+               }\r
+               \r
+               throw new UnsupportedOperationException("未対応の項目");\r
+       }\r
+\r
+}\r
diff --git a/TinyBannavi/src/tainavi/GetEventId.java b/TinyBannavi/src/tainavi/GetEventId.java
new file mode 100644 (file)
index 0000000..6f1d00c
--- /dev/null
@@ -0,0 +1,333 @@
+package tainavi;\r
+\r
+import java.io.File;\r
+import java.util.ArrayList;\r
+import java.util.GregorianCalendar;\r
+import java.util.HashMap;\r
+import java.util.Set;\r
+import java.util.regex.Matcher;\r
+import java.util.regex.Pattern;\r
+\r
+/**\r
+ * 番組IDってなんですか?私、気になります!\r
+ */\r
+public class GetEventId extends TVProgramUtils implements TVProgram,Cloneable {\r
+\r
+       private static final String thisEncoding = "MS932";\r
+       \r
+       private static boolean debug = false;\r
+       \r
+       private static final String MSGID = "[番組ID取得] ";\r
+       private static final String ERRID = "[ERROR]"+MSGID;\r
+       private static final String DBGID = "[DEBUG]"+MSGID;\r
+       private static final String pFILE = "env"+File.separator+"eventidmap.xml";\r
+       \r
+       private static final int REMAIN_CACHE = 24*3600;        // これだけキャッシュは残しておく\r
+       \r
+       private HashMap<String,Integer> pMap = new HashMap<String, Integer>();\r
+       \r
+       private String errmsg = null;\r
+       \r
+       /*\r
+        * コンストラクタ\r
+        */\r
+       public GetEventId() {\r
+               this.load();\r
+               setRetryCount(0);\r
+       }\r
+       \r
+       // 本文\r
+       public Integer getEvId(final String chId, final String startDateTime, boolean force) {\r
+               \r
+               errmsg = "";\r
+               \r
+               if (startDateTime == null) {\r
+                       return -1; // 使いたくないのでしょう\r
+               }\r
+               \r
+               final GregorianCalendar ca = CommonUtils.getCalendar(startDateTime);\r
+               if ( ca == null ) {\r
+                       System.err.println(errmsg = ERRID+"開始日時の書式が不正です: "+startDateTime);\r
+                       return -1;\r
+               }\r
+               final String ymd = CommonUtils.getDateTimeYMD(ca).replaceFirst("\\d\\d$", "");  // YYYYMMDDhhmm\r
+               \r
+               int chType;\r
+               int chNum;\r
+               try {\r
+                       chType = Integer.decode("0x"+chId.substring(0,4));\r
+                       chNum = Integer.decode("0x"+chId.substring(8,12));\r
+               }\r
+               catch (StringIndexOutOfBoundsException e) {\r
+                       System.err.println(errmsg = ERRID+"放送局IDが不正です: "+chId+" "+e.toString());\r
+                       return -1;\r
+               }\r
+               catch (NumberFormatException e) {\r
+                       System.err.println(errmsg = ERRID+"放送局IDが不正です: "+chId+" "+e.toString());\r
+                       return -1;\r
+               }\r
+               \r
+               Integer evId = pMap.get(chId+ymd);\r
+               if ( evId != null ) {\r
+                       if (debug) System.out.println(MSGID+"キャッシュにヒットしました。");\r
+                       return evId;\r
+               }\r
+               if ( ! force ) {\r
+                       return evId;\r
+               }\r
+               \r
+               String chStr = null;\r
+               switch ( chType ) {\r
+               case 0x0004:\r
+                       chStr = String.format("200%03d",chNum%1000);\r
+                       break;\r
+               case 0x0006:\r
+               case 0x0007:\r
+                       chStr = String.format("500%03d",chNum%1000);\r
+                       break;\r
+               default:\r
+                       chStr = String.format("%06d",(chNum+100000)%1000000);\r
+                       break;\r
+               }\r
+               \r
+               final String uri = "http://tv.so-net.ne.jp/iepg.tvpid?id="+chStr+ymd;\r
+               final String res = webToBuffer(uri, thisEncoding, false);\r
+               if ( res == null ) {\r
+                       System.err.println(errmsg = ERRID+"サイトへのアクセスが失敗しました: "+uri);\r
+                       return null;\r
+               }\r
+               \r
+               if (debug) System.out.println(DBGID+"レスポンス: \n"+res);\r
+               \r
+               Matcher ma = Pattern.compile("program-id: (\\d+)").matcher(res);\r
+               if ( ! ma.find() ) {\r
+                       System.err.println(errmsg = ERRID+"program-idがみつかりません: "+uri);\r
+                       return -1;\r
+               }\r
+               \r
+               evId = Integer.valueOf(ma.group(1));\r
+               \r
+               this.put(chId+ymd, evId);\r
+               \r
+               this.save();\r
+               \r
+               return evId;\r
+       }\r
+       \r
+       //\r
+       private Integer put(String key, Integer value) {\r
+               // マップのリフレッシュ\r
+               Set<String> keyset = pMap.keySet();\r
+               String[] kd = new String[keyset.size()];\r
+               int i=0;\r
+               for ( String k : keyset ) {\r
+                       kd[i++] = k;\r
+               }\r
+               String curdt = CommonUtils.getDateTimeYMD(-REMAIN_CACHE).substring(0,12);\r
+               for ( String k : kd ) {\r
+                       if ( k.substring(12).compareTo(curdt) >= 0) {\r
+                               if (debug) System.out.println(DBGID+"有効なキャッシュです: キャッシュ削除期限="+curdt+" キャッシュ上の番組の開始日時="+k.substring(12));\r
+                               continue;\r
+                       }\r
+                       \r
+                       if (debug) System.out.println(DBGID+"期限切れのキャッシュを削除しました: キャッシュ削除期限="+curdt+" キャッシュ上の番組の開始日時="+k.substring(12));\r
+                       pMap.remove(k);\r
+               }\r
+               \r
+               // 追加\r
+               return pMap.put(key, value);\r
+       }\r
+       \r
+       //\r
+       private boolean load() {\r
+               if ( ! new File(pFILE).exists() ) {\r
+                       return true;\r
+               }\r
+               \r
+               @SuppressWarnings("unchecked")\r
+               HashMap<String,Integer> tmp = (HashMap<String, Integer>) CommonUtils.readXML(pFILE);\r
+               if ( tmp == null ) {\r
+                       return false;\r
+               }\r
+               \r
+               pMap = tmp;\r
+               \r
+               return true;\r
+       }\r
+       \r
+       //\r
+       private boolean save() {\r
+               if ( ! CommonUtils.writeXML(pFILE, pMap) ) {\r
+                       return false;\r
+               }\r
+               return false;\r
+       }\r
+       \r
+       /*\r
+        *  以下は全部ダミー(non-Javadoc)\r
+        * @see tainavi.TVProgramUtils#clone()\r
+        */\r
+       \r
+       @Override\r
+       public GetEventId clone() {\r
+               return null;\r
+       }\r
+       \r
+       @Override\r
+       public String getTVProgramId() {\r
+               return null;\r
+       }\r
+\r
+       @Override\r
+       public ArrayList<ProgList> getCenters() {\r
+               return null;\r
+       }\r
+\r
+       @Override\r
+       public ArrayList<Center> getCRlist() {\r
+               return null;\r
+       }\r
+\r
+       @Override\r
+       public ArrayList<Center> getSortedCRlist() {\r
+               return null;\r
+       }\r
+\r
+       @Override\r
+       public void setSortedCRlist() {\r
+       }\r
+\r
+       @Override\r
+       public void setExtension(String spoexSearchStart, String spoexSearchEnd,\r
+                       boolean spoexLimitation, ArrayList<SearchKey> extKeys) {\r
+       }\r
+\r
+       @Override\r
+       public void abon(ArrayList<String> ngword) {\r
+       }\r
+\r
+       @Override\r
+       public String chkComplete() {\r
+               return null;\r
+       }\r
+\r
+       @Override\r
+       public ProgType getType() {\r
+               return null;\r
+       }\r
+\r
+       @Override\r
+       public ProgSubtype getSubtype() {\r
+               return null;\r
+       }\r
+\r
+       @Override\r
+       public void loadProgram(String areaCode, boolean force) {\r
+       }\r
+\r
+       @Override\r
+       public int getTimeBarStart() {\r
+               return 0;\r
+       }\r
+\r
+       @Override\r
+       public void setExpandTo8(boolean b) {\r
+       }\r
+\r
+       @Override\r
+       public void setUseDetailCache(boolean b) {\r
+       }\r
+\r
+       @Override\r
+       public ArrayList<AreaCode> getAClist() {\r
+               return null;\r
+       }\r
+\r
+       @Override\r
+       public void loadAreaCode() {\r
+       }\r
+\r
+       @Override\r
+       public void saveAreaCode() {\r
+       }\r
+\r
+       @Override\r
+       public String getDefaultArea() {\r
+               return null;\r
+       }\r
+\r
+       @Override\r
+       public String getArea(String code) {\r
+               return null;\r
+       }\r
+\r
+       @Override\r
+       public String getCode(String Area) {\r
+               return null;\r
+       }\r
+\r
+       @Override\r
+       public String setSelectedAreaByName(String area) {\r
+               return null;\r
+       }\r
+\r
+       @Override\r
+       public String getSelectedArea() {\r
+               return null;\r
+       }\r
+\r
+       @Override\r
+       public String getSelectedCode() {\r
+               return null;\r
+       }\r
+\r
+       @Override\r
+       public void loadCenter(String area, boolean force) {\r
+       }\r
+\r
+       @Override\r
+       public boolean saveCenter() {\r
+               return false;\r
+       }\r
+\r
+       @Override\r
+       public void setUserAgent(String s) {\r
+       }\r
+\r
+       @Override\r
+       public void setProgDir(String s) {\r
+       }\r
+\r
+       @Override\r
+       public void setCacheExpired(int h) {\r
+       }\r
+\r
+       /*\r
+       @Override\r
+       public void setProgressArea(StatusWindow o) {\r
+       }\r
+\r
+       @Override\r
+       public void setChConv(ChannelConvert chconv) {\r
+       }\r
+       */\r
+\r
+       @Override\r
+       public void setContinueTomorrow(boolean b) {\r
+       }\r
+\r
+       @Override\r
+       public void setSplitEpno(boolean b) {\r
+       }\r
+\r
+       @Override\r
+       public boolean setOptString(String s) {\r
+               return false;\r
+       }\r
+\r
+       @Override\r
+       public String getOptString() {\r
+               return null;\r
+       }\r
+\r
+}\r
diff --git a/TinyBannavi/src/tainavi/GetRDStatus.java b/TinyBannavi/src/tainavi/GetRDStatus.java
new file mode 100644 (file)
index 0000000..9b29cda
--- /dev/null
@@ -0,0 +1,518 @@
+package tainavi;\r
+\r
+import java.io.DataInputStream;\r
+import java.io.DataOutputStream;\r
+import java.io.IOException;\r
+import java.net.InetSocketAddress;\r
+import java.net.Socket;\r
+import java.net.SocketTimeoutException;\r
+import java.util.regex.Matcher;\r
+import java.util.regex.Pattern;\r
+\r
+/**\r
+ * \r
+ */\r
+public class GetRDStatus implements Cloneable {\r
+\r
+       private byte[] bb = new byte[65536];\r
+       private byte[] bb2 = new byte[231];\r
+       \r
+       public String title = "";\r
+       public String enc = "";\r
+       public String ch = "";\r
+       public String mod = "";\r
+       public String typ = "";\r
+       public String okk = "";\r
+       public String unk = "";\r
+       public String dvd = "";\r
+       public String opn = "";\r
+       public String ply = "";\r
+       public String dig = "";\r
+       public int title_no = 0;\r
+       public int title_len = 0;\r
+       public String title_len_s = "";\r
+       public int chapter = 0;\r
+       public String chapter_name = "";\r
+       public int time_all = 0;\r
+       public String time_all_s = "";\r
+       public int time_chap = 0;\r
+       public String time_chap_s = "";\r
+       public String title_chno = "";\r
+       public String title_chname = "";\r
+       public String title_date = "";\r
+       public String title_time = "";\r
+       public String title_gnr = "";\r
+       public String title_chcode = "";\r
+       \r
+       private final int port = 1048;\r
+\r
+       //byte[] dummy  = {0x00,0x0c,0x00,0x00,0x00,0x00,0x00,0x40,0x00,0x00,0x00,0x00};\r
+       //byte[] init   = {0x01,0x79,0x00,0x00,(byte) 0xff,(byte) 0xff,(byte) 0xff,(byte) 0xff};\r
+       //byte[] init2  = {0x00,(byte) 0xe9,0x00,0x00,0x00,0x02,(byte) 0xbc,(byte) 0x81};\r
+       //byte[] none   = {0x00,0x08,0x00,0x00,0x00,0x00,0x00,0x00};\r
+       //byte[] chgchA = {0x00,0x11,0x00,0x00,0x00,0x02,0x00,(byte) 0x80};\r
+       //byte[] chgchB = {0x00,0x0D,0x00,0x00,0x00,0x02,0x00,0x00};\r
+       //byte[] chgTit = {0x00,(byte) 0xE0,0x00,0x00,0x00,0x00,(byte) 0xBC,0x01};\r
+       \r
+       // 現在どのチャンネルを見ているのか確認する\r
+       public String getCurChannel(String host) {\r
+               \r
+               Socket sock = null;\r
+               DataOutputStream out = null;\r
+               DataInputStream in = null;\r
+\r
+               if (sock == null) {\r
+                       \r
+                       sock = new Socket();\r
+                       \r
+                       try {\r
+                               sock.setSoTimeout(1*1000);\r
+                               \r
+                               sock.connect(new InetSocketAddress(host,port),1000);\r
+                               \r
+                               out = new DataOutputStream(sock.getOutputStream());\r
+                               in = new DataInputStream(sock.getInputStream());\r
+                               \r
+                       } catch (SocketTimeoutException e) {\r
+                               System.err.println("ConnectException : Connection timeout");\r
+                               ply = "";\r
+                               sock = null;\r
+                               return null;\r
+                       } catch (IOException e) {\r
+                               e.printStackTrace();\r
+                               sock = null;\r
+                               return null;\r
+                       }\r
+               }\r
+               \r
+               while (true) {\r
+                       try {\r
+                               \r
+                               /*\r
+                               out.writeByte(0x10);\r
+                               out.writeByte(0x48);\r
+                               out.flush();\r
+                               */\r
+                               \r
+                               byte[] hdr = new byte[2];\r
+                               in.read(hdr,0,2);\r
+                               \r
+                               int datlen = b2d(new byte[] { 0, 0, hdr[0],hdr[1] });\r
+                               \r
+                               // 例外回避\r
+                               if (datlen <= 1) {\r
+                                       // 終了する\r
+                                       break;\r
+                               }\r
+\r
+                               byte[] b = new byte[datlen-2];\r
+                               in.read(b,0,datlen-2);\r
+                               \r
+                               switch (datlen) {\r
+                               case 8:         // none\r
+                                       System.out.println(" <-none");\r
+                                       break;\r
+                                       \r
+                               case 12:        // dummy\r
+                                       System.out.println(" <-dummy");\r
+                                       break;\r
+                                       \r
+                               case 233:       // init2\r
+                                       System.out.println(" <-init2");\r
+                                       \r
+                                       for ( int x=6; x<231; x++ ) {\r
+                                               if (bb2[x] != b[x]) {\r
+                                                       System.err.println(x+": "+Integer.valueOf(bb2[x])+" -> "+Integer.valueOf(b[x]));\r
+                                               }\r
+                                               \r
+                                               bb2[x] = b[x];\r
+                                       }\r
+                                       break;\r
+                                       \r
+                               default:\r
+                                       if (datlen >= 373) {\r
+                                               // A600以降(共通部)\r
+                                               {\r
+                                                       System.out.println(" <-init");\r
+                                                       \r
+                                                       int typd = b2d(new byte[] { b[6],b[7],b[8],b[9] });\r
+                                                       if (typd == 0) {\r
+                                                               typ = "DVD";\r
+                                                       }\r
+                                                       else if (typd == 1) {\r
+                                                               typ = "HDD";\r
+                                                       }\r
+                                                       else {\r
+                                                               typ = "UNKNOWN("+typd+")";\r
+                                                       }\r
+                                                       \r
+                                                       int okkd = b2d(new byte[] { b[10],b[11],b[12],b[13] });\r
+                                                       if (okkd == 0) {\r
+                                                               okk = "なし";\r
+                                                       }\r
+                                                       else if (okkd == 1) {\r
+                                                               okk = "追っかけ再生";\r
+                                                       }\r
+                                                       else if (okkd == 2) {\r
+                                                               okk = "TVお好み再生";\r
+                                                       }\r
+                                                       else {\r
+                                                               okk = "UNKNOWN("+okkd+")";\r
+                                                       }\r
+                                                       \r
+                                                       int unkd = b2d(new byte[] { b[14],b[15],b[16],b[17] });\r
+                                                       if (unkd == 0) {\r
+                                                               unk = "";\r
+                                                       }\r
+                                                       else if (unkd == 1) {\r
+                                                               unk = "録画";\r
+                                                       }\r
+                                                       else if (unkd == 2) {\r
+                                                               unk = "再生";\r
+                                                       }\r
+                                                       else if (unkd == 3) {\r
+                                                               unk = "録画+再生";\r
+                                                       }\r
+                                                       else {\r
+                                                               unk = "UNKNOWN("+unkd+")";\r
+                                                       }\r
+                                                       \r
+                                                       int dvdd = b2d(new byte[] { b[18],b[19],b[20],b[21] });\r
+                                                       if (dvdd == 0) {\r
+                                                               dvd = "";\r
+                                                       }\r
+                                                       else if (dvdd == 1) {\r
+                                                               dvd = "録画";\r
+                                                       }\r
+                                                       else if (dvdd == 2) {\r
+                                                               dvd = "再生";\r
+                                                       }\r
+                                                       else if (dvdd == 3) {\r
+                                                               dvd = "録画+再生";\r
+                                                       }\r
+                                                       else {\r
+                                                               dvd = "UNKNOWN("+dvdd+")";\r
+                                                       }\r
+                                                       \r
+                                                       int plyd = b2d(new byte[] { b[22],b[23],b[24],b[25] });\r
+                                                       if (plyd == 1) {\r
+                                                               ply = "録画";\r
+                                                       }\r
+                                                       else if (plyd == 2) {\r
+                                                               ply = "録画一時停止";\r
+                                                       }\r
+                                                       else if (plyd == 3) {\r
+                                                               ply = "停止";\r
+                                                       }\r
+                                                       else if (plyd == 4) {\r
+                                                               ply = "再生";\r
+                                                       }\r
+                                                       else if (plyd == 5) {\r
+                                                               ply = "一時停止";\r
+                                                       }\r
+                                                       else if (plyd == 6) {\r
+                                                               ply = "早送り";\r
+                                                       }\r
+                                                       else if (plyd == 7) {\r
+                                                               ply = "巻戻し";\r
+                                                       }\r
+                                                       else if (plyd == 8) {\r
+                                                               ply = "スロー送り";\r
+                                                       }\r
+                                                       else if (plyd == 9) {\r
+                                                               ply = "スロー戻し";\r
+                                                       }\r
+                                                       else if (plyd == 10) {\r
+                                                               ply = "コマ送り";\r
+                                                       }\r
+                                                       else if (plyd == 11) {\r
+                                                               ply = "コマ戻し";\r
+                                                       }\r
+                                                       else if (plyd == 12) {\r
+                                                               ply = "読込み中";\r
+                                                       }\r
+                                                       else if (plyd == 13) {\r
+                                                               ply = "処理中";\r
+                                                       }\r
+                                                       else if (plyd == 15) {\r
+                                                               ply = "ダビング";\r
+                                                       }\r
+                                                       else if (plyd == 24) {\r
+                                                               ply = "ネットdeレック";\r
+                                                       }\r
+                                                       else {\r
+                                                               ply = "不明("+plyd+")";\r
+                                                       }\r
+                                                       \r
+                                                       int opnd = b2d(new byte[] { b[26],b[27],b[28],b[29] });\r
+                                                       if (opnd == 0) {\r
+                                                               opn = "OPEN";\r
+                                                       }\r
+                                                       else if (opnd == 1) {\r
+                                                               opn = "CLOSE";\r
+                                                       }\r
+                                                       else if (opnd == 7) {\r
+                                                               opn = "LOADED";\r
+                                                       }\r
+                                                       else if (opnd == 15) {\r
+                                                               opn = "BD-RE(?)";\r
+                                                       }\r
+                                                       else {\r
+                                                               opn = "UNKNOWN("+opnd+")";\r
+                                                       }\r
+                                                       \r
+                                                       int modd = b2d(new byte[] { b[30],b[31],b[32],b[33] });\r
+                                                       if (modd == 0x00) {\r
+                                                               mod = "なし";\r
+                                                       }\r
+                                                       else if (modd == 1) {\r
+                                                               mod = "見るナビ";\r
+                                                       }\r
+                                                       else if (modd == 3) {\r
+                                                               mod = "編集ナビ";\r
+                                                       }\r
+                                                       else if (modd == 5) {\r
+                                                               mod = "設定メニュー";\r
+                                                       }\r
+                                                       else if (modd == 6) {\r
+                                                               mod = "番組ナビ";\r
+                                                       }\r
+                                                       else if (modd == 7) {\r
+                                                               mod = "スタートメニュー";\r
+                                                       }\r
+                                                       else if (modd == 8) {\r
+                                                               mod = "見ながら";\r
+                                                       }\r
+                                                       else if (modd == 9) {\r
+                                                               mod = "簡単ダビング";\r
+                                                       }\r
+                                                       else if (modd == 10) {\r
+                                                               mod = "はじめての設定";\r
+                                                       }\r
+                                                       else if (modd == 11) {\r
+                                                               mod = "番組説明";\r
+                                                       }\r
+                                                       else if (modd == 13) {\r
+                                                               mod = "つぎこれ";\r
+                                                       }\r
+                                                       else {\r
+                                                               mod = "不明("+modd+")";\r
+                                                       }\r
+                                                       \r
+                                                       time_all = b2d(new byte[] { b[46],b[47],b[48],b[49] });\r
+                                                       \r
+                                                       time_chap = b2d(new byte[] { b[50],b[51],b[52],b[53] });\r
+                                                       \r
+                                                       title_no = b2d(new byte[] { b[54],b[55],b[56],b[57] });\r
+                                                       \r
+                                                       title = new String(b,58,96,"MS932");\r
+                                                       int idx = title.indexOf('\0');\r
+                                                       if (idx != -1) {\r
+                                                               title = title.substring(0,idx);\r
+                                                       }\r
+                                                       \r
+                                                       chapter = b2d(new byte[] { b[154],b[155],b[156],b[157] });\r
+                                                       \r
+                                                       chapter_name = new String(b,158,96,"MS932");\r
+                                                       idx = chapter_name.indexOf('\0');\r
+                                                       if (idx != -1) {\r
+                                                               chapter_name = chapter_name.substring(0,idx);\r
+                                                       }\r
+                                                       \r
+                                                       title_len = b2d(new byte[] { b[354],b[355],b[356],b[357] });\r
+                                                       \r
+                                                       title_len_s = getHMS(title_len);\r
+                                                       time_all_s = getHMS(time_all);\r
+                                                       time_chap_s = getHMS(time_chap);\r
+                                                       \r
+                                                       int encd = b2d(new byte[] { b[358],b[359],b[360],b[361] });\r
+                                                       if (encd == 0x04) {\r
+                                                               enc = "TS1";\r
+                                                       }\r
+                                                       else if (encd == 0x05) {\r
+                                                               enc = "TS2";\r
+                                                       }\r
+                                                       else if (encd == 0x06) {\r
+                                                               enc = "RE";\r
+                                                       }\r
+                                                       else {\r
+                                                               enc = "不明("+encd+")";\r
+                                                       }\r
+                                                       \r
+                                                       int digd = b2d(new byte[] { b[34],b[35],b[36],b[37] });\r
+                                                       if (digd == 0) {\r
+                                                               // デジタル放送\r
+                                                               ch = new String(b,362,5);\r
+                                                               idx = ch.indexOf('\0');\r
+                                                               if (idx != -1) {\r
+                                                                       ch = ch.substring(0,idx);\r
+                                                               }\r
+                                                       }\r
+                                                       else if (digd < 200) {\r
+                                                               ch = String.format("CH %d", digd);\r
+                                                       }\r
+                                                       else {\r
+                                                               ch = String.format("L %d", digd%200);\r
+                                                       }\r
+                                               }\r
+                                               // BZ700世代以降\r
+                                               if (datlen >= 462) {\r
+                                                       int idx = 0;\r
+                                                       \r
+                                                       title_chno = new String(b,371,5,"MS932");\r
+                                                       idx = title_chno.indexOf('\0');\r
+                                                       if (idx != -1) {\r
+                                                               title_chno = title_chno.substring(0,idx);\r
+                                                       }\r
+                                                       \r
+                                                       title_chname = new String(b,378,42,"MS932");\r
+                                                       idx = title_chname.indexOf('\0');\r
+                                                       if (idx != -1) {\r
+                                                               title_chname = title_chname.substring(0,idx);\r
+                                                       }\r
+                                                       \r
+                                                       title_date = new String(b,424,10);\r
+                                                       idx = title_date.indexOf('\0');\r
+                                                       if (idx != -1) {\r
+                                                               title_date = title_date.substring(0,idx);\r
+                                                       }\r
+                                                       \r
+                                                       title_time = new String(b,436,5);\r
+                                                       idx = title_time.indexOf('\0');\r
+                                                       if (idx != -1) {\r
+                                                               title_time = title_time.substring(0,idx);\r
+                                                       }\r
+                                                       \r
+                                                       int title_gnrd = b2d(new byte[] { 0,0,0,b[447] });\r
+                                                       if (title_gnrd == 0x00) {\r
+                                                               title_gnr = "ジャンルなし または ニュース報道";\r
+                                                       }\r
+                                                       else if (title_gnrd == 1) {\r
+                                                               title_gnr = "スポーツ";\r
+                                                       }\r
+                                                       else if (title_gnrd == 2) {\r
+                                                               title_gnr = "情報ワイドショー";\r
+                                                       }\r
+                                                       else if (title_gnrd == 3) {\r
+                                                               title_gnr = "ドラマ";\r
+                                                       }\r
+                                                       else if (title_gnrd == 4) {\r
+                                                               title_gnr = "音楽";\r
+                                                       }\r
+                                                       else if (title_gnrd == 5) {\r
+                                                               title_gnr = "バラエティ";\r
+                                                       }\r
+                                                       else if (title_gnrd == 6) {\r
+                                                               title_gnr = "映画";\r
+                                                       }\r
+                                                       else if (title_gnrd == 7) {\r
+                                                               title_gnr = "アニメ/特撮";\r
+                                                       }\r
+                                                       else if (title_gnrd == 8) {\r
+                                                               title_gnr = "ドキュメンタリー教養";\r
+                                                       }\r
+                                                       else if (title_gnrd == 9) {\r
+                                                               title_gnr = "劇場公演";\r
+                                                       }\r
+                                                       else if (title_gnrd == 10) {\r
+                                                               title_gnr = "趣味教育";\r
+                                                       }\r
+                                                       else if (title_gnrd == 11) {\r
+                                                               title_gnr = "福祉";\r
+                                                       }\r
+                                                       else if (title_gnrd == 15) {\r
+                                                               title_gnr = "その他";\r
+                                                       }\r
+                                                       else {\r
+                                                               title_gnr = "不明("+title_gnrd+")";\r
+                                                       }\r
+                                                       \r
+                                                       Matcher ma = null;\r
+                                                       ma = Pattern.compile("(\\d+)").matcher(title_chno);\r
+                                                       if (ma.find()) {\r
+                                                               int code1 = b2d(new byte[] { 0,0,b[452],b[453] });\r
+                                                               int code2 = b2d(new byte[] { 0,0,b[454],b[455] });\r
+                                                               Matcher mb = null;\r
+                                                               mb = Pattern.compile("^L").matcher(title_chno);\r
+                                                               if (mb.find()) {\r
+                                                                       title_chcode = String.format("%s:%08d:%03d", ma.group(1), code1, code2);\r
+                                                               } else {\r
+                                                                       if (code1 != 0 || code2 != 0) {\r
+                                                                               title_chcode = String.format("%s:%d:%d", ma.group(1), code1, code2);\r
+                                                                       } else {\r
+                                                                               title_chcode = "不明";\r
+                                                                       }\r
+                                                               }\r
+                                                       } else {\r
+                                                               title_chcode = "不明";\r
+                                                       }\r
+                                               }\r
+                                               \r
+                                               // 項目の変化を見るための差分出力(デバッグ用)\r
+                                               for ( int x=6; x<datlen-2; x++ ) {\r
+                                                       if ( x == 9 || x == 25 || x == 29 || x ==  33 || x == 157 || x == 361 ||\r
+                                                                       (x >= 34 && x <= 37) || (x >= 46 && x <= 49) || (x >= 50 && x <= 53) || (x >= 54 && x <= 57) || (x >= 154 && x <= 157) || (x >= 354 && x <= 357) ||\r
+                                                                       (x >= 58 && x <= 58+95) || (x >= 362 && x <= 366)) {\r
+                                                               continue;\r
+                                                       }\r
+                                                       if (bb[x] != b[x]) {\r
+                                                               System.err.println(x+": "+Integer.valueOf(bb[x])+" -> "+Integer.valueOf(b[x]));\r
+                                                       }\r
+                                                       \r
+                                                       bb[x] = b[x];\r
+                                               }\r
+                                               \r
+                                               //sock.close();\r
+                                               //sock = null;\r
+                                               return(new String(b,361,6));\r
+                                       }\r
+                                       else {\r
+                                               // 旧default:\r
+                                               System.out.println(" <-unknown("+datlen+")");\r
+                                               \r
+                                               // 永久ループではなく、373バイト未満の未知のサイズのデータはエラーにしてしまうことにした\r
+                                               //sock.close();\r
+                                               //sock = null;\r
+                                               return null;\r
+                                       }\r
+                               }\r
+                       }\r
+                       catch (SocketTimeoutException e) {\r
+                               e.printStackTrace();\r
+                               return null;\r
+                       }\r
+                       catch (IOException e) {\r
+                               e.printStackTrace();\r
+                               return null;\r
+                       }\r
+                       finally {\r
+                               CommonUtils.closing(sock);\r
+                       }\r
+               }\r
+               \r
+               return null;\r
+       }\r
+       \r
+       public String getHMS(int t)     {\r
+               String flg = "";\r
+               if ( t < 0 ) {\r
+                       t = -t;\r
+                       flg = "-";\r
+               }\r
+               \r
+               int s = t % 60;\r
+               int m = (t-s)/60 % 60;\r
+               int h = (t - m * 60 - s)/3600;\r
+               \r
+               return String.format("%s%02d:%02d:%02d", flg,h,m,s);\r
+       }\r
+       \r
+       private int b2d(byte[] data) {\r
+               return( (((data[0] & 0x80) == 0)?(1):(-1)) * (((data[0] & 0x7f) << 24) | ((data[1] & 0xff) << 16) | ((data[2] & 0xff) << 8) | (data[3] & 0xff)));\r
+       }\r
+       \r
+       //\r
+       public GetRDStatus clone() {\r
+               return this;\r
+       }\r
+}\r
diff --git a/TinyBannavi/src/tainavi/HDDRecorder.java b/TinyBannavi/src/tainavi/HDDRecorder.java
new file mode 100644 (file)
index 0000000..00fda8b
--- /dev/null
@@ -0,0 +1,363 @@
+package tainavi;\r
+\r
+import java.util.ArrayList;\r
+\r
+/**\r
+ * レコーダプラグインに実装されるべきインタフェースです。\r
+ * @see HDDRecorderUtils\r
+ */\r
+public interface HDDRecorder {\r
+\r
+       // 定数\r
+       \r
+       /**\r
+        * 繰り返し予約を表す文字列定数。(毎日曜日~毎日)\r
+        */\r
+       public static final String[] RPTPTN = {"毎日曜日","毎月曜日","毎火曜日","毎水曜日","毎木曜日","毎金曜日","毎土曜日","毎月~木","毎月~金","毎月~土","毎日"};\r
+       \r
+       // enum使えよ!!\r
+       public static final int RPTPTN_ID_SAT = 6;\r
+       public static final int RPTPTN_ID_MON2THU = 7;\r
+       public static final int RPTPTN_ID_MON2FRI = 8;\r
+       public static final int RPTPTN_ID_MON2SAT = 9;\r
+       public static final int RPTPTN_ID_EVERYDAY = 10;\r
+       public static final int RPTPTN_ID_BYDATE = 11;\r
+       \r
+       /**\r
+        * レコーダの種別を表す定数\r
+        */\r
+       public static enum RecType { RECORDER, EPG, MAIL, CALENDAR, TUNER, NULL };\r
+       \r
+       /**\r
+        * 録画結果と予約一覧の引き当てを行う範囲の最大\r
+        */\r
+       public static final int SCOPEMAX = 366;\r
+\r
+       // 種族の特性\r
+       public String getRecorderId();\r
+       \r
+       public RecType getType();\r
+       \r
+       public ArrayList<TextValueSet> getVideoRateList();\r
+       public ArrayList<TextValueSet> getAudioRateList();\r
+       public ArrayList<TextValueSet> getFolderList();\r
+       public ArrayList<TextValueSet> getEncoderList();\r
+       public ArrayList<TextValueSet> getDVDCompatList();\r
+       public ArrayList<TextValueSet> getDeviceList();\r
+       public ArrayList<TextValueSet> getXChapter();\r
+       public ArrayList<TextValueSet> getMsChapter();\r
+       public ArrayList<TextValueSet> getMvChapter();\r
+       public ArrayList<TextValueSet> getChValue();\r
+       public ArrayList<TextValueSet> getChType();\r
+       public ArrayList<TextValueSet> getAspect();\r
+       public ArrayList<TextValueSet> getBVperf();\r
+       public ArrayList<TextValueSet> getLVoice();\r
+       public ArrayList<TextValueSet> getAutodel();\r
+       \r
+       public String getLabel_Videorate();\r
+       public String getLabel_Audiorate();\r
+       public String getLabel_Folder();\r
+       public String getLabel_Device();\r
+       public String getLabel_DVDCompat();\r
+       public String getLabel_XChapter();\r
+       public String getLabel_MsChapter();\r
+       public String getLabel_MvChapter();\r
+       public String getLabel_Aspect();\r
+       public String getLabel_BVperf();\r
+       public String getLabel_LVoice();\r
+       public String getLabel_Autodel();\r
+       \r
+       public String text2value(ArrayList<TextValueSet> tvs, String text);\r
+       public String value2text(ArrayList<TextValueSet> tvs, String value);\r
+\r
+       \r
+       // 個体の特性 \r
+       public String getIPAddr();\r
+       public void setIPAddr(String s);\r
+       public String getPortNo();\r
+       public void setPortNo(String s);\r
+       public String getUser();\r
+       public void setUser(String s);\r
+       public String getPasswd();\r
+       public void setPasswd(String s);\r
+       public String getColor(String tuner);\r
+       public void setColor(String s);\r
+       public String getMacAddr();\r
+       public void setMacAddr(String s);\r
+       public String getBroadcast();\r
+       public void setBroadcast(String s);\r
+       \r
+       /**\r
+        * デバッグ用\r
+        */\r
+       public ArrayList<String> getColors();\r
+       \r
+       /**\r
+        * 仮想エンコーダ数(チューナ数)を返します。DIGAなどレコーダからエンコーダ数を取得できない場合に使用します。\r
+        */\r
+       public int getTunerNum();\r
+       \r
+       /**\r
+        * 仮想エンコーダ数(チューナ数)を設定します。DIGAなどレコーダからエンコーダ数を取得できない場合に使用します。\r
+        */\r
+       public void setTunerNum(int n);\r
+\r
+       \r
+       \r
+       /*\r
+        * 予約一覧系\r
+        */\r
+       \r
+       /**\r
+        * 特定の予約を返します。\r
+        */\r
+       public ReserveList getReserveList(String rsvId);\r
+       \r
+       /**\r
+        * 予約一覧を返します。\r
+        */\r
+       public ArrayList<ReserveList> getReserves();\r
+       \r
+       /**\r
+        * 予約一覧を返します。\r
+        */\r
+       public ArrayList<RecordedInfo> getRecorded();\r
+       \r
+       /**\r
+        * 過去日の情報を削除したり、繰り返し予約の次回実行予定日を設定したりします。\r
+        * @see HDDRecorderUtils#refreshReserves()\r
+        */\r
+       public void refreshReserves();\r
+       \r
+       \r
+       \r
+       /*\r
+        * オプション系\r
+        */\r
+       \r
+       /**\r
+        * <P>予約一覧へのアクセスだけでは予約の詳細がわからないレコーダなので詳細情報の個別取得が必要か?どうかを返します。\r
+        * <P>ただし、これをtrueにするのは個別取得に時間がかかる(旧RD系など)だけで、高速なもの(TvRockやEDCBなど)ではfalseでかまいません。\r
+        * @see #GetRdReserveDetails()\r
+        */\r
+       public boolean isThereAdditionalDetails();\r
+       \r
+       /**\r
+        * 自動エンコーダ選択を使用するかどうかを返します。\r
+        */\r
+       public boolean isAutoEncSelectEnabled();\r
+       \r
+       /**\r
+        * 繰り返し予約をサポートしているかどうかを返します。\r
+        */\r
+       public boolean isRepeatReserveSupported();\r
+       \r
+       /**\r
+        * 番組追従の変更をサポートしているかどうかを返します。\r
+        */\r
+       public boolean isPursuesEditable();\r
+       \r
+       /**\r
+        * タイトル自動補完をサポートしているかどうかを返します。\r
+        */\r
+       public boolean isAutocompleteSupported();\r
+       \r
+       /**\r
+        * チャンネル操作をサポートしているかどうかを返します。\r
+        */\r
+       public boolean isChangeChannelSupported();\r
+       \r
+       /**\r
+        * Googleカレンダープラグインなど、通常操作用には選択できないプラグインです。\r
+        */\r
+       public boolean isBackgroundOnly();\r
+\r
+       /**\r
+        * 「レコーダの放送局名」にchvalueの値を利用できる\r
+        */\r
+       public boolean isChValueAvailable();\r
+\r
+       /**\r
+        * 「レコーダの放送局名」と「放送局コード」に異なる値を設定する必要がある(isChValueAvailable()==trueなら通常使われない)\r
+        */\r
+       public boolean isChCodeNeeded();\r
+\r
+       /**\r
+        * レコーダの放送局名すら必要ない(NULL)\r
+        */\r
+       public boolean isRecChNameNeeded();\r
+       \r
+       /**\r
+        * 放送波の種別設定を必要とする(TvRock)\r
+        */\r
+       public boolean isBroadcastTypeNeeded();\r
+       \r
+       /**\r
+        *  フリーワードオプションの処理(設定)\r
+        */\r
+       public boolean setOptString(String s);\r
+       \r
+       /**\r
+        *  フリーワードオプションの処理(記録)\r
+        */\r
+       public String getOptString();\r
+       \r
+       \r
+       /*\r
+        * 識別系\r
+        */\r
+       \r
+       /**\r
+        * 実際のレコーダを示すユニークID返却します。\r
+        * @return IP:PORT:RECORDER_ID (基本形。違う形式もあるかもしれない。)\r
+        */\r
+       public String Myself();\r
+       \r
+       /**\r
+        * このプラグインが実際のレコーダとマッチするかどうか判定します。\r
+        * @param id : MySelf()で得られる値\r
+        */\r
+       public boolean isMyself(String id);\r
+       \r
+       \r
+       /**\r
+        * \r
+        */\r
+       public String getChDatHelp();\r
+       \r
+       \r
+       /*\r
+        * 動作設定系\r
+        */\r
+       \r
+       /**\r
+        * デバッグログ出力(主にレコーダとのHTTPのやりとり)をON/OFFします。\r
+        */\r
+       public void setDebug(boolean b);\r
+       \r
+       /**\r
+        * 終了時刻と開始時刻が重なる番組を重複として処理するかどうかを指定します。\r
+        * @param b : trueの場合、重複とみましません。\r
+        */\r
+       public void setAdjNotRep(boolean b);\r
+       \r
+       /**\r
+        * カレンダー連携を個別にON/OFFします。\r
+        */\r
+       public void setUseCalendar(boolean b);\r
+       public boolean getUseCalendar();\r
+\r
+       /**\r
+        * カレンダー連携を個別にON/OFFします。\r
+        */\r
+       public void setUseChChange(boolean b);\r
+       public boolean getUseChChange();\r
+\r
+       /**\r
+        * 成功した記録をチェックする範囲\r
+        */\r
+       public void setRecordedCheckScope(int n);\r
+       //public int getRecordedCheckScope();\r
+\r
+       /**\r
+        * 録画結果を残す範囲\r
+        */\r
+       public void setRecordedSaveScope(int n);\r
+       \r
+       /**\r
+        * <P>HTTPアクセス時のUser-Agentの値を設定します。\r
+        * <P>基本的に設定する意味はないでしょう。\r
+        */\r
+       public void setUserAgent(String s);\r
+\r
+       /**\r
+        * 動作状況を出力するステータスウィンドウを設定します。\r
+        */\r
+       public void setProgressArea(StatusWindow o);\r
+       \r
+       \r
+       /*\r
+        *  主要な操作系メソッド\r
+        */\r
+       \r
+       public ChannelCode getChCode();\r
+       \r
+       /**\r
+        * <P>レコーダを起動させます。MACアドレスとブロードキャストアドレスの設定が必要です。\r
+        * <P>基本的にはWOLを実行しますが、DIGAは電源が落ちていてもHTTPサーバが生きているのでHTTPリクエストによる起動が実行されます。\r
+        */\r
+       public void wakeup();\r
+       \r
+       /**\r
+        * レコーダを停止させます。MACアドレスとブロードキャストアドレスの設定が必要です。\r
+        */\r
+       public void shutdown();\r
+\r
+       /**\r
+        * チャンネルを切り替えます。\r
+        * @param Channel : Web番組表の放送局名を指定します。\r
+        */\r
+       public boolean ChangeChannel(String Channel);\r
+\r
+       /**\r
+        * <P>レコーダから各種設定の取得を行います。(全部のレコーダには実装していない)\r
+        * @param force : trueの場合レコーダへのアクセスを強制します。falseの場合キャッシュファイルがあればそちらを利用します。\r
+        * @see #GetRdReserve(boolean)\r
+        * @see #GetRdReserveDetails()\r
+        * @see #GetRdRecorded(boolean)\r
+        */\r
+       public boolean GetRdSettings(boolean force);\r
+       \r
+       /**\r
+        * <P>レコーダから予約一覧(と各種設定の取得を行います。\r
+        * <P>将来的には、各種設定の取得は別メソッドにわけたいところ。\r
+        * @param force : trueの場合レコーダへのアクセスを強制します。falseの場合キャッシュファイルがあればそちらを利用します。\r
+        * @see #GetRdSettings(boolean)\r
+        * @see #GetRdReserveDetails()\r
+        */\r
+       public boolean GetRdReserve(boolean force);\r
+\r
+       /**\r
+        * <P>レコーダから録画結果一覧の取得を行います。\r
+        * @see #GetRdSettings(boolean)\r
+        */\r
+       public boolean GetRdRecorded(boolean force);\r
+       \r
+       /**\r
+        * 詳細情報の個別取得を行います。\r
+        * @see #GetRdReserve(boolean)\r
+        * @see #isThereAdditionalDetails()\r
+        */\r
+       public boolean GetRdReserveDetails();\r
+       \r
+       /**\r
+        * 予約の新規登録を行います。\r
+        */\r
+       public boolean PostRdEntry(ReserveList r);\r
+       \r
+       /**\r
+        * 予約の更新を行います。\r
+        * @param o : 旧情報\r
+        * @param r : 新情報\r
+        */\r
+       public boolean UpdateRdEntry(ReserveList o, ReserveList r);\r
+       \r
+       /**\r
+        * 予約の削除を行います。\r
+        * @param delno : 削除する予約の予約IDを指定します。\r
+        * @see ReserveList#id\r
+        */\r
+       public ReserveList RemoveRdEntry(String delno);\r
+       \r
+       /**\r
+        * 処理の結果に応じて追加のメッセージが取得できます。\r
+        * @return "" : まったくの正常に終わった場合。\r
+        */\r
+       public String getErrmsg();\r
+       \r
+       \r
+       /**\r
+        * クローンとコンピュータの融合体は新たな生命と呼べるのか\r
+        */\r
+       public HDDRecorder clone();\r
+}\r
diff --git a/TinyBannavi/src/tainavi/HDDRecorderList.java b/TinyBannavi/src/tainavi/HDDRecorderList.java
new file mode 100644 (file)
index 0000000..43bd57a
--- /dev/null
@@ -0,0 +1,70 @@
+package tainavi;\r
+\r
+import java.util.ArrayList;\r
+\r
+import tainavi.HDDRecorder.RecType;\r
+\r
+/**\r
+ * {@link HDDRecorder} のリストを実現するクラスです. \r
+ * @version 3.15.4β~\r
+ */\r
+public class HDDRecorderList extends ArrayList<HDDRecorder> {\r
+\r
+       private static final long serialVersionUID = 1L;\r
+\r
+       private static final HDDRecorderList mylist = new HDDRecorderList();\r
+       \r
+       /*\r
+       // 重複チェックをしておく\r
+       @Override\r
+       public boolean add(HDDRecorder r) {\r
+               if ( this.get(r.getRecorderId()).size() > 0 ) {\r
+                       System.err.println("[DEBUG] プラグインのレコーダIDが重複しています: "+r.getRecorderId());   // これは余計だった\r
+               }\r
+               return super.add(r);\r
+       }\r
+       */\r
+       \r
+       // レコーダIDから種類を調べる\r
+       public RecType getRecId2Type(String recId) {\r
+               ArrayList<HDDRecorder> rl = this.get(recId);\r
+               if ( rl.size() > 0 ) {\r
+                       return rl.get(0).getType();\r
+               }\r
+               return RecType.RECORDER;\r
+       }\r
+       \r
+       /**\r
+        *  レコーダIDに合ったプラグイン(種族)を探す\r
+        */\r
+       public HDDRecorderList get(String recId) {\r
+               if ( recId == null ) {\r
+                       return this;\r
+               }\r
+               HDDRecorderList list = new HDDRecorderList();\r
+               for ( HDDRecorder rec : this ) {\r
+                       if ( recId.equals(rec.getRecorderId()) ) {\r
+                               list.add(rec);\r
+                       }\r
+               }\r
+               return list;\r
+       }\r
+       \r
+       /**\r
+        * 実レコーダのプラグイン(個体)を探す\r
+        * @return 本来{@link HDDRecorder}を返すべきだが、呼び出し側の処理を書きやすくするために{@link HDDRecorderList}を返す。よって、==nullではなく.size()==0で確認する。\r
+        * @param myself 「すべて」を指定する場合はNULLをどうぞ\r
+        */\r
+       public HDDRecorderList getMyself(String myself) {\r
+               if (myself == null || myself.length() == 0) {\r
+                       return this;\r
+               }\r
+               mylist.clear();\r
+               for ( HDDRecorder rec : this ) {\r
+                       if ( rec.isMyself(myself) ) {\r
+                               mylist.add(rec);\r
+                       }\r
+               }\r
+               return mylist;\r
+       }\r
+}\r
diff --git a/TinyBannavi/src/tainavi/HDDRecorderUtils.java b/TinyBannavi/src/tainavi/HDDRecorderUtils.java
new file mode 100644 (file)
index 0000000..ca2197e
--- /dev/null
@@ -0,0 +1,1351 @@
+package tainavi;\r
+\r
+import java.io.BufferedReader;\r
+import java.io.File;\r
+import java.io.IOException;\r
+import java.io.InputStreamReader;\r
+import java.io.OutputStreamWriter;\r
+import java.io.UnsupportedEncodingException;\r
+import java.net.Authenticator;\r
+import java.net.DatagramPacket;\r
+import java.net.DatagramSocket;\r
+import java.net.HttpURLConnection;\r
+import java.net.InetSocketAddress;\r
+import java.net.PasswordAuthentication;\r
+import java.net.SocketException;\r
+import java.net.URL;\r
+import java.util.ArrayList;\r
+import java.util.Calendar;\r
+import java.util.Date;\r
+import java.util.GregorianCalendar;\r
+import java.util.Hashtable;\r
+import java.util.List;\r
+import java.util.Map;\r
+import java.util.regex.Matcher;\r
+import java.util.regex.Pattern;\r
+\r
+\r
+/**\r
+ * {@link HDDRecorder}インタフェース をインプルメントしたレコーダプラグインのクラスで利用できる、共有部品の集合です。\r
+ * @version 3.15.4β クラス名を RecorderUtils から HDDRecorderUtils に変更しました。\r
+ */\r
+public class HDDRecorderUtils implements HDDRecorder,Cloneable {\r
+       \r
+       /*******************************************************************************\r
+        * ディープコピーが意外と大変\r
+        ******************************************************************************/\r
+       \r
+       public HDDRecorderUtils clone() {\r
+               try {\r
+                       HDDRecorderUtils ru = (HDDRecorderUtils) super.clone();\r
+                       CommonUtils.FieldCopy(ru, this); // ディープコピーするよ\r
+                       return ru;\r
+               } catch (CloneNotSupportedException e) {\r
+                       throw new InternalError(e.toString());\r
+               }\r
+       }\r
+\r
+       /*******************************************************************************\r
+        * オプション確認\r
+        ******************************************************************************/\r
+       \r
+       @Override\r
+       public boolean isThereAdditionalDetails() { return false; }\r
+       @Override\r
+       public boolean isRepeatReserveSupported() { return true; }\r
+       @Override\r
+       public boolean isPursuesEditable() { return false; }\r
+       @Override\r
+       public boolean isAutocompleteSupported() { return true; }\r
+       @Override\r
+       public boolean isChangeChannelSupported() { return ChangeChannel(null); }\r
+       @Override\r
+       public boolean isBackgroundOnly() { return false; }\r
+       @Override\r
+       public boolean isChValueAvailable() { return false; }\r
+       @Override\r
+       public boolean isChCodeNeeded() { return true; }\r
+       @Override\r
+       public boolean isRecChNameNeeded() { return true; }\r
+       @Override\r
+       public boolean isBroadcastTypeNeeded() { return false; }\r
+       @Override\r
+       public boolean isAutoEncSelectEnabled() { return true; }\r
+       @Override\r
+       public String getChDatHelp() { return ""; }\r
+       \r
+       // フリーテキストによるオプション指定\r
+       @Override\r
+       public boolean setOptString(String s) { return true; }          // ダミー\r
+       @Override\r
+       public String getOptString() { return null; }   // ダミー\r
+       \r
+       \r
+       /*******************************************************************************\r
+        * 定数\r
+        ******************************************************************************/\r
+       \r
+       // メッセージID\r
+       private static final String MSGID = "[レコーダ共通] ";\r
+       private static final String DBGID = "[DEBUG]"+MSGID;\r
+       private static final String ERRID = "[ERROR]"+MSGID;\r
+       \r
+       \r
+       /*******************************************************************************\r
+        * メンバ変数関連\r
+        ******************************************************************************/\r
+       \r
+       // デバッグログを出力するか\r
+       public void setDebug(boolean b) { debug = b; }\r
+       protected boolean getDebug() { return debug; }\r
+       private boolean debug = false;\r
+       \r
+       // 終了時刻と開始時刻が重なる番組を重複扱いするか\r
+       public void setAdjNotRep(boolean b) { adjnotrep = b; }\r
+       private boolean getAdjNotRep() { return adjnotrep; }\r
+       private boolean adjnotrep = false;\r
+\r
+       // カレンダー連携を行うかどうか\r
+       public void setUseCalendar(boolean b) { usecalendar = b; }\r
+       public boolean getUseCalendar() { return usecalendar; }\r
+       private boolean usecalendar = true;\r
+\r
+       // チャンネル操作を行うかどうか\r
+       public void setUseChChange(boolean b) { usechchange = b; }\r
+       public boolean getUseChChange() { return usechchange; }\r
+       private boolean usechchange = true;\r
+\r
+       // 録画完了チェックの範囲\r
+       public void setRecordedCheckScope(int n) { recordedCheckScope = n; }\r
+       protected int getRecordedCheckScope() { return recordedCheckScope; }\r
+       private int recordedCheckScope = 14;\r
+\r
+       // 録画結果一覧の保存期間\r
+       public void setRecordedSaveScope(int n) { recordedSaveScope = n; }\r
+       protected int getRecordedSaveScope() { return recordedSaveScope; }\r
+       private int recordedSaveScope = 90;\r
+       \r
+       // HTTPアクセス時のUser-Agent\r
+       public void setUserAgent(String s) { userAgent = s; }\r
+       //public String getUserAgent() { return userAgent; }\r
+       private String userAgent = "";\r
+       \r
+       \r
+       /*******************************************************************************\r
+        * レコーダーの固有情報\r
+        ******************************************************************************/\r
+       \r
+       public String Myself() { return(getIPAddr()+":"+getPortNo()+":"+getRecorderId()); }\r
+       public boolean isMyself(String myself) { return Myself().equals(myself); }\r
+       \r
+       protected ArrayList<TextValueSet> vrate = new ArrayList<TextValueSet>();\r
+       protected ArrayList<TextValueSet> arate = new ArrayList<TextValueSet>();\r
+       protected ArrayList<TextValueSet> folder = new ArrayList<TextValueSet>();\r
+       protected ArrayList<TextValueSet> encoder = new ArrayList<TextValueSet>();\r
+       protected ArrayList<TextValueSet> dvdcompat = new ArrayList<TextValueSet>();\r
+       protected ArrayList<TextValueSet> device = new ArrayList<TextValueSet>();\r
+       protected ArrayList<TextValueSet> channel = new ArrayList<TextValueSet>();\r
+       protected ArrayList<TextValueSet> xchapter = new ArrayList<TextValueSet>();\r
+       protected ArrayList<TextValueSet> mschapter = new ArrayList<TextValueSet>();\r
+       protected ArrayList<TextValueSet> mvchapter = new ArrayList<TextValueSet>();\r
+       protected ArrayList<TextValueSet> chvalue = new ArrayList<TextValueSet>();\r
+       protected ArrayList<TextValueSet> chtype = new ArrayList<TextValueSet>();\r
+       protected ArrayList<TextValueSet> genre = new ArrayList<TextValueSet>();\r
+\r
+       protected ArrayList<TextValueSet> aspect = new ArrayList<TextValueSet>();\r
+       protected ArrayList<TextValueSet> bvperf = new ArrayList<TextValueSet>();\r
+       protected ArrayList<TextValueSet> lvoice = new ArrayList<TextValueSet>();\r
+       protected ArrayList<TextValueSet> autodel = new ArrayList<TextValueSet>();\r
+       \r
+       public ArrayList<TextValueSet> getVideoRateList() { return(vrate); }\r
+       public ArrayList<TextValueSet> getAudioRateList() { return(arate); }\r
+       public ArrayList<TextValueSet> getFolderList() { return(folder); }\r
+       public ArrayList<TextValueSet> getEncoderList() { return(encoder); }\r
+       public ArrayList<TextValueSet> getDVDCompatList() { return(dvdcompat); }\r
+       public ArrayList<TextValueSet> getDeviceList() { return(device); }\r
+       public ArrayList<TextValueSet> getXChapter() { return(xchapter); }\r
+       public ArrayList<TextValueSet> getMsChapter() { return(mschapter); }\r
+       public ArrayList<TextValueSet> getMvChapter() { return(mvchapter); }\r
+       public ArrayList<TextValueSet> getChValue() { return(chvalue); }\r
+       public ArrayList<TextValueSet> getChType() { return(chtype); }\r
+\r
+       public ArrayList<TextValueSet> getAspect() { return(aspect); }\r
+       public ArrayList<TextValueSet> getBVperf() { return(bvperf); }\r
+       public ArrayList<TextValueSet> getLVoice() { return(lvoice); }\r
+       public ArrayList<TextValueSet> getAutodel() { return(autodel); }\r
+       \r
+       public String getLabel_Videorate() { return null; }\r
+       public String getLabel_Audiorate() { return null; }\r
+       public String getLabel_Folder() { return null; }\r
+       public String getLabel_Device() { return null; }\r
+       public String getLabel_DVDCompat() { return null; }\r
+       public String getLabel_XChapter() { return null; }\r
+       public String getLabel_MsChapter() { return null; }\r
+       public String getLabel_MvChapter() { return null; }\r
+       public String getLabel_Aspect() { return null; }\r
+       public String getLabel_BVperf() { return null; }\r
+       public String getLabel_LVoice() { return null; }\r
+       public String getLabel_Autodel() { return null; }\r
+\r
+       /*******************************************************************************\r
+        * レコーダー操作のための固有情報\r
+        ******************************************************************************/\r
+       \r
+       public String getRecorderId() { return "THIS IS DUMMY METHOD"; }\r
+       \r
+       private String recorderIPAddr = "";\r
+       private String recorderPortNo = "";\r
+       private String recorderUser = "";\r
+       private String recorderPasswd = "";\r
+       private String recorderMacAddr = "";\r
+       private String recorderBroadcast = "";\r
+       private int recorderTunerNum = 0;\r
+\r
+       public String getIPAddr() { return recorderIPAddr; }\r
+       public void setIPAddr(String s) { recorderIPAddr = s; }\r
+       public String getPortNo() { return recorderPortNo; }\r
+       public void setPortNo(String s) { recorderPortNo = s; }\r
+       public String getUser() { return recorderUser; }\r
+       public void setUser(String s) { recorderUser = s; }\r
+       public String getPasswd() { return recorderPasswd; }\r
+       public void setPasswd(String s) { recorderPasswd = s; }\r
+       public String getMacAddr() { return recorderMacAddr; }\r
+       public void setMacAddr(String s) { recorderMacAddr = s; }\r
+       public String getBroadcast() { return recorderBroadcast; }\r
+       public void setBroadcast(String s) { recorderBroadcast = s; }\r
+       public int getTunerNum() { return recorderTunerNum; }\r
+       public void setTunerNum(int n) { recorderTunerNum = n; }\r
+\r
+       // エンコーダ別の配色を取得する\r
+       public String getColor(String tuner) {\r
+               //\r
+               if (tuner != null && tuner.length() != 0) {\r
+                       int idx = 0;\r
+                       for (TextValueSet e : getEncoderList()) {\r
+                               if (tuner.equals(e.getText())) {\r
+                                       if (recorderColorList.size() > idx) {\r
+                                               return recorderColorList.get(idx);\r
+                                       }\r
+                                       break;\r
+                               }\r
+                               idx++;\r
+                       }\r
+               }\r
+               // 適当なエンコーダが見つからない場合は既定値\r
+               return recorderColorList.get(0);\r
+       }\r
+       \r
+       // Envからもらってきたものを分解して保持する\r
+       public void setColor(String s) {\r
+               //\r
+               recorderColorList.clear();\r
+               //\r
+               Matcher ma = null;\r
+               ma = Pattern.compile("^#......$").matcher(s);\r
+               if (ma.find()) {\r
+                       recorderColorList.add(s);\r
+               }\r
+               else {\r
+                       ma = Pattern.compile("(#......);").matcher(s);\r
+                       while (ma.find()) {\r
+                               recorderColorList.add(ma.group(1));\r
+                       }\r
+               }\r
+               return;\r
+       }\r
+\r
+       public ArrayList<String> getColors() { return recorderColorList; }\r
+       \r
+       private ArrayList<String> recorderColorList = new ArrayList<String>();\r
+       \r
+       /**\r
+        * 特定の予約を決め打ちで\r
+        */\r
+       public ReserveList getReserveList(String rsvId) {\r
+               for ( ReserveList rsv : RESERVES ) {\r
+                       if ( rsv.getId() != null && rsv.getId().equals(rsvId) ) {\r
+                               return rsv;\r
+                       }\r
+               }\r
+               return null;\r
+       }\r
+       \r
+       /**\r
+        * 持っている予約をすべて…吐き出させるっ…!\r
+        */\r
+       public ArrayList<ReserveList> getReserves() { return RESERVES; }\r
+       \r
+       private ArrayList<ReserveList> RESERVES = new ArrayList<ReserveList>();\r
+       \r
+       /**\r
+        * 録画済み一覧\r
+        */\r
+       public ArrayList<RecordedInfo> getRecorded() { return RECORDED; }\r
+       protected void setRecorded(ArrayList<RecordedInfo> r ) { RECORDED = r; }\r
+       \r
+       private ArrayList<RecordedInfo> RECORDED = new ArrayList<RecordedInfo>();\r
+       \r
+       /*******************************************************************************\r
+        * 小物\r
+        ******************************************************************************/\r
+       \r
+       // 素直にHashMapつかっておけばよかった\r
+       public String text2value(ArrayList<TextValueSet> tvs, String text) {\r
+               for ( TextValueSet t : tvs ) {\r
+                       if (t.getText().equals(text)) {\r
+                               return(t.getValue());\r
+                       }\r
+               }\r
+               return("");\r
+       }\r
+       public String value2text(ArrayList<TextValueSet> tvs, String value) {\r
+               for ( TextValueSet t : tvs ) {\r
+                       if (t.getValue().equals(value)) {\r
+                               return(t.getText());\r
+                       }\r
+               }\r
+               return("");\r
+       }\r
+       \r
+       protected TextValueSet add2tvs(ArrayList<TextValueSet> tvs, String text, String value) {\r
+               TextValueSet t = new TextValueSet();\r
+               t.setText(text);\r
+               t.setValue(value);\r
+               tvs.add(t);\r
+               return t;\r
+       }\r
+       protected TextValueSet add2tvs(int n, ArrayList<TextValueSet> tvs, String text, String value) {\r
+               TextValueSet t = new TextValueSet();\r
+               t.setText(text);\r
+               t.setValue(value);\r
+               tvs.add(n,t);\r
+               return t;\r
+       }\r
+       \r
+       // 予約日付をId化する(単日以外)\r
+       protected int getRec_pattern_Id(String s) {\r
+               int i = 0;\r
+               for (; i<HDDRecorder.RPTPTN.length;i++) {\r
+                       //System.out.println(s + "->" + HDDRecorder.RPTPTN[i]);\r
+                       if (s.equals(HDDRecorder.RPTPTN[i])) {\r
+                               return(i);\r
+                       }\r
+               }\r
+               return(i);\r
+       }\r
+       \r
+       /* 予約IDが動的に変化するレコーダ向けの処理 */\r
+       private int rsvcnt = 0;\r
+       protected String getUniqId(String rsvId) { return (!rsvId.startsWith("U$"))?(String.format("U$%14s,%05d,%s",CommonUtils.getDateTimeYMD(0),(rsvcnt++)%100000,rsvId)):(rsvId); }\r
+       protected String getRsvId(String uniqId) { return (uniqId.startsWith("U$"))?(uniqId.substring(23)):(uniqId); }\r
+       \r
+\r
+       // 開始日時・終了日時を算出する\r
+       public void getStartEndDateTime(ReserveList r) {\r
+               // \r
+               GregorianCalendar c = CommonUtils.getCalendar(r.getRec_nextdate());\r
+               if ( c != null ) {\r
+                       // ★★★ MM/DDをYYYY/MM/DDに戻す? ★★★\r
+                       c.set(Calendar.MINUTE, Integer.valueOf(r.getAmm()));\r
+                       c.set(Calendar.HOUR_OF_DAY, Integer.valueOf(r.getAhh()));\r
+                       r.setStartDateTime(CommonUtils.getDateTime(c));\r
+       \r
+                       c.add(Calendar.MINUTE, Integer.valueOf(r.getRec_min()));\r
+                       r.setEndDateTime(CommonUtils.getDateTime(c));\r
+               }\r
+       }\r
+       \r
+       //\r
+       public String[] _mmdd2yyyymmdd(String mm, String dd)\r
+       {\r
+               GregorianCalendar c = new GregorianCalendar();\r
+               c.setTime(new Date());\r
+               if ( Integer.valueOf(mm) < c.get(Calendar.MONTH)+1 ) {\r
+                       c.add(Calendar.YEAR, 1);\r
+               }\r
+               \r
+               return (new String[] { String.format("%04d",c.get(Calendar.YEAR)), mm, dd });\r
+       }\r
+       \r
+       //\r
+       public String[] _hhmm2hhmm_min(String ahhmm, String zhhmm)\r
+       {\r
+               String ahh="";\r
+               String amm="";\r
+               String zhh="";\r
+               String zmm="";\r
+               \r
+               Matcher ma = Pattern.compile("^(\\d+):(\\d+)").matcher(ahhmm);\r
+               if (ma.find()) {\r
+                       ahh = String.format("%02d",Integer.valueOf(ma.group(1)));\r
+                       amm = String.format("%02d",Integer.valueOf(ma.group(2)));\r
+               }\r
+               \r
+               ma = Pattern.compile("^(\\d+):(\\d+)").matcher(zhhmm);\r
+               if (ma.find()) {\r
+                       zhh = String.format("%02d",Integer.valueOf(ma.group(1)));\r
+                       zmm = String.format("%02d",Integer.valueOf(ma.group(2)));\r
+               }\r
+               \r
+               int min = Integer.valueOf(zhh)*60+Integer.valueOf(zmm) - (Integer.valueOf(ahh)*60+Integer.valueOf(amm));\r
+               if ( min < 0 ) min += 24*60;\r
+               \r
+               return (new String[] {ahh, amm, zhh, zmm, Integer.toString(min)});\r
+       }\r
+\r
+       // レコーダの設定情報をキャッシュする\r
+       public ArrayList<TextValueSet> TVSload(String filename) {\r
+               File f = new File(filename);\r
+               if ( ! f.exists() ) {\r
+               return new ArrayList<TextValueSet>();\r
+               }\r
+               \r
+               @SuppressWarnings("unchecked")\r
+               ArrayList<TextValueSet> ar = (ArrayList<TextValueSet>) CommonUtils.readXML(filename);\r
+               if ( ar == null ) {\r
+               System.err.println("設定ファイルの読み込みに失敗しました: "+filename);\r
+               return new ArrayList<TextValueSet>();\r
+               }\r
+               \r
+               return ar;\r
+       }\r
+       public void TVSsave(ArrayList<TextValueSet> ar, String filename) {\r
+               if ( ! CommonUtils.writeXML(filename, ar) ) {\r
+               System.err.println("設定ファイルの保存に失敗しました: "+filename);\r
+               }\r
+       }\r
+\r
+       /**\r
+        * レコーダの予約リストをキャッシュする\r
+        * @param rsvedFile\r
+        * @return nullは返さない!\r
+        * @see #ReservesToFile(ArrayList, String)\r
+        */\r
+       protected ArrayList<ReserveList> ReservesFromFile(String rsvedFile) {\r
+               File f = new File(rsvedFile);\r
+               if ( ! f.exists() ) {\r
+               System.out.println("+予約キャッシュはありません: "+rsvedFile);\r
+               return new ArrayList<ReserveList>();\r
+               }\r
+               \r
+               @SuppressWarnings("unchecked")\r
+               ArrayList<ReserveList> tmp = (ArrayList<ReserveList>) CommonUtils.readXML(rsvedFile);\r
+               if ( tmp == null ) {\r
+               System.err.println("予約キャッシュの読み込みに失敗しました: "+rsvedFile);\r
+               return new ArrayList<ReserveList>();\r
+               }\r
+\r
+               /* もういらんやろ\r
+        // 後方互換\r
+        for (ReserveList r : tmp) {\r
+               if (r.getId() == null && r.getNo() > 0) {\r
+                       r.setId(String.valueOf(r.getNo()));\r
+               }\r
+        }\r
+        */\r
+               \r
+        System.out.println("+予約キャッシュを読み込みました("+tmp.size()+"): "+rsvedFile);\r
+        return tmp;\r
+       }\r
+       \r
+       /**\r
+        * @param reserves\r
+        * @param rsvedFile\r
+        * @see #ReservesFromFile(String)\r
+        */\r
+       protected void ReservesToFile(ArrayList<ReserveList> reserves, String rsvedFile) {\r
+               if ( ! CommonUtils.writeXML(rsvedFile, reserves) ) {\r
+               System.err.println("予約キャッシュの保存に失敗しました: "+rsvedFile);\r
+               }\r
+       }\r
+\r
+       /**\r
+        * レコーダの録画結果リストをキャッシュする<BR>\r
+        * ※キャッシュから取得したものはIDがnullでクリアされる\r
+        * @param recedFile\r
+        * @return nullは返さない!\r
+        * @see #RecordedToFile(ArrayList, String)\r
+        */\r
+       protected ArrayList<RecordedInfo> RecordedFromFile(String recedFile) {\r
+               \r
+               File f = new File(recedFile);\r
+               if ( ! f.exists() ) {\r
+               System.out.println("+録画結果キャッシュはありません: "+recedFile);\r
+               return new ArrayList<RecordedInfo>();\r
+               }\r
+               \r
+               @SuppressWarnings("unchecked")\r
+               ArrayList<RecordedInfo> tmp = (ArrayList<RecordedInfo>) CommonUtils.readXML(recedFile);\r
+               if ( tmp == null ) {\r
+               System.err.println("録画結果キャッシュの読み込みに失敗しました: "+recedFile);\r
+               return new ArrayList<RecordedInfo>();\r
+               }\r
+               \r
+               // 期限切れの情報は捨てる\r
+               String critDate = CommonUtils.getDate(CommonUtils.getCalendar(-86400*getRecordedSaveScope()));\r
+               String specialDate = CommonUtils.getDate(CommonUtils.getCalendar("1970/01/01"));\r
+               for ( int i=tmp.size()-1; i>=0; i-- ) {\r
+                       if ( tmp.get(i).getDate().compareTo(critDate) < 0 && tmp.get(i).getDate().compareTo(specialDate) > 0) {\r
+                               // 期限切れ\r
+                               if (debug) System.out.println("録画結果のキャッシュを削除しました: "+tmp.get(i).getDate()+" "+tmp.get(i).getTitle());\r
+                               tmp.remove(i);\r
+                       }\r
+                       else {\r
+                               // オワタ\r
+                               break;\r
+                       }\r
+               }\r
+               \r
+               for ( RecordedInfo ri : tmp ) {\r
+                       // キャッシュから読みだしたものはIDをクリアする\r
+                       ri.setId(null);\r
+                       // 後方互換\r
+                       if ( ri.getCh_orig() == null ) {\r
+                               ri.setCh_orig(ri.getCh_name());\r
+                       }\r
+               }\r
+               \r
+        System.out.println("+録画結果キャッシュを読み込みました("+tmp.size()+"件): "+recedFile);\r
+        return tmp;\r
+       }\r
+       \r
+       /**\r
+        * @param reserves\r
+        * @param recedFile\r
+        * @see #RecordedToFile(ArrayList, String)\r
+        */\r
+       protected void RecordedToFile(ArrayList<RecordedInfo> recorded, String recedFile) {\r
+               if ( ! CommonUtils.writeXML(recedFile, recorded) ) {\r
+               System.err.println("録画結果キャッシュの保存に失敗しました: "+recedFile);\r
+               }\r
+       }\r
+       \r
+       /*\r
+        * \r
+        */\r
+       protected boolean matchReserveV1(ReserveList n, ReserveList o) {\r
+               return (\r
+                               n.getTitle().equals(o.getTitle()) &&\r
+                               n.getChannel().equals(o.getChannel()) &&\r
+                               n.getRec_pattern().equals(o.getRec_pattern()) &&\r
+                               n.getAhh().equals(o.getAhh()) && n.getAmm().equals(o.getAmm())\r
+               );\r
+       }\r
+       public void setReservesV1(ArrayList<ReserveList> r) {\r
+               // ライン入力のチャンネル名を保持する\r
+               //System.out.println(RESERVES.size()+","+r.size());\r
+               for (ReserveList o : RESERVES) {\r
+                       for (ReserveList n : r) {\r
+                               if (matchReserveV1(n, o)) {\r
+                                       // 外部入力以外は知らん\r
+                                       if (o.getCh_name() != null && n.getCh_name() != null && n.getCh_name().startsWith("外部入力")) {\r
+                                               System.out.println("外部入力を次の放送局で置き換えます: "+n.getCh_name()+"->"+o.getCh_name());\r
+                                               n.setCh_name(o.getCh_name());\r
+                                       }\r
+                                       \r
+                                       // 鯛ナビの内部フラグ\r
+                                       n.setAutocomplete(o.getAutocomplete());\r
+                                       // 予約一覧からは取得できない情報\r
+                                       n.setDetail(o.getDetail());\r
+                                       n.setRec_genre(o.getRec_genre());\r
+                                       n.setRec_device(o.getRec_device());\r
+                                       n.setRec_folder(o.getRec_folder());\r
+                                       n.setRec_dvdcompat(o.getRec_dvdcompat());\r
+                                       n.setRec_xchapter(o.getRec_xchapter());\r
+                                       n.setRec_mschapter(o.getRec_mschapter());\r
+                                       n.setRec_mvchapter(o.getRec_mvchapter());\r
+                                       //\r
+                                       n.setRec_aspect(o.getRec_aspect());\r
+                                       n.setRec_bvperf(o.getRec_bvperf());\r
+                                       n.setRec_lvoice(o.getRec_lvoice());\r
+                                       n.setRec_autodel(o.getRec_autodel());\r
+                               }\r
+                       }\r
+               }\r
+               //\r
+               RESERVES = r;\r
+               //\r
+               refreshReserves();\r
+       }\r
+       public void setReserves(ArrayList<ReserveList> r) {\r
+               // ライン入力のチャンネル名を保持する\r
+               for (ReserveList o : RESERVES) {\r
+                       for (ReserveList n : r) {\r
+                               // 外部入力以外は知らん\r
+                               if (o.getCh_name() != null && n.getCh_name() != null && n.getCh_name().startsWith("外部入力")) {\r
+                                       if (o.getId() == n.getId() && o.getChannel().equals(n.getChannel())) {\r
+                                               System.out.println("外部入力を次の放送局で置き換えます: "+n.getCh_name()+"->"+o.getCh_name());\r
+                                               n.setCh_name(o.getCh_name());\r
+                                       }\r
+                               }\r
+                       }\r
+               }\r
+               // 主にDIGA用\r
+               {\r
+                       if ( getTunerNum() >= 2 ) {\r
+                               // 2チューナー以上は可変\r
+                               \r
+                               // ちょっと時刻順に整理しよう\r
+                               ArrayList<ReserveList> s = new ArrayList<ReserveList>();\r
+                               for ( ReserveList o : r ) {\r
+                                       int idx = -1;\r
+                                       for ( int i=0; i<s.size(); i++ ) {\r
+                                               if ( o.getStartDateTime().compareTo(s.get(i).getStartDateTime()) < 0 ) {\r
+                                                       idx = i;\r
+                                                       break;\r
+                                               }\r
+                                       }\r
+                                       if ( idx == -1 ) {\r
+                                               s.add(o);\r
+                                       }\r
+                                       else {\r
+                                               s.add(idx,o);\r
+                                       }\r
+                                       \r
+                                       o.setTuner("");\r
+                               }\r
+\r
+                               // チューナーを割り振ろう\r
+                               for ( int x=0; x<s.size(); x++ ) {\r
+                                       // 全チューナーをリストアップする\r
+                                       ArrayList<String> tuns = new ArrayList<String>();\r
+                                       for ( TextValueSet enc : encoder ) {\r
+                                               tuns.add(enc.getText());\r
+                                       }\r
+                                       // 残っているチューナーをリストアップする\r
+                                       for ( int y=0; y<s.size() && tuns.size()>0; y++ ) {\r
+                                               if ( x == y || s.get(y).getTuner().equals("") ) {\r
+                                                       // 自分自身と、チューナー番号が振られていない相手はスルー\r
+                                                       continue;\r
+                                               }\r
+                                               // 時間が重なっている予約が既に使用しているチューナーは除外する\r
+                                               ArrayList<String> starts = new ArrayList<String>();\r
+                                               ArrayList<String> ends = new ArrayList<String>();\r
+                                               CommonUtils.getStartEndList(starts, ends, s.get(y));\r
+                                               for ( int z=0; z<starts.size(); z++ ) {\r
+                                                       // 帯予約を正しく処理するために全予約日時をなめるようにする\r
+                                                       if ( CommonUtils.isOverlap(s.get(x).getStartDateTime(), s.get(x).getEndDateTime(), starts.get(z), ends.get(z), getAdjNotRep()) ) {\r
+                                                               tuns.remove(s.get(y).getTuner());\r
+                                                               break;\r
+                                                       }\r
+                                               }\r
+                                       }\r
+                                       // 残っているチューナーを割り振る\r
+                                       if ( tuns.size() == 0 ) {\r
+                                               // 余ってないなら全部0\r
+                                               s.get(x).setTuner(encoder.get(0).getText());\r
+                                       }\r
+                                       else {\r
+                                               // 余っているならそのうちの最初のものを使用\r
+                                               s.get(x).setTuner(tuns.get(0));\r
+                                       }\r
+                               }\r
+                       }\r
+                       else if ( getTunerNum() == 1 ) {\r
+                               // 1チューナーは固定値\r
+                               for ( int x=0; x<r.size(); x++ ) {\r
+                                       r.get(x).setTuner("■");\r
+                               }\r
+                       }\r
+               }\r
+\r
+               //\r
+               RESERVES = r;\r
+               \r
+               //\r
+               refreshReserves();\r
+       }\r
+       public void refreshReserves() {\r
+               //\r
+               String curDateTime = CommonUtils.getCritDateTime();\r
+               //\r
+               for (int i=RESERVES.size()-1; i>=0; i--) {\r
+                       ReserveList r = RESERVES.get(i);\r
+                       if (r.getRec_pattern_id() == HDDRecorder.RPTPTN_ID_BYDATE) {\r
+                               // 単日予約\r
+                               if (r.getEndDateTime().compareTo(curDateTime) < 0) {\r
+                                       // 当日以前のエントリを削除\r
+                                       RESERVES.remove(r);\r
+                               }\r
+                       }\r
+                       else {\r
+                               // 繰り返し予約\r
+                               String nextDate = CommonUtils.getNextDate(r);\r
+                               r.setRec_nextdate(nextDate);\r
+                       }\r
+               }\r
+       }\r
+       \r
+       \r
+       \r
+       \r
+       /***\r
+        *  RDデジタル系の予約一覧の解読\r
+        */\r
+       protected ArrayList<ReserveList> decodeReservedList(String response) {\r
+               \r
+               ArrayList<ReserveList> newReserveList = new ArrayList<ReserveList>();\r
+               \r
+               Matcher ma = Pattern.compile("(c1\\[\\d+?\\]=[\\s\\S]+?\";)\\n").matcher(response);\r
+               while ( ma.find() ) {\r
+                       // 個々のデータを取り出す\r
+                       ReserveList entry = new ReserveList();\r
+                       \r
+                       Matcher mb = null;\r
+                               \r
+                       String[] d = new String[17];\r
+                       mb = Pattern.compile("c\\d+?\\[\\d+?\\]=\"(.*?)\";").matcher(ma.group(1));\r
+                       for (int i=0; i<d.length; i++) {\r
+                               if ( mb.find()) {\r
+                                       d[i] = mb.group(1);\r
+                               }\r
+                               //System.out.println(i+") "+d[i]);\r
+                       }\r
+                       \r
+                       // 実行ON/OFF\r
+                       if (d[1].equals("2")) {\r
+                               entry.setExec(false);\r
+                       }\r
+                       \r
+                       // 番組追跡\r
+                       //if (d[12].equals("0") || d[12].equals("4") || d[12].equals("3")) {\r
+                       if (d[12].equals("4")) {\r
+                               entry.setPursues(true);\r
+                       }\r
+                       \r
+                       // 記録先デバイス\r
+                       entry.setRec_device(d[8]);\r
+                       \r
+                       // 予約名のエスケープを解除する\r
+                       String title = CommonUtils.unEscape(d[2]).replaceAll("<BR>","");\r
+\r
+                       //\r
+                       entry.setId(d[0]);\r
+                       \r
+                       //\r
+                       entry.setRec_pattern(d[5]);\r
+                       entry.setRec_pattern_id(getRec_pattern_Id(entry.getRec_pattern()));\r
+                       mb = Pattern.compile("(\\d\\d):(\\d\\d).*?(\\d\\d):(\\d\\d)").matcher(d[6]+"-"+d[7]);\r
+                       if (mb.find()) {\r
+                               entry.setAhh(mb.group(1));\r
+                               entry.setAmm(mb.group(2));\r
+                               entry.setZhh(mb.group(3));\r
+                               entry.setZmm(mb.group(4));\r
+                       }\r
+                       entry.setRec_nextdate(CommonUtils.getNextDate(entry));\r
+                       //entry.setRec_nextdate(getNextDate(entry.getRec_pattern(), entry.getZhh()+":"+entry.getZmm()));\r
+                       entry.setRec_min(CommonUtils.getRecMin(entry.getAhh(), entry.getAmm(), entry.getZhh(), entry.getZmm()));\r
+                       getStartEndDateTime(entry);\r
+                       \r
+                       //\r
+                       if (d[3].equals("18") || d[3].equals("10")  || d[3].equals("9")) {\r
+                               entry.setTuner("TS2");\r
+                       }\r
+                       else if (d[3].equals("17") || d[3].equals("12")  || d[3].equals("11")) {\r
+                               entry.setTuner("TS1");\r
+                       }\r
+                       else if (d[3].equals("16") || d[3].equals("7")) {\r
+                               entry.setTuner("RE");\r
+                       }\r
+                       else {\r
+                               entry.setTuner("--");\r
+                       }\r
+                       \r
+                       //\r
+                       if (d[10].equals("  ")) {\r
+                               if (d[9].equals("A1")) {\r
+                                       d[9] = "[TSE] AT 4.7GB";\r
+                               }\r
+                               else if (d[9].equals("A2")) {\r
+                                       d[9] = "[TSE] AT 9.4GB";\r
+                               }\r
+                               else if (d[9].equals("DL")) {\r
+                                       d[9] = "[TSE] AT 8.5GB";\r
+                               }\r
+                               else {\r
+                                       Matcher mc = Pattern.compile("^MN").matcher(d[9]);\r
+                                       if (mc.find()) {\r
+                                               d[9] = mc.replaceFirst("[TSE] ");\r
+                                       }\r
+                               }\r
+                       }\r
+                       else {\r
+                               if (d[9].equals("A1")) {\r
+                                       d[9] = "[VR] AT 4.7GB";\r
+                               }\r
+                               else if (d[9].equals("A2")) {\r
+                                       d[9] = "[VR] AT 9.4GB";\r
+                               }\r
+                               else if (d[9].equals("DL")) {\r
+                                       d[9] = "[VR] AT 8.5GB";\r
+                               }\r
+                               else {\r
+                                       Matcher mc = Pattern.compile("^MN").matcher(d[9]);\r
+                                       if (mc.find()) {\r
+                                               d[9] = mc.replaceFirst("[VR] ");\r
+                                       }\r
+                                       else if ( d[9].startsWith("SP") || d[9].startsWith("LP")) {\r
+                                               d[9] = "[VR] "+d[9];\r
+                                       }\r
+                               }\r
+                       }\r
+                       \r
+                       if (d[9].equals("TS")) {\r
+                               entry.setRec_mode("[TS]");\r
+                       }\r
+                       else {\r
+                               entry.setRec_mode(d[9]);\r
+                       }\r
+                       \r
+                       entry.setTitle(title);\r
+                       entry.setTitlePop(TraceProgram.replacePop(title));\r
+                       //entry.setCh_name(getChCode().getCH_NO2NAME(d[4]));    // 機種固有領域に移動\r
+                       entry.setChannel(d[4]);\r
+\r
+                       entry.setRec_audio(d[10]);\r
+                       //entry.rec_folder = data.get();        // 予約一覧からはとれない\r
+                       //entry.rec_genre = data.get();         // 予約一覧からはとれない\r
+                       \r
+                       // 予約情報を保存\r
+                       newReserveList.add(entry.clone());\r
+               }\r
+               return(newReserveList);\r
+       }\r
+       \r
+       /**\r
+        * レコーダーから取得できない情報は直接コピー(既存のリストから探して)\r
+        */\r
+       protected void copyAttributes(ReserveList to, ArrayList<ReserveList> fromlist) {\r
+               ReserveList olde = null;\r
+               for ( ReserveList from : fromlist ) {\r
+                       if ( from.getId() != null && from.getId().equals(to.getId()) ) {\r
+                               copyAttribute(to, olde = from);\r
+                               break;\r
+                       }\r
+               }\r
+               \r
+               // DIGAの終了時間"未定"対応だけど、別にDIGAかどうか確認したりはしない。\r
+               setAttributesDiga(to,olde);\r
+       }\r
+       \r
+       /**\r
+        * レコーダーから取得できない情報は直接コピー(既存エントリから直に)\r
+        */\r
+       protected void copyAttribute(ReserveList to, ReserveList from) {\r
+               // 鯛ナビの内部フラグ\r
+               to.setAutocomplete(from.getAutocomplete());\r
+               // 予約一覧からは取得できない情報\r
+               to.setDetail(from.getDetail());\r
+               to.setRec_genre(from.getRec_genre());\r
+               //n.setRec_device(o.getRec_device());\r
+               to.setRec_folder(from.getRec_folder());\r
+               to.setRec_dvdcompat(from.getRec_dvdcompat());\r
+               to.setRec_xchapter(from.getRec_xchapter());\r
+               to.setRec_mschapter(from.getRec_mschapter());\r
+               to.setRec_mvchapter(from.getRec_mvchapter());\r
+               //\r
+               to.setRec_aspect(from.getRec_aspect());\r
+               to.setRec_bvperf(from.getRec_bvperf());\r
+               to.setRec_lvoice(from.getRec_lvoice());\r
+               to.setRec_autodel(from.getRec_autodel());\r
+               // BZ700以降の取得一覧から取得できない画質の対応\r
+               if (to.getRec_mode().equals("")) {\r
+                       to.setRec_mode(from.getRec_mode());\r
+               }\r
+       }\r
+       \r
+       protected void setAttributesDiga(ReserveList to, ReserveList from) {\r
+               if ( to.getZhh() != null && to.getZmm() != null && to.getRec_min() != null ) {\r
+                       // 埋まってる\r
+                       return;\r
+               }\r
+               \r
+               if ( from != null && from.getZhh() != null ) {\r
+                       // 引継ぎもとがあれば引き継ぐ\r
+                       to.setZhh(from.getZhh());\r
+                       to.setZmm(from.getZmm());\r
+                       to.setRec_min(from.getRec_min());\r
+                       to.setRec_nextdate(CommonUtils.getNextDate(to));\r
+                       getStartEndDateTime(to);\r
+                       return;\r
+               }\r
+               \r
+               /*\r
+               // 現在時刻から30分後か、開始時刻から1時間後の、どちらか短い方に強制設定する\r
+               String curTM = CommonUtils.getTime(30);\r
+               String endTM = String.format("%02d:%s", (Integer.valueOf(to.getAhh())+1)%24,to.getAmm());\r
+               if ( curTM.compareTo(endTM) > 0 ) {\r
+                       endTM = curTM;\r
+               }\r
+               to.setZhh(endTM.substring(0,2));\r
+               to.setZmm(endTM.substring(3,5));\r
+               to.setRec_min(CommonUtils.getRecMin(to.getAhh()+":"+to.getAmm(), endTM));\r
+               */\r
+               \r
+               // 開始時刻から1時間後に強制設定する\r
+               to.setZhh(String.format("%02d", (Integer.valueOf(to.getAhh())+1)%24));\r
+               to.setZmm(to.getAmm());\r
+               to.setRec_min("60");\r
+               \r
+               to.setRec_nextdate(CommonUtils.getNextDate(to));\r
+               getStartEndDateTime(to);\r
+       }\r
+       \r
+       /**\r
+        * 録画済みフラグを立てる\r
+        */\r
+       protected void setRecordedFlag() {\r
+               \r
+               // 過去X日分までチェック(初期値は14日)\r
+               final String critDateTime = CommonUtils.getDateTimeW(-86400*getRecordedCheckScope());\r
+               \r
+               for ( ReserveList reserved : RESERVES ) {\r
+                       reserved.setRecorded(false);\r
+               }\r
+               \r
+               for ( RecordedInfo recorded : RECORDED ) {\r
+                       if ( critDateTime.compareTo(recorded.getDate()+" "+recorded.getAhh()+":"+recorded.getAmm()) > 0 ) {\r
+                               break;\r
+                       }\r
+                       String chktitle = recorded.getTitle().replaceFirst(TVProgram.titlePrefixRemoveExpr, "");\r
+                       for ( ReserveList reserved : RESERVES ) {\r
+                               if ( reserved.getRecorded() ) {\r
+                                       // 既にフラグが立ってるものはスルー\r
+                                       continue;\r
+                               }\r
+                               if ( reserved.getRec_pattern_id() != HDDRecorder.RPTPTN_ID_BYDATE ) {\r
+                                       // 単日予約のみ\r
+                                       continue;\r
+                               }\r
+                               String restitle = reserved.getTitle().replaceFirst(TVProgram.titlePrefixRemoveExpr, "");\r
+                               boolean chchk = (recorded.getChannel() != null && recorded.getChannel().length() > 0) ?  (recorded.getChannel().equals(reserved.getChannel())) : (true);\r
+                               if ( recorded.getSucceeded() && recorded.getDrop_mpeg() == 0 && chchk && chktitle.equals(restitle) ) {\r
+                                       // 成功していて、放送局とタイトルが一致\r
+                                       reserved.setRecorded(true);\r
+                               }\r
+                       }\r
+               }\r
+       }\r
+       \r
+       /**\r
+        * 録画結果一覧は開始日時降順で保存\r
+        * @param newRecordedList\r
+        * @param entry\r
+        */\r
+       protected RecordedInfo addRecorded(ArrayList<RecordedInfo> newRecordedList, RecordedInfo entry) {\r
+               \r
+               String endt = entry.getDate()+entry.getAhh()+entry.getAmm();\r
+               \r
+               int n = 0;\r
+               int dn = -1;\r
+               for ( ; n<newRecordedList.size(); n++ ) {\r
+                       RecordedInfo ri = newRecordedList.get(n);\r
+                       String ridt = ri.getDate()+ri.getAhh()+ri.getAmm();\r
+                       int result = ridt.compareTo(endt);\r
+                       if ( result == 0 ) {\r
+                               if ( dn == -1 ) {\r
+                                       // 開始時刻が同じ情報を発見したら、最終的にはそれの前に差し込みたいので、nを保存する\r
+                                       dn = n;\r
+                               }\r
+                               \r
+                               // 開始時刻が一致して\r
+                               if ( ri.getId() == null ) {\r
+                                       // キャッシュから取得したものだった場合に重複チェックする\r
+                                       if ( ri.getTitle().equals(entry.getTitle()) && ri.getCh_orig().equals(entry.getCh_orig()) && ri.getLength() == entry.getLength()/* && ri.getDrop() == entry.getDrop()*/ ) {\r
+                                               // 重複しているようなので捨てる\r
+                                               System.out.println(MSGID+"録画結果はすでにキャッシュ上に存在していたようです: "+endt+" "+entry.getTitle());\r
+                                               return null;\r
+                                       }\r
+                                       else {\r
+                                               if (getDebug()) System.out.println(DBGID+"よく似た録画結果です: "+endt+" "+entry.getTitle()+" <-> "+ridt+ri.getTitle());\r
+                                       }\r
+                               }\r
+                       }\r
+                       else if ( result < 0 ) {\r
+                               break;\r
+                       }\r
+               }\r
+               \r
+               entry.setDrop_mpeg(entry.getDrop()-entry.getDrop_mpeg());\r
+               \r
+               newRecordedList.add((dn!=-1)?(dn):(n),entry);\r
+               \r
+               if (getDebug()) System.out.println(DBGID+"録画結果を追加しました: "+endt+" "+entry.getTitle());\r
+               \r
+               return entry;\r
+       }\r
+\r
+       /*******************************************************************************\r
+        * ログと進捗ダイアログ\r
+        ******************************************************************************/\r
+       \r
+       private StatusWindow stw = null;\r
+       public void setProgressArea(StatusWindow o) { stw = o; }\r
+       protected void reportProgress(String msg) {\r
+               if (stw != null) {\r
+                       stw.append(msg);\r
+               }\r
+               System.out.println(msg);\r
+       }\r
+       \r
+       // RD系のNewId取得\r
+       protected String getNewId(String response) {\r
+               Matcher ma = null;\r
+               String newid = null;\r
+               ma = Pattern.compile("c1\\[\\d+?\\]=\"(\\d+?)\";").matcher(response);\r
+               while (ma.find()) {\r
+\r
+                       String idtmp = ma.group(1);\r
+                       \r
+                       boolean flag = true;\r
+                       for (ReserveList rx : getReserves()) {\r
+                               if (rx.getId().equals(idtmp)) {\r
+                                       flag = false;\r
+                                       break;\r
+                               }\r
+                       }\r
+                       if (flag == true) {\r
+                               newid = idtmp;\r
+                               break;\r
+                       }\r
+               }\r
+               return(newid);\r
+       }\r
+       \r
+       /*******************************************************************************\r
+        * 通信系\r
+        ******************************************************************************/\r
+\r
+       private final DumpHttp dump = new DumpHttp();\r
+       \r
+       //\r
+       public void wakeup() {\r
+               if ( ! getMacAddr().equals("") && ! getBroadcast().equals("")) {\r
+                       //\r
+                       byte[] magic = new byte[102];\r
+                       int i = 0;\r
+                       for (; i<6; i++) {\r
+                               magic[i] = (byte) 0xff;\r
+                       }\r
+                       for (int j=0; j<16; j++) {\r
+                               for (int k=0; k<6; k++) {\r
+                                       short sv = Short.decode("0x"+getMacAddr().substring(k*2,k*2+2));\r
+                                       magic[i++] = (byte)sv;\r
+                               }\r
+                       }\r
+                       \r
+                       //\r
+                       try {\r
+                               InetSocketAddress remote = new InetSocketAddress(getBroadcast(), 1234);\r
+                               DatagramPacket packet = new DatagramPacket(magic, magic.length, remote);\r
+                               new DatagramSocket().send(packet);\r
+                       } catch (SocketException e) {\r
+                               e.printStackTrace();\r
+                       } catch (IOException e) {\r
+                               e.printStackTrace();\r
+                       }\r
+                       \r
+                       //\r
+                       System.out.println("send magic packet to "+getBroadcast()+","+getMacAddr());\r
+               }\r
+       }\r
+       \r
+       //\r
+       public void shutdown() {\r
+               // おまじない\r
+               Authenticator.setDefault(new MyAuthenticator(getUser(), getPasswd()));\r
+               //\r
+               reqGET("http://"+getIPAddr()+":"+getPortNo()+"/remote/remote.htm?key=12", null);\r
+               //\r
+               System.out.println("send shutdown request to "+getBroadcast()+","+getMacAddr());\r
+       }\r
+       \r
+       // おまじない\r
+       public class MyAuthenticator extends Authenticator {\r
+               private String username;\r
+               private String password;\r
+\r
+               public MyAuthenticator(String username, String password) {\r
+                       this.username = username;\r
+                       this.password = password;\r
+               }\r
+               protected PasswordAuthentication getPasswordAuthentication() {\r
+                       return new PasswordAuthentication(username, password.toCharArray());\r
+               }\r
+       }\r
+       \r
+       // GET\r
+       public String[] reqGET(String uri, Hashtable<String, String>property) {\r
+               return reqGET(uri, property, "MS932");\r
+       }\r
+       public String[] reqGET(String uri, Hashtable<String, String>property, String encoding)\r
+       {\r
+        //CookieManager manager = new CookieManager();\r
+        //CookieHandler.setDefault(manager);\r
+\r
+               String header = "";\r
+               String response = "";\r
+               boolean getSuccess = false;\r
+               \r
+               HttpURLConnection conn = null;\r
+               BufferedReader reader = null;\r
+               InputStreamReader sr = null;\r
+               try {\r
+                       if (debug) {\r
+                               System.out.println("# GET: "+uri);\r
+                               dump.request("# GET: "+uri);\r
+                       }\r
+\r
+                       // コネクションの確立\r
+                       URL url = new URL(uri);\r
+                       conn = (HttpURLConnection)url.openConnection();\r
+                       conn.setRequestMethod("GET");\r
+                       conn.setConnectTimeout(5*1000);\r
+                       conn.setReadTimeout(15*1000);\r
+                       conn.addRequestProperty("User-Agent", userAgent);\r
+                       if (property != null) {\r
+                               for (String key : property.keySet()) {\r
+                                       conn.setRequestProperty(key, property.get(key));\r
+                               }\r
+                       }\r
+                       \r
+                       //conn.connect();\r
+\r
+                       Map<String, List<String>> h = conn.getHeaderFields();\r
+                       for ( String key : h.keySet() ) {\r
+                               // ヘッダ情報\r
+                               if (key == null) {\r
+                                       header += h.get(key).get(0)+"\n";\r
+                                       Matcher ma = Pattern.compile(" 200 ").matcher(h.get(key).get(0).toString());\r
+                                       if (ma.find()) {\r
+                                               getSuccess = true;\r
+                                       }\r
+                               }\r
+                               else {\r
+                                       header += key+": "+h.get(key).get(0)+"\n";\r
+                               }\r
+                       }\r
+                       if (debug) {\r
+                               System.out.println("# Header");\r
+                               System.out.println(header);\r
+                               dump.res_header("# Header\n"+header);\r
+                       }\r
+                       if (getSuccess == false) {\r
+                               // コネクション切断はfinallyで\r
+                               return(new String[] {header,null});\r
+                       }\r
+                       \r
+                       // データ部\r
+                       sr = new InputStreamReader(conn.getInputStream(),encoding);\r
+                       reader = new BufferedReader(sr);\r
+                       \r
+                       String s = null;\r
+                       StringBuilder sb = new StringBuilder();\r
+                       while ((s = reader.readLine()) != null) {\r
+                               sb.append(s);\r
+                               sb.append("\n");\r
+                       }\r
+                       \r
+                       response = sb.toString();\r
+\r
+                       // コネクション切断はfinallyで\r
+\r
+                   if (debug) {\r
+                               //System.out.printf("# RESPONSE\n%s\n", response);\r
+                       System.out.println("# DUMP TO FILE: "+dump.res_body("<!-- # RESPONSE -->\n"+response));\r
+                       }\r
+                   \r
+                    return(new String[] {header,response});\r
+               }\r
+               catch (UnsupportedEncodingException e) {\r
+                       System.err.println("[ERROR] レコーダへのアクセスで問題が発生しました(GET): "+e.toString());\r
+                       if (getSuccess == true) {\r
+                               return(new String[] {header,null});\r
+                       }\r
+               }\r
+               catch (IOException e) {\r
+                       System.err.println("[ERROR] レコーダへのアクセスで問題が発生しました(GET): "+e.toString());\r
+                       if (getSuccess == true) {\r
+                               return(new String[] {header,null});\r
+                       }\r
+               }\r
+               finally {\r
+                       CommonUtils.closing(reader);\r
+                       CommonUtils.closing(sr);\r
+                       CommonUtils.closing(conn);\r
+               }\r
+               \r
+               return(new String[] {null,null});\r
+       }\r
+\r
+       // POST\r
+       public String[] reqPOST(String uri, String pstr, Hashtable<String, String>property) {\r
+               return reqPOST(uri, pstr, property, "MS932");\r
+       }\r
+       public String[] reqPOST(String uri, String pstr, Hashtable<String, String>property, String encoding)\r
+       {\r
+        //CookieManager manager = new CookieManager();\r
+        //CookieHandler.setDefault(manager);\r
+\r
+               boolean postSuccess = false;\r
+               String header = "";\r
+               String response = "";\r
+               \r
+               HttpURLConnection conn = null;\r
+               OutputStreamWriter writer = null;\r
+               BufferedReader reader = null;\r
+               InputStreamReader sr = null;\r
+               try {\r
+                       if (debug) {\r
+                               System.out.println("# POST: "+uri+"?"+pstr);\r
+                               dump.request("# POST: "+uri+"?"+pstr);\r
+                       }\r
+\r
+                       URL url = new URL(uri);\r
+                       conn = (HttpURLConnection)url.openConnection();\r
+                       conn.setRequestMethod("POST");\r
+                       conn.setConnectTimeout(5*1000);\r
+                       conn.setReadTimeout(15*1000);\r
+                       conn.setDoOutput(true);\r
+                       conn.addRequestProperty("User-Agent", userAgent);\r
+                       if (property != null) {\r
+                               for (String key : property.keySet()) {\r
+                                       conn.setRequestProperty(key, property.get(key));\r
+                               }\r
+                       }\r
+                       \r
+                       //conn.connect();\r
+                       \r
+                       writer = new OutputStreamWriter(conn.getOutputStream(),encoding);\r
+                       writer.write(pstr);\r
+                       writer.close();\r
+                       writer = null;\r
+\r
+                       Map<String, List<String>> h = conn.getHeaderFields();\r
+                       for ( String key : h.keySet() ) {\r
+                               // ヘッダ情報\r
+                               if (key == null) {\r
+                                       header += h.get(key).get(0)+"\n";\r
+                                       Matcher ma = Pattern.compile(" 200 ").matcher(h.get(key).get(0).toString());\r
+                                       if (ma.find()) {\r
+                                               postSuccess = true;\r
+                                       }\r
+                               }\r
+                               else {\r
+                                       header += key+": "+h.get(key).get(0)+"\n";\r
+                               }\r
+                       }\r
+                       if (debug) {\r
+                               System.out.println("# Header");\r
+                               System.out.println(header);\r
+                               dump.res_header("# Header\n"+header);\r
+                       }\r
+                       if (postSuccess == false) {\r
+                               // コネクション切断はfinallyで\r
+                               return(new String[] {header,null});\r
+                       }\r
+                       \r
+                       sr = new InputStreamReader(conn.getInputStream(),encoding);\r
+                       reader = new BufferedReader(sr);\r
+                       \r
+                       String s = null;\r
+                       StringBuilder sb = new StringBuilder();\r
+                       while ((s = reader.readLine()) != null) {\r
+                               sb.append(s);\r
+                               sb.append("\n");\r
+                       }\r
+\r
+                       response = sb.toString();\r
+\r
+                       // コネクション切断はfinallyで\r
+                   \r
+                   if (debug) {\r
+                               //System.out.printf("# RESPONSE\n%s\n", response);\r
+                       System.out.println("# DUMP TO FILE: "+dump.res_body("<!-- # RESPONSE -->\n"+response));\r
+                       }\r
+\r
+                       return(new String[] {header,response});\r
+               }\r
+               catch (UnsupportedEncodingException e) {\r
+                       System.err.println("[ERROR] レコーダへのアクセスで問題が発生しました(POST): "+e.toString());\r
+                       if (postSuccess == true) {\r
+                               return(new String[] {header,null});\r
+                       }\r
+               }\r
+               catch (IOException e) {\r
+                       System.err.println("[ERROR] レコーダへのアクセスで問題が発生しました(POST): "+e.toString());\r
+                       if (postSuccess == true) {\r
+                               return(new String[] {header,null});\r
+                       }\r
+               }\r
+               finally {\r
+                       CommonUtils.closing(writer);\r
+                       CommonUtils.closing(reader);\r
+                       CommonUtils.closing(sr);\r
+                       CommonUtils.closing(conn);\r
+               }\r
+               \r
+               return(new String[] {null,null});\r
+       }\r
+       \r
+       /*******************************************************************************\r
+        * ここから下は該当機能が無効なプラグイン用のダミー\r
+        ******************************************************************************/\r
+       \r
+       @Override\r
+       public RecType getType() {\r
+               // TODO Auto-generated method stub\r
+               return null;\r
+       }\r
+       @Override\r
+       public ChannelCode getChCode() {\r
+               // TODO Auto-generated method stub\r
+               return null;\r
+       }\r
+       @Override\r
+       public boolean ChangeChannel(String Channel) {\r
+               // TODO Auto-generated method stub\r
+               return false;\r
+       }\r
+       @Override\r
+       public boolean GetRdSettings(boolean force) {\r
+               // TODO Auto-generated method stub\r
+               return true;\r
+       }\r
+       @Override\r
+       public boolean GetRdReserve(boolean force) {\r
+               // TODO Auto-generated method stub\r
+               return true;\r
+       }\r
+       @Deprecated\r
+       @Override\r
+       public boolean GetRdReserveDetails() {\r
+               // TODO Auto-generated method stub\r
+               return false;\r
+       }\r
+       @Override\r
+       public boolean GetRdRecorded(boolean force) {\r
+               // TODO Auto-generated method stub\r
+               return true;\r
+       }\r
+       @Override\r
+       public boolean PostRdEntry(ReserveList r) {\r
+               // TODO Auto-generated method stub\r
+               return false;\r
+       }\r
+       @Override\r
+       public boolean UpdateRdEntry(ReserveList o, ReserveList r) {\r
+               // TODO Auto-generated method stub\r
+               return false;\r
+       }\r
+       @Override\r
+       public ReserveList RemoveRdEntry(String delno) {\r
+               // TODO Auto-generated method stub\r
+               return null;\r
+       }\r
+       @Override\r
+       public String getErrmsg() {\r
+               // TODO Auto-generated method stub\r
+               return null;\r
+       }\r
+}\r
diff --git a/TinyBannavi/src/tainavi/JCCLabel.java b/TinyBannavi/src/tainavi/JCCLabel.java
new file mode 100644 (file)
index 0000000..45a19ce
--- /dev/null
@@ -0,0 +1,89 @@
+package tainavi;\r
+\r
+import java.awt.Color;\r
+import java.awt.Component;\r
+import java.awt.event.MouseAdapter;\r
+import java.awt.event.MouseEvent;\r
+\r
+import javax.swing.JLabel;\r
+import javax.swing.SwingConstants;\r
+import javax.swing.SwingUtilities;\r
+import javax.swing.border.LineBorder;\r
+\r
+\r
+public class JCCLabel extends JLabel {\r
+\r
+       private static final long serialVersionUID = 1L;\r
+\r
+       private boolean invert = true;\r
+       \r
+       private MouseAdapter mLsr = null;\r
+       \r
+       /**\r
+        * @param invert true:背景色を選択する、false:文字色を選択する\r
+        */\r
+       public JCCLabel(String s, Color c, boolean invert, final Component parent, final VWColorChooserDialog ccwin) {\r
+               super(s,SwingConstants.CENTER);\r
+               this.invert = invert;\r
+               \r
+               this.setChoosed(c);\r
+               this.setOpaque(true);\r
+               this.setBorder(new LineBorder(new Color(0,0,0)));\r
+               \r
+               mLsr = new MouseAdapter() {\r
+                       @Override\r
+                       public void mouseClicked(MouseEvent e) {\r
+                               if (SwingUtilities.isLeftMouseButton(e)) {\r
+                                       \r
+                                       JCCLabel jl = (JCCLabel)e.getSource();\r
+                                       \r
+                                       ccwin.setColor(jl.getChoosed());\r
+                                       CommonSwingUtils.setLocationCenter(parent,ccwin);\r
+                                       ccwin.setVisible(true);\r
+                                       \r
+                                       if (ccwin.getSelectedColor() != null ) {\r
+                                               jl.setChoosed(ccwin.getSelectedColor());\r
+                                       }\r
+                               }\r
+                       }\r
+               };\r
+               \r
+               this.addMouseListener(mLsr);\r
+       }\r
+       \r
+       @Override\r
+       public void setEnabled(boolean enabled) {\r
+               if ( super.isEnabled() == enabled ) {\r
+                       // 処理なし\r
+                       return;\r
+               }\r
+               \r
+               // disabledなら触んな!\r
+               super.setEnabled(enabled);\r
+               if ( enabled ) {\r
+                       this.addMouseListener(mLsr);\r
+               }\r
+               else {\r
+                       this.removeMouseListener(mLsr);\r
+               }\r
+       }\r
+       \r
+       public void setChoosed(Color c) {\r
+               if (invert) {\r
+                       setBackground(c);\r
+               }\r
+               else {\r
+                       setForeground(c);\r
+                       setBackground(Color.WHITE);\r
+               }\r
+       }\r
+       \r
+       public Color getChoosed() {\r
+               if (invert) {\r
+                       return getBackground();\r
+               }\r
+               else {\r
+                       return getForeground();\r
+               }\r
+       }\r
+}
\ No newline at end of file
diff --git a/TinyBannavi/src/tainavi/JCheckBoxPanel.java b/TinyBannavi/src/tainavi/JCheckBoxPanel.java
new file mode 100644 (file)
index 0000000..b7fd030
--- /dev/null
@@ -0,0 +1,120 @@
+package tainavi;\r
+\r
+import java.awt.Color;\r
+import java.awt.Dimension;\r
+import java.awt.event.ActionListener;\r
+import java.awt.event.ItemListener;\r
+import java.awt.event.MouseAdapter;\r
+import java.awt.event.MouseEvent;\r
+\r
+import javax.swing.BoxLayout;\r
+import javax.swing.JCheckBox;\r
+import javax.swing.JLabel;\r
+import javax.swing.JPanel;\r
+\r
+\r
+public class JCheckBoxPanel extends JPanel {\r
+\r
+       private static final long serialVersionUID = 1L;\r
+\r
+       private JCheckBox jcheckbox = null;\r
+       private JLabel jlabel = null;\r
+       \r
+       public JCheckBoxPanel(String s, int labelWidth) {\r
+               this(s,labelWidth,false);\r
+       }\r
+       public JCheckBoxPanel(String s, int labelWidth, boolean rev) {\r
+               if ( rev ) {\r
+                       _JCheckBoxPanelRev(s,labelWidth);\r
+               }\r
+               else {\r
+                       _JCheckBoxPanel(s,labelWidth);\r
+               }\r
+       }\r
+       \r
+       private void _JCheckBoxPanel(String s, int labelWidth) {\r
+               this.setLayout(new BoxLayout(this,BoxLayout.LINE_AXIS));\r
+               \r
+               jlabel = new JLabel(s);\r
+               Dimension d = jlabel.getPreferredSize();\r
+               if ( labelWidth > 0 ) {\r
+                       d.width = labelWidth;\r
+               }\r
+               d.height = 100;\r
+               jlabel.setMaximumSize(d);\r
+               this.add(jlabel);\r
+               \r
+               this.add(jcheckbox = new JCheckBox());\r
+               \r
+               jlabel.addMouseListener(ml_labelClicked);\r
+       }\r
+       private void _JCheckBoxPanelRev(String s, int labelWidth) {\r
+               this.setLayout(new BoxLayout(this,BoxLayout.LINE_AXIS));\r
+               \r
+               this.add(jcheckbox = new JCheckBox());\r
+               \r
+               jlabel = new JLabel(s);\r
+               Dimension d = jlabel.getPreferredSize();\r
+               if ( labelWidth > 0 ) {\r
+                       d.width = labelWidth;\r
+               }\r
+               d.height = 100;\r
+               jlabel.setMaximumSize(d);\r
+               this.add(jlabel);\r
+               \r
+               jlabel.addMouseListener(ml_labelClicked);\r
+       }\r
+       \r
+       public boolean isSelected() {\r
+               return jcheckbox.isSelected();\r
+       }\r
+       public void setSelected(boolean b) {\r
+               jcheckbox.setSelected(b);\r
+       }\r
+       public void setEnabled(boolean b) {\r
+               if (this.jlabel != null) {\r
+                       this.jlabel.setEnabled(b);\r
+                       \r
+                       jlabel.removeMouseListener(ml_labelClicked);\r
+                       if ( b ) {\r
+                               jlabel.addMouseListener(ml_labelClicked);\r
+                       }\r
+               }\r
+               if (this.jcheckbox != null) {\r
+                       this.jcheckbox.setEnabled(b);\r
+               }\r
+       }\r
+       public void setText(String s) {\r
+               jlabel.setText(s);\r
+       }\r
+       \r
+       public void addActionListener(ActionListener l) {\r
+               this.jcheckbox.addActionListener(l);\r
+       }\r
+       public void removeActionListener(ActionListener l) {\r
+               this.jcheckbox.removeActionListener(l);\r
+       }\r
+       \r
+       public void addItemListener(ItemListener l) {\r
+               this.jcheckbox.addItemListener(l);\r
+       }\r
+       public void removeItemListener(ItemListener l) {\r
+               this.jcheckbox.removeItemListener(l);\r
+       }\r
+\r
+       public void setForeground(Color fg) {\r
+               if (this.jlabel != null)\r
+                       this.jlabel.setForeground(fg);\r
+       }\r
+       \r
+       \r
+       //\r
+       private MouseAdapter ml_labelClicked = new MouseAdapter() {\r
+               @Override\r
+               public void mouseClicked(MouseEvent e) {\r
+                       if ( jcheckbox != null ) {\r
+                               jcheckbox.setSelected( ! jcheckbox.isSelected());\r
+                       }\r
+               }\r
+       };\r
+}\r
diff --git a/TinyBannavi/src/tainavi/JColorChooseSlider.java b/TinyBannavi/src/tainavi/JColorChooseSlider.java
new file mode 100644 (file)
index 0000000..f5ecc4d
--- /dev/null
@@ -0,0 +1,179 @@
+package tainavi;\r
+\r
+import java.awt.Dimension;\r
+import java.util.ArrayList;\r
+\r
+import javax.swing.Box;\r
+import javax.swing.BoxLayout;\r
+import javax.swing.JLabel;\r
+import javax.swing.JPanel;\r
+import javax.swing.JSlider;\r
+import javax.swing.JSpinner;\r
+import javax.swing.SpinnerListModel;\r
+import javax.swing.SwingConstants;\r
+import javax.swing.event.ChangeEvent;\r
+import javax.swing.event.ChangeListener;\r
+\r
+\r
+abstract class JColorChooseSlider extends JPanel {\r
+\r
+       private static final long serialVersionUID = 1L;\r
+\r
+       private final int min = 0;\r
+       private final int max = 255;\r
+       \r
+       private JLabel jlabel;\r
+       private JSlider jslider;\r
+       private JSpinner jspinner;\r
+       \r
+       private ChangeListener slCl;\r
+       private ChangeListener spCl;\r
+       \r
+       private SpinnerListModel model = new SpinnerListModel();\r
+       private boolean bHex = false;\r
+       private ArrayList<String> decData = new ArrayList<String>();\r
+       private ArrayList<String> hexData = new ArrayList<String>();\r
+       \r
+       // コンストラクタ\r
+       \r
+       public JColorChooseSlider(String s) {\r
+               \r
+               super();\r
+               \r
+               this.setLayout(new BoxLayout(this, BoxLayout.LINE_AXIS));\r
+               jlabel = new JLabel(s);\r
+               jslider = new JSlider(min,max,min);\r
+               jspinner = new JSpinner(model);\r
+               \r
+               jlabel.setHorizontalAlignment(SwingConstants.CENTER);\r
+               \r
+               jslider.setMinorTickSpacing(17);\r
+               jslider.setMajorTickSpacing(85);\r
+               jslider.setPaintTicks(true);\r
+               jslider.setPaintLabels(true);\r
+               \r
+               Dimension d;\r
+               \r
+               d = jlabel.getPreferredSize();\r
+               d.width = 25;\r
+               jlabel.setPreferredSize(d);\r
+               jlabel.setMaximumSize(d);\r
+               \r
+               d = jslider.getPreferredSize();\r
+               d.width = max;\r
+               jslider.setPreferredSize(d);\r
+               jslider.setMaximumSize(d);\r
+               \r
+               d = jspinner.getPreferredSize();\r
+               d.width = 75;\r
+               jspinner.setPreferredSize(d);\r
+               jspinner.setMaximumSize(d);\r
+               \r
+               this.setLayout(new BoxLayout(this, BoxLayout.X_AXIS));\r
+               this.add(jlabel);\r
+               this.add(Box.createRigidArea(new Dimension(10,10)));\r
+               this.add(jslider);\r
+               this.add(Box.createRigidArea(new Dimension(10,10)));\r
+               this.add(jspinner);\r
+\r
+               // 初期設定はDEC\r
+               bHex = false;\r
+               for (int i=min; i<=max; i++) {\r
+                       decData.add(v2s(i));\r
+               }\r
+               bHex = true;\r
+               for (int i=min; i<=max; i++) {\r
+                       hexData.add(v2s(i));\r
+               }\r
+               setHex(false);\r
+               \r
+               // スライダーとスピンネルを連動させる\r
+               \r
+               slCl = new ChangeListener() {\r
+                       @Override\r
+                       public void stateChanged(ChangeEvent e) {\r
+                               jspinner.removeChangeListener(spCl);\r
+                               model.setValue(v2s(jslider.getValue()));\r
+                               evHandle(jslider.getValue());\r
+                               jspinner.addChangeListener(spCl);\r
+                       }\r
+               };\r
+               spCl = new ChangeListener() {\r
+                       @Override\r
+                       public void stateChanged(ChangeEvent e) {\r
+                               int n = s2v((String) jspinner.getValue());\r
+                               if (n>=min && n<=max) {\r
+                                       // 入力が適正な場合\r
+                                       jslider.removeChangeListener(slCl);\r
+                                       jslider.setValue(n);\r
+                                       evHandle(n);\r
+                                       jslider.addChangeListener(slCl);\r
+                               }\r
+                               else {\r
+                                       // 入力が不正な場合\r
+                                       jspinner.removeChangeListener(spCl);\r
+                                       jspinner.setValue(v2s(jslider.getValue()));\r
+                                       jspinner.addChangeListener(spCl);\r
+                               }\r
+                       }\r
+               };\r
+               jslider.addChangeListener(slCl);\r
+               jspinner.addChangeListener(spCl);\r
+       }\r
+       \r
+       // 抽象メソッド\r
+       \r
+       abstract void evHandle(int n);\r
+       \r
+       // 公開メソッド\r
+       \r
+       public int getValue() {\r
+               return jslider.getValue();\r
+       }\r
+       \r
+       public void setValue(int n) {\r
+               jslider.setValue(n);\r
+       }\r
+       \r
+       public void setHex(boolean b) {\r
+               bHex = b;\r
+               int n = jslider.getValue();\r
+               if (bHex) {\r
+                       model.setList(hexData);\r
+               }\r
+               else {\r
+                       model.setList(decData);\r
+               }\r
+               jslider.setValue(n);\r
+       }\r
+       \r
+       // 非公開メソッド\r
+       \r
+       private String v2s(int n) {\r
+               if (bHex) {\r
+                       return String.format("%x",n);\r
+               }\r
+               else {\r
+                       return String.format("%d",n);\r
+               }\r
+       }\r
+       \r
+       private int s2v(String s) {\r
+               if (bHex) {\r
+                       if (s.matches("^[0-9a-fA-F]+$")) {\r
+                               return Integer.decode("0x"+s);\r
+                       }\r
+                       else {\r
+                               return -1;\r
+                       }\r
+               }\r
+               else {\r
+                       if (s.matches("^[0-9]+$")) {\r
+                               return Integer.valueOf(s);\r
+                       }\r
+                       else {\r
+                               return -1;\r
+                       }\r
+               }\r
+       }\r
+}\r
diff --git a/TinyBannavi/src/tainavi/JComboBoxPanel.java b/TinyBannavi/src/tainavi/JComboBoxPanel.java
new file mode 100644 (file)
index 0000000..d96c9e8
--- /dev/null
@@ -0,0 +1,158 @@
+package tainavi;\r
+\r
+import java.awt.Dimension;\r
+import java.awt.ItemSelectable;\r
+import java.awt.Rectangle;\r
+import java.awt.event.ActionListener;\r
+import java.awt.event.ItemListener;\r
+\r
+import javax.swing.BoxLayout;\r
+import javax.swing.ComboBoxModel;\r
+import javax.swing.JComboBox;\r
+import javax.swing.JLabel;\r
+import javax.swing.JPanel;\r
+\r
+public class JComboBoxPanel extends JPanel implements ItemSelectable {\r
+\r
+       private static final long serialVersionUID = 1L;\r
+\r
+       private JComboBoxWithPopup jcombobox = null;\r
+       private JLabel jlabel = null;\r
+       \r
+       private final int h = 25;\r
+\r
+       // 旧版\r
+       public JComboBoxPanel(String s, int labelWidth, int comboboxWidth) {\r
+               makeComboBoxPanel(s, labelWidth, comboboxWidth, false);\r
+       }\r
+\r
+       // 新版\r
+       public JComboBoxPanel(String s, int labelWidth, int comboboxWidth, boolean horizontal) {\r
+               makeComboBoxPanel(s, labelWidth, comboboxWidth,  horizontal);\r
+       }\r
+       \r
+       private void makeComboBoxPanel(String s, int labelWidth, int comboboxWidth, boolean horizontal) {\r
+               if ( horizontal == true ) {\r
+                       // 左・右\r
+                       this.setLayout(new BoxLayout(this,BoxLayout.LINE_AXIS));\r
+                       \r
+                       jlabel = new JLabel(s);\r
+                       Dimension d = jlabel.getPreferredSize();\r
+                       d.width = labelWidth;\r
+                       d.height = 100;\r
+                       jlabel.setMaximumSize(d);\r
+                       this.add(jlabel);\r
+                       \r
+                       jcombobox = new JComboBoxWithPopup();\r
+                       d = jcombobox.getPreferredSize();\r
+                       d.width = comboboxWidth;\r
+                       d.height = 100;\r
+                       jcombobox.setMaximumSize(d);\r
+                       this.add(jcombobox);\r
+               }\r
+               else {\r
+                       // 上・下\r
+                       this.setLayout(null);\r
+                       \r
+                       this.add(jlabel = new JLabel(s));\r
+                       //Dimension d1 = jlabel.getPreferredSize();\r
+                       jlabel.setBounds(new Rectangle(0,0,labelWidth,h));\r
+                       \r
+                       this.add(jcombobox = new JComboBoxWithPopup());\r
+                       //Dimension d2 = jcombobox.getPreferredSize();\r
+                       jcombobox.setBounds(new Rectangle(5,25,comboboxWidth,h));\r
+                       \r
+                       this.setPreferredSize(new Dimension(comboboxWidth+5,h*2+5));\r
+               }\r
+       }\r
+\r
+       public void removeAllItems() {\r
+               this.jcombobox.removeAllItems();\r
+       }\r
+       \r
+       public void addItem(Object o) {\r
+               this.jcombobox.addItem(o);\r
+       }\r
+       \r
+       public int getSelectedIndex() {\r
+               return this.jcombobox.getSelectedIndex();\r
+       }\r
+       public Object getSelectedItem() {\r
+               return this.jcombobox.getSelectedItem();\r
+       }\r
+       public Object getItemAt(int index) {\r
+               return this.jcombobox.getItemAt(index);\r
+       }\r
+       \r
+       public int getItemCount() {\r
+               return this.jcombobox.getItemCount();\r
+       }\r
+       \r
+       public void setSelectedItem(Object o) {\r
+               this.jcombobox.setSelectedItem(o);\r
+       }\r
+       public void setSelectedIndex(int anIndex) {\r
+               this.jcombobox.setSelectedIndex(anIndex);\r
+       }\r
+       \r
+       public void setEditable(boolean b) {\r
+               this.jcombobox.setEditable(b);\r
+       }\r
+       \r
+       @Override\r
+       public void setEnabled(boolean b) {\r
+               this.jlabel.setEnabled(b);\r
+               this.jcombobox.setEnabled(b);\r
+       }\r
+       @Override\r
+       public boolean isEnabled() {\r
+               return this.jcombobox.isEnabled();\r
+       }\r
+       \r
+       public void setToolTipText(String s) {\r
+               this.jlabel.setToolTipText(s);\r
+       }\r
+       \r
+       public void setText(String s) {\r
+               this.jlabel.setText(s);\r
+       }\r
+       \r
+       public ComboBoxModel getModel() {\r
+               return this.jcombobox.getModel();\r
+       }\r
+\r
+       public JComboBox getJComboBox() { return jcombobox; }\r
+\r
+       // オーバーライドではない\r
+       \r
+       public void addActionListener(ActionListener l) {\r
+               this.jcombobox.addActionListener(l);\r
+       }\r
+       \r
+       /*\r
+       public ActionListener[] getActionListeners() {\r
+               return this.jcombobox.getActionListeners();\r
+       }\r
+       */\r
+       \r
+       public void removeActionListener(ActionListener l) {\r
+               this.jcombobox.removeActionListener(l);\r
+       }\r
+       \r
+       // オーバーライド\r
+       \r
+       @Override\r
+       public void addItemListener(ItemListener l) {\r
+               this.jcombobox.addItemListener(l);\r
+       }\r
+\r
+       @Override\r
+       public Object[] getSelectedObjects() {\r
+               return this.jcombobox.getSelectedObjects();\r
+       }\r
+\r
+       @Override\r
+       public void removeItemListener(ItemListener l) {\r
+               this.jcombobox.removeItemListener(l);\r
+       }\r
+}\r
diff --git a/TinyBannavi/src/tainavi/JComboBoxWithPopup.java b/TinyBannavi/src/tainavi/JComboBoxWithPopup.java
new file mode 100644 (file)
index 0000000..69eafaa
--- /dev/null
@@ -0,0 +1,23 @@
+package tainavi;\r
+\r
+\r
+public class JComboBoxWithPopup extends JWideComboBox {\r
+\r
+       private static final long serialVersionUID = 1L;\r
+\r
+       private TextEditPopupMenu tepm = new TextEditPopupMenu();\r
+       \r
+       public JComboBoxWithPopup() {\r
+               super();\r
+       }\r
+       \r
+       public void setEditable(boolean b) {\r
+               super.setEditable(b);\r
+               if (b) {\r
+                       this.getEditor().getEditorComponent().addMouseListener(tepm);\r
+               }\r
+               else {\r
+                       this.getEditor().getEditorComponent().removeMouseListener(tepm);\r
+               }\r
+       }\r
+}\r
diff --git a/TinyBannavi/src/tainavi/JDetailPanel.java b/TinyBannavi/src/tainavi/JDetailPanel.java
new file mode 100644 (file)
index 0000000..14236d3
--- /dev/null
@@ -0,0 +1,74 @@
+package tainavi;\r
+\r
+import java.awt.BorderLayout;\r
+import java.awt.Color;\r
+import java.awt.Font;\r
+\r
+import javax.swing.JLabel;\r
+import javax.swing.JPanel;\r
+import javax.swing.JScrollPane;\r
+import javax.swing.border.EmptyBorder;\r
+\r
+\r
+public class JDetailPanel extends JPanel {\r
+\r
+       private static final long serialVersionUID = 1L;\r
+\r
+       JScrollPane jscrollpane = null;\r
+       private JTextAreaWithPopup jta = null;\r
+       private JLabel jlabel_time = null;\r
+       private JLabel jlabel_title = null;\r
+       \r
+       private final int titleFontSize = 20;\r
+       //private int textAreaRows = 4;\r
+       \r
+       public JDetailPanel() {\r
+               \r
+               this.setLayout(new BorderLayout());\r
+               \r
+               Font f = null;\r
+               \r
+               jlabel_time = new JLabel();\r
+               f = jlabel_time.getFont();\r
+               jlabel_time.setFont(f.deriveFont(f.getStyle() | Font.BOLD, titleFontSize));\r
+               this.add(jlabel_time,BorderLayout.LINE_START);\r
+               \r
+               jlabel_title = new JLabel();\r
+               f = jlabel_title.getFont();\r
+               jlabel_title.setFont(f.deriveFont(f.getStyle() | Font.BOLD, titleFontSize));\r
+               jlabel_title.setForeground(Color.BLUE);\r
+               this.add(jlabel_title,BorderLayout.CENTER);\r
+               jlabel_title.setText(" ");\r
+               \r
+               jta = CommonSwingUtils.getJta(this,4,0);\r
+               \r
+               jscrollpane = new JScrollPane(jta,JScrollPane.VERTICAL_SCROLLBAR_AS_NEEDED,JScrollPane.HORIZONTAL_SCROLLBAR_NEVER);\r
+               jscrollpane.setBorder(new EmptyBorder(0,0,0,0));\r
+               this.add(jscrollpane,BorderLayout.PAGE_END);\r
+       }\r
+       \r
+       public void setLabel(String s, String e, String t) {\r
+               if (s == null || s.length() == 0 || e == null || e.length() == 0) {\r
+                       jlabel_time.setText(" ");\r
+               }\r
+               else {\r
+                       jlabel_time.setText(s+"~"+e+" ");\r
+               }\r
+               jlabel_title.setText(t);\r
+       }\r
+       public String getText() {\r
+               return jta.getText();\r
+       }\r
+       public void setText(String s) {\r
+               jta.setText(s);\r
+               jta.setCaretPosition(0);\r
+       }\r
+       \r
+       public int getRows() {\r
+               return jta.getRows();\r
+       }\r
+       \r
+       public void setRows(int rows) {\r
+               jta.setRows(rows);\r
+       }\r
+}\r
diff --git a/TinyBannavi/src/tainavi/JListSortDialog.java b/TinyBannavi/src/tainavi/JListSortDialog.java
new file mode 100644 (file)
index 0000000..78114d2
--- /dev/null
@@ -0,0 +1,221 @@
+package tainavi;\r
+\r
+import java.awt.Dimension;\r
+import java.awt.event.ActionEvent;\r
+import java.awt.event.ActionListener;\r
+import java.util.ArrayList;\r
+\r
+import javax.swing.JButton;\r
+import javax.swing.JDialog;\r
+import javax.swing.JPanel;\r
+import javax.swing.JScrollPane;\r
+import javax.swing.ListSelectionModel;\r
+import javax.swing.ScrollPaneConstants;\r
+import javax.swing.SpringLayout;\r
+import javax.swing.table.DefaultTableModel;\r
+\r
+\r
+public class JListSortDialog extends JDialog {\r
+\r
+       private static final long serialVersionUID = 1L;\r
+\r
+       private boolean reg = false;\r
+       \r
+       private ArrayList<String> rowData = null;\r
+\r
+       private JPanel jpan = null;\r
+       private JButton jbtn_update = null;\r
+       private JButton jbtn_cancel = null;\r
+       private JButton jbtn_remove = null;\r
+       private JButton jbtn_up = null;\r
+       private JButton jbtn_down = null;\r
+       \r
+       private JScrollPane jscr_entries = null;\r
+       private JNETable jtbl_entries = null;\r
+       \r
+       /*\r
+        *  コンストラクタ\r
+        */\r
+       \r
+       public JListSortDialog(String wTitle, ArrayList<String> oList) {\r
+               \r
+               super();\r
+\r
+               rowData = oList;\r
+               \r
+               this.setModal(true);\r
+               this.setTitle(wTitle);\r
+               this.setContentPane(getJPan());\r
+               \r
+               // タイトルバーの高さも考慮する必要がある\r
+               Dimension d = jpan.getPreferredSize();\r
+               this.pack();\r
+               this.setPreferredSize(new Dimension(d.width, d.height+this.getInsets().top));\r
+               this.setResizable(false);\r
+       }\r
+       \r
+       /*\r
+        * 公開メソッド\r
+        */\r
+       \r
+       public boolean isRegistered() { return reg; } \r
+\r
+       /*\r
+        * 非公開メソッド\r
+        */\r
+\r
+       private JPanel getJPan() {\r
+               if (jpan == null) {\r
+                       \r
+                       jpan = new JPanel();\r
+                       jpan.setLayout(new SpringLayout());\r
+                       \r
+                       //\r
+                       int y = 10;\r
+                       CommonSwingUtils.putComponentOn(jpan, getJScr_entries(), 400, 500, 10, y);\r
+                       \r
+                       CommonSwingUtils.putComponentOn(jpan, getJBtn_remove("削除"), 100, 25, 10+400+10, y+500-(10+25)*4);\r
+                       \r
+                       CommonSwingUtils.putComponentOn(jpan, getJBtn_up("上へ"), 100, 25, 10+400+10, y+500-(10+25)*2);\r
+                       CommonSwingUtils.putComponentOn(jpan, getJBtn_down("下へ"), 100, 25, 10+400+10, y+500-(10+25)*1);\r
+\r
+                       y+=(500+10);\r
+                       \r
+                       CommonSwingUtils.putComponentOn(jpan, getJBtn_update("更新"), 100, 25, 10+20, y);\r
+                       CommonSwingUtils.putComponentOn(jpan, getJBtn_cancel("キャンセル"), 100, 25, 10+400-(100+20), y);\r
+                       \r
+                       y+=(25+10);\r
+                       \r
+                       Dimension d = new Dimension(530,y);\r
+                       jpan.setPreferredSize(d);\r
+               }\r
+               return jpan;\r
+       }\r
+\r
+       // テーブル作成\r
+       private JScrollPane getJScr_entries() {\r
+               if (jscr_entries == null) {\r
+                       jscr_entries = new JScrollPane();\r
+                       jscr_entries.setVerticalScrollBarPolicy(ScrollPaneConstants.VERTICAL_SCROLLBAR_ALWAYS);\r
+                       jscr_entries.getVerticalScrollBar().setUnitIncrement(25);\r
+                       jscr_entries.setViewportView(getJTbl_entries());\r
+               }\r
+               return jscr_entries;\r
+       }\r
+       private JNETable getJTbl_entries() {\r
+               if (jtbl_entries == null) {\r
+                       String[] colname = { "アイテム" };\r
+                       DefaultTableModel model = new DefaultTableModel(colname,0);\r
+                       jtbl_entries = new JNETable(model,false) {\r
+\r
+                               private static final long serialVersionUID = 1L;\r
+\r
+                               @Override\r
+                               public Object getValueAt(int row, int column) {\r
+                                       return rowData.get(row);\r
+                               }\r
+                               @Override\r
+                               public int getRowCount() {\r
+                                       return rowData.size();\r
+                               }\r
+                       };\r
+                       jtbl_entries.getTableHeader().setReorderingAllowed(false);\r
+                       \r
+                       // 本来ならコンストラクタで指定できるべきであったが\r
+                       jtbl_entries.setSelectionMode(ListSelectionModel.SINGLE_INTERVAL_SELECTION);\r
+               }\r
+               return jtbl_entries;\r
+       }\r
+       \r
+       // 削除\r
+       private JButton getJBtn_remove(String s) {\r
+               if (jbtn_remove == null) {\r
+                       jbtn_remove = new JButton(s);\r
+                       jbtn_remove.addActionListener(new ActionListener() {\r
+                               @Override\r
+                               public void actionPerformed(ActionEvent e) {\r
+                                       int vrow = jtbl_entries.getSelectedRow();\r
+                                       for ( int i=0; i<jtbl_entries.getSelectedRowCount(); i++ ) {\r
+                                               rowData.remove(vrow);\r
+                                       }\r
+                                       ((DefaultTableModel) jtbl_entries.getModel()).fireTableDataChanged();\r
+                               }\r
+                       });\r
+               }\r
+               return jbtn_remove;\r
+       }\r
+       \r
+       // 上へ・下へ\r
+       private JButton getJBtn_up(String s) {\r
+               if (jbtn_up == null) {\r
+                       jbtn_up = new JButton(s);\r
+                       jbtn_up.addActionListener(new ActionListener() {\r
+                               @Override\r
+                               public void actionPerformed(ActionEvent e) {\r
+                                       int vrow = jtbl_entries.getSelectedRow();\r
+                                       int cnt = jtbl_entries.getSelectedRowCount();\r
+                                       if (vrow >= 1) {\r
+                                               String a = rowData.get(vrow-1);\r
+                                               for ( int i=0; i<cnt; i++ ) {\r
+                                                       rowData.set(vrow+i-1,rowData.get(vrow+i));\r
+                                               }\r
+                                               rowData.set(vrow+cnt-1,a);\r
+                                               ((DefaultTableModel) jtbl_entries.getModel()).fireTableDataChanged();\r
+                                               jtbl_entries.setRowSelectionInterval(vrow-1, vrow-1+(cnt-1));\r
+                                       }\r
+                               }\r
+                       });\r
+               }\r
+               return jbtn_up;\r
+       }\r
+       private JButton getJBtn_down(String s) {\r
+               if (jbtn_down == null) {\r
+                       jbtn_down = new JButton(s);\r
+                       jbtn_down.addActionListener(new ActionListener() {\r
+                               @Override\r
+                               public void actionPerformed(ActionEvent e) {\r
+                                       int vrow = jtbl_entries.getSelectedRow();\r
+                                       int cnt = jtbl_entries.getSelectedRowCount();\r
+                                       if ((vrow+cnt-1) <= jtbl_entries.getRowCount()-2) {\r
+                                               String a = rowData.get(vrow+cnt);\r
+                                               for ( int i=cnt-1; i>=0; i-- ) {\r
+                                                       rowData.set(vrow+i+1,rowData.get(vrow+i));\r
+                                               }\r
+                                               rowData.set(vrow,a);\r
+                                               ((DefaultTableModel) jtbl_entries.getModel()).fireTableDataChanged();\r
+                                               jtbl_entries.setRowSelectionInterval(vrow+1, vrow+1+(cnt-1));\r
+                                       }\r
+                               }\r
+                       });\r
+               }\r
+               return jbtn_down;\r
+       }\r
+       \r
+       //\r
+       private JButton getJBtn_update(String s) {\r
+               if (jbtn_update == null) {\r
+                       jbtn_update = new JButton(s);\r
+                       jbtn_update.addActionListener(new ActionListener() {\r
+                               @Override\r
+                               public void actionPerformed(ActionEvent e) {\r
+                                       reg = true;\r
+                                       dispose();\r
+                               }\r
+                       });\r
+               }\r
+               return jbtn_update;\r
+       }\r
+       private JButton getJBtn_cancel(String s) {\r
+               if (jbtn_cancel == null) {\r
+                       jbtn_cancel = new JButton(s);\r
+                       jbtn_cancel.addActionListener(new ActionListener() {\r
+                               @Override\r
+                               public void actionPerformed(ActionEvent e) {\r
+                                       reg = false;\r
+                                       dispose();\r
+                               }\r
+                       });\r
+               }\r
+               return jbtn_cancel;\r
+       }\r
+}\r
diff --git a/TinyBannavi/src/tainavi/JNETable.java b/TinyBannavi/src/tainavi/JNETable.java
new file mode 100644 (file)
index 0000000..1f120c9
--- /dev/null
@@ -0,0 +1,135 @@
+package tainavi;\r
+\r
+import java.awt.Color;\r
+import java.awt.Component;\r
+\r
+import javax.swing.JTable;\r
+import javax.swing.ListSelectionModel;\r
+import javax.swing.table.TableCellRenderer;\r
+import javax.swing.table.TableColumn;\r
+import javax.swing.table.TableModel;\r
+\r
+\r
+public class JNETable extends JTable {\r
+\r
+       private static final long serialVersionUID = 1L;\r
+\r
+       // 編集禁止\r
+       @Override\r
+       public boolean isCellEditable(int row, int column) {\r
+               return false;\r
+       }\r
+\r
+       // 行ごとに色を変える\r
+       protected boolean isSepRowColor = true;\r
+       public void setSepRowColor(boolean b) { isSepRowColor = b; }\r
+       \r
+       protected static final Color evenColor = new Color(240,240,255);\r
+       \r
+       @Override\r
+       public Component prepareRenderer(TableCellRenderer tcr, int row, int column) {\r
+               Component c = super.prepareRenderer(tcr, row, column);\r
+               Color fgColor = null;\r
+               Color bgColor = null;\r
+               if(isRowSelected(row)) {\r
+                       fgColor = this.getSelectionForeground();\r
+                       bgColor = this.getSelectionBackground();\r
+               }\r
+               else {\r
+                       fgColor = this.getForeground();\r
+                       bgColor = (isSepRowColor && row%2 == 1)?(evenColor):(super.getBackground());\r
+               }\r
+               if ( ! (tcr instanceof VWColorCharCellRenderer) && ! (tcr instanceof VWColorCharCellRenderer2) && ! (tcr instanceof VWColorCellRenderer)) {\r
+                       c.setForeground((this.isEnabled()) ? fgColor : Color.GRAY);\r
+               }               \r
+               if ( ! (tcr instanceof VWColorCellRenderer)) {\r
+                       c.setBackground(bgColor);\r
+               }\r
+               return c;\r
+       }\r
+       \r
+       // 列の表示・非表示\r
+       private class ColumnData {\r
+               TableColumn column;\r
+               boolean visible;\r
+       }\r
+       \r
+       private ColumnData[] colDat = null;\r
+       \r
+       public void setColumnVisible(String name, boolean b) {\r
+               \r
+               if ( name == null ) {\r
+                       return;\r
+               }\r
+               \r
+               // 初回は情報を蓄える\r
+               if ( colDat == null ) {\r
+                       colDat = new ColumnData[this.getColumnModel().getColumnCount()];\r
+                       for ( int i=0; i<colDat.length; i++ ) {\r
+                               ColumnData cdat = new ColumnData();\r
+                               cdat.column = this.getColumnModel().getColumn(i);\r
+                               cdat.visible = true;\r
+                               colDat[i] = cdat;\r
+                       }\r
+               }\r
+               \r
+               // 列を検索する\r
+               int idx = -1;\r
+               for ( int i=0; i<colDat.length; i++ ) {\r
+                       if ( name.equals((String)colDat[i].column.getHeaderValue()) ) {\r
+                               if ( colDat[i].visible != b ) {\r
+                                       idx = i;\r
+                               }\r
+                               break;\r
+                       }\r
+               }\r
+               if ( idx == -1 ) {\r
+                       return;\r
+               }\r
+               \r
+               if ( ! b ) {\r
+                       // 削除する\r
+                       colDat[idx].visible = false;\r
+                       this.removeColumn(colDat[idx].column);\r
+               }\r
+               else {\r
+                       // 一旦全て削除して\r
+                       for ( int i=0; i<colDat.length; i++ ) {\r
+                               if ( colDat[i].visible == true ) {\r
+                                       this.removeColumn(colDat[i].column);\r
+                               }\r
+                       }\r
+                       // 有効列のみ追加しなおす\r
+                       colDat[idx].visible = true;\r
+                       for ( int i=0; i<colDat.length; i++ ) {\r
+                               if ( colDat[i].visible == true ) {\r
+                                       this.addColumn(colDat[i].column);\r
+                               }\r
+                       }\r
+               }\r
+       }\r
+       \r
+       /*\r
+        * コンストラクタ\r
+        */\r
+       public JNETable(boolean multi_select) {\r
+               if (multi_select) {\r
+                       this.setSelectionMode(ListSelectionModel.MULTIPLE_INTERVAL_SELECTION);\r
+               }\r
+               else {\r
+                       this.setSelectionMode(ListSelectionModel.SINGLE_SELECTION);\r
+               }\r
+               this.getTableHeader().setReorderingAllowed(false);\r
+               \r
+               // フォントサイズ変更にあわせて行の高さを変える\r
+               this.addPropertyChangeListener("font", new RowHeightChangeListener(4));\r
+\r
+               // 行の高さの初期値の設定\r
+               this.firePropertyChange("font", "old", "new");\r
+       }\r
+       \r
+       public JNETable(TableModel d, boolean b) {\r
+               this(b);\r
+               this.setModel(d);\r
+       }\r
+}\r
diff --git a/TinyBannavi/src/tainavi/JOptOptionPane.java b/TinyBannavi/src/tainavi/JOptOptionPane.java
new file mode 100644 (file)
index 0000000..94336bc
--- /dev/null
@@ -0,0 +1,34 @@
+package tainavi;\r
+\r
+import java.awt.Component;\r
+\r
+import javax.swing.BoxLayout;\r
+import javax.swing.JLabel;\r
+import javax.swing.JOptionPane;\r
+import javax.swing.JPanel;\r
+\r
+public class JOptOptionPane extends JOptionPane {\r
+       \r
+       private static final long serialVersionUID = 1L;\r
+       \r
+       private static JCheckBoxPanel jcheckbox = null;\r
+\r
+       public static int showConfirmDialog(Component parentComponent, String message, String notice, String title, int optionType) {\r
+               JPanel panel = new JPanel();\r
+               BoxLayout layout = new BoxLayout(panel,BoxLayout.Y_AXIS);\r
+               panel.setLayout(layout);\r
+               JLabel jlabel = new JLabel(message,JLabel.CENTER);\r
+               jlabel.setAlignmentX(Component.LEFT_ALIGNMENT);\r
+               panel.add(jlabel);\r
+               panel.add(new JLabel(" "));     // Vgap\r
+               jcheckbox = new JCheckBoxPanel(notice, 0, true);\r
+               jcheckbox.setAlignmentX(Component.LEFT_ALIGNMENT);\r
+               panel.add(jcheckbox);\r
+               return showConfirmDialog(parentComponent, panel, title, optionType);\r
+       }\r
+       \r
+       public static boolean isSelected() {\r
+               return (jcheckbox != null && jcheckbox.isSelected());\r
+       }\r
+\r
+}\r
diff --git a/TinyBannavi/src/tainavi/JRMLabel.java b/TinyBannavi/src/tainavi/JRMLabel.java
new file mode 100644 (file)
index 0000000..e746b33
--- /dev/null
@@ -0,0 +1,133 @@
+package tainavi;\r
+\r
+import java.awt.Color;\r
+import java.awt.Dimension;\r
+import java.awt.FontMetrics;\r
+import java.awt.Graphics;\r
+import java.awt.Graphics2D;\r
+import java.awt.font.TextAttribute;\r
+import java.awt.geom.Rectangle2D;\r
+import java.awt.image.BufferedImage;\r
+import java.text.AttributedCharacterIterator;\r
+import java.text.AttributedString;\r
+\r
+import javax.swing.JLabel;\r
+\r
+public class JRMLabel extends JLabel {\r
+\r
+       private static final long serialVersionUID = 1L;\r
+\r
+       private String enc = "";\r
+       private String date = "";\r
+       private String center = "";\r
+       private boolean exec = true;\r
+       private BufferedImage image = null;\r
+       private Color encForeground = Color.WHITE;\r
+       private Color encBackground = Color.BLACK;\r
+       \r
+       // 予約枠の仮想座標\r
+       private int vrow;\r
+       private int vcolumn;\r
+       private int vheight;\r
+       private int vwidth;\r
+       \r
+       private static int columnWidth = 0;\r
+       private static float heightMultiplier = 0;\r
+       \r
+       @Override\r
+       public void repaint() {\r
+               image = null;\r
+               super.repaint();\r
+       }\r
+\r
+       @Override\r
+       protected void paintComponent(Graphics g) { \r
+               \r
+               super.paintComponent(g);\r
+               \r
+               // 初回描画時\r
+               if (image == null) {\r
+                       //\r
+                       Dimension  d  = this.getSize();\r
+                       int w = d.width;\r
+                       int h = d.height;\r
+                       image = new BufferedImage(w, h, BufferedImage.TYPE_INT_ARGB);\r
+                       Graphics2D g2 = (Graphics2D)image.createGraphics();\r
+                       \r
+                       // 書き込む文字列のサイズを算出する\r
+                       FontMetrics fm = g2.getFontMetrics();\r
+                       Rectangle2D r = fm.getStringBounds(enc,g2);\r
+                       \r
+                       // 書き込む位置を決定する\r
+                       int x = 0;\r
+                       int y = 0;\r
+                       if (this.getHorizontalAlignment() == JLabel.RIGHT) {\r
+                               x = w - (int)r.getWidth() - 6;\r
+                       }\r
+                       else {\r
+                               x = 6;\r
+                       }\r
+                       if (this.getVerticalAlignment() == JLabel.BOTTOM) {\r
+                               y = h - (int)r.getHeight()/2 - 1;\r
+                       }\r
+                       else {\r
+                               y = (int)r.getHeight() + 1;\r
+                       }\r
+                       \r
+                       // 書き込む\r
+                       AttributedString as = new AttributedString(enc);\r
+                       as.addAttribute(TextAttribute.FOREGROUND, this.getEncForeground());\r
+                       as.addAttribute(TextAttribute.BACKGROUND, this.getEncBackground());\r
+                       AttributedCharacterIterator ac = as.getIterator();\r
+                       g2.drawString(ac, x, y);\r
+               }\r
+               \r
+               // 反映\r
+               g.drawImage(image, 0, 0, this);\r
+       }\r
+       \r
+       // データ操作メソッド\r
+       public void setDate(String s) { date = s; }\r
+       public String getDate() { return date; }\r
+       public void setCenter(String s) { center = s; }\r
+       public String getCenter() { return center; }\r
+       public void setEncoder(String s) { enc = s; }\r
+       public String getEncoder() { return enc; }\r
+       public void setExec(boolean b) { exec = b; }\r
+       public boolean getExec() { return exec; }\r
+\r
+       public void setEncForeground(Color c) { encForeground = c; }\r
+       public Color getEncForeground() { return encForeground; }\r
+       public void setEncBackground(Color c) { encBackground = c; }\r
+       public Color getEncBackground() { return encBackground; }\r
+       \r
+       //public void setVRow(int n) { vrow = n; }\r
+       public int getVRow() { return vrow; }\r
+       //public void setVColumn(int n) { vcolumn = n; }\r
+       public int getVColumn() { return vcolumn; }\r
+       //public void setVHeight(int n) { vheight = n; }\r
+       public int getVHeight() { return vheight; }\r
+       \r
+       public static void setColumnWidth(int n) { columnWidth = n; }\r
+       public static void setHeightMultiplier(float f) { heightMultiplier = f; }\r
+       \r
+       public void setVBounds(int x, int y, int width, int height) {\r
+               vrow = y;\r
+               vcolumn = x;\r
+               vheight = height;\r
+               vwidth = width;\r
+               super.setBounds(\r
+                               vcolumn*columnWidth,\r
+                               (int) Math.ceil(((float)vrow)*heightMultiplier),\r
+                               vwidth*columnWidth,\r
+                               (int) Math.ceil(((float)vheight)*heightMultiplier));\r
+       }\r
+       \r
+       public void reVBounds() {\r
+               super.setBounds(\r
+                               vcolumn*columnWidth,\r
+                               (int) Math.ceil(((float)vrow)*heightMultiplier),\r
+                               vwidth*columnWidth,\r
+                               (int) Math.ceil(((float)vheight)*heightMultiplier));\r
+       }\r
+}\r
diff --git a/TinyBannavi/src/tainavi/JRadioButtonPanel.java b/TinyBannavi/src/tainavi/JRadioButtonPanel.java
new file mode 100644 (file)
index 0000000..d801d57
--- /dev/null
@@ -0,0 +1,89 @@
+package tainavi;\r
+\r
+import java.awt.Dimension;\r
+import java.awt.event.ItemListener;\r
+import java.util.ArrayList;\r
+\r
+import javax.swing.BoxLayout;\r
+import javax.swing.ButtonGroup;\r
+import javax.swing.JLabel;\r
+import javax.swing.JPanel;\r
+import javax.swing.JRadioButton;\r
+\r
+\r
+public class JRadioButtonPanel extends JPanel {\r
+\r
+       private static final long serialVersionUID = 1L;\r
+\r
+       private ButtonGroup bg = null;\r
+       private ArrayList<JRadioButton> jrblist = null;\r
+       private JLabel jlabel = null;\r
+       private JPanel jpanel = null;\r
+       \r
+       public JRadioButtonPanel(String s, int labelWidth) {\r
+               this.setLayout(new BoxLayout(this,BoxLayout.LINE_AXIS));\r
+               \r
+               jlabel = new JLabel(s);\r
+               Dimension d = jlabel.getPreferredSize();\r
+               d.width = labelWidth;\r
+               jlabel.setMaximumSize(d);\r
+               this.add(jlabel);\r
+               \r
+               jpanel = new JPanel();\r
+               jpanel.setLayout(new BoxLayout(jpanel,BoxLayout.PAGE_AXIS));\r
+               this.add(jpanel);\r
+               \r
+               //this.add(Box.createRigidArea(new Dimension(10,d.height)));\r
+               \r
+               bg = new ButtonGroup();\r
+               jrblist = new ArrayList<JRadioButton>();\r
+       }\r
+\r
+       public void add(String text, boolean selected) {\r
+               JRadioButton jrb = new JRadioButton(text,selected);\r
+               bg.add(jrb);\r
+               jpanel.add(jrb);\r
+               jrblist.add(jrb);\r
+       }\r
+       public JRadioButton getSelectedItem() {\r
+               for ( int i=0; i<jrblist.size(); i++ ) {\r
+                       if (jrblist.get(i).isSelected()) {\r
+                               return jrblist.get(i);\r
+                       }\r
+               }\r
+               return null;\r
+       }\r
+       public void setSelectedItem(String text) {\r
+               for ( int i=0; i<jrblist.size(); i++ ) {\r
+                       if (jrblist.get(i).getText().equals(text)) {\r
+                               jrblist.get(i).setSelected(true);\r
+                               return;\r
+                       }\r
+               }\r
+       }\r
+       public int getSelectedIndex() {\r
+               for ( int i=0; i<jrblist.size(); i++ ) {\r
+                       if (jrblist.get(i).isSelected()) {\r
+                               return i;\r
+                       }\r
+               }\r
+               return 0;\r
+       }\r
+       public void setSelectedIndex(int n) {\r
+               if (jrblist.size() > n) {\r
+                       jrblist.get(n).setSelected(true);\r
+               }\r
+       }\r
+       \r
+       public void addItemListener(ItemListener l) {\r
+               for ( JRadioButton b : jrblist ) {\r
+                       b.addItemListener(l);\r
+               }\r
+       }\r
+       \r
+       public void removeItemListener(ItemListener l) {\r
+               for ( JRadioButton b : jrblist ) {\r
+                       b.removeItemListener(l);\r
+               }\r
+       }\r
+}\r
diff --git a/TinyBannavi/src/tainavi/JSliderPanel.java b/TinyBannavi/src/tainavi/JSliderPanel.java
new file mode 100644 (file)
index 0000000..44c7879
--- /dev/null
@@ -0,0 +1,75 @@
+\r
+package tainavi;\r
+\r
+import java.awt.Dimension;\r
+\r
+import javax.swing.BoxLayout;\r
+import javax.swing.JLabel;\r
+import javax.swing.JPanel;\r
+import javax.swing.JSlider;\r
+import javax.swing.event.ChangeEvent;\r
+import javax.swing.event.ChangeListener;\r
+\r
+public class JSliderPanel extends JPanel {\r
+\r
+       private static final long serialVersionUID = 1L;\r
+\r
+       private JSlider jslider = null;\r
+       private JLabel jlabel = null;\r
+       private String labelstr = null;\r
+       \r
+       public JSliderPanel(String s, int labelWidth, int min, int max, int sliderWidth) {\r
+               this(s, labelWidth, min, max, 1, sliderWidth);\r
+       }\r
+       public JSliderPanel(String s, int labelWidth, int min, int max, int step, int sliderWidth) {\r
+               \r
+               this.setLayout(new BoxLayout(this,BoxLayout.LINE_AXIS));\r
+               \r
+               Dimension d = null;\r
+               \r
+               jlabel = new JLabel(labelstr = s);\r
+               d = jlabel.getPreferredSize();\r
+               d.width = labelWidth;\r
+               d.height = 100;\r
+               jlabel.setMaximumSize(d);\r
+               this.add(jlabel);\r
+\r
+               final int stepby = (step>0)?(step):(1);\r
+               max /= stepby;\r
+               min /= stepby;\r
+               \r
+               jslider = new JSlider(min,max);\r
+               d = jslider.getPreferredSize();\r
+               d.width = sliderWidth;\r
+               d.height = 100;\r
+               jslider.setMaximumSize(d);\r
+               jslider.setSnapToTicks(true);\r
+               \r
+               jslider.addChangeListener(new ChangeListener() {\r
+                       @Override\r
+                       public void stateChanged(ChangeEvent e) {\r
+                               jlabel.setText(labelstr+"["+(jslider.getValue()*stepby)+"]");\r
+                       }\r
+               });\r
+               setValue(min);\r
+               this.add(jslider);\r
+       }\r
+       \r
+       public int getValue() {\r
+               return jslider.getValue();\r
+       }\r
+       public void setValue(int n) {\r
+               jslider.setValue(n);\r
+       }\r
+       public void setEnabled(boolean b) {\r
+               this.jlabel.setEnabled(b);\r
+               this.jslider.setEnabled(b);\r
+       }\r
+       \r
+       public void addChangeListener(ChangeListener l) {\r
+               this.jslider.addChangeListener(l);\r
+       }\r
+       public void removeChangeListener(ChangeListener l) {\r
+               this.jslider.removeChangeListener(l);\r
+       }\r
+}\r
diff --git a/TinyBannavi/src/tainavi/JTXTButton.java b/TinyBannavi/src/tainavi/JTXTButton.java
new file mode 100644 (file)
index 0000000..a1a1b02
--- /dev/null
@@ -0,0 +1,398 @@
+package tainavi;\r
+\r
+import java.awt.AlphaComposite;\r
+import java.awt.Color;\r
+import java.awt.Dimension;\r
+import java.awt.Font;\r
+import java.awt.Graphics;\r
+import java.awt.Graphics2D;\r
+import java.awt.RenderingHints;\r
+import java.awt.font.FontRenderContext;\r
+import java.awt.font.LineBreakMeasurer;\r
+import java.awt.font.TextAttribute;\r
+import java.awt.font.TextLayout;\r
+import java.awt.image.BufferedImage;\r
+import java.text.AttributedCharacterIterator;\r
+import java.text.AttributedString;\r
+import java.util.ArrayList;\r
+\r
+import javax.swing.JButton;\r
+import javax.swing.JLabel;\r
+\r
+\r
+public class JTXTButton extends JLabel {\r
+\r
+       private static final long serialVersionUID = 1L;\r
+\r
+       /*******************************************************************************\r
+        * 定数\r
+        ******************************************************************************/\r
+       \r
+       /**\r
+        *  フォントスタイル\r
+        */\r
+       public static enum FontStyle {\r
+               BOLD    ("太字"),\r
+               ITALIC  ("斜体"),\r
+               UNDERLINE       ("下線");\r
+               \r
+               private String name;\r
+               \r
+               private FontStyle(String name) {\r
+                       this.name = name;\r
+               }\r
+               \r
+               @Override\r
+               public String toString() {\r
+                       return name;\r
+               }\r
+               \r
+               \r
+               public String getId() {\r
+                       return super.toString();\r
+               }\r
+               \r
+               public static FontStyle get(String id) {\r
+                       for ( FontStyle fs : FontStyle.values() ) {\r
+                               if ( fs.getId().equals(id) ) {\r
+                                       return fs;\r
+                               }\r
+                       }\r
+                       return null;\r
+               }\r
+       };\r
+       \r
+       private static final float DRAWTAB = 2.0F; \r
+       \r
+       \r
+       /*******************************************************************************\r
+        * 部品\r
+        ******************************************************************************/\r
+       \r
+       // 描画バッファ\r
+       private BufferedImage image = null;             // ビットマップ\r
+       \r
+       private int vrow;                                               // 仮想座標縦位置\r
+       private int vcolumn;                                    // 仮想座標横位置\r
+       private int vheight;                                    // 仮想座標高さ\r
+       private int vwidth;                                             // 仮想座標幅\r
+       \r
+       // 番組情報\r
+       private ProgDetailList tvd = null;              // 番組情報そのまま\r
+       \r
+       // 表示設定\r
+       private static boolean showStart = true;\r
+       private static boolean splitEpno = false;\r
+       private static boolean showDetail = true;\r
+       private static float detailTab = 2.0F;\r
+       private static int detailRows = 3;\r
+       \r
+       private static Font defaultFont = new JLabel().getFont();\r
+       private static Font titleFont = defaultFont;\r
+       private static int titleFontSize = defaultFont.getSize();\r
+       private static Color titleFontColor = Color.BLUE;\r
+       private static int titleFontStyle = Font.BOLD;\r
+       private static boolean titleFontUL = true;\r
+       private static Font detailFont = defaultFont;\r
+       private static int detailFontSize = defaultFont.getSize();\r
+       private static Color detailFontColor = Color.DARK_GRAY;\r
+       private static int detailFontStyle = defaultFont.getStyle();\r
+       private static boolean detailFontUL = false;\r
+       private static Object aahint = RenderingHints.VALUE_TEXT_ANTIALIAS_ON;\r
+\r
+       private static int columnWidth = 0;\r
+       private static float heightMultiplier = 0;\r
+       \r
+       /*******************************************************************************\r
+        * コンストラクタ\r
+        ******************************************************************************/\r
+       \r
+       // ないよ\r
+       \r
+       \r
+       /*******************************************************************************\r
+        * メソッド\r
+        ******************************************************************************/\r
+       \r
+       // 内容をリセットする\r
+       // setVisible(false)するとリソースが解放されてしまうのか再描画に時間がかかるようになるので表示範囲外に出して隠してしまう\r
+       public void clean() {\r
+               tvd = null;\r
+               image = null;\r
+               setBounds(-1,-1,0,0);\r
+       }\r
+       \r
+       // フラグを変えた後に再描画させる\r
+       public void forceRepaint() {\r
+               image = null;\r
+               super.repaint();\r
+       }\r
+       \r
+       // 仮想位置の変更\r
+       //public void setVRow(int n) { vrow = n; }\r
+       public int getVRow() { return vrow; }\r
+       //public void setVColumn(int n) { vcolumn = n; }\r
+       public int getVColumn() { return vcolumn; }\r
+       //public void setVHeight(int n) { vheight = n; }\r
+       public int getVHeight() { return vheight; }\r
+       \r
+       public static void setColumnWidth(int n) { columnWidth = n; }\r
+       public static void setHeightMultiplier(float f) { heightMultiplier = f; }\r
+       \r
+       public void setVBounds(int x, int y, int width, int height) {\r
+               vrow = y;\r
+               vcolumn = x;\r
+               vheight = height;\r
+               vwidth = width;\r
+               super.setBounds(\r
+                               vcolumn*columnWidth,\r
+                               (int) Math.ceil(((float)vrow)*heightMultiplier),\r
+                               vwidth*columnWidth,\r
+                               (int) Math.ceil(((float)vheight)*heightMultiplier));\r
+       }\r
+       \r
+       public void reVBounds() {\r
+               super.setBounds(\r
+                               vcolumn*columnWidth,\r
+                               (int) Math.ceil(((float)vrow)*heightMultiplier),\r
+                               vwidth*columnWidth,\r
+                               (int) Math.ceil(((float)vheight)*heightMultiplier));\r
+       }\r
+       \r
+       // 番組情報のやりとり\r
+       public void setInfo(ProgDetailList tvd) {\r
+               this.tvd = tvd;\r
+               this.setText(null);     // 簡易表示時代の名残\r
+               \r
+               this.setVerticalAlignment(JButton.TOP);\r
+               this.setHorizontalAlignment(JButton.LEFT);\r
+               //this.setBorder(new LineBorder(Color.BLACK,1));\r
+               this.setOpaque(true);\r
+       }\r
+       public ProgDetailList getInfo() {\r
+               return tvd;\r
+       }\r
+       \r
+       // 予約待機枠を表示するかどうかの確認\r
+       public boolean isStandby() { return tvd.marked && tvd.showinstandby; }\r
+       \r
+       // 表示スタイル\r
+       public static void setShowStart(boolean b) {\r
+               showStart = b;\r
+       }\r
+       public static void setSplitEpno(boolean b) {\r
+               splitEpno = b;\r
+       }\r
+       public static void setShowDetail(boolean b) {\r
+               showDetail = b;\r
+       }\r
+       public static void setDetailTab(float n) {\r
+               detailTab = n;\r
+       }\r
+       public static void setDetailRows(int n) {\r
+               detailRows = n;\r
+       }\r
+       \r
+       // フォントスタイル\r
+       public static void setTitleFont(String fn) {\r
+               if ( fn != null && ! fn.equals("") ) {\r
+                       Font f = new Font(fn,titleFontStyle,titleFontSize);\r
+                       if ( f != null ) {\r
+                               titleFont = f;\r
+                               return;\r
+                       }\r
+               }\r
+               //titleFont = this.getFont();\r
+       }\r
+       public static void setTitleFontSize(int n) {\r
+               titleFontSize = n;\r
+               titleFont = titleFont.deriveFont((float)titleFontSize);\r
+       }\r
+       public static void setTitleFontColor(Color c) {\r
+               titleFontColor = c;\r
+       }\r
+       public static void setDetailFont(String fn) {\r
+               if ( fn != null && ! fn.equals("") ) {\r
+                       Font f = new Font(fn,detailFontStyle,detailFontSize);\r
+                       if ( f != null ) {\r
+                               detailFont = f;\r
+                               return;\r
+                       }\r
+               }\r
+               //detailFont = new JLabel().getFont();\r
+       }\r
+       public static void setDetailFontSize(int n) {\r
+               detailFontSize = n;\r
+               detailFont = detailFont.deriveFont((float)detailFontSize);\r
+       }\r
+       public static void setDetailFontColor(Color c) {\r
+               detailFontColor = c;\r
+       }\r
+       \r
+       // フォントスタイルの変更\r
+       public static void setTitleFontStyle(ArrayList<FontStyle> fsa) {\r
+               setTmpFontStyle(fsa);\r
+               titleFontStyle = tmpFontStyle;\r
+               titleFontUL = tmpFontUL;\r
+               titleFont = titleFont.deriveFont((titleFont.getStyle() & ~(Font.BOLD|Font.ITALIC)) | titleFontStyle);\r
+       }\r
+       public static void setDetailFontStyle(ArrayList<FontStyle> fsa) {\r
+               setTmpFontStyle(fsa);\r
+               detailFontStyle = tmpFontStyle;\r
+               detailFontUL = tmpFontUL;\r
+               detailFont = detailFont.deriveFont((detailFont.getStyle() & ~(Font.BOLD|Font.ITALIC)) | detailFontStyle);\r
+       }\r
+       private static void setTmpFontStyle(ArrayList<FontStyle> fsa) {\r
+               tmpFontStyle = 0;\r
+               tmpFontUL = false;\r
+               for ( FontStyle fs : fsa ) {\r
+                       switch (fs) {\r
+                       case BOLD:\r
+                               tmpFontStyle |= Font.BOLD;\r
+                               break;\r
+                       case ITALIC:\r
+                               tmpFontStyle |= Font.ITALIC;\r
+                               break;\r
+                       case UNDERLINE:\r
+                               tmpFontUL = true;\r
+                               break;\r
+                       }\r
+               }\r
+       }\r
+       private static int tmpFontStyle;\r
+       private static boolean tmpFontUL;\r
+       \r
+       // フォントエイリアスの変更\r
+       public static void setAAHint(Object o) {\r
+               aahint = o;\r
+       }\r
+       \r
+       \r
+       /*******************************************************************************\r
+        * メソッド\r
+        ******************************************************************************/\r
+       \r
+       /**\r
+        * ビットマップの描画処理\r
+        */\r
+       @Override\r
+       protected void paintComponent(Graphics g) { \r
+               \r
+               super.paintComponent(g);\r
+               \r
+               // 初回描画時\r
+               if (image == null) {\r
+                       //\r
+                       Dimension  d  = getSize();\r
+                       int imgw = d.width;\r
+                       int imgh = d.height;\r
+\r
+                       float draww = (float)imgw-DRAWTAB*2.0F;\r
+                       float drawh = (float)imgh;\r
+                       float detailw = draww-detailTab;\r
+\r
+                       image = new BufferedImage(imgw, imgh, BufferedImage.TYPE_INT_ARGB);\r
+                       Graphics2D g2 = (Graphics2D)image.createGraphics();\r
+                       \r
+                       g2.setRenderingHint(RenderingHints.KEY_TEXT_ANTIALIASING,aahint);\r
+                       g2.setComposite(AlphaComposite.getInstance(AlphaComposite.SRC_OVER, 1.0f));\r
+                       \r
+                       float baseline = 0.0F;\r
+                       \r
+                       // 開始時刻と延長警告の描画\r
+                       if (showStart && tvd.start != null && tvd.start.length() > 0) {\r
+                               //\r
+                               Font fs = detailFont;\r
+                               String sStr = tvd.start+" "+tvd.extension_mark;\r
+                               //\r
+                               Font f = fs.deriveFont(fs.getStyle() | Font.BOLD);\r
+                               AttributedString as = new AttributedString(sStr);\r
+                               as.addAttribute(TextAttribute.FONT, f);\r
+                               as.addAttribute(TextAttribute.FOREGROUND, Color.BLACK, 0, 5);\r
+                               if (sStr.length() > 6) {\r
+                                       as.addAttribute(TextAttribute.FOREGROUND, Color.RED, 6, sStr.length());\r
+                               }\r
+                               AttributedCharacterIterator ac = as.getIterator();\r
+                               FontRenderContext fc = g2.getFontRenderContext();\r
+                               LineBreakMeasurer m = new LineBreakMeasurer(ac,fc);\r
+                               while ( m.getPosition() < sStr.length() ) {\r
+                                       TextLayout tl = m.nextLayout(draww);\r
+                                       baseline += tl.getAscent();\r
+                                       tl.draw(g2, DRAWTAB, baseline);\r
+                                       baseline += tl.getDescent() + tl.getLeading();\r
+                               }\r
+                       }\r
+                       \r
+                       // タイトルの描画\r
+                       String title = ( splitEpno ) ? tvd.splitted_title : tvd.title;\r
+                       if (title.length() > 0) {\r
+                               //\r
+                               String aMark;\r
+                               if (showStart && tvd.start.length() > 0) {\r
+                                       aMark = tvd.prefix_mark + tvd.newlast_mark;\r
+                               }\r
+                               else {\r
+                                       if (tvd.start.length() > 0) {\r
+                                               aMark = tvd.extension_mark + tvd.prefix_mark + tvd.newlast_mark;\r
+                                       }\r
+                                       else {\r
+                                               aMark = tvd.prefix_mark + tvd.newlast_mark;\r
+                                       }\r
+                               }\r
+                               String tStr = aMark+title+tvd.postfix_mark;\r
+                               //\r
+                               AttributedString as = new AttributedString(tStr);\r
+                               as.addAttribute(TextAttribute.FONT, titleFont);\r
+                               {\r
+                                       if (titleFontUL) {\r
+                                               as.addAttribute(TextAttribute.UNDERLINE, TextAttribute.UNDERLINE_LOW_ONE_PIXEL, aMark.length(), aMark.length()+title.length());\r
+                                       }\r
+                                       as.addAttribute(TextAttribute.FOREGROUND, titleFontColor, aMark.length(), tStr.length());\r
+                                       if (aMark.length() > 0) {\r
+                                               as.addAttribute(TextAttribute.FOREGROUND, Color.RED, 0, aMark.length());\r
+                                       }\r
+                               }\r
+                               AttributedCharacterIterator ac = as.getIterator();\r
+                               FontRenderContext fc = g2.getFontRenderContext();\r
+                               LineBreakMeasurer m = new LineBreakMeasurer(ac,fc);\r
+                               while (m.getPosition() < tStr.length()) {\r
+                                       TextLayout tl = m.nextLayout(draww);\r
+                                       baseline += tl.getAscent();\r
+                                       tl.draw(g2, DRAWTAB, baseline);\r
+                                       baseline += tl.getDescent() + tl.getLeading();\r
+                               }\r
+                       }\r
+                       \r
+                       // 番組詳細の描画\r
+                       if ( showDetail ) {\r
+                               String detail;\r
+                               if ( splitEpno ) {\r
+                                       detail = tvd.splitted_detail;\r
+                               }\r
+                               else {\r
+                                       detail = tvd.detail;\r
+                               }\r
+                               if ( detail.length() > 0 ) {\r
+                                       AttributedString as = new AttributedString(detail);\r
+                                       as.addAttribute(TextAttribute.FONT, detailFont);\r
+                                       if (detailFontUL) {\r
+                                               as.addAttribute(TextAttribute.UNDERLINE, TextAttribute.UNDERLINE_LOW_ONE_PIXEL);\r
+                                       }\r
+                                       as.addAttribute(TextAttribute.FOREGROUND, detailFontColor);\r
+                                       AttributedCharacterIterator ac = as.getIterator();\r
+                                       FontRenderContext fc = g2.getFontRenderContext();\r
+                                       LineBreakMeasurer m = new LineBreakMeasurer(ac,fc);\r
+                                       for ( int row=0; m.getPosition()<detail.length() && baseline<=drawh && (detailRows>0 && row<detailRows); row++ ) {\r
+                                               TextLayout tl = m.nextLayout(detailw);\r
+                                               baseline += tl.getAscent();\r
+                                               tl.draw(g2, (DRAWTAB+detailTab), baseline);\r
+                                               baseline += tl.getDescent() + tl.getLeading();\r
+                                       }\r
+                               }\r
+                       }\r
+               }\r
+               \r
+               // 反映\r
+               g.drawImage(image, 0, 0, this);\r
+       }\r
+}\r
diff --git a/TinyBannavi/src/tainavi/JTXTLabel.java b/TinyBannavi/src/tainavi/JTXTLabel.java
new file mode 100644 (file)
index 0000000..edb4948
--- /dev/null
@@ -0,0 +1,13 @@
+package tainavi;\r
+\r
+import javax.swing.JLabel;\r
+\r
+public class JTXTLabel extends JLabel {\r
+\r
+       private static final long serialVersionUID = 1L;\r
+\r
+       private String value = "";\r
+       \r
+       public void setValue(String s) { value = s; }\r
+       public String getValue() { return value; }\r
+}\r
diff --git a/TinyBannavi/src/tainavi/JTableRowHeader.java b/TinyBannavi/src/tainavi/JTableRowHeader.java
new file mode 100644 (file)
index 0000000..a18ceb5
--- /dev/null
@@ -0,0 +1,117 @@
+package tainavi;\r
+\r
+import java.awt.Color;\r
+import java.awt.Component;\r
+import java.awt.Font;\r
+import java.util.ArrayList;\r
+\r
+import javax.swing.JLabel;\r
+import javax.swing.JTable;\r
+import javax.swing.table.DefaultTableCellRenderer;\r
+import javax.swing.table.DefaultTableColumnModel;\r
+import javax.swing.table.DefaultTableModel;\r
+import javax.swing.table.TableColumn;\r
+\r
+public class JTableRowHeader extends JTable {\r
+\r
+       private static final long serialVersionUID = 1L;\r
+\r
+       // 新バージョン\r
+       public JTableRowHeader(final RowItemList rowData) {\r
+               super();\r
+               \r
+               String[] colname = {""};\r
+               \r
+               DefaultTableModel tableModel_rowheader = new DefaultTableModel(colname,0) {\r
+\r
+                       private static final long serialVersionUID = 1L;\r
+\r
+                       @Override\r
+                       public Object getValueAt(int row, int column) {\r
+                               return row+1;\r
+                       }\r
+                       @Override\r
+                       public int getRowCount() {\r
+                               return rowData.size();\r
+                       }\r
+               }; \r
+               \r
+               _JTableRowHeader(tableModel_rowheader);\r
+       }\r
+       \r
+       // 旧バージョン\r
+       public JTableRowHeader(final ArrayList<String[]> rowData) {\r
+               super();\r
+               \r
+               String[] colname = {""};\r
+               \r
+               DefaultTableModel tableModel_rowheader = new DefaultTableModel(colname,0) {\r
+\r
+                       private static final long serialVersionUID = 1L;\r
+\r
+                       @Override\r
+                       public Object getValueAt(int row, int column) {\r
+                               return row+1;\r
+                       }\r
+                       @Override\r
+                       public int getRowCount() {\r
+                               return rowData.size();\r
+                       }\r
+               }; \r
+               \r
+               _JTableRowHeader(tableModel_rowheader);\r
+       }\r
+               \r
+       private void _JTableRowHeader(DefaultTableModel tableModel_rowheader) {\r
+               \r
+               int colwidth = 35;\r
+               \r
+               this.setModel(tableModel_rowheader);\r
+               this.setAutoResizeMode(JTable.AUTO_RESIZE_OFF);\r
+               this.setRowSelectionAllowed(false);\r
+               this.setEnabled(false);\r
+               \r
+               DefaultTableCellRenderer r = new DefaultTableCellRenderer() {\r
+\r
+                       private static final long serialVersionUID = 1L;\r
+\r
+                       @Override\r
+                       public Component getTableCellRendererComponent(JTable table, Object value,\r
+                                       boolean isSelected, boolean hasFocus, int row, int column) {\r
+                               //\r
+                               JLabel label = new JLabel();\r
+                               label.setHorizontalAlignment(JLabel.CENTER);\r
+                               //\r
+                               int no = (Integer)value;\r
+                               label.setText(String.valueOf(no));\r
+                               //\r
+                               label.setOpaque(true);\r
+                               label.setBackground(table.getTableHeader().getBackground());\r
+                               //\r
+                               Font f = table.getFont();\r
+                               int sPlane = (f.getStyle() | Font.BOLD) ^ Font.BOLD;\r
+                               int sBold = (f.getStyle() | Font.BOLD);\r
+                               if (no % 10 == 0) {\r
+                                       label.setFont(new Font(f.getFontName(),sBold,f.getSize()));\r
+                                       label.setForeground(Color.RED);\r
+                               }\r
+                               else {\r
+                                       label.setFont(new Font(f.getFontName(),sPlane,f.getSize()));\r
+                                       label.setForeground(Color.GRAY);\r
+                               }\r
+                               return label;\r
+                       }\r
+               };\r
+               \r
+               DefaultTableColumnModel columnModel = (DefaultTableColumnModel)this.getColumnModel();\r
+               TableColumn column = columnModel.getColumn(0);\r
+               column.setPreferredWidth(colwidth);\r
+               column.setCellRenderer(r);\r
+               \r
+               // フォントサイズ変更にあわせて行の高さを変える\r
+               this.addPropertyChangeListener("font", new RowHeightChangeListener(4));\r
+\r
+               // 行の高さの初期値の設定\r
+               this.firePropertyChange("font", "old", "new");\r
+       }\r
+}\r
diff --git a/TinyBannavi/src/tainavi/JTaggedLayeredPane.java b/TinyBannavi/src/tainavi/JTaggedLayeredPane.java
new file mode 100644 (file)
index 0000000..58d37c4
--- /dev/null
@@ -0,0 +1,17 @@
+package tainavi;\r
+\r
+import javax.swing.JLayeredPane;\r
+\r
+/**\r
+ * 自身が表示している日付の保持を追加した{@link JLayeredPane}\r
+ */\r
+public class JTaggedLayeredPane extends JLayeredPane {\r
+\r
+       private static final long serialVersionUID = 1L;\r
+\r
+       //\r
+       private String tagstr = "";\r
+       //\r
+       public void setTagstr(String s) { tagstr = s; }\r
+       public String getTagstr() { return tagstr; }\r
+}\r
diff --git a/TinyBannavi/src/tainavi/JTextAreaWithPopup.java b/TinyBannavi/src/tainavi/JTextAreaWithPopup.java
new file mode 100644 (file)
index 0000000..ecedb7d
--- /dev/null
@@ -0,0 +1,18 @@
+package tainavi;\r
+\r
+import javax.swing.JTextArea;\r
+\r
+public class JTextAreaWithPopup extends JTextArea {\r
+\r
+       private static final long serialVersionUID = 1L;\r
+\r
+       public JTextAreaWithPopup() {\r
+               super();\r
+               this.addMouseListener(new TextEditPopupMenu());\r
+       }\r
+\r
+       public JTextAreaWithPopup(int rows, int columns) {\r
+               super(rows, columns);\r
+               this.addMouseListener(new TextEditPopupMenu());\r
+       }\r
+}\r
diff --git a/TinyBannavi/src/tainavi/JTextFieldPanel.java b/TinyBannavi/src/tainavi/JTextFieldPanel.java
new file mode 100644 (file)
index 0000000..753110f
--- /dev/null
@@ -0,0 +1,48 @@
+package tainavi;\r
+\r
+import java.awt.Dimension;\r
+\r
+import javax.swing.BoxLayout;\r
+import javax.swing.JLabel;\r
+import javax.swing.JPanel;\r
+import javax.swing.JTextField;\r
+\r
+public class JTextFieldPanel extends JPanel {\r
+\r
+       private static final long serialVersionUID = 1L;\r
+\r
+       private JTextField jtextfield = null;\r
+       private JLabel jlabel = null;\r
+       \r
+       public JTextFieldPanel(String s, int labelWidth, String text, int textFieldWidth) {\r
+               \r
+               this.setLayout(new BoxLayout(this,BoxLayout.LINE_AXIS));\r
+               \r
+               Dimension d = null;\r
+               \r
+               jlabel = new JLabel(s);\r
+               d = jlabel.getPreferredSize();\r
+               d.width = labelWidth;\r
+               jlabel.setMaximumSize(d);\r
+               this.add(jlabel);\r
+               \r
+               jtextfield = new JTextField();\r
+               d = jlabel.getPreferredSize();\r
+               d.width = textFieldWidth;\r
+               jtextfield.setMaximumSize(d);\r
+               jtextfield.setText(text);\r
+               jtextfield.setCaretPosition(0);\r
+               this.add(jtextfield);\r
+       }\r
+       \r
+       public String getText() {\r
+               return jtextfield.getText();\r
+       }\r
+       public void setText(String s) {\r
+               jtextfield.setText(s);\r
+       }\r
+       public void setEnabled(boolean b) {\r
+               this.jlabel.setEnabled(b);\r
+               this.jtextfield.setEnabled(b);\r
+       }\r
+}\r
diff --git a/TinyBannavi/src/tainavi/JTextFieldWithPopup.java b/TinyBannavi/src/tainavi/JTextFieldWithPopup.java
new file mode 100644 (file)
index 0000000..62ffb50
--- /dev/null
@@ -0,0 +1,19 @@
+package tainavi;\r
+\r
+import javax.swing.JTextField;\r
+\r
+\r
+public class JTextFieldWithPopup extends JTextField {\r
+\r
+       private static final long serialVersionUID = 1L;\r
+\r
+       public JTextFieldWithPopup() {\r
+               super();\r
+               this.addMouseListener(new TextEditPopupMenu());\r
+       }\r
+\r
+       public JTextFieldWithPopup(int col) {\r
+               super(col);\r
+               this.addMouseListener(new TextEditPopupMenu());\r
+       }\r
+}\r
diff --git a/TinyBannavi/src/tainavi/JTimebarLabel.java b/TinyBannavi/src/tainavi/JTimebarLabel.java
new file mode 100644 (file)
index 0000000..644f7ef
--- /dev/null
@@ -0,0 +1,64 @@
+package tainavi;\r
+\r
+import java.awt.Color;\r
+import java.awt.Dimension;\r
+import java.awt.Font;\r
+import java.awt.FontMetrics;\r
+import java.awt.Graphics;\r
+import java.awt.Graphics2D;\r
+import java.awt.RenderingHints;\r
+import java.awt.geom.Rectangle2D;\r
+import java.awt.image.BufferedImage;\r
+\r
+import javax.swing.JLabel;\r
+\r
+public class JTimebarLabel extends JLabel {\r
+\r
+       private static final long serialVersionUID = 1L;\r
+\r
+       private String ts = "";\r
+       private BufferedImage image = null;\r
+       \r
+       @Override\r
+       public void repaint() {\r
+               image = null;\r
+               super.repaint();\r
+       }\r
+       \r
+       @Override\r
+       protected void paintComponent(Graphics g) { \r
+               \r
+               super.paintComponent(g);\r
+               \r
+               // 初回描画時\r
+               if (image == null) {\r
+                       //\r
+                       Dimension  d  = this.getSize();\r
+                       int w = d.width;\r
+                       int h = d.height;\r
+                       image = new BufferedImage(w, h, BufferedImage.TYPE_INT_ARGB);\r
+                       Graphics2D g2 = (Graphics2D)image.createGraphics();\r
+                       \r
+                       // フォント関連のほげほげ\r
+                       Font f = this.getFont();\r
+                       g2.setFont(f.deriveFont(f.getStyle() | Font.BOLD));\r
+                       g2.setColor(Color.BLACK);\r
+                       g2.setRenderingHint(RenderingHints.KEY_TEXT_ANTIALIASING,RenderingHints.VALUE_TEXT_ANTIALIAS_ON);\r
+                       \r
+                       // 書き込む\r
+                       FontMetrics fm = g2.getFontMetrics();\r
+                       Rectangle2D r = fm.getStringBounds(ts,g);\r
+                       g2.drawString(ts, (w-(int)r.getWidth())/2, (h+(int)r.getHeight())/2);\r
+               }\r
+               \r
+               // 反映\r
+               g.drawImage(image, 0, 0, this);\r
+       }\r
+       \r
+       public String getTs() { return ts; }\r
+       \r
+       public JTimebarLabel(String s) {\r
+               super();\r
+               ts = s;\r
+       }\r
+}\r
diff --git a/TinyBannavi/src/tainavi/JTreeLabel.java b/TinyBannavi/src/tainavi/JTreeLabel.java
new file mode 100644 (file)
index 0000000..9b8afe7
--- /dev/null
@@ -0,0 +1,125 @@
+package tainavi;\r
+\r
+import javax.swing.JLabel;\r
+\r
+public class JTreeLabel extends JLabel {\r
+\r
+       private static final long serialVersionUID = 1L;\r
+       \r
+       /*******************************************************************************\r
+        * 定数\r
+        ******************************************************************************/\r
+       \r
+       private static final String NODESEPARATOR = ">";\r
+\r
+       public static final String PREVIEW = "プレビュー";\r
+\r
+       public static enum Nodes {\r
+               // 共通\r
+               ROOT                    ("root"),\r
+               NONE                    ("*NONE*"),\r
+               NOW                             ("現在放送中"),\r
+               \r
+               // リスト形式\r
+               SEARCHED                ("検索結果"),\r
+               FILTERED                ("絞込検索結果"),\r
+               \r
+               SEARCHHIST              ("過去ログ検索履歴"),\r
+               START                   ("新番組一覧"),\r
+               END                             ("最終回一覧"),\r
+               SYOBOCAL                ("しょぼかる"),\r
+               SYOBOALL                ("全しょぼかる"),\r
+               STANDBY                 ("予約待機"),\r
+               NEWARRIVAL              ("新着一覧"),\r
+               MODIFIED                ("詳細更新一覧"),\r
+               SYOBOONLY               ("しょぼかるのみに存在"),\r
+               TRACE                   ("番組追跡"),\r
+               KEYWORD                 ("キーワード検索"),\r
+               KEYWORDGROUP    ("キーワードグループ"),\r
+               PICKUP                  ("ピックアップ"),\r
+               GENRE                   ("ジャンル別"),\r
+               BCASTLIST               ("放送局別"),\r
+               EXTENTION               ("延長警告管理"),\r
+\r
+               // 新聞形式\r
+               DATE                    ("日付"),\r
+               TERRA                   ("地上波"),\r
+               BS                              ("BS"),\r
+               CS                              ("CS"),\r
+               RADIO                   ("ラジオ"),\r
+               BCAST                   ("放送局"),\r
+               PASSED                  ("過去ログ"),\r
+               ;\r
+               \r
+               private String label;\r
+               \r
+               private Nodes(String s) {\r
+                       label = s;\r
+               }\r
+               \r
+               public String getLabel() { return label; }\r
+               \r
+               public static Nodes getNode(String label) {\r
+                       for ( Nodes nd : Nodes.values() ) {\r
+                               if ( nd.getLabel().equals(label) ) {\r
+                                       return nd;\r
+                               }\r
+                       }\r
+                       return null;\r
+               }\r
+\r
+       };\r
+\r
+       /*******************************************************************************\r
+        * 部品\r
+        ******************************************************************************/\r
+       \r
+       private Nodes selected = Nodes.NONE;\r
+       private String value;\r
+\r
+       \r
+       /*******************************************************************************\r
+        * コンストラクタ\r
+        ******************************************************************************/\r
+\r
+\r
+       /*******************************************************************************\r
+        * アクション\r
+        ******************************************************************************/\r
+       \r
+       public Nodes getNode() { return selected; }\r
+       \r
+       public String getValue() { return value; }\r
+       \r
+       public void setView(Nodes sel, String val) {\r
+               selected = (sel == null) ? Nodes.NONE : sel;\r
+               value = val;\r
+               if ( value == null ) {\r
+                       super.setText(selected.getLabel());\r
+               }\r
+               else {\r
+                       super.setText(selected.getLabel()+NODESEPARATOR+value);\r
+               }\r
+       }\r
+       \r
+       public String getView() { return super.getText(); }\r
+       \r
+       @Deprecated\r
+       @Override\r
+       public String getText() {\r
+               return super.getText();\r
+       }\r
+       \r
+       @Deprecated\r
+       @Override\r
+       public void setText(String text) {\r
+               selected = Nodes.NONE;\r
+               value = text;\r
+               if ( value == null ) {\r
+                       super.setText(selected.getLabel());\r
+               }\r
+               else {\r
+                       super.setText(selected.getLabel()+NODESEPARATOR+value);\r
+               }\r
+       }\r
+}\r
diff --git a/TinyBannavi/src/tainavi/JWideComboBox.java b/TinyBannavi/src/tainavi/JWideComboBox.java
new file mode 100644 (file)
index 0000000..8d343ef
--- /dev/null
@@ -0,0 +1,77 @@
+package tainavi;\r
+\r
+import java.awt.Component;\r
+import java.awt.Dimension;\r
+import java.util.Vector;\r
+\r
+import javax.swing.ComboBoxModel;\r
+import javax.swing.DefaultListCellRenderer;\r
+import javax.swing.JComboBox;\r
+import javax.swing.JList;\r
+\r
+/**\r
+ * コンボボックスの幅よりポップアップの幅を広げる\r
+ */\r
+public class JWideComboBox extends JComboBox {\r
+       \r
+       private static final long serialVersionUID = 1L;\r
+       \r
+       public JWideComboBox() {\r
+               super();\r
+               setMyRenderer();\r
+       }\r
+\r
+       public JWideComboBox(ComboBoxModel aModel) {\r
+               super(aModel);\r
+               setMyRenderer();\r
+       }\r
+\r
+       public JWideComboBox(Object[] items) {\r
+               super(items);\r
+               setMyRenderer();\r
+       }\r
+\r
+       public JWideComboBox(Vector<?> items) {\r
+               super(items);\r
+               setMyRenderer();\r
+       }\r
+       \r
+       private void setMyRenderer() {\r
+               this.setRenderer(new DefaultListCellRenderer() {\r
+                       \r
+                       private static final long serialVersionUID = 1L;\r
+\r
+                       @Override\r
+                       public Component getListCellRendererComponent(JList list, Object value, int index, boolean isSelected, boolean cellHasFocus) {\r
+                               return super.getListCellRendererComponent(list, (value==null&&list.getModel().getSize()>0)?("(選択されていません)"):(value), index, isSelected, cellHasFocus);\r
+                       }\r
+               });\r
+       }\r
+\r
+       private boolean layingOut = false; \r
+    \r
+       private int w = 50;\r
+       \r
+       public void addPopupWidth(int w) {\r
+               this.w = w;\r
+       }\r
+       \r
+       @Override\r
+       public void doLayout() { \r
+               try { \r
+                       layingOut = true; \r
+                       super.doLayout(); \r
+               }\r
+               finally { \r
+                       layingOut = false; \r
+               }\r
+       } \r
\r
+       @Override\r
+       public Dimension getSize() { \r
+               Dimension dim = super.getSize(); \r
+               if ( ! layingOut ) \r
+                       dim.width += w; \r
+               return dim;\r
+       }\r
+}
\ No newline at end of file
diff --git a/TinyBannavi/src/tainavi/LikeReserveItem.java b/TinyBannavi/src/tainavi/LikeReserveItem.java
new file mode 100644 (file)
index 0000000..d1f1d62
--- /dev/null
@@ -0,0 +1,22 @@
+package tainavi;\r
+\r
+public class LikeReserveItem {\r
+\r
+       private HDDRecorder rec = null;\r
+       private ReserveList rsv = null;\r
+       \r
+       public LikeReserveItem(HDDRecorder rec, ReserveList rsv) {\r
+               this.rec = rec;\r
+               this.rsv = rsv;\r
+       }\r
+       \r
+       public HDDRecorder getRec() { return rec; }\r
+       \r
+       public ReserveList getRsv() { return rsv; }\r
+       \r
+       @Override\r
+       public String toString() {\r
+               return rsv.getTitle()+", "+rsv.getRec_pattern()+", "+rsv.getAhh()+":"+rsv.getAmm()+", "+rec.Myself()+", "+rsv.getTuner();\r
+       }\r
+       \r
+}\r
diff --git a/TinyBannavi/src/tainavi/LikeReserveList.java b/TinyBannavi/src/tainavi/LikeReserveList.java
new file mode 100644 (file)
index 0000000..0394dc2
--- /dev/null
@@ -0,0 +1,13 @@
+package tainavi;\r
+\r
+import java.util.ArrayList;\r
+\r
+public class LikeReserveList extends ArrayList<LikeReserveItem> {\r
+\r
+       private static final long serialVersionUID = 1L;\r
+\r
+       public ReserveList getRsv(int i) { return this.get(i).getRsv(); }\r
+       \r
+       public HDDRecorder getRec(int i) { return this.get(i).getRec(); }\r
+       \r
+}\r
diff --git a/TinyBannavi/src/tainavi/Locker.java b/TinyBannavi/src/tainavi/Locker.java
new file mode 100644 (file)
index 0000000..0ee9ed7
--- /dev/null
@@ -0,0 +1,12 @@
+package tainavi;\r
+\r
+/**\r
+ * ロック処理について、swingの利用を隠ぺいできないかなーと思って作ったinterface\r
+ * @sinc 3.15.4β\r
+ */\r
+abstract class Locker {\r
+\r
+       public abstract void unlock(); \r
+       public abstract boolean waitfor();\r
+       \r
+}\r
diff --git a/TinyBannavi/src/tainavi/LogViewer.java b/TinyBannavi/src/tainavi/LogViewer.java
new file mode 100644 (file)
index 0000000..1c5684d
--- /dev/null
@@ -0,0 +1,59 @@
+package tainavi;\r
+\r
+import java.io.BufferedReader;\r
+import java.io.File;\r
+import java.io.FileNotFoundException;\r
+import java.io.FileReader;\r
+import java.io.IOException;\r
+\r
+import javax.swing.JDialog;\r
+import javax.swing.JScrollPane;\r
+import javax.swing.JTextArea;\r
+\r
+\r
+public class LogViewer extends JDialog {\r
+\r
+       private static final long serialVersionUID = 1L;\r
+\r
+       JScrollPane jpane = null;\r
+       JTextArea jtext = null;\r
+       \r
+       public LogViewer(String logfile) {\r
+               \r
+               super();\r
+               \r
+               this.setTitle("LogViewer - "+logfile);\r
+               jtext = new JTextArea(25,60);\r
+               jtext.setEditable(false);\r
+               this.setContentPane(jpane = new JScrollPane(jtext));\r
+               this.pack();\r
+               \r
+               try {\r
+                       File f = new File(logfile);\r
+                       BufferedReader r = new BufferedReader(new FileReader(f));\r
+                       StringBuilder sb = new StringBuilder();\r
+                       String msg;\r
+                       while ((msg = r.readLine()) != null) {\r
+                               sb.append(msg);\r
+                               sb.append("\n");\r
+                       }\r
+                       r.close();\r
+                       jtext.setText(sb.toString());\r
+                       jtext.setCaretPosition(jtext.getText().length());\r
+               } catch (FileNotFoundException e) {\r
+                       // TODO Auto-generated catch block\r
+                       e.printStackTrace();\r
+               } catch (IOException e) {\r
+                       // TODO Auto-generated catch block\r
+                       e.printStackTrace();\r
+               }\r
+       }\r
+       \r
+       public void setCaretPosition(int pos) {\r
+               jtext.setCaretPosition(pos);\r
+       }\r
+       \r
+       public void setModal(boolean b) {\r
+               super.setModal(b);\r
+       }\r
+}\r
diff --git a/TinyBannavi/src/tainavi/MapCtrl.java b/TinyBannavi/src/tainavi/MapCtrl.java
new file mode 100644 (file)
index 0000000..e4b00e4
--- /dev/null
@@ -0,0 +1,296 @@
+package tainavi;\r
+\r
+import java.io.File;\r
+import java.util.HashMap;\r
+import java.util.Map.Entry;\r
+import java.util.Set;\r
+import java.util.regex.Matcher;\r
+import java.util.regex.Pattern;\r
+\r
+/**\r
+ * フリーオプション文字列を操作するクラス\r
+ * @since 3.15\r
+ */\r
+abstract public class MapCtrl {\r
+\r
+       /*\r
+        * 抽象メソッド\r
+        */\r
+       \r
+       /**\r
+        * 呼び出し元固有の内容チェック\r
+        */\r
+       abstract protected boolean chkOptString();\r
+       \r
+       /*\r
+        * 定数\r
+        */\r
+       \r
+       public static enum KeyType { DIR, FILE, URL, SYMLINK, DEC, HEX, BOOLEAN, OTHER };\r
+       \r
+       public static final String MSGID = "[MapCtrl] ";\r
+       public static final String ERRID = "[ERROR]"+MSGID;\r
+       public static final String DBGID = "[DEBUG]"+MSGID;\r
+       \r
+       \r
+       /*\r
+        * \r
+        */\r
+       private HashMap<String,String> map = new HashMap<String, String>();\r
+       private HashMap<String,KeyType> optkeys = new HashMap<String, KeyType>();\r
+       private String fname = null;\r
+\r
+       /*\r
+        * 公開メソッド\r
+        */\r
+       \r
+       /**\r
+        * 保存先を決定する\r
+        */\r
+       public void setFilename(String s) {\r
+               fname = s;\r
+       }\r
+\r
+       /**\r
+        * キーリストを登録する\r
+        */\r
+       public KeyType putdef(String key, KeyType keytype) {\r
+               return optkeys.put(key, keytype);\r
+       }\r
+\r
+       /**\r
+        *  ちょっとだけみせちゃう\r
+        */\r
+       public String get(String key) {\r
+               return map.get(key);\r
+       }\r
+\r
+       /**\r
+        *  ぜんぶみせちゃう\r
+        */\r
+       @Override\r
+       public String toString() {\r
+               String s = "";\r
+               for ( String key : map.keySet() ) {\r
+                       if ( map.get(key) != null ) {\r
+                               s += key+"="+map.get(key)+";";\r
+                       }\r
+               }\r
+               return s;\r
+       }\r
+       \r
+       /**\r
+        * フリーワードオプションの操作(起動時)\r
+        */\r
+       public boolean initOptString() {\r
+               \r
+               // ロード\r
+               if ( ! load() ) {\r
+                       // 呼び出し元固有のチェック\r
+                       chkOptString();\r
+                       return false;\r
+               }\r
+               \r
+               // 呼び出し元固有のチェック\r
+               chkOptString();\r
+               \r
+               // 保存はしない\r
+               return true;\r
+       }\r
+       \r
+       /**\r
+        * フリーワードオプションの操作(更新時)\r
+        */\r
+       public boolean setOptString(String s) {\r
+               \r
+               if ( s == null ) {\r
+                       map = new HashMap<String, String>();\r
+               }\r
+               else {\r
+                       // フリーテキストを変換\r
+                       if ( ! set(s) ) {\r
+                               //\r
+                       }\r
+               }\r
+\r
+               // 呼び出し元固有のチェック\r
+               chkOptString();\r
+               \r
+               // 保存する\r
+               if ( ! save() ) {\r
+                       return false;\r
+               }\r
+               return true;\r
+       }\r
+       \r
+       /*\r
+        * 共有メソッド\r
+        */\r
+       \r
+       /**\r
+        * put into it\r
+        */\r
+       protected void put(String key, String value) {\r
+               for ( String k : optkeys.keySet() ) {\r
+                       if ( k.equals(key) ) {\r
+                               map.put(key, value);\r
+                               return;\r
+                       }\r
+               }\r
+       }\r
+\r
+       // いるのか?\r
+       protected Set<String> keySet() {\r
+               return optkeys.keySet();\r
+       }\r
+       \r
+       // いるのか?\r
+       protected KeyType getType(String key) {\r
+               return optkeys.get(key);\r
+       }\r
+       \r
+       // いるのか?\r
+       protected void remove(String key) {\r
+               map.remove(key);\r
+       }\r
+       \r
+       // いるのか?\r
+       protected void clear() {\r
+               map.clear();\r
+       }\r
+       \r
+       /*\r
+        * 非公開メソッド\r
+        */\r
+       \r
+       /**\r
+        * 入力文字列を分解する\r
+        */\r
+       private boolean set(String input) {\r
+               map = new HashMap<String, String>();\r
+               Matcher ma = Pattern.compile("(.+?)=(.+?);",Pattern.DOTALL).matcher(input);\r
+               while ( ma.find() ) {\r
+                       map.put(ma.group(1).trim().toLowerCase(), ma.group(2).trim());\r
+               }\r
+               \r
+               refresh();\r
+               \r
+               return checktype();\r
+       }\r
+       \r
+       /**\r
+        * 定義されたキー以外が含まれていたら捨てる\r
+        */\r
+       private void refresh() {\r
+               HashMap<String,String> newmap = new HashMap<String, String>();\r
+               for ( Entry<String, String> entry : map.entrySet() ) {\r
+                       for ( String key : optkeys.keySet() ) {\r
+                               if ( map.containsKey(key) ) {\r
+                                       newmap.put(entry.getKey(), entry.getValue());\r
+                                       break;\r
+                               }\r
+                       }\r
+               }\r
+               map = newmap;\r
+       }\r
+       \r
+       /**\r
+        * 型をチェックする\r
+        */\r
+       private boolean checktype() {\r
+               HashMap<String,String> newmap = new HashMap<String, String>();\r
+               for ( Entry<String, KeyType> entry : optkeys.entrySet() ) {\r
+                       //\r
+                       String val = map.get(entry.getKey());\r
+                       if ( val == null ) {\r
+                               System.err.println(ERRID+"キーがみつかりません: "+entry.getKey()+", "+entry.getValue());\r
+                               continue;\r
+                       }\r
+                       //\r
+                       try {\r
+                               switch ( entry.getValue() ) {\r
+                               case DIR:\r
+                                       File fd = new File(val);\r
+                                       if ( ! fd.isDirectory() ) {\r
+                                               System.err.println(ERRID+"存在しないかディレクトリではありません: "+entry.getKey()+"="+val);\r
+                                               continue;\r
+                                       }\r
+                                       val = fd.getCanonicalPath();\r
+                                       if ( ! val.endsWith(File.separator)) val += File.separator;\r
+                                       val = val.replace("\\", "/");\r
+                                       break;\r
+                               case FILE:\r
+                                       File ff = new File(val);\r
+                                       if ( ! ff.isFile() ) {\r
+                                               System.err.println(ERRID+"存在しないかファイルではありません: "+entry.getKey()+"="+val);\r
+                                               continue;\r
+                                       }\r
+                                       val = ff.getCanonicalPath();\r
+                                       if ( ! val.endsWith(File.separator)) val += File.separator;\r
+                                       val = val.replace("\\", "/");\r
+                                       break;\r
+                               case DEC:\r
+                                       Integer.decode(val);\r
+                                       break;\r
+                               case HEX:\r
+                                       val = "0x"+val;\r
+                                       Integer.decode(val);\r
+                                       break;\r
+                               default:\r
+                                       // のーちぇっく\r
+                                       break;\r
+                               }\r
+                       }\r
+                       catch (NumberFormatException e) {\r
+                               System.err.println(ERRID+"数字の形式が不正出です: "+entry.getKey()+"="+val);\r
+                               continue;\r
+                       }\r
+                       catch (Exception e) {\r
+                               System.err.println(e.toString()+" "+entry.getKey()+", "+entry.getValue());\r
+                               continue;\r
+                       }\r
+                       \r
+                       System.out.println(DBGID+"有効なオプション: "+entry.getKey()+"="+val);\r
+                       newmap.put(entry.getKey(), val);\r
+               }\r
+               \r
+               map = newmap;\r
+               \r
+               return true;\r
+       }\r
+       \r
+       /**\r
+        *  保存\r
+        */\r
+       private boolean save() {\r
+               if ( fname == null ) {\r
+                       return true;\r
+               }\r
+               return CommonUtils.writeXML(fname, map);\r
+       }\r
+\r
+       /**\r
+        *  読み出し\r
+        */\r
+       private boolean load() {\r
+               if ( fname == null ) {\r
+                       map = new HashMap<String, String>();\r
+                       return true;\r
+               }\r
+               \r
+               if ( ! new File(fname).exists() ) {\r
+                       return false;\r
+               }\r
+               @SuppressWarnings("unchecked")\r
+               HashMap<String,String> newmap = (HashMap<String, String>) CommonUtils.readXML(fname);\r
+               if ( newmap == null ) {\r
+                       return false;\r
+               }\r
+               map = newmap;\r
+               \r
+               refresh();\r
+               \r
+               return true;\r
+       }\r
+\r
+}\r
diff --git a/TinyBannavi/src/tainavi/MarkChar.java b/TinyBannavi/src/tainavi/MarkChar.java
new file mode 100644 (file)
index 0000000..e248a18
--- /dev/null
@@ -0,0 +1,223 @@
+package tainavi;\r
+\r
+import tainavi.TVProgram.ProgFlags;\r
+import tainavi.TVProgram.ProgOption;\r
+import tainavi.TVProgram.ProgScrumble;\r
+\r
+/**\r
+ * 番組情報のマークを扱うクラス\r
+ */\r
+public class MarkChar {\r
+\r
+       /*******************************************************************************\r
+        * 定数\r
+        ******************************************************************************/\r
+       \r
+       public static enum MarkItem {\r
+               EXTENTION               ( "★延長注意★" ),\r
+               EXTENTION_S             ( "(延)" ),\r
+               NEWARRIVAL              ( "[NEW]" ),\r
+               NEWARRIVAL_S    ( "(N)" ),\r
+               MOVED                   ( "(移)" ),\r
+\r
+               NEW                             ( "【新】" ),\r
+               LAST                    ( "【終】" ),\r
+               NOSCRUMBLE              ( "【無料】" ),\r
+               MODIFIED                ( "(更)" ),\r
+               NONREPEATED             ( "(初)" ),\r
+               SPECIAL                 ( "【特】" ),\r
+               RATING                  ( "【R】" ),\r
+               FIRST                   ( "【初】" ),\r
+               NOSYOBO                 ( "[!]" ),\r
+               LIVE                    ( "[生]" ),\r
+               PV                              ( "[PV]" ),\r
+               SUBTITLE                ( "[字]" ),\r
+               BILINGUAL               ( "[二]" ),\r
+               MULTIVOICE              ( "[多]" ),\r
+               STANDIN                 ( "[吹]" ),\r
+               DATA                    ( "[デ]" ),\r
+               SORRUND                 ( "[5.1]" ),\r
+               PRECEDING               ( "【先】" ),\r
+               \r
+               REPEATED                ( "[再]" ),\r
+               ;\r
+               \r
+               private String name;\r
+               \r
+               private MarkItem(String name) {\r
+                       this.name = name;\r
+               }\r
+               \r
+               public String getName() {\r
+                       return name;\r
+               }\r
+       }\r
+       \r
+       /*******************************************************************************\r
+        * 部品\r
+        ******************************************************************************/\r
+       \r
+       private Env env;\r
+       \r
+       /*******************************************************************************\r
+        * コンストラクタ\r
+        ******************************************************************************/\r
+       \r
+       public MarkChar(Env env) {\r
+               this.env = env;\r
+       }\r
+       \r
+       /*******************************************************************************\r
+        * アクション\r
+        ******************************************************************************/\r
+       \r
+       /**\r
+        *  延長警告マークの取得\r
+        */\r
+       public String getExtensionMark(ProgDetailList tvd) {\r
+               return ((tvd.extension) ? ((env.getShortExtMark())?(MarkItem.EXTENTION_S.getName()):(MarkItem.EXTENTION.getName())) : (""));\r
+       }\r
+       \r
+       /**\r
+        * 新番組と最終回だけわける\r
+        */\r
+       public String getNewLastMark(ProgDetailList tvd) {\r
+               if (tvd.flag == ProgFlags.NEW && env.getOptMarks().get(ProgOption.HIDDEN_NEW) == Boolean.TRUE) {\r
+                       return MarkItem.NEW.getName();\r
+               }\r
+               else if (tvd.flag == ProgFlags.LAST && env.getOptMarks().get(ProgOption.HIDDEN_LAST) == Boolean.TRUE) {\r
+                       return MarkItem.LAST.getName();\r
+               }\r
+               return "";\r
+       }\r
+       \r
+       /**\r
+        *  普通のマークの取得\r
+        */\r
+       public String getOptionMark(ProgDetailList tvd) {\r
+               \r
+               String mkNewArr = "";\r
+               String mkModified = "";\r
+               String mkNoscr = "";\r
+               String mkSpecial = "";\r
+               String mkNoSyobo = "";\r
+               String mkRating = "";\r
+               \r
+               String mkFirst = "";\r
+               String mkLive = "";\r
+               String mkSubtitle = "";\r
+               String mkBilingual = "";\r
+               String mkTextbc = "";\r
+               String mkMultivoice = "";\r
+               String mkStandin = "";\r
+               String mkPv = "";\r
+               String mkData = "";\r
+               String mkSurround = "";\r
+               String mkNonrepeated = "";\r
+               String mkMoved = "";\r
+               String mkPrec = "";\r
+               \r
+               if (tvd.noscrumble == ProgScrumble.NOSCRUMBLE && env.getOptMarks().get(ProgOption.HIDDEN_NOSCRUMBLE) == Boolean.TRUE) {\r
+                       mkNoscr = MarkItem.NOSCRUMBLE.getName();\r
+               }\r
+               \r
+               if ( tvd.newarrival && env.getOptMarks().get(ProgOption.NEWARRIVAL) == Boolean.TRUE ) {\r
+                       mkNewArr = ((env.getShortExtMark())?(MarkItem.NEWARRIVAL_S.getName()):(MarkItem.NEWARRIVAL.getName()));\r
+               }\r
+               if ( tvd.modified && env.getOptMarks().get(ProgOption.MODIFIED) == Boolean.TRUE ) {\r
+                       mkModified = MarkItem.MODIFIED.getName();\r
+               }\r
+               if ( tvd.nonrepeated && env.getOptMarks().get(ProgOption.NONREPEATED) == Boolean.TRUE ) {\r
+                       mkNonrepeated = MarkItem.NONREPEATED.getName();\r
+               }\r
+               for ( ProgOption opt : tvd.getOption() ) {\r
+                       if ( env.getOptMarks().get(opt) != null && env.getOptMarks().get(opt) == Boolean.FALSE ) {\r
+                               continue;\r
+                       }\r
+                       switch (opt) {\r
+                       case SPECIAL:\r
+                               // 特別番組\r
+                               mkSpecial = MarkItem.SPECIAL.getName();\r
+                               break;\r
+                       case RATING:\r
+                               // 視聴年齢制限\r
+                               mkRating = MarkItem.RATING.getName();\r
+                               break;\r
+                       case FIRST:\r
+                               // 初回放送\r
+                               mkFirst = MarkItem.FIRST.getName();\r
+                               break;\r
+                       \r
+                       case NOSYOBO:\r
+                               mkNoSyobo = MarkItem.NOSYOBO.getName();\r
+                               break;\r
+                       \r
+                       case LIVE:\r
+                               // 生放送\r
+                               mkLive = MarkItem.LIVE.getName();\r
+                               break;\r
+                       case PV:\r
+                               // ペイパービュー\r
+                               mkPv = MarkItem.PV.getName();\r
+                               break;\r
+                       case SUBTITLE:\r
+                               // 字幕放送\r
+                               mkSubtitle = MarkItem.SUBTITLE.getName();\r
+                               break;\r
+                       case BILINGUAL:\r
+                               // 二か国語放送\r
+                               mkBilingual = MarkItem.BILINGUAL.getName();\r
+                               break;\r
+                       case MULTIVOICE:\r
+                               // 音声多重放送\r
+                               mkMultivoice = MarkItem.MULTIVOICE.getName();\r
+                               break;\r
+                       case STANDIN:\r
+                               // 吹き替え\r
+                               mkStandin = MarkItem.STANDIN.getName();\r
+                               break;\r
+                       case DATA:\r
+                               // データ放送\r
+                               mkData = MarkItem.DATA.getName();\r
+                               break;\r
+                       case SURROUND:\r
+                               // 5.1chサラウンド\r
+                               mkSurround = MarkItem.SORRUND.getName();\r
+                               break;\r
+                       case MOVED:\r
+                               // 先週無かったか時間が違う\r
+                               mkMoved = MarkItem.MOVED.getName();\r
+                               break;\r
+                       case PRECEDING:\r
+                               // 先行放送\r
+                               mkPrec = MarkItem.PRECEDING.getName();\r
+                               break;\r
+                       default:\r
+                               break;\r
+                       }\r
+               }\r
+               \r
+               return(mkNewArr+mkNoSyobo+mkMoved+mkModified+mkFirst+mkNonrepeated+mkNoscr+mkSpecial+mkRating+mkPrec+mkLive+mkPv+mkSubtitle+mkBilingual+mkTextbc+mkMultivoice+mkStandin+mkData+mkSurround);\r
+       }\r
+       \r
+       /**\r
+        * タイトルの後ろにつくマークの取得\r
+        */\r
+       public String getPostfixMark(ProgDetailList tvd) {\r
+               \r
+               String mkRep = "";\r
+               \r
+               for ( ProgOption opt : tvd.getOption() ) {\r
+                       switch (opt) {\r
+                       case REPEAT:\r
+                               mkRep = MarkItem.REPEATED.getName();\r
+                               break;\r
+                       default:\r
+                               break;\r
+                       }\r
+               }\r
+               \r
+               return(mkRep);\r
+       }\r
+\r
+}\r
diff --git a/TinyBannavi/src/tainavi/MarkedHistory.java b/TinyBannavi/src/tainavi/MarkedHistory.java
new file mode 100644 (file)
index 0000000..8eb0b77
--- /dev/null
@@ -0,0 +1,23 @@
+package tainavi;\r
+\r
+public class MarkedHistory {\r
+\r
+       private String center = "";\r
+       private String startDateTime = "";\r
+       private String detail = "";\r
+       \r
+       public String getCenter() { return center; }\r
+       public void setCenter(String s) { center = s; }\r
+       public String getStartDateTime() { return startDateTime; }\r
+       public void setStartDateTime(String s) { startDateTime = s; }\r
+       public String getDetail() { return detail; }\r
+       public void setDetail(String s) { detail = s; }\r
+       \r
+       public boolean isMatch(MarkedHistory mh) {\r
+               return (this.center.equals(mh.getCenter()) && this.startDateTime.equals(mh.getStartDateTime()));\r
+       }\r
+       \r
+       public boolean isModified(MarkedHistory mh) {\r
+               return ! this.detail.equals(mh.getDetail());\r
+       }\r
+}\r
diff --git a/TinyBannavi/src/tainavi/MarkedHistoryList.java b/TinyBannavi/src/tainavi/MarkedHistoryList.java
new file mode 100644 (file)
index 0000000..b30fa59
--- /dev/null
@@ -0,0 +1,117 @@
+package tainavi;\r
+\r
+import java.io.File;\r
+import java.util.ArrayList;\r
+\r
+/**\r
+ * 予約待機の履歴を保存して、前日と当日でどの番組が新しくリストアップされるようになったか判断する\r
+ */\r
+public class MarkedHistoryList {\r
+       \r
+       /*\r
+        * 定数\r
+        */\r
+\r
+       private static final String MSGID = "[予約待機履歴] ";\r
+       private static final String ERRID = "[ERROR]"+MSGID;\r
+       private static final String DBGID = "[DEBUG]"+MSGID;\r
+       \r
+       // メンバー\r
+       private String saveDateTime = "";\r
+       private ArrayList<MarkedHistory> hists = new ArrayList<MarkedHistory>();\r
+       \r
+       public int size() { return hists.size(); }\r
+       \r
+       public String getSaveDateTime() { return saveDateTime; }\r
+       public void setSaveDateTime(String s) { saveDateTime = s; }\r
+       // get/setHists()と書くはずがなぜかget/setCenters()になってしまった…\r
+       public ArrayList<MarkedHistory> getCenters() { return hists; }\r
+       public void setCenters(ArrayList<MarkedHistory> a) { hists = a; }\r
+       \r
+       // 比較\r
+       public boolean isMatch(MarkedHistory mh) {\r
+               for ( MarkedHistory mho : hists ) {\r
+                       if ( mho.isMatch(mh) ) {\r
+                               return true;\r
+                       }\r
+               }\r
+               return false;\r
+       }\r
+       \r
+       public boolean isModified(MarkedHistory mh) {\r
+               for ( MarkedHistory mho : hists ) {\r
+                       if ( mho.isMatch(mh) ) {\r
+                               return mho.isModified(mh);\r
+                       }\r
+               }\r
+               return false;\r
+       }\r
+       \r
+       // 追加\r
+       public void add(MarkedHistory mh) {\r
+               hists.add(mh);\r
+       }\r
+       \r
+       // 保存先\r
+       private static final String hFile = "env"+File.separator+"makredhistory.xml";\r
+       private static final String hFileTmp = "env"+File.separator+"makredhistory.xml.tmp";\r
+       \r
+       // セーブ・ロード\r
+       public void save(boolean historyOnlyUpdateOnce) {\r
+               System.out.println(MSGID+"履歴を保存します: "+hFile);\r
+               setSaveDateTime(CommonUtils.getDateTime(0));\r
+               String fname = null;\r
+               if (historyOnlyUpdateOnce) {\r
+                       // 一時ファイルに保存する\r
+                       fname = hFileTmp;\r
+               }\r
+               else {\r
+                       // 直接保存する\r
+                       fname = hFile;\r
+               }\r
+               if ( ! CommonUtils.writeXML(fname, this) ) {\r
+                       System.out.println(ERRID+"履歴の保存に失敗しました: "+fname);\r
+               }\r
+       }\r
+       public static MarkedHistoryList load(boolean historyOnlyUpdateOnce) {\r
+               System.out.println(MSGID+"履歴を読み込みます: "+hFile);\r
+\r
+               MarkedHistoryList b = null;\r
+               if ( historyOnlyUpdateOnce ) {\r
+                       // 日に一回しか確認しないよ\r
+                       File ft = new File(hFileTmp);\r
+                       if ( ft.exists() ) {\r
+                               b = (MarkedHistoryList) CommonUtils.readXML(hFileTmp);\r
+                               if ( b == null ) {\r
+                                       System.err.println(ERRID+"履歴を読み込めませんでした: "+hFileTmp);\r
+                               }\r
+                               else {\r
+                                       if ( b.saveDateTime.compareTo(CommonUtils.getCritDateTime()) < 0 ) {\r
+                                               // テンポラリ履歴ファイルがすでに過去日のものなら利用する\r
+                                               System.out.println(MSGID+"日付が変わっているので前回作成した履歴ファイルに利用を切り替えます: "+b.saveDateTime);\r
+                                               File f = new File(hFile);\r
+                                               if ( f.exists() ) {\r
+                                                       f.delete();\r
+                                               }\r
+                                               ft.renameTo(f);\r
+                                               \r
+                                               return b;\r
+                                       }\r
+                               }\r
+                       }\r
+               }\r
+               \r
+               // 通常の履歴ファイルを利用する\r
+               if ( new File(hFile).exists() ) {\r
+                       b = (MarkedHistoryList) CommonUtils.readXML(hFile);\r
+                       if ( b == null ) {\r
+                               System.err.println(ERRID+"履歴を読み込めませんでした: "+hFile);\r
+                       }\r
+               }\r
+               if ( b == null ) {\r
+                       b = new MarkedHistoryList();\r
+               }\r
+               \r
+               return b;\r
+       }\r
+}\r
diff --git a/TinyBannavi/src/tainavi/MarkedProgramList.java b/TinyBannavi/src/tainavi/MarkedProgramList.java
new file mode 100644 (file)
index 0000000..b3c655b
--- /dev/null
@@ -0,0 +1,470 @@
+package tainavi;\r
+\r
+import java.util.ArrayList;\r
+import java.util.Calendar;\r
+import java.util.GregorianCalendar;\r
+import java.util.HashMap;\r
+import java.util.regex.Matcher;\r
+import java.util.regex.Pattern;\r
+\r
+import tainavi.TVProgram.ProgOption;\r
+import tainavi.TVProgram.ProgType;\r
+\r
+/**\r
+ * 検索は都度行うのではなく、番組表を読み込んだり検索条件を変更したりといったイベントの際に全件作成してしまって、表示はそれを絞り込むだけにして高速化をはかる\r
+ */\r
+public class MarkedProgramList {\r
+       \r
+       /*\r
+        * 定数\r
+        */\r
+\r
+       private static final String MSGID = "[検索結果生成] ";\r
+       //private static final String ERRID = "[ERROR]"+MSGID;\r
+       //private static final String DBGID = "[DEBUG]"+MSGID;\r
+       \r
+       /*\r
+        * \r
+        */\r
+       \r
+       private ArrayList<ProgDetailList> programs = null;\r
+       private ArrayList<ArrayList<TraceKey>> traceKeys = null;\r
+       private ArrayList<ArrayList<Integer>> traceScores = null;\r
+       private ArrayList<ArrayList<SearchKey>> searchKeys = null;\r
+       private ArrayList<ArrayList<String>> searchStrs = null;\r
+\r
+       public ProgDetailList getProg(int n) { return this.programs.get(n); }\r
+       public ArrayList<TraceKey> getTKey(int n) { return this.traceKeys.get(n); }\r
+       public ArrayList<Integer> getTScore(int n) { return this.traceScores.get(n); }\r
+       public ArrayList<SearchKey> getSKey(int n) { return this.searchKeys.get(n); }\r
+       public ArrayList<String> getSStr(int n) { return this.searchStrs.get(n); }\r
+\r
+       public int size() { return (programs==null)?(0):(programs.size()); }\r
+\r
+       private boolean disableFazzySearch = false;\r
+       private boolean disableFazzySearchReverse = false;\r
+       \r
+       // \r
+       private boolean historyOnlyUpdateOnce = false;\r
+       public void setHistoryOnlyUpdateOnce(boolean b) { historyOnlyUpdateOnce = b; }\r
+       \r
+       //\r
+       private boolean showOnlyNonrepeated = true;\r
+       public void setShowOnlyNonrepeated(boolean b) { showOnlyNonrepeated = b; }\r
+       \r
+       \r
+       //\r
+       private HashMap<String,String> mpTrKeyLabels = new HashMap<String, String>();   // 番組追跡ヒット情報\r
+       private HashMap<String,String> mpSrKeyLabels = new HashMap<String, String>();   // キーワード検索ヒット情報\r
+       public boolean isTrKeyUsed(String label) { return (mpTrKeyLabels.get(label) != null); } \r
+       public boolean isSrKeyUsed(String label) { return (mpSrKeyLabels.get(label) != null); } \r
+       \r
+       \r
+       public void build(ArrayList<TVProgram> progs, ArrayList<TraceKey> trKeys, ArrayList<SearchKey> srKeys) {\r
+               // フラグを落とす\r
+               clearMarkedFlag(progs);\r
+               // 番組追跡\r
+       for (TraceKey trace : trKeys) {\r
+               buildByKeyword(progs, trace, null);\r
+       }\r
+       // キーワード検索\r
+               for (SearchKey search : srKeys) {\r
+               buildByKeyword(progs, null, search);\r
+       }\r
+               // 新着チェック\r
+               chkNewArrival();\r
+               \r
+               // 検索条件のラベルを収集する\r
+               mpTrKeyLabels.clear();\r
+               mpSrKeyLabels.clear();\r
+               for ( int n=0; n<this.size(); n++ ) {\r
+                       // Web番組表・しょぼかる分岐点\r
+                       if (this.getProg(n).type != ProgType.PROG) {\r
+                               continue;\r
+                       }\r
+                       \r
+                       // 番組追跡\r
+                       if (this.getTKey(n) != null) {\r
+                               for ( TraceKey tk : this.getTKey(n) ) {\r
+                                       mpTrKeyLabels.put(tk.getLabel(), "BINGO!");\r
+                               }\r
+                       }\r
+                       // キーワード検索\r
+                       if (this.getSKey(n) != null) {\r
+                               for ( SearchKey sk : this.getSKey(n) ) {\r
+                                       mpSrKeyLabels.put(sk.getLabel(), "BINGO!");\r
+                               }\r
+                       }\r
+               }\r
+       }\r
+       \r
+       /**\r
+        * 検索条件がかわったなら全番組のフラグを初期化しないといけない\r
+        */\r
+       private void clearMarkedFlag(ArrayList<TVProgram> tvprograms) {\r
+               for ( TVProgram tvp : tvprograms ) {\r
+                       if (tvp.getType() != TVProgram.ProgType.PROG && tvp.getType() != TVProgram.ProgType.SYOBO) {\r
+                               continue;\r
+                       }\r
+                       \r
+                       for ( ProgList tvpl : tvp.getCenters() ) {\r
+                               if ( ! tvpl.enabled) {\r
+                               continue;\r
+                       }\r
+                               \r
+                               for ( ProgDateList tvc : tvpl.pdate ) {\r
+                                       for ( ProgDetailList tvd : tvc.pdetail ) {\r
+                                               tvd.marked = false;\r
+                                               tvd.nonrepeated = false;\r
+                                               tvd.showinstandby = false;\r
+                                       }\r
+                               }\r
+                       }\r
+               }\r
+       }\r
+       \r
+       private void buildByKeyword(ArrayList<TVProgram> tvprograms, TraceKey trace, SearchKey keyword) {\r
+               //\r
+               for (int siteid=0; siteid<tvprograms.size(); siteid++) {\r
+                       // ほげほげ\r
+                       TVProgram tvp = tvprograms.get(siteid);\r
+                       \r
+                       if (tvp.getType() != TVProgram.ProgType.PROG && tvp.getType() != TVProgram.ProgType.SYOBO) {\r
+                               continue;\r
+                       }\r
+                       \r
+                       for (int centerid=0; centerid<tvp.getCenters().size(); centerid++) {\r
+                               // ほげほげ\r
+                               ProgList tvpl = tvp.getCenters().get(centerid);\r
+                       \r
+                               if ( ! tvpl.enabled) {\r
+                               continue;\r
+                       }\r
+                               \r
+                               if (trace != null && ! trace.getCenter().equals(tvpl.Center) ) {\r
+                                       continue;\r
+                               }\r
+                               \r
+                               if ( trace != null && trace.getShowLatestOnly() ) {\r
+                                       System.out.println(MSGID+"[リピート放送判定] リピート放送を排除する検索キー: *"+tvp.getType()+"* "+trace.getLabel());\r
+                               }\r
+                               \r
+                               // 一時保存用\r
+                               MatchedBuffer mBuf = new MatchedBuffer();\r
+                       \r
+                       // キーワード検索用\r
+                       String centerPop = TraceProgram.replacePop(tvpl.Center);\r
+                       \r
+                               for (int dateid=0; dateid<tvpl.pdate.size(); dateid++) {\r
+                                       String matchedString = null;\r
+                                       ProgDateList tvc = tvpl.pdate.get(dateid);\r
+                                       for (int progid=0; progid<tvc.pdetail.size(); progid++) {\r
+                                               ProgDetailList tvd = tvc.pdetail.get(progid);\r
+                                               \r
+                                               // 番組情報がありませんは表示しない\r
+                                               if (tvd.start.equals("")) {\r
+                                                       continue;\r
+                                               }\r
+                                               \r
+                                       //マッチング\r
+                                               int fazScore = 0;\r
+                                               boolean isFind = false;\r
+                                               if (trace != null) {\r
+                                                       if (trace.getDisableRepeat() == true && tvd.isOptionEnabled(ProgOption.REPEAT)) {\r
+                                                               // 再放送を除く\r
+                                                               continue;\r
+                                                       }\r
+                                                       \r
+                                                       if (this.disableFazzySearch == true) {\r
+                                                               // 完全一致\r
+                                                               if (trace.getTitlePop().equals(tvd.titlePop)) {\r
+                                                                       isFind = true;\r
+                                                               }\r
+                                                       }\r
+                                                       else {\r
+                                                               //あいまい検索・正引き\r
+                                                               fazScore = TraceProgram.sumScore(tvd.SearchStrKeys,trace.getTitlePop());\r
+                                                               if (fazScore >= trace.getFazzyThreshold()) {\r
+                                                                       isFind = true;\r
+                                                               }\r
+                                                               else if ( ! this.disableFazzySearchReverse) {\r
+                                                                       // 逆引き\r
+                                                                       fazScore = TraceProgram.sumScore(trace.getSearchStrKeys(),tvd.titlePop);\r
+                                                                       if (fazScore >= trace.getFazzyThreshold()) {\r
+                                                                               isFind = true;\r
+                                                                       }\r
+                                                               }\r
+                                                       }\r
+                                               }\r
+                                               else if (keyword != null) {\r
+                                               isFind = SearchProgram.isMatchKeyword(keyword, ((keyword.getCaseSensitive()==false)?(centerPop):(tvpl.Center)), tvd);\r
+                                               if ( isFind ) {\r
+                                                       matchedString = SearchProgram.getMatchedString();\r
+                                               }\r
+                                       }\r
+                                               \r
+                                               if (isFind) {\r
+                                                       tvd.marked = true;\r
+                                                       mBuf.add(tvd, trace, fazScore, keyword, matchedString);\r
+                                               }\r
+                                       }\r
+                               }\r
+                               for ( MatchedBufferData d : mBuf.getData() ) {\r
+                                       if ( d.prog.marked ) {\r
+                                               if ( trace != null && trace.getShowLatestOnly() ) {\r
+                                                       System.out.println(MSGID+"[リピート放送判定] [結果] リピート放送ではないと判断されました: "+d.prog.startDateTime+" 「"+d.prog.title+"("+d.bareTitle+")」 ("+d.storyNo+")");\r
+                                                       d.prog.nonrepeated = true;\r
+                                               }\r
+                                               if ( ! (keyword != null && ! keyword.getShowInStandby()) ) {\r
+                                                       d.prog.showinstandby = true;\r
+                                               }\r
+                                               this.add(d.prog, d.tKey, d.tScore, d.sKey, d.sStr);\r
+                                       }\r
+                                       else {\r
+                                               if ( trace != null && trace.getShowLatestOnly() ) {\r
+                                                       if ( ! showOnlyNonrepeated ) {\r
+                                                               // 復活戦\r
+                                                               d.prog.marked = true;\r
+                                                               if ( keyword != null && keyword.getShowInStandby() ) {\r
+                                                                       d.prog.showinstandby = true;\r
+                                                               }\r
+                                                               this.add(d.prog, d.tKey, d.tScore, d.sKey, d.sStr);\r
+                                                       }\r
+                                               }\r
+                                       }\r
+                               }\r
+                       }\r
+               }\r
+       }\r
+       \r
+       // 一時保存のためのサブクラス\r
+       private class MatchedBufferData {\r
+               public ProgDetailList prog = null;\r
+               public TraceKey tKey = null;\r
+               public int tScore = 0;\r
+               public SearchKey sKey = null;\r
+               public String sStr = null;\r
+               //\r
+               public String bareTitle = null;\r
+               public Integer storyNo = null;\r
+               \r
+               public MatchedBufferData(ProgDetailList prog, TraceKey tKey, int tScore, SearchKey sKey, String sStr) {\r
+                       this.prog = prog;\r
+                       this.tKey = tKey;\r
+                       this.tScore = tScore;\r
+                       this.sKey = sKey;\r
+                       this.sStr = sStr;\r
+                       //\r
+                       this.bareTitle = null;\r
+                       this.storyNo = null;\r
+               }\r
+       }\r
+       private class MatchedBuffer {\r
+               //\r
+               public ArrayList<MatchedBufferData> data = new ArrayList<MatchedBufferData>();\r
+               public String xDateTime = null ;\r
+               \r
+               // コンストラクタ\r
+               public MatchedBuffer() {\r
+                       GregorianCalendar c = CommonUtils.getCalendar(0);\r
+                       if ( CommonUtils.isLateNight(c) ) {\r
+                               c.add(Calendar.DATE, 6);\r
+                       }\r
+                       else {\r
+                               c.add(Calendar.DATE, 7);\r
+                       }\r
+                       c.set(Calendar.HOUR_OF_DAY, 5);\r
+                       c.set(Calendar.MINUTE, 0);\r
+                       xDateTime = CommonUtils.getDateTime(c); \r
+               }\r
+               \r
+               //\r
+               public ArrayList<MatchedBufferData> getData() {\r
+                       return data;\r
+               }\r
+               \r
+               private final String[] exprs = {\r
+                               // AT-XやANIMAXの場合\r
+                               "[##]([0-90-9]+)",\r
+                               "第([0-90-9]+)[話回]",\r
+                               // NHKの場合\r
+                               "[((]([0-90-9]+?)[))]",\r
+               };\r
+               \r
+               //\r
+               public void add(ProgDetailList prog, TraceKey tKey, int tScore, SearchKey sKey, String sStr) {\r
+                       MatchedBufferData bd = new MatchedBufferData(prog, tKey, tScore, sKey, sStr);\r
+                       if ( tKey == null || ! tKey.getShowLatestOnly() ) {\r
+                               // キーワード検索だったりリピート有効な場合はそのまま\r
+                               data.add(bd);\r
+                               return;\r
+                       }\r
+                       // 話数をとりだす\r
+                       {\r
+                               // タイトルと番組詳細両方でしらべるもの\r
+                               for ( String expr : exprs ) {\r
+                                       Matcher ma = Pattern.compile(expr).matcher(prog.title);\r
+                                       if ( ma.find() ) {\r
+                                               bd.storyNo = Integer.valueOf(CommonUtils.toHANUM(ma.group(1)));\r
+                                               break;\r
+                                       }\r
+                                       ma = Pattern.compile("^[  \t]*"+expr).matcher(prog.detail);\r
+                                       if ( ma.find() ) {\r
+                                               bd.storyNo = Integer.valueOf(CommonUtils.toHANUM(ma.group(1)));\r
+                                               break;\r
+                                       }\r
+                               }\r
+                       }\r
+                       if ( bd.storyNo != null ) {\r
+                               \r
+                               // 半角数字にそろえりーな\r
+                               //bd.storyNo = CommonUtils.toHANUM(bd.storyNo);\r
+                               \r
+                               // 同一タイトルで重複するものを排除する\r
+                               \r
+                               // 話数を外した裸のタイトルだけを抽出する\r
+                               bd.bareTitle = TraceProgram.replacePop(prog.title.replaceFirst("\\s*([##][0-90-9]+|[((][0-90-9]+[))]|第[0-90-9]+[話回]).*$", ""));\r
+                               \r
+                               for ( MatchedBufferData d : data ) {\r
+                                       if ( d.prog.marked == false ) {\r
+                                               // 既に無効化されていれば無視\r
+                                               continue;\r
+                                       }\r
+                                       if ( prog.startDateTime.compareTo(xDateTime) < 0) {\r
+                                               // 7日以内のデータ\r
+                                               if ( d.bareTitle != null && d.bareTitle.equals(bd.bareTitle) ) {\r
+                                                       if ( d.storyNo != null && d.storyNo >= bd.storyNo ) {\r
+                                                               // 同じかより新しいものがすでにあったら自分を捨てる\r
+                                                               bd.prog.marked = false;\r
+                                                               System.out.println(MSGID+"[リピート放送判定] [結果] リピート放送と判定されました(すでに新しいものがある): "+bd.prog.startDateTime+" 「"+bd.prog.title+"("+bd.bareTitle+")」 ("+bd.storyNo+")");\r
+                                                       }\r
+                                                       else {\r
+                                                               // 自分より古いものは捨てる\r
+                                                               d.prog.marked = false;\r
+                                                               System.out.println(MSGID+"[リピート放送判定] [結果] リピート放送と判定されました(より新し番組がみつかった): "+d.prog.startDateTime+" 「"+d.prog.title+"("+d.bareTitle+")」 ("+d.storyNo+")");\r
+                                                       }\r
+                                               }\r
+                                       }\r
+                                       else {\r
+                                               // 8日目以降のデータは特殊扱い\r
+                                               if ( d.bareTitle != null && d.bareTitle.equals(bd.bareTitle) ) {\r
+                                                       if ( d.storyNo != null && d.storyNo >= bd.storyNo ) {\r
+                                                               // 同じかより新しいものがすでにあったら自分を捨てる\r
+                                                               bd.prog.marked = false;\r
+                                                               System.out.println(MSGID+"[リピート放送判定] [結果] リピート放送と判定されました(すでに新しいものがある[8日目]): "+bd.prog.startDateTime+" 「"+bd.prog.title+"("+bd.bareTitle+")」 ("+bd.storyNo+")");\r
+                                                       }\r
+                                                       else {\r
+                                                               // 自分より古いものがあってもなにもしない\r
+                                                       }\r
+                                               }\r
+                                       }\r
+                               }\r
+                       }\r
+                       else {\r
+                               // 話数のついてないもの\r
+                               bd.bareTitle = TraceProgram.replacePop(prog.title);\r
+                               bd.storyNo = -1;\r
+                       }\r
+                       data.add(bd);\r
+               }\r
+       }\r
+               \r
+       private void add(ProgDetailList prog, TraceKey tKey, int tScore, SearchKey sKey, String sStr) {\r
+               \r
+               // 既存に\r
+               \r
+               for (int n=0; n<this.programs.size(); n++) {\r
+                       if (this.programs.get(n).equals(prog)) {\r
+                               if (tKey != null) {\r
+                                       this.traceKeys.get(n).add(tKey);\r
+                                       this.traceScores.get(n).add(tScore);\r
+                               }\r
+                               else if (sKey != null) {\r
+                                       this.searchKeys.get(n).add(sKey);\r
+                                       this.searchStrs.get(n).add(sStr);\r
+                               }\r
+                               return;\r
+                       }\r
+               }\r
+                       \r
+               // 新規追加\r
+\r
+               // 開始時刻順にソート\r
+               int p=0;\r
+               for (; p<this.programs.size(); p++) {\r
+                       if (this.programs.get(p).startDateTime.compareTo(prog.startDateTime) > 0) {\r
+                               break;\r
+                       }\r
+               }\r
+               \r
+               this.programs.add(p,prog);\r
+               this.traceKeys.add(p, new ArrayList<TraceKey>());\r
+               this.traceScores.add(p, new ArrayList<Integer>());\r
+               this.searchKeys.add(p, new ArrayList<SearchKey>());\r
+               this.searchStrs.add(p, new ArrayList<String>());\r
+               if (tKey != null) {\r
+                       this.traceKeys.get(p).add(tKey);\r
+                       this.traceScores.get(p).add(tScore);\r
+               }\r
+               if (sKey != null) {\r
+                       this.searchKeys.get(p).add(sKey);\r
+                       this.searchStrs.get(p).add(sStr);\r
+               }\r
+       }\r
+       \r
+       // \r
+       public void clear(boolean b1, boolean b2) {\r
+               //\r
+               this.disableFazzySearch = b1;\r
+               this.disableFazzySearchReverse = b2;\r
+               \r
+               //\r
+               this.programs = new ArrayList<ProgDetailList>();\r
+               \r
+               this.traceKeys = new ArrayList<ArrayList<TraceKey>>();\r
+               this.traceKeys.add(new ArrayList<TraceKey>());\r
+               this.traceScores = new ArrayList<ArrayList<Integer>>();\r
+               this.traceScores.add(new ArrayList<Integer>());\r
+               \r
+               this.searchKeys = new ArrayList<ArrayList<SearchKey>>();\r
+               this.searchKeys.add(new ArrayList<SearchKey>());\r
+               this.searchStrs = new ArrayList<ArrayList<String>>();\r
+               this.searchStrs.add(new ArrayList<String>());\r
+       }\r
+\r
+       // 検索結果の履歴と突き合わせて新着をチェックする\r
+       public void chkNewArrival() {\r
+               \r
+               MarkedHistoryList oldhist = MarkedHistoryList.load(historyOnlyUpdateOnce);\r
+               MarkedHistoryList newhist = new MarkedHistoryList();\r
+               \r
+               int max = this.size();\r
+               for ( int i=0; i<max; i++ ) {\r
+                       \r
+                       // 通常の番組情報のみ(しょぼかるは対象外)\r
+                       if ( this.getProg(i).type != ProgType.PROG ) {\r
+                               continue;\r
+                       }\r
+                       \r
+                       MarkedHistory mh = new MarkedHistory();\r
+                       mh.setCenter(this.getProg(i).center);\r
+                       mh.setStartDateTime(this.getProg(i).startDateTime);\r
+                       mh.setDetail(this.getProg(i).detail);\r
+                       \r
+                       // 番組追跡とキーワード検索のダブリを排除\r
+                       if ( newhist.isMatch(mh) ) {\r
+                               continue;\r
+                       }\r
+                       \r
+                       // 新着チェック( ! isMatch()注意)\r
+                       this.getProg(i).newarrival = ! oldhist.isMatch(mh);\r
+                       this.getProg(i).modified = (this.getProg(i).newarrival)?(false):(oldhist.isModified(mh));\r
+                       \r
+                       // 履歴更新\r
+                       newhist.add(mh);\r
+               }\r
+               \r
+               // 履歴を保存\r
+               newhist.save(historyOnlyUpdateOnce);\r
+       }\r
+}\r
diff --git a/TinyBannavi/src/tainavi/PaperColorsMap.java b/TinyBannavi/src/tainavi/PaperColorsMap.java
new file mode 100644 (file)
index 0000000..d43e104
--- /dev/null
@@ -0,0 +1,113 @@
+package tainavi;\r
+\r
+import java.awt.Color;\r
+import java.io.File;\r
+import java.util.HashMap;\r
+\r
+import tainavi.TVProgram.ProgGenre;\r
+\r
+/**\r
+ * 新聞形式タブのジャンル別背景色を保持する\r
+ * @since 3.15.4β {@link paperColors}から移行\r
+ */\r
+public class PaperColorsMap extends HashMap<ProgGenre,Color> implements Cloneable {\r
+\r
+       private static final long serialVersionUID = 1L;\r
+       \r
+       public static void setDebug(boolean b) {debug = b; }\r
+       private static boolean debug = false;\r
+\r
+       // 定数\r
+       private static final String cFileOld = "env"+File.separator+"papercolors.xml";\r
+       private static final String cFile = "env"+File.separator+"papercolorsmap.xml";\r
+       \r
+       private static final Color noneColor = new Color(255,255,255); \r
+       \r
+       //\r
+       //@Override\r
+       public Color get(ProgGenre g) {\r
+               Color c = super.get(g);\r
+               if ( c != null ) {\r
+                       return(c);\r
+               }\r
+               return(noneColor);\r
+       }\r
+       \r
+       /**\r
+        * 別にオーバーライドしなくてもいいけど\r
+        */\r
+       @Override\r
+       public Color put(ProgGenre key, Color value) {\r
+               return super.put(key, value);\r
+       }\r
+\r
+       //\r
+       public boolean load() {\r
+               System.out.println("ジャンル別背景色設定を読み込みます: "+cFile);\r
+               \r
+               if ( ! new File(cFile).exists() ) {\r
+                       if ( new File(cFileOld).exists() ) {\r
+                               @SuppressWarnings("unchecked")\r
+                               HashMap<ProgGenre,Color> clx = (HashMap<ProgGenre,Color>) CommonUtils.readXML(cFileOld);\r
+                               if ( clx == null ) {\r
+                                       System.err.println("ジャンル別背景色設定が読み込めなかったのでデフォルト設定で起動します.");\r
+                                       if (this.size()==0) loadDefaults();\r
+                                       return true;\r
+                               }\r
+                               \r
+                               //\r
+                               this.clear();\r
+                               for ( ProgGenre key : clx.keySet() ) {\r
+                                       this.put(key, clx.get(key));\r
+                               }\r
+                               if ( this.save() ) {\r
+                                       System.err.println("ジャンル別背景色設定ファイルを置き換えます: "+cFileOld+"->"+cFile);\r
+                                       new File(cFileOld).delete();\r
+                               }\r
+                               System.out.println("ジャンル別背景色設定を読み込みました.");\r
+                               return true;\r
+                       }\r
+               }\r
+               else {\r
+                       PaperColorsMap cl = (PaperColorsMap) CommonUtils.readXML(cFile);\r
+                       if ( cl != null ) {\r
+                               System.out.println("ジャンル別背景色設定を読み込みました.");\r
+                               CommonUtils.FieldCopy(this, cl);\r
+                               return true;\r
+                       }\r
+               }\r
+               \r
+               System.err.println("ジャンル別背景色設定が読み込めなかったのでデフォルト設定で起動します.");\r
+               loadDefaults();\r
+               \r
+               return false;\r
+       }\r
+       private void loadDefaults() {\r
+               // デフォルト設定\r
+               this.put(ProgGenre.NEWS, new Color(0xff,0xff,0xaa));\r
+               this.put(ProgGenre.SPORTS, new Color(0x99,0x99,0x99));\r
+               this.put(ProgGenre.DORAMA, new Color(0xcc,0xcc,0xcc));\r
+               this.put(ProgGenre.MOVIE, new Color(0xcc,0xff,0xcc));\r
+               this.put(ProgGenre.ANIME, new Color(0xff,0x99,0x00));\r
+               this.put(ProgGenre.DOCUMENTARY, new Color(0xaa,0xff,0xff));\r
+       }\r
+       \r
+       //\r
+       public boolean save() {\r
+               System.out.println("ジャンル別背景色設定を保存します: "+cFile);\r
+               if ( CommonUtils.writeXML(cFile,this) ) {\r
+                       System.out.println("ジャンル別背景色設定を保存しました.");\r
+                       return true;\r
+               }\r
+               \r
+               System.out.println("ジャンル別背景色設定の保存に失敗しました.");\r
+               return false;\r
+       }\r
+       \r
+       //\r
+       @Override\r
+       public PaperColorsMap clone() {\r
+               PaperColorsMap map = (PaperColorsMap) super.clone();\r
+               return map;\r
+       }\r
+}\r
diff --git a/TinyBannavi/src/tainavi/PassedProgram.java b/TinyBannavi/src/tainavi/PassedProgram.java
new file mode 100644 (file)
index 0000000..86d2f68
--- /dev/null
@@ -0,0 +1,720 @@
+package tainavi;\r
+\r
+import java.io.File;\r
+import java.util.ArrayList;\r
+import java.util.Arrays;\r
+import java.util.Calendar;\r
+import java.util.GregorianCalendar;\r
+import java.util.regex.Matcher;\r
+import java.util.regex.Pattern;\r
+\r
+import tainavi.ProgDetailList.WrHeader;\r
+import tainavi.TVProgramIterator.IterationType;\r
+\r
+/**\r
+ * 過去ログを処理するクラス\r
+ */\r
+public class PassedProgram extends TVProgramUtils implements TVProgram,Cloneable {\r
+\r
+       //private static final String thisEncoding = "UTF-8";\r
+       \r
+       public void setDebug(boolean b) { debug = b; }\r
+       private static boolean debug = false;\r
+       \r
+       /* 必須コード  - ここから */\r
+       \r
+       /*******************************************************************************\r
+        * 種族の特性\r
+        ******************************************************************************/\r
+       \r
+       @Override\r
+       public String getTVProgramId() { return tvProgId; }\r
+       private static final String tvProgId = "PassedProgram";\r
+       \r
+       @Override\r
+       public boolean isAreaSelectSupported() { return false; }\r
+       \r
+       @Override\r
+       public ProgType getType() { return ProgType.PASSED; }\r
+       @Override\r
+       public ProgSubtype getSubtype() { return ProgSubtype.NONE; }\r
+       \r
+       public PassedProgram clone() {\r
+               return (PassedProgram) super.clone();\r
+       }\r
+       \r
+       // リフレッシュされないようにする\r
+       @Override\r
+       public void refresh() {}\r
+       \r
+       /*******************************************************************************\r
+        * 個体の特性\r
+        ******************************************************************************/\r
+       \r
+       @Override\r
+       public int getTimeBarStart() {return 5;}\r
+       \r
+       //private int getDogDays() { return 7; }\r
+       \r
+       public void setUseXML(boolean b) { useXML = b; }\r
+       private static boolean useXML = false;\r
+       \r
+       \r
+       /*******************************************************************************\r
+        * 定数\r
+        ******************************************************************************/\r
+       \r
+       public static final String SEARCH_RESULT_CENTER = "$SRCRESCNT$";\r
+       \r
+       private static final String MSGID = "[過去ログ] ";\r
+       private static final String ERRID = "[ERROR]"+MSGID;\r
+       private static final String DBGID = "[DEBUG]"+MSGID;\r
+       \r
+       /*******************************************************************************\r
+        * 部品\r
+        ******************************************************************************/\r
+       \r
+       // コンポーネント以外\r
+       \r
+       /**\r
+        * 保存先のディレクトリを設定する\r
+        * @param s\r
+        */\r
+       public void setPassedDir(String s) {\r
+               dname = s;\r
+       }\r
+       private static String dname = "passed";\r
+       \r
+       public int getProgCount() { return progcount; }\r
+       private int progcount = 0;\r
+       \r
+\r
+       /*******************************************************************************\r
+        * コンストラクタ\r
+        ******************************************************************************/\r
+       \r
+       public PassedProgram() {\r
+               super();\r
+               cleanup();\r
+       }\r
+\r
+       /**\r
+        * 情報を全部破棄\r
+        */\r
+       private void cleanup() {\r
+               \r
+               crlist = new ArrayList<Center>();               // 番組情報を全部破棄\r
+               sortedcrlist = new ArrayList<Center>(); // 番組情報を全部破棄\r
+               pcenter = new ArrayList<ProgList>();    // 番組情報を全部破棄\r
+               \r
+       }\r
+\r
+       \r
+       /*******************************************************************************\r
+        * 本体\r
+        ******************************************************************************/\r
+       \r
+       public void loadProgram(String areaCode, boolean force) {}      // 使用しない。ダミー。\r
+       \r
+       /**\r
+        * 指定の日付のすべての過去ログを取得する\r
+        * @param date\r
+        * @return\r
+        */\r
+       public boolean loadAllCenters(String date) {\r
+               return loadByCenter(date,null);\r
+       }\r
+       \r
+       final String expr_a = "^(\\d)_(\\d+)_(.+)\\.";\r
+       final String expr_z = "(xml|txt)$";\r
+       \r
+       /**\r
+        * 指定の日付の指定の放送局の過去ログを取得する\r
+        * @param date\r
+        * @param center\r
+        * @return\r
+        */\r
+       public boolean loadByCenter(String date, String center) {\r
+               \r
+               progcount = 0;\r
+               \r
+               final String ymname = dname+File.separator+date.substring(0,7).replace("/", "_");\r
+               final String ddname = ymname+File.separator+date.replace("/", "_");\r
+               \r
+               File f = new File(ymname);\r
+               if ( ! f.exists() && ! f.isDirectory()) {\r
+                       System.out.println(MSGID+"年ディレクトがみつかりません: "+f.getAbsolutePath());\r
+                       return false;\r
+               }\r
+               \r
+               File g = new File(ddname);\r
+               if ( ! g.exists() && ! g.isDirectory()) {\r
+                       System.out.println(MSGID+"月日ディレクトがみつかりません: "+g.getAbsolutePath());\r
+                       return false;\r
+               }\r
+\r
+               // 取得開始\r
+               \r
+               TatCount tc = new TatCount();\r
+               \r
+               // 番組情報を全部破棄\r
+               this.cleanup();\r
+               \r
+               String[] flist = g.list();\r
+               Arrays.sort(flist);\r
+               ArrayList<String> fxlist = new ArrayList<String>();\r
+               for (int i=0; i<flist.length; i++)\r
+               {\r
+                       if ( ! flist[i].matches(expr_a+expr_z) )\r
+                       {\r
+                               if (debug) System.out.println(DBGID+"Invalid file name: "+flist[i]);\r
+                               continue;                               // ファイル名の書式が合わないものは無視\r
+                       }\r
+                       \r
+                       String base = flist[i].replaceFirst(expr_z, "");\r
+                       if ( ! fxlist.contains(base) )\r
+                       {\r
+                               fxlist.add(base);\r
+                       }\r
+               }\r
+               \r
+               for ( String fx : fxlist ) {\r
+                       \r
+                       Matcher ma = Pattern.compile(expr_a,Pattern.DOTALL).matcher(fx);\r
+                       ma.find();\r
+                       \r
+                       String fcenter = CommonUtils.unEscape(ma.group(3));\r
+                       \r
+                       if ( center != null && ! center.equals(fcenter) )\r
+                       {\r
+                               if (debug) System.out.println(DBGID+"not selected: "+fx);\r
+                               continue;                               // 放送局指定がされている場合はその局のみ\r
+                       }\r
+                       \r
+                       int order = Integer.valueOf(ma.group(2));\r
+                       if ( order == 0 )\r
+                       {\r
+                               if (debug) System.out.println(DBGID+"ignored center: "+fx);\r
+                               continue;                               // 0 だったらそれは無効放送局の情報\r
+                       }\r
+\r
+                       final String txtname = ddname+File.separator+fx+"txt";\r
+                       final String xmlname = ddname+File.separator+fx+"xml";\r
+\r
+                       // 局リストの追加\r
+                       Center cr = new Center();\r
+                       cr.setCenter(fcenter);\r
+                       cr.setEnabled(true);\r
+                       cr.setOrder(order);\r
+                       crlist.add(cr);\r
+                       sortedcrlist.add(cr);\r
+                       \r
+                       // 番組表局リストの追加\r
+                       ProgList pl = new ProgList();\r
+                       pl.Center = fcenter;\r
+                       pl.enabled = true;\r
+                       pcenter.add(pl);\r
+                       \r
+                       // 日付リストの追加\r
+                       ProgDateList pcl = new ProgDateList();\r
+                       pcl.Date = date;\r
+                       pl.pdate.add(pcl);\r
+                       \r
+                       TatCount tx = new TatCount();\r
+                       if ( new File(txtname).exists() ) {\r
+                               progcount += readTXT(pl.Center,pcl,txtname);\r
+                               if (debug) System.err.println(String.format(DBGID+"テキスト形式での読み込みにかかった時間(%.4f秒): %s",tx.end(),txtname));\r
+                       }\r
+                       else if ( new File(xmlname).exists() ) {\r
+                               progcount += readXML(pl.Center,pcl,date,xmlname);\r
+                               if (debug) System.err.println(String.format(DBGID+"XML形式での読み込みにかかった時間(%.4f秒): %s",tx.end(),xmlname));\r
+                               tx.restart();\r
+                               writeTXT(pcl,txtname);  //かきこ\r
+                               if (debug) System.err.println(String.format(DBGID+"テキスト形式での書き込みにかかった時間(%.4f秒): %s",tx.end(),txtname));\r
+                       }\r
+                       else {\r
+                               System.err.println(String.format(ERRID+"ファイルがない: %s.(xml|txt)",ddname+File.separator+fx));\r
+                       }\r
+               }\r
+\r
+               if (debug)\r
+               {\r
+                       for ( ProgList pl : pcenter )\r
+                       {\r
+                               int cnt = 0;\r
+                               for ( ProgDateList pcl : pl.pdate )\r
+                               {\r
+                                       for ( ProgDetailList pdl : pcl.pdetail )\r
+                                       {\r
+                                               cnt++;\r
+                                       }\r
+                               }\r
+                               System.err.println(DBGID+"取得した過去ログ: "+pl.Center+" "+cnt);\r
+                               progcount += cnt;\r
+                       }\r
+               }\r
+               System.out.println(String.format(MSGID+"取得しました(%.2f秒): %s %s %d件",tc.end(),date,(center!=null)?(center):("全放送局"),progcount));\r
+\r
+               return true;\r
+       }\r
+       \r
+       private int readTXT(String center, ProgDateList pcl, String txtname) {\r
+               \r
+               String txt = CommonUtils.read4file(txtname, false);\r
+               if ( txt == null ) {\r
+                       return -1;\r
+               }\r
+               \r
+               int index = 0;\r
+               while ( index < txt.length() )\r
+               {\r
+                       // 番組ヘッダを探す\r
+                       int newtop = txt.indexOf(WrHeader.STARTMARK.toString(),index);\r
+                       if ( newtop == -1 ) {\r
+                               break;\r
+                       }\r
+                       newtop += WrHeader.STARTMARK.toString().length()+1;\r
+                       \r
+                       // 番組フッタを探す\r
+                       int newtail = txt.indexOf(WrHeader.ENDMARK.toString(),newtop);\r
+                       if ( newtail == -1 ) {\r
+                               break;\r
+                       }\r
+                       index = newtail+WrHeader.ENDMARK.toString().length()+1;\r
+                       \r
+                       // 解析する\r
+                       ProgDetailList pdl = new ProgDetailList(txt.substring(newtop,newtail));\r
+                       \r
+                       // サブタイトル分離\r
+                       doSplitSubtitle(pdl);\r
+                       \r
+                       pcl.pdetail.add(pdl);\r
+\r
+                       // 情報を追加\r
+                       pdl.center = center;\r
+                       pdl.type = this.getType();\r
+               }\r
+               \r
+               return pcl.pdetail.size();\r
+       }\r
+       \r
+       private int readXML(String center, ProgDateList pcl, String date, String xmlname) {\r
+               try {\r
+\r
+                       @SuppressWarnings("unchecked")\r
+                       ArrayList<PassedProgramList> da = (ArrayList<PassedProgramList>) CommonUtils.readXML(xmlname);\r
+                       if ( da == null )\r
+                       {\r
+                               System.err.println(ERRID+"読み込みに失敗しました: "+xmlname);\r
+                               return -1;\r
+                       }\r
+                       \r
+                       GregorianCalendar cal = CommonUtils.getCalendar(date);\r
+                       for ( int j=0; j<da.size(); j++ )\r
+                       {\r
+                               PassedProgramList data = da.get(j);\r
+\r
+                               // 5:00以前から続いている場合の長さ補正\r
+                               int prelength = 0;\r
+\r
+                               // クソバグ対応(GENRE.KIDSを廃止したら過去ログが読めなくなったという)\r
+                               if (data.genre == null)\r
+                               {\r
+                                       data.genre = ProgGenre.NOGENRE;\r
+                                       data.subgenre = ProgSubgenre.NOGENRE_ETC;\r
+                               }\r
+                               if ( data.genrelist != null )\r
+                               {\r
+                                       for ( int n=0; n<data.genrelist.size(); n++ )\r
+                                       {\r
+                                               if (data.genrelist.get(n) == null)\r
+                                               {\r
+                                                       data.genrelist.set(n, ProgGenre.NOGENRE);\r
+                                                       data.subgenrelist.set(n, ProgSubgenre.NOGENRE_ETC);\r
+                                               }\r
+                                       }\r
+                               }\r
+                               \r
+                               // クソバグ対応(番組詳細のない情報があった)\r
+                               if (data.detail == null)\r
+                               {\r
+                                       data.detail="";\r
+                               }\r
+                               \r
+                               // 後方互換(EDCB番組表で追加された追加番組詳細情報への対応)\r
+                               if (data.addedDetail == null)\r
+                               {\r
+                                       data.addedDetail = "";\r
+                               }\r
+                               \r
+                               //\r
+                               data.splitted_title = data.title;\r
+                               data.splitted_detail = data.detail;\r
+                               \r
+                               // 検索用インデックスを生成\r
+                               data.titlePop = TraceProgram.replacePop(data.title);\r
+                               data.detailPop = TraceProgram.replacePop(data.detail);\r
+                               data.SearchStrKeys = TraceProgram.splitKeys(data.titlePop);\r
+                               \r
+                               // 終了時刻・実日付を生成\r
+                               String[] Ahm = data.start.split(":",2);\r
+                               if ( Ahm.length == 2 )\r
+                               {\r
+                                       // 詳細情報をもつもの\r
+                                       GregorianCalendar cale = (GregorianCalendar) cal.clone();\r
+                                       int hh = Integer.valueOf(Ahm[0]);\r
+                                       int mm = Integer.valueOf(Ahm[1]);\r
+                                       \r
+                                       if ( j == 0 )\r
+                                       {\r
+                                               if ( CommonUtils.isLateNight(hh) )\r
+                                               {\r
+                                                       // 1個目で00-05時はうめうめ\r
+                                                       prelength = (5*60 - (hh*60+mm));\r
+                                               }\r
+                                               else if ( hh >= 18 && hh < 24 )\r
+                                               {\r
+                                                       // 1個目で18-24時は前日だ\r
+                                                       cale.add(Calendar.DATE, -1);\r
+                                                       prelength = (29*60 - (hh*60+mm));\r
+                                               }\r
+                                       }\r
+                                       else if ( CommonUtils.isLateNight(hh) )\r
+                                       {\r
+                                               // 2個目以降で00-05時は翌日だ\r
+                                               cale.add(Calendar.DATE, 1);\r
+                                       }\r
+                                       \r
+                                       data.accurateDate = CommonUtils.getDate(cale);\r
+                                       \r
+                                       cale.set(Calendar.HOUR_OF_DAY, hh);\r
+                                       cale.set(Calendar.MINUTE, mm);\r
+                                       data.startDateTime = CommonUtils.getDateTime(cale);\r
+                                       data.start = CommonUtils.getTime(cale);\r
+                                       \r
+                                       cale.add(Calendar.MINUTE, prelength+data.length);\r
+                                       data.end = CommonUtils.getTime(cale);\r
+                                       data.endDateTime = CommonUtils.getDateTime(cale);\r
+                                       \r
+                                       data.recmin = CommonUtils.getRecMinVal(data.start,data.end);\r
+                               }\r
+                               \r
+                               pcl.pdetail.add(data);\r
+\r
+                               // 情報を追加\r
+                               data.center = center;\r
+                               data.type = this.getType();\r
+                       }\r
+                       \r
+                       return pcl.pdetail.size();\r
+               }\r
+               catch ( Exception e )\r
+               {\r
+                       System.err.println(ERRID+"過去ログの読み込みに失敗しました: "+xmlname);\r
+                       e.printStackTrace();\r
+               } \r
+               return -1;\r
+       }\r
+       \r
+       /**\r
+        * プログラムリストをファイルに保存する\r
+        * @param tviterator\r
+        * @param clst\r
+        * @param prepCount 何日先のログまで保存するか\r
+        * @return\r
+        */\r
+       public boolean save(TVProgramIterator tviterator, ArrayList<Center> clst, int prepCount) {\r
+               \r
+               TVProgramIterator pli = tviterator.build(clst, IterationType.ALL);\r
+               \r
+               String curDate = CommonUtils.getDate529(0,true);\r
+               \r
+               // 古いログは削除\r
+               {\r
+                       ArrayList<String> dirs = new ArrayList<String>();\r
+                       // リストアップ\r
+                       for ( ProgList pl : pli )\r
+                       {\r
+                               for (ProgDateList cl : pl.pdate)\r
+                               {\r
+                                       if (cl.Date.compareTo(curDate) < 0)\r
+                                       {\r
+                                               continue;\r
+                                       }\r
+                                       String ymname = dname+File.separator+cl.Date.substring(0,7).replace("/", "_");\r
+                                       String ddname = ymname+File.separator+cl.Date.replace("/", "_");\r
+                                       boolean b  = true;\r
+                                       for (String dir : dirs)\r
+                                       {\r
+                                               if (dir.equals(ddname))\r
+                                               {\r
+                                                       b = false;\r
+                                                       break;\r
+                                               }\r
+                                       }\r
+                                       if (b)\r
+                                       {\r
+                                               dirs.add(ddname);\r
+                                       }\r
+                               }\r
+                       }\r
+                       \r
+                       // 削除\r
+                       for (String dir : dirs)\r
+                       {\r
+                               File d = new File(dir);\r
+                               if (d.exists())\r
+                               {\r
+                                       for (String file : d.list())\r
+                                       {\r
+                                               if (file.matches(expr_a+expr_z))\r
+                                               {\r
+                                                       File f = new File(dir+File.separator+file);\r
+                                                       f.delete();\r
+                                               }\r
+                                       }\r
+                               }\r
+                       }\r
+               }\r
+               \r
+               // 放送局名をキーにプログラムリストをなめる\r
+               TatCount tc = new TatCount();\r
+               \r
+               pli.rewind();   // まきもどせ\r
+               \r
+               int centerid = 0;\r
+               for ( ProgList pl : pli )\r
+               {\r
+                       ++centerid;\r
+                       \r
+                       tc.restart();\r
+                       String firstD = "";\r
+                       String lastD = "";\r
+                       \r
+                       int count = 1;\r
+                       for ( ProgDateList cl : pl.pdate )\r
+                       {\r
+                               if (cl.Date.compareTo(curDate) < 0)\r
+                               {\r
+                                       continue;       // 過去日のログは不要\r
+                               }\r
+                               \r
+                               if ( count++ > prepCount )\r
+                               {\r
+                                       break;          // 指定日数以上は保存しない\r
+                               }\r
+                               \r
+                               if ( firstD.length() == 0 ) firstD = cl.Date;\r
+                               lastD = cl.Date;\r
+                               \r
+                               // 出力先の決定\r
+                               String ymname = dname+File.separator+cl.Date.substring(0,7).replace("/", "_");\r
+                               String ddname = ymname+File.separator+cl.Date.replace("/", "_");\r
+                               String xmlname = ddname+File.separator+String.format("%d_%04d_%s.xml", 1, centerid, CommonUtils.escapeFilename(pl.Center));\r
+                               String txtname = ddname+File.separator+String.format("%d_%04d_%s.txt", 1, centerid, CommonUtils.escapeFilename(pl.Center));\r
+                               \r
+                               for ( String xname : new String[] { dname,ymname,ddname } )\r
+                               {\r
+                                       File f = new File(xname);\r
+                                       if ( ! f.exists() && ! f.mkdir() )\r
+                                       {\r
+                                       System.err.println(ERRID+"ディレクトリの作成に失敗しました: "+f.getAbsolutePath());\r
+                                       continue;\r
+                                       }\r
+                               }\r
+\r
+                               TatCount tx = new TatCount();\r
+                               if ( ! useXML ) {\r
+                                       writeTXT(cl,txtname);\r
+                                       if (debug) System.err.println(String.format(DBGID+"テキスト形式での保存にかかった時間(%.4f秒): %s->%s %s",tx.end(),firstD,lastD,pl.Center));\r
+                               }\r
+                               else {\r
+                                       writeXML(cl,xmlname);\r
+                                       if (debug) System.err.println(String.format(DBGID+"XML形式での保存にかかった時間(%.4f秒): %s->%s %s",tx.end(),firstD,lastD,pl.Center));\r
+                               }\r
+                       }\r
+                       \r
+                       reportProgress(String.format(MSGID+"保存しました(%.2f秒): %s->%s %s",tc.end(),firstD,lastD,pl.Center));\r
+               }\r
+               return true;\r
+               \r
+       }\r
+       \r
+       private boolean writeTXT(ProgDateList pcl, String txtname) {\r
+               StringBuilder sb = new StringBuilder();\r
+               for ( ProgDetailList d : pcl.pdetail ) {\r
+                       sb.append(d.toString());\r
+               }\r
+               if ( ! CommonUtils.write2file(txtname, sb.toString()) ) {\r
+                       return false;\r
+               }\r
+               return true;\r
+       }\r
+\r
+       private boolean writeXML(ProgDateList pcl, String xmlname) {\r
+               // 詳細をコピーする\r
+               ArrayList<PassedProgramList> da = new ArrayList<PassedProgramList>();\r
+               for ( ProgDetailList d : pcl.pdetail )\r
+               {\r
+                       PassedProgramList data = new PassedProgramList();\r
+                       data.setTitle(d.title);\r
+                       data.setDetail(d.detail);\r
+                       data.setAddedDetail(d.addedDetail);\r
+                       data.setStart(d.start);\r
+                       data.setLength(d.length);\r
+                       data.setExtension(d.extension);\r
+                       data.setNoscrumble(d.noscrumble);\r
+                       data.setGenre(d.genre);\r
+                       data.setSubgenre(d.subgenre);\r
+                       data.setFlag(d.flag);\r
+                       data.setOption(d.option);\r
+                       da.add(data);\r
+               }\r
+               \r
+               // 出力\r
+               if ( ! CommonUtils.writeXML(xmlname, da) )\r
+               {\r
+               return false;\r
+               }\r
+               \r
+               return true;\r
+       }\r
+\r
+       // @see #getDateList()\r
+       private static String pregetdate = "1999/12/31";\r
+       private static String[] dd = new String[0];\r
+\r
+       /**\r
+        *  ディレクトリ一覧を取得する(日に一回しか検索しない)\r
+        */\r
+       public String[] getDateList(int maxCnt) {\r
+               \r
+               String getdate = CommonUtils.getDate529(0,true);\r
+               if ( getdate.compareTo(pregetdate) <= 0 )\r
+               {\r
+                       return dd;\r
+               }\r
+               pregetdate = getdate;\r
+               \r
+               System.out.println(MSGID+"フォルダを検索して過去ログをリストアップします. "+getdate);\r
+                               \r
+               // 最初のエントリーはダミー\r
+               ArrayList<String> da = new ArrayList<String>();\r
+               da.add("過去ログ");\r
+               \r
+               // 過去日のディレクトリのみが対象\r
+               GregorianCalendar cal = CommonUtils.getCalendar(0);\r
+               if ( CommonUtils.isLateNight(cal) )\r
+               {\r
+                       cal.add(Calendar.DAY_OF_MONTH, -1);\r
+               }\r
+               String date = String.format("%04d_%02d_%02d", cal.get(Calendar.YEAR), cal.get(Calendar.MONTH)+1, cal.get(Calendar.DAY_OF_MONTH));\r
+               \r
+               // トップディレクトリは存在するかな?\r
+               File d = new File(dname);\r
+               if ( ! d.exists() && ! d.isDirectory())\r
+               {\r
+                       System.out.println(MSGID+"ディレクトリがみつかりません: "+d.getAbsolutePath());\r
+                       return da.toArray(new String[0]);\r
+               }\r
+               \r
+               // 日付降順に処理する\r
+               int cnt = 0;\r
+               String[] dlist = d.list();\r
+               Arrays.sort(dlist);\r
+               for (int j=dlist.length-1; j>=0 && cnt < maxCnt; j--)\r
+               {\r
+                       // YYYY_MM形式のもののみ\r
+                       Matcher ma = Pattern.compile("^\\d\\d\\d\\d_\\d\\d$").matcher(dlist[j]);\r
+                       if ( ! ma.find())\r
+                       {\r
+                               continue;\r
+                       }\r
+                       \r
+                       // ディレクトリだよね?\r
+                       final String ddname = d.getName()+File.separator+dlist[j];\r
+                       File f = new File(ddname);\r
+                       if ( ! f.exists() && ! f.isDirectory())\r
+                       {\r
+                               System.out.println(MSGID+"ディレクトリがみつかりません: "+f.getAbsolutePath());\r
+                               return null;\r
+                       }\r
+                       \r
+                       // 日付降順に処理する\r
+                       String[] flist = f.list();\r
+                       Arrays.sort(flist);\r
+                       for (int i=flist.length-1; i>=0 && cnt<maxCnt; i--)\r
+                       {\r
+                               // YYYY_MM_DD(曜日)形式で過去日のもののみ\r
+                               ma = Pattern.compile("^(\\d\\d\\d\\d_\\d\\d_\\d\\d)").matcher(flist[i]);\r
+                               if ( ! ma.find())\r
+                               {\r
+                                       continue;\r
+                               }\r
+                               if (ma.group(1).compareTo(date) >= 0)\r
+                               {\r
+                                       continue;\r
+                               }\r
+                               da.add(flist[i].replace("_", "/"));\r
+                               cnt++;\r
+                       }\r
+               }\r
+               dd = da.toArray(new String[0]);\r
+               return dd;\r
+       }\r
+       \r
+       /* ここまで */\r
+       \r
+       \r
+       \r
+       /*\r
+        * ★★★★★★★★★★★★★★★★★★★★★★★★★★★★★★★\r
+        * ★★★★★ 放送地域を取得する(TVAreaから降格)-ここから ★★★★★\r
+        * ★★★★★★★★★★★★★★★★★★★★★★★★★★★★★★★\r
+        */\r
+       \r
+       /*\r
+        * 公開メソッド\r
+        */\r
+       \r
+       //\r
+       @Override\r
+       public String getDefaultArea() {return "東京";}\r
+       \r
+       //\r
+       public void loadAreaCode() {}\r
+       \r
+       //\r
+       public void saveAreaCode() {}\r
+       \r
+       /*\r
+        * ★★★★★★★★★★★★★★★★★★★★★★★★★★★★★★★\r
+        * ★★★★★ 放送地域を取得する(TVAreaから降格)-ここまで ★★★★★\r
+        * ★★★★★★★★★★★★★★★★★★★★★★★★★★★★★★★\r
+        */\r
+       \r
+       \r
+       \r
+       /*\r
+        * ★★★★★★★★★★★★★★★★★★★★★★★★★★★★★★★\r
+        * ★★★★★ 放送局を選択する(TVCenterから降格)-ここから ★★★★★\r
+        * ★★★★★★★★★★★★★★★★★★★★★★★★★★★★★★★\r
+        */\r
+       \r
+       /*\r
+        * 公開メソッド\r
+        */\r
+       \r
+       // 設定ファイルがなければWebから取得\r
+       public void loadCenter(String code, boolean force) {}\r
+       \r
+       // 設定ファイルへ書き出し\r
+       public boolean saveCenter() { return false; }\r
+       \r
+       /*\r
+        * ★★★★★★★★★★★★★★★★★★★★★★★★★★★★★★★\r
+        * ★★★★★ 放送局を選択する(TVCenterから降格)-ここまで ★★★★★\r
+        * ★★★★★★★★★★★★★★★★★★★★★★★★★★★★★★★\r
+        */\r
+}\r
+\r
diff --git a/TinyBannavi/src/tainavi/PassedProgramList.java b/TinyBannavi/src/tainavi/PassedProgramList.java
new file mode 100644 (file)
index 0000000..d8ca83c
--- /dev/null
@@ -0,0 +1,60 @@
+package tainavi;\r
+\r
+import java.util.ArrayList;\r
+\r
+import tainavi.TVProgram.ProgFlags;\r
+import tainavi.TVProgram.ProgGenre;\r
+import tainavi.TVProgram.ProgOption;\r
+import tainavi.TVProgram.ProgScrumble;\r
+import tainavi.TVProgram.ProgSubgenre;\r
+\r
+/**\r
+ * 過去ログ保存のTXT化で不要になったが、古いXML形式を読むためには必要\r
+ * @deprecated\r
+ */\r
+public class PassedProgramList extends ProgDetailList {\r
+\r
+       /*\r
+        * for SAVE/LOAD用のgetter・setter\r
+        */\r
+       \r
+       public void setTitle(String s) { this.title=s; }\r
+       public String getTitle() { return this.title; }\r
+       //public void setTitlePop(String s) {}\r
+       //public String getTitlePop() { return TraceProgram.replacePop(this.title); }\r
+       public void setDetail(String s) { this.detail=s; }\r
+       public String getDetail() { return this.detail; }\r
+       //public void setDetailPop(String s) {}\r
+       //public String getDetailPop() { return TraceProgram.replacePop(this.detail); }\r
+       @Override\r
+       public void setAddedDetail(String s) { this.addedDetail=s; }\r
+       @Override\r
+       public String getAddedDetail() { return this.addedDetail; }\r
+       public void setStart(String s) { this.start=s; }\r
+       public String getStart() { return this.start; }\r
+       public void setEnd(String s) { this.end=s; }\r
+       public String getEnd() { return this.end; }\r
+       //public void setStartDateTime(String s) { this.startDateTime=s; }\r
+       //public String getStartDateTime() { return this.startDateTime; }\r
+       //public void setEndDateTime(String s) { this.endDateTime=s; }\r
+       //public String getEndDateTime() { return this.endDateTime; }\r
+       //public void setLink(String s) { this.link=s; }\r
+       //public String getLink() { return this.link; }\r
+       public void setLength(int s) { this.length=s; }\r
+       public int getLength() { return this.length; }\r
+       public void setExtension(boolean s) { this.extension=s; }\r
+       public boolean getExtension() { return this.extension; }\r
+       //public void setReserved(boolean s) { this.reserved=s; }\r
+       //public boolean getReserved() { return this.reserved; }\r
+       public void setNoscrumble(ProgScrumble noscrumble) { this.noscrumble=noscrumble; }\r
+       public ProgScrumble getNoscrumble() { return this.noscrumble; }\r
+       public void setGenre(ProgGenre genre) { this.genre=genre; }\r
+       public ProgGenre getGenre() { return this.genre; }\r
+       public void setSubgenre(ProgSubgenre subgenre) { this.subgenre=subgenre; }\r
+       public ProgSubgenre getSubgenre() { return this.subgenre; }\r
+       public void setFlag(ProgFlags flag) { this.flag=flag; }\r
+       public ProgFlags getFlag() { return this.flag; }\r
+       public void setOption(ArrayList<ProgOption> option) { this.option=option; }\r
+       @Override\r
+       public ArrayList<ProgOption> getOption() { return this.option; }\r
+}\r
diff --git a/TinyBannavi/src/tainavi/PickedProgram.java b/TinyBannavi/src/tainavi/PickedProgram.java
new file mode 100644 (file)
index 0000000..a3ec024
--- /dev/null
@@ -0,0 +1,508 @@
+package tainavi;\r
+\r
+import java.io.File;\r
+import java.util.ArrayList;\r
+import java.util.Calendar;\r
+import java.util.Date;\r
+import java.util.GregorianCalendar;\r
+\r
+/**\r
+ * ソース中にPickupとPickedと表記揺れが発生してしまったのが非常に心残り\r
+ */\r
+public class PickedProgram extends TVProgramUtils implements TVProgram,Cloneable {\r
+\r
+       //private static final String thisEncoding = "UTF-8";\r
+       \r
+       public void setDebug(boolean b) { debug = b; }\r
+       private static boolean debug = false;\r
+       \r
+       /* 必須コード  - ここから */\r
+       \r
+       /*******************************************************************************\r
+        * 種族の特性\r
+        ******************************************************************************/\r
+       \r
+       @Override\r
+       public String getTVProgramId() { return tvProgId; }\r
+       private static final String tvProgId = "PickedProgram";\r
+       \r
+       @Override\r
+       public boolean isAreaSelectSupported() { return false; }\r
+       \r
+       @Override\r
+       public ProgType getType() { return ProgType.PICKED; }\r
+       @Override\r
+       public ProgSubtype getSubtype() { return ProgSubtype.NONE; }\r
+       \r
+       @Override\r
+       public PickedProgram clone() {\r
+               return (PickedProgram) super.clone();\r
+       }\r
+       \r
+       /*******************************************************************************\r
+        * 個体の特性\r
+        ******************************************************************************/\r
+       //\r
+       @Override\r
+       public int getTimeBarStart() {return 5;}\r
+\r
+       /*******************************************************************************\r
+        * 定数\r
+        ******************************************************************************/\r
+       \r
+       public static enum WrHeader {\r
+               \r
+               // 順番をかえなければ、どこに追加してもいい\r
+               \r
+               CENTER          ( "$CH$", true ),\r
+               DATE            ( "$DT$", true ),\r
+               \r
+               // ここから下は別の領域なので追加はNG\r
+               \r
+               BEND            ( "$PI#YY$", false ),   // 項目ごとのフッタ\r
+               STARTMARK       ( "$PI#AA$", false ),   // ヘッダ\r
+               ENDMARK         ( "$PI#ZZ$", false ),   // フッタ\r
+               \r
+               ;\r
+               \r
+               private String hdr;\r
+               private boolean marker;\r
+               \r
+               private WrHeader(String hdr, boolean marker) {\r
+                       this.hdr = hdr;\r
+                       this.marker = marker;\r
+               }\r
+               \r
+               // ここはtoString()をOverrideしてよい\r
+               @Override\r
+               public String toString() { return hdr; }\r
+       }\r
+       \r
+       // 区切り文字\r
+       private static final String S_CR = "\n";\r
+       \r
+       private final static String txtname = "env"+File.separator+"picked.txt";\r
+       \r
+       private static final String MSGID = "[ピックアップ] ";\r
+       private static final String DBGID = "[DEBUG]"+MSGID;\r
+       private static final String ERRID = "[ERROR]"+MSGID;\r
+       \r
+       /*******************************************************************************\r
+        * 部品\r
+        ******************************************************************************/\r
+       \r
+       /*******************************************************************************\r
+        * 本体\r
+        ******************************************************************************/\r
+       \r
+       @Override\r
+       public void loadProgram(String areaCode, boolean force) {\r
+               \r
+               //\r
+               if ( ! new File(txtname).exists() ) {\r
+                       System.out.println(MSGID+"ピックアップリストのファイルがありませんでした: "+txtname);\r
+                       return;\r
+               }\r
+               \r
+               // 番組リストの追加\r
+               int cnt = load();\r
+               System.out.println(MSGID+"ピックアップリストを取得しました: "+cnt);\r
+               \r
+       }\r
+       \r
+       \r
+       \r
+       \r
+       // 内部的な\r
+       \r
+       /*\r
+        * プログラムリストをファイルに保存する\r
+        */\r
+       public boolean save() {\r
+               \r
+               StringBuilder sb = new StringBuilder();\r
+               \r
+               for (ProgList pl : pcenter) {\r
+                       for (ProgDateList cl : pl.pdate) {\r
+                               if ( cl.pdetail.size() == 0 ) {\r
+                                       continue;\r
+                               }\r
+                               \r
+                               sb.append(WrHeader.STARTMARK);\r
+                               sb.append(S_CR);\r
+                               sb.append(WrHeader.CENTER);\r
+                               sb.append(pl.Center);\r
+                               sb.append(WrHeader.BEND);\r
+                               sb.append(WrHeader.DATE);\r
+                               sb.append(cl.Date);\r
+                               sb.append(WrHeader.BEND);\r
+                               \r
+                               // 詳細をコピーする\r
+                               for (ProgDetailList d : cl.pdetail) {\r
+                                       sb.append(d.toString());\r
+                               }\r
+\r
+                               sb.append(WrHeader.ENDMARK);\r
+                               sb.append(S_CR);\r
+                       }\r
+               }\r
+               \r
+               // 出力\r
+               if ( ! CommonUtils.write2file(txtname, sb.toString()) ) {\r
+               System.err.println(ERRID+"保存に失敗しました: "+txtname);\r
+               return false;\r
+               }\r
+               \r
+               return true;\r
+       }\r
+       \r
+       private int load() {\r
+               \r
+               String txt = CommonUtils.read4file(txtname, false);\r
+               if ( txt == null ) {\r
+                       return -1;\r
+               }\r
+\r
+               ArrayList<ProgList> newplist = new ArrayList<ProgList>();\r
+\r
+               int cnt = 0;\r
+               \r
+               while ( txt.length() > 0 )\r
+               {\r
+                       if ( ! txt.startsWith(WrHeader.STARTMARK.toString()+S_CR) ) {\r
+                               break;\r
+                       }\r
+                       int newtail = txt.indexOf(WrHeader.ENDMARK.toString()+S_CR);\r
+                       if ( newtail == -1 ) {\r
+                               break;\r
+                       }\r
+                       \r
+                       // 今回の情報\r
+                       String data = txt.substring(WrHeader.STARTMARK.toString().length()+1, newtail+1);\r
+                       \r
+                       // 次回に続く\r
+                       txt = txt.substring(newtail+WrHeader.STARTMARK.toString().length()+1);\r
+                       \r
+                       // 放送局名\r
+                       String center = null;\r
+                       \r
+                       {\r
+                               int btail = 0;\r
+                               \r
+                               if ( ! data.startsWith(WrHeader.CENTER.toString()) ) {\r
+                                       break;\r
+                               }\r
+                               data = data.substring(WrHeader.CENTER.toString().length());\r
+                               \r
+                               btail = data.indexOf(WrHeader.BEND.toString());\r
+                               if ( btail == -1 ) {\r
+                                       break;\r
+                               }\r
+                               center = data.substring(0,btail);\r
+                               data = data.substring(btail+WrHeader.BEND.toString().length());\r
+                       }\r
+\r
+                       // 日付\r
+                       String date =null;\r
+                       \r
+                       {\r
+                               int btail = 0;\r
+                               \r
+                               if ( ! data.startsWith(WrHeader.DATE.toString()) ) {\r
+                                       break;\r
+                               }\r
+                               data = data.substring(WrHeader.DATE.toString().length());\r
+                               \r
+                               btail = data.indexOf(WrHeader.BEND.toString());\r
+                               if ( btail == -1 ) {\r
+                                       break;\r
+                               }\r
+                               date = data.substring(0,btail);\r
+                               data = data.substring(btail+WrHeader.BEND.toString().length());\r
+                       }\r
+\r
+                       // 番組詳細へ\r
+                       \r
+                       // 解析する\r
+                       ProgList pcenter = null;\r
+                       for ( ProgList p : newplist ) {\r
+                               if ( p.Center.equals(center) ) {\r
+                                       pcenter = p;\r
+                                       break;\r
+                               }\r
+                       }\r
+                       if ( pcenter == null ) {\r
+                               pcenter = new ProgList();\r
+                               pcenter.Center = center;\r
+                               pcenter.pdate = new ArrayList<ProgDateList>();\r
+                               newplist.add(pcenter);\r
+                       }\r
+                       \r
+                       ProgDateList pdate = null;\r
+                       for ( ProgDateList p : pcenter.pdate ) {\r
+                               if ( p.Date.equals(date) ) {\r
+                                       pdate = p;\r
+                                       break;\r
+                               }\r
+                       }\r
+                       if ( pdate == null ) {\r
+                               pdate = new ProgDateList();\r
+                               pdate.Date = date;\r
+                               pdate.pdetail = new ArrayList<ProgDetailList>();\r
+                               pcenter.pdate.add(pdate);\r
+                       }\r
+                       \r
+                       cnt += loadDetail(pdate.pdetail,data);\r
+               }\r
+               \r
+               pcenter = newplist;\r
+               \r
+               return cnt;\r
+       }\r
+       \r
+       private int loadDetail(ArrayList<ProgDetailList> pdetail, String txt) {\r
+               int index = 0;\r
+               while ( index < txt.length() )\r
+               {\r
+                       // 番組ヘッダを探す\r
+                       int newtop = txt.indexOf(ProgDetailList.WrHeader.STARTMARK.toString(),index);\r
+                       if ( newtop == -1 ) {\r
+                               break;\r
+                       }\r
+                       newtop += ProgDetailList.WrHeader.STARTMARK.toString().length()+1;\r
+                       \r
+                       // 番組フッタを探す\r
+                       int newtail = txt.indexOf(ProgDetailList.WrHeader.ENDMARK.toString(),newtop);\r
+                       if ( newtail == -1 ) {\r
+                               break;\r
+                       }\r
+                       index = newtail+ProgDetailList.WrHeader.ENDMARK.toString().length()+1;\r
+                       \r
+                       // 解析する\r
+                       ProgDetailList pdl = new ProgDetailList(txt.substring(newtop,newtail));\r
+                       pdetail.add(pdl);\r
+\r
+               }\r
+               \r
+               return pdetail.size();\r
+       }\r
+\r
+       /*\r
+        * 日付でリフレッシュ \r
+        */\r
+       public void refresh() {\r
+               \r
+               GregorianCalendar cs = CommonUtils.getCalendar(0);\r
+               if (CommonUtils.isLateNight(cs.get(Calendar.HOUR_OF_DAY))) {\r
+                       cs.add(Calendar.DATE, -1);\r
+               }\r
+               \r
+               ArrayList<ProgList> tPlist = new ArrayList<ProgList>();\r
+               for (ProgList tPl : pcenter) {\r
+                       GregorianCalendar c = (GregorianCalendar)cs.clone();\r
+                       ProgList pl = new ProgList();\r
+                       pl.Area = tPl.Area;\r
+                       pl.Center = tPl.Center;\r
+                       pl.enabled = tPl.enabled;\r
+                       for (int i=0; i<8; i++) {\r
+                               ProgDateList tPcl = new ProgDateList();\r
+                               tPcl.Date = CommonUtils.getDate(c);\r
+                               pl.pdate.add(tPcl);\r
+                               c.add(Calendar.DATE, 1);\r
+                       }\r
+                       for (ProgDateList tPcl : tPl.pdate) {\r
+                               for (ProgDateList pcl : pl.pdate) {\r
+                                       if (pcl.Date.equals(tPcl.Date)) {\r
+                                               for (ProgDetailList tPdl : tPcl.pdetail) {\r
+                                                       pcl.pdetail.add(tPdl);\r
+                                               }\r
+                                               tPcl.pdetail.clear();   // ごみ掃除\r
+                                       }\r
+                               }\r
+                       }\r
+                       tPlist.add(pl);\r
+               }\r
+               \r
+               pcenter = tPlist;\r
+       }\r
+\r
+       /*\r
+        * 追加しよう\r
+        */\r
+       public boolean add(ProgDetailList tvd) {\r
+               \r
+               ProgList pl = null;\r
+               for (ProgList tPl : pcenter) {\r
+                       if (tPl.Center.equals(tvd.center)) {\r
+                               pl = tPl;\r
+                               break;\r
+                       }\r
+               }\r
+               if (pl == null) {\r
+                       // 放送局リスト\r
+                       pl = new ProgList();\r
+                       pl.Area = getDefaultArea();\r
+                       pl.Center = tvd.center;\r
+                       pl.enabled = true;\r
+                       // 日付リスト\r
+                       {\r
+                               GregorianCalendar c = new GregorianCalendar();\r
+                               c.setTime(new Date());\r
+                               if (CommonUtils.isLateNight(c.get(Calendar.HOUR_OF_DAY))) {\r
+                                       c.add(Calendar.DATE, -1);\r
+                               }\r
+                               for (int i=0; i<8; i++) {\r
+                                       ProgDateList tPcl = new ProgDateList();\r
+                                       tPcl.Date = CommonUtils.getDate(c);\r
+                                       pl.pdate.add(tPcl);\r
+                                       c.add(Calendar.DATE, 1);\r
+                               }\r
+                       }\r
+                       pcenter.add(pl);\r
+               }\r
+               \r
+               ProgDateList pcl = null;\r
+               for (ProgDateList tPcl : pl.pdate) {\r
+                       if (tPcl.Date.equals(tvd.accurateDate)) {\r
+                               pcl = tPcl;\r
+                               break;\r
+                       }\r
+               }\r
+               if (pcl == null) {\r
+                       System.err.println(ERRID+"過去の番組は登録できません:"+tvd.title+"("+tvd.startDateTime+")");\r
+                       return false;\r
+               }\r
+               for (ProgDetailList tPdl : pcl.pdetail) {\r
+                       if (tPdl.title.equals(tvd.title) && tPdl.start.equals(tvd.start) && tPdl.length == tvd.length) {\r
+                               System.err.println(ERRID+"二重登録はできません:"+tvd.title+"("+tvd.startDateTime+")");\r
+                               return false;\r
+                       }\r
+               }\r
+               \r
+               // clone()する必要はあるのか?\r
+               pcl.pdetail.add(tvd);\r
+               \r
+               return true;\r
+       }\r
+\r
+       \r
+       /*\r
+        * 削除しよう\r
+        */\r
+       public boolean remove(ProgDetailList data, String center, String date, boolean force) {\r
+               \r
+               ProgList pl = null;\r
+               for (ProgList tPl : pcenter) {\r
+                       if (tPl.Center.equals(center)) {\r
+                               pl = tPl;\r
+                               break;\r
+                       }\r
+               }\r
+               if (pl == null) {\r
+                       // なんかおかしくねー\r
+                       return false;\r
+               }\r
+               \r
+               ProgDateList pcl = null;\r
+               for (ProgDateList tPcl : pl.pdate) {\r
+                       if (tPcl.Date.equals(date)) {\r
+                               pcl = tPcl;\r
+                               break;\r
+                       }\r
+               }\r
+               if (pcl == null) {\r
+                       // 過去ログとかね…\r
+                       return false;\r
+               }\r
+               \r
+               for (ProgDetailList tPdl : pcl.pdetail) {\r
+                       if (tPdl.title.equals(data.title) && tPdl.start.equals(data.start) && tPdl.end.equals(data.end)) {\r
+                               if (force) {\r
+                                       pcl.pdetail.remove(tPdl);\r
+                               }\r
+                               return true;\r
+                       }\r
+               }\r
+               return false;\r
+       }\r
+       \r
+       /**\r
+        * 指定した番組情報に対応するピックアップ情報を返す\r
+        */\r
+       public ProgDetailList find(ProgDetailList srctvd) {\r
+               \r
+               for ( ProgList tvpl : pcenter ) {\r
+                       if ( ! tvpl.Center.equals(srctvd.center) ) {\r
+                               continue;\r
+                       }\r
+                       \r
+                       for ( ProgDateList tvc : tvpl.pdate ) {\r
+                               if ( ! tvc.Date.equals(srctvd.accurateDate) ) {\r
+                                       continue;\r
+                               }\r
+                               \r
+                               for ( ProgDetailList tvd : tvc.pdetail ) {\r
+                                       if ( tvd.startDateTime.equals(srctvd.startDateTime) && tvd.endDateTime.equals(srctvd.endDateTime) ) {\r
+                                               return tvd;\r
+                                       }\r
+                               }\r
+                       }\r
+               }\r
+               return null;\r
+       }\r
+       \r
+       /* ここまで */\r
+       \r
+       \r
+       \r
+       /*\r
+        * ★★★★★★★★★★★★★★★★★★★★★★★★★★★★★★★\r
+        * ★★★★★ 放送地域を取得する(TVAreaから降格)-ここから ★★★★★\r
+        * ★★★★★★★★★★★★★★★★★★★★★★★★★★★★★★★\r
+        */\r
+       \r
+       /*\r
+        * 公開メソッド\r
+        */\r
+       \r
+       //\r
+       @Override\r
+       public String getDefaultArea() {return "東京";}\r
+       \r
+       //\r
+       public void loadAreaCode() {}\r
+       \r
+       //\r
+       public void saveAreaCode() {}\r
+       \r
+       /*\r
+        * ★★★★★★★★★★★★★★★★★★★★★★★★★★★★★★★\r
+        * ★★★★★ 放送地域を取得する(TVAreaから降格)-ここまで ★★★★★\r
+        * ★★★★★★★★★★★★★★★★★★★★★★★★★★★★★★★\r
+        */\r
+       \r
+       \r
+       \r
+       /*\r
+        * ★★★★★★★★★★★★★★★★★★★★★★★★★★★★★★★\r
+        * ★★★★★ 放送局を選択する(TVCenterから降格)-ここから ★★★★★\r
+        * ★★★★★★★★★★★★★★★★★★★★★★★★★★★★★★★\r
+        */\r
+       \r
+       /*\r
+        * 公開メソッド\r
+        */\r
+       \r
+       // 設定ファイルがなければWebから取得\r
+       public void loadCenter(String code, boolean force) {}\r
+       \r
+       // 設定ファイルへ書き出し\r
+       public boolean saveCenter() { return false; }\r
+       \r
+       /*\r
+        * ★★★★★★★★★★★★★★★★★★★★★★★★★★★★★★★\r
+        * ★★★★★ 放送局を選択する(TVCenterから降格)-ここまで ★★★★★\r
+        * ★★★★★★★★★★★★★★★★★★★★★★★★★★★★★★★\r
+        */\r
+}\r
+\r
diff --git a/TinyBannavi/src/tainavi/PlugIn_CSPDimora.java b/TinyBannavi/src/tainavi/PlugIn_CSPDimora.java
new file mode 100644 (file)
index 0000000..97b35da
--- /dev/null
@@ -0,0 +1,104 @@
+package tainavi;\r
+\r
+import java.io.File;\r
+import java.util.ArrayList;\r
+\r
+\r
+public class PlugIn_CSPDimora extends PlugIn_TVPDimora implements TVProgram,Cloneable {\r
+\r
+       //private static final String thisEncoding = "UTF-8";\r
+       \r
+       /* 必須コード  - ここから */\r
+       \r
+       // 種族の特性\r
+       @Override\r
+       public String getTVProgramId() { return "Dimora(CSデジ)"; }\r
+       \r
+       @Override\r
+       public boolean isAreaSelectSupported() { return false; }\r
+       \r
+       @Override\r
+       public ProgType getType() { return ProgType.PROG; }\r
+       @Override\r
+       public ProgSubtype getSubtype() { return ProgSubtype.CS2; }\r
+\r
+       //\r
+       @Override\r
+       public PlugIn_CSPDimora clone() {\r
+               return (PlugIn_CSPDimora) super.clone();\r
+       }\r
+\r
+       @Override\r
+       protected String getCenterInfoId() { return "ChannelTypeC"; }\r
+       @Override\r
+       protected String getChType() { return "8"; }\r
+       @Override\r
+       protected String getCenterCode(String id, String code) { return code; }\r
+       \r
+       @Override\r
+       protected String getProgCacheFile(String areacode, String adate) { return String.format(getProgDir()+File.separator+"DimoraCS_%s_%s.html", areacode, adate.substring(6,8)); }\r
+       \r
+       \r
+       /*\r
+        * ★★★★★★★★★★★★★★★★★★★★★★★★★★★★★★★\r
+        * ★★★★★ 放送地域を取得する(TVAreaから降格)-ここから ★★★★★\r
+        * ★★★★★★★★★★★★★★★★★★★★★★★★★★★★★★★\r
+        */\r
+       \r
+       /*\r
+        * 公開メソッド\r
+        */\r
+       \r
+       @Override\r
+       public String getDefaultArea() {return "全国";}\r
+       private String getDefaultCode() {return "03";}\r
+       \r
+       @Override\r
+       public void loadAreaCode() {\r
+               aclist = new ArrayList<AreaCode>();\r
+               AreaCode ac = new AreaCode();\r
+               ac.setArea(getDefaultArea());\r
+               ac.setCode(getDefaultCode());\r
+               ac.setSelected(true);\r
+               aclist.add(ac);\r
+       }\r
+       @Override\r
+       public void saveAreaCode() {}\r
+       \r
+       @Override\r
+       public String getArea(String code) { return(getDefaultArea()); }\r
+       @Override\r
+       public String getCode(String area) { return(getDefaultCode()); }\r
+       @Override\r
+       public String setSelectedAreaByName(String area) { return(getDefaultCode()); }\r
+       @Override\r
+       public String getSelectedArea() { return(getDefaultArea()); }\r
+       @Override\r
+       public String getSelectedCode() { return(getDefaultCode()); }\r
+       \r
+       /*\r
+        * ★★★★★★★★★★★★★★★★★★★★★★★★★★★★★★★\r
+        * ★★★★★ 放送地域を取得する(TVAreaから降格)-ここまで ★★★★★\r
+        * ★★★★★★★★★★★★★★★★★★★★★★★★★★★★★★★\r
+        */\r
+       \r
+       \r
+       \r
+       /*\r
+        * ★★★★★★★★★★★★★★★★★★★★★★★★★★★★★★★\r
+        * ★★★★★ 放送局を選択する(TVCenterから降格)-ここから ★★★★★\r
+        * ★★★★★★★★★★★★★★★★★★★★★★★★★★★★★★★\r
+        */\r
+       \r
+       /*\r
+        * 公開メソッド\r
+        */\r
+\r
+       // ここにはなにもない.\r
+       \r
+       /*\r
+        * ★★★★★★★★★★★★★★★★★★★★★★★★★★★★★★★\r
+        * ★★★★★ 放送局を選択する(TVCenterから降格)-ここまで ★★★★★\r
+        * ★★★★★★★★★★★★★★★★★★★★★★★★★★★★★★★\r
+        */\r
+}\r
diff --git a/TinyBannavi/src/tainavi/PlugIn_CSPSkyperfectTV2012.java b/TinyBannavi/src/tainavi/PlugIn_CSPSkyperfectTV2012.java
new file mode 100644 (file)
index 0000000..73d95d8
--- /dev/null
@@ -0,0 +1,691 @@
+package tainavi;\r
+\r
+import java.io.File;\r
+import java.util.ArrayList;\r
+import java.util.Arrays;\r
+import java.util.Calendar;\r
+import java.util.Date;\r
+import java.util.GregorianCalendar;\r
+import java.util.HashMap;\r
+import java.util.regex.Matcher;\r
+import java.util.regex.Pattern;\r
+\r
+\r
+public class PlugIn_CSPSkyperfectTV2012 extends TVProgramUtils implements TVProgram,Cloneable {\r
+\r
+       public PlugIn_CSPSkyperfectTV2012 clone() {\r
+               return (PlugIn_CSPSkyperfectTV2012) super.clone();\r
+       }\r
+       \r
+       private static final String thisEncoding = "UTF-8";\r
+       \r
+       \r
+       /*******************************************************************************\r
+        * 種族の特性\r
+        ******************************************************************************/\r
+       \r
+       @Override\r
+       public String getTVProgramId() { return "スカパー!"; }\r
+       \r
+       @Override\r
+       public ProgType getType() { return ProgType.PROG; }\r
+       @Override\r
+       public ProgSubtype getSubtype() { return ProgSubtype.CS; }\r
+       \r
+       // エリア選択はない\r
+       @Override\r
+       public boolean isAreaSelectSupported() { return false; }\r
+\r
+       /*******************************************************************************\r
+        * 個体の特性\r
+        ******************************************************************************/\r
+       \r
+       @Override\r
+       public int getTimeBarStart() {return 5;}\r
+       \r
+       private int getDogDays() { return ((getExpandTo8())?(8):(7)); }\r
+       \r
+       \r
+       /*******************************************************************************\r
+        * 定数\r
+        ******************************************************************************/\r
+       \r
+       private static final String XTYPE_BASIC = "basic";\r
+       private static final String XTYPE_PREMIUM = "premium";\r
+       \r
+       private static final String CHNM_PREFIX_BS = "BS";\r
+       private static final String CHNM_PREFIX_CS = "CS";\r
+       private static final String CHNM_PREFIX_PR = "Ch.";\r
+       \r
+       private static final String CHID_PREFIX_CS = "e2";\r
+       private static final String CHID_PREFIX_HD = "HD";\r
+       private static final String CHID_PREFIX_SD = "SD";\r
+\r
+       private static final String CHCD_PREFIX_BS = "bs";\r
+       private static final String CHCD_PREFIX_CS = "cs";\r
+\r
+       private final String MSGID = "["+getTVProgramId()+"] ";\r
+       private final String ERRID = "[ERROR]"+MSGID;\r
+       private final String DBGID = "[DEBUG]"+MSGID;\r
+       \r
+       \r
+       /*******************************************************************************\r
+        * 部品\r
+        ******************************************************************************/\r
+       \r
+       protected ArrayList<ProgList> newplist;\r
+       \r
+       private HashMap<String,String> nf = null;\r
+       private HashMap<String,String> gf = null;\r
+       \r
+       \r
+       //\r
+       private static final HashMap<String,ProgGenre> genremap = new HashMap<String, TVProgram.ProgGenre>() {\r
+\r
+               private static final long serialVersionUID = 1L;\r
+\r
+               {\r
+                       put("アニメ/特撮",ProgGenre.ANIME);\r
+                       put("こども向け/教育",ProgGenre.ANIME);\r
+                       \r
+                       put("映画",ProgGenre.MOVIE);\r
+                       put("洋画",ProgGenre.MOVIE);\r
+                       put("邦画",ProgGenre.MOVIE);\r
+                       \r
+                       put("ドラマ/演劇",ProgGenre.DORAMA);\r
+                       put("ドラマ",ProgGenre.DORAMA);\r
+                       \r
+                       put("劇場/公演",ProgGenre.THEATER);\r
+                       \r
+                       put("ドキュメンタリー/教養",ProgGenre.DOCUMENTARY);\r
+\r
+                       put("音楽",ProgGenre.MUSIC);\r
+                       \r
+                       put("バラエティー",ProgGenre.VARIETYSHOW);\r
+                       put("バラエティ",ProgGenre.VARIETYSHOW);\r
+                       \r
+                       put("スポーツ",ProgGenre.SPORTS);\r
+                       \r
+                       put("ニュース/報道",ProgGenre.NEWS);\r
+                       \r
+                       put("アダルト",ProgGenre.NOGENRE);\r
+               }\r
+       };\r
+       \r
+       //\r
+       @Override\r
+       public void loadProgram(String areaCode, boolean force) {\r
+               // 新しい入れ物(トップ)を用意する\r
+               newplist = new ArrayList<ProgList>();\r
+               \r
+               nf = new HashMap<String, String>();\r
+               gf = new HashMap<String, String>();\r
+               \r
+               // 最終日の24時以降の情報は、+1したとこから取得する\r
+               int counterMax = getSortedCRlist().size() * (getDogDays()+1);\r
+               int counter=1;\r
+               for ( Center c : getSortedCRlist() ) {\r
+                       if (getDebug()) System.err.println(DBGID+"load program: "+c.getCenter());\r
+                       _loadProgram(c, force, counter, counterMax);\r
+                       counter += getDogDays()+1;\r
+               }\r
+               \r
+               // 古いデータから補完できないかな?\r
+               CompensatesPrograms(newplist);\r
+               \r
+               // 解析用\r
+               {\r
+                       for ( String f : nf.keySet() ) {\r
+                               System.err.println(String.format("【デバッグ情報】未定義のフラグです: [%s]",f));\r
+                       }\r
+                       for ( String g : gf.keySet() ) {\r
+                               System.err.println(String.format("【デバッグ情報】未定義のジャンルです: [%s]",g));\r
+                       }\r
+               }\r
+               \r
+               // 古い番組データを置き換える\r
+               pcenter = newplist;\r
+       }\r
+       \r
+       /* ここまで */\r
+\r
+       \r
+       \r
+       /*\r
+        * 非公開メソッド\r
+        */\r
+       protected void _loadProgram(Center cr, boolean force, int counter, int counterMax) {\r
+               //\r
+               try {\r
+                       // progfilesの読み出し\r
+                       \r
+                       // 局リストの追加\r
+                       ProgList pl = new ProgList();\r
+                       pl.Center = cr.getCenter();\r
+                       pl.CenterId = cr.getLink();\r
+                       pl.ChId = "";\r
+                       {\r
+                               int onid = -1;\r
+                               if ( cr.getCenterOrig().startsWith(CHNM_PREFIX_BS) ) {\r
+                                       onid = 4;\r
+                               }\r
+                               else if ( cr.getCenterOrig().startsWith(CHNM_PREFIX_CS) ) {\r
+                                       onid = 7;\r
+                               }\r
+                               if ( onid != -1 ) {\r
+                                       Matcher ma = Pattern.compile("(\\d+)").matcher(cr.getCenterOrig());\r
+                                       if ( ma.find() ) {\r
+                                               ContentIdDIMORA.decodeChId(String.format("%04X%04X%04X", onid,0,Integer.valueOf(ma.group(1))));\r
+                                               pl.ChId = ContentIdDIMORA.getContentId(0,"");\r
+                                       }\r
+                               }\r
+                       }\r
+                       pl.Area = cr.getAreaCode();\r
+                       pl.BgColor = cr.getBgColor();\r
+                       pl.enabled = true;\r
+                       newplist.add(pl);\r
+                       \r
+                       // 日付リストの追加\r
+                       getDate(pl);\r
+\r
+                       //\r
+                       for ( int i=0; i<pl.pdate.size(); i++ ) {\r
+                               //\r
+                               GregorianCalendar cal = CommonUtils.getCalendar(pl.pdate.get(i).Date);\r
+                               final String progCacheFile = String.format("%s%sSKP2012_%s_%d.txt", getProgDir(), File.separator, pl.CenterId, cal.get(Calendar.DAY_OF_MONTH));\r
+                               //\r
+                               File f = new File(progCacheFile);\r
+                               if (force == true ||\r
+                                               (f.exists() == true && isCacheOld(progCacheFile) == true) ||\r
+                                               (f.exists() == false && isCacheOld(null) == true)) {\r
+                                       //\r
+                                       String url = null;\r
+                                       String dt = CommonUtils.getDateYMD(cal);\r
+                                       url = "http://bangumi.skyperfectv.co.jp/api/version:3/search/date:"+dt.substring(2)+"/channel:"+pl.CenterId+"/?api_key=336eec3423";\r
+                                       /*\r
+                                       if ( pl.ChId.length() == 0 ) {\r
+                                               url = "http://bangumi.skyperfectv.co.jp/api/version:3/search/date:"+dt.substring(2)+"/channel:"+pl.CenterId+"/?api_key=336eec3423";\r
+                                       }\r
+                                       else {\r
+                                               url = "http://www.skyperfectv.co.jp/xml/"+dt+"_"+pl.ChId.substring(2)+".xml";\r
+                                       }\r
+                                       */\r
+                                       webToFile(url, progCacheFile, thisEncoding);\r
+                                       \r
+                                       reportProgress(getTVProgramId()+"(オンライン)を取得しました: ("+(counter+i)+"/"+counterMax+") "+pl.Center+"["+cal.get(Calendar.DAY_OF_MONTH)+"日]    "+url);\r
+                               }\r
+                               else if (CommonUtils.isFileAvailable(f,10)) {\r
+                                       reportProgress(getTVProgramId()+"(キャッシュ)を取得しました: ("+(counter+i)+"/"+counterMax+") "+pl.Center+"["+cal.get(Calendar.DAY_OF_MONTH)+"日]    "+progCacheFile);\r
+                               }\r
+                               else {\r
+                                       reportProgress(getTVProgramId()+"(キャッシュ)がみつかりません: ("+(counter+i)+"/"+counterMax+") "+pl.Center+"["+cal.get(Calendar.DAY_OF_MONTH)+"日]    "+progCacheFile);\r
+                                       continue;\r
+                               }\r
+       \r
+                               String response = CommonUtils.read4file(progCacheFile, true);\r
+                               \r
+                               // 番組リストの追加\r
+                               try {\r
+                                       getPrograms(pl, i, response);\r
+                                       /*\r
+                                       if ( pl.ChId.length() == 0 ) {\r
+                                               getPrograms(pl, i, response);\r
+                                       }\r
+                                       else {\r
+                                               getPrograms_basic(pl, i, response);\r
+                                       }\r
+                                       */\r
+                               }\r
+                               catch ( Exception e ) {\r
+                                       e.printStackTrace();\r
+                               }\r
+                       }\r
+                       \r
+                       // 日付の調整\r
+                       refreshList(pl.pdate);\r
+               }\r
+               catch (Exception e) {\r
+                       // 例外\r
+                       System.out.println("Exception: _loadProgram()");\r
+               }\r
+       }\r
+       \r
+       //\r
+       private void getDate(ProgList pl) {\r
+               // 日付の処理\r
+               GregorianCalendar c = new GregorianCalendar();\r
+               c.setTime(new Date());\r
+               if ( CommonUtils.isLateNight(c) ) {\r
+                       // 4時までは当日扱いにする\r
+                       c.add(Calendar.DATE, -1);\r
+               }\r
+               // 最終日の24時以降の情報は、+1したとこから取得する\r
+               for ( int i=0; i<getDogDays()+1; i++ ) {\r
+                       ProgDateList cl = new ProgDateList();\r
+                       cl.Date = CommonUtils.getDate(c);\r
+                       pl.pdate.add(cl);\r
+                       \r
+                       c.add(Calendar.DATE,1);\r
+               }\r
+       }\r
+       \r
+       //\r
+       private void refreshList(ArrayList<ProgDateList> pcenter) {\r
+               // 前日分の情報は前日のリストに入れ替える\r
+               for ( int i=0; i<pcenter.size(); i++ ) {\r
+                       ProgDateList pl = pcenter.get(i);\r
+                       ArrayList<ProgDetailList> pre = new ArrayList<ProgDetailList>();\r
+                       ArrayList<ProgDetailList> cur = new ArrayList<ProgDetailList>();\r
+                       for ( ProgDetailList pdl : pl.pdetail ) {\r
+                               if ( pl.Date.compareTo(pdl.accurateDate) >= 0 && CommonUtils.isLateNight(pdl.start.substring(0, 2)) ) {\r
+                                       // 前日分とする\r
+                                       pre.add(pdl);\r
+                               }\r
+                               else {\r
+                                       // 当日分とする\r
+                                       cur.add(pdl);\r
+                               }\r
+                       }\r
+                       if ( i > 0 ) {\r
+                               // 前日のリストに混ぜる\r
+                               for ( ProgDetailList pdl : pre ) {\r
+                                       pcenter.get(i-1).pdetail.add(pdl);\r
+                               }\r
+                       }\r
+                       String tbstart = CommonUtils.getTime(getTimeBarStart(),0);\r
+                       if ( pre.size() > 0 && pre.get(pre.size()-1).end.compareTo(tbstart) > 0 ) {\r
+                               if (getDebug()) System.err.println(DBGID+"overlap "+pre.get(pre.size()-1).end+" "+pre.get(pre.size()-1).title);\r
+                               // 日またがり(29時またがり)のものを当日のリストにもコピーする\r
+                               ProgDetailList pdl = pre.get(pre.size()-1).clone();\r
+                               pdl.length = (int)(CommonUtils.getDiffDateTime(pdl.accurateDate+" "+tbstart, pdl.accurateDate+" "+pdl.end)/60000L);\r
+                               cur.add(0,pdl);\r
+                       }\r
+                       pl.pdetail = cur;\r
+               }\r
+               // 隙間を埋める\r
+               for ( ProgDateList pl : pcenter ) {\r
+                       ArrayList<ProgDetailList> cur = new ArrayList<ProgDetailList>();\r
+                       String preend = pl.Date.substring(0,10)+" "+CommonUtils.getTime(getTimeBarStart(),0);   // 最初の"前番組のおしり"は05:00\r
+                       for ( int i=0; i<pl.pdetail.size(); i++ ) {\r
+                               ProgDetailList pdl = pl.pdetail.get(i);\r
+                               if ( preend.compareTo(pdl.startDateTime) < 0 ) {\r
+                                       // 前の番組との間に隙間があれば埋める\r
+                                       ProgDetailList npdl = new ProgDetailList();\r
+                                       npdl.title = npdl.splitted_title = "番組情報がありません";\r
+                                       npdl.length = (int)(CommonUtils.getDiffDateTime(preend, pdl.startDateTime)/60000L);\r
+                                       cur.add(npdl);\r
+                               }\r
+                               cur.add(pdl);\r
+                               preend = pdl.endDateTime;\r
+                       }\r
+                       pl.pdetail = cur;\r
+               }\r
+               \r
+               // 24時以降の情報は日付+1したとこから取得しているので、最終日の次のリストは削除\r
+               pcenter.remove(pcenter.size()-1);\r
+               \r
+               // 総時間数等を整理する\r
+               for ( ProgDateList pl : pcenter ) {\r
+                       // 1日の合計分数を足し合わせる\r
+                       pl.row = 0;\r
+                       for ( ProgDetailList pdl : pl.pdetail ) {\r
+                               pl.row += pdl.length;\r
+                       }\r
+                       // おしりがとどかない場合(デメリット:これをやると、サイト側のエラーで欠けてるのか、そもそも休止なのかの区別がつかなくなる)\r
+                       if ( pl.row < 24*60 ) {\r
+                               ProgDetailList npdl = new ProgDetailList();\r
+                               npdl.title = npdl.splitted_title = "番組情報がありません";\r
+                               npdl.length = 24*60 - pl.row;\r
+                               pl.pdetail.add(npdl);\r
+                               pl.row += npdl.length;\r
+                       }\r
+               }\r
+       }\r
+       \r
+       //\r
+       private void getPrograms(ProgList pl, int dtidx, String response) {\r
+               \r
+               ProgDateList pcl = pl.pdate.get(dtidx);\r
+               \r
+               Matcher ma = Pattern.compile("\\{(.+?)\\}[,\\]]",Pattern.DOTALL).matcher(response.replaceFirst("^.+?:\\[", ""));\r
+               while ( ma.find() ) {\r
+                       ProgDetailList pdl = new ProgDetailList();\r
+                       String subtitle = "";\r
+                       String person = "";\r
+                       Matcher mb = Pattern.compile("\"(.+?)\":(\\[(.*?)\\]|\"?(.*?)\"?)[,}]").matcher(ma.group(1));\r
+                       while ( mb.find() ) {\r
+                               if ( mb.group(1).equals("start") ) {\r
+                                       GregorianCalendar c = new GregorianCalendar();\r
+                                       c.setTimeInMillis(Long.valueOf(mb.group(2).replaceFirst("\\.\\d+$", ""))*1000L);\r
+                                       pdl.accurateDate = CommonUtils.getDate(c);\r
+                                       pdl.startDateTime = CommonUtils.getDateTime(c); \r
+                                       pdl.start = CommonUtils.getTime(c).replaceFirst("^.+ ", "");\r
+                               }\r
+                               else if ( mb.group(1).equals("end") ) {\r
+                                       GregorianCalendar c = new GregorianCalendar();\r
+                                       c.setTimeInMillis(Long.valueOf(mb.group(2).replaceFirst("\\.\\d+$", ""))*1000L);\r
+                                       pdl.endDateTime = CommonUtils.getDateTime(c); \r
+                                       pdl.end = CommonUtils.getTime(c).replaceFirst("^.+ ", "");\r
+                               }\r
+                               else if ( mb.group(1).equals("title") ) {\r
+                                       pdl.title = mb.group(4).replace("\\\"", "\"");\r
+                               }\r
+                               else if ( mb.group(1).equals("episode_title") ) {\r
+                                       subtitle = mb.group(4);\r
+                               }\r
+                               else if ( mb.group(1).equals("explanation") ) {\r
+                                       pdl.detail = mb.group(4).replace("\\\"", "\"").replace("\\n", "\n");\r
+                               }\r
+                               else if ( mb.group(1).equals("person") ) {\r
+                                       String[] d = mb.group(3).split(",");\r
+                                       for ( String s : d ) {\r
+                                               Matcher mc = Pattern.compile("\"(.+?)\"").matcher(s);\r
+                                               if ( mc.find() ) {\r
+                                                       person += "、"+mc.group(1);\r
+                                               }\r
+                                       }\r
+                                       if ( person.length() > 0 ) {\r
+                                               person = person.substring(1);\r
+                                       }\r
+                               }\r
+                               else if ( mb.group(1).equals("duration") ) {\r
+                                       //pdl.length = Integer.valueOf(mb.group(4))/60; // 使えないっぽい\r
+                               }\r
+                               else if ( mb.group(1).equals("genres") ) {\r
+                                       // サブジャンルは無視することにする\r
+                                       String[] d = mb.group(3).split(",");\r
+                                       if ( d.length >= 1 ) {\r
+                                               Matcher mc = Pattern.compile("\"(.+?)\"").matcher(d[0]);\r
+                                               if ( mc.find() ) {\r
+                                                        ProgGenre genre = genremap.get(mc.group(1));\r
+                                                        if ( genre == null ) {\r
+                                                                // 未定義のジャンルです!\r
+                                                                pdl.genre = ProgGenre.NOGENRE;\r
+                                                                gf.put(mc.group(1),null);\r
+                                                        }\r
+                                                        else {\r
+                                                                pdl.genre = genre;\r
+                                                        }\r
+                                               }\r
+                                       }\r
+                                       if ( d.length >= 2 ) {\r
+                                               // サブジャンルは使用しないことにする\r
+                                               /*\r
+                                               Matcher mc = Pattern.compile("\"(.+?)\"").matcher(d[1]);\r
+                                               if ( mc.find() ) {\r
+                                                       String subgenrestr = mc.group(1);\r
+                                               }\r
+                                               */\r
+                                       }\r
+                               }\r
+                               else if ( mb.group(1).equals("no_scramble") ) {\r
+                                       pdl.noscrumble = (mb.group(4).equals("ノンスクランブル"))?(ProgScrumble.NOSCRUMBLE):(ProgScrumble.SCRUMBLED);\r
+                               }\r
+                       }\r
+                       \r
+                       // 算出してみる\r
+                       pdl.length = CommonUtils.getRecMinVal(pdl.start, pdl.end);\r
+                       //(int)(CommonUtils.getDiffDateTime(pdl.startDateTime, pdl.endDateTime)/60000L);\r
+                       \r
+                       // くっつけてみる\r
+                       pdl.detail =\r
+                                       ((subtitle.length()>0)?(subtitle+DETAIL_SEP):(""))\r
+                                       +pdl.detail\r
+                                       +((person.length()>0)?(DETAIL_SEP+person):(""));\r
+                       pdl.detail = pdl.detail.replaceFirst("[\r\n]+$", "");\r
+\r
+                       // タイトルから各種フラグを分離する\r
+                       doSplitFlags(pdl, nf);\r
+                       \r
+                       // サブタイトル分離(ポインタを活用してメモリを節約する)\r
+                       doSplitSubtitle(pdl);\r
+                       \r
+                       // 番組ID\r
+                       if ( ContentIdDIMORA.isValid(pl.ChId) ) {\r
+                               pdl.progid = pl.ChId;\r
+                       }\r
+                       \r
+                       // その他フラグ\r
+                       pdl.extension = false;\r
+                       pdl.nosyobo = false;\r
+                       \r
+                       pcl.pdetail.add(pdl);\r
+                       \r
+                       if (getDebug()) System.err.println(DBGID+"program: "+pdl.startDateTime+" - "+pdl.endDateTime+" "+pdl.length+"m "+pdl.noscrumble+" "+pdl.title);\r
+               }\r
+       }\r
+\r
+       \r
+       /**\r
+        * こちらは番組IDがとれる代わりに出演者情報がとれなくなるので保留とする\r
+        */\r
+       private void getPrograms_basic(ProgList pl, int dtidx, String response) {\r
+               \r
+               Matcher ma = Pattern.compile("<SIInformation(.+?)</SIInformation>",Pattern.DOTALL).matcher(response);\r
+               while ( ma.find() ) {\r
+                       Matcher mb = Pattern.compile("eventId=\"(.+?)\".+?broadCastStartDate=\"(\\d{8})(\\d{4})",Pattern.DOTALL).matcher(ma.group(1));\r
+                       if ( mb.find() ) {\r
+                               \r
+                       }\r
+                       /*\r
+                       mb = Pattern.compile("<(.+?)>(.+?)</\\1>",Pattern.DOTALL).matcher(ma.group(1));\r
+                       while ( mb.find() ) {\r
+                               if ( mb.group(1).equals("ChannelName") ) {\r
+                                       \r
+                               }\r
+                               else if ( mb.group(1).equals("Title") ) {\r
+                                       \r
+                               }\r
+                               else if ( mb.group(1).equals("Synopsis") ) {\r
+                                       \r
+                               }\r
+                               else if ( mb.group(1).equals("Genres") ) {\r
+                                       Matcher mc = Pattern.compile("<Genre majorGenreId=\".+?\" minorGenreId=\".+?\"",Pattern.DOTALL).matcher(mb.group(2));\r
+                                       while ( mc.find() ) {\r
+                                               \r
+                                       }\r
+                               }\r
+                       }\r
+                       */\r
+               }\r
+       }\r
+\r
+       \r
+       /*\r
+        * ★★★★★★★★★★★★★★★★★★★★★★★★★★★★★★★\r
+        * ★★★★★ 放送地域を取得する(TVAreaから降格)-ここから ★★★★★\r
+        * ★★★★★★★★★★★★★★★★★★★★★★★★★★★★★★★\r
+        */\r
+       \r
+       /*\r
+        * 公開メソッド\r
+        */\r
+       \r
+       @Override\r
+       public String getDefaultArea() {return "全国";}\r
+       private String getDefaultCode() {return "SKP2012";}\r
+       \r
+       @Override\r
+       public void loadAreaCode() {\r
+               aclist = new ArrayList<AreaCode>();\r
+               AreaCode ac = new AreaCode();\r
+               ac.setArea(getDefaultArea());\r
+               ac.setCode(getDefaultCode());\r
+               ac.setSelected(true);\r
+               aclist.add(ac);\r
+       }\r
+       @Override\r
+       public void saveAreaCode() {}\r
+       \r
+       @Override\r
+       public String getArea(String code) { return(getDefaultArea()); }\r
+       @Override\r
+       public String getCode(String area) { return(getDefaultCode()); }\r
+       @Override\r
+       public String setSelectedAreaByName(String area) { return(getDefaultCode()); }\r
+       @Override\r
+       public String getSelectedArea() { return(getDefaultArea()); }\r
+       @Override\r
+       public String getSelectedCode() { return(getDefaultCode()); }\r
+       \r
+       /*\r
+        * ★★★★★★★★★★★★★★★★★★★★★★★★★★★★★★★\r
+        * ★★★★★ 放送地域を取得する(TVAreaから降格)-ここまで ★★★★★\r
+        * ★★★★★★★★★★★★★★★★★★★★★★★★★★★★★★★\r
+        */\r
+       \r
+       \r
+       \r
+       /*\r
+        * ★★★★★★★★★★★★★★★★★★★★★★★★★★★★★★★\r
+        * ★★★★★ 放送局を選択する(TVCenterから降格)-ここから ★★★★★\r
+        * ★★★★★★★★★★★★★★★★★★★★★★★★★★★★★★★\r
+        */\r
+       \r
+       /*\r
+        * 公開メソッド\r
+        */\r
+\r
+       // 設定ファイルがなければWebから取得\r
+       @Override\r
+       public void loadCenter(String code, boolean force) {\r
+               \r
+               if ( code == null ) {\r
+                       System.out.println(ERRID+"地域コードがnullです.");\r
+                       return;\r
+               }\r
+               \r
+               String centerListFile = getCenterListFile(getTVProgramId(), code);\r
+               \r
+               if (force) {\r
+                       File f = new File(centerListFile);\r
+                       f.delete();\r
+               }\r
+               \r
+               File f = new File(centerListFile);\r
+               if (f.exists() == true) {\r
+                       @SuppressWarnings("unchecked")\r
+                       ArrayList<Center> tmp = (ArrayList<Center>)CommonUtils.readXML(centerListFile);\r
+                       if ( tmp != null ) {\r
+                               crlist = tmp;\r
+                               // 放送局名変換\r
+                               attachChFilters();\r
+                               System.out.println("放送局リストを読み込みました: "+centerListFile);\r
+                               return;\r
+                       }\r
+                       else {\r
+                               System.out.println("放送局リストの読み込みに失敗しました: "+centerListFile);\r
+                       }\r
+               }\r
+\r
+               // 放送局をつくるよ\r
+               ArrayList<Center> newcrlist = new ArrayList<Center>();\r
+               \r
+               for ( String xtype : new String[] { XTYPE_BASIC,XTYPE_PREMIUM } ) {\r
+                       ArrayList<Center> crl = getCenters(xtype,code);\r
+                       if ( crl != null ) {\r
+                               reportProgress("放送局情報を取得しました: ("+xtype+") "+crl.size()+"ch");\r
+                       }\r
+                       \r
+                       for ( Center cr : crl ) {\r
+                               newcrlist.add(cr);\r
+                       }\r
+               }\r
+               \r
+               if ( newcrlist.size() == 0 ) {\r
+                       System.err.println(ERRID+"放送局情報の取得結果が0件だったため情報を更新しません");\r
+                       return;\r
+               }\r
+               \r
+               crlist = newcrlist;\r
+               attachChFilters();      // 放送局名にフィルタをかける\r
+               saveCenter();\r
+       }\r
+       \r
+       private ArrayList<Center> getCenters(String xtype, String areacode) {\r
+               \r
+               ArrayList<Center> crl = new ArrayList<Center>();\r
+               \r
+               String url = String.format("http://www.skyperfectv.co.jp/channel/%s/list.html",xtype);\r
+               if (getDebug()) System.err.println(DBGID+"get page: "+url);\r
+               String response = webToBuffer(url,thisEncoding,true);\r
+               if ( response == null ) {\r
+                       reportProgress("放送局情報の取得に失敗しました: "+xtype);\r
+                       return null;\r
+               }\r
+               \r
+               Matcher ma = Pattern.compile("<tr class=\"[^\"]*?ch-(.)d\">(.+?)</tr>",Pattern.DOTALL).matcher(response);\r
+               while ( ma.find() ) {\r
+                       Matcher mb = Pattern.compile("<div class=\"channel ch-channel\">.+?<a href=\"/channel/"+xtype+"/(bs|cs)?(\\d+)\\.html\">[  \\t]*(.+?)[  \\t]*</a>",Pattern.DOTALL).matcher(ma.group(2));\r
+                       if ( mb.find() ) {\r
+                               String chid_prefix = null;\r
+                               String chnm_prefix = null;\r
+                               if ( xtype.equals(XTYPE_BASIC) && CHCD_PREFIX_CS.equals(mb.group(1)) ) {\r
+                                       chid_prefix = CHID_PREFIX_CS;\r
+                                       chnm_prefix = CHNM_PREFIX_CS;\r
+                               }\r
+                               else {\r
+                                       if ( xtype.equals(XTYPE_BASIC) ) {\r
+                                               chid_prefix = CHID_PREFIX_CS;\r
+                                       }\r
+                                       else {\r
+                                               if ( ma.group(1).equals("h") ) {\r
+                                                       chid_prefix = CHID_PREFIX_HD;\r
+                                               }\r
+                                               else {\r
+                                                       chid_prefix = CHID_PREFIX_SD;\r
+                                               }\r
+                                       }\r
+                                       if ( CHCD_PREFIX_BS.equals(mb.group(1)) ) {\r
+                                               chnm_prefix = CHNM_PREFIX_BS;\r
+                                       }\r
+                                       else {\r
+                                               chnm_prefix = CHNM_PREFIX_PR;\r
+                                       }\r
+                               }\r
+                       \r
+                               if ( chid_prefix != null ) {\r
+                                       Center cr = new Center();\r
+                                       cr.setAreaCode(areacode);\r
+                                       cr.setType("");\r
+                                       cr.setEnabled(true);\r
+                                       String name = CommonUtils.toHANALNUM(CommonUtils.unEscape(chnm_prefix+mb.group(2)+" "+mb.group(3)));\r
+                                       String chid = null;\r
+                                       if ( xtype.equals(XTYPE_BASIC) && name.contains("スター・チャンネル") ) {\r
+                                               // e2のスター・チャンネルは専用の番組表が用意されていないみたい\r
+                                               chid = String.format("%s%03d",CHID_PREFIX_HD,Integer.valueOf(mb.group(2))+425);\r
+                                       }\r
+                                       else {\r
+                                               chid = chid_prefix+mb.group(2);\r
+                                       }\r
+                                       if ( ! name.endsWith("▲") && chid.startsWith("SD") ) {\r
+                                               // スカパーのサイトにバグがあった!\r
+                                               chid = chid.replaceFirst("^SD", "HD");\r
+                                               if (getDebug()) System.err.println("+コード修正: "+name+" (SD->"+chid+")");\r
+                                       }\r
+                                       cr.setCenterOrig(name.replaceFirst("[  \\t]+▲$", ""));\r
+                                       cr.setLink(chid);\r
+                                       \r
+                                       int idx = 0;\r
+                                       for ( Center ct : crl ) {\r
+                                               if ( ct.getCenterOrig().compareTo(cr.getCenterOrig()) > 0 ) {\r
+                                                       break;\r
+                                               }\r
+                                               idx++;\r
+                                       }\r
+                                       crl.add(idx, cr);\r
+                                       \r
+                                       if (getDebug()) System.err.println(DBGID+"center: "+cr.getCenterOrig()+", "+cr.getLink());\r
+                               }\r
+                       }\r
+               }\r
+               \r
+               return crl;\r
+       }\r
+       \r
+       /*\r
+        * ★★★★★★★★★★★★★★★★★★★★★★★★★★★★★★★\r
+        * ★★★★★ 放送局を選択する(TVCenterから降格)-ここまで ★★★★★\r
+        * ★★★★★★★★★★★★★★★★★★★★★★★★★★★★★★★\r
+        */\r
+}\r
diff --git a/TinyBannavi/src/tainavi/PlugIn_CSPTVKingdom.java b/TinyBannavi/src/tainavi/PlugIn_CSPTVKingdom.java
new file mode 100644 (file)
index 0000000..bee7883
--- /dev/null
@@ -0,0 +1,61 @@
+package tainavi;\r
+\r
+import java.util.ArrayList;\r
+import java.util.regex.Matcher;\r
+import java.util.regex.Pattern;\r
+\r
+\r
+public class PlugIn_CSPTVKingdom extends PlugIn_CSPTVKingdom110 implements TVProgram,Cloneable {\r
+\r
+       final String thisEncoding = "UTF-8";\r
+       \r
+       /* 必須コード  - ここから */\r
+       \r
+       // 種族の特性\r
+       @Override\r
+       public String getTVProgramId() { return "Gガイド.テレビ王国(スカパー!)"; }\r
+       protected String getCSType() { return csCode; }\r
+       \r
+       @Override\r
+       public boolean isAreaSelectSupported() { return false; }\r
+       \r
+       @Override\r
+       public ProgType getType() { return ProgType.PROG; }\r
+       @Override\r
+       public ProgSubtype getSubtype() { return ProgSubtype.CS; }\r
+       \r
+       //\r
+       public PlugIn_CSPTVKingdom clone() {\r
+               return (PlugIn_CSPTVKingdom) super.clone();\r
+       }\r
+\r
+       @Override\r
+       protected boolean _loadCenter(ArrayList<Center> newcrlist, String code, String type, String uri) {\r
+               String response = webToBuffer(uri,thisEncoding,true);\r
+               if ( response == null ) {\r
+                       System.err.println("放送局情報の取得に失敗しました: "+uri);\r
+                       return false;\r
+               }\r
+               \r
+               reportProgress("放送局情報を取得しました: (1/1) "+uri);\r
+               \r
+               // 局名リストに追加する\r
+               \r
+               Matcher ma = Pattern.compile("\"9999\":\\s*\\{\\s*\"name\":\\s*(.+?)\\s*\\}\\s*},").matcher(response);\r
+               if (ma.find()) {\r
+                       Matcher mb = Pattern.compile("\"([^\"]+?)\":\\s*\\{\\s*\"name\":\\s*\"([^\"]+?)\"\\s*\\},").matcher(ma.group(1));\r
+                       while (mb.find()) {\r
+                               String centerName = mb.group(2);\r
+                               Center cr = new Center();\r
+                               cr.setAreaCode(code);\r
+                               cr.setCenterOrig(centerName);\r
+                               cr.setLink(mb.group(1));\r
+                               cr.setType(type);\r
+                               cr.setEnabled(true);\r
+                               newcrlist.add(cr);\r
+                       }\r
+               }\r
+               \r
+               return true;\r
+       }\r
+}\r
diff --git a/TinyBannavi/src/tainavi/PlugIn_CSPTVKingdom110.java b/TinyBannavi/src/tainavi/PlugIn_CSPTVKingdom110.java
new file mode 100644 (file)
index 0000000..7c43e6c
--- /dev/null
@@ -0,0 +1,347 @@
+package tainavi;\r
+\r
+import java.io.File;\r
+import java.util.ArrayList;\r
+import java.util.Calendar;\r
+import java.util.Date;\r
+import java.util.GregorianCalendar;\r
+import java.util.HashMap;\r
+import java.util.regex.Matcher;\r
+import java.util.regex.Pattern;\r
+\r
+\r
+public class PlugIn_CSPTVKingdom110 extends PlugIn_TVPTVKingdom implements TVProgram,Cloneable {\r
+\r
+       public PlugIn_CSPTVKingdom110 clone() {\r
+               return (PlugIn_CSPTVKingdom110) super.clone();\r
+       }\r
+\r
+       private static final String thisEncoding = "UTF-8"; \r
+       \r
+       \r
+       /*******************************************************************************\r
+        * 種族の特性\r
+        ******************************************************************************/\r
+       \r
+       @Override\r
+       public String getTVProgramId() { return "Gガイド.テレビ王国(スカパー!e2)"; }\r
+       \r
+       @Override\r
+       public ProgType getType() { return ProgType.PROG; }\r
+       \r
+       @Override\r
+       public ProgSubtype getSubtype() { return ProgSubtype.CS2; }\r
+       \r
+       \r
+       /*******************************************************************************\r
+        * 個体の特性\r
+        ******************************************************************************/\r
+       \r
+       private int getDogDays() { return ((getExpandTo8())?(8):(7)); }\r
+       \r
+       protected String getCSType() { return "cs110"; }\r
+       \r
+       @Override\r
+       public boolean isAreaSelectSupported() { return false; }\r
+\r
+\r
+       /*******************************************************************************\r
+        * 定数\r
+        ******************************************************************************/\r
+       \r
+       private final String MSGID = "["+getTVProgramId()+"] ";\r
+       private final String ERRID = "[ERROR]"+MSGID;\r
+       private final String DBGID = "[DEBUG]"+MSGID;\r
+\r
+\r
+       /*******************************************************************************\r
+        * 部品\r
+        ******************************************************************************/\r
+       \r
+       \r
+       /*******************************************************************************\r
+        * コンストラクタ\r
+        ******************************************************************************/\r
+       \r
+       \r
+       /*******************************************************************************\r
+        * 番組情報を取得する\r
+        ******************************************************************************/\r
+       @Override\r
+       public void loadProgram(String areaCode, boolean force) {\r
+               \r
+               // 入れ物を空にする\r
+               newplist.clear();\r
+               nf.clear(); \r
+\r
+               //\r
+               int counterMax = getSortedCRlist().size();\r
+               int counter=1;\r
+               for ( Center c : getSortedCRlist() ) {\r
+                       _loadProgram(c, force, counter++, counterMax);\r
+               }\r
+               \r
+               debugNF();\r
+               \r
+               // 古い番組データを置き換える\r
+               pcenter = newplist;\r
+       }\r
+       \r
+       private void _loadProgram(Center cr, boolean force, int counter, int counterMax) {\r
+               //\r
+               try {\r
+                       // 局リストの追加\r
+                       ProgList pl = new ProgList();\r
+                       pl.Area = cr.getAreaCode();\r
+                       pl.Center = cr.getCenter();\r
+                       pl.CenterId = cr.getLink();\r
+                       pl.Type = cr.getType();\r
+                       pl.BgColor = cr.getBgColor();\r
+                       pl.enabled = true;\r
+                       newplist.add(pl);\r
+                       \r
+                       String response = null;\r
+                       \r
+                       //\r
+                       final String progCacheFile = String.format(getProgDir()+File.separator+"tvk_%s_%s_%s.html", getCSType(), pl.Area, pl.CenterId);\r
+                       //\r
+                       File f = new File(progCacheFile);\r
+                       if (force == true ||\r
+                                       (f.exists() == true && isCacheOld(progCacheFile) == true) ||\r
+                                       (f.exists() == false && isCacheOld(null) == true)) {\r
+                               //\r
+                               GregorianCalendar c = new GregorianCalendar();\r
+                               c.setTime(new Date());\r
+                               if (CommonUtils.isLateNight(c.get(Calendar.HOUR_OF_DAY))) {\r
+                                       c.add(Calendar.DATE, -1);\r
+                               }\r
+                               //\r
+                               String url = String.format("http://tv.so-net.ne.jp/chart/%s/%s.action?parentId=9999&childId=%s&id=%s&head=%04d%02d%02d0500&span=24",\r
+                                               getCSType(), pl.CenterId,pl.CenterId,pl.CenterId, c.get(Calendar.YEAR),c.get(Calendar.MONTH)+1,c.get(Calendar.DAY_OF_MONTH));\r
+                               //\r
+                               \r
+                               response = webToBuffer(url, thisEncoding, true);\r
+                               if ( response == null ) {\r
+                                       return;\r
+                               }\r
+                               CommonUtils.write2file(progCacheFile, response);\r
+                               \r
+                               reportProgress(String.format("%s (オンライン)を取得しました: (%d/%d) %s %s",getTVProgramId(),counter,counterMax,pl.Center,url));\r
+                               \r
+                               // 連続アクセス規制よけ\r
+                               CommonUtils.milSleep(accessWait);\r
+                       }\r
+                       else if (f.exists()) {\r
+                               response = CommonUtils.read4file(progCacheFile, true);\r
+                               if ( response == null ) {\r
+                                       return;\r
+                               }\r
+                               \r
+                               reportProgress(String.format("%s (キャッシュ)を取得しました: (%d/%d) %s %s",getTVProgramId(),counter,counterMax,pl.Center,progCacheFile));\r
+                       }\r
+                       else {\r
+                               reportProgress(String.format("%s (キャッシュ)がみつかりません: (%d/%d) %s %s",getTVProgramId(),counter,counterMax,pl.Center,progCacheFile));\r
+                               return;\r
+                       }\r
+\r
+                       // 日付リストの追加\r
+                       getDateList(pl);\r
+                       \r
+                       // 番組リストの追加\r
+                       getPrograms(pl, response);\r
+                       \r
+                       // 日付の調整\r
+                       setAccurateDate(pl.pdate);\r
+               }\r
+               catch (Exception e) {\r
+                       // 例外\r
+                       System.out.println("Exception: _loadProgram()");\r
+                       e.printStackTrace();\r
+               }\r
+       }\r
+       \r
+       //\r
+       private void getDateList(ProgList pl) {\r
+               //\r
+               GregorianCalendar c = new GregorianCalendar();\r
+               c.setTime(new Date());\r
+               if (CommonUtils.isLateNight(c.get(Calendar.HOUR_OF_DAY))) {\r
+                       c.add(Calendar.DATE, -1);\r
+               }\r
+               for (int i=0; i<getDogDays(); i++) {\r
+                   ProgDateList cl = new ProgDateList();\r
+                       cl.Date = String.format("%04d/%02d/%02d(%s)", c.get(Calendar.YEAR), c.get(Calendar.MONTH)+1, c.get(Calendar.DAY_OF_MONTH), CommonUtils.WDTPTN[c.get(Calendar.DAY_OF_WEEK)-1]);\r
+                       cl.row = 0;\r
+                       pl.pdate.add(cl);\r
+                       c.add(Calendar.DAY_OF_MONTH,1);\r
+               }\r
+       }\r
+       \r
+       //\r
+       private void getPrograms(ProgList pl, String src) {\r
+               //\r
+               Matcher ma = Pattern.compile("<div class=\"cell-date cell-top cell-station\"(.+?)<div class=\"cell-date cell-station\"").matcher(src);\r
+               for (int i=0; ma.find() && i<getDogDays(); i++) {\r
+                       Matcher mb = Pattern.compile("title=\"(\\d+)月\\s*(\\d+)日").matcher(ma.group(1));\r
+                       if (mb.find()) {\r
+                               String date = String.format("%02d/%02d",Integer.valueOf(mb.group(1)),Integer.valueOf(mb.group(2)));\r
+                               for ( int j=0; j<pl.pdate.size(); j++ ) {\r
+                                       if ( pl.pdate.get(j).Date.substring(5,10).equals(date) ) {\r
+                                               // 一日分取り込む\r
+                                               getDetails(pl.pdate.get(j), ma.group(1));\r
+                                               break;\r
+                                       }\r
+                               }\r
+                       }\r
+               }\r
+       }\r
+       \r
+       /*\r
+        * ★★★★★★★★★★★★★★★★★★★★★★★★★★★★★★★\r
+        * ★★★★★ 放送地域を取得する(TVAreaから降格)-ここから ★★★★★\r
+        * ★★★★★★★★★★★★★★★★★★★★★★★★★★★★★★★\r
+        */\r
+       \r
+       /*\r
+        * 公開メソッド\r
+        */\r
+       \r
+       public String getDefaultArea() {return "全国";}\r
+       public String getDefaultCode() {return "tokyo";}\r
+       \r
+       public void loadAreaCode() {\r
+               aclist = new ArrayList<AreaCode>();\r
+               AreaCode ac = new AreaCode();\r
+               ac.setArea(getDefaultArea());\r
+               ac.setCode(getDefaultCode());\r
+               ac.setSelected(true);\r
+               aclist.add(ac);\r
+       }\r
+       public void saveAreaCode() {}\r
+       \r
+       public String getArea(String code) { return(getDefaultArea()); }\r
+       public String getCode(String area) { return(getDefaultCode()); }\r
+       public String setSelectedAreaByName(String area) { return(getDefaultCode()); }\r
+       public String getSelectedArea() { return(getDefaultArea()); }\r
+       public String getSelectedCode() { return(getDefaultCode()); }\r
+       \r
+       /*\r
+        * ★★★★★★★★★★★★★★★★★★★★★★★★★★★★★★★\r
+        * ★★★★★ 放送地域を取得する(TVAreaから降格)-ここまで ★★★★★\r
+        * ★★★★★★★★★★★★★★★★★★★★★★★★★★★★★★★\r
+        */\r
+       \r
+       \r
+       \r
+       /*\r
+        * ★★★★★★★★★★★★★★★★★★★★★★★★★★★★★★★\r
+        * ★★★★★ 放送局を選択する(TVCenterから降格)-ここから ★★★★★\r
+        * ★★★★★★★★★★★★★★★★★★★★★★★★★★★★★★★\r
+        */\r
+       \r
+       /*\r
+        * 公開メソッド\r
+        */\r
+\r
+       // 設定ファイルがなければWebから取得\r
+       public void loadCenter(String code, boolean force) {\r
+               \r
+               if ( code == null ) {\r
+                       System.out.println(ERRID+"地域コードがnullです.");\r
+                       return;\r
+               }\r
+               \r
+               String centerListFile = getCenterListFile(getTVProgramId(), code);\r
+               \r
+               if (force) {\r
+                       File f = new File(centerListFile);\r
+                       f.delete();\r
+               }\r
+               \r
+               File f = new File(centerListFile);\r
+               if (f.exists() == true) {\r
+                       @SuppressWarnings("unchecked")\r
+                       ArrayList<Center> tmp = (ArrayList<Center>) CommonUtils.readXML(centerListFile);\r
+                       if ( tmp != null ) {\r
+                               \r
+                               crlist = tmp;\r
+                               \r
+                       // 放送局名変換\r
+                       attachChFilters();\r
+                       \r
+                               System.out.println("放送局リストを読み込みました: "+centerListFile);\r
+                               return;\r
+                       }\r
+\r
+                       System.out.println("放送局リストの読み込みに失敗しました: "+centerListFile);\r
+               }\r
+               \r
+               // Web上から放送局の一覧を取得する\r
+               ArrayList<Center> newcrlist = new ArrayList<Center>();\r
+               \r
+               if (getCSType().equals(csCode)) {\r
+                       _loadCenter(newcrlist,code,getCSType(),"http://tv.so-net.ne.jp/chart/cs/skylist.action");\r
+               }\r
+               else {\r
+                       _loadCenter(newcrlist,code,getCSType(),"http://tv.so-net.ne.jp/chart/cs110/e2list.action");\r
+               }\r
+\r
+               if ( newcrlist.size() == 0 ) {\r
+                       System.err.println(ERRID+"放送局情報の取得結果が0件だったため情報を更新しません");\r
+                       return;\r
+               }\r
+\r
+               // ソートする\r
+               ArrayList<Center> tmpcrlist = new ArrayList<Center>();\r
+               for (Center cr : newcrlist) {\r
+                       int idx = 0;\r
+                       for ( Center ncr : tmpcrlist ) {\r
+                               if (ncr.getCenter().compareTo(cr.getCenter()) > 0) {\r
+                                       break;\r
+                               }\r
+                               idx++;\r
+                       }\r
+                       tmpcrlist.add(idx, cr);\r
+               }\r
+               \r
+               crlist = tmpcrlist;\r
+               attachChFilters();      // 放送局名変換\r
+               saveCenter();\r
+       }\r
+       \r
+       protected boolean _loadCenter(ArrayList<Center> newcrlist, String code, String type, String uri) {\r
+               \r
+               String response = webToBuffer(uri,thisEncoding,true);\r
+               if ( response == null ) {\r
+                       System.out.println("放送局情報の取得に失敗しました: "+uri);\r
+                       return false;\r
+               }\r
+               \r
+               reportProgress("放送局情報を取得しました: (1/1) "+uri);\r
+               \r
+               // 局名リストに追加する\r
+               \r
+               Matcher ma = Pattern.compile("<select name=\"id\" id=\"id\" class=\"inputTxt\">(.+?)</select>").matcher(response);\r
+               if (ma.find()) {\r
+                       Matcher mb = Pattern.compile("<option value=\"(.+?)\"( selected)?>(.+?)</option>").matcher(ma.group(1));\r
+                       while (mb.find()) {\r
+                               String centerName = mb.group(3);\r
+                               Center cr = new Center();\r
+                               cr.setAreaCode(code);\r
+                               cr.setCenterOrig(centerName);\r
+                               cr.setLink(mb.group(1));\r
+                               cr.setType(type);\r
+                               cr.setEnabled(true);\r
+                               newcrlist.add(cr);\r
+                       }\r
+               }\r
+               \r
+               return true;\r
+       }\r
+       \r
+       /*\r
+        * ★★★★★★★★★★★★★★★★★★★★★★★★★★★★★★★\r
+        * ★★★★★ 放送局を選択する(TVCenterから降格)-ここまで ★★★★★\r
+        * ★★★★★★★★★★★★★★★★★★★★★★★★★★★★★★★\r
+        */\r
+}\r
diff --git a/TinyBannavi/src/tainavi/PlugIn_CSPtheTelevisionA.java b/TinyBannavi/src/tainavi/PlugIn_CSPtheTelevisionA.java
new file mode 100644 (file)
index 0000000..614fc4a
--- /dev/null
@@ -0,0 +1,167 @@
+package tainavi;\r
+\r
+import java.io.File;\r
+import java.util.ArrayList;\r
+\r
+\r
+public class PlugIn_CSPtheTelevisionA extends PlugIn_TVPtheTelevision implements TVProgram,Cloneable {\r
+\r
+       final String thisEncoding = "UTF-8";\r
+       \r
+       /* 必須コード  - ここから */\r
+       \r
+       // 種族の特性\r
+       @Override\r
+       public String getTVProgramId() { return "webザテレビジョン(CSアナ)"; }\r
+       \r
+       @Override\r
+       public boolean isAreaSelectSupported() { return false; }\r
+       \r
+       @Override\r
+       public ProgType getType() { return ProgType.PROG; }\r
+       @Override\r
+       public ProgSubtype getSubtype() { return ProgSubtype.CS; }\r
+\r
+       //\r
+       public PlugIn_CSPtheTelevisionA clone() {\r
+               return (PlugIn_CSPtheTelevisionA) super.clone();\r
+       }\r
+\r
+       private final String MSGID = "["+getTVProgramId()+"] ";\r
+       private final String ERRID = "[ERROR]"+MSGID;\r
+       private final String DBGID = "[DEBUG]"+MSGID;\r
+       \r
+       //\r
+       public void loadProgram(String areaCode, boolean force) {\r
+               \r
+               // 新しい入れ物(トップ)を用意する\r
+               newplist = new ArrayList<ProgList>();\r
+               \r
+               //\r
+               int counterMax = getSortedCRlist().size();\r
+               int counter=1;\r
+               for ( Center c : getSortedCRlist() ) {\r
+                       _loadProgram(c, force, counter++, counterMax);\r
+               }\r
+               \r
+               // 古い番組データを置き換える\r
+               pcenter = newplist;\r
+       }\r
+       \r
+       \r
+       /*\r
+        * ★★★★★★★★★★★★★★★★★★★★★★★★★★★★★★★\r
+        * ★★★★★ 放送地域を取得する(TVAreaから降格)-ここから ★★★★★\r
+        * ★★★★★★★★★★★★★★★★★★★★★★★★★★★★★★★\r
+        */\r
+       \r
+       /*\r
+        * 公開メソッド\r
+        */\r
+       \r
+       public String getDefaultArea() {return "全国";}\r
+       public String getDefaultCode() {return "tokyo";}\r
+       \r
+       public void loadAreaCode() {\r
+               aclist = new ArrayList<AreaCode>();\r
+               AreaCode ac = new AreaCode();\r
+               ac.setArea(getDefaultArea());\r
+               ac.setCode(getDefaultCode());\r
+               ac.setSelected(true);\r
+               aclist.add(ac);\r
+       }\r
+       public void saveAreaCode() {}\r
+       \r
+       public String getArea(String code) { return(getDefaultArea()); }\r
+       public String getCode(String area) { return(getDefaultCode()); }\r
+       public String setSelectedAreaByName(String area) { return(getDefaultCode()); }\r
+       public String getSelectedArea() { return(getDefaultArea()); }\r
+       public String getSelectedCode() { return(getDefaultCode()); }\r
+       \r
+       /*\r
+        * ★★★★★★★★★★★★★★★★★★★★★★★★★★★★★★★\r
+        * ★★★★★ 放送地域を取得する(TVAreaから降格)-ここまで ★★★★★\r
+        * ★★★★★★★★★★★★★★★★★★★★★★★★★★★★★★★\r
+        */\r
+       \r
+       \r
+       \r
+       /*\r
+        * ★★★★★★★★★★★★★★★★★★★★★★★★★★★★★★★\r
+        * ★★★★★ 放送局を選択する(TVCenterから降格)-ここから ★★★★★\r
+        * ★★★★★★★★★★★★★★★★★★★★★★★★★★★★★★★\r
+        */\r
+       \r
+       /*\r
+        * 公開メソッド\r
+        */\r
+\r
+       // 設定ファイルがなければWebから取得\r
+       public void loadCenter(String code, boolean force) {\r
+               \r
+               if ( code == null ) {\r
+                       System.out.println(ERRID+"地域コードがnullです.");\r
+                       return;\r
+               }\r
+               \r
+               String centerListFile = getCenterListFile(getTVProgramId(), code);\r
+               \r
+               if (force) {\r
+                       File f = new File(centerListFile);\r
+                       f.delete();\r
+               }\r
+               \r
+               File f = new File(centerListFile);\r
+               if (f.exists() == true) {\r
+                       @SuppressWarnings("unchecked")\r
+                       ArrayList<Center> tmp = (ArrayList<Center>) CommonUtils.readXML(centerListFile);\r
+            if ( tmp != null ) {\r
+                       \r
+               crlist = tmp;\r
+                       attachChFilters();      // 放送局名変換\r
+                       \r
+                               System.out.println("放送局リストを読み込みました: "+centerListFile);\r
+                               return;\r
+                       }\r
+            else {\r
+                               System.err.println("放送局リストの読み込みに失敗しました: "+centerListFile);\r
+               }\r
+               }\r
+               \r
+               // Web上から放送局の一覧を取得する\r
+               ArrayList<Center> newcrlist = new ArrayList<Center>();\r
+               \r
+               String url = "http://www.television.co.jp/programlist/guide.php?type=cs&page=1";\r
+               if ( _loadCenter(newcrlist, code,"csa",url) ) {\r
+                       reportProgress("放送局情報を取得しました: (1/1) "+url);\r
+               }\r
+               \r
+               if ( newcrlist.size() == 0 ) {\r
+                       System.err.println(ERRID+"放送局情報の取得結果が0件だったため情報を更新しません");\r
+                       return;\r
+               }\r
+\r
+               // ソートする\r
+               ArrayList<Center> tmpcrlist = new ArrayList<Center>();\r
+               for (Center cr : newcrlist) {\r
+                       int idx = 0;\r
+                       for ( Center ncr : tmpcrlist ) {\r
+                               if (ncr.getCenter().compareTo(cr.getCenter()) > 0) {\r
+                                       break;\r
+                               }\r
+                               idx++;\r
+                       }\r
+                       tmpcrlist.add(idx, cr);\r
+               }\r
+               \r
+               crlist = tmpcrlist;\r
+               attachChFilters();      // 放送局名変換\r
+               saveCenter();\r
+       }\r
+       \r
+       /*\r
+        * ★★★★★★★★★★★★★★★★★★★★★★★★★★★★★★★\r
+        * ★★★★★ 放送局を選択する(TVCenterから降格)-ここまで ★★★★★\r
+        * ★★★★★★★★★★★★★★★★★★★★★★★★★★★★★★★\r
+        */\r
+}\r
diff --git a/TinyBannavi/src/tainavi/PlugIn_CSPtheTelevisionD.java b/TinyBannavi/src/tainavi/PlugIn_CSPtheTelevisionD.java
new file mode 100644 (file)
index 0000000..7a58665
--- /dev/null
@@ -0,0 +1,166 @@
+package tainavi;\r
+\r
+import java.io.File;\r
+import java.util.ArrayList;\r
+\r
+\r
+public class PlugIn_CSPtheTelevisionD extends PlugIn_TVPtheTelevision implements TVProgram,Cloneable {\r
+\r
+       final String thisEncoding = "UTF-8";\r
+       \r
+       /* 必須コード  - ここから */\r
+       \r
+       // 種族の特性\r
+       @Override\r
+       public String getTVProgramId() { return "webザテレビジョン(CSデジ)"; }\r
+       \r
+       @Override\r
+       public boolean isAreaSelectSupported() { return false; }\r
+       \r
+       @Override\r
+       public ProgType getType() { return ProgType.PROG; }\r
+       public ProgSubtype getSubtype() { return ProgSubtype.CS2; }\r
+\r
+       //\r
+       public PlugIn_CSPtheTelevisionD clone() {\r
+               return (PlugIn_CSPtheTelevisionD) super.clone();\r
+       }\r
+\r
+       private final String MSGID = "["+getTVProgramId()+"] ";\r
+       private final String ERRID = "[ERROR]"+MSGID;\r
+       private final String DBGID = "[DEBUG]"+MSGID;\r
+       \r
+       //\r
+       public void loadProgram(String areaCode, boolean force) {\r
+               \r
+               // 新しい入れ物(トップ)を用意する\r
+               newplist = new ArrayList<ProgList>();\r
+\r
+               //\r
+               int counterMax = getSortedCRlist().size();\r
+               int counter=1;\r
+               for ( Center c : getSortedCRlist() ) {\r
+                       _loadProgram(c, force, counter++, counterMax);\r
+               }\r
+               \r
+               // 古い番組データを置き換える\r
+               pcenter = newplist;\r
+       }\r
+       \r
+       \r
+       /*\r
+        * ★★★★★★★★★★★★★★★★★★★★★★★★★★★★★★★\r
+        * ★★★★★ 放送地域を取得する(TVAreaから降格)-ここから ★★★★★\r
+        * ★★★★★★★★★★★★★★★★★★★★★★★★★★★★★★★\r
+        */\r
+       \r
+       /*\r
+        * 公開メソッド\r
+        */\r
+       \r
+       public String getDefaultArea() {return "全国";}\r
+       public String getDefaultCode() {return "tokyo";}\r
+       \r
+       public void loadAreaCode() {\r
+               aclist = new ArrayList<AreaCode>();\r
+               AreaCode ac = new AreaCode();\r
+               ac.setArea(getDefaultArea());\r
+               ac.setCode(getDefaultCode());\r
+               ac.setSelected(true);\r
+               aclist.add(ac);\r
+       }\r
+       public void saveAreaCode() {}\r
+       \r
+       public String getArea(String code) { return(getDefaultArea()); }\r
+       public String getCode(String area) { return(getDefaultCode()); }\r
+       public String setSelectedAreaByName(String area) { return(getDefaultCode()); }\r
+       public String getSelectedArea() { return(getDefaultArea()); }\r
+       public String getSelectedCode() { return(getDefaultCode()); }\r
+       \r
+       /*\r
+        * ★★★★★★★★★★★★★★★★★★★★★★★★★★★★★★★\r
+        * ★★★★★ 放送地域を取得する(TVAreaから降格)-ここまで ★★★★★\r
+        * ★★★★★★★★★★★★★★★★★★★★★★★★★★★★★★★\r
+        */\r
+       \r
+       \r
+       \r
+       /*\r
+        * ★★★★★★★★★★★★★★★★★★★★★★★★★★★★★★★\r
+        * ★★★★★ 放送局を選択する(TVCenterから降格)-ここから ★★★★★\r
+        * ★★★★★★★★★★★★★★★★★★★★★★★★★★★★★★★\r
+        */\r
+       \r
+       /*\r
+        * 公開メソッド\r
+        */\r
+\r
+       // 設定ファイルがなければWebから取得\r
+       public void loadCenter(String code, boolean force) {\r
+               \r
+               if ( code == null ) {\r
+                       System.out.println(ERRID+"地域コードがnullです.");\r
+                       return;\r
+               }\r
+               \r
+               String centerListFile = getCenterListFile(getTVProgramId(), code);\r
+               \r
+               if (force) {\r
+                       File f = new File(centerListFile);\r
+                       f.delete();\r
+               }\r
+               \r
+               File f = new File(centerListFile);\r
+               if (f.exists() == true) {\r
+                       @SuppressWarnings("unchecked")\r
+                       ArrayList<Center> tmp = (ArrayList<Center>) CommonUtils.readXML(centerListFile);\r
+                       if ( tmp != null ) {\r
+                               \r
+                               crlist = tmp;\r
+                               attachChFilters();      // 放送局名変換\r
+                       \r
+                               System.out.println("放送局リストを読み込みました: "+centerListFile);\r
+                               return;\r
+                       }\r
+                       else {\r
+                               System.out.println("放送局リストの読み込みに失敗しました: "+centerListFile);\r
+               }\r
+               }\r
+               \r
+               // Web上から放送局の一覧を取得する\r
+               ArrayList<Center> newcrlist = new ArrayList<Center>();\r
+               \r
+               String url = "http://www.television.co.jp/digitalguide/guide.php?type=cs&page=1";\r
+               if ( _loadCenter(newcrlist, code,"csd",url) ) {\r
+                       reportProgress("放送局情報を取得しました: (1/1) "+url);\r
+               }\r
+               \r
+               if ( newcrlist.size() == 0 ) {\r
+                       System.err.println(ERRID+"放送局情報の取得結果が0件だったため情報を更新しません");\r
+                       return;\r
+               }\r
+\r
+               // ソートする\r
+               ArrayList<Center> tmpcrlist = new ArrayList<Center>();\r
+               for (Center cr : newcrlist) {\r
+                       int idx = 0;\r
+                       for ( Center ncr : tmpcrlist ) {\r
+                               if (ncr.getCenter().compareTo(cr.getCenter()) > 0) {\r
+                                       break;\r
+                               }\r
+                               idx++;\r
+                       }\r
+                       tmpcrlist.add(idx, cr);\r
+               }\r
+               \r
+               crlist = tmpcrlist;\r
+               attachChFilters();      // 放送局名変換\r
+               saveCenter();\r
+       }\r
+       \r
+       /*\r
+        * ★★★★★★★★★★★★★★★★★★★★★★★★★★★★★★★\r
+        * ★★★★★ 放送局を選択する(TVCenterから降格)-ここまで ★★★★★\r
+        * ★★★★★★★★★★★★★★★★★★★★★★★★★★★★★★★\r
+        */\r
+}\r
diff --git a/TinyBannavi/src/tainavi/PlugIn_RecDIGA.java b/TinyBannavi/src/tainavi/PlugIn_RecDIGA.java
new file mode 100644 (file)
index 0000000..255a0b9
--- /dev/null
@@ -0,0 +1,1360 @@
+package tainavi;\r
+\r
+import java.io.File;\r
+import java.io.UnsupportedEncodingException;\r
+import java.net.URLEncoder;\r
+import java.security.MessageDigest;\r
+import java.util.ArrayList;\r
+import java.util.Calendar;\r
+import java.util.Date;\r
+import java.util.GregorianCalendar;\r
+import java.util.Hashtable;\r
+import java.util.Locale;\r
+import java.util.regex.Matcher;\r
+import java.util.regex.Pattern;\r
+\r
+/*\r
+ * \r
+ */\r
+\r
+public class PlugIn_RecDIGA extends HDDRecorderUtils implements HDDRecorder,Cloneable {\r
+\r
+       public PlugIn_RecDIGA clone() {\r
+               return (PlugIn_RecDIGA) super.clone();\r
+       }\r
+       \r
+       /* 必須コード  - ここから */\r
+       \r
+       // 種族の特性\r
+       public String getRecorderId() { return "DIGA DMR-XW120"; }\r
+       public RecType getType() { return RecType.RECORDER; }\r
+\r
+       // 個体の特性\r
+       private ChannelCode cc = new ChannelCode(getRecorderId());\r
+       private String rsvedFile = "";\r
+       private String vrateTFile = "";\r
+       private String chValueTFile = "";\r
+       \r
+       private String errmsg = "";\r
+\r
+       \r
+\r
+       // 公開メソッド\r
+       \r
+       /*\r
+        * \r
+        */\r
+       public ChannelCode getChCode() {\r
+               return cc;\r
+       }\r
+       \r
+       /*\r
+        * \r
+        */\r
+       public boolean ChangeChannel(String Channel) {\r
+               //\r
+               if (Channel == null) {\r
+                       return true;\r
+               }\r
+               \r
+               errmsg = "";\r
+               \r
+               String chcode = cc.getCH_WEB2CODE(Channel);\r
+               if ( chcode == null ) {\r
+                       System.err.println(errmsg = "no such ch: "+Channel);\r
+                       return false;\r
+               }\r
+               Matcher ma = Pattern.compile("^(.+?):(.+?)$").matcher(chcode);\r
+               if (ma.find()) {\r
+                       {\r
+                               System.out.println("change channel.(1/2)");\r
+                               while ( doLogin() == false ) {\r
+                                       System.out.println("Wait for retry ...");\r
+                                       try {\r
+                                               Thread.sleep(5000);\r
+                                       }\r
+                                       catch (Exception e) {\r
+                                               // 例外\r
+                                       }\r
+                               }\r
+                       }\r
+                       {\r
+                               System.out.println("change channel.(2/2)");\r
+                               String url = "http://"+getIPAddr()+":"+getPortNo()+"/cgi-bin/dvdr/dvdr_chtune.cgi";\r
+                               String pstr = "cCHSRC="+ma.group(2)+"&cCHNUM="+ma.group(1)+"&cCMD_SET.x=49&cCMD_SET.y=8";\r
+                               reqPOST(url, pstr, null);\r
+                       }\r
+               }\r
+               \r
+               return true;\r
+       }\r
+\r
+       /*\r
+        * \r
+        */\r
+       @Override\r
+       public void wakeup() {\r
+               poweronoff(true);\r
+       }\r
+       //\r
+       @Override\r
+       public void shutdown() {\r
+               poweronoff(false);\r
+       }\r
+       \r
+       /*\r
+        *      レコーダーから予約一覧を取得する \r
+        */\r
+       public boolean GetRdReserve(boolean force)\r
+       {\r
+               System.out.println("Run: GetRdReserve("+force+")");\r
+               \r
+               //\r
+               rsvedFile = "env/reserved."+getIPAddr()+"_"+getPortNo()+"_"+getRecorderId()+".xml";\r
+               vrateTFile = "env/videorate."+getIPAddr()+"_"+getPortNo()+"_"+getRecorderId()+".xml";\r
+               chValueTFile = "env/chvalue."+getIPAddr()+"_"+getPortNo()+"_"+getRecorderId()+".xml";\r
+\r
+               // チューナー数は自動生成\r
+               {\r
+                       encoder.clear();\r
+                       if ( getTunerNum() >= 2 ) {\r
+                               for ( int i=1; i<=getTunerNum(); i++ ) {\r
+                                       TextValueSet t = new TextValueSet();\r
+                                       t.setText("D"+i);\r
+                                       t.setValue("D"+i);\r
+                                       encoder.add(t);\r
+                               }\r
+                       }\r
+               }\r
+               \r
+               File f = new File(rsvedFile);\r
+               if ( force == false && f.exists()) {\r
+                       \r
+                       // キャッシュから読み出し(録画設定)\r
+                       vrate = TVSload(vrateTFile);\r
+                       if ( vrate.size() > 0 ) {\r
+                               System.out.println("+video rate="+vrateTFile);\r
+                       }\r
+                       \r
+                       // チャンネルコードバリュー\r
+                       chvalue = TVSload(chValueTFile);\r
+                       if ( chvalue.size() > 0 ) {\r
+                               System.out.println("+chvalue="+chValueTFile);\r
+                       }\r
+                       \r
+                       // キャッシュから読み出し(予約一覧)\r
+                       setReserves(ReservesFromFile(rsvedFile));\r
+                       if (getReserves().size() > 0) {\r
+                               System.out.println("+read from="+rsvedFile);\r
+                       }\r
+                       \r
+                       // なぜか設定ファイルが空になっている場合があるので、その際は再取得する\r
+                       if (vrate.size()>0 && chvalue.size()>0 && getReserves().size()>0) {\r
+                               return(true);\r
+                       }\r
+                       \r
+                       getReserves().removeAll(getReserves());\r
+               }\r
+               \r
+               // とりあえず再ログインしてみる\r
+               while ( doLogin() == false ) {\r
+                       reportProgress("Wait for retry ...");\r
+                       try {\r
+                               Thread.sleep(5000);\r
+                       }\r
+                       catch (Exception e) {\r
+                               // 例外\r
+                       }\r
+               }\r
+\r
+               // DIGAから情報取得\r
+               \r
+               // (1)録画設定の取得\r
+               while ( getDigaRecordSetting() < 0 ) {\r
+                       reportProgress("Wait for retry ...");\r
+                       try {\r
+                               Thread.sleep(5000);\r
+                       }\r
+                       catch (Exception e) {\r
+                               // 例外\r
+                       }\r
+               }\r
+               \r
+               // (2)予約一覧の取得・キャッシュへの保存\r
+               getDigaReserveList();\r
+               \r
+               return(true);\r
+       }\r
+       \r
+       /*\r
+        *      予約を実行する\r
+        */\r
+       public boolean PostRdEntry(ReserveList r)\r
+       {\r
+               //\r
+               if (cc.getCH_WEB2CODE(r.getCh_name()) == null) {\r
+                       errmsg = "【警告】Web番組表の放送局名「"+r.getCh_name()+"」をCHコードに変換できません。CHコード設定を修正してください。" ;\r
+                       System.out.println(errmsg);\r
+                       return(false);\r
+               }\r
+               \r
+               //\r
+               System.out.println("Run: PostRdEntry("+r.getTitle()+")");\r
+\r
+               // とりあえず再ログインしてみる\r
+               while ( doLogin() == false ) {\r
+                       reportProgress("Wait for retry ...");\r
+                       try {\r
+                               Thread.sleep(5000);\r
+                       }\r
+                       catch (Exception e) {\r
+                               // 例外\r
+                       }\r
+               }\r
+               \r
+               // POSTデータを変換する\r
+               Hashtable<String, String> pdat = modPostdata(r);\r
+               \r
+               // 事前データを取得する(ログイン後、処理を待たされることがある)\r
+               String header;\r
+               String response;\r
+               while (true) {\r
+                       String[] d = reqPOST("http://"+getIPAddr()+":"+getPortNo()+"/cgi-bin/dvdr/dvdr_ctrl.cgi", "cCMD_RSVADD.x=31&cCMD_RSVADD.y=8", null);\r
+                       header = d[0];\r
+                       response = d[1];\r
+\r
+                       Matcher ma = Pattern.compile("DIGAと通信中です。しばらくお待ちください。").matcher(response);\r
+                       if (! ma.find()) {\r
+                               break;\r
+                       }\r
+                       \r
+                       reportProgress("Wait for retry ...");\r
+                       try {\r
+                               Thread.sleep(5000);\r
+                       }\r
+                       catch (Exception e) {\r
+                               // 例外\r
+                       }\r
+               }\r
+               \r
+               // 予約情報送信\r
+               String[] keys = {"cRPG", "cRHEX", "cTSTR", "cRHEXEX"};\r
+               for ( String c : keys ) {\r
+                       Matcher ma = Pattern.compile("name=\""+c+"\" value=\"(.*?)\"").matcher(response);\r
+                       if ( ma.find() ) {\r
+                               pdat.put(c,ma.group(1));\r
+                       }\r
+               }\r
+               pdat.put("RSV_FIX.x","29");\r
+               pdat.put("RSV_FIX.y","7");\r
+               String pstr = joinPoststr(1, pdat);\r
+               {\r
+                       String[] d = reqPOST("http://"+getIPAddr()+":"+getPortNo()+"/cgi-bin/reserve_add.cgi", pstr, null);\r
+                       header = d[0];\r
+                       response = d[1];\r
+               }\r
+               \r
+               // 予約実行\r
+               Hashtable<String, String> dgdat = new Hashtable<String, String>();\r
+               \r
+               String[] keys2 = { "cRPG", "cRHEX", "cTSTR", "cRHEXEX" };\r
+               for ( String c : keys2 ) {\r
+                       Matcher ma = Pattern.compile("name=\""+c+"\" value=\"(.*?)\"").matcher(response);\r
+                       if ( ma.find() ) {\r
+                               dgdat.put(c,ma.group(1));\r
+                       }\r
+               }\r
+               dgdat.put("RSV_EXEC.x","45");\r
+               dgdat.put("RSV_EXEC.y","5");\r
+               pstr = joinPoststr(2, dgdat);\r
+               {\r
+                       String[] d = reqPOST("http://"+getIPAddr()+":"+getPortNo()+"/cgi-bin/reserve_addq.cgi", pstr, null);\r
+                       header = d[0];\r
+                       response = d[1];\r
+               }\r
+               \r
+               // エラー対応\r
+               Matcher ma = Pattern.compile("name=\"cERR\" value=\"13\"").matcher(response);\r
+               if ( ma.find() ) {\r
+                       errmsg = "★★★ 予約が重複しています。 ★★★";\r
+                       System.out.printf("\n<<< Message from DIGA >>> \"%s\"\n\n", errmsg);\r
+                       // 予約情報送信\r
+                       String[] keys3 = {"cRPG", "cRHEX", "cTSTR", "cRHEXEX"};\r
+                       for ( String c : keys3 ) {\r
+                               Matcher mb = Pattern.compile("name=\""+c+"\" value=\"(.*?)\"").matcher(response);\r
+                               if ( mb.find() ) {\r
+                                       pdat.put(c,mb.group(1));\r
+                               }\r
+                       }\r
+                       pdat.put("Image_BtnRyoukai.x","30");\r
+                       pdat.put("Image_BtnRyoukai.y","13");\r
+                       pstr = joinPoststr(6, pdat);\r
+                       {\r
+                               String[] d = reqPOST("http://"+getIPAddr()+":"+getPortNo()+"/cgi-bin/apl_err.cgi", pstr, null);\r
+                               header = d[0];\r
+                               response = d[1];\r
+                       }\r
+               }\r
+               \r
+               // 登録結果の返信\r
+               ma = Pattern.compile("本体操作中、または現在実行できない操作です。").matcher(response);\r
+               if ( ma.find() ) {\r
+                       errmsg = "本体操作中、または現在実行できない操作です。" ;\r
+                       System.out.printf("\n<<< Message from DIGA >>> \"%s\"\n\n", errmsg);\r
+                       return(false);\r
+               }\r
+               ma = Pattern.compile("予約が設定できませんでした。").matcher(response);\r
+               if ( ma.find() ) {\r
+                       errmsg = "予約が設定できませんでした。" ;\r
+                       System.out.printf("\n<<< Message from DIGA >>> \"%s\"\n\n", errmsg);\r
+                       return(false);\r
+               }\r
+               \r
+               \r
+               \r
+               /*\r
+                * 予約情報の調整 \r
+                */\r
+               \r
+               // 予約リスト番号を取得(キャッシュに存在しない番号が新番号)\r
+               r.setId(getNewId(response));\r
+\r
+               // 予約パターンID\r
+               r.setRec_pattern_id(getRec_pattern_Id(r.getRec_pattern()));\r
+               \r
+               // 次回予定日\r
+               r.setRec_nextdate(CommonUtils.getNextDate(r));\r
+               //r.setRec_nextdate(getNextDate(r.getRec_pattern(), r.getZhh()+":"+r.getZmm()));\r
+               \r
+               // 録画長\r
+               r.setRec_min(CommonUtils.getRecMin(r.getAhh(),r.getAmm(),r.getZhh(),r.getZmm()));\r
+               \r
+               // 開始日時・終了日時\r
+               getStartEndDateTime(r);\r
+               \r
+               // 予約リストを更新\r
+               getReserves().add(r);\r
+               \r
+               // キャッシュに保存\r
+               ReservesToFile(getReserves(), rsvedFile);\r
+               \r
+               System.out.printf("\n<<< Message from DIGA >>> \"%s\"\n\n", "正常に登録できました。");\r
+               return(true);\r
+       }\r
+       \r
+       /*\r
+        *      予約を更新する\r
+        */\r
+       public boolean UpdateRdEntry(ReserveList o, ReserveList r) {\r
+               //\r
+               if (cc.getCH_WEB2CODE(r.getCh_name()) == null) {\r
+                       errmsg = "【警告】Web番組表の放送局名「"+r.getCh_name()+"」をCHコードに変換できません。CHコード設定を修正してください。" ;\r
+                       System.out.println(errmsg);\r
+                       return(false);\r
+               }\r
+               \r
+               System.out.println("Run: UpdateRdEntry()");\r
+\r
+               // とりあえず再ログインしてみる\r
+               while ( doLogin() == false ) {\r
+                       reportProgress("Wait for retry ...");\r
+                       try {\r
+                               Thread.sleep(5000);\r
+                       }\r
+                       catch (Exception e) {\r
+                               // 例外\r
+                       }\r
+               }\r
+               \r
+               // POSTデータを変換する\r
+               Hashtable<String, String> pdat = modPostdata(r);\r
+               \r
+               // 事前データを取得する(ログイン後、処理を待たされることがある)\r
+               String header;\r
+               String response;\r
+               while (true) {\r
+                       String[] d = reqGET("http://"+getIPAddr()+":"+getPortNo()+"/cgi-bin/reserve_list.cgi?ANC_RSVLSTNO="+r.getId()+"&cDMYDT="+DMY_TIME, null);\r
+                       header = d[0];\r
+                       response = d[1];\r
+\r
+                       Matcher ma = Pattern.compile("DIGAと通信中です。しばらくお待ちください。").matcher(response);\r
+                       if (! ma.find()) {\r
+                               break;\r
+                       }\r
+                       \r
+                       reportProgress("Wait for retry ...");\r
+                       try {\r
+                               Thread.sleep(5000);\r
+                       }\r
+                       catch (Exception e) {\r
+                               // 例外\r
+                       }\r
+               }\r
+               \r
+               // 更新情報送信\r
+               String[] keys = { "cRVID","cRVORG","cRVORGEX","cRPG","cRHEX","cTSTR","cRHEXEX" };\r
+               for ( String c : keys ) {\r
+                       Matcher ma = Pattern.compile("name=\""+c+"\" value=\"(.*?)\"").matcher(response);\r
+                       if ( ma.find() ) {\r
+                               pdat.put(c,ma.group(1));\r
+                       }\r
+               }\r
+               pdat.put("RSV_EDIT.x","48");\r
+               pdat.put("RSV_EDIT.y","14");\r
+               String pstr = joinPoststr(5, pdat);\r
+               {\r
+                       String[] d = reqPOST("http://"+getIPAddr()+":"+getPortNo()+"/cgi-bin/reserve_edit.cgi", pstr, null);\r
+                       header = d[0];\r
+                       response = d[1];\r
+               }\r
+\r
+               // 予約実行\r
+               Hashtable<String, String> dgdat = new Hashtable<String, String>();\r
+               \r
+               String[] keys2 = { "cRVID", "cRVORG", "cRVORGEX", "cRPG", "cRHEX", "cTSTR", "cRHEXEX" };\r
+               for ( String c : keys2 ) {\r
+                       Matcher ma = Pattern.compile("name=\""+c+"\" value=\"(.*?)\"").matcher(response);\r
+                       if ( ma.find() ) {\r
+                               dgdat.put(c,ma.group(1));\r
+                       }\r
+               }\r
+               dgdat.put("RSV_EXEC.x","29");\r
+               dgdat.put("RSV_EXEC.y","12");\r
+               pstr = joinPoststr(4, dgdat);\r
+               {\r
+                       String[] d = reqPOST("http://"+getIPAddr()+":"+getPortNo()+"/cgi-bin/reserve_editq.cgi", pstr, null);\r
+                       header = d[0];\r
+                       response = d[1];\r
+               }\r
+\r
+               // エラー対応\r
+               Matcher ma = Pattern.compile("name=\"cERR\" value=\"13\"").matcher(response);\r
+               if ( ma.find() ) {\r
+                       errmsg = "★★★ 予約が重複しています。 ★★★";\r
+                       System.out.printf("\n<<< Message from DIGA >>> \"%s\"\n\n", errmsg);\r
+                       // 予約情報送信\r
+                       String[] keys3 = {"cRPG", "cRHEX", "cTSTR", "cRHEXEX"};\r
+                       for ( String c : keys3 ) {\r
+                               Matcher mb = Pattern.compile("name=\""+c+"\" value=\"(.*?)\"").matcher(response);\r
+                               if ( mb.find() ) {\r
+                                       pdat.put(c,mb.group(1));\r
+                               }\r
+                       }\r
+                       pdat.put("Image_BtnRyoukai.x","30");\r
+                       pdat.put("Image_BtnRyoukai.y","13");\r
+                       pstr = joinPoststr(6, pdat);\r
+                       {\r
+                               String[] d = reqPOST("http://"+getIPAddr()+":"+getPortNo()+"/cgi-bin/apl_err.cgi", pstr, null);\r
+                               header = d[0];\r
+                               response = d[1];\r
+                       }\r
+               }\r
+               \r
+               // 登録結果の返信\r
+               ma = Pattern.compile("本体操作中、または現在実行できない操作です。").matcher(response);\r
+               if ( ma.find() ) {\r
+                       errmsg = "本体操作中、または現在実行できない操作です。" ;\r
+                       System.out.printf("\n<<< Message from DIGA >>> \"%s\"\n\n", errmsg);\r
+                       return(false);\r
+               }\r
+               ma = Pattern.compile("予約が設定できませんでした。").matcher(response);\r
+               if ( ma.find() ) {\r
+                       errmsg = "予約が設定できませんでした。" ;\r
+                       System.out.printf("\n<<< Message from DIGA >>> \"%s\"\n\n", errmsg);\r
+                       return(false);\r
+               }\r
+\r
+               \r
+               \r
+               /*\r
+                * 予約情報の調整 \r
+                */\r
+\r
+               // 予約パターンID\r
+               r.setRec_pattern_id(getRec_pattern_Id(r.getRec_pattern()));\r
+               \r
+               // 次回予定日\r
+               r.setRec_nextdate(CommonUtils.getNextDate(r));\r
+               //r.setRec_nextdate(getNextDate(r.getRec_pattern(), r.getZhh()+":"+r.getZmm()));\r
+               \r
+               // 録画長\r
+               r.setRec_min(CommonUtils.getRecMin(r.getAhh(),r.getAmm(),r.getZhh(),r.getZmm()));\r
+               \r
+               // 開始日時・終了日時\r
+               getStartEndDateTime(r);\r
+               \r
+               // 情報置き換え\r
+               getReserves().remove(o);\r
+               getReserves().add(r);\r
+               \r
+               // キャッシュに保存\r
+               ReservesToFile(getReserves(), rsvedFile);\r
+               \r
+               System.out.printf("\n<<< Message from DIGA >>> \"%s\"\n\n", "正常に更新できました。");\r
+               return(true);\r
+       }\r
+\r
+       /*\r
+        *      予約を削除する\r
+        */\r
+       public ReserveList RemoveRdEntry(String delid) {\r
+               System.out.println("Run: RemoveRdEntry()");\r
+\r
+               // 削除対象を探す\r
+               ReserveList rx = null;\r
+               for (  ReserveList reserve : getReserves() )  {\r
+                       if (reserve.getId().equals(delid)) {\r
+                               rx = reserve;\r
+                               break;\r
+                       }\r
+               }\r
+               if (rx == null) {\r
+                       return(null);\r
+               }\r
+               \r
+               // とりあえず再ログインしてみる\r
+               while ( doLogin() == false ) {\r
+                       reportProgress("Wait for retry ...");\r
+                       try {\r
+                               Thread.sleep(5000);\r
+                       }\r
+                       catch (Exception e) {\r
+                               // 例外\r
+                       }\r
+               }\r
+               \r
+               // POSTデータの入れ物を作る\r
+               Hashtable<String, String> pdat = new Hashtable<String, String>();\r
+               Hashtable<String, String> dgdat = new Hashtable<String, String>();\r
+               \r
+               // 事前データを取得する(ログイン後、処理を待たされることがある)\r
+               String header;\r
+               String response;\r
+               while (true) {\r
+                       String[] d = reqGET("http://"+getIPAddr()+":"+getPortNo()+"/cgi-bin/reserve_list.cgi?ANC_RSVLSTNO="+delid+"&cDMYDT="+DMY_TIME, null);\r
+                       header = d[0];\r
+                       response = d[1];\r
+\r
+                       Matcher ma = Pattern.compile("DIGAと通信中です。しばらくお待ちください。").matcher(response);\r
+                       if (! ma.find()) {\r
+                               break;\r
+                       }\r
+                       \r
+                       reportProgress("Wait for retry ...");\r
+                       try {\r
+                               Thread.sleep(5000);\r
+                       }\r
+                       catch (Exception e) {\r
+                               // 例外\r
+                       }\r
+               }\r
+               \r
+               // 削除情報送信\r
+               Matcher mb = Pattern.compile("option value=\"(.*?)\" selected").matcher(response);\r
+               String[] keys = {"cRVMD","cRVHM1","cRVHM2","cCHSRC","cCHNUM","cRSPD1","cTHEX","cTIMER","cRVID","cRVORG","cRVORGEX","cRPG","cRHEX","cTSTR","cRHEXEX"}; \r
+               for ( String c : keys ) {\r
+                       if ( c.equals("cCHSRC") ) {\r
+                               if (mb.find()) {\r
+                                       pdat.put(c,mb.group(1));\r
+                                       //mb.replaceFirst("");\r
+                               }\r
+                       }\r
+                       else if ( c.equals("cRSPD1")) {\r
+                               if (mb.find()) {\r
+                                       pdat.put(c,mb.group(1));\r
+                                       //mb.replaceFirst("");\r
+                               }\r
+                       }\r
+                       else if ( c.equals("cTIMER")) {\r
+                               if (mb.find()) {\r
+                                       pdat.put(c,mb.group(1));\r
+                                       //mb.replaceFirst("");\r
+                               }\r
+                       }\r
+                       else {\r
+                               Matcher ma = Pattern.compile("name=\""+c+"\" value=\"(.*?)\"").matcher(response);\r
+                               if ( ma.find() ) {\r
+                                       pdat.put(c,ma.group(1));\r
+                               }\r
+                       }\r
+               }\r
+               pdat.put("RSV_DEL.x","33");\r
+               pdat.put("RSV_DEL.y","19");\r
+               String pstr = joinPoststr(3, pdat);\r
+               {\r
+                       String[] d = reqPOST("http://"+getIPAddr()+":"+getPortNo()+"/cgi-bin/reserve_edit.cgi", pstr, null);\r
+                       header = d[0];\r
+                       response = d[1];\r
+               }\r
+               \r
+               // 削除実行\r
+               String keys2[] = {"cRVID", "cRVORG", "cRVORGEX", "cRPG", "cRHEX", "cTSTR", "cRHEXEX"};\r
+               for ( String c : keys2 ) {\r
+                       Matcher ma = Pattern.compile("name=\""+c+"\" value=\"(.*?)\"").matcher(response) ;\r
+                       if ( ma.find() ) {\r
+                               dgdat.put(c, ma.group(1));\r
+                       }\r
+               }\r
+               dgdat.put("RSV_EXEC.x","47");\r
+               dgdat.put("RSV_EXEC.y","6");\r
+               pstr = joinPoststr(4, dgdat);\r
+               {\r
+                       String[] d = reqPOST("http://"+getIPAddr()+":"+getPortNo()+"/cgi-bin/reserve_delq.cgi", pstr, null);\r
+                       header = d[0];\r
+                       response = d[1];\r
+               }\r
+\r
+               // 削除結果の返信\r
+               Matcher ma = Pattern.compile("本体操作中、または現在実行できない操作です。").matcher(response);\r
+               if ( ma.find() ) {\r
+                       System.out.printf("\n<<< Message from DIGA >>> \"%s\"\n\n", "本体操作中、または現在実行できない操作です。");\r
+                       return(null);\r
+               }\r
+               \r
+               // 予約リストを更新\r
+               getReserves().remove(rx);\r
+               \r
+               // キャッシュに保存\r
+               ReservesToFile(getReserves(), rsvedFile);\r
+               \r
+               System.out.printf("\n<<< Message from DIGA >>> \"%s\"\n\n", "正常に削除できました。");\r
+               return(rx);\r
+       }\r
+       \r
+       /*\r
+        * \r
+        */\r
+       public String getErrmsg() {\r
+               return(errmsg.replaceAll("\\\\r\\\\n", ""));\r
+       }\r
+       \r
+       public PlugIn_RecDIGA() {\r
+               super();\r
+               this.setTunerNum(2);\r
+       }\r
+       \r
+       /* ここまで */\r
+\r
+       \r
+       \r
+       \r
+       \r
+       \r
+       \r
+       \r
+       \r
+       /* 個別コード-ここから最後まで */\r
+       \r
+       private String DMY_TIME = "";\r
+       private String NONCE = "";\r
+       private String DIGEST = "";\r
+\r
+       private final String[] DIGA_WDPTNSTR  = { "cSUN", "cMON", "cTUE", "cWED", "cTHU", "cFRI", "cSAT", "cALL" };\r
+       private final String[] DIGA_WDPTNCODE = { "2",    "4",    "8",    "10",   "20",   "40",   "80",   "fe"   };\r
+       private final String[] DIGA_WDAYSTR   = { "日",   "月",   "火",   "水",   "木",   "金",   "土",   "毎日"  };\r
+       \r
+\r
+       /*\r
+        * 非公開メソッド \r
+        */\r
+       public void poweronoff(boolean isWakeup) {\r
+               // とりあえず再ログインしてみる\r
+               while ( doLogin() == false ) {\r
+                       System.out.println("Wait for retry ...");\r
+                       try {\r
+                               Thread.sleep(5000);\r
+                       }\r
+                       catch (Exception e) {\r
+                               // 例外\r
+                       }\r
+               }\r
+               // 電源断かどうか確認する\r
+               String header;\r
+               String response;\r
+               while (true) {\r
+                       String[] d = reqGET("http://"+getIPAddr()+":"+getPortNo()+"/cgi-bin/dispframe.cgi?DISP_PAGE=1001&Radio_Drive=1", null);\r
+                       header = d[0];\r
+                       response = d[1];\r
+\r
+                       Matcher ma = Pattern.compile("DIGAと通信中です。しばらくお待ちください。").matcher(response);\r
+                       if (! ma.find()) {\r
+                               break;\r
+                       }\r
+                       \r
+                       System.out.println("Wait for retry ...");\r
+                       try {\r
+                               Thread.sleep(5000);\r
+                       }\r
+                       catch (Exception e) {\r
+                               // 例外\r
+                       }\r
+               }\r
+               Matcher ma = Pattern.compile("電源切").matcher(response);\r
+               if (isWakeup) {\r
+                       if ( ! ma.find()) {\r
+                               System.out.println("Already wakeup");\r
+                               return;\r
+                       }\r
+               }\r
+               else {\r
+                       if (ma.find()) {\r
+                               System.out.println("Already shutdown");\r
+                               return;\r
+                       }\r
+               }\r
+               // 電源入or切\r
+               {\r
+                       String[] d = reqPOST("http://"+getIPAddr()+":"+getPortNo()+"/cgi-bin/dvdr/dvdr_ctrl.cgi", "cCMD_POWER.x=61&cCMD_POWER.y=9", null);\r
+                       header = d[0];\r
+                       response = d[1];\r
+                       //System.out.println(response);\r
+               }\r
+       }\r
+       \r
+       @Override\r
+       protected String getNewId(String response) {\r
+               Matcher ma = null;\r
+               String newid = null;\r
+               ma = Pattern.compile("ANC_RSVLSTNO=(\\d+)&").matcher(response);\r
+               while (ma.find()) {\r
+                       \r
+                       String idtmp = ma.group(1);\r
+                       \r
+                       boolean flag = true;\r
+                       for (ReserveList rx : getReserves()) {\r
+                               if (rx.getId().equals(idtmp)) {\r
+                                       flag = false;\r
+                                       break;\r
+                               }\r
+                       }\r
+                       if (flag == true) {\r
+                               newid = idtmp;\r
+                               break;\r
+                       }\r
+               }\r
+               return(newid);\r
+       }\r
+\r
+       /*\r
+        *      DIGA MANAGERにログインする\r
+        */\r
+       private boolean doLogin()\r
+       {\r
+               reportProgress("Run: doLogin("+getIPAddr()+":"+getPortNo()+")");\r
+\r
+               // ホームページ\r
+               String header;\r
+               String response;\r
+               {\r
+                       String[] d = reqGET("http://"+getIPAddr()+":"+getPortNo()+"/", null);\r
+                       header = d[0];\r
+                       response = d[1];\r
+               }\r
+               Matcher ma = Pattern.compile("DMY_TIME=(\\d+?)'").matcher(response);\r
+               if (ma.find()) {\r
+                       DMY_TIME = ma.group(1);\r
+                       //System.out.println("DMY_TIME="+DMY_TIME);\r
+               }\r
+\r
+               // ログインフレーム\r
+               {\r
+                       String[] d = reqGET("http://"+getIPAddr()+":"+getPortNo()+"/cgi-bin/prevLogin.cgi?DMY_TIME="+DMY_TIME, null);\r
+                       header = d[0];\r
+                       response = d[1];\r
+               }\r
+               ma = Pattern.compile("nonce=\"(\\d+)\"").matcher(response);\r
+               if (ma.find()) {\r
+                       NONCE = ma.group(1);\r
+                       //System.out.println("nonce="+NONCE);\r
+                       \r
+                       try {\r
+                               MessageDigest md = MessageDigest.getInstance("MD5");\r
+                               byte[] bd = new String(NONCE+getPasswd()).getBytes();\r
+                               md.update(bd);\r
+                               DIGEST = b64.enc(md.digest());\r
+                               DIGEST = DIGEST.replaceAll("\\x2B", "%2B"); // replaces +\r
+                               DIGEST = DIGEST.replaceAll("\\x2F", "%2F"); // replaces /\r
+                               DIGEST = DIGEST.replaceAll("\\x3D", "%3D"); // replaces =\r
+                               //System.out.println("digest="+DIGEST);\r
+                       }\r
+                       catch (Exception e) {\r
+                               // 例外\r
+                               System.out.println("Exception: doLogin()");\r
+                       }\r
+               }\r
+               \r
+               // ログイン実行\r
+               {\r
+                       String[] d = reqPOST("http://"+this.getIPAddr()+":"+this.getPortNo()+"/cgi-bin/loginPsWd.cgi", "passwd=&nonce="+NONCE+"&digest="+DIGEST+"&cmd.x=42&cmd.y=10", null);\r
+                       header = d[0];\r
+                       response = d[1];\r
+               }\r
+               ma = Pattern.compile("onLoadLoginNext").matcher(response);\r
+               if (ma.find()) {\r
+                       return(true);\r
+               }\r
+\r
+               return(false);\r
+       }\r
+\r
+       \r
+       \r
+       /*\r
+        * 内部処理用に情報を整理する\r
+        */\r
+       private void modPostdata_date(Hashtable<String, String> newdat, String rec_pattern, String Ahh) {\r
+               int i = 0;\r
+               for ( ; i < RPTPTN.length && RPTPTN[i].equals(rec_pattern) == false; i++ ) {\r
+                       // 処理はないよ\r
+               }\r
+               \r
+               if ( 0 <= i && i <= 10 ) {\r
+                       if ( i <= 6 ) {\r
+                               // 毎週予約\r
+                               newdat.put(DIGA_WDPTNSTR[i], DIGA_WDPTNCODE[i]);\r
+                       }\r
+                       else if ( 7 <= i && i <= 9 ) {\r
+                               // 帯予約(月~木・金・土)\r
+                               int d = ((CommonUtils.isLateNight(Ahh))?(1):(0));\r
+                               for ( int c=1; c <= i-3; c++ ) {\r
+                                       newdat.put(DIGA_WDPTNSTR[(c+d)%7], DIGA_WDPTNCODE[(c+d)%7]);\r
+                               }\r
+                       }\r
+                       else {  // 毎日\r
+                               newdat.put(DIGA_WDPTNSTR[DIGA_WDPTNSTR.length-1],DIGA_WDPTNCODE[DIGA_WDPTNSTR.length-1]);\r
+                       }\r
+                       \r
+                       newdat.put("cRVMD", "");\r
+                       newdat.put("date", rec_pattern);\r
+               }\r
+               else {\r
+                       Matcher ma = Pattern.compile("^(\\d\\d\\d\\d)/(\\d\\d)/(\\d\\d)").matcher(rec_pattern);\r
+                       if ( ma.find() ) {\r
+                               newdat.put("cRVMD", ma.group(2)+ma.group(3));\r
+                               newdat.put("date", rec_pattern);\r
+                       }\r
+               }\r
+       }\r
+       private Hashtable<String, String> modPostdata(ReserveList r) {\r
+               \r
+               Hashtable<String, String> newdat = new Hashtable<String, String>();\r
+               \r
+               // 録画チャンネル(ここは良く落ちる)\r
+               r.setChannel(cc.getCH_WEB2CODE(r.getCh_name()));\r
+               Matcher ma = Pattern.compile("^20([123])$").matcher(r.getChannel());\r
+               if ( ma.find() ) {\r
+                       newdat.put("cCHSRC","3");               // 外部入力\r
+                       newdat.put("cCHNUM","");\r
+               }\r
+               else {\r
+                       String[] s = r.getChannel().split(":");\r
+                       newdat.put("cCHSRC",s[1]);              // 外部入力\r
+                       newdat.put("cCHNUM",s[0]);\r
+               }\r
+               \r
+               // 録画レート\r
+               newdat.put("cRSPD1", _recstr2mode(r.getRec_mode()));\r
+               \r
+               // 日付\r
+               modPostdata_date( newdat, r.getRec_pattern(), r.getAhh() );\r
+               \r
+               // 時刻\r
+               newdat.put("cRVHM1", String.format("%02d%02d",Integer.valueOf(r.getAhh()), Integer.valueOf(r.getAmm())));\r
+               newdat.put("cRVHM2", String.format("%02d%02d",Integer.valueOf(r.getZhh()), Integer.valueOf(r.getZmm())));\r
+               \r
+               // タイトル\r
+               newdat.put("cTHEX", r.getTitle());\r
+               \r
+               // 予約実行\r
+               newdat.put("cTIMER", (r.getExec())?("1"):("0"));\r
+               \r
+               // 作業用\r
+               newdat.put("channel", r.getChannel());\r
+               newdat.put("date", r.getRec_pattern());\r
+               \r
+               return newdat;\r
+       }\r
+       \r
+       private String[] joinArrays(String[] a1,String[] a2,String[] a3,String[] a4) {\r
+               String[] dim = new String[a1.length+a2.length+a3.length+a4.length];\r
+               int i = 0;\r
+               for ( String s : a1 ) {\r
+                       dim[i++] = s;\r
+               }\r
+               for ( String s : a2 ) {\r
+                       dim[i++] = s;\r
+               }\r
+               for ( String s : a3 ) {\r
+                       dim[i++] = s;\r
+               }\r
+               for ( String s : a4 ) {\r
+                       dim[i++] = s;\r
+               }\r
+               return(dim.clone());\r
+       }\r
+       private String joinPoststr(int mode, Hashtable<String, String> pdat) {\r
+\r
+               String[] pkeys1a = {"cRVMD","cRVHM1","cRVHM2","cCHSRC","cCHNUM","cRSPD1"};\r
+               String[] pkeys1b = {"cTHEX","cTIMER"};\r
+               String[] pkeys1 = joinArrays(pkeys1a, DIGA_WDPTNSTR, pkeys1b, new String[] {});\r
+               String[] pkeys2 = { "cRVID","cRVORG","cRVORGEX" };\r
+               String[] pkeys3 = {     "cRPG","cRHEX","cTSTR","cRHEXEX" };\r
+               String[] pkeys4 = {     "RSV_FIX.x","RSV_FIX.y" };\r
+               String[] pkeys5 = {     "RSV_EXEC.x","RSV_EXEC.y" };\r
+               String[] pkeys6 = { "RSV_DEL.x","RSV_DEL.y" };\r
+               String[] pkeys7 = { "RSV_EDIT.x","RSV_EDIT.y" };\r
+               String[] pkeys8 = {     "cRPG","cERR","TTL_DRIVE","cRVID","cRHEX","cTSTR","cRHEXEX","Image_BtnRyoukai.x","Image_BtnRyoukai.y" };\r
+\r
+               ArrayList<String> pkeys = new ArrayList<String>();\r
+               switch (mode) {\r
+               case 1:\r
+                       String[] keys1 = joinArrays( pkeys1, pkeys3, pkeys4, new String[] {} );\r
+                       for ( String s : keys1 ) {\r
+                               pkeys.add(s);\r
+                       }\r
+                       break;\r
+               case 3:\r
+                       String[] keys3 = joinArrays( pkeys1, pkeys2, pkeys3, pkeys6 );\r
+                       for ( String s : keys3 ) {\r
+                               pkeys.add(s);\r
+                       }\r
+                       break;\r
+               case 4:\r
+                       String[] keys4 = joinArrays( pkeys2, pkeys3, pkeys5, new String[] {} );\r
+                       for ( String s : keys4 ) {\r
+                               pkeys.add(s);\r
+                       }\r
+                       break;\r
+               case 5:\r
+                       String[] keys5 = joinArrays( pkeys1, pkeys2, pkeys3, pkeys7 );\r
+                       for ( String s : keys5 ) {\r
+                               pkeys.add(s);\r
+                       }\r
+                       break;\r
+               case 6:\r
+                       String[] keys6 = joinArrays( pkeys8, new String[] {}, new String[] {}, new String[] {} );\r
+                       for ( String s : keys6 ) {\r
+                               pkeys.add(s);\r
+                       }\r
+                       break;\r
+               default:\r
+                       String[] keys99 = joinArrays( pkeys3, pkeys5, new String[] {}, new String[] {} );\r
+                       for ( String s : keys99 ) {\r
+                               pkeys.add(s);\r
+                       }\r
+                       break;\r
+               }\r
+               \r
+               String pstr = "";\r
+               for ( String key : pkeys ) {\r
+                       for ( String s : DIGA_WDPTNSTR ) {\r
+                               if (pdat.get(key) != null) {\r
+                                       if ( key.equals(s) ) {\r
+                                               if ( pdat.get(key).equals("") ) {\r
+                                                       continue;\r
+                                               }\r
+                                       }\r
+                               }\r
+                       }\r
+                       if (pdat.get(key) != null) {\r
+                               if (key.equals("cTHEX")) {\r
+                                       try {\r
+                                               pdat.put(key, URLEncoder.encode(pdat.get(key),"MS932"));\r
+                                       } catch (UnsupportedEncodingException e) {\r
+                                               // 例外\r
+                                       }\r
+                               }\r
+                               else {\r
+                                       pdat.put(key, pdat.get(key));\r
+                               }\r
+                               Matcher ma = Pattern.compile(" ").matcher(pdat.get(key));\r
+                               pdat.put(key, ma.replaceAll("+"));\r
+                               pstr += key+"="+pdat.get(key)+"&";\r
+                       }\r
+               }\r
+               pstr = pstr.substring(0, pstr.length()-1);\r
+               System.out.println("POST data: "+pstr);\r
+               return(pstr);\r
+       }\r
+       \r
+       /*\r
+        *      録画を取得する \r
+        */\r
+       private int getDigaRecordSetting() {\r
+               System.out.println("run: getDigaRecordSetting");\r
+               \r
+               // 旧リストは全部削除\r
+               vrate.clear();\r
+               chvalue.clear();\r
+               \r
+               // リクエスト発行 \r
+               String[] d = reqPOST("http://"+getIPAddr()+":"+getPortNo()+"/cgi-bin/dvdr/dvdr_ctrl.cgi", "cCMD_RSVADD.x=31&cCMD_RSVADD.y=8", null);\r
+               String header = d[0];\r
+               String response = d[1];\r
+                       \r
+               if (response == null) {\r
+                       // エラーになった場合\r
+                       System.out.println("予約できませんでした");\r
+                       return(-99);\r
+               }\r
+               \r
+               Matcher ma = Pattern.compile("DIGAと通信中です。しばらくお待ちください。").matcher(response);\r
+               if (ma.find()) {\r
+                       // リトライしてね\r
+                       return(-1);\r
+               }\r
+               ma = Pattern.compile("ログインしてからアクセスしてください。").matcher(response);\r
+               if (ma.find()) {\r
+                       // 再ログインしてね\r
+                       return(-2);\r
+               }\r
+               \r
+               ma = Pattern.compile("<select name=\"cRSPD1\">([\\s\\S]+?)</select>").matcher(response);\r
+               if (ma.find()) {\r
+                       Matcher mb = Pattern.compile("<option value=\"(.+?)\".*?>(.+?)\\(HDD\\)</option>").matcher(ma.group(1));\r
+                       while (mb.find()) {\r
+                               TextValueSet t = new TextValueSet();\r
+                               t.setText(mb.group(2));\r
+                               t.setValue(mb.group(1));\r
+                               vrate.add(t);\r
+                       }\r
+               }\r
+               \r
+               // キャッシュファイルに保存\r
+               TVSsave(vrate, vrateTFile);\r
+               \r
+               ma = Pattern.compile("<select name=\"cCHSRC\">([\\s\\S]+?)</select>").matcher(response);\r
+               if (ma.find()) {\r
+                       Matcher mb = Pattern.compile("<option value=\"(.+?)\".*?>(.+?)</option>").matcher(ma.group(1));\r
+                       while (mb.find()) {\r
+                               TextValueSet t = new TextValueSet();\r
+                               t.setText(mb.group(2));\r
+                               t.setValue(mb.group(1));\r
+                               chvalue.add(t);\r
+                       }\r
+               }\r
+               \r
+               // キャッシュファイルに保存\r
+               TVSsave(chvalue, chValueTFile);\r
+               \r
+               return(0);\r
+       }\r
+       \r
+       /*\r
+        *      予約一覧を取得する \r
+        */\r
+       private int getDigaReserveList() {\r
+               System.out.println("run: getDigaReserveList");\r
+\r
+               // 旧リストは全部削除\r
+               ArrayList<ReserveList> newReserveList = new ArrayList<ReserveList>();\r
+               \r
+               // リクエスト発行\r
+               String[] d = reqPOST("http://"+this.getIPAddr()+":"+this.getPortNo()+"/cgi-bin/dvdr/dvdr_ctrl.cgi", "cCMD_RSVLST.x=39&cCMD_RSVLST.y=16", null);\r
+               String header = d[0];\r
+               String response = d[1];\r
+\r
+               if (response == null) {\r
+                       // エラーになった場合\r
+                       System.out.println("予約できませんでした");\r
+                       return(-99);\r
+               }\r
+               \r
+               Matcher ma = Pattern.compile("DIGAと通信中です。しばらくお待ちください。").matcher(response);\r
+               if (ma.find()) {\r
+                       // リトライしてね\r
+                       return(-1);\r
+               }\r
+               ma = Pattern.compile("ログインしてからアクセスしてください。").matcher(response);\r
+               if (ma.find()) {\r
+                       // 再ログインしてね\r
+                       return(-2);\r
+               }\r
+               \r
+               // 予約詳細を作る\r
+               ma = Pattern.compile("<tr class=\\\"s_Bffffff\\\">((\\s|\\S)+?)</tr>").matcher(response);\r
+               while (ma.find()) {\r
+                       \r
+                       ReserveList entry = new ReserveList();\r
+                       \r
+                       // 予約内容の部分を切り出す\r
+                       String reserve = ma.group(1);\r
+\r
+                       // 番号\r
+                       Matcher mb = Pattern.compile("<a href=\\\"(.+?)\\\">").matcher(reserve);\r
+                       if (mb.find()) {\r
+                               String[] ar = mb.group(1).split("\\?",2);\r
+                               Matcher mc = Pattern.compile("ANC_RSVLSTNO=(\\d+)").matcher(ar[1]);\r
+                               if (mc.find()) {\r
+                                       entry.setId(mc.group(1));\r
+                               }\r
+                       }\r
+                       \r
+//意味無いのでごっそり削除\r
+                       \r
+                       // 予約情報を保存\r
+                       newReserveList.add(entry);\r
+               }\r
+               \r
+               // 詳細情報の取得\r
+               System.out.println("========");\r
+               for (int i=0; i<newReserveList.size(); i++) {\r
+                       ReserveList e = newReserveList.get(i);\r
+                       \r
+                       getDigaReserveInfo(e, e.getId());\r
+                       \r
+                       // タイトル自動補完フラグなど本体からは取得できない情報を引き継ぐ\r
+                       copyAttributes(e, getReserves());\r
+\r
+                       reportProgress(String.format("[%s] %s\t%s\t%s\t%s:%s\t%s:%s\t%sm\t%s\t%s\t%s\t%s",\r
+                                       (i+1), e.getId(), e.getRec_pattern(), e.getRec_nextdate(), e.getAhh(), e.getAmm(), e.getZhh(),  e.getZmm(),     e.getRec_min(), e.getRec_mode(), e.getTitle(), e.getChannel(), e.getCh_name()));\r
+               }\r
+               System.out.println("========");\r
+\r
+               // 入れ替え\r
+               setReserves(newReserveList);\r
+               \r
+               // キャッシュファイルに保存\r
+               ReservesToFile(getReserves(), rsvedFile);\r
+               \r
+               return(0);\r
+       }\r
+       /*\r
+        * 予約詳細を取得する\r
+        */\r
+       private ReserveList getDigaReserveInfo(ReserveList reserve_d, String id)\r
+       {\r
+\r
+               String header;\r
+               String response;\r
+               {\r
+                       String[] d = reqPOST("http://"+this.getIPAddr()+":"+this.getPortNo()+"/cgi-bin/reserve_list.cgi", "ANC_RSVLSTNO="+id+"&cDMYDT="+DMY_TIME, null);\r
+                       header = d[0];\r
+                       response = d[1];\r
+               }\r
+               if ( response == null ) {\r
+                       System.out.println("★");\r
+                       System.out.println("★ 予約情報を取得できませんでした。");\r
+                       System.out.println("★");\r
+                       return(null);\r
+               }\r
+               \r
+               // 予約日付\r
+               Matcher ma = Pattern.compile("name=\"cRVMD\" value=\"(\\d\\d)(\\d\\d)\"").matcher(response);\r
+               if (! ma.find()) {\r
+                       return(null);\r
+               }\r
+               String[] da = _mmdd2yyyymmdd(ma.group(1), ma.group(2));\r
+               String yy = da[0];\r
+               String mm = da[1];\r
+               String dd = da[2];\r
+               \r
+               // 開始終了時刻と録画時間\r
+               ma = Pattern.compile("name=\"cRVHM1\" value=\"(\\d\\d)(\\d\\d)\"").matcher(response);\r
+               if (! ma.find()) {\r
+                       return(null);\r
+               }\r
+               String ahh = ma.group(1);\r
+               String amm = ma.group(2);\r
+               ma = Pattern.compile("name=\"cRVHM2\" value=\"(\\d\\d)(\\d\\d)\"").matcher(response);\r
+               if (! ma.find()) {\r
+                       return(null);\r
+               }\r
+               String zhh = ma.group(1);\r
+               String zmm = ma.group(2);\r
+               String[] db = _hhmm2hhmm_min(ahh+":"+amm,zhh+":"+zmm);\r
+               ahh = db[0];\r
+               amm = db[1];\r
+               zhh = db[2];\r
+               zmm = db[3];\r
+               String rec_min = db[4];\r
+               \r
+               // 画質\r
+               String rec_mode = null;\r
+               boolean pursues = false;\r
+               ma = Pattern.compile("<select name=\"cRSPD1\">([\\s\\S]+?)</select>").matcher(response);\r
+               if (ma.find()) {\r
+                       // 手動\r
+                       Matcher mb = Pattern.compile("<option value=\"(.+?)\" selected>").matcher(ma.group(1));\r
+                       if ( ! mb.find()) {\r
+                               return(null);\r
+                       }\r
+                       else {\r
+                                rec_mode = _recmode2str(mb.group(1));\r
+                       }\r
+               }\r
+               else {\r
+                       // 追跡\r
+                       ma = Pattern.compile("<input type=\"hidden\" name=\"cRSPD1\" value=\"(.+?)\">").matcher(response);\r
+                       if (ma.find()) {\r
+                               rec_mode = _recmode2str(ma.group(1));\r
+                               pursues = true;\r
+                       }\r
+                       else {\r
+                               return(null);\r
+                       }\r
+               }\r
+               if (rec_mode == null) {\r
+                       System.err.println("未定義の画質設定が検出されました: "+ma.group(1));\r
+               }\r
+               \r
+               // チャンネル\r
+               String ch_source = "";\r
+               String ch_number = "";\r
+               String channel = "";\r
+               ma = Pattern.compile("<select name=\"cCHSRC\">([\\s\\S]+?)</select>").matcher(response);\r
+               if (ma.find()) {\r
+                       // 手動\r
+                       Matcher mb = Pattern.compile("<option value=\"(.+?)\" selected>").matcher(ma.group(1));\r
+                       Matcher mc = Pattern.compile("name=\"cCHNUM\" value=\"(.+?)\"").matcher(response);\r
+                       if ( ! mb.find() || ! mc.find()) {\r
+                               System.err.println("チャンネル設定がみつかりません");\r
+                               return(null);\r
+                       }\r
+                       else {\r
+                               ch_source = mb.group(1);\r
+                               ch_number = mc.group(1);\r
+                       }\r
+               }\r
+               else {\r
+                       // 自動\r
+                       Matcher mb = Pattern.compile("<input type=\"hidden\" name=\"cCHSRC\" value=\"(.+?)\">").matcher(response);\r
+                       Matcher mc = Pattern.compile("name=\"cCHNUM\" value=\"(.+?)\"").matcher(response);\r
+                       if ( ! mb.find() || ! mc.find()) {\r
+                               System.err.println("チャンネル設定がみつかりません");\r
+                               return(null);\r
+                       }\r
+                       else {\r
+                               ch_source = mb.group(1);\r
+                               ch_number = mc.group(1);\r
+                       }\r
+               }\r
+               for ( TextValueSet t : chvalue ) {\r
+                       if ( t.getValue().equals(ch_source) ) {\r
+                               channel = t.getText()+" "+ch_number;\r
+                               break;\r
+                       }\r
+               }\r
+               String ch_name = cc.getCH_REC2WEB(channel);\r
+               \r
+               // パターン\r
+               ma = Pattern.compile("<td width=\"92\" class=\"s_F16_Cffffff\">毎週</td>([\\s\\S]+?)</tr>").matcher(response);\r
+               if ( ! ma.find()) {\r
+                       return(null);\r
+               }\r
+               String rec_pattern = _recpatternDG2RDstr(ma.group(1), yy, mm, dd, ahh);\r
+               \r
+               // タイトル\r
+               ma = Pattern.compile("name=\"cTHEX\" value=\"([^\"]*?)\"").matcher(response);\r
+               if (! ma.find()) {\r
+                       return(null);\r
+               }\r
+               String title = ma.group(1);\r
+               \r
+               // 予約実行\r
+               boolean exec = true;\r
+               ma = Pattern.compile("<select name=\"cTIMER\">([\\s\\S]+?)</select>").matcher(response);\r
+               if (ma.find()) {\r
+                       Matcher mb = Pattern.compile("<option value=\"(.+?)\" selected>").matcher(ma.group(1));\r
+                       if ( ! mb.find()) {\r
+                               return(null);\r
+                       }\r
+                       else {\r
+                                exec = (mb.group(1).equals("0"))?(false):(true);\r
+                       }\r
+               }\r
+               \r
+               \r
+               // 予約情報更新\r
+               reserve_d.setId(id);\r
+               reserve_d.setRec_pattern(rec_pattern);\r
+               reserve_d.setRec_pattern_id(getRec_pattern_Id(rec_pattern));\r
+               reserve_d.setAhh(ahh);\r
+               reserve_d.setAmm(amm);\r
+               reserve_d.setZhh(zhh);\r
+               reserve_d.setZmm(zmm);\r
+               reserve_d.setRec_min(rec_min);\r
+               reserve_d.setRec_mode(rec_mode);\r
+               reserve_d.setTitle(title);\r
+               reserve_d.setTitlePop(TraceProgram.replacePop(title));\r
+               reserve_d.setChannel(channel);\r
+               reserve_d.setCh_name(ch_name);\r
+               \r
+               reserve_d.setPursues(pursues);\r
+               reserve_d.setExec(exec);\r
+               \r
+               reserve_d.setRec_nextdate(CommonUtils.getNextDate(reserve_d));\r
+               getStartEndDateTime(reserve_d);\r
+               \r
+               return(reserve_d);\r
+       }\r
+       \r
+       //\r
+       private String _recmode2str(String mode)\r
+       {\r
+               for ( TextValueSet t : vrate ) {\r
+                       if (t.getValue().equals(mode)) {\r
+                               return(t.getText());\r
+                       }\r
+               }\r
+               return(null);\r
+       }\r
+       private String _recstr2mode(String str)\r
+       {\r
+               for ( TextValueSet t : vrate ) {\r
+                       if (t.getText().equals(str)) {\r
+                               return(t.getValue());\r
+                       }\r
+               }\r
+               return(null);\r
+       }\r
+       \r
+       //\r
+       private String _recpatternDG2RDstr(String source, String yy, String mm, String dd, String Ahh)\r
+       {\r
+               String rstr = "";\r
+               int rptn = 0;\r
+               for ( int c=0; c < DIGA_WDPTNSTR.length; c++ ) {\r
+                       Matcher ma = Pattern.compile("name=\""+DIGA_WDPTNSTR[c]+"\" value=\"[^\"]+?\" checked").matcher(source);\r
+                       if ( ma.find() ) {\r
+                               rstr = DIGA_WDAYSTR[c];\r
+                               rptn += (int)Math.pow((double)2,(double)c);\r
+                       }\r
+               }\r
+               \r
+               if ( rptn != 0 ) {\r
+                       // 24:00~28:59開始は深夜帯\r
+                       if (CommonUtils.isLateNight(Ahh) && (rptn == 125 || rptn == 124 || rptn == 60)) {\r
+                               rptn = -rptn;\r
+                       }\r
+                       \r
+                       if ( rptn == 126 || rptn == -125 ) {            // 2+4+8+16+32+64\r
+                               return "毎月~土";\r
+                       }\r
+                       else if ( rptn == 62 || rptn == -124 ) {        // 2+4+8+16+32\r
+                               return "毎月~金";\r
+                       }\r
+                       else if ( rptn == 30 || rptn == -60 ) {         // 2+4+8+16\r
+                               return "毎月~木";\r
+                       }\r
+                       else if ( rptn < 128 ) {\r
+                               return "毎"+rstr+"曜日";\r
+                       }\r
+                       else { // if ( rptn & 128 ) {\r
+                               return rstr;\r
+                       }\r
+               }\r
+               else {\r
+                       GregorianCalendar cal = new GregorianCalendar(Locale.JAPAN);\r
+                       cal.setTime(new Date());\r
+                       cal.set(Calendar.SECOND, 0);\r
+                       cal.set(Calendar.MINUTE, 0);\r
+                       cal.set(Calendar.HOUR_OF_DAY, 0);\r
+                       cal.set(Calendar.DATE, Integer.valueOf(dd));\r
+                       cal.set(Calendar.MONTH, Integer.valueOf(mm)-1);\r
+                       cal.set(Calendar.YEAR, Integer.valueOf(yy));\r
+                       return String.format("%04d/%02d/%02d(%s)", cal.get(Calendar.YEAR),cal.get(Calendar.MONTH)+1,cal.get(Calendar.DATE),CommonUtils.WDTPTN[cal.get(Calendar.DAY_OF_WEEK)-1]);\r
+               }\r
+       }\r
+//ごっそり削除\r
+}\r
diff --git a/TinyBannavi/src/tainavi/PlugIn_RecDIGA_DMR_BW200.java b/TinyBannavi/src/tainavi/PlugIn_RecDIGA_DMR_BW200.java
new file mode 100644 (file)
index 0000000..2ba4e48
--- /dev/null
@@ -0,0 +1,1388 @@
+package tainavi;\r
+\r
+import java.io.File;\r
+import java.io.UnsupportedEncodingException;\r
+import java.net.URLEncoder;\r
+import java.security.MessageDigest;\r
+import java.util.ArrayList;\r
+import java.util.Calendar;\r
+import java.util.Date;\r
+import java.util.GregorianCalendar;\r
+import java.util.Hashtable;\r
+import java.util.Locale;\r
+import java.util.regex.Matcher;\r
+import java.util.regex.Pattern;\r
+\r
+/*\r
+ * \r
+ */\r
+\r
+public class PlugIn_RecDIGA_DMR_BW200 extends HDDRecorderUtils implements HDDRecorder,Cloneable {\r
+\r
+       public PlugIn_RecDIGA_DMR_BW200 clone() {\r
+               return (PlugIn_RecDIGA_DMR_BW200) super.clone();\r
+       }\r
+       \r
+       /* 必須コード  - ここから */\r
+       \r
+       // 種族の特性\r
+       public String getRecorderId() { return "DIGA DMR-BW200"; }\r
+       public RecType getType() { return RecType.RECORDER; }\r
+\r
+       // 個体の特性\r
+       private ChannelCode cc = new ChannelCode(getRecorderId());\r
+       private String rsvedFile = "";\r
+       private String vrateTFile = "";\r
+       private String chValueTFile = "";\r
+       \r
+       private String errmsg = "";\r
+\r
+       \r
+\r
+       // 公開メソッド\r
+       \r
+       /*\r
+        * \r
+        */\r
+       public ChannelCode getChCode() {\r
+               return cc;\r
+       }\r
+       \r
+       /*\r
+        * \r
+        */\r
+       public boolean ChangeChannel(String Channel) {\r
+               //\r
+               if (Channel == null) {\r
+                       return true;\r
+               }\r
+               \r
+               errmsg = "";\r
+               \r
+               String chcode = cc.getCH_WEB2CODE(Channel);\r
+               if ( chcode == null ) {\r
+                       System.err.println(errmsg = "no such ch: "+Channel);\r
+                       return false;\r
+               }\r
+               Matcher ma = Pattern.compile("^(.+?):(.+?)$").matcher(chcode);\r
+               if (ma.find()) {\r
+                       {\r
+                               System.out.println("change channel.(1/2)");\r
+                               while ( doLogin() == false ) {\r
+                                       System.out.println("Wait for retry ...");\r
+                                       try {\r
+                                               Thread.sleep(5000);\r
+                                       }\r
+                                       catch (Exception e) {\r
+                                               // 例外\r
+                                       }\r
+                               }\r
+                       }\r
+                       {\r
+                               System.out.println("change channel.(2/2)");\r
+                               String url = "http://"+getIPAddr()+":"+getPortNo()+"/cgi-bin/dvdr/dvdr_chtune.cgi";\r
+                               String pstr = "cCHSRC="+ma.group(2)+"&cCHNUM="+ma.group(1)+"&cCMD_SET.x=49&cCMD_SET.y=8";\r
+                               reqPOST(url, pstr, null);\r
+                       }\r
+               }\r
+               \r
+               return true;\r
+       }\r
+\r
+       /*\r
+        * \r
+        */\r
+       @Override\r
+       public void wakeup() {\r
+               poweronoff(true);\r
+       }\r
+       //\r
+       @Override\r
+       public void shutdown() {\r
+               poweronoff(false);\r
+       }\r
+       \r
+       /*\r
+        *      レコーダーから予約一覧を取得する \r
+        */\r
+       public boolean GetRdReserve(boolean force)\r
+       {\r
+               System.out.println("Run: GetRdReserve("+force+")");\r
+               \r
+               //\r
+               rsvedFile = "env/reserved."+getIPAddr()+"_"+getPortNo()+"_"+getRecorderId()+".xml";\r
+               vrateTFile = "env/videorate."+getIPAddr()+"_"+getPortNo()+"_"+getRecorderId()+".xml";\r
+               chValueTFile = "env/chvalue."+getIPAddr()+"_"+getPortNo()+"_"+getRecorderId()+".xml";\r
+\r
+               // チューナー数は自動生成\r
+               {\r
+                       encoder.clear();\r
+                       if ( getTunerNum() >= 2 ) {\r
+                               for ( int i=1; i<=getTunerNum(); i++ ) {\r
+                                       TextValueSet t = new TextValueSet();\r
+                                       t.setText("D"+i);\r
+                                       t.setValue("D"+i);\r
+                                       encoder.add(t);\r
+                               }\r
+                       }\r
+               }\r
+\r
+               File f = new File(rsvedFile);\r
+               if ( force == false && f.exists()) {\r
+                       \r
+                       // キャッシュから読み出し(録画設定)\r
+                       vrate = TVSload(vrateTFile);\r
+                       if ( vrate.size() > 0 ) {\r
+                               System.out.println("+video rate="+vrateTFile);\r
+                       }\r
+                       \r
+                       // チャンネルコードバリュー\r
+                       chvalue = TVSload(chValueTFile);\r
+                       if ( chvalue.size() > 0 ) {\r
+                               System.out.println("+chvalue="+chValueTFile);\r
+                       }\r
+                       \r
+                       // キャッシュから読み出し(予約一覧)\r
+                       setReserves(ReservesFromFile(rsvedFile));\r
+                       if (getReserves().size() > 0) {\r
+                               System.out.println("+read from="+rsvedFile);\r
+                       }\r
+                       \r
+                       // なぜか設定ファイルが空になっている場合があるので、その際は再取得する\r
+                       if (vrate.size()>0 && chvalue.size()>0 && getReserves().size()>0) {\r
+                               return(true);\r
+                       }\r
+                       \r
+                       getReserves().removeAll(getReserves());\r
+               }\r
+               \r
+               // とりあえず再ログインしてみる\r
+               while ( doLogin() == false ) {\r
+                       reportProgress("Wait for retry ...");\r
+                       try {\r
+                               Thread.sleep(5000);\r
+                       }\r
+                       catch (Exception e) {\r
+                               // 例外\r
+                       }\r
+               }\r
+\r
+               // DIGAから情報取得\r
+               \r
+               // (1)録画設定の取得\r
+               while ( getDigaRecordSetting() < 0 ) {\r
+                       reportProgress("Wait for retry ...");\r
+                       try {\r
+                               Thread.sleep(5000);\r
+                       }\r
+                       catch (Exception e) {\r
+                               // 例外\r
+                       }\r
+               }\r
+               \r
+               // (2)予約一覧の取得・キャッシュへの保存\r
+               getDigaReserveList();\r
+               \r
+               return(true);\r
+       }\r
+       \r
+       /*\r
+        *      予約を実行する\r
+        */\r
+       public boolean PostRdEntry(ReserveList r)\r
+       {\r
+               //\r
+               if (cc.getCH_WEB2CODE(r.getCh_name()) == null) {\r
+                       errmsg = "【警告】Web番組表の放送局名「"+r.getCh_name()+"」をCHコードに変換できません。CHコード設定を修正してください。" ;\r
+                       System.out.println(errmsg);\r
+                       return(false);\r
+               }\r
+               \r
+               //\r
+               System.out.println("Run: PostRdEntry("+r.getTitle()+")");\r
+\r
+               // とりあえず再ログインしてみる\r
+               while ( doLogin() == false ) {\r
+                       reportProgress("Wait for retry ...");\r
+                       try {\r
+                               Thread.sleep(5000);\r
+                       }\r
+                       catch (Exception e) {\r
+                               // 例外\r
+                       }\r
+               }\r
+               \r
+               // POSTデータを変換する\r
+               Hashtable<String, String> pdat = modPostdata(r);\r
+               \r
+               // 事前データを取得する(ログイン後、処理を待たされることがある)\r
+               String header;\r
+               String response;\r
+               while (true) {\r
+                       String[] d = reqPOST("http://"+getIPAddr()+":"+getPortNo()+"/cgi-bin/dvdr/dvdr_ctrl.cgi", "cCMD_RSVADD.x=32&cCMD_RSVADD.y=16", null);\r
+                       header = d[0];\r
+                       response = d[1];\r
+\r
+                       Matcher ma = Pattern.compile("DIGAと通信中です。しばらくお待ちください。").matcher(response);\r
+                       if (! ma.find()) {\r
+                               break;\r
+                       }\r
+                       \r
+                       reportProgress("Wait for retry ...");\r
+                       try {\r
+                               Thread.sleep(5000);\r
+                       }\r
+                       catch (Exception e) {\r
+                               // 例外\r
+                       }\r
+               }\r
+               \r
+               // 予約情報送信\r
+               String[] keys = {"cRPG", "cRHEX", "cTSTR", "cRHEXEX"};\r
+               for ( String c : keys ) {\r
+                       Matcher ma = Pattern.compile("name=\""+c+"\" value=\"(.*?)\"").matcher(response);\r
+                       if ( ma.find() ) {\r
+                               pdat.put(c,ma.group(1));\r
+                       }\r
+               }\r
+               pdat.put("RSV_FIX.x","39");\r
+               pdat.put("RSV_FIX.y","14");\r
+               String pstr = joinPoststr(1, pdat);\r
+               {\r
+                       String[] d = reqPOST("http://"+getIPAddr()+":"+getPortNo()+"/cgi-bin/reserve_add.cgi", pstr, null);\r
+                       header = d[0];\r
+                       response = d[1];\r
+               }\r
+               \r
+               // 予約実行\r
+               Hashtable<String, String> dgdat = new Hashtable<String, String>();\r
+               \r
+               String[] keys2 = { "cRPG", "cRHEX", "cTSTR", "cRHEXEX" };\r
+               for ( String c : keys2 ) {\r
+                       Matcher ma = Pattern.compile("name=\""+c+"\" value=\"(.*?)\"").matcher(response);\r
+                       if ( ma.find() ) {\r
+                               dgdat.put(c,ma.group(1));\r
+                       }\r
+               }\r
+               dgdat.put("RSV_EXEC.x","39");\r
+               dgdat.put("RSV_EXEC.y","12");\r
+               pstr = joinPoststr(2, dgdat);\r
+               {\r
+                       String[] d = reqPOST("http://"+getIPAddr()+":"+getPortNo()+"/cgi-bin/reserve_addq.cgi", pstr, null);\r
+                       header = d[0];\r
+                       response = d[1];\r
+               }\r
+               \r
+               // エラー対応\r
+               Matcher ma = Pattern.compile("name=\"cERR\" value=\"13\"").matcher(response);\r
+               if ( ma.find() ) {\r
+                       errmsg = "★★★ 予約が重複しています。 ★★★";\r
+                       System.out.printf("\n<<< Message from DIGA >>> \"%s\"\n\n", errmsg);\r
+                       // 予約情報送信\r
+                       String[] keys3 = {"cRPG", "cRHEX", "cTSTR", "cRHEXEX"};\r
+                       for ( String c : keys3 ) {\r
+                               Matcher mb = Pattern.compile("name=\""+c+"\" value=\"(.*?)\"").matcher(response);\r
+                               if ( mb.find() ) {\r
+                                       pdat.put(c,mb.group(1));\r
+                               }\r
+                       }\r
+                       pdat.put("Image_BtnRyoukai.x","30");\r
+                       pdat.put("Image_BtnRyoukai.y","13");\r
+                       pstr = joinPoststr(6, pdat);\r
+                       {\r
+                               String[] d = reqPOST("http://"+getIPAddr()+":"+getPortNo()+"/cgi-bin/apl_err.cgi", pstr, null);\r
+                               header = d[0];\r
+                               response = d[1];\r
+                       }\r
+               }\r
+               \r
+               // 登録結果の返信\r
+               ma = Pattern.compile("本体操作中、または現在実行できない操作です。").matcher(response);\r
+               if ( ma.find() ) {\r
+                       errmsg = "本体操作中、または現在実行できない操作です。" ;\r
+                       System.out.printf("\n<<< Message from DIGA >>> \"%s\"\n\n", errmsg);\r
+                       return(false);\r
+               }\r
+               ma = Pattern.compile("予約が設定できませんでした。").matcher(response);\r
+               if ( ma.find() ) {\r
+                       errmsg = "予約が設定できませんでした。" ;\r
+                       System.out.printf("\n<<< Message from DIGA >>> \"%s\"\n\n", errmsg);\r
+                       return(false);\r
+               }\r
+               \r
+               \r
+               \r
+               /*\r
+                * 予約情報の調整 \r
+                */\r
+               \r
+               // 予約リスト番号を取得(キャッシュに存在しない番号が新番号)\r
+               r.setId(getNewId(response));\r
+\r
+               // 予約パターンID\r
+               r.setRec_pattern_id(getRec_pattern_Id(r.getRec_pattern()));\r
+               \r
+               // 次回予定日\r
+               r.setRec_nextdate(CommonUtils.getNextDate(r));\r
+               //r.setRec_nextdate(getNextDate(r.getRec_pattern(), r.getZhh()+":"+r.getZmm()));\r
+               \r
+               // 録画長\r
+               r.setRec_min(CommonUtils.getRecMin(r.getAhh(),r.getAmm(),r.getZhh(),r.getZmm()));\r
+               \r
+               // 開始日時・終了日時\r
+               getStartEndDateTime(r);\r
+               \r
+               // 予約リストを更新\r
+               getReserves().add(r);\r
+               \r
+               // キャッシュに保存\r
+               ReservesToFile(getReserves(), rsvedFile);\r
+               \r
+               System.out.printf("\n<<< Message from DIGA >>> \"%s\"\n\n", "正常に登録できました。");\r
+               return(true);\r
+       }\r
+       \r
+       /*\r
+        *      予約を更新する\r
+        */\r
+       public boolean UpdateRdEntry(ReserveList o, ReserveList r) {\r
+               //\r
+               if (cc.getCH_WEB2CODE(r.getCh_name()) == null) {\r
+                       errmsg = "【警告】Web番組表の放送局名「"+r.getCh_name()+"」をCHコードに変換できません。CHコード設定を修正してください。" ;\r
+                       System.out.println(errmsg);\r
+                       return(false);\r
+               }\r
+               \r
+               System.out.println("Run: UpdateRdEntry()");\r
+\r
+               // とりあえず再ログインしてみる\r
+               while ( doLogin() == false ) {\r
+                       reportProgress("Wait for retry ...");\r
+                       try {\r
+                               Thread.sleep(5000);\r
+                       }\r
+                       catch (Exception e) {\r
+                               // 例外\r
+                       }\r
+               }\r
+               \r
+               // POSTデータを変換する\r
+               Hashtable<String, String> pdat = modPostdata(r);\r
+               \r
+               // 事前データを取得する(ログイン後、処理を待たされることがある)\r
+               String header;\r
+               String response;\r
+               while (true) {\r
+                       String[] d = reqGET("http://"+getIPAddr()+":"+getPortNo()+"/cgi-bin/reserve_list.cgi?ANC_RSVLSTNO="+r.getId()+"&cDMYDT="+DMY_TIME, null);\r
+                       header = d[0];\r
+                       response = d[1];\r
+\r
+                       Matcher ma = Pattern.compile("DIGAと通信中です。しばらくお待ちください。").matcher(response);\r
+                       if (! ma.find()) {\r
+                               break;\r
+                       }\r
+                       \r
+                       reportProgress("Wait for retry ...");\r
+                       try {\r
+                               Thread.sleep(5000);\r
+                       }\r
+                       catch (Exception e) {\r
+                               // 例外\r
+                       }\r
+               }\r
+               \r
+               // 更新情報送信\r
+               String[] keys = { "cRVID","cRVORG","cRVORGEX","cRPG","cRHEX","cTSTR","cRHEXEX" };\r
+               for ( String c : keys ) {\r
+                       Matcher ma = Pattern.compile("name=\""+c+"\" value=\"(.*?)\"").matcher(response);\r
+                       if ( ma.find() ) {\r
+                               pdat.put(c,ma.group(1));\r
+                       }\r
+               }\r
+               pdat.put("RSV_EDIT.x","48");\r
+               pdat.put("RSV_EDIT.y","14");\r
+               String pstr = joinPoststr(5, pdat);\r
+               {\r
+                       String[] d = reqPOST("http://"+getIPAddr()+":"+getPortNo()+"/cgi-bin/reserve_edit.cgi", pstr, null);\r
+                       header = d[0];\r
+                       response = d[1];\r
+               }\r
+\r
+               // 予約実行\r
+               Hashtable<String, String> dgdat = new Hashtable<String, String>();\r
+               \r
+               String[] keys2 = { "cRVID", "cRVORG", "cRVORGEX", "cRPG", "cRHEX", "cTSTR", "cRHEXEX" };\r
+               for ( String c : keys2 ) {\r
+                       Matcher ma = Pattern.compile("name=\""+c+"\" value=\"(.*?)\"").matcher(response);\r
+                       if ( ma.find() ) {\r
+                               dgdat.put(c,ma.group(1));\r
+                       }\r
+               }\r
+               dgdat.put("RSV_EXEC.x","29");\r
+               dgdat.put("RSV_EXEC.y","12");\r
+               pstr = joinPoststr(4, dgdat);\r
+               {\r
+                       String[] d = reqPOST("http://"+getIPAddr()+":"+getPortNo()+"/cgi-bin/reserve_editq.cgi", pstr, null);\r
+                       header = d[0];\r
+                       response = d[1];\r
+               }\r
+\r
+               // エラー対応\r
+               Matcher ma = Pattern.compile("name=\"cERR\" value=\"13\"").matcher(response);\r
+               if ( ma.find() ) {\r
+                       errmsg = "★★★ 予約が重複しています。 ★★★";\r
+                       System.out.printf("\n<<< Message from DIGA >>> \"%s\"\n\n", errmsg);\r
+                       // 予約情報送信\r
+                       String[] keys3 = {"cRPG", "cRHEX", "cTSTR", "cRHEXEX"};\r
+                       for ( String c : keys3 ) {\r
+                               Matcher mb = Pattern.compile("name=\""+c+"\" value=\"(.*?)\"").matcher(response);\r
+                               if ( mb.find() ) {\r
+                                       pdat.put(c,mb.group(1));\r
+                               }\r
+                       }\r
+                       pdat.put("Image_BtnRyoukai.x","30");\r
+                       pdat.put("Image_BtnRyoukai.y","13");\r
+                       pstr = joinPoststr(6, pdat);\r
+                       {\r
+                               String[] d = reqPOST("http://"+getIPAddr()+":"+getPortNo()+"/cgi-bin/apl_err.cgi", pstr, null);\r
+                               header = d[0];\r
+                               response = d[1];\r
+                       }\r
+               }\r
+               \r
+               // 登録結果の返信\r
+               ma = Pattern.compile("本体操作中、または現在実行できない操作です。").matcher(response);\r
+               if ( ma.find() ) {\r
+                       errmsg = "本体操作中、または現在実行できない操作です。" ;\r
+                       System.out.printf("\n<<< Message from DIGA >>> \"%s\"\n\n", errmsg);\r
+                       return(false);\r
+               }\r
+               ma = Pattern.compile("予約が設定できませんでした。").matcher(response);\r
+               if ( ma.find() ) {\r
+                       errmsg = "予約が設定できませんでした。" ;\r
+                       System.out.printf("\n<<< Message from DIGA >>> \"%s\"\n\n", errmsg);\r
+                       return(false);\r
+               }\r
+\r
+               \r
+               \r
+               /*\r
+                * 予約情報の調整 \r
+                */\r
+\r
+               // 予約パターンID\r
+               r.setRec_pattern_id(getRec_pattern_Id(r.getRec_pattern()));\r
+               \r
+               // 次回予定日\r
+               r.setRec_nextdate(CommonUtils.getNextDate(r));\r
+               //r.setRec_nextdate(getNextDate(r.getRec_pattern(), r.getZhh()+":"+r.getZmm()));\r
+               \r
+               // 録画長\r
+               r.setRec_min(CommonUtils.getRecMin(r.getAhh(),r.getAmm(),r.getZhh(),r.getZmm()));\r
+               \r
+               // 開始日時・終了日時\r
+               getStartEndDateTime(r);\r
+               \r
+               // 情報置き換え\r
+               getReserves().remove(o);\r
+               getReserves().add(r);\r
+               \r
+               // キャッシュに保存\r
+               ReservesToFile(getReserves(), rsvedFile);\r
+               \r
+               System.out.printf("\n<<< Message from DIGA >>> \"%s\"\n\n", "正常に更新できました。");\r
+               return(true);\r
+       }\r
+\r
+       /*\r
+        *      予約を削除する\r
+        */\r
+       public ReserveList RemoveRdEntry(String delid) {\r
+               System.out.println("Run: RemoveRdEntry()");\r
+\r
+               // 削除対象を探す\r
+               ReserveList rx = null;\r
+               for (  ReserveList reserve : getReserves() )  {\r
+                       if (reserve.getId().equals(delid)) {\r
+                               rx = reserve;\r
+                               break;\r
+                       }\r
+               }\r
+               if (rx == null) {\r
+                       return(null);\r
+               }\r
+               \r
+               // とりあえず再ログインしてみる\r
+               while ( doLogin() == false ) {\r
+                       reportProgress("Wait for retry ...");\r
+                       try {\r
+                               Thread.sleep(5000);\r
+                       }\r
+                       catch (Exception e) {\r
+                               // 例外\r
+                       }\r
+               }\r
+               \r
+               // POSTデータの入れ物を作る\r
+               Hashtable<String, String> pdat = new Hashtable<String, String>();\r
+               Hashtable<String, String> dgdat = new Hashtable<String, String>();\r
+               \r
+               // 事前データを取得する(ログイン後、処理を待たされることがある)\r
+               String header;\r
+               String response;\r
+               while (true) {\r
+                       String[] d = reqGET("http://"+getIPAddr()+":"+getPortNo()+"/cgi-bin/reserve_list.cgi?ANC_RSVLSTNO="+delid+"&cDMYDT="+DMY_TIME, null);\r
+                       header = d[0];\r
+                       response = d[1];\r
+\r
+                       Matcher ma = Pattern.compile("DIGAと通信中です。しばらくお待ちください。").matcher(response);\r
+                       if (! ma.find()) {\r
+                               break;\r
+                       }\r
+                       \r
+                       reportProgress("Wait for retry ...");\r
+                       try {\r
+                               Thread.sleep(5000);\r
+                       }\r
+                       catch (Exception e) {\r
+                               // 例外\r
+                       }\r
+               }\r
+               \r
+               // 削除情報送信\r
+               Matcher mb = Pattern.compile("option value=\"(.*?)\" selected").matcher(response);\r
+               String[] keys = {"cRVMD","cWEKV","cRVHM1","cRVHM2","cCHSRC","cCHNUM","cRSPD1","cTHEX","cTIMER","cRVID","cRVORG","cRVORGEX","cRPG","cRHEX","cTSTR","cRHEXEX"}; \r
+               for ( String c : keys ) {\r
+                       if ( c.equals("cWEKV") ) {\r
+                               if (mb.find()) {\r
+                                       pdat.put(c,mb.group(1));\r
+                                       //mb.replaceFirst("");\r
+                               }\r
+                       }\r
+                       else if ( c.equals("cCHSRC") ) {\r
+                               if (mb.find()) {\r
+                                       pdat.put(c,mb.group(1));\r
+                                       //mb.replaceFirst("");\r
+                               }\r
+                       }\r
+                       else if ( c.equals("cRSPD1")) {\r
+                               if (mb.find()) {\r
+                                       pdat.put(c,mb.group(1));\r
+                                       //mb.replaceFirst("");\r
+                               }\r
+                       }\r
+                       else if ( c.equals("cTIMER")) {\r
+                               if (mb.find()) {\r
+                                       pdat.put(c,mb.group(1));\r
+                                       //mb.replaceFirst("");\r
+                               }\r
+                       }\r
+                       else {\r
+                               Matcher ma = Pattern.compile("name=\""+c+"\" value=\"(.*?)\"").matcher(response);\r
+                               if ( ma.find() ) {\r
+                                       pdat.put(c,ma.group(1));\r
+                               }\r
+                       }\r
+               }\r
+               pdat.put("RSV_DEL.x","64");\r
+               pdat.put("RSV_DEL.y","5");\r
+               String pstr = joinPoststr(3, pdat);\r
+               {\r
+                       String[] d = reqPOST("http://"+getIPAddr()+":"+getPortNo()+"/cgi-bin/reserve_edit.cgi", pstr, null);\r
+                       header = d[0];\r
+                       response = d[1];\r
+               }\r
+               \r
+               // 削除実行\r
+               String keys2[] = {"cRVID", "cRVORG", "cRVORGEX", "cRPG", "cRHEX", "cTSTR", "cRHEXEX"};\r
+               for ( String c : keys2 ) {\r
+                       Matcher ma = Pattern.compile("name=\""+c+"\" value=\"(.*?)\"").matcher(response) ;\r
+                       if ( ma.find() ) {\r
+                               dgdat.put(c, ma.group(1));\r
+                       }\r
+               }\r
+               dgdat.put("RSV_EXEC.x","33");\r
+               dgdat.put("RSV_EXEC.y","9");\r
+               pstr = joinPoststr(4, dgdat);\r
+               {\r
+                       String[] d = reqPOST("http://"+getIPAddr()+":"+getPortNo()+"/cgi-bin/reserve_delq.cgi", pstr, null);\r
+                       header = d[0];\r
+                       response = d[1];\r
+               }\r
+\r
+               // 削除結果の返信\r
+               Matcher ma = Pattern.compile("本体操作中、または現在実行できない操作です。").matcher(response);\r
+               if ( ma.find() ) {\r
+                       System.out.printf("\n<<< Message from DIGA >>> \"%s\"\n\n", "本体操作中、または現在実行できない操作です。");\r
+                       errmsg = "本体操作中、または現在実行できない操作です。";\r
+                       return(null);\r
+               }\r
+               \r
+               // 予約リストを更新\r
+               getReserves().remove(rx);\r
+               \r
+               // キャッシュに保存\r
+               ReservesToFile(getReserves(), rsvedFile);\r
+               \r
+               System.out.printf("\n<<< Message from DIGA >>> \"%s\"\n\n", "正常に削除できました。");\r
+               return(rx);\r
+       }\r
+       \r
+       /*\r
+        * \r
+        */\r
+       public String getErrmsg() {\r
+               return(errmsg.replaceAll("\\\\r\\\\n", ""));\r
+       }\r
+       \r
+       public PlugIn_RecDIGA_DMR_BW200() {\r
+               super();\r
+               this.setTunerNum(2);\r
+       }\r
+       \r
+       /* ここまで */\r
+\r
+       \r
+       \r
+       \r
+       \r
+       \r
+       \r
+       \r
+       \r
+       /* 個別コード-ここから最後まで */\r
+       \r
+       private String DMY_TIME = "";\r
+       private String NONCE = "";\r
+       private String DIGEST = "";\r
+\r
+\r
+       \r
+       /*\r
+        * 非公開メソッド \r
+        */\r
+       public void poweronoff(boolean isWakeup) {\r
+               // とりあえず再ログインしてみる\r
+               while ( doLogin() == false ) {\r
+                       System.out.println("Wait for retry ...");\r
+                       try {\r
+                               Thread.sleep(5000);\r
+                       }\r
+                       catch (Exception e) {\r
+                               // 例外\r
+                       }\r
+               }\r
+               // 電源断かどうか確認する\r
+               String header;\r
+               String response;\r
+               while (true) {\r
+                       String[] d = reqGET("http://"+getIPAddr()+":"+getPortNo()+"/cgi-bin/dispframe.cgi?DISP_PAGE=1001&Radio_Drive=1", null);\r
+                       header = d[0];\r
+                       response = d[1];\r
+\r
+                       Matcher ma = Pattern.compile("DIGAと通信中です。しばらくお待ちください。").matcher(response);\r
+                       if (! ma.find()) {\r
+                               break;\r
+                       }\r
+                       \r
+                       System.out.println("Wait for retry ...");\r
+                       try {\r
+                               Thread.sleep(5000);\r
+                       }\r
+                       catch (Exception e) {\r
+                               // 例外\r
+                       }\r
+               }\r
+               Matcher ma = Pattern.compile("電源切").matcher(response);\r
+               if (isWakeup) {\r
+                       if ( ! ma.find()) {\r
+                               System.out.println("Already wakeup");\r
+                               return;\r
+                       }\r
+               }\r
+               else {\r
+                       if (ma.find()) {\r
+                               System.out.println("Already shutdown");\r
+                               return;\r
+                       }\r
+               }\r
+               // 電源入or切\r
+               {\r
+                       String[] d = reqPOST("http://"+getIPAddr()+":"+getPortNo()+"/cgi-bin/dvdr/dvdr_ctrl.cgi", "cCMD_POWER.x=61&cCMD_POWER.y=9", null);\r
+                       header = d[0];\r
+                       response = d[1];\r
+                       //System.out.println(response);\r
+               }\r
+       }\r
+       \r
+       @Override\r
+       protected String getNewId(String response) {\r
+               Matcher ma = null;\r
+               String newid = null;\r
+               ma = Pattern.compile("ANC_RSVLSTNO=(\\d+)&").matcher(response);\r
+               while (ma.find()) {\r
+                       \r
+                       String idtmp = ma.group(1);\r
+                       \r
+                       boolean flag = true;\r
+                       for (ReserveList rx : getReserves()) {\r
+                               if (rx.getId().equals(idtmp)) {\r
+                                       flag = false;\r
+                                       break;\r
+                               }\r
+                       }\r
+                       if (flag == true) {\r
+                               newid = idtmp;\r
+                               break;\r
+                       }\r
+               }\r
+               return(newid);\r
+       }\r
+\r
+       /*\r
+        *      DIGA MANAGERにログインする\r
+        */\r
+       private boolean doLogin()\r
+       {\r
+               reportProgress("Run: doLogin("+getIPAddr()+":"+getPortNo()+")");\r
+\r
+               // ホームページ\r
+               String header;\r
+               String response;\r
+               {\r
+                       String[] d = reqGET("http://"+getIPAddr()+":"+getPortNo()+"/", null);\r
+                       header = d[0];\r
+                       response = d[1];\r
+               }\r
+               Matcher ma = Pattern.compile("DMY_TIME=(\\d+?)'").matcher(response);\r
+               if (ma.find()) {\r
+                       DMY_TIME = ma.group(1);\r
+                       //System.out.println("DMY_TIME="+DMY_TIME);\r
+               }\r
+\r
+               // ログインフレーム\r
+               {\r
+                       String[] d = reqGET("http://"+getIPAddr()+":"+getPortNo()+"/cgi-bin/prevLogin.cgi?DMY_TIME="+DMY_TIME, null);\r
+                       header = d[0];\r
+                       response = d[1];\r
+               }\r
+               ma = Pattern.compile("nonce=\"(\\d+)\"").matcher(response);\r
+               if (ma.find()) {\r
+                       NONCE = ma.group(1);\r
+                       //System.out.println("nonce="+NONCE);\r
+                       \r
+                       try {\r
+                               MessageDigest md = MessageDigest.getInstance("MD5");\r
+                               byte[] bd = new String(NONCE+getPasswd()).getBytes();\r
+                               md.update(bd);\r
+                               DIGEST = b64.enc(md.digest());\r
+                               DIGEST = DIGEST.replaceAll("\\x2B", "%2B"); // replaces +\r
+                               DIGEST = DIGEST.replaceAll("\\x2F", "%2F"); // replaces /\r
+                               DIGEST = DIGEST.replaceAll("\\x3D", "%3D"); // replaces =\r
+                               //System.out.println("digest="+DIGEST);\r
+                       }\r
+                       catch (Exception e) {\r
+                               // 例外\r
+                               System.out.println("Exception: doLogin()");\r
+                       }\r
+               }\r
+               \r
+               // ログイン実行\r
+               {\r
+                       String[] d = reqPOST("http://"+this.getIPAddr()+":"+this.getPortNo()+"/cgi-bin/loginPsWd.cgi", "passwd=&nonce="+NONCE+"&digest="+DIGEST+"&cmd.x=19&cmd.y=10", null);\r
+                       header = d[0];\r
+                       response = d[1];\r
+               }\r
+               ma = Pattern.compile("onLoadLoginNext").matcher(response);\r
+               if (ma.find()) {\r
+                       return(true);\r
+               }\r
+\r
+               return(false);\r
+       }\r
+\r
+       \r
+       \r
+       /*\r
+        * 内部処理用に情報を整理する\r
+        */\r
+       \r
+       public static final String[] DIGA_WDPTNCODE = {"2", "4", "8", "10", "20", "40", "80", "7c", "7c", "fc", "fe" };\r
+       \r
+       private void modPostdata_date(Hashtable<String, String> newdat, String rec_pattern) {\r
+               int i = 0;\r
+               for ( ; i < RPTPTN.length && RPTPTN[i].equals(rec_pattern) == false; i++ ) {\r
+                       // 処理はないよ\r
+               }\r
+               System.out.println(rec_pattern+"("+i+")");\r
+               if ( i < RPTPTN.length ) {\r
+                       newdat.put("cWEKV", DIGA_WDPTNCODE[i]);\r
+                       newdat.put("cRVMD", "");\r
+                       newdat.put("date", rec_pattern);\r
+               }\r
+               else {\r
+                       Matcher ma = Pattern.compile("^(\\d\\d\\d\\d)/(\\d\\d)/(\\d\\d)").matcher(rec_pattern);\r
+                       if ( ma.find() ) {\r
+                               newdat.put("cWEKV", "0");\r
+                               newdat.put("cRVMD", ma.group(2)+ma.group(3));\r
+                               newdat.put("date", rec_pattern);\r
+                       }\r
+               }\r
+       }\r
+       private Hashtable<String, String> modPostdata(ReserveList r) {\r
+               \r
+               Hashtable<String, String> newdat = new Hashtable<String, String>();\r
+               \r
+               // 録画チャンネル(ここは良く落ちる)\r
+               r.setChannel(cc.getCH_WEB2CODE(r.getCh_name()));\r
+               Matcher ma = Pattern.compile("^20([123])$").matcher(r.getChannel());\r
+               if ( ma.find() ) {\r
+                       newdat.put("cCHSRC","3");               // 外部入力\r
+                       newdat.put("cCHNUM","");\r
+               }\r
+               else {\r
+                       String[] s = r.getChannel().split(":");\r
+                       newdat.put("cCHSRC",s[1]);\r
+                       newdat.put("cCHNUM",s[0]);\r
+               }\r
+               \r
+               // 録画レート\r
+               newdat.put("cRSPD1", _recstr2mode(r.getRec_mode()));\r
+               \r
+               // 日付\r
+               modPostdata_date( newdat, r.getRec_pattern() );\r
+               \r
+               // 時刻\r
+               newdat.put("cRVHM1", String.format("%02d%02d",Integer.valueOf(r.getAhh()), Integer.valueOf(r.getAmm())));\r
+               newdat.put("cRVHM2", String.format("%02d%02d",Integer.valueOf(r.getZhh()), Integer.valueOf(r.getZmm())));\r
+               \r
+               // タイトル\r
+               newdat.put("cTHEX", r.getTitle());\r
+               \r
+               // タイマー???なんだろうこれは…思い出せない\r
+               newdat.put("cTIMER", "1");\r
+               \r
+               // 作業用\r
+               newdat.put("channel", r.getChannel());\r
+               newdat.put("date", r.getRec_pattern());\r
+               \r
+               return newdat;\r
+       }\r
+       \r
+       private String[] joinArrays(String[] a1,String[] a2,String[] a3,String[] a4) {\r
+               String[] dim = new String[a1.length+a2.length+a3.length+a4.length];\r
+               int i = 0;\r
+               for ( String s : a1 ) {\r
+                       dim[i++] = s;\r
+               }\r
+               for ( String s : a2 ) {\r
+                       dim[i++] = s;\r
+               }\r
+               for ( String s : a3 ) {\r
+                       dim[i++] = s;\r
+               }\r
+               for ( String s : a4 ) {\r
+                       dim[i++] = s;\r
+               }\r
+               return(dim.clone());\r
+       }\r
+       private String joinPoststr(int mode, Hashtable<String, String> pdat) {\r
+\r
+               String[] pkeys1 = { "cRVMD","cWEKV","cRVHM1","cRVHM2","cCHSRC","cCHNUM","cRSPD1","cTHEX","cTIMER"};\r
+               String[] pkeys2 = { "cRVID","cRVORG","cRVORGEX" };\r
+               String[] pkeys3 = {     "cRPG","cRHEX","cTSTR","cRHEXEX" };\r
+               String[] pkeys4 = {     "RSV_FIX.x","RSV_FIX.y" };\r
+               String[] pkeys5 = {     "RSV_EXEC.x","RSV_EXEC.y" };\r
+               String[] pkeys6 = { "RSV_DEL.x","RSV_DEL.y" };\r
+               String[] pkeys7 = { "RSV_EDIT.x","RSV_EDIT.y" };\r
+               String[] pkeys8 = {     "cRPG","cERR","TTL_DRIVE","cRVID","cRHEX","cTSTR","cRHEXEX","Image_BtnRyoukai.x","Image_BtnRyoukai.y" };\r
+\r
+               ArrayList<String> pkeys = new ArrayList<String>();\r
+               switch (mode) {\r
+               case 1:\r
+                       String[] keys1 = joinArrays( pkeys1, pkeys3, pkeys4, new String[] {} );\r
+                       for ( String s : keys1 ) {\r
+                               pkeys.add(s);\r
+                       }\r
+                       break;\r
+               case 3:\r
+                       String[] keys3 = joinArrays( pkeys1, pkeys2, pkeys3, pkeys6 );\r
+                       for ( String s : keys3 ) {\r
+                               pkeys.add(s);\r
+                       }\r
+                       break;\r
+               case 4:\r
+                       String[] keys4 = joinArrays( pkeys2, pkeys3, pkeys5, new String[] {} );\r
+                       for ( String s : keys4 ) {\r
+                               pkeys.add(s);\r
+                       }\r
+                       break;\r
+               case 5:\r
+                       String[] keys5 = joinArrays( pkeys1, pkeys2, pkeys3, pkeys7 );\r
+                       for ( String s : keys5 ) {\r
+                               pkeys.add(s);\r
+                       }\r
+                       break;\r
+               case 6:\r
+                       String[] keys6 = joinArrays( pkeys8, new String[] {}, new String[] {}, new String[] {} );\r
+                       for ( String s : keys6 ) {\r
+                               pkeys.add(s);\r
+                       }\r
+                       break;\r
+               default:\r
+                       String[] keys99 = joinArrays( pkeys3, pkeys5, new String[] {}, new String[] {} );\r
+                       for ( String s : keys99 ) {\r
+                               pkeys.add(s);\r
+                       }\r
+                       break;\r
+               }\r
+               \r
+               String pstr = "";\r
+               for ( String key : pkeys ) {\r
+                       if (pdat.get(key) != null) {\r
+                               if (key.equals("cTHEX")) {\r
+                                       try {\r
+                                               pdat.put(key, URLEncoder.encode(pdat.get(key),"MS932"));\r
+                                       } catch (UnsupportedEncodingException e) {\r
+                                               // 例外\r
+                                       }\r
+                               }\r
+                               else {\r
+                                       pdat.put(key, pdat.get(key));\r
+                               }\r
+                               Matcher ma = Pattern.compile(" ").matcher(pdat.get(key));\r
+                               pdat.put(key, ma.replaceAll("+"));\r
+                               pstr += key+"="+pdat.get(key)+"&";\r
+                       }\r
+               }\r
+               pstr = pstr.substring(0, pstr.length()-1);\r
+               System.out.println("POST data: "+pstr);\r
+               return(pstr);\r
+       }\r
+       \r
+       /*\r
+        *      録画を取得する \r
+        */\r
+       private int getDigaRecordSetting() {\r
+               System.out.println("run: getDigaRecordSetting");\r
+               \r
+               // 旧リストは全部削除\r
+               vrate.clear();\r
+               chvalue.clear();\r
+               \r
+               // リクエスト発行 \r
+               String[] d = reqPOST("http://"+getIPAddr()+":"+getPortNo()+"/cgi-bin/dvdr/dvdr_ctrl.cgi", "cCMD_RSVADD.x=32&cCMD_RSVADD.y=16", null);\r
+               String header = d[0];\r
+               String response = d[1];\r
+                       \r
+               if (response == null) {\r
+                       // エラーになった場合\r
+                       System.out.println("予約できませんでした");\r
+                       return(-99);\r
+               }\r
+               \r
+               Matcher ma = Pattern.compile("DIGAと通信中です。しばらくお待ちください。").matcher(response);\r
+               if (ma.find()) {\r
+                       // リトライしてね\r
+                       return(-1);\r
+               }\r
+               ma = Pattern.compile("ログインしてからアクセスしてください。").matcher(response);\r
+               if (ma.find()) {\r
+                       // 再ログインしてね\r
+                       return(-2);\r
+               }\r
+               \r
+               ma = Pattern.compile("<select name=\"cRSPD1\">([\\s\\S]+?)</select>").matcher(response);\r
+               if (ma.find()) {\r
+                       Matcher mb = Pattern.compile("<option value=\"(.+?)\".*?>(.+?)\\(HDD\\)</option>").matcher(ma.group(1));\r
+                       while (mb.find()) {\r
+                               TextValueSet t = new TextValueSet();\r
+                               t.setText(mb.group(2));\r
+                               t.setValue(mb.group(1));\r
+                               vrate.add(t);\r
+                       }\r
+               }\r
+               \r
+               // キャッシュファイルに保存\r
+               TVSsave(vrate, vrateTFile);\r
+               \r
+               ma = Pattern.compile("<select name=\"cCHSRC\">([\\s\\S]+?)</select>").matcher(response);\r
+               if (ma.find()) {\r
+                       Matcher mb = Pattern.compile("<option value=\"(.+?)\".*?>(.+?)</option>").matcher(ma.group(1));\r
+                       while (mb.find()) {\r
+                               TextValueSet t = new TextValueSet();\r
+                               t.setText(mb.group(2));\r
+                               t.setValue(mb.group(1));\r
+                               chvalue.add(t);\r
+                       }\r
+               }\r
+               \r
+               // キャッシュファイルに保存\r
+               TVSsave(chvalue, chValueTFile);\r
+               \r
+               return(0);\r
+       }\r
+       \r
+       /*\r
+        *      予約一覧を取得する \r
+        */\r
+       private int getDigaReserveList() {\r
+               System.out.println("run: getDigaReserveList");\r
+\r
+               // 旧リストは全部削除\r
+               ArrayList<ReserveList> newReserveList = new ArrayList<ReserveList>();\r
+               \r
+               // リクエスト発行\r
+               String[] d = reqPOST("http://"+this.getIPAddr()+":"+this.getPortNo()+"/cgi-bin/dvdr/dvdr_ctrl.cgi", "cCMD_RSVLST.x=34&cCMD_RSVLST.y=9", null);\r
+               String header = d[0];\r
+               String response = d[1];\r
+\r
+               if (response == null) {\r
+                       // エラーになった場合\r
+                       System.out.println("予約できませんでした");\r
+                       return(-99);\r
+               }\r
+               \r
+               Matcher ma = Pattern.compile("DIGAと通信中です。しばらくお待ちください。").matcher(response);\r
+               if (ma.find()) {\r
+                       // リトライしてね\r
+                       return(-1);\r
+               }\r
+               ma = Pattern.compile("ログインしてからアクセスしてください。").matcher(response);\r
+               if (ma.find()) {\r
+                       // 再ログインしてね\r
+                       return(-2);\r
+               }\r
+               \r
+               // 予約詳細を作る\r
+               ReserveList entry = new ReserveList();\r
+               ma = Pattern.compile("<tr class=\\\"s_Bffffff\\\">((\\s|\\S)+?)</tr>").matcher(response);\r
+               while (ma.find()) {\r
+                       // 予約内容の部分を切り出す\r
+                       String reserve = ma.group(1);\r
+\r
+                       // 番号\r
+                       Matcher mb = Pattern.compile("<a href=\\\"(.+?)\\\">").matcher(reserve);\r
+                       if (mb.find()) {\r
+                               String[] ar = mb.group(1).split("\\?",2);\r
+                               Matcher mc = Pattern.compile("ANC_RSVLSTNO=(\\d+)").matcher(ar[1]);\r
+                               if (mc.find()) {\r
+                                       entry.setId(mc.group(1));\r
+                               }\r
+                       }\r
+                       \r
+                       // 日付\r
+                       String yy = "";\r
+                       String mm = "";\r
+                       String dd = "";\r
+                       mb = Pattern.compile("\\n(\\d+?)/(\\d+?)\\((.+?)\\)</div>").matcher(reserve);\r
+                       if (mb.find()) {\r
+                               mm = mb.group(1);\r
+                               dd = mb.group(2);\r
+\r
+                               String[] ar = _mmdd2yyyymmdd(mm, dd);\r
+                               yy = ar[0];\r
+                               mm = ar[1];\r
+                               dd = ar[2];\r
+                       }\r
+\r
+                       // チャンネル\r
+                       mb = Pattern.compile("\\n<td width=\"100\" height=\"20\" class=\"s_F16\">&nbsp;(.+?)&nbsp;(.+?)</td>").matcher(reserve);\r
+                       if (mb.find()) {\r
+                               entry.setChannel(mb.group(1)+" "+mb.group(2));\r
+                               entry.setCh_name(cc.getCH_REC2WEB(entry.getChannel()));\r
+                       }\r
+                       \r
+                       // 開始終了時刻と録画時間\r
+                       mb = Pattern.compile("\\n(\\d+):(\\d+)~(\\d+):(\\d+)</div>").matcher(reserve);\r
+                       if (mb.find()) {\r
+                               String ar[] = _hhmm2hhmm_min( mb.group(1)+":"+mb.group(2), mb.group(3)+":"+mb.group(4));\r
+                               entry.setAhh(ar[0]);\r
+                               entry.setAmm(ar[1]);\r
+                               entry.setZhh(ar[2]);\r
+                               entry.setZmm(ar[3]);\r
+                               entry.setRec_min(ar[4]);\r
+                       }\r
+                       \r
+                       // 画質\r
+                       mb = Pattern.compile("\\n(.+?)\\(HDD\\)</div>").matcher(reserve);\r
+                       if (mb.find()) {\r
+                               entry.setRec_mode(mb.group(1));\r
+                       }\r
+                       \r
+                       // パターン\r
+                       mb = Pattern.compile("\\n<td width=\"250\" height=\"20\" class=\"s_F16\">&nbsp;(.+?)、").matcher(reserve);\r
+                       if (mb.find()) {\r
+                               entry.setRec_pattern(_recpatternDGstr2RDstr(mb.group(1),yy,mm,dd));\r
+                               entry.setRec_pattern_id(getRec_pattern_Id(entry.getRec_pattern()));\r
+                               entry.setRec_nextdate(CommonUtils.getNextDate(entry));\r
+                               //entry.setRec_nextdate(getNextDate(entry.getRec_pattern(), entry.getZhh()+":"+entry.getZmm()));\r
+                       }\r
+                       \r
+                       // 開始日時\r
+                       mb = Pattern.compile("^(\\d+)/(\\d+)/(\\d+)").matcher(entry.getRec_nextdate());\r
+                       if (mb.find()) {\r
+                               // ★★★ MM/DDをYYYY/MM/DDに戻す? ★★★\r
+                               GregorianCalendar c = new GregorianCalendar(Locale.JAPAN);\r
+                               c.set(Calendar.MINUTE, Integer.valueOf(entry.getAmm()));\r
+                               c.set(Calendar.HOUR_OF_DAY, Integer.valueOf(entry.getAhh()));\r
+                               c.set(Calendar.DATE, Integer.valueOf(mb.group(3)));\r
+                               c.set(Calendar.MONTH, Integer.valueOf(mb.group(2))-1);\r
+                               c.set(Calendar.YEAR, Integer.valueOf(mb.group(1)));\r
+                               entry.setStartDateTime(String.format("%04d/%02d/%02d %02d:%02d", c.get(Calendar.YEAR),c.get(Calendar.MONTH)+1,c.get(Calendar.DATE),c.get(Calendar.HOUR_OF_DAY),c.get(Calendar.MINUTE)));\r
+\r
+                               c.add(Calendar.MINUTE, Integer.valueOf(entry.getRec_min()));\r
+                               entry.setEndDateTime(String.format("%04d/%02d/%02d %02d:%02d", c.get(Calendar.YEAR),c.get(Calendar.MONTH)+1,c.get(Calendar.DATE),c.get(Calendar.HOUR_OF_DAY),c.get(Calendar.MINUTE)));\r
+                       }\r
+                       \r
+                       \r
+                       // タイトル自動補完フラグなど本体からは取得できない情報を引き継ぐ\r
+                       copyAttributes(entry, getReserves());\r
+                       \r
+                       // 予約情報を保存\r
+                       newReserveList.add(entry.clone());\r
+               }\r
+               setReserves(newReserveList);\r
+               \r
+               // 詳細情報の取得\r
+               System.out.println("========");\r
+               for (int i=0; i<getReserves().size(); i++) {\r
+                       ReserveList e = getReserves().get(i);\r
+                       \r
+                       getDigaReserveInfo(e, e.getId());\r
+\r
+                       reportProgress(String.format("[%s] %s\t%s\t%s\t%s:%s\t%s:%s\t%sm\t%s\t%s\t%s\t%s",\r
+                                       (i+1), e.getId(), e.getRec_pattern(), e.getRec_nextdate(), e.getAhh(), e.getAmm(), e.getZhh(),  e.getZmm(),     e.getRec_min(), e.getRec_mode(), e.getTitle(), e.getChannel(), e.getCh_name()));\r
+               }\r
+               System.out.println("========");\r
+\r
+               // キャッシュファイルに保存\r
+               ReservesToFile(getReserves(), rsvedFile);\r
+               \r
+               return(0);\r
+       }\r
+       /*\r
+        * 予約詳細を取得する\r
+        */\r
+       private ReserveList getDigaReserveInfo(ReserveList reserve_d, String id)\r
+       {\r
+\r
+               String header;\r
+               String response;\r
+               {\r
+                       String[] d = reqPOST("http://"+this.getIPAddr()+":"+this.getPortNo()+"/cgi-bin/reserve_list.cgi", "ANC_RSVLSTNO="+id+"&cDMYDT="+DMY_TIME, null);\r
+                       header = d[0];\r
+                       response = d[1];\r
+               }\r
+               if ( response == null ) {\r
+                       System.out.println("★");\r
+                       System.out.println("★ 予約情報を取得できませんでした。");\r
+                       System.out.println("★");\r
+                       return(null);\r
+               }\r
+               \r
+               // 予約日付\r
+               Matcher ma = Pattern.compile("name=\"cRVMD\" value=\"(\\d\\d)(\\d\\d)\"").matcher(response);\r
+               if (! ma.find()) {\r
+                       return(null);\r
+               }\r
+               String[] da = _mmdd2yyyymmdd(ma.group(1), ma.group(2));\r
+               String yy = da[0];\r
+               String mm = da[1];\r
+               String dd = da[2];\r
+               \r
+               // 開始終了時刻と録画時間\r
+               ma = Pattern.compile("name=\"cRVHM1\" value=\"(\\d\\d)(\\d\\d)\"").matcher(response);\r
+               if (! ma.find()) {\r
+                       return(null);\r
+               }\r
+               String ahh = ma.group(1);\r
+               String amm = ma.group(2);\r
+               ma = Pattern.compile("name=\"cRVHM2\" value=\"(\\d\\d)(\\d\\d)\"").matcher(response);\r
+               if (! ma.find()) {\r
+                       return(null);\r
+               }\r
+               String zhh = ma.group(1);\r
+               String zmm = ma.group(2);\r
+               String[] db = _hhmm2hhmm_min(ahh+":"+amm,zhh+":"+zmm);\r
+               ahh = db[0];\r
+               amm = db[1];\r
+               zhh = db[2];\r
+               zmm = db[3];\r
+               String rec_min = db[4];\r
+               \r
+               // 画質\r
+               ma = Pattern.compile("name=\"cRSPD1\"[\\s\\S]+?value=\"(\\d+?)\" selected>").matcher(response);\r
+               if (! ma.find()) {\r
+                       return(null);\r
+               }\r
+               String rec_mode = _recmode2str(ma.group(1));\r
+               \r
+               // チャンネル\r
+               String ch_source = "";\r
+               String ch_number = "";\r
+               String channel = "";\r
+               ma = Pattern.compile("<select name=\"cCHSRC\">(\\s|\\S)*?value=\"(.+?)\" selected(\\s|\\S)*?</select>").matcher(response);\r
+               if (ma.find()) {\r
+                       ch_source = ma.group(2);\r
+                       Matcher mb = Pattern.compile("name=\"cCHNUM\" value=\"(.+?)\"").matcher(response);\r
+                       if (mb.find()) {\r
+                               ch_number = mb.group(1);\r
+                       }\r
+               }\r
+               else {\r
+                       Matcher mb = Pattern.compile("<input type=\"hidden\" name=\"cCHSRC\" value=\"(.+?)\">(\\s|\\S)*?<input type=\"hidden\" name=\"cCHNUM\" value=\"(.+?)\" maxlength=\"5\" size=\"5\">").matcher(response);\r
+                       if (mb.find()) {\r
+                               ch_source = mb.group(1);\r
+                               ch_number = mb.group(3);\r
+                       }\r
+               }\r
+               for ( TextValueSet t : chvalue ) {\r
+                       if ( t.getValue().equals(ch_source) ) {\r
+                               channel = t.getText()+" "+ch_number;\r
+                               break;\r
+                       }\r
+               }\r
+               String ch_name = cc.getCH_REC2WEB(channel);\r
+               \r
+               // パターン\r
+               ma = Pattern.compile("name=\"cWEKV\"[\\s\\S]+?value=\"(\\d+?)\" selected>").matcher(response);\r
+               if ( ! ma.find()) {\r
+                       return(null);\r
+               }\r
+               String rec_pattern = _recpatternDG2RDstr(ma.group(1), yy, mm, dd);\r
+               \r
+               // タイトル\r
+               ma = Pattern.compile("name=\"cTHEX\" value=\"(.*?)\"").matcher(response);\r
+               if (! ma.find()) {\r
+                       return(null);\r
+               }\r
+               String title = ma.group(1);\r
+               \r
+               \r
+               // 予約情報更新\r
+               reserve_d.setId(id);\r
+               reserve_d.setRec_pattern(rec_pattern);\r
+               reserve_d.setRec_pattern_id(getRec_pattern_Id(rec_pattern));\r
+               reserve_d.setAhh(ahh);\r
+               reserve_d.setAmm(amm);\r
+               reserve_d.setZhh(zhh);\r
+               reserve_d.setZmm(zmm);\r
+               reserve_d.setRec_min(rec_min);\r
+               reserve_d.setRec_mode(rec_mode);\r
+               reserve_d.setTitle(title);\r
+               reserve_d.setTitlePop(TraceProgram.replacePop(title));\r
+               reserve_d.setChannel(channel);\r
+               reserve_d.setCh_name(ch_name);\r
+               \r
+               \r
+               \r
+               return(reserve_d);\r
+       }\r
+       \r
+       //\r
+       private String _recmode2str(String mode)\r
+       {\r
+               for ( TextValueSet t : vrate ) {\r
+                       if (t.getValue().equals(mode)) {\r
+                               return(t.getText());\r
+                       }\r
+               }\r
+               return(null);\r
+       }\r
+       private String _recstr2mode(String str)\r
+       {\r
+               for ( TextValueSet t : vrate ) {\r
+                       if (t.getText().equals(str)) {\r
+                               return(t.getValue());\r
+                       }\r
+               }\r
+               return(null);\r
+       }\r
+       \r
+       //\r
+       private String _recpatternDG2RDstr(String source, String yy, String mm, String dd)\r
+       {\r
+               if ( source.equals("fe") ) {\r
+                       return "毎日";\r
+               }\r
+               else if ( source.equals("fc") ) {\r
+                       return "毎月~土";\r
+               }\r
+               else if ( source.equals("7c") ) {\r
+                       return "毎月~金";\r
+               }\r
+               else if ( source.equals("2") ) {\r
+                       return "毎日曜日";\r
+               }\r
+               else if ( source.equals("4") ) {\r
+                       return "毎月曜日";\r
+               }\r
+               else if ( source.equals("8") ) {\r
+                       return "毎火曜日";\r
+               }\r
+               else if ( source.equals("10") ) {\r
+                       return "毎水曜日";\r
+               }\r
+               else if ( source.equals("20") ) {\r
+                       return "毎木曜日";\r
+               }\r
+               else if ( source.equals("40") ) {\r
+                       return "毎金曜日";\r
+               }\r
+               else if ( source.equals("80") ) {\r
+                       return "毎土曜日";\r
+               }\r
+               else {\r
+                       GregorianCalendar cal = new GregorianCalendar(Locale.JAPAN);\r
+                       cal.setTime(new Date());\r
+                       cal.set(Calendar.SECOND, 0);\r
+                       cal.set(Calendar.MINUTE, 0);\r
+                       cal.set(Calendar.HOUR_OF_DAY, 0);\r
+                       cal.set(Calendar.DATE, Integer.valueOf(dd));\r
+                       cal.set(Calendar.MONTH, Integer.valueOf(mm)-1);\r
+                       cal.set(Calendar.YEAR, Integer.valueOf(yy));\r
+                       return String.format("%04d/%02d/%02d(%s)", cal.get(Calendar.YEAR),cal.get(Calendar.MONTH)+1,cal.get(Calendar.DATE),CommonUtils.WDTPTN[cal.get(Calendar.DAY_OF_WEEK)-1]);\r
+               }\r
+       }\r
+       private String _recpatternDGstr2RDstr(String source, String syy, String smm, String sdd)\r
+       {\r
+\r
+               int yy = Integer.valueOf(syy);\r
+               int mm = Integer.valueOf(smm);\r
+               int dd = Integer.valueOf(sdd);\r
+               \r
+               GregorianCalendar cal = new GregorianCalendar(Locale.JAPAN);\r
+               cal.setTime(new Date());\r
+               cal.set(Calendar.SECOND, 0);\r
+               cal.set(Calendar.MINUTE, 0);\r
+               cal.set(Calendar.HOUR_OF_DAY, 0);\r
+               cal.set(Calendar.DATE, dd);\r
+               cal.set(Calendar.MONTH, mm-1);\r
+               cal.set(Calendar.YEAR, yy);\r
+               \r
+               //Matcher m = Pattern.compile("^曜日指定").matcher(source);\r
+               Matcher m = Pattern.compile("^毎週").matcher(source);\r
+               if (m.find()) {\r
+                       return("毎"+CommonUtils.WDTPTN[cal.get(Calendar.DAY_OF_WEEK)-1]+"曜日");\r
+               }\r
+               m = Pattern.compile("^曜日指定").matcher(source);\r
+               if (m.find()) {\r
+                       return("毎月~木");\r
+               }\r
+               m = Pattern.compile("^月金").matcher(source);\r
+               if (m.find()) {\r
+                       return("毎月~金");\r
+               }\r
+               m = Pattern.compile("^月土").matcher(source);\r
+               if (m.find()) {\r
+                       return("毎月~土");\r
+               }\r
+               m = Pattern.compile("^毎日").matcher(source);\r
+               if (m.find()) {\r
+                       return("毎日");\r
+               }\r
+               \r
+               return(String.format("%04d/%02d/%02d(%s)", cal.get(Calendar.YEAR), cal.get(Calendar.MONTH)+1, cal.get(Calendar.DATE), CommonUtils.WDTPTN[cal.get(Calendar.DAY_OF_WEEK)-1]));\r
+       }\r
+}\r
diff --git a/TinyBannavi/src/tainavi/PlugIn_RecDIGA_DMR_BWT2100.java b/TinyBannavi/src/tainavi/PlugIn_RecDIGA_DMR_BWT2100.java
new file mode 100644 (file)
index 0000000..119b81e
--- /dev/null
@@ -0,0 +1,2052 @@
+package tainavi;\r
+\r
+import java.io.File;\r
+import java.io.UnsupportedEncodingException;\r
+import java.net.URLEncoder;\r
+import java.security.MessageDigest;\r
+import java.util.ArrayList;\r
+import java.util.GregorianCalendar;\r
+import java.util.Hashtable;\r
+import java.util.HashMap;\r
+import java.util.regex.Matcher;\r
+import java.util.regex.Pattern;\r
+\r
+// "cRECMODE" はBZT720用に追加したもの\r
+\r
+\r
+public class PlugIn_RecDIGA_DMR_BWT2100 extends HDDRecorderUtils implements HDDRecorder,Cloneable {\r
+       \r
+       @Override\r
+       public PlugIn_RecDIGA_DMR_BWT2100 clone() {\r
+               return (PlugIn_RecDIGA_DMR_BWT2100) super.clone();\r
+       }\r
+       \r
+       /*******************************************************************************\r
+        * 種族の特性\r
+        ******************************************************************************/\r
+       \r
+       @Override\r
+       public String getRecorderId() { return "DIGA DMR-BWT2100"; }\r
+       @Override\r
+       public RecType getType() { return RecType.RECORDER; }\r
+\r
+       // 番組追従は編集できない\r
+       @Override\r
+       public boolean isPursuesEditable() { return false; }\r
+       // タイトル自動補完はできない\r
+       @Override\r
+       public boolean isAutocompleteSupported() { return false; }\r
+       // チャンネル操作が可能\r
+       @Override\r
+       public boolean isChangeChannelSupported() { return true; }\r
+       // \r
+       @Override\r
+       public boolean isThereAdditionalDetails() { return true; }\r
+       \r
+       /*******************************************************************************\r
+        * 予約ダイアログなどのテキストのオーバーライド\r
+        ******************************************************************************/\r
+       \r
+       @Override\r
+       public String getLabel_Audiorate() { return "予約方法"; }\r
+       \r
+       @Override\r
+       public String getChDatHelp() { return chdathelp; }\r
+       \r
+       private static final String chdathelp =\r
+                       "■放送局コード設定方法はプロジェクトwikiを参照してください。http://sourceforge.jp/projects/tainavi/wiki/DIGA#CHCODE"+\r
+                       " ■予約方法の読み替え:EPG=番組・プログラム=時間"+\r
+                       " ■未対応の曜日指定パターンは単日扱いになります";\r
+       \r
+       /*******************************************************************************\r
+        * CHコード設定、エラーメッセージ\r
+        ******************************************************************************/\r
+       \r
+       @Override\r
+       public ChannelCode getChCode() {\r
+               return cc;\r
+       }\r
+       \r
+       private final ChannelCode cc = new ChannelCode(getRecorderId());\r
+       \r
+       @Override\r
+       public String getErrmsg() {\r
+               return(errmsg.replaceAll("\\\\r\\\\n", ""));\r
+       }\r
+       \r
+       private String errmsg = "";\r
+\r
+       /*******************************************************************************\r
+        * 定数\r
+        ******************************************************************************/\r
+       \r
+       protected static final String[] DIGA_WDPTNSTR  = { "cSUN", "cMON", "cTUE", "cWED", "cTHU", "cFRI", "cSAT", "cALL" };\r
+       protected static final String[] DIGA_WDPTNCODE = { "2",    "4",    "8",    "10",   "20",   "40",   "80",   "fe"   };\r
+       protected static final String[] DIGA_WDAYSTR   = { "日",   "月",   "火",   "水",   "木",   "金",   "土",   "毎日"  };\r
+       \r
+       private static final String ITEM_VIDEO_TYPE_NONE        = "LAN予約";\r
+       \r
+       private static final String ITEM_REC_TYPE_EPG           = "EPG";\r
+       private static final String ITEM_REC_TYPE_PROG          = "プログラム";\r
+       \r
+       private static final String VALUE_VIDEO_TYPE_NONE       = "#NONE#";\r
+       \r
+       private static final String VALUE_REC_TYPE_EPG          = "EPG";\r
+       private static final String VALUE_REC_TYPE_PROG         = "PROGRAM";\r
+       \r
+       private static final String DIGAMSG_WAITFORLOGIN        = "DIGAと通信中です。しばらくお待ちください。";\r
+       private static final String DIGAMSG_PLEASELOGIN         = "ログインしてからアクセスしてください。";\r
+       private static final String DIGAMSG_BUSYNOW                     = "本体操作中、または現在実行できない操作です。";\r
+       private static final String DIGAMSG_CANNOTRESERVE       = "予約が設定できませんでした。";\r
+       private static final String DIGAMSG_PLEASEPOWON         = "電源をオンしてから操作してください。";\r
+       private static final String DIGAMSG_POWOFF                      = "電源切";\r
+       private static final String DIGAMSG_LOGGEDIN            = "onLoadLoginNext";\r
+\r
+       private static final String MISS_HDR = "★★DIGAが追跡に失敗★★ ";\r
+       private static final String MISS_TITLE = "★★番組詳細を取得しなおしてください★★";\r
+\r
+       private static final int RETCODE_SUCCESS = 0;\r
+//     private static final int RETCODE_BUSY = -1;\r
+//     private static final int RETCODE_REDO = -2;\r
+       private static final int RETCODE_FATAL = -99;\r
+       \r
+       private static final int DIGAEVID_NONE = 0;\r
+       private static final int DIGAEVID_CANNOTFOLLOW = 0xFFFE;\r
+       private static final int DIGAEVID_PROGRSV = 0xFFFF;\r
+       \r
+       // ログ関連\r
+       \r
+       private final String MSGID = "["+getRecorderId()+"] ";\r
+       private final String ERRID = "[ERROR]"+MSGID;\r
+       private final String DBGID = "[DEBUG]"+MSGID;\r
+       \r
+       /*******************************************************************************\r
+        * 部品\r
+        ******************************************************************************/\r
+       \r
+       private String rsvedFile = "";\r
+       private String vrateTFile = "";\r
+       private String chValueTFile = "";\r
+       \r
+       private String DMY_TIME = "";\r
+       private String NONCE = "";\r
+       private String DIGEST = "";\r
+       \r
+       protected int get_com_try_count() { return 9; }\r
+       //private int COM_TRY_COUNT = 5;\r
+       private int WAIT_FOR_LOGIN = 7500;\r
+       \r
+       /*******************************************************************************\r
+        * コンストラクタ\r
+        ******************************************************************************/\r
+       \r
+       public PlugIn_RecDIGA_DMR_BWT2100() {\r
+               super();\r
+               this.setTunerNum(2);\r
+       }\r
+       \r
+       /*******************************************************************************\r
+        * チャンネルリモコン機能\r
+        ******************************************************************************/\r
+       \r
+       @Override\r
+       public boolean ChangeChannel(String Channel) {\r
+               //\r
+               if (Channel == null) {\r
+                       return true;\r
+               }\r
+               \r
+               errmsg = "";\r
+               \r
+               String chcode = cc.getCH_WEB2CODE(Channel);\r
+               if ( chcode == null ) {\r
+                       System.err.println(errmsg = "no such ch: "+Channel);\r
+                       return false;\r
+               }\r
+               Matcher ma = Pattern.compile("^(.+?):(.+?)$").matcher(chcode);\r
+               if (ma.find()) {\r
+                       {\r
+                               System.out.println("change channel.(1/2)");\r
+                               if ( doLogin() == false ) {\r
+                                       errmsg = "ログインに失敗しました.";\r
+                                       return false;\r
+                               }\r
+                       }\r
+                       {\r
+                               System.out.println("change channel.(2/2)");\r
+                               String url = "http://"+getIPAddr()+":"+getPortNo()+"/cgi-bin/dvdr/dvdr_chtune.cgi";\r
+                               String pstr = "cCHSRC="+ma.group(2)+"&cCHNUM="+ma.group(1)+"&cCMD_SET.x=49&cCMD_SET.y=8";\r
+                               String[] d = reqPOST(url, pstr, null);\r
+                               String response = d[1];\r
+                               if ( response == null ) {\r
+                                       errmsg = "レコーダが無応答.";\r
+                                       return false;\r
+                               }\r
+                               if ( response.contains(DIGAMSG_BUSYNOW) ) {\r
+                                       errmsg = DIGAMSG_PLEASEPOWON;\r
+                                       return false;\r
+                               }\r
+                               if ( response.contains(DIGAMSG_PLEASEPOWON) ) {\r
+                                       errmsg = DIGAMSG_PLEASEPOWON;\r
+                                       return false;\r
+                               }\r
+                       }\r
+               }\r
+               \r
+               return true;\r
+       }\r
+\r
+       @Override\r
+       public void wakeup() {\r
+               poweronoff(true);\r
+       }\r
+       \r
+       @Override\r
+       public void shutdown() {\r
+               poweronoff(false);\r
+       }\r
+       \r
+       /*******************************************************************************\r
+        * レコーダーから予約一覧を取得する\r
+        ******************************************************************************/\r
+       \r
+       @Override\r
+       public boolean GetRdReserve(boolean force)\r
+       {\r
+               errmsg = "" ;\r
+               \r
+               System.out.println("Run: GetRdReserve("+force+")");\r
+               \r
+               //\r
+               rsvedFile = "env/reserved."+getIPAddr()+"_"+getPortNo()+"_"+getRecorderId()+".xml";\r
+               vrateTFile = "env/videorate."+getIPAddr()+"_"+getPortNo()+"_"+getRecorderId()+".xml";\r
+               chValueTFile = "env/chvalue."+getIPAddr()+"_"+getPortNo()+"_"+getRecorderId()+".xml";\r
+\r
+               setSettingEncoder(encoder);     // チューナー数は自動生成\r
+               setSettingRecType(arate);       // 録画種別は自動生成\r
+\r
+               File f = new File(rsvedFile);\r
+               if ( force == false && f.exists()) {\r
+                       \r
+                       // キャッシュから読み出し(録画設定ほか)\r
+                       vrate = TVSload(vrateTFile);\r
+                       chvalue = TVSload(chValueTFile);\r
+                       \r
+                       // キャッシュから読み出し(予約一覧)\r
+                       setReserves(ReservesFromFile(rsvedFile));\r
+                       \r
+                       // なぜか設定ファイルが空になっている場合があるので、その際は再取得する\r
+                       if (vrate.size()>0 && chvalue.size()>0) {\r
+                               return(true);\r
+                       }\r
+               }\r
+               \r
+               // とりあえず再ログインしてみる\r
+               if ( doLogin() == false ) {\r
+                       errmsg = "ログインに失敗しました.";\r
+                       return false;\r
+               }\r
+\r
+               // DIGAから情報取得\r
+               \r
+               // (1)録画設定の取得\r
+               if ( getDigaRecordSetting() == false ) {\r
+                       errmsg = "録画設定が取得できませんでした.";\r
+                       return false;\r
+               }\r
+               \r
+               // (2)予約一覧の取得・キャッシュへの保存\r
+               ArrayList<ReserveList> newReserveList = new ArrayList<ReserveList>();\r
+               if ( getDigaReserveList(newReserveList,getReserves()) < 0 ) {\r
+                       return false;\r
+               }\r
+               \r
+               // 詳細取得は分離されました\r
+\r
+               // 入れ替えて保存\r
+               setReserves(newReserveList);\r
+               ReservesToFile(getReserves(), rsvedFile);\r
+               \r
+               return(true);\r
+       }\r
+       \r
+       @Override\r
+       public boolean GetRdReserveDetails()\r
+       {\r
+               ArrayList<ReserveList> newReserveList = new ArrayList<ReserveList>();\r
+               \r
+               if ( ! getDigaReserveDetails(newReserveList,getReserves()) ) {\r
+                       return false;   // ダミーだな\r
+               }\r
+               \r
+               // 入れ替えて保存\r
+               setReserves(newReserveList);\r
+               ReservesToFile(getReserves(), rsvedFile);\r
+               \r
+               return true;\r
+       }\r
+\r
+       \r
+       /*******************************************************************************\r
+        * 新規予約\r
+        ******************************************************************************/\r
+       \r
+       @Override\r
+       public boolean PostRdEntry(ReserveList reqr) {\r
+               \r
+               errmsg = "";\r
+               \r
+               int cntMax = 4;\r
+               int cnt = 1;\r
+               \r
+               //\r
+               System.out.println("Run: PostRdEntry("+reqr.getTitle()+")");\r
+               \r
+               //\r
+               if (cc.getCH_WEB2CODE(reqr.getCh_name()) == null) {\r
+                       errmsg = "【警告】Web番組表の放送局名「"+reqr.getCh_name()+"」をCHコードに変換できません。CHコード設定を修正してください。" ;\r
+                       System.out.println(errmsg);\r
+                       return(false);\r
+               }\r
+               \r
+               if (reqr.getRec_mode().equals(ITEM_VIDEO_TYPE_NONE)) {\r
+                       errmsg = "【警告】画質指定が\""+ITEM_VIDEO_TYPE_NONE+"\"の新規登録はできません。" ;\r
+                       return(false) ;\r
+               }\r
+               \r
+               // EPG判定\r
+               \r
+               int evid = -1;\r
+               \r
+               boolean rec_type_epg = true;\r
+               if ( ITEM_REC_TYPE_EPG.equals(reqr.getRec_audio()) ) {\r
+                       if ( ContentIdEDCB.isValid(reqr.getContentId()) ) {\r
+                               ContentIdEDCB.decodeContentId(reqr.getContentId());\r
+                               evid = ContentIdEDCB.getEvId();\r
+                       }\r
+                       else if ( ContentIdDIMORA.isValid(reqr.getContentId()) ) {\r
+                               ContentIdDIMORA.decodeContentId(reqr.getContentId());\r
+                               evid = ContentIdDIMORA.getEvId();\r
+                               // 後続処理あり\r
+                       }\r
+                       else if ( ContentIdREGZA.isValid(reqr.getContentId()) ) {\r
+                               ContentIdREGZA.decodeContentId(reqr.getContentId());\r
+                               evid = ContentIdREGZA.getEvId();\r
+                               // 後続処理あり\r
+                       }\r
+                       \r
+                       if ( evid == -1 || evid == 0 || evid == 0xffff ) {\r
+                               errmsg = "番組表に予約IDがないためEPG予約は利用できません。プログラム予約を行ってください。";\r
+                               return false;\r
+                       }\r
+               }\r
+               else {\r
+                       rec_type_epg = false;\r
+               }\r
+               \r
+               // EPG予約かどうかと連動です\r
+               reqr.setPursues(rec_type_epg);\r
+               \r
+               // 繰り返し予約の開始日っぽい(EPG予約で必須)\r
+               String prg_date = reqr.getRec_nextdate();\r
+               \r
+               // 予約パターンID\r
+               // 次回予定日\r
+               // 録画長\r
+               // 開始日時・終了日時\r
+               reqr.setRec_pattern_id(getRec_pattern_Id(reqr.getRec_pattern()));\r
+               reqr.setRec_nextdate(CommonUtils.getNextDate(reqr));\r
+               reqr.setRec_min(CommonUtils.getRecMin(reqr.getAhh(),reqr.getAmm(),reqr.getZhh(),reqr.getZmm()));\r
+               getStartEndDateTime(reqr);\r
+               \r
+               // とりあえず再ログインしてみる\r
+               if ( doLogin() == false ) {\r
+                       errmsg = "ログインに失敗しました.";\r
+                       return false;\r
+               }\r
+               \r
+               // 登録前の予約IDの一覧を取得する\r
+               reportProgress(String.format("登録前の予約IDのリストを取得します(%d/%d).",cnt++,cntMax));\r
+               ArrayList<ReserveList> preReserveList = new ArrayList<ReserveList>();\r
+               if ( getDigaReserveList(preReserveList,null) < 0 ) {\r
+                       errmsg = "登録前の予約IDのリストの取得に失敗しました";\r
+                       return false;\r
+               }\r
+               \r
+               // POSTデータを変換する\r
+               HashMap<String, String> pdat = modPostdata(reqr,prg_date);\r
+               \r
+               // 事前データを取得する(ログイン後、処理を待たされることがある)\r
+               String response;\r
+               {\r
+                       reportProgress(String.format("処理IDを取得します(%d/%d).",cnt++,cntMax));\r
+                       String[] d = reqPOST("http://"+getIPAddr()+":"+getPortNo()+"/cgi-bin/dvdr/dvdr_ctrl.cgi", "cCMD_RSVADD.x=31&cCMD_RSVADD.y=8", null);\r
+                       response = d[1];\r
+               }\r
+               \r
+               // 予約情報送信\r
+               String[] keys = {"cRPG", "cRHEX", "cTSTR", "cRHEXEX"};\r
+               for ( String c : keys ) {\r
+                       Matcher ma = Pattern.compile("name=\""+c+"\" value=\"(.*?)\"").matcher(response);\r
+                       if ( ma.find() ) {\r
+                               pdat.put(c,ma.group(1));\r
+                       }\r
+               }\r
+               pdat.put("RSV_FIX.x","29");\r
+               pdat.put("RSV_FIX.y","7");\r
+               String pstr = joinPoststr(PostMode.ADD_CMD, pdat);\r
+               {\r
+                       reportProgress(String.format("予約情報を送信します(%d/%d).",cnt++,cntMax));\r
+                       String[] d = reqPOST("http://"+getIPAddr()+":"+getPortNo()+"/cgi-bin/reserve_add.cgi", pstr, null);\r
+                       response = d[1];\r
+               }\r
+               \r
+               // 予約実行\r
+               HashMap<String, String> dgdat = new HashMap<String, String>();\r
+               \r
+               String[] keys2 = { "cRECMODE", "cRPG", "cRHEX", "cTSTR", "cRHEXEX" };\r
+               for ( String c : keys2 ) {\r
+                       Matcher ma = Pattern.compile("name=\""+c+"\" value=\"(.*?)\"").matcher(response);\r
+                       if ( ma.find() ) {\r
+                               dgdat.put(c,ma.group(1));\r
+                       }\r
+               }\r
+               dgdat.put("RSV_EXEC.x","45");\r
+               dgdat.put("RSV_EXEC.y","5");\r
+               \r
+               //\r
+               if ( rec_type_epg ) {\r
+                       //String cRHEX = dgdat.get("cRHEX");\r
+                       String cRHEXEX = dgdat.get("cRHEXEX");\r
+                       if ( cRHEXEX != null ) {\r
+                               //dgdat.put("cRHEX",cRHEX.replaceFirst("....(....)$", reqr.));\r
+                               dgdat.put("cRHEXEX",cRHEXEX.replaceFirst("^......", String.format("10%04x",evid)));\r
+                       }\r
+                       else {\r
+                               errmsg = "番組IDを設定できませんでした。プログラム予約を行ってください。";\r
+                               return false;\r
+                       }\r
+               }\r
+\r
+               //\r
+               pstr = joinPoststr(PostMode.ADD_EXEC, dgdat);\r
+               {\r
+                       reportProgress(String.format("予約を確定します(%d/%d).",cnt++,cntMax));\r
+                       String[] d = reqPOST("http://"+getIPAddr()+":"+getPortNo()+"/cgi-bin/reserve_addq.cgi", pstr, null);\r
+                       response = d[1];\r
+               }\r
+               \r
+               // エラー対応\r
+               Matcher ma = Pattern.compile("name=\"cERR\" value=\"13\"").matcher(response);\r
+               if ( ma.find() ) {\r
+                       errmsg = "★★★ 予約が重複しています。 ★★★";\r
+                       System.out.printf("\n<<< Message from DIGA >>> \"%s\"\n\n", errmsg);\r
+                       // 予約情報送信\r
+                       String[] keys3 = {"cRPG", "cRHEX", "cTSTR", "cRHEXEX"};\r
+                       for ( String c : keys3 ) {\r
+                               Matcher mb = Pattern.compile("name=\""+c+"\" value=\"(.*?)\"").matcher(response);\r
+                               if ( mb.find() ) {\r
+                                       pdat.put(c,mb.group(1));\r
+                               }\r
+                       }\r
+                       pdat.put("Image_BtnRyoukai.x","30");\r
+                       pdat.put("Image_BtnRyoukai.y","13");\r
+                       pstr = joinPoststr(PostMode.ERR_OK, pdat);\r
+                       {\r
+                               String[] d = reqPOST("http://"+getIPAddr()+":"+getPortNo()+"/cgi-bin/apl_err.cgi", pstr, null);\r
+                               response = d[1];\r
+                       }\r
+                       \r
+                       reqr.setTunershort(true);\r
+               }\r
+               \r
+               // 登録結果の返信\r
+               if ( response.contains(DIGAMSG_BUSYNOW) ) {\r
+                       errmsg = DIGAMSG_BUSYNOW ;\r
+                       System.out.printf("\n<<< Message from DIGA >>> \"%s\"\n\n", errmsg);\r
+                       return(false);\r
+               }\r
+               if ( response.contains(DIGAMSG_CANNOTRESERVE) ) {\r
+                       errmsg = DIGAMSG_CANNOTRESERVE ;\r
+                       System.out.printf("\n<<< Message from DIGA >>> \"%s\"\n\n", errmsg);\r
+                       return(false);\r
+               }\r
+               \r
+               // 予約リストを作り直したい!\r
+               ArrayList<ReserveList> newReserveList = new ArrayList<ReserveList>();\r
+               \r
+               // 予約リスト番号を取得(キャッシュに存在しない番号が新番号)\r
+               {\r
+                       ArrayList<ReserveList> aftReserveList = new ArrayList<ReserveList>();\r
+                       if ( _getDigaReserveList(aftReserveList,response) < 0 ) {\r
+                               errmsg = "登録後の予約IDのリストの取得に失敗しました";\r
+                               return false;\r
+                       }\r
+                       ReserveList newr = refreshReserveList(newReserveList,preReserveList,aftReserveList,getReserves());\r
+                       if ( newr != null ) {\r
+                               // 新規あり!\r
+                               reqr.setId(newr.getId());\r
+                               reqr.setTunershort(newr.getTunershort());\r
+                       }\r
+               }\r
+               \r
+               if ( reqr.getId() == null ) {\r
+                       errmsg = ERRID+"【致命的エラー】 予約は成功したと思われますが、該当する予約IDを見つけることができませんでした。";\r
+                       System.out.printf("\n<<< Message from RD >>> \"%s\"\n\n", errmsg);\r
+                       return false;\r
+               }\r
+               \r
+               if ( rec_type_epg ) {\r
+                       ReserveList newr = new ReserveList();\r
+                       CommonUtils.FieldCopy(newr, reqr);\r
+                       if ( getDigaReserveDetail(newr, reqr.getId()) == null ) {\r
+                               // 情報を取得できなかったので、鯛ナビの情報をそのままで\r
+                               errmsg = "登録した予約の情報を取得しなおそうとしましたが、失敗しました。";\r
+                       }\r
+                       else if ( isModified(reqr, newr) ) {\r
+                               // 情報が取得できて、かつ強制変更されているならば置き換える\r
+                               errmsg = "DIGA番組表からの情報で内容が変更されました: "+newr.getStartDateTime()+"~"+newr.getZhh()+":"+newr.getZmm()+" "+newr.getTitle();\r
+                               reqr = newr;\r
+                       }\r
+               }\r
+               \r
+               newReserveList.add(reqr);                                       // 予約リストに追加\r
+               setReserves(newReserveList);                            // 予約リストを更新\r
+               ReservesToFile(getReserves(), rsvedFile);       // キャッシュに保存\r
+               \r
+               System.out.printf("\n<<< Message from DIGA >>> \"%s\"\n\n", "正常に登録できました。");\r
+               return(true);\r
+       }\r
+\r
+       \r
+       /*******************************************************************************\r
+        * 予約更新\r
+        ******************************************************************************/\r
+       \r
+       @Override\r
+       public boolean UpdateRdEntry(ReserveList cacher, ReserveList reqr) {\r
+\r
+               errmsg = "";\r
+               \r
+               System.out.println("Run: UpdateRdEntry()");\r
+               \r
+               //\r
+               if (cc.getCH_WEB2CODE(reqr.getCh_name()) == null) {\r
+                       errmsg = "【警告】Web番組表の放送局名「"+reqr.getCh_name()+"」をCHコードに変換できません。CHコード設定を修正してください。" ;\r
+                       System.out.println(errmsg);\r
+                       return(false);\r
+               }\r
+               \r
+               if (cacher.getRec_mode().equals(ITEM_VIDEO_TYPE_NONE)) {\r
+                       if ( ! cacher.getRec_mode().equals(reqr.getRec_mode())) {\r
+                               errmsg = "【警告】画質指定を\""+ITEM_VIDEO_TYPE_NONE+"\"から別の設定に変更することはできません。" ;\r
+                               return(false) ;\r
+                       }\r
+               }\r
+               else if (reqr.getRec_mode().equals(ITEM_VIDEO_TYPE_NONE)) {\r
+                       errmsg = "【警告】画質指定を\""+ITEM_VIDEO_TYPE_NONE+"\"に変更することはできません。" ;\r
+                       return(false) ;\r
+               }\r
+               \r
+               if ( ! cacher.getRec_audio().equals(reqr.getRec_audio()) ) {\r
+                       errmsg = String.format("異なる予約方式への更新は行えません(%s->%s)",cacher.getRec_audio(),reqr.getRec_audio());\r
+                       return false;\r
+               }\r
+               \r
+               int pattern_id = getRec_pattern_Id(reqr.getRec_pattern());\r
+               \r
+               if ( ITEM_REC_TYPE_EPG.equals(reqr.getRec_audio()) ) {\r
+                       // EPG予約の場合の制限\r
+                       if ( (pattern_id != cacher.getRec_pattern_id()) ||\r
+                                       ! cacher.getRec_mode().equals(reqr.getRec_mode()) ) {\r
+                               errmsg = "EPG予約で変更出るのは予約のON/OFFのみです。";\r
+                               return false;\r
+                       }\r
+               }\r
+               \r
+               // 予約パターンID\r
+               // 次回予定日\r
+               // 録画長\r
+               // 開始日時・終了日時\r
+               reqr.setRec_pattern_id(pattern_id);\r
+               reqr.setRec_nextdate(CommonUtils.getNextDate(reqr));\r
+               reqr.setRec_min(CommonUtils.getRecMin(reqr.getAhh(),reqr.getAmm(),reqr.getZhh(),reqr.getZmm()));\r
+               getStartEndDateTime(reqr);\r
+               \r
+               // とりあえず再ログインしてみる\r
+               if ( doLogin() == false ) {\r
+                       errmsg = "ログインに失敗しました.";\r
+                       return false;\r
+               }\r
+               \r
+               // POSTデータを変換する\r
+               HashMap<String, String> pdat = modPostdata(reqr,null);\r
+               \r
+               // 事前データを取得する(ログイン後、処理を待たされることがある)\r
+               String response;\r
+               {\r
+                       reportProgress("処理IDを取得します(1/3).");\r
+                       String[] d = reqDigaGET("http://"+getIPAddr()+":"+getPortNo()+"/cgi-bin/reserve_list.cgi?ANC_RSVLSTNO="+reqr.getId()+"&cDMYDT="+DMY_TIME, null);\r
+                       response = d[1];\r
+               }\r
+               \r
+               // 更新情報送信\r
+               String[] keys = { "cRVID","cRVORG","cRVORGEX","cRVORGEX2","cRVORGEX3","cRPG","cRHEX","cTSTR","cRHEXEX" };\r
+               for ( String c : keys ) {\r
+                       Matcher ma = Pattern.compile("name=\""+c+"\" value=\"(.*?)\"").matcher(response);\r
+                       if ( ma.find() ) {\r
+                               pdat.put(c,ma.group(1));\r
+                       }\r
+               }\r
+               pdat.put("RSV_EDIT.x","48");\r
+               pdat.put("RSV_EDIT.y","14");\r
+               String pstr = joinPoststr(PostMode.UPD_CMD, pdat);\r
+               {\r
+                       reportProgress("更新情報を送信します(2/3).");\r
+                       String[] d = reqPOST("http://"+getIPAddr()+":"+getPortNo()+"/cgi-bin/reserve_edit.cgi", pstr, null);\r
+                       response = d[1];\r
+               }\r
+\r
+               // 予約実行\r
+               HashMap<String, String> dgdat = new HashMap<String, String>();\r
+               \r
+               String[] keys2 = { "cRECMODE", "cRVID", "cRVORG", "cRVORGEX", "cRVORGEX2", "cRVORGEX3", "cRPG", "cRHEX", "cTSTR", "cRHEXEX" };\r
+               for ( String c : keys2 ) {\r
+                       Matcher ma = Pattern.compile("name=\""+c+"\" value=\"(.*?)\"").matcher(response);\r
+                       if ( ma.find() ) {\r
+                               dgdat.put(c,ma.group(1));\r
+                       }\r
+               }\r
+               dgdat.put("RSV_EXEC.x","29");\r
+               dgdat.put("RSV_EXEC.y","12");\r
+               pstr = joinPoststr(PostMode.UPD_EXEC, dgdat);\r
+               {\r
+                       reportProgress("更新を確定します(3/3).");\r
+                       String[] d = reqPOST("http://"+getIPAddr()+":"+getPortNo()+"/cgi-bin/reserve_editq.cgi", pstr, null);\r
+                       response = d[1];\r
+               }\r
+\r
+               // エラー対応\r
+               Matcher ma = Pattern.compile("name=\"cERR\" value=\"13\"").matcher(response);\r
+               if ( ma.find() ) {\r
+                       errmsg = "★★★ 予約が重複しています。 ★★★";\r
+                       System.out.printf("\n<<< Message from DIGA >>> \"%s\"\n\n", errmsg);\r
+                       // 予約情報送信\r
+                       String[] keys3 = {"cRPG", "cRHEX", "cTSTR", "cRHEXEX"};\r
+                       for ( String c : keys3 ) {\r
+                               Matcher mb = Pattern.compile("name=\""+c+"\" value=\"(.*?)\"").matcher(response);\r
+                               if ( mb.find() ) {\r
+                                       pdat.put(c,mb.group(1));\r
+                               }\r
+                       }\r
+                       pdat.put("Image_BtnRyoukai.x","30");\r
+                       pdat.put("Image_BtnRyoukai.y","13");\r
+                       pstr = joinPoststr(PostMode.ERR_OK, pdat);\r
+                       {\r
+                               String[] d = reqPOST("http://"+getIPAddr()+":"+getPortNo()+"/cgi-bin/apl_err.cgi", pstr, null);\r
+                               response = d[1];\r
+                       }\r
+                       \r
+                       reqr.setTunershort(true);\r
+               }\r
+               \r
+               // 登録結果の返信\r
+               if ( response.contains(DIGAMSG_BUSYNOW) ) {\r
+                       errmsg = DIGAMSG_BUSYNOW ;\r
+                       System.out.printf("\n<<< Message from DIGA >>> \"%s\"\n\n", errmsg);\r
+                       return(false);\r
+               }\r
+               if ( response.contains(DIGAMSG_CANNOTRESERVE) ) {\r
+                       errmsg = DIGAMSG_CANNOTRESERVE ;\r
+                       System.out.printf("\n<<< Message from DIGA >>> \"%s\"\n\n", errmsg);\r
+                       return(false);\r
+               }\r
+\r
+               // 予約リストを作り直したい!\r
+               ArrayList<ReserveList> newReserveList = new ArrayList<ReserveList>();\r
+               \r
+               // 予約リスト番号を取得\r
+               {\r
+                       ArrayList<ReserveList> aftReserveList = new ArrayList<ReserveList>();\r
+                       if ( _getDigaReserveList(aftReserveList,response) < 0 ) {\r
+                               errmsg = "登録後の予約IDのリストの取得に失敗しました";\r
+                               return false;\r
+                       }\r
+                       refreshReserveList(newReserveList,null,aftReserveList,getReserves());\r
+                       for ( ReserveList newr : newReserveList ) {\r
+                               if ( newr.getId().equals(cacher.getId()) ) {\r
+                                       reqr.setTunershort(newr.getTunershort());\r
+                                       newReserveList.remove(newr);\r
+                                       newReserveList.add(reqr);\r
+                                       break;\r
+                               }\r
+                       }\r
+                       \r
+                       // リストにいねーじゃんチェックは未実装\r
+               }\r
+               \r
+               // 情報置き換え\r
+               setReserves(newReserveList);\r
+               ReservesToFile(getReserves(), rsvedFile);       // キャッシュに保存\r
+               \r
+               System.out.printf("\n<<< Message from DIGA >>> \"%s\"\n\n", "正常に更新できました。");\r
+               return(true);\r
+       }\r
+\r
+       \r
+       /*******************************************************************************\r
+        * 予約削除\r
+        ******************************************************************************/\r
+       \r
+       @Override\r
+       public ReserveList RemoveRdEntry(String delid) {\r
+\r
+               errmsg = "";\r
+               \r
+               System.out.println("Run: RemoveRdEntry()");\r
+\r
+               // 削除対象を探す\r
+               ReserveList delr = null;\r
+               for (  ReserveList reserve : getReserves() )  {\r
+                       if (reserve.getId().equals(delid)) {\r
+                               delr = reserve;\r
+                               break;\r
+                       }\r
+               }\r
+               if (delr == null) {\r
+                       return(null);\r
+               }\r
+               \r
+               // とりあえず再ログインしてみる\r
+               if ( doLogin() == false ) {\r
+                       errmsg = "ログインに失敗しました.";\r
+                       return null;\r
+               }\r
+               \r
+               // POSTデータの入れ物を作る\r
+               HashMap<String, String> pdat = modPostdata(delr,null);\r
+               \r
+               // 事前データを取得する(ログイン後、処理を待たされることがある)\r
+               String response;\r
+               {\r
+                       reportProgress("処理IDを取得します(1/3).");\r
+                       String[] d = reqDigaGET("http://"+getIPAddr()+":"+getPortNo()+"/cgi-bin/reserve_list.cgi?ANC_RSVLSTNO="+delid+"&cDMYDT="+DMY_TIME, null);\r
+                       response = d[1];\r
+               }\r
+               \r
+               // 削除情報送信\r
+               String[] keys = { "cRVID","cRVORG","cRVORGEX","cRVORGEX2","cRVORGEX3","cRPG","cRHEX","cTSTR","cRHEXEX" };\r
+               for ( String c : keys ) {\r
+                       Matcher ma = Pattern.compile("name=\""+c+"\" value=\"(.*?)\"").matcher(response);\r
+                       if ( ma.find() ) {\r
+                               pdat.put(c,ma.group(1));\r
+                       }\r
+               }\r
+               pdat.put("RSV_DEL.x","33");\r
+               pdat.put("RSV_DEL.y","19");\r
+               String pstr = joinPoststr(PostMode.DEL_CMD, pdat);\r
+               {\r
+                       reportProgress("削除情報を送信します(2/3).");\r
+                       String[] d = reqPOST("http://"+getIPAddr()+":"+getPortNo()+"/cgi-bin/reserve_edit.cgi", pstr, null);\r
+                       response = d[1];\r
+               }\r
+               \r
+               // 削除実行\r
+               HashMap<String, String> dgdat = new HashMap<String, String>();\r
+               \r
+               String[] keys2 = { "cRECMODE", "cRVID", "cRVORG", "cRVORGEX", "cRVORGEX2", "cRVORGEX3", "cRPG", "cRHEX", "cTSTR", "cRHEXEX" };\r
+               for ( String c : keys2 ) {\r
+                       Matcher ma = Pattern.compile("name=\""+c+"\" value=\"(.*?)\"").matcher(response) ;\r
+                       if ( ma.find() ) {\r
+                               dgdat.put(c,ma.group(1));\r
+                       }\r
+               }\r
+               dgdat.put("RSV_EXEC.x","47");\r
+               dgdat.put("RSV_EXEC.y","6");\r
+               pstr = joinPoststr(PostMode.DEL_EXEC, dgdat);\r
+               {\r
+                       reportProgress("削除を確定します(3/3).");\r
+                       String[] d = reqPOST("http://"+getIPAddr()+":"+getPortNo()+"/cgi-bin/reserve_delq.cgi", pstr, null);\r
+                       response = d[1];\r
+               }\r
+\r
+               // 削除結果の返信\r
+               if ( response.contains(DIGAMSG_BUSYNOW) ) {\r
+                       errmsg = DIGAMSG_BUSYNOW;\r
+                       System.out.printf("\n<<< Message from DIGA >>> \"%s\"\n\n", DIGAMSG_BUSYNOW);\r
+                       return(null);\r
+               }\r
+               if ( response.contains(DIGAMSG_CANNOTRESERVE) ) {\r
+                       errmsg = DIGAMSG_CANNOTRESERVE ;\r
+                       System.out.printf("\n<<< Message from DIGA >>> \"%s\"\n\n", errmsg);\r
+                       return(null);\r
+               }\r
+               \r
+\r
+               // 予約リストを作り直したい!\r
+               ArrayList<ReserveList> newReserveList = new ArrayList<ReserveList>();\r
+               \r
+               // 予約リスト番号を取得\r
+               {\r
+                       ArrayList<ReserveList> aftReserveList = new ArrayList<ReserveList>();\r
+                       if ( _getDigaReserveList(aftReserveList,response) < 0 ) {\r
+                               errmsg = "登録後の予約IDのリストの取得に失敗しました";\r
+                       }\r
+                       refreshReserveList(newReserveList,null,aftReserveList,getReserves());\r
+                       for ( ReserveList newr : newReserveList ) {\r
+                               if ( newr.getId().equals(delr.getId()) ) {\r
+                                       errmsg = "削除されていません(失敗したようです)。";\r
+                               }\r
+                       }\r
+                       \r
+                       // リストにいねーじゃんチェックは未実装\r
+               }\r
+               \r
+               // 情報置き換え\r
+               setReserves(newReserveList);\r
+               ReservesToFile(getReserves(), rsvedFile);       // キャッシュに保存\r
+               \r
+               System.out.printf("\n<<< Message from DIGA >>> \"%s\"\n\n", "正常に削除できました。");\r
+               return(delr);\r
+       }\r
+       \r
+       /* ここまで */\r
+       \r
+       \r
+       \r
+       \r
+       \r
+       \r
+       /*******************************************************************************\r
+        * 電源ON/OFF\r
+        ******************************************************************************/\r
+       \r
+       private void poweronoff(boolean isWakeup) {\r
+               // とりあえず再ログインしてみる\r
+               if ( doLogin() == false ) {\r
+                       errmsg = "ログインに失敗しました.";\r
+                       return;\r
+               }\r
+               // 電源断かどうか確認する\r
+               String response;\r
+               {\r
+                       String[] d = reqDigaGET("http://"+getIPAddr()+":"+getPortNo()+"/cgi-bin/dispframe.cgi?DISP_PAGE=1001&Radio_Drive=1", null);\r
+                       response = d[1];\r
+                       if ( response == null ) {\r
+                               errmsg = "レコーダが無応答.";\r
+                               return;\r
+                       }\r
+               }\r
+               boolean isPowOff = response.contains(DIGAMSG_POWOFF);\r
+               if (isWakeup && ! isPowOff) {\r
+                       System.out.println("Already wakeup");\r
+                       return;\r
+               }\r
+               else if ( ! isWakeup && isPowOff) {\r
+                       System.out.println("Already shutdown");\r
+                       return;\r
+               }\r
+               \r
+               // 電源入or切\r
+               {\r
+                       String[] d = reqPOST("http://"+getIPAddr()+":"+getPortNo()+"/cgi-bin/dvdr/dvdr_ctrl.cgi", "cCMD_POWER.x=61&cCMD_POWER.y=9", null);\r
+                       response = d[1];\r
+                       //System.out.println(response);\r
+               }\r
+       }\r
+\r
+       /*******************************************************************************\r
+        * 予約で投入する各種設定を整理して一時保存する\r
+        ******************************************************************************/\r
+\r
+       private HashMap<String, String> modPostdata(ReserveList r, String prg_date) {\r
+               \r
+               HashMap<String, String> newdat = new HashMap<String, String>();\r
+               \r
+               // 録画チャンネル(ここは良く落ちる)\r
+               r.setChannel(cc.getCH_WEB2CODE(r.getCh_name()));\r
+               Matcher ma = Pattern.compile("^20([123])$").matcher(r.getChannel());\r
+               if ( ma.find() ) {\r
+                       newdat.put("cCHSRC","3");               // 外部入力\r
+                       newdat.put("cCHNUM","");\r
+               }\r
+               else {\r
+                       String[] s = r.getChannel().split(":");\r
+                       newdat.put("cCHSRC",s[1]);              // 外部入力\r
+                       newdat.put("cCHNUM",s[0]);\r
+               }\r
+               \r
+               // 録画レート\r
+               String vrval = text2value(vrate,r.getRec_mode());\r
+               newdat.put("cRSPD1", (vrval.equals(VALUE_VIDEO_TYPE_NONE)?(""):(vrval)));\r
+               \r
+               // 日付\r
+               _modPostdata_date( newdat, r.getRec_pattern(), (prg_date!=null)?prg_date:r.getRec_nextdate(), r.getAhh(), r.getPursues(), r.getId() );\r
+               \r
+               // 時刻\r
+               newdat.put("cRVHM1", String.format("%02d%02d",Integer.valueOf(r.getAhh()), Integer.valueOf(r.getAmm())));\r
+               newdat.put("cRVHM2", String.format("%02d%02d",Integer.valueOf(r.getZhh()), Integer.valueOf(r.getZmm())));\r
+               \r
+               // タイトル\r
+               newdat.put("cTHEX", r.getTitle());\r
+               \r
+               // 予約実行\r
+               newdat.put("cTIMER", (r.getExec())?("1"):("0"));\r
+               \r
+               // 作業用\r
+               newdat.put("channel", r.getChannel());\r
+               newdat.put("date", r.getRec_pattern());\r
+               \r
+               return newdat;\r
+       }\r
+\r
+       /*******************************************************************************\r
+        * 予約で投入する各種設定を整理して一時保存する(パターン部分だけ切り出したもの)\r
+        ******************************************************************************/\r
+       \r
+       private void _modPostdata_date(HashMap<String, String> newdat, String rec_pattern, String nextdate, String Ahh, boolean pursues, String id) {\r
+\r
+               // 当日(単日)or最初の日付(繰り返し)\r
+               Matcher ma = Pattern.compile("^(\\d\\d\\d\\d)/(\\d\\d)/(\\d\\d)").matcher(nextdate);\r
+               if ( ma.find() ) {\r
+                       newdat.put("cRVMD", ma.group(2)+ma.group(3));\r
+               }\r
+               \r
+               if ( pursues && (id != null && id.length() > 0) ) {\r
+                       // 番組追従ありの更新の場合、曜日フラグはdisabled\r
+                       return;\r
+               }\r
+               \r
+               int i = 0;\r
+               for ( ; i < HDDRecorder.RPTPTN.length && HDDRecorder.RPTPTN[i].equals(rec_pattern) == false; i++ ) {\r
+                       // 処理はないよ\r
+               }\r
+               \r
+               if ( 0 <= i && i <= 10 ) {\r
+                       if ( i <= 6 ) {\r
+                               // 毎週予約\r
+                               newdat.put(DIGA_WDPTNSTR[i], DIGA_WDPTNCODE[i]);\r
+                       }\r
+                       else if ( 7 <= i && i <= 9 ) {\r
+                               // 帯予約(月~木・金・土)\r
+                               int d = ((CommonUtils.isLateNight(Ahh))?(1):(0));\r
+                               for ( int c=1; c <= i-3; c++ ) {\r
+                                       newdat.put(DIGA_WDPTNSTR[(c+d)%7], DIGA_WDPTNCODE[(c+d)%7]);\r
+                               }\r
+                       }\r
+                       else {  // 毎日\r
+                               newdat.put(DIGA_WDPTNSTR[DIGA_WDPTNSTR.length-1],DIGA_WDPTNCODE[DIGA_WDPTNSTR.length-1]);\r
+                       }\r
+               }\r
+       }\r
+       \r
+       protected String[] joinArrays(String[] a1,String[] a2,String[] a3,String[] a4) {\r
+               \r
+               ArrayList<String> ar = new ArrayList<String>();\r
+               \r
+               if ( a1 != null ) {\r
+                       for ( String s : a1 ) {\r
+                               ar.add(s);\r
+                       }\r
+               }\r
+               if ( a2 != null ) {\r
+                       for ( String s : a2 ) {\r
+                               ar.add(s);\r
+                       }\r
+               }\r
+               if ( a3 != null ) {\r
+                       for ( String s : a3 ) {\r
+                               ar.add(s);\r
+                       }\r
+               }\r
+               if ( a4 != null ) {\r
+                       for ( String s : a4 ) {\r
+                               ar.add(s);\r
+                       }\r
+               }\r
+               \r
+               return (String[])ar.toArray(new String[0]);\r
+       }\r
+       \r
+       \r
+       /*******************************************************************************\r
+        * 一時保存した各種設定をPOSTデータに変換する\r
+        ******************************************************************************/\r
+       \r
+       private String joinPoststr(PostMode mode, HashMap<String, String> pdat) {\r
+\r
+               HashMap<PostMode, String[]> keymap = getPostKeys();\r
+               String[] keys = keymap.get(mode);\r
+               if ( keys == null ) {\r
+                       System.err.println("【エラー】未定義のPostMode: "+mode);\r
+                       return null;\r
+               }\r
+               \r
+               String pstr = "";\r
+               try {\r
+                       for ( String key : keys ) {\r
+                               for ( String s : DIGA_WDPTNSTR ) {\r
+                                       if (pdat.get(key) != null) {\r
+                                               if ( key.equals(s) ) {\r
+                                                       if ( pdat.get(key).equals("") ) {\r
+                                                               continue;\r
+                                                       }\r
+                                               }\r
+                                       }\r
+                               }\r
+                               if (pdat.get(key) != null) {\r
+                                       if (key.equals("cTHEX")) {\r
+                                               try {\r
+                                                       pdat.put(key, URLEncoder.encode(pdat.get(key),"MS932"));\r
+                                               } catch (UnsupportedEncodingException e) {\r
+                                                       // 例外\r
+                                               }\r
+                                       }\r
+                                       else {\r
+                                               pdat.put(key, pdat.get(key));\r
+                                       }\r
+                                       Matcher ma = Pattern.compile(" ").matcher(pdat.get(key));\r
+                                       pdat.put(key, ma.replaceAll("+"));\r
+                                       pstr += key+"="+pdat.get(key)+"&";\r
+                               }\r
+                       }\r
+                       \r
+                       pstr = pstr.substring(0, pstr.length()-1);\r
+               }\r
+               catch ( Exception e) {\r
+                       e.printStackTrace();\r
+               }\r
+               System.out.println("POST data: "+pstr);\r
+               return(pstr);\r
+       }\r
+       \r
+       protected static enum PostMode { ADD_CMD, ADD_EXEC, UPD_CMD, UPD_EXEC, DEL_CMD, DEL_EXEC, ERR_OK };\r
+       \r
+       protected HashMap<PostMode, String[]> getPostKeys() {\r
+               \r
+               String[] pkeys1a = {"cRVMD","cRVHM1","cRVHM2","cCHSRC","cCHNUM","cRSPD1"};\r
+               String[] pkeys1b = {"cTHEX","cTIMER"};\r
+               String[] pkeys1 = joinArrays(pkeys1a, DIGA_WDPTNSTR, pkeys1b, null);\r
+               String[] pkeys2 = { "cRVID","cRVORG","cRVORGEX","cRVORGEX2","cRVORGEX3" };\r
+               String[] pkeys3 = {     "cRPG","cRHEX","cTSTR","cRHEXEX" };\r
+               String[] pkeys4 = {     "RSV_FIX.x","RSV_FIX.y" };\r
+               String[] pkeys5 = {     "RSV_EXEC.x","RSV_EXEC.y" };\r
+               String[] pkeys6 = { "RSV_DEL.x","RSV_DEL.y" };\r
+               String[] pkeys7 = { "RSV_EDIT.x","RSV_EDIT.y" };\r
+               String[] pkeys8 = {     "cRPG","cERR","TTL_DRIVE","cRVID","cRHEX","cTSTR","cRHEXEX","Image_BtnRyoukai.x","Image_BtnRyoukai.y" };\r
+               \r
+               HashMap<PostMode, String[]> keys = new HashMap<PostMode, String[]>();\r
+               keys.put(PostMode.ADD_CMD,  joinArrays( pkeys1, pkeys3, pkeys4, null ));\r
+               keys.put(PostMode.ADD_EXEC, joinArrays( pkeys3, pkeys5, null, null ));\r
+               keys.put(PostMode.UPD_CMD,  joinArrays( pkeys1, pkeys2, pkeys3, pkeys7 ));\r
+               keys.put(PostMode.UPD_EXEC, joinArrays( pkeys2, pkeys3, pkeys5, null ));\r
+               keys.put(PostMode.DEL_CMD,  joinArrays( pkeys1, pkeys2, pkeys3, pkeys6 ));\r
+               keys.put(PostMode.DEL_EXEC, joinArrays( pkeys2, pkeys3, pkeys5, null ));\r
+               keys.put(PostMode.ERR_OK,   joinArrays( pkeys8, null, null, null ));\r
+               \r
+               return keys;\r
+       }\r
+       \r
+       /*******************************************************************************\r
+        * DIGAで利用できる各種選択肢を取得する\r
+        ******************************************************************************/\r
+       \r
+       private boolean getDigaRecordSetting() {\r
+               \r
+               System.out.println("Run: getDigaRecordSetting");\r
+               \r
+               int ret = 0 ;\r
+               for ( int cnt=1; (ret = _getDigaRecordSetting()) < 0 && cnt <= get_com_try_count(); cnt++ ) {\r
+                       if ( cnt < get_com_try_count() ) {\r
+                               reportProgress(String.format("+設定情報の取得を再試行します (%d回中%d回目)",(get_com_try_count()-1),cnt));\r
+                               CommonUtils.milSleep(WAIT_FOR_LOGIN);\r
+                       }\r
+               }\r
+               return (ret == 0);\r
+       }\r
+       \r
+       private int _getDigaRecordSetting() {\r
+               \r
+               // 旧リストは全部削除\r
+               vrate.clear();\r
+               chvalue.clear();\r
+               \r
+               // リクエスト発行 \r
+               String[] d = reqPOST("http://"+getIPAddr()+":"+getPortNo()+"/cgi-bin/dvdr/dvdr_ctrl.cgi", "cCMD_RSVADD.x=31&cCMD_RSVADD.y=8", null);\r
+               String response = d[1];\r
+                       \r
+               if (response == null) {\r
+                       // エラーになった場合\r
+                       System.out.println(ERRID+"レコーダから設定情報が返却されませんでした.");\r
+                       return(-99);\r
+               }\r
+               \r
+               if ( response.contains(DIGAMSG_WAITFORLOGIN) ) {\r
+                       // リトライしてね\r
+                       if (getDebug()) System.out.println(DBGID+DIGAMSG_WAITFORLOGIN);\r
+                       return(-1);\r
+               }\r
+               if ( response.contains(DIGAMSG_PLEASELOGIN) ) {\r
+                       // 再ログインしてね\r
+                       if (getDebug()) System.out.println(DBGID+DIGAMSG_PLEASELOGIN);\r
+                       return(-2);\r
+               }\r
+               \r
+               // 画質\r
+               setSettingVrate(vrate,response);\r
+               if ( vrate.size() == 0 ) {\r
+                       System.out.println(ERRID+"録画画質の選択肢を取得できませんでした。録画先ドライブにHDD以外が選択されているなどが考えられます.");\r
+                       return(-99);\r
+               }\r
+               TVSsave(vrate, vrateTFile);\r
+               \r
+               // チャンネルの種類\r
+               setSettingChCodeValue(chvalue,response);\r
+               TVSsave(chvalue, chValueTFile);\r
+               \r
+               return(0);\r
+       }\r
+\r
+       /*******************************************************************************\r
+        * HTMLから画質設定を抽出する\r
+        ******************************************************************************/\r
+\r
+       protected void setSettingVrate(ArrayList<TextValueSet> vr, String response) {\r
+               vr.clear();\r
+               Matcher ma = Pattern.compile(" name=\"cRSPD1\">([\\s\\S]+?)</select>").matcher(response);\r
+               if (ma.find()) {\r
+                       //\r
+                       //ArrayList<TextValueSet> hdd = new ArrayList<TextValueSet>();\r
+                       Matcher mb = Pattern.compile("<option value=\"(.+?)\".*?>(&nbsp;)*(.+?)\\(HDD\\)</option>").matcher(ma.group(1));       // 謎の&nbsp;\r
+                       while (mb.find()) {\r
+                               TextValueSet t = new TextValueSet();\r
+                               t.setText(mb.group(3));\r
+                               t.setValue(mb.group(1));\r
+                               vr.add(t);\r
+                       }\r
+                       if ( vr.size() == 0 ) {\r
+                               return;\r
+                       }\r
+                       \r
+                       //\r
+                       ArrayList<TextValueSet> usb = new ArrayList<TextValueSet>();\r
+                       mb = Pattern.compile("<option value=\"(.+?)\".*?>(&nbsp;)*(.+?)\\(USB\\)</option>").matcher(ma.group(1));       // 謎の&nbsp;\r
+                       while (mb.find()) {\r
+                               TextValueSet t = new TextValueSet();\r
+                               t.setText(mb.group(3)+"(USB)");\r
+                               t.setValue(mb.group(1));\r
+                               usb.add(t);\r
+                       }\r
+                       if ( usb.size() == 0 ) {\r
+                               for ( TextValueSet h : vr ) {\r
+                                       TextValueSet t = new TextValueSet();\r
+                                       t.setText(h.getText()+"(USB)");\r
+                                       t.setValue(String.valueOf(Integer.valueOf(h.getValue())+200));\r
+                                       usb.add(t);\r
+                               }\r
+                       }\r
+                       for ( TextValueSet t : usb ) {\r
+                               vr.add(t);\r
+                       }\r
+               }\r
+               TextValueSet t = new TextValueSet();\r
+               \r
+               t.setText(ITEM_VIDEO_TYPE_NONE);\r
+               t.setValue(VALUE_VIDEO_TYPE_NONE);\r
+               vr.add(t);\r
+       }\r
+       \r
+       /*******************************************************************************\r
+        * HTMLからCHコードを抽出する\r
+        ******************************************************************************/\r
+       \r
+       protected void setSettingChCodeValue(ArrayList<TextValueSet> cv, String response) {\r
+               cv.clear();\r
+               Matcher ma = Pattern.compile(" name=\"cCHSRC\">([\\s\\S]+?)</select>").matcher(response);\r
+               if (ma.find()) {\r
+                       Matcher mb = Pattern.compile("<option value=\"(.+?)\".*?>(.+?)</option>").matcher(ma.group(1));\r
+                       while (mb.find()) {\r
+                               TextValueSet t = new TextValueSet();\r
+                               t.setText(mb.group(2));\r
+                               t.setValue(mb.group(1));\r
+                               cv.add(t);\r
+                       }\r
+               }\r
+       }\r
+       \r
+       /*******************************************************************************\r
+        * HTMLから各種設定を抽出する\r
+        ******************************************************************************/\r
+       \r
+       private void setSettingEncoder(ArrayList<TextValueSet> enc) {\r
+               enc.clear();\r
+               if ( getTunerNum() >= 2 ) {\r
+                       for ( int i=1; i<=getTunerNum(); i++ ) {\r
+                               TextValueSet t = new TextValueSet();\r
+                               t.setText("D"+i);\r
+                               t.setValue("D"+i);\r
+                               enc.add(t);\r
+                       }\r
+               }\r
+       }\r
+       \r
+       /*******************************************************************************\r
+        * 予約種別は固定\r
+        ******************************************************************************/\r
+       \r
+       private void setSettingRecType(ArrayList<TextValueSet> tvs) {\r
+               tvs.clear();\r
+               add2tvs(tvs,ITEM_REC_TYPE_PROG,VALUE_REC_TYPE_PROG);\r
+               add2tvs(tvs,ITEM_REC_TYPE_EPG,VALUE_REC_TYPE_EPG);\r
+       }\r
+\r
+       /*******************************************************************************\r
+        * 予約のタイトル一覧を取得する。詳細は{@link #getDigaReserveDetail}で。\r
+        ******************************************************************************/\r
+       \r
+       private int getDigaReserveList(ArrayList<ReserveList> newReserveList, ArrayList<ReserveList> oldReserveList) {\r
+               \r
+               System.out.println("Run: getDigaReserveList");\r
+\r
+               // リクエスト発行\r
+               String[] d = reqDigaPOST("http://"+this.getIPAddr()+":"+this.getPortNo()+"/cgi-bin/dvdr/dvdr_ctrl.cgi", "cCMD_RSVLST.x=39&cCMD_RSVLST.y=16", null);\r
+               String response = d[1];\r
+               if (response == null) {\r
+                       // エラーになった場合\r
+                       errmsg = "予約一覧を取得できませんでした.";\r
+                       return(RETCODE_FATAL);\r
+               }\r
+               \r
+               int n = _getDigaReserveList(newReserveList, response);\r
+\r
+               // タイトル自動補完フラグなど本体からは取得できない情報を引き継ぐ\r
+               if ( oldReserveList != null ) {\r
+                       for ( ReserveList oldr : oldReserveList ) {\r
+                               for ( ReserveList newr : newReserveList ) {\r
+                                       if ( oldr.getId().equals(newr.getId()) ) {\r
+                                               copyAttribute(newr,oldr);\r
+                                               newr.setTitle(oldr.getTitle());\r
+                                               newr.setTitlePop(oldr.getTitlePop());\r
+                                               break;\r
+                                       }\r
+                               }\r
+                       }\r
+               }\r
+               \r
+               return n;\r
+       }\r
+       \r
+       private int _getDigaReserveList(ArrayList<ReserveList> newReserveList, String response) {\r
+               \r
+               // 予約詳細を作る\r
+               Matcher ma = Pattern.compile("<tr class=\".*?\">(.+?)</tr>",Pattern.DOTALL).matcher(response);\r
+               while ( ma.find() ) {\r
+                       \r
+                       ReserveList reqr = new ReserveList();\r
+                       \r
+                       // 予約内容の部分を切り出す\r
+                       String id = null;\r
+                       String date = null;\r
+                       int wday = 0;\r
+                       String channel = null;\r
+                       String ch_name = null;\r
+                       String ahh = null;\r
+                       String amm = null;\r
+                       String zhh = null;\r
+                       String zmm = null;\r
+                       String rec_min = null;\r
+                       String rec_mode = null;\r
+                       \r
+                       boolean pursues = false;\r
+                       boolean exec = true;\r
+                       boolean tunershort = false;\r
+                       String rec_type = ITEM_REC_TYPE_PROG;\r
+                       \r
+                       boolean repeat_everyday = false;\r
+                       boolean repeat_mon2fri = false;\r
+                       boolean repeat_mon2sat = false;\r
+                       boolean repeat_wday = false;\r
+                       boolean repeat_none = false;\r
+\r
+                       Matcher mb = Pattern.compile("<td(.*?)</td>",Pattern.DOTALL).matcher(ma.group(1));\r
+                       Matcher mc = null;\r
+                       for ( int i=0; mb.find(); i++ ) {\r
+                               switch (i) {\r
+                               case 0:         // 予約ID\r
+                                       mc = Pattern.compile("\\?ANC_RSVLSTNO=(\\d+)&",Pattern.DOTALL).matcher(mb.group(1));\r
+                                       if ( mc.find() ) {\r
+                                               id = mc.group(1);\r
+                                       }\r
+                                       break;\r
+                               case 2:         // 日付\r
+                                       mc = Pattern.compile(">\\s*(\\d+)/(\\d+)\\((.)\\)",Pattern.DOTALL).matcher(mb.group(1));\r
+                                       if ( mc.find() ) {\r
+                                               wday = CommonUtils.getWday(mc.group(3));\r
+                                               date = CommonUtils.getDateByMD(Integer.valueOf(mc.group(1)), Integer.valueOf(mc.group(2)), wday, true);\r
+                                       }\r
+                                       break;\r
+                               case 3:         // CH\r
+                                       mc = Pattern.compile(">\\s*(.+?)\\s*$",Pattern.DOTALL).matcher(mb.group(1));\r
+                                       if ( mc.find() ) {\r
+                                               String recChName = CommonUtils.unEscape(mc.group(1)).trim();\r
+                                               channel = cc.getCH_REC2CODE(recChName);\r
+                                               if ( channel != null ) {\r
+                                                       ch_name = cc.getCH_CODE2WEB(channel);\r
+                                               }\r
+                                       }\r
+                                       break;\r
+                               case 4:         // 時刻\r
+                                       mc = Pattern.compile(">\\s*(\\d+):(\\d+)~((\\d+):(\\d+)|未定)",Pattern.DOTALL).matcher(mb.group(1));\r
+                                       if ( mc.find() ) {\r
+                                               if ( mc.group(4) == null ) {\r
+                                                       // 終了時刻='未定(fefe)'の場合はcopyattributes()で過去情報から補完するか、または2時間後に仮置き\r
+                                                       System.out.println(DBGID+"終了時刻がみつかりません");\r
+                                                       String[] db = _hhmm2hhmm_min(mc.group(1)+":"+mc.group(2),"00:00");\r
+                                                       ahh = db[0];\r
+                                                       amm = db[1];\r
+                                               }\r
+                                               else {\r
+                                                       String[] db = _hhmm2hhmm_min(mc.group(1)+":"+mc.group(2),mc.group(4)+":"+mc.group(5));\r
+                                                       ahh = db[0];\r
+                                                       amm = db[1];\r
+                                                       zhh = db[2];\r
+                                                       zmm = db[3];\r
+                                                       rec_min = db[4];\r
+                                               }\r
+                                       }\r
+                                       break;\r
+                               case 5:         // 録画モード\r
+                                       mc = Pattern.compile(">\\s*(.+?)\\s*<").matcher(mb.group(1));\r
+                                       if ( mc.find() ) {\r
+                                               rec_mode = CommonUtils.unEscape(mc.group(1)).trim().replaceFirst("\\(HDD\\)", "");\r
+                                       }\r
+                                       break;\r
+                               case 6:         // 補足情報\r
+                                       if ( mb.group(1).contains("毎日") ) {\r
+                                               repeat_everyday = true;\r
+                                       }\r
+                                       else if ( mb.group(1).contains("月金") ) {\r
+                                               repeat_mon2fri = true;\r
+                                       }\r
+                                       else if ( mb.group(1).contains("月土") ) {\r
+                                               repeat_mon2sat = true;\r
+                                       }\r
+                                       else if ( mb.group(1).contains("毎週") || mb.group(1).contains("曜日指定") ) {\r
+                                               repeat_wday = true;\r
+                                       }\r
+                                       else {\r
+                                               repeat_none = true;\r
+                                       }\r
+                                       \r
+                                       if ( mb.group(1).contains("実行切") ) {\r
+                                               exec = false;\r
+                                       }\r
+                                       if ( mb.group(1).contains("重複") ) {\r
+                                               tunershort = true;\r
+                                       }\r
+                                       if ( mb.group(1).contains("番組予約") ) {\r
+                                               pursues = true;\r
+                                               rec_type = ITEM_REC_TYPE_EPG;\r
+                                       }\r
+                                       break;\r
+                               }\r
+                       }\r
+                       \r
+                       // 予約情報更新\r
+                       reqr.setId(id);\r
+                       \r
+                       reqr.setAhh(ahh);\r
+                       reqr.setAmm(amm);\r
+                       \r
+                       reqr.setZhh(zhh);\r
+                       reqr.setZmm(zmm);\r
+                       reqr.setRec_min(rec_min);\r
+                       \r
+                       if ( repeat_everyday ) {\r
+                               // 毎日\r
+                               reqr.setRec_pattern(HDDRecorder.RPTPTN[HDDRecorder.RPTPTN_ID_EVERYDAY]);\r
+                               reqr.setRec_pattern_id(HDDRecorder.RPTPTN_ID_EVERYDAY);\r
+                       }\r
+                       else if ( repeat_mon2sat ) {\r
+                               // 毎月~土\r
+                               reqr.setRec_pattern(HDDRecorder.RPTPTN[HDDRecorder.RPTPTN_ID_MON2SAT]);\r
+                               reqr.setRec_pattern_id(HDDRecorder.RPTPTN_ID_MON2SAT);\r
+                       }\r
+                       else if ( repeat_mon2fri ) {\r
+                               // 毎月~金\r
+                               reqr.setRec_pattern(HDDRecorder.RPTPTN[HDDRecorder.RPTPTN_ID_MON2FRI]);\r
+                               reqr.setRec_pattern_id(HDDRecorder.RPTPTN_ID_MON2FRI);\r
+                       }\r
+                       else if ( repeat_wday && wday > 0 ) {\r
+                               // 毎週\r
+                               reqr.setRec_pattern(HDDRecorder.RPTPTN[wday-1]);\r
+                               reqr.setRec_pattern_id(wday-1);\r
+                       }\r
+                       else {\r
+                               // 単日\r
+                               reqr.setRec_pattern(date);\r
+                               reqr.setRec_pattern_id(HDDRecorder.RPTPTN_ID_BYDATE);\r
+                       }\r
+                       if ( zhh != null && zmm != null ) {\r
+                               reqr.setRec_nextdate(CommonUtils.getNextDate(reqr));\r
+                               getStartEndDateTime(reqr);\r
+                       }\r
+                       else {\r
+                               // 終了時刻="未定"の場合(setRec_nextdateとかは↓の中で)\r
+                               setAttributesDiga(reqr,null);\r
+                       }\r
+                       \r
+                       if ( pursues && ! repeat_none ) {\r
+                               reqr.setAutoreserved(true);\r
+                       }\r
+                       \r
+                       reqr.setRec_mode(rec_mode);\r
+                       reqr.setTitle(MISS_TITLE);\r
+                       reqr.setTitlePop("");\r
+                       reqr.setChannel(channel);\r
+                       reqr.setCh_name(ch_name);\r
+                       \r
+                       reqr.setPursues(pursues);\r
+                       reqr.setExec(exec);\r
+                       reqr.setContentId(null);\r
+                       reqr.setRec_audio(rec_type);\r
+\r
+                       reqr.setTunershort(tunershort);\r
+\r
+                       // 予約情報を保存\r
+                       newReserveList.add(reqr);\r
+               }\r
+               \r
+               return(RETCODE_SUCCESS);\r
+       }\r
+       \r
+       /*******************************************************************************\r
+        * 予約の詳細情報を取得する(全部)\r
+        ******************************************************************************/\r
+       private boolean getDigaReserveDetails(ArrayList<ReserveList> newReserveList, ArrayList<ReserveList> oldReserveList) {\r
+               \r
+               // 詳細情報の取得\r
+               System.out.println("========");\r
+               for (int i=0; i<oldReserveList.size(); i++) {\r
+                       \r
+                       ReserveList oldr = oldReserveList.get(i);\r
+                       ReserveList newr = new ReserveList();\r
+                       \r
+                       getDigaReserveDetail(newr, oldr.getId());\r
+                       \r
+                       // タイトル自動補完フラグなど本体からは取得できない情報を引き継ぐ\r
+                       copyAttributes(newr, getReserves());\r
+                       \r
+                       //\r
+                       if ( newr.getContentId() != null ) {\r
+                               StringBuilder dt = new StringBuilder();\r
+                               dt.append("■番組ID:");\r
+                               dt.append(newr.getContentId());\r
+                               dt.append("\n");\r
+                               String cid = dt.toString();\r
+                               if ( newr.getDetail() != null ) {\r
+                                       if ( newr.getDetail().contains("■番組ID:") ) {\r
+                                               newr.setDetail(newr.getDetail().replaceFirst("■番組ID:.*?\n", Matcher.quoteReplacement(cid)));\r
+                                       }\r
+                                       else {\r
+                                               newr.setDetail(newr.getDetail()+cid);\r
+                                       }\r
+                               }\r
+                               else {\r
+                                       newr.setDetail(cid);\r
+                               }\r
+                       }\r
+                       \r
+                       newReserveList.add(newr);\r
+\r
+                       reportProgress(String.format("[%s] %s\t%s\t%s\t%s:%s\t%s:%s\t%sm\t%s\t%s\t%s\t%s\t%s",\r
+                                       (i+1), newr.getId(), newr.getRec_pattern(), newr.getRec_nextdate(), newr.getAhh(), newr.getAmm(), newr.getZhh(),        newr.getZmm(),  newr.getRec_min(), newr.getRec_mode(), newr.getTitle(), newr.getChannel(), newr.getCh_name(), newr.getContentId()));\r
+               }\r
+               System.out.println("========");\r
+               \r
+               return true;\r
+       }\r
+       \r
+       /*******************************************************************************\r
+        * 予約の詳細情報を取得する(個別)\r
+        ******************************************************************************/\r
+       \r
+       private ReserveList getDigaReserveDetail(ReserveList reserve_d, String id) {\r
+\r
+               String response;\r
+               {\r
+                       String url = "http://"+this.getIPAddr()+":"+this.getPortNo()+"/cgi-bin/reserve_list.cgi";\r
+                       String pstr = "ANC_RSVLSTNO="+id+"&cDMYDT="+DMY_TIME;\r
+                       //String[] d = reqPOST(url,pstr,null);\r
+                       String[] d = reqGET(url+"?"+pstr,null);\r
+                       response = d[1];\r
+               }\r
+               if ( response == null ) {\r
+                       System.out.println(ERRID+"★");\r
+                       System.out.println(ERRID+"★ 予約情報を取得できませんでした。");\r
+                       System.out.println(ERRID+"★");\r
+                       return(null);\r
+               }\r
+               \r
+               // 予約日付\r
+               Matcher ma = Pattern.compile("name=\"cRVMD\" value=\"(\\d\\d)(\\d\\d)\"").matcher(response);\r
+               if (! ma.find()) {\r
+                       System.err.println(ERRID+"日付情報がみつかりません");\r
+                       return(null);\r
+               }\r
+               String[] da = _mmdd2yyyymmdd(ma.group(1), ma.group(2));\r
+               String yy = da[0];\r
+               String mm = da[1];\r
+               String dd = da[2];\r
+               \r
+               // 開始終了時刻と録画時間\r
+               ma = Pattern.compile("name=\"cRVHM1\" value=\"(\\d\\d)(\\d\\d)\"").matcher(response);\r
+               if (! ma.find()) {\r
+                       System.err.println(ERRID+"開始時刻がみつかりません");\r
+                       return(null);\r
+               }\r
+               String ahh = ma.group(1);\r
+               String amm = ma.group(2);\r
+               \r
+               String zhh = null;\r
+               String zmm = null;\r
+               String rec_min = null;\r
+               ma = Pattern.compile("name=\"cRVHM2\" value=\"(.+?)\"",Pattern.DOTALL).matcher(response);\r
+               if ( ! ma.find()) {\r
+                       System.err.println(ERRID+"終了時刻がみつかりません");\r
+                       return(null);\r
+               }\r
+               else {\r
+                       Matcher mb = Pattern.compile("^(\\d\\d)(\\d\\d)$").matcher(ma.group(1));\r
+                       if ( mb.find() ) {\r
+                               zhh = mb.group(1);\r
+                               zmm = mb.group(2);\r
+                               \r
+                               String[] db = _hhmm2hhmm_min(ahh+":"+amm,zhh+":"+zmm);\r
+                               ahh = db[0];\r
+                               amm = db[1];\r
+                               zhh = db[2];\r
+                               zmm = db[3];\r
+                               rec_min = db[4];\r
+                       }\r
+                       else {\r
+                               // 終了時刻='未定(fefe)'の場合はcopyattributes()で過去情報から補完するか、または2時間後に仮置き\r
+                               System.out.println(DBGID+"終了時刻がみつかりません");\r
+                       }\r
+               }\r
+               \r
+               // 画質\r
+               boolean pursues = false;\r
+               String rec_mode = getRsvInfoVrateDT(vrate, response);\r
+               if (rec_mode == null) {\r
+                       rec_mode = getRsvInfoVrateTR(vrate, response);\r
+                       if (rec_mode != null) {\r
+                               // 追跡あり?\r
+                               pursues = true;\r
+                       }\r
+               }\r
+               if ( rec_mode == null ) {\r
+                       System.err.println(ERRID+"画質情報がみつかりません");\r
+                       return(null);\r
+               }\r
+               \r
+               // チャンネル\r
+               String ch_source = "";\r
+               String ch_number = "";\r
+               String ch_name = "";\r
+               String channel = "";\r
+               ma = Pattern.compile(" name=\"cCHSRC\">(.+?)</select>",Pattern.DOTALL).matcher(response);\r
+               if (ma.find()) {\r
+                       // 手動\r
+                       Matcher mb = Pattern.compile("<option value=\"([^\"]+?)\" selected>",Pattern.DOTALL).matcher(ma.group(1));\r
+                       Matcher mc = Pattern.compile("name=\"cCHNUM\" value=\"(.+?)\"",Pattern.DOTALL).matcher(response);\r
+                       if ( ! mb.find() || ! mc.find()) {\r
+                               System.err.println(ERRID+"チャンネル設定がみつかりません");\r
+                               return(null);\r
+                       }\r
+                       else {\r
+                               ch_source = mb.group(1);\r
+                               ch_number = mc.group(1);\r
+                               \r
+                               String ch_source_text = value2text(chvalue, ch_source);\r
+                               if ( ! ch_source_text.equals("") ) {\r
+                                       channel = ch_source_text+" "+ch_number;\r
+                                       ch_name = cc.getCH_REC2WEB(channel);\r
+                               }\r
+                               else {\r
+                                       System.out.println(DBGID+"認識できない放送波種別: "+ch_source);\r
+                               }\r
+                       }\r
+               }\r
+               else {\r
+                       // 自動\r
+                       Matcher mb = Pattern.compile("<input type=\"hidden\" name=\"cCHSRC\" value=\"(.+?)\">\\s*(.+?)(&nbsp;)*\\s*<").matcher(response);\r
+                       Matcher mc = Pattern.compile("name=\"cCHNUM\" value=\"(.+?)\"").matcher(response);\r
+                       if ( ! mb.find() || ! mc.find()) {\r
+                               System.err.println(ERRID+"チャンネル設定がみつかりません");\r
+                               return(null);\r
+                       }\r
+                       else {\r
+                               ch_source = mb.group(1);\r
+                               ch_number = mc.group(1);\r
+                               \r
+                               String ch_source_text = value2text(chvalue, ch_source);\r
+                               if ( ! ch_source_text.equals("") ) {\r
+                                       channel = ch_source_text+" "+ch_number;\r
+                                       ch_name = cc.getCH_REC2WEB(channel);\r
+                               }\r
+                               else {\r
+                                       System.out.println(DBGID+"認識できない放送波種別: "+ch_source);\r
+                                       \r
+                                       {\r
+                                               // 通常は選択できない特殊なチャンネルコード\r
+                                               TextValueSet t = new TextValueSet();\r
+                                               t.setText(mb.group(2));\r
+                                               t.setValue(mb.group(1));\r
+                                               chvalue.add(t);\r
+                                               TVSsave(chvalue, chValueTFile);\r
+                                       }\r
+                                       \r
+                                       channel = mb.group(2)+" "+mc.group(1);\r
+                                       ch_name = cc.getCH_REC2WEB(channel);\r
+                               }\r
+                       }\r
+               }\r
+               /*\r
+               if ( channel.length() == 0 ) {\r
+                       System.err.println("CHコードがみつかりませんでした.");\r
+                       return(null);\r
+               }\r
+               else if ( ch_name.length() == 0 ) {\r
+                       System.err.println("CHコードをWeb番組表の放送局名に変換できませんでした: CHコード="+channel);\r
+                       return(null);\r
+               }\r
+               */\r
+               \r
+               // パターン\r
+               ma = Pattern.compile("<td width=\"92\" class=\"s_F16_Cffffff\">毎週</td>(.+?)</tr>",Pattern.DOTALL).matcher(response);\r
+               if ( ! ma.find()) {\r
+                       System.err.println(ERRID+"予約パターン情報がみつかりません");\r
+                       return(null);\r
+               }\r
+               String rec_pattern = _recpatternDG2RDstr(ma.group(1), yy, mm, dd, ahh);\r
+               \r
+               // タイトル\r
+               ma = Pattern.compile("name=\"cTHEX\" value=\"([^\"]*?)\"").matcher(response);\r
+               if ( ! ma.find()) {\r
+                       System.err.println(ERRID+"タイトルがみつかりません");\r
+                       return(null);\r
+               }\r
+               String title = CommonUtils.unEscape(ma.group(1));\r
+               \r
+               // 予約実行\r
+               boolean exec = true;\r
+               ma = Pattern.compile(" name=\"cTIMER\">([\\s\\S]+?)</select>").matcher(response);\r
+               if (ma.find()) {\r
+                       Matcher mb = Pattern.compile("<option value=\"(.+?)\" selected>").matcher(ma.group(1));\r
+                       if ( ! mb.find()) {\r
+                               System.err.println(ERRID+"実行情報がみつかりません");\r
+                               return(null);\r
+                       }\r
+                       else {\r
+                                exec = (mb.group(1).equals("0"))?(false):(true);\r
+                       }\r
+               }\r
+               \r
+               // 番組ID\r
+               String contentid = null;\r
+               boolean miss = false;\r
+               ma = Pattern.compile(" name=\"cRHEXEX\" value=\"..([0-9a-fA-Z]{4})([0-9a-fA-Z]{12})").matcher(response);\r
+               if ( ma.find() ) {\r
+                       int evid = Integer.decode("0x"+ma.group(1));\r
+                       if ( evid != DIGAEVID_NONE && evid != DIGAEVID_PROGRSV ) {\r
+                               if ( ContentIdEDCB.decodeChId(ma.group(2)) ) {\r
+                                       contentid = ContentIdEDCB.getContentId(evid);\r
+                               }\r
+                               if ( evid == DIGAEVID_CANNOTFOLLOW ) {\r
+                                       // DIGAの番組表が更新されていないか、マッチする番組が見つかっていない。\r
+                                       miss = true;\r
+                               }\r
+                       }\r
+               }\r
+               \r
+               // 予約情報更新\r
+               reserve_d.setId(id);\r
+               \r
+               reserve_d.setAhh(ahh);\r
+               reserve_d.setAmm(amm);\r
+               \r
+               reserve_d.setZhh(zhh);\r
+               reserve_d.setZmm(zmm);\r
+               reserve_d.setRec_min(rec_min);\r
+               \r
+               reserve_d.setRec_pattern(rec_pattern);\r
+               reserve_d.setRec_pattern_id(getRec_pattern_Id(rec_pattern));\r
+               if ( zhh != null && zmm != null ) {\r
+                       reserve_d.setRec_nextdate(CommonUtils.getNextDate(reserve_d));\r
+                       getStartEndDateTime(reserve_d);\r
+               }\r
+               \r
+               reserve_d.setRec_mode(rec_mode);\r
+               reserve_d.setTitle((miss?MISS_HDR:"")+title);\r
+               reserve_d.setTitlePop(TraceProgram.replacePop(title));\r
+               reserve_d.setChannel(channel);\r
+               reserve_d.setCh_name(ch_name);\r
+               \r
+               reserve_d.setPursues(pursues);\r
+               reserve_d.setExec(exec);\r
+               reserve_d.setContentId(contentid);\r
+               if ( contentid != null ) {\r
+                       reserve_d.setRec_audio(ITEM_REC_TYPE_EPG);\r
+                       if ( reserve_d.getRec_pattern_id() != HDDRecorder.RPTPTN_ID_BYDATE ) {\r
+                               // EPG予約で、かつ単日予約でないものは自動予約と思われる\r
+                               reserve_d.setAutoreserved(true);\r
+                       }\r
+               }\r
+               else {\r
+                       reserve_d.setRec_audio(ITEM_REC_TYPE_PROG);\r
+               }\r
+               \r
+               return(reserve_d);\r
+       }\r
+\r
+       /*******************************************************************************\r
+        * 番組の画質設定を取得する(番組追従なし)\r
+        ******************************************************************************/\r
+\r
+       protected String getRsvInfoVrateDT(ArrayList<TextValueSet> vr, String response) {\r
+               // 手動予約\r
+               Matcher ma = Pattern.compile(" name=\"cRSPD1\">([\\s\\S]+?)</select>").matcher(response);\r
+               if (ma.find()) {\r
+                       Matcher mb = Pattern.compile("<option value=\"(.+?)\" selected>").matcher(ma.group(1));\r
+                       if ( ! mb.find()) {\r
+                               return (null);\r
+                       }\r
+                       else {\r
+                               String rec_mode = value2text(vr, mb.group(1));\r
+                               if ( rec_mode.equals("") ) {\r
+                                       System.err.println("未定義の画質設定が検出されました(手動予約): "+mb.group(1));\r
+                                       return null;\r
+                               }\r
+                               return rec_mode;\r
+                       }\r
+               }\r
+               return null;\r
+       }\r
+       \r
+       /*******************************************************************************\r
+        * 番組の画質設定を取得する(番組追従あり)\r
+        ******************************************************************************/\r
+       \r
+       protected String getRsvInfoVrateTR(ArrayList<TextValueSet> vr, String response) {\r
+               // 番組表予約(追跡あり)\r
+               Matcher ma = Pattern.compile("<input type=\"hidden\" name=\"cRSPD1\" value=\"(.+?)\">").matcher(response);\r
+               if (ma.find()) {\r
+                       String rec_mode = value2text(vr, ma.group(1));\r
+                       if ( rec_mode.equals("") ) {\r
+                               System.err.println("未定義の画質設定が検出されました(番組表予約): "+ma.group(1));\r
+                               return null;\r
+                       }\r
+                       return rec_mode;\r
+               }\r
+               ma = Pattern.compile("<input type=\"hidden\" name=\"cRSPD1\">").matcher(response);\r
+               if (ma.find()) {\r
+                       // スカパーHD予約\r
+                       return ITEM_VIDEO_TYPE_NONE;\r
+               }\r
+               return(null);\r
+       }\r
+       \r
+\r
+       /*******************************************************************************\r
+        * 1.予約登録前と予約登録後の予約一覧を比較して、追加された予約IDをみつける(新規予約のみ)\r
+        * 2.既存の予約一覧にないものがあったら取り込んでおく\r
+        * \r
+        * @param newReserveList 新しく作る予約一覧\r
+        * @param preReserveList 登録前予約一覧\r
+        * @param aftReserveList 登録後予約一覧\r
+        * @param curReserveList 既存の予約一覧\r
+        * @return 新規予約があればそれを返す。それ以外はnull。\r
+        ******************************************************************************/\r
+       private ReserveList refreshReserveList(ArrayList<ReserveList> newReserveList, ArrayList<ReserveList> preReserveList, ArrayList<ReserveList> aftReserveList, ArrayList<ReserveList> curReserveList) {\r
+               \r
+               ReserveList newr = null;\r
+               \r
+               for ( ReserveList ar : aftReserveList ) {\r
+                       \r
+                       ReserveList nr = null;\r
+                       \r
+                       if ( preReserveList != null ) {\r
+                               // 新規登録の場合\r
+                               for ( ReserveList pr : preReserveList ) {\r
+                                       if ( ar.getId().equals(pr.getId()) ) {\r
+                                               nr = ar;\r
+                                               break;\r
+                                       }\r
+                               }\r
+                       }\r
+                       else {\r
+                               // 更新の場合\r
+                               nr = ar;\r
+                       }\r
+                       \r
+                       if ( nr != null ) {\r
+                               // 新規に登録した予約ではないね\r
+                               for ( ReserveList cr : curReserveList ) {\r
+                                       if ( ar.getId().equals(cr.getId()) ) {\r
+                                               // 前から鯛ナビにあった予約だね\r
+                                               nr = cr;\r
+                                               \r
+                                               // 実行ON/OFF\r
+                                               nr.setExec(ar.getExec());\r
+                                               // 重複\r
+                                               nr.setTunershort(ar.getTunershort());\r
+                                               /*\r
+                                               // 開始終了日時(そのうち追加したい)\r
+                                               String nrdt = nr.getRec_nextdate()+" "+nr.getAhh()+":"+nr.getAmm();\r
+                                               String ardt = ar.getRec_nextdate()+" "+ar.getAhh()+":"+ar.getAmm();\r
+                                               if ( CommonUtils.getCompareDateTime(nrdt,ardt) != 0 || ! nr.getRec_min().equals(ar.getRec_min()) ) {\r
+                                                       nr.setAhh(ar.getAhh());\r
+                                                       nr.setAmm(ar.getAmm());\r
+                                                       nr.setZhh(ar.getZhh());\r
+                                                       nr.setZmm(ar.getZmm());\r
+                                                       nr.setRec_min(ar.getRec_min());\r
+                                                       getStartEndDateTime(nr);\r
+                                                       nr.setRec_nextdate(cr.getRec_nextdate());\r
+                                               }\r
+                                               */\r
+                                               \r
+                                               break;\r
+                                       }\r
+                               }\r
+                               \r
+                               if ( nr == ar ) {\r
+                                       // 詳細とってくるわー\r
+                                       reportProgress("DIGAで登録された予約の詳細情報を1件取り込んでいます。");\r
+                                       if ( getDigaReserveDetail(nr, nr.getId()) == null ) {\r
+                                               // 情報を取得できなかったので、鯛ナビの情報をそのままで\r
+                                       }\r
+                               }\r
+                               newReserveList.add(nr);\r
+                       }\r
+                       else {\r
+                               // これは新規!\r
+                               newr = ar;\r
+                       }\r
+               }\r
+               \r
+               return newr;\r
+       }\r
+\r
+       /*******************************************************************************\r
+        * DIGAから取得した繰り返し予約の値を鯛ナビの内部形式に変換する\r
+        ******************************************************************************/\r
+       \r
+       private static final int RPTN_SUN = 0x01;\r
+       private static final int RPTN_MON = 0x02;\r
+       private static final int RPTN_TUE = 0x04;\r
+       private static final int RPTN_WED = 0x08;\r
+       private static final int RPTN_THU = 0x10;\r
+       private static final int RPTN_FRI = 0x20;\r
+       private static final int RPTN_SAT = 0x40;\r
+       private static final int RPTN_ALL = 0x80;\r
+       private static final int RPTN_MON2THU = (RPTN_MON|RPTN_TUE|RPTN_WED|RPTN_THU);\r
+       private static final int RPTN_MON2FRI = (RPTN_MON2THU|RPTN_FRI);\r
+       private static final int RPTN_MON2SAT = (RPTN_MON2FRI|RPTN_SAT);\r
+       private static final int RPTN_MON2SUN = (RPTN_MON2SAT|RPTN_SUN);\r
+       private static final int RPTN_MON2THU_LN = (RPTN_TUE|RPTN_WED|RPTN_THU|RPTN_FRI);\r
+       private static final int RPTN_MON2FRI_LN = (RPTN_MON2THU_LN|RPTN_SAT);\r
+       private static final int RPTN_MON2SAT_LN = (RPTN_MON2FRI_LN|RPTN_SUN);\r
+       \r
+       private String _recpatternDG2RDstr(String source, String yy, String mm, String dd, String Ahh)\r
+       {\r
+               int rptn = 0;\r
+               int rptcnt = 0;\r
+               for ( int c=0; c < DIGA_WDPTNSTR.length; c++ ) {\r
+                       Matcher ma = Pattern.compile("name=\""+DIGA_WDPTNSTR[c]+"\" value=\"[^\"]+?\" checked").matcher(source);\r
+                       if ( ma.find() ) {\r
+                               rptn |= (0x01<<c);\r
+                               rptcnt++;\r
+                       }\r
+               }\r
+               \r
+               if ( rptn != 0 ) {\r
+                       // 24:00~28:59開始は深夜帯\r
+                       boolean isln = CommonUtils.isLateNight(Ahh);\r
+                       \r
+                       if ( rptn == RPTN_MON2SAT || (isln && rptn == RPTN_MON2SAT_LN) ) {\r
+                               return HDDRecorder.RPTPTN[9];   // "毎月~土";\r
+                       }\r
+                       else if ( rptn == RPTN_MON2FRI || (isln && rptn == RPTN_MON2FRI_LN) ) {\r
+                               return HDDRecorder.RPTPTN[8];   // "毎月~金";\r
+                       }\r
+                       else if ( rptn == RPTN_MON2THU || (isln && rptn == RPTN_MON2THU_LN) ) {\r
+                               return HDDRecorder.RPTPTN[7];   // "毎月~木";\r
+                       }\r
+                       else if ( rptcnt == 1 && rptn < RPTN_ALL ) {\r
+                               for ( int c=0; c<7; c++ ) {\r
+                                       if ( (rptn & (0x01<<c)) != 0 ) {\r
+                                               return HDDRecorder.RPTPTN[c];   // "毎*曜日";\r
+                                       }\r
+                               }\r
+                       }\r
+                       else if ( (rptn & RPTN_ALL) == RPTN_ALL || (rptn & RPTN_MON2SUN) == RPTN_MON2SUN ) {\r
+                               return HDDRecorder.RPTPTN[10];  // 毎日\r
+                       }\r
+                       \r
+                       // 未対応のパターンは単日扱いとなる\r
+               }\r
+\r
+               GregorianCalendar cal = new GregorianCalendar(\r
+                               Integer.valueOf(yy),\r
+                               Integer.valueOf(mm)-1,\r
+                               Integer.valueOf(dd));\r
+               \r
+               return CommonUtils.getDate(cal, true);\r
+       }\r
+\r
+       /*******************************************************************************\r
+        * DIGAにログインする\r
+        ******************************************************************************/\r
+       \r
+       private boolean doLogin() {\r
+               \r
+               reportProgress("DIGAにログインします("+getIPAddr()+":"+getPortNo()+").");\r
+\r
+               boolean isLoggedin = false;\r
+               for ( int cnt=1; (isLoggedin = _doLogin()) == false && cnt <= get_com_try_count(); cnt++ ) {\r
+                       if ( cnt < get_com_try_count() ) {\r
+                               reportProgress(String.format("+ログインを再試行します (%d回中%d回目)",(get_com_try_count()-1),cnt));\r
+                               CommonUtils.milSleep(WAIT_FOR_LOGIN);\r
+                       }\r
+               }\r
+               if ( ! isLoggedin ) {\r
+                       return false;\r
+               }\r
+               return true;\r
+       }\r
+       \r
+       private boolean _doLogin() {\r
+               \r
+               // ホームページ\r
+               String response;\r
+               {\r
+                       String[] d = reqGET("http://"+getIPAddr()+":"+getPortNo()+"/", null);\r
+                       response = d[1];\r
+                       \r
+                       if ( response == null ) {\r
+                               return false;\r
+                       }\r
+               }\r
+               Matcher ma = Pattern.compile("DMY_TIME=(\\d+?)'").matcher(response);\r
+               if (ma.find()) {\r
+                       DMY_TIME = ma.group(1);\r
+                       //System.out.println("DMY_TIME="+DMY_TIME);\r
+               }\r
+\r
+               // ログインフレーム\r
+               {\r
+                       String[] d = reqGET("http://"+getIPAddr()+":"+getPortNo()+"/cgi-bin/prevLogin.cgi?DMY_TIME="+DMY_TIME, null);\r
+                       response = d[1];\r
+               }\r
+               ma = Pattern.compile("nonce=\"(\\d+)\"").matcher(response);\r
+               if (ma.find()) {\r
+                       NONCE = ma.group(1);\r
+                       //System.out.println("nonce="+NONCE);\r
+                       \r
+                       try {\r
+                               MessageDigest md = MessageDigest.getInstance("MD5");\r
+                               byte[] bd = new String(NONCE+getPasswd()).getBytes();\r
+                               md.update(bd);\r
+                               DIGEST = b64.enc(md.digest());\r
+                               DIGEST = DIGEST.replaceAll("\\x2B", "%2B"); // replaces +\r
+                               DIGEST = DIGEST.replaceAll("\\x2F", "%2F"); // replaces /\r
+                               DIGEST = DIGEST.replaceAll("\\x3D", "%3D"); // replaces =\r
+                               //System.out.println("digest="+DIGEST);\r
+                       }\r
+                       catch (Exception e) {\r
+                               // 例外\r
+                               System.out.println("Exception: doLogin()");\r
+                       }\r
+               }\r
+               \r
+               // ログイン実行\r
+               {\r
+                       String[] d = reqPOST("http://"+this.getIPAddr()+":"+this.getPortNo()+"/cgi-bin/loginPsWd.cgi", "passwd=&nonce="+NONCE+"&digest="+DIGEST+"&cmd.x=42&cmd.y=10", null);\r
+                       response = d[1];\r
+               }\r
+               if (response.contains(DIGAMSG_LOGGEDIN)) {\r
+                       return(true);\r
+               }\r
+\r
+               return(false);\r
+       }\r
+       \r
+       /*******************************************************************************\r
+        * HTTPアクセスをまとめてみた\r
+        ******************************************************************************/\r
+       //\r
+       private String[] reqDigaGET(String uri, Hashtable<String, String>property) {\r
+               return reqDigaPOST(uri,null,property);\r
+       }\r
+       \r
+       private String[] reqDigaPOST(String uri, String pstr, Hashtable<String, String>property) {\r
+               String header = null;\r
+               String response = null;\r
+               for ( int cnt=1; cnt <= get_com_try_count(); cnt++ ) {\r
+\r
+                       String h = null;\r
+                       String r = null;\r
+                       if ( pstr == null ) {\r
+                               String[] d = reqGET(uri, property);\r
+                               h = d[0];\r
+                               r = d[1];\r
+                       }\r
+                       else {\r
+                               String[] d = reqPOST(uri, pstr, property);\r
+                               h = d[0];\r
+                               r = d[1];\r
+                       }\r
+\r
+                       if ( r.contains(DIGAMSG_PLEASELOGIN) ) {\r
+                               // ログイン失敗だからもうだめぽ\r
+                               break;\r
+                       }\r
+                       if ( ! r.contains(DIGAMSG_WAITFORLOGIN) && ! r.contains(DIGAMSG_BUSYNOW) ) {\r
+                               // いそがしいのでちょっとまって\r
+                               header = h;\r
+                               response = r;\r
+                               break;\r
+                       }\r
+\r
+                       if (getDebug()) {\r
+                               if (r.contains(DIGAMSG_WAITFORLOGIN)) System.out.println(DBGID+DIGAMSG_WAITFORLOGIN);\r
+                               else if (r.contains(DIGAMSG_BUSYNOW)) System.out.println(DBGID+DIGAMSG_BUSYNOW);\r
+                       }\r
+\r
+                       // リトライしてね\r
+                       if ( cnt < get_com_try_count() ) {\r
+                               reportProgress(String.format("+通信を再試行します  (%d回中%d回目)",(get_com_try_count()-1),cnt));\r
+                               CommonUtils.milSleep(WAIT_FOR_LOGIN);\r
+                       }\r
+               }\r
+               \r
+               return new String[] { header, response };\r
+       }\r
+       \r
+       \r
+       // 予約情報同士を比較する\r
+       private boolean isModified(ReserveList o, ReserveList n) {\r
+               if (\r
+                               ! o.getAhh().equals(n.getAhh()) ||\r
+                               ! o.getAmm().equals(n.getAmm()) ||\r
+                               ! o.getZhh().equals(n.getZhh()) ||\r
+                               ! o.getZmm().equals(n.getZmm()) ||\r
+                               ! o.getTitle().equals(n.getTitle()) ||\r
+                               false\r
+                               ) {\r
+                       return true;\r
+               }\r
+               return false;\r
+       }\r
+}\r
diff --git a/TinyBannavi/src/tainavi/PlugIn_RecDIGA_DMR_BZT710.java b/TinyBannavi/src/tainavi/PlugIn_RecDIGA_DMR_BZT710.java
new file mode 100644 (file)
index 0000000..9db283c
--- /dev/null
@@ -0,0 +1,30 @@
+package tainavi;\r
+\r
+\r
+/*\r
+ * \r
+ */\r
+\r
+public class PlugIn_RecDIGA_DMR_BZT710 extends PlugIn_RecDIGA_DMR_BWT2100 implements HDDRecorder,Cloneable {\r
+\r
+       @Override\r
+       public PlugIn_RecDIGA_DMR_BZT710 clone() {\r
+               return (PlugIn_RecDIGA_DMR_BZT710) super.clone();\r
+       }\r
+\r
+       /* 必須コード  - ここから */\r
+       \r
+       // 種族の特性\r
+       @Override\r
+       public String getRecorderId() { return "DIGA DMR-BZT710"; }\r
+       @Override\r
+       public RecType getType() { return RecType.RECORDER; }\r
+       \r
+       @Override\r
+       protected int get_com_try_count() { return 5; }\r
+       \r
+       public PlugIn_RecDIGA_DMR_BZT710() {\r
+               super();\r
+               this.setTunerNum(3);\r
+       }\r
+}\r
diff --git a/TinyBannavi/src/tainavi/PlugIn_RecDIGA_DMR_BZT720.java b/TinyBannavi/src/tainavi/PlugIn_RecDIGA_DMR_BZT720.java
new file mode 100644 (file)
index 0000000..9250d31
--- /dev/null
@@ -0,0 +1,71 @@
+package tainavi;\r
+\r
+import java.util.HashMap;\r
+\r
+\r
+/*\r
+ * \r
+ */\r
+\r
+public class PlugIn_RecDIGA_DMR_BZT720 extends PlugIn_RecDIGA_DMR_BWT2100 implements HDDRecorder,Cloneable {\r
+\r
+       @Override\r
+       public PlugIn_RecDIGA_DMR_BZT720 clone() {\r
+               return (PlugIn_RecDIGA_DMR_BZT720) super.clone();\r
+       }\r
+\r
+       /* 必須コード  - ここから */\r
+       \r
+       // 種族の特性\r
+       @Override\r
+       public String getRecorderId() { return "DIGA DMR-BZT720"; }\r
+       @Override\r
+       public RecType getType() { return RecType.RECORDER; }\r
+       \r
+       @Override\r
+       protected int get_com_try_count() { return 5; }\r
+       \r
+       public PlugIn_RecDIGA_DMR_BZT720() {\r
+               super();\r
+               this.setTunerNum(3);\r
+       }\r
+       \r
+       /*\r
+        * 公開メソッド\r
+        */\r
+       \r
+       /*\r
+        * 非公開メソッド\r
+        */\r
+       \r
+       @Override\r
+       protected HashMap<PostMode, String[]> getPostKeys() {\r
+               \r
+               String[] pkeys1a = { "cRVMD","cRVHM1","cRVHM2","cCHSRC","cCHNUM","cRSPD1" };\r
+               String[] pkeys1b = { "cTHEX","cTIMER" };\r
+               String[] pkeys1 = joinArrays(pkeys1a, DIGA_WDPTNSTR, pkeys1b, null);\r
+               String[] pkeys2 = { "cRVID","cRVORG","cRVORGEX","cRVORGEX2","cRVORGEX3" };\r
+               String[] pkeys3 = {     "cRPG","cRHEX","cTSTR","cRHEXEX" };\r
+               String[] pkeys4 = {     "RSV_FIX.x","RSV_FIX.y" };\r
+               String[] pkeys5 = {     "RSV_EXEC.x","RSV_EXEC.y" };\r
+               String[] pkeys6 = { "RSV_DEL.x","RSV_DEL.y" };\r
+               String[] pkeys7 = { "RSV_EDIT.x","RSV_EDIT.y" };\r
+               String[] pkeys8 = {     "cRPG","cERR","TTL_DRIVE","cRVID","cRHEX","cTSTR","cRHEXEX","Image_BtnRyoukai.x","Image_BtnRyoukai.y" };\r
+               String[] pkeys9 = { "cRECMODE" };\r
+               \r
+               HashMap<PostMode, String[]> keys = new HashMap<PostMode, String[]>();\r
+               \r
+               keys.put(PostMode.ADD_CMD,  joinArrays( pkeys1, pkeys3, pkeys4, null ));\r
+               keys.put(PostMode.ADD_EXEC, joinArrays( pkeys9, pkeys3, pkeys5, null ));\r
+               \r
+               keys.put(PostMode.UPD_CMD,  joinArrays( pkeys1, pkeys2, pkeys3, pkeys7 ));\r
+               keys.put(PostMode.UPD_EXEC, joinArrays( pkeys9, pkeys2, pkeys3, pkeys5 ));\r
+               \r
+               keys.put(PostMode.DEL_CMD,  joinArrays( pkeys1, pkeys2, pkeys3, pkeys6 ));\r
+               keys.put(PostMode.DEL_EXEC, joinArrays( pkeys9, pkeys2, pkeys3, pkeys5 ));\r
+               \r
+               keys.put(PostMode.ERR_OK,   joinArrays( pkeys8, null, null, null ));\r
+               \r
+               return keys;\r
+       }\r
+}\r
diff --git a/TinyBannavi/src/tainavi/PlugIn_RecGoogleCalendar.java b/TinyBannavi/src/tainavi/PlugIn_RecGoogleCalendar.java
new file mode 100644 (file)
index 0000000..a67b604
--- /dev/null
@@ -0,0 +1,454 @@
+package tainavi;\r
+\r
+import java.net.URL;\r
+import java.util.ArrayList;\r
+import java.util.Calendar;\r
+import java.util.GregorianCalendar;\r
+import java.util.regex.Matcher;\r
+import java.util.regex.Pattern;\r
+\r
+import com.google.gdata.client.calendar.CalendarQuery;\r
+import com.google.gdata.client.calendar.CalendarService;\r
+import com.google.gdata.data.DateTime;\r
+import com.google.gdata.data.Person;\r
+import com.google.gdata.data.PlainTextConstruct;\r
+import com.google.gdata.data.calendar.CalendarEventEntry;\r
+import com.google.gdata.data.calendar.CalendarEventFeed;\r
+import com.google.gdata.data.extensions.Recurrence;\r
+import com.google.gdata.data.extensions.Reminder;\r
+import com.google.gdata.data.extensions.Where;\r
+import com.google.gdata.data.extensions.When;\r
+import com.google.gdata.data.extensions.Reminder.Method;\r
+import com.google.gdata.util.RedirectRequiredException;\r
+\r
+/**\r
+ * <P>レコーダへの予約のついでにGoogleカレンダーに情報を登録するプラグインです。\r
+ * <P>通常の操作は行えません。\r
+ */\r
+public class PlugIn_RecGoogleCalendar extends HDDRecorderUtils implements HDDRecorder,Cloneable {\r
+\r
+       public PlugIn_RecGoogleCalendar clone() {\r
+               return (PlugIn_RecGoogleCalendar) super.clone();\r
+       }\r
+       \r
+       /* 必須コード  - ここから */\r
+       \r
+       /*******************************************************************************\r
+        * 種族の特性\r
+        ******************************************************************************/\r
+       \r
+       public String getRecorderId() { return "GoogleCalendar"; }\r
+       public RecType getType() { return RecType.CALENDAR; }\r
+       \r
+       @Override\r
+       public boolean isBackgroundOnly() { return true; }\r
+       \r
+       /*******************************************************************************\r
+        * 予約ダイアログなどのテキストのオーバーライド\r
+        ******************************************************************************/\r
+       \r
+       /*******************************************************************************\r
+        * 定数\r
+        ******************************************************************************/\r
+\r
+       private static final String[] wd = {"SU","MO","TU","WE","TH","FR","SA"};\r
+\r
+       /*******************************************************************************\r
+        * CHコード設定、エラーメッセージ\r
+        ******************************************************************************/\r
+       \r
+       public ChannelCode getChCode() {\r
+               return cc;\r
+       }\r
+       \r
+       private ChannelCode cc = new ChannelCode(getRecorderId());\r
+       \r
+       public String getErrmsg() {\r
+               return(errmsg);\r
+       }\r
+       \r
+       protected void setErrmsg(String s) {\r
+               errmsg = s;\r
+       }\r
+       \r
+       private String errmsg = "";\r
+\r
+       /*******************************************************************************\r
+        * 部品\r
+        ******************************************************************************/\r
+\r
+       private ArrayList<TextValueSet> vrate = new ArrayList<TextValueSet>();\r
+       private ArrayList<TextValueSet> arate = new ArrayList<TextValueSet>();\r
+       private ArrayList<TextValueSet> folder = new ArrayList<TextValueSet>();\r
+       private ArrayList<TextValueSet> encoder = new ArrayList<TextValueSet>();\r
+       private ArrayList<TextValueSet> dvdcompat = new ArrayList<TextValueSet>();\r
+       public ArrayList<TextValueSet> getVideoRateList() { return(vrate); }\r
+       public ArrayList<TextValueSet> getAudioRateList() { return(arate); }\r
+       public ArrayList<TextValueSet> getFolderList() { return(folder); }\r
+       public ArrayList<TextValueSet> getEncoderList() { return(encoder); }\r
+       public ArrayList<TextValueSet> getDVDCompatList() { return(dvdcompat); }\r
+\r
+       /*******************************************************************************\r
+        * コンストラクタ\r
+        ******************************************************************************/\r
+\r
+       public PlugIn_RecGoogleCalendar() {\r
+               setUser("ほげほげ@gmail.com(サンプル)");\r
+               setPasswd("********");\r
+               setMacAddr("10(分で指定してください。不要な場合は0で)");\r
+       }\r
+\r
+       /*******************************************************************************\r
+        * チャンネルリモコン機能\r
+        ******************************************************************************/\r
+       \r
+       public boolean ChangeChannel(String Channel) {\r
+               return false;\r
+       }\r
+\r
+       /*******************************************************************************\r
+        * レコーダーから予約一覧を取得する\r
+        ******************************************************************************/\r
+       \r
+       public boolean GetRdReserve(boolean force)\r
+       {\r
+               return(true);\r
+       }\r
+       \r
+       /*******************************************************************************\r
+        * 新規予約\r
+        ******************************************************************************/\r
+       \r
+       public boolean PostRdEntry(ReserveList r)\r
+       {\r
+               try {\r
+                       String postUrl = "http://www.google.com/calendar/feeds/default/private/full";\r
+                       \r
+                       CalendarEventEntry myEntry = new CalendarEventEntry();\r
+       \r
+                       // 番組タイトル\r
+                       myEntry.setTitle(new PlainTextConstruct(r.getTitle()));\r
+                       // 番組詳細\r
+                       myEntry.setContent(new PlainTextConstruct(r.getDetail()));\r
+                       // 放送局\r
+                       Where evLocation = new Where();\r
+                       evLocation.setValueString(r.getCh_name());\r
+                       myEntry.addLocation(evLocation);\r
+                       // 開始終了時刻\r
+                       if (r.getRec_pattern_id() == -1) {\r
+                               // 通常のレコーダプラグインではないので必要ない処理\r
+                               r.setRec_pattern_id(getRec_pattern_Id(r.getRec_pattern()));\r
+                               r.setRec_nextdate(CommonUtils.getNextDate(r));\r
+                               r.setRec_min(CommonUtils.getRecMin(r.getAhh(),r.getAmm(),r.getZhh(),r.getZmm()));\r
+                               getStartEndDateTime(r);\r
+                       }\r
+                       if (r.getRec_pattern_id() == 11) {\r
+                               // 単日\r
+                               When eventTimes = new When();\r
+                               Matcher ma = Pattern.compile("^(\\d+?)/(\\d+?)/(\\d+?) (\\d+?):(\\d+?)$").matcher(r.getStartDateTime());\r
+                               if (ma.find()) {\r
+                                       DateTime startTime = DateTime.parseDateTime(ma.group(1)+"-"+ma.group(2)+"-"+ma.group(3)+"T"+ma.group(4)+":"+ma.group(5)+":00+09:00");\r
+                                       eventTimes.setStartTime(startTime);\r
+                               }\r
+                               ma = Pattern.compile("^(\\d+?)/(\\d+?)/(\\d+?) (\\d+?):(\\d+?)$").matcher(r.getEndDateTime());\r
+                               if (ma.find()) {\r
+                                       DateTime endTime = DateTime.parseDateTime(ma.group(1)+"-"+ma.group(2)+"-"+ma.group(3)+"T"+ma.group(4)+":"+ma.group(5)+":00+09:00");\r
+                                       eventTimes.setEndTime(endTime);\r
+                               }\r
+                               myEntry.addTime(eventTimes);\r
+                       }\r
+                       else { \r
+                               // 繰り返しパターン\r
+                               String startTime = "";\r
+                               Matcher ma = Pattern.compile("^(\\d+?)/(\\d+?)/(\\d+?) (\\d+?):(\\d+?)$").matcher(r.getStartDateTime());\r
+                               if (ma.find()) {\r
+                                       startTime = ma.group(1)+ma.group(2)+ma.group(3)+"T"+ma.group(4)+ma.group(5)+"00";\r
+                               }\r
+                               String endTime = "";\r
+                               ma = Pattern.compile("^(\\d+?)/(\\d+?)/(\\d+?) (\\d+?):(\\d+?)$").matcher(r.getEndDateTime());\r
+                               if (ma.find()) {\r
+                                       endTime = ma.group(1)+ma.group(2)+ma.group(3)+"T"+ma.group(4)+ma.group(5)+"00";\r
+                               }\r
+                               String recurData = "";\r
+                               if (r.getRec_pattern_id() == 10) {\r
+                                       // 毎日\r
+                                       recurData = "DTSTART;TZID=Japan:"+startTime+"\r\n"+\r
+                                               "DTEND;TZID=Japan:"+endTime+"\r\n"+\r
+                                               "RRULE:FREQ=DAILY\r\n";\r
+                               }\r
+                               else if (9 >= r.getRec_pattern_id() && r.getRec_pattern_id() >= 7) {\r
+                                       // 帯\r
+                                       String days = "";\r
+                                       int d = ((CommonUtils.isLateNight(r.getAhh()))?(1):(0));\r
+                                       for (int i=1; i<=r.getRec_pattern_id()-3; i++) {\r
+                                               days += wd[(i+d)%7]+",";\r
+                                       }\r
+                                       days = days.substring(0, days.length()-1);\r
+                                       System.out.println(days);\r
+                                       recurData = "DTSTART;TZID=Japan:"+startTime+"\r\n"+\r
+                                               "DTEND;TZID=Japan:"+endTime+"\r\n"+\r
+                                               "RRULE:FREQ=WEEKLY;BYDAY="+days+"\r\n";\r
+                               }\r
+                               else if (r.getRec_pattern_id() < 7) {\r
+                                       // 週次\r
+                                       recurData = "DTSTART;TZID=Japan:"+startTime+"\r\n"+\r
+                                               "DTEND;TZID=Japan:"+endTime+"\r\n"+\r
+                                               "RRULE:FREQ=WEEKLY;BYDAY="+wd[r.getRec_pattern_id()]+"\r\n";\r
+                               }\r
+                               Recurrence recur = new Recurrence();\r
+                               recur.setValue(recurData);\r
+                               myEntry.setRecurrence(recur);\r
+                       }\r
+                       // リマインダー\r
+                       try {\r
+                               int reminderMinutes = Integer.valueOf(getMacAddr());\r
+                               if (reminderMinutes > 0) {\r
+                                       Method methodType = Method.EMAIL;\r
+\r
+                                       Reminder reminder = new Reminder();\r
+                                       reminder.setMinutes(reminderMinutes);\r
+                                       reminder.setMethod(methodType);\r
+\r
+                                       myEntry.getReminder().add(reminder);\r
+                               }\r
+                       }\r
+                       catch (NumberFormatException e) {\r
+                               // なにもしないよー\r
+                       }\r
+                       // 登録アプリ\r
+                       Person author = new Person("tainavi", "http://sourceforge.jp/projects/tainavi/", getUser());\r
+                       myEntry.getAuthors().add(author);\r
+                       \r
+                       CalendarService myService = new CalendarService("tainavi");\r
+                       myService.setUserCredentials(getUser(), getPasswd());\r
+                       \r
+                       reportProgress("Googleカレンダーに登録しています");\r
+                       \r
+                       // リダイレクトのループ\r
+                       CalendarEventEntry insertedEntry = null;\r
+                       for (int i=0; i<2; i++) {\r
+                               try {\r
+                                       insertedEntry = myService.insert(new URL(postUrl), myEntry);\r
+                                       break;\r
+                               } catch (RedirectRequiredException e) {\r
+                                       postUrl = e.getRedirectLocation();\r
+                                       System.out.println("redirect to: "+postUrl);\r
+                               }\r
+                               insertedEntry = null;\r
+                       }\r
+                       if (insertedEntry == null) {\r
+                               setErrmsg("Googleカレンダーへの登録に失敗しました");\r
+                               return(false);\r
+                       }\r
+                       \r
+                       return(true);\r
+               \r
+               } catch (Exception e) {\r
+                       e.printStackTrace();\r
+               }\r
+               \r
+               setErrmsg("Googleカレンダーへの登録に失敗しました");\r
+               return(false);\r
+       }\r
+\r
+       /*******************************************************************************\r
+        * 予約更新\r
+        ******************************************************************************/\r
+       \r
+       public boolean UpdateRdEntry(ReserveList o, ReserveList r) {\r
+               \r
+               {\r
+                       // 削除、だよ\r
+\r
+                       // 開始終了時刻\r
+                       String startTime = "";\r
+                       String endTime = "";\r
+                       String rrule = null;\r
+                       if (o.getRec_pattern_id() == 11) {\r
+                               // 単日\r
+                               Matcher ma = Pattern.compile("^(\\d+?)/(\\d+?)/(\\d+?) (\\d+?):(\\d+?)$").matcher(o.getStartDateTime());\r
+                               if (ma.find()) {\r
+                                       startTime = ma.group(1)+"-"+ma.group(2)+"-"+ma.group(3)+"T"+ma.group(4)+":"+ma.group(5)+":00.000+09:00";\r
+                               }\r
+                               ma = Pattern.compile("^(\\d+?)/(\\d+?)/(\\d+?) (\\d+?):(\\d+?)$").matcher(o.getEndDateTime());\r
+                               if (ma.find()) {\r
+                                       endTime = ma.group(1)+"-"+ma.group(2)+"-"+ma.group(3)+"T"+ma.group(4)+":"+ma.group(5)+":00.000+09:00";\r
+                               }\r
+                               \r
+                               System.out.println("単日予定の削除: "+o.getTitle()+"("+o.getCh_name()+"),"+startTime+","+endTime);\r
+                       }\r
+                       else {\r
+                               // 繰り返しパターン\r
+                               Matcher ma = Pattern.compile("^(\\d+?)/(\\d+?)/(\\d+?) (\\d+?):(\\d+?)$").matcher(o.getStartDateTime());\r
+                               if (ma.find()) {\r
+                                       startTime = ma.group(4)+ma.group(5)+"00";\r
+                               }\r
+                               ma = Pattern.compile("^(\\d+?)/(\\d+?)/(\\d+?) (\\d+?):(\\d+?)$").matcher(o.getEndDateTime());\r
+                               if (ma.find()) {\r
+                                       endTime = ma.group(4)+ma.group(5)+"00";\r
+                               }\r
+                               if (o.getRec_pattern_id() == 10) {\r
+                                       // 毎日\r
+                                       rrule = "RRULE:FREQ=DAILY\n";\r
+                               }\r
+                               else if (9 >= o.getRec_pattern_id() && o.getRec_pattern_id() >= 7) {\r
+                                       // 帯\r
+                                       String days = "";\r
+                                       int d = ((CommonUtils.isLateNight(o.getAhh()))?(1):(0));\r
+                                       for (int i=1; i<=o.getRec_pattern_id()-3; i++) {\r
+                                               days += wd[(i+d)%7]+",";\r
+                                       }\r
+                                       days = days.substring(0, days.length()-1);\r
+                                       rrule = "RRULE:FREQ=WEEKLY;BYDAY="+days+"\n";\r
+                               }\r
+                               else if (o.getRec_pattern_id() < 7) {\r
+                                       // 週次\r
+                                       rrule = "RRULE:FREQ=WEEKLY;BYDAY="+wd[o.getRec_pattern_id()]+"\n";\r
+                               }\r
+                               \r
+                               if (rrule == null) {\r
+                                       System.err.println("繰り返し予定のパターンが不正: "+o.getRec_pattern_id());\r
+                                       return false;\r
+                               }\r
+                               \r
+                               System.out.println("繰り返し予定の削除: "+o.getTitle()+"("+o.getCh_name()+"),"+rrule.replaceFirst("\n","")+","+startTime+","+endTime);\r
+                       }\r
+                       \r
+                       // 削除クエリ\r
+                       try {\r
+                               \r
+                               String feedUrl = "http://www.google.com/calendar/feeds/default/private/full";\r
+                               \r
+                               CalendarService myService = new CalendarService("tainavi");\r
+                               myService.setUserCredentials(getUser(),getPasswd());\r
+                               \r
+                               CalendarEventFeed myResultsFeed = null;\r
+\r
+                               String nDate = CommonUtils.getNextDate(o);\r
+                               GregorianCalendar c = CommonUtils.getCalendar(nDate+" "+o.getAhh()+":"+o.getAmm());\r
+                               String sDate = CommonUtils.getDateTime(c).replaceFirst("\\(.\\)","").replaceFirst(" ","T").replaceAll("/","-")+":00.000+09:00";\r
+                               c.add(Calendar.MINUTE, Integer.valueOf(o.getRec_min()));\r
+                               String eDate = CommonUtils.getDateTime(c).replaceFirst("\\(.\\)","").replaceFirst(" ","T").replaceAll("/","-")+":00.000+09:00";\r
+                               //System.err.println(sDate+","+eDate);\r
+                               \r
+                               // リダイレクトのループ\r
+                               for (int i=0; i<2; i++) {\r
+                                       try {\r
+                                               CalendarQuery myQuery = new CalendarQuery(new URL(feedUrl));\r
+                                               //myQuery.setStringCustomParameter("q", "\""+o.getTitle()+"\"");\r
+                                               myQuery.setStringCustomParameter("sortorder", "a");\r
+                                               if (o.getRec_pattern_id() == 11) {\r
+                                                       myQuery.setStringCustomParameter("singleevents", "true");       // 単日\r
+                                                       myQuery.setStringCustomParameter("start-min", startTime);\r
+                                                       myQuery.setStringCustomParameter("start-max", endTime);\r
+                                               }\r
+                                               else {\r
+                                                       myQuery.setStringCustomParameter("singleevents", "false");      // 繰り返し\r
+                                               }\r
+                                               myQuery.setMinimumStartTime(DateTime.parseDateTime(sDate));\r
+                                               myQuery.setMaximumStartTime(DateTime.parseDateTime(eDate));\r
+                                               myQuery.setMaxResults(25);\r
+                                               myResultsFeed = myService.query(myQuery,CalendarEventFeed.class);\r
+                                               break;\r
+                                       }\r
+                                       catch (RedirectRequiredException e) {\r
+                                               feedUrl = e.getRedirectLocation();\r
+                                               System.out.println("redirect to: "+feedUrl);\r
+                                       }\r
+                                       myResultsFeed = null;\r
+                               }\r
+                               if (myResultsFeed == null) {\r
+                                       setErrmsg("Googleカレンダーの更新に失敗しました");\r
+                                       return(false);\r
+                               }\r
+                               \r
+                               System.out.println("削除候補が "+myResultsFeed.getEntries().size()+" 件見つかりました");\r
+                               \r
+                               for (CalendarEventEntry entry : myResultsFeed.getEntries()) {\r
+                                       System.out.println("削除候補: "+entry.getTitle().getPlainText()+" ("+entry.getLocations().get(0).getValueString()+"),"+entry.getTimes().get(0).getStartTime().toString()+","+entry.getTimes().get(0).getEndTime().toString());\r
+                               }\r
+                               for (CalendarEventEntry entry : myResultsFeed.getEntries()) {\r
+                                       // タイトル\r
+                                       if ( ! entry.getTitle().getPlainText().equals(o.getTitle())) {\r
+                                               continue;\r
+                                       }\r
+                                       // 放送局\r
+                                       if ( entry.getLocations().size() == 0 || ! entry.getLocations().get(0).getValueString().equals(o.getCh_name())) {\r
+                                               continue;\r
+                                       }\r
+                                       \r
+                                       // 開始終了日時\r
+                                       if (rrule == null) {\r
+                                               // 単日\r
+                                               if (entry.getTimes().size() == 0) {\r
+                                                       continue;\r
+                                               }\r
+                                               \r
+                                               System.out.println("単日予定: "+entry.getTimes().get(0).getStartTime().toString()+" - "+entry.getTimes().get(0).getEndTime().toString());\r
+                                               \r
+                                               if ( ! entry.getTimes().get(0).getStartTime().toString().equals(startTime)) {\r
+                                                       continue;\r
+                                               }\r
+                                               if ( ! entry.getTimes().get(0).getEndTime().toString().equals(endTime)) {\r
+                                                       continue;\r
+                                               }\r
+                                       }\r
+                                       else {\r
+                                               // 繰り返し\r
+                                               if (entry.getRecurrence() == null) {\r
+                                                       continue;\r
+                                               }\r
+                                               \r
+                                               System.out.println("繰り返し予定: "+entry.getRecurrence().getValue());\r
+                                               \r
+                                               Matcher ma = Pattern.compile("(DTSTART;[^:]+?:\\d\\d\\d\\d\\d\\d\\d\\dT"+startTime+")").matcher(entry.getRecurrence().getValue());\r
+                                               if ( ! ma.find()) {\r
+                                                       continue;\r
+                                               }\r
+                                               Matcher mb = Pattern.compile("(DTEND;[^:]+?:\\d\\d\\d\\d\\d\\d\\d\\dT"+endTime+")").matcher(entry.getRecurrence().getValue());\r
+                                               if ( ! mb.find()) {\r
+                                                       continue;\r
+                                               }\r
+                                               Matcher mc = Pattern.compile("("+rrule+")").matcher(entry.getRecurrence().getValue());\r
+                                               if ( ! mc.find()) {\r
+                                                       //System.out.println(entry.getRecurrence().getValue());\r
+                                                       continue;\r
+                                               }\r
+                                       }\r
+                                       \r
+                                       System.out.println("削除対象が見つかりました: "+entry.getTitle().getPlainText()+" ("+entry.getLocations().get(0).getValueString()+")");\r
+                                       \r
+                                       // 削除実行\r
+                                       reportProgress("Googleカレンダーから削除(更新)しています");\r
+                                       URL deleteUrl = new URL(entry.getEditLink().getHref());\r
+                                       myService.delete(deleteUrl);\r
+                                       if (r == null) {\r
+                                               return(true);\r
+                                       }\r
+                                       break;\r
+                               }\r
+                               \r
+                               // 更新、だよ。\r
+                               if (r != null) {\r
+                                       if (PostRdEntry(r)) {\r
+                                               return(true);\r
+                                       }\r
+                               }\r
+                               \r
+                       } catch (Exception e1) {\r
+                               e1.printStackTrace();\r
+                       }\r
+               }\r
+               \r
+               setErrmsg("Googleカレンダーの更新に失敗しました");\r
+               return(false);\r
+       }\r
+\r
+       /*******************************************************************************\r
+        * 予約削除\r
+        ******************************************************************************/\r
+       \r
+       public ReserveList RemoveRdEntry(String delno) {\r
+               return(null);\r
+       }\r
+}\r
diff --git a/TinyBannavi/src/tainavi/PlugIn_RecRD_A600.java b/TinyBannavi/src/tainavi/PlugIn_RecRD_A600.java
new file mode 100644 (file)
index 0000000..a5ac6a5
--- /dev/null
@@ -0,0 +1,1391 @@
+package tainavi;\r
+\r
+import java.io.File;\r
+import java.io.UnsupportedEncodingException;\r
+import java.net.Authenticator;\r
+import java.net.URLEncoder;\r
+import java.util.ArrayList;\r
+import java.util.Hashtable;\r
+import java.util.regex.Matcher;\r
+import java.util.regex.Pattern;\r
+\r
+/*\r
+ * \r
+ */\r
+\r
+public class PlugIn_RecRD_A600 extends HDDRecorderUtils implements HDDRecorder,Cloneable {\r
+\r
+       public PlugIn_RecRD_A600 clone() {\r
+               return (PlugIn_RecRD_A600) super.clone();\r
+       }\r
+\r
+       private static final String thisEncoding = "MS932";\r
+\r
+       /* 必須コード  - ここから */\r
+       \r
+       // 種族の特性\r
+       public String getRecorderId() { return "VARDIA RD-A600"; }\r
+       public RecType getType() { return RecType.RECORDER; }\r
+       \r
+       // 個体の特性\r
+       private GetRDStatus gs = new GetRDStatus();\r
+       private ChannelCode cc = new ChannelCode(getRecorderId());\r
+       private String rsvedFile = "";\r
+       \r
+       private String errmsg = "";\r
+\r
+       // 公開メソッド\r
+       \r
+       /*\r
+        * \r
+        */\r
+       public ChannelCode getChCode() {\r
+               return cc;\r
+       }\r
+       \r
+       /*\r
+        * チャンネルリモコン機能\r
+        */\r
+       public boolean ChangeChannel(String Channel) {\r
+               \r
+               if (Channel == null) {\r
+                       return true;\r
+               }\r
+               \r
+               errmsg = "";\r
+               \r
+               int newBC = 0;                  // 新しい放送の種類\r
+               int newEnc = 0;                 // 新しいエンコーダ\r
+               String newChNo = "";    // 新しいチャンネル番号\r
+               String ch = cc.getCH_WEB2REC(Channel);\r
+\r
+               // 新しい放送の種類とチャンネル番号とエンコーダ\r
+               Matcher ma = null;\r
+               ma = Pattern.compile("^CH(\\d+)").matcher(ch); // CHxxで良いかどうか自信なし\r
+               if (ma.find()) {\r
+                       newBC = 0; // 地上アナログ\r
+                       newChNo = ma.group(1);\r
+                       newEnc = 0; // RE\r
+               }\r
+               else {\r
+                       ma = Pattern.compile("^SP(\\d+)").matcher(ch);\r
+                       if (ma.find()) {\r
+                               newBC = 1; // 外部入力(スカパー!無印)\r
+                               newChNo = /*ma.group(1);*/"3"; // 外部入力L3まで\r
+                               newEnc = 0; // RE\r
+                       }\r
+                       else {\r
+                               ma = Pattern.compile("^BS(\\d+)").matcher(ch);\r
+                               if (ma.find()) {\r
+                                       newBC = 2; // BS\r
+                                       newChNo = ma.group(1);\r
+                                       newEnc = 1; // TS1\r
+                               }\r
+                               else {\r
+                                       ma = Pattern.compile("^CS(\\d+)").matcher(ch);\r
+                                       if (ma.find()) {\r
+                                               newBC = 3; // 110CS\r
+                                               newChNo = ma.group(1);\r
+                                               newEnc = 1; // TS1\r
+                                       }\r
+                                       else {\r
+                                               ma = Pattern.compile("^(\\d+)").matcher(ch);\r
+                                               if (ma.find()) {\r
+                                                       newBC = 4; // 地上デジタル\r
+                                                       newChNo = ma.group(1);\r
+                                                       newEnc = 1; // TS1\r
+                                               }\r
+                                       }\r
+                               }\r
+                       }\r
+               }\r
+\r
+               System.out.println("Change to【" + Channel + "】: (new)enc = " + newEnc + ", (new)BC = " + newBC + ", (new)ChNo = " + newChNo);\r
+\r
+               int tryCntEnc  = 0; // エンコーダ変更試行回数\r
+               int tryCntBC   = 0; // 放送の種類変更試行回数\r
+               int tryCntChNo = 0; // チャンネル番号変更試行回数\r
+               //String s;\r
+\r
+               while (gs.getCurChannel(getIPAddr()) != null) {\r
+                       // 試行回数チェック\r
+                       if (10 < tryCntEnc || 10 < tryCntBC || 10 < tryCntChNo) {\r
+                               System.out.println(errmsg = "チャンネル切り替え失敗!");\r
+                               return false;\r
+                       }\r
+                       \r
+                       int curBC = 0;                  // 選択中の放送の種類\r
+                       int curEnc = 0;                 // 選択中のエンコーダ\r
+                       String curChNo = "";    // 選択中のチャンネル番号\r
+\r
+                       // 選択中の放送の種類とチャンネル番号\r
+                       ma = Pattern.compile("^CH (\\d+)").matcher(gs.ch);\r
+                       if (ma.find()) {\r
+                               curBC = 0; // 地上アナログ\r
+                               curChNo = ma.group(1);\r
+                       }\r
+                       else {\r
+                               ma = Pattern.compile("^L (\\d)").matcher(gs.ch);\r
+                               if (ma.find()) {\r
+                                       curBC = 1; // 外部入力\r
+                                       curChNo = ma.group(1);\r
+                               }\r
+                               else {\r
+                                       ma = Pattern.compile("^BS(\\d+)").matcher(gs.ch);\r
+                                       if (ma.find()) {\r
+                                               curBC = 2; // BS\r
+                                               curChNo = ma.group(1);\r
+                                       }\r
+                                       else {\r
+                                               ma = Pattern.compile("^CS(\\d+)").matcher(gs.ch);\r
+                                               if (ma.find()) {\r
+                                                       curBC = 3; // 110CS\r
+                                                       curChNo = ma.group(1);\r
+                                               }\r
+                                               else {\r
+                                                       ma = Pattern.compile("^(\\d+)").matcher(gs.ch);\r
+                                                       if (ma.find()) {\r
+                                                               curBC = 4; // 地上デジタル\r
+                                                               curChNo = ma.group(1);\r
+                                                       }\r
+                                               }\r
+                                       }\r
+                               }\r
+                       }\r
+                       \r
+                       // 選択中のエンコーダ\r
+                       if (gs.enc.equals("RE")) {\r
+                               curEnc = 0; // RE\r
+                       }\r
+                       else if (gs.enc.equals("TS1")) {\r
+                               curEnc = 1; // TS1\r
+                       }\r
+                       else if (gs.enc.equals("TS2")) {\r
+                               curEnc = 2; // TS2\r
+                       }\r
+\r
+                       System.out.println("Change to【" + Channel + "】: enc = " + curEnc + ", BC = " + curBC + ", ChNo = " + curChNo);\r
+\r
+                       // エンコーダの変更\r
+                       if (curEnc != newEnc) {\r
+                               // おまじない\r
+                               Authenticator.setDefault(new MyAuthenticator(getUser(), getPasswd()));\r
+\r
+                               reqGET("http://"+getIPAddr()+":"+getPortNo()+"/remote/remote.htm?key=63", null); // W録\r
+                               CommonUtils.milSleep(2000);\r
+\r
+                               tryCntEnc++;\r
+                               \r
+                               continue;\r
+                       }\r
+\r
+                       // 放送の種類の変更\r
+                       if (curBC != newBC) {\r
+                               // おまじない\r
+                               Authenticator.setDefault(new MyAuthenticator(getUser(), getPasswd()));\r
+                               \r
+                               // TS1またはTS2\r
+                               if (newEnc == 1 || newEnc == 2) {\r
+                                       reqGET("http://"+getIPAddr()+":"+getPortNo()+"/remote/remote.htm?key=21", null); // 放送切換\r
+                                       CommonUtils.milSleep(2000);\r
+                               }\r
+                               // RE\r
+                               else {\r
+                                       reqGET("http://"+getIPAddr()+":"+getPortNo()+"/remote/remote.htm?key=0F", null); // 入力切換\r
+                                       CommonUtils.milSleep(2000);\r
+                               }\r
+\r
+                               tryCntBC++;\r
+                               \r
+                               continue;\r
+                       }\r
+                       \r
+                       // チャンネル番号の変更\r
+                       if (!curChNo.equals(newChNo)) {\r
+                               // おまじない\r
+                               Authenticator.setDefault(new MyAuthenticator(getUser(), getPasswd()));\r
+                               \r
+                               // 地上アナログ BS 110CS 地上デジタル\r
+                               if (newBC == 0 || newBC == 2 || newBC == 3 || newBC == 4) {\r
+                                       reqGET("http://"+getIPAddr()+":"+getPortNo()+"/remote/remote.htm?key=25", null); // CH番号入力\r
+                                       CommonUtils.milSleep(1000);\r
+                                       reqGET("http://"+getIPAddr()+":"+getPortNo()+"/remote/remote.htm?key=0"+newChNo.substring(0,1), null); // 数字\r
+                                       CommonUtils.milSleep(200);\r
+                                       reqGET("http://"+getIPAddr()+":"+getPortNo()+"/remote/remote.htm?key=0"+newChNo.substring(1,2), null); // 数字\r
+                                       CommonUtils.milSleep(200);\r
+                                       reqGET("http://"+getIPAddr()+":"+getPortNo()+"/remote/remote.htm?key=0"+newChNo.substring(2,3), null); // 数字\r
+                                       CommonUtils.milSleep(200);\r
+                                       reqGET("http://"+getIPAddr()+":"+getPortNo()+"/remote/remote.htm?key=44", null); // 決定\r
+                                       CommonUtils.milSleep(5000);\r
+                               }\r
+                               else {\r
+                                       reqGET("http://"+getIPAddr()+":"+getPortNo()+"/remote/remote.htm?key=0F", null); // 入力切換\r
+                                       CommonUtils.milSleep(2000);\r
+                               }\r
+\r
+                               tryCntChNo++;\r
+                               \r
+                               continue;\r
+                       }\r
+                       \r
+                       System.out.println("Change to【" + Channel + "】: done");\r
+                       break;\r
+               }\r
+               \r
+               return true;\r
+       }\r
+\r
+       /*\r
+        *      レコーダーから予約一覧を取得する \r
+        */\r
+       public boolean GetRdReserve(boolean force)\r
+       {\r
+               System.out.println("レコーダから予約一覧を取得します("+force+")");\r
+               \r
+               errmsg = "";\r
+\r
+               //\r
+               rsvedFile = "env/reserved."+getIPAddr()+"_"+getPortNo()+"_"+getRecorderId()+".xml";\r
+               \r
+               String vrateTFile = "env/videorate."+getIPAddr()+"_"+getPortNo()+"_"+getRecorderId()+".xml";\r
+               String arateTFile = "env/audiorate."+getIPAddr()+"_"+getPortNo()+"_"+getRecorderId()+".xml";\r
+               String folderTFile = "env/folders."+getIPAddr()+"_"+getPortNo()+"_"+getRecorderId()+".xml";\r
+               String encoderTFile = "env/encoders."+getIPAddr()+"_"+getPortNo()+"_"+getRecorderId()+".xml";\r
+               String dvdcompatTFile = "env/dvdcompat."+getIPAddr()+"_"+getPortNo()+"_"+getRecorderId()+".xml";\r
+               String deviceTFile = "env/device."+getIPAddr()+"_"+getPortNo()+"_"+getRecorderId()+".xml";\r
+               String xChapterTFile = "env/xchapter."+getIPAddr()+"_"+getPortNo()+"_"+getRecorderId()+".xml";\r
+               String msChapterTFile = "env/mschapter."+getIPAddr()+"_"+getPortNo()+"_"+getRecorderId()+".xml";\r
+               String mvChapterTFile = "env/mvchapter."+getIPAddr()+"_"+getPortNo()+"_"+getRecorderId()+".xml";\r
+               String chValueTFile = "env/chvalue."+getIPAddr()+"_"+getPortNo()+"_"+getRecorderId()+".xml";\r
+               String chTypeTFile = "env/chtype."+getIPAddr()+"_"+getPortNo()+"_"+getRecorderId()+".xml";\r
+               \r
+               File f = new File(rsvedFile);\r
+               if ( force == false && f.exists()) {\r
+                       // キャッシュから読み出し(録画設定ほか)\r
+                       vrate = TVSload(vrateTFile);\r
+                       if ( vrate.size() > 0 ) {\r
+                               System.out.println("+video rate="+vrateTFile);\r
+                       }\r
+                       arate = TVSload(arateTFile);\r
+                       if ( arate.size() > 0 ) {\r
+                               System.out.println("+audio rate="+arateTFile);\r
+                       }\r
+                       folder = TVSload(folderTFile);\r
+                       if ( folder.size() > 0 ) {\r
+                               System.out.println("+folder="+folderTFile);\r
+                       }\r
+                       encoder = TVSload(encoderTFile);\r
+                       if ( encoder.size() > 0 ) {\r
+                               System.out.println("+encoder="+encoderTFile);\r
+                       }\r
+                       dvdcompat = TVSload(dvdcompatTFile);\r
+                       if ( dvdcompat.size() > 0 ) {\r
+                               System.out.println("+dvdcompat="+dvdcompatTFile);\r
+                       }\r
+                       device = TVSload(deviceTFile);\r
+                       if ( device.size() > 0 ) {\r
+                               System.out.println("+device="+deviceTFile);\r
+                       }\r
+                       // 自動チャプタ関連\r
+                       xchapter = TVSload(xChapterTFile);\r
+                       if ( xchapter.size() > 0 ) {\r
+                               System.out.println("+xchapter="+xChapterTFile);\r
+                       }\r
+                       mschapter = TVSload(msChapterTFile);\r
+                       if ( mschapter.size() > 0 ) {\r
+                               System.out.println("+mschapter="+msChapterTFile);\r
+                       }\r
+                       mvchapter = TVSload(mvChapterTFile);\r
+                       if ( mvchapter.size() > 0 ) {\r
+                               System.out.println("+mvchapter="+mvChapterTFile);\r
+                       }\r
+                       // チャンネルコードバリュー\r
+                       chvalue = TVSload(chValueTFile);\r
+                       if ( chvalue.size() > 0 ) {\r
+                               System.out.println("+chvalue="+chValueTFile);\r
+                       }\r
+                       chtype = TVSload(chTypeTFile);\r
+                       if ( chtype.size() > 0 ) {\r
+                               System.out.println("+chtype="+chTypeTFile);\r
+                       }\r
+                       \r
+                       // キャッシュから読み出し(予約一覧)\r
+                       setReserves(ReservesFromFile(rsvedFile));\r
+                       if (getReserves().size() > 0) {\r
+                               System.out.println("+read from="+rsvedFile);\r
+                       }\r
+                       \r
+                       // なぜか設定ファイルが空になっている場合があるので、その際は再取得する\r
+                       if (vrate.size()>0 && arate.size()>0 &&\r
+                                       folder.size()>0 && encoder.size()>0 && device.size()>0 && // add >>> device\r
+                                       xchapter.size()>0 && mschapter.size()>0 && mvchapter.size()>0) {\r
+                               return(true);\r
+                       }\r
+                       \r
+                       //getReserves().removeAll(getReserves());\r
+               }\r
+\r
+               // おまじない\r
+               Authenticator.setDefault(new MyAuthenticator(getUser(), getPasswd()));\r
+               \r
+               // RDから予約一覧を取り出す\r
+               Matcher ma = null;\r
+               String idx = "";\r
+               String header="";\r
+               String response="";\r
+               {\r
+                       reportProgress("get reserved list(1/5).");\r
+                       String[] d = reqGET("http://"+getIPAddr()+":"+getPortNo()+"/reserve/b_rsv.htm",null);\r
+                       header = d[0];\r
+                       response = d[1];\r
+                       \r
+                       if (response == null) {\r
+                               errmsg = "レコーダーが反応しません";\r
+                               return(false);\r
+                       }\r
+               }\r
+               ma = Pattern.compile("/reserve/(\\d+?)/reserve.htm").matcher(response);\r
+               if (ma.find()) {\r
+                       idx = ma.group(1);\r
+                       reportProgress("get reserved list(2/5).");\r
+                       String[] d = reqGET("http://"+getIPAddr()+":"+getPortNo()+"/reserve/"+idx+"/reserve.htm",null);\r
+                       header = d[0];\r
+                       response = d[1];\r
+                       \r
+                       if (response == null) {\r
+                               errmsg = "レコーダーが反応しません";\r
+                               return(false);\r
+                       }\r
+               }\r
+               \r
+               // 予約エントリー数を取得する\r
+               int RsvCnt = 0;\r
+               ma = Pattern.compile("RsvCnt\\s*=\\s*(\\d+?);").matcher(response);\r
+               if ( ! ma.find()) {\r
+                       errmsg = "レコーダーからの情報取得に失敗しました(予約一覧)";\r
+                       return false;\r
+               }\r
+               RsvCnt = Integer.valueOf(ma.group(1));\r
+               \r
+               // (1)録画設定の取得\r
+               {\r
+                       // ハング防止のおまじない\r
+                       reportProgress("get reserved list(3/5).");\r
+                       reqGET("http://"+getIPAddr()+":"+getPortNo()+"/reserve/"+idx+"/b_rsvinfo.htm?0&"+(RsvCnt+1),null);\r
+               }\r
+               {\r
+                       reportProgress("get reserved list(4/5).");\r
+                       String[] d = reqGET("http://"+getIPAddr()+":"+getPortNo()+"/reserve/"+idx+"/rsvinfo.htm",null);\r
+                       String hdr = d[0];\r
+                       String res = d[1];\r
+                       Matcher mb = null;\r
+                       \r
+                       // 正常取得チェック\r
+                       if ( ! res.matches("[\\s\\S]*var hdd_folder_text[\\s\\S]*")) {\r
+                               errmsg = "レコーダーからの情報取得に失敗しました(録画設定)";\r
+                               return false;\r
+                       }\r
+                       \r
+                       // (1-3)フォルダ一覧\r
+                       folder.clear();\r
+                       \r
+                       mb = Pattern.compile("var hdd_folder_text\\s*= new Array\\(([\\s\\S]+?)\\);").matcher(res);\r
+                       if (mb.find()) {\r
+                               Matcher mc = Pattern.compile("\"(.+?)\",?").matcher(mb.group(1));\r
+                               while (mc.find()) {\r
+                                       TextValueSet t = new TextValueSet();\r
+                                       t.setText(mc.group(1));\r
+                                       folder.add(t);\r
+                               }\r
+                       }\r
+                       mb = Pattern.compile("var hdd_folder_value\\s*= new Array\\(([\\s\\S]+?)\\);").matcher(res);\r
+                       if (mb.find()) {\r
+                               Matcher mc = Pattern.compile("\"(.+?)\",?").matcher(mb.group(1));\r
+                               for (TextValueSet t : folder) {\r
+                                       if (mc.find()) {\r
+                                               t.setValue(mc.group(1));\r
+                                       }\r
+                               }\r
+                       }\r
+                       \r
+                       TVSsave(folder, folderTFile);\r
+                       \r
+                       // (1-4)エンコーダ\r
+                       encoder.clear();\r
+                       \r
+                       mb = Pattern.compile("var double_encode_flg = (\\d+?);").matcher(res);\r
+                       while (mb.find()) {\r
+                               Matcher mc = Pattern.compile("\\n\\s*?switch \\( double_encode_flg \\) \\{([\\s\\S]+?default:)").matcher(res);\r
+                               if (mc.find()) {\r
+                                       Matcher md = Pattern.compile("(case "+mb.group(1)+":[\\s\\S]+?break;)").matcher(mc.group(1));\r
+                                       if (md.find()) {\r
+                                               //System.out.println(md.group(1));\r
+                                               Matcher me = Pattern.compile("name=enc_type value=.\"(\\d+?).\"[\\s\\S]+?toru_(.+?)\\.gif").matcher(md.group(1));\r
+                                               while (me.find()) {\r
+                                                       TextValueSet t = new TextValueSet();\r
+                                                       t.setText(me.group(2));\r
+                                                       t.setValue(me.group(1));\r
+                                                       encoder.add(t);\r
+                                               }\r
+                                       }\r
+                               }\r
+                       }\r
+                       \r
+                       TVSsave(encoder, encoderTFile);\r
+                       \r
+                       // (1-5)DVD互換モード\r
+                       dvdcompat.clear();\r
+                       \r
+                       mb = Pattern.compile("var dvdr_text\\s*= new Array\\(([\\s\\S]+?)\\);").matcher(res);\r
+                       if (mb.find()) {\r
+                               Matcher mc = Pattern.compile("\"(.+?)\",?").matcher(mb.group(1));\r
+                               while (mc.find()) {\r
+                                       TextValueSet t = new TextValueSet();\r
+                                       t.setText(mc.group(1));\r
+                                       dvdcompat.add(t);\r
+                               }\r
+                       }\r
+                       mb = Pattern.compile("var dvdr_value\\s*= new Array\\(([\\s\\S]+?)\\);").matcher(res);\r
+                       if (mb.find()) {\r
+                               Matcher mc = Pattern.compile("\"(.+?)\",?").matcher(mb.group(1));\r
+                               for (TextValueSet t : dvdcompat) {\r
+                                       if (mc.find()) {\r
+                                               t.setValue(mc.group(1));\r
+                                       }\r
+                               }\r
+                       }\r
+                       \r
+                       TVSsave(dvdcompat, dvdcompatTFile);\r
+\r
+                       // (1-6)記録先デバイス\r
+                       device.clear();\r
+                       \r
+                       mb = Pattern.compile("var media_text\\s*= new Array\\(([\\s\\S]+?)\\);").matcher(res);\r
+                       if (mb.find()) {\r
+                               Matcher mc = Pattern.compile("\"(.+?)\",?").matcher(mb.group(1));\r
+                               while (mc.find()) {\r
+                                       TextValueSet t = new TextValueSet();\r
+                                       t.setText(mc.group(1));\r
+                                       device.add(t);\r
+                               }\r
+                       }\r
+                       mb = Pattern.compile("var media_value\\s*= new Array\\(([\\s\\S]+?)\\);").matcher(res);\r
+                       if (mb.find()) {\r
+                               Matcher mc = Pattern.compile("(\\d+),?").matcher(mb.group(1));\r
+                               for (TextValueSet t : device) {\r
+                                       if (mc.find()) {\r
+                                               t.setValue(mc.group(1));\r
+                                       }\r
+                               }\r
+                       }\r
+                       \r
+                       TVSsave(device, deviceTFile);\r
+\r
+                       // (1-7)自動チャプター関連\r
+                       xchapter.clear();\r
+                       mb = Pattern.compile("var mutechapter_text\\s*= new Array\\(([\\s\\S]+?)\\);").matcher(res);\r
+                       if (mb.find()) {\r
+                               Matcher mc = Pattern.compile("\"(.+?)\",?").matcher(mb.group(1));\r
+                               while (mc.find()) {\r
+                                       TextValueSet t = new TextValueSet();\r
+                                       t.setText(mc.group(1));\r
+                                       xchapter.add(t);\r
+                               }\r
+                       }\r
+                       mb = Pattern.compile("var mutechapter_value\\s*= new Array\\(([\\s\\S]+?)\\);").matcher(res);\r
+                       if (mb.find()) {\r
+                               Matcher mc = Pattern.compile("(\\d+),?").matcher(mb.group(1));\r
+                               for (TextValueSet t : xchapter) {\r
+                                       if (mc.find()) {\r
+                                               t.setValue(mc.group(1));\r
+                                       }\r
+                               }\r
+                       }\r
+                       TVSsave(xchapter, xChapterTFile);\r
+                       \r
+                       mschapter.clear();\r
+                       mb = Pattern.compile("var magicchapter_text\\s*= new Array\\(([\\s\\S]+?)\\);").matcher(res);\r
+                       if (mb.find()) {\r
+                               Matcher mc = Pattern.compile("\"(.+?)\",?").matcher(mb.group(1));\r
+                               while (mc.find()) {\r
+                                       TextValueSet t = new TextValueSet();\r
+                                       t.setText(mc.group(1));\r
+                                       mschapter.add(t);\r
+                               }\r
+                       }\r
+                       mb = Pattern.compile("var magicchapter_value\\s*= new Array\\(([\\s\\S]+?)\\);").matcher(res);\r
+                       if (mb.find()) {\r
+                               Matcher mc = Pattern.compile("(\\d+),?").matcher(mb.group(1));\r
+                               for (TextValueSet t : mschapter) {\r
+                                       if (mc.find()) {\r
+                                               t.setValue(mc.group(1));\r
+                                       }\r
+                               }\r
+                       }\r
+                       TVSsave(mschapter, msChapterTFile);\r
+\r
+                       mvchapter.clear();\r
+                       mb = Pattern.compile("var cmchapter_text\\s*= new Array\\(([\\s\\S]+?)\\);").matcher(res);\r
+                       if (mb.find()) {\r
+                               Matcher mc = Pattern.compile("\"(.+?)\",?").matcher(mb.group(1));\r
+                               while (mc.find()) {\r
+                                       TextValueSet t = new TextValueSet();\r
+                                       t.setText(mc.group(1));\r
+                                       mvchapter.add(t);\r
+                               }\r
+                       }\r
+                       mb = Pattern.compile("var cmchapter_value\\s*= new Array\\(([\\s\\S]+?)\\);").matcher(res);\r
+                       if (mb.find()) {\r
+                               Matcher mc = Pattern.compile("(\\d+),?").matcher(mb.group(1));\r
+                               for (TextValueSet t : mvchapter) {\r
+                                       if (mc.find()) {\r
+                                               t.setValue(mc.group(1));\r
+                                       }\r
+                               }\r
+                       }\r
+                       TVSsave(mvchapter, mvChapterTFile);\r
+                       \r
+                       // (1-8)チャンネルコードバリュー\r
+                       chvalue.clear();\r
+                       chtype.clear();\r
+                       for ( String typ : new String[] { "uvd","bsd","csd","uva","bsa","l1","l2","l3" } ) {\r
+                               String txtkey = typ+"_ch_text";\r
+                               String valkey = typ+"_ch_value";\r
+                               Matcher mc = Pattern.compile("var "+txtkey+"\\s*= new Array\\(([\\s\\S]+?)\\);").matcher(res);\r
+                               Matcher md = Pattern.compile("var "+valkey+"\\s*= new Array\\(([\\s\\S]+?)\\);").matcher(res);\r
+                               if ( mc.find() && md.find() ) {\r
+                                       Matcher me = Pattern.compile("\"(.+?)\",?").matcher(mc.group(1));\r
+                                       Matcher mf = null;\r
+                                       if ( typ.equals("uva") ) {\r
+                                               mf = Pattern.compile("(\\d+),").matcher(md.group(1));\r
+                                       }\r
+                                       else {\r
+                                               mf = Pattern.compile("\"([^\"]+?)\",").matcher(md.group(1));\r
+                                       }\r
+                                       while ( me.find() && mf.find() ) {\r
+                                               TextValueSet t = new TextValueSet();\r
+                                               t.setText(me.group(1));\r
+                                               t.setValue(mf.group(1));\r
+                                               chvalue.add(t);\r
+                                               \r
+                                               TextValueSet x = new TextValueSet();\r
+                                               x.setText(mf.group(1));\r
+                                               x.setValue(typ);\r
+                                               chtype.add(x);\r
+                                       }\r
+                               }\r
+                       }\r
+                       TVSsave(chvalue, chValueTFile);\r
+                       TVSsave(chtype, chTypeTFile);\r
+               }\r
+               {\r
+                       reportProgress("get reserved list(5/5).");\r
+                       /*\r
+                       String[] d = reqGET("http://"+getIPAddr()+":"+getPortNo()+"/cmn/script_function.htm",null);\r
+                       String hdr = d[0];\r
+                       String res = d[1];\r
+                       Matcher mb = null;\r
+                       */\r
+                       \r
+                       // (1-1)画質設定\r
+                       {\r
+                               vrate.clear();\r
+                               TextValueSet t = null;\r
+                               \r
+                               t = new TextValueSet();\r
+                               t.setText("[TS]");\r
+                               t.setValue("128:");\r
+                               vrate.add(t);\r
+                               \r
+                               t = new TextValueSet();\r
+                               t.setText("[TSE] 1.0");\r
+                               t.setValue("2:1000");\r
+                               vrate.add(t);\r
+                               t = new TextValueSet();\r
+                               t.setText("[TSE] 1.4");\r
+                               t.setValue("2:1400");\r
+                               vrate.add(t);\r
+                               for (int br=2000; br<=17000; ) {\r
+                                       t = new TextValueSet();\r
+                                       t.setText(String.format("[TSE] %d.%d", (br-br%1000)/1000, (br%1000)/100));\r
+                                       t.setValue("2:"+String.valueOf(br));\r
+                                       vrate.add(t);\r
+                                       if (br < 10000) {\r
+                                               br += 200;\r
+                                       }\r
+                                       else {\r
+                                               br += 500;\r
+                                       }\r
+                               }\r
+                               \r
+                               t = new TextValueSet();\r
+                               t.setText("[VR] SP4.4/4.6");\r
+                               t.setValue("1:1");\r
+                               vrate.add(t);\r
+                               t = new TextValueSet();\r
+                               t.setText("[VR] LP2.0/2.2");\r
+                               t.setValue("1:2");\r
+                               vrate.add(t);\r
+                               t = new TextValueSet();\r
+                               t.setText("[VR] 1.0");\r
+                               t.setValue("1:1000");\r
+                               vrate.add(t);\r
+                               t = new TextValueSet();\r
+                               t.setText("[VR] 1.4");\r
+                               t.setValue("1:1400");\r
+                               vrate.add(t);\r
+                               for (int br=2000; br<=9200; br+=200) {\r
+                                       t = new TextValueSet();\r
+                                       t.setText(String.format("[VR] %d.%d", (br-br%1000)/1000, (br%1000)/100));\r
+                                       t.setValue("1:"+String.valueOf(br));\r
+                                       vrate.add(t);\r
+                               }\r
+                               t = new TextValueSet();\r
+                               t.setText("[VR] 高レート節約");\r
+                               t.setValue("1:10");\r
+                               vrate.add(t);\r
+                               \r
+                               TVSsave(vrate, vrateTFile);\r
+                       }\r
+                       \r
+                       // (1-2)音質設定\r
+                       {\r
+                               arate.clear();\r
+                               TextValueSet t = null;\r
+                               \r
+                               t = new TextValueSet();\r
+                               t.setText("M1");\r
+                               t.setValue("1");\r
+                               arate.add(t);\r
+                               t = new TextValueSet();\r
+                               t.setText("M2");\r
+                               t.setValue("2");\r
+                               arate.add(t);\r
+                               t = new TextValueSet();\r
+                               t.setText("L-PCM");\r
+                               t.setValue("3");\r
+                               arate.add(t);\r
+                               \r
+                               TVSsave(arate, arateTFile);\r
+                       }\r
+               }\r
+               \r
+               // 予約一覧データの分析\r
+               ArrayList<ReserveList> ra = decodeReservedList(response); \r
+               for (ReserveList entry : ra) {\r
+                       entry.setCh_name(getChCode().getCH_REC2WEB(entry.getChannel()));\r
+                       \r
+                       // タイトル自動補完フラグなど本体からは取得できない情報を引き継ぐ\r
+                       for ( ReserveList e : getReserves() ) {\r
+                               if ( e.getId().equals(entry.getId()) ) {\r
+                                       entry.setDetail(e.getDetail());\r
+                                       entry.setAutocomplete(e.getAutocomplete());\r
+                                       break;\r
+                               }\r
+                       }\r
+               }\r
+               setReserves(ra);\r
+               \r
+               // キャッシュに保存\r
+               ReservesToFile(getReserves(), rsvedFile);\r
+               \r
+               // 取得した情報の表示\r
+               System.out.println("---Reserved List Start---");\r
+               for ( int i = 0; i<getReserves().size(); i++ ) {\r
+                       // 詳細情報の取得\r
+                       ReserveList e = getReserves().get(i);\r
+                       System.out.println(String.format("[%s] %s\t%s\t%s\t%s:%s\t%s:%s\t%sm\t%s\t%s(%s)\t%s\t%s",\r
+                                       (i+1), e.getId(), e.getRec_pattern(), e.getRec_nextdate(), e.getAhh(), e.getAmm(), e.getZhh(),  e.getZmm(),     e.getRec_min(), e.getRec_mode(), e.getTitle(), e.getTitlePop(), e.getChannel(), e.getCh_name()));\r
+               }\r
+               System.out.println("---Reserved List End---");\r
+               \r
+               return(true);\r
+       }\r
+       \r
+       /*\r
+        *      予約を実行する\r
+        */\r
+       public boolean PostRdEntry(ReserveList r)\r
+       {\r
+               //\r
+               if (cc.getCH_WEB2CODE(r.getCh_name()) == null) {\r
+                       errmsg = "【警告】Web番組表の放送局名「"+r.getCh_name()+"」をCHコードに変換できません。CHコード設定を修正してください。" ;\r
+                       System.out.println(errmsg);\r
+                       return(false);\r
+               }\r
+               \r
+               System.out.println("Run: PostRdEntry("+r.getTitle()+")");\r
+               \r
+               errmsg = "";\r
+\r
+               // おまじない\r
+               Authenticator.setDefault(new MyAuthenticator(getUser(), getPasswd()));\r
+               \r
+               // RDから予約一覧を取り出す\r
+               Matcher ma = null;\r
+               String idx = "";\r
+               String header;\r
+               String response;\r
+               {\r
+                       reportProgress("get reserved list(1/7).");\r
+                       String[] d = reqGET("http://"+getIPAddr()+":"+getPortNo()+"/reserve/b_rsv.htm",null);\r
+                       header = d[0];\r
+                       response = d[1];\r
+                       \r
+                       if ( response == null ) {\r
+                               errmsg = "レコーダーが反応しません。";\r
+                               return(false);\r
+                       }\r
+               }\r
+               ma = Pattern.compile("/reserve/(\\d+?)/reserve.htm").matcher(response);\r
+               if (ma.find()) {\r
+                       reportProgress("get reserved list(2/7).");\r
+                       idx = ma.group(1);\r
+                       String[] d = reqGET("http://"+getIPAddr()+":"+getPortNo()+"/reserve/"+idx+"/reserve.htm",null);\r
+                       header = d[0];\r
+                       response = d[1];\r
+                       \r
+                       if ( response == null ) {\r
+                               errmsg = "レコーダーが反応しません。";\r
+                               return(false);\r
+                       }\r
+               }\r
+               \r
+               // 予約エントリー数を取得する\r
+               int RsvCnt = 0;\r
+               ma = Pattern.compile("RsvCnt\\s*=\\s*(\\d+?);").matcher(response);\r
+               if (ma.find()) {\r
+                       RsvCnt = Integer.valueOf(ma.group(1));\r
+               }\r
+       \r
+               // RDに新規登録要請\r
+               {\r
+                       // ハング防止のおまじない\r
+                       reportProgress("get program(3/7).");\r
+                       reqGET("http://"+getIPAddr()+":"+getPortNo()+"/reserve/"+idx+"/b_rsvinfo.htm?0&"+(RsvCnt+1),null);\r
+                       \r
+                       reportProgress("get program(4/7).");\r
+                       reqGET("http://"+getIPAddr()+":"+getPortNo()+"/reserve/"+idx+"/rsvinfo.htm",null);\r
+               }\r
+\r
+               // POSTデータを変換する\r
+               Hashtable<String, String> pdat = modPostdata(r);\r
+\r
+               // RDへの情報作成\r
+               String pstr = joinPoststr(pdat);\r
+\r
+               // RDへ情報送信\r
+               {               \r
+                       reportProgress("send request(5/7).");\r
+                       String[] d = reqPOST("http://"+getIPAddr()+":"+getPortNo()+"/reserve/"+idx+"/entry.htm", pstr, null);\r
+                       header = d[0];\r
+                       response = d[1];\r
+               }\r
+               \r
+               // 登録結果の確認\r
+               ma = Pattern.compile("alert\\(msg\\)").matcher(response);\r
+               if ( ma.find() ) {\r
+                       Matcher mb = Pattern.compile("\\bmsg=\"([\\s\\S]+?)\";").matcher(response);\r
+                       if (mb.find()) {\r
+                               errmsg = mb.group(1);\r
+                               System.out.printf("\n<<< Message from RD >>> \"%s\"\n\n", errmsg);\r
+                               Matcher mc = Pattern.compile("(予約時間が重複しています。|W録の振り替えをおこないました)").matcher(errmsg);\r
+                               if ( ! mc.find() ) {\r
+                                       return(false);\r
+                               }\r
+                       }                       \r
+               }\r
+\r
+\r
+               \r
+               /*\r
+                * 予約情報の調整 \r
+                */\r
+               \r
+               // 予約ID番号を取得(キャッシュに存在しない番号が新番号)\r
+               {\r
+                       reportProgress("get identifier(6/7).");\r
+                       String[] d = reqGET("http://"+getIPAddr()+":"+getPortNo()+"/reserve/b_rsv.htm",null);\r
+                       header = d[0];\r
+                       response = d[1];\r
+               }\r
+               ma = Pattern.compile("/reserve/(\\d+?)/reserve.htm").matcher(response);\r
+               if (ma.find()) {\r
+                       idx = ma.group(1);\r
+                       reportProgress("get identifier(7/7).");\r
+                       String[] d = reqGET("http://"+getIPAddr()+":"+getPortNo()+"/reserve/"+idx+"/reserve.htm",null);\r
+                       header = d[0];\r
+                       response = d[1];\r
+               }\r
+               r.setId(getNewId(response));\r
+               \r
+               // 音質(TS/TSEでは音質の設定はできない)\r
+               ma = Pattern.compile("^\\[TS").matcher(r.getRec_mode());\r
+               if (ma.find()) {\r
+                       r.setRec_audio("");\r
+               }\r
+\r
+               // 予約パターンID\r
+               r.setRec_pattern_id(getRec_pattern_Id(r.getRec_pattern()));\r
+\r
+               // 次回予定日\r
+               r.setRec_nextdate(CommonUtils.getNextDate(r));\r
+               //r.setRec_nextdate(getNextDate(r.getRec_pattern(), r.getZhh()+":"+r.getZmm()));\r
+               \r
+               // 録画長\r
+               r.setRec_min(CommonUtils.getRecMin(r.getAhh(),r.getAmm(),r.getZhh(),r.getZmm()));\r
+               \r
+               // 開始日時・終了日時\r
+               getStartEndDateTime(r);\r
+               \r
+               // 予約リストを更新\r
+               getReserves().add(r);\r
+               \r
+               // キャッシュに保存\r
+               ReservesToFile(getReserves(), rsvedFile);\r
+               \r
+               System.out.printf("\n<<< Message from RD >>> \"%s\"\n\n", "正常に登録できました。");\r
+               return(true);\r
+       }\r
+\r
+       \r
+       /*\r
+        *      予約を更新する\r
+        */\r
+       public boolean UpdateRdEntry(ReserveList o, ReserveList r) {\r
+               //\r
+               if (cc.getCH_WEB2CODE(r.getCh_name()) == null) {\r
+                       errmsg = "【警告】Web番組表の放送局名「"+r.getCh_name()+"」をCHコードに変換できません。CHコード設定を修正してください。" ;\r
+                       System.out.println(errmsg);\r
+                       return(false);\r
+               }\r
+               \r
+               System.out.println("Run: UpdateRdEntry()");\r
+               \r
+               errmsg = "";\r
+\r
+               // おまじない\r
+               Authenticator.setDefault(new MyAuthenticator(getUser(), getPasswd()));\r
+               \r
+               // 更新準備\r
+               Matcher ma = null;\r
+               String idx = "";\r
+               int lineno = 0;\r
+               String header;\r
+               String response;\r
+               {\r
+                       reportProgress("get reserved list(1/5).");\r
+                       String[] d = reqGET("http://"+getIPAddr()+":"+getPortNo()+"/reserve/b_rsv.htm",null);\r
+                       header = d[0];\r
+                       response = d[1];\r
+                       \r
+                       if ( response == null ) {\r
+                               errmsg = "レコーダーが反応しません。";\r
+                               return(false);\r
+                       }\r
+               }\r
+               ma = Pattern.compile("/reserve/(\\d+?)/reserve.htm").matcher(response);\r
+               if (ma.find()) {\r
+                       idx = ma.group(1);\r
+                       reportProgress("get identifier(2/5).");\r
+                       String[] d = reqGET("http://"+getIPAddr()+":"+getPortNo()+"/reserve/"+idx+"/reserve.htm",null);\r
+                       header = d[0];\r
+                       response = d[1];\r
+                       \r
+                       if ( response == null ) {\r
+                               errmsg = "レコーダーが反応しません。";\r
+                               return(false);\r
+                       }\r
+               }\r
+               ma = Pattern.compile("c1\\[(\\d+?)\\]=\""+r.getId()+"\";").matcher(response);\r
+               if ( ma.find() ) {\r
+                       lineno = Integer.valueOf(ma.group(1))+1;\r
+               }\r
+               \r
+               // RDに更新要請\r
+               {\r
+                       // ハング防止のおまじない\r
+                       reportProgress("get program(3/5).");\r
+                       reqGET("http://"+getIPAddr()+":"+getPortNo()+"/reserve/"+idx+"/b_rsvinfo.htm?"+r.getId()+"&"+lineno,null);\r
+                       \r
+                       reportProgress("get program(4/5).");\r
+                       reqGET("http://"+getIPAddr()+":"+getPortNo()+"/reserve/"+idx+"/rsvinfo.htm",null);\r
+               }\r
+\r
+               // POSTデータを変換する\r
+               Hashtable<String, String> pdat = modPostdata(r);\r
+               \r
+               // RDへの情報作成\r
+               String pstr = joinPoststr(pdat);\r
+\r
+               // RDへ情報送信\r
+               {               \r
+                       reportProgress("send request.(5/5)");\r
+                       String[] d = reqPOST("http://"+getIPAddr()+":"+getPortNo()+"/reserve/"+idx+"/entry.htm", pstr, null);\r
+                       header = d[0];\r
+                       response = d[1];\r
+               }\r
+\r
+               // 更新結果の確認\r
+               ma = Pattern.compile("alert\\(msg\\)").matcher(response);\r
+               if ( ma.find() ) {\r
+                       Matcher mb = Pattern.compile("\\bmsg=\"([\\s\\S]+?)\";").matcher(response);\r
+                       if (mb.find()) {\r
+                               errmsg = mb.group(1);\r
+                               System.out.printf("\n<<< Message from RD >>> \"%s\"\n\n", errmsg);\r
+                               Matcher mc = Pattern.compile("(予約時間が重複しています。|W録の振り替えをおこないました)").matcher(errmsg);\r
+                               if ( ! mc.find() ) {\r
+                                       return(false);\r
+                               }\r
+                       }                       \r
+               }\r
+\r
+               System.out.printf("\n<<< Message from RD >>> \"%s\"\n\n", "正常に更新できました。");\r
+\r
+               /*\r
+                * 予約情報の調整 \r
+                */\r
+               \r
+               // 音質(TS/TSEでは音質の設定はできない)\r
+               ma = Pattern.compile("^\\[TS").matcher(r.getRec_mode());\r
+               if (ma.find()) {\r
+                       r.setRec_audio("");\r
+               }\r
+\r
+               // 予約パターンID\r
+               r.setRec_pattern_id(getRec_pattern_Id(r.getRec_pattern()));\r
+               \r
+               // 次回予定日\r
+               r.setRec_nextdate(CommonUtils.getNextDate(r));\r
+               \r
+               // 録画長\r
+               r.setRec_min(CommonUtils.getRecMin(r.getAhh(),r.getAmm(),r.getZhh(),r.getZmm()));\r
+               \r
+               // 開始日時・終了日時\r
+               getStartEndDateTime(r);\r
+               \r
+               // 情報置き換え\r
+               getReserves().remove(o);\r
+               getReserves().add(r);\r
+\r
+               // キャッシュに保存\r
+               ReservesToFile(getReserves(), rsvedFile);\r
+\r
+               return(true);\r
+       }\r
+\r
+       /*\r
+        *      予約を削除する\r
+        */\r
+       public ReserveList RemoveRdEntry(String delid) {\r
+               \r
+               System.out.println("Run: RemoveRdEntry()");\r
+               \r
+               errmsg = "";\r
+\r
+               // おまじない\r
+               Authenticator.setDefault(new MyAuthenticator(getUser(), getPasswd()));\r
+\r
+               // 削除対象を探す\r
+               ReserveList rx = null;\r
+               for (  ReserveList reserve : getReserves() )  {\r
+                       if (reserve.getId().equals(delid)) {\r
+                               rx = reserve;\r
+                               break;\r
+                       }\r
+               }\r
+               if (rx == null) {\r
+                       return(null);\r
+               }\r
+\r
+               // 予約行番号を取得\r
+               Matcher ma = null;\r
+               String idx = "";\r
+               int lineno = 0;\r
+               String header;\r
+               String response;\r
+               {\r
+                       reportProgress("get identifier(1/5).");\r
+                       String[] d = reqGET("http://"+getIPAddr()+":"+getPortNo()+"/reserve/b_rsv.htm",null);\r
+                       header = d[0];\r
+                       response = d[1];\r
+                       if ( response == null ) {\r
+                               errmsg = "レコーダーが反応しません。";\r
+                               return(null);\r
+                       }\r
+               }\r
+               ma = Pattern.compile("/reserve/(\\d+?)/reserve.htm").matcher(response);\r
+               if (ma.find()) {\r
+                       idx = ma.group(1);\r
+                       reportProgress("get identifier(2/5).");\r
+                       String[] d = reqGET("http://"+getIPAddr()+":"+getPortNo()+"/reserve/"+idx+"/reserve.htm",null);\r
+                       header = d[0];\r
+                       response = d[1];\r
+               }\r
+               ma = Pattern.compile("c1\\[(\\d+?)\\]=\""+rx.getId()+"\";").matcher(response);\r
+               if ( ma.find() ) {\r
+                       lineno = Integer.valueOf(ma.group(1))+1;\r
+               }\r
+\r
+               // RDに削除要請\r
+               {\r
+                       // ハング防止のおまじない\r
+                       reportProgress("get program(3/5).");\r
+                       reqGET("http://"+getIPAddr()+":"+getPortNo()+"/reserve/"+idx+"/b_rsvinfo.htm?"+rx.getId()+"&"+lineno,null);\r
+                       \r
+                       reportProgress("get program(4/5).");\r
+                       reqGET("http://"+getIPAddr()+":"+getPortNo()+"/reserve/"+idx+"/rsvinfo.htm",null);\r
+               }\r
+               {               \r
+                       reportProgress("send request.(5/5).");\r
+                       String[] d = reqGET("http://"+getIPAddr()+":"+getPortNo()+"/reserve/"+idx+"/delete.htm", null);\r
+                       header = d[0];\r
+                       response = d[1];\r
+               }\r
+\r
+               // 削除結果の確認\r
+               ma = Pattern.compile("alert\\(msg\\)").matcher(response);\r
+               if ( ma.find() ) {\r
+                       Matcher mb = Pattern.compile("\\bmsg=\"([\\s\\S]+?)\";").matcher(response);\r
+                       if (mb.find()) {\r
+                               errmsg = mb.group(1);\r
+                               System.out.printf("\n<<< Message from RD >>> \"%s\"\n\n", errmsg);\r
+                               Matcher mc = Pattern.compile("W録の振り替えをおこないました").matcher(errmsg);\r
+                               if ( ! mc.find() ) {\r
+                                       return(null);\r
+                               }\r
+                       }                       \r
+               }\r
+               \r
+               // 予約リストを更新\r
+               getReserves().remove(rx);\r
+               \r
+               // キャッシュに保存\r
+               ReservesToFile(getReserves(), rsvedFile);\r
+               \r
+               System.out.printf("\n<<< Message from RD >>> \"%s\"\n\n", "正常に削除できました。");\r
+               return(rx);\r
+       }\r
+       \r
+       /*\r
+        * \r
+        */\r
+       public String getErrmsg() {\r
+               return(errmsg.replaceAll("\\\\r\\\\n", ""));\r
+       }\r
+       \r
+       \r
+       /* ここまで */\r
+       \r
+       \r
+       \r
+\r
+       \r
+       \r
+       /* 個別コード-ここから最後まで */\r
+\r
+       /*\r
+        * 非公開メソッド \r
+        */\r
+\r
+       private String joinPoststr(Hashtable<String, String> pdat)\r
+       {\r
+               String[] pkeys = {\r
+                       "bExec",\r
+                       "start_form",\r
+                       "title_name",\r
+                       "detail",\r
+                       "genre",\r
+                       "enc_type",\r
+                       "broadcast",\r
+                       "channel_list",\r
+                       "rec_priority",\r
+                       "maiyoubi_type",\r
+                       "date",\r
+                       "start_hour",\r
+                       "start_minute",\r
+                       "end_hour",\r
+                       "end_minute",\r
+                       "disc",\r
+                       "folder",\r
+                       "vrate",\r
+                       "amode",\r
+                       "videotype_digital",    // new\r
+                       "videomode_digital",    //new\r
+                       "auto_delete",\r
+                       "dvdr",\r
+                       "lVoice",\r
+                       "edge_left",\r
+                       "bAutoChapter",\r
+                       "MagicChapter",\r
+                       "CM_Chapter",\r
+                       "channel_no",\r
+                       "dtv_sid",\r
+                       "dtv_nid",\r
+                       "net_link",\r
+                       //"sport_ext",\r
+                       //"title_link",\r
+                       "add_ch_text",\r
+                       "add_ch_value",\r
+                       "sport_ext_submit",\r
+                       "title_link_submit",\r
+                       "end_form",\r
+               };\r
+               \r
+               String pstr = "";\r
+               for ( String key : pkeys ) {\r
+                       //$pdat{"$key"} =~ s/(\W)/'%'.unpack("H2", $1)/ego;\r
+                       if (pdat.get(key) == null) {\r
+                               pstr += key+"=&";\r
+                       }\r
+                       else {\r
+                               pstr += key+"="+pdat.get(key)+"&";\r
+                       }\r
+               }\r
+               pstr = pstr.substring(0, pstr.length()-1);\r
+               \r
+               return(pstr);\r
+       }\r
+       \r
+       private Hashtable<String, String> modPostdata(ReserveList r) {\r
+\r
+               Hashtable<String, String> newdat = new Hashtable<String, String>();\r
+               \r
+               // 実行するよ\r
+               newdat.put("bExec", (r.getExec())?("ON"):("OFF"));\r
+\r
+               // 予約名・予約詳細\r
+               if ( r.getAutocomplete() ) {\r
+                       // RDが勝手につけるので空白\r
+                       // リピート予約の場合は空白が必須\r
+                       // 空白にしない場合はリピート予約の巡回時に予約名が固定になるので\r
+                       // たとえば見ため的には「侵略!イカ娘 #1」の#1が#2に更新されない動作になる(左記動作はA600で確認済み)\r
+                       // 録画時間1分前倒しと1分短縮のときも正しい予約名になるのを確認済み\r
+                       newdat.put("title_name", "");\r
+                       newdat.put("detail", "");\r
+               }\r
+               else {\r
+                       try {\r
+                               newdat.put("title_name", URLEncoder.encode(r.getTitle(),thisEncoding));\r
+                       } catch (UnsupportedEncodingException e1) {\r
+                               // TODO Auto-generated catch block\r
+                               e1.printStackTrace();\r
+                       }\r
+                       \r
+                       try {\r
+                               //newdat.put("detail", URLEncoder.encode(r.getDetail(), thisEncoding));\r
+                               // 改行を\r\nに変換する必要あり\r
+                               // 変換しない場合はA600のネットdeナビの詳細表示がバグる\r
+                               newdat.put("detail", URLEncoder.encode(r.getDetail().replaceAll("\n", Matcher.quoteReplacement("\r\n")), thisEncoding));\r
+                               //System.out.println(r.getDetail().replaceAll("\n", Matcher.quoteReplacement("\r\n")));\r
+                       } catch (UnsupportedEncodingException e) {\r
+                               // TODO Auto-generated catch block\r
+                               e.printStackTrace();\r
+                       }\r
+               }\r
+               \r
+               // 保存先\r
+               try {\r
+                       newdat.put("folder", URLEncoder.encode(text2value(folder, r.getRec_folder()),thisEncoding));\r
+               } catch (UnsupportedEncodingException e) {\r
+                       // TODO Auto-generated catch block\r
+                       e.printStackTrace();\r
+               }\r
+               \r
+               // ジャンル\r
+               newdat.put("genre", _recstr2genre(r.getRec_genre()));\r
+\r
+               // DVD互換モード\r
+               newdat.put("dvdr", text2value(dvdcompat, r.getRec_dvdcompat()));\r
+               \r
+               // 録画チャンネル\r
+               String channel = cc.getCH_WEB2CODE(r.getCh_name());\r
+               String ch_no = cc.getCH_CODE2REC(channel);\r
+               if (chtype.size() > 0) {\r
+                       String typ = text2value(chtype, channel);\r
+                       if (typ.equals("uva")) {\r
+                               newdat.put("broadcast","0");            // 地上アナログ\r
+                       }\r
+                       else if (typ.equals("bsa")) {\r
+                               newdat.put("broadcast","1");            // BSアナログ\r
+                       }\r
+                       else if (typ.equals("l1")) {\r
+                               newdat.put("broadcast","2");            // 外部入力(L1)\r
+                       }\r
+                       else if (typ.equals("l2")) {\r
+                               newdat.put("broadcast","3");            // 外部入力(L2)\r
+                       }\r
+                       else if (typ.equals("l3")) {\r
+                               newdat.put("broadcast","4");            // 外部入力(L3)\r
+                       }\r
+                       else if (typ.equals("bsd")) {\r
+                               newdat.put("broadcast","10");           // BSデジタル\r
+                       }\r
+                       else if (typ.equals("csd")) {\r
+                               newdat.put("broadcast","11");           // 110度CSデジタル\r
+                       }\r
+                       else if (typ.equals("uvd")) {\r
+                               newdat.put("broadcast","12");           // 地上デジタル\r
+                       }\r
+                       else {\r
+                               // 普通ここには落ちない\r
+                               if (ch_no.startsWith("C")) {\r
+                                       newdat.put("broadcast","2");            // "C***"は外部入力(L1)\r
+                               }\r
+                               else if (ch_no.startsWith("SP")) {\r
+                                       newdat.put("broadcast","4");            // "SP***"は外部入力(L3)\r
+                               }\r
+                               else {\r
+                                       newdat.put("broadcast","3");            // 未定義は全部外部入力(L2)\r
+                               }\r
+                       }\r
+                       \r
+                       newdat.put("channel_list", channel);\r
+                       newdat.put("channel_no", channel);\r
+               }\r
+               else {\r
+                       // 後方互換\r
+                       Matcher ma = Pattern.compile("^(.)(.)").matcher(ch_no);\r
+                       if (ma.find()) {\r
+                               if ((ma.group(1)+ma.group(2)).equals("CH")) {\r
+                                       newdat.put("broadcast","0");            // 地上アナログ\r
+                               }\r
+                               else if ((ma.group(1)+ma.group(2)).equals("BS")) {\r
+                                       newdat.put("broadcast","10");           // BSデジタル\r
+                               }\r
+                               else if ((ma.group(1)+ma.group(2)).equals("CS")) {\r
+                                       newdat.put("broadcast","11");           // 110度CSデジタル\r
+                               }\r
+                               else if (ma.group(1).equals("L")) {\r
+                                       newdat.put("broadcast", String.valueOf(Integer.valueOf(ma.group(2))+1));        // ライン入力ABC\r
+                               }\r
+                               else if ((ma.group(1)+ma.group(2)).equals("SP")) {\r
+                                       newdat.put("broadcast","4"/*L3と同等*/);             // CSアナログ\r
+                               }\r
+                               else {\r
+                                       newdat.put("broadcast","12");           // 地上デジタル\r
+                               }\r
+                               \r
+                               newdat.put("channel_list", channel);\r
+                               newdat.put("channel_no", channel);\r
+                       }\r
+               }\r
+               \r
+               // 録画レート\r
+               newdat.put("enc_type", text2value(encoder, r.getTuner()));\r
+               \r
+               Matcher ma = Pattern.compile("^(\\d+?):(.*?)$").matcher(text2value(vrate, r.getRec_mode()));\r
+               if (ma.find()) {\r
+                       if (ma.group(1).equals("1")) {\r
+                               // VR\r
+                               newdat.put("vrate",ma.group(2));        \r
+                               newdat.put("amode",text2value(arate, r.getRec_audio()));        \r
+                               newdat.put("videotype_digital",ma.group(1));    \r
+                               newdat.put("videomode_digital",ma.group(2));\r
+                       }\r
+                       else if (ma.group(1).equals("2")) {\r
+                               // TSE\r
+                               newdat.put("vrate","1");        \r
+                               newdat.put("amode","1");        \r
+                               newdat.put("videotype_digital",ma.group(1));    \r
+                               newdat.put("videomode_digital",ma.group(2));\r
+                       }\r
+                       else {\r
+                               // TS\r
+                               newdat.put("vrate","1");        \r
+                               newdat.put("amode","1");        \r
+                               newdat.put("videotype_digital",ma.group(1));    \r
+                               //newdat.put("videomode_digital",ma.group(2));\r
+                       }\r
+                       \r
+               }\r
+\r
+               // 繰り返し\r
+               ma = Pattern.compile("^\\d").matcher(r.getRec_pattern());\r
+               if (ma.find()) { \r
+                       newdat.put("maiyoubi_type","0");\r
+                       newdat.put("date", r.getRec_pattern());\r
+               }\r
+               else {\r
+                       newdat.put("maiyoubi_type", "1");\r
+                       int i = 1;\r
+                       for ( String s : RPTPTN ) {\r
+                               if ( s.equals(r.getRec_pattern()) == true ) {\r
+                                       newdat.put("date", String.valueOf(i));\r
+                               }\r
+                               i++;\r
+                       }\r
+               }\r
+\r
+\r
+               newdat.put("start_hour", r.getAhh());\r
+               newdat.put("start_minute", r.getAmm());\r
+               newdat.put("end_hour", r.getZhh());\r
+               newdat.put("end_minute", r.getZmm());\r
+\r
+               // 記録先\r
+               newdat.put("disc", text2value(device, r.getRec_device()));\r
+\r
+               // 自動チャプター関連\r
+               newdat.put("bAutoChapter"               , text2value(xchapter, r.getRec_xchapter()));\r
+               newdat.put("MagicChapter"               , text2value(mschapter, r.getRec_mschapter()));\r
+               newdat.put("CM_Chapter"                 , text2value(mvchapter, r.getRec_mvchapter()));\r
+               \r
+               // 追加\r
+               newdat.put("start_form"                 , "");\r
+               newdat.put("rec_priority"               , "150");\r
+               newdat.put("title_link"                 , "0");\r
+               //newdat.put("sport_ext"                        , "1");\r
+               //newdat.put("title_link"                       , "1"); // 追っかけ有効 (有効にならない?->ブラウザからもデフォルトで無効)\r
+               newdat.put("auto_delete"                , "0");\r
+               newdat.put("video_es"                   , "00");\r
+               newdat.put("audio_es"                   , "10");\r
+               newdat.put("edge_left"                  , "0");         // 録画のりしろ\r
+               //newdat.put("MagicChapter"             , "0");\r
+               //newdat.put("MagicChapter"             , "0");\r
+               //newdat.put("CM_Chapter"                       , "0");\r
+               newdat.put("add_ch_text"                , "");\r
+               newdat.put("add_ch_value"               , "");\r
+               newdat.put("sport_ext_submit"   , "undefined");\r
+               newdat.put("title_link_submit"  , "undefined");\r
+               //newdat.put("sport_ext_submit" , "1");\r
+               //newdat.put("title_link_submit"        , "1"); // 追っかけ有効 (有効にならない?->ブラウザからもデフォルトで無効)\r
+               newdat.put("end_form"                   , "0");\r
+               \r
+               return(newdat);\r
+       }\r
+\r
+       private String _recstr2genre(String genre)\r
+       {\r
+               if (genre.equals(TVProgram.ProgGenre.MOVIE.toString())) {\r
+                       return "0";\r
+               }\r
+               else if (genre.equals(TVProgram.ProgGenre.MUSIC.toString())) {\r
+                       return "1";\r
+               }\r
+               else if (genre.equals(TVProgram.ProgGenre.DORAMA.toString())) {\r
+                       return "2";\r
+               }\r
+               else if (genre.equals(TVProgram.ProgGenre.ANIME.toString())) {\r
+                       return "3";\r
+               }\r
+               else if (genre.equals(TVProgram.ProgGenre.SPORTS.toString())) {\r
+                       return "4";\r
+               }\r
+               else if (genre.equals(TVProgram.ProgGenre.DOCUMENTARY.toString())) {\r
+                       return "5";\r
+               }\r
+               else if (genre.equals(TVProgram.ProgGenre.THEATER.toString())) {\r
+                       return "6";\r
+               }\r
+               else if (genre.equals(TVProgram.ProgGenre.HOBBY.toString())) {\r
+                       return "7";\r
+               }\r
+               else if (genre.equals(TVProgram.ProgGenre.VARIETY.toString())) {\r
+                       return "8";\r
+               }\r
+               else {\r
+                       return "10";\r
+               }\r
+       }\r
+}\r
diff --git a/TinyBannavi/src/tainavi/PlugIn_RecRD_BR610.java b/TinyBannavi/src/tainavi/PlugIn_RecRD_BR610.java
new file mode 100644 (file)
index 0000000..57d9855
--- /dev/null
@@ -0,0 +1,61 @@
+package tainavi;\r
+\r
+import java.util.ArrayList;\r
+import java.util.regex.Matcher;\r
+import java.util.regex.Pattern;\r
+\r
+public class PlugIn_RecRD_BR610 extends PlugIn_RecRD_BZ700 implements HDDRecorder,Cloneable {\r
+\r
+       public PlugIn_RecRD_BR610 clone() {\r
+               return (PlugIn_RecRD_BR610) super.clone();\r
+       }\r
+\r
+       // 種族の特性\r
+       @Override\r
+       public String getRecorderId() { return "REGZA RD-BR610"; }\r
+       \r
+       @Override\r
+       protected void setSettingEncoder(ArrayList<TextValueSet> encoder, String res)\r
+       {\r
+               System.out.println("BR610's setSettingEncoder().");\r
+               \r
+               encoder.clear();\r
+               \r
+               Matcher mb = Pattern.compile("var double_encode_flg = (\\d+?);").matcher(res);\r
+               while (mb.find()) {\r
+                       Matcher mc = Pattern.compile("\\n\\s*?switch \\( double_encode_flg \\) \\{([\\s\\S]+?default:)").matcher(res);\r
+                       if (mc.find()) {\r
+                               Matcher md = Pattern.compile("(case "+mb.group(1)+":[\\s\\S]+?break;)").matcher(mc.group(1));\r
+                               if (md.find()) {\r
+                                       Matcher me = Pattern.compile("name=enc_type value=.\"(\\d+?).\"[\\s\\S]+?act_(.+?)\\.gif").matcher(md.group(1));\r
+                                       while (me.find()) {\r
+                                               if ( ! me.group(2).equals("RE")) {\r
+                                                       TextValueSet t = new TextValueSet();\r
+                                                       t.setText(me.group(2).replaceFirst("DR","--")); \r
+                                                       t.setValue(me.group(1));\r
+                                                       encoder.add(t);\r
+                                               }\r
+                                       }\r
+                               }\r
+                       }\r
+               }\r
+       }\r
+       \r
+       //\r
+       @Override\r
+       protected void translateAttributeTuner(ReserveList entry) {\r
+               String tuner = entry.getTuner();\r
+               if (tuner.equals("RE")) {\r
+                       entry.setTuner("--");\r
+               }\r
+       }\r
+       \r
+       //\r
+       @Override\r
+       protected String text2valueEncoderSP(ArrayList<TextValueSet> tvs, String text, String vrate) {\r
+               if ( ! vrate.equals("[DR]")) {\r
+                       return "1"; // RE\r
+               }\r
+               return text2value(tvs, text);\r
+       }\r
+}
\ No newline at end of file
diff --git a/TinyBannavi/src/tainavi/PlugIn_RecRD_BZ700.java b/TinyBannavi/src/tainavi/PlugIn_RecRD_BZ700.java
new file mode 100644 (file)
index 0000000..fa119a2
--- /dev/null
@@ -0,0 +1,1687 @@
+package tainavi;\r
+\r
+import java.io.File;\r
+import java.io.UnsupportedEncodingException;\r
+import java.net.Authenticator;\r
+import java.net.URLEncoder;\r
+import java.util.ArrayList;\r
+import java.util.Hashtable;\r
+import java.util.LinkedHashMap;\r
+import java.util.regex.Matcher;\r
+import java.util.regex.Pattern;\r
+\r
+/**\r
+ * REGZA RD-BZ700用のレコーダプラグインです。\r
+ * @author original:番ナビスレ12・102氏\r
+ * @version 3.15.4β ネットdeナビのHTMLが変更されてエンコーダ情報がとりにくくなったのでZ260同様固定でもたせるようにした\r
+ */\r
+public class PlugIn_RecRD_BZ700 extends HDDRecorderUtils implements HDDRecorder,Cloneable {\r
+\r
+       public PlugIn_RecRD_BZ700 clone() {\r
+               return (PlugIn_RecRD_BZ700) super.clone();\r
+       }\r
+       \r
+       private static final String thisEncoding = "MS932";\r
+       \r
+       /*******************************************************************************\r
+        * 種族の特性\r
+        ******************************************************************************/\r
+       \r
+       // 種族の特性\r
+       @Override\r
+       public String getRecorderId() { return "REGZA RD-BZ700"; }\r
+       @Override\r
+       public RecType getType() { return RecType.RECORDER; }\r
+\r
+       // chvalueを使っていいよ\r
+       @Override\r
+       public boolean isChValueAvailable() { return true; }\r
+       // CHコードは入力しなくていい\r
+       @Override\r
+       public boolean isChCodeNeeded() { return false; }\r
+       \r
+       /*******************************************************************************\r
+        * 予約ダイアログなどのテキストのオーバーライド\r
+        ******************************************************************************/\r
+       \r
+       @Override\r
+       public String getChDatHelp() { return\r
+                       "「レコーダの放送局名」はネットdeナビの録画予約一覧で表示される「CH」の欄の値を設定してください。\n"+\r
+                       "予約一覧取得が正常に完了していれば設定候補がコンボボックスで選択できるようになります。"\r
+                       ;\r
+       }\r
+       \r
+       /*******************************************************************************\r
+        * 定数\r
+        ******************************************************************************/\r
+       \r
+       protected final String MSGID = "["+getRecorderId()+"] ";\r
+       protected final String ERRID = "[ERROR]"+MSGID;\r
+       protected final String DBGID = "[DEBUG]"+MSGID;\r
+\r
+       protected static final String ITEM_ENCODER_R1 = "R1";\r
+       protected static final String ITEM_ENCODER_R2 = "R2";\r
+\r
+       protected static final String ITEM_VIDEO_TYPE_DR  = "[DR]";\r
+       protected static final String ITEM_VIDEO_TYPE_VR  = "[VR]";\r
+       protected static final String ITEM_VIDEO_TYPE_AVC = "[AVC]";\r
+\r
+       protected static final String ITEM_MEDIA_HDD = "[HDD] ";\r
+       protected static final String ITEM_MEDIA_USB = "[USB] ";\r
+\r
+       protected static final String VALUE_ENCODER_RE_1 = "1";\r
+       //protected static final String VALUE_ENCODER_RE_2 = "2";\r
+       protected static final String VALUE_ENCODER_R1 = "3";\r
+       protected static final String VALUE_ENCODER_R2 = "4";\r
+       \r
+       /*******************************************************************************\r
+        * CHコード設定、エラーメッセージ\r
+        ******************************************************************************/\r
+       \r
+       public ChannelCode getChCode() {\r
+               return cc;\r
+       }\r
+       \r
+       private ChannelCode cc = new ChannelCode(getRecorderId());\r
+       \r
+       protected void setErrmsg(String s) { errmsg = s; }\r
+\r
+       public String getErrmsg() {\r
+               return(errmsg.replaceAll("\\\\r\\\\n", ""));\r
+       }\r
+\r
+       private String errmsg = "";\r
+\r
+       /*******************************************************************************\r
+        * 部品\r
+        ******************************************************************************/\r
+       \r
+       private GetRDStatus gs = new GetRDStatus();\r
+       \r
+       private String rsvedFile = "";\r
+       \r
+       /*******************************************************************************\r
+        * コンストラクタ\r
+        ******************************************************************************/\r
+       \r
+       /*******************************************************************************\r
+        * チャンネルリモコン機能\r
+        ******************************************************************************/\r
+       \r
+       public boolean ChangeChannel(String Channel) {\r
+               \r
+               if (Channel == null) {\r
+                       return true;\r
+               }\r
+               \r
+               int curBC;\r
+               int newBC;\r
+               String chNo;\r
+               //byte enc;\r
+               \r
+               errmsg = "";\r
+               \r
+               /*\r
+                * 変更前の放送(地上波/BS/CS)\r
+                */\r
+               {\r
+                       String ch = null;\r
+                       for (int i=0; i<3 && (ch = gs.getCurChannel(getIPAddr())) == null; i++) {\r
+                               CommonUtils.milSleep(500);\r
+                       }\r
+                       if (ch == null) {\r
+                               errmsg = "レコーダへのアクセスに失敗しました(チャンネルリモコン)。";\r
+                               System.err.println(errmsg);\r
+                               return false;\r
+                       }\r
+                       \r
+                       //byte[] ba = ch.getBytes();\r
+                       //enc = ba[0];\r
+                       ch = ch.substring(1,5);\r
+                       \r
+                       if (ch.matches("^\\d.+")) {\r
+                               curBC = 0;\r
+                       }\r
+                       else if (ch.startsWith("BS")) {\r
+                               curBC = 1;\r
+                       }\r
+                       else if (ch.startsWith("CS")) {\r
+                               curBC = 2;\r
+                       }\r
+                       else {\r
+                               errmsg = "現在のチャンネルが認識できません("+ch+")。";\r
+                               System.err.println(errmsg);\r
+                               return false;\r
+                       }\r
+               }\r
+               \r
+               /*\r
+                * 変更後のCH\r
+                */\r
+               {\r
+                       // 放送(地上波/BS/CS)\r
+                       String cd = cc.getCH_WEB2CODE(Channel);\r
+                       String ch = cc.getCH_CODE2REC(cd);\r
+                       String typ = text2value(chtype, cd);\r
+                       if (typ == null) {\r
+                               errmsg = "鯛ナビに情報が同期されていないチャンネルです("+Channel+", "+ch+")。";\r
+                               System.err.println(errmsg);\r
+                               return false;\r
+                       }\r
+                       else if (typ.matches("^l[123]$")) {\r
+                               errmsg = "外部入力にアサインされているチャンネルには変更できません("+Channel+", "+ch+", "+typ+")。";\r
+                               System.err.println(errmsg);\r
+                               return false;\r
+                       }\r
+                       else if (typ.equals("uvd")) {\r
+                               newBC = 0;\r
+                       }\r
+                       else if (typ.equals("bsd")) {\r
+                               newBC = 1;\r
+                       }\r
+                       else if (typ.equals("csd")) {\r
+                               newBC = 2;\r
+                       }\r
+                       else {\r
+                               errmsg = "放送種別が識別できません。プログラム異常です("+Channel+", "+ch+")。";\r
+                               System.err.println(errmsg);\r
+                               return false;\r
+                       }\r
+                       \r
+                       // CH番号\r
+                       Matcher ma = Pattern.compile("(\\d\\d\\d)").matcher(ch);\r
+                       if (ma.find()) {\r
+                               chNo = ma.group(1);\r
+                       }\r
+                       else {\r
+                               errmsg = "CH番号が取得できません("+Channel+", "+ch+")。";\r
+                               System.err.println(errmsg);\r
+                               return false;\r
+                       }\r
+               }\r
+               \r
+               /*\r
+                * CH変更実行\r
+                */\r
+               \r
+               // おまじない\r
+               Authenticator.setDefault(new MyAuthenticator(getUser(), getPasswd()));\r
+               \r
+               int cBC;\r
+               for (int i=0; i<3 && newBC != (cBC = (curBC+i)%3); i++) {\r
+                       // 切り替え実行\r
+                       reqGET("http://"+getIPAddr()+":"+getPortNo()+"/remote/remote.htm?key=21", null);\r
+                       \r
+                       // 地上波→BS・BS→CSは切り替え完了までに時間がかかる\r
+                       CommonUtils.milSleep((cBC == 0 || cBC == 1)?(3000):(1000));\r
+               }\r
+               \r
+               reqGET("http://"+getIPAddr()+":"+getPortNo()+"/remote/remote.htm?key=25", null);\r
+               CommonUtils.milSleep(1000);\r
+               for (int i=0; i<3; i++) {\r
+                       reqGET("http://"+getIPAddr()+":"+getPortNo()+"/remote/remote.htm?key=0"+chNo.substring(i,i+1), null);\r
+                       CommonUtils.milSleep(200);\r
+               }\r
+               reqGET("http://"+getIPAddr()+":"+getPortNo()+"/remote/remote.htm?key=44", null);\r
+               \r
+               return true;\r
+       }\r
+\r
+       /*******************************************************************************\r
+        * レコーダーから予約一覧を取得する\r
+        ******************************************************************************/\r
+       \r
+       public boolean GetRdReserve(boolean force)\r
+       {\r
+               System.out.println("レコーダから予約一覧を取得します("+force+"): "+getRecorderId()+"("+getIPAddr()+":"+getPortNo()+")");\r
+\r
+               errmsg = "";\r
+               \r
+               //\r
+               String myFileId = getIPAddr()+"_"+getPortNo()+"_"+getRecorderId();\r
+               \r
+               rsvedFile                               = "env/reserved."+myFileId+".xml";\r
+               \r
+               String arateTFile               = "env/audiorate."+myFileId+".xml";\r
+               String folderTFile              = "env/folders."+myFileId+".xml";\r
+               String encoderTFile             = "env/encoders."+myFileId+".xml";\r
+               String dvdcompatTFile   = "env/dvdcompat."+myFileId+".xml";\r
+               String deviceTFile              = "env/device."+myFileId+".xml";\r
+               String xChapterTFile    = "env/xchapter."+myFileId+".xml";\r
+               String msChapterTFile   = "env/mschapter."+myFileId+".xml";\r
+               String mvChapterTFile   = "env/mvchapter."+myFileId+".xml";\r
+               String chValueTFile             = "env/chvalue."+myFileId+".xml";\r
+               String chTypeTFile              = "env/chtype."+myFileId+".xml";\r
+               String aspectTFile              = "env/aspect."+myFileId+".xml";\r
+               String bvperfTFile              = "env/bvperf."+myFileId+".xml";\r
+               String lvoiceTFile              = "env/lvoice."+myFileId+".xml";\r
+               String autodelTFile             = "env/autodel."+myFileId+".xml";\r
+               \r
+               File f = new File(rsvedFile);\r
+               if ( force == false && f.exists()) {\r
+                       // ハードコーディング(録画設定ほか)\r
+                       setSettingVrate(vrate);\r
+                       setSettingGenre(genre);\r
+                       \r
+                       // キャッシュから読み出し(録画設定ほか)\r
+                       arate = TVSload(arateTFile);\r
+                       folder = TVSload(folderTFile);\r
+                       encoder = TVSload(encoderTFile);\r
+                       dvdcompat = TVSload(dvdcompatTFile);\r
+                       device = TVSload(deviceTFile);\r
+                       xchapter = TVSload(xChapterTFile);\r
+                       mschapter = TVSload(msChapterTFile);\r
+                       mvchapter = TVSload(mvChapterTFile);\r
+                       chvalue = TVSload(chValueTFile);\r
+                       chtype = TVSload(chTypeTFile);\r
+                       \r
+                       // その他\r
+                       aspect = TVSload(aspectTFile);\r
+                       bvperf = TVSload(bvperfTFile);\r
+                       lvoice = TVSload(lvoiceTFile);\r
+                       autodel = TVSload(autodelTFile);\r
+                       \r
+                       // キャッシュから読み出し(予約一覧)\r
+                       setReserves(ReservesFromFile(rsvedFile));\r
+                       \r
+                       // なぜか設定ファイルが空になっている場合があるので、その際は再取得する\r
+                       if (arate.size()>0 && folder.size()>0 && encoder.size()>0 && device.size()>0 &&\r
+                                       xchapter.size()>0 && mschapter.size()>0 && mvchapter.size()>0 &&\r
+                                       chvalue.size()>0 && chtype.size()>0) {\r
+                               return(true);\r
+                       }\r
+               }\r
+               \r
+               boolean isfault = false;\r
+               \r
+               // おまじない\r
+               Authenticator.setDefault(new MyAuthenticator(getUser(), getPasswd()));\r
+               \r
+               // RDから予約一覧を取り出す\r
+               Matcher ma = null;\r
+               String idx = "";\r
+               String response="";\r
+               {\r
+                       reportProgress(MSGID+"処理IDを取得します(1/3).");\r
+                       String[] d = reqGET("http://"+getIPAddr()+":"+getPortNo()+"/reserve/b_rsv.htm",null);\r
+                       response = d[1];\r
+                       \r
+                       if (response == null) {\r
+                               errmsg = ERRID+"レコーダーが反応しません";\r
+                               return(false);\r
+                       }\r
+               }\r
+               ma = Pattern.compile("/reserve/(\\d+?)/reserve.htm").matcher(response);\r
+               if ( ! ma.find()) {\r
+                       errmsg = ERRID+"レコーダーからの情報取得に失敗しました(処理ID)";\r
+                       return(false);\r
+               }\r
+               \r
+               idx = ma.group(1);      // 処理ID\r
+               \r
+               {\r
+                       reportProgress(MSGID+"予約一覧を取得します(2/3).");\r
+                       String[] d = reqGET("http://"+getIPAddr()+":"+getPortNo()+"/reserve/"+idx+"/reserve.htm",null);\r
+                       response = d[1];\r
+                       \r
+                       if (response == null) {\r
+                               errmsg = ERRID+"レコーダーが反応しません";\r
+                               return(false);\r
+                       }\r
+               }\r
+               \r
+               // 予約エントリー数を取得する\r
+               int RsvCnt = 0;\r
+               ma = Pattern.compile("RsvCnt\\s*=\\s*(\\d+?);").matcher(response);\r
+               if ( ! ma.find()) {\r
+                       errmsg = ERRID+"レコーダーからの情報取得に失敗しました(予約一覧)";\r
+                       return false;\r
+               }\r
+               RsvCnt = Integer.valueOf(ma.group(1));\r
+               \r
+               // (1)録画設定の取得\r
+               {\r
+                       // ハング防止のおまじない\r
+                       reportProgress(MSGID+"録画設定を取得します(3/3).");\r
+                       reqGET("http://"+getIPAddr()+":"+getPortNo()+"/reserve/"+idx+"/b_rsvinfo.htm?0&"+(RsvCnt+1),null);\r
+                       \r
+                       String[] d = reqGET("http://"+getIPAddr()+":"+getPortNo()+"/reserve/"+idx+"/rsvinfo.htm",null);\r
+                       String res = d[1];\r
+                       \r
+                       if (res == null) {\r
+                               errmsg = ERRID+"レコーダーが反応しません";\r
+                               return(false);\r
+                       }\r
+                       \r
+                       //Matcher mb = null;\r
+                       \r
+                       // 正常取得チェック\r
+                       if ( ! res.matches("[\\s\\S]*var hdd_folder_text[\\s\\S]*")) {\r
+                               errmsg = ERRID+"レコーダーからの情報取得に失敗しました(録画設定)";\r
+                               return false;\r
+                       }\r
+                       \r
+                       ArrayList<TextValueSet> tvsa = null;\r
+                       \r
+                       // (1-1)画質設定 [保存しない]\r
+                       setSettingVrate(vrate);\r
+                       \r
+                       // (1-2)音質設定\r
+                       tvsa = new ArrayList<TextValueSet>();\r
+                       setSettingEtc(tvsa,"amode",0,res);\r
+                       if ( tvsa.size() > 0 ) {\r
+                               TVSsave(arate = tvsa, arateTFile);\r
+                       }\r
+                       else {\r
+                               System.err.println(errmsg = ERRID+"【致命的エラー】 音質設定が取得できません");\r
+                               isfault = true;\r
+                       }\r
+                       \r
+                       // (1-3)フォルダ一覧\r
+                       tvsa = new ArrayList<TextValueSet>();\r
+                       setSettingFolder(tvsa,res);\r
+                       if ( tvsa.size() > 0 ) {\r
+                               TVSsave(folder = tvsa, folderTFile);\r
+                       }\r
+                       else {\r
+                               System.err.println(errmsg = ERRID+"【致命的エラー】 フォルダ一覧が取得できません");\r
+                               isfault = true;\r
+                       }\r
+                       \r
+                       // (1-4)エンコーダ\r
+                       tvsa = new ArrayList<TextValueSet>();\r
+                       setSettingEncoder(tvsa,res);\r
+                       if ( tvsa.size() == 0 ) {\r
+                               // HTMLからとれなければ固定で返す\r
+                               setSettingEncoder(tvsa);\r
+                       }\r
+                       if ( tvsa.size() > 0 ) {\r
+                               TVSsave(encoder = tvsa, encoderTFile);\r
+                       }\r
+                       else {\r
+                               System.err.println(errmsg = ERRID+"【致命的エラー】 エンコーダが取得できません");\r
+                               isfault = true;\r
+                       }\r
+                       \r
+                       // (1-5)DVD互換モード\r
+                       tvsa = new ArrayList<TextValueSet>();\r
+                       setSettingEtc(tvsa,"dvdr",1,res);\r
+                       if ( tvsa.size() > 0 ) {\r
+                               TVSsave(dvdcompat = tvsa, dvdcompatTFile);\r
+                       }\r
+                       else {\r
+                               System.err.println(errmsg = ERRID+"【致命的エラー】 DVD互換モードが取得できません");\r
+                               isfault = true;\r
+                       }\r
+                       \r
+                       // (1-6)記録先デバイス\r
+                       tvsa = new ArrayList<TextValueSet>();\r
+                       setSettingEtc(tvsa,"media",0,res);\r
+                       if ( tvsa.size() > 0 ) {\r
+                               TVSsave(device = tvsa, deviceTFile);\r
+                       }\r
+                       else {\r
+                               System.err.println(errmsg = ERRID+"【致命的エラー】 記録先デバイスが取得できません");\r
+                               isfault = true;\r
+                       }\r
+                       \r
+                       // (1-7)自動チャプター関連\r
+                       tvsa = new ArrayList<TextValueSet>();\r
+                       setSettingEtc(tvsa,"mutechapter",0,res);\r
+                       if ( tvsa.size() > 0 ) {\r
+                               TVSsave(xchapter = tvsa, xChapterTFile);\r
+                       }\r
+                       else {\r
+                               System.err.println(errmsg = ERRID+"【致命的エラー】 自動チャプタ(1)が取得できません");\r
+                               isfault = true;\r
+                       }\r
+                       \r
+                       tvsa = new ArrayList<TextValueSet>();\r
+                       setSettingEtc(tvsa,"magicchapter",0,res);\r
+                       if ( tvsa.size() > 0 ) {\r
+                               TVSsave(mschapter = tvsa, msChapterTFile);\r
+                       }\r
+                       else {\r
+                               System.err.println(errmsg = ERRID+"【致命的エラー】 自動チャプタ(2)が取得できません");\r
+                               isfault = true;\r
+                       }\r
+                       \r
+                       tvsa = new ArrayList<TextValueSet>();\r
+                       setSettingEtc(tvsa,"cmchapter",0,res);\r
+                       if ( tvsa.size() > 0 ) {\r
+                               TVSsave(mvchapter = tvsa, mvChapterTFile);\r
+                       }\r
+                       else {\r
+                               System.err.println(errmsg = ERRID+"【致命的エラー】 自動チャプタ(3)が取得できません");\r
+                               isfault = true;\r
+                       }\r
+                       \r
+                       // (1-8)チャンネルコードバリュー  - uva、bsaは廃止 -\r
+                       ArrayList<TextValueSet> tvsb = new ArrayList<TextValueSet>(); \r
+                       ArrayList<TextValueSet> tvsc = new ArrayList<TextValueSet>(); \r
+                       setSettingChCodeValue(tvsb,tvsc,res);\r
+                       if ( tvsb.size() > 0 && tvsc.size() > 0 ) {\r
+                               TVSsave(chvalue = tvsb, chValueTFile);\r
+                               TVSsave(chtype = tvsc, chTypeTFile);\r
+                       }\r
+                       else {\r
+                               System.err.println(errmsg = ERRID+"【致命的エラー】 チャンネルコードバリューが取得できません");\r
+                               isfault = true;\r
+                       }\r
+                       \r
+                       // (1-9)ジャンル [保存しない]\r
+                       setSettingGenre(genre);\r
+                       \r
+                       // (1-10)番組詳細 [関係ない]\r
+                       \r
+                       // (1-11)録画のりしろ\r
+                       tvsa = new ArrayList<TextValueSet>();\r
+                       setSettingEtc(tvsa,"edge",0,res);\r
+                       if ( tvsa.size() > 0 ) {\r
+                               TVSsave(aspect = tvsa, aspectTFile);\r
+                       }\r
+                       else {\r
+                               System.err.println(errmsg = ERRID+"【致命的エラー】 録画のりしろが取得できません");\r
+                               isfault = true;\r
+                       }\r
+                       \r
+                       // (1-12)録画優先度\r
+                       tvsa = new ArrayList<TextValueSet>();\r
+                       setSettingEtc(tvsa,"rec_priority",0,res);\r
+                       if ( tvsa.size() > 0 ) {\r
+                               TVSsave(bvperf = tvsa, bvperfTFile);\r
+                       }\r
+                       else {\r
+                               System.err.println(errmsg = ERRID+"【致命的エラー】 録画優先度が取得できません");\r
+                               isfault = true;\r
+                       }\r
+                       \r
+                       // (1-13)ライン音声選択\r
+                       tvsa = new ArrayList<TextValueSet>();\r
+                       setSettingEtc(tvsa,"lvoice",0,res);\r
+                       if ( tvsa.size() > 0 ) {\r
+                               TVSsave(lvoice = tvsa, lvoiceTFile);\r
+                       }\r
+                       else {\r
+                               System.err.println(errmsg = ERRID+"【致命的エラー】 ライン音声選択が取得できません");\r
+                               isfault = true;\r
+                       }\r
+                       \r
+                       // (1-14)自動削除\r
+                       tvsa = new ArrayList<TextValueSet>();\r
+                       setSettingEtc(tvsa,"auto_del",0,res);\r
+                       if ( tvsa.size() > 0 ) {\r
+                               TVSsave(autodel = tvsa, autodelTFile);\r
+                       }\r
+                       else {\r
+                               System.err.println(errmsg = ERRID+"【致命的エラー】 自動削除が取得できません");\r
+                               isfault = true;\r
+                       }\r
+               }\r
+               \r
+               // 予約一覧データの分析\r
+               ArrayList<ReserveList> ra = decodeReservedList(response); \r
+               for (ReserveList entry : ra) {\r
+                       \r
+                       // 放送局名変換\r
+                       entry.setCh_name(getChCode().getCH_REC2WEB(entry.getChannel()));\r
+                       \r
+                       // TS/TSE->DR/ACV\r
+                       if (entry.getRec_mode().startsWith("[TS]")) {\r
+                               entry.setRec_mode(entry.getRec_mode().replaceFirst("\\[TS\\]", ITEM_VIDEO_TYPE_DR));\r
+                       }\r
+                       else if (entry.getRec_mode().startsWith("[TSE]")) {\r
+                               entry.setRec_mode(entry.getRec_mode().replaceFirst("\\[TSE\\]", ITEM_VIDEO_TYPE_AVC));\r
+                       }\r
+                       else if (entry.getRec_mode().matches("^\\s*$")) {\r
+                               entry.setRec_mode("");\r
+                       }\r
+                       \r
+                       // TS->DR\r
+                       translateAttributeTuner(entry);\r
+                       \r
+                       // タイトル自動補完フラグなど本体からは取得できない情報を引き継ぐ\r
+                       copyAttributes(entry, getReserves());\r
+               }\r
+               setReserves(ra);\r
+               \r
+               // キャッシュに保存\r
+               ReservesToFile(getReserves(), rsvedFile);\r
+               \r
+               // 取得した情報の表示\r
+               System.out.println("---Reserved List Start---");\r
+               for ( int i = 0; i<getReserves().size(); i++ ) {\r
+                       // 詳細情報の取得\r
+                       ReserveList e = getReserves().get(i);\r
+                       System.out.println(String.format("[%s] %s\t%s\t%s\t%s:%s\t%s:%s\t%sm\t%s\t%s(%s)\t%s\t%s",\r
+                                       (i+1), e.getId(), e.getRec_pattern(), e.getRec_nextdate(), e.getAhh(), e.getAmm(), e.getZhh(),  e.getZmm(),     e.getRec_min(), e.getRec_mode(), e.getTitle(), e.getTitlePop(), e.getChannel(), e.getCh_name()));\r
+               }\r
+               System.out.println("---Reserved List End---");\r
+               \r
+               return(!isfault);\r
+       }\r
+       \r
+       @Override\r
+       public boolean isThereAdditionalDetails() {\r
+               return true;\r
+       }\r
+       @Override\r
+       public boolean GetRdReserveDetails()\r
+       {\r
+               /*\r
+                *  前処理\r
+                */\r
+               \r
+               // おまじない\r
+               Authenticator.setDefault(new MyAuthenticator(getUser(), getPasswd()));\r
+               \r
+               Matcher mx = null;\r
+               String idx = "";\r
+               String header="";\r
+               String response="";\r
+               {\r
+                       reportProgress("処理IDを取得します(1/1).");\r
+                       String[] d = reqGET("http://"+getIPAddr()+":"+getPortNo()+"/reserve/b_rsv.htm",null);\r
+                       header = d[0];\r
+                       response = d[1];\r
+                       \r
+                       if (response == null) {\r
+                               errmsg = "レコーダーが反応しません";\r
+                               return(false);\r
+                       }\r
+               }\r
+               mx = Pattern.compile("/reserve/(\\d+?)/reserve.htm").matcher(response);\r
+               if ( ! mx.find()) {\r
+                       errmsg = "レコーダーからの情報取得に失敗しました(処理ID)";\r
+                       return(false);\r
+               }\r
+               idx = mx.group(1);\r
+               \r
+               /*\r
+                *  詳細確認\r
+                */\r
+               int lineno = 0;\r
+               ArrayList<ReserveList> ra = getReserves();\r
+               for (ReserveList entry : ra) {\r
+                       \r
+                       lineno++;\r
+                       \r
+                       // 詳細情報を引いてみる\r
+                       reportProgress("+番組詳細を取得します("+lineno+"/"+ra.size()+").");\r
+                       reqGET("http://"+getIPAddr()+":"+getPortNo()+"/reserve/"+idx+"/b_rsvinfo.htm?"+entry.getId()+"&"+lineno,null);\r
+                       \r
+                       String[] d = reqGET("http://"+getIPAddr()+":"+getPortNo()+"/reserve/"+idx+"/rsvinfo.htm",null);\r
+                       if (d[1] == null) {\r
+                               reportProgress("レコーダーからの戻り値が不正です");\r
+                               continue;\r
+                       }\r
+                       \r
+                       // (1-1)画質設定\r
+                       mx = Pattern.compile("videotype_digital_value = (\\d+);").matcher(d[1]);\r
+                       if ( ! mx.find()) {\r
+                               reportProgress("レコーダーからの戻り値が不正です(画質1)");\r
+                               continue;\r
+                       }\r
+                       String vtypestr = mx.group(1);\r
+                       mx = Pattern.compile("videomode_digital_value = (\\d+);").matcher(d[1]);\r
+                       if ( ! mx.find()) {\r
+                               reportProgress("レコーダーからの戻り値が不正です(画質2)");\r
+                               continue;\r
+                       }\r
+                       String vmodestr = mx.group(1);\r
+                       entry.setRec_mode(value2text(vrate, vtypestr+":"+vmodestr));\r
+\r
+                       // (1-2)音質設定 [一覧から取得]\r
+                       \r
+                       // (1-6)記録先デバイス [一覧から取得]\r
+                       \r
+                       // (1-3)フォルダ\r
+                       mx = Pattern.compile("var folder_current = \"(.*?)\";").matcher(d[1]);\r
+                       if ( ! mx.find()) {\r
+                               reportProgress("レコーダーからの戻り値が不正です(フォルダ)");\r
+                               continue;\r
+                       }\r
+                       String fTyp = (entry.getRec_device().equals("HDD")) ? (ITEM_MEDIA_HDD) : (ITEM_MEDIA_USB);\r
+                       for ( TextValueSet t : folder ) {\r
+                               if (t.getText().startsWith(fTyp)) {\r
+                                       if (t.getValue().equals(mx.group(1))) {\r
+                                               entry.setRec_folder(t.getText());\r
+                                               break;\r
+                                       }\r
+                               }\r
+                       }\r
+                       \r
+                       // (1-4)エンコーダ [一覧から取得]\r
+                       \r
+                       // (1-5)DVD互換モード\r
+                       mx = Pattern.compile("var dvdr_current = (\\d+?);").matcher(d[1]);\r
+                       if ( ! mx.find()) {\r
+                               reportProgress("レコーダーからの戻り値が不正です(DVD互換モード)");\r
+                               continue;\r
+                       }\r
+                       entry.setRec_dvdcompat(dvdcompat.get(Integer.valueOf(mx.group(1))).getText());\r
+                       \r
+                       // (1-7)自動チャプター関連\r
+                       mx = Pattern.compile("var mutechapter_current = (\\d+?);").matcher(d[1]);\r
+                       if ( ! mx.find()) {\r
+                               reportProgress("レコーダーからの戻り値が不正です(自動チャプタ1)");\r
+                               continue;\r
+                       }\r
+                       entry.setRec_xchapter(xchapter.get(Integer.valueOf(mx.group(1))).getText());\r
+                       \r
+                       mx = Pattern.compile("var magicchapter_current = (\\d+?);").matcher(d[1]);\r
+                       if ( ! mx.find()) {\r
+                               reportProgress("レコーダーからの戻り値が不正です(自動チャプタ2)");\r
+                               continue;\r
+                       }\r
+                       entry.setRec_mschapter(mschapter.get(Integer.valueOf(mx.group(1))).getText());\r
+                       \r
+                       mx = Pattern.compile("var cmchapter_current = (\\d+?);").matcher(d[1]);\r
+                       if ( ! mx.find()) {\r
+                               reportProgress("レコーダーからの戻り値が不正です(自動チャプタ3)");\r
+                               continue;\r
+                       }\r
+                       entry.setRec_mvchapter(mvchapter.get(Integer.valueOf(mx.group(1))).getText());\r
+                       \r
+                       // (1-8)チャンネル [一覧から取得]\r
+                       \r
+                       // (1-9)ジャンル\r
+                       mx = Pattern.compile("var genre_current = (\\d+?);").matcher(d[1]);\r
+                       if ( ! mx.find()) {\r
+                               reportProgress("レコーダーからの戻り値が不正です(ジャンル)");\r
+                               continue;\r
+                       }\r
+                       int gVal = Integer.valueOf(mx.group(1));\r
+                       if (gVal >= genre.size()) {\r
+                               gVal = genre.size()-1;\r
+                       }\r
+                       entry.setRec_genre(genre.get(gVal).getText());\r
+                       \r
+                       // (1-10)番組詳細\r
+                       mx = Pattern.compile("var title_detail = \"(.*?)\";").matcher(d[1]);\r
+                       if ( ! mx.find()) {\r
+                               reportProgress("レコーダーからの戻り値が不正です(番組詳細)");\r
+                               continue;\r
+                       }\r
+                       entry.setDetail(CommonUtils.unEscape(mx.group(1)).replaceAll("\\\\r\\\\n","\n"));\r
+                       \r
+                       // (1-11)録画のりしろ\r
+                       mx = Pattern.compile("var edge_current = (\\d+?);").matcher(d[1]);\r
+                       if ( ! mx.find()) {\r
+                               reportProgress("レコーダーからの戻り値が不正です(録画のりしろ)");\r
+                               continue;\r
+                       }\r
+                       entry.setRec_aspect(aspect.get(Integer.valueOf(mx.group(1))).getText());\r
+                       \r
+                       // (1-12)録画優先度\r
+                       mx = Pattern.compile("var rec_priority_current = (\\d+?);").matcher(d[1]);\r
+                       if ( ! mx.find()) {\r
+                               reportProgress("レコーダーからの戻り値が不正です(録画優先度)");\r
+                               continue;\r
+                       }\r
+                       entry.setRec_bvperf(bvperf.get(Integer.valueOf(mx.group(1))).getText());\r
+                       \r
+                       // (1-13)ライン音声選択\r
+                       mx = Pattern.compile("var lvoice_current = (\\d+?);").matcher(d[1]);\r
+                       if ( ! mx.find()) {\r
+                               reportProgress("レコーダーからの戻り値が不正です(ライン音声選択)");\r
+                               continue;\r
+                       }\r
+                       entry.setRec_lvoice(lvoice.get(Integer.valueOf(mx.group(1))).getText());\r
+                       \r
+                       // (1-14)自動削除\r
+                       mx = Pattern.compile("var auto_del_current = (\\d+?);").matcher(d[1]);\r
+                       if ( ! mx.find()) {\r
+                               reportProgress("レコーダーからの戻り値が不正です(自動削除)");\r
+                               continue;\r
+                       }\r
+                       entry.setRec_autodel(autodel.get(Integer.valueOf(mx.group(1))).getText());\r
+               }\r
+               \r
+               // キャッシュに保存\r
+               ReservesToFile(getReserves(), rsvedFile);\r
+               \r
+               return(true);\r
+       }\r
+       \r
+       /*******************************************************************************\r
+        * 新規予約\r
+        ******************************************************************************/\r
+       \r
+       public boolean PostRdEntry(ReserveList r)\r
+       {\r
+               //\r
+               String chcode = cc.getCH_WEB2CODE(r.getCh_name());\r
+               if (chcode == null) {\r
+                       errmsg = "【警告】Web番組表の放送局名「"+r.getCh_name()+"」をCHコードに変換できません。CHコード設定を修正してください。" ;\r
+                       System.out.println(errmsg);\r
+                       return(false);\r
+               }\r
+               \r
+               System.out.println("Run: PostRdEntry("+r.getTitle()+")");\r
+               \r
+               errmsg = "";\r
+\r
+               // おまじない\r
+               Authenticator.setDefault(new MyAuthenticator(getUser(), getPasswd()));\r
+               \r
+               // RDから予約一覧を取り出す\r
+               Matcher ma = null;\r
+               String idx = "";\r
+               String header;\r
+               String response;\r
+               {\r
+                       reportProgress("get reserved list(1/7).");\r
+                       String[] d = reqGET("http://"+getIPAddr()+":"+getPortNo()+"/reserve/b_rsv.htm",null);\r
+                       header = d[0];\r
+                       response = d[1];\r
+                       \r
+                       if ( response == null ) {\r
+                               errmsg = "レコーダーが反応しません。";\r
+                               return(false);\r
+                       }\r
+               }\r
+               ma = Pattern.compile("/reserve/(\\d+?)/reserve.htm").matcher(response);\r
+               if (ma.find()) {\r
+                       reportProgress("get reserved list(2/7).");\r
+                       idx = ma.group(1);\r
+                       String[] d = reqGET("http://"+getIPAddr()+":"+getPortNo()+"/reserve/"+idx+"/reserve.htm",null);\r
+                       header = d[0];\r
+                       response = d[1];\r
+                       \r
+                       if ( response == null ) {\r
+                               errmsg = "レコーダーが反応しません。";\r
+                               return(false);\r
+                       }\r
+               }\r
+               \r
+               // 予約エントリー数を取得する\r
+               int RsvCnt = 0;\r
+               ma = Pattern.compile("RsvCnt\\s*=\\s*(\\d+?);").matcher(response);\r
+               if (ma.find()) {\r
+                       RsvCnt = Integer.valueOf(ma.group(1));\r
+               }\r
+               \r
+               // RDに新規登録要請\r
+               {\r
+                       // ハング防止のおまじない\r
+                       reportProgress("get program(3/7).");\r
+                       reqGET("http://"+getIPAddr()+":"+getPortNo()+"/reserve/"+idx+"/b_rsvinfo.htm?0&"+(RsvCnt+1),null);\r
+                       \r
+                       reportProgress("get program(4/7).");\r
+                       reqGET("http://"+getIPAddr()+":"+getPortNo()+"/reserve/"+idx+"/rsvinfo.htm",null);\r
+               }\r
+\r
+               // POSTデータを変換する\r
+               Hashtable<String, String> pdat = modPostdata(r);\r
+               \r
+               // RDへの情報作成\r
+               String pstr = joinPoststr(pdat);\r
+\r
+               // RDへ情報送信\r
+               {               \r
+                       reportProgress("send request(5/7).");\r
+                       String[] d = reqPOST("http://"+getIPAddr()+":"+getPortNo()+"/reserve/"+idx+"/entry.htm", pstr, null);\r
+                       header = d[0];\r
+                       response = d[1];\r
+               }\r
+               \r
+               // 登録結果の確認\r
+               ma = Pattern.compile("alert\\(msg\\)").matcher(response);\r
+               if ( ma.find() ) {\r
+                       Matcher mb = Pattern.compile("\\bmsg=\"([\\s\\S]+?)\";").matcher(response);\r
+                       if (mb.find()) {\r
+                               errmsg = mb.group(1);\r
+                               System.out.printf("\n<<< Message from RD >>> \"%s\"\n\n", errmsg);\r
+                               Matcher mc = Pattern.compile("(予約時間が重複しています。|W録の振り替えをおこないました)").matcher(errmsg);\r
+                               if ( ! mc.find() ) {\r
+                                       return(false);\r
+                               }\r
+                       }                       \r
+               }\r
+\r
+\r
+               \r
+               /*\r
+                * 予約情報の調整 \r
+                */\r
+               \r
+               // 予約ID番号を取得(キャッシュに存在しない番号が新番号)\r
+               {\r
+                       reportProgress("get identifier(6/7).");\r
+                       String[] d = reqGET("http://"+getIPAddr()+":"+getPortNo()+"/reserve/b_rsv.htm",null);\r
+                       header = d[0];\r
+                       response = d[1];\r
+               }\r
+               ma = Pattern.compile("/reserve/(\\d+?)/reserve.htm").matcher(response);\r
+               if (ma.find()) {\r
+                       idx = ma.group(1);\r
+                       reportProgress("get identifier(7/7).");\r
+                       String[] d = reqGET("http://"+getIPAddr()+":"+getPortNo()+"/reserve/"+idx+"/reserve.htm",null);\r
+                       header = d[0];\r
+                       response = d[1];\r
+               }\r
+               r.setId(getNewId(response));\r
+               \r
+               if ( r.getId() == null ) {\r
+                       errmsg = ERRID+"【致命的エラー】 予約は成功したと思われますが、該当する予約IDを見つけることができませんでした。";\r
+                       System.out.printf("\n<<< Message from RD >>> \"%s\"\n\n", errmsg);\r
+                       return false;\r
+               }\r
+               \r
+               reportProgress("予約IDは"+r.getId()+"です。");\r
+\r
+               \r
+               // 音質(TS/TSEでは音質の設定はできない)\r
+               //ma = Pattern.compile("^\\[TS").matcher(r.getRec_mode());\r
+               if (r.getRec_mode().startsWith(ITEM_VIDEO_TYPE_DR) || r.getRec_mode().startsWith(ITEM_VIDEO_TYPE_AVC)) {\r
+                       r.setRec_audio("");\r
+               }\r
+\r
+               // 予約パターンID\r
+               r.setRec_pattern_id(getRec_pattern_Id(r.getRec_pattern()));\r
+\r
+               // 次回予定日\r
+               r.setRec_nextdate(CommonUtils.getNextDate(r));\r
+               //r.setRec_nextdate(getNextDate(r.getRec_pattern(), r.getZhh()+":"+r.getZmm()));\r
+               \r
+               // 録画長\r
+               r.setRec_min(CommonUtils.getRecMin(r.getAhh(),r.getAmm(),r.getZhh(),r.getZmm()));\r
+               \r
+               // 開始日時・終了日時\r
+               getStartEndDateTime(r);\r
+               \r
+               // 予約リストを更新\r
+               getReserves().add(r);\r
+               \r
+               // キャッシュに保存\r
+               ReservesToFile(getReserves(), rsvedFile);\r
+               \r
+               System.out.printf("\n<<< Message from RD >>> \"%s\"\n\n", "正常に登録できました。");\r
+               return(true);\r
+       }\r
+\r
+       /*******************************************************************************\r
+        * 予約更新\r
+        ******************************************************************************/\r
+       \r
+       public boolean UpdateRdEntry(ReserveList o, ReserveList r) {\r
+               //\r
+               String chcode = cc.getCH_WEB2CODE(r.getCh_name());\r
+               if (chcode == null) {\r
+                       errmsg = "【警告】Web番組表の放送局名「"+r.getCh_name()+"」をCHコードに変換できません。CHコード設定を修正してください。" ;\r
+                       System.out.println(errmsg);\r
+                       return(false);\r
+               }\r
+\r
+               System.out.println("Run: UpdateRdEntry()");\r
+               \r
+               errmsg = "";\r
+\r
+               // おまじない\r
+               Authenticator.setDefault(new MyAuthenticator(getUser(), getPasswd()));\r
+               \r
+               // 更新準備\r
+               Matcher ma = null;\r
+               String idx = "";\r
+               int lineno = 0;\r
+               String header;\r
+               String response;\r
+               {\r
+                       reportProgress("get reserved list(1/5).");\r
+                       String[] d = reqGET("http://"+getIPAddr()+":"+getPortNo()+"/reserve/b_rsv.htm",null);\r
+                       header = d[0];\r
+                       response = d[1];\r
+                       \r
+                       if ( response == null ) {\r
+                               errmsg = "レコーダーが反応しません。";\r
+                               return(false);\r
+                       }\r
+               }\r
+               ma = Pattern.compile("/reserve/(\\d+?)/reserve.htm").matcher(response);\r
+               if (ma.find()) {\r
+                       idx = ma.group(1);\r
+                       reportProgress("get identifier(2/5).");\r
+                       String[] d = reqGET("http://"+getIPAddr()+":"+getPortNo()+"/reserve/"+idx+"/reserve.htm",null);\r
+                       header = d[0];\r
+                       response = d[1];\r
+                       \r
+                       if ( response == null ) {\r
+                               errmsg = "レコーダーが反応しません。";\r
+                               return(false);\r
+                       }\r
+               }\r
+               ma = Pattern.compile("c1\\[(\\d+?)\\]=\""+r.getId()+"\";").matcher(response);\r
+               if ( ma.find() ) {\r
+                       lineno = Integer.valueOf(ma.group(1))+1;\r
+               }\r
+               \r
+               // RDに更新要請\r
+               {\r
+                       // ハング防止のおまじない\r
+                       reportProgress("get program(3/5).");\r
+                       reqGET("http://"+getIPAddr()+":"+getPortNo()+"/reserve/"+idx+"/b_rsvinfo.htm?"+r.getId()+"&"+lineno,null);\r
+                       \r
+                       reportProgress("get program(4/5).");\r
+                       reqGET("http://"+getIPAddr()+":"+getPortNo()+"/reserve/"+idx+"/rsvinfo.htm",null);\r
+               }\r
+\r
+               // POSTデータを変換する\r
+               Hashtable<String, String> pdat = modPostdata(r);\r
+               \r
+               // RDへの情報作成\r
+               String pstr = joinPoststr(pdat);\r
+\r
+               // RDへ情報送信\r
+               {               \r
+                       reportProgress("send request.(5/5)");\r
+                       String[] d = reqPOST("http://"+getIPAddr()+":"+getPortNo()+"/reserve/"+idx+"/entry.htm", pstr, null);\r
+                       header = d[0];\r
+                       response = d[1];\r
+               }\r
+\r
+               // 更新結果の確認\r
+               ma = Pattern.compile("alert\\(msg\\)").matcher(response);\r
+               if ( ma.find() ) {\r
+                       Matcher mb = Pattern.compile("\\bmsg=\"([\\s\\S]+?)\";").matcher(response);\r
+                       if (mb.find()) {\r
+                               errmsg = mb.group(1);\r
+                               System.out.printf("\n<<< Message from RD >>> \"%s\"\n\n", errmsg);\r
+                               Matcher mc = Pattern.compile("(予約時間が重複しています。|W録の振り替えをおこないました)").matcher(errmsg);\r
+                               if ( ! mc.find() ) {\r
+                                       return(false);\r
+                               }\r
+                       }                       \r
+               }\r
+\r
+               System.out.printf("\n<<< Message from RD >>> \"%s\"\n\n", "正常に更新できました。");\r
+\r
+               /*\r
+                * 予約情報の調整 \r
+                */\r
+               \r
+               // 音質(TS/TSEでは音質の設定はできない)\r
+               //ma = Pattern.compile("^\\[TS").matcher(r.getRec_mode());\r
+               if (r.getRec_mode().startsWith(ITEM_VIDEO_TYPE_DR) || r.getRec_mode().startsWith(ITEM_VIDEO_TYPE_AVC)) {\r
+                       r.setRec_audio("");\r
+               }\r
+\r
+               // 予約パターンID\r
+               r.setRec_pattern_id(getRec_pattern_Id(r.getRec_pattern()));\r
+               \r
+               // 次回予定日\r
+               r.setRec_nextdate(CommonUtils.getNextDate(r));\r
+               \r
+               // 録画長\r
+               r.setRec_min(CommonUtils.getRecMin(r.getAhh(),r.getAmm(),r.getZhh(),r.getZmm()));\r
+               \r
+               // 開始日時・終了日時\r
+               getStartEndDateTime(r);\r
+               \r
+               // 情報置き換え\r
+               getReserves().remove(o);\r
+               getReserves().add(r);\r
+\r
+               // キャッシュに保存\r
+               ReservesToFile(getReserves(), rsvedFile);\r
+\r
+               return(true);\r
+       }\r
+\r
+       /*******************************************************************************\r
+        * 予約削除\r
+        ******************************************************************************/\r
+       \r
+       public ReserveList RemoveRdEntry(String delid) {\r
+               \r
+               System.out.println("Run: RemoveRdEntry()");\r
+               \r
+               errmsg = "";\r
+\r
+               // おまじない\r
+               Authenticator.setDefault(new MyAuthenticator(getUser(), getPasswd()));\r
+\r
+               // 削除対象を探す\r
+               ReserveList rx = null;\r
+               for (  ReserveList reserve : getReserves() )  {\r
+                       if (reserve.getId().equals(delid)) {\r
+                               rx = reserve;\r
+                               break;\r
+                       }\r
+               }\r
+               if (rx == null) {\r
+                       return(null);\r
+               }\r
+\r
+               // 予約行番号を取得\r
+               Matcher ma = null;\r
+               String idx = "";\r
+               int lineno = 0;\r
+               String header;\r
+               String response;\r
+               {\r
+                       reportProgress("get identifier(1/5).");\r
+                       String[] d = reqGET("http://"+getIPAddr()+":"+getPortNo()+"/reserve/b_rsv.htm",null);\r
+                       header = d[0];\r
+                       response = d[1];\r
+                       if ( response == null ) {\r
+                               errmsg = "レコーダーが反応しません。";\r
+                               return(null);\r
+                       }\r
+               }\r
+               ma = Pattern.compile("/reserve/(\\d+?)/reserve.htm").matcher(response);\r
+               if (ma.find()) {\r
+                       idx = ma.group(1);\r
+                       reportProgress("get identifier(2/5).");\r
+                       String[] d = reqGET("http://"+getIPAddr()+":"+getPortNo()+"/reserve/"+idx+"/reserve.htm",null);\r
+                       header = d[0];\r
+                       response = d[1];\r
+               }\r
+               ma = Pattern.compile("c1\\[(\\d+?)\\]=\""+rx.getId()+"\";").matcher(response);\r
+               if ( ma.find() ) {\r
+                       lineno = Integer.valueOf(ma.group(1))+1;\r
+               }\r
+\r
+               // RDに削除要請\r
+               {\r
+                       // ハング防止のおまじない\r
+                       reportProgress("get program(3/5).");\r
+                       reqGET("http://"+getIPAddr()+":"+getPortNo()+"/reserve/"+idx+"/b_rsvinfo.htm?"+rx.getId()+"&"+lineno,null);\r
+                       \r
+                       reportProgress("get program(4/5).");\r
+                       reqGET("http://"+getIPAddr()+":"+getPortNo()+"/reserve/"+idx+"/rsvinfo.htm",null);\r
+               }\r
+               {               \r
+                       reportProgress("send request.(5/5).");\r
+                       String[] d = reqGET("http://"+getIPAddr()+":"+getPortNo()+"/reserve/"+idx+"/delete.htm", null);\r
+                       header = d[0];\r
+                       response = d[1];\r
+               }\r
+\r
+               // 削除結果の確認\r
+               ma = Pattern.compile("alert\\(msg\\)").matcher(response);\r
+               if ( ma.find() ) {\r
+                       Matcher mb = Pattern.compile("\\bmsg=\"([\\s\\S]+?)\";").matcher(response);\r
+                       if (mb.find()) {\r
+                               errmsg = mb.group(1);\r
+                               System.out.printf("\n<<< Message from RD >>> \"%s\"\n\n", errmsg);\r
+                               Matcher mc = Pattern.compile("W録の振り替えをおこないました").matcher(errmsg);\r
+                               if ( ! mc.find() ) {\r
+                                       return(null);\r
+                               }\r
+                       }                       \r
+               }\r
+               \r
+               // 予約リストを更新\r
+               getReserves().remove(rx);\r
+               \r
+               // キャッシュに保存\r
+               ReservesToFile(getReserves(), rsvedFile);\r
+               \r
+               System.out.printf("\n<<< Message from RD >>> \"%s\"\n\n", "正常に削除できました。");\r
+               return(rx);\r
+       }\r
+       \r
+       \r
+       /* ここまで */\r
+       \r
+       \r
+       \r
+\r
+       \r
+       \r
+       /* 個別コード-ここから最後まで */\r
+\r
+       /*******************************************************************************\r
+        * 非公開メソッド\r
+        ******************************************************************************/\r
+\r
+       private String joinPoststr(Hashtable<String, String> pdat)\r
+       {\r
+               String[] pkeys = {\r
+                       "bExec",\r
+                       "start_form",\r
+                       "title_name",\r
+                       "detail",\r
+                       "genre",\r
+                       "enc_type",\r
+                       "broadcast",\r
+                       "channel_list",\r
+                       "rec_priority",\r
+                       "maiyoubi_type",\r
+                       "date",\r
+                       "start_hour",\r
+                       "start_minute",\r
+                       "end_hour",\r
+                       "end_minute",\r
+                       "disc",\r
+                       "folder",\r
+                       "vrate",\r
+                       "amode",\r
+                       "videotype_digital",    // new\r
+                       "videomode_digital",    //new\r
+                       "audiomode_digital",\r
+                       "auto_delete",\r
+                       "dvdr",\r
+                       "lVoice",\r
+                       "edge_left",\r
+                       "bAutoChapter",\r
+                       "MagicChapter",\r
+                       "CM_Chapter",\r
+                       "channel_no",\r
+                       "dtv_sid",\r
+                       "dtv_nid",\r
+                       "net_link",\r
+                       "add_ch_text",\r
+                       "add_ch_value",\r
+                       "sport_ext_submit",\r
+                       "title_link_submit",\r
+                       "end_form"\r
+               };\r
+               \r
+               String pstr = "";\r
+               for ( String key : pkeys ) {\r
+                       if (pdat.containsKey(key)) {\r
+                               pstr += key+"="+pdat.get(key)+"&";\r
+                       }\r
+               }\r
+               pstr = pstr.substring(0, pstr.length()-1);\r
+               \r
+               System.err.println("poststr: "+pstr);\r
+               \r
+               return(pstr);\r
+       }\r
+       \r
+       private Hashtable<String, String> modPostdata(ReserveList r) {\r
+\r
+               Hashtable<String, String> newdat = new Hashtable<String, String>();\r
+               try {\r
+                       // 実行するよ\r
+                       newdat.put("bExec", (r.getExec())?("ON"):("OFF"));\r
+                       if (r.getUpdateOnlyExec()) {\r
+                               return(newdat);\r
+                       }\r
+       \r
+                       // 予約名・予約詳細\r
+                       if ( r.getAutocomplete() ) {\r
+                               newdat.put("title_name", "");\r
+                               newdat.put("detail", "");\r
+                       }\r
+                       else {\r
+                               try {\r
+                                       newdat.put("title_name", URLEncoder.encode(CommonUtils.substringrb(r.getTitle(),86),thisEncoding));\r
+                               } catch (UnsupportedEncodingException e1) {\r
+                                       // TODO Auto-generated catch block\r
+                                       e1.printStackTrace();\r
+                               }\r
+               \r
+                               // 予約詳細\r
+                               try {\r
+                                       newdat.put("detail", URLEncoder.encode(CommonUtils.substringrb(r.getDetail().replaceAll("\n", Matcher.quoteReplacement("\r\n")),75*5), thisEncoding));\r
+                               } catch (UnsupportedEncodingException e) {\r
+                                       // TODO Auto-generated catch block\r
+                                       e.printStackTrace();\r
+                               }\r
+                       }\r
+                       \r
+                       // 保存先\r
+                       try {\r
+                               newdat.put("folder", URLEncoder.encode(text2value(folder, r.getRec_folder()),thisEncoding));\r
+                       } catch (UnsupportedEncodingException e) {\r
+                               // TODO Auto-generated catch block\r
+                               e.printStackTrace();\r
+                       }\r
+                       \r
+                       // ジャンル\r
+                       String gstr = text2value(genre,r.getRec_genre());\r
+                       if (gstr == null || gstr.length() == 0) {\r
+                               gstr = text2value(genre,TVProgram.ProgGenre.NOGENRE.toString());\r
+                       }\r
+                       newdat.put("genre", gstr);\r
+                       \r
+                       // DVD互換モード\r
+                       newdat.put("dvdr", text2value(dvdcompat, r.getRec_dvdcompat()));\r
+                       \r
+                       // 録画チャンネル - uva、bsaは廃止 -\r
+                       String channel = cc.getCH_WEB2CODE(r.getCh_name());\r
+                       String ch_no = cc.getCH_CODE2REC(channel);\r
+                       String typ = text2value(chtype, channel);\r
+                       if (typ.equals("l1")) {\r
+                               newdat.put("broadcast","2");            // 外部入力(L1)\r
+                       }\r
+                       else if (typ.equals("l2")) {\r
+                               newdat.put("broadcast","3");            // 外部入力(L2)\r
+                       }\r
+                       else if (typ.equals("l3")) {\r
+                               newdat.put("broadcast","4");            // 外部入力(L3)\r
+                       }\r
+                       else if (typ.equals("bsd")) {\r
+                               newdat.put("broadcast","10");           // BSデジタル\r
+                       }\r
+                       else if (typ.equals("csd")) {\r
+                               newdat.put("broadcast","11");           // 110度CSデジタル\r
+                       }\r
+                       else if (typ.equals("uvd")) {\r
+                               newdat.put("broadcast","12");           // 地上デジタル\r
+                       }\r
+                       else {\r
+                               // 普通ここには落ちない\r
+                               if (ch_no.startsWith("C")) {\r
+                                       newdat.put("broadcast","2");            // "C***"は外部入力(L1)\r
+                               }\r
+                               else if (ch_no.startsWith("SP")) {\r
+                                       newdat.put("broadcast","4");            // "SP***"は外部入力(L3)\r
+                               }\r
+                               else {\r
+                                       newdat.put("broadcast","3");            // 未定義は全部外部入力(L2)\r
+                               }\r
+                       }\r
+                       \r
+                       newdat.put("channel_list", channel);\r
+                       newdat.put("channel_no", channel);\r
+                       \r
+                       // 録画レート\r
+                       newdat.put("enc_type", text2valueEncoderSP(encoder, r.getTuner(), r.getRec_mode()));\r
+                       \r
+                       Matcher ma = Pattern.compile("^(\\d+?):(.*?)$").matcher(text2value(vrate, r.getRec_mode()));\r
+                       if (ma.find()) {\r
+                               if (ma.group(1).equals("1")) {\r
+                                       // VR\r
+                                       newdat.put("vrate","1");        \r
+                                       newdat.put("amode","1");\r
+                                       newdat.put("videotype_digital",ma.group(1));    \r
+                                       newdat.put("videomode_digital",ma.group(2));\r
+                                       newdat.put("audiomode_digital",text2value(arate, r.getRec_audio()));\r
+                               }\r
+                               else if (ma.group(1).equals("2")) {\r
+                                       // TSE\r
+                                       newdat.put("vrate","1");        \r
+                                       newdat.put("amode","1");        \r
+                                       newdat.put("videotype_digital",ma.group(1));    \r
+                                       newdat.put("videomode_digital",ma.group(2));\r
+                               }\r
+                               else {\r
+                                       // TS\r
+                                       newdat.put("vrate","1");        \r
+                                       newdat.put("amode","1");        \r
+                                       newdat.put("videotype_digital",ma.group(1));    \r
+                                       //newdat.put("videomode_digital",ma.group(2));\r
+                               }\r
+                               \r
+                       }\r
+       \r
+                       // 繰り返し\r
+                       ma = Pattern.compile("^\\d").matcher(r.getRec_pattern());\r
+                       if (ma.find()) { \r
+                               newdat.put("maiyoubi_type","0");\r
+                               try {\r
+                                       newdat.put("date", URLEncoder.encode(r.getRec_pattern(),thisEncoding));\r
+                               } catch (UnsupportedEncodingException e1) {\r
+                                       // TODO Auto-generated catch block\r
+                                       e1.printStackTrace();\r
+                               }\r
+                       }\r
+                       else {\r
+                               newdat.put("maiyoubi_type", "1");\r
+                               int i = 1;\r
+                               for ( String s : RPTPTN ) {\r
+                                       if ( s.equals(r.getRec_pattern()) == true ) {\r
+                                               newdat.put("date", String.valueOf(i));\r
+                                       }\r
+                                       i++;\r
+                               }\r
+                       }\r
+\r
+                       // 開始・終了時刻\r
+                       newdat.put("start_hour", r.getAhh());\r
+                       newdat.put("start_minute", r.getAmm());\r
+                       newdat.put("end_hour", r.getZhh());\r
+                       newdat.put("end_minute", r.getZmm());\r
+       \r
+                       // 自動チャプター関連\r
+                       newdat.put("bAutoChapter"               , text2value(xchapter, r.getRec_xchapter()));\r
+                       newdat.put("MagicChapter"               , text2value(mschapter, r.getRec_mschapter()));\r
+                       newdat.put("CM_Chapter"                 , text2value(mvchapter, r.getRec_mvchapter()));\r
+                       \r
+                       // 記録先\r
+                       newdat.put("disc", text2value(device, r.getRec_device()));\r
+                       \r
+                       String val;\r
+                       \r
+                       val = text2value(aspect, r.getRec_aspect());\r
+                       newdat.put("edge_left", (val!=null)?(val):("0"));\r
+                       \r
+                       val = text2value(lvoice, r.getRec_lvoice());\r
+                       newdat.put("lVoice", (val!=null)?(val):("1"));\r
+                       \r
+                       val = text2value(bvperf, r.getRec_bvperf());\r
+                       newdat.put("rec_priority", (val!=null)?(val):("150"));\r
+                       \r
+                       val = text2value(autodel, r.getRec_autodel());\r
+                       newdat.put("auto_delete", (val!=null)?(val):("0"));\r
+                       \r
+                       // 追加\r
+                       newdat.put("start_form"                 , "");\r
+                       //newdat.put("rec_priority"             , "150");\r
+                       newdat.put("title_link"                 , "0");\r
+                       //newdat.put("auto_delete"              , "0");\r
+                       newdat.put("video_es"                   , "00");\r
+                       newdat.put("audio_es"                   , "10");\r
+                       //newdat.put("lVoice"           , "1");\r
+                       //newdat.put("edge_left"                        , "0");         // 録画のりしろ\r
+                       //newdat.put("MagicChapter"             , "0");\r
+                       //newdat.put("MagicChapter"             , "0");\r
+                       //newdat.put("CM_Chapter"                       , "0");\r
+                       newdat.put("add_ch_text"                , "");\r
+                       newdat.put("add_ch_value"               , "");\r
+                       //newdat.put("sport_ext_submit" , "undefined");         // 本体からの予約の状態を保持する(>>208.)\r
+                       //newdat.put("title_link_submit"        , "undefined"); // 本体からの予約の状態を保持する(>>208.)\r
+                       newdat.put("end_form"                   , "0");\r
+               }\r
+               catch ( Exception e ) {\r
+                       e.printStackTrace();\r
+               }\r
+               \r
+               return(newdat);\r
+       }\r
+       \r
+       \r
+       \r
+       /*\r
+        * 録画設定の解読\r
+        */\r
+       private void setSettingVrate(ArrayList<TextValueSet> tvs)\r
+       {\r
+               tvs.clear();\r
+               TextValueSet t = null;\r
+               \r
+               t = new TextValueSet();\r
+               t.setText("[DR]");\r
+               t.setValue("128:6");\r
+               tvs.add(t);\r
+               \r
+               t = new TextValueSet();\r
+               t.setText("[AVC] AF 12.0");\r
+               t.setValue("2:8");\r
+               tvs.add(t);\r
+               t = new TextValueSet();\r
+               t.setText("[AVC] AN 8.0");\r
+               t.setValue("2:9");\r
+               tvs.add(t);\r
+               t = new TextValueSet();\r
+               t.setText("[AVC] AE 2.0");\r
+               t.setValue("2:10");\r
+               tvs.add(t);\r
+               \r
+               t = new TextValueSet();\r
+               t.setText("[AVC] AT 4.7GB");\r
+               t.setValue("2:4");\r
+               tvs.add(t);\r
+               t = new TextValueSet();\r
+               t.setText("[AVC] AT 8.5GB");\r
+               t.setValue("2:7");\r
+               tvs.add(t);\r
+               t = new TextValueSet();\r
+               t.setText("[AVC] AT 9.4GB");\r
+               t.setValue("2:5");\r
+               tvs.add(t);\r
+               t = new TextValueSet();\r
+               t.setText("[AVC] AT 25GB");\r
+               t.setValue("2:11");\r
+               tvs.add(t);\r
+               t = new TextValueSet();\r
+               t.setText("[AVC] AT 50GB");\r
+               t.setValue("2:12");\r
+               tvs.add(t);\r
+               \r
+               for (int br=1400; br<=17000; ) {\r
+                       t = new TextValueSet();\r
+                       t.setText(String.format("[AVC] %d.%d", (br-br%1000)/1000, (br%1000)/100));\r
+                       t.setValue("2:"+String.valueOf(br));\r
+                       tvs.add(t);\r
+                       if (br < 10000) {\r
+                               br += 200;\r
+                       }\r
+                       else {\r
+                               br += 500;\r
+                       }\r
+               }\r
+               t = new TextValueSet();\r
+               t.setText("[AVC] 高レート節約");\r
+               t.setValue("2:16");\r
+               tvs.add(t);\r
+               \r
+               t = new TextValueSet();\r
+               t.setText("[VR] SP4.4/4.6");\r
+               t.setValue("1:1");\r
+               tvs.add(t);\r
+               t = new TextValueSet();\r
+               t.setText("[VR] LP2.0/2.2");\r
+               t.setValue("1:2");\r
+               tvs.add(t);\r
+               \r
+               t = new TextValueSet();\r
+               t.setText("[VR] AT 4.7GB");\r
+               t.setValue("1:4");\r
+               tvs.add(t);\r
+               t = new TextValueSet();\r
+               t.setText("[VR] AT 8.5GB");\r
+               t.setValue("1:7");\r
+               tvs.add(t);\r
+               t = new TextValueSet();\r
+               t.setText("[VR] AT 9.4GB");\r
+               t.setValue("1:5");\r
+               tvs.add(t);\r
+               \r
+               t = new TextValueSet();\r
+               t.setText("[VR] 1.0");\r
+               t.setValue("1:1000");\r
+               tvs.add(t);\r
+               t = new TextValueSet();\r
+               t.setText("[VR] 1.4");\r
+               t.setValue("1:1400");\r
+               tvs.add(t);\r
+               for (int br=2000; br<=9200; br+=200) {\r
+                       t = new TextValueSet();\r
+                       t.setText(String.format("[VR] %d.%d", (br-br%1000)/1000, (br%1000)/100));\r
+                       t.setValue("1:"+String.valueOf(br));\r
+                       tvs.add(t);\r
+               }\r
+               t = new TextValueSet();\r
+               t.setText("[VR] 高レート節約");\r
+               t.setValue("1:13");\r
+               tvs.add(t);\r
+       }\r
+       \r
+       private void setSettingFolder(ArrayList<TextValueSet> folder, String res) {\r
+               folder.clear();\r
+               LinkedHashMap<String,String> folderKey = new LinkedHashMap<String, String>();\r
+               folderKey.put("hdd",ITEM_MEDIA_HDD);\r
+               folderKey.put("dvd",ITEM_MEDIA_USB);\r
+               for (String typ : folderKey.keySet()) {\r
+                       String txtkey = typ+"_folder_text";\r
+                       String valkey = typ+"_folder_value";\r
+                       Matcher mc = Pattern.compile("var "+txtkey+"\\s*= new Array\\(([\\s\\S]+?)\\);").matcher(res);\r
+                       Matcher md = Pattern.compile("var "+valkey+"\\s*= new Array\\(([\\s\\S]+?)\\);").matcher(res);\r
+                       if ( mc.find() && md.find() ) {\r
+                               Matcher me = Pattern.compile("\"(.+?)\",?").matcher(mc.group(1));\r
+                               Matcher mf = Pattern.compile("\"(.+?)\",?").matcher(md.group(1));\r
+                               while (me.find() && mf.find()) {\r
+                                       TextValueSet t = new TextValueSet();\r
+                                       t.setText(folderKey.get(typ)+me.group(1));\r
+                                       t.setValue(mf.group(1));\r
+                                       folder.add(t);\r
+                               }\r
+                       }\r
+               }\r
+       }\r
+       \r
+       protected void setSettingEncoder(ArrayList<TextValueSet> tvs, String res)\r
+       {\r
+               tvs.clear();\r
+               \r
+               Matcher mb = Pattern.compile("var double_encode_flg = (\\d+?);").matcher(res);\r
+               while (mb.find()) {\r
+                       Matcher mc = Pattern.compile("\\n\\s*?switch \\( double_encode_flg \\) \\{([\\s\\S]+?default:)").matcher(res);\r
+                       if (mc.find()) {\r
+                               Matcher md = Pattern.compile("(case "+mb.group(1)+":[\\s\\S]+?break;)").matcher(mc.group(1));\r
+                               if (md.find()) {\r
+                                       {\r
+                                               Matcher me = Pattern.compile("name=enc_type value=.\"(\\d+?).\"[\\s\\S]+?toru_(.+?)\\.gif").matcher(md.group(1));\r
+                                               while (me.find()) {\r
+                                                       TextValueSet t = new TextValueSet();\r
+                                                       t.setText(me.group(2).replaceFirst("^TS","DR"));\r
+                                                       t.setValue(me.group(1));\r
+                                                       tvs.add(t);\r
+                                               }\r
+                                               if ( tvs.size() > 0 ) {\r
+                                                       System.out.println(DBGID+" エンコーダ情報から2012/10/18以前のファームと判断しました");\r
+                                                       return;\r
+                                               }\r
+                                       }\r
+                                       \r
+                                       // 2012/10/18ファームのBZ700対応\r
+                                       {\r
+                                               Matcher me = Pattern.compile("name=enc_type value=.\"(\\d+?).\"[\\s\\S]+?act_(.+?)\\.gif").matcher(md.group(1));\r
+                                               while (me.find()) {\r
+                                                       TextValueSet t = new TextValueSet();\r
+                                                       t.setText(me.group(2));\r
+                                                       t.setValue(me.group(1));\r
+                                                       tvs.add(t);\r
+                                               }\r
+                                               if ( tvs.size() > 0 ) {\r
+                                                       System.out.println(DBGID+" エンコーダ情報から2012/10/18以降のファームと判断しました");\r
+                                                       return;\r
+                                               }\r
+                                       }\r
+                                       \r
+                                       System.out.println(ERRID+" エンコーダ情報を解析できませんでした。未対応のファームと思われます。ダンプとログを添付して開発元にお問い合わせください。");\r
+                               }\r
+                       }\r
+               }\r
+       }\r
+       \r
+       /**\r
+        * エンコーダ情報がHTMLからとれなけば固定値を使う\r
+        * @see #setSettingEncoder(ArrayList, String)\r
+        */\r
+       protected void setSettingEncoder(ArrayList<TextValueSet> tvs)\r
+       {\r
+               tvs.clear();\r
+               TextValueSet t = null;\r
+               \r
+               t = new TextValueSet();\r
+               t.setText(ITEM_ENCODER_R1);\r
+               t.setValue(VALUE_ENCODER_R1);\r
+               tvs.add(t);\r
+               t = new TextValueSet();\r
+               t.setText(ITEM_ENCODER_R2);\r
+               t.setValue(VALUE_ENCODER_R2);\r
+               tvs.add(t);\r
+       }\r
+       \r
+       private void setSettingChCodeValue(ArrayList<TextValueSet> tvsvalue, ArrayList<TextValueSet> tvstype, String res) {\r
+               tvsvalue.clear();\r
+               tvstype.clear();\r
+               for ( String typ : new String[] { "uvd","bsd","csd","l1","l2","l3" } ) {\r
+                       String txtkey = typ+"_ch_text";\r
+                       String valkey = typ+"_ch_value";\r
+                       Matcher mc = Pattern.compile("var "+txtkey+"\\s*= new Array\\(([\\s\\S]+?)\\);").matcher(res);\r
+                       Matcher md = Pattern.compile("var "+valkey+"\\s*= new Array\\(([\\s\\S]+?)\\);").matcher(res);\r
+                       if ( mc.find() && md.find() ) {\r
+                               Matcher me = Pattern.compile("\"(.+?)\",?").matcher(mc.group(1));\r
+                               Matcher mf = Pattern.compile("\"([^\"]+?)\",").matcher(md.group(1));\r
+                               while ( me.find() && mf.find() ) {\r
+                                       TextValueSet t = new TextValueSet();\r
+                                       t.setText(me.group(1));\r
+                                       t.setValue(mf.group(1));\r
+                                       tvsvalue.add(t);\r
+                                       \r
+                                       TextValueSet x = new TextValueSet();\r
+                                       x.setText(mf.group(1));\r
+                                       x.setValue(typ);\r
+                                       tvstype.add(x);\r
+                               }\r
+                       }\r
+               }\r
+       }\r
+       \r
+       private void setSettingGenre(ArrayList<TextValueSet> tvs)\r
+       {\r
+               tvs.clear();\r
+               TextValueSet t = null;\r
+               \r
+               t = new TextValueSet();\r
+               t.setText(TVProgram.ProgGenre.MOVIE.toString());\r
+               t.setValue("0");\r
+               tvs.add(t);\r
+               t = new TextValueSet();\r
+               t.setText(TVProgram.ProgGenre.MUSIC.toString());\r
+               t.setValue("1");\r
+               tvs.add(t);\r
+               t = new TextValueSet();\r
+               t.setText(TVProgram.ProgGenre.DORAMA.toString());\r
+               t.setValue("2");\r
+               tvs.add(t);\r
+               t = new TextValueSet();\r
+               t.setText(TVProgram.ProgGenre.ANIME.toString());\r
+               t.setValue("3");\r
+               tvs.add(t);\r
+               t = new TextValueSet();\r
+               t.setText(TVProgram.ProgGenre.SPORTS.toString());\r
+               t.setValue("4");\r
+               tvs.add(t);\r
+               t = new TextValueSet();\r
+               t.setText(TVProgram.ProgGenre.DOCUMENTARY.toString());\r
+               t.setValue("5");\r
+               tvs.add(t);\r
+               t = new TextValueSet();\r
+               t.setText(TVProgram.ProgGenre.THEATER.toString());\r
+               t.setValue("6");\r
+               tvs.add(t);\r
+               t = new TextValueSet();\r
+               t.setText(TVProgram.ProgGenre.HOBBY.toString());\r
+               t.setValue("7");\r
+               tvs.add(t);\r
+               t = new TextValueSet();\r
+               t.setText(TVProgram.ProgGenre.VARIETY.toString());\r
+               t.setValue("8");\r
+               tvs.add(t);\r
+               t = new TextValueSet();\r
+               t.setText(TVProgram.ProgGenre.NOGENRE.toString());\r
+               t.setValue("10");\r
+               tvs.add(t);\r
+       }\r
+       \r
+       private void setSettingEtc(ArrayList<TextValueSet> tvs, String key, int typ, String res) {\r
+               tvs.clear();\r
+               String valExpr = "(\\d+),?";\r
+               if (typ == 1) {\r
+                       valExpr = "\"(.+?)\",?";\r
+               }\r
+               Matcher mc = Pattern.compile("var "+key+"_text\\s*= new Array\\(([\\s\\S]+?)\\);").matcher(res);\r
+               Matcher md = Pattern.compile("var "+key+"_value\\s*= new Array\\(([\\s\\S]+?)\\);").matcher(res);\r
+               if (mc.find() && md.find()) {\r
+                       Matcher me = Pattern.compile("\"(.+?)\",?").matcher(mc.group(1));\r
+                       Matcher mf = Pattern.compile(valExpr).matcher(md.group(1));\r
+                       while (me.find() && mf.find()) {\r
+                               TextValueSet t = new TextValueSet();\r
+                               t.setText(me.group(1));\r
+                               t.setValue(mf.group(1));\r
+                               tvs.add(t);\r
+                       }\r
+               }\r
+       }\r
+       \r
+       //\r
+       protected void translateAttributeTuner(ReserveList entry) {\r
+               if (entry.getTuner().startsWith("TS")) {\r
+                       entry.setTuner(entry.getTuner().replaceFirst("^TS", "DR"));\r
+               }\r
+       }\r
+       \r
+       //\r
+       protected String text2valueEncoderSP(ArrayList<TextValueSet> tvs, String text, String vrate) {\r
+               return text2value(tvs, text);\r
+       }\r
+}\r
diff --git a/TinyBannavi/src/tainavi/PlugIn_RecRD_BZ810.java b/TinyBannavi/src/tainavi/PlugIn_RecRD_BZ810.java
new file mode 100644 (file)
index 0000000..f3205bf
--- /dev/null
@@ -0,0 +1,64 @@
+package tainavi;\r
+\r
+import java.util.ArrayList;\r
+import java.util.regex.Matcher;\r
+import java.util.regex.Pattern;\r
+\r
+public class PlugIn_RecRD_BZ810 extends PlugIn_RecRD_BZ700 implements HDDRecorder,Cloneable {\r
+\r
+       public PlugIn_RecRD_BZ810 clone() {\r
+               return (PlugIn_RecRD_BZ810) super.clone();\r
+       }\r
+\r
+       // 種族の特性\r
+       @Override\r
+       public String getRecorderId() { return "REGZA RD-BZ810"; }\r
+       \r
+       @Override\r
+       protected void setSettingEncoder(ArrayList<TextValueSet> encoder, String res)\r
+       {\r
+               System.out.println("BZ810's setSettingEncoder().");\r
+               \r
+               encoder.clear();\r
+               \r
+               Matcher mb = Pattern.compile("var double_encode_flg = (\\d+?);").matcher(res);\r
+               while (mb.find()) {\r
+                       Matcher mc = Pattern.compile("\\n\\s*?switch \\( double_encode_flg \\) \\{([\\s\\S]+?default:)").matcher(res);\r
+                       if (mc.find()) {\r
+                               Matcher md = Pattern.compile("(case "+mb.group(1)+":[\\s\\S]+?break;)").matcher(mc.group(1));\r
+                               if (md.find()) {\r
+                                       Matcher me = Pattern.compile("name=enc_type value=.\"(\\d+?).\"[\\s\\S]+?toru_(.+?)\\.gif").matcher(md.group(1));\r
+                                       while (me.find()) {\r
+                                               if ( ! me.group(2).equals("RE")) {\r
+                                                       TextValueSet t = new TextValueSet();\r
+                                                       t.setText(me.group(2).replaceFirst("^TS","R"));\r
+                                                       t.setValue(me.group(1));\r
+                                                       encoder.add(t);\r
+                                               }\r
+                                       }\r
+                               }\r
+                       }\r
+               }\r
+       }\r
+       \r
+       //\r
+       @Override\r
+       protected void translateAttributeTuner(ReserveList entry) {\r
+               if (entry.getTuner().startsWith("TS")) {\r
+                       entry.setTuner(entry.getTuner().replaceFirst("^TS", "R"));\r
+               }\r
+               else if (entry.getTuner().startsWith("RE")) {\r
+                       entry.setTuner(ITEM_ENCODER_R1);\r
+               }\r
+       }\r
+       \r
+       //\r
+       @Override\r
+       protected String text2valueEncoderSP(ArrayList<TextValueSet> tvs, String text, String vrate) {\r
+               if (ITEM_ENCODER_R2.equals(text) && ITEM_VIDEO_TYPE_VR.equals(vrate)) {\r
+                       //return VALUE_ENCODER_RE_2;    // RE? 指定できない\r
+                       setErrmsg("【警告】BZ810プラグインでは、R2選択時は画質に[VR]を指定できません。");\r
+               }\r
+               return text2value(tvs, text);\r
+       }\r
+}\r
diff --git a/TinyBannavi/src/tainavi/PlugIn_RecRD_BZ810_TSync.java b/TinyBannavi/src/tainavi/PlugIn_RecRD_BZ810_TSync.java
new file mode 100644 (file)
index 0000000..b0abf0f
--- /dev/null
@@ -0,0 +1,546 @@
+package tainavi;\r
+\r
+import java.io.BufferedReader;\r
+import java.io.File;\r
+import java.io.FileNotFoundException;\r
+import java.io.FileReader;\r
+import java.io.IOException;\r
+import java.io.UnsupportedEncodingException;\r
+import java.net.URLEncoder;\r
+import java.util.ArrayList;\r
+import java.util.regex.Matcher;\r
+import java.util.regex.Pattern;\r
+\r
+\r
+public class PlugIn_RecRD_BZ810_TSync extends HDDRecorderUtils implements HDDRecorder,Cloneable {\r
+       \r
+       public PlugIn_RecRD_BZ810_TSync clone() {\r
+               return (PlugIn_RecRD_BZ810_TSync) super.clone();\r
+       }\r
+       \r
+       //private static final String thisEncoding = "MS932";\r
+       \r
+       /* 必須コード  - ここから */\r
+       \r
+       // 種族の特性\r
+       public String getRecorderId() { return "REGZA RD-BZ810(TaiSync)"; }\r
+       public RecType getType() { return RecType.RECORDER; }\r
+       \r
+       // 個体の特性\r
+       private ChannelCode cc = new ChannelCode(getRecorderId());\r
+       private String rsvedFile = "";\r
+       \r
+       private String errmsg = "";\r
+       \r
+       protected String getTSyncVersion() { return "bz810"; }\r
+       \r
+       // 公開メソッド\r
+       \r
+       /*\r
+        * \r
+        */\r
+       public ChannelCode getChCode() {\r
+               return cc;\r
+       }\r
+       \r
+       /*\r
+        * \r
+        */\r
+       public boolean ChangeChannel(String Channel) {\r
+               \r
+               if (Channel == null) {\r
+                       return true;\r
+               }\r
+               \r
+               String ch = null;\r
+               int curBC;\r
+               int newBC;\r
+               String chNo;\r
+               \r
+               errmsg = "";\r
+               \r
+               /*\r
+                * 変更前の放送(地上波/BS/CS)\r
+                */\r
+               \r
+               for (int i=0; i<3; i++) {\r
+                       String[] d = reqGET("http://"+getIPAddr()+":"+getPortNo()+"/getcurchannel.htm", null);\r
+                       if ((ch = d[1]) != null) {\r
+                               break;\r
+                       }\r
+                       CommonUtils.milSleep(500);\r
+               }\r
+               if (ch == null || ch.startsWith("null")) {\r
+                       errmsg = "レコーダへのアクセスに失敗しました(チャンネルリモコン)。";\r
+                       System.err.println(errmsg);\r
+                       return false;\r
+               }\r
+               \r
+               //byte[] ba = ch.getBytes();\r
+               //byte enc = ba[0];\r
+               ch = ch.substring(1,5);\r
+               \r
+               if (ch.matches("^\\d.+")) {\r
+                       curBC = 0;\r
+               }\r
+               else if (ch.startsWith("BS")) {\r
+                       curBC = 1;\r
+               }\r
+               else if (ch.startsWith("CS")) {\r
+                       curBC = 2;\r
+               }\r
+               else {\r
+                       errmsg = "現在のチャンネルが認識できません("+ch+")。";\r
+                       System.err.println(errmsg);\r
+                       return false;\r
+               }\r
+               \r
+               /*\r
+                * 変更後のCH\r
+                */\r
+               \r
+               // 放送(地上波/BS/CS)\r
+               ch = cc.getCH_WEB2REC(Channel);\r
+               if (ch.startsWith("SP")) {\r
+                       errmsg = "外部入力にアサインされているチャンネルには変更できません("+Channel+", "+ch+")。";\r
+                       System.err.println(errmsg);\r
+                       return false;\r
+               }\r
+               else if (ch.matches("^\\d.+")) {\r
+                       newBC = 0;\r
+               }\r
+               else if (ch.startsWith("BS")) {\r
+                       newBC = 1;\r
+               }\r
+               else if (ch.startsWith("CS")) {\r
+                       newBC = 2;\r
+               }\r
+               else {\r
+                       errmsg = "放送種別が識別できません。プログラム異常かも?("+Channel+", "+ch+")。";\r
+                       System.err.println(errmsg);\r
+                       return false;\r
+               }\r
+               \r
+               // CH番号\r
+               Matcher ma = Pattern.compile("(\\d\\d\\d)").matcher(ch);\r
+               if (ma.find()) {\r
+                       chNo = ma.group(1);\r
+               }\r
+               else {\r
+                       errmsg = "CH番号が取得できません("+Channel+", "+ch+")。";\r
+                       System.err.println(errmsg);\r
+                       return false;\r
+               }\r
+               \r
+               /*\r
+                * CH変更実行\r
+                */\r
+               \r
+               int cBC;\r
+               for (int i=0; i<3 && newBC != (cBC = (curBC+i)%3); i++) {\r
+                       // 切り替え実行\r
+                       reqGET("http://"+getIPAddr()+":"+getPortNo()+"/remote/remote.htm?key=21", null);\r
+                       \r
+                       // 地上波→BS・BS→CSは切り替え完了までに時間がかかる\r
+                       CommonUtils.milSleep((cBC == 0 || cBC == 1)?(3000):(1000));\r
+               }\r
+               \r
+               reqGET("http://"+getIPAddr()+":"+getPortNo()+"/remote/remote.htm?key=25", null);\r
+               CommonUtils.milSleep(1000);\r
+               for (int i=0; i<3; i++) {\r
+                       reqGET("http://"+getIPAddr()+":"+getPortNo()+"/remote/remote.htm?key=0"+chNo.substring(i,i+1), null);\r
+                       CommonUtils.milSleep(200);\r
+               }\r
+               reqGET("http://"+getIPAddr()+":"+getPortNo()+"/remote/remote.htm?key=44", null);\r
+               \r
+               return true;\r
+       }\r
+       \r
+       /*\r
+        *      レコーダーから予約一覧を取得する \r
+        */\r
+       public boolean GetRdReserve(boolean force) {\r
+               \r
+               errmsg = "";\r
+               \r
+               System.out.println("レコーダから予約一覧を取得します("+force+"): "+getRecorderId()+"("+getIPAddr()+":"+getPortNo()+")");\r
+               \r
+               // 録画設定\r
+               if (force == true || encoder.size() == 0) {\r
+                       FileReader fr = null;\r
+                       BufferedReader r = null;\r
+                       try {\r
+                               encoder.clear();\r
+                               vrate.clear();\r
+                               arate.clear();\r
+                               device.clear();\r
+                               dvdcompat.clear();\r
+                               xchapter.clear();\r
+                               mschapter.clear();\r
+                               mvchapter.clear();\r
+                               autodel.clear();\r
+                               aspect.clear();\r
+                               lvoice.clear();\r
+                               \r
+                               String defFile = "env/mail_"+getTSyncVersion()+".def" ;\r
+                               fr = new FileReader(defFile);\r
+                               r = new BufferedReader(fr);\r
+                               String s ;\r
+                               while ( (s = r.readLine()) != null ) {\r
+                                       String[] b = s.split(",");\r
+                                       if ( b.length >= 3 ) {\r
+                                               TextValueSet t = new TextValueSet() ;\r
+                                               t.setText(b[1]) ;\r
+                                               t.setValue(b[2]) ;\r
+                                               \r
+                                               if ( b[0].equals("7") ) {\r
+                                                       if ( ! t.getText().startsWith("RE")) {\r
+                                                               encoder.add(t) ;\r
+                                                       }\r
+                                               }\r
+                                               else if ( b[0].equals("10") ) {\r
+                                                               vrate.add(t) ;\r
+                                               }\r
+                                               else if ( b[0].equals("11") ) {\r
+                                                       arate.add(t) ;\r
+                                               }\r
+                                               else if ( b[0].equals("12") ) {\r
+                                                       device.add(t) ;\r
+                                               }\r
+                                               else if ( b[0].equals("14") ) {\r
+                                                       dvdcompat.add(t) ;\r
+                                               }\r
+                                               else if ( b[0].equals("17") ) {\r
+                                                       xchapter.add(t) ;\r
+                                               }\r
+                                               else if ( b[0].equals("18") ) {\r
+                                                       mschapter.add(t) ;\r
+                                               }\r
+                                               else if ( b[0].equals("19") ) {\r
+                                                       mvchapter.add(t) ;\r
+                                               }\r
+                                               else if ( b[0].equals("13") ) {\r
+                                                       autodel.add(t) ;\r
+                                               }\r
+                                               else if ( b[0].equals("15") ) {\r
+                                                       lvoice.add(t) ;\r
+                                               }\r
+                                               else if ( b[0].equals("16") ) {\r
+                                                       aspect.add(t) ;\r
+                                               }\r
+                                       }\r
+                               }\r
+                       } catch (FileNotFoundException e) {\r
+                               e.printStackTrace();\r
+                       } catch (IOException e) {\r
+                               e.printStackTrace();\r
+                       }\r
+                       finally {\r
+                               if (r != null) try { r.close(); } catch (Exception e) {};\r
+                               if (fr != null) try { fr.close(); } catch (Exception e) {};\r
+                       }\r
+               }\r
+               \r
+               \r
+               // 予約一覧をキャッシュから\r
+               rsvedFile = "env/reserved."+getIPAddr()+"_"+getPortNo()+"_"+getRecorderId()+".xml";\r
+               \r
+               File f = new File(rsvedFile);\r
+               if ( force == false && f.exists()) {\r
+                       // キャッシュから読み出し(予約一覧)\r
+                       setReserves(ReservesFromFile(rsvedFile));\r
+                       return(true);\r
+               }\r
+               \r
+               // 予約一覧をレコーダーから取得する\r
+               reportProgress("get reserved list(1/1).");\r
+               String[] d = reqGET("http://"+getIPAddr()+":"+getPortNo()+"/reserve.htm",null);\r
+               String header = d[0];\r
+               String response = d[1];\r
+               if (response == null) {\r
+                       errmsg = "レコーダーが反応しません";\r
+                       return(false);\r
+               }\r
+               \r
+               // 取得したデータを内部形式に変換する\r
+               ArrayList<ReserveList> ra = decodeReservedList(response); \r
+               for (ReserveList entry : ra) {\r
+                       entry.setCh_name(getChCode().getCH_REC2WEB(entry.getChannel()));\r
+                       \r
+                       // TS/TSE->DR/AVC\r
+                       if (entry.getRec_mode().startsWith("[TS]")) {\r
+                               entry.setRec_mode(entry.getRec_mode().replaceFirst("\\[TS\\]", "[DR]"));\r
+                       }\r
+                       else if (entry.getRec_mode().startsWith("[TSE]")) {\r
+                               entry.setRec_mode(entry.getRec_mode().replaceFirst("\\[TSE\\]", "[AVC]"));\r
+                       }\r
+                       else if (entry.getRec_mode().matches("^\\s*$")) {\r
+                               entry.setRec_mode("");\r
+                       }\r
+                       \r
+                       // TSx/REx->Rx\r
+                       if (entry.getTuner().startsWith("TS")) {\r
+                               entry.setTuner(entry.getTuner().replaceFirst("^TS","R"));\r
+                       }\r
+                       else if (entry.getTuner().equals("RE")) {\r
+                               entry.setTuner("R1");\r
+                       }\r
+                       \r
+                       // タイトル自動補完フラグなど本体からは取得できない情報を引き継ぐ\r
+                       copyAttributes(entry, getReserves());\r
+               }\r
+               setReserves(ra);\r
+               \r
+               // キャッシュに保存\r
+               ReservesToFile(getReserves(), rsvedFile);\r
+               \r
+               // 取得した情報の表示\r
+               System.out.println("---Reserved List Start---");\r
+               for ( int i = 0; i<getReserves().size(); i++ ) {\r
+                       // 詳細情報の取得\r
+                       ReserveList e = getReserves().get(i);\r
+                       System.out.println(String.format("[%s] %s\t%s\t%s\t%s:%s\t%s:%s\t%sm\t%s\t%s(%s)\t%s\t%s",\r
+                                       (i+1), e.getId(), e.getRec_pattern(), e.getRec_nextdate(), e.getAhh(), e.getAmm(), e.getZhh(),  e.getZmm(),     e.getRec_min(), e.getRec_mode(), e.getTitle(), e.getTitlePop(), e.getChannel(), e.getCh_name()));\r
+               }\r
+               System.out.println("---Reserved List End---");\r
+               \r
+               return(true);\r
+       }\r
+       \r
+       /*\r
+        *      予約を実行する\r
+        */\r
+       public boolean PostRdEntry(ReserveList r) {\r
+               \r
+               errmsg = "";\r
+               \r
+               // 設定もれを撥ねる\r
+               if (cc.getCH_WEB2CODE(r.getCh_name()) == null) {\r
+                       errmsg = "【警告】Web番組表の放送局名「"+r.getCh_name()+"」をCHコードに変換できません。CHコード設定を修正してください。" ;\r
+                       System.out.println(errmsg);\r
+                       return(false);\r
+               }\r
+               \r
+               System.out.println("Run: PostRdEntry("+r.getTitle()+")");\r
+               \r
+               // ポストデータを作る\r
+               r.setId(null);\r
+               String pstr = getPoststr(r);\r
+               \r
+               // リクエストを送る\r
+               reportProgress("send request.(1/1)");\r
+               String[] d = reqPOST("http://"+getIPAddr()+":"+getPortNo()+"/entry.htm", pstr, null);\r
+               String header = d[0];\r
+               String response = d[1];\r
+               if ( response == null ) {\r
+                       errmsg = "レコーダーが反応しません。";\r
+                       return(false);\r
+               }\r
+               \r
+               /*\r
+                * 予約情報の調整 \r
+                */\r
+               \r
+               // 予約ID番号を取得\r
+               Matcher ma = Pattern.compile("^(\\d+)").matcher(response);\r
+               if (ma.find()) {\r
+                       r.setId(ma.group(1));\r
+               }\r
+               \r
+               // 音質(TS/TSEでは音質の設定はできない)\r
+               if (r.getRec_mode().startsWith("[AVC] ") || r.getRec_mode().equals("[DR]")) {\r
+                       r.setRec_audio("");\r
+               }\r
+               \r
+               // 予約パターンID\r
+               r.setRec_pattern_id(getRec_pattern_Id(r.getRec_pattern()));\r
+               \r
+               // 次回予定日\r
+               r.setRec_nextdate(CommonUtils.getNextDate(r));\r
+               \r
+               // 録画長\r
+               r.setRec_min(CommonUtils.getRecMin(r.getAhh(),r.getAmm(),r.getZhh(),r.getZmm()));\r
+               \r
+               // 開始日時・終了日時\r
+               getStartEndDateTime(r);\r
+               \r
+               // 予約リストを更新\r
+               getReserves().add(r);\r
+               \r
+               // キャッシュに保存\r
+               ReservesToFile(getReserves(), rsvedFile);\r
+               \r
+               System.out.printf("\n<<< Message from RD >>> \"%s\"\n\n", "正常に登録できました。");\r
+               return(true);\r
+       }\r
+       \r
+       /*\r
+        *      予約を更新する\r
+        */\r
+       public boolean UpdateRdEntry(ReserveList o, ReserveList r) {\r
+               \r
+               errmsg = "";\r
+               \r
+               // 設定もれを撥ねる\r
+               if (cc.getCH_WEB2CODE(r.getCh_name()) == null) {\r
+                       errmsg = "【警告】Web番組表の放送局名「"+r.getCh_name()+"」をCHコードに変換できません。CHコード設定を修正してください。" ;\r
+                       System.out.println(errmsg);\r
+                       return(false);\r
+               }\r
+               \r
+               System.out.println("Run: UpdateRdEntry()");\r
+               \r
+               // ポストデータを作る\r
+               String pstr = getPoststr(r);\r
+               \r
+               // リクエストを送る\r
+               reportProgress("send request.(1/1)");\r
+               String[] d = reqPOST("http://"+getIPAddr()+":"+getPortNo()+"/update.htm", pstr, null);\r
+               String header = d[0];\r
+               String response = d[1];\r
+               if ( response == null ) {\r
+                       errmsg = "レコーダーが反応しません。";\r
+                       return(false);\r
+               }\r
+               \r
+               /*\r
+                * 予約情報の調整 \r
+                */\r
+               \r
+               // 音質(TS/TSEでは音質の設定はできない)\r
+               if (r.getRec_mode().startsWith("[AVC] ") || r.getRec_mode().equals("[DR]")) {\r
+                       r.setRec_audio("");\r
+               }\r
+               \r
+               // 予約パターンID\r
+               r.setRec_pattern_id(getRec_pattern_Id(r.getRec_pattern()));\r
+               \r
+               // 次回予定日\r
+               r.setRec_nextdate(CommonUtils.getNextDate(r));\r
+               \r
+               // 録画長\r
+               r.setRec_min(CommonUtils.getRecMin(r.getAhh(),r.getAmm(),r.getZhh(),r.getZmm()));\r
+               \r
+               // 開始日時・終了日時\r
+               getStartEndDateTime(r);\r
+               \r
+               // 予約リストを更新\r
+               getReserves().remove(o);\r
+               getReserves().add(r);\r
+               \r
+               // キャッシュに保存\r
+               ReservesToFile(getReserves(), rsvedFile);\r
+               \r
+               System.out.printf("\n<<< Message from RD >>> \"%s\"\n\n", "正常に登録できました。");\r
+               return(true);\r
+       }\r
+       \r
+       /*\r
+        *      予約を削除する\r
+        */\r
+       public ReserveList RemoveRdEntry(String delid) {\r
+               \r
+               errmsg = "";\r
+               \r
+               System.out.println("Run: RemoveRdEntry()");\r
+               \r
+               // 削除対象を探す\r
+               ReserveList rx = null;\r
+               for (  ReserveList reserve : getReserves() )  {\r
+                       if (reserve.getId().equals(delid)) {\r
+                               rx = reserve;\r
+                               break;\r
+                       }\r
+               }\r
+               if (rx == null) {\r
+                       return(null);\r
+               }\r
+               \r
+               // リクエストを送る\r
+               reportProgress("send request.(1/1).");\r
+               String[] d = reqGET("http://"+getIPAddr()+":"+getPortNo()+"/delete.htm?id="+rx.getId(), null);\r
+               String header = d[0];\r
+               String response = d[1];\r
+               if ( response == null ) {\r
+                       errmsg = "レコーダーが反応しません。";\r
+                       return(null);\r
+               }\r
+               \r
+               // 予約リストを更新\r
+               getReserves().remove(rx);\r
+               \r
+               // キャッシュに保存\r
+               ReservesToFile(getReserves(), rsvedFile);\r
+               \r
+               System.out.printf("\n<<< Message from RD >>> \"%s\"\n\n", "正常に削除できました。");\r
+               return(rx);\r
+       }\r
+       \r
+       //\r
+       public String getErrmsg() {\r
+               return(errmsg);\r
+       }\r
+       \r
+       \r
+       //\r
+       private String getPoststr(ReserveList r) {\r
+               \r
+               StringBuilder sb = new StringBuilder();\r
+               \r
+               try {\r
+                       String exec             = (r.getExec())?("ON"):("OFF");\r
+                       String title    = URLEncoder.encode(r.getTitle(), "UTF-8");\r
+                       String ch_code  = cc.getCH_WEB2CODE(r.getCh_name());\r
+                       String channel  = cc.getCH_CODE2REC(ch_code);\r
+                       String pattern  = URLEncoder.encode(r.getRec_pattern(), "UTF-8");\r
+                       String dvdr             = (r.getRec_dvdcompat() != null) ? URLEncoder.encode(r.getRec_dvdcompat(), "UTF-8") : "";\r
+                       String autod    = (r.getRec_autodel() != null) ? URLEncoder.encode(r.getRec_autodel(), "UTF-8") : "";\r
+                       String lvoice   = (r.getRec_lvoice() != null) ? URLEncoder.encode(r.getRec_lvoice(), "UTF-8") : "";\r
+                       String edge             = (r.getRec_aspect() != null) ? URLEncoder.encode(r.getRec_aspect(), "UTF-8") : "";\r
+                       String xchap    = (r.getRec_xchapter() != null) ? URLEncoder.encode(r.getRec_xchapter(), "UTF-8") : "";\r
+                       String mschap   = (r.getRec_mschapter() != null) ? URLEncoder.encode(r.getRec_mschapter(), "UTF-8") : "";\r
+                       String mvchap   = (r.getRec_mvchapter() != null) ? URLEncoder.encode(r.getRec_mvchapter(), "UTF-8") : "";\r
+                       String autocomp = (r.getAutocomplete()) ? ("ON") : ("OFF");\r
+                       \r
+                       String tuner    = r.getTuner();\r
+                       if (r.getRec_mode().startsWith("[AVC] ") || r.getRec_mode().startsWith("[VR] ")) {\r
+                               if (tuner.endsWith("1")) {\r
+                                       tuner = "RE1";\r
+                               }\r
+                               else {\r
+                                       tuner = "RE2";\r
+                               }\r
+                       }\r
+                       \r
+                       if (r.getId() != null) sb.append("id="+r.getId()+"&");\r
+                       \r
+                       sb.append("ver="+getTSyncVersion()+"&");\r
+                       sb.append("exec="+exec+"&");\r
+                       sb.append("title="+title+"&");\r
+                       sb.append("ch_code="+ch_code+"&");\r
+                       sb.append("channel="+channel+"&");\r
+                       sb.append("pattern="+pattern+"&");\r
+                       sb.append("ahh="+r.getAhh()+"&");\r
+                       sb.append("amm="+r.getAmm()+"&");\r
+                       sb.append("zhh="+r.getZhh()+"&");\r
+                       sb.append("zmm="+r.getZmm()+"&");\r
+                       sb.append("tuner="+tuner+"&");\r
+                       sb.append("video="+r.getRec_mode()+"&");\r
+                       sb.append("audio="+r.getRec_audio()+"&");\r
+                       sb.append("disc="+r.getRec_device()+"&");\r
+                       sb.append("dvdr="+dvdr+"&");\r
+                       sb.append("xchapter="+xchap+"&");\r
+                       sb.append("mschapter="+mschap+"&");\r
+                       sb.append("mvchapter="+mvchap+"&");\r
+                       sb.append("auto_delete="+autod+"&");\r
+                       sb.append("lVoice="+lvoice+"&");\r
+                       sb.append("edge_left="+edge+"&");\r
+                       sb.append("autocomp="+autocomp+"&");\r
+                       sb.append("\n");\r
+               } catch (UnsupportedEncodingException e) {\r
+                       e.printStackTrace();\r
+               }\r
+               \r
+               return(sb.toString());\r
+       }\r
+}\r
diff --git a/TinyBannavi/src/tainavi/PlugIn_RecRD_E300.java b/TinyBannavi/src/tainavi/PlugIn_RecRD_E300.java
new file mode 100644 (file)
index 0000000..ce1470f
--- /dev/null
@@ -0,0 +1,1119 @@
+package tainavi;\r
+\r
+import java.io.File;\r
+import java.io.UnsupportedEncodingException;\r
+import java.net.Authenticator;\r
+import java.net.URLEncoder;\r
+import java.util.ArrayList;\r
+import java.util.Hashtable;\r
+import java.util.regex.Matcher;\r
+import java.util.regex.Pattern;\r
+\r
+/*\r
+ * \r
+ */\r
+\r
+public class PlugIn_RecRD_E300 extends HDDRecorderUtils implements HDDRecorder,Cloneable {\r
+\r
+       public PlugIn_RecRD_E300 clone() {\r
+               return (PlugIn_RecRD_E300) super.clone();\r
+       }\r
+\r
+       private static final String thisEncoding = "MS932";\r
+\r
+       /* 必須コード  - ここから */\r
+       \r
+       // 種族の特性\r
+       public String getRecorderId() { return "VARDIA RD-E300"; }\r
+       public RecType getType() { return RecType.RECORDER; }\r
+       \r
+       // 個体の特性\r
+       private GetRDStatus gs = new GetRDStatus();\r
+       private ChannelCode cc = new ChannelCode(getRecorderId());\r
+       private String rsvedFile = "";\r
+       \r
+       private String errmsg = "";\r
+\r
+       // 公開メソッド\r
+       \r
+       /*\r
+        * \r
+        */\r
+       public ChannelCode getChCode() {\r
+               return cc;\r
+       }\r
+       \r
+       /*\r
+        * チャンネルリモコン機能\r
+        */\r
+       public boolean ChangeChannel(String Channel) {\r
+               \r
+               if (Channel == null) {\r
+                       return true;\r
+               }\r
+               \r
+               int curBC;\r
+               int newBC;\r
+               String chNo;\r
+               byte enc;\r
+               \r
+               errmsg = "";\r
+               \r
+               /*\r
+                * 変更前の放送(地上波/BS/CS)\r
+                */\r
+               {\r
+                       String ch = null;\r
+                       for (int i=0; i<3 && (ch = gs.getCurChannel(getIPAddr())) == null; i++) {\r
+                               CommonUtils.milSleep(500);\r
+                       }\r
+                       if (ch == null) {\r
+                               errmsg = "レコーダへのアクセスに失敗しました(チャンネルリモコン)。";\r
+                               System.err.println(errmsg);\r
+                               return false;\r
+                       }\r
+                       \r
+                       byte[] ba = ch.getBytes();\r
+                       enc = ba[0];\r
+                       ch = ch.substring(1,5);\r
+                       \r
+                       if (ch.matches("^\\d.+")) {\r
+                               curBC = 0;\r
+                       }\r
+                       else if (ch.startsWith("BS")) {\r
+                               curBC = 1;\r
+                       }\r
+                       else if (ch.startsWith("CS")) {\r
+                               curBC = 2;\r
+                       }\r
+                       else {\r
+                               errmsg = "現在のチャンネルが認識できません("+ch+")。";\r
+                               System.err.println(errmsg);\r
+                               return false;\r
+                       }\r
+               }\r
+               \r
+               /*\r
+                * 変更後のCH\r
+                */\r
+               {\r
+                       // 放送(地上波/BS/CS)\r
+                       String cd = cc.getCH_WEB2CODE(Channel);\r
+                       String ch = cc.getCH_CODE2REC(cd);\r
+                       String typ = text2value(chtype, cd);\r
+                       if (typ == null) {\r
+                               errmsg = "鯛ナビに情報が同期されていないチャンネルです("+Channel+", "+ch+")。";\r
+                               System.err.println(errmsg);\r
+                               return false;\r
+                       }\r
+                       else if (typ.matches("^l[123]$")) {\r
+                               errmsg = "外部入力にアサインされているチャンネルには変更できません("+Channel+", "+ch+", "+typ+")。";\r
+                               System.err.println(errmsg);\r
+                               return false;\r
+                       }\r
+                       else if (typ.equals("uvd")) {\r
+                               newBC = 0;\r
+                       }\r
+                       else if (typ.equals("bsd")) {\r
+                               newBC = 1;\r
+                       }\r
+                       else if (typ.equals("csd")) {\r
+                               newBC = 2;\r
+                       }\r
+                       else {\r
+                               errmsg = "放送種別が識別できません。プログラム異常です("+Channel+", "+ch+")。";\r
+                               System.err.println(errmsg);\r
+                               return false;\r
+                       }\r
+                       \r
+                       // CH番号\r
+                       Matcher ma = Pattern.compile("(\\d\\d\\d)").matcher(ch);\r
+                       if (ma.find()) {\r
+                               chNo = ma.group(1);\r
+                       }\r
+                       else {\r
+                               errmsg = "CH番号が取得できません("+Channel+", "+ch+")。";\r
+                               System.err.println(errmsg);\r
+                               return false;\r
+                       }\r
+               }\r
+               \r
+               /*\r
+                * CH変更実行\r
+                */\r
+               \r
+               // おまじない\r
+               Authenticator.setDefault(new MyAuthenticator(getUser(), getPasswd()));\r
+               \r
+               int cBC;\r
+               for (int i=0; i<4 && newBC != (cBC = (curBC+i)%4); i++) {\r
+                       // 地上アナログ(3)が選択できるのはRE(0x06)だけ\r
+                       if (enc != 0x06 && cBC == 3) {\r
+                               continue;\r
+                       }\r
+                       \r
+                       // 切り替え実行\r
+                       reqGET("http://"+getIPAddr()+":"+getPortNo()+"/remote/remote.htm?key=21", null);\r
+                       \r
+                       // 地上波→BS・BS→CSは切り替え完了までに時間がかかる\r
+                       CommonUtils.milSleep((cBC == 0 || cBC == 1)?(3000):(1000));\r
+               }\r
+               \r
+               reqGET("http://"+getIPAddr()+":"+getPortNo()+"/remote/remote.htm?key=25", null);\r
+               CommonUtils.milSleep(1000);\r
+               for (int i=0; i<3; i++) {\r
+                       reqGET("http://"+getIPAddr()+":"+getPortNo()+"/remote/remote.htm?key=0"+chNo.substring(i,i+1), null);\r
+                       CommonUtils.milSleep(200);\r
+               }\r
+               reqGET("http://"+getIPAddr()+":"+getPortNo()+"/remote/remote.htm?key=44", null);\r
+               \r
+               return true;\r
+       }\r
+\r
+       /*\r
+        *      レコーダーから予約一覧を取得する \r
+        */\r
+       public boolean GetRdReserve(boolean force)\r
+       {\r
+               System.out.println("Run: GetRdReserve("+force+")");\r
+               \r
+               errmsg = "";\r
+               \r
+               //\r
+               rsvedFile = "env/reserved."+getIPAddr()+"_"+getPortNo()+"_"+getRecorderId()+".xml";\r
+               String vrateTFile = "env/videorate."+getIPAddr()+"_"+getPortNo()+"_"+getRecorderId()+".xml";\r
+               String arateTFile = "env/audiorate."+getIPAddr()+"_"+getPortNo()+"_"+getRecorderId()+".xml";\r
+               String folderTFile = "env/folders."+getIPAddr()+"_"+getPortNo()+"_"+getRecorderId()+".xml";\r
+               String encoderTFile = "env/encoders."+getIPAddr()+"_"+getPortNo()+"_"+getRecorderId()+".xml";\r
+               String dvdcompatTFile = "env/dvdcompat."+getIPAddr()+"_"+getPortNo()+"_"+getRecorderId()+".xml";\r
+               String deviceTFile = "env/device."+getIPAddr()+"_"+getPortNo()+"_"+getRecorderId()+".xml";\r
+               String xChapterTFile = "env/xchapter."+getIPAddr()+"_"+getPortNo()+"_"+getRecorderId()+".xml";\r
+               String msChapterTFile = "env/mschapter."+getIPAddr()+"_"+getPortNo()+"_"+getRecorderId()+".xml";\r
+               String mvChapterTFile = "env/mvchapter."+getIPAddr()+"_"+getPortNo()+"_"+getRecorderId()+".xml";\r
+               String chValueTFile = "env/chvalue."+getIPAddr()+"_"+getPortNo()+"_"+getRecorderId()+".xml";\r
+               String chTypeTFile = "env/chtype."+getIPAddr()+"_"+getPortNo()+"_"+getRecorderId()+".xml";\r
+\r
+               File f = new File(rsvedFile);\r
+               if ( force == false && f.exists()) {\r
+                       // キャッシュから読み出し(録画設定ほか)\r
+                       vrate = TVSload(vrateTFile);\r
+                       arate = TVSload(arateTFile);\r
+                       folder = TVSload(folderTFile);\r
+                       encoder = TVSload(encoderTFile);\r
+                       dvdcompat = TVSload(dvdcompatTFile);\r
+                       device = TVSload(deviceTFile);\r
+                       xchapter = TVSload(xChapterTFile);\r
+                       mschapter = TVSload(msChapterTFile);\r
+                       mvchapter = TVSload(mvChapterTFile);\r
+                       chvalue = TVSload(chValueTFile);\r
+                       chtype = TVSload(chTypeTFile);\r
+                       \r
+                       // キャッシュから読み出し(予約一覧)\r
+                       setReserves(ReservesFromFile(rsvedFile));\r
+                       \r
+                       // なぜか設定ファイルが空になっている場合があるので、その際は再取得する\r
+                       if (vrate.size()>0 && arate.size()>0 &&\r
+                                       folder.size()>0 && encoder.size()>0 && device.size()>0 &&\r
+                                       xchapter.size()>0 && mschapter.size()>0 && mvchapter.size()>0 &&\r
+                                       chvalue.size()>0 && chtype.size()>0) {\r
+                               return(true);\r
+                       }\r
+               }\r
+               \r
+               // おまじない\r
+               Authenticator.setDefault(new MyAuthenticator(getUser(), getPasswd()));\r
+\r
+               // RDから予約一覧を取り出す\r
+               Matcher ma = null;\r
+               String idx = "";\r
+               String header="";\r
+               String response="";\r
+               {\r
+                       reportProgress("get reserved list(1/4).");\r
+                       String[] d = reqGET("http://"+getIPAddr()+":"+getPortNo()+"/reserve/b_rsv.htm",null);\r
+                       header = d[0];\r
+                       response = d[1];\r
+                       \r
+                       if (response == null) {\r
+                               errmsg = "レコーダーが反応しません";\r
+                               return(false);\r
+                       }\r
+               }\r
+               ma = Pattern.compile("/reserve/(\\d+?)/reserve.htm").matcher(response);\r
+               if (ma.find()) {\r
+                       idx = ma.group(1);\r
+                       reportProgress("get reserved list(2/4).");\r
+                       String[] d = reqGET("http://"+getIPAddr()+":"+getPortNo()+"/reserve/"+idx+"/reserve.htm",null);\r
+                       header = d[0];\r
+                       response = d[1];\r
+                       \r
+                       if (response == null) {\r
+                               errmsg = "レコーダーが反応しません";\r
+                               return(false);\r
+                       }\r
+               }\r
+               \r
+               // 予約エントリー数を取得する\r
+               int RsvCnt = 0;\r
+               ma = Pattern.compile("RsvCnt\\s*=\\s*(\\d+?);").matcher(response);\r
+               if ( ! ma.find()) {\r
+                       errmsg = "レコーダーからの情報取得に失敗しました(予約一覧)";\r
+                       return false;\r
+               }\r
+               RsvCnt = Integer.valueOf(ma.group(1));\r
+               \r
+               // (1)録画設定の取得\r
+               {\r
+                       // ハング防止のおまじない\r
+                       reportProgress("get reserved list(3/4).");\r
+                       reqGET("http://"+getIPAddr()+":"+getPortNo()+"/reserve/"+idx+"/b_rsvinfo.htm?0&"+(RsvCnt+1),null);\r
+               }\r
+               {\r
+                       reportProgress("get reserved list(4/4).");\r
+                       String[] d = reqGET("http://"+getIPAddr()+":"+getPortNo()+"/reserve/"+idx+"/rsvinfo.htm",null);\r
+                       String hdr = d[0];\r
+                       String res = d[1];\r
+                       Matcher mb = null;\r
+                       \r
+                       // 正常取得チェック\r
+                       if ( ! res.matches("[\\s\\S]*var hdd_folder_text[\\s\\S]*")) {\r
+                               errmsg = "レコーダーからの情報取得に失敗しました(録画設定)";\r
+                               return false;\r
+                       }\r
+                       \r
+                       // (1-1)画質設定\r
+                       setSettingEtc(vrate,"vrate",1,res);\r
+                       TVSsave(vrate, vrateTFile);\r
+                       \r
+                       // (1-2)音質設定\r
+                       setSettingEtc(arate,"amode",0,res);\r
+                       TVSsave(arate, arateTFile);\r
+                       \r
+                       // (1-3)フォルダ一覧\r
+                       setSettingEtc(folder,"hdd_folder",1,res);\r
+                       TVSsave(folder, folderTFile);\r
+                       \r
+                       // (1-4)エンコーダ\r
+                       encoder.clear();\r
+                       \r
+                       mb = Pattern.compile("var double_encode_flg = (\\d+?);").matcher(res);\r
+                       if (mb.find()) {\r
+                               Matcher mc = Pattern.compile("\\n\\s*?switch \\( double_encode_flg \\) \\{([\\s\\S]+?default:)").matcher(res);\r
+                               if (mc.find()) {\r
+                                       Matcher md = Pattern.compile("(case "+mb.group(1)+":[\\s\\S]+?break;)").matcher(mc.group(1));\r
+                                       if (md.find()) {\r
+                                               Matcher me = Pattern.compile("name=enc_type value=.\"(\\d+?).\"[\\s\\S]+?act_(.+?)\\.gif").matcher(md.group(1));\r
+                                               while (me.find()) {\r
+                                                       TextValueSet t = new TextValueSet();\r
+                                                       t.setText(me.group(2));\r
+                                                       t.setValue(me.group(1));\r
+                                                       encoder.add(t);\r
+                                               }\r
+                                       }\r
+                               }\r
+                       }\r
+                       \r
+                       TVSsave(encoder, encoderTFile);\r
+                       \r
+                       // (1-5)DVD互換モード\r
+                       setSettingEtc(dvdcompat,"dvdr",1,res);\r
+                       TVSsave(dvdcompat, dvdcompatTFile);\r
+                       \r
+                       // (1-6)記録先デバイス\r
+                       setSettingEtc(device,"media",0,res);\r
+                       TVSsave(device, deviceTFile);\r
+                       \r
+                       // (1-7)自動チャプター関連\r
+                       setSettingEtc(xchapter,"mutechapter",0,res);\r
+                       TVSsave(xchapter, xChapterTFile);\r
+                       \r
+                       setSettingEtc(mschapter,"magicchapter",0,res);\r
+                       TVSsave(mschapter, msChapterTFile);\r
+\r
+                       setSettingEtc(mvchapter,"cmchapter",0,res);\r
+                       TVSsave(mvchapter, mvChapterTFile);\r
+                       \r
+                       // (1-8)チャンネルコードバリュー\r
+                       chvalue.clear();\r
+                       chtype.clear();\r
+                       for ( String typ : new String[] { "uvd","bsd","csd","uva","bsa","l1","l2","l3" } ) {\r
+                               String txtkey = typ+"_ch_text";\r
+                               String valkey = typ+"_ch_value";\r
+                               Matcher mc = Pattern.compile("var "+txtkey+"\\s*= new Array\\(([\\s\\S]+?)\\);").matcher(res);\r
+                               Matcher md = Pattern.compile("var "+valkey+"\\s*= new Array\\(([\\s\\S]+?)\\);").matcher(res);\r
+                               if ( mc.find() && md.find() ) {\r
+                                       Matcher me = Pattern.compile("\"(.+?)\",?").matcher(mc.group(1));\r
+                                       Matcher mf = null;\r
+                                       if ( typ.equals("uva") ) {\r
+                                               mf = Pattern.compile("(\\d+),").matcher(md.group(1));\r
+                                       }\r
+                                       else {\r
+                                               mf = Pattern.compile("\"([^\"]+?)\",").matcher(md.group(1));\r
+                                       }\r
+                                       while ( me.find() && mf.find() ) {\r
+                                               TextValueSet t = new TextValueSet();\r
+                                               t.setText(me.group(1));\r
+                                               t.setValue(mf.group(1));\r
+                                               chvalue.add(t);\r
+                                               \r
+                                               TextValueSet x = new TextValueSet();\r
+                                               x.setText(mf.group(1));\r
+                                               x.setValue(typ);\r
+                                               chtype.add(x);\r
+                                       }\r
+                               }\r
+                       }\r
+                       TVSsave(chvalue, chValueTFile);\r
+                       TVSsave(chtype, chTypeTFile);\r
+               }\r
+               \r
+               // 予約一覧データの分析\r
+               // 情報ブロックをとりだし…\r
+               /*\r
+                       var     c1  = new Array(RsvCnt);                // 予約ID\r
+                       var     c2  = new Array(RsvCnt);                // 実行フラグ\r
+                       var     c3  = new Array(RsvCnt);                // 予約名\r
+                       var     c4  = new Array(RsvCnt);                // REC1/REC2\r
+                       var     c5  = new Array(RsvCnt);                // 表示CH\r
+                       var     c6  = new Array(RsvCnt);                // 録画日付\r
+                       var     c7  = new Array(RsvCnt);                // 録画開始時刻\r
+                       var     c8  = new Array(RsvCnt);                // 録画終了時刻\r
+                       var     c9  = new Array(RsvCnt);                // 記録先\r
+                       var     c10 = new Array(RsvCnt);                // 画質\r
+                       var     c11 = new Array(RsvCnt);                // 音質\r
+                       var     c12 = new Array(RsvCnt);                // 録画開始時刻の文字色\r
+                       var     c13 = new Array(RsvCnt);                // 録画終了時刻の文字色\r
+                       var     c14 = new Array(RsvCnt);                // おすすめ\r
+                       var     c15 = new Array(RsvCnt);                // 番組追っかけ\r
+                       var     c16 = new Array(RsvCnt);                // スポーツ延長\r
+                       var     c17 = new Array(RsvCnt);                // 優先度\r
+                */\r
+               {\r
+                       //int cnt = 0;\r
+                       ArrayList<ReserveList> newReserveList = new ArrayList<ReserveList>();\r
+                       \r
+                       ma = Pattern.compile("(c1\\[\\d+?\\]=[\\s\\S]+?\";)\\n").matcher(response);\r
+                       while ( ma.find() ) {\r
+                               // 個々のデータを取り出す\r
+                               ReserveList entry = new ReserveList();\r
+                               \r
+                               Matcher mb = null;\r
+                                       \r
+                               String[] d = new String[17];\r
+                               mb = Pattern.compile("c\\d+?\\[\\d+?\\]=\"(.*?)\";").matcher(ma.group(1));\r
+                               for (int i=0; i<d.length; i++) {\r
+                                       if ( mb.find()) {\r
+                                               d[i] = mb.group(1);\r
+                                       }\r
+                               }\r
+                               \r
+                               // 予約ID\r
+                               entry.setId(d[0]);\r
+                               \r
+                               // 実行フラグ\r
+                               if ( ! d[1].equals("1")) {\r
+                                       entry.setExec(false);\r
+                               }\r
+                               \r
+                               // 予約名\r
+                               entry.setTitle(CommonUtils.unEscape(d[2]).replaceAll("<BR>", ""));\r
+                               entry.setTitlePop(TraceProgram.replacePop(entry.getTitle()));\r
+                               \r
+                               // REC1/REC2\r
+                               entry.setTuner(value2text(encoder, d[3]));\r
+                               \r
+                               // 表示CH\r
+                               entry.setChannel(d[4]);\r
+                               entry.setCh_name(cc.getCH_REC2WEB(d[4]));\r
+                               \r
+                               // 録画日付・開始時刻・終了時刻\r
+                               entry.setRec_pattern(d[5]);\r
+                               entry.setRec_pattern_id(getRec_pattern_Id(entry.getRec_pattern()));\r
+                               mb = Pattern.compile("(\\d\\d):(\\d\\d).*?(\\d\\d):(\\d\\d)").matcher(d[6]+"-"+d[7]);\r
+                               if (mb.find()) {\r
+                                       entry.setAhh(mb.group(1));\r
+                                       entry.setAmm(mb.group(2));\r
+                                       entry.setZhh(mb.group(3));\r
+                                       entry.setZmm(mb.group(4));\r
+                               }\r
+                               entry.setRec_nextdate(CommonUtils.getNextDate(entry));\r
+                               entry.setRec_min(CommonUtils.getRecMin(entry.getAhh(), entry.getAmm(), entry.getZhh(), entry.getZmm()));\r
+                               getStartEndDateTime(entry);\r
+                               \r
+                               // 記録先\r
+                               entry.setRec_device(d[8]);\r
+                               \r
+                               // 画質・音質\r
+                               entry.setRec_mode(d[9]);\r
+                               entry.setRec_audio(d[10]);\r
+                               \r
+                               // タイトル自動補完フラグなど本体からは取得できない情報を引き継ぐ\r
+                               copyAttributes(entry, getReserves());\r
+                               \r
+                               // 予約情報を保存\r
+                               newReserveList.add(entry.clone());\r
+                       }\r
+                       setReserves(newReserveList);\r
+               }\r
+               \r
+               // 取得した情報の表示\r
+               System.out.println("---Reserved List Start---");\r
+               for ( int i = 0; i<getReserves().size(); i++ ) {\r
+                       // 詳細情報の取得\r
+                       ReserveList e = getReserves().get(i);\r
+                       System.out.println(String.format("[%s] %s\t%s\t%s\t%s:%s\t%s:%s\t%sm\t%s\t%s\t%s\t%s",\r
+                                       (i+1), e.getId(), e.getRec_pattern(), e.getRec_nextdate(), e.getAhh(), e.getAmm(), e.getZhh(),  e.getZmm(),     e.getRec_min(), e.getRec_mode(), e.getTitle(), e.getChannel(), e.getCh_name()));\r
+               }\r
+               System.out.println("---Reserved List End---");\r
+               \r
+               // キャッシュに保存\r
+               ReservesToFile(getReserves(), rsvedFile);\r
+               \r
+               return(true);\r
+       }\r
+       \r
+       /*\r
+        *      予約を実行する\r
+        */\r
+       public boolean PostRdEntry(ReserveList r)\r
+       {\r
+               //\r
+               if (cc.getCH_WEB2CODE(r.getCh_name()) == null) {\r
+                       errmsg = "【警告】Web番組表の放送局名「"+r.getCh_name()+"」をCHコードに変換できません。CHコード設定を修正してください。" ;\r
+                       System.out.println(errmsg);\r
+                       return(false);\r
+               }\r
+               \r
+               System.out.println("Run: PostRdEntry("+r.getTitle()+")");\r
+               \r
+               errmsg = "";\r
+\r
+               // おまじない\r
+               Authenticator.setDefault(new MyAuthenticator(getUser(), getPasswd()));\r
+               \r
+               // RDから予約一覧を取り出す\r
+               Matcher ma = null;\r
+               String idx = "";\r
+               String header;\r
+               String response;\r
+               {\r
+                       reportProgress("get reserved list(1/7).");\r
+                       String[] d = reqGET("http://"+getIPAddr()+":"+getPortNo()+"/reserve/b_rsv.htm",null);\r
+                       header = d[0];\r
+                       response = d[1];\r
+                       \r
+                       if ( response == null ) {\r
+                               errmsg = "レコーダーが反応しません。";\r
+                               return(false);\r
+                       }\r
+               }\r
+               ma = Pattern.compile("/reserve/(\\d+?)/reserve.htm").matcher(response);\r
+               if (ma.find()) {\r
+                       reportProgress("get reserved list(2/7).");\r
+                       idx = ma.group(1);\r
+                       String[] d = reqGET("http://"+getIPAddr()+":"+getPortNo()+"/reserve/"+idx+"/reserve.htm",null);\r
+                       header = d[0];\r
+                       response = d[1];\r
+                       \r
+                       if ( response == null ) {\r
+                               errmsg = "レコーダーが反応しません。";\r
+                               return(false);\r
+                       }\r
+               }\r
+               \r
+               // 予約エントリー数を取得する\r
+               int RsvCnt = 0;\r
+               ma = Pattern.compile("RsvCnt\\s*=\\s*(\\d+?);").matcher(response);\r
+               if (ma.find()) {\r
+                       RsvCnt = Integer.valueOf(ma.group(1));\r
+               }\r
+       \r
+               // RDに新規登録要請\r
+               {\r
+                       // ハング防止のおまじない\r
+                       reportProgress("get program(3/7).");\r
+                       reqGET("http://"+getIPAddr()+":"+getPortNo()+"/reserve/"+idx+"/b_rsvinfo.htm?0&"+(RsvCnt+1),null);\r
+                       \r
+                       reportProgress("get program(4/7).");\r
+                       reqGET("http://"+getIPAddr()+":"+getPortNo()+"/reserve/"+idx+"/rsvinfo.htm",null);\r
+               }\r
+\r
+               // POSTデータを変換する\r
+               Hashtable<String, String> pdat = modPostdata(r);\r
+               \r
+               // RDへの情報作成\r
+               String pstr = joinPoststr(pdat);\r
+\r
+               // RDへ情報送信\r
+               {               \r
+                       reportProgress("send request.(5/7)");\r
+                       String[] d = reqPOST("http://"+getIPAddr()+":"+getPortNo()+"/reserve/"+idx+"/entry.htm", pstr, null);\r
+                       header = d[0];\r
+                       response = d[1];\r
+               }\r
+               \r
+               // 登録結果の確認\r
+               ma = Pattern.compile("alert\\(msg\\)").matcher(response);\r
+               if ( ma.find() ) {\r
+                       Matcher mb = Pattern.compile("\\bmsg=\"([\\s\\S]+?)\";").matcher(response);\r
+                       if (mb.find()) {\r
+                               errmsg = mb.group(1);\r
+                               System.out.printf("\n<<< Message from RD >>> \"%s\"\n\n", errmsg);\r
+                               Matcher mc = Pattern.compile("(予約時間が重複しています。|修正しました。)").matcher(errmsg);\r
+                               if ( ! mc.find() ) {\r
+                                       return(false);\r
+                               }\r
+                       }                       \r
+               }\r
+\r
+\r
+               \r
+               /*\r
+                * 予約情報の調整 \r
+                */\r
+               \r
+               // 予約ID番号を取得(キャッシュに存在しない番号が新番号)\r
+               {\r
+                       reportProgress("get identifier(6/7).");\r
+                       String[] d = reqGET("http://"+getIPAddr()+":"+getPortNo()+"/reserve/b_rsv.htm",null);\r
+                       header = d[0];\r
+                       response = d[1];\r
+               }\r
+               ma = Pattern.compile("/reserve/(\\d+?)/reserve.htm").matcher(response);\r
+               if (ma.find()) {\r
+                       idx = ma.group(1);\r
+                       reportProgress("get identifier(7/7).");\r
+                       String[] d = reqGET("http://"+getIPAddr()+":"+getPortNo()+"/reserve/"+idx+"/reserve.htm",null);\r
+                       header = d[0];\r
+                       response = d[1];\r
+               }\r
+               r.setId(getNewId(response));\r
+\r
+               // 予約パターンID\r
+               r.setRec_pattern_id(getRec_pattern_Id(r.getRec_pattern()));\r
+\r
+               // 次回予定日\r
+               r.setRec_nextdate(CommonUtils.getNextDate(r));\r
+               //r.setRec_nextdate(getNextDate(r.getRec_pattern(), r.getZhh()+":"+r.getZmm()));\r
+               \r
+               // 録画長\r
+               r.setRec_min(CommonUtils.getRecMin(r.getAhh(),r.getAmm(),r.getZhh(),r.getZmm()));\r
+               \r
+               // 開始日時・終了日時\r
+               getStartEndDateTime(r);\r
+               \r
+               // 予約リストを更新\r
+               getReserves().add(r);\r
+               \r
+               // キャッシュに保存\r
+               ReservesToFile(getReserves(), rsvedFile);\r
+               \r
+               System.out.printf("\n<<< Message from RD >>> \"%s\"\n\n", "正常に登録できました。");\r
+               return(true);\r
+       }\r
+\r
+       \r
+       /*\r
+        *      予約を更新する\r
+        */\r
+       public boolean UpdateRdEntry(ReserveList o, ReserveList r) {\r
+               //\r
+               if (cc.getCH_WEB2CODE(r.getCh_name()) == null) {\r
+                       errmsg = "【警告】Web番組表の放送局名「"+r.getCh_name()+"」をCHコードに変換できません。CHコード設定を修正してください。" ;\r
+                       System.out.println(errmsg);\r
+                       return(false);\r
+               }\r
+               \r
+               System.out.println("Run: UpdateRdEntry()");\r
+               \r
+               errmsg = "";\r
+\r
+               // おまじない\r
+               Authenticator.setDefault(new MyAuthenticator(getUser(), getPasswd()));\r
+               \r
+               // 更新準備\r
+               Matcher ma = null;\r
+               String idx = "";\r
+               int lineno = 0;\r
+               String header;\r
+               String response;\r
+               {\r
+                       reportProgress("get identifier(1/5).");\r
+                       String[] d = reqGET("http://"+getIPAddr()+":"+getPortNo()+"/reserve/b_rsv.htm",null);\r
+                       header = d[0];\r
+                       response = d[1];\r
+               }\r
+               ma = Pattern.compile("/reserve/(\\d+?)/reserve.htm").matcher(response);\r
+               if (ma.find()) {\r
+                       idx = ma.group(1);\r
+                       reportProgress("get identifier(2/5).");\r
+                       String[] d = reqGET("http://"+getIPAddr()+":"+getPortNo()+"/reserve/"+idx+"/reserve.htm",null);\r
+                       header = d[0];\r
+                       response = d[1];\r
+               }\r
+               ma = Pattern.compile("c1\\[(\\d+?)\\]=\""+r.getId()+"\";").matcher(response);\r
+               if ( ma.find() ) {\r
+                       lineno = Integer.valueOf(ma.group(1))+1;\r
+               }\r
+               \r
+               // RDに更新要請\r
+               {\r
+                       // ハング防止のおまじない\r
+                       reportProgress("get program(3/5).");\r
+                       reqGET("http://"+getIPAddr()+":"+getPortNo()+"/reserve/"+idx+"/b_rsvinfo.htm?"+r.getId()+"&"+lineno,null);\r
+                       \r
+                       reportProgress("get program(4/5).");\r
+                       reqGET("http://"+getIPAddr()+":"+getPortNo()+"/reserve/"+idx+"/rsvinfo.htm",null);\r
+               }\r
+\r
+               // POSTデータを変換する\r
+               Hashtable<String, String> pdat = modPostdata(r);\r
+               \r
+               // RDへの情報作成\r
+               String pstr = joinPoststr(pdat);\r
+\r
+               // RDへ情報送信\r
+               {               \r
+                       reportProgress("send request.(5/5)");\r
+                       String[] d = reqPOST("http://"+getIPAddr()+":"+getPortNo()+"/reserve/"+idx+"/entry.htm", pstr, null);\r
+                       header = d[0];\r
+                       response = d[1];\r
+               }\r
+\r
+               // 更新結果の確認\r
+               ma = Pattern.compile("alert\\(msg\\)").matcher(response);\r
+               if ( ma.find() ) {\r
+                       Matcher mb = Pattern.compile("\\bmsg=\"([\\s\\S]+?)\";").matcher(response);\r
+                       if (mb.find()) {\r
+                               errmsg = mb.group(1);\r
+                               System.out.printf("\n<<< Message from RD >>> \"%s\"\n\n", errmsg);\r
+                               Matcher mc = Pattern.compile("(予約時間が重複しています。|修正しました。)").matcher(errmsg);\r
+                               if ( ! mc.find() ) {\r
+                                       return(false);\r
+                               }\r
+                       }                       \r
+               }\r
+\r
+               System.out.printf("\n<<< Message from RD >>> \"%s\"\n\n", "正常に更新できました。");\r
+\r
+               /*\r
+                * 予約情報の調整 \r
+                */\r
+\r
+               // 予約パターンID\r
+               r.setRec_pattern_id(getRec_pattern_Id(r.getRec_pattern()));\r
+               \r
+               // 次回予定日\r
+               r.setRec_nextdate(CommonUtils.getNextDate(r));\r
+               //r.setRec_nextdate(getNextDate(r.getRec_pattern(), r.getZhh()+":"+r.getZmm()));\r
+               \r
+               // 録画長\r
+               r.setRec_min(CommonUtils.getRecMin(r.getAhh(),r.getAmm(),r.getZhh(),r.getZmm()));\r
+               \r
+               // 開始日時・終了日時\r
+               getStartEndDateTime(r);\r
+               \r
+               // 情報置き換え\r
+               getReserves().remove(o);\r
+               getReserves().add(r);\r
+\r
+               // キャッシュに保存\r
+               ReservesToFile(getReserves(), rsvedFile);\r
+               \r
+               return(true);\r
+       }\r
+\r
+       /*\r
+        *      予約を削除する\r
+        */\r
+       public ReserveList RemoveRdEntry(String delid) {\r
+               System.out.println("Run: RemoveRdEntry()");\r
+               \r
+               errmsg = "";\r
+\r
+               // おまじない\r
+               Authenticator.setDefault(new MyAuthenticator(getUser(), getPasswd()));\r
+\r
+               // 削除対象を探す\r
+               ReserveList rx = null;\r
+               for (  ReserveList reserve : getReserves() )  {\r
+                       if (reserve.getId().equals(delid)) {\r
+                               rx = reserve;\r
+                               break;\r
+                       }\r
+               }\r
+               if (rx == null) {\r
+                       return(null);\r
+               }\r
+\r
+               // 予約行番号を取得\r
+               Matcher ma = null;\r
+               String idx = "";\r
+               int lineno = 0;\r
+               String header;\r
+               String response;\r
+               {\r
+                       reportProgress("get identifier(1/5).");\r
+                       String[] d = reqGET("http://"+getIPAddr()+":"+getPortNo()+"/reserve/b_rsv.htm",null);\r
+                       header = d[0];\r
+                       response = d[1];\r
+               }\r
+               ma = Pattern.compile("/reserve/(\\d+?)/reserve.htm").matcher(response);\r
+               if (ma.find()) {\r
+                       idx = ma.group(1);\r
+                       reportProgress("get identifier(2/5).");\r
+                       String[] d = reqGET("http://"+getIPAddr()+":"+getPortNo()+"/reserve/"+idx+"/reserve.htm",null);\r
+                       header = d[0];\r
+                       response = d[1];\r
+               }\r
+               ma = Pattern.compile("c1\\[(\\d+?)\\]=\""+rx.getId()+"\";").matcher(response);\r
+               if ( ma.find() ) {\r
+                       lineno = Integer.valueOf(ma.group(1))+1;\r
+               }\r
+\r
+               // RDに削除要請\r
+               {\r
+                       // ハング防止のおまじない\r
+                       reportProgress("get program(3/5).");\r
+                       reqGET("http://"+getIPAddr()+":"+getPortNo()+"/reserve/"+idx+"/b_rsvinfo.htm?"+rx.getId()+"&"+lineno,null);\r
+                       \r
+                       reportProgress("get program(4/5).");\r
+                       reqGET("http://"+getIPAddr()+":"+getPortNo()+"/reserve/"+idx+"/rsvinfo.htm",null);\r
+               }\r
+               {               \r
+                       reportProgress("send request.(5/5)");\r
+                       String[] d = reqGET("http://"+getIPAddr()+":"+getPortNo()+"/reserve/"+idx+"/delete.htm", null);\r
+                       header = d[0];\r
+                       response = d[1];\r
+               }\r
+\r
+               // 削除結果の確認\r
+               ma = Pattern.compile("alert\\(msg\\)").matcher(response);\r
+               if ( ma.find() ) {\r
+                       Matcher mb = Pattern.compile("\\bmsg=\"([\\s\\S]+?)\";").matcher(response);\r
+                       if (mb.find()) {\r
+                               errmsg = mb.group(1);\r
+                               System.out.printf("\n<<< Message from RD >>> \"%s\"\n\n", errmsg);\r
+                       }\r
+                       return(null);\r
+               }\r
+               \r
+               // 予約リストを更新\r
+               getReserves().remove(rx);\r
+               \r
+               // キャッシュに保存\r
+               ReservesToFile(getReserves(), rsvedFile);\r
+               \r
+               System.out.printf("\n<<< Message from RD >>> \"%s\"\n\n", "正常に削除できました。");\r
+               return(rx);\r
+       }\r
+       \r
+       /*\r
+        * \r
+        */\r
+       public String getErrmsg() {\r
+               return(errmsg.replaceAll("\\\\r\\\\n", ""));\r
+       }\r
+       \r
+       \r
+       /* ここまで */\r
+       \r
+       \r
+       \r
+\r
+       \r
+       \r
+       /* 個別コード-ここから最後まで */\r
+\r
+       /*\r
+        * 非公開メソッド \r
+        */\r
+\r
+       private String joinPoststr(Hashtable<String, String> pdat)\r
+       {\r
+               String[] pkeys = {\r
+                       "bExec",\r
+                       "start_form",\r
+                       "title_name",\r
+                       "detail",\r
+                       "genre",\r
+                       "enc_type",\r
+                       "broadcast",\r
+                       "channel_list",\r
+                       "rec_priority",\r
+                       "maiyoubi_type",\r
+                       "date",\r
+                       "start_hour",\r
+                       "start_minute",\r
+                       "end_hour",\r
+                       "end_minute",\r
+                       "disc",\r
+                       "folder",\r
+                       "vrate",\r
+                       "amode",\r
+//                     "title_link",\r
+                       "auto_delete",\r
+//                     "video_es",\r
+//                     "audio_es",\r
+                       "dvdr",\r
+                       "lVoice",\r
+                       "edge_left",\r
+                       "bAutoChapter",\r
+                       "MagicChapter",\r
+                       "CM_Chapter",\r
+                       "channel_no",\r
+                       "dtv_sid",\r
+                       "dtv_nid",\r
+                       "net_link",\r
+                       "add_ch_text",\r
+                       "add_ch_value",\r
+                       "sport_ext_submit",\r
+                       "title_link_submit",\r
+                       "end_form"\r
+               };\r
+               \r
+               String pstr = "";\r
+               for ( String key : pkeys ) {\r
+                       if (pdat.containsKey(key)) {\r
+                               pstr += key+"="+pdat.get(key)+"&";\r
+                       }\r
+               }\r
+               pstr = pstr.substring(0, pstr.length()-1);\r
+               \r
+               return(pstr);\r
+       }\r
+       \r
+       private Hashtable<String, String> modPostdata(ReserveList r) {\r
+\r
+               Hashtable<String, String> newdat = new Hashtable<String, String>();\r
+               \r
+               // 実行するよ\r
+               newdat.put("bExec", (r.getExec())?("ON"):("OFF"));\r
+               if (r.getUpdateOnlyExec()) {\r
+                       return(newdat);\r
+               }\r
+\r
+               // 予約名・予約詳細\r
+               if ( r.getAutocomplete() ) {\r
+                       newdat.put("title_name", "");\r
+                       newdat.put("detail", "");\r
+               }\r
+               else {\r
+                       try {\r
+                               newdat.put("title_name", URLEncoder.encode(r.getTitle(),thisEncoding));\r
+                       } catch (UnsupportedEncodingException e1) {\r
+                               // TODO Auto-generated catch block\r
+                               e1.printStackTrace();\r
+                       }\r
+       \r
+                       // 予約詳細\r
+                       try {\r
+                               newdat.put("detail", URLEncoder.encode(r.getDetail().replaceAll("\n", Matcher.quoteReplacement("\r\n")), thisEncoding));\r
+                       } catch (UnsupportedEncodingException e) {\r
+                               // TODO Auto-generated catch block\r
+                               e.printStackTrace();\r
+                       }\r
+               }\r
+               \r
+               // 保存先\r
+               try {\r
+                       newdat.put("folder", URLEncoder.encode(text2value(folder, r.getRec_folder()),thisEncoding));\r
+               } catch (UnsupportedEncodingException e) {\r
+                       // TODO Auto-generated catch block\r
+                       e.printStackTrace();\r
+               }\r
+               \r
+               // ジャンル\r
+               newdat.put("genre", _recstr2genre(r.getRec_genre()));\r
+               \r
+               // DVD互換モード\r
+               newdat.put("dvdr", text2value(dvdcompat, r.getRec_dvdcompat()));\r
+               \r
+               // 録画チャンネル\r
+               String channel = cc.getCH_WEB2CODE(r.getCh_name());\r
+               String ch_no = cc.getCH_CODE2REC(channel);\r
+               if (chtype.size() > 0) {\r
+                       String typ = text2value(chtype, channel);\r
+                       if (typ.equals("uva")) {\r
+                               newdat.put("broadcast","0");            // 地上アナログ\r
+                       }\r
+                       else if (typ.equals("bsa")) {\r
+                               newdat.put("broadcast","1");            // BSアナログ\r
+                       }\r
+                       else if (typ.equals("l1")) {\r
+                               newdat.put("broadcast","2");            // 外部入力(L1)\r
+                       }\r
+                       else if (typ.equals("l2")) {\r
+                               newdat.put("broadcast","3");            // 外部入力(L2)\r
+                       }\r
+                       else if (typ.equals("l3")) {\r
+                               newdat.put("broadcast","4");            // 外部入力(L3)\r
+                       }\r
+                       else if (typ.equals("bsd")) {\r
+                               newdat.put("broadcast","10");           // BSデジタル\r
+                       }\r
+                       else if (typ.equals("csd")) {\r
+                               newdat.put("broadcast","11");           // 110度CSデジタル\r
+                       }\r
+                       else if (typ.equals("uvd")) {\r
+                               newdat.put("broadcast","12");           // 地上デジタル\r
+                       }\r
+                       else {\r
+                               // 普通ここには落ちない\r
+                               if (ch_no.startsWith("C")) {\r
+                                       newdat.put("broadcast","2");            // "C***"は外部入力(L1)\r
+                               }\r
+                               else if (ch_no.startsWith("SP")) {\r
+                                       newdat.put("broadcast","4");            // "SP***"は外部入力(L3)\r
+                               }\r
+                               else {\r
+                                       newdat.put("broadcast","3");            // 未定義は全部外部入力(L2)\r
+                               }\r
+                       }\r
+                       \r
+                       newdat.put("channel_list", channel);\r
+                       newdat.put("channel_no", channel);\r
+               }\r
+               else {\r
+                       // 後方互換\r
+                       Matcher ma = Pattern.compile("^(.)(.)").matcher(ch_no);\r
+                       if (ma.find()) {\r
+                               if ((ma.group(1)+ma.group(2)).equals("CH")) {\r
+                                       newdat.put("broadcast","0");            // 地上アナログ\r
+                               }\r
+                               else if ((ma.group(1)+ma.group(2)).equals("BS")) {\r
+                                       newdat.put("broadcast","10");           // BSデジタル\r
+                               }\r
+                               else if ((ma.group(1)+ma.group(2)).equals("CS")) {\r
+                                       newdat.put("broadcast","11");           // 110度CSデジタル\r
+                               }\r
+                               else if (ma.group(1).equals("L")) {\r
+                                       newdat.put("broadcast", String.valueOf(Integer.valueOf(ma.group(2))+1));        // ライン入力ABC\r
+                               }\r
+                               else {\r
+                                       newdat.put("broadcast","12");           // 地上デジタル\r
+                               }\r
+                               \r
+                               newdat.put("channel_list", channel);\r
+                               newdat.put("channel_no", channel);\r
+                       }\r
+               }\r
+               \r
+               // 録画レート\r
+               newdat.put("enc_type", text2value(encoder, r.getTuner()));\r
+               newdat.put("vrate", text2value(vrate, r.getRec_mode()));\r
+               \r
+               // 録音レート\r
+               newdat.put("amode", text2value(arate, r.getRec_audio()));\r
+\r
+               // TS時の修正\r
+               if (r.getTuner().equals("TS")) {\r
+                       r.setRec_mode("TS");\r
+                       r.setRec_audio("");\r
+               }\r
+\r
+               // 繰り返し\r
+               Matcher ma = Pattern.compile("^\\d").matcher(r.getRec_pattern());\r
+               if (ma.find()) { \r
+                       newdat.put("maiyoubi_type","0");\r
+                       newdat.put("date", r.getRec_pattern());\r
+               }\r
+               else {\r
+                       newdat.put("maiyoubi_type", "1");\r
+                       int i = 1;\r
+                       for ( String s : RPTPTN ) {\r
+                               if ( s.equals(r.getRec_pattern()) == true ) {\r
+                                       newdat.put("date", String.valueOf(i));\r
+                               }\r
+                               i++;\r
+                       }\r
+               }\r
+\r
+\r
+               newdat.put("start_hour", r.getAhh());\r
+               newdat.put("start_minute", r.getAmm());\r
+               newdat.put("end_hour", r.getZhh());\r
+               newdat.put("end_minute", r.getZmm());\r
+\r
+               // 記録先\r
+               newdat.put("disc", text2value(device, r.getRec_device()));\r
+\r
+               // 自動チャプター関連\r
+               newdat.put("bAutoChapter"               , text2value(xchapter, r.getRec_xchapter()));\r
+               newdat.put("MagicChapter"               , text2value(mschapter, r.getRec_mschapter()));\r
+               newdat.put("CM_Chapter"                 , text2value(mvchapter, r.getRec_mvchapter()));\r
+               \r
+               // 追加\r
+               newdat.put("start_form"                 , "");\r
+               newdat.put("rec_priority"               , "150");\r
+               newdat.put("title_link"                 , "0");\r
+               newdat.put("auto_delete"                , "0");\r
+               newdat.put("video_es"                   , "00");\r
+               newdat.put("audio_es"                   , "10");\r
+               newdat.put("edge_left"                  , "0");         // 録画のりしろ\r
+               //newdat.put("bAutoChapter"             , "0");\r
+               //newdat.put("MagicChapter"             , "0");\r
+               //newdat.put("CM_Chapter"                       , "0");\r
+               newdat.put("add_ch_text"                , "");\r
+               newdat.put("add_ch_value"               , "");\r
+               //newdat.put("sport_ext_submit" , "undefined");\r
+               //newdat.put("title_link_submit"        , "undefined");\r
+               newdat.put("end_form"                   , "0");\r
+               \r
+               return(newdat);\r
+       }\r
+\r
+       private String _recstr2genre(String genre)\r
+       {\r
+               if (genre.equals(TVProgram.ProgGenre.MOVIE.toString())) {\r
+                       return "0";\r
+               }\r
+               else if (genre.equals(TVProgram.ProgGenre.MUSIC.toString())) {\r
+                       return "1";\r
+               }\r
+               else if (genre.equals(TVProgram.ProgGenre.DORAMA.toString())) {\r
+                       return "2";\r
+               }\r
+               else if (genre.equals(TVProgram.ProgGenre.ANIME.toString())) {\r
+                       return "3";\r
+               }\r
+               else if (genre.equals(TVProgram.ProgGenre.SPORTS.toString())) {\r
+                       return "4";\r
+               }\r
+               else if (genre.equals(TVProgram.ProgGenre.DOCUMENTARY.toString())) {\r
+                       return "5";\r
+               }\r
+               else if (genre.equals(TVProgram.ProgGenre.THEATER.toString())) {\r
+                       return "6";\r
+               }\r
+               else if (genre.equals(TVProgram.ProgGenre.HOBBY.toString())) {\r
+                       return "7";\r
+               }\r
+               else if (genre.equals(TVProgram.ProgGenre.VARIETY.toString())) {\r
+                       return "8";\r
+               }\r
+               else {\r
+                       return "10";\r
+               }\r
+       }\r
+\r
+       private void setSettingEtc(ArrayList<TextValueSet> tvs, String key, int typ, String res) {\r
+               tvs.clear();\r
+               String valExpr = "(\\d+),?";\r
+               if (typ == 1) {\r
+                       valExpr = "\"(.+?)\",?";\r
+               }\r
+               Matcher mc = Pattern.compile("var "+key+"_text\\s*= new Array\\(([\\s\\S]+?)\\);").matcher(res);\r
+               Matcher md = Pattern.compile("var "+key+"_value\\s*= new Array\\(([\\s\\S]+?)\\);").matcher(res);\r
+               if (mc.find() && md.find()) {\r
+                       Matcher me = Pattern.compile("\"(.+?)\",?").matcher(mc.group(1));\r
+                       Matcher mf = Pattern.compile(valExpr).matcher(md.group(1));\r
+                       while (me.find() && mf.find()) {\r
+                               TextValueSet t = new TextValueSet();\r
+                               t.setText(me.group(1));\r
+                               t.setValue(mf.group(1));\r
+                               tvs.add(t);\r
+                       }\r
+               }\r
+       }\r
+}\r
diff --git a/TinyBannavi/src/tainavi/PlugIn_RecRD_EDCB.java b/TinyBannavi/src/tainavi/PlugIn_RecRD_EDCB.java
new file mode 100644 (file)
index 0000000..b5b0191
--- /dev/null
@@ -0,0 +1,2781 @@
+package tainavi;\r
+\r
+import java.io.File;\r
+import java.io.UnsupportedEncodingException;\r
+import java.net.Authenticator;\r
+import java.net.URLEncoder;\r
+import java.util.ArrayList;\r
+import java.util.Calendar;\r
+import java.util.GregorianCalendar;\r
+import java.util.HashMap;\r
+import java.util.regex.Matcher;\r
+import java.util.regex.Pattern;\r
+\r
+\r
+/**\r
+ * \r
+ */\r
+public class PlugIn_RecRD_EDCB extends HDDRecorderUtils implements HDDRecorder,Cloneable {\r
+\r
+       @Override\r
+       public PlugIn_RecRD_EDCB clone() {\r
+               return (PlugIn_RecRD_EDCB) super.clone();\r
+       }\r
+       \r
+       private static final String thisEncoding = "MS932";\r
+       \r
+       /*******************************************************************************\r
+        * 種族の特性\r
+        ******************************************************************************/\r
+\r
+       @Override\r
+       public String getRecorderId() { return "EpgDataCap_Bon"; }\r
+       @Override\r
+       public RecType getType() { return RecType.RECORDER; }\r
+       \r
+       // 自動エンコーダ選択は禁止\r
+       @Override\r
+       public boolean isAutoEncSelectEnabled() { return false; }\r
+       // 繰り返し予約はできない\r
+       @Override\r
+       public boolean isRepeatReserveSupported() { return false; }\r
+       // 番組追従が可能\r
+       @Override\r
+       public boolean isPursuesEditable() { return true; }\r
+       // タイトル自動補完はできない\r
+       @Override\r
+       public boolean isAutocompleteSupported() { return false; }\r
+       // chvalueを使っていいよ\r
+       @Override\r
+       public boolean isChValueAvailable() { return true; }\r
+       // CHコードは入力しなくていい\r
+       @Override\r
+       public boolean isChCodeNeeded() { return false; }\r
+\r
+       /*******************************************************************************\r
+        * 予約ダイアログなどのテキストのオーバーライド\r
+        ******************************************************************************/\r
+       \r
+       @Override\r
+       public String getLabel_Audiorate() { return "予約方法"; }\r
+       @Override\r
+       public String getLabel_Folder() { return "録画モード"; }\r
+       @Override\r
+       public String getLabel_Device() { return "指定サービス対象"; }\r
+       @Override\r
+       public String getLabel_XChapter() { return "録画開始(秒前)"; }\r
+       @Override\r
+       public String getLabel_MsChapter() { return "録画終了(秒後)"; }\r
+       @Override\r
+       public String getLabel_MvChapter() { return "復帰後再起動"; }\r
+       @Override\r
+       public String getLabel_DVDCompat() { return "連続録画動作"; }\r
+       @Override\r
+       public String getLabel_LVoice() { return "ぴったり録画"; }\r
+       @Override\r
+       public String getLabel_Aspect() { return "録画後動作"; }\r
+       @Override\r
+       public String getLabel_Videorate() { return "プリセット"; }\r
+       @Override\r
+       public String getLabel_Autodel() { return "録画優先度"; }\r
+       @Override\r
+       public String getLabel_BVperf() { return "ワンセグを別ファイルに出力"; }\r
+       \r
+       @Override\r
+       public String getChDatHelp() { return\r
+                       "「レコーダの放送局名」は、予約一覧取得が正常に完了していれば設定候補がコンボボックスで選択できるようになります。"+\r
+                       "";\r
+       }\r
+       \r
+       /*******************************************************************************\r
+        * 定数\r
+        ******************************************************************************/\r
+\r
+       private static final String ITEM_REC_MODE_DISABLE       = "無効";\r
+       \r
+       private static final String ITEM_JUST_ENABLE            = "する";\r
+\r
+       private static final String ITEM_REC_TYPE_EPG           = "EPG";\r
+       private static final String ITEM_REC_TYPE_PROG          = "プログラム";\r
+\r
+       private static final String ITEM_PRESETID_REG           = "予約時";\r
+\r
+       private static final String ITEM_MARGINE_DEFAULT        = "デフォルト";\r
+       \r
+       private static final String ITEM_SERVMOCE_DEFAULT       = "デフォルト";\r
+       private static final String ITEM_SERVMOCE_SUBT          = "+字幕";\r
+       private static final String ITEM_SERVMOCE_CARO          = "+カルーセル";\r
+       private static final String ITEM_SERVMOCE_SUBTCARO      = "+字幕&カルーセル";\r
+       private static final String ITEM_SERVMOCE_NONE          = "なし";\r
+       \r
+       private static final String ITEM_YES = "する";\r
+       private static final String ITEM_NO = "しない";\r
+\r
+       private static final String VALUE_REC_MODE_DISABLE  = "5";\r
+\r
+       private static final String VALUE_YES  = "1";\r
+       private static final String VALUE_NO  = "0";\r
+       \r
+       private static final String VALUE_TRACE_DISABLE         = "0";\r
+       private static final String VALUE_TRACE_ENABLE          = "1";\r
+\r
+       private static final String VALUE_REC_TYPE_EPG          = "EPG";\r
+       private static final String VALUE_REC_TYPE_PROG         = "PROGRAM";\r
+\r
+       private static final String VALUE_PRESETID_REG          = "65535";\r
+       \r
+       private static final String VALUE_MARGINE_DEFAULT       = "FF";\r
+       \r
+       private static final String VALUE_SERVMOCE_DEFAULT      = "FF";\r
+       private static final String VALUE_SERVMOCE_SUBT         = "0-";\r
+       private static final String VALUE_SERVMOCE_CARO         = "-0";\r
+       private static final String VALUE_SERVMOCE_SUBTCARO     = "00";\r
+       private static final String VALUE_SERVMOCE_NONE         = "--";\r
+       \r
+       private static final String RETVAL_KEY_RECFOLDERCOUNT           = "recFolderCount";\r
+       private static final String RETVAL_KEY_R_RECFOLDER                      = "RrecFolder";\r
+       private static final String RETVAL_KEY_R_WRITEPLUGIN            = "RwritePlugIn";\r
+       private static final String RETVAL_KEY_R_RECNAMEPLUGIN          = "RrecNamePlugIn";\r
+       \r
+       private static final String RETVAL_KEY_PARTIALFOLDERCOUNT       = "partialFolderCount";\r
+       private static final String RETVAL_KEY_P_RECFOLDER                      = "PrecFolder";\r
+       private static final String RETVAL_KEY_P_WRITEPLUGIN            = "PwritePlugIn";\r
+       private static final String RETVAL_KEY_P_RECNAMEPLUGIN          = "PrecNamePlugIn";\r
+\r
+       private static final String TEXT_NANSHICHO_HEADER = "臨)";\r
+       \r
+       // EPG予約の確認範囲は前後4時間まで\r
+       private static final long likersvrange = 4L*3600L*1000L;\r
+\r
+       // HTTPリトライ回数\r
+       private static final int retryMax = 3;\r
+\r
+       // ログ関連\r
+       \r
+       private final String MSGID = "["+getRecorderId()+"] ";\r
+       private final String ERRID = "[ERROR]"+MSGID;\r
+       private final String DBGID = "[DEBUG]"+MSGID;\r
+       \r
+       \r
+       /*******************************************************************************\r
+        * CHコード設定、エラーメッセージ\r
+        ******************************************************************************/\r
+       \r
+       @Override\r
+       public ChannelCode getChCode() {\r
+               return cc;\r
+       }\r
+       \r
+       private ChannelCode cc = new ChannelCode(getRecorderId());\r
+       \r
+       @Override\r
+       public String getErrmsg() {\r
+               return(errmsg);\r
+       }\r
+       \r
+       private String errmsg = "";\r
+       \r
+       /*******************************************************************************\r
+        * 部品\r
+        ******************************************************************************/\r
+\r
+       private String rsvedFile = null;\r
+\r
+       /*******************************************************************************\r
+        * 素のTVSではコードが読めなくなるのでラッピングしてみる\r
+        ******************************************************************************/\r
+\r
+       private ArrayList<TextValueSet> getListPresetID()                       { return vrate; }               // プリセット\r
+       private String getTextPresetID(ReserveList r)                           { return r.getRec_mode(); }\r
+       private void setTextPresetID(ReserveList r, String text)        { r.setRec_mode(text); }\r
+       \r
+       private ArrayList<TextValueSet> getListRecMode()                        { return folder; }              // 録画モード\r
+       private String getTextRecMode(ReserveList r)                            { return r.getRec_folder(); }\r
+       private void setTextRecMode(ReserveList r, String text)         { r.setRec_folder(text); }\r
+       \r
+       private ArrayList<TextValueSet> getListPriority()                       { return autodel; }             // 優先度\r
+       private String getTextPriority(ReserveList r)                           { return r.getRec_autodel(); }\r
+       private void setTextPriority(ReserveList r, String text)        { r.setRec_autodel(text); }\r
+       \r
+       private ArrayList<TextValueSet> getListPittariFlag()            { return lvoice; }              // ぴったり(?)録画\r
+       private String getTextPittariFlag(ReserveList r)                        { return r.getRec_lvoice(); }\r
+       private void setTextPittariFlag(ReserveList r, String text)     { r.setRec_lvoice(text); }\r
+       \r
+       private ArrayList<TextValueSet> getListSuspendMode()            { return aspect; }      // 録画後動作\r
+       private String getTextSuspendMode(ReserveList r)                        { return r.getRec_aspect(); }\r
+       private void setTextSuspendMode(ReserveList r, String text)     { r.setRec_aspect(text); }\r
+       \r
+       private ArrayList<TextValueSet> getListRebootFlag()                     { return mvchapter; }   // 復帰後再起動する\r
+       private String getTextRebootFlag(ReserveList r)                         { return r.getRec_mvchapter(); }\r
+       private void setTextRebootFlag(ReserveList r, String text)      { r.setRec_mvchapter(text); }\r
+       \r
+       private ArrayList<TextValueSet> getListStartMargine()           { return xchapter; }    // 録画マージン(開始)\r
+       private String getTextStartMargine(ReserveList r)                       { return r.getRec_xchapter(); }\r
+       private void setTextStartMargine(ReserveList r, String text){ r.setRec_xchapter(text); }\r
+       \r
+       private ArrayList<TextValueSet> getListEndMargine()                     { return mschapter; }   // 録画マージン(終了)\r
+       private String getTextEndMargine(ReserveList r)                         { return r.getRec_mschapter(); }\r
+       private void setTextEndMargine(ReserveList r, String text)      { r.setRec_mschapter(text); }\r
+       \r
+       private ArrayList<TextValueSet> getListContinueRecFlag()        { return dvdcompat; }           // 連続録画動作\r
+       private String getTextContinueRecFlag(ReserveList r)            { return r.getRec_dvdcompat(); }\r
+       private void setTextContinueRecFlag(ReserveList r, String text) { r.setRec_dvdcompat(text); }\r
+       \r
+       private ArrayList<TextValueSet> getListServiceMode()            { return device; }              // 指定サービス対象データ\r
+       private String getTextServiceMode(ReserveList r)                        { return r.getRec_device(); }\r
+       private void setTextServiceMode(ReserveList r, String text)     { r.setRec_device(text); }\r
+       \r
+       private ArrayList<TextValueSet> getListTunerID()                        { return encoder; }             // 使用チューナー強制指定\r
+       private String getTextTunerID(ReserveList r)                            { return r.getTuner(); }\r
+       private void setTextTunerID(ReserveList r, String text)         { r.setTuner(text); }\r
+       \r
+       private ArrayList<TextValueSet> getListPartialRecFlag()         { return bvperf; }              // 別ファイルに同時出力する\r
+       private String getTextPartialRecFlag(ReserveList r)                     { return r.getRec_bvperf(); }\r
+       private void setTextPartialRecFlag(ReserveList r, String text)  { r.setRec_bvperf(text); }\r
+       \r
+       private ArrayList<TextValueSet> getListRecType()                        { return arate; }               // 予約方式 ★鯛ナビ独自\r
+       private String getTextRecType(ReserveList r)                            { return r.getRec_audio(); }\r
+       private void setTextRecType(ReserveList r, String text)         { r.setRec_audio(text); }\r
+       \r
+       private ArrayList<TextValueSet> getListChValue()                        { return chvalue; }             // CH番号 ★鯛ナビ独自\r
+       //private String getTextChValue(ReserveList r)                          { return null; }\r
+       \r
+       private ArrayList<TextValueSet> getListChType()                         { return chtype; }              // CH種別 ★鯛ナビ独自\r
+       //private String getTextChType(ReserveList r)                                   { return null; }\r
+       \r
+       private void setListPresetID(ArrayList<TextValueSet> tvs)               { ArrayList<TextValueSet> o = getListPresetID();                o.clear(); o.addAll(tvs); }\r
+       private void setListRecMode(ArrayList<TextValueSet> tvs)                { ArrayList<TextValueSet> o = getListRecMode();                 o.clear(); o.addAll(tvs); }\r
+       private void setListPriority(ArrayList<TextValueSet> tvs)               { ArrayList<TextValueSet> o = getListPriority();                o.clear(); o.addAll(tvs); }\r
+       private void setListPittariFlag(ArrayList<TextValueSet> tvs)    { ArrayList<TextValueSet> o = getListPittariFlag();             o.clear(); o.addAll(tvs); }\r
+       private void setListSuspendMode(ArrayList<TextValueSet> tvs)    { ArrayList<TextValueSet> o = getListSuspendMode();             o.clear(); o.addAll(tvs); }\r
+       //private void setListRebootFlag(ArrayList<TextValueSet> tvs)           { ArrayList<TextValueSet> o = getListRebootFlag();              o.clear(); o.addAll(tvs); }\r
+       //private void setListStartMargine(ArrayList<TextValueSet> tvs) { ArrayList<TextValueSet> o = getListStartMargine();    o.clear(); o.addAll(tvs); }\r
+       //private void setListEndMargine(ArrayList<TextValueSet> tvs)           { ArrayList<TextValueSet> o = getListEndMargine();              o.clear(); o.addAll(tvs); }\r
+       //private void setListContinueRecFlag(ArrayList<TextValueSet> tvs){ ArrayList<TextValueSet> o = getListContinueRecFlag();       o.clear(); o.addAll(tvs); }\r
+       private void setListTunerID(ArrayList<TextValueSet> tvs)                { ArrayList<TextValueSet> o = getListTunerID();                 o.clear(); o.addAll(tvs); }\r
+       //private void setListPartialRecFlag(ArrayList<TextValueSet> tvs)       { ArrayList<TextValueSet> o = getListPartialRecFlag();  o.clear(); o.addAll(tvs); }\r
+       //private void setListRecType(ArrayList<TextValueSet> tvs)              { ArrayList<TextValueSet> o = getListRecType();                 o.clear(); o.addAll(tvs); }\r
+       private void setListChValue(ArrayList<TextValueSet> tvs)                { ArrayList<TextValueSet> o = getListChValue();                 o.clear(); o.addAll(tvs); }\r
+       private void setListChType(ArrayList<TextValueSet> tvs)                 { ArrayList<TextValueSet> o = getListChType();                  o.clear(); o.addAll(tvs); }\r
+\r
+       /*******************************************************************************\r
+        * コンストラクタ\r
+        ******************************************************************************/\r
+       \r
+       public PlugIn_RecRD_EDCB() {\r
+               setIPAddr("127.0.0.1(サンプル)");\r
+               setPortNo("5510(サンプル)");\r
+               setUser("IDとPASSはダミーで結構です");\r
+               setPasswd("********");\r
+       }\r
+\r
+       /*******************************************************************************\r
+        * チャンネルリモコン機能\r
+        ******************************************************************************/\r
+       \r
+       @Override\r
+       public boolean ChangeChannel(String Channel) {\r
+               // 何もない\r
+               return false;\r
+       }\r
+       \r
+       /*\r
+       @Override\r
+       public void wakeup() {\r
+       }\r
+       */\r
+       \r
+       @Override\r
+       public void shutdown() {\r
+       }\r
+\r
+       \r
+       /*******************************************************************************\r
+        * レコーダーから予約一覧を取得する\r
+        ******************************************************************************/\r
+       \r
+       @Override\r
+       public boolean GetRdSettings(boolean force) {\r
+               \r
+               System.out.println("レコーダの各種設定情報を取得します.");\r
+               \r
+               /*\r
+                *  CHコード設定\r
+                */\r
+               \r
+               cc.load(force);\r
+               replaceChNames(cc);             // これは予約一覧取得からの場合は無駄な処理になるが、GetRdSettings単体呼び出しなら意味がある\r
+               \r
+               /*\r
+                *  選択肢集団\r
+                */\r
+               \r
+               final String presetIdTFile      = String.format("%s%srs_%s.%s.%s_%s.xml", "env", File.separator, getRecorderId(), "presetId", getIPAddr(), getPortNo());\r
+               final String recModeTFile       = String.format("%s%srs_%s.%s.%s_%s.xml", "env", File.separator, getRecorderId(), "recMode", getIPAddr(), getPortNo());\r
+               final String prioTFile          = String.format("%s%srs_%s.%s.%s_%s.xml", "env", File.separator, getRecorderId(), "priority", getIPAddr(), getPortNo());\r
+               final String pittariTFile       = String.format("%s%srs_%s.%s.%s_%s.xml", "env", File.separator, getRecorderId(), "pittari", getIPAddr(), getPortNo());\r
+               final String suspModeTFile      = String.format("%s%srs_%s.%s.%s_%s.xml", "env", File.separator, getRecorderId(), "suspendMode", getIPAddr(), getPortNo());\r
+               final String tunerIdTFile       = String.format("%s%srs_%s.%s.%s_%s.xml", "env", File.separator, getRecorderId(), "tunerId", getIPAddr(), getPortNo());\r
+               final String chTypeTFile        = String.format("%s%srs_%s.%s.%s_%s.xml", "env", File.separator, getRecorderId(), "chType", getIPAddr(), getPortNo());\r
+               final String chValueTFile       = String.format("%s%srs_%s.%s.%s_%s.xml", "env", File.separator, getRecorderId(), "chValue", getIPAddr(), getPortNo());\r
+\r
+               // ハードコーディングな選択肢の面々\r
+               setSettingNoYes(getListRebootFlag());\r
+               setSettingRecMargin(getListStartMargine());\r
+               setSettingRecMargin(getListEndMargine());\r
+               setSettingServiceMode(getListServiceMode());\r
+               setSettingNoYes(getListContinueRecFlag());\r
+               setSettingNoYes(getListPartialRecFlag());\r
+               setSettingRecType(getListRecType());\r
+       \r
+               if ( force == false ) {\r
+                       /*\r
+                        *  キャッシュから読み出し\r
+                        */\r
+                       setListPresetID(TVSload(presetIdTFile));\r
+                       setListRecMode(TVSload(recModeTFile));\r
+                       setListPriority(TVSload(prioTFile));\r
+                       setListPittariFlag(TVSload(pittariTFile));\r
+                       setListSuspendMode(TVSload(suspModeTFile));\r
+                       setListTunerID(TVSload(tunerIdTFile));\r
+                       setListChValue(TVSload(chValueTFile));\r
+                       setListChType(TVSload(chTypeTFile));\r
+               }\r
+               else {\r
+                       /*\r
+                        *  レコーダから読み出し\r
+                        */\r
+                       \r
+                       // おまじない\r
+                       Authenticator.setDefault(new MyAuthenticator(getUser(), getPasswd()));\r
+                       \r
+                       // (1)録画設定の取得\r
+                       {\r
+                               reportProgress("録画設定を取得します.");\r
+                               String[] d = reqGET("http://"+getIPAddr()+":"+getPortNo()+"/addprogres.html",null);\r
+                               //String hdr = d[0];\r
+                               String res = d[1];\r
+                               \r
+                               if ( res == null ) {\r
+                                       errmsg = "レコーダーが反応しません";\r
+                                       return false;\r
+                               }\r
+                               \r
+                               ArrayList<TextValueSet> tvs = new ArrayList<TextValueSet>();\r
+                               ArrayList<TextValueSet> tvs2 = new ArrayList<TextValueSet>();\r
+                               \r
+                               // (1-1)プリセット\r
+                               setSettingEtc(tvs,"presetID",0,res);\r
+                               add2tvs(tvs, ITEM_PRESETID_REG, VALUE_PRESETID_REG);    // なんつーか\r
+                               TVSsave(tvs, presetIdTFile);\r
+                               setListPresetID(tvs);\r
+       \r
+                               // (1-2)録画モード\r
+                               setSettingEtc(tvs,"recMode",0,res);\r
+                               for ( TextValueSet o : tvs ) {\r
+                                       if ( o.getText().equals(ITEM_REC_MODE_DISABLE) ) {\r
+                                               tvs.remove(o);\r
+                                               break;\r
+                                       }\r
+                               }\r
+                               TVSsave(tvs, recModeTFile);\r
+                               setListRecMode(tvs);\r
+                               \r
+                               // (1-12)録画優先度\r
+                               setSettingEtc(tvs, "priority", 0, res);\r
+                               TVSsave(tvs, prioTFile);\r
+                               setListPriority(tvs);\r
+                               \r
+                               // (1-11)ぴったり\r
+                               setSettingEtc(tvs,"pittariFlag",0,res);\r
+                               TVSsave(tvs, pittariTFile);\r
+                               setListPittariFlag(tvs);\r
+                               \r
+                               // (1-6)録画後動作\r
+                               setSettingEtc(tvs,"suspendMode",0,res);\r
+                               TVSsave(tvs, suspModeTFile);\r
+                               setListSuspendMode(tvs);\r
+                               \r
+                               // (1-4)エンコーダ\r
+                               setSettingEtc(tvs,"tunerID",0,res);\r
+                               TVSsave(tvs, tunerIdTFile);\r
+                               setListTunerID(tvs);\r
+                               \r
+                               // (1-5)チャンネル\r
+                               setSettingChCodeValue(tvs,tvs2,"serviceID",res);\r
+                               TVSsave(tvs, chValueTFile);\r
+                               TVSsave(tvs2, chTypeTFile);\r
+                               setListChValue(tvs);\r
+                               setListChType(tvs);\r
+                       }\r
+               }\r
+               \r
+               // ちゃんと設定を取得できているよね?\r
+               return (getListTunerID().size()>0 && getListChValue().size()>0 && getListPresetID().size()>0);\r
+       }\r
+       \r
+       /*\r
+        *      レコーダーから予約一覧を取得する \r
+        */\r
+       \r
+       @Override\r
+       public boolean GetRdReserve(boolean force)\r
+       {\r
+               System.out.println("レコーダから予約一覧を取得します("+force+")");\r
+               \r
+               errmsg = "";\r
+               \r
+               // 予約情報キャッシュ保存先ファイル名\r
+               rsvedFile = String.format("%s%s%s.%s_%s_%s.xml", "env", File.separator, "reserved", getIPAddr(), getPortNo(), getRecorderId());\r
+\r
+               // おまじない\r
+               Authenticator.setDefault(new MyAuthenticator(getUser(), getPasswd()));\r
+               \r
+               File f = new File(rsvedFile);\r
+               if ( force == false && f.exists() ) {\r
+                       // キャッシュから読み出し(予約一覧)\r
+                       setReserves(ReservesFromFile(rsvedFile));\r
+                       replaceChNames(cc);\r
+                       if (getDebug()) ShowReserves();\r
+\r
+                       return true;\r
+               }\r
+               \r
+               // レコーダから読み出し(予約一覧)\r
+               ArrayList<ReserveList> newReserveList = new ArrayList<ReserveList>();\r
+               if ( ! GetRdReservedList(newReserveList) ) {\r
+                       return(false);\r
+               }\r
+               \r
+               // 既存予約一覧からの情報引き継ぎ\r
+               copyAttributesAllList(getReserves(), newReserveList);\r
+               \r
+               // 保存\r
+               setReserves(newReserveList);\r
+               ReservesToFile(getReserves(), rsvedFile);       // キャッシュに保存\r
+               \r
+               // 録画済みフラグを立てる(録画結果一覧→予約一覧)\r
+               setRecordedFlag();\r
+               \r
+               ShowReserves();\r
+               \r
+               return true;\r
+       }\r
+       \r
+       /**\r
+        * CHコードを置き換えよう(EDCBの場合はCODE2WEB)\r
+        */\r
+       private void replaceChNames(ChannelCode cc) {\r
+               for ( ReserveList r : getReserves() ) {\r
+                       r.setCh_name(cc.getCH_CODE2WEB(r.getChannel()));\r
+               }\r
+       }\r
+       \r
+       /**\r
+        * @see #GetRdSettings(boolean)\r
+        */\r
+       @Override\r
+       public boolean GetRdRecorded(boolean force) {\r
+               \r
+               // おまじない\r
+               Authenticator.setDefault(new MyAuthenticator(getUser(), getPasswd()));\r
+               \r
+               System.out.println("レコーダから録画結果一覧を取得します("+force+"): "+getRecorderId()+"("+getIPAddr()+":"+getPortNo()+")");\r
+               \r
+               String recedFile = String.format("%s%s%s.%s_%s_%s.xml", "env", File.separator, "recorded", getIPAddr(), getPortNo(), getRecorderId());\r
+               \r
+               // 既存のログをチェック\r
+               ArrayList<RecordedInfo> newRecordedList = RecordedFromFile(recedFile);\r
+               \r
+               File f = new File(recedFile);\r
+               if ( ! force && f.exists() ) {\r
+                       \r
+                       // キャッシュから読み出し(録画結果一覧)\r
+                       setRecorded(newRecordedList);\r
+                       if (getDebug()) ShowRecorded();\r
+       \r
+                       // 録画済みフラグを立てる(録画結果一覧→予約一覧)\r
+                       setRecordedFlag();\r
+               \r
+                       return true;\r
+               }\r
+               \r
+               if ( ! GetRdRecordedList(newRecordedList) ) {\r
+                       return false;\r
+               }\r
+               if ( ! GetRdRecordedListDetailAll(newRecordedList) ) {\r
+                       return false;\r
+               }\r
+               setRecorded(newRecordedList);                           // 置き換え\r
+               RecordedToFile(getRecorded(), recedFile);       // キャッシュに保存\r
+               \r
+               // 録画済みフラグを立てる(録画結果一覧→予約一覧)\r
+               setRecordedFlag();\r
+               \r
+               ShowRecorded();\r
+\r
+               return true;\r
+       }\r
+       \r
+       private void ShowReserves() {\r
+               System.out.println("---Reserved List Start---");\r
+               for ( int i = 0; i<getReserves().size(); i++ ) {\r
+                       // 詳細情報の取得\r
+                       ReserveList e = getReserves().get(i);\r
+                       System.out.println(String.format("[%s] %s\t%s\t%s %s:%s-%s:%s\t%sm\t%s\t%s\t%s(%s)\t%s\t%s\t%s",\r
+                                       (i+1), e.getId(), e.getRec_pattern(), e.getRec_nextdate(), e.getAhh(), e.getAmm(), e.getZhh(),  e.getZmm(),     e.getRec_min(), e.getContentId(), getTextRecType(e), e.getTitle(), e.getTitlePop(), e.getChannel(), e.getCh_name(), e.getRecorded()));\r
+               }\r
+               System.out.println("---Reserved List End---");\r
+       }\r
+       private void ShowRecorded() {\r
+               System.out.println("---Recorded List Start---");\r
+               for ( int i = 0; i<getRecorded().size(); i++ ) {\r
+                       // 詳細情報の取得\r
+                       RecordedInfo e = getRecorded().get(i);\r
+                       System.out.println(String.format("[%s] %s %s\t%s:%s-%s:%s\t%s(%s)\t%s",\r
+                                       (i+1), e.getId(), e.getDate(), e.getAhh(), e.getAmm(), e.getZhh(), e.getZmm(), e.getTitle(), e.getCh_name(), e.getResult()));\r
+               }\r
+               System.out.println("---Recorded List End---");\r
+       }\r
+       \r
+       private boolean GetRdReservedList(ArrayList<ReserveList> newReserveList) {\r
+               \r
+               // RDから予約一覧を取り出す\r
+               String response="";\r
+               {\r
+                       reportProgress("予約一覧を取得します.");\r
+                       String[] d = reqGET("http://"+getIPAddr()+":"+getPortNo()+"/api/EnumReserveInfo",null,"utf8");\r
+                       response = d[1];\r
+                       \r
+                       if (response == null) {\r
+                               errmsg = "レコーダーが反応しません";\r
+                               return(false);\r
+                       }\r
+               }\r
+               \r
+               Matcher ma = Pattern.compile("<reserveinfo>(.+?)</reserveinfo>", Pattern.DOTALL).matcher(response);\r
+               while ( ma.find() ) {\r
+                       String id = null;\r
+                       String title = null;\r
+                       GregorianCalendar cal = null;\r
+                       Integer hh = null;\r
+                       Integer mm = null;\r
+                       Integer length = null;\r
+                       Integer onid = null;\r
+                       Integer tsid = null;\r
+                       Integer sid = null;\r
+                       Integer evid = null;            // 65536:プログラム予約\r
+                       String comment = null;\r
+                       Integer overlapMode = null;\r
+                       \r
+                       Integer recMode = null;\r
+                       Integer priority = null;\r
+                       Integer tuijyuuFlag = null;\r
+                       Integer serviceMode = null;\r
+                       Integer pittariFlag = null;\r
+                       Integer suspendMode = null;\r
+                       Integer rebootFlag = null;\r
+                       Integer useMargineFlag = null;\r
+                       Integer startMargine = null;\r
+                       Integer endMargine = null;\r
+                       Integer continueRecFlag = null;\r
+                       Integer partialRecFlag = null;\r
+                       Integer tunerID = null;\r
+                       \r
+                       String batFilePath = null;\r
+                       \r
+                       int recFolderCount = 0;\r
+                       ArrayList<String> rRecFolder = new ArrayList<String>();\r
+                       ArrayList<String> rWritePlugIn = new ArrayList<String>();\r
+                       ArrayList<String> rRecNamePlugIn = new ArrayList<String>();\r
+                       \r
+                       int partialFolderCount = 0;\r
+                       ArrayList<String> pRecFolder = new ArrayList<String>();\r
+                       ArrayList<String> pWritePlugIn = new ArrayList<String>();\r
+                       ArrayList<String> pRecNamePlugIn = new ArrayList<String>();\r
+                       \r
+                       ArrayList<String> recFileNameList = new ArrayList<String>();\r
+\r
+                       Matcher mb = Pattern.compile("<(.+?)>(.+?)</\\1>", Pattern.DOTALL).matcher(ma.group(1));\r
+                       while ( mb.find() ) {\r
+                               /*\r
+                                * 解析\r
+                                */\r
+                               \r
+                               // 基本情報\r
+                               if ( mb.group(1).equals("ID") ) {\r
+                                       id = mb.group(2);\r
+                               }\r
+                               else if ( mb.group(1).equals("title") ) {\r
+                                       title = CommonUtils.unEscape(mb.group(2));\r
+                               }\r
+                               else if ( mb.group(1).equals("startDate") ) {\r
+                                       Matcher mc = Pattern.compile("^(\\d+)/(\\d+)/(\\d+)").matcher(mb.group(2));\r
+                                       if ( mc.find() ) {\r
+                                               cal = CommonUtils.getCalendar(String.format("%04d/%02d/%02d",Integer.valueOf(mc.group(1)),Integer.valueOf(mc.group(2)),Integer.valueOf(mc.group(3))));\r
+                                       }\r
+                               }\r
+                               else if ( mb.group(1).equals("startTime") ) {\r
+                                       Matcher mc = Pattern.compile("^(\\d+):(\\d+)").matcher(mb.group(2));\r
+                                       if ( mc.find() ) {\r
+                                               hh = Integer.valueOf(mc.group(1));\r
+                                               mm = Integer.valueOf(mc.group(2));\r
+                                       }\r
+                               }\r
+                               else if ( mb.group(1).equals("duration") ) {\r
+                                       length = Integer.valueOf(mb.group(2))/60;\r
+                               }\r
+                               else if ( mb.group(1).equals("ONID") ) {\r
+                                       onid = Integer.valueOf(mb.group(2));\r
+                               }\r
+                               else if ( mb.group(1).equals("TSID") ) {\r
+                                       tsid = Integer.valueOf(mb.group(2));\r
+                               }\r
+                               else if ( mb.group(1).equals("SID") ) {\r
+                                       sid = Integer.valueOf(mb.group(2));\r
+                               }\r
+                               else if ( mb.group(1).equals("eventID") ) {\r
+                                       evid = Integer.valueOf(mb.group(2));\r
+                               }\r
+                               else if ( mb.group(1).equals("comment") ) {\r
+                                       comment = CommonUtils.unEscape(mb.group(2));\r
+                               }\r
+                               else if ( mb.group(1).equals("overlapMode") ) {\r
+                                       overlapMode = Integer.valueOf(mb.group(2));\r
+                               }\r
+                               else if ( mb.group(1).equals("recsetting") ) {\r
+                                       // 録画設定\r
+                                       Matcher mc = Pattern.compile("<(.+?)>(.+?)</\\1>", Pattern.DOTALL).matcher(mb.group(2));\r
+                                       while ( mc.find() ) {\r
+                                               if ( mc.group(1).equals("recMode") ) {\r
+                                                       recMode = Integer.valueOf(mc.group(2));\r
+                                               }\r
+                                               else if ( mc.group(1).equals("priority") ) {\r
+                                                       priority = Integer.valueOf(mc.group(2));\r
+                                               }\r
+                                               else if ( mc.group(1).equals("tuijyuuFlag") ) {\r
+                                                       tuijyuuFlag = Integer.valueOf(mc.group(2));\r
+                                               }\r
+                                               else if ( mc.group(1).equals("serviceMode") ) {\r
+                                                       serviceMode = Integer.valueOf(mc.group(2));\r
+                                               }\r
+                                               else if ( mc.group(1).equals("pittariFlag") ) {\r
+                                                       pittariFlag = Integer.valueOf(mc.group(2));\r
+                                               }\r
+                                               else if ( mc.group(1).equals("batFilePath") ) {\r
+                                                       batFilePath = mc.group(2);\r
+                                               }\r
+                                               else if ( mc.group(1).equals("suspendMode") ) {\r
+                                                       suspendMode = Integer.valueOf(mc.group(2));\r
+                                               }\r
+                                               else if ( mc.group(1).equals("rebootFlag") ) {\r
+                                                       rebootFlag = Integer.valueOf(mc.group(2));\r
+                                               }\r
+                                               else if ( mc.group(1).equals("useMargineFlag") ) {\r
+                                                       useMargineFlag = Integer.valueOf(mc.group(2));\r
+                                               }\r
+                                               else if ( mc.group(1).equals("startMargine") ) {\r
+                                                       startMargine = Integer.valueOf(mc.group(2));\r
+                                               }\r
+                                               else if ( mc.group(1).equals("endMargine") ) {\r
+                                                       endMargine = Integer.valueOf(mc.group(2));\r
+                                               }\r
+                                               else if ( mc.group(1).equals("continueRecFlag") ) {\r
+                                                       continueRecFlag = Integer.valueOf(mc.group(2));\r
+                                               }\r
+                                               else if ( mc.group(1).equals("partialRecFlag") ) {\r
+                                                       partialRecFlag = Integer.valueOf(mc.group(2));\r
+                                               }\r
+                                               else if ( mc.group(1).equals("tunerID") ) {\r
+                                                       tunerID = Integer.valueOf(mc.group(2));\r
+                                               }\r
+                                               else if ( mc.group(1).equals("recFolderList") ) {\r
+                                                       Matcher md = Pattern.compile("<(.+?)>(.+?)</\\1>", Pattern.DOTALL).matcher(mc.group(2));\r
+                                                       while ( md.find() ) {\r
+                                                               if ( md.group(1).equals("recFolderInfo") ) {\r
+                                                                       recFolderCount++;\r
+                                                                       Matcher me = Pattern.compile("<(.+?)>(.*?)</\\1>", Pattern.DOTALL).matcher(md.group(2));\r
+                                                                       while ( me.find() ) {\r
+                                                                               if ( me.group(1).equals("recFolder") ) {\r
+                                                                                       rRecFolder.add(me.group(2));\r
+                                                                               }\r
+                                                                               else if ( me.group(1).equals("writePlugIn") ) {\r
+                                                                                       rWritePlugIn.add(me.group(2));\r
+                                                                               }\r
+                                                                               else if ( me.group(1).equals("recNamePlugIn") ) {\r
+                                                                                       rRecNamePlugIn.add(me.group(2));\r
+                                                                               }\r
+                                                                       }\r
+                                                               }\r
+                                                       }\r
+                                               }\r
+                                               else if ( mc.group(1).equals("partialRecFolder") ) {\r
+                                                       Matcher md = Pattern.compile("<(.+?)>(.+?)</\\1>", Pattern.DOTALL).matcher(mc.group(2));\r
+                                                       while ( md.find() ) {\r
+                                                               if ( md.group(1).equals("recFolderInfo") ) {\r
+                                                                       partialFolderCount++;\r
+                                                                       Matcher me = Pattern.compile("<(.+?)>(.*?)</\\1>", Pattern.DOTALL).matcher(md.group(2));\r
+                                                                       while ( me.find() ) {\r
+                                                                               if ( me.group(1).equals("recFolder") ) {\r
+                                                                                       pRecFolder.add(me.group(2));\r
+                                                                               }\r
+                                                                               else if ( me.group(1).equals("writePlugIn") ) {\r
+                                                                                       pWritePlugIn.add(me.group(2));\r
+                                                                               }\r
+                                                                               else if ( me.group(1).equals("recNamePlugIn") ) {\r
+                                                                                       pRecNamePlugIn.add(me.group(2));\r
+                                                                               }\r
+                                                                       }\r
+                                                               }\r
+                                                       }\r
+                                               }\r
+                                       }\r
+                               }\r
+                               else if ( mb.group(1).equals("recFileNameList") ) {\r
+                                       Matcher mc = Pattern.compile("<(.+?)>(.+?)</\\1>", Pattern.DOTALL).matcher(mb.group(2));\r
+                                       while ( mc.find() ) {\r
+                                               if ( mc.group(1).equals("name") ) {\r
+                                                       recFileNameList.add(mc.group(2));\r
+                                               }\r
+                                       }\r
+                               }\r
+                       }\r
+                       \r
+                       /*\r
+                        * 生成\r
+                        */\r
+                       \r
+                       ReserveList r = new ReserveList();\r
+                       \r
+                       r.setId(getUniqId(id));\r
+                       r.setTitle(title);\r
+                       r.setTitlePop(TraceProgram.replacePop(title));\r
+                       \r
+                       String chid = ContentIdEDCB.getChId(onid, tsid, sid);\r
+                       String edcbid = ContentIdEDCB.getContentId(chid, evid);\r
+                       String contentid = ContentIdEDCB.stripMark(edcbid);\r
+\r
+                       {\r
+                               StringBuilder dt = new StringBuilder("■予約状況:");\r
+                               if ( evid != 0 && evid != 65535 ) {\r
+                                       // EPG予約\r
+                                       r.setContentId(edcbid);\r
+                                       setTextRecType(r,ITEM_REC_TYPE_EPG);\r
+                                       //\r
+                                       if ( comment != null && comment.contains("EPG自動予約") ) {\r
+                                               dt.append(comment);\r
+                                               r.setAutoreserved(true);\r
+                                       }\r
+                                       else {\r
+                                               dt.append("EPG手動予約");\r
+                                               r.setAutoreserved(false);\r
+                                       }\r
+                               }\r
+                               else {\r
+                                       // プログラム予約\r
+                                       r.setContentId(null);\r
+                                       setTextRecType(r,ITEM_REC_TYPE_PROG);\r
+                                       //\r
+                                       dt.append("プログラム予約");\r
+                                       r.setAutoreserved(false);\r
+                               }\r
+                               dt.append("\n");\r
+                               r.setDetail(dt.toString());             // 初期化\r
+                       }\r
+                       \r
+                       r.setChannel(String.valueOf(Long.decode("0x"+chid)));\r
+                       r.setCh_name(cc.getCH_CODE2WEB(r.getChannel()));\r
+                       \r
+                       r.setRec_pattern(CommonUtils.getDate(cal));\r
+                       r.setRec_pattern_id(11);\r
+                       cal.set(Calendar.HOUR_OF_DAY, hh);\r
+                       cal.set(Calendar.MINUTE, mm);\r
+                       r.setAhh(String.format("%02d", cal.get(Calendar.HOUR_OF_DAY)));\r
+                       r.setAmm(String.format("%02d", cal.get(Calendar.MINUTE)));\r
+                       cal.add(Calendar.MINUTE,length);\r
+                       r.setZhh(String.format("%02d", cal.get(Calendar.HOUR_OF_DAY)));\r
+                       r.setZmm(String.format("%02d", cal.get(Calendar.MINUTE)));\r
+                       r.setRec_nextdate(CommonUtils.getNextDate(r));\r
+                       r.setRec_min(CommonUtils.getRecMin(r.getAhh(), r.getAmm(), r.getZhh(), r.getZmm()));\r
+                       getStartEndDateTime(r);\r
+                       \r
+                       // 予約実行とチューナー不足\r
+                       {\r
+                               String text = value2text(getListRecMode(),String.valueOf(recMode));\r
+                               setTextRecMode(r, text);\r
+                               if ( text.equals("") ) {\r
+                                       r.setExec(false);\r
+                                       r.setTunershort(false);\r
+                               }\r
+                               else {\r
+                                       r.setExec(true);\r
+                                       r.setTunershort(overlapMode!=0);\r
+                               }\r
+                       }\r
+                       \r
+                       // プリセット(予約一覧からとれる情報は常に「予約時」)\r
+                       setTextPresetID(r, value2text(getListPresetID(),VALUE_PRESETID_REG));\r
+                       \r
+                       // チューナー\r
+                       setTextTunerID(r, value2text(getListTunerID(),String.valueOf(tunerID)));\r
+                       \r
+                       // 録画後動作\r
+                       setTextSuspendMode(r, value2text(getListSuspendMode(),String.valueOf(suspendMode)));\r
+\r
+                       // 追従\r
+                       r.setPursues(VALUE_YES.equals(String.valueOf(tuijyuuFlag)));\r
+                       \r
+                       // ぴったり\r
+                       setTextPittariFlag(r, value2text(getListPittariFlag(),String.valueOf(pittariFlag)));\r
+                       \r
+                       // 録画優先度\r
+                       setTextPriority(r, value2text(getListPriority(),String.valueOf(priority)));\r
+                       \r
+                       // 連続録画動作\r
+                       if ( VALUE_YES.equals(String.valueOf(continueRecFlag)) ) {\r
+                               setTextContinueRecFlag(r, ITEM_YES);\r
+                       }\r
+                       else {\r
+                               setTextContinueRecFlag(r, ITEM_NO);\r
+                       }\r
+                       \r
+                       // 復帰後動作\r
+                       if ( VALUE_YES.equals(String.valueOf(rebootFlag)) ) {\r
+                               setTextRebootFlag(r, ITEM_YES);\r
+                       }\r
+                       else {\r
+                               setTextRebootFlag(r, ITEM_NO);\r
+                       }\r
+                       \r
+                       // 指定サービス対象データ\r
+                       if ( serviceMode == 0x00 ) {\r
+                               setTextServiceMode(r, ITEM_SERVMOCE_DEFAULT);\r
+                       }\r
+                       else if ( serviceMode == 0x01 ) {\r
+                               setTextServiceMode(r, ITEM_SERVMOCE_NONE);\r
+                       }\r
+                       else if ( serviceMode == 0x11 ) {\r
+                               setTextServiceMode(r, ITEM_SERVMOCE_SUBT);\r
+                       }\r
+                       else if ( serviceMode == 0x21 ) {\r
+                               setTextServiceMode(r, ITEM_SERVMOCE_CARO);\r
+                       }\r
+                       else if ( serviceMode == 0x31 ) {\r
+                               setTextServiceMode(r, ITEM_SERVMOCE_SUBTCARO);\r
+                       }\r
+                       \r
+                       // 録画マージン\r
+                       if ( useMargineFlag == 0 ) {\r
+                               setTextStartMargine(r, ITEM_MARGINE_DEFAULT);\r
+                               setTextEndMargine(r, ITEM_MARGINE_DEFAULT);\r
+                       }\r
+                       else {\r
+                               setTextStartMargine(r, String.valueOf(startMargine));\r
+                               setTextEndMargine(r, String.valueOf(endMargine));\r
+                       }\r
+                       \r
+                       // 別ファイルに同時出力する\r
+                       if ( VALUE_YES.equals(String.valueOf(partialRecFlag)) ) {\r
+                               setTextPartialRecFlag(r, ITEM_YES);\r
+                       }\r
+                       else {\r
+                               setTextPartialRecFlag(r, ITEM_NO);\r
+                       }\r
+                       \r
+                       // 録画後実行bat\r
+                       if ( batFilePath != null ) {\r
+                               StringBuilder dt = new StringBuilder(r.getDetail());\r
+                               dt.append("■録画後実行bat:");\r
+                               dt.append(batFilePath);\r
+                               dt.append("\n");\r
+                               r.setDetail(dt.toString());\r
+                       }\r
+                       \r
+                       // オプショナル\r
+                       r.getHidden_params().put(RETVAL_KEY_RECFOLDERCOUNT, String.valueOf(recFolderCount));\r
+                       if ( recFolderCount > 0 && rRecFolder.size() >= recFolderCount && rWritePlugIn.size() >= recFolderCount && rRecNamePlugIn.size() >= recFolderCount ) {\r
+                               StringBuilder dt = new StringBuilder(r.getDetail());\r
+                               StringBuilder rf = new StringBuilder();\r
+                               StringBuilder wp = new StringBuilder();\r
+                               StringBuilder rp = new StringBuilder();\r
+                               for ( int i=0; i<recFolderCount; i++ ) {\r
+                                       dt.append("■録画フォルダ・出力PlugIn・ファイル名PlugIn(");\r
+                                       dt.append(String.valueOf(i+1));\r
+                                       dt.append("):");\r
+                                       dt.append(rRecFolder.get(i));\r
+                                       dt.append(" / ");\r
+                                       dt.append(rWritePlugIn.get(i));\r
+                                       dt.append(" / ");\r
+                                       dt.append(rRecNamePlugIn.get(i));\r
+                                       dt.append("\n");\r
+                                       \r
+                                       rf.append(rRecFolder.get(i));\r
+                                       rf.append("\t");\r
+                                       \r
+                                       wp.append(rWritePlugIn.get(i));\r
+                                       wp.append("\t");\r
+                                       \r
+                                       rp.append(rRecNamePlugIn.get(i));\r
+                                       rp.append("\t");\r
+                               }\r
+                               r.setDetail(dt.toString());\r
+                               r.getHidden_params().put(RETVAL_KEY_R_RECFOLDER, rf.toString());\r
+                               r.getHidden_params().put(RETVAL_KEY_R_WRITEPLUGIN, wp.toString());\r
+                               r.getHidden_params().put(RETVAL_KEY_R_RECNAMEPLUGIN, rp.toString());\r
+                       }\r
+                       \r
+                       r.getHidden_params().put(RETVAL_KEY_PARTIALFOLDERCOUNT, String.valueOf(partialFolderCount));\r
+                       if ( partialFolderCount > 0 && pRecFolder.size() >= recFolderCount && pWritePlugIn.size() >= recFolderCount && pRecNamePlugIn.size() >= recFolderCount ) {\r
+                               StringBuilder dt = new StringBuilder(r.getDetail());\r
+                               StringBuilder rf = new StringBuilder();\r
+                               StringBuilder wp = new StringBuilder();\r
+                               StringBuilder rp = new StringBuilder();\r
+                               for ( int i=0; i<recFolderCount; i++ ) {\r
+                                       dt.append("■[1SEG] 録画フォルダ・出力PlugIn・ファイル名PlugIn(");\r
+                                       dt.append(String.valueOf(i+1));\r
+                                       dt.append("):");\r
+                                       dt.append(pRecFolder.get(i));\r
+                                       dt.append(" / ");\r
+                                       dt.append(pWritePlugIn.get(i));\r
+                                       dt.append(" / ");\r
+                                       dt.append(pRecNamePlugIn.get(i));\r
+                                       dt.append("\n");\r
+                                       \r
+                                       rf.append(pRecFolder.get(i));\r
+                                       rf.append("\t");\r
+                                       \r
+                                       wp.append(pWritePlugIn.get(i));\r
+                                       wp.append("\t");\r
+                                       \r
+                                       rp.append(pRecNamePlugIn.get(i));\r
+                                       rp.append("\t");\r
+                               }\r
+                               r.setDetail(dt.toString());\r
+                               r.getHidden_params().put(RETVAL_KEY_P_RECFOLDER, rf.toString());\r
+                               r.getHidden_params().put(RETVAL_KEY_P_WRITEPLUGIN, wp.toString());\r
+                               r.getHidden_params().put(RETVAL_KEY_P_RECNAMEPLUGIN, rp.toString());\r
+                       }\r
+                       \r
+                       // 予定ファイル名\r
+                       if ( recFileNameList.size() > 0 ) {\r
+                               StringBuilder dt = new StringBuilder(r.getDetail());\r
+                               for ( int i=0; i<recFileNameList.size(); i++ ) {\r
+                                       dt.append("■予定ファイル名(");\r
+                                       dt.append(String.valueOf(i+1));\r
+                                       dt.append("):");\r
+                                       dt.append(recFileNameList.get(i));\r
+                               }\r
+                               dt.append("\n");\r
+                               r.setDetail(dt.toString());\r
+                       }\r
+                       \r
+                       // 番組ID\r
+                       if ( evid != 0 && evid != 65535 ) {\r
+                               StringBuilder dt = new StringBuilder(r.getDetail());\r
+                               dt.append("■番組ID:");\r
+                               dt.append(contentid);\r
+                               dt.append("\n");\r
+                               r.setDetail(dt.toString());\r
+                       }\r
+                       \r
+                       /*\r
+                        *  既存予約一覧からの情報引き継ぎ\r
+                        */\r
+                       //XcopyAttributes(r, getReserves());\r
+                       \r
+                       /*\r
+                        * 追加\r
+                        */\r
+                       newReserveList.add(r);\r
+               }\r
+               \r
+               return true;\r
+       }\r
+\r
+       private boolean GetRdReservedDetailByContentId(ReserveList r, int cnt) {\r
+               String pstr = genPoststrEPGA(r);\r
+               String url = "http://"+getIPAddr()+":"+getPortNo()+"/epginfo.html?"+pstr;\r
+               System.out.println("URL: "+url);\r
+               return _GetRdReservedDetail(r, url, cnt);\r
+       }\r
+       private boolean GetRdReservedDetailByReserveId(ReserveList r, int cnt) {\r
+               String url = "http://"+getIPAddr()+":"+getPortNo()+"/reserveinfo.html?id="+getRsvId(r.getId());\r
+               System.out.println("URL: "+url);\r
+               return _GetRdReservedDetail(r, url, cnt);\r
+       }\r
+       private boolean _GetRdReservedDetail(ReserveList r, String url, int cnt) {\r
+               \r
+               reportProgress("+予約詳細を取得します("+cnt+")");\r
+\r
+               String[] d = reqGET(url,null);\r
+               //String hdr = d[0];\r
+               String res = d[1];\r
+               Matcher mb = null;\r
+               \r
+               if (res == null) {\r
+                       return false;\r
+               }\r
+               \r
+               try {\r
+                       // 予約ID\r
+                       mb = Pattern.compile("<form method=\"POST\" action=\"reservedel\\.html\\?id=(\\d+?)\">").matcher(res);\r
+                       if ( mb.find() ) {\r
+                               r.setId(getUniqId(mb.group(1)));\r
+                       }\r
+                       \r
+                       // イベントIDと予約方式\r
+                       int evid = -1;\r
+                       mb = Pattern.compile("EventID:\\d+\\(0x(.+)\\)").matcher(res);\r
+                       if ( mb.find() ) {\r
+                               evid = Integer.decode("0X"+mb.group(1));\r
+                               setTextRecType(r,ITEM_REC_TYPE_EPG);\r
+                       }\r
+                       else {\r
+                               setTextRecType(r,ITEM_REC_TYPE_PROG);\r
+                       }\r
+\r
+                       // EPG予約場合のみの処理\r
+                       if ( getTextRecType(r).equals(ITEM_REC_TYPE_EPG) ) {\r
+                               // 放送局\r
+                               int onid = -1;\r
+                               mb = Pattern.compile("OriginalNetworkID:\\d+\\(0x(.+)\\)").matcher(res);\r
+                               if ( mb.find() ) {\r
+                                       onid = Integer.decode("0X"+mb.group(1));\r
+                               }\r
+                               int tsid = -1;\r
+                               mb = Pattern.compile("TransportStreamID:\\d+\\(0x(.+)\\)").matcher(res);\r
+                               if ( mb.find() ) {\r
+                                       tsid = Integer.decode("0X"+mb.group(1));\r
+                               }\r
+                               int sid = -1;\r
+                               mb = Pattern.compile("ServiceID:\\d+\\(0x(.+)\\)").matcher(res);\r
+                               if ( mb.find() ) {\r
+                                       sid = Integer.decode("0X"+mb.group(1));\r
+                               }\r
+                               if ( onid == -1 || tsid == -1 || sid == -1 ) {\r
+                                       System.err.println("放送局IDが取得できませんでした: "+url);\r
+                               }\r
+                               else {\r
+                                       String chid = String.valueOf(Long.decode("0x"+ContentIdEDCB.getChId(onid,tsid,sid)));\r
+                                       r.setCh_name(cc.getCH_CODE2WEB(chid));\r
+                                       r.setChannel(chid);\r
+                                       \r
+                                       r.setContentId(ContentIdEDCB.getContentId(onid,tsid,sid,evid));\r
+                               }\r
+                               \r
+                               // (1-X) 開始・終了日時と、番組詳細、タイトル\r
+                               // ★一覧の開始・終了日時は、登録した際の開始・終了日時のままなので詳細を参照して修正が必要\r
+                               mb = Pattern.compile("<HR>番組情報<HR>\\s*(\\d\\d\\d\\d/\\d\\d/\\d\\d\\(.\\)) (\\d\\d):(\\d\\d)~(\\d\\d):(\\d\\d)<BR>.+?<BR>\\s*(.*?)<BR>.*?<BR>\\s*(.+?)\\s*<HR>録画設定<HR>",Pattern.DOTALL).matcher(res);\r
+                               if ( mb.find() ) {\r
+                                       GregorianCalendar cal = CommonUtils.getCalendar(mb.group(1));\r
+                                       r.setRec_pattern(CommonUtils.getDate(cal));\r
+                                       r.setRec_pattern_id(11);\r
+                                       \r
+                                       r.setStartDateTime(CommonUtils.getDateTime(cal));\r
+                                       r.setRec_nextdate(r.getStartDateTime());\r
+                                       r.setAhh(mb.group(2));\r
+                                       r.setAmm(mb.group(3));\r
+                                       r.setZhh(mb.group(4));\r
+                                       r.setZmm(mb.group(5));\r
+                                       \r
+                                       r.setRec_nextdate(CommonUtils.getNextDate(r));\r
+                                       r.setRec_min(CommonUtils.getRecMin(r.getAhh(), r.getAmm(), r.getZhh(), r.getZmm()));\r
+                                       getStartEndDateTime(r);\r
+                                       \r
+                                       r.setTitle(CommonUtils.unEscape(mb.group(6)));\r
+                                       r.setTitlePop(TraceProgram.replacePop(r.getTitle()));\r
+                                       \r
+                                       r.setDetail(CommonUtils.unEscape(mb.group(7)).replaceAll("<BR>", ""));\r
+                               }\r
+                       }\r
+               }\r
+               catch ( Exception e ) {\r
+                       e.printStackTrace();\r
+               }\r
+               \r
+               // (1-1)プリセット\r
+               setTextPresetID(r, getSelectedSetting("presetID",res));\r
+               \r
+               // (1-3)録画モード\r
+               {\r
+                       String rec_mode = getSelectedSetting("recMode",res);\r
+                       if ( rec_mode.equals(ITEM_REC_MODE_DISABLE) ) {\r
+                               // "無効"は「予約実行」で扱うので\r
+                               r.setExec(false);\r
+                               setTextRecMode(r,getListRecMode().get(0).getText());\r
+                               // チューナー不足警告(リスト取得時に立ててるので落とす)\r
+                               r.setTunershort(false);\r
+                       }\r
+                       else {\r
+                               setTextRecMode(r,rec_mode);\r
+                       }\r
+               }\r
+               \r
+               // (1-4)エンコーダ\r
+               setTextTunerID(r, getSelectedSetting("tunerID",res));\r
+               \r
+               // (1-6)録画後動作\r
+               setTextSuspendMode(r, getSelectedSetting("suspendMode",res));\r
+               \r
+               // (1-10)追従\r
+               r.setPursues(ITEM_YES.equals(getSelectedSetting("tuijyuuFlag",res)));\r
+               \r
+               // (1-11)ぴったり\r
+               setTextPittariFlag(r, getSelectedSetting("pittariFlag",res));\r
+               \r
+               // (1-12)録画優先度\r
+               setTextPriority(r, getSelectedSetting("priority",res));\r
+               \r
+               // (1-13)連続録画動作\r
+               if ( getCheckedSetting("continueRecFlag", res) != null ) {\r
+                       setTextContinueRecFlag(r, ITEM_YES);\r
+               }\r
+               else {\r
+                       setTextContinueRecFlag(r, ITEM_NO);\r
+               }\r
+               \r
+               // (1-14)復帰後再起動\r
+               if ( getCheckedSetting("rebootFlag", res) != null ) {\r
+                       setTextRebootFlag(r, ITEM_YES);\r
+               }\r
+               else {\r
+                       setTextRebootFlag(r, ITEM_NO);\r
+               }\r
+               \r
+               // (1-15) 指定サービス対象データ\r
+               if ( getCheckedSetting("serviceMode", res) != null ) {\r
+                       setTextServiceMode(r, ITEM_SERVMOCE_DEFAULT);\r
+               }\r
+               else {\r
+                       boolean b1 = "0".equals(getCheckedSetting("serviceMode_1", res));\r
+                       boolean b2 = "0".equals(getCheckedSetting("serviceMode_2", res));\r
+                       if ( b1 && b2 ) {\r
+                               setTextServiceMode(r, ITEM_SERVMOCE_SUBTCARO);\r
+                       }\r
+                       else if ( b1 ) {\r
+                               setTextServiceMode(r, ITEM_SERVMOCE_SUBT);\r
+                       }\r
+                       else if ( b2 ) {\r
+                               setTextServiceMode(r, ITEM_SERVMOCE_CARO);\r
+                       }\r
+                       else {\r
+                               setTextServiceMode(r, ITEM_SERVMOCE_NONE);\r
+                       }\r
+               }\r
+               \r
+               // (1-16) 録画マージン\r
+               {\r
+                       if ( getCheckedSetting("useDefMargineFlag", res) != null ) {\r
+                               setTextStartMargine(r, ITEM_MARGINE_DEFAULT);\r
+                               setTextEndMargine(r, ITEM_MARGINE_DEFAULT);\r
+                       }\r
+                       else {\r
+                               setTextStartMargine(r, getEditedSetting("startMargine", res));\r
+                               setTextEndMargine(r, getEditedSetting("endMargine", res));\r
+                       }\r
+               }\r
+               \r
+               // (1-17) 別ファイルに同時出力する\r
+               if ( getCheckedSetting("partialRecFlag", res) != null ) {\r
+                       setTextPartialRecFlag(r, ITEM_YES);\r
+               }\r
+               else {\r
+                       setTextPartialRecFlag(r, ITEM_NO);\r
+               }\r
+               \r
+               String batFilePath = null;\r
+               ArrayList<String> rRecFolder = new ArrayList<String>();\r
+               ArrayList<String> rWritePlugIn = new ArrayList<String>();\r
+               ArrayList<String> rRecNamePlugIn = new ArrayList<String>();\r
+               ArrayList<String> pRecFolder = new ArrayList<String>();\r
+               ArrayList<String> pWritePlugIn = new ArrayList<String>();\r
+               ArrayList<String> pRecNamePlugIn = new ArrayList<String>();\r
+               \r
+               // 録画後実行bat\r
+               mb = Pattern.compile("録画後実行bat.*?:\\s*(.*?)<BR>",Pattern.DOTALL).matcher(d[1]);\r
+               if ( mb.find() ) {\r
+                       batFilePath = mb.group(1);\r
+               }\r
+               \r
+               // オプショナル\r
+               {\r
+                       // 録画フォルダ等\r
+                       Matcher mc = Pattern.compile("録画フォルダ.*?(<TABLE.*?</TABLE>)\\s*<input type=hidden name=\"recFolderCount\" value=\"(\\d+?)\">",Pattern.DOTALL).matcher(d[1]);\r
+                       if ( ! mc.find() ) {\r
+                               errmsg = "情報が見つかりません:"+RETVAL_KEY_RECFOLDERCOUNT;\r
+                       }\r
+                       else {\r
+                               r.getHidden_params().put(RETVAL_KEY_RECFOLDERCOUNT, mc.group(2));\r
+                               \r
+                               Matcher md = Pattern.compile("<TR>(.*?)</TR>",Pattern.DOTALL).matcher(mc.group(1));\r
+                               int idx = 0;\r
+                               while ( md.find() ) {\r
+                                       if ( idx++ == 0 ) {\r
+                                               continue;\r
+                                       }\r
+                                       Matcher me = Pattern.compile("<TD>(.*?)</TD>",Pattern.DOTALL).matcher(md.group(1));\r
+                                       for ( int i=0; i<3 && me.find(); i++ ) {\r
+                                               switch (i) {\r
+                                               case 0:\r
+                                                       rRecFolder.add(me.group(1));\r
+                                                       break;\r
+                                               case 1:\r
+                                                       rWritePlugIn.add(me.group(1));\r
+                                                       break;\r
+                                               case 2:\r
+                                                       rRecNamePlugIn.add(me.group(1));\r
+                                                       break;\r
+                                               }\r
+                                       }\r
+                               }\r
+                       }\r
+                       \r
+                       // 録画フォルダ等(ワンセグ)\r
+                       mc = Pattern.compile("partialRecFlag.*?(<TABLE.*?</TABLE>)\\s*<input type=hidden name=\"partialFolderCount\" value=\"(\\d+?)\">",Pattern.DOTALL).matcher(d[1]);\r
+                       if ( ! mc.find() ) {\r
+                               errmsg = "情報が見つかりません:"+RETVAL_KEY_PARTIALFOLDERCOUNT;\r
+                       }\r
+                       else {\r
+                               r.getHidden_params().put(RETVAL_KEY_PARTIALFOLDERCOUNT, mc.group(2));\r
+                               \r
+                               \r
+                               Matcher md = Pattern.compile("<TR>(.*?)</TR>",Pattern.DOTALL).matcher(mc.group(1));\r
+                               int idx = 0;\r
+                               while ( md.find() ) {\r
+                                       if ( idx++ == 0 ) {\r
+                                               continue;\r
+                                       }\r
+                                       Matcher me = Pattern.compile("<TD>(.*?)</TD>",Pattern.DOTALL).matcher(md.group(1));\r
+                                       for ( int i=0; i<3 && me.find(); i++ ) {\r
+                                               switch (i) {\r
+                                               case 0:\r
+                                                       pRecFolder.add(me.group(1));\r
+                                                       break;\r
+                                               case 1:\r
+                                                       pWritePlugIn.add(me.group(1));\r
+                                                       break;\r
+                                               case 2:\r
+                                                       pRecNamePlugIn.add(me.group(1));\r
+                                                       break;\r
+                                               }\r
+                                       }\r
+                               }\r
+                       }\r
+               }\r
+               \r
+               {\r
+                       StringBuilder dt = new StringBuilder("■予約状況:");\r
+                       if ( r.getContentId() != null ) {\r
+                               dt.append("EPG予約(自動手動不明→予約一覧再取得を行ってください)");\r
+                       }\r
+                       else {\r
+                               dt.append("プログラム予約");\r
+                       }\r
+                       dt.append("\n");\r
+                       r.setDetail(dt.toString());\r
+               }\r
+               \r
+               if ( batFilePath != null && batFilePath.length() > 0 ) {\r
+                       StringBuilder dt = new StringBuilder(r.getDetail());\r
+                       dt.append("■録画後実行bat:");\r
+                       dt.append(batFilePath);\r
+                       dt.append("\n");\r
+                       r.setDetail(dt.toString());\r
+               }\r
+               \r
+               if ( rRecFolder.size() > 0 ) {\r
+                       StringBuilder dt = new StringBuilder(r.getDetail());\r
+                       StringBuilder rf = new StringBuilder();\r
+                       StringBuilder wp = new StringBuilder();\r
+                       StringBuilder rp = new StringBuilder();\r
+                       for ( int i=0; i<rRecFolder.size(); i++ ) {\r
+                               dt.append("■録画フォルダ・出力PlugIn・ファイル名PlugIn(");\r
+                               dt.append(String.valueOf(i+1));\r
+                               dt.append("):");\r
+                               dt.append(rRecFolder.get(i));\r
+                               dt.append(" / ");\r
+                               dt.append(rWritePlugIn.get(i));\r
+                               dt.append(" / ");\r
+                               dt.append(rRecNamePlugIn.get(i));\r
+                               dt.append("\n");\r
+                               \r
+                               rf.append(rRecFolder.get(i));\r
+                               rf.append("\t");\r
+                               \r
+                               wp.append(rWritePlugIn.get(i));\r
+                               wp.append("\t");\r
+                               \r
+                               rp.append(rRecNamePlugIn.get(i));\r
+                               rp.append("\t");\r
+                       }\r
+                       r.setDetail(dt.toString());\r
+                       r.getHidden_params().put(RETVAL_KEY_R_RECFOLDER, rf.toString());\r
+                       r.getHidden_params().put(RETVAL_KEY_R_WRITEPLUGIN, wp.toString());\r
+                       r.getHidden_params().put(RETVAL_KEY_R_RECNAMEPLUGIN, rp.toString());\r
+               }\r
+               \r
+               if ( pRecFolder.size() > 0 ) {\r
+                       StringBuilder dt = new StringBuilder(r.getDetail());\r
+                       StringBuilder rf = new StringBuilder();\r
+                       StringBuilder wp = new StringBuilder();\r
+                       StringBuilder rp = new StringBuilder();\r
+                       for ( int i=0; i<rRecFolder.size(); i++ ) {\r
+                               dt.append("■[1SEG] 録画フォルダ・出力PlugIn・ファイル名PlugIn(");\r
+                               dt.append(String.valueOf(i+1));\r
+                               dt.append("):");\r
+                               dt.append(pRecFolder.get(i));\r
+                               dt.append(" / ");\r
+                               dt.append(pWritePlugIn.get(i));\r
+                               dt.append(" / ");\r
+                               dt.append(pRecNamePlugIn.get(i));\r
+                               dt.append("\n");\r
+                               \r
+                               rf.append(pRecFolder.get(i));\r
+                               rf.append("\t");\r
+                               \r
+                               wp.append(pWritePlugIn.get(i));\r
+                               wp.append("\t");\r
+                               \r
+                               rp.append(pRecNamePlugIn.get(i));\r
+                               rp.append("\t");\r
+                       }\r
+                       r.setDetail(dt.toString());\r
+                       r.getHidden_params().put(RETVAL_KEY_P_RECFOLDER, rf.toString());\r
+                       r.getHidden_params().put(RETVAL_KEY_P_WRITEPLUGIN, wp.toString());\r
+                       r.getHidden_params().put(RETVAL_KEY_P_RECNAMEPLUGIN, rp.toString());\r
+               }\r
+               \r
+               // 番組ID\r
+               if ( r.getContentId() != null ) {\r
+                       StringBuilder dt = new StringBuilder(r.getDetail());\r
+                       dt.append("■番組ID:");\r
+                       dt.append(ContentIdEDCB.stripMark(r.getContentId()));\r
+                       dt.append("\n");\r
+                       r.setDetail(dt.toString());\r
+               }\r
+\r
+               return true;\r
+       }\r
+       private String getSelectedSetting(String key, String res) {\r
+               Matcher mb = Pattern.compile("<select name=\""+key+"\">[\\s\\S]*?<option value=\"([^\"]+?)\"\\s*selected>(.+?)\n").matcher(res);\r
+               if (mb.find()) {\r
+                       return mb.group(2);\r
+               }\r
+               return null;\r
+       }\r
+       private String getEditedSetting(String key, String res) {\r
+               Matcher mb = Pattern.compile("<input type=text name=\""+key+"\" value=\"([^\"]+?)\">").matcher(res);\r
+               if (mb.find()) {\r
+                       return mb.group(1);\r
+               }\r
+               return null;\r
+       }\r
+       private String getCheckedSetting(String key, String res) {\r
+               Matcher mb = Pattern.compile("<input type=checkbox name=\""+key+"\" value=\"([^\"]+?)\" checked>").matcher(res);\r
+               if (mb.find()) {\r
+                       return mb.group(1);\r
+               }\r
+               return null;\r
+       }\r
+       \r
+       /**\r
+        *      録画結果一覧を取得する\r
+        */\r
+       \r
+       private boolean GetRdRecordedList(ArrayList<RecordedInfo> newRecordedList) {\r
+               \r
+               String critDate = null;\r
+               if ( newRecordedList.size() > 0 ) {\r
+                       // 最新の情報の前日分までチェックする\r
+                       GregorianCalendar cal = CommonUtils.getCalendar(newRecordedList.get(0).getDate());\r
+                       cal.add(Calendar.DATE, -1);\r
+                       critDate = CommonUtils.getDate(cal);\r
+               }\r
+               else {\r
+                       // 既存情報が無ければ上限まで\r
+                       critDate = CommonUtils.getDate(CommonUtils.getCalendar(-86400*getRecordedSaveScope()));\r
+               }\r
+               \r
+               if (getDebug()) System.out.println(DBGID+"録画結果の取り込みはここまで: "+critDate);\r
+               \r
+               // RDから予約一覧を取り出す\r
+               String response="";\r
+               {\r
+                       reportProgress("録画結果一覧を取得します.(1)");\r
+                       String[] d = reqGET("http://"+getIPAddr()+":"+getPortNo()+"/recinfo.html",null);\r
+                       response = d[1];\r
+                       \r
+                       if (response == null) {\r
+                               errmsg = "レコーダーが反応しません";\r
+                               return false;\r
+                       }\r
+               }\r
+\r
+               // 30件を超えるとページが増える\r
+               int maxpage = 0;\r
+               Matcher ma = Pattern.compile("\"recinfo.html\\?page=(\\d+)\"").matcher(response);\r
+               while ( ma.find() ) {\r
+                       int page = Integer.valueOf(ma.group(1));\r
+                       if ( maxpage < page ) {\r
+                               maxpage = page;\r
+                       }\r
+               }\r
+               \r
+               for ( int page = 0; page<=maxpage; page++ ) {\r
+                       if ( page > 0 ) {\r
+                               reportProgress(String.format("録画結果一覧を取得します.(%d)",page+1));\r
+                               String[] d = reqGET("http://"+getIPAddr()+":"+getPortNo()+"/recinfo.html?page="+page,null,thisEncoding);\r
+                               response = d[1];\r
+                       }\r
+                       if ( GetRdRecordedListSub(newRecordedList, response, critDate) <= 0) {\r
+                               // おわったぽ\r
+                               break;\r
+                       }\r
+               }\r
+\r
+               return true;\r
+       }\r
+       \r
+       private int GetRdRecordedListSub(ArrayList<RecordedInfo> newRecordedList, String response, String critDate) {\r
+               \r
+               int cnt = 0;\r
+               \r
+               //\r
+               Matcher mc = Pattern.compile("<TABLE BORDER=\"1\">(.+?)</TABLE>",Pattern.DOTALL).matcher(response);\r
+               if (mc.find()) {\r
+                       Matcher ma = Pattern.compile("<TR( BGCOLOR=#......)?><TD>(\\d\\d\\d\\d/\\d\\d/\\d\\d)\\(.\\) (\\d\\d):(\\d\\d)~(\\d\\d):(\\d\\d)</TD><TD>(.+?)</TD>.*?<A HREF=\"recinfodesc\\.html\\?id=(\\d+?)\">詳細</A></TD></TR>\\s*?<TR( BGCOLOR=#......)?><TD COLSPAN=\"2\">(.*?)</TD></TR>\\s*?<TR( BGCOLOR=#......)?><TD COLSPAN=\"2\">(.*?)</TD></TR>",Pattern.DOTALL).matcher(mc.group(1));\r
+                       while ( ma.find() ) {\r
+                               \r
+                               // 日付を調べる\r
+                               GregorianCalendar cal = CommonUtils.getCalendar(ma.group(2));\r
+                               String date = CommonUtils.getDate(cal);\r
+                               \r
+                               // 既存の情報に追いついたので取得終了\r
+                               if ( date.compareTo(critDate) < 0 ) {\r
+                                       return -1;\r
+                               }\r
+                               \r
+                               // 個々のデータを取り出す\r
+                               RecordedInfo entry = new RecordedInfo();\r
+\r
+                               entry.setDate(date);\r
+                               \r
+                               entry.setAhh(ma.group(3));\r
+                               entry.setAmm(ma.group(4));\r
+                               entry.setZhh(ma.group(5));\r
+                               entry.setZmm(ma.group(6));\r
+                               \r
+                               long lenL = CommonUtils.getCompareDateTime(entry.getDate()+" "+entry.getZhh()+":"+entry.getZmm(), entry.getDate()+" "+entry.getAhh()+":"+entry.getAmm());\r
+                               if ( lenL < 0 ) {\r
+                                       lenL += 86400000;\r
+                               }\r
+                               int len =  (int) (lenL / 60000L);\r
+                               entry.setLength(len);\r
+                               \r
+                               // 放送局(仮)\r
+                               String recChName = ma.group(7);\r
+                               String chid = cc.getCH_REC2CODE(recChName);\r
+                               if ( chid == null ) {\r
+                                       // 難視聴対策局対策\r
+                                       String nan = TEXT_NANSHICHO_HEADER+recChName;\r
+                                       chid = cc.getCH_REC2CODE(nan);\r
+                                       if ( chid != null ) {\r
+                                               recChName = nan;\r
+                                       }\r
+                               }\r
+                               \r
+                               if ( chid == null ) {\r
+                                       // CHコードにできなければ、HTMLから取得した放送局名をそのまま使う\r
+                                       entry.setChannel(null);\r
+                                       entry.setCh_name(recChName);\r
+                               }\r
+                               else {\r
+                                       entry.setChannel(chid);\r
+                                       String ch_name = cc.getCH_CODE2WEB(chid);\r
+                                       if ( ch_name == null ) {\r
+                                               // CHコード設定がうまくないようですよ?\r
+                                               entry.setCh_name(ma.group(7));\r
+                                       }\r
+                                       else {\r
+                                               entry.setCh_name(ch_name);\r
+                                       }\r
+                               }\r
+                               \r
+                               entry.setCh_orig(recChName);\r
+                               \r
+                               // ID\r
+                               entry.setId(ma.group(8));\r
+                               \r
+                               // 予約名\r
+                               entry.setTitle(CommonUtils.unEscape(ma.group(10)).replaceAll("<BR>", ""));\r
+                               \r
+                               // 録画結果\r
+                               entry.setResult(ma.group(12));\r
+                               entry.setSucceeded(entry.getResult().matches("^(録画終了|開始時間が変更されました)$"));\r
+                               \r
+                               // その他\r
+                               \r
+                               addRecorded(newRecordedList, entry);\r
+                               \r
+                               ++cnt;\r
+                       }\r
+               }\r
+               \r
+               return cnt;\r
+       }\r
+\r
+       private boolean GetRdRecordedListDetailAll(ArrayList<RecordedInfo> newRecordedList) {\r
+               // 詳細情報を取得する\r
+               int i=0;\r
+               for ( RecordedInfo entry : newRecordedList ) {\r
+                       \r
+                       if ( entry.getId() == null ) {\r
+                               // 過去ログかな…?\r
+                               continue;\r
+                       }\r
+                       \r
+                       ++i;\r
+                       \r
+                       reportProgress("+録画結果詳細を取得します("+i+")");\r
+                       \r
+                       String response="";\r
+                       {\r
+                               String[] d = reqGET("http://"+getIPAddr()+":"+getPortNo()+"/recinfodesc.html?id="+entry.getId(),null);\r
+                               response = d[1];\r
+                               \r
+                               if (response == null) {\r
+                                       errmsg = "レコーダーが反応しません";\r
+                                       return false;\r
+                               }\r
+                       }\r
+                       \r
+                       Matcher ma = Pattern.compile("ドロップ:(\\d+)", Pattern.DOTALL).matcher(response);\r
+                       if ( ma.find() ) {\r
+                               entry.setDrop(Integer.valueOf(ma.group(1)));\r
+                       }\r
+                       \r
+                       ma = Pattern.compile("Drop:\\s*(\\d+)\\s*Scramble:\\s*(\\d+)\\s*MPEG2", Pattern.DOTALL).matcher(response);\r
+                       while ( ma.find() ) {\r
+                               entry.setDrop_mpeg(entry.getDrop_mpeg()+Integer.valueOf(ma.group(1)));\r
+                       }\r
+                       \r
+                       ma = Pattern.compile("(<HR>番組情報<HR>.*</PRE>)", Pattern.DOTALL).matcher(response);\r
+                       if ( ma.find() ) {\r
+                               entry.setDetail(CommonUtils.unEscape(ma.group(1).replaceAll("<BR>", "").replaceAll("</?PRE>", "").replaceAll("<HR>", "====").replaceAll("\n\n+", "\n\n")/*.replaceFirst("^([\\s\\S]*?)(====エラーログ====[\\s\\S]*?)$","$2\n\n$1")*/));\r
+                       }\r
+                       \r
+                       // 放送局(訂正)\r
+                       Matcher md = Pattern.compile("OriginalNetworkID:(\\d+).*?TransportStreamID:(\\d+).*?ServiceID:(\\d+)",Pattern.DOTALL).matcher(response);\r
+                       if ( md.find() ) {\r
+                               // 詳細情報からCHコードが取得できるなら\r
+                               String chid = String.valueOf(Long.decode("0x"+ContentIdEDCB.getChId(Integer.valueOf(md.group(1)),Integer.valueOf(md.group(2)),Integer.valueOf(md.group(3)))));\r
+                               entry.setChannel(chid);\r
+                               String ch_name = cc.getCH_CODE2WEB(chid);\r
+                               if ( ch_name != null ) {\r
+                                       entry.setCh_name(ch_name);\r
+                               }\r
+                       }\r
+                       \r
+\r
+               }\r
+               return true;\r
+       }\r
+\r
+       \r
+       /*******************************************************************************\r
+        * 新規予約\r
+        ******************************************************************************/\r
+       \r
+       @Override\r
+       public boolean PostRdEntry(ReserveList reqr)\r
+       {\r
+               \r
+               ArrayList<ReserveList> tmprl = getReserves();\r
+               \r
+               boolean b = _PostRdEntry(reqr);\r
+               \r
+               // 予約一覧が更新されていたら、本体から取得できない情報は引き継ぐ\r
+               if ( getReserves() != tmprl ) {\r
+                       copyAttributesAllList(tmprl, getReserves());\r
+               }\r
+               \r
+               // 成功しても失敗してもキャッシュが更新されている可能性があるので保存し直す\r
+               ReservesToFile(getReserves(), rsvedFile);\r
+               setRecordedFlag();\r
+               \r
+               return b;\r
+       }\r
+       private boolean _PostRdEntry(ReserveList reqr) \r
+       {\r
+               //\r
+               System.out.println("Run: PostRdEntry("+reqr.getTitle()+")");\r
+               \r
+               errmsg = "";\r
+\r
+               // 放送局と日付のチェック\r
+               if ( ! PrePostCheck(reqr) ) {\r
+                       return(false);\r
+               }\r
+               \r
+               // プリセット\r
+               if ( reqr.getRec_audio().equals(ITEM_PRESETID_REG) ) {\r
+                       errmsg = "新規予約では次の指定はできません:プリセット="+ITEM_PRESETID_REG;\r
+                       return false;\r
+               }\r
+               \r
+               // おまじない\r
+               Authenticator.setDefault(new MyAuthenticator(getUser(), getPasswd()));\r
+               \r
+               String header;\r
+               String response;\r
+               \r
+               int onid = -1;\r
+               int tsid = -1;\r
+               int sid = -1;\r
+               int evid = -1;\r
+               \r
+               boolean rec_type_epg = true;\r
+               if ( getTextRecType(reqr) != null && getTextRecType(reqr).equals(ITEM_REC_TYPE_EPG) ) {\r
+                       if ( ContentIdEDCB.isValid(reqr.getContentId()) ) {\r
+                               ContentIdEDCB.decodeContentId(reqr.getContentId());\r
+                               onid = ContentIdEDCB.getOnId();\r
+                               tsid = ContentIdEDCB.getTSId();\r
+                               sid = ContentIdEDCB.getSId();\r
+                               evid = ContentIdEDCB.getEvId();\r
+                       }\r
+                       else if ( ContentIdDIMORA.isValid(reqr.getContentId()) ) {\r
+                               ContentIdDIMORA.decodeContentId(reqr.getContentId());\r
+                               evid = ContentIdDIMORA.getEvId();\r
+                               // 後続処理あり\r
+                       }\r
+                       else if ( ContentIdREGZA.isValid(reqr.getContentId()) ) {\r
+                               ContentIdREGZA.decodeContentId(reqr.getContentId());\r
+                               evid = ContentIdREGZA.getEvId();\r
+                               // 後続処理あり\r
+                       }\r
+                       else {\r
+                               errmsg = "番組表に予約IDがないためEPG予約は利用できません。EDCB番組表を利用するかプログラム予約を行ってください。";\r
+                               return false;\r
+                       }\r
+                       \r
+                       if ( evid != -1 && onid == -1 ) {\r
+                               // Dimora、REGZA形式の場合 onid と tsid が取得できないので自前の情報で補完\r
+                               ContentIdEDCB.decodeChId(ContentIdEDCB.getChId(cc.getCH_WEB2CODE(reqr.getCh_name())));\r
+                               onid = ContentIdEDCB.getOnId();\r
+                               tsid = ContentIdEDCB.getTSId();\r
+                               sid = ContentIdEDCB.getSId();\r
+                               // EDCB形式で置き換え\r
+                               reqr.setContentId(ContentIdEDCB.getContentId(onid, tsid, sid, evid));\r
+                       }\r
+               }\r
+               else {\r
+                       rec_type_epg = false;\r
+               }\r
+               \r
+               ReserveList newr = null;\r
+               \r
+               // 予約IDが含まれていたら削る\r
+               reqr.setId("");\r
+               \r
+               /*\r
+                *  EPG予約\r
+                */\r
+               if ( rec_type_epg ) {\r
+                       \r
+                       int cntMax = 3;\r
+                       int cnt = 1;\r
+                       \r
+                       \r
+                       // 番組情報ページを開く\r
+                       {\r
+                               reportProgress(String.format("EPG予約を実行します(%d/%d).",cnt++,cntMax));\r
+                               \r
+                               ReserveList tmpr = new ReserveList();\r
+                               tmpr.setContentId(reqr.getContentId());\r
+                               setTextPresetID(tmpr, getTextPresetID(reqr));\r
+                               if ( ! GetRdReservedDetailByContentId(tmpr,0) ) {\r
+                                       errmsg = "予約ページが開けません。";\r
+                                       return(false);\r
+                               }\r
+                               \r
+                               if ( tmpr.getId() != null && tmpr.getId().length() > 0 ) {\r
+                                       System.out.println("重複予約が実行されます.");\r
+                               }\r
+                       }\r
+                       \r
+                       // 予約登録を実行する\r
+                       {               \r
+                               reportProgress(String.format("追加を実行します(%d/%d).",cnt++,cntMax));\r
+                               \r
+                               String pstr = genPoststrEPGB(reqr);\r
+                               String uri = "http://"+getIPAddr()+":"+getPortNo()+"/"+String.format("reserveadd.html?onid=%d&tsid=%d&sid=%d&evid=%d",onid,tsid,sid,evid);;\r
+                               \r
+                               System.err.println("URL: "+uri);\r
+                               \r
+                               header = response = null;\r
+                               for (int i=0; i<retryMax; i++) {\r
+                                       String[] d = reqPOST(uri, pstr, null);\r
+                                       header = d[0];\r
+                                       response = d[1];\r
+                                       if ( header != null && response == null ) {\r
+                                               reportProgress("コネクションがリセットされました。リトライします。");\r
+                                               CommonUtils.milSleep(1000);\r
+                                       }\r
+                                       else {\r
+                                               break;\r
+                                       }\r
+                               }\r
+                               if ( header == null || response == null ) {\r
+                                       errmsg = "レコーダーが反応しません。";\r
+                                       return(false);\r
+                               }\r
+                       }\r
+                       \r
+                       // EPG予約の場合はすぐわかる(はずだった→重複予約だとわからない)\r
+                       {\r
+                               reportProgress(String.format("新しい予約IDを取得します(%d/%d).",cnt++,cntMax));\r
+                               \r
+                               newr = getRsvOnEdcbTR(reqr);\r
+                               if ( newr == null ) {\r
+                                       return(false);\r
+                               }\r
+                               \r
+                               // 本体から取得できない情報は引き継ぐ\r
+                               copyAttributesMethod(reqr, newr);\r
+                               \r
+                               // 一発ヒットしたものはキャッシュ上にないので載せる\r
+                               if ( ! getReserves().contains(newr) ) {\r
+                                       getReserves().add(newr);\r
+                               }\r
+                               \r
+                               reportProgress("+新しい予約ID: "+newr.getId());\r
+                               \r
+                               if ( isModified(reqr, newr) ) {\r
+                                       errmsg = "EDCB番組表からの情報で内容が変更されました: "+newr.getStartDateTime()+"~"+newr.getZhh()+":"+newr.getZmm()+" "+newr.getTitle();\r
+                               }\r
+                       }\r
+               }\r
+               \r
+               /*\r
+                * プログラム予約 \r
+                */\r
+               else {\r
+                       \r
+                       int cntMax = 3;\r
+                       int cnt = 1;\r
+                       \r
+                       // プログラム予約の制限のチェック\r
+                       if ( ! PreProgResCheck(reqr) ) {\r
+                               return(false);\r
+                       }\r
+                       \r
+                       // 番組IDが含まれていたら削る\r
+                       reqr.setContentId(null);\r
+                       \r
+                       // 予約情報の一部を確定する\r
+                       {               \r
+                               reportProgress(String.format("プログラム予約を実行します(%d/%d).",cnt++,cntMax));\r
+                               \r
+                               String pstr = genPoststrDTA(reqr);\r
+                               String uri = "http://"+getIPAddr()+":"+getPortNo()+"/addprogres.html";\r
+                               \r
+                               System.err.println("URL: "+uri+"?"+pstr);\r
+                               \r
+                               header = response = null;\r
+                               for (int i=0; i<retryMax; i++) {\r
+                                       String[] d = reqPOST(uri, pstr, null);\r
+                                       header = d[0];\r
+                                       response = d[1];\r
+                                       if ( header != null && response == null ) {\r
+                                               reportProgress("コネクションがリセットされました。リトライします。");\r
+                                               CommonUtils.milSleep(1000);\r
+                                       }\r
+                                       else {\r
+                                               break;\r
+                                       }\r
+                               }\r
+                               if ( header == null || response == null ) {\r
+                                       errmsg = "レコーダーが反応しません。";\r
+                                       return(false);\r
+                               }\r
+\r
+                               Matcher ma = Pattern.compile("<form method=\"POST\" action=\"reservepgadd.html\">").matcher(response);\r
+                               if ( ! ma.find() ) {\r
+                                       errmsg = "予約に失敗しました。";\r
+                                       return(false);\r
+                               }\r
+                               \r
+                               ma = Pattern.compile("<input type=hidden name=\"recFolderCount\" value=\"(\\d+?)\">").matcher(response);\r
+                               if ( ! ma.find() ) {\r
+                                       errmsg = "予約に失敗しました。";\r
+                                       return(false);\r
+                               }\r
+                               reqr.getHidden_params().put(RETVAL_KEY_RECFOLDERCOUNT, ma.group(1));\r
+                               \r
+                               ma = Pattern.compile("<input type=hidden name=\"partialFolderCount\" value=\"(\\d+?)\">").matcher(response);\r
+                               if ( ! ma.find() ) {\r
+                                       errmsg = "予約に失敗しました。";\r
+                                       return(false);\r
+                               }\r
+                               reqr.getHidden_params().put(RETVAL_KEY_PARTIALFOLDERCOUNT, ma.group(1));\r
+                       }\r
+                       \r
+                       // 予約登録を実行する\r
+                       {               \r
+                               reportProgress(String.format("追加を実行します(%d/%d).",cnt++,cntMax));\r
+                               \r
+                               String pstr = genPoststrDTB(reqr);\r
+                               String uri = "http://"+getIPAddr()+":"+getPortNo()+"/reservepgadd.html";\r
+                               \r
+                               System.err.println("URL: "+uri+"?"+pstr);\r
+                               \r
+                               header = response = null;\r
+                               for (int i=0; i<retryMax; i++) {\r
+                                       String[] d = reqPOST(uri, pstr, null);\r
+                                       header = d[0];\r
+                                       response = d[1];\r
+                                       if ( header != null && response == null ) {\r
+                                               reportProgress("コネクションがリセットされました。リトライします。");\r
+                                               CommonUtils.milSleep(1000);\r
+                                       }\r
+                                       else {\r
+                                               break;\r
+                                       }\r
+                               }\r
+                               if ( header == null || response == null ) {\r
+                                       errmsg = "レコーダーが反応しません。";\r
+                                       return(false);\r
+                               }\r
+                       }\r
+                       \r
+                       // EDCBに追加された予約IDを検索する ★EDCBは再起動すると予約IDが振り直しになるので必要!!\r
+                       {\r
+                               reportProgress(String.format("新しい予約IDを番号を取得します(%d/%d).",cnt++,cntMax));\r
+                               \r
+                               reqr.setId(""); // 予約番号未定のためダミー\r
+                               newr = getRsvOnEdcbDT(reqr);\r
+                               if ( newr == null ) {\r
+                                       errmsg = "予約IDがみつかりません。";\r
+                                       return(false);\r
+                               }\r
+                               \r
+                               // 本体から取得できない情報は引き継ぐ\r
+                               copyAttributesMethod(reqr, newr);\r
+\r
+                               // 一発ヒットしたものはキャッシュ上にないので載せる\r
+                               if ( ! getReserves().contains(newr) ) {\r
+                                       getReserves().add(newr);\r
+                               }\r
+                               \r
+                               reportProgress("+新しい予約ID: "+newr.getId());\r
+                       }\r
+               }\r
+               \r
+               System.out.printf("\n<<< Message from RD >>> \"%s\"\n\n", "正常に登録できました。");\r
+               \r
+               return(true);\r
+               \r
+               // 長いよ!分割しる!!\r
+       }\r
+\r
+       \r
+       /*******************************************************************************\r
+        * 予約更新\r
+        ******************************************************************************/\r
+       \r
+       /**\r
+        * 予約を更新する(入口)\r
+        */\r
+       @Override\r
+       public boolean UpdateRdEntry(ReserveList cacher, ReserveList reqr)\r
+       {\r
+               ArrayList<ReserveList> tmprl = getReserves();\r
+               \r
+               boolean b = _UpdateRdEntry(cacher, reqr);\r
+               \r
+               // 予約一覧が更新されていたら、本体から取得できない情報は引き継ぐ\r
+               if ( getReserves() != tmprl ) {\r
+                       copyAttributesAllList(tmprl, getReserves());\r
+               }\r
+               \r
+               // 成功しても失敗してもキャッシュが更新されている可能性があるので保存し直す\r
+               ReservesToFile(getReserves(), rsvedFile);\r
+               setRecordedFlag();\r
+               \r
+               return b;\r
+       }\r
+       \r
+       /**\r
+        * 予約を更新する(本体)\r
+        */\r
+       private boolean _UpdateRdEntry(ReserveList cacher, ReserveList reqr)\r
+       {\r
+               //\r
+               System.out.println("Run: UpdateRdEntry()");\r
+               \r
+               errmsg = "";\r
+               \r
+               // おまじない\r
+               Authenticator.setDefault(new MyAuthenticator(getUser(), getPasswd()));\r
+               \r
+               // 放送局と日付のチェック\r
+               if ( ! PrePostCheck(reqr) ) {\r
+                       return(false);\r
+               }\r
+               \r
+               if ( ! getTextRecType(cacher).equals(getTextRecType(reqr)) ) {\r
+                       errmsg = String.format("異なる予約方式への更新は行えません(%s->%s)",getTextRecType(cacher),getTextRecType(reqr));\r
+                       return false;\r
+               }\r
+               \r
+               boolean rec_type_epg = true;\r
+               if ( getTextRecType(reqr) != null && getTextRecType(reqr).equals(ITEM_REC_TYPE_EPG) ) {\r
+                       if ( ContentIdEDCB.isValid(reqr.getContentId()) ) {\r
+                               // 正しい番組IDのようですね\r
+                       }\r
+                       else {\r
+                               errmsg = "番組表に予約IDがないためEPG予約は利用できません。EDCB番組表を利用するかプログラム予約を行ってください。";\r
+                               return false;\r
+                       }\r
+               }\r
+               else {\r
+                       rec_type_epg = false;\r
+                       // 番組IDが含まれていたら削る\r
+                       reqr.setContentId(null);\r
+               }\r
+               \r
+               if ( rec_type_epg ) {\r
+                       // EPG予約は普通に更新\r
+                       if (getDebug()) System.err.println("[DEBUG] Update EPG RSV");\r
+                       return UpdateRdEntryTR(cacher,reqr);\r
+               }\r
+               else {\r
+                       \r
+                       // プログラム予約の制限のチェック\r
+                       if ( ! PreProgResCheck(reqr)) {\r
+                               return(false);\r
+                       }\r
+                       \r
+                       // プログラム予約\r
+                       if ( isEqualsDate(cacher, reqr) && cacher.getTitle().equals(reqr.getTitle()) ) {\r
+                               // 開始・終了日時とタイトルに変更がない場合は更新\r
+                               if (getDebug()) System.err.println("[DEBUG] Update PROG RSV");\r
+                               return UpdateRdEntryDT2(cacher,reqr);\r
+                       }\r
+                       else {\r
+                               // 開始・終了日時とタイトルに変更がある場合は削除して追加\r
+                               if (getDebug()) System.err.println("[DEBUG] Refresh PROG RSV");\r
+                               return UpdateRdEntryDT1(cacher,reqr);\r
+                       }\r
+               }\r
+       }\r
+       \r
+       /**\r
+        * EPG予約を更新する\r
+        */\r
+       private boolean UpdateRdEntryTR(ReserveList cacher, ReserveList reqr) {\r
+               \r
+               // EDCBで更新する\r
+               int cnt = 1;\r
+               int cntMax = 2;\r
+               \r
+               ReserveList oldr = null;        // 本体にあった既存の予約情報\r
+               ReserveList newr = null;        // 更新後に取得しなおした新しい予約情報\r
+               \r
+               // 予約IDの検索\r
+               {\r
+                       reportProgress(String.format("更新するEPG予約の予約IDを取得します(%d/%d).",cnt++,cntMax));\r
+                       \r
+                       // 予約IDを取得する\r
+                       oldr = getRsvOnEdcbTR(cacher);\r
+                       if ( oldr == null ) {\r
+                               return false;\r
+                       }\r
+                       \r
+                       reportProgress("+更新される予約ID: "+oldr.getId());\r
+               }\r
+               \r
+               // 予約更新\r
+               {               \r
+                       reportProgress(String.format("更新を実行します(%d/%d).",cnt++,cntMax));\r
+                       \r
+                       newr = _UpdateRdEntrySub(oldr, reqr);\r
+                       if ( newr == null  ) {\r
+                               return false;\r
+                       }\r
+                       \r
+                       // 本体から取得できない情報は引き継ぐ\r
+                       copyAttributesMethod(reqr, newr);\r
+                       \r
+                       // 一発ヒットした場合は予約リストの更新が必要。再取得している場合は不要\r
+                       int idx = getReserves().indexOf(cacher);\r
+                       if ( idx >= 0 ) {\r
+                               getReserves().set(idx,newr);\r
+                       }\r
+               }\r
+               \r
+               if ( isModified(cacher, newr) ) {\r
+                       // ピンピンうるさいので実行OFFの予約の場合はだんまりで\r
+                       errmsg = "EDCB番組表からの情報で内容が変更されました: "+newr.getStartDateTime()+"~"+newr.getZhh()+":"+newr.getZmm()+" "+newr.getTitle();\r
+               }\r
+               \r
+               return true;\r
+       }\r
+       \r
+       /**\r
+        * プログラム予約を更新する(開始・終了日時とタイトルに変更がない場合は更新)\r
+        */\r
+       private boolean UpdateRdEntryDT2(ReserveList cacher, ReserveList reqr) {\r
+               \r
+               // EDCBで更新する\r
+               int cnt = 1;\r
+               int cntMax = 2;\r
+               \r
+               ReserveList oldr = null;        // 本体にあった既存の予約情報\r
+               ReserveList newr = null;        // 更新後に取得しなおした新しい予約情報\r
+               \r
+               // EDCBの更新すべき予約IDを検索する ★EDCBは再起動すると予約IDが振り直しになるので必要!!\r
+               {\r
+                       reportProgress(String.format("更新対象の予約を探します(%d/%d).",cnt++,cntMax));\r
+                       \r
+                       oldr = getRsvOnEdcbDT(cacher);\r
+                       if ( oldr == null ) {\r
+                               errmsg = "予約一覧に更新対象が見つかりません。";\r
+                               return false;\r
+                       }\r
+                       \r
+                       // 予約IDのみ更新\r
+                       reportProgress("+更新される予約ID: "+oldr.getId());\r
+               }\r
+               \r
+               // 予約更新\r
+               {\r
+                       reportProgress(String.format("プログラム予約を更新します(%d/%d).",cnt++,cntMax));\r
+\r
+                       newr = _UpdateRdEntrySub(oldr, reqr);\r
+                       if ( newr == null  ) {\r
+                               return false;\r
+                       }\r
+                       \r
+                       // 本体から取得できない情報は引き継ぐ\r
+                       copyAttributesMethod(reqr, newr);\r
+                       \r
+                       // 一発ヒットした場合は予約リストの更新が必要。再取得している場合は不要\r
+                       int idx = getReserves().indexOf(cacher);\r
+                       if ( idx >= 0 ) {\r
+                               getReserves().set(idx,newr);\r
+                       }\r
+               }\r
+               \r
+               return true;\r
+       }\r
+       \r
+       /**\r
+        * プログラム予約を更新する(開始・終了日時とタイトルに変更がある場合は削除して追加)\r
+        */\r
+       private boolean UpdateRdEntryDT1(ReserveList cacher, ReserveList reqr) {\r
+               \r
+               reportProgress("プログラム予約を登録し直します.");\r
+               \r
+               // 削除して\r
+               if ( RemoveRdEntry(cacher.getId()) == null ) {\r
+                       return(false);\r
+               }\r
+               \r
+               // 追加する\r
+               if ( ! PostRdEntry(reqr) ) {\r
+                       errmsg += "予約が削除されたので登録しなおしてください。";\r
+               }\r
+               \r
+               return(true);\r
+       }\r
+\r
+       /**\r
+        * 予約更新の共通部分\r
+        */\r
+       private ReserveList _UpdateRdEntrySub(ReserveList oldr, ReserveList reqr) {\r
+               \r
+               String pstr = genPoststrDTB(reqr);\r
+               String uri = "http://"+getIPAddr()+":"+getPortNo()+"/reservechg.html?id="+getRsvId(oldr.getId());\r
+               \r
+               System.err.println("URL: "+uri);\r
+               \r
+               String header = null;\r
+               String response = null;\r
+               for (int i=0; i<retryMax; i++) {\r
+                       String[] d = reqPOST(uri, pstr, null);\r
+                       header = d[0];\r
+                       response = d[1];\r
+                       if ( header != null && response == null ) {\r
+                               reportProgress("コネクションがリセットされました。リトライします。");\r
+                               CommonUtils.milSleep(1000);\r
+                       }\r
+                       else {\r
+                               break;\r
+                       }\r
+               }\r
+               if ( header == null || response == null ) {\r
+                       errmsg = "レコーダーが反応しません。";\r
+                       return null;\r
+               }\r
+               if ( ! response.contains("予約を変更しました") ) {\r
+                       errmsg = "更新に失敗しました。";\r
+                       return null;\r
+               }\r
+               \r
+               // 更新後の情報を再取得\r
+               ReserveList newr = reqr.clone();\r
+               newr.setId(oldr.getId());\r
+               if ( ! GetRdReservedDetailByReserveId(newr,0) ) {\r
+                       errmsg = "更新後の情報を取得できませんでした。";\r
+                       return null;\r
+               }\r
+\r
+               System.out.printf("\n<<< Message from RD >>> \"%s\"\n\n", "正常に更新できました。");\r
+               return newr;\r
+       }\r
+       \r
+       \r
+       /*******************************************************************************\r
+        * 予約削除\r
+        ******************************************************************************/\r
+       \r
+       @Override\r
+       public ReserveList RemoveRdEntry(String delid) {\r
+               \r
+               System.out.println("Run: RemoveRdEntry()");\r
+               \r
+               errmsg = "";\r
+               \r
+               // おまじない\r
+               Authenticator.setDefault(new MyAuthenticator(getUser(), getPasswd()));\r
+\r
+               ReserveList delr = null;        // キャッシュ上の削除対象\r
+               ReserveList oldr = null;        // レコーダ上の削除対象\r
+               \r
+               // キャッシュから削除対象を探す\r
+               for ( ReserveList reserve : getReserves() )  {\r
+                       if (reserve.getId().equals(delid)) {\r
+                               delr = reserve;\r
+                               break;\r
+                       }\r
+               }\r
+               if (delr == null) {\r
+                       return(null);\r
+               }\r
+               \r
+               // EDCBから削除対象を探す ★EDCBは再起動すると予約IDが振り直しになるので必要!!\r
+               reportProgress(String.format("削除する予約IDを取得します."));\r
+               \r
+               if ( ContentIdEDCB.isValid(delr.getContentId()) ) {\r
+                       oldr = getRsvOnEdcbTR(delr);\r
+               }\r
+               else {\r
+                       oldr = getRsvOnEdcbDT(delr);\r
+               }\r
+               if ( oldr == null ) {\r
+                       errmsg = "レコーダ上に削除対象が見つかりません。";\r
+                       return(null);\r
+               }\r
+               \r
+               reportProgress("+削除される予約ID: "+oldr.getId());\r
+               \r
+               // EDCBから削除する\r
+               String header;\r
+               String response;\r
+\r
+               {               \r
+                       reportProgress("削除を実行します.");\r
+                       String uri = "http://"+getIPAddr()+":"+getPortNo()+"/reservedel.html?id="+getRsvId(oldr.getId());\r
+                       \r
+                       System.err.println("URL: "+uri);\r
+                       \r
+                       header = response = null;\r
+                       for (int i=0; i<retryMax; i++) {\r
+                               String[] d = reqPOST(uri, "", null);\r
+                               header = d[0];\r
+                               response = d[1];\r
+                               if ( header != null && response == null ) {\r
+                                       reportProgress("コネクションがリセットされました。リトライします。");\r
+                                       CommonUtils.milSleep(1000);\r
+                               }\r
+                               else {\r
+                                       break;\r
+                               }\r
+                       }\r
+                       if ( header == null || response == null ) {\r
+                               errmsg = "レコーダーが反応しません。";\r
+                               return(null);\r
+                       }\r
+                       if ( ! response.contains("予約を削除しました") ) {\r
+                               errmsg = "削除に失敗しました。";\r
+                               return(null);\r
+                       }\r
+               }\r
+               \r
+               // 予約リストを更新\r
+               getReserves().remove(delr);\r
+               \r
+               // キャッシュに保存(削除なので録画済みフラグは操作しなくてよい)\r
+               ReservesToFile(getReserves(), rsvedFile);\r
+               //setRecordedFlag();\r
+               \r
+               System.out.printf("\n<<< Message from RD >>> \"%s\"\n\n", "正常に削除できました。");\r
+               \r
+               return delr;\r
+       }\r
+       \r
+       \r
+       /*******************************************************************************\r
+        * 予約用の部品\r
+        ******************************************************************************/\r
+       \r
+       /**\r
+        * 放送局と日付のチェック\r
+        */\r
+       private boolean PrePostCheck(ReserveList r) {\r
+               // 放送局\r
+               r.setChannel(cc.getCH_WEB2CODE(r.getCh_name()));\r
+               if ( r.getChannel() == null) {\r
+                       errmsg = "【警告】Web番組表の放送局名「"+r.getCh_name()+"」をCHコードに変換できません。CHコード設定を修正してください。" ;\r
+                       System.out.println(errmsg);\r
+                       return(false);\r
+               }\r
+               \r
+               // 予約パターンID\r
+               // 次回予定日\r
+               // 録画長\r
+               // 開始日時・終了日時\r
+               r.setRec_pattern_id(getRec_pattern_Id(r.getRec_pattern()));\r
+               r.setRec_nextdate(CommonUtils.getNextDate(r));\r
+               r.setRec_min(CommonUtils.getRecMin(r.getAhh(),r.getAmm(),r.getZhh(),r.getZmm()));\r
+               getStartEndDateTime(r);\r
+               if ( r.getRec_pattern_id() != 11 ) {\r
+                       errmsg = "日付指定しか利用出来ません。";\r
+                       System.out.println(errmsg);\r
+                       return(false) ;\r
+               }\r
+               \r
+               return true;\r
+       }\r
+       \r
+       /**\r
+        * プログラム予約の制限のチェック\r
+        */\r
+       private boolean PreProgResCheck(ReserveList r) {\r
+               if (\r
+                               getTextPittariFlag(r).equals(ITEM_JUST_ENABLE)\r
+                       ) {\r
+                       errmsg = "プログラム予約では次の指定はできません:ぴったり録画="+ITEM_JUST_ENABLE;\r
+                       return(false) ;\r
+               }\r
+               \r
+               if (\r
+                               r.getPursues()\r
+                       ) {\r
+                       errmsg = "プログラム予約では次の指定はできないので規定値に変更しました:番組追従="+ITEM_YES;\r
+                       r.setPursues(false);\r
+                       //return(false) ;\r
+               }\r
+               \r
+               return true;\r
+       }\r
+       \r
+       /**\r
+        * EDCB予約一覧から操作対象を探すのです(EPG予約版)\r
+        * @see #isEqualsTR(ReserveList, ReserveList, int)\r
+        * @see #getRsvOnEdcbDT(ReserveList)\r
+        */\r
+       private ReserveList getRsvOnEdcbTR(ReserveList origr) {\r
+               \r
+               // 予約IDを試してみる(EDCBがリブートしていなければこれでOKなはず)\r
+               if ( origr.getId() != null && origr.getId().length() > 0 ) {\r
+                       ReserveList newr = origr.clone();\r
+                       if ( GetRdReservedDetailByReserveId(newr,0) && isEqualsTR(origr, newr, -1) ) {\r
+                               //System.out.println("+-一致する予約です(予約ID直接): "+newr.getId());\r
+                               return newr;\r
+                       }\r
+               }\r
+               \r
+               // 番組IDを試してみる(重複予約されていなければこれで大丈夫なはず)\r
+               if ( origr.getContentId() != null && ContentIdEDCB.isValid(origr.getContentId()) ) {\r
+                       ReserveList newr = origr.clone();\r
+                       if ( GetRdReservedDetailByContentId(newr,0) && isEqualsTR(origr, newr, 0) ) {\r
+                               //System.out.println("+-一致する予約です(番組ID直接): "+newr.getId());\r
+                               return newr;\r
+                       }\r
+               }\r
+\r
+               // 一覧取得しなおし(3.17.5b)\r
+               ArrayList<ReserveList> rl = new ArrayList<ReserveList>();\r
+               if ( ! GetRdReservedList(rl) ) {\r
+                       errmsg = "予約一覧の取得に失敗しました。";\r
+                       return null;\r
+               }\r
+\r
+               // 置き換えていいよ\r
+               setReserves(rl);\r
+\r
+               // 一覧から該当する予約を探す\r
+               int idx=1;\r
+               for ( ReserveList newr : rl ) {\r
+                       if ( isEqualsTR(origr, newr, idx++) ) {\r
+                               return newr;\r
+                       }\r
+               }\r
+               \r
+               errmsg = "予約IDがみつかりません。";\r
+               return null;\r
+       }\r
+       \r
+       /**\r
+        * 同じEPG予約かどうかしらべる\r
+        */\r
+       private boolean isEqualsTR(ReserveList origr, ReserveList newr, Integer idx) {\r
+               \r
+               // 放送局が一緒で、開始日時が近いかどうか(旧情報vs一覧)\r
+               if ( ! isLikesRsvOnList(origr, newr) ) {\r
+                       return false;\r
+               }\r
+               \r
+               // EPG予約かどうか(詳細)\r
+               if ( ! ContentIdEDCB.isValid(newr.getContentId()) ) {\r
+                       if ( idx != null ) System.out.println("+-そっくりですがEPG予約ではありません: "+newr.getId());\r
+                       return false;\r
+               }\r
+               \r
+               // おなじ番組かどうか(旧情報vs一覧)\r
+               if ( ! origr.getContentId().equals(newr.getContentId()) ) {\r
+                       if ( idx != null ) System.out.println("+-そっくりですが別の番組です: "+newr.getId());\r
+                       return false;\r
+               }\r
+               \r
+               // チューナー指定が等しいかどうか(旧情報vs一覧)\r
+               if ( ! origr.getTuner().equals(newr.getTuner()) ) {\r
+                       if ( idx != null ) System.out.println("+-そっくりですが異なる予約です(使用するチューナーが異なります): "+newr.getId());\r
+                       return false;\r
+               }\r
+               \r
+               // 時間移動も確認しておくか\r
+               if ( isEqualsDate(origr, newr) ) {\r
+                       if ( idx != null ) {\r
+                               if ( idx == -1 ) {\r
+                                       System.out.println("+-一致する予約です(予約ID直接): "+newr.getId());\r
+                               }\r
+                               else if ( idx == 0 ) {\r
+                                       System.out.println("+-一致する予約です(番組ID直接): "+newr.getId());\r
+                               }\r
+                               else {\r
+                                       System.out.println("+-一致する予約です(間接)["+idx+"]: "+newr.getId());\r
+                               }\r
+                       }\r
+               }\r
+               else {\r
+                       System.out.println("+-一致する予約のようです(時間が移動しています)["+idx+"]: "+newr.getId());\r
+               }\r
+               \r
+               return true;\r
+       }\r
+       \r
+       /**\r
+        * EDCB予約一覧から操作対象を探すのです(プログラム予約版)<BR>\r
+        * ※一発ヒットしたものはキャッシュに乗らないので注意\r
+        * @see #isEqualsDT(ReserveList, ReserveList, int)\r
+        * @see #getRsvOnEdcbTR(ReserveList)\r
+        */\r
+       private ReserveList getRsvOnEdcbDT(ReserveList origr) {\r
+               \r
+               // 予約IDを試してみる(EDCBがリブートしていなければこれでOKなはず)\r
+               if ( origr.getId() != null && origr.getId().length() > 0 ) {\r
+                       ReserveList newr = origr.clone();\r
+                       if ( GetRdReservedDetailByReserveId(newr,0) && isEqualsDT(origr, newr, 0) ) {\r
+                               //System.out.println("+-一致する予約です(番組ID直接): "+newr.getId());\r
+                               return newr;\r
+                       }\r
+               }\r
+\r
+               // 予約IDは固定ではないので再度取得しなおさないと(3.17.5b変更)\r
+               ArrayList<ReserveList> rl = new ArrayList<ReserveList>();\r
+               if ( ! GetRdReservedList(rl) ) {\r
+                       errmsg = "予約一覧の取得に失敗しました。";\r
+                       return null;\r
+               }\r
+\r
+               // 置き換えていいよ\r
+               setReserves(rl);\r
+               \r
+               // 一覧から該当する予約を探す\r
+               int idx=0;\r
+               for ( ReserveList newr : rl ) {\r
+                       if ( isEqualsDT(origr, newr, idx++) ) {\r
+                               return newr;\r
+                       }\r
+               }\r
+               \r
+               errmsg = "予約IDが見つかりません。";\r
+               return null;\r
+       }\r
+       \r
+       /**\r
+        * 同じプログラム予約かどうかしらべる\r
+        */\r
+       private boolean isEqualsDT(ReserveList origr, ReserveList newr, Integer idx) {\r
+               \r
+               // 放送局がいっしょで、開始日時・終了時刻が等しいかどうか(旧情報vs一覧)\r
+               if ( ! isEqualsRsvOnList(origr, newr) ) {\r
+                       return false;\r
+               }\r
+               \r
+               // プログラム予約かどうか\r
+               if ( ContentIdEDCB.isValid(newr.getContentId()) ) {\r
+                       if ( idx != null ) System.out.println("+-そっくりですがプログラム予約ではありません: "+newr.getId());\r
+                       return false;\r
+               }\r
+               \r
+               // チューナー指定が等しいかどうか(旧情報vs一覧)\r
+               if ( ! origr.getTuner().equals(newr.getTuner()) ) {\r
+                       if ( idx != null ) System.out.println("+-そっくりですが異なる予約です(使用するチューナーが異なります): "+newr.getId());\r
+                       return false;\r
+               }\r
+               \r
+               if ( idx != null ) {\r
+                       if ( idx == 0 ) {\r
+                               System.out.println("+-一致する予約です(予約ID直接): "+newr.getId());\r
+                       }\r
+                       else {\r
+                               System.out.println("+-一致する予約です(間接)["+idx+"]: "+newr.getId());\r
+                       }\r
+               }\r
+               \r
+               return true;\r
+       }\r
+\r
+       /**\r
+        * 古い予約一覧から情報を引き継ぐ\r
+        */\r
+       private void copyAttributesAllList( ArrayList<ReserveList> oldrl, ArrayList<ReserveList> newrl ) {\r
+               \r
+               // 予約一覧の再取得があった\r
+               for ( ReserveList newr : newrl ) {\r
+                       if ( newr.getContentId() != null ) {\r
+                               for ( ReserveList oldr : oldrl ) {\r
+                                       if ( oldr.getContentId() != null && isEqualsTR(oldr, newr, null) ) {\r
+                                               copyAttributesMethod(oldr, newr);\r
+                                       }\r
+                               }\r
+                       }\r
+                       else {\r
+                               for ( ReserveList oldr : oldrl ) {\r
+                                       if ( oldr.getContentId() == null && isEqualsDT(oldr, newr, null) ) {\r
+                                               copyAttributesMethod(oldr, newr);\r
+                                       }\r
+                               }\r
+                       }\r
+               }\r
+               \r
+       }\r
+       private void copyAttributesMethod( ReserveList oldr, ReserveList newr ) {\r
+               newr.setRec_genre(oldr.getRec_genre());\r
+               newr.setRec_subgenre(oldr.getRec_subgenre());\r
+               setTextPresetID(newr, getTextPresetID(oldr));\r
+       }\r
+       \r
+       \r
+       /* ここまで */\r
+       \r
+       \r
+       \r
+\r
+       \r
+       \r
+       /*******************************************************************************\r
+        * 非公開メソッド\r
+        ******************************************************************************/\r
+\r
+       //\r
+       private String genPoststrEPGA(ReserveList r) {\r
+               StringBuilder sb = new StringBuilder();\r
+               try {\r
+                       sb.append("presetID="+text2value(getListPresetID(),getTextPresetID(r))+"&");\r
+                       sb.append("onid="+Integer.decode("0x"+r.getContentId().substring(5,9))+"&");\r
+                       sb.append("tsid="+Integer.decode("0x"+r.getContentId().substring(9,13))+"&");\r
+                       sb.append("sid="+Integer.decode("0x"+r.getContentId().substring(13,17))+"&");\r
+                       sb.append("evid="+Integer.decode("0x"+r.getContentId().substring(17,21)));\r
+               }\r
+               catch ( Exception e ) {\r
+                       e.printStackTrace();\r
+               }\r
+               return sb.toString();\r
+       }\r
+       private String genPoststrEPGB(ReserveList r) {\r
+               StringBuilder sb = new StringBuilder();\r
+               sb.append(genPoststrEPGA(r)+"&");\r
+               sb.append(genPoststrCom(r));\r
+               return sb.toString();\r
+       }\r
+       \r
+       //\r
+       private String genPoststrDTCom(ReserveList r) {\r
+               StringBuilder sb = new StringBuilder();\r
+               \r
+               // 日付指定しか対応してないが…\r
+               GregorianCalendar cal = CommonUtils.getCalendar(r.getRec_nextdate().substring(0,10));\r
+               sb.append("sdy="+cal.get(Calendar.YEAR)+"&");\r
+               sb.append("sdm="+(cal.get(Calendar.MONTH)+1)+"&");\r
+               sb.append("sdd="+cal.get(Calendar.DATE)+"&");\r
+               sb.append("sth="+Integer.valueOf(r.getAhh())+"&");\r
+               sb.append("stm="+Integer.valueOf(r.getAmm())+"&");\r
+               //\r
+               if ( r.getAhh().compareTo(r.getZhh()) > 0 ) {\r
+                       cal.add(Calendar.DATE, 1);\r
+               }\r
+               sb.append("edy="+cal.get(Calendar.YEAR)+"&");\r
+               sb.append("edm="+(cal.get(Calendar.MONTH)+1)+"&");\r
+               sb.append("edd="+cal.get(Calendar.DATE)+"&");\r
+               sb.append("eth="+Integer.valueOf(r.getZhh())+"&");\r
+               sb.append("etm="+Integer.valueOf(r.getZmm()));\r
+               \r
+               return sb.toString();\r
+       }\r
+       private String genPoststrDTA(ReserveList r) {\r
+               StringBuilder sb = new StringBuilder();\r
+               try {\r
+                       //sb.append("presetID="+text2value(getListPresetID(),getTextPresetID(r))+"&");\r
+                       sb.append("serviceID="+cc.getCH_WEB2CODE(r.getCh_name())+"&");\r
+                       sb.append("pgname="+URLEncoder.encode(r.getTitle(),thisEncoding)+"&");\r
+                       sb.append(genPoststrDTCom(r));\r
+               } catch (UnsupportedEncodingException e) {\r
+                       e.printStackTrace();\r
+               }\r
+               \r
+               return sb.toString();\r
+       }\r
+       private String genPoststrDTB(ReserveList r) {\r
+               StringBuilder sb = new StringBuilder();\r
+               try {\r
+                       sb.append("serviceID="+cc.getCH_WEB2CODE(r.getCh_name())+"&");\r
+                       sb.append("pgname="+URLEncoder.encode(r.getTitle(),thisEncoding)+"&");\r
+                       sb.append(genPoststrDTCom(r)+"&");\r
+                       sb.append(genPoststrCom(r));\r
+               } catch (UnsupportedEncodingException e) {\r
+                       e.printStackTrace();\r
+               }\r
+               \r
+               return sb.toString();\r
+       }\r
+       \r
+       //                      \r
+       private String genPoststrCom(ReserveList r) {\r
+               // EPG・プログラム予約共通\r
+               StringBuilder sb = new StringBuilder();\r
+               try {\r
+                       sb.append("presetID="+text2value(getListPresetID(),getTextPresetID(r))+"&");\r
+                       if ( r.getExec() ) {\r
+                               sb.append("recMode="+text2value(getListRecMode(),getTextRecMode(r))+"&");\r
+                       }\r
+                       else {\r
+                               sb.append("recMode="+VALUE_REC_MODE_DISABLE+"&");\r
+                       }\r
+                       if ( r.getPursues() ) {\r
+                               sb.append("tuijyuuFlag="+VALUE_TRACE_ENABLE+"&");\r
+                       }\r
+                       else {\r
+                               sb.append("tuijyuuFlag="+VALUE_TRACE_DISABLE+"&");\r
+                       }\r
+                       sb.append("priority="+text2value(getListPriority(),getTextPriority(r))+"&");\r
+                       sb.append("pittariFlag="+text2value(getListPittariFlag(),getTextPittariFlag(r))+"&");\r
+                       sb.append("suspendMode="+text2value(getListSuspendMode(),getTextSuspendMode(r))+"&");\r
+                       if ( getTextStartMargine(r).equals(ITEM_MARGINE_DEFAULT) || getTextEndMargine(r).equals(ITEM_MARGINE_DEFAULT) ) {\r
+                               sb.append("useDefMargineFlag=1&");\r
+                               sb.append("startMargine=0&");\r
+                               sb.append("endMargine=0&");\r
+                       }\r
+                       else {\r
+                               sb.append("startMargine="+text2value(getListStartMargine(),getTextStartMargine(r))+"&");\r
+                               sb.append("endMargine="+text2value(getListEndMargine(),getTextEndMargine(r))+"&");\r
+                       }\r
+                       {\r
+                               String s = getTextServiceMode(r);\r
+                               if ( s.equals(ITEM_SERVMOCE_DEFAULT) ) {\r
+                                       sb.append("serviceMode=0&");\r
+                               }\r
+                               else {\r
+                                       if ( s.equals(ITEM_SERVMOCE_SUBT) || s.equals(ITEM_SERVMOCE_SUBTCARO) ) {\r
+                                               sb.append("serviceMode_1=0&");\r
+                                       }\r
+                                       if ( s.equals(ITEM_SERVMOCE_CARO) || s.equals(ITEM_SERVMOCE_SUBTCARO) ) {\r
+                                               sb.append("serviceMode_2=0&");\r
+                                       }\r
+                               }\r
+                       }\r
+                       sb.append("tunerID="+text2value(getListTunerID(),getTextTunerID(r))+"&");\r
+                       if ( getTextContinueRecFlag(r).equals(ITEM_YES) ) {\r
+                               sb.append("continueRecFlag=1&");\r
+                       }\r
+                       if ( getTextRebootFlag(r).equals(ITEM_YES) ) {\r
+                               sb.append("rebootFlag=1&");\r
+                       }\r
+                       if ( getTextPartialRecFlag(r).equals(ITEM_YES) ) {\r
+                               sb.append("partialRecFlag=1&");\r
+                       }\r
+                       \r
+                       // オプショナル\r
+                       {\r
+                               String val = r.getHidden_params().get(RETVAL_KEY_RECFOLDERCOUNT);\r
+                               sb.append("recFolderCount=");\r
+                               sb.append((val!=null)?(val):("0"));\r
+                               sb.append("&");\r
+                               \r
+                               val = r.getHidden_params().get(RETVAL_KEY_PARTIALFOLDERCOUNT);\r
+                               sb.append("partialFolderCount=");\r
+                               sb.append((val!=null)?(val):("0"));\r
+                       }\r
+               }\r
+               catch ( Exception e ) {\r
+                       e.printStackTrace();\r
+               }\r
+               \r
+               return sb.toString();\r
+       }\r
+       \r
+       //\r
+       private void setSettingRecMargin(ArrayList<TextValueSet> tvs) {\r
+               tvs.clear();\r
+               add2tvs(tvs,ITEM_MARGINE_DEFAULT,VALUE_MARGINE_DEFAULT);\r
+               for (int i=0; i<=90; i++) {\r
+                       add2tvs(tvs,String.valueOf(i),String.valueOf(i));\r
+               }\r
+       }\r
+       private void setSettingServiceMode(ArrayList<TextValueSet> tvs) {\r
+               tvs.clear();\r
+               add2tvs(tvs,ITEM_SERVMOCE_DEFAULT,VALUE_SERVMOCE_DEFAULT);\r
+               add2tvs(tvs,ITEM_SERVMOCE_SUBT,VALUE_SERVMOCE_SUBT);\r
+               add2tvs(tvs,ITEM_SERVMOCE_CARO,VALUE_SERVMOCE_CARO);\r
+               add2tvs(tvs,ITEM_SERVMOCE_SUBTCARO,VALUE_SERVMOCE_SUBTCARO);\r
+               add2tvs(tvs,ITEM_SERVMOCE_NONE,VALUE_SERVMOCE_NONE);\r
+       }\r
+       private void setSettingNoYes(ArrayList<TextValueSet> tvs) {\r
+               tvs.clear();\r
+               add2tvs(tvs,ITEM_NO,VALUE_NO);\r
+               add2tvs(tvs,ITEM_YES,VALUE_YES);\r
+       }\r
+       private void setSettingRecType(ArrayList<TextValueSet> tvs) {\r
+               tvs.clear();\r
+               add2tvs(tvs,ITEM_REC_TYPE_EPG,VALUE_REC_TYPE_EPG);\r
+               add2tvs(tvs,ITEM_REC_TYPE_PROG,VALUE_REC_TYPE_PROG);\r
+       }\r
+       //\r
+       private void setSettingChCodeValue(ArrayList<TextValueSet> tvsValue, ArrayList<TextValueSet> tvsType, String key, String res) {\r
+               \r
+               HashMap<String, String> typ = new HashMap<String, String>();\r
+               typ.put("地デジ", "uvd");\r
+               typ.put("BS", "bsd");\r
+               typ.put("","csd");\r
+               \r
+               tvsValue.clear();\r
+               tvsType.clear();\r
+               Matcher mb = Pattern.compile("<select name=\""+key+"\">([\\s\\S]+?)</select>").matcher(res);\r
+               if (mb.find()) {\r
+                       Matcher mc = Pattern.compile("<option value=\"([^\"]*?)\"(\\s*selected\\s*)?>(.+?)\\((.+?)\\)\\n").matcher(mb.group(1));\r
+                       while (mc.find()) {\r
+                               // ワンセグは対象外\r
+                               if (mc.group(4).equals("ワンセグ")) {\r
+                                       continue;\r
+                               }\r
+                               \r
+                               String chname = mc.group(3);\r
+                               Long chid = 0L;\r
+                               \r
+                               // 難視聴対策放送\r
+                               try {\r
+                                       chid = Long.valueOf(mc.group(1));\r
+                                       Long tsid = (chid & 0x0000FFFF0000L);\r
+                                       if ( tsid == 0x000043100000L || tsid == 0x000043110000L ) {\r
+                                               chname = TEXT_NANSHICHO_HEADER+chname;\r
+                                               System.out.println("[DEBUG] "+chname);\r
+                                       }\r
+                               }\r
+                               catch (NumberFormatException e) {\r
+                                       continue;\r
+                               }\r
+                               \r
+                               // 重複する放送局名は対象外\r
+                               boolean dup = false;\r
+                               for ( TextValueSet t : tvsValue ) {\r
+                                       if ( t.getText().equals(chname) ) {\r
+                                               dup = true;\r
+                                       }\r
+                               }\r
+                               if (dup) {\r
+                                       continue;\r
+                               }\r
+                               \r
+                               // 放送局ID順でソートして追加\r
+                               {\r
+                                       int n = 0;\r
+                                       for ( ; n<tvsValue.size(); n++ ) {\r
+                                               if ( Long.valueOf(tvsValue.get(n).getValue()) > Long.valueOf(chid) ) {\r
+                                                       break;\r
+                                               }\r
+                                       }\r
+                                       add2tvs(n,tvsValue,chname,mc.group(1));\r
+                               }\r
+\r
+                               // 地デジでもBSでもないものは全部CSあつかい\r
+                               {\r
+                                       String val = typ.get(mc.group(4));\r
+                                       if (val == null) {\r
+                                               val = typ.get("");\r
+                                       }\r
+                                       add2tvs(tvsType,chname,val);\r
+                               }\r
+                       }\r
+               }\r
+               \r
+               System.err.println("=== CHコード一覧 for EDCB ===");\r
+               System.err.println("放送局 : \"レコーダの放送局名\",\"放送局コード\"");\r
+               System.err.println("=============================");\r
+               \r
+               for ( TextValueSet tv : tvsValue ) {\r
+                       System.err.println(String.format("%-20s : \"%s\",\"%s\"",tv.getText(),tv.getValue(),tv.getValue()));\r
+               }\r
+               System.err.println("=============================");\r
+       }\r
+       //\r
+       protected void setSettingEtc(ArrayList<TextValueSet> tvs, String key, int typ, String res) {\r
+               tvs.clear();\r
+               Matcher mb = Pattern.compile("<select name=\""+key+"\">([\\s\\S]+?)</select>").matcher(res);\r
+               if (mb.find()) {\r
+                       Matcher mc = Pattern.compile("<option value=\"([^\"]*?)\"(\\s*selected\\s*)?>(.*?)\\n").matcher(mb.group(1));\r
+                       while (mc.find()) {\r
+                               TextValueSet t = add2tvs(tvs,mc.group(3),mc.group(1));\r
+                               if (mc.group(2) != null) {\r
+                                       t.setDefval(true);\r
+                               }\r
+                       }\r
+               }\r
+       }\r
+       \r
+       // 予約情報同志を比較する\r
+       private boolean isModified(ReserveList o, ReserveList r) {\r
+               return ! (isEqualsDate(o, r) && o.getTitle().equals(r.getTitle()));\r
+       }\r
+       private boolean isEqualsRsvOnDetail(ReserveList o, ReserveList r) {\r
+               // ここまで一緒なら同じ予約情報だろ\r
+               return (isEqualsRsvOnList(o, r) && o.getTuner().equals(r.getTuner()));\r
+       }\r
+       private boolean isEqualsRsvOnList(ReserveList o, ReserveList r) {\r
+               // ここまで一緒なら同じ予約情報だろ\r
+               return (isEqualsDate(o, r) && o.getChannel().equals(r.getChannel()));\r
+       }\r
+       private boolean isEqualsDate(ReserveList o, ReserveList r) {\r
+               return (o.getStartDateTime().equals(r.getStartDateTime()) && o.getEndDateTime().equals(r.getEndDateTime()));\r
+       }\r
+       private boolean isLikesRsvOnList(ReserveList o, ReserveList r) {\r
+               // ここまで一緒なら親戚の予約情報だろ\r
+               return (isLikesDate(o, r) && o.getChannel().equals(r.getChannel()));\r
+       }\r
+       private boolean isLikesDate(ReserveList o, ReserveList r) {\r
+               return (CommonUtils.getDiffDateTime(o.getStartDateTime(), r.getStartDateTime()) < likersvrange);\r
+       }\r
+}\r
diff --git a/TinyBannavi/src/tainavi/PlugIn_RecRD_H1.java b/TinyBannavi/src/tainavi/PlugIn_RecRD_H1.java
new file mode 100644 (file)
index 0000000..371620b
--- /dev/null
@@ -0,0 +1,127 @@
+package tainavi;\r
+\r
+import java.util.ArrayList;\r
+import java.util.regex.Matcher;\r
+import java.util.regex.Pattern;\r
+\r
+/*\r
+ * \r
+ */\r
+\r
+public class PlugIn_RecRD_H1 extends PlugIn_RecRD_H2 implements HDDRecorder,Cloneable {\r
+\r
+       @Override\r
+       public PlugIn_RecRD_H1 clone() {\r
+               return (PlugIn_RecRD_H1) super.clone();\r
+       }\r
+       \r
+       //private static final String thisEncoding = "MS932";\r
+       \r
+       /* 必須コード  - ここから */\r
+       \r
+       // 種族の特性\r
+       @Override\r
+       public String getRecorderId() { return "RD-H1"; }\r
+       \r
+       /* ここまで */\r
+       \r
+       \r
+       \r
+       /* 個別コード-ここから最後まで */\r
+       \r
+       /*\r
+        * 公開メソッド \r
+        */\r
+       \r
+       /*\r
+        * 非公開メソッド \r
+        */\r
+       \r
+       //\r
+       @Override\r
+       protected ArrayList<ReserveList> GetRdReservedList(String response) {\r
+               \r
+               System.out.println("H1's GetRdReservedList()");\r
+               //\r
+               response = response.replaceAll("\n", "");\r
+\r
+               ArrayList<ReserveList> newReserveList = new ArrayList<ReserveList>();\r
+\r
+               Matcher ma = Pattern.compile("(<td [\\s\\S]+?)</tr>").matcher(response);\r
+               while (ma.find()) {\r
+\r
+                       // 個々のデータを取り出す\r
+                       String buf = ma.group(1);\r
+\r
+                       Matcher mb = null;\r
+                       mb = Pattern.compile(">新規予約<").matcher(buf);\r
+                       if (mb.find()) {\r
+                               break;\r
+                       }\r
+                       \r
+                       ReserveList entry = new ReserveList();\r
+                       \r
+                       /*\r
+                        * 0 : ID\r
+                        * 1 : 実行ON/OFF\r
+                        * 2 : タイトル\r
+                        * 3 : 放送局\r
+                        * 4 : 日付\r
+                        * 5 : 時刻\r
+                        * 6 : デバイス\r
+                        * 7 : 画質\r
+                        * 8 : 音質\r
+                        */\r
+                       String[] d = new String[9];\r
+                       mb = Pattern.compile("<(td|TD).*?>(.*?)</(td|TD)>").matcher(buf);\r
+                       for (int i=0; i<d.length; i++) {\r
+                               if ( mb.find()) {\r
+                                       d[i] = mb.group(2);\r
+                               }\r
+                       }\r
+                       \r
+                       // 予約ID\r
+                       entry.setId(String.valueOf(Integer.valueOf(d[0])-1));\r
+                       \r
+                       // 予約実行ON/OFF\r
+                       mb = Pattern.compile("check_off\\.gif").matcher(d[1]);\r
+                       if (mb.find()) {\r
+                               entry.setExec(false);\r
+                       }\r
+                       \r
+                       // 予約名のエスケープを解除する\r
+                       String title = CommonUtils.unEscape(d[2]).replaceAll("<[bB][rR]>","");\r
+                       mb = Pattern.compile("<[aA] .*?>(.+?)</[aA]>").matcher(title);\r
+                       if (mb.find()) title = mb.group(1);\r
+                       entry.setTitle(title);\r
+                       entry.setTitlePop(TraceProgram.replacePop(title));\r
+                       \r
+                       entry.setRec_pattern(d[4]);\r
+                       entry.setRec_pattern_id(getRec_pattern_Id(entry.getRec_pattern()));\r
+                       mb = Pattern.compile("(\\d\\d):(\\d\\d).*?(\\d\\d):(\\d\\d)").matcher(d[5]);\r
+                       if (mb.find()) {\r
+                               entry.setAhh(mb.group(1));\r
+                               entry.setAmm(mb.group(2));\r
+                               entry.setZhh(mb.group(3));\r
+                               entry.setZmm(mb.group(4));\r
+                       }\r
+                       entry.setRec_nextdate(CommonUtils.getNextDate(entry));\r
+                       entry.setRec_min(CommonUtils.getRecMin(entry.getAhh(), entry.getAmm(), entry.getZhh(), entry.getZmm()));\r
+                       getStartEndDateTime(entry);\r
+                       \r
+                       entry.setChannel(d[3]);\r
+                       entry.setCh_name(getChCode().getCH_REC2WEB(entry.getChannel()));\r
+                       \r
+                       entry.setRec_audio(d[8]);\r
+                       entry.setRec_mode(d[7]);\r
+                       \r
+                       // 予約一覧からはとれない [本体で予約名や時刻を変えてしまうとアウト]\r
+                       /* setReservesV1()内に移動 */\r
+                       \r
+                       // 予約情報を保存\r
+                       newReserveList.add(entry);\r
+               }\r
+               \r
+               return(newReserveList);\r
+       }\r
+}\r
diff --git a/TinyBannavi/src/tainavi/PlugIn_RecRD_H1EX.java b/TinyBannavi/src/tainavi/PlugIn_RecRD_H1EX.java
new file mode 100644 (file)
index 0000000..ddacd83
--- /dev/null
@@ -0,0 +1,127 @@
+package tainavi;\r
+\r
+import java.util.ArrayList;\r
+import java.util.regex.Matcher;\r
+import java.util.regex.Pattern;\r
+\r
+/*\r
+ * \r
+ */\r
+\r
+public class PlugIn_RecRD_H1EX extends PlugIn_RecRD_H2 implements HDDRecorder,Cloneable {\r
+\r
+       @Override\r
+       public PlugIn_RecRD_H1EX clone() {\r
+               return (PlugIn_RecRD_H1EX) super.clone();\r
+       }\r
+       \r
+       //private static final String thisEncoding = "MS932";\r
+       \r
+       /* 必須コード  - ここから */\r
+       \r
+       // 種族の特性\r
+       @Override\r
+       public String getRecorderId() { return "RD-H1EX"; }\r
+       \r
+       /* ここまで */\r
+       \r
+       \r
+       \r
+       /* 個別コード-ここから最後まで */\r
+       \r
+       /*\r
+        * 公開メソッド \r
+        */\r
+       \r
+       /*\r
+        * 非公開メソッド \r
+        */\r
+       \r
+       //\r
+       @Override\r
+       protected ArrayList<ReserveList> GetRdReservedList(String response) {\r
+               \r
+               System.out.println("H1EX's GetRdReservedList()");\r
+               //\r
+               response = response.replaceAll("\n", "");\r
+\r
+               ArrayList<ReserveList> newReserveList = new ArrayList<ReserveList>();\r
+\r
+               Matcher ma = Pattern.compile("(<td [\\s\\S]+?)</tr>").matcher(response);\r
+               while (ma.find()) {\r
+\r
+                       // 個々のデータを取り出す\r
+                       String buf = ma.group(1);\r
+\r
+                       Matcher mb = null;\r
+                       mb = Pattern.compile(">新規予約<").matcher(buf);\r
+                       if (mb.find()) {\r
+                               break;\r
+                       }\r
+                       \r
+                       ReserveList entry = new ReserveList();\r
+                       \r
+                       /*\r
+                        * 0 : ID\r
+                        * 1 : 実行ON/OFF\r
+                        * 2 : タイトル\r
+                        * 3 : 放送局\r
+                        * 4 : 日付\r
+                        * 5 : 時刻\r
+                        * 6 : デバイス\r
+                        * 7 : 画質\r
+                        * 8 : 音質\r
+                        */\r
+                       String[] d = new String[10];\r
+                       mb = Pattern.compile("<(td|TD).*?>(.*?)</(td|TD)>").matcher(buf);\r
+                       for (int i=0; i<d.length; i++) {\r
+                               if ( mb.find()) {\r
+                                       d[i] = mb.group(2);\r
+                               }\r
+                       }\r
+                       \r
+                       // 予約ID\r
+                       entry.setId(String.valueOf(Integer.valueOf(d[0])-1));\r
+                       \r
+                       // 予約実行ON/OFF\r
+                       mb = Pattern.compile("check_off\\.gif").matcher(d[1]);\r
+                       if (mb.find()) {\r
+                               entry.setExec(false);\r
+                       }\r
+                       \r
+                       // 予約名のエスケープを解除する\r
+                       String title = CommonUtils.unEscape(d[2]).replaceAll("<[bB][rR]>","");\r
+                       mb = Pattern.compile("<[aA] .*?>(.+?)</[aA]>").matcher(title);\r
+                       if (mb.find()) title = mb.group(1);\r
+                       entry.setTitle(title);\r
+                       entry.setTitlePop(TraceProgram.replacePop(title));\r
+                       \r
+                       entry.setRec_pattern(d[4]);\r
+                       entry.setRec_pattern_id(getRec_pattern_Id(entry.getRec_pattern()));\r
+                       mb = Pattern.compile("(\\d\\d):(\\d\\d).*?(\\d\\d):(\\d\\d)").matcher(d[5]);\r
+                       if (mb.find()) {\r
+                               entry.setAhh(mb.group(1));\r
+                               entry.setAmm(mb.group(2));\r
+                               entry.setZhh(mb.group(3));\r
+                               entry.setZmm(mb.group(4));\r
+                       }\r
+                       entry.setRec_nextdate(CommonUtils.getNextDate(entry));\r
+                       entry.setRec_min(CommonUtils.getRecMin(entry.getAhh(), entry.getAmm(), entry.getZhh(), entry.getZmm()));\r
+                       getStartEndDateTime(entry);\r
+                       \r
+                       entry.setChannel(d[3]);\r
+                       entry.setCh_name(getChCode().getCH_REC2WEB(entry.getChannel()));\r
+                       \r
+                       entry.setRec_audio(d[9]);\r
+                       entry.setRec_mode(d[8]);\r
+                       \r
+                       // 予約一覧からはとれない [本体で予約名や時刻を変えてしまうとアウト]\r
+                       /* setReservesV1()内に移動 */\r
+                       \r
+                       // 予約情報を保存\r
+                       newReserveList.add(entry);\r
+               }\r
+               \r
+               return(newReserveList);\r
+       }\r
+}\r
diff --git a/TinyBannavi/src/tainavi/PlugIn_RecRD_H2.java b/TinyBannavi/src/tainavi/PlugIn_RecRD_H2.java
new file mode 100644 (file)
index 0000000..b3bbbdf
--- /dev/null
@@ -0,0 +1,337 @@
+package tainavi;\r
+\r
+import java.io.File;\r
+import java.net.Authenticator;\r
+import java.util.ArrayList;\r
+import java.util.Hashtable;\r
+import java.util.regex.Matcher;\r
+import java.util.regex.Pattern;\r
+\r
+/*\r
+ * \r
+ */\r
+\r
+public class PlugIn_RecRD_H2 extends PlugIn_RecRD_X5 implements HDDRecorder,Cloneable {\r
+\r
+       @Override\r
+       public PlugIn_RecRD_H2 clone() {\r
+               return (PlugIn_RecRD_H2) super.clone();\r
+       }\r
+       \r
+       //private static final String thisEncoding = "MS932";\r
+       \r
+       /* 必須コード  - ここから */\r
+       \r
+       // 種族の特性\r
+       @Override\r
+       public String getRecorderId() { return "RD-H2"; }\r
+       \r
+       /* ここまで */\r
+       \r
+       \r
+       \r
+       /* 個別コード-ここから最後まで */\r
+       \r
+       /*\r
+        * 公開メソッド \r
+        */\r
+       \r
+       /*\r
+        *      レコーダーから予約一覧を取得する \r
+        */\r
+       public boolean GetRdReserve(boolean force)\r
+       {\r
+               System.out.println("Run: GetRdReserve("+force+")");\r
+               \r
+               setErrmsg("");\r
+               \r
+               //\r
+               setRsvedFile("env/reserved."+getIPAddr()+"_"+getPortNo()+"_"+getRecorderId()+".xml");\r
+               \r
+               String vrateTFile = "env/videorate."+getIPAddr()+"_"+getPortNo()+"_"+getRecorderId()+".xml";\r
+               String arateTFile = "env/audiorate."+getIPAddr()+"_"+getPortNo()+"_"+getRecorderId()+".xml";\r
+               String folderTFile = "env/folders."+getIPAddr()+"_"+getPortNo()+"_"+getRecorderId()+".xml";\r
+               String dvdcompatTFile = "env/dvdcompat."+getIPAddr()+"_"+getPortNo()+"_"+getRecorderId()+".xml";\r
+               String xChapterTFile = "env/xchapter."+getIPAddr()+"_"+getPortNo()+"_"+getRecorderId()+".xml";\r
+               String mvChapterTFile = "env/mvchapter."+getIPAddr()+"_"+getPortNo()+"_"+getRecorderId()+".xml";\r
+               String chValueTFile = "env/chvalue."+getIPAddr()+"_"+getPortNo()+"_"+getRecorderId()+".xml";\r
+               String bvperfTFile = "env/bvperf."+getIPAddr()+"_"+getPortNo()+"_"+getRecorderId()+".xml";\r
+               String lvoiceTFile = "env/lvoice."+getIPAddr()+"_"+getPortNo()+"_"+getRecorderId()+".xml";\r
+               \r
+               File f = new File(getRsvedFile());\r
+               if ( force == false && f.exists()) {\r
+                       // キャッシュから読み出し(録画設定ほか)\r
+                       vrate = TVSload(vrateTFile);\r
+                       arate = TVSload(arateTFile);\r
+                       folder = TVSload(folderTFile);\r
+                       dvdcompat = TVSload(dvdcompatTFile);\r
+                       \r
+                       // 自動チャプタ関連\r
+                       xchapter = TVSload(xChapterTFile);\r
+                       mvchapter = TVSload(mvChapterTFile);\r
+                       \r
+                       // その他\r
+                       bvperf = TVSload(bvperfTFile);\r
+                       lvoice = TVSload(lvoiceTFile);\r
+                       \r
+                       // チャンネルコードバリュー\r
+                       chvalue = TVSload(chValueTFile);\r
+                       \r
+                       // キャッシュから読み出し(予約一覧)\r
+                       setReservesV1(ReservesFromFile(getRsvedFile()));\r
+                       \r
+                       // なぜか設定ファイルが空になっている場合があるので、その際は再取得する\r
+                       if (vrate.size()>0 && arate.size()>0 &&\r
+                                       folder.size()>0 &&\r
+                                       xchapter.size()>0 && mvchapter.size()>0) {\r
+                               return(true);\r
+                       }\r
+               }\r
+               \r
+               // おまじない\r
+               Authenticator.setDefault(new MyAuthenticator(getUser(), getPasswd()));\r
+               \r
+               // RDから予約一覧を取り出す\r
+               Matcher ma = null;\r
+               String idx = "";\r
+               String response;\r
+               {\r
+                       reportProgress("処理IDを取得します(1/3).");\r
+                       String[] d = reqGET("http://"+getIPAddr()+":"+getPortNo()+"/program/b_prgrm.htm",null);\r
+                       response = d[1];\r
+                       \r
+                       if (response == null) {\r
+                               setErrmsg("レコーダが応答しません(処理ID).");\r
+                               return(false);\r
+                       }\r
+               }\r
+               ma = Pattern.compile("/program/(\\d+?)/program.htm").matcher(response);\r
+               if ( ! ma.find()) {\r
+                       setErrmsg("レコーダからの戻り値が不正です(処理ID).");\r
+                       return(false);\r
+               }\r
+               \r
+               idx = ma.group(1);      // 処理ID\r
+               \r
+               {\r
+                       reportProgress("予約一覧を取得します(2/3).");\r
+                       String[] d = reqGET("http://"+getIPAddr()+":"+getPortNo()+"/program/"+idx+"/program.htm",null);\r
+                       response = d[1];\r
+                       \r
+                       if (response == null) {\r
+                               setErrmsg("レコーダが応答しません(予約一覧).");\r
+                               return(false);\r
+                       }\r
+               }\r
+               \r
+               // (1)録画設定の取得\r
+               {\r
+                       // ハングさせないためのおまじない\r
+                       reportProgress("録画設定を取得します(3/3).");\r
+                       reqGET("http://"+getIPAddr()+":"+getPortNo()+"/program/"+idx+"/b_proginfo.htm?0",null);\r
+                       \r
+                       String[] d = reqGET("http://"+getIPAddr()+":"+getPortNo()+"/program/"+idx+"/proginfo.htm",null);\r
+                       String res = d[1];\r
+                       \r
+                       if (res == null) {\r
+                               setErrmsg("レコーダが応答しません(録画設定).");\r
+                               return(false);\r
+                       }\r
+                       \r
+                       //Matcher mb = null;\r
+                       \r
+                       // (1-1)画質設定\r
+                       setSettingEtc(vrate, "\"vrate\"", 1, res, vrateTFile);\r
+                       \r
+                       // (1-2)音質設定\r
+                       setSettingEtc(arate, "amode", 0, res, arateTFile);\r
+                       \r
+                       // (1-3)フォルダ一覧\r
+                       setSettingFolder(folder,res,folderTFile);\r
+                       \r
+                       // (1-4)エンコーダ [RD-H2にはないよ]\r
+                       \r
+                       // (1-5)DVD互換モード\r
+                       setSettingEtc(dvdcompat, "dvdr", 1, res, dvdcompatTFile);\r
+                       \r
+                       // (1-6)記録先デバイス [RD-H2にはないよ]\r
+                       \r
+                       // (1-7)自動チャプタ関連\r
+                       setSettingEtc(xchapter, "bAutoChapter", 0, res, xChapterTFile);\r
+                       \r
+                       // bDvdAutoChapter [RD-H2にはないよ]\r
+                       \r
+                       setSettingEtc(mvchapter, "bAudioAutoChapter", 0, res, mvChapterTFile);\r
+                       \r
+                       // (1-8)チャンネルコードバリュー\r
+                       setSettingEtc(chvalue, "channel", 1, res, chValueTFile);\r
+                       \r
+                       // (1-10)記録時画面比 [RD-H2にはないよ]\r
+                       \r
+                       // (1-11)高レート節約\r
+                       setSettingEtc(bvperf, "bVPerform", 0, res, bvperfTFile);\r
+                       \r
+                       // (1-12)ライン音声選択\r
+                       setSettingEtc(lvoice, "lVoice", 0, res, lvoiceTFile);\r
+               }\r
+               \r
+               // (2)予約一覧データの分析\r
+               setReservesV1(GetRdReservedList(response));\r
+               \r
+               // 取得した情報の表示\r
+               System.out.println("---Reserved List Start---");\r
+               for ( int i = 0; i<getReserves().size(); i++ ) {\r
+                       // 詳細情報の取得\r
+                       ReserveList e = getReserves().get(i);\r
+                       System.out.println(String.format("[%s] %s\t%s\t%s\t%s:%s\t%s:%s\t%sm\t%s\t%s\t%s\t%s",\r
+                                       (i+1), e.getId(), e.getRec_pattern(), e.getRec_nextdate(), e.getAhh(), e.getAmm(), e.getZhh(),  e.getZmm(),     e.getRec_min(), e.getRec_mode(), e.getTitle(), e.getChannel(), e.getCh_name()));\r
+               }\r
+               System.out.println("---Reserved List End---");\r
+               \r
+               // キャッシュに保存\r
+               ReservesToFile(getReserves(), getRsvedFile());\r
+               \r
+               return(true);\r
+       }\r
+       \r
+       /*\r
+        * 非公開メソッド \r
+        */\r
+       \r
+       //\r
+       @Override\r
+       protected ArrayList<ReserveList> GetRdReservedList(String response) {\r
+               \r
+               System.out.println("H2's GetRdReservedList()");\r
+               //\r
+               response = response.replaceAll("\n", "");\r
+\r
+               ArrayList<ReserveList> newReserveList = new ArrayList<ReserveList>();\r
+\r
+               Matcher ma = Pattern.compile("<tr [^>]*?>([\\s\\S]*?)</tr>").matcher(response);\r
+               while (ma.find()) {\r
+\r
+                       // 個々のデータを取り出す\r
+                       String buf = ma.group(1);\r
+\r
+                       Matcher mb = null;\r
+                       mb = Pattern.compile(">新規予約<").matcher(buf);\r
+                       if (mb.find()) {\r
+                               break;\r
+                       }\r
+                       \r
+                       ReserveList entry = new ReserveList();\r
+                       \r
+                       /*\r
+                        * 0 : ID\r
+                        * 1 : 実行ON/OFF\r
+                        * 2 : タイトル\r
+                        * 3 : 放送局\r
+                        * 4 : 日付\r
+                        * 5 : 時刻\r
+                        * 6 : デバイス\r
+                        * 7 : 画質\r
+                        * 8 : 音質\r
+                        */\r
+                       String[] d = new String[9];\r
+                       mb = Pattern.compile("<(td|TD).*?>(.*?)</(td|TD)>").matcher(buf);\r
+                       for (int i=0; i<d.length; i++) {\r
+                               if ( mb.find()) {\r
+                                       d[i] = mb.group(2);\r
+                               }\r
+                               //System.out.println(i+") "+d[i]);\r
+                       }\r
+                       \r
+                       // 予約ID\r
+                       entry.setId(String.valueOf(Integer.valueOf(d[0])-1));\r
+                       \r
+                       // 予約実行ON/OFF\r
+                       mb = Pattern.compile("check_off\\.gif").matcher(d[1]);\r
+                       if (mb.find()) {\r
+                               entry.setExec(false);\r
+                       }\r
+                       \r
+                       // 予約名のエスケープを解除する\r
+                       String title = CommonUtils.unEscape(d[2]).replaceAll("<[bB][rR]>","");\r
+                       mb = Pattern.compile("<[aA] .*?>(.+?)</[aA]>").matcher(title);\r
+                       if (mb.find()) title = mb.group(1);\r
+                       entry.setTitle(title);\r
+                       entry.setTitlePop(TraceProgram.replacePop(title));\r
+                       \r
+                       entry.setRec_pattern(d[4]);\r
+                       entry.setRec_pattern_id(getRec_pattern_Id(entry.getRec_pattern()));\r
+                       mb = Pattern.compile("(\\d\\d):(\\d\\d).*?(\\d\\d):(\\d\\d)").matcher(d[5]);\r
+                       if (mb.find()) {\r
+                               entry.setAhh(mb.group(1));\r
+                               entry.setAmm(mb.group(2));\r
+                               entry.setZhh(mb.group(3));\r
+                               entry.setZmm(mb.group(4));\r
+                       }\r
+                       entry.setRec_nextdate(CommonUtils.getNextDate(entry));\r
+                       entry.setRec_min(CommonUtils.getRecMin(entry.getAhh(), entry.getAmm(), entry.getZhh(), entry.getZmm()));\r
+                       getStartEndDateTime(entry);\r
+                       \r
+                       entry.setChannel(d[3]);\r
+                       entry.setCh_name(getChCode().getCH_REC2WEB(entry.getChannel()));\r
+                       \r
+                       entry.setRec_audio(d[8]);\r
+                       entry.setRec_mode(d[7]);\r
+                       \r
+                       // 予約一覧からはとれない [本体で予約名や時刻を変えてしまうとアウト]\r
+                       /* setReservesV1()内に移動 */\r
+                       \r
+                       // 予約情報を保存\r
+                       newReserveList.add(entry);\r
+               }\r
+               \r
+               return(newReserveList);\r
+       }\r
+       \r
+       //\r
+       @Override\r
+       protected String joinPoststr(Hashtable<String, String> pdat)\r
+       {\r
+               System.out.println("H2's joinPoststr()");\r
+               \r
+               String[] pkeys = {\r
+                       "bExec",\r
+                       "title_name",\r
+                       "genre",\r
+                       "channel",\r
+                       "date",\r
+                       "start_hour",\r
+                       "start_minute",\r
+                       "end_hour",\r
+                       "end_minute",\r
+                       "disc",\r
+                       "vrate",\r
+                       "amode",\r
+                       "folder",                               // for X5\r
+                       "dvdr",\r
+                       "lVoice",                               // for X5\r
+                       "bVPerform",\r
+                       "bAutoChapter",\r
+                       "bAudioAutoChapter",    // for X5\r
+                       "detail",\r
+                       "dtv_sid",\r
+                       "dtv_nid",\r
+                       "net_link",                             // for X5\r
+                       "add_ch_text",                  // for H2\r
+                       "add_ch_value",                 // for H2\r
+                       "end_form"\r
+               };\r
+               \r
+               String pstr = "";\r
+               for ( String key : pkeys ) {\r
+                       if ( ! pdat.containsKey(key)) {\r
+                               pstr += key+"=&";\r
+                       }\r
+                       else {\r
+                               pstr += key+"="+pdat.get(key)+"&";\r
+                       }\r
+               }\r
+               pstr = pstr.substring(0, pstr.length()-1);\r
+               \r
+               return(pstr);\r
+       }\r
+}\r
diff --git a/TinyBannavi/src/tainavi/PlugIn_RecRD_M190.java b/TinyBannavi/src/tainavi/PlugIn_RecRD_M190.java
new file mode 100644 (file)
index 0000000..a5f9f7a
--- /dev/null
@@ -0,0 +1,774 @@
+package tainavi;\r
+\r
+import java.io.File;\r
+import java.net.Authenticator;\r
+import java.util.ArrayList;\r
+import java.util.Calendar;\r
+import java.util.GregorianCalendar;\r
+import java.util.HashMap;\r
+import java.util.regex.Matcher;\r
+import java.util.regex.Pattern;\r
+\r
+\r
+/**\r
+ * REGZAテレビ系\r
+ */\r
+public class PlugIn_RecRD_M190 extends HDDRecorderUtils implements HDDRecorder,Cloneable {\r
+\r
+       public PlugIn_RecRD_M190 clone() {\r
+               return (PlugIn_RecRD_M190) super.clone();\r
+       }\r
+       \r
+       //private static final String thisEncoding = "MS932";\r
+\r
+       /* 必須コード  - ここから */\r
+       \r
+       /*******************************************************************************\r
+        * 種族の特性\r
+        ******************************************************************************/\r
+       \r
+       public String getRecorderId() { return "REGZA DBR-M190"; }\r
+       public RecType getType() { return RecType.RECORDER; }\r
+       \r
+       // 繰り返し予約をサポートしていない\r
+       @Override\r
+       public boolean isRepeatReserveSupported() { return false; }\r
+       // 番組追従が可能\r
+       @Override\r
+       public boolean isPursuesEditable() { return true; }\r
+       // chvalueをつかってもいーよ\r
+       @Override\r
+       public boolean isChValueAvailable() { return true; }\r
+       // CHコードは入力しなくていい\r
+       @Override\r
+       public boolean isChCodeNeeded() { return false; }\r
+       \r
+       /*******************************************************************************\r
+        * 予約ダイアログなどのテキストのオーバーライド\r
+        ******************************************************************************/\r
+       \r
+       @Override\r
+       public String getLabel_Videorate() { return "予約方式"; }\r
+       @Override\r
+       public String getLabel_MsChapter() { return "放送時間連動"; }\r
+       @Override\r
+       public String getLabel_MvChapter() { return "マジックチャプタ"; }\r
+       \r
+       @Override\r
+       public String getChDatHelp() { return\r
+                       "「レコーダの放送局名」は、予約一覧取得が正常に完了していれば設定候補がコンボボックスで選択できるようになります。"+\r
+                       "";\r
+       }\r
+       \r
+       /*******************************************************************************\r
+        * 定数\r
+        ******************************************************************************/\r
+       \r
+       protected String getDefFile() { return "env/rzparam_m190.def"; }\r
+       \r
+       private static final String RSVINFOEXPR = "([0-9a-f]+?) (\\d\\d\\d\\d-\\d\\d-\\d\\dT(\\d\\d):(\\d\\d):\\d\\d) (\\d\\d\\d\\d-\\d\\d-\\d\\dT(\\d\\d):(\\d\\d):\\d\\d) (.+?) (.+?) (.+?) (.+?)[\\r\\n]";\r
+\r
+       private static final String ITEM_REQUEST_TYPE_DT  = "日時指定予約";\r
+       private static final String ITEM_REQUEST_TYPE_RZ  = "RZスケジューラ予約";\r
+       private static final String ITEM_REQUEST_TYPE_DTX = "本体予約(日時)\0#ff0000";\r
+       private static final String ITEM_REQUEST_TYPE_HON = "本体予約\0#ff0000";\r
+       private static final String ITEM_REQUEST_TYPE_REN = "連ドラ予約\0#ff0000";\r
+\r
+       private static final String ID_REQUEST_TYPE_DT   = "00000000";\r
+       private static final String ID_REQUEST_TYPE_RZ   = "00000004";\r
+       private static final String ID_REQUEST_TYPE_DTX  = "00000008";\r
+       private static final String ID_REQUEST_TYPE_HON  = "0000000c";\r
+       private static final String ID_REQUEST_TYPE_REN  = "0000000d";\r
+\r
+       //private static final String ITEM_FOLLW_OFF  = "切";\r
+       private static final String ITEM_FOLLW_ON   = "入";\r
+       \r
+       // ログ関連\r
+       \r
+       private final String MSGID = "["+getRecorderId()+"] ";\r
+       private final String ERRID = "[ERROR]"+MSGID;\r
+       private final String DBGID = "[DEBUG]"+MSGID;\r
+       \r
+       /*******************************************************************************\r
+        * CHコード設定、エラーメッセージ\r
+        ******************************************************************************/\r
+\r
+       public ChannelCode getChCode() {\r
+               return cc;\r
+       }\r
+       private ChannelCode cc = new ChannelCode(getRecorderId());\r
+       \r
+       public String getErrmsg() {\r
+               return(errmsg.replaceAll("\\\\r\\\\n", ""));\r
+       }\r
+       protected void setErrmsg(String s) { errmsg = s; }\r
+       private String errmsg = "";\r
+\r
+       /*******************************************************************************\r
+        * フリーオプション関係\r
+        ******************************************************************************/\r
+       \r
+       // ないよ\r
+       \r
+       /*******************************************************************************\r
+        * 部品\r
+        ******************************************************************************/\r
+\r
+       private String rsvedFile = "";\r
+       \r
+       /*******************************************************************************\r
+        * コンストラクタ\r
+        ******************************************************************************/\r
+       \r
+       // ないよ\r
+       \r
+       /*******************************************************************************\r
+        * チャンネルリモコン機能\r
+        ******************************************************************************/\r
+\r
+       public boolean ChangeChannel(String Channel) {\r
+               return false;\r
+       }\r
+\r
+       @Override\r
+       public void shutdown() {\r
+               // おまじない\r
+               Authenticator.setDefault(new MyAuthenticator(getUser(), getPasswd()));\r
+               //\r
+               reqGET("http://"+getIPAddr()+":"+getPortNo()+"/remote/remote.htm?key=40BF12", null);\r
+               //\r
+               System.out.println("send shutdown request to "+getBroadcast()+","+getMacAddr());\r
+       }\r
+\r
+       /*******************************************************************************\r
+        * レコーダーから予約一覧を取得する\r
+        ******************************************************************************/\r
+       \r
+       public boolean GetRdReserve(boolean force)\r
+       {\r
+               System.out.println("レコーダから予約一覧を取得します("+force+"): "+getRecorderId()+"("+getIPAddr()+":"+getPortNo()+")");\r
+\r
+               errmsg = "";\r
+               \r
+               String defFile = getDefFile();\r
+               \r
+               // 選択肢はREGZAからとれないので定義ファイルに固定で記述\r
+               String res = CommonUtils.read4file(defFile, false);\r
+               if ( res == null ) {\r
+                       errmsg = ERRID+"設定ファイルが取得できない: "+defFile;\r
+                       return false;\r
+               }\r
+               vrate = new ArrayList<TextValueSet>();\r
+               device = new ArrayList<TextValueSet>();\r
+               mvchapter = new ArrayList<TextValueSet>();\r
+               mschapter = new ArrayList<TextValueSet>();\r
+               for ( String s : res.split("[\r\n]+") ) {\r
+                       String[] b = s.split(",");\r
+                       if ( b.length >= 3 ) {\r
+                               TextValueSet t = new TextValueSet() ;\r
+                               t.setText(b[1]) ;\r
+                               t.setValue(b[2]) ;\r
+                               \r
+                               if ( b[0].equals("10") ) {\r
+                                       vrate.add(t) ;\r
+                               }\r
+                               else if ( b[0].equals("12") ) {\r
+                                       device.add(t) ;\r
+                               }\r
+                               else if ( b[0].equals("19") ) {\r
+                                       mvchapter.add(t) ;\r
+                               }\r
+                               else if ( b[0].equals("102") ) {\r
+                                       mschapter.add(t) ;\r
+                               }\r
+                       }\r
+               }\r
+               \r
+               // 予約一覧をキャッシュから\r
+               rsvedFile = "env/reserved."+getIPAddr()+"_"+getPortNo()+"_"+getRecorderId()+".xml";\r
+               \r
+               String chValueTFile = "env/chvalue."+getIPAddr()+"_"+getPortNo()+"_"+getRecorderId()+".xml";\r
+               \r
+               File f = new File(rsvedFile);\r
+               File g = new File(chValueTFile);\r
+               if ( force == false && f.exists() && g.exists() ) {\r
+                       // キャッシュから読み出し(予約一覧)\r
+                       ArrayList<ReserveList> newrl = ReservesFromFile(rsvedFile);\r
+                       ArrayList<TextValueSet> newcv = TVSload(chValueTFile);\r
+                       if ( newrl != null && (newcv != null && newcv.size() > 0) ) {\r
+                               setReserves(newrl);\r
+                               chvalue = newcv;\r
+                               return true;\r
+                       }\r
+               }\r
+\r
+               // 放送局名一覧の取得\r
+               if ( ! getRecChNameList() ) {\r
+                       errmsg = ERRID+"放送局一覧が取得できませんでした。対応していないレコーダ/テレビなのかもしれません。";\r
+                       return false;\r
+               }\r
+               \r
+               TVSsave(chvalue, chValueTFile);\r
+               // 予約一覧の取得・キャッシュへの保存\r
+               reportProgress("予約一覧を取得します(1/1).");\r
+               ArrayList<ReserveList> newReserveList = getRegzaReserveList();\r
+               if ( newReserveList == null ) {\r
+                       return(false);\r
+               }\r
+\r
+               // リストの置き換え\r
+               setReserves(newReserveList);\r
+               \r
+               // 詳細情報の取得\r
+               System.out.println("========");\r
+               for (int i=0; i<getReserves().size(); i++) {\r
+                       ReserveList e = getReserves().get(i);\r
+                       \r
+                       reportProgress(String.format("[%s] %s\t%s\t%s\t%s:%s\t%s:%s\t%sm\t%s\t%s\t%s\t%s",\r
+                                       (i+1), e.getId(), e.getRec_pattern(), e.getRec_nextdate(), e.getAhh(), e.getAmm(), e.getZhh(),  e.getZmm(),     e.getRec_min(), e.getRec_mode(), e.getTitle(), e.getChannel(), e.getCh_name()));\r
+               }\r
+               System.out.println("========");\r
+\r
+               // キャッシュファイルに保存\r
+               ReservesToFile(getReserves(), rsvedFile);\r
+               \r
+               return(true);\r
+       }\r
+       \r
+       /*******************************************************************************\r
+        * 新規予約\r
+        ******************************************************************************/\r
+       \r
+       public boolean PostRdEntry(ReserveList r) {\r
+               \r
+               System.out.println("Run: PostRdEntry("+r.getTitle()+")");\r
+\r
+               errmsg = "";\r
+               \r
+               //\r
+               if (cc.getCH_WEB2CODE(r.getCh_name()) == null) {\r
+                       errmsg = "【警告】Web番組表の放送局名「"+r.getCh_name()+"」をCHコードに変換できません。CHコード設定を修正してください。" ;\r
+                       System.out.println(errmsg);\r
+                       return(false);\r
+               }\r
+               \r
+               GregorianCalendar c = CommonUtils.getCalendar(r.getRec_pattern());\r
+               if (c == null) {\r
+                       errmsg = "【警告】日付指定しか利用出来ません。" ;\r
+                       return(false) ;\r
+               }\r
+               \r
+               //\r
+               int stepc = 1;\r
+               int steps = (r.getRec_mode().equals(ITEM_REQUEST_TYPE_DT))?(3):(4);\r
+               \r
+               // おまじない\r
+               Authenticator.setDefault(new MyAuthenticator(getUser(), getPasswd()));\r
+               \r
+               String header;\r
+               String response;\r
+               \r
+               // 現在の予約IDのリストを作る\r
+               ArrayList<ReserveList> oldrl = null;\r
+               {\r
+                       reportProgress(String.format("%s(%d/%d).","予約登録前の予約一覧を取得します",stepc++,steps));\r
+                       \r
+                       oldrl = getRegzaReserveList();\r
+                       if ( oldrl == null ) {\r
+                               return(false);\r
+                       }\r
+               }\r
+               \r
+               // 予約パターンID\r
+               // 次回予定日\r
+               // 録画長\r
+               // 開始日時・終了日時\r
+               r.setRec_pattern_id(getRec_pattern_Id(r.getRec_pattern()));\r
+               r.setRec_nextdate(CommonUtils.getNextDate(r));\r
+               r.setRec_min(CommonUtils.getRecMin(r.getAhh(),r.getAmm(),r.getZhh(),r.getZmm()));\r
+               getStartEndDateTime(r);\r
+               \r
+               if ( r.getRec_mode().equals(ITEM_REQUEST_TYPE_RZ) ) {\r
+                       if ( r.getRec_mschapter().equals(ITEM_FOLLW_ON) ) {\r
+                               r.setPursues(true);\r
+                       }\r
+               }\r
+               \r
+               // RDへ情報送信\r
+               if ( r.getRec_mode().equals(ITEM_REQUEST_TYPE_DT) ) {\r
+                       // POSTデータを変換する\r
+                       HashMap<String, String> pdat = modPostdata(r);\r
+\r
+                       // RDへの情報作成\r
+                       String pstr = joinPoststrRsvDate(pdat);\r
+\r
+                       reportProgress(String.format("%s(%d/%d).","予約を登録します",stepc++,steps));\r
+                       \r
+                       String[] d = reqGET("http://"+getIPAddr()+":"+getPortNo()+"/remote/rsvdate.htm?"+pstr, null);\r
+                       header = d[0];\r
+                       response = d[1];\r
+                       \r
+                       if (response == null) {\r
+                               errmsg = "レコーダーが反応しません";\r
+                               return(false);\r
+                       }\r
+                       \r
+                       if ( ! response.substring(0,1).equals("0") ) {\r
+                               errmsg = "予約の登録に失敗しました。";\r
+                               return(false);\r
+                       }\r
+               }\r
+               else {\r
+                       \r
+                       {\r
+                               // POSTデータを変換する\r
+                               HashMap<String, String> pdat = modPostdata(r);\r
+                               \r
+                               // RDへの情報作成\r
+                               String pstr = joinPoststrRsvCnv(pdat);\r
+                               \r
+                               reportProgress(String.format("%s(%d/%d).","番組IDを取得します",stepc++,steps));\r
+                               \r
+                               String[] d = reqGET("http://"+getIPAddr()+":"+getPortNo()+"/remote/rsvcnv.htm?"+pstr, null);\r
+                               header = d[0];\r
+                               response = d[1];\r
+                               \r
+                               if (response == null) {\r
+                                       errmsg = "レコーダーが反応しません";\r
+                                       return(false);\r
+                               }\r
+                               \r
+                               Matcher ma = Pattern.compile("content_id=(.+?)[\\n\\r]+?start_time=(.+?)[\\n\\r]+?end_time=(.+?)[\\n\\r]+?title_name=(.*?)[\\n\\r]").matcher(response);\r
+                               if ( ! ma.find() ) {\r
+                                       errmsg = "番組IDの取得に失敗しました。";\r
+                                       return(false);\r
+                               }\r
+                               \r
+                               r.setContentId(ma.group(1));\r
+                               \r
+                               GregorianCalendar ca = CommonUtils.getCalendar(ma.group(2));\r
+                               GregorianCalendar cz = CommonUtils.getCalendar(ma.group(3));\r
+                               r.setRec_pattern(CommonUtils.getDate(ca));\r
+                               System.err.println(CommonUtils.getDateTime(ca));\r
+                               r.setAhh(String.format("%02d",ca.get(Calendar.HOUR_OF_DAY)));\r
+                               r.setAmm(String.format("%02d",ca.get(Calendar.MINUTE)));\r
+                               r.setZhh(String.format("%02d",cz.get(Calendar.HOUR_OF_DAY)));\r
+                               r.setZmm(String.format("%02d",cz.get(Calendar.MINUTE)));\r
+                               r.setRec_pattern_id(getRec_pattern_Id(r.getRec_pattern()));\r
+                               r.setRec_nextdate(CommonUtils.getNextDate(r));\r
+                               r.setRec_min(CommonUtils.getRecMin(r.getAhh(),r.getAmm(),r.getZhh(),r.getZmm()));\r
+                               getStartEndDateTime(r);\r
+                               \r
+                               r.setTitle(ma.group(4));\r
+                               r.setTitlePop(TraceProgram.replacePop(r.getTitle()));\r
+                       }\r
+                       \r
+                       {\r
+                               // POSTデータを変換する\r
+                               HashMap<String, String> pdat = modPostdata(r);\r
+                               \r
+                               // RDへの情報作成\r
+                               String pstr = joinPoststrRsvId(pdat);\r
+                               \r
+                               reportProgress(String.format("%s(%d/%d).","予約を登録します",stepc++,steps));\r
+                               \r
+                               String[] d = reqGET("http://"+getIPAddr()+":"+getPortNo()+"/remote/rsvid.htm?"+pstr, null);\r
+                               header = d[0];\r
+                               response = d[1];\r
+                               \r
+                               if ( ! response.substring(0,1).equals("0") ) {\r
+                                       errmsg = "予約の登録に失敗しました。";\r
+                                       return(false);\r
+                               }\r
+                       }\r
+                       \r
+                       errmsg = "REGZA本体からの情報で内容が変更されました: "+r.getStartDateTime()+"~"+r.getZhh()+":"+r.getZmm()+" "+r.getTitle();\r
+               }\r
+\r
+               // 予約IDのリフレッシュ\r
+               ArrayList<ReserveList> newrl = null;\r
+               {\r
+                       reportProgress(String.format("%s(%d/%d).","予約IDを取得します",stepc++,steps));\r
+                       \r
+                       newrl = getRegzaReserveList();\r
+                       if ( newrl == null ) {\r
+                               return(false);\r
+                       }\r
+                       \r
+                       ReserveList id = null;\r
+                       for ( ReserveList newid : newrl ) {\r
+                               id = newid;\r
+                               for ( ReserveList oldid : oldrl ) {\r
+                                       if ( oldid.getId().equals(newid.getId()) ) {\r
+                                               id = null;\r
+                                               break;\r
+                                       }\r
+                               }\r
+                               if ( id != null ) {\r
+                                       r.setId(id.getId());\r
+                                       newrl.remove(id);\r
+                                       newrl.add(r);\r
+                                       break;\r
+                               }\r
+                       }\r
+                       if ( id == null ) {\r
+                               errmsg = "予約IDの取得に失敗しました。";\r
+                               return(false);\r
+                       }\r
+               }\r
+               \r
+               // 予約リストを更新\r
+               setReserves(newrl);\r
+               \r
+               // キャッシュに保存\r
+               ReservesToFile(getReserves(), rsvedFile);\r
+               \r
+               System.out.printf("\n<<< Message from RD >>> \"%s\"\n\n", "正常に登録できました。");\r
+               return(true);\r
+       }\r
+\r
+       /*******************************************************************************\r
+        * 予約更新\r
+        ******************************************************************************/\r
+       \r
+       public boolean UpdateRdEntry(ReserveList o, ReserveList r) {\r
+               \r
+               System.out.println("Run: UpdateRdEntry()");\r
+               \r
+               errmsg = "";\r
+\r
+               //\r
+               if ( ! r.getModEnabled() ) {\r
+                       errmsg = "【警告】鯛ナビから予約したエントリ以外は更新できません。" ;\r
+                       return(false);\r
+               }\r
+               \r
+               GregorianCalendar c = CommonUtils.getCalendar(r.getRec_pattern());\r
+               if (c == null) {\r
+                       errmsg = "【警告】日付指定しか利用出来ません。" ;\r
+                       return(false) ;\r
+               }\r
+               \r
+               reportProgress("既存の予約を削除します<1/2>.");\r
+               if ( RemoveRdEntry(o.getId()) == null ) {\r
+                       return(false);\r
+               }\r
+\r
+               reportProgress("新規の予約を登録します<2/2>.");\r
+               if ( PostRdEntry(r) == false ) {\r
+                       return(false);\r
+               }\r
+\r
+               System.out.printf("\n<<< Message from RD >>> \"%s\"\n\n", "正常に更新できました。");\r
+               return(true);\r
+       }\r
+\r
+       /*******************************************************************************\r
+        * 予約削除\r
+        ******************************************************************************/\r
+       \r
+       public ReserveList RemoveRdEntry(String delid) {\r
+               \r
+               System.out.println("Run: RemoveRdEntry()");\r
+               \r
+               errmsg = "";\r
+\r
+               // 削除対象を探す\r
+               ReserveList rx = null;\r
+               for (  ReserveList reserve : getReserves() )  {\r
+                       if (reserve.getId().equals(delid)) {\r
+                               rx = reserve;\r
+                               break;\r
+                       }\r
+               }\r
+               if (rx == null) {\r
+                       return(null);\r
+               }\r
+\r
+               // おまじない\r
+               Authenticator.setDefault(new MyAuthenticator(getUser(), getPasswd()));\r
+\r
+               String header;\r
+               String response;\r
+\r
+               // RDに削除要請\r
+               {               \r
+                       reportProgress("予約を削除します(1/1).");\r
+                       String[] d = reqGET("http://"+getIPAddr()+":"+getPortNo()+"/remote/rsvdel.htm?reserve_id="+rx.getId(), null);\r
+                       header = d[0];\r
+                       response = d[1];\r
+                       \r
+                       if (response == null) {\r
+                               errmsg = "レコーダーが反応しません";\r
+                               return(null);\r
+                       }\r
+                       \r
+                       if ( ! response.substring(0,1).equals("0") ) {\r
+                               errmsg = "予約の削除に失敗しました。";\r
+                               return(null);\r
+                       }\r
+               }\r
+\r
+               // 予約リストを更新\r
+               getReserves().remove(rx);\r
+               \r
+               // キャッシュに保存\r
+               ReservesToFile(getReserves(), rsvedFile);\r
+               \r
+               System.out.printf("\n<<< Message from RD >>> \"%s\"\n\n", "正常に削除できました。");\r
+               return(rx);\r
+       }\r
+       \r
+\r
+       \r
+       /* 個別コード-ここから最後まで */\r
+\r
+       /*******************************************************************************\r
+        * 非公開メソッド\r
+        ******************************************************************************/\r
+       \r
+       /*\r
+        *      予約一覧を取得する \r
+        */\r
+       private ArrayList<ReserveList> getRegzaReserveList() {\r
+               return _getRegzaReserveList("/remote/rsvlist.htm");\r
+       }\r
+       private ArrayList<ReserveList> _getRegzaReserveList(String html) {\r
+               \r
+               // おまじない\r
+               Authenticator.setDefault(new MyAuthenticator(getUser(), getPasswd()));\r
+               \r
+               String header="";\r
+               String response="";\r
+               \r
+               // RDから予約一覧を取り出す\r
+               {\r
+                       String[] d = reqGET("http://"+getIPAddr()+":"+getPortNo()+html,null);\r
+                       header = d[0];\r
+                       response = d[1];\r
+                       \r
+                       if (response == null) {\r
+                               errmsg = "レコーダーが反応しません";\r
+                               return(null);\r
+                       }\r
+                       \r
+                       if ( ! response.substring(0,1).equals("0") ) {\r
+                               errmsg = "予約一覧の取得に失敗しました。";\r
+                               return(null);\r
+                       }\r
+               }\r
+               \r
+               // 旧リストは全部削除\r
+               ArrayList<ReserveList> newReserveList = new ArrayList<ReserveList>();\r
+               \r
+               // 予約詳細を作る\r
+               ReserveList entry = new ReserveList();\r
+               Matcher ma = Pattern.compile(RSVINFOEXPR).matcher(response);\r
+               while (ma.find()) {\r
+                       \r
+                       // 予約番号\r
+                       entry.setId(ma.group(1));\r
+                       \r
+                       // 開始終了日時と録画時間・パターン\r
+                       GregorianCalendar ca = CommonUtils.getCalendar(ma.group(2));\r
+                       //GregorianCalendar cz = CommonUtils.getCalendar(ma.group(5));\r
+                       entry.setRec_pattern(CommonUtils.getDate(ca));\r
+                       entry.setAhh(ma.group(3));\r
+                       entry.setAmm(ma.group(4));\r
+                       entry.setZhh(ma.group(6));\r
+                       entry.setZmm(ma.group(7));\r
+                       entry.setRec_pattern_id(getRec_pattern_Id(entry.getRec_pattern()));\r
+                       entry.setRec_nextdate(CommonUtils.getNextDate(entry));\r
+                       entry.setRec_min(String.valueOf(CommonUtils.getDiffDateTime(ma.group(2),ma.group(5))/60000L));\r
+                       getStartEndDateTime(entry);\r
+                       \r
+                       // チャンネル\r
+                       entry.setChannel(ma.group(8));\r
+                       entry.setCh_name(cc.getCH_CODE2WEB(entry.getChannel()));\r
+                       \r
+                       // 録画先\r
+                       entry.setRec_device(ma.group(9));\r
+                       \r
+                       // 本体予約\r
+                       if ( ma.group(10).equals(ID_REQUEST_TYPE_DT) ) {\r
+                               entry.setModEnabled(true);\r
+                               entry.setRec_mode(ITEM_REQUEST_TYPE_DT);\r
+                       }\r
+                       else if ( ma.group(10).equals(ID_REQUEST_TYPE_RZ) ) {\r
+                               entry.setModEnabled(true);\r
+                               entry.setRec_mode(ITEM_REQUEST_TYPE_RZ);\r
+                       }\r
+                       else if ( ma.group(10).equals(ID_REQUEST_TYPE_DTX) ) {\r
+                               entry.setModEnabled(false);\r
+                               entry.setRec_mode(ITEM_REQUEST_TYPE_DTX);\r
+                       }\r
+                       else if ( ma.group(10).equals(ID_REQUEST_TYPE_HON) ) {\r
+                               entry.setModEnabled(false);\r
+                               entry.setRec_mode(ITEM_REQUEST_TYPE_HON);\r
+                       }\r
+                       else if ( ma.group(10).equals(ID_REQUEST_TYPE_REN) ) {\r
+                               entry.setModEnabled(false);\r
+                               entry.setRec_mode(ITEM_REQUEST_TYPE_REN);\r
+                       }\r
+                       else {\r
+                               entry.setModEnabled(false);\r
+                               entry.setRec_mode(String.format("★未定義(%s)", ma.group(10)));\r
+                       }\r
+\r
+                       // タイトル\r
+                       String title = CommonUtils.unEscape(ma.group(11)).replaceAll("<BR>","");\r
+                       entry.setTitle(title);\r
+                       entry.setTitlePop(TraceProgram.replacePop(title));\r
+\r
+                       // タイトル自動補完フラグなど本体からは取得できない情報を引き継ぐ\r
+                       copyAttributes(entry, getReserves());\r
+                       \r
+                       // 予約情報を保存\r
+                       newReserveList.add(entry.clone());\r
+               }\r
+               \r
+               return(newReserveList);\r
+       }\r
+\r
+       /**\r
+        * レコーダの放送局名&CHコードをログに出力する\r
+        */\r
+       private boolean getRecChNameList() {\r
+               \r
+               // おまじない\r
+               Authenticator.setDefault(new MyAuthenticator(getUser(), getPasswd()));\r
+               \r
+               String url = "http://"+getIPAddr()+":"+getPortNo()+"/remote/channel2.htm";\r
+               String[] d = reqGET(url, null);\r
+               if (d[1] == null) {\r
+                       errmsg = "レコーダーが反応しません: "+url;\r
+                       return false;\r
+               }\r
+               \r
+               ArrayList<TextValueSet> newcv = new ArrayList<TextValueSet>();\r
+               for ( String s : d[1].split("[\\r\\n]+") ) {\r
+                       String[] e = s.split("\\s+",4);\r
+                       if ( e.length == 4 ) {\r
+                               TextValueSet t = new TextValueSet();\r
+                               t.setText(e[3]);\r
+                               t.setValue(e[0]);\r
+                               newcv.add(t);\r
+                       }\r
+               }\r
+               if ( newcv.size() == 0 ) {\r
+                       errmsg = "放送局リストが取得できませんでした。非対応機種なのかもしれません: "+url;\r
+                       return false;\r
+               }\r
+               chvalue = newcv;\r
+               \r
+               if (getDebug()) {\r
+                       System.err.println("=== CHコード一覧 for "+getRecorderId()+" ===");\r
+                       for ( TextValueSet t : chvalue ) {\r
+                               System.err.println(String.format("レコーダの放送局名&CHコード=\"%s\" 放送局=%s",t.getValue(),t.getText()));\r
+                       }\r
+                       System.err.println("=============================");\r
+               }\r
+               \r
+               return true;\r
+       }\r
+       \r
+       // レコーダーから取得できない情報は直接コピー\r
+       @Override\r
+       protected void copyAttributes(ReserveList entry, ArrayList<ReserveList> reserves) {\r
+               for ( ReserveList e : reserves ) {\r
+                       if ( e.getId().equals(entry.getId()) ) {\r
+                               // 鯛ナビの内部フラグ\r
+                               entry.setAutocomplete(e.getAutocomplete());\r
+                               // 予約一覧からは取得できない情報\r
+                               if ( ! e.getModEnabled() && ! e.getTitle().equals("") ) {\r
+                                       entry.setTitle(e.getTitle());\r
+                                       entry.setTitlePop(e.getTitlePop());\r
+                               }\r
+                               entry.setDetail(e.getDetail());\r
+                               entry.setRec_genre(e.getRec_genre());\r
+                               entry.setRec_mschapter(e.getRec_mschapter());\r
+                               entry.setRec_mvchapter(e.getRec_mvchapter());\r
+                               return;\r
+                       }\r
+               }\r
+       }\r
+       private String joinPoststrRsvDate(HashMap<String, String> pdat) {\r
+               \r
+               String[] pkeys = {\r
+                       "start_time",\r
+                       "end_time",\r
+                       "ch_code",\r
+                       "media",\r
+                       "indexing"\r
+               };\r
+               \r
+               return _joinPoststr(pdat,pkeys);\r
+       }\r
+       private String joinPoststrRsvCnv(HashMap<String, String> pdat) {\r
+               \r
+               String[] pkeys = {\r
+                       "start_time",\r
+                       "ch_code"\r
+               };\r
+               \r
+               return _joinPoststr(pdat,pkeys);\r
+       }\r
+       private String joinPoststrRsvId(HashMap<String, String> pdat) {\r
+               \r
+               String[] pkeys = {\r
+                       "content_id",\r
+                       "follow",\r
+                       "media",\r
+                       "indexing"\r
+               };\r
+               \r
+               return _joinPoststr(pdat,pkeys);\r
+       }\r
+       private String _joinPoststr(HashMap<String, String> pdat, String[] pkeys) {\r
+               \r
+               String pstr = "";\r
+               for ( String key : pkeys ) {\r
+                       if (pdat.containsKey(key)) {\r
+                               pstr += key+"="+pdat.get(key)+"+";\r
+                       }\r
+               }\r
+               pstr = pstr.substring(0, pstr.length()-1);\r
+               \r
+               System.err.println("poststr: "+pstr);\r
+               \r
+               return(pstr);\r
+       }\r
+       \r
+       private HashMap<String, String> modPostdata(ReserveList r) {\r
+\r
+               HashMap<String, String> newdat = new HashMap<String, String>();\r
+               try {\r
+                       \r
+                       // 録画チャンネル\r
+                       newdat.put("ch_code", cc.getCH_WEB2CODE(r.getCh_name()));\r
+                       \r
+                       // 開始終了日時\r
+                       newdat.put("start_time", CommonUtils.getIsoDateTime(CommonUtils.getCalendar(r.getStartDateTime())));\r
+                       newdat.put("end_time", CommonUtils.getIsoDateTime(CommonUtils.getCalendar(r.getEndDateTime())));\r
+                       \r
+                       // 番組ID\r
+                       newdat.put("content_id", r.getContentId());\r
+                       \r
+                       // 番組追跡\r
+                       newdat.put("follow", text2value(mschapter, r.getRec_mschapter()));\r
+                       \r
+                       // 自動チャプター関連\r
+                       newdat.put("indexing", text2value(mvchapter, r.getRec_mvchapter()));\r
+                       \r
+                       // 記録先\r
+                       newdat.put("media", text2value(device, r.getRec_device()));\r
+                       \r
+                       // 保護\r
+                       //newdat.put("protect", text2value(autodel, r.getRec_autodel()));\r
+               }\r
+               catch (Exception e) {\r
+                       e.printStackTrace();\r
+               }\r
+               return(newdat);\r
+       }\r
+}\r
diff --git a/TinyBannavi/src/tainavi/PlugIn_RecRD_MAIL.java b/TinyBannavi/src/tainavi/PlugIn_RecRD_MAIL.java
new file mode 100644 (file)
index 0000000..5c05b5f
--- /dev/null
@@ -0,0 +1,360 @@
+package tainavi;\r
+\r
+import java.io.BufferedReader;\r
+import java.io.File;\r
+import java.io.FileNotFoundException;\r
+import java.io.FileReader;\r
+import java.io.IOException;\r
+import java.io.UnsupportedEncodingException;\r
+import java.util.Calendar;\r
+import java.util.Date;\r
+import java.util.GregorianCalendar;\r
+import java.util.Properties;\r
+\r
+import javax.mail.AuthenticationFailedException;\r
+import javax.mail.Authenticator;\r
+import javax.mail.Message;\r
+import javax.mail.MessagingException;\r
+import javax.mail.PasswordAuthentication;\r
+import javax.mail.Session;\r
+import javax.mail.Transport;\r
+import javax.mail.internet.InternetAddress;\r
+import javax.mail.internet.MimeMessage;\r
+\r
+\r
+public class PlugIn_RecRD_MAIL extends HDDRecorderUtils implements HDDRecorder,Cloneable {\r
+\r
+       public PlugIn_RecRD_MAIL clone() {\r
+               return (PlugIn_RecRD_MAIL) super.clone();\r
+       }\r
+       \r
+       /* 必須コード  - ここから */\r
+       \r
+       // 種族の特性\r
+       public String getRecorderId() { return "RD(Mail)"; }\r
+       public RecType getType() { return RecType.MAIL; }\r
+       \r
+       // 個体の特性\r
+       private ChannelCode cc = new ChannelCode(getRecorderId());\r
+       private String rsvedFile = "";\r
+       \r
+       private String errmsg = "";\r
+\r
+       protected String getDefFile() { return "env/mail.def"; }\r
+\r
+       // 公開メソッド\r
+       \r
+       /*\r
+        * \r
+        */\r
+       public String Myself() {\r
+               return("MAIL"+":"+getMacAddr()+":"+getRecorderId());\r
+       }\r
+       public ChannelCode getChCode() {\r
+               return cc;\r
+       }\r
+       \r
+       // 繰り返し予約をサポートしていない\r
+       @Override\r
+       public boolean isRepeatReserveSupported() { return false; }\r
+       \r
+       /*\r
+        * \r
+        */\r
+       public boolean ChangeChannel(String Channel) {\r
+               return false;\r
+       }\r
+\r
+       /*\r
+        *      レコーダーから予約一覧を取得する \r
+        */\r
+       public boolean GetRdReserve(boolean force)\r
+       {\r
+               errmsg = "";\r
+               \r
+               System.out.println("Through: GetRdReserve("+force+")");\r
+               \r
+               String defFile = getDefFile();\r
+               \r
+               FileReader fr = null;\r
+               BufferedReader r = null;\r
+               try {\r
+                       encoder.clear();\r
+                       vrate.clear();\r
+                       arate.clear();\r
+                       device.clear();\r
+                       dvdcompat.clear();\r
+                       aspect.clear();\r
+                       xchapter.clear();\r
+                       mschapter.clear();\r
+                       mvchapter.clear();\r
+                       \r
+                       fr = new FileReader(defFile);\r
+                       r = new BufferedReader(fr);\r
+                       String s ;\r
+                       while ( (s = r.readLine()) != null ) {\r
+                               String[] b = s.split(",");\r
+                               if ( b.length >= 3 ) {\r
+                                       TextValueSet t = new TextValueSet() ;\r
+                                       t.setText(b[1]) ;\r
+                                       t.setValue(b[2]) ;\r
+                                       \r
+                                       if ( b[0].equals("7") ) {\r
+                                               encoder.add(t) ;\r
+                                       }\r
+                                       else if ( b[0].equals("10") ) {\r
+                                               vrate.add(t) ;\r
+                                       }\r
+                                       else if ( b[0].equals("11") ) {\r
+                                               arate.add(t) ;\r
+                                       }\r
+                                       else if ( b[0].equals("12") ) {\r
+                                               device.add(t) ;\r
+                                       }\r
+                                       else if ( b[0].equals("14") ) {\r
+                                               dvdcompat.add(t) ;\r
+                                       }\r
+                                       else if ( b[0].equals("16") ) {\r
+                                               aspect.add(t) ;\r
+                                       }\r
+                                       else if ( b[0].equals("17") ) {\r
+                                               xchapter.add(t) ;\r
+                                       }\r
+                                       else if ( b[0].equals("18") ) {\r
+                                               mschapter.add(t) ;\r
+                                       }\r
+                                       else if ( b[0].equals("19") ) {\r
+                                               mvchapter.add(t) ;\r
+                                       }\r
+                               }\r
+                       }\r
+               } catch (FileNotFoundException e) {\r
+                       // TODO Auto-generated catch block\r
+                       e.printStackTrace();\r
+               } catch (IOException e) {\r
+                       // TODO Auto-generated catch block\r
+                       e.printStackTrace();\r
+               }\r
+               finally {\r
+                       if (r != null) try { r.close(); } catch (Exception e) {};\r
+                       if (fr != null) try { fr.close(); } catch (Exception e) {};\r
+               }\r
+               \r
+               // 予約一覧をキャッシュから\r
+               rsvedFile = "env/reserved."+getIPAddr()+"_"+getPortNo()+"_"+getRecorderId()+".xml";\r
+               \r
+               File f = new File(rsvedFile);\r
+               if ( force == false && f.exists()) {\r
+                       // キャッシュから読み出し(予約一覧)\r
+                       setReserves(ReservesFromFile(rsvedFile));\r
+                       if (getReserves().size() > 0) {\r
+                               System.out.println("+read from="+rsvedFile);\r
+                       }\r
+                       return(true);\r
+               }\r
+               \r
+               return(true);\r
+       }\r
+       \r
+       /*\r
+        *      予約を実行する\r
+        */\r
+       public boolean PostRdEntry(ReserveList r)\r
+       {\r
+               errmsg = "";\r
+               \r
+               //\r
+               if (cc.getCH_WEB2CODE(r.getCh_name()) == null) {\r
+                       errmsg = "【警告】Web番組表の放送局名「"+r.getCh_name()+"」をCHコードに変換できません。CHコード設定を修正してください。" ;\r
+                       System.out.println(errmsg);\r
+                       return(false);\r
+               }\r
+               \r
+               System.out.println("Run: PostRdEntry("+r.getTitle()+")");\r
+\r
+               // メール本文を作る\r
+               String msg = "";\r
+               String message = "";\r
+               \r
+               GregorianCalendar c = CommonUtils.getCalendar(r.getRec_pattern());\r
+               if (c == null) {\r
+                       errmsg = "日付指定しか利用出来ません。" ;\r
+                       return(false) ;\r
+               }\r
+               msg = getMailBody(r, this.getBroadcast());\r
+               \r
+               try {\r
+                       message = new String(msg.getBytes("MS932"),"Shift_JIS");\r
+               } catch (UnsupportedEncodingException e1) {\r
+                       // TODO Auto-generated catch block\r
+                       e1.printStackTrace();\r
+               }\r
+               \r
+               // メールを送信する\r
+               try {\r
+                       Properties props = new Properties() ;\r
+                       props.put("mail.smtp.host", this.getIPAddr()) ;\r
+                       props.put("mail.host", this.getIPAddr()) ;\r
+                       props.put("mail.smtp.port", this.getPortNo()) ;\r
+                       props.put("mail.smtp.auth", "true") ;\r
+                       props.put("mail.smtp.starttls.enable","true");\r
+                       \r
+                       Session session = Session.getInstance( props, new Authenticator() {\r
+                               protected PasswordAuthentication getPasswordAuthentication() {\r
+                                       return new PasswordAuthentication(getUser(), getPasswd()) ;\r
+                               }\r
+                       });\r
+                       //session.setDebug(true);\r
+                       MimeMessage mimeMessage = new MimeMessage(session);\r
+                       mimeMessage.setRecipients(Message.RecipientType.TO, this.getMacAddr());\r
+                       mimeMessage.setFrom(new InternetAddress(this.getUser()));\r
+                       mimeMessage.setSubject(new Date().toString());\r
+                       mimeMessage.setSentDate(new Date());\r
+                       //mimeMessage.setText(message, "ISO-2022-JP");\r
+                       mimeMessage.setContent(message, "text/html;charset=iso-2022-jp");\r
+                       mimeMessage.setHeader("Content-Transfer-Encoding", "7bit"); \r
+                       Transport.send(mimeMessage);\r
+        } catch (AuthenticationFailedException e) {\r
+               errmsg = "SMTP認証に失敗しました:"+e.toString() ;\r
+               return(false) ;\r
+        } catch (MessagingException e) {\r
+               errmsg = "メール送信に失敗しました:"+e.toString() ;\r
+               return(false) ;\r
+               }\r
+               \r
+               // 予約ID\r
+               long no = 0;\r
+               for (ReserveList x : getReserves()) {\r
+                       if (Long.valueOf(x.getId()) > no) {\r
+                               no = Long.valueOf(x.getId());\r
+                       }\r
+               }\r
+               \r
+               r.setId(String.valueOf(++no));\r
+               // 予約パターンID\r
+               r.setRec_pattern_id(getRec_pattern_Id(r.getRec_pattern()));\r
+               \r
+               // 次回予定日\r
+               r.setRec_nextdate(CommonUtils.getNextDate(r));\r
+               \r
+               // 録画長\r
+               r.setRec_min(CommonUtils.getRecMin(r.getAhh(),r.getAmm(),r.getZhh(),r.getZmm()));\r
+               \r
+               // 開始日時・終了日時\r
+               getStartEndDateTime(r);\r
+               \r
+               // 予約リストを更新\r
+               getReserves().add(r);\r
+               \r
+               // キャッシュに保存\r
+               ReservesToFile(getReserves(), rsvedFile);\r
+               \r
+               return(true);\r
+       }\r
+       protected String getMailBody(ReserveList r, String passwd) {\r
+               GregorianCalendar c = CommonUtils.getCalendar(r.getRec_pattern());\r
+               \r
+               StringBuilder sb = new StringBuilder();\r
+               \r
+               sb.append("open");\r
+               sb.append(" ");\r
+               sb.append(this.getBroadcast());\r
+               sb.append(" ");\r
+               sb.append("prog ");\r
+               sb.append("add ");\r
+               sb.append(String.format("%04d%02d%02d ",c.get(Calendar.YEAR),c.get(Calendar.MONTH)+1,c.get(Calendar.DATE))+" ");\r
+               sb.append(" ");\r
+               sb.append(r.getAhh()+r.getAmm());\r
+               sb.append(" ");\r
+               sb.append(r.getZhh()+r.getZmm());\r
+               sb.append(" ");\r
+               sb.append(cc.getCH_WEB2CODE(r.getCh_name()));\r
+               sb.append(" ");\r
+               sb.append(text2value(encoder,r.getTuner()));\r
+               sb.append(" ");\r
+               sb.append(text2value(vrate,r.getRec_mode()));\r
+               sb.append(" ");\r
+               if (r.getRec_mode().indexOf("[TS") != 0) {\r
+                       sb.append(text2value(arate,r.getRec_audio()));\r
+                       sb.append(" ");\r
+               }\r
+               sb.append(text2value(device,r.getRec_device()));\r
+               sb.append(" ");\r
+               sb.append(text2value(dvdcompat,r.getRec_dvdcompat()));\r
+               String chapter_mode = text2value(xchapter,r.getRec_xchapter());\r
+               if (chapter_mode != null) {\r
+                       sb.append(" ");\r
+                       sb.append(chapter_mode);\r
+               }\r
+               chapter_mode = text2value(mvchapter,r.getRec_mvchapter());\r
+               if (chapter_mode != null) {\r
+                       sb.append(" ");\r
+                       sb.append(chapter_mode);\r
+               }\r
+               else {\r
+                       sb.append(" ");\r
+                       sb.append("CPN");       // マジックチャプターOFFをつけないとエラー発生\r
+               }\r
+               chapter_mode = text2value(mschapter,r.getRec_mschapter());\r
+               if (chapter_mode != null) {\r
+                       sb.append(" ");\r
+                       sb.append(chapter_mode);\r
+               }\r
+               sb.append(" ");\r
+               sb.append((r.getExec())?("RY"):("RN"));\r
+               sb.append("\r\n");\r
+               sb.append(r.getTitle());\r
+               sb.append("\r\n");\r
+               \r
+               return sb.toString();\r
+       }\r
+       \r
+       /*\r
+        *      予約を更新する\r
+        */\r
+       public boolean UpdateRdEntry(ReserveList o, ReserveList r) {\r
+               System.out.println("Through: UpdateRdEntry()");\r
+               \r
+               errmsg = "更新処理は無効です。";\r
+\r
+               return(false);\r
+       }\r
+\r
+       /*\r
+        *      予約を削除する\r
+        */\r
+       public ReserveList RemoveRdEntry(String delid) {\r
+               errmsg = "";\r
+               \r
+               System.out.println("Through: RemoveRdEntry()");\r
+\r
+               // 削除対象を探す\r
+               ReserveList rx = null;\r
+               for (  ReserveList reserve : getReserves() )  {\r
+                       if (reserve.getId().equals(delid)) {\r
+                               rx = reserve;\r
+                               break;\r
+                       }\r
+               }\r
+               if (rx == null) {\r
+                       return(null);\r
+               }\r
+               \r
+               // 予約リストを更新\r
+               getReserves().remove(rx);\r
+               \r
+               // キャッシュに保存\r
+               ReservesToFile(getReserves(), rsvedFile);\r
+               \r
+               errmsg = "鯛ナビのエントリは削除しました。REGZA上で実際のエントリを削除して下さい。";\r
+               \r
+               return(rx);\r
+       }\r
+       \r
+       /*\r
+        * \r
+        */\r
+       public String getErrmsg() {\r
+               return(errmsg.replaceAll("\\\\r\\\\n", ""));\r
+       }\r
+}\r
diff --git a/TinyBannavi/src/tainavi/PlugIn_RecRD_MAIL_M190.java b/TinyBannavi/src/tainavi/PlugIn_RecRD_MAIL_M190.java
new file mode 100644 (file)
index 0000000..15447da
--- /dev/null
@@ -0,0 +1,82 @@
+package tainavi;\r
+\r
+import java.util.Calendar;\r
+import java.util.GregorianCalendar;\r
+\r
+public class PlugIn_RecRD_MAIL_M190 extends PlugIn_RecRD_MAIL implements HDDRecorder,Cloneable {\r
+\r
+       public PlugIn_RecRD_MAIL_M190 clone() {\r
+               return (PlugIn_RecRD_MAIL_M190) super.clone();\r
+       }\r
+       \r
+       @Override\r
+       public String getRecorderId() { return "REGZA DBR-M190(Mail)"; }\r
+\r
+       @Override\r
+       protected String getDefFile() { return "env/mail_m190.def"; }\r
+\r
+       @Override\r
+       protected String getMailBody(ReserveList r, String passwd) {\r
+               GregorianCalendar c = CommonUtils.getCalendar(r.getRec_pattern());\r
+               \r
+               StringBuilder sb = new StringBuilder();\r
+               \r
+               sb.append("open");\r
+               sb.append(" ");\r
+               sb.append(this.getBroadcast());\r
+               sb.append(" ");\r
+               sb.append("prog ");\r
+               sb.append("add ");\r
+               sb.append(String.format("%04d%02d%02d ",c.get(Calendar.YEAR),c.get(Calendar.MONTH)+1,c.get(Calendar.DATE)));\r
+               sb.append(" ");\r
+               sb.append(r.getAhh()+r.getAmm());\r
+               sb.append(" ");\r
+               sb.append(r.getZhh()+r.getZmm());\r
+               sb.append(" ");\r
+               //sb.append(cc.getCH_NAME2CODE(r.getCh_name()));\r
+               sb.append(getChCode().getCH_WEB2CODE(r.getCh_name()));\r
+               sb.append(" ");\r
+               //sb.append(text2value(encoder,r.getTuner()));\r
+               //sb.append(" ");\r
+               sb.append(text2value(vrate,r.getRec_mode()));\r
+               sb.append(" ");\r
+               //if (r.getRec_mode().indexOf("[TS") != 0) {\r
+               //      sb.append(text2value(arate,r.getRec_audio()));\r
+               //      sb.append(" ");\r
+               //}\r
+               sb.append(text2value(device,r.getRec_device()));\r
+               sb.append(" ");\r
+               /*\r
+               sb.append(text2value(dvdcompat,r.getRec_dvdcompat()));\r
+               String chapter_mode = text2value(xchapter,r.getRec_xchapter());\r
+               if (chapter_mode != null) {\r
+                       sb.append(" ");\r
+                       sb.append(chapter_mode);\r
+               }\r
+               chapter_mode = text2value(mvchapter,r.getRec_mvchapter());\r
+               if (chapter_mode != null) {\r
+                       sb.append(" ");\r
+                       sb.append(chapter_mode);\r
+               }\r
+               else {\r
+                       sb.append(" ");\r
+                       sb.append("CPN");       // マジックチャプターOFFをつけないとエラー発生\r
+               }\r
+               chapter_mode = text2value(mschapter,r.getRec_mschapter());\r
+               if (chapter_mode != null) {\r
+                       sb.append(" ");\r
+                       sb.append(chapter_mode);\r
+               }\r
+               */\r
+               sb.append(text2value(mvchapter,r.getRec_mvchapter()));\r
+               sb.append(" ");\r
+               sb.append(text2value(aspect,r.getRec_aspect()));\r
+               sb.append(" ");\r
+               //sb.append((r.getExec())?("RY"):("RN"));\r
+               //sb.append("\r\n");\r
+               //sb.append(r.getTitle());\r
+               sb.append("\r\n");\r
+               \r
+               return sb.toString();\r
+       }\r
+}\r
diff --git a/TinyBannavi/src/tainavi/PlugIn_RecRD_MAIL_RE2.java b/TinyBannavi/src/tainavi/PlugIn_RecRD_MAIL_RE2.java
new file mode 100644 (file)
index 0000000..d32037e
--- /dev/null
@@ -0,0 +1,31 @@
+package tainavi;\r
+\r
+import java.util.Calendar;\r
+import java.util.GregorianCalendar;\r
+\r
+public class PlugIn_RecRD_MAIL_RE2 extends PlugIn_RecRD_MAIL_Z9500 implements HDDRecorder,Cloneable {\r
+\r
+       public PlugIn_RecRD_MAIL_RE2 clone() {\r
+               return (PlugIn_RecRD_MAIL_RE2) super.clone();\r
+       }\r
+\r
+       @Override\r
+       public String getRecorderId() { return "REGZA RE2(Mail)"; }\r
+\r
+       @Override\r
+       protected String getDefFile() { return "env/mail_re2.def"; }\r
+\r
+       @Override\r
+       protected String getMailBody(ReserveList r, String passwd) {\r
+               GregorianCalendar c = CommonUtils.getCalendar(r.getRec_pattern());\r
+               String msg = "dtvopen ";\r
+               msg += passwd+" ";\r
+               msg += String.format("%04d%02d%02d ",c.get(Calendar.YEAR),c.get(Calendar.MONTH)+1,c.get(Calendar.DATE))+" ";\r
+               msg += r.getAhh()+r.getAmm()+" ";\r
+               msg += r.getZhh()+r.getZmm()+" ";\r
+               msg += getChCode().getCH_WEB2CODE(r.getCh_name())+" ";\r
+               msg += text2value(device,r.getRec_device());\r
+               msg += "\r\n";\r
+               return msg;\r
+       }\r
+}\r
diff --git a/TinyBannavi/src/tainavi/PlugIn_RecRD_MAIL_Z1.java b/TinyBannavi/src/tainavi/PlugIn_RecRD_MAIL_Z1.java
new file mode 100644 (file)
index 0000000..3f40fa5
--- /dev/null
@@ -0,0 +1,31 @@
+package tainavi;\r
+\r
+import java.util.Calendar;\r
+import java.util.GregorianCalendar;\r
+\r
+public class PlugIn_RecRD_MAIL_Z1 extends PlugIn_RecRD_MAIL_Z9500 implements HDDRecorder,Cloneable {\r
+\r
+       public PlugIn_RecRD_MAIL_Z1 clone() {\r
+               return (PlugIn_RecRD_MAIL_Z1) super.clone();\r
+       }\r
+\r
+       @Override\r
+       public String getRecorderId() { return "REGZA Z1(Mail)"; }\r
+\r
+       @Override\r
+       protected String getDefFile() { return "env/mail_z1.def"; }\r
+\r
+       @Override\r
+       protected String getMailBody(ReserveList r, String passwd) {\r
+               GregorianCalendar c = CommonUtils.getCalendar(r.getRec_pattern());\r
+               String msg = "dtvopen ";\r
+               msg += passwd+" ";\r
+               msg += String.format("%04d%02d%02d ",c.get(Calendar.YEAR),c.get(Calendar.MONTH)+1,c.get(Calendar.DATE))+" ";\r
+               msg += r.getAhh()+r.getAmm()+" ";\r
+               msg += r.getZhh()+r.getZmm()+" ";\r
+               msg += getChCode().getCH_WEB2CODE(r.getCh_name())+" ";\r
+               msg += text2value(device,r.getRec_device());\r
+               msg += "\r\n";\r
+               return msg;\r
+       }\r
+}\r
diff --git a/TinyBannavi/src/tainavi/PlugIn_RecRD_MAIL_Z3.java b/TinyBannavi/src/tainavi/PlugIn_RecRD_MAIL_Z3.java
new file mode 100644 (file)
index 0000000..1de005e
--- /dev/null
@@ -0,0 +1,33 @@
+package tainavi;\r
+\r
+import java.util.Calendar;\r
+import java.util.GregorianCalendar;\r
+\r
+public class PlugIn_RecRD_MAIL_Z3 extends PlugIn_RecRD_MAIL_Z9500 implements HDDRecorder,Cloneable {\r
+\r
+       @Override\r
+       public PlugIn_RecRD_MAIL_Z3 clone() {\r
+               return (PlugIn_RecRD_MAIL_Z3) super.clone();\r
+       }\r
+\r
+       @Override\r
+       public String getRecorderId() { return "REGZA Z3(Mail)"; }\r
+\r
+       @Override\r
+       protected String getDefFile() { return "env/mail_z3.def"; }\r
+\r
+       @Override\r
+       protected String getMailBody(ReserveList r, String passwd) {\r
+               GregorianCalendar c = CommonUtils.getCalendar(r.getRec_pattern());\r
+               String msg = "dtvopen ";\r
+               msg += passwd+" ";\r
+               msg += String.format("%04d%02d%02d",c.get(Calendar.YEAR),c.get(Calendar.MONTH)+1,c.get(Calendar.DATE))+" ";\r
+               msg += r.getAhh()+r.getAmm()+" ";\r
+               msg += r.getZhh()+r.getZmm()+" ";\r
+               msg += getChCode().getCH_WEB2CODE(r.getCh_name())+" ";\r
+               msg += text2value(device,r.getRec_device())+" ";\r
+               msg += text2value(vrate,r.getRec_mode())+" ";\r
+               msg += "\r\n";\r
+               return msg;\r
+       }\r
+}\r
diff --git a/TinyBannavi/src/tainavi/PlugIn_RecRD_MAIL_Z9500.java b/TinyBannavi/src/tainavi/PlugIn_RecRD_MAIL_Z9500.java
new file mode 100644 (file)
index 0000000..3a78027
--- /dev/null
@@ -0,0 +1,300 @@
+package tainavi;\r
+\r
+import java.io.BufferedReader;\r
+import java.io.File;\r
+import java.io.FileNotFoundException;\r
+import java.io.FileReader;\r
+import java.io.IOException;\r
+import java.io.UnsupportedEncodingException;\r
+import java.util.Calendar;\r
+import java.util.Date;\r
+import java.util.GregorianCalendar;\r
+import java.util.Properties;\r
+\r
+import javax.mail.AuthenticationFailedException;\r
+import javax.mail.Authenticator;\r
+import javax.mail.Message;\r
+import javax.mail.MessagingException;\r
+import javax.mail.PasswordAuthentication;\r
+import javax.mail.Session;\r
+import javax.mail.Transport;\r
+import javax.mail.internet.InternetAddress;\r
+import javax.mail.internet.MimeMessage;\r
+\r
+\r
+public class PlugIn_RecRD_MAIL_Z9500 extends HDDRecorderUtils implements HDDRecorder,Cloneable {\r
+\r
+       public PlugIn_RecRD_MAIL_Z9500 clone() {\r
+               return (PlugIn_RecRD_MAIL_Z9500) super.clone();\r
+       }\r
+       \r
+       /* 必須コード  - ここから */\r
+       \r
+       // 種族の特性\r
+       public String getRecorderId() { return "REGZA Z9500(Mail)"; }\r
+       public RecType getType() { return RecType.MAIL; }\r
+       \r
+       // 個体の特性\r
+       private ChannelCode cc = new ChannelCode(getRecorderId());\r
+       private String rsvedFile = "";\r
+       \r
+       private String errmsg = "";\r
+       \r
+       protected String getDefFile() { return "env/mail_z9500.def"; }\r
+       \r
+       // 公開メソッド\r
+       \r
+       /*\r
+        * \r
+        */\r
+       public String Myself() {\r
+               return("MAIL"+":"+getMacAddr()+":"+getRecorderId());\r
+       }\r
+       public ChannelCode getChCode() {\r
+               return cc;\r
+       }\r
+       \r
+       // 繰り返し予約をサポートしていない\r
+       @Override\r
+       public boolean isRepeatReserveSupported() { return false; }\r
+       \r
+       /*\r
+        * \r
+        */\r
+       public boolean ChangeChannel(String Channel) {\r
+               return false;\r
+       }\r
+\r
+       /*\r
+        *      レコーダーから予約一覧を取得する \r
+        */\r
+       public boolean GetRdReserve(boolean force)\r
+       {\r
+               errmsg = "";\r
+               \r
+               System.out.println("Through: GetRdReserve("+force+")");\r
+               \r
+               String defFile = getDefFile();\r
+               \r
+               FileReader fr = null;\r
+               BufferedReader r = null;\r
+               try {\r
+                       encoder.clear();\r
+                       vrate.clear();\r
+                       arate.clear();\r
+                       dvdcompat.clear();\r
+                       device.clear();\r
+                       \r
+                       fr = new FileReader(defFile);\r
+                       r = new BufferedReader(fr);\r
+                       String s ;\r
+                       while ( (s = r.readLine()) != null ) {\r
+                               String[] b = s.split(",");\r
+                               if ( b.length >= 3 ) {\r
+                                       TextValueSet t = new TextValueSet() ;\r
+                                       t.setText(b[1]) ;\r
+                                       t.setValue(b[2]) ;\r
+                                       \r
+                                       if ( b[0].equals("7") ) {\r
+                                               encoder.add(t) ;\r
+                                       }\r
+                                       else if ( b[0].equals("10") ) {\r
+                                               vrate.add(t) ;\r
+                                       }\r
+                                       else if ( b[0].equals("11") ) {\r
+                                               arate.add(t) ;\r
+                                       }\r
+                                       else if ( b[0].equals("14") ) {\r
+                                               dvdcompat.add(t) ;\r
+                                       }\r
+                                       else if ( b[0].equals("12") ) {\r
+                                               device.add(t) ;\r
+                                       }\r
+                               }\r
+                       }\r
+               } catch (FileNotFoundException e) {\r
+                       // TODO Auto-generated catch block\r
+                       e.printStackTrace();\r
+               } catch (IOException e) {\r
+                       // TODO Auto-generated catch block\r
+                       e.printStackTrace();\r
+               }\r
+               finally {\r
+                       if (r != null) try { r.close(); } catch (Exception e) {};\r
+                       if (fr != null) try { fr.close(); } catch (Exception e) {};\r
+               }\r
+               \r
+               // 予約一覧をキャッシュから\r
+               rsvedFile = "env/reserved."+getIPAddr()+"_"+getPortNo()+"_"+getRecorderId()+".xml";\r
+               \r
+               File f = new File(rsvedFile);\r
+               if ( force == false && f.exists()) {\r
+                       // キャッシュから読み出し(予約一覧)\r
+                       setReserves(ReservesFromFile(rsvedFile));\r
+                       if (getReserves().size() > 0) {\r
+                               System.out.println("+read from="+rsvedFile);\r
+                       }\r
+                       return(true);\r
+               }\r
+\r
+               return(true);\r
+       }\r
+       \r
+       /*\r
+        *      予約を実行する\r
+        */\r
+       public boolean PostRdEntry(ReserveList r)\r
+       {\r
+               errmsg = "";\r
+               \r
+               //\r
+               if (cc.getCH_WEB2CODE(r.getCh_name()) == null) {\r
+                       errmsg = "【警告】Web番組表の放送局名「"+r.getCh_name()+"」をCHコードに変換できません。CHコード設定を修正してください。" ;\r
+                       System.out.println(errmsg);\r
+                       return(false);\r
+               }\r
+               \r
+               System.out.println("Run: PostRdEntry("+r.getTitle()+")");\r
+\r
+               // メール本文を作る\r
+               String msg = "" ;\r
+               String message = "";\r
+               \r
+               GregorianCalendar c = CommonUtils.getCalendar(r.getRec_pattern());\r
+               if (c == null) {\r
+                       errmsg = "日付指定しか利用出来ません。" ;\r
+                       return(false) ;\r
+               }\r
+               msg = getMailBody(r, this.getBroadcast());\r
+               \r
+               try {\r
+                       message = new String(msg.getBytes("MS932"),"Shift_JIS");\r
+               } catch (UnsupportedEncodingException e1) {\r
+                       // TODO Auto-generated catch block\r
+                       e1.printStackTrace();\r
+               }\r
+               \r
+               // メールを送信する\r
+               try {\r
+                       Properties props = new Properties() ;\r
+                       props.put("mail.smtp.host", this.getIPAddr()) ;\r
+                       props.put("mail.host", this.getIPAddr()) ;\r
+                       props.put("mail.smtp.port", this.getPortNo()) ;\r
+                       props.put("mail.smtp.auth", "true") ;\r
+                       props.put("mail.smtp.starttls.enable","true");\r
+                       \r
+                       Session session = Session.getInstance( props, new Authenticator() {\r
+                               protected PasswordAuthentication getPasswordAuthentication() {\r
+                                       return new PasswordAuthentication(getUser(), getPasswd()) ;\r
+                               }\r
+                       });\r
+                       //session.setDebug(true);\r
+                       MimeMessage mimeMessage = new MimeMessage(session);\r
+                       mimeMessage.setRecipients(Message.RecipientType.TO, this.getMacAddr());\r
+                       mimeMessage.setFrom(new InternetAddress(this.getUser()));\r
+                       mimeMessage.setSubject(new Date().toString());\r
+                       mimeMessage.setSentDate(new Date());\r
+                       //mimeMessage.setText(message, "ISO-2022-JP");\r
+                       mimeMessage.setContent(message, "text/html;charset=iso-2022-jp");\r
+                       mimeMessage.setHeader("Content-Transfer-Encoding", "7bit"); \r
+                       Transport.send(mimeMessage);\r
+        } catch (AuthenticationFailedException e) {\r
+               errmsg = "SMTP認証に失敗しました:"+e.toString() ;\r
+               return(false) ;\r
+        } catch (MessagingException e) {\r
+               errmsg = "メール送信に失敗しました:"+e.toString() ;\r
+               return(false) ;\r
+               }\r
+               \r
+               // 予約ID\r
+               long no = 0;\r
+               for (ReserveList x : getReserves()) {\r
+                       if (Long.valueOf(x.getId()) > no) {\r
+                               no = Long.valueOf(x.getId());\r
+                       }\r
+               }\r
+               \r
+               r.setId(String.valueOf(++no));\r
+               // 予約パターンID\r
+               r.setRec_pattern_id(getRec_pattern_Id(r.getRec_pattern()));\r
+               \r
+               // 次回予定日\r
+               r.setRec_nextdate(CommonUtils.getNextDate(r));\r
+               \r
+               // 録画長\r
+               r.setRec_min(CommonUtils.getRecMin(r.getAhh(),r.getAmm(),r.getZhh(),r.getZmm()));\r
+               \r
+               // 開始日時・終了日時\r
+               getStartEndDateTime(r);\r
+               \r
+               // 予約リストを更新\r
+               getReserves().add(r);\r
+               \r
+               // キャッシュに保存\r
+               ReservesToFile(getReserves(), rsvedFile);\r
+               \r
+               return(true);\r
+       }\r
+       protected String getMailBody(ReserveList r, String passwd) {\r
+               GregorianCalendar c = CommonUtils.getCalendar(r.getRec_pattern());\r
+               String msg = "dtvopen ";\r
+               msg += passwd+" ";\r
+               msg += String.format("%04d%02d%02d ",c.get(Calendar.YEAR),c.get(Calendar.MONTH)+1,c.get(Calendar.DATE))+" ";\r
+               msg += r.getAhh()+r.getAmm()+" ";\r
+               msg += r.getZhh()+r.getZmm()+" ";\r
+               msg += getChCode().getCH_WEB2CODE(r.getCh_name())+" ";\r
+               msg += text2value(device,r.getRec_device())+" ";\r
+               msg += text2value(dvdcompat,r.getRec_dvdcompat()); \r
+               msg += "\r\n";\r
+               return msg;\r
+       }\r
+\r
+       /*\r
+        *      予約を更新する\r
+        */\r
+       public boolean UpdateRdEntry(ReserveList o, ReserveList r) {\r
+               System.out.println("Through: UpdateRdEntry()");\r
+               \r
+               errmsg = "更新処理は無効です。";\r
+\r
+               return(false);\r
+       }\r
+\r
+       /*\r
+        *      予約を削除する\r
+        */\r
+       public ReserveList RemoveRdEntry(String delid) {\r
+               errmsg = "";\r
+               \r
+               System.out.println("Through: RemoveRdEntry()");\r
+\r
+               // 削除対象を探す\r
+               ReserveList rx = null;\r
+               for (  ReserveList reserve : getReserves() )  {\r
+                       if (reserve.getId().equals(delid)) {\r
+                               rx = reserve;\r
+                               break;\r
+                       }\r
+               }\r
+               if (rx == null) {\r
+                       return(null);\r
+               }\r
+               \r
+               // 予約リストを更新\r
+               getReserves().remove(rx);\r
+               \r
+               // キャッシュに保存\r
+               ReservesToFile(getReserves(), rsvedFile);\r
+               \r
+               errmsg = "鯛ナビのエントリは削除しました。REGZA上で実際のエントリを削除して下さい。";\r
+               \r
+               return(rx);\r
+       }\r
+       \r
+       /*\r
+        * \r
+        */\r
+       public String getErrmsg() {\r
+               return(errmsg.replaceAll("\\\\r\\\\n", ""));\r
+       }\r
+}\r
diff --git a/TinyBannavi/src/tainavi/PlugIn_RecRD_NULL.java b/TinyBannavi/src/tainavi/PlugIn_RecRD_NULL.java
new file mode 100644 (file)
index 0000000..e8acc47
--- /dev/null
@@ -0,0 +1,226 @@
+package tainavi;\r
+\r
+import java.io.File;\r
+\r
+\r
+/*\r
+ * \r
+ */\r
+\r
+public class PlugIn_RecRD_NULL extends HDDRecorderUtils implements HDDRecorder,Cloneable {\r
+\r
+       public PlugIn_RecRD_NULL clone() {\r
+               return (PlugIn_RecRD_NULL) super.clone();\r
+       }\r
+       \r
+       /*******************************************************************************\r
+        * 種族の特性\r
+        ******************************************************************************/\r
+       \r
+       @Override\r
+       public String getRecorderId() { return "NULL"; }\r
+       @Override\r
+       public RecType getType() { return RecType.NULL; }\r
+       \r
+       @Override\r
+       public boolean isAutocompleteSupported() { return false; }\r
+       @Override\r
+       public boolean isRecChNameNeeded() { return false; }\r
+       @Override\r
+       public boolean isChCodeNeeded()  { return false; }\r
+\r
+       /*******************************************************************************\r
+        * 予約ダイアログなどのテキストのオーバーライド\r
+        ******************************************************************************/\r
+       \r
+       @Override\r
+       public String getChDatHelp() { return\r
+                       "下記CHコードはCH設定から自動生成して表示しているものですが、NULLプラグインへの反映は自動では行われません。"+\r
+                       "初回起動時、またはCH設定変更時は必ず「更新を確定する」ボタンを押して確定してください。";\r
+       }\r
+\r
+       /*******************************************************************************\r
+        * CHコード設定、エラーメッセージ\r
+        ******************************************************************************/\r
+       \r
+       public ChannelCode getChCode() {\r
+               return cc;\r
+       }\r
+       \r
+       private final ChannelCode cc = new ChannelCode(getRecorderId());\r
+       \r
+       public String getErrmsg() {\r
+               return(errmsg.replaceAll("[\r\n]", ""));\r
+       }\r
+       \r
+       protected void setErrmsg(String s) {\r
+               errmsg = s;\r
+       }\r
+       \r
+       private String errmsg = "";\r
+\r
+       /*******************************************************************************\r
+        * フリーオプション関係\r
+        ******************************************************************************/\r
+       \r
+       /*******************************************************************************\r
+        * 部品\r
+        ******************************************************************************/\r
+       \r
+       private String rsvedFile = "";\r
+       \r
+       protected String getRsvedFile() {\r
+               return(rsvedFile);\r
+       }\r
+       \r
+       protected void setRsvedFile(String s) {\r
+               rsvedFile = s;\r
+       }\r
+\r
+       /*******************************************************************************\r
+        * コンストラクタ\r
+        ******************************************************************************/\r
+       \r
+       public PlugIn_RecRD_NULL() {\r
+               super();\r
+               this.setTunerNum(4);\r
+               this.setUseCalendar(false);\r
+       }\r
+       \r
+       /*******************************************************************************\r
+        * チャンネルリモコン機能\r
+        ******************************************************************************/\r
+       \r
+       public boolean ChangeChannel(String Channel) {\r
+               return false;\r
+       }\r
+\r
+       /*******************************************************************************\r
+        * レコーダーから予約一覧を取得する\r
+        ******************************************************************************/\r
+       \r
+       public boolean GetRdReserve(boolean force)\r
+       {\r
+               System.out.println("Run: GetRdReserve("+force+")");\r
+               \r
+               setRsvedFile("env/reserved."+getIPAddr()+"_"+getPortNo()+"_"+getRecorderId()+".xml");\r
+               \r
+               // チューナー数は自動生成\r
+               {\r
+                       encoder.clear();\r
+                       if ( getTunerNum() >= 2 ) {\r
+                               for ( int i=1; i<=getTunerNum(); i++ ) {\r
+                                       TextValueSet t = new TextValueSet();\r
+                                       t.setText("N"+i);\r
+                                       t.setValue("N"+i);\r
+                                       encoder.add(t);\r
+                               }\r
+                       }\r
+               }\r
+               \r
+               File f = new File(getRsvedFile());\r
+               if ( f.exists()) {\r
+                       // キャッシュから読み出し(予約一覧)\r
+                       setReserves(ReservesFromFile(getRsvedFile()));\r
+               }\r
+               \r
+               return(true);\r
+       }\r
+       \r
+       /*******************************************************************************\r
+        * 新規予約\r
+        ******************************************************************************/\r
+       \r
+       public boolean PostRdEntry(ReserveList r)\r
+       {\r
+               //\r
+               if (getChCode().getCH_WEB2CODE(r.getCh_name()) == null) {\r
+                       errmsg = "【警告】Web番組表の放送局名「"+r.getCh_name()+"」をCHコードに変換できません。CHコード設定を修正してください。" ;\r
+                       System.out.println(getErrmsg());\r
+                       return(false);\r
+               }\r
+               \r
+               System.out.println("Run: PostRdEntry("+r.getTitle()+")");\r
+               \r
+               setErrmsg("");\r
+\r
+               //\r
+               long no = 0;\r
+               for (ReserveList x : getReserves()) {\r
+                       if (Long.valueOf(x.getId()) > no) {\r
+                               no = Long.valueOf(x.getId());\r
+                       }\r
+               }\r
+               r.setId(String.valueOf(++no));\r
+\r
+               // 予約パターンID\r
+               r.setRec_pattern_id(getRec_pattern_Id(r.getRec_pattern()));\r
+               \r
+               // 次回予定日\r
+               r.setRec_nextdate(CommonUtils.getNextDate(r));\r
+               \r
+               // 録画長\r
+               r.setRec_min(CommonUtils.getRecMin(r.getAhh(),r.getAmm(),r.getZhh(),r.getZmm()));\r
+               \r
+               // 開始日時・終了日時\r
+               getStartEndDateTime(r);\r
+               \r
+               // 予約リストを更新\r
+               getReserves().add(r);\r
+               \r
+               // キャッシュに保存\r
+               ReservesToFile(getReserves(), getRsvedFile());\r
+               \r
+               return(true);\r
+       }\r
+\r
+       /*******************************************************************************\r
+        * 予約更新\r
+        ******************************************************************************/\r
+       \r
+       public boolean UpdateRdEntry(ReserveList o, ReserveList r) {\r
+               System.out.println("Through: UpdateRdEntry()");\r
+               \r
+               setErrmsg("更新処理は無効です。");\r
+\r
+               return(false);\r
+       }\r
+\r
+       /*******************************************************************************\r
+        * 予約削除\r
+        ******************************************************************************/\r
+       \r
+       public ReserveList RemoveRdEntry(String delid) {\r
+               \r
+               System.out.println("Run: RemoveRdEntry()");\r
+\r
+               setErrmsg("");\r
+\r
+               // 削除対象を探す\r
+               ReserveList rx = null;\r
+               for (  ReserveList reserve : getReserves() )  {\r
+                       if (reserve.getId().equals(delid)) {\r
+                               rx = reserve;\r
+                               break;\r
+                       }\r
+               }\r
+               if (rx == null) {\r
+                       return(null);\r
+               }\r
+               \r
+               // 予約リストを更新\r
+               getReserves().remove(rx);\r
+               \r
+               // キャッシュに保存\r
+               ReservesToFile(getReserves(), getRsvedFile());\r
+\r
+               System.out.printf("\n<<< Message from RD >>> \"%s\"\n\n", "正常に削除できました。");\r
+\r
+               return(rx);\r
+       }\r
+       \r
+       /*******************************************************************************\r
+        * 非公開メソッド\r
+        ******************************************************************************/\r
+       \r
+}\r
diff --git a/TinyBannavi/src/tainavi/PlugIn_RecRD_S1004K.java b/TinyBannavi/src/tainavi/PlugIn_RecRD_S1004K.java
new file mode 100644 (file)
index 0000000..134eead
--- /dev/null
@@ -0,0 +1,503 @@
+package tainavi;\r
+\r
+import java.io.BufferedReader;\r
+import java.io.File;\r
+import java.io.FileNotFoundException;\r
+import java.io.FileReader;\r
+import java.io.IOException;\r
+import java.io.UnsupportedEncodingException;\r
+import java.net.URLEncoder;\r
+import java.util.ArrayList;\r
+import java.util.regex.Matcher;\r
+import java.util.regex.Pattern;\r
+\r
+\r
+public class PlugIn_RecRD_S1004K extends HDDRecorderUtils implements HDDRecorder,Cloneable {\r
+\r
+       public PlugIn_RecRD_S1004K clone() {\r
+               return (PlugIn_RecRD_S1004K) super.clone();\r
+       }\r
+\r
+       //private static final String thisEncoding = "MS932";\r
+       \r
+       /* 必須コード  - ここから */\r
+       \r
+       // 種族の特性\r
+       public String getRecorderId() { return "VARDIA RD-S1004K"; }\r
+       public RecType getType() { return RecType.RECORDER; }\r
+       \r
+       // 個体の特性\r
+       private ChannelCode cc = new ChannelCode(getRecorderId());\r
+       private String rsvedFile = "";\r
+       \r
+       private String errmsg = "";\r
+       \r
+       // 公開メソッド\r
+       \r
+       /*\r
+        * \r
+        */\r
+       public ChannelCode getChCode() {\r
+               return cc;\r
+       }\r
+       \r
+       /*\r
+        * \r
+        */\r
+       public boolean ChangeChannel(String Channel) {\r
+               \r
+               if (Channel == null) {\r
+                       return true;\r
+               }\r
+               \r
+               String ch = null;\r
+               int curBC;\r
+               int newBC;\r
+               String chNo;\r
+               \r
+               errmsg = "";\r
+               \r
+               /*\r
+                * 変更前の放送(地上波/BS/CS)\r
+                */\r
+               \r
+               for (int i=0; i<3; i++) {\r
+                       String[] d = reqGET("http://"+getIPAddr()+":"+getPortNo()+"/getcurchannel.htm", null);\r
+                       if ((ch = d[1]) != null) {\r
+                               break;\r
+                       }\r
+                       CommonUtils.milSleep(500);\r
+               }\r
+               if (ch == null || ch.startsWith("null")) {\r
+                       errmsg = "レコーダへのアクセスに失敗しました(チャンネルリモコン)。";\r
+                       System.err.println(errmsg);\r
+                       return false;\r
+               }\r
+               \r
+               byte[] ba = ch.getBytes();\r
+               byte enc = ba[0];\r
+               ch = ch.substring(1,5);\r
+               \r
+               if (ch.matches("^\\d.+")) {\r
+                       curBC = 0;\r
+               }\r
+               else if (ch.startsWith("BS")) {\r
+                       curBC = 1;\r
+               }\r
+               else if (ch.startsWith("CS")) {\r
+                       curBC = 2;\r
+               }\r
+               else {\r
+                       errmsg = "現在のチャンネルが認識できません("+ch+")。";\r
+                       System.err.println(errmsg);\r
+                       return false;\r
+               }\r
+               \r
+               /*\r
+                * 変更後のCH\r
+                */\r
+               \r
+               // 放送(地上波/BS/CS)\r
+               ch = cc.getCH_WEB2REC(Channel);\r
+               if (ch.startsWith("SP")) {\r
+                       errmsg = "外部入力にアサインされているチャンネルには変更できません("+Channel+", "+ch+")。";\r
+                       System.err.println(errmsg);\r
+                       return false;\r
+               }\r
+               else if (ch.matches("^\\d.+")) {\r
+                       newBC = 0;\r
+               }\r
+               else if (ch.startsWith("BS")) {\r
+                       newBC = 1;\r
+               }\r
+               else if (ch.startsWith("CS")) {\r
+                       newBC = 2;\r
+               }\r
+               else {\r
+                       errmsg = "放送種別が識別できません。プログラム異常かも?("+Channel+", "+ch+")。";\r
+                       System.err.println(errmsg);\r
+                       return false;\r
+               }\r
+               \r
+               // CH番号\r
+               Matcher ma = Pattern.compile("(\\d\\d\\d)").matcher(ch);\r
+               if (ma.find()) {\r
+                       chNo = ma.group(1);\r
+               }\r
+               else {\r
+                       errmsg = "CH番号が取得できません("+Channel+", "+ch+")。";\r
+                       System.err.println(errmsg);\r
+                       return false;\r
+               }\r
+               \r
+               /*\r
+                * CH変更実行\r
+                */\r
+               \r
+               int cBC;\r
+               for (int i=0; i<4 && newBC != (cBC = (curBC+i)%4); i++) {\r
+                       // 地上アナログ(3)が選択できるのはRE(0x06)だけ\r
+                       if (enc != 0x06 && cBC == 3) {\r
+                               continue;\r
+                       }\r
+                       \r
+                       // 切り替え実行\r
+                       reqGET("http://"+getIPAddr()+":"+getPortNo()+"/remote/remote.htm?key=21", null);\r
+                       \r
+                       // 地上波→BS・BS→CSは切り替え完了までに時間がかかる\r
+                       CommonUtils.milSleep((cBC == 0 || cBC == 1)?(3000):(1000));\r
+               }\r
+               \r
+               reqGET("http://"+getIPAddr()+":"+getPortNo()+"/remote/remote.htm?key=25", null);\r
+               CommonUtils.milSleep(1000);\r
+               for (int i=0; i<3; i++) {\r
+                       reqGET("http://"+getIPAddr()+":"+getPortNo()+"/remote/remote.htm?key=0"+chNo.substring(i,i+1), null);\r
+                       CommonUtils.milSleep(200);\r
+               }\r
+               reqGET("http://"+getIPAddr()+":"+getPortNo()+"/remote/remote.htm?key=44", null);\r
+               \r
+               return true;\r
+       }\r
+       \r
+       /*\r
+        *      レコーダーから予約一覧を取得する \r
+        */\r
+       public boolean GetRdReserve(boolean force) {\r
+               \r
+               errmsg = "";\r
+               \r
+               System.out.println("Run: GetRdReserve("+force+")");\r
+               \r
+               // 録画設定\r
+               if (force == true || encoder.size() == 0) {\r
+                       FileReader fr = null;\r
+                       BufferedReader r = null;\r
+                       try {\r
+                               encoder.clear();\r
+                               vrate.clear();\r
+                               arate.clear();\r
+                               device.clear();\r
+                               dvdcompat.clear();\r
+                               xchapter.clear();\r
+                               mschapter.clear();\r
+                               mvchapter.clear();\r
+                               \r
+                               String defFile = "env/mail.def" ;\r
+                               fr = new FileReader(defFile);\r
+                               r = new BufferedReader(fr);\r
+                               String s ;\r
+                               while ( (s = r.readLine()) != null ) {\r
+                                       String[] b = s.split(",");\r
+                                       if ( b.length >= 3 ) {\r
+                                               TextValueSet t = new TextValueSet() ;\r
+                                               t.setText(b[1]) ;\r
+                                               t.setValue(b[2]) ;\r
+                                               \r
+                                               if ( b[0].equals("7") ) {\r
+                                                       encoder.add(t) ;\r
+                                               }\r
+                                               else if ( b[0].equals("10") ) {\r
+                                                       vrate.add(t) ;\r
+                                               }\r
+                                               else if ( b[0].equals("11") ) {\r
+                                                       arate.add(t) ;\r
+                                               }\r
+                                               else if ( b[0].equals("12") ) {\r
+                                                       device.add(t) ;\r
+                                               }\r
+                                               else if ( b[0].equals("14") ) {\r
+                                                       dvdcompat.add(t) ;\r
+                                               }\r
+                                               else if ( b[0].equals("17") ) {\r
+                                                       xchapter.add(t) ;\r
+                                               }\r
+                                               else if ( b[0].equals("18") ) {\r
+                                                       mschapter.add(t) ;\r
+                                               }\r
+                                               else if ( b[0].equals("19") ) {\r
+                                                       mvchapter.add(t) ;\r
+                                               }\r
+                                       }\r
+                               }\r
+                       } catch (FileNotFoundException e) {\r
+                               e.printStackTrace();\r
+                       } catch (IOException e) {\r
+                               e.printStackTrace();\r
+                       }\r
+                       finally {\r
+                               if (r != null) try { r.close(); } catch (Exception e) {};\r
+                               if (fr != null) try { fr.close(); } catch (Exception e) {};\r
+                       }\r
+               }\r
+               \r
+               \r
+               // 予約一覧をキャッシュから\r
+               rsvedFile = "env/reserved."+getIPAddr()+"_"+getPortNo()+"_"+getRecorderId()+".xml";\r
+               \r
+               File f = new File(rsvedFile);\r
+               if ( force == false && f.exists()) {\r
+                       // キャッシュから読み出し(予約一覧)\r
+                       setReserves(ReservesFromFile(rsvedFile));\r
+                       if (getReserves().size() > 0) {\r
+                               System.out.println("+read from="+rsvedFile);\r
+                       }\r
+                       return(true);\r
+               }\r
+               \r
+               // 予約一覧をレコーダーから取得する\r
+               reportProgress("get reserved list(1/1).");\r
+               String[] d = reqGET("http://"+getIPAddr()+":"+getPortNo()+"/reserve.htm",null);\r
+               String header = d[0];\r
+               String response = d[1];\r
+               if (response == null) {\r
+                       errmsg = "レコーダーが反応しません";\r
+                       return(false);\r
+               }\r
+               \r
+               // 取得したデータを内部形式に変換する\r
+               ArrayList<ReserveList> ra = decodeReservedList(response); \r
+               for (ReserveList entry : ra) {\r
+                       entry.setCh_name(getChCode().getCH_REC2WEB(entry.getChannel()));\r
+                       \r
+                       // タイトル自動補完フラグなど本体からは取得できない情報を引き継ぐ\r
+                       copyAttributes(entry, getReserves());\r
+               }\r
+               setReserves(ra);\r
+               \r
+               // キャッシュに保存\r
+               ReservesToFile(getReserves(), rsvedFile);\r
+               \r
+               // 取得した情報の表示\r
+               System.out.println("---Reserved List Start---");\r
+               for ( int i = 0; i<getReserves().size(); i++ ) {\r
+                       // 詳細情報の取得\r
+                       ReserveList e = getReserves().get(i);\r
+                       System.out.println(String.format("[%s] %s\t%s\t%s\t%s:%s\t%s:%s\t%sm\t%s\t%s(%s)\t%s\t%s",\r
+                                       (i+1), e.getId(), e.getRec_pattern(), e.getRec_nextdate(), e.getAhh(), e.getAmm(), e.getZhh(),  e.getZmm(),     e.getRec_min(), e.getRec_mode(), e.getTitle(), e.getTitlePop(), e.getChannel(), e.getCh_name()));\r
+               }\r
+               System.out.println("---Reserved List End---");\r
+               \r
+               return(true);\r
+       }\r
+       \r
+       /*\r
+        *      予約を実行する\r
+        */\r
+       public boolean PostRdEntry(ReserveList r) {\r
+               \r
+               errmsg = "";\r
+               \r
+               // 設定もれを撥ねる\r
+               if (cc.getCH_WEB2CODE(r.getCh_name()) == null) {\r
+                       errmsg = "【警告】Web番組表の放送局名「"+r.getCh_name()+"」をCHコードに変換できません。CHコード設定を修正してください。" ;\r
+                       System.out.println(errmsg);\r
+                       return(false);\r
+               }\r
+               \r
+               System.out.println("Run: PostRdEntry("+r.getTitle()+")");\r
+               \r
+               // ポストデータを作る\r
+               r.setId(null);\r
+               String pstr = getPoststr(r);\r
+               \r
+               // リクエストを送る\r
+               reportProgress("send request.(1/1)");\r
+               String[] d = reqPOST("http://"+getIPAddr()+":"+getPortNo()+"/entry.htm", pstr, null);\r
+               String header = d[0];\r
+               String response = d[1];\r
+               if ( response == null ) {\r
+                       errmsg = "レコーダーが反応しません。";\r
+                       return(false);\r
+               }\r
+               \r
+               /*\r
+                * 予約情報の調整 \r
+                */\r
+               \r
+               // 予約ID番号を取得\r
+               Matcher ma = Pattern.compile("^(\\d+)").matcher(response);\r
+               if (ma.find()) {\r
+                       r.setId(ma.group(1));\r
+               }\r
+               \r
+               // 音質(TS/TSEでは音質の設定はできない)\r
+               if (r.getRec_mode().indexOf("[TS") == 0) {\r
+                       r.setRec_audio("");\r
+               }\r
+               \r
+               // 予約パターンID\r
+               r.setRec_pattern_id(getRec_pattern_Id(r.getRec_pattern()));\r
+               \r
+               // 次回予定日\r
+               r.setRec_nextdate(CommonUtils.getNextDate(r));\r
+               \r
+               // 録画長\r
+               r.setRec_min(CommonUtils.getRecMin(r.getAhh(),r.getAmm(),r.getZhh(),r.getZmm()));\r
+               \r
+               // 開始日時・終了日時\r
+               getStartEndDateTime(r);\r
+               \r
+               // 予約リストを更新\r
+               getReserves().add(r);\r
+               \r
+               // キャッシュに保存\r
+               ReservesToFile(getReserves(), rsvedFile);\r
+               \r
+               System.out.printf("\n<<< Message from RD >>> \"%s\"\n\n", "正常に登録できました。");\r
+               return(true);\r
+       }\r
+       \r
+       /*\r
+        *      予約を更新する\r
+        */\r
+       public boolean UpdateRdEntry(ReserveList o, ReserveList r) {\r
+               \r
+               errmsg = "";\r
+               \r
+               // 設定もれを撥ねる\r
+               if (cc.getCH_WEB2CODE(r.getCh_name()) == null) {\r
+                       errmsg = "【警告】Web番組表の放送局名「"+r.getCh_name()+"」をCHコードに変換できません。CHコード設定を修正してください。" ;\r
+                       System.out.println(errmsg);\r
+                       return(false);\r
+               }\r
+               \r
+               System.out.println("Run: UpdateRdEntry()");\r
+               \r
+               // ポストデータを作る\r
+               String pstr = getPoststr(r);\r
+               \r
+               // リクエストを送る\r
+               reportProgress("send request.(1/1)");\r
+               String[] d = reqPOST("http://"+getIPAddr()+":"+getPortNo()+"/update.htm", pstr, null);\r
+               String header = d[0];\r
+               String response = d[1];\r
+               if ( response == null ) {\r
+                       errmsg = "レコーダーが反応しません。";\r
+                       return(false);\r
+               }\r
+               \r
+               /*\r
+                * 予約情報の調整 \r
+                */\r
+               \r
+               // 音質(TS/TSEでは音質の設定はできない)\r
+               if (r.getRec_mode().indexOf("[TS") == 0) {\r
+                       r.setRec_audio("");\r
+               }\r
+               \r
+               // 予約パターンID\r
+               r.setRec_pattern_id(getRec_pattern_Id(r.getRec_pattern()));\r
+               \r
+               // 次回予定日\r
+               r.setRec_nextdate(CommonUtils.getNextDate(r));\r
+               \r
+               // 録画長\r
+               r.setRec_min(CommonUtils.getRecMin(r.getAhh(),r.getAmm(),r.getZhh(),r.getZmm()));\r
+               \r
+               // 開始日時・終了日時\r
+               getStartEndDateTime(r);\r
+               \r
+               // 予約リストを更新\r
+               getReserves().remove(o);\r
+               getReserves().add(r);\r
+               \r
+               // キャッシュに保存\r
+               ReservesToFile(getReserves(), rsvedFile);\r
+               \r
+               System.out.printf("\n<<< Message from RD >>> \"%s\"\n\n", "正常に登録できました。");\r
+               return(true);\r
+       }\r
+       \r
+       /*\r
+        *      予約を削除する\r
+        */\r
+       public ReserveList RemoveRdEntry(String delid) {\r
+               \r
+               errmsg = "";\r
+               \r
+               System.out.println("Run: RemoveRdEntry()");\r
+               \r
+               // 削除対象を探す\r
+               ReserveList rx = null;\r
+               for (  ReserveList reserve : getReserves() )  {\r
+                       if (reserve.getId().equals(delid)) {\r
+                               rx = reserve;\r
+                               break;\r
+                       }\r
+               }\r
+               if (rx == null) {\r
+                       return(null);\r
+               }\r
+               \r
+               // リクエストを送る\r
+               reportProgress("send request.(1/1).");\r
+               String[] d = reqGET("http://"+getIPAddr()+":"+getPortNo()+"/delete.htm?id="+rx.getId(), null);\r
+               String header = d[0];\r
+               String response = d[1];\r
+               if ( response == null ) {\r
+                       errmsg = "レコーダーが反応しません。";\r
+                       return(null);\r
+               }\r
+               \r
+               // 予約リストを更新\r
+               getReserves().remove(rx);\r
+               \r
+               // キャッシュに保存\r
+               ReservesToFile(getReserves(), rsvedFile);\r
+               \r
+               System.out.printf("\n<<< Message from RD >>> \"%s\"\n\n", "正常に削除できました。");\r
+               return(rx);\r
+       }\r
+       \r
+       //\r
+       public String getErrmsg() {\r
+               return(errmsg);\r
+       }\r
+       \r
+       \r
+       \r
+       //\r
+       private String getPoststr(ReserveList r) {\r
+               \r
+               StringBuilder sb = new StringBuilder();\r
+               \r
+               try {\r
+                       String exec             = (r.getExec())?("ON"):("OFF");\r
+                       String title    = URLEncoder.encode(r.getTitle(), "UTF-8");\r
+                       String ch_code  = cc.getCH_WEB2CODE(r.getCh_name());\r
+                       String channel  = cc.getCH_CODE2REC(ch_code);\r
+                       String pattern  = URLEncoder.encode(r.getRec_pattern(), "UTF-8");\r
+                       String dvdr             = URLEncoder.encode(r.getRec_dvdcompat(), "UTF-8");\r
+                       String xchap    = URLEncoder.encode(r.getRec_xchapter(), "UTF-8");\r
+                       String mschap   = URLEncoder.encode(r.getRec_mschapter(), "UTF-8");\r
+                       String mvchap   = URLEncoder.encode(r.getRec_mvchapter(), "UTF-8");\r
+                       String autocomp = (r.getAutocomplete())?("ON"):("OFF");\r
+                       \r
+                       if (r.getId() != null) sb.append("id="+r.getId()+"&");\r
+                       \r
+                       sb.append("exec="+exec+"&");\r
+                       sb.append("title="+title+"&");\r
+                       sb.append("ch_code="+ch_code+"&");\r
+                       sb.append("channel="+channel+"&");\r
+                       sb.append("pattern="+pattern+"&");\r
+                       sb.append("ahh="+r.getAhh()+"&");\r
+                       sb.append("amm="+r.getAmm()+"&");\r
+                       sb.append("zhh="+r.getZhh()+"&");\r
+                       sb.append("zmm="+r.getZmm()+"&");\r
+                       sb.append("tuner="+r.getTuner()+"&");\r
+                       sb.append("video="+r.getRec_mode()+"&");\r
+                       sb.append("audio="+r.getRec_audio()+"&");\r
+                       sb.append("disc="+r.getRec_device()+"&");\r
+                       sb.append("dvdr="+dvdr+"&");\r
+                       sb.append("xchapter="+xchap+"&");\r
+                       sb.append("mschapter="+mschap+"&");\r
+                       sb.append("mvchapter="+mvchap+"&");\r
+                       sb.append("autocomp="+autocomp+"&");\r
+                       sb.append("\n");\r
+               } catch (UnsupportedEncodingException e) {\r
+                       e.printStackTrace();\r
+               }\r
+               \r
+               return(sb.toString());\r
+       }\r
+}\r
diff --git a/TinyBannavi/src/tainavi/PlugIn_RecRD_S304K.java b/TinyBannavi/src/tainavi/PlugIn_RecRD_S304K.java
new file mode 100644 (file)
index 0000000..b915cf9
--- /dev/null
@@ -0,0 +1,1137 @@
+package tainavi;\r
+\r
+import java.io.File;\r
+import java.io.UnsupportedEncodingException;\r
+import java.net.Authenticator;\r
+import java.net.URLEncoder;\r
+import java.util.ArrayList;\r
+import java.util.Hashtable;\r
+import java.util.regex.Matcher;\r
+import java.util.regex.Pattern;\r
+\r
+\r
+/*\r
+ * \r
+ */\r
+\r
+public class PlugIn_RecRD_S304K extends HDDRecorderUtils implements HDDRecorder,Cloneable {\r
+\r
+       public PlugIn_RecRD_S304K clone() {\r
+               return (PlugIn_RecRD_S304K) super.clone();\r
+       }\r
+       \r
+       private static final String thisEncoding = "MS932";\r
+\r
+       /* 必須コード - ここから */\r
+\r
+       // 種族の特性\r
+       public String getRecorderId() { return "VARDIA RD-S304K"; }\r
+       public RecType getType() { return RecType.RECORDER; }\r
+\r
+       // 個体の特性\r
+       private GetRDStatus gs = new GetRDStatus();\r
+       private ChannelCode cc = new ChannelCode(getRecorderId());\r
+       private String rsvedFile = "";\r
+\r
+       private String errmsg = "";\r
+\r
+       // 公開メソッド\r
+\r
+       /*\r
+        * \r
+        */\r
+       public ChannelCode getChCode() {\r
+               return cc;\r
+       }\r
+\r
+       /*\r
+        * チャンネルリモコン機能\r
+        */\r
+       public boolean ChangeChannel(String Channel) {\r
+               \r
+               if (Channel == null) {\r
+                       return true;\r
+               }\r
+               \r
+               int curBC;\r
+               int newBC;\r
+               String chNo;\r
+               byte enc;\r
+               \r
+               errmsg = "";\r
+               \r
+               /*\r
+                * 変更前の放送(地上波/BS/CS)\r
+                */\r
+               {\r
+                       String ch = null;\r
+                       for (int i=0; i<3 && (ch = gs.getCurChannel(getIPAddr())) == null; i++) {\r
+                               CommonUtils.milSleep(500);\r
+                       }\r
+                       if (ch == null) {\r
+                               errmsg = "レコーダへのアクセスに失敗しました(チャンネルリモコン)。";\r
+                               System.err.println(errmsg);\r
+                               return false;\r
+                       }\r
+                       \r
+                       byte[] ba = ch.getBytes();\r
+                       enc = ba[0];\r
+                       ch = ch.substring(1,5);\r
+                       \r
+                       if (ch.matches("^\\d.+")) {\r
+                               curBC = 0;\r
+                       }\r
+                       else if (ch.startsWith("BS")) {\r
+                               curBC = 1;\r
+                       }\r
+                       else if (ch.startsWith("CS")) {\r
+                               curBC = 2;\r
+                       }\r
+                       else {\r
+                               errmsg = "現在のチャンネルが認識できません("+ch+")。";\r
+                               System.err.println(errmsg);\r
+                               return false;\r
+                       }\r
+               }\r
+               \r
+               /*\r
+                * 変更後のCH\r
+                */\r
+               {\r
+                       // 放送(地上波/BS/CS)\r
+                       String cd = cc.getCH_WEB2CODE(Channel);\r
+                       String ch = cc.getCH_CODE2REC(cd);\r
+                       String typ = text2value(chtype, cd);\r
+                       if (typ == null) {\r
+                               errmsg = "鯛ナビに情報が同期されていないチャンネルです("+Channel+", "+ch+")。";\r
+                               System.err.println(errmsg);\r
+                               return false;\r
+                       }\r
+                       else if (typ.matches("^l[123]$")) {\r
+                               errmsg = "外部入力にアサインされているチャンネルには変更できません("+Channel+", "+ch+", "+typ+")。";\r
+                               System.err.println(errmsg);\r
+                               return false;\r
+                       }\r
+                       else if (typ.equals("uvd")) {\r
+                               newBC = 0;\r
+                       }\r
+                       else if (typ.equals("bsd")) {\r
+                               newBC = 1;\r
+                       }\r
+                       else if (typ.equals("csd")) {\r
+                               newBC = 2;\r
+                       }\r
+                       else {\r
+                               errmsg = "放送種別が識別できません。プログラム異常です("+Channel+", "+ch+")。";\r
+                               System.err.println(errmsg);\r
+                               return false;\r
+                       }\r
+                       \r
+                       // CH番号\r
+                       Matcher ma = Pattern.compile("(\\d\\d\\d)").matcher(ch);\r
+                       if (ma.find()) {\r
+                               chNo = ma.group(1);\r
+                       }\r
+                       else {\r
+                               errmsg = "CH番号が取得できません("+Channel+", "+ch+")。";\r
+                               System.err.println(errmsg);\r
+                               return false;\r
+                       }\r
+               }\r
+               \r
+               /*\r
+                * CH変更実行\r
+                */\r
+               \r
+               // おまじない\r
+               Authenticator.setDefault(new MyAuthenticator(getUser(), getPasswd()));\r
+               \r
+               int cBC;\r
+               for (int i=0; i<4 && newBC != (cBC = (curBC+i)%4); i++) {\r
+                       // 地上アナログ(3)が選択できるのはRE(0x06)だけ\r
+                       if (enc != 0x06 && cBC == 3) {\r
+                               continue;\r
+                       }\r
+                       \r
+                       // 切り替え実行\r
+                       reqGET("http://"+getIPAddr()+":"+getPortNo()+"/remote/remote.htm?key=21", null);\r
+                       \r
+                       // 地上波→BS・BS→CSは切り替え完了までに時間がかかる\r
+                       CommonUtils.milSleep((cBC == 0 || cBC == 1)?(3000):(1000));\r
+               }\r
+               \r
+               reqGET("http://"+getIPAddr()+":"+getPortNo()+"/remote/remote.htm?key=25", null);\r
+               CommonUtils.milSleep(1000);\r
+               for (int i=0; i<3; i++) {\r
+                       reqGET("http://"+getIPAddr()+":"+getPortNo()+"/remote/remote.htm?key=0"+chNo.substring(i,i+1), null);\r
+                       CommonUtils.milSleep(200);\r
+               }\r
+               reqGET("http://"+getIPAddr()+":"+getPortNo()+"/remote/remote.htm?key=44", null);\r
+               \r
+               return true;\r
+       }\r
+\r
+       /*\r
+        * レコーダーから予約一覧を取得する\r
+        */\r
+       public boolean GetRdReserve(boolean force)\r
+       {\r
+               System.out.println("レコーダから予約一覧を取得します("+force+")");\r
+               \r
+               errmsg = "";\r
+               \r
+               //\r
+               rsvedFile = "env/reserved."+getIPAddr()+"_"+getPortNo()+"_"+getRecorderId()+".xml";\r
+               \r
+               String vrateTFile = "env/videorate."+getIPAddr()+"_"+getPortNo()+"_"+getRecorderId()+".xml";\r
+               String arateTFile = "env/audiorate."+getIPAddr()+"_"+getPortNo()+"_"+getRecorderId()+".xml";\r
+               String folderTFile = "env/folders."+getIPAddr()+"_"+getPortNo()+"_"+getRecorderId()+".xml";\r
+               String encoderTFile = "env/encoders."+getIPAddr()+"_"+getPortNo()+"_"+getRecorderId()+".xml";\r
+               String dvdcompatTFile = "env/dvdcompat."+getIPAddr()+"_"+getPortNo()+"_"+getRecorderId()+".xml";\r
+               String deviceTFile = "env/device."+getIPAddr()+"_"+getPortNo()+"_"+getRecorderId()+".xml";\r
+               String xChapterTFile = "env/xchapter."+getIPAddr()+"_"+getPortNo()+"_"+getRecorderId()+".xml";\r
+               String msChapterTFile = "env/mschapter."+getIPAddr()+"_"+getPortNo()+"_"+getRecorderId()+".xml";\r
+               String mvChapterTFile = "env/mvchapter."+getIPAddr()+"_"+getPortNo()+"_"+getRecorderId()+".xml";\r
+               String chValueTFile = "env/chvalue."+getIPAddr()+"_"+getPortNo()+"_"+getRecorderId()+".xml";\r
+               String chTypeTFile = "env/chtype."+getIPAddr()+"_"+getPortNo()+"_"+getRecorderId()+".xml";\r
+               \r
+               File f = new File(rsvedFile);\r
+               if ( force == false && f.exists()) {\r
+                       // キャッシュから読み出し(録画設定ほか)\r
+                       vrate = TVSload(vrateTFile);\r
+                       if ( vrate.size() > 0 ) {\r
+                               System.out.println("+video rate="+vrateTFile);\r
+                       }\r
+                       arate = TVSload(arateTFile);\r
+                       if ( arate.size() > 0 ) {\r
+                               System.out.println("+audio rate="+arateTFile);\r
+                       }\r
+                       folder = TVSload(folderTFile);\r
+                       if ( folder.size() > 0 ) {\r
+                               System.out.println("+folder="+folderTFile);\r
+                       }\r
+                       encoder = TVSload(encoderTFile);\r
+                       if ( encoder.size() > 0 ) {\r
+                               System.out.println("+encoder="+encoderTFile);\r
+                       }\r
+                       dvdcompat = TVSload(dvdcompatTFile);\r
+                       if ( dvdcompat.size() > 0 ) {\r
+                               System.out.println("+dvdcompat="+dvdcompatTFile);\r
+                       }\r
+                       device = TVSload(deviceTFile);\r
+                       if ( device.size() > 0 ) {\r
+                               System.out.println("+device="+deviceTFile);\r
+                       }\r
+                       // 自動チャプタ関連\r
+                       xchapter = TVSload(xChapterTFile);\r
+                       if ( xchapter.size() > 0 ) {\r
+                               System.out.println("+xchapter="+xChapterTFile);\r
+                       }\r
+                       mschapter = TVSload(msChapterTFile);\r
+                       if ( mschapter.size() > 0 ) {\r
+                               System.out.println("+mschapter="+msChapterTFile);\r
+                       }\r
+                       mvchapter = TVSload(mvChapterTFile);\r
+                       if ( mvchapter.size() > 0 ) {\r
+                               System.out.println("+mvchapter="+mvChapterTFile);\r
+                       }\r
+                       // チャンネルコードバリュー\r
+                       chvalue = TVSload(chValueTFile);\r
+                       if ( chvalue.size() > 0 ) {\r
+                               System.out.println("+chvalue="+chValueTFile);\r
+                       }\r
+                       chtype = TVSload(chTypeTFile);\r
+                       if ( chtype.size() > 0 ) {\r
+                               System.out.println("+chtype="+chTypeTFile);\r
+                       }\r
+                       \r
+                       // キャッシュから読み出し(予約一覧)\r
+                       setReserves(ReservesFromFile(rsvedFile));\r
+                       if (getReserves().size() > 0) {\r
+                               System.out.println("+read from="+rsvedFile);\r
+                       }\r
+                       \r
+                       // なぜか設定ファイルが空になっている場合があるので、その際は再取得する\r
+                       if (vrate.size()>0 && arate.size()>0 &&\r
+                                       folder.size()>0 && encoder.size()>0 && device.size()>0 &&\r
+                                       xchapter.size()>0 && mschapter.size()>0 && mvchapter.size()>0) {\r
+                               return(true);\r
+                       }\r
+                       \r
+                       //getReserves().removeAll(getReserves());\r
+               }\r
+\r
+               // おまじない\r
+               Authenticator.setDefault(new MyAuthenticator(getUser(), getPasswd()));\r
+               \r
+               // RDから予約一覧を取り出す\r
+               Matcher ma = null;\r
+               String idx = "";\r
+               String header="";\r
+               String response="";\r
+               {\r
+                       reportProgress("get reserved list(1/5).");\r
+                       String[] d = reqGET("http://"+getIPAddr()+":"+getPortNo()+"/reserve/b_rsv.htm",null);\r
+                       header = d[0];\r
+                       response = d[1];\r
+                       \r
+                       if (response == null) {\r
+                               errmsg = "レコーダーが反応しません";\r
+                               return(false);\r
+                       }\r
+               }\r
+               ma = Pattern.compile("/reserve/(\\d+?)/reserve.htm").matcher(response);\r
+               if (ma.find()) {\r
+                       idx = ma.group(1);\r
+                       reportProgress("get reserved list(2/5).");\r
+                       String[] d = reqGET("http://"+getIPAddr()+":"+getPortNo()+"/reserve/"+idx+"/reserve.htm",null);\r
+                       header = d[0];\r
+                       response = d[1];\r
+                       \r
+                       if (response == null) {\r
+                               errmsg = "レコーダーが反応しません";\r
+                               return(false);\r
+                       }\r
+               }\r
+               \r
+               // 予約エントリー数を取得する\r
+               //int RsvCnt = 0;\r
+               ma = Pattern.compile("RsvCnt\\s*=\\s*(\\d+?);").matcher(response);\r
+               if ( ! ma.find()) {\r
+                       errmsg = "レコーダーからの情報取得に失敗しました(予約一覧)";\r
+                       return false;\r
+               }\r
+               //RsvCnt = Integer.valueOf(ma.group(1));\r
+               \r
+               // (1)録画設定の取得\r
+               {\r
+                       // ハング防止のおまじない\r
+                       reportProgress("get reserved list(3/5).");\r
+                       //reqGET("http://"+getIPAddr()+":"+getPortNo()+"/reserve/"+idx+"/b_rsvinfo.htm?0&"+(RsvCnt+1),null);\r
+                       reqGET("http://"+getIPAddr()+":"+getPortNo()+"/program/b_proginfo.htm?bExec=1",null);\r
+               }\r
+               {\r
+                       reportProgress("get reserved list(4/5).");\r
+                       String[] d = reqGET("http://"+getIPAddr()+":"+getPortNo()+"/reserve/"+idx+"/rsvinfo.htm",null);\r
+                       String hdr = d[0];\r
+                       String res = d[1];\r
+                       Matcher mb = null;\r
+                       \r
+                       // 正常取得チェック\r
+                       if ( ! res.matches("[\\s\\S]*var hdd_folder_text[\\s\\S]*")) {\r
+                               errmsg = "レコーダーからの情報取得に失敗しました(録画設定)";\r
+                               return false;\r
+                       }\r
+                       \r
+                       // (1-3)フォルダ一覧\r
+                       folder.clear();\r
+                       \r
+                       mb = Pattern.compile("var hdd_folder_text\\s*= new Array\\(([\\s\\S]+?)\\);").matcher(res);\r
+                       if (mb.find()) {\r
+                               Matcher mc = Pattern.compile("\"(.+?)\",?").matcher(mb.group(1));\r
+                               while (mc.find()) {\r
+                                       TextValueSet t = new TextValueSet();\r
+                                       t.setText(mc.group(1));\r
+                                       folder.add(t);\r
+                               }\r
+                       }\r
+                       mb = Pattern.compile("var hdd_folder_value\\s*= new Array\\(([\\s\\S]+?)\\);").matcher(res);\r
+                       if (mb.find()) {\r
+                               Matcher mc = Pattern.compile("\"(.+?)\",?").matcher(mb.group(1));\r
+                               for (TextValueSet t : folder) {\r
+                                       if (mc.find()) {\r
+                                               t.setValue(mc.group(1));\r
+                                       }\r
+                               }\r
+                       }\r
+                       \r
+                       TVSsave(folder, folderTFile);\r
+                       \r
+                       // (1-4)エンコーダ\r
+                       encoder.clear();\r
+                       \r
+                       mb = Pattern.compile("var double_encode_flg = (\\d+?);").matcher(res);\r
+                       while (mb.find()) {\r
+                               Matcher mc = Pattern.compile("\\n\\s*?switch \\( double_encode_flg \\) \\{([\\s\\S]+?default:)").matcher(res);\r
+                               if (mc.find()) {\r
+                                       Matcher md = Pattern.compile("(case "+mb.group(1)+":[\\s\\S]+?break;)").matcher(mc.group(1));\r
+                                       if (md.find()) {\r
+                                               //System.out.println(md.group(1));\r
+                                               Matcher me = Pattern.compile("name=enc_type value=.\"(\\d+?).\"[\\s\\S]+?toru_(.+?)\\.gif").matcher(md.group(1));\r
+                                               while (me.find()) {\r
+                                                       TextValueSet t = new TextValueSet();\r
+                                                       t.setText(me.group(2));\r
+                                                       t.setValue(me.group(1));\r
+                                                       encoder.add(t);\r
+                                               }\r
+                                       }\r
+                               }\r
+                       }\r
+                       \r
+                       TVSsave(encoder, encoderTFile);\r
+                       \r
+                       // (1-5)DVD互換モード\r
+                       dvdcompat.clear();\r
+                       \r
+                       mb = Pattern.compile("var dvdr_text\\s*= new Array\\(([\\s\\S]+?)\\);").matcher(res);\r
+                       if (mb.find()) {\r
+                               Matcher mc = Pattern.compile("\"(.+?)\",?").matcher(mb.group(1));\r
+                               while (mc.find()) {\r
+                                       TextValueSet t = new TextValueSet();\r
+                                       t.setText(mc.group(1));\r
+                                       dvdcompat.add(t);\r
+                               }\r
+                       }\r
+                       mb = Pattern.compile("var dvdr_value\\s*= new Array\\(([\\s\\S]+?)\\);").matcher(res);\r
+                       if (mb.find()) {\r
+                               Matcher mc = Pattern.compile("\"(.+?)\",?").matcher(mb.group(1));\r
+                               for (TextValueSet t : dvdcompat) {\r
+                                       if (mc.find()) {\r
+                                               t.setValue(mc.group(1));\r
+                                       }\r
+                               }\r
+                       }\r
+                       \r
+                       TVSsave(dvdcompat, dvdcompatTFile);\r
+                       \r
+                       // (1-6)記録先デバイス\r
+                       device.clear();\r
+                       \r
+                       mb = Pattern.compile("var media_text\\s*= new Array\\(([\\s\\S]+?)\\);").matcher(res);\r
+                       if (mb.find()) {\r
+                               Matcher mc = Pattern.compile("\"(.+?)\",?").matcher(mb.group(1));\r
+                               while (mc.find()) {\r
+                                       TextValueSet t = new TextValueSet();\r
+                                       t.setText(mc.group(1));\r
+                                       device.add(t);\r
+                               }\r
+                       }\r
+                       mb = Pattern.compile("var media_value\\s*= new Array\\(([\\s\\S]+?)\\);").matcher(res);\r
+                       if (mb.find()) {\r
+                               Matcher mc = Pattern.compile("(\\d+),?").matcher(mb.group(1));\r
+                               for (TextValueSet t : device) {\r
+                                       if (mc.find()) {\r
+                                               t.setValue(mc.group(1));\r
+                                       }\r
+                               }\r
+                       }\r
+                       \r
+                       TVSsave(device, deviceTFile);\r
+\r
+                       // (1-7)自動チャプター関連\r
+                       xchapter.clear();\r
+                       mb = Pattern.compile("var mutechapter_text\\s*= new Array\\(([\\s\\S]+?)\\);").matcher(res);\r
+                       if (mb.find()) {\r
+                               Matcher mc = Pattern.compile("\"(.+?)\",?").matcher(mb.group(1));\r
+                               while (mc.find()) {\r
+                                       TextValueSet t = new TextValueSet();\r
+                                       t.setText(mc.group(1));\r
+                                       xchapter.add(t);\r
+                               }\r
+                       }\r
+                       mb = Pattern.compile("var mutechapter_value\\s*= new Array\\(([\\s\\S]+?)\\);").matcher(res);\r
+                       if (mb.find()) {\r
+                               Matcher mc = Pattern.compile("(\\d+),?").matcher(mb.group(1));\r
+                               for (TextValueSet t : xchapter) {\r
+                                       if (mc.find()) {\r
+                                               t.setValue(mc.group(1));\r
+                                       }\r
+                               }\r
+                       }\r
+                       TVSsave(xchapter, xChapterTFile);\r
+                       \r
+                       mschapter.clear();\r
+                       mb = Pattern.compile("var magicchapter_text\\s*= new Array\\(([\\s\\S]+?)\\);").matcher(res);\r
+                       if (mb.find()) {\r
+                               Matcher mc = Pattern.compile("\"(.+?)\",?").matcher(mb.group(1));\r
+                               while (mc.find()) {\r
+                                       TextValueSet t = new TextValueSet();\r
+                                       t.setText(mc.group(1));\r
+                                       mschapter.add(t);\r
+                               }\r
+                       }\r
+                       mb = Pattern.compile("var magicchapter_value\\s*= new Array\\(([\\s\\S]+?)\\);").matcher(res);\r
+                       if (mb.find()) {\r
+                               Matcher mc = Pattern.compile("(\\d+),?").matcher(mb.group(1));\r
+                               for (TextValueSet t : mschapter) {\r
+                                       if (mc.find()) {\r
+                                               t.setValue(mc.group(1));\r
+                                       }\r
+                               }\r
+                       }\r
+                       TVSsave(mschapter, msChapterTFile);\r
+\r
+                       mvchapter.clear();\r
+                       mb = Pattern.compile("var cmchapter_text\\s*= new Array\\(([\\s\\S]+?)\\);").matcher(res);\r
+                       if (mb.find()) {\r
+                               Matcher mc = Pattern.compile("\"(.+?)\",?").matcher(mb.group(1));\r
+                               while (mc.find()) {\r
+                                       TextValueSet t = new TextValueSet();\r
+                                       t.setText(mc.group(1));\r
+                                       mvchapter.add(t);\r
+                               }\r
+                       }\r
+                       mb = Pattern.compile("var cmchapter_value\\s*= new Array\\(([\\s\\S]+?)\\);").matcher(res);\r
+                       if (mb.find()) {\r
+                               Matcher mc = Pattern.compile("(\\d+),?").matcher(mb.group(1));\r
+                               for (TextValueSet t : mvchapter) {\r
+                                       if (mc.find()) {\r
+                                               t.setValue(mc.group(1));\r
+                                       }\r
+                               }\r
+                       }\r
+                       TVSsave(mvchapter, mvChapterTFile);\r
+                       \r
+                       // (1-8)チャンネルコードバリュー\r
+                       chvalue.clear();\r
+                       chtype.clear();\r
+                       for ( String typ : new String[] { "uvd","bsd","csd","uva","bsa","l1","l2","l3" } ) {\r
+                               String txtkey = typ+"_ch_text";\r
+                               String valkey = typ+"_ch_value";\r
+                               Matcher mc = Pattern.compile("var "+txtkey+"\\s*= new Array\\(([\\s\\S]+?)\\);").matcher(res);\r
+                               Matcher md = Pattern.compile("var "+valkey+"\\s*= new Array\\(([\\s\\S]+?)\\);").matcher(res);\r
+                               if ( mc.find() && md.find() ) {\r
+                                       Matcher me = Pattern.compile("\"(.+?)\",?").matcher(mc.group(1));\r
+                                       Matcher mf = null;\r
+                                       if ( typ.equals("uva") ) {\r
+                                               mf = Pattern.compile("(\\d+),").matcher(md.group(1));\r
+                                       }\r
+                                       else {\r
+                                               mf = Pattern.compile("\"([^\"]+?)\",").matcher(md.group(1));\r
+                                       }\r
+                                       while ( me.find() && mf.find() ) {\r
+                                               TextValueSet t = new TextValueSet();\r
+                                               t.setText(me.group(1));\r
+                                               t.setValue(mf.group(1));\r
+                                               chvalue.add(t);\r
+                                               \r
+                                               TextValueSet x = new TextValueSet();\r
+                                               x.setText(mf.group(1));\r
+                                               x.setValue(typ);\r
+                                               chtype.add(x);\r
+                                       }\r
+                               }\r
+                       }\r
+                       TVSsave(chvalue, chValueTFile);\r
+                       TVSsave(chtype, chTypeTFile);\r
+               }\r
+               {\r
+                       reportProgress("get reserved list(5/5).");\r
+                       /*\r
+                       String[] d = reqGET("http://"+getIPAddr()+":"+getPortNo()+"/cmn/script_function.htm",null);\r
+                       String hdr = d[0];\r
+                       String res = d[1];\r
+                       Matcher mb = null;\r
+                       */\r
+                       \r
+                       // (1-1)画質設定\r
+                       {\r
+                               vrate.clear();\r
+                               TextValueSet t = null;\r
+                               \r
+                               t = new TextValueSet();\r
+                               t.setText("[TS]");\r
+                               t.setValue("128:");\r
+                               vrate.add(t);\r
+                               \r
+                               t = new TextValueSet();\r
+                               t.setText("[TSE] AT 4.7GB");\r
+                               t.setValue("2:4");\r
+                               vrate.add(t);\r
+                               t = new TextValueSet();\r
+                               t.setText("[TSE] AT 9.4GB");\r
+                               t.setValue("2:5");\r
+                               vrate.add(t);\r
+                               t = new TextValueSet();\r
+                               t.setText("[TSE] AT 8.5GB");\r
+                               t.setValue("2:7");\r
+                               vrate.add(t);\r
+                               \r
+                               t = new TextValueSet();\r
+                               t.setText("[TSE] 1.0");\r
+                               t.setValue("2:1000");\r
+                               vrate.add(t);\r
+                               t = new TextValueSet();\r
+                               t.setText("[TSE] 1.4");\r
+                               t.setValue("2:1400");\r
+                               vrate.add(t);\r
+                               for (int br=2000; br<=17000; ) {\r
+                                       t = new TextValueSet();\r
+                                       t.setText(String.format("[TSE] %d.%d", (br-br%1000)/1000, (br%1000)/100));\r
+                                       t.setValue("2:"+String.valueOf(br));\r
+                                       vrate.add(t);\r
+                                       if (br < 10000) {\r
+                                               br += 200;\r
+                                       }\r
+                                       else {\r
+                                               br += 500;\r
+                                       }\r
+                               }\r
+                               \r
+                               t = new TextValueSet();\r
+                               t.setText("[VR] SP4.4/4.6");\r
+                               t.setValue("1:1");\r
+                               vrate.add(t);\r
+                               t = new TextValueSet();\r
+                               t.setText("[VR] LP2.0/2.2");\r
+                               t.setValue("1:2");\r
+                               vrate.add(t);\r
+                               \r
+                               t = new TextValueSet();\r
+                               t.setText("[VR] AT 4.7GB");\r
+                               t.setValue("1:4");\r
+                               vrate.add(t);\r
+                               t = new TextValueSet();\r
+                               t.setText("[VR] AT 9.4GB");\r
+                               t.setValue("1:5");\r
+                               vrate.add(t);\r
+                               t = new TextValueSet();\r
+                               t.setText("[VR] AT 8.5GB");\r
+                               t.setValue("1:7");\r
+                               vrate.add(t);\r
+                               \r
+                               t = new TextValueSet();\r
+                               t.setText("[VR] 1.0");\r
+                               t.setValue("1:1000");\r
+                               vrate.add(t);\r
+                               t = new TextValueSet();\r
+                               t.setText("[VR] 1.4");\r
+                               t.setValue("1:1400");\r
+                               vrate.add(t);\r
+                               for (int br=2000; br<=9200; br+=200) {\r
+                                       t = new TextValueSet();\r
+                                       t.setText(String.format("[VR] %d.%d", (br-br%1000)/1000, (br%1000)/100));\r
+                                       t.setValue("1:"+String.valueOf(br));\r
+                                       vrate.add(t);\r
+                               }\r
+                               t = new TextValueSet();\r
+                               t.setText("[VR] 高レート節約");\r
+                               t.setValue("1:10");\r
+                               vrate.add(t);\r
+                               \r
+                               TVSsave(vrate, vrateTFile);\r
+                       }\r
+                       \r
+                       // (1-2)音質設定\r
+                       {\r
+                               arate.clear();\r
+                               TextValueSet t = null;\r
+                               \r
+                               t = new TextValueSet();\r
+                               t.setText("M1");\r
+                               t.setValue("1");\r
+                               arate.add(t);\r
+                               t = new TextValueSet();\r
+                               t.setText("M2");\r
+                               t.setValue("2");\r
+                               arate.add(t);\r
+                               t = new TextValueSet();\r
+                               t.setText("L-PCM");\r
+                               t.setValue("3");\r
+                               arate.add(t);\r
+                               \r
+                               TVSsave(arate, arateTFile);\r
+                       }\r
+               }\r
+               \r
+               // 予約一覧データの分析\r
+               ArrayList<ReserveList> ra = decodeReservedList(response); \r
+               for (ReserveList entry : ra) {\r
+                       entry.setCh_name(getChCode().getCH_REC2WEB(entry.getChannel()));\r
+                       \r
+                       // タイトル自動補完フラグなど本体からは取得できない情報を引き継ぐ\r
+                       copyAttributes(entry, getReserves());\r
+               }\r
+               setReserves(ra);\r
+               \r
+               // キャッシュに保存\r
+               ReservesToFile(getReserves(), rsvedFile);\r
+               \r
+               // 取得した情報の表示\r
+               System.out.println("---Reserved List Start---");\r
+               for ( int i = 0; i<getReserves().size(); i++ ) {\r
+                       // 詳細情報の取得\r
+                       ReserveList e = getReserves().get(i);\r
+                       System.out.println(String.format("[%s] %s\t%s\t%s\t%s:%s\t%s:%s\t%sm\t%s\t%s(%s)\t%s\t%s",\r
+                                       (i+1), e.getId(), e.getRec_pattern(), e.getRec_nextdate(), e.getAhh(), e.getAmm(), e.getZhh(),  e.getZmm(),     e.getRec_min(), e.getRec_mode(), e.getTitle(), e.getTitlePop(), e.getChannel(), e.getCh_name()));\r
+               }\r
+               System.out.println("---Reserved List End---");\r
+               \r
+               return(true);\r
+       }\r
+\r
+       /*\r
+        * 予約を実行する\r
+        */\r
+       public boolean PostRdEntry(ReserveList r) {\r
+               //\r
+               if (cc.getCH_WEB2CODE(r.getCh_name()) == null) {\r
+                       errmsg = "【警告】Web番組表の放送局名「"+r.getCh_name()+"」をCHコードに変換できません。CHコード設定を修正してください。" ;\r
+                       System.out.println(errmsg);\r
+                       return(false);\r
+               }\r
+               \r
+               System.out.println("Run: PostRdEntry("+r.getTitle()+")");\r
+               \r
+               errmsg = "";\r
+\r
+               // おまじない\r
+               Authenticator.setDefault(new MyAuthenticator(getUser(), getPasswd()));\r
+               \r
+               // RDから予約一覧を取り出す\r
+               Matcher ma = null;\r
+               String idx = "";\r
+               String header;\r
+               String response;\r
+               {\r
+                       reportProgress("get reserved list(1/7).");\r
+                       String[] d = reqGET("http://"+getIPAddr()+":"+getPortNo()+"/reserve/b_rsv.htm",null);\r
+                       header = d[0];\r
+                       response = d[1];\r
+                       \r
+                       if ( response == null ) {\r
+                               errmsg = "レコーダーが反応しません。";\r
+                               return(false);\r
+                       }\r
+               }\r
+               ma = Pattern.compile("/reserve/(\\d+?)/reserve.htm").matcher(response);\r
+               if (ma.find()) {\r
+                       reportProgress("get reserved list(2/7).");\r
+                       idx = ma.group(1);\r
+                       String[] d = reqGET("http://"+getIPAddr()+":"+getPortNo()+"/reserve/"+idx+"/reserve.htm",null);\r
+                       header = d[0];\r
+                       response = d[1];\r
+                       \r
+                       if ( response == null ) {\r
+                               errmsg = "レコーダーが反応しません。";\r
+                               return(false);\r
+                       }\r
+               }\r
+\r
+               // 予約エントリー数を取得する\r
+               /*\r
+               int RsvCnt = 0;\r
+               ma = Pattern.compile("RsvCnt\\s*=\\s*(\\d+?);").matcher(response);\r
+               if (ma.find()) {\r
+                       RsvCnt = Integer.valueOf(ma.group(1));\r
+               }\r
+               */\r
+       \r
+               // RDに新規登録要請\r
+               {\r
+                       // ハング防止のおまじない\r
+                       reportProgress("get program(3/7).");\r
+                       //reqGET("http://"+getIPAddr()+":"+getPortNo()+"/reserve/"+idx+"/b_rsvinfo.htm?0&"+(RsvCnt+1),null);\r
+                       reqGET("http://"+getIPAddr()+":"+getPortNo()+"/program/b_proginfo.htm?bExec=1",null);\r
+                       \r
+                       reportProgress("get program(4/7).");\r
+                       reqGET("http://"+getIPAddr()+":"+getPortNo()+"/reserve/"+idx+"/rsvinfo.htm",null);\r
+               }\r
+               \r
+               // POSTデータを変換する\r
+               Hashtable<String, String> pdat = modPostdata(r);\r
+               \r
+               // RDへの情報作成\r
+               String pstr = joinPoststr(pdat);\r
+\r
+               // RDへ情報送信\r
+               {               \r
+                       reportProgress("send request(5/7).");\r
+                       String[] d = reqPOST("http://"+getIPAddr()+":"+getPortNo()+"/reserve/"+idx+"/entry.htm", pstr, null);\r
+                       header = d[0];\r
+                       response = d[1];\r
+               }\r
+               \r
+               // 登録結果の確認\r
+               ma = Pattern.compile("alert\\(msg\\)").matcher(response);\r
+               if ( ma.find() ) {\r
+                       Matcher mb = Pattern.compile("\\bmsg=\"([\\s\\S]+?)\";").matcher(response);\r
+                       if (mb.find()) {\r
+                               errmsg = mb.group(1);\r
+                               System.out.printf("\n<<< Message from RD >>> \"%s\"\n\n", errmsg);\r
+                               Matcher mc = Pattern.compile("(正常に登録できました。|予約時間が重複しています。|W録の振り替えをおこないました)").matcher(errmsg);\r
+                               if ( ! mc.find() ) {\r
+                                       return(false);\r
+                               }\r
+                       }\r
+               }\r
+\r
+\r
+               \r
+               /*\r
+                * 予約情報の調整 \r
+                */\r
+               \r
+               // 予約ID番号を取得(キャッシュに存在しない番号が新番号)\r
+               {\r
+                       reportProgress("get identifier(6/7).");\r
+                       String[] d = reqGET("http://"+getIPAddr()+":"+getPortNo()+"/reserve/b_rsv.htm",null);\r
+                       header = d[0];\r
+                       response = d[1];\r
+               }\r
+               ma = Pattern.compile("/reserve/(\\d+?)/reserve.htm").matcher(response);\r
+               if (ma.find()) {\r
+                       idx = ma.group(1);\r
+                       reportProgress("get identifier(7/7).");\r
+                       String[] d = reqGET("http://"+getIPAddr()+":"+getPortNo()+"/reserve/"+idx+"/reserve.htm",null);\r
+                       header = d[0];\r
+                       response = d[1];\r
+               }\r
+               r.setId(getNewId(response));\r
+               \r
+               // 音質(TS/TSEでは音質の設定はできない)\r
+               ma = Pattern.compile("^\\[TS").matcher(r.getRec_mode());\r
+               if (ma.find()) {\r
+                       r.setRec_audio("");\r
+               }\r
+\r
+               // 予約パターンID\r
+               r.setRec_pattern_id(getRec_pattern_Id(r.getRec_pattern()));\r
+\r
+               // 次回予定日\r
+               r.setRec_nextdate(CommonUtils.getNextDate(r));\r
+               //r.setRec_nextdate(getNextDate(r.getRec_pattern(), r.getZhh()+":"+r.getZmm()));\r
+               \r
+               // 録画長\r
+               r.setRec_min(CommonUtils.getRecMin(r.getAhh(),r.getAmm(),r.getZhh(),r.getZmm()));\r
+               \r
+               // 開始日時・終了日時\r
+               getStartEndDateTime(r);\r
+               \r
+               // 予約リストを更新\r
+               getReserves().add(r);\r
+               \r
+               // キャッシュに保存\r
+               ReservesToFile(getReserves(), rsvedFile);\r
+               \r
+               System.out.printf("\n<<< Message from RD >>> \"%s\"\n\n", "正常に登録できました。");\r
+               return(true);\r
+       }\r
+\r
+       /*\r
+        * 予約を更新する\r
+        */\r
+       public boolean UpdateRdEntry(ReserveList o, ReserveList r) {\r
+               setErrmsg("更新処理は無効です。");\r
+               return (false);\r
+       }\r
+\r
+       /*\r
+        * 予約を削除する\r
+        */\r
+       public ReserveList RemoveRdEntry(String delno) {\r
+               setErrmsg("削除処理は無効です。");\r
+               return (null);\r
+       }\r
+\r
+       /*\r
+        * \r
+        */\r
+       public String getErrmsg() {\r
+               return (errmsg.replaceAll("\\\\r\\\\n", ""));\r
+       }\r
+\r
+       protected void setErrmsg(String s) {\r
+               errmsg = s;\r
+       }\r
+\r
+       /* ここまで */\r
+\r
+       /* 個別コード-ここから最後まで */\r
+\r
+       /*\r
+        * 非公開メソッド\r
+        */\r
+       \r
+       private String joinPoststr(Hashtable<String, String> pdat)\r
+       {\r
+               String[] pkeys = {\r
+                       "bExec",\r
+                       "start_form",\r
+                       "title_name",\r
+                       "detail",\r
+                       "genre",\r
+                       "enc_type",\r
+                       "broadcast",\r
+                       "channel_list",\r
+                       "rec_priority",\r
+                       "maiyoubi_type",\r
+                       "date",\r
+                       "start_hour",\r
+                       "start_minute",\r
+                       "end_hour",\r
+                       "end_minute",\r
+                       "disc",\r
+                       "folder",\r
+                       "vrate",\r
+                       "amode",\r
+                       "videotype_digital",    // new\r
+                       "videomode_digital",    //new\r
+                       "auto_delete",\r
+                       "dvdr",\r
+                       "lVoice",\r
+                       "edge_left",\r
+                       "bAutoChapter",\r
+                       "MagicChapter",\r
+                       "CM_Chapter",\r
+                       "channel_no",\r
+                       "dtv_sid",\r
+                       "dtv_nid",\r
+                       "net_link",\r
+                       "add_ch_text",\r
+                       "add_ch_value",\r
+                       "sport_ext_submit",\r
+                       "title_link_submit",\r
+                       "end_form"\r
+               };\r
+               \r
+               String pstr = "";\r
+               for ( String key : pkeys ) {\r
+                       //$pdat{"$key"} =~ s/(\W)/'%'.unpack("H2", $1)/ego;\r
+                       if (pdat.get(key) == null) {\r
+                               pstr += key+"=&";\r
+                       }\r
+                       else {\r
+                               pstr += key+"="+pdat.get(key)+"&";\r
+                       }\r
+               }\r
+               pstr = pstr.substring(0, pstr.length()-1);\r
+               \r
+               return(pstr);\r
+       }\r
+       \r
+       private Hashtable<String, String> modPostdata(ReserveList r) {\r
+\r
+               Hashtable<String, String> newdat = new Hashtable<String, String>();\r
+               \r
+               // 実行するよ\r
+               newdat.put("bExec", (r.getExec())?("ON"):("OFF"));\r
+\r
+               // 予約名・予約詳細\r
+               if ( r.getAutocomplete() ) {\r
+                       // RDが勝手につけるので空白\r
+                       // リピート予約の場合は空白が必須\r
+                       // 空白にしない場合はリピート予約の巡回時に予約名が固定になるので\r
+                       // たとえば見ため的には「侵略!イカ娘 #1」の#1が#2に更新されない動作になる(左記動作はA600で確認済み)\r
+                       // 録画時間1分前倒しと1分短縮のときも正しい予約名になるのを確認済み\r
+                       newdat.put("title_name", "");\r
+                       newdat.put("detail", "");\r
+               }\r
+               else {\r
+                       try {\r
+                               newdat.put("title_name", URLEncoder.encode(r.getTitle(),thisEncoding));\r
+                       } catch (UnsupportedEncodingException e1) {\r
+                               // TODO Auto-generated catch block\r
+                               e1.printStackTrace();\r
+                       }\r
+                       \r
+                       try {\r
+                               //newdat.put("detail", URLEncoder.encode(r.getDetail(), thisEncoding));\r
+                               // 改行を\r\nに変換する必要あり\r
+                               // 変換しない場合はA600のネットdeナビの詳細表示がバグる\r
+                               newdat.put("detail", URLEncoder.encode(r.getDetail().replaceAll("\n", Matcher.quoteReplacement("\r\n")), thisEncoding));\r
+                               //System.out.println(r.getDetail().replaceAll("\n", Matcher.quoteReplacement("\r\n")));\r
+                       } catch (UnsupportedEncodingException e) {\r
+                               // TODO Auto-generated catch block\r
+                               e.printStackTrace();\r
+                       }\r
+               }\r
+               \r
+               // 保存先\r
+               try {\r
+                       newdat.put("folder", URLEncoder.encode(text2value(folder, r.getRec_folder()),thisEncoding));\r
+               } catch (UnsupportedEncodingException e) {\r
+                       // TODO Auto-generated catch block\r
+                       e.printStackTrace();\r
+               }\r
+               \r
+               // ジャンル\r
+               newdat.put("genre", _recstr2genre(r.getRec_genre()));\r
+               \r
+               // DVD互換モード\r
+               newdat.put("dvdr", text2value(dvdcompat, r.getRec_dvdcompat()));\r
+               \r
+               // 録画チャンネル\r
+               String channel = cc.getCH_WEB2CODE(r.getCh_name());\r
+               String ch_no = cc.getCH_CODE2REC(channel);\r
+               if (chtype.size() > 0) {\r
+                       String typ = text2value(chtype, channel);\r
+                       if (typ.equals("uva")) {\r
+                               newdat.put("broadcast","0");            // 地上アナログ\r
+                       }\r
+                       else if (typ.equals("bsa")) {\r
+                               newdat.put("broadcast","1");            // BSアナログ\r
+                       }\r
+                       else if (typ.equals("l1")) {\r
+                               newdat.put("broadcast","2");            // 外部入力(L1)\r
+                       }\r
+                       else if (typ.equals("l2")) {\r
+                               newdat.put("broadcast","3");            // 外部入力(L2)\r
+                       }\r
+                       else if (typ.equals("l3")) {\r
+                               newdat.put("broadcast","4");            // 外部入力(L3)\r
+                       }\r
+                       else if (typ.equals("bsd")) {\r
+                               newdat.put("broadcast","10");           // BSデジタル\r
+                       }\r
+                       else if (typ.equals("csd")) {\r
+                               newdat.put("broadcast","11");           // 110度CSデジタル\r
+                       }\r
+                       else if (typ.equals("uvd")) {\r
+                               newdat.put("broadcast","12");           // 地上デジタル\r
+                       }\r
+                       else {\r
+                               // 普通ここには落ちない\r
+                               if (ch_no.startsWith("C")) {\r
+                                       newdat.put("broadcast","2");            // "C***"は外部入力(L1)\r
+                               }\r
+                               else if (ch_no.startsWith("SP")) {\r
+                                       newdat.put("broadcast","4");            // "SP***"は外部入力(L3)\r
+                               }\r
+                               else {\r
+                                       newdat.put("broadcast","3");            // 未定義は全部外部入力(L2)\r
+                               }\r
+                       }\r
+                       \r
+                       newdat.put("channel_list", channel);\r
+                       newdat.put("channel_no", channel);\r
+               }\r
+               else {\r
+                       // 後方互換\r
+                       Matcher ma = Pattern.compile("^(.)(.)").matcher(ch_no);\r
+                       if (ma.find()) {\r
+                               if ((ma.group(1)+ma.group(2)).equals("CH")) {\r
+                                       newdat.put("broadcast","0");            // 地上アナログ\r
+                               }\r
+                               else if ((ma.group(1)+ma.group(2)).equals("BS")) {\r
+                                       newdat.put("broadcast","10");           // BSデジタル\r
+                               }\r
+                               else if ((ma.group(1)+ma.group(2)).equals("CS")) {\r
+                                       newdat.put("broadcast","11");           // 110度CSデジタル\r
+                               }\r
+                               else if (ma.group(1).equals("L")) {\r
+                                       newdat.put("broadcast", String.valueOf(Integer.valueOf(ma.group(2))+1));        // ライン入力ABC\r
+                               }\r
+                               else {\r
+                                       newdat.put("broadcast","12");           // 地上デジタル\r
+                               }\r
+                               \r
+                               newdat.put("channel_list", channel);\r
+                               newdat.put("channel_no", channel);\r
+                       }\r
+               }\r
+               \r
+               // 録画レート\r
+               newdat.put("enc_type", text2value(encoder, r.getTuner()));\r
+               \r
+               Matcher ma = Pattern.compile("^(\\d+?):(.*?)$").matcher(text2value(vrate, r.getRec_mode()));\r
+               if (ma.find()) {\r
+                       if (ma.group(1).equals("1")) {\r
+                               // VR\r
+                               newdat.put("vrate",ma.group(2));        \r
+                               newdat.put("amode",text2value(arate, r.getRec_audio()));        \r
+                               newdat.put("videotype_digital",ma.group(1));    \r
+                               newdat.put("videomode_digital",ma.group(2));\r
+                       }\r
+                       else if (ma.group(1).equals("2")) {\r
+                               // TSE\r
+                               newdat.put("vrate","1");        \r
+                               newdat.put("amode","1");        \r
+                               newdat.put("videotype_digital",ma.group(1));    \r
+                               newdat.put("videomode_digital",ma.group(2));\r
+                       }\r
+                       else {\r
+                               // TS\r
+                               newdat.put("vrate","1");        \r
+                               newdat.put("amode","1");        \r
+                               newdat.put("videotype_digital",ma.group(1));    \r
+                               //newdat.put("videomode_digital",ma.group(2));\r
+                       }\r
+                       \r
+               }\r
+\r
+               // 繰り返し\r
+               ma = Pattern.compile("^\\d").matcher(r.getRec_pattern());\r
+               if (ma.find()) { \r
+                       newdat.put("maiyoubi_type","0");\r
+                       newdat.put("date", r.getRec_pattern());\r
+               }\r
+               else {\r
+                       newdat.put("maiyoubi_type", "1");\r
+                       int i = 1;\r
+                       for ( String s : RPTPTN ) {\r
+                               if ( s.equals(r.getRec_pattern()) == true ) {\r
+                                       newdat.put("date", String.valueOf(i));\r
+                               }\r
+                               i++;\r
+                       }\r
+               }\r
+\r
+\r
+               newdat.put("start_hour", r.getAhh());\r
+               newdat.put("start_minute", r.getAmm());\r
+               newdat.put("end_hour", r.getZhh());\r
+               newdat.put("end_minute", r.getZmm());\r
+\r
+               // 記録先\r
+               newdat.put("disc", text2value(device, r.getRec_device()));\r
+\r
+               // 自動チャプター関連\r
+               newdat.put("bAutoChapter"               , text2value(xchapter, r.getRec_xchapter()));\r
+               newdat.put("MagicChapter"               , text2value(mschapter, r.getRec_mschapter()));\r
+               newdat.put("CM_Chapter"                 , text2value(mvchapter, r.getRec_mvchapter()));\r
+               \r
+               // 追加\r
+               newdat.put("start_form"                 , "");\r
+               newdat.put("rec_priority"               , "150");\r
+               newdat.put("title_link"                 , "0");\r
+               newdat.put("auto_delete"                , "0");\r
+               newdat.put("video_es"                   , "00");\r
+               newdat.put("audio_es"                   , "10");\r
+               newdat.put("edge_left"                  , "0");         // 録画のりしろ\r
+               //newdat.put("MagicChapter"             , "0");\r
+               //newdat.put("MagicChapter"             , "0");\r
+               //newdat.put("CM_Chapter"                       , "0");\r
+               newdat.put("add_ch_text"                , "");\r
+               newdat.put("add_ch_value"               , "");\r
+               newdat.put("sport_ext_submit"   , "undefined");\r
+               newdat.put("title_link_submit"  , "undefined");\r
+               newdat.put("end_form"                   , "0");\r
+               \r
+               return(newdat);\r
+       }\r
+\r
+       private String _recstr2genre(String genre)\r
+       {\r
+               if (genre.equals(TVProgram.ProgGenre.MOVIE.toString())) {\r
+                       return "0";\r
+               }\r
+               else if (genre.equals(TVProgram.ProgGenre.MUSIC.toString())) {\r
+                       return "1";\r
+               }\r
+               else if (genre.equals(TVProgram.ProgGenre.DORAMA.toString())) {\r
+                       return "2";\r
+               }\r
+               else if (genre.equals(TVProgram.ProgGenre.ANIME.toString())) {\r
+                       return "3";\r
+               }\r
+               else if (genre.equals(TVProgram.ProgGenre.SPORTS.toString())) {\r
+                       return "4";\r
+               }\r
+               else if (genre.equals(TVProgram.ProgGenre.DOCUMENTARY.toString())) {\r
+                       return "5";\r
+               }\r
+               else if (genre.equals(TVProgram.ProgGenre.THEATER.toString())) {\r
+                       return "6";\r
+               }\r
+               else if (genre.equals(TVProgram.ProgGenre.HOBBY.toString())) {\r
+                       return "7";\r
+               }\r
+               else if (genre.equals(TVProgram.ProgGenre.VARIETY.toString())) {\r
+                       return "8";\r
+               }\r
+               else {\r
+                       return "10";\r
+               }\r
+       }\r
+}\r
diff --git a/TinyBannavi/src/tainavi/PlugIn_RecRD_S600.java b/TinyBannavi/src/tainavi/PlugIn_RecRD_S600.java
new file mode 100644 (file)
index 0000000..eed9349
--- /dev/null
@@ -0,0 +1,1194 @@
+package tainavi;\r
+\r
+import java.io.File;\r
+import java.io.UnsupportedEncodingException;\r
+import java.net.Authenticator;\r
+import java.net.URLEncoder;\r
+import java.util.ArrayList;\r
+import java.util.Hashtable;\r
+import java.util.regex.Matcher;\r
+import java.util.regex.Pattern;\r
+\r
+\r
+/*\r
+ * \r
+ */\r
+\r
+public class PlugIn_RecRD_S600 extends HDDRecorderUtils implements HDDRecorder,Cloneable {\r
+\r
+       @Override\r
+       public PlugIn_RecRD_S600 clone() {\r
+               return (PlugIn_RecRD_S600) super.clone();\r
+       }\r
+       \r
+       private static final String thisEncoding = "MS932";\r
+\r
+       /* 必須コード  - ここから */\r
+       \r
+       // 種族の特性\r
+       @Override\r
+       public String getRecorderId() { return "VARDIA RD-S600"; }\r
+       @Override\r
+       public RecType getType() { return RecType.RECORDER; }\r
+       \r
+       // 個体の特性\r
+       private GetRDStatus gs = new GetRDStatus();\r
+       private ChannelCode cc = new ChannelCode(getRecorderId());\r
+       private String rsvedFile = "";\r
+       \r
+       private String errmsg = "";\r
+\r
+       // 公開メソッド\r
+       \r
+       /*\r
+        * \r
+        */\r
+       @Override\r
+       public ChannelCode getChCode() {\r
+               return cc;\r
+       }\r
+       \r
+       /*\r
+        * チャンネルリモコン機能\r
+        */\r
+       @Override\r
+       public boolean ChangeChannel(String Channel) {\r
+               \r
+               if (Channel == null) {\r
+                       return true;\r
+               }\r
+               \r
+               int curBC;\r
+               int newBC;\r
+               String chNo;\r
+               byte enc;\r
+               \r
+               errmsg = "";\r
+               \r
+               /*\r
+                * 変更前の放送(地上波/BS/CS)\r
+                */\r
+               {\r
+                       String ch = null;\r
+                       for (int i=0; i<3 && (ch = gs.getCurChannel(getIPAddr())) == null; i++) {\r
+                               CommonUtils.milSleep(500);\r
+                       }\r
+                       if (ch == null) {\r
+                               errmsg = "レコーダへのアクセスに失敗しました(チャンネルリモコン)。";\r
+                               System.err.println(errmsg);\r
+                               return false;\r
+                       }\r
+                       \r
+                       byte[] ba = ch.getBytes();\r
+                       enc = ba[0];\r
+                       ch = ch.substring(1,5);\r
+                       \r
+                       if (ch.matches("^\\d.+")) {\r
+                               curBC = 0;\r
+                       }\r
+                       else if (ch.startsWith("BS")) {\r
+                               curBC = 1;\r
+                       }\r
+                       else if (ch.startsWith("CS")) {\r
+                               curBC = 2;\r
+                       }\r
+                       else {\r
+                               errmsg = "現在のチャンネルが認識できません("+ch+")。";\r
+                               System.err.println(errmsg);\r
+                               return false;\r
+                       }\r
+               }\r
+               \r
+               /*\r
+                * 変更後のCH\r
+                */\r
+               {\r
+                       // 放送(地上波/BS/CS)\r
+                       String cd = cc.getCH_WEB2CODE(Channel);\r
+                       String ch = cc.getCH_CODE2REC(cd);\r
+                       String typ = text2value(chtype, cd);\r
+                       if (typ == null) {\r
+                               errmsg = "鯛ナビに情報が同期されていないチャンネルです("+Channel+", "+ch+")。";\r
+                               System.err.println(errmsg);\r
+                               return false;\r
+                       }\r
+                       else if (typ.matches("^l[123]$")) {\r
+                               errmsg = "外部入力にアサインされているチャンネルには変更できません("+Channel+", "+ch+", "+typ+")。";\r
+                               System.err.println(errmsg);\r
+                               return false;\r
+                       }\r
+                       else if (typ.equals("uvd")) {\r
+                               newBC = 0;\r
+                       }\r
+                       else if (typ.equals("bsd")) {\r
+                               newBC = 1;\r
+                       }\r
+                       else if (typ.equals("csd")) {\r
+                               newBC = 2;\r
+                       }\r
+                       else {\r
+                               errmsg = "放送種別が識別できません。プログラム異常です("+Channel+", "+ch+")。";\r
+                               System.err.println(errmsg);\r
+                               return false;\r
+                       }\r
+                       \r
+                       // CH番号\r
+                       Matcher ma = Pattern.compile("(\\d\\d\\d)").matcher(ch);\r
+                       if (ma.find()) {\r
+                               chNo = ma.group(1);\r
+                       }\r
+                       else {\r
+                               errmsg = "CH番号が取得できません("+Channel+", "+ch+")。";\r
+                               System.err.println(errmsg);\r
+                               return false;\r
+                       }\r
+               }\r
+               \r
+               /*\r
+                * CH変更実行\r
+                */\r
+               \r
+               // おまじない\r
+               Authenticator.setDefault(new MyAuthenticator(getUser(), getPasswd()));\r
+               \r
+               int cBC;\r
+               for (int i=0; i<4 && newBC != (cBC = (curBC+i)%4); i++) {\r
+                       // 地上アナログ(3)が選択できるのはRE(0x06)だけ\r
+                       if (enc != 0x06 && cBC == 3) {\r
+                               continue;\r
+                       }\r
+                       \r
+                       // 切り替え実行\r
+                       reqGET("http://"+getIPAddr()+":"+getPortNo()+"/remote/remote.htm?key=21", null);\r
+                       \r
+                       // 地上波→BS・BS→CSは切り替え完了までに時間がかかる\r
+                       CommonUtils.milSleep((cBC == 0 || cBC == 1)?(3000):(1000));\r
+               }\r
+               \r
+               reqGET("http://"+getIPAddr()+":"+getPortNo()+"/remote/remote.htm?key=25", null);\r
+               CommonUtils.milSleep(1000);\r
+               for (int i=0; i<3; i++) {\r
+                       reqGET("http://"+getIPAddr()+":"+getPortNo()+"/remote/remote.htm?key=0"+chNo.substring(i,i+1), null);\r
+                       CommonUtils.milSleep(200);\r
+               }\r
+               reqGET("http://"+getIPAddr()+":"+getPortNo()+"/remote/remote.htm?key=44", null);\r
+               \r
+               return true;\r
+       }\r
+\r
+       /*\r
+        *      レコーダーから予約一覧を取得する \r
+        */\r
+       @Override\r
+       public boolean GetRdReserve(boolean force)\r
+       {\r
+               System.out.println("Run: GetRdReserve("+force+")");\r
+               \r
+               errmsg = "";\r
+               \r
+               //\r
+               rsvedFile = "env/reserved."+getIPAddr()+"_"+getPortNo()+"_"+getRecorderId()+".xml";\r
+               String vrateTFile = "env/videorate."+getIPAddr()+"_"+getPortNo()+"_"+getRecorderId()+".xml";\r
+               String arateTFile = "env/audiorate."+getIPAddr()+"_"+getPortNo()+"_"+getRecorderId()+".xml";\r
+               String folderTFile = "env/folders."+getIPAddr()+"_"+getPortNo()+"_"+getRecorderId()+".xml";\r
+               String encoderTFile = "env/encoders."+getIPAddr()+"_"+getPortNo()+"_"+getRecorderId()+".xml";\r
+               String dvdcompatTFile = "env/dvdcompat."+getIPAddr()+"_"+getPortNo()+"_"+getRecorderId()+".xml";\r
+               String deviceTFile = "env/device."+getIPAddr()+"_"+getPortNo()+"_"+getRecorderId()+".xml";\r
+               String xChapterTFile = "env/xchapter."+getIPAddr()+"_"+getPortNo()+"_"+getRecorderId()+".xml";\r
+               String msChapterTFile = "env/mschapter."+getIPAddr()+"_"+getPortNo()+"_"+getRecorderId()+".xml";\r
+               String mvChapterTFile = "env/mvchapter."+getIPAddr()+"_"+getPortNo()+"_"+getRecorderId()+".xml";\r
+               String chValueTFile = "env/chvalue."+getIPAddr()+"_"+getPortNo()+"_"+getRecorderId()+".xml";\r
+               String chTypeTFile = "env/chtype."+getIPAddr()+"_"+getPortNo()+"_"+getRecorderId()+".xml";\r
+\r
+               File f = new File(rsvedFile);\r
+               if ( force == false && f.exists()) {\r
+                       // キャッシュから読み出し(録画設定ほか)\r
+                       vrate = TVSload(vrateTFile);\r
+                       if ( vrate.size() > 0 ) {\r
+                               System.out.println("+video rate="+vrateTFile);\r
+                       }\r
+                       arate = TVSload(arateTFile);\r
+                       if ( arate.size() > 0 ) {\r
+                               System.out.println("+audio rate="+arateTFile);\r
+                       }\r
+                       folder = TVSload(folderTFile);\r
+                       if ( folder.size() > 0 ) {\r
+                               System.out.println("+folder="+folderTFile);\r
+                       }\r
+                       encoder = TVSload(encoderTFile);\r
+                       if ( encoder.size() > 0 ) {\r
+                               System.out.println("+encoder="+encoderTFile);\r
+                       }\r
+                       dvdcompat = TVSload(dvdcompatTFile);\r
+                       if ( dvdcompat.size() > 0 ) {\r
+                               System.out.println("+dvdcompat="+dvdcompatTFile);\r
+                       }\r
+                       device = TVSload(deviceTFile);\r
+                       if ( device.size() > 0 ) {\r
+                               System.out.println("+device="+deviceTFile);\r
+                       }\r
+                       // 自動チャプタ関連\r
+                       xchapter = TVSload(xChapterTFile);\r
+                       if ( xchapter.size() > 0 ) {\r
+                               System.out.println("+xchapter="+xChapterTFile);\r
+                       }\r
+                       mschapter = TVSload(msChapterTFile);\r
+                       if ( mschapter.size() > 0 ) {\r
+                               System.out.println("+mschapter="+msChapterTFile);\r
+                       }\r
+                       mvchapter = TVSload(mvChapterTFile);\r
+                       if ( mvchapter.size() > 0 ) {\r
+                               System.out.println("+mvchapter="+mvChapterTFile);\r
+                       }\r
+                       // チャンネルコードバリュー\r
+                       chvalue = TVSload(chValueTFile);\r
+                       if ( chvalue.size() > 0 ) {\r
+                               System.out.println("+chvalue="+chValueTFile);\r
+                       }\r
+                       chtype = TVSload(chTypeTFile);\r
+                       if ( chtype.size() > 0 ) {\r
+                               System.out.println("+chtype="+chTypeTFile);\r
+                       }\r
+                       \r
+                       // キャッシュから読み出し(予約一覧)\r
+                       setReserves(ReservesFromFile(rsvedFile));\r
+                       if (getReserves().size() > 0) {\r
+                               System.out.println("+read from="+rsvedFile);\r
+                       }\r
+                       \r
+                       // なぜか設定ファイルが空になっている場合があるので、その際は再取得する\r
+                       if (vrate.size()>0 && arate.size()>0 &&\r
+                                       folder.size()>0 && encoder.size()>0 && device.size()>0 &&\r
+                                       xchapter.size()>0 && mschapter.size()>0 && mvchapter.size()>0) {\r
+                               return(true);\r
+                       }\r
+                       \r
+                       //getReserves().removeAll(getReserves());\r
+               }\r
+               \r
+               // おまじない\r
+               Authenticator.setDefault(new MyAuthenticator(getUser(), getPasswd()));\r
+\r
+               // RDから予約一覧を取り出す\r
+               Matcher ma = null;\r
+               String idx = "";\r
+               String header="";\r
+               String response="";\r
+               {\r
+                       reportProgress("get reserved list(1/4).");\r
+                       String[] d = reqGET("http://"+getIPAddr()+":"+getPortNo()+"/reserve/b_rsv.htm",null);\r
+                       header = d[0];\r
+                       response = d[1];\r
+                       \r
+                       if (response == null) {\r
+                               errmsg = "レコーダーが反応しません";\r
+                               return(false);\r
+                       }\r
+               }\r
+               ma = Pattern.compile("/reserve/(\\d+?)/reserve.htm").matcher(response);\r
+               if (ma.find()) {\r
+                       idx = ma.group(1);\r
+                       reportProgress("get reserved list(2/4).");\r
+                       String[] d = reqGET("http://"+getIPAddr()+":"+getPortNo()+"/reserve/"+idx+"/reserve.htm",null);\r
+                       header = d[0];\r
+                       response = d[1];\r
+                       \r
+                       if (response == null) {\r
+                               errmsg = "レコーダーが反応しません";\r
+                               return(false);\r
+                       }\r
+               }\r
+               \r
+               // 予約エントリー数を取得する\r
+               int RsvCnt = 0;\r
+               ma = Pattern.compile("RsvCnt\\s*=\\s*(\\d+?);").matcher(response);\r
+               if ( ! ma.find()) {\r
+                       errmsg = "レコーダーからの情報取得に失敗しました(予約一覧)";\r
+                       return false;\r
+               }\r
+               RsvCnt = Integer.valueOf(ma.group(1));\r
+               \r
+               // (1)録画設定の取得\r
+               {\r
+                       // ハング防止のおまじない\r
+                       reportProgress("get reserved list(3/4).");\r
+                       reqGET("http://"+getIPAddr()+":"+getPortNo()+"/reserve/"+idx+"/b_rsvinfo.htm?0&"+(RsvCnt+1),null);\r
+               }\r
+               {\r
+                       reportProgress("get reserved list(4/4).");\r
+                       String[] d = reqGET("http://"+getIPAddr()+":"+getPortNo()+"/reserve/"+idx+"/rsvinfo.htm",null);\r
+                       String hdr = d[0];\r
+                       String res = d[1];\r
+                       Matcher mb = null;\r
+                       \r
+                       // 正常取得チェック\r
+                       if ( ! res.matches("[\\s\\S]*var hdd_folder_text[\\s\\S]*")) {\r
+                               errmsg = "レコーダーからの情報取得に失敗しました(録画設定)";\r
+                               return false;\r
+                       }\r
+                       \r
+                       // (1-1)画質設定\r
+                       setSettingEtc(vrate,"vrate",1,res);\r
+                       TVSsave(vrate, vrateTFile);\r
+                       \r
+                       // (1-2)音質設定\r
+                       setSettingEtc(arate,"amode",0,res);\r
+                       TVSsave(arate, arateTFile);\r
+                       \r
+                       // (1-3)フォルダ一覧\r
+                       setSettingEtc(folder,"hdd_folder",1,res);\r
+                       TVSsave(folder, folderTFile);\r
+                       \r
+                       // (1-4)エンコーダ\r
+                       encoder.clear();\r
+                       \r
+                       mb = Pattern.compile("var double_encode_flg = (\\d+?);").matcher(res);\r
+                       while (mb.find()) {\r
+                               Matcher mc = Pattern.compile("\\n\\s*?switch \\( double_encode_flg \\) \\{([\\s\\S]+?default:)").matcher(res);\r
+                               if (mc.find()) {\r
+                                       Matcher md = Pattern.compile("(case "+mb.group(1)+":[\\s\\S]+?break;)").matcher(mc.group(1));\r
+                                       if (md.find()) {\r
+                                               Matcher me = Pattern.compile("name=enc_type value=.\"(\\d+?).\"[\\s\\S]+?toru_(.+?)\\.gif").matcher(md.group(1));\r
+                                               while (me.find()) {\r
+                                                       TextValueSet t = new TextValueSet();\r
+                                                       t.setText(me.group(2));\r
+                                                       t.setValue(me.group(1));\r
+                                                       encoder.add(t);\r
+                                               }\r
+                                       }\r
+                               }\r
+                       }\r
+                       \r
+                       TVSsave(encoder, encoderTFile);\r
+                       \r
+                       // (1-5)DVD互換モード\r
+                       setSettingEtc(dvdcompat,"dvdr",0,res);\r
+                       TVSsave(dvdcompat, dvdcompatTFile);\r
+                       \r
+                       // (1-6)記録先デバイス\r
+                       setSettingEtc(device,"media",0,res);\r
+                       TVSsave(device, deviceTFile);\r
+                       \r
+                       // (1-7)自動チャプター関連\r
+                       setSettingEtc(xchapter,"mutechapter",0,res);\r
+                       TVSsave(xchapter, xChapterTFile);\r
+                       \r
+                       setSettingEtc(mschapter,"magicchapter",0,res);\r
+                       TVSsave(mschapter, msChapterTFile);\r
+\r
+                       setSettingEtc(mvchapter,"cmchapter",0,res);\r
+                       TVSsave(mvchapter, mvChapterTFile);\r
+                       \r
+                       // (1-8)チャンネルコードバリュー\r
+                       chvalue.clear();\r
+                       chtype.clear();\r
+                       for ( String typ : new String[] { "uvd","bsd","csd","uva","bsa","l1","l2","l3" } ) {\r
+                               String txtkey = typ+"_ch_text";\r
+                               String valkey = typ+"_ch_value";\r
+                               Matcher mc = Pattern.compile("var "+txtkey+"\\s*= new Array\\(([\\s\\S]+?)\\);").matcher(res);\r
+                               Matcher md = Pattern.compile("var "+valkey+"\\s*= new Array\\(([\\s\\S]+?)\\);").matcher(res);\r
+                               if ( mc.find() && md.find() ) {\r
+                                       Matcher me = Pattern.compile("\"(.+?)\",?").matcher(mc.group(1));\r
+                                       Matcher mf = null;\r
+                                       if ( typ.equals("uva") ) {\r
+                                               mf = Pattern.compile("(\\d+),").matcher(md.group(1));\r
+                                       }\r
+                                       else {\r
+                                               mf = Pattern.compile("\"([^\"]+?)\",").matcher(md.group(1));\r
+                                       }\r
+                                       while ( me.find() && mf.find() ) {\r
+                                               TextValueSet t = new TextValueSet();\r
+                                               t.setText(me.group(1));\r
+                                               t.setValue(mf.group(1));\r
+                                               chvalue.add(t);\r
+                                               \r
+                                               TextValueSet x = new TextValueSet();\r
+                                               x.setText(mf.group(1));\r
+                                               x.setValue(typ);\r
+                                               chtype.add(x);\r
+                                       }\r
+                               }\r
+                       }\r
+                       TVSsave(chvalue, chValueTFile);\r
+                       TVSsave(chtype, chTypeTFile);\r
+               }\r
+               \r
+               // 予約一覧データの分析\r
+               // 情報ブロックをとりだし…\r
+               {\r
+                       //int cnt = 0;\r
+                       ArrayList<ReserveList> newReserveList = new ArrayList<ReserveList>();\r
+                       \r
+                       ma = Pattern.compile("(c1\\[\\d+?\\]=[\\s\\S]+?\";)\\n").matcher(response);\r
+                       while ( ma.find() ) {\r
+                               // 個々のデータを取り出す\r
+                               ReserveList entry = new ReserveList();\r
+                               \r
+                               Matcher mb = null;\r
+                                       \r
+                               String[] d = new String[17];\r
+                               mb = Pattern.compile("c\\d+?\\[\\d+?\\]=\"(.*?)\";").matcher(ma.group(1));\r
+                               for (int i=0; i<d.length; i++) {\r
+                                       if ( mb.find()) {\r
+                                               d[i] = mb.group(1);\r
+                                       }\r
+                                       //System.out.println(i+") "+d[i]);\r
+                               }\r
+                               \r
+                               // 実行ON/OFF\r
+                               if ( ! d[1].equals("1")) {\r
+                                       entry.setExec(false);\r
+                               }\r
+                               \r
+                               // 予約名のエスケープを解除する\r
+                               String title = d[2];\r
+                               mb = Pattern.compile("<BR>").matcher(title);    // 余計な改行の削除\r
+                               if (mb.find()) title = mb.replaceAll("");\r
+                               mb = Pattern.compile("&quot;").matcher(title);\r
+                               if (mb.find()) title = mb.replaceAll("\"");\r
+                               mb = Pattern.compile("&lt;").matcher(title);\r
+                               if (mb.find()) title = mb.replaceAll("<");\r
+                               mb = Pattern.compile("&gt;").matcher(title);\r
+                               if (mb.find()) title = mb.replaceAll(">");\r
+                               mb = Pattern.compile("&nbsp;").matcher(title);\r
+                               if (mb.find()) title = mb.replaceAll(" ");\r
+                               \r
+                               entry.setId(d[0]);\r
+                               entry.setRec_pattern(d[5]);\r
+                               entry.setRec_pattern_id(getRec_pattern_Id(entry.getRec_pattern()));\r
+                               mb = Pattern.compile("(\\d\\d):(\\d\\d).*?(\\d\\d):(\\d\\d)").matcher(d[6]+"-"+d[7]);\r
+                               if (mb.find()) {\r
+                                       entry.setAhh(mb.group(1));\r
+                                       entry.setAmm(mb.group(2));\r
+                                       entry.setZhh(mb.group(3));\r
+                                       entry.setZmm(mb.group(4));\r
+                               }\r
+                               entry.setRec_nextdate(CommonUtils.getNextDate(entry));\r
+                               //entry.setRec_nextdate(getNextDate(entry.getRec_pattern(), entry.getZhh()+":"+entry.getZmm()));\r
+                               entry.setRec_min(CommonUtils.getRecMin(entry.getAhh(), entry.getAmm(), entry.getZhh(), entry.getZmm()));\r
+                               getStartEndDateTime(entry);\r
+                               \r
+                               if (d[3].equals("18") || d[3].equals("10")  || d[3].equals("9")) {\r
+                                       entry.setTuner("TS2");\r
+                               }\r
+                               else if (d[3].equals("17") || d[3].equals("12")  || d[3].equals("11")) {\r
+                                       entry.setTuner("TS1");\r
+                               }\r
+                               else if (d[3].equals("16") || d[3].equals("7")) {\r
+                                       entry.setTuner("RE");\r
+                               }\r
+                               else {\r
+                                       entry.setTuner("--");\r
+                               }\r
+                               \r
+                               entry.setRec_mode(d[9]);\r
+                               entry.setTitle(title);\r
+                               entry.setTitlePop(TraceProgram.replacePop(title));\r
+                               entry.setCh_name(cc.getCH_REC2WEB(d[4]));\r
+                               entry.setChannel(d[4]);\r
+       \r
+                               //entry.setRec_audio(_recstr2audio(d[10]));\r
+                               entry.setRec_audio(d[10]);\r
+\r
+                               //entry.rec_folder = data.get();        // 予約一覧からはとれない\r
+                               //entry.rec_genre = data.get();         // 予約一覧からはとれない\r
+\r
+                               // タイトル自動補完フラグなど本体からは取得できない情報を引き継ぐ\r
+                               copyAttributes(entry, getReserves());\r
+                               \r
+                               // 予約情報を保存\r
+                               newReserveList.add(entry.clone());\r
+                       }\r
+                       setReserves(newReserveList);\r
+               }\r
+               \r
+               // 取得した情報の表示\r
+               System.out.println("---Reserved List Start---");\r
+               for ( int i = 0; i<getReserves().size(); i++ ) {\r
+                       // 詳細情報の取得\r
+                       ReserveList e = getReserves().get(i);\r
+                       System.out.println(String.format("[%s] %s\t%s\t%s\t%s:%s\t%s:%s\t%sm\t%s\t%s\t%s\t%s",\r
+                                       (i+1), e.getId(), e.getRec_pattern(), e.getRec_nextdate(), e.getAhh(), e.getAmm(), e.getZhh(),  e.getZmm(),     e.getRec_min(), e.getRec_mode(), e.getTitle(), e.getChannel(), e.getCh_name()));\r
+               }\r
+               System.out.println("---Reserved List End---");\r
+               \r
+               // キャッシュに保存\r
+               ReservesToFile(getReserves(), rsvedFile);\r
+               \r
+               return(true);\r
+       }\r
+       \r
+       /*\r
+        *      予約を実行する\r
+        */\r
+       @Override\r
+       public boolean PostRdEntry(ReserveList r)\r
+       {\r
+               //\r
+               if (cc.getCH_WEB2CODE(r.getCh_name()) == null) {\r
+                       errmsg = "【警告】Web番組表の放送局名「"+r.getCh_name()+"」をCHコードに変換できません。CHコード設定を修正してください。" ;\r
+                       System.out.println(errmsg);\r
+                       return(false);\r
+               }\r
+               \r
+               System.out.println("Run: PostRdEntry("+r.getTitle()+")");\r
+               \r
+               errmsg = "";\r
+\r
+               // おまじない\r
+               Authenticator.setDefault(new MyAuthenticator(getUser(), getPasswd()));\r
+               \r
+               // RDから予約一覧を取り出す\r
+               Matcher ma = null;\r
+               String idx = "";\r
+               String header;\r
+               String response;\r
+               {\r
+                       reportProgress("get reserved list(1/7).");\r
+                       String[] d = reqGET("http://"+getIPAddr()+":"+getPortNo()+"/reserve/b_rsv.htm",null);\r
+                       header = d[0];\r
+                       response = d[1];\r
+                       \r
+                       if ( response == null ) {\r
+                               errmsg = "レコーダーが反応しません。";\r
+                               return(false);\r
+                       }\r
+               }\r
+               ma = Pattern.compile("/reserve/(\\d+?)/reserve.htm").matcher(response);\r
+               if (ma.find()) {\r
+                       reportProgress("get reserved list(2/7).");\r
+                       idx = ma.group(1);\r
+                       String[] d = reqGET("http://"+getIPAddr()+":"+getPortNo()+"/reserve/"+idx+"/reserve.htm",null);\r
+                       header = d[0];\r
+                       response = d[1];\r
+                       \r
+                       if ( response == null ) {\r
+                               errmsg = "レコーダーが反応しません。";\r
+                               return(false);\r
+                       }\r
+               }\r
+               \r
+               // 予約エントリー数を取得する\r
+               int RsvCnt = 0;\r
+               ma = Pattern.compile("RsvCnt\\s*=\\s*(\\d+?);").matcher(response);\r
+               if (ma.find()) {\r
+                       RsvCnt = Integer.valueOf(ma.group(1));\r
+               }\r
+       \r
+               // RDに新規登録要請\r
+               {\r
+                       // ハング防止のおまじない\r
+                       reportProgress("get program(3/7).");\r
+                       reqGET("http://"+getIPAddr()+":"+getPortNo()+"/reserve/"+idx+"/b_rsvinfo.htm?0&"+(RsvCnt+1),null);\r
+                       \r
+                       reportProgress("get program(4/7).");\r
+                       reqGET("http://"+getIPAddr()+":"+getPortNo()+"/reserve/"+idx+"/rsvinfo.htm",null);\r
+               }\r
+               \r
+               // POSTデータを変換する\r
+               Hashtable<String, String> pdat = modPostdata(r);\r
+               \r
+               // RDへの情報作成\r
+               String pstr = joinPoststr(pdat);\r
+\r
+               // RDへ情報送信\r
+               {               \r
+                       reportProgress("send request.(5/7)");\r
+                       String[] d = reqPOST("http://"+getIPAddr()+":"+getPortNo()+"/reserve/"+idx+"/entry.htm", pstr, null);\r
+                       header = d[0];\r
+                       response = d[1];\r
+               }\r
+               \r
+               // 登録結果の確認\r
+               ma = Pattern.compile("alert\\(msg\\)").matcher(response);\r
+               if ( ma.find() ) {\r
+                       Matcher mb = Pattern.compile("\\bmsg=\"([\\s\\S]+?)\";").matcher(response);\r
+                       if (mb.find()) {\r
+                               errmsg = mb.group(1);\r
+                               System.out.printf("\n<<< Message from RD >>> \"%s\"\n\n", errmsg);\r
+                               Matcher mc = Pattern.compile("(予約時間が重複しています。|修正しました。)").matcher(errmsg);\r
+                               if ( ! mc.find() ) {\r
+                                       return(false);\r
+                               }\r
+                       }                       \r
+               }\r
+\r
+\r
+               \r
+               /*\r
+                * 予約情報の調整 \r
+                */\r
+               \r
+               // 予約ID番号を取得(キャッシュに存在しない番号が新番号)\r
+               {\r
+                       reportProgress("get identifier(6/7).");\r
+                       String[] d = reqGET("http://"+getIPAddr()+":"+getPortNo()+"/reserve/b_rsv.htm",null);\r
+                       header = d[0];\r
+                       response = d[1];\r
+               }\r
+               ma = Pattern.compile("/reserve/(\\d+?)/reserve.htm").matcher(response);\r
+               if (ma.find()) {\r
+                       idx = ma.group(1);\r
+                       reportProgress("get identifier(7/7).");\r
+                       String[] d = reqGET("http://"+getIPAddr()+":"+getPortNo()+"/reserve/"+idx+"/reserve.htm",null);\r
+                       header = d[0];\r
+                       response = d[1];\r
+               }\r
+               r.setId(getNewId(response));\r
+\r
+               // 予約パターンID\r
+               r.setRec_pattern_id(getRec_pattern_Id(r.getRec_pattern()));\r
+\r
+               // 次回予定日\r
+               r.setRec_nextdate(CommonUtils.getNextDate(r));\r
+               //r.setRec_nextdate(getNextDate(r.getRec_pattern(), r.getZhh()+":"+r.getZmm()));\r
+               \r
+               // 録画長\r
+               r.setRec_min(CommonUtils.getRecMin(r.getAhh(),r.getAmm(),r.getZhh(),r.getZmm()));\r
+               \r
+               // 開始日時・終了日時\r
+               getStartEndDateTime(r);\r
+               \r
+               // 予約リストを更新\r
+               getReserves().add(r);\r
+               \r
+               // キャッシュに保存\r
+               ReservesToFile(getReserves(), rsvedFile);\r
+               \r
+               System.out.printf("\n<<< Message from RD >>> \"%s\"\n\n", "正常に登録できました。");\r
+               return(true);\r
+       }\r
+\r
+       \r
+       /*\r
+        *      予約を更新する\r
+        */\r
+       @Override\r
+       public boolean UpdateRdEntry(ReserveList o, ReserveList r) {\r
+               //\r
+               if (cc.getCH_WEB2CODE(r.getCh_name()) == null) {\r
+                       errmsg = "【警告】Web番組表の放送局名「"+r.getCh_name()+"」をCHコードに変換できません。CHコード設定を修正してください。" ;\r
+                       System.out.println(errmsg);\r
+                       return(false);\r
+               }\r
+               \r
+               System.out.println("Run: UpdateRdEntry()");\r
+               \r
+               errmsg = "";\r
+\r
+               // おまじない\r
+               Authenticator.setDefault(new MyAuthenticator(getUser(), getPasswd()));\r
+               \r
+               // 更新準備\r
+               Matcher ma = null;\r
+               String idx = "";\r
+               int lineno = 0;\r
+               String header;\r
+               String response;\r
+               {\r
+                       reportProgress("get identifier(1/5).");\r
+                       String[] d = reqGET("http://"+getIPAddr()+":"+getPortNo()+"/reserve/b_rsv.htm",null);\r
+                       header = d[0];\r
+                       response = d[1];\r
+               }\r
+               ma = Pattern.compile("/reserve/(\\d+?)/reserve.htm").matcher(response);\r
+               if (ma.find()) {\r
+                       idx = ma.group(1);\r
+                       reportProgress("get identifier(2/5).");\r
+                       String[] d = reqGET("http://"+getIPAddr()+":"+getPortNo()+"/reserve/"+idx+"/reserve.htm",null);\r
+                       header = d[0];\r
+                       response = d[1];\r
+               }\r
+               ma = Pattern.compile("c1\\[(\\d+?)\\]=\""+r.getId()+"\";").matcher(response);\r
+               if ( ma.find() ) {\r
+                       lineno = Integer.valueOf(ma.group(1))+1;\r
+               }\r
+               \r
+               // RDに更新要請\r
+               {\r
+                       // ハング防止のおまじない\r
+                       reportProgress("get program(3/5).");\r
+                       reqGET("http://"+getIPAddr()+":"+getPortNo()+"/reserve/"+idx+"/b_rsvinfo.htm?"+r.getId()+"&"+lineno,null);\r
+                       \r
+                       reportProgress("get program(4/5).");\r
+                       reqGET("http://"+getIPAddr()+":"+getPortNo()+"/reserve/"+idx+"/rsvinfo.htm",null);\r
+               }\r
+\r
+               // POSTデータを変換する\r
+               Hashtable<String, String> pdat = modPostdata(r);\r
+               \r
+               // RDへの情報作成\r
+               String pstr = joinPoststr(pdat);\r
+\r
+               // RDへ情報送信\r
+               {               \r
+                       reportProgress("send request.(5/5)");\r
+                       String[] d = reqPOST("http://"+getIPAddr()+":"+getPortNo()+"/reserve/"+idx+"/entry.htm", pstr, null);\r
+                       header = d[0];\r
+                       response = d[1];\r
+               }\r
+\r
+               // 更新結果の確認\r
+               ma = Pattern.compile("alert\\(msg\\)").matcher(response);\r
+               if ( ma.find() ) {\r
+                       Matcher mb = Pattern.compile("\\bmsg=\"([\\s\\S]+?)\";").matcher(response);\r
+                       if (mb.find()) {\r
+                               errmsg = mb.group(1);\r
+                               System.out.printf("\n<<< Message from RD >>> \"%s\"\n\n", errmsg);\r
+                               Matcher mc = Pattern.compile("(予約時間が重複しています。|修正しました。)").matcher(errmsg);\r
+                               if ( ! mc.find() ) {\r
+                                       return(false);\r
+                               }\r
+                       }                       \r
+               }\r
+\r
+               System.out.printf("\n<<< Message from RD >>> \"%s\"\n\n", "正常に更新できました。");\r
+\r
+               /*\r
+                * 予約情報の調整 \r
+                */\r
+\r
+               // 予約パターンID\r
+               r.setRec_pattern_id(getRec_pattern_Id(r.getRec_pattern()));\r
+               \r
+               // 次回予定日\r
+               r.setRec_nextdate(CommonUtils.getNextDate(r));\r
+               //r.setRec_nextdate(getNextDate(r.getRec_pattern(), r.getZhh()+":"+r.getZmm()));\r
+               \r
+               // 録画長\r
+               r.setRec_min(CommonUtils.getRecMin(r.getAhh(),r.getAmm(),r.getZhh(),r.getZmm()));\r
+               \r
+               // 開始日時・終了日時\r
+               getStartEndDateTime(r);\r
+               \r
+               // 情報置き換え\r
+               getReserves().remove(o);\r
+               getReserves().add(r);\r
+\r
+               // キャッシュに保存\r
+               ReservesToFile(getReserves(), rsvedFile);\r
+               \r
+               return(true);\r
+       }\r
+\r
+       /*\r
+        *      予約を削除する\r
+        */\r
+       @Override\r
+       public ReserveList RemoveRdEntry(String delid) {\r
+               System.out.println("Run: RemoveRdEntry()");\r
+               \r
+               errmsg = "";\r
+\r
+               // おまじない\r
+               Authenticator.setDefault(new MyAuthenticator(getUser(), getPasswd()));\r
+\r
+               // 削除対象を探す\r
+               ReserveList rx = null;\r
+               for (  ReserveList reserve : getReserves() )  {\r
+                       if (reserve.getId().equals(delid)) {\r
+                               rx = reserve;\r
+                               break;\r
+                       }\r
+               }\r
+               if (rx == null) {\r
+                       return(null);\r
+               }\r
+\r
+               // 予約行番号を取得\r
+               Matcher ma = null;\r
+               String idx = "";\r
+               int lineno = 0;\r
+               String header;\r
+               String response;\r
+               {\r
+                       reportProgress("get identifier(1/5).");\r
+                       String[] d = reqGET("http://"+getIPAddr()+":"+getPortNo()+"/reserve/b_rsv.htm",null);\r
+                       header = d[0];\r
+                       response = d[1];\r
+               }\r
+               ma = Pattern.compile("/reserve/(\\d+?)/reserve.htm").matcher(response);\r
+               if (ma.find()) {\r
+                       idx = ma.group(1);\r
+                       reportProgress("get identifier(2/5).");\r
+                       String[] d = reqGET("http://"+getIPAddr()+":"+getPortNo()+"/reserve/"+idx+"/reserve.htm",null);\r
+                       header = d[0];\r
+                       response = d[1];\r
+               }\r
+               ma = Pattern.compile("c1\\[(\\d+?)\\]=\""+rx.getId()+"\";").matcher(response);\r
+               if ( ma.find() ) {\r
+                       lineno = Integer.valueOf(ma.group(1))+1;\r
+               }\r
+\r
+               // RDに削除要請\r
+               {\r
+                       // ハング防止のおまじない\r
+                       reportProgress("get program(3/5).");\r
+                       reqGET("http://"+getIPAddr()+":"+getPortNo()+"/reserve/"+idx+"/b_rsvinfo.htm?"+rx.getId()+"&"+lineno,null);\r
+                       \r
+                       reportProgress("get program(4/5).");\r
+                       reqGET("http://"+getIPAddr()+":"+getPortNo()+"/reserve/"+idx+"/rsvinfo.htm",null);\r
+               }\r
+               {               \r
+                       reportProgress("send request.(5/5)");\r
+                       String[] d = reqGET("http://"+getIPAddr()+":"+getPortNo()+"/reserve/"+idx+"/delete.htm", null);\r
+                       header = d[0];\r
+                       response = d[1];\r
+               }\r
+\r
+               // 削除結果の確認\r
+               ma = Pattern.compile("alert\\(msg\\)").matcher(response);\r
+               if ( ma.find() ) {\r
+                       Matcher mb = Pattern.compile("\\bmsg=\"([\\s\\S]+?)\";").matcher(response);\r
+                       if (mb.find()) {\r
+                               errmsg = mb.group(1);\r
+                               System.out.printf("\n<<< Message from RD >>> \"%s\"\n\n", errmsg);\r
+                       }\r
+                       return(null);\r
+               }\r
+               \r
+               // 予約リストを更新\r
+               getReserves().remove(rx);\r
+               \r
+               // キャッシュに保存\r
+               ReservesToFile(getReserves(), rsvedFile);\r
+               \r
+               System.out.printf("\n<<< Message from RD >>> \"%s\"\n\n", "正常に削除できました。");\r
+               return(rx);\r
+       }\r
+       \r
+       /*\r
+        * \r
+        */\r
+       @Override\r
+       public String getErrmsg() {\r
+               return(errmsg.replaceAll("\\\\r\\\\n", ""));\r
+       }\r
+\r
+       \r
+       \r
+       /* ここまで */\r
+       \r
+       \r
+       \r
+\r
+       \r
+       \r
+       /* 個別コード-ここから最後まで */\r
+\r
+       /*\r
+        * 非公開メソッド \r
+        */\r
+\r
+       private String joinPoststr(Hashtable<String, String> pdat)\r
+       {\r
+               String[] pkeys = {\r
+                       "bExec",\r
+                       "start_form",\r
+                       "title_name",\r
+                       "detail",\r
+                       "genre",\r
+                       "enc_type",\r
+                       "broadcast",\r
+                       "channel_list",\r
+                       "rec_priority",\r
+                       "maiyoubi_type",\r
+                       "date",\r
+                       "start_hour",\r
+                       "start_minute",\r
+                       "end_hour",\r
+                       "end_minute",\r
+                       "disc",\r
+                       "folder",\r
+                       "vrate",\r
+                       "amode",\r
+//                     "title_link",\r
+                       "auto_delete",\r
+//                     "video_es",\r
+//                     "audio_es",\r
+                       "dvdr",\r
+                       "lVoice",\r
+                       "edge_left",\r
+                       "bAutoChapter",\r
+                       "MagicChapter",\r
+                       "CM_Chapter",\r
+                       "channel_no",\r
+                       "dtv_sid",\r
+                       "dtv_nid",\r
+                       "net_link",\r
+                       "add_ch_text",\r
+                       "add_ch_value",\r
+                       "sport_ext_submit",\r
+                       "title_link_submit",\r
+                       "end_form"\r
+               };\r
+               \r
+               String pstr = "";\r
+               for ( String key : pkeys ) {\r
+                       //$pdat{"$key"} =~ s/(\W)/'%'.unpack("H2", $1)/ego;\r
+                       if (pdat.get(key) == null) {\r
+                               pstr += key+"=&";\r
+                       }\r
+                       else {\r
+                               pstr += key+"="+pdat.get(key)+"&";\r
+                       }\r
+               }\r
+               pstr = pstr.substring(0, pstr.length()-1);\r
+               \r
+               return(pstr);\r
+       }\r
+       \r
+       private Hashtable<String, String> modPostdata(ReserveList r) {\r
+\r
+               Hashtable<String, String> newdat = new Hashtable<String, String>();\r
+               \r
+               try {\r
+                       \r
+                       // 実行するよ\r
+                       newdat.put("bExec", (r.getExec())?("ON"):("OFF"));\r
+                       \r
+                       if (getDebug()) System.err.println("[DEBUG] Exec="+newdat.get("bExec"));\r
+                       \r
+       \r
+                       // 予約名・予約詳細\r
+                       if ( r.getAutocomplete() ) {\r
+                               newdat.put("title_name", "");\r
+                               newdat.put("detail", "");\r
+                       }\r
+                       else {\r
+                               try {\r
+                                       newdat.put("title_name", URLEncoder.encode(r.getTitle(),thisEncoding));\r
+                               } catch (UnsupportedEncodingException e1) {\r
+                                       // TODO Auto-generated catch block\r
+                                       e1.printStackTrace();\r
+                               }\r
+               \r
+                               try {\r
+                                       newdat.put("detail", URLEncoder.encode(r.getDetail().replaceAll("\n", Matcher.quoteReplacement("\r\n")), thisEncoding));\r
+                               } catch (UnsupportedEncodingException e1) {\r
+                                       // TODO Auto-generated catch block\r
+                                       e1.printStackTrace();\r
+                               }\r
+                       }\r
+                       \r
+                       if (getDebug()) System.err.println("[DEBUG] title_name="+newdat.get("title_name"));\r
+                       \r
+                       // 保存先\r
+                       try {\r
+                               newdat.put("folder", URLEncoder.encode(text2value(folder, r.getRec_folder()),thisEncoding));\r
+                       } catch (UnsupportedEncodingException e1) {\r
+                               // TODO Auto-generated catch block\r
+                               e1.printStackTrace();\r
+                       }\r
+                       \r
+                       if (getDebug()) System.err.println("[DEBUG] folder="+newdat.get("folder"));\r
+                       \r
+                       // ジャンル\r
+                       newdat.put("genre", _recstr2genre(r.getRec_genre()));\r
+                       \r
+                       if (getDebug()) System.err.println("[DEBUG] genre="+newdat.get("genre"));\r
+                       \r
+                       // DVD互換モード\r
+                       newdat.put("dvdr", text2value(dvdcompat, r.getRec_dvdcompat()));\r
+                       \r
+                       if (getDebug()) System.err.println("[DEBUG] dvdr="+newdat.get("dvdr"));\r
+                       \r
+                       // 録画チャンネル\r
+                       String channel = cc.getCH_WEB2CODE(r.getCh_name());\r
+                       String ch_no = cc.getCH_CODE2REC(channel);\r
+                       if (chtype.size() > 0) {\r
+                               String typ = text2value(chtype, channel);\r
+                               if (typ.equals("uva")) {\r
+                                       newdat.put("broadcast","0");            // 地上アナログ\r
+                               }\r
+                               else if (typ.equals("bsa")) {\r
+                                       newdat.put("broadcast","1");            // BSアナログ\r
+                               }\r
+                               else if (typ.equals("l1")) {\r
+                                       newdat.put("broadcast","2");            // 外部入力(L1)\r
+                               }\r
+                               else if (typ.equals("l2")) {\r
+                                       newdat.put("broadcast","3");            // 外部入力(L2)\r
+                               }\r
+                               else if (typ.equals("l3")) {\r
+                                       newdat.put("broadcast","4");            // 外部入力(L3)\r
+                               }\r
+                               else if (typ.equals("bsd")) {\r
+                                       newdat.put("broadcast","10");           // BSデジタル\r
+                               }\r
+                               else if (typ.equals("csd")) {\r
+                                       newdat.put("broadcast","11");           // 110度CSデジタル\r
+                               }\r
+                               else if (typ.equals("uvd")) {\r
+                                       newdat.put("broadcast","12");           // 地上デジタル\r
+                               }\r
+                               else {\r
+                                       // 普通ここには落ちない\r
+                                       if (ch_no.startsWith("C")) {\r
+                                               newdat.put("broadcast","2");            // "C***"は外部入力(L1)\r
+                                       }\r
+                                       else if (ch_no.startsWith("SP")) {\r
+                                               newdat.put("broadcast","4");            // "SP***"は外部入力(L3)\r
+                                       }\r
+                                       else {\r
+                                               newdat.put("broadcast","3");            // 未定義は全部外部入力(L2)\r
+                                       }\r
+                               }\r
+                               \r
+                               newdat.put("channel_list", channel);\r
+                               newdat.put("channel_no", channel);\r
+                       }\r
+                       else {\r
+                               // 後方互換\r
+                               Matcher ma = Pattern.compile("^(.)(.)").matcher(ch_no);\r
+                               if (ma.find()) {\r
+                                       if ((ma.group(1)+ma.group(2)).equals("CH")) {\r
+                                               newdat.put("broadcast","0");            // 地上アナログ\r
+                                       }\r
+                                       else if ((ma.group(1)+ma.group(2)).equals("BS")) {\r
+                                               newdat.put("broadcast","10");           // BSデジタル\r
+                                       }\r
+                                       else if ((ma.group(1)+ma.group(2)).equals("CS")) {\r
+                                               newdat.put("broadcast","11");           // 110度CSデジタル\r
+                                       }\r
+                                       else if (ma.group(1).equals("L")) {\r
+                                               newdat.put("broadcast", String.valueOf(Integer.valueOf(ma.group(2))+1));        // ライン入力ABC\r
+                                       }\r
+                                       else {\r
+                                               newdat.put("broadcast","12");           // 地上デジタル\r
+                                       }\r
+                                       \r
+                                       newdat.put("channel_list", channel);\r
+                                       newdat.put("channel_no", channel);\r
+                               }\r
+                       }\r
+                       \r
+                       if (getDebug()) System.err.println("[DEBUG] channel_no="+newdat.get("channel_no"));\r
+                       \r
+                       // 録画レート\r
+                       newdat.put("enc_type", text2value(encoder, r.getTuner()));\r
+                       newdat.put("vrate", text2value(vrate, r.getRec_mode()));\r
+                       \r
+                       if (getDebug()) System.err.println("[DEBUG] vrate="+newdat.get("vrate"));\r
+                       \r
+                       // 録音レートの指定がねえな…\r
+                       newdat.put("amode", text2value(arate, r.getRec_audio()));\r
+                       \r
+                       if (getDebug()) System.err.println("[DEBUG] arate="+newdat.get("arate"));\r
+                       \r
+                       // 繰り返し\r
+                       Matcher ma = Pattern.compile("^\\d").matcher(r.getRec_pattern());\r
+                       if (ma.find()) { \r
+                               newdat.put("maiyoubi_type","0");\r
+                               newdat.put("date", r.getRec_pattern());\r
+                       }\r
+                       else {\r
+                               newdat.put("maiyoubi_type", "1");\r
+                               int i = 1;\r
+                               for ( String s : RPTPTN ) {\r
+                                       if ( s.equals(r.getRec_pattern()) == true ) {\r
+                                               newdat.put("date", String.valueOf(i));\r
+                                       }\r
+                                       i++;\r
+                               }\r
+                       }\r
+       \r
+                       if (getDebug()) System.err.println("[DEBUG] date="+newdat.get("date"));\r
+\r
+                       newdat.put("start_hour", r.getAhh());\r
+                       newdat.put("start_minute", r.getAmm());\r
+                       newdat.put("end_hour", r.getZhh());\r
+                       newdat.put("end_minute", r.getZmm());\r
+       \r
+                       // 記録先\r
+                       newdat.put("disc", text2value(device, r.getRec_device()));\r
+       \r
+                       // 自動チャプター関連\r
+                       newdat.put("bAutoChapter"               , text2value(xchapter, r.getRec_xchapter()));\r
+                       newdat.put("MagicChapter"               , text2value(mschapter, r.getRec_mschapter()));\r
+                       newdat.put("CM_Chapter"                 , text2value(mvchapter, r.getRec_mvchapter()));\r
+                       \r
+                       // 追加\r
+                       newdat.put("start_form"                 , "");\r
+                       newdat.put("rec_priority"               , "150");\r
+                       newdat.put("title_link"                 , "0");\r
+                       newdat.put("auto_delete"                , "0");\r
+                       newdat.put("video_es"                   , "00");\r
+                       newdat.put("audio_es"                   , "10");\r
+                       newdat.put("edge_left"                  , "0");         // 録画のりしろ\r
+                       //newdat.put("bAutoChapter"             , "0");\r
+                       //newdat.put("MagicChapter"             , "0");\r
+                       //newdat.put("CM_Chapter"                       , "0");\r
+                       newdat.put("add_ch_text"                , "");\r
+                       newdat.put("add_ch_value"               , "");\r
+                       newdat.put("sport_ext_submit"   , "undefined");\r
+                       newdat.put("title_link_submit"  , "undefined");\r
+                       newdat.put("end_form"                   , "0");\r
+               }\r
+               catch ( Exception e ) {\r
+                       e.printStackTrace();\r
+               }\r
+               \r
+               return(newdat);\r
+       }\r
+\r
+       private String _recstr2genre(String genre)\r
+       {\r
+               if (genre.equals(TVProgram.ProgGenre.MOVIE.toString())) {\r
+                       return "0";\r
+               }\r
+               else if (genre.equals(TVProgram.ProgGenre.MUSIC.toString())) {\r
+                       return "1";\r
+               }\r
+               else if (genre.equals(TVProgram.ProgGenre.DORAMA.toString())) {\r
+                       return "2";\r
+               }\r
+               else if (genre.equals(TVProgram.ProgGenre.ANIME.toString())) {\r
+                       return "3";\r
+               }\r
+               else if (genre.equals(TVProgram.ProgGenre.SPORTS.toString())) {\r
+                       return "4";\r
+               }\r
+               else if (genre.equals(TVProgram.ProgGenre.DOCUMENTARY.toString())) {\r
+                       return "5";\r
+               }\r
+               else if (genre.equals(TVProgram.ProgGenre.THEATER.toString())) {\r
+                       return "6";\r
+               }\r
+               else if (genre.equals(TVProgram.ProgGenre.HOBBY.toString())) {\r
+                       return "7";\r
+               }\r
+               else if (genre.equals(TVProgram.ProgGenre.VARIETY.toString())) {\r
+                       return "8";\r
+               }\r
+               else {\r
+                       return "10";\r
+               }\r
+       }\r
+       \r
+       private void setSettingEtc(ArrayList<TextValueSet> tvs, String key, int typ, String res) {\r
+               tvs.clear();\r
+               String valExpr = "(\\d+),?";\r
+               if (typ == 1) {\r
+                       valExpr = "\"(.+?)\",?";\r
+               }\r
+               Matcher mc = Pattern.compile("var "+key+"_text\\s*= new Array\\(([\\s\\S]+?)\\);").matcher(res);\r
+               Matcher md = Pattern.compile("var "+key+"_value\\s*= new Array\\(([\\s\\S]+?)\\);").matcher(res);\r
+               if (mc.find() && md.find()) {\r
+                       Matcher me = Pattern.compile("\"(.+?)\",?").matcher(mc.group(1));\r
+                       Matcher mf = Pattern.compile(valExpr).matcher(md.group(1));\r
+                       while (me.find() && mf.find()) {\r
+                               TextValueSet t = new TextValueSet();\r
+                               if (key.equals("vrate")) {\r
+                                       t.setText(me.group(1).replaceAll(" ", ""));\r
+                               }\r
+                               else {\r
+                                       t.setText(me.group(1));\r
+                               }\r
+                               //t.setText(me.group(1));\r
+                               t.setValue(mf.group(1));\r
+                               tvs.add(t);\r
+                       }\r
+               }\r
+       }\r
+}\r
diff --git a/TinyBannavi/src/tainavi/PlugIn_RecRD_TVTest.java b/TinyBannavi/src/tainavi/PlugIn_RecRD_TVTest.java
new file mode 100644 (file)
index 0000000..090ac8d
--- /dev/null
@@ -0,0 +1,295 @@
+package tainavi;\r
+\r
+import java.io.File;\r
+import java.io.IOException;\r
+import java.util.ArrayList;\r
+import java.util.regex.Matcher;\r
+import java.util.regex.Pattern;\r
+\r
+/**\r
+ * TVTestで視聴するプラグイン\r
+ */\r
+public class PlugIn_RecRD_TVTest extends HDDRecorderUtils implements HDDRecorder,Cloneable {\r
+\r
+       public PlugIn_RecRD_TVTest clone() {\r
+               return (PlugIn_RecRD_TVTest) super.clone();\r
+       }\r
+       \r
+       /* 必須コード  - ここから */\r
+       \r
+       private static final String thisEncoding = "MS932";\r
+\r
+       // 種族の特性\r
+       public String getRecorderId() { return "TVTest"; }\r
+       public RecType getType() { return RecType.TUNER; }\r
+       \r
+       @Override\r
+       public String getChDatHelp() { return\r
+                       "TVTestプラグインの設定が正しければ、「レコーダの放送局名」は設定すべき値がプルダウンで選択できるはずです。\n"+\r
+                       "★TVTestプラグインはTVの視聴のみ可能です(右クリックメニューの「視聴する」コマンド)。";\r
+       }\r
+       \r
+       // chvalueをつかってもいーよ\r
+       @Override\r
+       public boolean isChValueAvailable() { return true; }\r
+       // CHコードは入力しなくていい\r
+       @Override\r
+       public boolean isChCodeNeeded() { return false; }\r
+\r
+       // 個体の特性\r
+       private ChannelCode cc = new ChannelCode(getRecorderId());\r
+       private String errmsg = "";\r
+       //private HashMap<String,String> ccMap;\r
+\r
+       private final String MSGID = "["+getRecorderId()+"] ";\r
+       private final String ERRID = "[ERROR]"+MSGID;\r
+       private final String DBGID = "[DEBUG]"+MSGID;\r
+\r
+       // コンストラクタ\r
+       \r
+       public PlugIn_RecRD_TVTest() {\r
+               super();\r
+               this.setUser("D:\\PT2\\TVTest");\r
+       }\r
+       \r
+       // 公開メソッド\r
+\r
+       /*\r
+        * \r
+        */\r
+       public ChannelCode getChCode() {\r
+               return cc;\r
+       }\r
+\r
+       /*\r
+        * チャンネルリモコン機能\r
+        */\r
+       public boolean ChangeChannel(String webChName) {\r
+               \r
+               if (webChName == null) {\r
+                       return true;\r
+               }\r
+               \r
+               final String recChName = cc.getCH_WEB2REC(webChName);\r
+               final String sysDir = getUser();\r
+               final String exeFile = sysDir+File.separator+"TVTest.exe";\r
+               \r
+               String str = text2value(chtype,recChName);\r
+               if (str == null || str.length() == 0) {\r
+                       String msg = ERRID+"指定のチャンネルをCHコードに変換できません:"+webChName; \r
+                       setErrmsg(msg);\r
+                       return false;\r
+               }\r
+               \r
+               String[] da = str.split(",");\r
+               String[] dd = da[1].split(":");\r
+               \r
+               try {\r
+                       ProcessBuilder pb = new ProcessBuilder(new String[] {exeFile, "/s", "/d", da[0], "/nid", dd[0], "/tsid", dd[1], "/sid", dd[2]});\r
+                       pb.start();\r
+                       return true;\r
+               }\r
+               catch (IOException e) {\r
+                       e.printStackTrace();\r
+               }\r
+               \r
+               String msg = ERRID+"指定のチャンネルが見つかりません:"+recChName; \r
+               setErrmsg(msg);\r
+               return false;\r
+       }\r
+\r
+       /*\r
+        * レコーダーから予約一覧を取得する\r
+        */\r
+       public boolean GetRdReserve(boolean force) {\r
+               \r
+               final String chTypeTFile        = String.format("%s%srs_%s.%s.xml", "env", File.separator, getRecorderId(), "chType");\r
+               final String chValueTFile       = String.format("%s%srs_%s.%s.xml", "env", File.separator, getRecorderId(), "chValue");\r
+               \r
+               ArrayList<TextValueSet> cht = null;\r
+               ArrayList<TextValueSet> chv = null;\r
+               if ( new File(chTypeTFile).exists() && new File(chValueTFile).exists() ) {\r
+                       cht = TVSload(chTypeTFile);\r
+                       if ( cht != null ) {\r
+                               chv = TVSload(chValueTFile);\r
+                       }\r
+               }\r
+               \r
+               boolean b = _GetRdReserve();\r
+               if ( b ) {\r
+                       // 通常はからなずTVTestの設定を取得しなおすが\r
+                       TVSsave(chtype, chTypeTFile);\r
+                       TVSsave(chvalue, chValueTFile);\r
+               }\r
+               else if ( cht != null && chv != null ) {\r
+                       // とれなかったらキャッシュを使う\r
+                       chtype = cht;\r
+                       chvalue = chv;\r
+               }\r
+\r
+               if (getDebug()) System.out.println(DBGID+"有効な放送局の数: "+chtype.size()); \r
+                       \r
+               return b;\r
+       }\r
+       \r
+       public boolean _GetRdReserve() {\r
+               \r
+               setErrmsg(MSGID+"予約一覧取得は無効です。");\r
+               \r
+               final String sysDir = getUser();\r
+               final String iniFile = sysDir+File.separator+"TVTest.ini";\r
+               \r
+               File d = new File(sysDir);\r
+               if ( ! d.exists() || ! d.isDirectory()) {\r
+                       String msg = ERRID+"指定のディレクトリがみつからないか、あるいはディレクトリではありません:"+sysDir; \r
+                       setErrmsg(msg);\r
+                       //System.out.println(msg);\r
+                       return(false);\r
+               }\r
+               \r
+               String DriverDirectory = null;\r
+               ArrayList<String> Drivers = new ArrayList<String>();\r
+               String buf = CommonUtils.read4file(iniFile, false, "Unicode");\r
+               if ( buf == null ) {\r
+                       String msg = ERRID+"設定ファイルが取得できませんでした:"+iniFile; \r
+                       setErrmsg(msg);\r
+                       //System.out.println(msg);\r
+                       return false;\r
+               }\r
+               \r
+               String[] sa = buf.split("[\r\n]+");\r
+               for ( String str : sa ) {\r
+                       Matcher ma = Pattern.compile("^DriverDirectory=(.+?)\\s*$",Pattern.DOTALL).matcher(str);\r
+                       if ( ma.find() ) {\r
+                               DriverDirectory = ma.group(1);\r
+                               if (getDebug()) System.out.println(DBGID+"ドライバーディレクトリの取得: "+ma.group(1)); \r
+                               continue;\r
+                       }\r
+                       ma = Pattern.compile("^Driver=(.+?)\\.dll").matcher(str);\r
+                       if ( ma.find() ) {\r
+                               Drivers.add(ma.group(1));\r
+                               if (getDebug()) System.out.println(DBGID+"ドライバーの追加: "+ma.group(1)); \r
+                               continue;\r
+                       }\r
+                       ma = Pattern.compile("^Driver\\d+_FileName=(.+?)\\.dll").matcher(str);\r
+                       if ( ma.find() ) {\r
+                               Drivers.add(ma.group(1));\r
+                               if (getDebug()) System.out.println(DBGID+"ドライバーの追加: "+ma.group(1)); \r
+                               continue;\r
+                       }\r
+               }\r
+               \r
+               for ( String Driver : Drivers ) {\r
+                       String ch2File = null;\r
+                       if ( Driver.contains(File.separator) ) {\r
+                               ch2File = Driver+".ch2";\r
+                       }\r
+                       else {\r
+                               ch2File = DriverDirectory+File.separator+Driver+".ch2";\r
+                       }\r
+                       File f = new File(ch2File);\r
+                       if ( ! f.exists() || ! f.isFile()) {\r
+                               String msg = ERRID+"チャンネル設定ファイルがみつかりません:"+ch2File; \r
+                               setErrmsg(msg);\r
+                               //System.out.println(msg);\r
+                               continue;\r
+                       }\r
+                       \r
+                       buf = CommonUtils.read4file(ch2File, false, thisEncoding);\r
+                       if ( buf == null ) {\r
+                               String msg = ERRID+"チャンネル設定ファイルが取得できませんでした:"+ch2File; \r
+                               setErrmsg(msg);\r
+                               //System.out.println(msg);\r
+                               return false;\r
+                       }\r
+                       \r
+                       sa = buf.split("[\r\n]+");\r
+                       for ( String str : sa ) {\r
+                               if ( str.startsWith(";") ) {\r
+                                       continue;\r
+                               }\r
+                               \r
+                               String[] da = str.split(",");\r
+                               if (da.length < 4) {\r
+                                       continue;\r
+                               }\r
+                               \r
+                               if ( da[7].equals("17168") || da[7].equals("17169") ) {\r
+                                       da[0] = "臨)"+da[0];\r
+                               }\r
+                               \r
+                               String val = text2value(chvalue, da[0]);\r
+                               if ( val == null || val.length() == 0 ) {\r
+                                       TextValueSet tn;\r
+\r
+                                       String chid;\r
+                                       try {\r
+                                               chid = ContentIdEDCB.getChId(Integer.valueOf(da[6]), Integer.valueOf(da[7]), Integer.valueOf(da[5]));\r
+                                       }\r
+                                       catch (NumberFormatException e) {\r
+                                               String msg = ERRID+"iniファイルの内容が不正です:"+ch2File; \r
+                                               setErrmsg(msg);\r
+                                               //System.out.println(msg);\r
+                                               return false;\r
+                                       }\r
+                                       tn = new TextValueSet();\r
+                                       tn.setText(da[0]);\r
+                                       tn.setValue(String.valueOf(Long.decode("0x"+chid)));\r
+                                       chvalue.add(tn);\r
+                                       \r
+                                       tn = new TextValueSet();\r
+                                       tn.setText(da[0]);\r
+                                       tn.setValue(Driver+".dll,"+da[6]+":"+da[7]+":"+da[5]);\r
+                                       chtype.add(tn);\r
+                                       \r
+                                       if (getDebug()) System.out.println(DBGID+"設定の追加: "+tn.getText()+"="+tn.getValue()); \r
+                               }\r
+                       }\r
+               }\r
+\r
+               return (true);\r
+       }\r
+\r
+       /*\r
+        * 予約を実行する\r
+        */\r
+       public boolean PostRdEntry(ReserveList r) {\r
+               setErrmsg(MSGID+"予約処理は無効です。");\r
+               return (false);\r
+       }\r
+\r
+       /*\r
+        * 予約を更新する\r
+        */\r
+       public boolean UpdateRdEntry(ReserveList o, ReserveList r) {\r
+               setErrmsg(MSGID+"更新処理は無効です。");\r
+               return (false);\r
+       }\r
+\r
+       /*\r
+        * 予約を削除する\r
+        */\r
+       public ReserveList RemoveRdEntry(String delno) {\r
+               setErrmsg(MSGID+"削除処理は無効です。");\r
+               return (null);\r
+       }\r
+\r
+       /*\r
+        * 電源ON・OFF\r
+        */\r
+       public void wakeup() {\r
+       }\r
+       public void shutdown() {\r
+       }\r
+\r
+       /*\r
+        * \r
+        */\r
+       public String getErrmsg() {\r
+               return (errmsg);\r
+       }\r
+\r
+       protected void setErrmsg(String s) {\r
+               errmsg = s;\r
+       }\r
+}\r
diff --git a/TinyBannavi/src/tainavi/PlugIn_RecRD_TvRock.java b/TinyBannavi/src/tainavi/PlugIn_RecRD_TvRock.java
new file mode 100644 (file)
index 0000000..09c4915
--- /dev/null
@@ -0,0 +1,1911 @@
+package tainavi;\r
+\r
+import java.io.File;\r
+import java.io.UnsupportedEncodingException;\r
+import java.net.Authenticator;\r
+import java.net.URLEncoder;\r
+import java.util.ArrayList;\r
+import java.util.Calendar;\r
+import java.util.GregorianCalendar;\r
+import java.util.HashMap;\r
+import java.util.regex.Matcher;\r
+import java.util.regex.Pattern;\r
+\r
+\r
+/**\r
+ * \r
+ */\r
+public class PlugIn_RecRD_TvRock extends HDDRecorderUtils implements HDDRecorder,Cloneable {\r
+\r
+       @Override\r
+       public PlugIn_RecRD_TvRock clone() {\r
+               return (PlugIn_RecRD_TvRock) super.clone();\r
+       }\r
+       \r
+       private static final String thisEncoding = "MS932";\r
+\r
+       /*******************************************************************************\r
+        * 種族の特性\r
+        ******************************************************************************/\r
+\r
+       @Override\r
+       public String getRecorderId() { return "TvRock"; }\r
+       @Override\r
+       public RecType getType() { return RecType.RECORDER; }\r
+       \r
+       // 番組追従が可能\r
+       @Override\r
+       public boolean isPursuesEditable() { return true; }\r
+       // タイトル自動補完はできない\r
+       @Override\r
+       public boolean isAutocompleteSupported() { return false; }\r
+       // chvalueをつかってもいーよ\r
+       @Override\r
+       public boolean isChValueAvailable() { return true; }\r
+       // CHコードは入力しなくていい\r
+       @Override\r
+       public boolean isChCodeNeeded() { return false; }\r
+       // 放送は種別は選択してほしい\r
+       @Override\r
+       public boolean isBroadcastTypeNeeded() { return true; }\r
+\r
+       /*******************************************************************************\r
+        * 予約ダイアログなどのテキストのオーバーライド\r
+        ******************************************************************************/\r
+\r
+       @Override\r
+       public String getLabel_Audiorate() { return "予約方法"; }\r
+       @Override\r
+       public String getLabel_XChapter() { return "待機時間(秒前)"; }\r
+       @Override\r
+       public String getLabel_MsChapter() { return "録画開始(秒前)"; }\r
+       @Override\r
+       public String getLabel_MvChapter() { return "録画終了(秒後)"; }\r
+       @Override\r
+       public String getLabel_DVDCompat() { return "録画終了後"; }\r
+       @Override\r
+       public String getLabel_Aspect() { return "終了後コマンド"; }\r
+       @Override\r
+       public String getLabel_LVoice() { return "コンピュータ名"; }\r
+       \r
+       @Override\r
+       public String getChDatHelp() { return\r
+                       "「レコーダの放送局名」は、予約一覧取得が正常に完了していれば設定候補がコンボボックスで選択できるようになります。"+\r
+                       "「放送波の種別」は、チューナーの自動選択に必要なので極力設定してください(詳細はWiki参照)";\r
+       }\r
+\r
+       /*******************************************************************************\r
+        * 定数\r
+        ******************************************************************************/\r
+       \r
+       private static final String ITEM_REC_TYPE_EPG           = "EPG";\r
+       private static final String ITEM_REC_TYPE_PROG          = "プログラム";\r
+       \r
+       private static final String VALUE_REC_TYPE_EPG          = "EPG";\r
+       private static final String VALUE_REC_TYPE_PROG         = "PROGRAM";\r
+\r
+       private static final String ITEM_CH_EPGGET              = "[番組情報取得スケジュール]";\r
+       \r
+       private static final String VALUE_CH_EPGGET             = "";\r
+\r
+       // ログ関連\r
+       \r
+       private final String MSGID = "["+getRecorderId()+"] ";\r
+       private final String ERRID = "[ERROR]"+MSGID;\r
+       private final String DBGID = "[DEBUG]"+MSGID;\r
+\r
+       // 定数ではない\r
+       private int retryMax = 3;\r
+\r
+       /*******************************************************************************\r
+        * CHコード設定、エラーメッセージ\r
+        ******************************************************************************/\r
+       \r
+       @Override\r
+       public ChannelCode getChCode() {\r
+               return cc;\r
+       }\r
+\r
+       private final ChannelCode cc = new ChannelCode(getRecorderId());\r
+\r
+       @Override\r
+       public String getErrmsg() {\r
+               return errmsg;\r
+       }\r
+\r
+       private String errmsg = "";\r
+       \r
+       /*******************************************************************************\r
+        * フリーオプション関係\r
+        ******************************************************************************/\r
+       /*\r
+       private static final String OPTKEY_DIR = "libdir";\r
+       private static final String OPTVAL_DIR_DEFAULT = "D:/PT2/TVRockLib/";\r
+       \r
+       private OptMap opts = null;\r
+       \r
+       private class OptMap extends MapCtrl {\r
+               @Override\r
+               protected boolean chkOptString() {\r
+                       // ディレクトリ設定があるかしら\r
+                       {\r
+                               String datRoot = get(OPTKEY_DIR);\r
+                               if ( datRoot == null ) {\r
+                                       // なければデフォルト\r
+                                       put(OPTKEY_DIR, OPTVAL_DIR_DEFAULT);\r
+                                       datRoot = get(OPTKEY_DIR);\r
+                                       System.out.println("[TvRock未使用の方は無視してください] TVRockLibフォルダは設定値になります: "+datRoot);\r
+                               }\r
+                               else {\r
+                                       System.out.println("[TvRock未使用の方は無視してください] TVRockLibフォルダはデフォルト値になります: "+datRoot);\r
+                               }\r
+                       }\r
+                       return true;\r
+               }\r
+       }\r
+       \r
+       @Override\r
+       public boolean setOptString(String s) {\r
+               return opts.setOptString(s);\r
+       }\r
+       \r
+       @Override\r
+       public String getOptString() {\r
+               return opts.toString();\r
+       }\r
+       */\r
+       \r
+       /*******************************************************************************\r
+        * 部品\r
+        ******************************************************************************/\r
+       \r
+       private String rsvedFile = "";\r
+       \r
+       private ArrayList<TextValueSet> recDefaults = new ArrayList<TextValueSet>();\r
+       private final String[] recDefaulltKeys = {"idle","ready","tale","watchonly","reconly","cname","extmd"};\r
+       \r
+       /*******************************************************************************\r
+        * コンストラクタ\r
+        ******************************************************************************/\r
+       \r
+       public PlugIn_RecRD_TvRock() {\r
+               setIPAddr("127.0.0.1(サンプル)");\r
+               setPortNo("8969(サンプル)");\r
+               setUser("nobody(サンプル)");\r
+               setPasswd("********");\r
+       }\r
+       \r
+       /*******************************************************************************\r
+        * チャンネルリモコン機能\r
+        ******************************************************************************/\r
+       \r
+       @Override\r
+       public boolean ChangeChannel(String webChName) {\r
+               \r
+               if (webChName == null) {\r
+                       return true;    // 有効だよと返す\r
+               }\r
+               \r
+               errmsg = "";\r
+               \r
+               String chCode = cc.getCH_WEB2CODE(webChName);\r
+               if ( chCode == null || chCode.length() == 0 ) {\r
+                       errmsg = ERRID+"Web番組表の放送局名CHコードに変換できない: "+webChName;\r
+                       return false;\r
+               }\r
+               \r
+               String recChName = cc.getCH_CODE2REC(chCode);\r
+               if ( recChName == null ) {\r
+                       errmsg = ERRID+"CHコードをレコーダの放送局名に変換できない: "+webChName+"("+chCode+")";\r
+                       return false;\r
+               }\r
+\r
+               String hdr = null;\r
+               if ( chCode.startsWith(BroadcastType.TERRA.getName()+":") ) {\r
+                       hdr = "地上";\r
+               }\r
+               else if ( chCode.startsWith(BroadcastType.BS.getName()+":") || chCode.startsWith(BroadcastType.CS.getName()+":") ) {\r
+                       hdr = "BS";\r
+               }\r
+               if ( hdr == null ) {\r
+                       errmsg = ERRID+"放送波の種別の指定がないか、不正: "+chCode;\r
+                       return false;\r
+               }\r
+\r
+               ArrayList<TextValueSet> tuners = new ArrayList<TextValueSet>();\r
+               for ( TextValueSet tun : encoder ) {\r
+                       if ( tun.getText().startsWith(hdr) ) {\r
+                               tuners.add(tun);        // 特定放送波のチューナーだけ\r
+                       }\r
+               }\r
+               if ( tuners.size() == 0 ) {\r
+                       tuners = encoder;       // 全部なめてみる\r
+               }\r
+\r
+               // おまじない\r
+               Authenticator.setDefault(new MyAuthenticator(getUser(), getPasswd()));\r
+\r
+               String selectedtuner = null;\r
+               String chid = null;\r
+               boolean selected = false;\r
+               boolean sleeping = false;\r
+               for ( TextValueSet tuner : tuners ) {\r
+                       \r
+                       // チューナーが操作対象か、起動中かどうか調べる\r
+                       String uri = "http://"+getIPAddr()+":"+getPortNo()+"/"+getUser()+"/cnt?d="+tuner.getValue();\r
+                       String[] d = null;\r
+                       for ( int l=2; l>0; l-- ) {\r
+                               if (getDebug()) System.out.println(DBGID+"tuner select "+uri);\r
+                               d = reqGET(uri, null);\r
+                               if ( d[0] == null || d[1] == null ) {\r
+                                       if (getDebug()) System.out.println(DBGID+"Retrying...");\r
+                               }\r
+                       }\r
+                       if ( d == null || d[0] == null || d[1] == null ) {\r
+                               errmsg = ERRID+"TvRockへのアクセスが失敗: "+uri;\r
+                               return false;\r
+                       }\r
+                       \r
+                       sleeping = ! d[1].matches("^[\\s\\S]*<td align=center bgcolor=#fed8c6><small><font color=#......>"+tuner.getText()+"</font></small></td>[\\s\\S]*$");\r
+                       boolean recording = d[1].matches("^[\\s\\S]*bgcolor=#c88084 width=\\d+%><small><font color=#......><b>録画</b>[\\s\\S]*$");\r
+                       \r
+                       // 放送局が選択可能が調べる\r
+                       Matcher ma = Pattern.compile("<tr><td align=center width=3% bgcolor=#......>(.*?)</tr>",Pattern.DOTALL).matcher(d[1]);\r
+                       while ( ma.find() ) {\r
+                               Matcher mb = Pattern.compile("&q=(.+?)\"",Pattern.DOTALL).matcher(ma.group(1));\r
+                               if ( mb.find() ) {\r
+                                       String chname = CommonUtils.unEscape(mb.group(1));\r
+                                       if ( recChName.equals(chname) ) {\r
+                                               mb = Pattern.compile("cnt\\?z=(\\d+)",Pattern.DOTALL).matcher(ma.group(1));\r
+                                               if ( mb.find() ) {\r
+                                                       chid = mb.group(1);\r
+                                               }\r
+                                               else {\r
+                                                       selected = true;\r
+                                               }\r
+                                               break;\r
+                                       }\r
+                               }\r
+                       }\r
+                       if ( ! selected && chid == null ) {\r
+                               errmsg = ERRID+"放送局が見つからない: "+webChName+"("+recChName+","+chCode+")";\r
+                               continue;\r
+                       }\r
+                       if ( recording ) {\r
+                               errmsg = MSGID+"録画中のためチャンネル変更できません: "+tuner.getText()+" "+webChName+"("+recChName+","+chCode+") @ "+tuner.getText();\r
+                               return false;\r
+                       }\r
+                       if ( selected ) {\r
+                               errmsg = MSGID+"すでに選択済みです: "+webChName+"("+recChName+","+chCode+") @ "+tuner.getText();\r
+                               break;\r
+                       }\r
+                       if ( chid != null ) {\r
+                               errmsg = "";\r
+                               selectedtuner = tuner.getText();\r
+                               break;  // みつかった\r
+                       }\r
+               }\r
+               if ( ! selected && chid == null ) {\r
+                       //errmsg = ERRID+"放送局が見つからない: "+webChName;\r
+                       return false;\r
+               }\r
+               \r
+               // 寝てたら起こす\r
+               if ( sleeping ) {\r
+                       String uri = "http://"+getIPAddr()+":"+getPortNo()+"/"+getUser()+"/cnt?bt=1";\r
+                       if (getDebug()) System.out.println(DBGID+"tuner wakeup "+uri);\r
+                       String[] d = reqGET(uri, null);\r
+                       if ( d[0] == null || d[1] == null ) {\r
+                               errmsg = ERRID+"TvRockへのアクセスが失敗: "+uri;\r
+                               return false;\r
+                       }\r
+                       CommonUtils.milSleep(3000);\r
+               }\r
+               \r
+               // 放送局を選択する\r
+               if ( ! selected ) {\r
+                       String uri = "http://"+getIPAddr()+":"+getPortNo()+"/"+getUser()+"/cnt?z="+chid;\r
+                       if (getDebug()) System.out.println(DBGID+"center select "+uri);\r
+                       String[] d = reqGET(uri, null);\r
+                       if ( d[0] == null || d[1] == null ) {\r
+                               errmsg = ERRID+"TvRockへのアクセスが失敗: "+uri;\r
+                               return false;\r
+                       }\r
+                       \r
+                       errmsg = MSGID+"視聴します: "+webChName+" @ "+selectedtuner;\r
+               }\r
+\r
+               return true;\r
+       }\r
+       \r
+       /*\r
+       @Override\r
+       public void wakeup() {\r
+       }\r
+       */\r
+       \r
+       @Override\r
+       public void shutdown() {\r
+       }\r
+\r
+       /*******************************************************************************\r
+        * レコーダーから予約一覧を取得する\r
+        ******************************************************************************/\r
+       \r
+       @Override\r
+       public boolean GetRdSettings(boolean force) {\r
+               \r
+               // おまじない\r
+               Authenticator.setDefault(new MyAuthenticator(getUser(), getPasswd()));\r
+               \r
+               System.out.println("レコーダから各種設定を取得します("+force+"): "+getRecorderId()+"("+getIPAddr()+":"+getPortNo()+")");\r
+               \r
+               errmsg = "";\r
+               \r
+               /*\r
+                *  CHコード設定\r
+                */\r
+               \r
+               cc.load(force);\r
+               replaceChNames(cc);             // これは予約一覧取得からの場合は無駄な処理になるが、GetRdSettings単体呼び出しなら意味がある\r
+               \r
+               /*\r
+                *  選択肢集団\r
+                */\r
+               \r
+               String xChapterTFile = "env/xchapter."+getIPAddr()+"_"+getPortNo()+"_"+getRecorderId()+".xml";\r
+               String msChapterTFile = "env/mschapter."+getIPAddr()+"_"+getPortNo()+"_"+getRecorderId()+".xml";\r
+               String mvChapterTFile = "env/mvchapter."+getIPAddr()+"_"+getPortNo()+"_"+getRecorderId()+".xml";\r
+               String encoderTFile = "env/encoders."+getIPAddr()+"_"+getPortNo()+"_"+getRecorderId()+".xml";\r
+               String dvdcompatTFile = "env/dvdcompat."+getIPAddr()+"_"+getPortNo()+"_"+getRecorderId()+".xml";\r
+               String aspectTFile = "env/aspect."+getIPAddr()+"_"+getPortNo()+"_"+getRecorderId()+".xml";\r
+               String lvoiceTFile = "env/lvoice."+getIPAddr()+"_"+getPortNo()+"_"+getRecorderId()+".xml";\r
+               String channelTFile = "env/channel."+getIPAddr()+"_"+getPortNo()+"_"+getRecorderId()+".xml";\r
+               \r
+               String recDefaultsTFile = "env/recdefaults."+getIPAddr()+"_"+getPortNo()+"_"+getRecorderId()+".xml";\r
+               \r
+               // ハードコーディングな選択肢の面々\r
+               setSettingRecType(arate);\r
+               setSettingBvperf(bvperf);\r
+               \r
+               if ( ! force ) {\r
+                       /*\r
+                        *  キャッシュから読み出し\r
+                        */\r
+                       encoder = TVSload(encoderTFile);\r
+                       chvalue = TVSload(channelTFile);\r
+                       \r
+                       xchapter = TVSload(xChapterTFile);\r
+                       mschapter = TVSload(msChapterTFile);\r
+                       mvchapter = TVSload(mvChapterTFile);\r
+                       \r
+                       dvdcompat = TVSload(dvdcompatTFile);\r
+                       aspect = TVSload(aspectTFile);\r
+                       lvoice = TVSload(lvoiceTFile);\r
+                       \r
+                       recDefaults = TVSload(recDefaultsTFile);\r
+               }\r
+               else {\r
+                       /*\r
+                        *  レコーダから読み出し\r
+                        */\r
+                       reportProgress("録画設定を取得します(1/1).");\r
+                       String[] d = reqGET("http://"+getIPAddr()+":"+getPortNo()+"/"+getUser()+"/reg",null);\r
+                       String res = d[1];\r
+                       \r
+                       if ( res == null ) {\r
+                               errmsg = "レコーダーが反応しません";\r
+                               return false;\r
+                       }\r
+                       \r
+                       // (1-X)録画デフォルト値\r
+                       setSettigRecDefaults(recDefaults,recDefaulltKeys,res);\r
+                       TVSsave(recDefaults, recDefaultsTFile);\r
+                       \r
+                       // (1-4)エンコーダ\r
+                       setSettingEtc(encoder,"devno",0,res);\r
+                       TVSsave(encoder, encoderTFile);\r
+                       \r
+                       // (1-5)チャンネル\r
+                       setSettingEtc(chvalue,"station",0,res);\r
+                       TVSsave(chvalue, channelTFile);\r
+                       \r
+                       // (1-6)録画終了後\r
+                       setSettingEtc(dvdcompat,"extmd",0,res);\r
+                       TVSsave(dvdcompat, dvdcompatTFile);\r
+                       \r
+                       // (1-7)待機時間\r
+                       setSettingEtc(xchapter,"idle",0,res);\r
+                       TVSsave(xchapter, xChapterTFile);\r
+                       \r
+                       // (1-8)録画開始\r
+                       setSettingEtc(mschapter,"ready",0,res);\r
+                       TVSsave(mschapter, msChapterTFile);\r
+                       \r
+                       // (1-9)録画終了\r
+                       setSettingEtc(mvchapter,"tale",0,res);\r
+                       TVSsave(mvchapter, mvChapterTFile);\r
+                       \r
+                       // (1-10)コンピュータ名\r
+                       setSettingEtc(lvoice,"cname",0,res);\r
+                       TVSsave(lvoice, lvoiceTFile);\r
+                       \r
+                       // (1-11)終了後コマンド\r
+                       setSettingEtc(aspect,"cuscom",0,res);\r
+                       TVSsave(aspect, aspectTFile);\r
+               }\r
+               \r
+               /*\r
+                * フリーオプション関連(future use.)\r
+                */\r
+               //opts.setFilename("env/options_"+getIPAddr()+"_"+getPortNo()+"_"+getRecorderId()+".xml");\r
+               \r
+               if ( getDebug() ) {\r
+                       System.err.println("=== CHコード一覧 for TvRock ===");\r
+                       System.err.println("有効な \"レコーダの放送局名\" 一覧");\r
+                       System.err.println("=============================");\r
+                       \r
+                       for ( TextValueSet tv : chvalue ) {\r
+                               System.err.println(String.format("\"%s\"",tv.getText()));\r
+                       }\r
+                       System.err.println("=============================");\r
+               }\r
+               \r
+               // ちゃんと設定を取得できているよね?\r
+               return (encoder.size()>0 && chvalue.size()>0);\r
+       }\r
+       \r
+       @Override\r
+       public boolean GetRdReserve(boolean force)\r
+       {\r
+               // おまじない\r
+               Authenticator.setDefault(new MyAuthenticator(getUser(), getPasswd()));\r
+\r
+               System.out.println("レコーダから予約一覧を取得します("+force+"): "+getRecorderId()+"("+getIPAddr()+":"+getPortNo()+")");\r
+               \r
+               errmsg = "";\r
+\r
+               //\r
+               rsvedFile = "env/reserved."+getIPAddr()+"_"+getPortNo()+"_"+getRecorderId()+".xml";\r
+               \r
+               File f = new File(rsvedFile);\r
+               if ( ! force && f.exists() ) {\r
+                       // キャッシュから読み出し(予約一覧)\r
+                       setReserves(ReservesFromFile(rsvedFile));\r
+                       replaceChNames(cc);\r
+                       if (getDebug()) ShowReserves();\r
+\r
+                       return true;\r
+               }\r
+               \r
+               // レコーダから読み出し(予約一覧)\r
+               ArrayList<ReserveList> newReserveList = new ArrayList<ReserveList>();\r
+               if ( ! GetRdReservedList(newReserveList) ) {\r
+                       return(false);\r
+               }\r
+               setReserves(newReserveList);\r
+               ReservesToFile(getReserves(), rsvedFile);       // キャッシュに保存\r
+\r
+               // 録画済みフラグを立てる(録画結果一覧→予約一覧)\r
+               setRecordedFlag();\r
+\r
+               ShowReserves();\r
+               \r
+               return(true);\r
+       }\r
+       \r
+       /**\r
+        * CHコードを置き換えよう(TvRockの場合はREC2WEB)\r
+        */\r
+       private void replaceChNames(ChannelCode cc) {\r
+               for ( ReserveList r : getReserves() ) {\r
+                       if ( VALUE_CH_EPGGET.equals(r.getChannel()) ) {\r
+                               r.setCh_name(ITEM_CH_EPGGET);\r
+                       }\r
+                       else {\r
+                               r.setCh_name(cc.getCH_REC2WEB(r.getChannel()));\r
+                       }\r
+               }\r
+       }\r
+       \r
+       /**\r
+        * @see #GetRdSettings(boolean)\r
+        */\r
+       @Override\r
+       public boolean GetRdRecorded(boolean force) {\r
+               \r
+               // おまじない\r
+               Authenticator.setDefault(new MyAuthenticator(getUser(), getPasswd()));\r
+               \r
+               System.out.println("レコーダから録画結果一覧を取得します("+force+"): "+getRecorderId()+"("+getIPAddr()+":"+getPortNo()+")");\r
+               \r
+               String recedFile = "env/recorded."+getIPAddr()+"_"+getPortNo()+"_"+getRecorderId()+".xml";\r
+               \r
+               ArrayList<RecordedInfo> newRecordedList = RecordedFromFile(recedFile);\r
+               \r
+               File f = new File(recedFile);\r
+               if ( ! force && f.exists() ) {\r
+                       \r
+                       // キャッシュから読み出し(録画結果一覧)\r
+                       setRecorded(newRecordedList);\r
+                       if (getDebug()) ShowRecorded();\r
+                       \r
+                       // 録画済みフラグを立てる(録画結果一覧→予約一覧)\r
+                       setRecordedFlag();\r
+                       \r
+                       return true;\r
+               }\r
+               \r
+               if ( ! GetRdRecordedList(newRecordedList) ) {\r
+                       return false;\r
+               }\r
+               setRecorded(newRecordedList);                           // 置き換え\r
+               RecordedToFile(getRecorded(), recedFile);       // キャッシュに保存\r
+               \r
+               // 録画済みフラグを立てる(録画結果一覧→予約一覧)\r
+               setRecordedFlag();\r
+               \r
+               ShowRecorded();\r
+               \r
+               return true;\r
+       }\r
+\r
+       private void ShowReserves() {\r
+               System.out.println("---Reserved List Start---");\r
+               for ( int i = 0; i<getReserves().size(); i++ ) {\r
+                       // 詳細情報の取得\r
+                       ReserveList e = getReserves().get(i);\r
+                       System.out.println(String.format("[%s] %s\t%s\t%s %s:%s-%s:%s\t%sm\t%s\t%s\t%s(%s)\t%s\t%s",\r
+                                       (i+1), e.getId(), e.getRec_pattern(), e.getRec_nextdate(), e.getAhh(), e.getAmm(), e.getZhh(),  e.getZmm(),     e.getRec_min(), e.getContentId(), "", e.getTitle(), e.getTitlePop(), e.getChannel(), e.getCh_name()));\r
+               }\r
+               System.out.println("---Reserved List End---");\r
+       }\r
+       private void ShowRecorded() {\r
+               System.out.println("---Recorded List Start---");\r
+               for ( int i = 0; i<getRecorded().size(); i++ ) {\r
+                       // 詳細情報の取得\r
+                       RecordedInfo e = getRecorded().get(i);\r
+                       System.out.println(String.format("[%s] %s %s\t%s:%s-%s:%s\t%s(%s)\t%s",\r
+                                       (i+1), e.getId(), e.getDate(), e.getAhh(), e.getAmm(), e.getZhh(), e.getZmm(), e.getTitle(), e.getCh_name(), e.getResult()));\r
+               }\r
+               System.out.println("---Recorded List End---");\r
+       }\r
+       \r
+       /**\r
+        * 予約一覧を取得する\r
+        */\r
+       private boolean GetRdReservedList(ArrayList<ReserveList> newReserveList) {\r
+               \r
+               // RDから予約一覧を取り出す\r
+               String response="";\r
+               {\r
+                       reportProgress("予約一覧を取得します(1/1).");\r
+                       String[] d = reqGET("http://"+getIPAddr()+":"+getPortNo()+"/"+getUser()+"/list?md=0&iv=0&st=0&ft=0",null);\r
+                       response = d[1];\r
+                       \r
+                       if (response == null) {\r
+                               errmsg = "レコーダーが反応しません";\r
+                               return(false);\r
+                       }\r
+               }\r
+               \r
+               Matcher mc = Pattern.compile("予約件数([\\s\\S]+?)</table>").matcher(response);\r
+               if ( mc.find() ) {\r
+                       Matcher mx = Pattern.compile("<tr>([\\s\\S]+?)</tr>").matcher(mc.group(1));\r
+                       while ( mx.find() ) {\r
+                               \r
+                               boolean exec = false;\r
+                               boolean pursues = false;\r
+                               boolean autoreserved = false;\r
+                               String title = "";\r
+                               String id = "";\r
+                               String center = "";\r
+                               boolean tunershort = false;\r
+                               \r
+                               int cnt = 1;\r
+                               Matcher mz = null;\r
+                               Matcher my = Pattern.compile("<td([\\s\\S]+?)</td>").matcher(mx.group(1));\r
+                               while ( my.find() ) {\r
+                                       switch (cnt) {\r
+                                       case 2: // 有効\r
+                                               if (my.group(1).contains(">○<")) {\r
+                                                       exec = true;\r
+                                               }\r
+                                               break;\r
+                                       case 5: // リピート\r
+                                               if (my.group(1).contains(">追<")) {\r
+                                                       pursues = true;\r
+                                               }\r
+                                               break;\r
+                                       case 8: // 検索予約\r
+                                               if (my.group(1).contains(">★<")) {\r
+                                                       autoreserved = true;\r
+                                               }\r
+                                               break;\r
+                                       case 10: // チャンネル\r
+                                               mz = Pattern.compile("<font.+?>(.+?)</font>").matcher(my.group(1));\r
+                                               if (mz.find()) {\r
+                                                       center = mz.group(1);\r
+                                               }\r
+                                               break;\r
+                                       case 11: // タイトル\r
+                                               mz = Pattern.compile("<a href=\"reg\\?i=(\\d+?)\"><small><font.+?><b>(.*?)</b>").matcher(my.group(1));\r
+                                               if (mz.find()) {\r
+                                                       id = mz.group(1);\r
+                                                       title = CommonUtils.unEscape(mz.group(2)).replaceAll("<BR>", "");\r
+                                               }\r
+                                               break;\r
+                                       case 1: // 順\r
+                                       case 3: // コンピュータ\r
+                                       case 4: // デバイス\r
+                                       case 6: // アプリ終了\r
+                                       case 7: // 予定\r
+                                       case 9: // 予定日時\r
+                                               mz = Pattern.compile("<font color=#f86878><b>").matcher(my.group(1));\r
+                                               tunershort = mz.find(); // チューナー重複警告\r
+                                               break;\r
+                                       }\r
+                                       cnt++;\r
+                               }\r
+                               if ( cnt != 12 || id.equals("") ) {\r
+                                       continue;\r
+                               }\r
+                               \r
+                               // 登録\r
+                               ReserveList entry = new ReserveList();\r
+                               \r
+                               // 実行ON/OFF\r
+                               entry.setExec(exec);\r
+\r
+                               // 追跡ON/OFF\r
+                               entry.setPursues(pursues);\r
+                               \r
+                               // 検索予約\r
+                               entry.setAutoreserved(autoreserved);\r
+                               \r
+                               // 放送局\r
+                               {\r
+                                       if ( center == null || center.length() == 0 ) {\r
+                                               entry.setCh_name(ITEM_CH_EPGGET);\r
+                                               entry.setChannel(VALUE_CH_EPGGET);\r
+                                       }\r
+                                       else {\r
+                                               entry.setCh_name(cc.getCH_REC2WEB(center));\r
+                                               entry.setChannel(center);\r
+                                       }\r
+                               }\r
+\r
+                               // Id\r
+                               entry.setId(id);\r
+\r
+                               // 予約名\r
+                               entry.setTitle(title);\r
+                               entry.setTitlePop(TraceProgram.replacePop(title));\r
+                               \r
+                               // チューナー重複警告\r
+                               entry.setTunershort(tunershort);\r
+                               \r
+                               // タイトル自動補完フラグなど本体からは取得できない情報を引き継ぐ\r
+                               copyAttributes(entry, getReserves());\r
+                               \r
+                               // 予約情報を保存\r
+                               newReserveList.add(entry);\r
+                       }\r
+               }\r
+\r
+               // 予約詳細を追加取得する\r
+               int cnt = 0;\r
+               for (ReserveList r : newReserveList) {\r
+                       // 進捗状況を報告する\r
+                       ++cnt;\r
+                       reportProgress("+番組詳細を取得します("+cnt+"/"+newReserveList.size()+")");\r
+                       getReserveDetail(r);\r
+               }\r
+               \r
+               return true;\r
+       }\r
+       \r
+       \r
+       /**\r
+        * 予約詳細を取得する\r
+        */\r
+       private boolean getReserveDetail(ReserveList r) {\r
+               \r
+               String[] d = reqGET("http://"+getIPAddr()+":"+getPortNo()+"/"+getUser()+"/reg?i="+r.getId(),null);\r
+               String res = d[1];\r
+               \r
+               if (res != null) {\r
+                       \r
+                       // 録画優先度\r
+                       Matcher mb = Pattern.compile("<input type=\"text\" name=\"pri\".+?value=\"(\\d+?)\">").matcher(res);\r
+                       if (mb.find()) {\r
+                               r.setRec_bvperf(mb.group(1));\r
+                       }\r
+\r
+                       // (1-4)エンコーダ\r
+                       r.setTuner(getSelectedSetting("devno",res));\r
+                       \r
+                       // (1-6)録画終了\r
+                       r.setRec_dvdcompat(getSelectedSetting("extmd",res));\r
+                       \r
+                       // (1-7)待機時間\r
+                       r.setRec_xchapter(getSelectedSetting("idle",res));\r
+                       \r
+                       // (1-8)録画開始\r
+                       r.setRec_mschapter(getSelectedSetting("ready",res));\r
+                       \r
+                       // (1-9)録画終了\r
+                       r.setRec_mvchapter(getSelectedSetting("tale",res));\r
+                       \r
+                       // (1-10)コンピュータ名\r
+                       r.setRec_lvoice(getSelectedSetting("cname",res));\r
+                       \r
+                       // (1-11)終了後コマンド\r
+                       r.setRec_aspect(getSelectedSetting("cuscom",res));\r
+                       \r
+                       // 時刻を調べる\r
+                       r.setAhh(getSelectedSetting("shour",res));\r
+                       r.setAmm(getSelectedSetting("smin",res));\r
+                       r.setZhh(getSelectedSetting("ehour",res));\r
+                       r.setZmm(getSelectedSetting("emin",res));\r
+\r
+                       // 日付を調べる\r
+                       mb = Pattern.compile("<input type=\"checkbox\" name=\"epgflw\" value=\"true\"(\\s*checked)?>").matcher(res);\r
+                       if ( mb.find() ) {\r
+                               // EPG予約\r
+                               if ( mb.group(1) != null ) {\r
+                                       // 追跡ON\r
+                                       getPatternPursues(r,res);\r
+                                       r.setPursues(true);\r
+                               }\r
+                               else {\r
+                                       // 追跡OFF\r
+                                       getPattern(r,res);\r
+                                       r.setPursues(false);\r
+                               }\r
+                               r.setContentId(ContentIdEDCB.getContentId(0xFFFF,0xFFFF,0xFFFF,0xFFFF));\r
+                               r.setRec_audio(ITEM_REC_TYPE_EPG);\r
+                       }\r
+                       else {\r
+                               // プログラム予約\r
+                               getPattern(r,res);\r
+                               r.setContentId("");\r
+                               r.setRec_audio(ITEM_REC_TYPE_PROG);\r
+                               r.setPursues(false);\r
+                       }\r
+                       \r
+                       // 番組詳細\r
+                       mb = Pattern.compile("<font color=#e8e8e8><small>([\\s\\S]*?)</small>(.*?<small>([\\s\\S]*?)</small>)?").matcher(res);\r
+                       if ( mb.find() ) {\r
+                               if ( mb.group(3) == null ) {\r
+                                       r.setDetail(CommonUtils.unEscape(mb.group(1)));\r
+                               }\r
+                               else {\r
+                                       r.setDetail(CommonUtils.unEscape(mb.group(1)+"\n\n"+mb.group(3)));\r
+                               }\r
+                       }\r
+                       \r
+                       // 調整する\r
+                       r.setRec_nextdate(CommonUtils.getNextDate(r));\r
+                       r.setRec_min(CommonUtils.getRecMin(r.getAhh(), r.getAmm(), r.getZhh(), r.getZmm()));\r
+                       getStartEndDateTime(r);\r
+                       \r
+                       return true;\r
+               }\r
+               return false;\r
+       }\r
+       \r
+       // 時間追従あり\r
+       private void getPatternPursues(ReserveList r, String res) {\r
+               \r
+               Matcher mb = null;\r
+               \r
+               /*\r
+                *  時刻を調べる\r
+                */\r
+               \r
+               String mm = null;\r
+               String dd = null;\r
+               \r
+               mb = Pattern.compile("</form>.*?>(\\d+)/(\\d+) (\\d+):(\\d+)~(\\d+):(\\d+)<br>").matcher(res);\r
+               if ( mb.find() ) {\r
+                       // 番組詳細から取得\r
+                       mm = mb.group(1);\r
+                       dd = mb.group(2);\r
+                       r.setAhh(String.format("%02d", Integer.valueOf(mb.group(3))));\r
+                       r.setAmm(String.format("%02d", Integer.valueOf(mb.group(4))));\r
+                       r.setZhh(String.format("%02d", Integer.valueOf(mb.group(5))));\r
+                       r.setZmm(String.format("%02d", Integer.valueOf(mb.group(6))));\r
+               }\r
+               else {\r
+                       // 「予約日時」から取得\r
+                       mb = Pattern.compile("<input type=\"hidden\" name=\"shour\" value=\"\\d+?\">(\\d+?)<").matcher(res);\r
+                       if (mb.find()) {\r
+                               r.setAhh(String.format("%02d", Integer.valueOf(mb.group(1))));\r
+                       }\r
+                       mb = Pattern.compile("<input type=\"hidden\" name=\"smin\" value=\"\\d+?\">(\\d+?)<").matcher(res);\r
+                       if (mb.find()) {\r
+                               r.setAmm(String.format("%02d", Integer.valueOf(mb.group(1))));\r
+                       }\r
+                       mb = Pattern.compile("<input type=\"hidden\" name=\"ehour\" value=\"\\d+?\">(\\d+?)<").matcher(res);\r
+                       if (mb.find()) {\r
+                               r.setZhh(String.format("%02d", Integer.valueOf(mb.group(1))));\r
+                       }\r
+                       mb = Pattern.compile("<input type=\"hidden\" name=\"emin\" value=\"\\d+?\">(\\d+?)<").matcher(res);\r
+                       if (mb.find()) {\r
+                               r.setZmm(String.format("%02d", Integer.valueOf(mb.group(1))));\r
+                       }\r
+               }\r
+               \r
+               /*\r
+                * 日付を調べる\r
+                */\r
+               \r
+               int year = 1970;\r
+               int month = 1;\r
+               int day = 1;\r
+               //int wday = 1;\r
+               mb = Pattern.compile("<input type=\"hidden\" name=\"year\" value=\"\\d+?\">(\\d+?)<").matcher(res);\r
+               if (mb.find()) {\r
+                       year = Integer.valueOf(mb.group(1));\r
+               }\r
+               if ( mm != null ) {\r
+                       // 番組詳細から取得\r
+                       month = Integer.valueOf(mm);\r
+                       day = Integer.valueOf(dd);\r
+               }\r
+               else {\r
+                       // 「予約日時」から取得\r
+                       mb = Pattern.compile("<input type=\"hidden\" name=\"mon\" value=\"\\d+?\">(\\d+?)<").matcher(res);\r
+                       if (mb.find()) {\r
+                               month = Integer.valueOf(mb.group(1));\r
+                       }\r
+                       mb = Pattern.compile("<input type=\"hidden\" name=\"day\" value=\"\\d+?\">(\\d+?)<").matcher(res);\r
+                       if (mb.find()) {\r
+                               day = Integer.valueOf(mb.group(1));\r
+                       }\r
+               }\r
+               GregorianCalendar c = new GregorianCalendar();\r
+               c.set(Calendar.DATE, day);\r
+               c.set(Calendar.MONTH, month-1);\r
+               c.set(Calendar.YEAR, year);\r
+               c.set(Calendar.MINUTE, Integer.valueOf(r.getAhh()));\r
+               c.set(Calendar.SECOND, Integer.valueOf(r.getAmm()));\r
+               //wday = c.get(Calendar.DAY_OF_WEEK);\r
+               \r
+               // 年をまたいで追跡されると追いかけられないのである\r
+               \r
+               r.setRec_pattern(CommonUtils.getDate(c));\r
+               r.setRec_pattern_id(11);\r
+       }\r
+       \r
+       // 時間追従なし\r
+       private void getPattern(ReserveList r, String res) {\r
+               int year = 1970;\r
+               int month = 1;\r
+               int day = 1;\r
+               int wday = 1;\r
+               Matcher mb = Pattern.compile("<select name=\"year\">.*?<option value=\"[^\"]+?\"\\s*selected>(.+?)</option>.*?</select>").matcher(res);\r
+               if (mb.find()) {\r
+                       year = Integer.valueOf(mb.group(1));\r
+               }\r
+               mb = Pattern.compile("<select name=\"mon\">.*?<option value=\"[^\"]+?\"\\s*selected>(.+?)</option>.*?</select>").matcher(res);\r
+               if (mb.find()) {\r
+                       month = Integer.valueOf(mb.group(1));\r
+               }\r
+               mb = Pattern.compile("<select name=\"day\">.*?<option value=\"[^\"]+?\"\\s*selected>(.+?)</option>.*?</select>").matcher(res);\r
+               if (mb.find()) {\r
+                       day = Integer.valueOf(mb.group(1));\r
+               }\r
+               GregorianCalendar c = new GregorianCalendar();\r
+               c.set(Calendar.DATE, day);\r
+               c.set(Calendar.MONTH, month-1);\r
+               c.set(Calendar.YEAR, year);\r
+               wday = c.get(Calendar.DAY_OF_WEEK);\r
+               \r
+               mb = Pattern.compile("<input type=\"radio\" name=\"repeat\" value=\"([^\"]+?)\"\\s*checked>").matcher(res);\r
+               if (mb.find()) {\r
+                       if (mb.group(1).equals("0")) {\r
+                               // 当日限り\r
+                               r.setRec_pattern(CommonUtils.getDate(c));\r
+                               r.setRec_pattern_id(11);\r
+                       }\r
+                       else if (mb.group(1).equals("1")) {\r
+                               // 毎日\r
+                               r.setRec_pattern(RPTPTN[10]);\r
+                               r.setRec_pattern_id(10);\r
+                       }\r
+                       else if (mb.group(1).equals("2")) {\r
+                               // 毎週\r
+                               for ( int i=0; i<7; i++ ) {\r
+                                       if (RPTPTN[i].equals("毎"+CommonUtils.WDTPTN[wday-1]+"曜日")) {\r
+                                               r.setRec_pattern(RPTPTN[i]);\r
+                                               r.setRec_pattern_id(i);\r
+                                               break;\r
+                                       }\r
+                               }\r
+                       }\r
+                       else if (mb.group(1).equals("4")) {\r
+                               // 帯予約\r
+                               int fw = 0;\r
+                               int i = 0;\r
+                               Matcher mc = Pattern.compile("<input type=\"checkbox\" name=\"repw.\" value=\"true\"\\s*(checked)?>").matcher(res);\r
+                               while (mc.find()) {\r
+                                       if (mc.group(1) != null) {\r
+                                               fw |= (1<<i);\r
+                                       }\r
+                                       i++;\r
+                               }\r
+                               \r
+                               // 24:00~28:59開始は深夜帯\r
+                               if (CommonUtils.isLateNight(r.getAhh()) && (fw == 125 || fw == 124 || fw == 60)) {\r
+                                       fw = -fw;\r
+                               }\r
+                                                       \r
+                               switch (fw) {\r
+                               case 126:       //   2+4+8+16+32+64\r
+                               case -125:      // 1+  4+8+16+32+64\r
+                                       r.setRec_pattern(RPTPTN[9]);\r
+                                       r.setRec_pattern_id(9);\r
+                                       break;\r
+                               case 30:        // 2+4+8+16\r
+                               case -60:       //   4+8+16+32\r
+                                       r.setRec_pattern(RPTPTN[7]);\r
+                                       r.setRec_pattern_id(7);\r
+                                       break;\r
+                               case 62:        // 2+4+8+16+32\r
+                               case -124:      //   4+8+16+32+64\r
+                               default:\r
+                                       r.setRec_pattern(RPTPTN[8]);\r
+                                       r.setRec_pattern_id(8);\r
+                                       break;\r
+                               }\r
+                       }\r
+               }\r
+       }\r
+       \r
+       // 各番組の、指定されている値を取得する\r
+       private String getSelectedSetting(String key, String res) {\r
+               Matcher mb = Pattern.compile("<select name=\""+key+"\">.*?<option value=\"([^\"]+?)\"\\s*selected>(.+?)</option>.*?</select>").matcher(res);\r
+               if (mb.find()) {\r
+                       return mb.group(2);\r
+               }\r
+               return null;\r
+       }\r
+       \r
+       \r
+       /**\r
+        *      録画結果一覧を取得する\r
+        */\r
+       \r
+       private boolean GetRdRecordedList(ArrayList<RecordedInfo> newRecordedList) {\r
+               \r
+               String critDate = null;\r
+               if ( newRecordedList.size() > 0 ) {\r
+                       // 最新の情報の前日分までチェックする\r
+                       GregorianCalendar cal = CommonUtils.getCalendar(newRecordedList.get(0).getDate());\r
+                       cal.add(Calendar.DATE, -1);\r
+                       critDate = CommonUtils.getDate(cal);\r
+               }\r
+               else {\r
+                       // 既存情報が無ければ上限まで\r
+                       critDate = CommonUtils.getDate(CommonUtils.getCalendar(-86400*getRecordedSaveScope()));\r
+               }\r
+               \r
+               // RDから予約一覧を取り出す\r
+               String response="";\r
+               {\r
+                       reportProgress("録画結果一覧を取得します.");\r
+                       String[] d = reqGET("http://"+getIPAddr()+":"+getPortNo()+"/nobody/page20",null,"utf8");\r
+                       response = d[1];\r
+                       \r
+                       if (response == null) {\r
+                               errmsg = "レコーダーが反応しません";\r
+                               return false;\r
+                       }\r
+               }\r
+               \r
+               String[] list = response.split("\n");\r
+               String other_messages = "";\r
+               \r
+               HashMap<String,RecordedInfo> results = new HashMap<String,RecordedInfo>();\r
+               \r
+               for ( int n=list.length-1; n>=0; n--) {\r
+                       if ( list[n].matches(".*(TvRockの(起動|終了)|システム休止|(休止|スタンバイ)[のを]キャンセル|番組情報の取得).*") ) {\r
+                               continue;\r
+                       }\r
+                       \r
+                       Matcher ma = Pattern.compile("\\[(\\d\\d/\\d\\d/\\d\\d) (\\d\\d):(\\d\\d):.+?\\]:\\[(.+?)\\](.+)$",Pattern.DOTALL).matcher(list[n]);\r
+                       if ( ma.find() ) {\r
+                               String date = "20"+ma.group(1);\r
+                               String hh = ma.group(2);\r
+                               String mm = ma.group(3);\r
+                               String tuner = ma.group(4);\r
+                               String message = ma.group(5);\r
+                               \r
+                               RecordedInfo entry = null;\r
+                               \r
+                               if ( message.contains("録画開始") ) {\r
+                                       if ( results.containsKey(tuner) ) {\r
+                                               // 既になんかあるね!吐き出せー\r
+                                               if (results.get(tuner).getDate().compareTo(critDate) >= 0) {\r
+                                                       entry = results.get(tuner);\r
+                                                       if ( entry.getZhh() == null || entry.getZhh().equals("") ) {\r
+                                                               entry.setResult("※録画が中断された可能性があります。");\r
+                                                               entry.setZhh(hh);\r
+                                                               entry.setZmm(mm);\r
+                                                               int len =  (int) (CommonUtils.getCompareDateTime(date+" "+hh+":"+mm, entry.getDate()+" "+entry.getAhh()+":"+entry.getAmm()) / 60000L);\r
+                                                               entry.setLength(len);\r
+                                                               entry.setSucceeded(false);\r
+                                                       }\r
+                                                       addRecorded(newRecordedList,entry);\r
+                                               }\r
+                                       }\r
+                                       \r
+                                       entry = new RecordedInfo();\r
+                                       results.put(tuner, entry);\r
+                                       \r
+                                       entry.setId(String.valueOf(n));\r
+                                       \r
+                                       entry.setDate(CommonUtils.getDate(CommonUtils.getCalendar(date)));\r
+                                       entry.setAhh(hh);\r
+                                       entry.setAmm(mm);\r
+                                       entry.setLength(0);     // 仮置き\r
+                                       \r
+                                       Matcher mb = Pattern.compile("Sig=(\\d+\\.\\d+),.*, Drop=(\\d+),.*, DiskFree=(\\d+\\.\\d+)\\%",Pattern.DOTALL).matcher(message);\r
+                                       if ( mb.find() ) {\r
+                                               entry.setSig_a(Float.valueOf(mb.group(1)));\r
+                                               //entry.setDrop(Integer.valueOf(mb.group(2)));  // リストに追加するときに調整してください\r
+                                               //entry.setResult("ディスクの空き領域:"+mb.group(3)+"%");\r
+                                               //entry.setSucceeded(Float.valueOf(mb.group(3))>0.1F);\r
+                                       }\r
+                                       \r
+                                       mb = Pattern.compile("番組「(.*?)」 録画開始",Pattern.DOTALL).matcher(message);\r
+                                       if ( mb.find() ) {\r
+                                               entry.setTitle(mb.group(1));\r
+                                               //entry.setCh_name(mb.group(2));\r
+                                       }\r
+                                       \r
+                                       entry.setDetail(list[n]+"\n#録画終了#\n");\r
+                                       \r
+                                       entry.setCh_name("TvRockでは取得できません");\r
+                                       entry.setCh_orig(entry.getCh_name());\r
+                                       \r
+                                       entry.setSucceeded(true);\r
+                               }\r
+                               else if ( message.contains("録画終了") ) {\r
+                                       if ( ! results.containsKey(tuner) ) {\r
+                                               // 「録画開始」以外から始まっていたら無視しよう\r
+                                               continue;\r
+                                       }\r
+                                       \r
+                                       entry = results.get(tuner);\r
+                                       if ( entry == null ) {\r
+                                               continue;\r
+                                       }\r
+                                       \r
+                                       entry.setZhh(hh);\r
+                                       entry.setZmm(mm);\r
+                                       \r
+                                       int len =  (int) (CommonUtils.getCompareDateTime(date+" "+hh+":"+mm, entry.getDate()+" "+entry.getAhh()+":"+entry.getAmm()) / 60000L);\r
+                                       entry.setLength(len);\r
+                                       \r
+                                       Matcher mb = Pattern.compile("Sig=(\\d+\\.\\d+),.*, Drop=(\\d+),.*, DiskFree=(\\d+\\.\\d+)\\%",Pattern.DOTALL).matcher(message);\r
+                                       if ( mb.find() ) {\r
+                                               entry.setSig_z(Float.valueOf(mb.group(1)));\r
+                                               entry.setDrop(Integer.valueOf(mb.group(2)));    // リストに追加するときに調整してください\r
+                                               entry.setResult(String.format("DiskFree=%s%% Sig=%.2f-%.2fdb",mb.group(3),entry.getSig_a(),entry.getSig_z()));\r
+                                               entry.setSucceeded(Float.valueOf(mb.group(3))>0.1F);\r
+                                       }\r
+                                       \r
+                                       entry.setDetail(entry.getDetail().replaceFirst("#録画終了#", list[n]));\r
+                               }\r
+                               else if ( message.contains("視聴開始") ) {\r
+                                       if ( results.containsKey(tuner) ) {\r
+                                               // 既になんかあるね!吐き出せー\r
+                                               if (results.get(tuner).getDate().compareTo(critDate) >= 0) addRecorded(newRecordedList,results.get(tuner));\r
+                                       }\r
+                                       \r
+                                       results.remove(tuner);\r
+                               }\r
+                               else {\r
+                                       if ( ! results.containsKey(tuner) ) {\r
+                                               // 「録画開始」以外から始まっていたら無視しよう\r
+                                               continue;\r
+                                       }\r
+                                       \r
+                                       entry = results.get(tuner);\r
+                                       if ( entry == null ) {\r
+                                               continue;\r
+                                       }\r
+                                       \r
+                                       // MPEG2のDropだけ拾うには?\r
+                                       Matcher mb = Pattern.compile("エラー詳細:PID (0x.+?), Total=(\\d+), Drop=(\\d+), Scrambling=(\\d+)",Pattern.DOTALL).matcher(message);\r
+                                       if ( mb.find() ) {\r
+                                               int pid = Integer.decode(mb.group(1));\r
+                                               if ( pid < 0x100 || pid == 0x500 ) {\r
+                                                       int drop = Integer.valueOf(mb.group(3));\r
+                                                       entry.setDrop_mpeg(entry.getDrop_mpeg()+drop);\r
+                                               }\r
+                                       }\r
+                                       \r
+                                       entry.setDetail(entry.getDetail()+list[n]+"\n");\r
+                               }\r
+                               \r
+                               continue;\r
+                       }\r
+                       \r
+                       ma = Pattern.compile("\\[(\\d\\d/\\d\\d/\\d\\d) (\\d\\d):(\\d\\d):.+?\\]:(.*)$",Pattern.DOTALL).matcher(list[n]);\r
+                       if ( ma.find() ) {\r
+                               \r
+                               String title = "";\r
+                               String message = "";\r
+                               Matcher mb = Pattern.compile("番組「(.*?)」の(予約は実行されませんでした)",Pattern.DOTALL).matcher(ma.group(4));\r
+                               if ( mb.find() ) {\r
+                                       title = mb.group(1);\r
+                                       message = mb.group(2);\r
+                               }\r
+                               else if ( (mb = Pattern.compile("ターゲットアプリケーションの異常終了コードを検出しました",Pattern.DOTALL).matcher(ma.group(4))) != null && mb.find() ) {\r
+                                       title = "<<<エラーメッセージ>>>";\r
+                                       message = ma.group(4);\r
+                               }\r
+                               else {\r
+                                       other_messages += list[n]+"\n";\r
+                                       continue;\r
+                               }\r
+                               \r
+                               String date = "20"+ma.group(1);\r
+                               String hh = ma.group(2);\r
+                               String mm = ma.group(3);\r
+                               \r
+                               RecordedInfo entry = new RecordedInfo();\r
+                                       \r
+                               entry.setId(String.valueOf(n));\r
+                                       \r
+                               entry.setDate(CommonUtils.getDate(CommonUtils.getCalendar(date)));\r
+                               entry.setAhh(hh);\r
+                               entry.setAmm(mm);\r
+                               entry.setZhh(hh);\r
+                               entry.setZmm(mm);\r
+                               entry.setLength(0);     // 仮置き\r
+                                       \r
+                               entry.setTitle(title);\r
+                               \r
+                               entry.setDetail(list[n]+"\n");\r
+                               \r
+                               entry.setChannel(null);\r
+                               entry.setCh_name("TvRockでは取得できません");\r
+                               \r
+                               entry.setResult(message);\r
+                               \r
+                               entry.setSucceeded(false);\r
+                               \r
+                               if (entry.getDate().compareTo(critDate) >= 0) addRecorded(newRecordedList, entry);\r
+                       \r
+                               continue;\r
+                       }\r
+               \r
+               }\r
+               \r
+               for ( RecordedInfo entry : results.values() ) {\r
+                       if ( entry.getLength() > 0 ) {\r
+                               if (entry.getDate().compareTo(critDate) >= 0) addRecorded(newRecordedList,entry);\r
+                       }\r
+               }\r
+\r
+               // その他のログ\r
+               if ( other_messages.length() > 0 ) {\r
+                       RecordedInfo entry = new RecordedInfo();\r
+                       \r
+                       entry.setDate(CommonUtils.getDate(CommonUtils.getCalendar("1970/01/01")));\r
+                       entry.setAhh("00");\r
+                       entry.setAmm("00");\r
+                       entry.setZhh("00");\r
+                       entry.setZmm("00");\r
+                       entry.setLength(0);     // 仮置き\r
+                               \r
+                       entry.setTitle("<<<その他のメッセージ>>>");\r
+                       \r
+                       entry.setDetail(other_messages);\r
+                       \r
+                       entry.setChannel(null);\r
+                       entry.setCh_name("TvRockでは取得できません");\r
+                       \r
+                       entry.setResult("その他のメッセージ");\r
+                       \r
+                       entry.setSucceeded(false);\r
+\r
+                       if ( newRecordedList.get(newRecordedList.size()-1).getTitle().equals("<<<その他のメッセージ>>>") ) {\r
+                               newRecordedList.remove(newRecordedList.size()-1);\r
+                       }\r
+                       newRecordedList.add(entry);\r
+               }\r
+\r
+               return true;\r
+       }\r
+       \r
+       /*******************************************************************************\r
+        * 新規予約\r
+        ******************************************************************************/\r
+       \r
+       @Override\r
+       public boolean PostRdEntry(ReserveList r) {\r
+               \r
+               errmsg = "";\r
+\r
+               String chCode = ((ITEM_CH_EPGGET.equals(r.getCh_name())) ? VALUE_CH_EPGGET : cc.getCH_WEB2REC(r.getCh_name()));\r
+               if ( chCode == null) {\r
+                       errmsg = "【警告】Web番組表の放送局名「"+r.getCh_name()+"」をCHコードに変換できません。CHコード設定を修正してください。" ;\r
+                       System.out.println(errmsg);\r
+                       return(false);\r
+               }\r
+               \r
+               // EPG予約かプログラム予約か\r
+               //int onid = -1;\r
+               //int tsid = -1;\r
+               int sid = -1;\r
+               int evid = -1;\r
+               if ( r.getRec_audio() != null && r.getRec_audio().equals(ITEM_REC_TYPE_EPG) ) {\r
+                       if ( ContentIdEDCB.isValid(r.getContentId()) ) {\r
+                               ContentIdEDCB.decodeContentId(r.getContentId());\r
+                               //onid = ContentIdEDCB.getOnId();\r
+                               //tsid = ContentIdEDCB.getTSId();\r
+                               sid = ContentIdEDCB.getSId();\r
+                               evid = ContentIdEDCB.getEvId();\r
+                       }\r
+                       else if ( ContentIdDIMORA.isValid(r.getContentId()) ) {\r
+                               ContentIdDIMORA.decodeContentId(r.getContentId());\r
+                               sid = ContentIdDIMORA.getSId();\r
+                               evid = ContentIdDIMORA.getEvId();\r
+                       }\r
+                       else if ( ContentIdREGZA.isValid(r.getContentId()) ) {\r
+                               ContentIdREGZA.decodeContentId(r.getContentId());\r
+                               sid = ContentIdREGZA.getSId();\r
+                               evid = ContentIdREGZA.getEvId();\r
+                       }\r
+                       else {\r
+                               errmsg = "番組表に予約IDがないためEPG予約は利用できません。EDCB番組表を利用するかプログラム予約を行ってください。";\r
+                               return false;\r
+                       }\r
+                       \r
+                       return PostRdEntryEPG(r,sid,evid);\r
+               }\r
+               \r
+               System.out.println("Run: PostRdEntry("+r.getTitle()+")");\r
+               \r
+               if ( r.getPursues() ) {\r
+                       errmsg = "プログラム予約では次の指定はできないので規定値に変更しました:番組追従=なし";\r
+                       r.setPursues(false);\r
+               }\r
+\r
+               // おまじない\r
+               Authenticator.setDefault(new MyAuthenticator(getUser(), getPasswd()));\r
+               \r
+               // 予約パターンID\r
+               // 次回予定日\r
+               // 録画長\r
+               // 開始日時・終了日時\r
+               r.setRec_pattern_id(getRec_pattern_Id(r.getRec_pattern()));\r
+               r.setRec_nextdate(CommonUtils.getNextDate(r));\r
+               r.setRec_min(CommonUtils.getRecMin(r.getAhh(),r.getAmm(),r.getZhh(),r.getZmm()));\r
+               getStartEndDateTime(r);\r
+               \r
+               // タイトルは200文字まで\r
+               r.setTitle(CommonUtils.substringrb(r.getTitle(), 200));\r
+               \r
+               // 番組詳細は200文字まで(3.7.10β)\r
+               r.setDetail(CommonUtils.substringrb(r.getDetail(), 200));\r
+               \r
+               // プログラム予約なので\r
+               r.setContentId("");\r
+               \r
+               // CHコード入れ忘れてたよ!\r
+               r.setChannel(chCode);\r
+               \r
+               // TVRockへ登録する\r
+               String header;\r
+               String response;\r
+               \r
+               // 情報作成\r
+               String pstr = genPoststr(r, "0", " 予約を追加 ");\r
+               \r
+               System.out.println(pstr);\r
+\r
+               // 情報送信\r
+               {               \r
+                       reportProgress("send request(1/1).");\r
+                       header = response = null;\r
+                       String reqStr = "http://"+getIPAddr()+":"+getPortNo()+"/"+getUser()+"/regsch?"+pstr;\r
+                       for (int i=0; i<retryMax; i++) {\r
+                               String[] d = reqGET(reqStr,null);\r
+                               header = d[0];\r
+                               response = d[1];\r
+                               if ( header != null && response == null ) {\r
+                                       reportProgress("コネクションがリセットされました。リトライします。");\r
+                                       CommonUtils.milSleep(1000);\r
+                               }\r
+                               else {\r
+                                       break;\r
+                               }\r
+                       }\r
+                       if ( header == null || response == null ) {\r
+                               errmsg = "レコーダーが反応しません。";\r
+                               return(false);\r
+                       }\r
+               }\r
+               \r
+               // 予約ID番号を取得(キャッシュに存在しない番号が新番号)\r
+               r.setId(getNewId(response));\r
+               if (r.getId() == null) {\r
+                       errmsg = "予約に失敗しました。";\r
+                       return(false);\r
+               }\r
+               reportProgress("予約IDは"+r.getId()+"です。");\r
+               \r
+               // 予約リストを更新\r
+               getReserves().add(r);\r
+               \r
+               // キャッシュに保存\r
+               ReservesToFile(getReserves(), rsvedFile);\r
+               \r
+               System.out.printf("\n<<< Message from RD >>> \"%s\"\n\n", "正常に登録できました。");\r
+               return(true);\r
+       }\r
+\r
+       private boolean PostRdEntryEPG(ReserveList r, int sid, int evid) {\r
+               \r
+               System.out.println("Run: PostRdEntryEPG("+r.getTitle()+")");\r
+               \r
+               String enc = text2value(encoder, r.getTuner());\r
+               if ( enc == null || enc.length() == 0 ) {\r
+                       errmsg = ERRID+"エンコーダが認識できない: "+r.getTuner();\r
+                       return false;\r
+               }\r
+               \r
+               // 予約パターンID\r
+               // 次回予定日\r
+               // 録画長\r
+               // 開始日時・終了日時\r
+               r.setRec_pattern_id(getRec_pattern_Id(r.getRec_pattern()));\r
+               r.setRec_nextdate(CommonUtils.getNextDate(r));\r
+               r.setRec_min(CommonUtils.getRecMin(r.getAhh(),r.getAmm(),r.getZhh(),r.getZmm()));\r
+               getStartEndDateTime(r);\r
+               \r
+               if ( r.getRec_pattern_id() != 11 ) {\r
+                       errmsg = ERRID+"EPG予約に繰り返し指定はできない: "+r.getRec_pattern();\r
+                       return false;\r
+               }\r
+               \r
+               // タイトルは200文字まで\r
+               r.setTitle(CommonUtils.substringrb(r.getTitle(), 200));\r
+               \r
+               // 番組詳細は200文字まで(3.7.10β)\r
+               r.setDetail(CommonUtils.substringrb(r.getDetail(), 200));\r
+\r
+               // EPG予約なので\r
+               //r.setContentId(ほげほげ);\r
+\r
+               // おまじない\r
+               Authenticator.setDefault(new MyAuthenticator(getUser(), getPasswd()));\r
+               \r
+               ReserveList newRsv = r; // 予約内容が強制変更されるかもしれん\r
+               \r
+               // 登録\r
+               {\r
+                       String reqStr = "http://"+getIPAddr()+":"+getPortNo()+"/"+getUser()+"/kws?r=1&c="+sid+"&e="+evid+"&d="+enc;\r
+                       \r
+                       reportProgress(DBGID+"EPG予約を登録します(1/2): "+reqStr);\r
+                       \r
+                       String header = null;\r
+                       String response = null;\r
+                       \r
+                       for (int i=0; i<retryMax; i++) {\r
+                               String[] d = reqGET(reqStr,null);\r
+                               header = d[0];\r
+                               response = d[1];\r
+                               if ( header != null && response == null ) {\r
+                                       reportProgress(ERRID+"コネクションがリセットされました。リトライします。");\r
+                                       CommonUtils.milSleep(1000);\r
+                               }\r
+                               else {\r
+                                       break;\r
+                               }\r
+                       }\r
+                       if ( header == null || response == null ) {\r
+                               errmsg = ERRID+"レコーダーが反応しません。";\r
+                               return(false);\r
+                       }\r
+                       \r
+                       String id = null;\r
+                       int cnt = 0;\r
+                       Matcher ma = Pattern.compile("<tr><td align=center width=\\d+% bgcolor=#(......)>(.*?)</td></tr>",Pattern.DOTALL).matcher(response);\r
+                       while ( ma.find() ) {\r
+                               \r
+                               ++cnt;\r
+                               System.err.println(ma.group(1));\r
+                               \r
+                               if ( ma.group(1).equals("c0f0d4") ) {\r
+                                       \r
+                                       Matcher mb = Pattern.compile("<a href=\"reg\\?i=(\\d+)\">").matcher(ma.group(2));\r
+                                       if ( mb.find() ) {\r
+                                                       \r
+                                               r.setId(id = mb.group(1));\r
+                                               reportProgress("予約IDは"+r.getId()+"です。");\r
+                                               \r
+                                               ReserveList n = r.clone();\r
+                                               \r
+                                               if ( ! getReserveDetail(n) ) {\r
+                                                       errmsg = ERRID+"登録は行われましたが、処理が途中で中断しました。確認してください。";\r
+                                                       \r
+                                                       getReserves().add(r);\r
+                                                       ReservesToFile(getReserves(), rsvedFile);\r
+                                                       return false;\r
+                                               }\r
+       \r
+                                               if ( isModified(r,n) ) {\r
+                                                       newRsv = n;\r
+                                               }\r
+                                               \r
+                                               break;\r
+                                       }\r
+                               }\r
+                       }\r
+                       if ( id == null ) {\r
+                               if ( cnt == 0 ) {\r
+                                       errmsg = ERRID+"登録できませんでした";\r
+                               }\r
+                               else {\r
+                                       errmsg = ERRID+"TvRock番組表に該当の番組IDが存在しないか、すでに登録済みです";\r
+                               }\r
+                               return false;\r
+                       }\r
+       \r
+               }\r
+\r
+               // 更新\r
+               {\r
+                       // 情報作成\r
+                       String pstr = genPoststr(r, newRsv.getId(), " 予約を変更 ");\r
+                       \r
+                       String reqStr = "http://"+getIPAddr()+":"+getPortNo()+"/"+getUser()+"/regsch?"+pstr;\r
+\r
+                       reportProgress(DBGID+"EPG予約を更新します(2/2): "+reqStr);\r
+                       \r
+                       String header = null;\r
+                       String response = null;\r
+                       \r
+                       for (int i=0; i<retryMax; i++) {\r
+                               String[] d = reqGET(reqStr,null);\r
+                               header = d[0];\r
+                               response = d[1];\r
+                               if ( header != null && response == null ) {\r
+                                       reportProgress("コネクションがリセットされました。リトライします。");\r
+                                       CommonUtils.milSleep(1000);\r
+                               }\r
+                               else {\r
+                                       break;\r
+                               }\r
+                       }\r
+                       if ( header == null || response == null ) {\r
+                               errmsg = "レコーダーが反応しません。";\r
+                               return(false);\r
+                       }\r
+               }\r
+               \r
+               if ( newRsv != r ) {\r
+                       errmsg = "TvRock番組表からの情報で内容が変更されました: "+newRsv.getStartDateTime()+"~"+newRsv.getZhh()+":"+newRsv.getZmm()+" "+newRsv.getTitle();\r
+               }\r
+               \r
+               getReserves().add(newRsv);\r
+               ReservesToFile(getReserves(), rsvedFile);\r
+\r
+               return true;\r
+       }\r
+       \r
+       \r
+       /*******************************************************************************\r
+        * 予約更新\r
+        ******************************************************************************/\r
+\r
+       @Override\r
+       public boolean UpdateRdEntry(ReserveList o, ReserveList r) {\r
+               \r
+               errmsg = "";\r
+\r
+               String chCode = ((ITEM_CH_EPGGET.equals(r.getCh_name())) ? VALUE_CH_EPGGET : cc.getCH_WEB2REC(r.getCh_name()));\r
+               if ( chCode == null) {\r
+                       errmsg = ERRID+"Web番組表の放送局名「"+r.getCh_name()+"」をCHコードに変換できません。CHコード設定を修正してください。" ;\r
+                       System.out.println(errmsg);\r
+                       return(false);\r
+               }\r
+               \r
+               System.out.println("Run: UpdateRdEntry()");\r
+               \r
+               if ( ! r.getRec_audio().equals(o.getRec_audio()) ) {\r
+                       errmsg = MSGID+"予約方式の変更はできません。旧予約方式のまま更新しました: 旧="+o.getRec_audio()+" 新="+r.getRec_audio();\r
+                       r.setRec_audio(o.getRec_audio());\r
+               }\r
+               if ( (r.getContentId() == null || r.getContentId().length() == 0) && r.getPursues() ) {\r
+                       System.out.println(MSGID+"プログラム予約では次の指定はできないので規定値に変更しました:番組追従=なし");\r
+                       r.setPursues(false);\r
+               }\r
+\r
+               // おまじない\r
+               Authenticator.setDefault(new MyAuthenticator(getUser(), getPasswd()));\r
+               \r
+               // 予約パターンID\r
+               // 次回予定日\r
+               // 録画長\r
+               // 開始日時・終了日時\r
+               r.setRec_pattern_id(getRec_pattern_Id(r.getRec_pattern()));\r
+               r.setRec_nextdate(CommonUtils.getNextDate(r));\r
+               r.setRec_min(CommonUtils.getRecMin(r.getAhh(),r.getAmm(),r.getZhh(),r.getZmm()));\r
+               getStartEndDateTime(r);\r
+               \r
+               // CHコード入れ忘れてたよ!\r
+               r.setChannel(chCode);\r
+               \r
+               if ( r.getRec_audio().equals(ITEM_REC_TYPE_EPG) && r.getRec_pattern_id() != 11 ) {\r
+                       errmsg = ERRID+"EPG予約に繰り返し指定はできない: "+r.getRec_pattern();\r
+                       return false;\r
+               }\r
+               \r
+               // タイトルは200文字まで\r
+               r.setTitle(CommonUtils.substringrb(r.getTitle(), 200));\r
+               \r
+               // 番組詳細は200文字まで(3.7.10β)\r
+               r.setDetail(CommonUtils.substringrb(r.getDetail(), 200));\r
+               \r
+               // TVRockへ登録する\r
+               String header;\r
+               String response;\r
+               \r
+               // 情報作成\r
+               String pstr = genPoststr(r, r.getId(), " 予約を変更 ");\r
+\r
+               // 情報送信\r
+               {\r
+                       reportProgress("send request(1/1).");\r
+                       header = response = null;\r
+                       String reqStr = "http://"+getIPAddr()+":"+getPortNo()+"/"+getUser()+"/regsch?"+pstr;\r
+                       for (int i=0; i<retryMax; i++) {\r
+                               String[] d = reqGET(reqStr,null);\r
+                               header = d[0];\r
+                               response = d[1];\r
+                               if ( header != null && response == null ) {\r
+                                       reportProgress("コネクションがリセットされました。リトライします。");\r
+                                       CommonUtils.milSleep(1000);\r
+                               }\r
+                               else {\r
+                                       break;\r
+                               }\r
+                       }\r
+                       if ( header == null || response == null ) {\r
+                               errmsg = "レコーダーが反応しません。";\r
+                               return(false);\r
+                       }\r
+               }\r
+               \r
+               // 情報置き換え\r
+               getReserves().remove(o);\r
+               getReserves().add(r);\r
+\r
+               // キャッシュに保存\r
+               ReservesToFile(getReserves(), rsvedFile);\r
+\r
+               System.out.printf("\n<<< Message from RD >>> \"%s\"\n\n", "正常に更新できました。");\r
+               return(true);\r
+       }\r
+\r
+       \r
+       /*******************************************************************************\r
+        * 予約削除\r
+        ******************************************************************************/\r
+\r
+       @Override\r
+       public ReserveList RemoveRdEntry(String delid) {\r
+               \r
+               System.out.println("Run: RemoveRdEntry()");\r
+               \r
+               errmsg = "";\r
+\r
+               // おまじない\r
+               Authenticator.setDefault(new MyAuthenticator(getUser(), getPasswd()));\r
+\r
+               // 削除対象を探す\r
+               ReserveList rx = null;\r
+               for (  ReserveList reserve : getReserves() )  {\r
+                       if (reserve.getId().equals(delid)) {\r
+                               rx = reserve;\r
+                               break;\r
+                       }\r
+               }\r
+               if (rx == null) {\r
+                       return(null);\r
+               }\r
+               \r
+               // TVRockから削除する\r
+               String header;\r
+               String response;\r
+\r
+               /*\r
+               // 情報作成\r
+               StringBuilder sb = new StringBuilder();\r
+               try {\r
+                       sb.append("i="+delid+"&");\r
+                       sb.append("submit="+URLEncoder.encode(" 予約を削除 ",thisEncoding));   // EOL\r
+               } catch (UnsupportedEncodingException e) {\r
+                       // TODO Auto-generated catch block\r
+                       e.printStackTrace();\r
+               }\r
+               String pstr = sb.toString();\r
+\r
+               // 情報送信\r
+               {               \r
+                       reportProgress("send request(1/2).");\r
+                       String reqStr = "http://"+getIPAddr()+":"+getPortNo()+"/"+getUser()+"/regsch?"+pstr;\r
+                       String[] d = reqGET(reqStr,null);\r
+                       header = d[0];\r
+                       response = d[1];\r
+                       if ( header != null && response == null ) {\r
+                               reportProgress("コネクションがリセットされました。リトライします。");\r
+                               String[] d2 = reqGET(reqStr,null);\r
+                               header = d2[0];\r
+                               response = d2[1];\r
+                       }\r
+                       if ( response == null ) {\r
+                               errmsg = "レコーダーが反応しません。";\r
+                               return(null);\r
+                       }\r
+               }\r
+                */\r
+\r
+               // はい。\r
+               StringBuilder sb = new StringBuilder();\r
+               try {\r
+                       sb.append("i="+delid+"&");\r
+                       sb.append("submit="+URLEncoder.encode("はい",thisEncoding)+"&");\r
+               } catch (UnsupportedEncodingException e) {\r
+                       // TODO Auto-generated catch block\r
+                       e.printStackTrace();\r
+               }\r
+               String pstr = sb.toString();\r
+               pstr = pstr.substring(0,pstr.length()-1);\r
+               {               \r
+                       reportProgress("send request(1/1).");\r
+                       header = response = null;\r
+                       String reqStr = "http://"+getIPAddr()+":"+getPortNo()+"/"+getUser()+"/regsch?"+pstr;\r
+                       for (int i=0; i<retryMax; i++) {\r
+                               String[] d = reqGET(reqStr,null);\r
+                               header = d[0];\r
+                               response = d[1];\r
+                               if ( header != null && response == null ) {\r
+                                       reportProgress("コネクションがリセットされました。リトライします。");\r
+                                       CommonUtils.milSleep(1000);\r
+                               }\r
+                               else {\r
+                                       break;\r
+                               }\r
+                       }\r
+                       if ( header == null || response == null ) {\r
+                               errmsg = "レコーダーが反応しません。";\r
+                               return(null);\r
+                       }\r
+               }\r
+               \r
+               // 予約リストを更新\r
+               getReserves().remove(rx);\r
+               \r
+               // キャッシュに保存\r
+               ReservesToFile(getReserves(), rsvedFile);\r
+               \r
+               System.out.printf("\n<<< Message from RD >>> \"%s\"\n\n", "正常に削除できました。");\r
+               return(rx);\r
+       }\r
+       \r
+       /* ここまで */\r
+       \r
+       \r
+       \r
+\r
+       /*******************************************************************************\r
+        * 非公開メソッド\r
+        ******************************************************************************/\r
+       \r
+       /* 個別コード-ここから最後まで */\r
+\r
+       @Override\r
+       protected String getNewId(String response) {\r
+               String newid = null;\r
+               Matcher ma = Pattern.compile("<a href=\"reg\\?i=([^\"]+?)\">").matcher(response);\r
+               while (ma.find()) {\r
+\r
+                       String idtmp = ma.group(1);\r
+                       \r
+                       boolean flag = true;\r
+                       for (ReserveList rx : getReserves()) {\r
+                               if (rx.getId().equals(idtmp)) {\r
+                                       flag = false;\r
+                                       break;\r
+                               }\r
+                       }\r
+                       if (flag == true) {\r
+                               newid = idtmp;\r
+                               break;\r
+                       }\r
+               }\r
+               return(newid);\r
+       }\r
+\r
+       private String genPoststr(ReserveList r, String rsvId, String cmd) {\r
+               StringBuilder sb = new StringBuilder();\r
+               try {\r
+                       sb.append("title="+URLEncoder.encode(r.getTitle().replaceAll("[\\r\\n]", ""),thisEncoding)+"&");\r
+                       sb.append("subtitle="+URLEncoder.encode(r.getDetail().replaceAll("[\\r\\n]", ""),thisEncoding)+"&");\r
+                       String chCode = (ITEM_CH_EPGGET.equals(r.getCh_name()) ? VALUE_CH_EPGGET : cc.getCH_WEB2CODE(r.getCh_name()));\r
+                       if (chCode.indexOf(":") >= 0) {\r
+                               sb.append("station="+URLEncoder.encode(chCode.substring(chCode.indexOf(":")+1),thisEncoding)+"&");\r
+                       }\r
+                       else {\r
+                               sb.append("station="+URLEncoder.encode(chCode,thisEncoding)+"&");\r
+                       }\r
+                       Matcher ma = Pattern.compile("^(\\d\\d\\d\\d)/(\\d\\d)/(\\d\\d)").matcher(r.getRec_nextdate());\r
+                       if (ma.find()) {\r
+                               sb.append("year="+Integer.valueOf(ma.group(1))+"&");\r
+                               sb.append("mon="+Integer.valueOf(ma.group(2))+"&");\r
+                               sb.append("day="+Integer.valueOf(ma.group(3))+"&");\r
+                       }\r
+                       sb.append("shour="+Integer.valueOf(r.getAhh())+"&");\r
+                       sb.append("smin="+Integer.valueOf(r.getAmm())+"&");\r
+                       sb.append("ehour="+Integer.valueOf(r.getZhh())+"&");\r
+                       sb.append("emin="+Integer.valueOf(r.getZmm())+"&");\r
+                       if ( ! r.getPursues() ) {\r
+                               // 時間追従なし\r
+                               if (r.getRec_pattern_id() < 7) {\r
+                                       // 毎*曜日\r
+                                       sb.append("repeat=2&");\r
+                               }\r
+                               else if (r.getRec_pattern_id() < 10) {\r
+                                       // 帯予約(月~木・金・土)\r
+                                       sb.append("repeat=4&");\r
+                                       int d = ((CommonUtils.isLateNight(r.getAhh()))?(1):(0));\r
+                                       for (int c=1; c <= r.getRec_pattern_id()-3; c++) {\r
+                                               sb.append(String.format("repw%d=true&", (c+d)%7+1));\r
+                                       }\r
+                               }\r
+                               else if (r.getRec_pattern_id() == 10) {\r
+                                       // 毎日\r
+                                       sb.append("repeat=1&");\r
+                               }\r
+                               else {\r
+                                       // 単日\r
+                                       sb.append("repeat=0&");\r
+                               }\r
+                       }\r
+                       else {\r
+                               // 時間追従あり\r
+                               sb.append("epgflw=true&");\r
+                       }\r
+                       if (r.getExec()) {\r
+                               sb.append("valid=true&");\r
+                       }\r
+                       if (aspect.size() > 0) {\r
+                               sb.append("reconly=true&");\r
+                               sb.append("watchonly=&");\r
+                               sb.append("extmd="+text2value(dvdcompat,r.getRec_dvdcompat())+"&");\r
+                               sb.append("idle="+text2value(xchapter,r.getRec_xchapter())+"&");\r
+                               sb.append("ready="+text2value(mschapter,r.getRec_mschapter())+"&");\r
+                               sb.append("tale="+text2value(mvchapter,r.getRec_mvchapter())+"&");\r
+                               sb.append("cname="+text2value(lvoice,r.getRec_lvoice())+"&");\r
+                               sb.append("cuscom="+text2value(aspect,r.getRec_aspect())+"&");\r
+                               sb.append("pri="+text2value(bvperf,r.getRec_bvperf())+"&");\r
+                       }\r
+                       else {\r
+                               // 後方互換\r
+                               for (TextValueSet rd : recDefaults) {\r
+                                       sb.append(rd.getText()+"="+rd.getValue()+"&");\r
+                               }\r
+                       }\r
+                       sb.append("appexit=true&");\r
+                       sb.append("coop=true&");\r
+                       //sb.append("cname="+getIPAddr()+"&");\r
+                       for ( TextValueSet t : encoder ) {\r
+                               if (t.getText().equals(r.getTuner())) {\r
+                                       sb.append("devno="+t.getValue()+"&");\r
+                               }\r
+                       }\r
+                       sb.append("i="+rsvId+"&");\r
+                       if (rsvId.equals("0")) {\r
+                               sb.append("lei=-1&");\r
+                       }\r
+                       sb.append("submit="+URLEncoder.encode(cmd,thisEncoding));       // EOL\r
+               } catch (UnsupportedEncodingException e) {\r
+                       // TODO Auto-generated catch block\r
+                       e.printStackTrace();\r
+               }\r
+               \r
+               return sb.toString();\r
+       }\r
+\r
+       //\r
+       private boolean isModified(ReserveList o, ReserveList r) {\r
+               if ( ! r.getRec_pattern().equals(o.getRec_pattern()) ||\r
+                               ! r.getAhh().equals(o.getAhh()) ||\r
+                               ! r.getAmm().equals(o.getAmm()) ||\r
+                               ! r.getZhh().equals(o.getZhh()) ||\r
+                               ! r.getZmm().equals(o.getZmm()) ||\r
+                               ! r.getTitle().equals(o.getTitle())\r
+                               ) {\r
+                       return true;\r
+               }\r
+               return false;\r
+       }\r
+       \r
+       // 既存ユーザが混乱するのでデフォルトはプログラム予約\r
+       private void setSettingRecType(ArrayList<TextValueSet> tvs) {\r
+               tvs.clear();\r
+               add2tvs(tvs,ITEM_REC_TYPE_PROG,VALUE_REC_TYPE_PROG);\r
+               add2tvs(tvs,ITEM_REC_TYPE_EPG,VALUE_REC_TYPE_EPG);\r
+       }\r
+       \r
+       //\r
+       private void setSettingBvperf(ArrayList<TextValueSet> tvs) {\r
+               tvs.clear();\r
+               for (int i=-1; i<100; i++) {\r
+                       TextValueSet t = new TextValueSet();\r
+                       t.setText(String.valueOf(i));\r
+                       t.setValue(String.valueOf(i));\r
+                       tvs.add(t);\r
+               }\r
+       }\r
+       private void setSettigRecDefaults(ArrayList<TextValueSet> tvs, String[] keys, String res) {\r
+               tvs.clear();\r
+               for (String key : keys) {\r
+                       if (key.equals("watchonly") || key.equals("reconly")) {\r
+                               Matcher mb = Pattern.compile("<input type=\"checkbox\" name=\""+key+"\"\\s*value=\"([^\"]+?)\"\\s*checked>").matcher(res);\r
+                               if (mb.find()) {\r
+                                       TextValueSet t = new TextValueSet();\r
+                                       t.setText(key);\r
+                                       t.setValue(mb.group(1));\r
+                                       tvs.add(t);\r
+                               }\r
+                       }\r
+                       else {\r
+                               Matcher mb = Pattern.compile("<select name=\""+key+"\">(.+?)</select>").matcher(res);\r
+                               if (mb.find()) {\r
+                                       Matcher mc = Pattern.compile("<option value=\"([^\"]+?)\"\\s*selected>").matcher(mb.group(1));\r
+                                       if (mc.find()) {\r
+                                               TextValueSet t = new TextValueSet();\r
+                                               t.setText(key);\r
+                                               t.setValue(mc.group(1));\r
+                                               tvs.add(t);\r
+                                       }\r
+                               }\r
+                       }\r
+               }\r
+       }\r
+       //\r
+       protected void setSettingEtc(ArrayList<TextValueSet> tvs, String key, int typ, String res) {\r
+               tvs.clear();\r
+               Matcher mb = Pattern.compile("<select name=\""+key+"\">(.+?)</select>").matcher(res);\r
+               if (mb.find()) {\r
+                       Matcher mc = Pattern.compile("<option value=\"([^\"]*?)\"(\\s*selected\\s*)?>(.*?)</option>").matcher(mb.group(1));\r
+                       while (mc.find()) {\r
+                               //if ( mc.group(1).length() == 0 || mc.group(3).length() == 0 ) {\r
+                               if ( mc.group(3).length() == 0 ) {\r
+                                       continue;\r
+                               }\r
+                               TextValueSet t = new TextValueSet();\r
+                               t.setText(mc.group(3));\r
+                               t.setValue(mc.group(1));\r
+                               if (mc.group(2) != null) {\r
+                                       t.setDefval(true);\r
+                               }\r
+                               else {\r
+                                       t.setDefval(false);\r
+                               }\r
+                               tvs.add(t);\r
+                       }\r
+               }\r
+       }\r
+}\r
diff --git a/TinyBannavi/src/tainavi/PlugIn_RecRD_X5.java b/TinyBannavi/src/tainavi/PlugIn_RecRD_X5.java
new file mode 100644 (file)
index 0000000..608f093
--- /dev/null
@@ -0,0 +1,1009 @@
+package tainavi;\r
+\r
+import java.io.File;\r
+import java.io.UnsupportedEncodingException;\r
+import java.net.Authenticator;\r
+import java.net.URLEncoder;\r
+import java.util.ArrayList;\r
+import java.util.Hashtable;\r
+import java.util.regex.Matcher;\r
+import java.util.regex.Pattern;\r
+\r
+/**\r
+ * RD-X5だよー\r
+ */\r
+public class PlugIn_RecRD_X5 extends HDDRecorderUtils implements HDDRecorder,Cloneable {\r
+\r
+       public PlugIn_RecRD_X5 clone() {\r
+               return (PlugIn_RecRD_X5) super.clone();\r
+       }\r
+       \r
+       private static final String thisEncoding = "MS932";\r
+\r
+       /*******************************************************************************\r
+        * 種族の特性\r
+        ******************************************************************************/\r
+       \r
+       @Override\r
+       public String getRecorderId() { return "RD-X5"; }\r
+       @Override\r
+       public RecType getType() { return RecType.RECORDER; }\r
+\r
+       // chvalueをつかってもいーよ\r
+       @Override\r
+       public boolean isChValueAvailable() { return true; }\r
+       // CHコードは入力しなくていい\r
+       @Override\r
+       public boolean isChCodeNeeded() { return false; }\r
+       \r
+       /*******************************************************************************\r
+        * 予約ダイアログなどのテキストのオーバーライド\r
+        ******************************************************************************/\r
+       \r
+       // 予約ダイアログのラベル\r
+       public String getLabel_XChapter() { return "無音部分チャプタ分割"; }\r
+       public String getLabel_MsChapter() { return "DVD時チャプタ分割"; }\r
+       public String getLabel_MvChapter() { return "音多連動チャプタ分割"; }\r
+       public String getLabel_DVDCompat() { return "DVD互換モード"; }\r
+       public String getLabel_Aspect() { return "DVD記録時画面比"; }\r
+       public String getLabel_BVperf() { return "高レート節約"; }\r
+       public String getLabel_LVoice() { return "ライン音声選択"; }\r
+       \r
+       /*******************************************************************************\r
+        * 定数\r
+        ******************************************************************************/\r
+       \r
+       // ログ関連\r
+       \r
+       private final String MSGID = "["+getRecorderId()+"] ";\r
+       private final String ERRID = "[ERROR]"+MSGID;\r
+       private final String DBGID = "[DEBUG]"+MSGID;\r
+       \r
+       /*******************************************************************************\r
+        * CHコード設定、エラーメッセージ\r
+        ******************************************************************************/\r
+       \r
+       public ChannelCode getChCode() {\r
+               return cc;\r
+       }\r
+       \r
+       private ChannelCode cc = new ChannelCode(getRecorderId());\r
+       \r
+       public String getErrmsg() {\r
+               return(errmsg.replaceAll("\\\\r\\\\n", ""));\r
+       }\r
+       \r
+       protected void setErrmsg(String s) { errmsg = s; }\r
+       \r
+       private String errmsg = "";\r
+\r
+       /*******************************************************************************\r
+        * 部品\r
+        ******************************************************************************/\r
+       \r
+       protected void setRsvedFile(String s) { rsvedFile = s; }\r
+       protected String getRsvedFile() { return rsvedFile; }\r
+\r
+       private String rsvedFile = "";\r
+       \r
+       /*******************************************************************************\r
+        * コンストラクタ\r
+        ******************************************************************************/\r
+       \r
+       // ないよ\r
+       \r
+       /*******************************************************************************\r
+        * チャンネルリモコン機能\r
+        ******************************************************************************/\r
+       \r
+       public boolean ChangeChannel(String Channel) {\r
+               return false;\r
+       }\r
+       \r
+\r
+       /*******************************************************************************\r
+        * レコーダーから予約一覧を取得する\r
+        ******************************************************************************/\r
+\r
+       public boolean GetRdReserve(boolean force)\r
+       {\r
+               System.out.println("Run: GetRdReserve("+force+")");\r
+               \r
+               errmsg = "";\r
+               \r
+               //\r
+               rsvedFile = "env/reserved."+getIPAddr()+"_"+getPortNo()+"_"+getRecorderId()+".xml";\r
+               String vrateTFile = "env/videorate."+getIPAddr()+"_"+getPortNo()+"_"+getRecorderId()+".xml";\r
+               String arateTFile = "env/audiorate."+getIPAddr()+"_"+getPortNo()+"_"+getRecorderId()+".xml";\r
+               String folderTFile = "env/folders."+getIPAddr()+"_"+getPortNo()+"_"+getRecorderId()+".xml";\r
+               String encoderTFile = "env/encoders."+getIPAddr()+"_"+getPortNo()+"_"+getRecorderId()+".xml";\r
+               String dvdcompatTFile = "env/dvdcompat."+getIPAddr()+"_"+getPortNo()+"_"+getRecorderId()+".xml";\r
+               String deviceTFile = "env/device."+getIPAddr()+"_"+getPortNo()+"_"+getRecorderId()+".xml";\r
+               String xChapterTFile = "env/xchapter."+getIPAddr()+"_"+getPortNo()+"_"+getRecorderId()+".xml";\r
+               String msChapterTFile = "env/mschapter."+getIPAddr()+"_"+getPortNo()+"_"+getRecorderId()+".xml";\r
+               String mvChapterTFile = "env/mvchapter."+getIPAddr()+"_"+getPortNo()+"_"+getRecorderId()+".xml";\r
+               String chValueTFile = "env/chvalue."+getIPAddr()+"_"+getPortNo()+"_"+getRecorderId()+".xml";\r
+               String aspectTFile = "env/aspect."+getIPAddr()+"_"+getPortNo()+"_"+getRecorderId()+".xml";\r
+               String bvperfTFile = "env/bvperf."+getIPAddr()+"_"+getPortNo()+"_"+getRecorderId()+".xml";\r
+               String lvoiceTFile = "env/lvoice."+getIPAddr()+"_"+getPortNo()+"_"+getRecorderId()+".xml";\r
+               \r
+\r
+               File f = new File(rsvedFile);\r
+               if ( force == false && f.exists()) {\r
+                       // キャッシュから読み出し(録画設定ほか)\r
+                       vrate = TVSload(vrateTFile);\r
+                       arate = TVSload(arateTFile);\r
+                       folder = TVSload(folderTFile);\r
+                       encoder = TVSload(encoderTFile);\r
+                       dvdcompat = TVSload(dvdcompatTFile);\r
+                       device = TVSload(deviceTFile);\r
+                       \r
+                       // 自動チャプタ関連\r
+                       xchapter = TVSload(xChapterTFile);\r
+                       mschapter = TVSload(msChapterTFile);\r
+                       mvchapter = TVSload(mvChapterTFile);\r
+                       \r
+                       // その他\r
+                       aspect = TVSload(aspectTFile);\r
+                       bvperf = TVSload(bvperfTFile);\r
+                       lvoice = TVSload(lvoiceTFile);\r
+                       \r
+                       // チャンネルコードバリュー\r
+                       chvalue = TVSload(chValueTFile);\r
+                       \r
+                       // キャッシュから読み出し(予約一覧)\r
+                       setReservesV1(ReservesFromFile(rsvedFile));\r
+                       if (getReserves().size() > 0) {\r
+                               System.out.println("+read from="+rsvedFile);\r
+                       }\r
+                       \r
+                       // なぜか設定ファイルが空になっている場合があるので、その際は再取得する\r
+                       if (vrate.size()>0 && arate.size()>0 &&\r
+                                       folder.size()>0 && encoder.size()>0 &&\r
+                                       xchapter.size()>0 && mschapter.size()>0 && mvchapter.size()>0) {\r
+                               return(true);\r
+                       }\r
+               }\r
+\r
+               // おまじない\r
+               Authenticator.setDefault(new MyAuthenticator(getUser(), getPasswd()));\r
+               \r
+               // RDから予約一覧を取り出す\r
+               Matcher ma = null;\r
+               String idx = "";\r
+               String response;\r
+               {\r
+                       reportProgress("get reserved list(1/4).");\r
+                       String[] d = reqGET("http://"+getIPAddr()+":"+getPortNo()+"/program/b_prgrm.htm",null);\r
+                       response = d[1];\r
+               }\r
+               ma = Pattern.compile("/program/(\\d+?)/program.htm").matcher(response);\r
+               if (ma.find()) {\r
+                       idx = ma.group(1);\r
+                       reportProgress("get reserved list(2/4).");\r
+                       String[] d = reqGET("http://"+getIPAddr()+":"+getPortNo()+"/program/"+idx+"/program.htm",null);\r
+                       response = d[1];\r
+               }\r
+               \r
+               // (1)録画設定の取得\r
+               {\r
+                       // ハングさせないためのおまじない\r
+                       reportProgress("get reserved list(3/4).");\r
+                       reqGET("http://"+getIPAddr()+":"+getPortNo()+"/program/"+idx+"/b_proginfo.htm?0",null);\r
+               }\r
+               {\r
+                       reportProgress("get reserved list(4/4).");\r
+                       String[] d = reqGET("http://"+getIPAddr()+":"+getPortNo()+"/program/"+idx+"/proginfo.htm",null);\r
+                       String res = d[1];\r
+                       \r
+                       // (1-1)画質設定\r
+                       setSettingEtc(vrate, "\"vrate\"", 1, res, vrateTFile);\r
+                       \r
+                       // (1-2)音質設定\r
+                       setSettingEtc(arate, "amode", 0, res, arateTFile);\r
+                       \r
+                       // (1-3)フォルダ一覧\r
+                       setSettingFolder(folder, res, folderTFile);\r
+                       \r
+                       // (1-4)エンコーダ\r
+                       setSettingEncoder(encoder, res, encoderTFile);\r
+                       \r
+                       // (1-5)DVD互換モード\r
+                       setSettingEtc(dvdcompat, "dvdr", 1, res, dvdcompatTFile);\r
+\r
+                       // (1-6)記録先デバイス\r
+                       setSettingEtc(device, "disc", 0, res, deviceTFile);\r
+\r
+                       // (1-7)自動チャプタ関連\r
+                       setSettingEtc(xchapter, "bAutoChapter", 0, res, xChapterTFile);\r
+                       \r
+                       setSettingEtc(mschapter, "bDvdAutoChapter", 0, res, msChapterTFile);\r
+\r
+                       setSettingEtc(mvchapter, "bAudioAutoChapter", 0, res, mvChapterTFile);\r
+                       \r
+                       // (1-8)チャンネルコードバリュー\r
+                       setSettingEtc(chvalue, "channel", 1, res, chValueTFile);\r
+                       \r
+                       // (1-11)記録時画面比\r
+                       setSettingEtc(aspect, "aspect", 0, res, aspectTFile);\r
+                       \r
+                       // (1-12)高レート節約\r
+                       setSettingEtc(bvperf, "bVPerform", 0, res, bvperfTFile);\r
+                       \r
+                       // (1-13)ライン音声選択\r
+                       setSettingEtc(lvoice, "lVoice", 0, res, lvoiceTFile);\r
+               }\r
+               \r
+               // (2)予約一覧データの分析\r
+               setReservesV1(GetRdReservedList(response));\r
+               \r
+               // 取得した情報の表示\r
+               System.out.println("---Reserved List Start---");\r
+               for ( int i = 0; i<getReserves().size(); i++ ) {\r
+                       // 詳細情報の取得\r
+                       ReserveList e = getReserves().get(i);\r
+                       System.out.println(String.format("[%s] %s\t%s\t%s\t%s:%s\t%s:%s\t%sm\t%s\t%s\t%s\t%s",\r
+                                       (i+1), e.getId(), e.getRec_pattern(), e.getRec_nextdate(), e.getAhh(), e.getAmm(), e.getZhh(),  e.getZmm(),     e.getRec_min(), e.getRec_mode(), e.getTitle(), e.getChannel(), e.getCh_name()));\r
+               }\r
+               System.out.println("---Reserved List End---");\r
+               \r
+               // キャッシュに保存\r
+               ReservesToFile(getReserves(), rsvedFile);\r
+               \r
+               return(true);\r
+       }\r
+       \r
+       /*******************************************************************************\r
+        * 新規予約\r
+        ******************************************************************************/\r
+\r
+       public boolean PostRdEntry(ReserveList r)\r
+       {\r
+               //\r
+               if (getChCode().getCH_WEB2CODE(r.getCh_name()) == null) {\r
+                       errmsg = ERRID+"【警告】Web番組表の放送局名「"+r.getCh_name()+"」をCHコードに変換できません。CHコード設定を修正してください。" ;\r
+                       System.out.println(errmsg);\r
+                       return(false);\r
+               }\r
+               \r
+               System.out.println("Run: PostRdEntry("+r.getTitle()+")");\r
+               \r
+               errmsg = "";\r
+\r
+               // おまじない\r
+               Authenticator.setDefault(new MyAuthenticator(getUser(), getPasswd()));\r
+\r
+               // RDから予約一覧を取り出す\r
+               Matcher ma = null;\r
+               String idx = "";\r
+               String response;\r
+               {\r
+                       reportProgress("get reserved list(1/5).");\r
+                       String[] d = reqGET("http://"+getIPAddr()+":"+getPortNo()+"/program/b_prgrm.htm",null);\r
+                       response = d[1];\r
+               }\r
+               ma = Pattern.compile("/program/(\\d+?)/program.htm").matcher(response);\r
+               if (ma.find()) {\r
+                       idx = ma.group(1);\r
+                       reportProgress("get reserved list(2/5).");\r
+                       String[] d = reqGET("http://"+getIPAddr()+":"+getPortNo()+"/program/"+idx+"/program.htm",null);\r
+                       response = d[1];\r
+               }\r
+               // 新規予約の行番号を取得する\r
+               int lineno = 0;\r
+               ma = Pattern.compile("\"b_proginfo.htm\\?(\\d+?)\"").matcher(response);\r
+               while (ma.find()) {\r
+                       lineno = Integer.valueOf(ma.group(1));\r
+               }\r
+               //r.setNo(no);\r
+               \r
+               // RDに新規登録要請\r
+               {\r
+                       // ハングさせないためのおまじない\r
+                       reportProgress("get program.(3/5)");\r
+                       reqGET("http://"+getIPAddr()+":"+getPortNo()+"/program/"+idx+"/b_proginfo.htm?"+lineno,null);\r
+                       \r
+                       reportProgress("get program.(4/5)");\r
+                       reqGET("http://"+getIPAddr()+":"+getPortNo()+"/program/"+idx+"/proginfo.htm",null);\r
+               }\r
+\r
+               // POSTデータを変換する\r
+               Hashtable<String, String> pdat = modPostdata(r);\r
+\r
+               // RDへの情報作成\r
+               String pstr = joinPoststr(pdat);\r
+\r
+               // RDへ情報送信\r
+               {               \r
+                       reportProgress("send request.(5/5)");\r
+                       String[] d = reqPOST("http://"+getIPAddr()+":"+getPortNo()+"/program/"+idx+"/entry.htm", pstr, null);\r
+                       response = d[1];\r
+               }\r
+\r
+               //System.out.println("#"+response);\r
+\r
+               // 登録結果の確認\r
+               ma = Pattern.compile("msg=\"(.+?)\"").matcher(response);\r
+               if ( ma.find() ) {\r
+                       errmsg = ma.group(1);\r
+                       System.out.printf("\n<<< Message from RD >>> \"%s\"\n\n", errmsg);\r
+                       \r
+                       ma = Pattern.compile("(正常に登録できました。|予約時間が一部重複しています。|現在のHDD残量では入りません。|画質を.*に修正しました。)").matcher(errmsg);\r
+                       if ( ! ma.find()) {\r
+                               return false;\r
+                       }\r
+               }\r
+               \r
+               /*\r
+                * 予約情報の調整 \r
+                */\r
+               \r
+               // キャッシュ上の予約情報を更新する(ライン入力放送局名引継対応)(2.6β)\r
+               getReserves().add(r);\r
+               \r
+               // 新しい予約一覧を取得する\r
+               {\r
+                       reportProgress("get identifier(6/7).");\r
+                       String[] d = reqGET("http://"+getIPAddr()+":"+getPortNo()+"/program/b_prgrm.htm",null);\r
+                       response = d[1];\r
+               }\r
+               ma = Pattern.compile("/program/(\\d+?)/program.htm").matcher(response);\r
+               if (ma.find()) {\r
+                       idx = ma.group(1);\r
+                       reportProgress("get identifier(7/7).");\r
+                       String[] d = reqGET("http://"+getIPAddr()+":"+getPortNo()+"/program/"+idx+"/program.htm",null);\r
+                       response = d[1];\r
+               }\r
+               \r
+               setReservesV1(GetRdReservedList(response));\r
+               \r
+               // キャッシュに保存\r
+               ReservesToFile(getReserves(), rsvedFile);\r
+\r
+               System.out.printf("\n<<< Message from RD >>> \"%s\"\n\n", "正常に登録できました。");\r
+               \r
+               return(true);\r
+       }\r
+\r
+       \r
+       /*******************************************************************************\r
+        * 予約更新\r
+        ******************************************************************************/\r
+\r
+       public boolean UpdateRdEntry(ReserveList o, ReserveList r) {\r
+               //\r
+               if (getChCode().getCH_WEB2CODE(r.getCh_name()) == null) {\r
+                       errmsg = ERRID+"【警告】Web番組表の放送局名「"+r.getCh_name()+"」をCHコードに変換できません。CHコード設定を修正してください。" ;\r
+                       System.out.println(errmsg);\r
+                       return(false);\r
+               }\r
+               \r
+               System.out.println("Run: UpdateRdEntry()");\r
+               \r
+               errmsg = "";\r
+\r
+               // おまじない\r
+               Authenticator.setDefault(new MyAuthenticator(getUser(), getPasswd()));\r
+\r
+               /*\r
+               // 更新対象を探す(内部)\r
+               ReserveList rx = null;\r
+               for ( ReserveList reserve : getReserves() )  {\r
+                       if (reserve.getId().equals(o.getId())) {\r
+                               rx = reserve;\r
+                               break;\r
+                       }\r
+               }\r
+               if (rx == null) {\r
+                       errmsg = "キャッシュ上に操作対象が見つかりません。";\r
+                       return(false);\r
+               }\r
+               */\r
+\r
+               // 更新準備\r
+               Matcher ma = null;\r
+               String idx = "";\r
+               String response;\r
+               {\r
+                       reportProgress("get identifier(1/7).");\r
+                       String[] d = reqGET("http://"+getIPAddr()+":"+getPortNo()+"/program/b_prgrm.htm",null);\r
+                       response = d[1];\r
+               }\r
+               ma = Pattern.compile("/program/(\\d+?)/program.htm").matcher(response);\r
+               if (ma.find()) {\r
+                       idx = ma.group(1);\r
+                       reportProgress("get identifier(2/7).");\r
+                       String[] d = reqGET("http://"+getIPAddr()+":"+getPortNo()+"/program/"+idx+"/program.htm",null);\r
+                       response = d[1];\r
+               }\r
+               \r
+               // 更新対象を探す(外部)\r
+               String id = null;\r
+               for ( ReserveList cr : GetRdReservedList(response) ) {\r
+                       if (matchReserveV1(cr, o)) {\r
+                               System.out.println("次のエントリを: "+cr.getTitle()+"; "+cr.getChannel()+"; "+cr.getRec_pattern()+"; "+cr.getAhh()+":"+cr.getAmm());\r
+                               id = cr.getId();\r
+                               break;\r
+                       }\r
+               }\r
+               if (id == null) {\r
+                       errmsg = ERRID+"レコーダ上に操作対象が見つかりません。";\r
+                       return(false);\r
+               }\r
+               System.out.println("次の情報に更新します: "+r.getTitle()+"; "+r.getChannel()+"; "+r.getRec_pattern()+"; "+r.getAhh()+":"+r.getAmm());\r
+\r
+               // RDに更新要請\r
+               {\r
+                       // ハングさせないためのおまじない\r
+                       reportProgress("get program.(3/7)");\r
+                       reqGET("http://"+getIPAddr()+":"+getPortNo()+"/program/"+idx+"/b_proginfo.htm?"+id,null);\r
+                       \r
+                       reportProgress("get program.(4/7)");\r
+                       reqGET("http://"+getIPAddr()+":"+getPortNo()+"/program/"+idx+"/proginfo.htm",null);\r
+               }\r
+\r
+               // POSTデータを変換する\r
+               Hashtable<String, String> pdat = modPostdata(r);\r
+               \r
+               // RDへの情報作成\r
+               String pstr = joinPoststr(pdat);\r
+\r
+               // RDへ情報送信\r
+               {               \r
+                       reportProgress("send request.(5/7)");\r
+                       String[] d = reqPOST("http://"+getIPAddr()+":"+getPortNo()+"/program/"+idx+"/entry.htm", pstr, null);\r
+                       response = d[1];\r
+               }\r
+\r
+               // 更新結果の確認\r
+               ma = Pattern.compile("msg=\"(.+?)\"").matcher(response);\r
+               if ( ma.find() ) {\r
+                       errmsg = ma.group(1);\r
+                       System.out.printf("\n<<< Message from RD >>> \"%s\"\n\n", errmsg);\r
+                       \r
+                       ma = Pattern.compile("(正常に登録できました。|予約時間が一部重複しています。|現在のHDD残量では入りません。|画質を.*に修正しました。)").matcher(errmsg);\r
+                       if ( ! ma.find()) {\r
+                               return false;\r
+                       }\r
+               }\r
+\r
+               System.out.printf("\n<<< Message from RD >>> \"%s\"\n\n", "正常に更新できました。");\r
+\r
+               /*\r
+                * 予約情報の調整 \r
+                */\r
+               // キャッシュ上の予約情報を更新する(ライン入力放送局名引継対応)\r
+               getReserves().remove(o);\r
+               getReserves().add(r);\r
+               \r
+               // 新しい予約一覧を取得する\r
+               {\r
+                       reportProgress("get identifier(6/7).");\r
+                       String[] d = reqGET("http://"+getIPAddr()+":"+getPortNo()+"/program/b_prgrm.htm",null);\r
+                       response = d[1];\r
+               }\r
+               ma = Pattern.compile("/program/(\\d+?)/program.htm").matcher(response);\r
+               if (ma.find()) {\r
+                       idx = ma.group(1);\r
+                       reportProgress("get identifier(7/7).");\r
+                       String[] d = reqGET("http://"+getIPAddr()+":"+getPortNo()+"/program/"+idx+"/program.htm",null);\r
+                       response = d[1];\r
+               }\r
+\r
+               setReservesV1(GetRdReservedList(response));\r
+\r
+               // キャッシュに保存\r
+               ReservesToFile(getReserves(), rsvedFile);\r
+               \r
+               return(true);\r
+       }\r
+\r
+\r
+       /*******************************************************************************\r
+        * 予約削除\r
+        ******************************************************************************/\r
+       \r
+       public ReserveList RemoveRdEntry(String delid) {\r
+               System.out.println("Run: RemoveRdEntry()");\r
+               \r
+               errmsg = "";\r
+\r
+               // おまじない\r
+               Authenticator.setDefault(new MyAuthenticator(getUser(), getPasswd()));\r
+\r
+               // 削除対象を探す\r
+               ReserveList rx = null;\r
+               for (  ReserveList reserve : getReserves() )  {\r
+                       if (reserve.getId().equals(delid)) {\r
+                               rx = reserve;\r
+                               break;\r
+                       }\r
+               }\r
+               if (rx == null) {\r
+                       errmsg = ERRID+"キャッシュ上に操作対象が見つかりません。";\r
+                       return(null);\r
+               }\r
+\r
+               // 削除準備\r
+               Matcher ma = null;\r
+               String idx = "";\r
+               String response;\r
+               {\r
+                       reportProgress("get identifier(1/7).");\r
+                       String[] d = reqGET("http://"+getIPAddr()+":"+getPortNo()+"/program/b_prgrm.htm",null);\r
+                       response = d[1];\r
+               }\r
+               ma = Pattern.compile("/program/(\\d+?)/program.htm").matcher(response);\r
+               if (ma.find()) {\r
+                       idx = ma.group(1);\r
+                       reportProgress("get identifier(2/7).");\r
+                       String[] d = reqGET("http://"+getIPAddr()+":"+getPortNo()+"/program/"+idx+"/program.htm",null);\r
+                       response = d[1];\r
+               }\r
+               \r
+               // 削除対象を探す(外部)\r
+               String id = null;\r
+               for ( ReserveList cr : GetRdReservedList(response) ) {\r
+                       if (matchReserveV1(cr, rx)) {\r
+                               System.out.println("次のエントリを削除します: "+cr.getTitle()+"; "+cr.getChannel()+"; "+cr.getRec_pattern()+"; "+cr.getAhh()+":"+cr.getAmm());\r
+                               id = cr.getId();\r
+                               break;\r
+                       }\r
+               }\r
+               if (id == null) {\r
+                       errmsg = ERRID+"レコーダ上に操作対象が見つかりません。";\r
+                       return(null);\r
+               }\r
+\r
+               // RDに削除要請\r
+               {\r
+                       // ハングさせないためのおまじない\r
+                       reportProgress("get program.(3/7)");\r
+                       reqGET("http://"+getIPAddr()+":"+getPortNo()+"/program/"+idx+"/b_proginfo.htm?"+id,null);\r
+                       \r
+                       reportProgress("get program.(4/7)");\r
+                       reqGET("http://"+getIPAddr()+":"+getPortNo()+"/program/"+idx+"/proginfo.htm",null);\r
+               }\r
+               \r
+               {               \r
+                       reportProgress("send request.(5/7)");\r
+                       String[] d = reqGET("http://"+getIPAddr()+":"+getPortNo()+"/program/"+idx+"/delete.htm", null);\r
+                       response = d[1];\r
+               }\r
+\r
+               // 削除結果の確認\r
+               ma = Pattern.compile("msg=\"(.+?)\"").matcher(response);\r
+               if ( ma.find() ) {\r
+                       errmsg = ma.group(1);\r
+                       System.out.printf("\n<<< Message from RD >>> \"%s\"\n\n", errmsg);\r
+                       \r
+                       ma = Pattern.compile("(正しく削除できました。)").matcher(errmsg);\r
+                       if ( ! ma.find()) {\r
+                               return null;\r
+                       }\r
+               }\r
+               \r
+               /*\r
+                * 予約情報の調整 \r
+                */\r
+               // キャッシュ上の予約情報を更新する(ライン入力放送局名引継対応)\r
+               getReserves().remove(rx);\r
+       \r
+               // 新しい予約一覧を取得する\r
+               {\r
+                       reportProgress("get identifier(6/7).");\r
+                       String[] d = reqGET("http://"+getIPAddr()+":"+getPortNo()+"/program/b_prgrm.htm",null);\r
+                       response = d[1];\r
+               }\r
+               ma = Pattern.compile("/program/(\\d+?)/program.htm").matcher(response);\r
+               if (ma.find()) {\r
+                       idx = ma.group(1);\r
+                       reportProgress("get identifier(7/7).");\r
+                       String[] d = reqGET("http://"+getIPAddr()+":"+getPortNo()+"/program/"+idx+"/program.htm",null);\r
+                       response = d[1];\r
+               }\r
+\r
+               setReservesV1(GetRdReservedList(response));\r
+               \r
+               // キャッシュに保存\r
+               ReservesToFile(getReserves(), rsvedFile);\r
+               \r
+               System.out.printf("\n<<< Message from RD >>> \"%s\"\n\n", "正常に削除できました。");\r
+               \r
+               return(rx);\r
+       }\r
+\r
+       \r
+       \r
+       /* ここまで */\r
+       \r
+       \r
+       \r
+\r
+       \r
+       \r
+       /*******************************************************************************\r
+        * 非公開メソッド\r
+        ******************************************************************************/\r
+\r
+       protected String joinPoststr(Hashtable<String, String> pdat)\r
+       {\r
+               String[] pkeys = {\r
+                       "bExec",\r
+                       "title_name",\r
+                       "genre",\r
+                       "channel",\r
+                       "date",\r
+                       "start_hour",\r
+                       "start_minute",\r
+                       "end_hour",\r
+                       "end_minute",\r
+                       "enc_type",                             // for X5\r
+                       "disc",\r
+                       "vrate",\r
+                       "amode",\r
+                       "folder",                               // for X5\r
+                       "dvdr",\r
+                       "lVoice",                               // for X5\r
+                       "bVPerform",\r
+                       "aspect",\r
+                       "bAutoChapter",\r
+                       "bDvdAutoChapter",              // for X5\r
+                       "bAudioAutoChapter",    // for X5\r
+                       "detail",\r
+                       "dtv_sid",\r
+                       "dtv_nid",\r
+                       "net_link",                             // for X5\r
+                       "end_form"\r
+               };\r
+               \r
+               String pstr = "";\r
+               for ( String key : pkeys ) {\r
+                       if (pdat.get(key) == null) {\r
+                               pstr += key+"=&";\r
+                       }\r
+                       else {\r
+                               pstr += key+"="+pdat.get(key)+"&";\r
+                       }\r
+               }\r
+               pstr = pstr.substring(0, pstr.length()-1);\r
+               \r
+               return(pstr);\r
+       }\r
+       \r
+       private Hashtable<String, String> modPostdata(ReserveList r) {\r
+\r
+               Hashtable<String, String> newdat = new Hashtable<String, String>();\r
+               \r
+               // 実行するよ\r
+               newdat.put("bExec", (r.getExec())?("ON"):("OFF"));\r
+\r
+               // 予約名\r
+               try {\r
+                       newdat.put("title_name", URLEncoder.encode(r.getTitle(),thisEncoding));\r
+               } catch (UnsupportedEncodingException e1) {\r
+                       e1.printStackTrace();\r
+               }\r
+\r
+               // 予約詳細\r
+               try {\r
+                       newdat.put("detail", URLEncoder.encode(r.getDetail().replaceAll("\n", Matcher.quoteReplacement("\r\n")),thisEncoding));\r
+               } catch (UnsupportedEncodingException e) {\r
+                       e.printStackTrace();\r
+               }\r
+               \r
+               // ジャンル\r
+               newdat.put("genre", _recstr2genre(r.getRec_genre()));\r
+               \r
+               // DVD互換モード\r
+               newdat.put("dvdr", text2value(dvdcompat,r.getRec_dvdcompat()));\r
+               \r
+               // 録画チャンネル\r
+               String channel = getChCode().getCH_WEB2CODE(r.getCh_name());\r
+               newdat.put("channel", channel);\r
+               String ch_no = getChCode().getCH_CODE2REC(channel);\r
+               r.setChannel(ch_no);\r
+               \r
+               // エンコーダ\r
+               newdat.put("enc_type", text2value(encoder,r.getTuner()));\r
+               \r
+               // 録画レート\r
+               newdat.put("vrate", text2value(vrate,r.getRec_mode()));\r
+\r
+               // 録音レート\r
+               newdat.put("amode", text2value(arate,r.getRec_audio()));\r
+\r
+               // 繰り返し\r
+               Matcher ma = Pattern.compile("^\\d").matcher(r.getRec_pattern());\r
+               if (ma.find()) { \r
+                       newdat.put("date", r.getRec_pattern());\r
+               }\r
+               else {\r
+                       int i = 1;\r
+                       for ( String s : RPTPTN ) {\r
+                               if ( s.equals(r.getRec_pattern()) == true ) {\r
+                                       newdat.put("date", String.valueOf(i));\r
+                               }\r
+                               i++;\r
+                       }\r
+               }\r
+               \r
+               // 番組詳細\r
+               //newdat.put("detail", r.getDetail()); // 上書きしてた…('д`)\r
+\r
+               // フォルダ\r
+               try {\r
+                       newdat.put("folder", URLEncoder.encode(text2value(folder,r.getRec_folder()),thisEncoding));\r
+               } catch (UnsupportedEncodingException e) {\r
+                       e.printStackTrace();\r
+               }\r
+               \r
+\r
+               newdat.put("start_hour", r.getAhh());\r
+               newdat.put("start_minute", r.getAmm());\r
+               newdat.put("end_hour", r.getZhh());\r
+               newdat.put("end_minute", r.getZmm());\r
+               \r
+               newdat.put("bAutoChapter"               , text2value(xchapter, r.getRec_xchapter()));\r
+               newdat.put("bDvdAutoChapter"    , text2value(mschapter, r.getRec_mschapter()));\r
+               newdat.put("bAudioAutoChapter"  , text2value(mvchapter, r.getRec_mvchapter()));\r
+\r
+               String val;\r
+               \r
+               val = text2value(device, r.getRec_device());\r
+               newdat.put("disc", (val!=null)?(val):("0"));\r
+               \r
+               val = text2value(aspect, r.getRec_aspect());\r
+               newdat.put("aspect", (val!=null)?(val):("1"));\r
+               \r
+               val = text2value(lvoice, r.getRec_lvoice());\r
+               newdat.put("lVoice", (val!=null)?(val):("1"));\r
+               \r
+               val = text2value(bvperf, r.getRec_bvperf());\r
+               newdat.put("bVPerform", (val!=null)?(val):("0"));\r
+               \r
+               // 追加\r
+               //newdat.put("disc"                             , "0");\r
+               //newdat.put("aspect"                           , "1");\r
+               //newdat.put("lVoice"                           , "1");\r
+               //newdat.put("bVPerform"                        , "0");\r
+               //newdat.put("bAutoChapter"             , "0");\r
+               //newdat.put("bDvdAutoChapter"  , "0");\r
+               //newdat.put("bAudioAutoChapter"        , "0");\r
+               newdat.put("dtv_sid"                    , "0");\r
+               newdat.put("dtv_nid"                    , "0");\r
+               newdat.put("net_link"                   , "0");\r
+               newdat.put("end_form"                   , "0");\r
+               \r
+               return(newdat);\r
+       }\r
+\r
+       private String _recstr2genre(String genre)\r
+       {\r
+               if (genre.equals(TVProgram.ProgGenre.MOVIE.toString())) {\r
+                       return "0";\r
+               }\r
+               else if (genre.equals(TVProgram.ProgGenre.MUSIC.toString())) {\r
+                       return "1";\r
+               }\r
+               else if (genre.equals(TVProgram.ProgGenre.DORAMA.toString())) {\r
+                       return "2";\r
+               }\r
+               else if (genre.equals(TVProgram.ProgGenre.ANIME.toString())) {\r
+                       return "3";\r
+               }\r
+               else if (genre.equals(TVProgram.ProgGenre.SPORTS.toString())) {\r
+                       return "4";\r
+               }\r
+               else if (genre.equals(TVProgram.ProgGenre.DOCUMENTARY.toString())) {\r
+                       return "5";\r
+               }\r
+               else if (genre.equals(TVProgram.ProgGenre.THEATER.toString())) {\r
+                       return "6";\r
+               }\r
+               else if (genre.equals(TVProgram.ProgGenre.HOBBY.toString())) {\r
+                       return "7";\r
+               }\r
+               else if (genre.equals(TVProgram.ProgGenre.VARIETY.toString())) {\r
+                       return "8";\r
+               }\r
+               else {\r
+                       return "10";\r
+               }\r
+       }\r
+       \r
+       \r
+       \r
+       //\r
+       protected ArrayList<ReserveList> GetRdReservedList(String response) {\r
+               \r
+               System.out.println("X5's GetRdReservedList()");\r
+               \r
+               //\r
+               response = response.replaceAll("\n", "");\r
+       \r
+               ArrayList<ReserveList> newReserveList = new ArrayList<ReserveList>();\r
+               \r
+               String buf = "";\r
+               Matcher ma = Pattern.compile("<tr [^>]*?>([\\s\\S]*?)</tr>").matcher(response);\r
+               while (ma.find()) {\r
+\r
+                       // 個々のデータを取り出す\r
+                       buf = ma.group(1);\r
+\r
+                       ReserveList entry = new ReserveList();\r
+                       \r
+                       String[] d = new String[10];\r
+                       Matcher mb = Pattern.compile("<td.*?>(.*?)</td>").matcher(buf);\r
+                       for (int i=0; i<d.length; i++) {\r
+                               if ( mb.find()) {\r
+                                       d[i] = mb.group(1);\r
+                               }\r
+                               //System.out.println(i+") "+d[i]);\r
+                       }\r
+                       \r
+                       Matcher mc = Pattern.compile(">新規予約<").matcher(buf);\r
+                       if (mc.find()) {\r
+                               break;\r
+                       }\r
+                       \r
+                       // 予約ID\r
+                       entry.setId(String.valueOf(Integer.valueOf(d[0])-1));\r
+                       \r
+                       // 予約実行ON/OFF\r
+                       mb = Pattern.compile("check_off\\.gif").matcher(d[1]);\r
+                       if (mb.find()) {\r
+                               entry.setExec(false);\r
+                       }\r
+                       \r
+                       // 予約名のエスケープを解除する\r
+                       String title = CommonUtils.unEscape(d[2]).replaceAll("<[bB][rR]>","");\r
+                       mb = Pattern.compile("<[aA] .*?>(.+?)</[aA]>").matcher(title);\r
+                       if (mb.find()) title = mb.group(1);\r
+                       entry.setTitle(title);\r
+                       entry.setTitlePop(TraceProgram.replacePop(title));\r
+                       \r
+                       // エンコーダ\r
+                       mb = Pattern.compile("act_(.+?)\\.gif").matcher(d[3]);\r
+                       if (mb.find()) {\r
+                               entry.setTuner(mb.group(1));\r
+                       }\r
+                       else {\r
+                               entry.setTuner("R1");\r
+                       }\r
+                       \r
+                       // 日時\r
+                       entry.setRec_pattern(d[5]);\r
+                       entry.setRec_pattern_id(getRec_pattern_Id(entry.getRec_pattern()));\r
+                       mb = Pattern.compile("(\\d\\d):(\\d\\d).*?(\\d\\d):(\\d\\d)").matcher(d[6]);\r
+                       if (mb.find()) {\r
+                               entry.setAhh(mb.group(1));\r
+                               entry.setAmm(mb.group(2));\r
+                               entry.setZhh(mb.group(3));\r
+                               entry.setZmm(mb.group(4));\r
+                       }\r
+                       entry.setRec_nextdate(CommonUtils.getNextDate(entry));\r
+                       entry.setRec_min(CommonUtils.getRecMin(entry.getAhh(), entry.getAmm(), entry.getZhh(), entry.getZmm()));\r
+                       getStartEndDateTime(entry);\r
+                       \r
+                       entry.setChannel(d[4]);\r
+                       entry.setCh_name(getChCode().getCH_REC2WEB(d[4]));\r
+\r
+                       entry.setRec_mode(d[8]);\r
+                       entry.setRec_audio(d[9]);\r
+                       \r
+                       //entry.rec_folder = data.get();        // 予約一覧からはとれない\r
+                       //entry.rec_genre = data.get();         // 予約一覧からはとれない\r
+                       \r
+                       // 予約情報を保存\r
+                       newReserveList.add(entry);\r
+               }\r
+               \r
+               return(newReserveList);\r
+       }\r
+       \r
+       \r
+       \r
+       /*\r
+        * X5/H1/H2で共有するぜ\r
+        */\r
+       \r
+       protected boolean setSettingFolder(ArrayList<TextValueSet> tvs, String res, String savefile) {\r
+               ArrayList<TextValueSet> newtvs = new ArrayList<TextValueSet>();\r
+               Matcher mb = Pattern.compile("obj\\.folder\\[n\\]\\.text=\"(.+?)\";\\s*?obj\\.folder\\[n\\]\\.value=\"(.+?)\";").matcher(res);\r
+               while (mb.find()) {\r
+                       boolean dub = false;\r
+                       for ( TextValueSet t : newtvs ) {\r
+                               if (t.getText().equals(mb.group(1))) {\r
+                                       dub = true;\r
+                                       break;\r
+                               }\r
+                       }\r
+                       if (dub == false) {\r
+                               TextValueSet t = new TextValueSet();\r
+                               t.setText(mb.group(1));\r
+                               t.setValue(mb.group(2));\r
+                               newtvs.add(t);\r
+                       }\r
+               }\r
+               \r
+               if ( newtvs.size() == 0 ) {\r
+                       errmsg = ERRID+"レコーダ設定情報が取得できなかった: "+"folder";\r
+                       System.err.println(errmsg);\r
+                       return false;\r
+               }\r
+               \r
+               tvs.clear();\r
+               for ( TextValueSet t : newtvs ) {\r
+                       tvs.add(t);\r
+               }\r
+               TVSsave(tvs,savefile);\r
+               return true;\r
+       }\r
+       \r
+       private boolean setSettingEncoder(ArrayList<TextValueSet> tvs, String res, String savefile) {\r
+               ArrayList<TextValueSet> newtvs = new ArrayList<TextValueSet>();\r
+               Matcher mb = Pattern.compile("<input type=\"radio\" [^>]*? name=enc_type value=\"(.+?)\".*?><img src=\"/img/parts/toru_(.+?)\\.gif\">").matcher(res);\r
+               while (mb.find()) {\r
+                       TextValueSet t = new TextValueSet();\r
+                       t.setText(mb.group(2));\r
+                       t.setValue(mb.group(1));\r
+                       newtvs.add(t);\r
+               }\r
+               \r
+               if (newtvs.size() == 0) {\r
+                       errmsg = ERRID+"レコーダ設定情報が取得できなかった: "+"enc_type";\r
+                       System.err.println(errmsg);\r
+                       return false;\r
+               }\r
+\r
+               tvs.clear();\r
+               for ( TextValueSet t : newtvs ) {\r
+                       tvs.add(t);\r
+               }\r
+               TVSsave(tvs,savefile);\r
+               return true;\r
+       }\r
+\r
+       protected boolean setSettingEtc(ArrayList<TextValueSet> tvs, String key, int typ, String res, String savefile) {\r
+               ArrayList<TextValueSet> newtvs = new ArrayList<TextValueSet>();\r
+               String valExpr = "(\\d+)";\r
+               if (typ == 1) {\r
+                       valExpr = "\"(.+?)\"";\r
+               }\r
+               Matcher mb = Pattern.compile("<(SELECT|select)[^>]+?name="+key+"[^.]*?>([\\s\\S]+?)</(SELECT|select)>").matcher(res);\r
+               if (mb.find()) {\r
+                       Matcher mc = Pattern.compile("<(OPTION|option)\\s+(VALUE|value)\\s*=\\s*"+valExpr+"\\s*( selected)?>\\s*(.+?)\\s*</(OPTION|option)>").matcher(mb.group(2));\r
+                       while (mc.find()) {\r
+                               TextValueSet t = new TextValueSet();\r
+                               if (key.equals("\"vrate\"")) {\r
+                                       t.setText(mc.group(5).replaceAll(" ", ""));\r
+                               }\r
+                               else if (key.equals("channel")) {\r
+                                       t.setText(mc.group(5).replaceAll("^CH ", "CH"));\r
+                               }\r
+                               else {\r
+                                       t.setText(mc.group(5));\r
+                               }\r
+                               t.setValue(mc.group(3));\r
+                               if (mc.group(4) != null) {\r
+                                       t.setDefval(true);\r
+                               }\r
+                               newtvs.add(t);\r
+                       }\r
+               }\r
+               \r
+               if ( newtvs.size() == 0 ) {\r
+                       errmsg = ERRID+"レコーダ設定情報が取得できなかった: "+key;\r
+                       System.err.println(errmsg);\r
+                       return false;\r
+               }\r
+               \r
+               tvs.clear();\r
+               for ( TextValueSet t : newtvs ) {\r
+                       tvs.add(t);\r
+               }\r
+               TVSsave(tvs,savefile);\r
+               return true;\r
+       }\r
+}\r
diff --git a/TinyBannavi/src/tainavi/PlugIn_RecRD_X5EX.java b/TinyBannavi/src/tainavi/PlugIn_RecRD_X5EX.java
new file mode 100644 (file)
index 0000000..34b0773
--- /dev/null
@@ -0,0 +1,130 @@
+package tainavi;\r
+\r
+import java.util.ArrayList;\r
+import java.util.regex.Matcher;\r
+import java.util.regex.Pattern;\r
+\r
+\r
+/*\r
+ * \r
+ */\r
+\r
+public class PlugIn_RecRD_X5EX extends PlugIn_RecRD_X5 implements HDDRecorder,Cloneable {\r
+       \r
+       @Override\r
+       public PlugIn_RecRD_X5EX clone() {\r
+               return (PlugIn_RecRD_X5EX) super.clone();\r
+       }\r
+\r
+       /* 必須コード  - ここから */\r
+       \r
+       // 種族の特性\r
+       @Override\r
+       public String getRecorderId() { return "RD-X5EX"; }\r
+\r
+       \r
+       \r
+       /* ここまで */\r
+\r
+       \r
+       \r
+       /* 個別コード-ここから最後まで */\r
+\r
+       /*\r
+        * 非公開メソッド \r
+        */\r
+       \r
+       //\r
+       @Override\r
+       protected ArrayList<ReserveList> GetRdReservedList(String response) {\r
+               \r
+               System.out.println("X5EX's GetRdReservedList()");\r
+               //\r
+               response = response.replaceAll("\n", "");\r
+\r
+               ArrayList<ReserveList> newReserveList = new ArrayList<ReserveList>();\r
+\r
+               Matcher ma = Pattern.compile("<tr [^>]*?>([\\s\\S]*?)</tr>").matcher(response);\r
+               while (ma.find()) {\r
+\r
+                       // 個々のデータを取り出す\r
+                       String buf = ma.group(1);\r
+\r
+                       Matcher mb = null;\r
+                       mb = Pattern.compile(">新規予約<").matcher(buf);\r
+                       if (mb.find()) {\r
+                               break;\r
+                       }\r
+                       \r
+                       ReserveList entry = new ReserveList();\r
+                       \r
+                       String[] d = new String[11];\r
+                       mb = Pattern.compile("<(td|TD).*?>(.*?)</(td|TD)>").matcher(buf);\r
+                       for (int i=0; i<d.length; i++) {\r
+                               if ( mb.find()) {\r
+                                       d[i] = mb.group(2);\r
+                               }\r
+                               //System.out.println(i+") "+d[i]);\r
+                       }\r
+                       \r
+                       // 予約実行ON/OFF\r
+                       mb = Pattern.compile("check_off\\.gif").matcher(d[1]);\r
+                       if (mb.find()) {\r
+                               entry.setExec(false);\r
+                       }\r
+                       \r
+                       // 予約名のエスケープを解除する\r
+                       String title = d[2];\r
+                       mb = Pattern.compile("<A .*?>(.+?)</A>").matcher(title);\r
+                       if (mb.find()) title = mb.group(1);\r
+                       mb = Pattern.compile("<BR>").matcher(title);    // 余計な改行の削除\r
+                       if (mb.find()) title = mb.replaceAll("");\r
+                       mb = Pattern.compile("&quot;").matcher(title);\r
+                       if (mb.find()) title = mb.replaceAll("\"");\r
+                       mb = Pattern.compile("&lt;").matcher(title);\r
+                       if (mb.find()) title = mb.replaceAll("<");\r
+                       mb = Pattern.compile("&gt;").matcher(title);\r
+                       if (mb.find()) title = mb.replaceAll(">");\r
+                       mb = Pattern.compile("&nbsp;").matcher(title);\r
+                       if (mb.find()) title = mb.replaceAll(" ");\r
+\r
+                       \r
+                       entry.setId(String.valueOf(Integer.valueOf(d[0])-1));\r
+                       entry.setRec_pattern(d[5]);\r
+                       entry.setRec_pattern_id(getRec_pattern_Id(entry.getRec_pattern()));\r
+                       mb = Pattern.compile("(\\d\\d):(\\d\\d).*?(\\d\\d):(\\d\\d)").matcher(d[6]);\r
+                       if (mb.find()) {\r
+                               entry.setAhh(mb.group(1));\r
+                               entry.setAmm(mb.group(2));\r
+                               entry.setZhh(mb.group(3));\r
+                               entry.setZmm(mb.group(4));\r
+                       }\r
+                       entry.setRec_nextdate(CommonUtils.getNextDate(entry));\r
+                       //entry.setRec_nextdate(getNextDate(entry.getRec_pattern(), entry.getZhh()+":"+entry.getZmm()));\r
+                       entry.setRec_min(CommonUtils.getRecMin(entry.getAhh(), entry.getAmm(), entry.getZhh(), entry.getZmm()));\r
+                       getStartEndDateTime(entry);\r
+                       \r
+                       mb = Pattern.compile("act_(.+?)\\.gif").matcher(d[3]);\r
+                       if (mb.find()) {\r
+                               entry.setTuner(mb.group(1));\r
+                       }\r
+                       else {\r
+                               entry.setTuner("R1");\r
+                       }\r
+                       entry.setRec_mode(d[9]);\r
+                       entry.setTitle(title);\r
+                       entry.setTitlePop(TraceProgram.replacePop(title));\r
+                       entry.setCh_name(getChCode().getCH_REC2WEB(d[4]));\r
+                       entry.setChannel(d[4]);\r
+\r
+                       entry.setRec_audio(d[10]);\r
+                       //entry.rec_folder = data.get();        // 予約一覧からはとれない\r
+                       //entry.rec_genre = data.get();         // 予約一覧からはとれない\r
+                       \r
+                       // 予約情報を保存\r
+                       newReserveList.add(entry);\r
+               }\r
+               \r
+               return(newReserveList);\r
+       }\r
+}\r
diff --git a/TinyBannavi/src/tainavi/PlugIn_RecRD_X8.java b/TinyBannavi/src/tainavi/PlugIn_RecRD_X8.java
new file mode 100644 (file)
index 0000000..eacb4a6
--- /dev/null
@@ -0,0 +1,1276 @@
+package tainavi;\r
+\r
+import java.io.File;\r
+import java.io.UnsupportedEncodingException;\r
+import java.net.Authenticator;\r
+import java.net.URLEncoder;\r
+import java.util.ArrayList;\r
+import java.util.Hashtable;\r
+import java.util.regex.Matcher;\r
+import java.util.regex.Pattern;\r
+\r
+\r
+/*\r
+ * \r
+ */\r
+\r
+public class PlugIn_RecRD_X8 extends HDDRecorderUtils implements HDDRecorder,Cloneable {\r
+\r
+       public PlugIn_RecRD_X8 clone() {\r
+               return (PlugIn_RecRD_X8) super.clone();\r
+       }\r
+       \r
+       private static final String thisEncoding = "MS932";\r
+\r
+       /* 必須コード  - ここから */\r
+       \r
+       // 種族の特性\r
+       public String getRecorderId() { return "VARDIA RD-X8"; }\r
+       public RecType getType() { return RecType.RECORDER; }\r
+       \r
+       // 個体の特性\r
+       private GetRDStatus gs = new GetRDStatus();\r
+       private ChannelCode cc = new ChannelCode(getRecorderId());\r
+       private String rsvedFile = "";\r
+       \r
+       private String errmsg = "";\r
+\r
+       // 公開メソッド\r
+       \r
+       /*\r
+        * \r
+        */\r
+       public ChannelCode getChCode() {\r
+               return cc;\r
+       }\r
+       \r
+       /*\r
+        * チャンネルリモコン機能\r
+        */\r
+       public boolean ChangeChannel(String Channel) {\r
+               \r
+               if (Channel == null) {\r
+                       return true;\r
+               }\r
+               \r
+               int curBC;\r
+               int newBC;\r
+               String chNo;\r
+               byte enc;\r
+               \r
+               errmsg = "";\r
+               \r
+               /*\r
+                * 変更前の放送(地上波/BS/CS)\r
+                */\r
+               {\r
+                       String ch = null;\r
+                       for (int i=0; i<3 && (ch = gs.getCurChannel(getIPAddr())) == null; i++) {\r
+                               CommonUtils.milSleep(500);\r
+                       }\r
+                       if (ch == null) {\r
+                               errmsg = "レコーダへのアクセスに失敗しました(チャンネルリモコン)。";\r
+                               System.err.println(errmsg);\r
+                               return false;\r
+                       }\r
+                       \r
+                       byte[] ba = ch.getBytes();\r
+                       enc = ba[0];\r
+                       ch = ch.substring(1,5);\r
+                       \r
+                       if (ch.matches("^\\d.+")) {\r
+                               curBC = 0;\r
+                       }\r
+                       else if (ch.startsWith("BS")) {\r
+                               curBC = 1;\r
+                       }\r
+                       else if (ch.startsWith("CS")) {\r
+                               curBC = 2;\r
+                       }\r
+                       else {\r
+                               errmsg = "現在のチャンネルが認識できません("+ch+")。";\r
+                               System.err.println(errmsg);\r
+                               return false;\r
+                       }\r
+               }\r
+               \r
+               /*\r
+                * 変更後のCH\r
+                */\r
+               {\r
+                       // 放送(地上波/BS/CS)\r
+                       String cd = cc.getCH_WEB2CODE(Channel);\r
+                       String ch = cc.getCH_CODE2REC(cd);\r
+                       String typ = text2value(chtype, cd);\r
+                       if (typ == null) {\r
+                               errmsg = "鯛ナビに情報が同期されていないチャンネルです("+Channel+", "+ch+")。";\r
+                               System.err.println(errmsg);\r
+                               return false;\r
+                       }\r
+                       else if (typ.matches("^l[123]$")) {\r
+                               errmsg = "外部入力にアサインされているチャンネルには変更できません("+Channel+", "+ch+", "+typ+")。";\r
+                               System.err.println(errmsg);\r
+                               return false;\r
+                       }\r
+                       else if (typ.equals("uvd")) {\r
+                               newBC = 0;\r
+                       }\r
+                       else if (typ.equals("bsd")) {\r
+                               newBC = 1;\r
+                       }\r
+                       else if (typ.equals("csd")) {\r
+                               newBC = 2;\r
+                       }\r
+                       else {\r
+                               errmsg = "放送種別が識別できません。プログラム異常です("+Channel+", "+ch+")。";\r
+                               System.err.println(errmsg);\r
+                               return false;\r
+                       }\r
+                       \r
+                       // CH番号\r
+                       Matcher ma = Pattern.compile("(\\d\\d\\d)").matcher(ch);\r
+                       if (ma.find()) {\r
+                               chNo = ma.group(1);\r
+                       }\r
+                       else {\r
+                               errmsg = "CH番号が取得できません("+Channel+", "+ch+")。";\r
+                               System.err.println(errmsg);\r
+                               return false;\r
+                       }\r
+               }\r
+               \r
+               /*\r
+                * CH変更実行\r
+                */\r
+               \r
+               // おまじない\r
+               Authenticator.setDefault(new MyAuthenticator(getUser(), getPasswd()));\r
+               \r
+               int cBC;\r
+               for (int i=0; i<4 && newBC != (cBC = (curBC+i)%4); i++) {\r
+                       // 地上アナログ(3)が選択できるのはRE(0x06)だけ\r
+                       if (enc != 0x06 && cBC == 3) {\r
+                               continue;\r
+                       }\r
+                       \r
+                       // 切り替え実行\r
+                       reqGET("http://"+getIPAddr()+":"+getPortNo()+"/remote/remote.htm?key=21", null);\r
+                       \r
+                       // 地上波→BS・BS→CSは切り替え完了までに時間がかかる\r
+                       CommonUtils.milSleep((cBC == 0 || cBC == 1)?(3000):(1000));\r
+               }\r
+               \r
+               reqGET("http://"+getIPAddr()+":"+getPortNo()+"/remote/remote.htm?key=25", null);\r
+               CommonUtils.milSleep(1000);\r
+               for (int i=0; i<3; i++) {\r
+                       reqGET("http://"+getIPAddr()+":"+getPortNo()+"/remote/remote.htm?key=0"+chNo.substring(i,i+1), null);\r
+                       CommonUtils.milSleep(200);\r
+               }\r
+               reqGET("http://"+getIPAddr()+":"+getPortNo()+"/remote/remote.htm?key=44", null);\r
+               \r
+               return true;\r
+       }\r
+\r
+       /*\r
+        *      レコーダーから予約一覧を取得する \r
+        */\r
+       public boolean GetRdReserve(boolean force)\r
+       {\r
+               System.out.println("レコーダから予約一覧を取得します("+force+")");\r
+               \r
+               errmsg = "";\r
+\r
+               //\r
+               rsvedFile = "env/reserved."+getIPAddr()+"_"+getPortNo()+"_"+getRecorderId()+".xml";\r
+               \r
+               String vrateTFile = "env/videorate."+getIPAddr()+"_"+getPortNo()+"_"+getRecorderId()+".xml";\r
+               String arateTFile = "env/audiorate."+getIPAddr()+"_"+getPortNo()+"_"+getRecorderId()+".xml";\r
+               String folderTFile = "env/folders."+getIPAddr()+"_"+getPortNo()+"_"+getRecorderId()+".xml";\r
+               String encoderTFile = "env/encoders."+getIPAddr()+"_"+getPortNo()+"_"+getRecorderId()+".xml";\r
+               String dvdcompatTFile = "env/dvdcompat."+getIPAddr()+"_"+getPortNo()+"_"+getRecorderId()+".xml";\r
+               String xChapterTFile = "env/xchapter."+getIPAddr()+"_"+getPortNo()+"_"+getRecorderId()+".xml";\r
+               String msChapterTFile = "env/mschapter."+getIPAddr()+"_"+getPortNo()+"_"+getRecorderId()+".xml";\r
+               String mvChapterTFile = "env/mvchapter."+getIPAddr()+"_"+getPortNo()+"_"+getRecorderId()+".xml";\r
+               String chValueTFile = "env/chvalue."+getIPAddr()+"_"+getPortNo()+"_"+getRecorderId()+".xml";\r
+               String chTypeTFile = "env/chtype."+getIPAddr()+"_"+getPortNo()+"_"+getRecorderId()+".xml";\r
+               \r
+               File f = new File(rsvedFile);\r
+               if ( force == false && f.exists()) {\r
+                       // キャッシュから読み出し(録画設定ほか)\r
+                       vrate = TVSload(vrateTFile);\r
+                       if ( vrate.size() > 0 ) {\r
+                               System.out.println("+video rate="+vrateTFile);\r
+                       }\r
+                       arate = TVSload(arateTFile);\r
+                       if ( arate.size() > 0 ) {\r
+                               System.out.println("+audio rate="+arateTFile);\r
+                       }\r
+                       folder = TVSload(folderTFile);\r
+                       if ( folder.size() > 0 ) {\r
+                               System.out.println("+folder="+folderTFile);\r
+                       }\r
+                       encoder = TVSload(encoderTFile);\r
+                       if ( encoder.size() > 0 ) {\r
+                               System.out.println("+encoder="+encoderTFile);\r
+                       }\r
+                       dvdcompat = TVSload(dvdcompatTFile);\r
+                       if ( dvdcompat.size() > 0 ) {\r
+                               System.out.println("+dvdcompat="+dvdcompatTFile);\r
+                       }\r
+                       // 自動チャプタ関連\r
+                       xchapter = TVSload(xChapterTFile);\r
+                       if ( xchapter.size() > 0 ) {\r
+                               System.out.println("+xchapter="+xChapterTFile);\r
+                       }\r
+                       mschapter = TVSload(msChapterTFile);\r
+                       if ( mschapter.size() > 0 ) {\r
+                               System.out.println("+mschapter="+msChapterTFile);\r
+                       }\r
+                       mvchapter = TVSload(mvChapterTFile);\r
+                       if ( mvchapter.size() > 0 ) {\r
+                               System.out.println("+mvchapter="+mvChapterTFile);\r
+                       }\r
+                       // チャンネルコードバリュー\r
+                       chvalue = TVSload(chValueTFile);\r
+                       if ( chvalue.size() > 0 ) {\r
+                               System.out.println("+chvalue="+chValueTFile);\r
+                       }\r
+                       chtype = TVSload(chTypeTFile);\r
+                       if ( chtype.size() > 0 ) {\r
+                               System.out.println("+chtype="+chTypeTFile);\r
+                       }\r
+                       \r
+                       // キャッシュから読み出し(予約一覧)\r
+                       setReserves(ReservesFromFile(rsvedFile));\r
+                       if (getReserves().size() > 0) {\r
+                               System.out.println("+read from="+rsvedFile);\r
+                       }\r
+                       \r
+                       // なぜか設定ファイルが空になっている場合があるので、その際は再取得する\r
+                       if (vrate.size()>0 && arate.size()>0 &&\r
+                                       folder.size()>0 && encoder.size()>0 &&\r
+                                       xchapter.size()>0 && mschapter.size()>0 && mvchapter.size()>0) {\r
+                               return(true);\r
+                       }\r
+                       \r
+                       //getReserves().removeAll(getReserves());\r
+               }\r
+\r
+               // おまじない\r
+               Authenticator.setDefault(new MyAuthenticator(getUser(), getPasswd()));\r
+               \r
+               // RDから予約一覧を取り出す\r
+               Matcher ma = null;\r
+               String idx = "";\r
+               String header="";\r
+               String response="";\r
+               {\r
+                       reportProgress("get reserved list(1/5).");\r
+                       String[] d = reqGET("http://"+getIPAddr()+":"+getPortNo()+"/reserve/b_rsv.htm",null);\r
+                       header = d[0];\r
+                       response = d[1];\r
+                       \r
+                       if (response == null) {\r
+                               errmsg = "レコーダーが反応しません";\r
+                               return(false);\r
+                       }\r
+               }\r
+               ma = Pattern.compile("/reserve/(\\d+?)/reserve.htm").matcher(response);\r
+               if (ma.find()) {\r
+                       idx = ma.group(1);\r
+                       reportProgress("get reserved list(2/5).");\r
+                       String[] d = reqGET("http://"+getIPAddr()+":"+getPortNo()+"/reserve/"+idx+"/reserve.htm",null);\r
+                       header = d[0];\r
+                       response = d[1];\r
+                       \r
+                       if (response == null) {\r
+                               errmsg = "レコーダーが反応しません";\r
+                               return(false);\r
+                       }\r
+               }\r
+               \r
+               // 予約エントリー数を取得する\r
+               int RsvCnt = 0;\r
+               ma = Pattern.compile("RsvCnt\\s*=\\s*(\\d+?);").matcher(response);\r
+               if ( ! ma.find()) {\r
+                       errmsg = "レコーダーからの情報取得に失敗しました(予約一覧)";\r
+                       return false;\r
+               }\r
+               RsvCnt = Integer.valueOf(ma.group(1));\r
+               \r
+               // (1)録画設定の取得\r
+               {\r
+                       // ハング防止のおまじない\r
+                       reportProgress("get reserved list(3/5).");\r
+                       reqGET("http://"+getIPAddr()+":"+getPortNo()+"/reserve/"+idx+"/b_rsvinfo.htm?0&"+(RsvCnt+1),null);\r
+               }\r
+               {\r
+                       reportProgress("get reserved list(4/5).");\r
+                       String[] d = reqGET("http://"+getIPAddr()+":"+getPortNo()+"/reserve/"+idx+"/rsvinfo.htm",null);\r
+                       String hdr = d[0];\r
+                       String res = d[1];\r
+                       Matcher mb = null;\r
+                       \r
+                       // 正常取得チェック\r
+                       if ( ! res.matches("[\\s\\S]*var hdd_folder_text[\\s\\S]*")) {\r
+                               errmsg = "レコーダーからの情報取得に失敗しました(録画設定)";\r
+                               return false;\r
+                       }\r
+                       \r
+                       // (1-3)フォルダ一覧\r
+                       folder.clear();\r
+                       \r
+                       mb = Pattern.compile("var hdd_folder_text\\s*= new Array\\(([\\s\\S]+?)\\);").matcher(res);\r
+                       if (mb.find()) {\r
+                               Matcher mc = Pattern.compile("\"(.+?)\",?").matcher(mb.group(1));\r
+                               while (mc.find()) {\r
+                                       TextValueSet t = new TextValueSet();\r
+                                       t.setText(mc.group(1));\r
+                                       folder.add(t);\r
+                               }\r
+                       }\r
+                       mb = Pattern.compile("var hdd_folder_value\\s*= new Array\\(([\\s\\S]+?)\\);").matcher(res);\r
+                       if (mb.find()) {\r
+                               Matcher mc = Pattern.compile("\"(.+?)\",?").matcher(mb.group(1));\r
+                               for (TextValueSet t : folder) {\r
+                                       if (mc.find()) {\r
+                                               t.setValue(mc.group(1));\r
+                                       }\r
+                               }\r
+                       }\r
+                       \r
+                       TVSsave(folder, folderTFile);\r
+                       \r
+                       // (1-4)エンコーダ\r
+                       encoder.clear();\r
+                       \r
+                       mb = Pattern.compile("var double_encode_flg = (\\d+?);").matcher(res);\r
+                       while (mb.find()) {\r
+                               Matcher mc = Pattern.compile("\\n\\s*?switch \\( double_encode_flg \\) \\{([\\s\\S]+?default:)").matcher(res);\r
+                               if (mc.find()) {\r
+                                       Matcher md = Pattern.compile("(case "+mb.group(1)+":[\\s\\S]+?break;)").matcher(mc.group(1));\r
+                                       if (md.find()) {\r
+                                               //System.out.println(md.group(1));\r
+                                               Matcher me = Pattern.compile("name=enc_type value=.\"(\\d+?).\"[\\s\\S]+?toru_(.+?)\\.gif").matcher(md.group(1));\r
+                                               while (me.find()) {\r
+                                                       TextValueSet t = new TextValueSet();\r
+                                                       t.setText(me.group(2));\r
+                                                       t.setValue(me.group(1));\r
+                                                       encoder.add(t);\r
+                                               }\r
+                                       }\r
+                               }\r
+                       }\r
+                       \r
+                       TVSsave(encoder, encoderTFile);\r
+                       \r
+                       // (1-5)DVD互換モード\r
+                       dvdcompat.clear();\r
+                       \r
+                       mb = Pattern.compile("var dvdr_text\\s*= new Array\\(([\\s\\S]+?)\\);").matcher(res);\r
+                       if (mb.find()) {\r
+                               Matcher mc = Pattern.compile("\"(.+?)\",?").matcher(mb.group(1));\r
+                               while (mc.find()) {\r
+                                       TextValueSet t = new TextValueSet();\r
+                                       t.setText(mc.group(1));\r
+                                       dvdcompat.add(t);\r
+                               }\r
+                       }\r
+                       mb = Pattern.compile("var dvdr_value\\s*= new Array\\(([\\s\\S]+?)\\);").matcher(res);\r
+                       if (mb.find()) {\r
+                               Matcher mc = Pattern.compile("\"(.+?)\",?").matcher(mb.group(1));\r
+                               for (TextValueSet t : dvdcompat) {\r
+                                       if (mc.find()) {\r
+                                               t.setValue(mc.group(1));\r
+                                       }\r
+                               }\r
+                       }\r
+                       \r
+                       TVSsave(dvdcompat, dvdcompatTFile);\r
+                       \r
+                       // (1-7)自動チャプター関連\r
+                       xchapter.clear();\r
+                       mb = Pattern.compile("var mutechapter_text\\s*= new Array\\(([\\s\\S]+?)\\);").matcher(res);\r
+                       if (mb.find()) {\r
+                               Matcher mc = Pattern.compile("\"(.+?)\",?").matcher(mb.group(1));\r
+                               while (mc.find()) {\r
+                                       TextValueSet t = new TextValueSet();\r
+                                       t.setText(mc.group(1));\r
+                                       xchapter.add(t);\r
+                               }\r
+                       }\r
+                       mb = Pattern.compile("var mutechapter_value\\s*= new Array\\(([\\s\\S]+?)\\);").matcher(res);\r
+                       if (mb.find()) {\r
+                               Matcher mc = Pattern.compile("(\\d+),?").matcher(mb.group(1));\r
+                               for (TextValueSet t : xchapter) {\r
+                                       if (mc.find()) {\r
+                                               t.setValue(mc.group(1));\r
+                                       }\r
+                               }\r
+                       }\r
+                       TVSsave(xchapter, xChapterTFile);\r
+                       \r
+                       mschapter.clear();\r
+                       mb = Pattern.compile("var magicchapter_text\\s*= new Array\\(([\\s\\S]+?)\\);").matcher(res);\r
+                       if (mb.find()) {\r
+                               Matcher mc = Pattern.compile("\"(.+?)\",?").matcher(mb.group(1));\r
+                               while (mc.find()) {\r
+                                       TextValueSet t = new TextValueSet();\r
+                                       t.setText(mc.group(1));\r
+                                       mschapter.add(t);\r
+                               }\r
+                       }\r
+                       mb = Pattern.compile("var magicchapter_value\\s*= new Array\\(([\\s\\S]+?)\\);").matcher(res);\r
+                       if (mb.find()) {\r
+                               Matcher mc = Pattern.compile("(\\d+),?").matcher(mb.group(1));\r
+                               for (TextValueSet t : mschapter) {\r
+                                       if (mc.find()) {\r
+                                               t.setValue(mc.group(1));\r
+                                       }\r
+                               }\r
+                       }\r
+                       TVSsave(mschapter, msChapterTFile);\r
+                       \r
+                       mvchapter.clear();\r
+                       mb = Pattern.compile("var cmchapter_text\\s*= new Array\\(([\\s\\S]+?)\\);").matcher(res);\r
+                       if (mb.find()) {\r
+                               Matcher mc = Pattern.compile("\"(.+?)\",?").matcher(mb.group(1));\r
+                               while (mc.find()) {\r
+                                       TextValueSet t = new TextValueSet();\r
+                                       t.setText(mc.group(1));\r
+                                       mvchapter.add(t);\r
+                               }\r
+                       }\r
+                       mb = Pattern.compile("var cmchapter_value\\s*= new Array\\(([\\s\\S]+?)\\);").matcher(res);\r
+                       if (mb.find()) {\r
+                               Matcher mc = Pattern.compile("(\\d+),?").matcher(mb.group(1));\r
+                               for (TextValueSet t : mvchapter) {\r
+                                       if (mc.find()) {\r
+                                               t.setValue(mc.group(1));\r
+                                       }\r
+                               }\r
+                       }\r
+                       TVSsave(mvchapter, mvChapterTFile);\r
+                       \r
+                       // (1-8)チャンネルコードバリュー\r
+                       chvalue.clear();\r
+                       chtype.clear();\r
+                       for ( String typ : new String[] { "uvd","bsd","csd","uva","bsa","l1","l2","l3" } ) {\r
+                               String txtkey = typ+"_ch_text";\r
+                               String valkey = typ+"_ch_value";\r
+                               Matcher mc = Pattern.compile("var "+txtkey+"\\s*= new Array\\(([\\s\\S]+?)\\);").matcher(res);\r
+                               Matcher md = Pattern.compile("var "+valkey+"\\s*= new Array\\(([\\s\\S]+?)\\);").matcher(res);\r
+                               if ( mc.find() && md.find() ) {\r
+                                       Matcher me = Pattern.compile("\"(.+?)\",?").matcher(mc.group(1));\r
+                                       Matcher mf = null;\r
+                                       if ( typ.equals("uva") ) {\r
+                                               mf = Pattern.compile("(\\d+),").matcher(md.group(1));\r
+                                       }\r
+                                       else {\r
+                                               mf = Pattern.compile("\"([^\"]+?)\",").matcher(md.group(1));\r
+                                       }\r
+                                       while ( me.find() && mf.find() ) {\r
+                                               TextValueSet t = new TextValueSet();\r
+                                               t.setText(me.group(1));\r
+                                               t.setValue(mf.group(1));\r
+                                               chvalue.add(t);\r
+                                               \r
+                                               TextValueSet x = new TextValueSet();\r
+                                               x.setText(mf.group(1));\r
+                                               x.setValue(typ);\r
+                                               chtype.add(x);\r
+                                       }\r
+                               }\r
+                       }\r
+                       TVSsave(chvalue, chValueTFile);\r
+                       TVSsave(chtype, chTypeTFile);\r
+               }\r
+               {\r
+                       reportProgress("get reserved list(5/5).");\r
+                       /*\r
+                       String[] d = reqGET("http://"+getIPAddr()+":"+getPortNo()+"/cmn/script_function.htm",null);\r
+                       String hdr = d[0];\r
+                       String res = d[1];\r
+                       Matcher mb = null;\r
+                       */\r
+                       \r
+                       // (1-1)画質設定\r
+                       {\r
+                               vrate.clear();\r
+                               TextValueSet t = null;\r
+                               \r
+                               t = new TextValueSet();\r
+                               t.setText("[TS]");\r
+                               t.setValue("128:");\r
+                               vrate.add(t);\r
+                               \r
+                               t = new TextValueSet();\r
+                               t.setText("[TSE] 1.0");\r
+                               t.setValue("2:1000");\r
+                               vrate.add(t);\r
+                               t = new TextValueSet();\r
+                               t.setText("[TSE] 1.4");\r
+                               t.setValue("2:1400");\r
+                               vrate.add(t);\r
+                               for (int br=2000; br<=17000; ) {\r
+                                       t = new TextValueSet();\r
+                                       t.setText(String.format("[TSE] %d.%d", (br-br%1000)/1000, (br%1000)/100));\r
+                                       t.setValue("2:"+String.valueOf(br));\r
+                                       vrate.add(t);\r
+                                       if (br < 10000) {\r
+                                               br += 200;\r
+                                       }\r
+                                       else {\r
+                                               br += 500;\r
+                                       }\r
+                               }\r
+                               \r
+                               t = new TextValueSet();\r
+                               t.setText("[VR] SP4.4/4.6");\r
+                               t.setValue("1:1");\r
+                               vrate.add(t);\r
+                               t = new TextValueSet();\r
+                               t.setText("[VR] LP2.0/2.2");\r
+                               t.setValue("1:2");\r
+                               vrate.add(t);\r
+                               t = new TextValueSet();\r
+                               t.setText("[VR] 1.0");\r
+                               t.setValue("1:1000");\r
+                               vrate.add(t);\r
+                               t = new TextValueSet();\r
+                               t.setText("[VR] 1.4");\r
+                               t.setValue("1:1400");\r
+                               vrate.add(t);\r
+                               for (int br=2000; br<=9200; br+=200) {\r
+                                       t = new TextValueSet();\r
+                                       t.setText(String.format("[VR] %d.%d", (br-br%1000)/1000, (br%1000)/100));\r
+                                       t.setValue("1:"+String.valueOf(br));\r
+                                       vrate.add(t);\r
+                               }\r
+                               t = new TextValueSet();\r
+                               t.setText("[VR] 高レート節約");\r
+                               t.setValue("1:10");\r
+                               vrate.add(t);\r
+                               \r
+                               TVSsave(vrate, vrateTFile);\r
+                       }\r
+                       \r
+                       // (1-2)音質設定\r
+                       {\r
+                               arate.clear();\r
+                               TextValueSet t = null;\r
+                               \r
+                               t = new TextValueSet();\r
+                               t.setText("M1");\r
+                               t.setValue("1");\r
+                               arate.add(t);\r
+                               t = new TextValueSet();\r
+                               t.setText("M2");\r
+                               t.setValue("2");\r
+                               arate.add(t);\r
+                               t = new TextValueSet();\r
+                               t.setText("L-PCM");\r
+                               t.setValue("3");\r
+                               arate.add(t);\r
+                               \r
+                               TVSsave(arate, arateTFile);\r
+                       }\r
+               }\r
+               \r
+               // 予約一覧データの分析\r
+               ArrayList<ReserveList> ra = decodeReservedList(response); \r
+               for (ReserveList entry : ra) {\r
+                       entry.setCh_name(getChCode().getCH_REC2WEB(entry.getChannel()));\r
+                       \r
+                       // タイトル自動補完フラグなど本体からは取得できない情報を引き継ぐ\r
+                       copyAttributes(entry, getReserves());\r
+               }\r
+               setReserves(ra);\r
+               \r
+               // キャッシュに保存\r
+               ReservesToFile(getReserves(), rsvedFile);\r
+               \r
+               // 取得した情報の表示\r
+               System.out.println("---Reserved List Start---");\r
+               for ( int i = 0; i<getReserves().size(); i++ ) {\r
+                       // 詳細情報の取得\r
+                       ReserveList e = getReserves().get(i);\r
+                       System.out.println(String.format("[%s] %s\t%s\t%s\t%s:%s\t%s:%s\t%sm\t%s\t%s(%s)\t%s\t%s",\r
+                                       (i+1), e.getId(), e.getRec_pattern(), e.getRec_nextdate(), e.getAhh(), e.getAmm(), e.getZhh(),  e.getZmm(),     e.getRec_min(), e.getRec_mode(), e.getTitle(), e.getTitlePop(), e.getChannel(), e.getCh_name()));\r
+               }\r
+               System.out.println("---Reserved List End---");\r
+               \r
+               return(true);\r
+       }\r
+       \r
+       /*\r
+        *      予約を実行する\r
+        */\r
+       public boolean PostRdEntry(ReserveList r)\r
+       {\r
+               //\r
+               if (cc.getCH_WEB2CODE(r.getCh_name()) == null) {\r
+                       errmsg = "【警告】Web番組表の放送局名「"+r.getCh_name()+"」をCHコードに変換できません。CHコード設定を修正してください。" ;\r
+                       System.out.println(errmsg);\r
+                       return(false);\r
+               }\r
+               \r
+               System.out.println("Run: PostRdEntry("+r.getTitle()+")");\r
+               \r
+               errmsg = "";\r
+\r
+               // おまじない\r
+               Authenticator.setDefault(new MyAuthenticator(getUser(), getPasswd()));\r
+               \r
+               // RDから予約一覧を取り出す\r
+               Matcher ma = null;\r
+               String idx = "";\r
+               String header;\r
+               String response;\r
+               {\r
+                       reportProgress("get reserved list(1/7).");\r
+                       String[] d = reqGET("http://"+getIPAddr()+":"+getPortNo()+"/reserve/b_rsv.htm",null);\r
+                       header = d[0];\r
+                       response = d[1];\r
+                       \r
+                       if ( response == null ) {\r
+                               errmsg = "レコーダーが反応しません。";\r
+                               return(false);\r
+                       }\r
+               }\r
+               ma = Pattern.compile("/reserve/(\\d+?)/reserve.htm").matcher(response);\r
+               if (ma.find()) {\r
+                       reportProgress("get reserved list(2/7).");\r
+                       idx = ma.group(1);\r
+                       String[] d = reqGET("http://"+getIPAddr()+":"+getPortNo()+"/reserve/"+idx+"/reserve.htm",null);\r
+                       header = d[0];\r
+                       response = d[1];\r
+                       \r
+                       if ( response == null ) {\r
+                               errmsg = "レコーダーが反応しません。";\r
+                               return(false);\r
+                       }\r
+               }\r
+               \r
+               // 予約エントリー数を取得する\r
+               int RsvCnt = 0;\r
+               ma = Pattern.compile("RsvCnt\\s*=\\s*(\\d+?);").matcher(response);\r
+               if (ma.find()) {\r
+                       RsvCnt = Integer.valueOf(ma.group(1));\r
+               }\r
+       \r
+               // RDに新規登録要請\r
+               {\r
+                       // ハング防止のおまじない\r
+                       reportProgress("get program(3/7).");\r
+                       reqGET("http://"+getIPAddr()+":"+getPortNo()+"/reserve/"+idx+"/b_rsvinfo.htm?0&"+(RsvCnt+1),null);\r
+                       \r
+                       reportProgress("get program(4/7).");\r
+                       reqGET("http://"+getIPAddr()+":"+getPortNo()+"/reserve/"+idx+"/rsvinfo.htm",null);\r
+               }\r
+\r
+               // POSTデータを変換する\r
+               Hashtable<String, String> pdat = modPostdata(r);\r
+               \r
+               // RDへの情報作成\r
+               String pstr = joinPoststr(pdat);\r
+\r
+               // RDへ情報送信\r
+               {               \r
+                       reportProgress("send request(5/7).");\r
+                       String[] d = reqPOST("http://"+getIPAddr()+":"+getPortNo()+"/reserve/"+idx+"/entry.htm", pstr, null);\r
+                       header = d[0];\r
+                       response = d[1];\r
+               }\r
+               \r
+               // 登録結果の確認\r
+               ma = Pattern.compile("alert\\(msg\\)").matcher(response);\r
+               if ( ma.find() ) {\r
+                       Matcher mb = Pattern.compile("\\bmsg=\"([\\s\\S]+?)\";").matcher(response);\r
+                       if (mb.find()) {\r
+                               errmsg = mb.group(1);\r
+                               System.out.printf("\n<<< Message from RD >>> \"%s\"\n\n", errmsg);\r
+                               Matcher mc = Pattern.compile("(予約時間が重複しています。|W録の振り替えをおこないました)").matcher(errmsg);\r
+                               if ( ! mc.find() ) {\r
+                                       return(false);\r
+                               }\r
+                       }                       \r
+               }\r
+\r
+\r
+               \r
+               /*\r
+                * 予約情報の調整 \r
+                */\r
+               \r
+               // 予約ID番号を取得(キャッシュに存在しない番号が新番号)\r
+               {\r
+                       reportProgress("get identifier(6/7).");\r
+                       String[] d = reqGET("http://"+getIPAddr()+":"+getPortNo()+"/reserve/b_rsv.htm",null);\r
+                       header = d[0];\r
+                       response = d[1];\r
+               }\r
+               ma = Pattern.compile("/reserve/(\\d+?)/reserve.htm").matcher(response);\r
+               if (ma.find()) {\r
+                       idx = ma.group(1);\r
+                       reportProgress("get identifier(7/7).");\r
+                       String[] d = reqGET("http://"+getIPAddr()+":"+getPortNo()+"/reserve/"+idx+"/reserve.htm",null);\r
+                       header = d[0];\r
+                       response = d[1];\r
+               }\r
+               r.setId(getNewId(response));\r
+               \r
+               // 音質(TS/TSEでは音質の設定はできない)\r
+               ma = Pattern.compile("^\\[TS").matcher(r.getRec_mode());\r
+               if (ma.find()) {\r
+                       r.setRec_audio("");\r
+               }\r
+\r
+               // 予約パターンID\r
+               r.setRec_pattern_id(getRec_pattern_Id(r.getRec_pattern()));\r
+\r
+               // 次回予定日\r
+               r.setRec_nextdate(CommonUtils.getNextDate(r));\r
+               //r.setRec_nextdate(getNextDate(r.getRec_pattern(), r.getZhh()+":"+r.getZmm()));\r
+               \r
+               // 録画長\r
+               r.setRec_min(CommonUtils.getRecMin(r.getAhh(),r.getAmm(),r.getZhh(),r.getZmm()));\r
+               \r
+               // 開始日時・終了日時\r
+               getStartEndDateTime(r);\r
+               \r
+               // 予約リストを更新\r
+               getReserves().add(r);\r
+               \r
+               // キャッシュに保存\r
+               ReservesToFile(getReserves(), rsvedFile);\r
+               \r
+               System.out.printf("\n<<< Message from RD >>> \"%s\"\n\n", "正常に登録できました。");\r
+               return(true);\r
+       }\r
+\r
+       \r
+       /*\r
+        *      予約を更新する\r
+        */\r
+       public boolean UpdateRdEntry(ReserveList o, ReserveList r) {\r
+               //\r
+               if (cc.getCH_WEB2CODE(r.getCh_name()) == null) {\r
+                       errmsg = "【警告】Web番組表の放送局名「"+r.getCh_name()+"」をCHコードに変換できません。CHコード設定を修正してください。" ;\r
+                       System.out.println(errmsg);\r
+                       return(false);\r
+               }\r
+               \r
+               System.out.println("Run: UpdateRdEntry()");\r
+               \r
+               errmsg = "";\r
+\r
+               // おまじない\r
+               Authenticator.setDefault(new MyAuthenticator(getUser(), getPasswd()));\r
+               \r
+               // 更新準備\r
+               Matcher ma = null;\r
+               String idx = "";\r
+               int lineno = 0;\r
+               String header;\r
+               String response;\r
+               {\r
+                       reportProgress("get reserved list(1/5).");\r
+                       String[] d = reqGET("http://"+getIPAddr()+":"+getPortNo()+"/reserve/b_rsv.htm",null);\r
+                       header = d[0];\r
+                       response = d[1];\r
+                       \r
+                       if ( response == null ) {\r
+                               errmsg = "レコーダーが反応しません。";\r
+                               return(false);\r
+                       }\r
+               }\r
+               ma = Pattern.compile("/reserve/(\\d+?)/reserve.htm").matcher(response);\r
+               if (ma.find()) {\r
+                       idx = ma.group(1);\r
+                       reportProgress("get identifier(2/5).");\r
+                       String[] d = reqGET("http://"+getIPAddr()+":"+getPortNo()+"/reserve/"+idx+"/reserve.htm",null);\r
+                       header = d[0];\r
+                       response = d[1];\r
+                       \r
+                       if ( response == null ) {\r
+                               errmsg = "レコーダーが反応しません。";\r
+                               return(false);\r
+                       }\r
+               }\r
+               ma = Pattern.compile("c1\\[(\\d+?)\\]=\""+r.getId()+"\";").matcher(response);\r
+               if ( ma.find() ) {\r
+                       lineno = Integer.valueOf(ma.group(1))+1;\r
+               }\r
+               \r
+               // RDに更新要請\r
+               {\r
+                       // ハング防止のおまじない\r
+                       reportProgress("get program(3/5).");\r
+                       reqGET("http://"+getIPAddr()+":"+getPortNo()+"/reserve/"+idx+"/b_rsvinfo.htm?"+r.getId()+"&"+lineno,null);\r
+                       \r
+                       reportProgress("get program(4/5).");\r
+                       reqGET("http://"+getIPAddr()+":"+getPortNo()+"/reserve/"+idx+"/rsvinfo.htm",null);\r
+               }\r
+\r
+               // POSTデータを変換する\r
+               Hashtable<String, String> pdat = modPostdata(r);\r
+               \r
+               // RDへの情報作成\r
+               String pstr = joinPoststr(pdat);\r
+\r
+               // RDへ情報送信\r
+               {               \r
+                       reportProgress("send request.(5/5)");\r
+                       String[] d = reqPOST("http://"+getIPAddr()+":"+getPortNo()+"/reserve/"+idx+"/entry.htm", pstr, null);\r
+                       header = d[0];\r
+                       response = d[1];\r
+               }\r
+\r
+               // 更新結果の確認\r
+               ma = Pattern.compile("alert\\(msg\\)").matcher(response);\r
+               if ( ma.find() ) {\r
+                       Matcher mb = Pattern.compile("\\bmsg=\"([\\s\\S]+?)\";").matcher(response);\r
+                       if (mb.find()) {\r
+                               errmsg = mb.group(1);\r
+                               System.out.printf("\n<<< Message from RD >>> \"%s\"\n\n", errmsg);\r
+                               Matcher mc = Pattern.compile("(予約時間が重複しています。|W録の振り替えをおこないました)").matcher(errmsg);\r
+                               if ( ! mc.find() ) {\r
+                                       return(false);\r
+                               }\r
+                       }                       \r
+               }\r
+\r
+               System.out.printf("\n<<< Message from RD >>> \"%s\"\n\n", "正常に更新できました。");\r
+\r
+               /*\r
+                * 予約情報の調整 \r
+                */\r
+               \r
+               // 音質(TS/TSEでは音質の設定はできない)\r
+               ma = Pattern.compile("^\\[TS").matcher(r.getRec_mode());\r
+               if (ma.find()) {\r
+                       r.setRec_audio("");\r
+               }\r
+\r
+               // 予約パターンID\r
+               r.setRec_pattern_id(getRec_pattern_Id(r.getRec_pattern()));\r
+               \r
+               // 次回予定日\r
+               r.setRec_nextdate(CommonUtils.getNextDate(r));\r
+               \r
+               // 録画長\r
+               r.setRec_min(CommonUtils.getRecMin(r.getAhh(),r.getAmm(),r.getZhh(),r.getZmm()));\r
+               \r
+               // 開始日時・終了日時\r
+               getStartEndDateTime(r);\r
+               \r
+               // 情報置き換え\r
+               getReserves().remove(o);\r
+               getReserves().add(r);\r
+\r
+               // キャッシュに保存\r
+               ReservesToFile(getReserves(), rsvedFile);\r
+\r
+               return(true);\r
+       }\r
+\r
+       /*\r
+        *      予約を削除する\r
+        */\r
+       public ReserveList RemoveRdEntry(String delid) {\r
+               \r
+               System.out.println("Run: RemoveRdEntry()");\r
+               \r
+               errmsg = "";\r
+\r
+               // おまじない\r
+               Authenticator.setDefault(new MyAuthenticator(getUser(), getPasswd()));\r
+\r
+               // 削除対象を探す\r
+               ReserveList rx = null;\r
+               for (  ReserveList reserve : getReserves() )  {\r
+                       if (reserve.getId().equals(delid)) {\r
+                               rx = reserve;\r
+                               break;\r
+                       }\r
+               }\r
+               if (rx == null) {\r
+                       return(null);\r
+               }\r
+\r
+               // 予約行番号を取得\r
+               Matcher ma = null;\r
+               String idx = "";\r
+               int lineno = 0;\r
+               String header;\r
+               String response;\r
+               {\r
+                       reportProgress("get identifier(1/5).");\r
+                       String[] d = reqGET("http://"+getIPAddr()+":"+getPortNo()+"/reserve/b_rsv.htm",null);\r
+                       header = d[0];\r
+                       response = d[1];\r
+                       if ( response == null ) {\r
+                               errmsg = "レコーダーが反応しません。";\r
+                               return(null);\r
+                       }\r
+               }\r
+               ma = Pattern.compile("/reserve/(\\d+?)/reserve.htm").matcher(response);\r
+               if (ma.find()) {\r
+                       idx = ma.group(1);\r
+                       reportProgress("get identifier(2/5).");\r
+                       String[] d = reqGET("http://"+getIPAddr()+":"+getPortNo()+"/reserve/"+idx+"/reserve.htm",null);\r
+                       header = d[0];\r
+                       response = d[1];\r
+               }\r
+               ma = Pattern.compile("c1\\[(\\d+?)\\]=\""+rx.getId()+"\";").matcher(response);\r
+               if ( ma.find() ) {\r
+                       lineno = Integer.valueOf(ma.group(1))+1;\r
+               }\r
+\r
+               // RDに削除要請\r
+               {\r
+                       // ハング防止のおまじない\r
+                       reportProgress("get program(3/5).");\r
+                       reqGET("http://"+getIPAddr()+":"+getPortNo()+"/reserve/"+idx+"/b_rsvinfo.htm?"+rx.getId()+"&"+lineno,null);\r
+                       \r
+                       reportProgress("get program(4/5).");\r
+                       reqGET("http://"+getIPAddr()+":"+getPortNo()+"/reserve/"+idx+"/rsvinfo.htm",null);\r
+               }\r
+               {               \r
+                       reportProgress("send request.(5/5).");\r
+                       String[] d = reqGET("http://"+getIPAddr()+":"+getPortNo()+"/reserve/"+idx+"/delete.htm", null);\r
+                       header = d[0];\r
+                       response = d[1];\r
+               }\r
+\r
+               // 削除結果の確認\r
+               ma = Pattern.compile("alert\\(msg\\)").matcher(response);\r
+               if ( ma.find() ) {\r
+                       Matcher mb = Pattern.compile("\\bmsg=\"([\\s\\S]+?)\";").matcher(response);\r
+                       if (mb.find()) {\r
+                               errmsg = mb.group(1);\r
+                               System.out.printf("\n<<< Message from RD >>> \"%s\"\n\n", errmsg);\r
+                               Matcher mc = Pattern.compile("W録の振り替えをおこないました").matcher(errmsg);\r
+                               if ( ! mc.find() ) {\r
+                                       return(null);\r
+                               }\r
+                       }                       \r
+               }\r
+               \r
+               // 予約リストを更新\r
+               getReserves().remove(rx);\r
+               \r
+               // キャッシュに保存\r
+               ReservesToFile(getReserves(), rsvedFile);\r
+               \r
+               System.out.printf("\n<<< Message from RD >>> \"%s\"\n\n", "正常に削除できました。");\r
+               return(rx);\r
+       }\r
+       \r
+       /*\r
+        * \r
+        */\r
+       public String getErrmsg() {\r
+               return(errmsg.replaceAll("\\\\r\\\\n", ""));\r
+       }\r
+\r
+       \r
+       \r
+       /* ここまで */\r
+       \r
+       \r
+       \r
+\r
+       \r
+       \r
+       /* 個別コード-ここから最後まで */\r
+\r
+       /*\r
+        * 非公開メソッド \r
+        */\r
+\r
+       private String joinPoststr(Hashtable<String, String> pdat)\r
+       {\r
+               String[] pkeys = {\r
+                       "bExec",\r
+                       "start_form",\r
+                       "title_name",\r
+                       "detail",\r
+                       "genre",\r
+                       "enc_type",\r
+                       "broadcast",\r
+                       "channel_list",\r
+                       "rec_priority",\r
+                       "maiyoubi_type",\r
+                       "date",\r
+                       "start_hour",\r
+                       "start_minute",\r
+                       "end_hour",\r
+                       "end_minute",\r
+                       "disc",\r
+                       "folder",\r
+                       "vrate",\r
+                       "amode",\r
+                       "videotype_digital",    // new\r
+                       "videomode_digital",    //new\r
+                       "auto_delete",\r
+                       "dvdr",\r
+                       "lVoice",\r
+                       "edge_left",\r
+                       "bAutoChapter",\r
+                       "MagicChapter",\r
+                       "CM_Chapter",\r
+                       "channel_no",\r
+                       "dtv_sid",\r
+                       "dtv_nid",\r
+                       "net_link",\r
+                       "add_ch_text",\r
+                       "add_ch_value",\r
+                       "sport_ext_submit",\r
+                       "title_link_submit",\r
+                       "end_form"\r
+               };\r
+               \r
+               String pstr = "";\r
+               for ( String key : pkeys ) {\r
+                       //$pdat{"$key"} =~ s/(\W)/'%'.unpack("H2", $1)/ego;\r
+                       if (pdat.get(key) == null) {\r
+                               pstr += key+"=&";\r
+                       }\r
+                       else {\r
+                               pstr += key+"="+pdat.get(key)+"&";\r
+                       }\r
+               }\r
+               pstr = pstr.substring(0, pstr.length()-1);\r
+               \r
+               return(pstr);\r
+       }\r
+       \r
+       private Hashtable<String, String> modPostdata(ReserveList r) {\r
+\r
+               Hashtable<String, String> newdat = new Hashtable<String, String>();\r
+               \r
+               // 実行するよ\r
+               newdat.put("bExec", (r.getExec())?("ON"):("OFF"));\r
+\r
+               // 予約名・予約詳細\r
+               if ( r.getAutocomplete() ) {\r
+                       newdat.put("title_name", "");\r
+                       newdat.put("detail", "");\r
+               }\r
+               else {\r
+                       try {\r
+                               newdat.put("title_name", URLEncoder.encode(r.getTitle(),thisEncoding));\r
+                       } catch (UnsupportedEncodingException e1) {\r
+                               // TODO Auto-generated catch block\r
+                               e1.printStackTrace();\r
+                       }\r
+       \r
+                       // 予約詳細\r
+                       try {\r
+                               newdat.put("detail", URLEncoder.encode(r.getDetail().replaceAll("\n", Matcher.quoteReplacement("\r\n")), thisEncoding));\r
+                       } catch (UnsupportedEncodingException e) {\r
+                               // TODO Auto-generated catch block\r
+                               e.printStackTrace();\r
+                       }\r
+               }\r
+               \r
+               // 保存先\r
+               try {\r
+                       newdat.put("folder", URLEncoder.encode(text2value(folder, r.getRec_folder()),thisEncoding));\r
+               } catch (UnsupportedEncodingException e) {\r
+                       // TODO Auto-generated catch block\r
+                       e.printStackTrace();\r
+               }\r
+               \r
+               // ジャンル\r
+               newdat.put("genre", _recstr2genre(r.getRec_genre()));\r
+               \r
+               // DVD互換モード\r
+               newdat.put("dvdr", text2value(dvdcompat, r.getRec_dvdcompat()));\r
+               \r
+               // 録画チャンネル\r
+               String channel = cc.getCH_WEB2CODE(r.getCh_name());\r
+               String ch_no = cc.getCH_CODE2REC(channel);\r
+               if (chtype.size() > 0) {\r
+                       String typ = text2value(chtype, channel);\r
+                       if (typ.equals("uva")) {\r
+                               newdat.put("broadcast","0");            // 地上アナログ\r
+                       }\r
+                       else if (typ.equals("bsa")) {\r
+                               newdat.put("broadcast","1");            // BSアナログ\r
+                       }\r
+                       else if (typ.equals("l1")) {\r
+                               newdat.put("broadcast","2");            // 外部入力(L1)\r
+                       }\r
+                       else if (typ.equals("l2")) {\r
+                               newdat.put("broadcast","3");            // 外部入力(L2)\r
+                       }\r
+                       else if (typ.equals("l3")) {\r
+                               newdat.put("broadcast","4");            // 外部入力(L3)\r
+                       }\r
+                       else if (typ.equals("bsd")) {\r
+                               newdat.put("broadcast","10");           // BSデジタル\r
+                       }\r
+                       else if (typ.equals("csd")) {\r
+                               newdat.put("broadcast","11");           // 110度CSデジタル\r
+                       }\r
+                       else if (typ.equals("uvd")) {\r
+                               newdat.put("broadcast","12");           // 地上デジタル\r
+                       }\r
+                       else {\r
+                               // 普通ここには落ちない\r
+                               if (ch_no.startsWith("C")) {\r
+                                       newdat.put("broadcast","2");            // "C***"は外部入力(L1)\r
+                               }\r
+                               else if (ch_no.startsWith("SP")) {\r
+                                       newdat.put("broadcast","4");            // "SP***"は外部入力(L3)\r
+                               }\r
+                               else {\r
+                                       newdat.put("broadcast","3");            // 未定義は全部外部入力(L2)\r
+                               }\r
+                       }\r
+                       \r
+                       newdat.put("channel_list", channel);\r
+                       newdat.put("channel_no", channel);\r
+               }\r
+               else {\r
+                       // 後方互換\r
+                       Matcher ma = Pattern.compile("^(.)(.)").matcher(ch_no);\r
+                       if (ma.find()) {\r
+                               if ((ma.group(1)+ma.group(2)).equals("CH")) {\r
+                                       newdat.put("broadcast","0");            // 地上アナログ\r
+                               }\r
+                               else if ((ma.group(1)+ma.group(2)).equals("BS")) {\r
+                                       newdat.put("broadcast","10");           // BSデジタル\r
+                               }\r
+                               else if ((ma.group(1)+ma.group(2)).equals("CS")) {\r
+                                       newdat.put("broadcast","11");           // 110度CSデジタル\r
+                               }\r
+                               else if (ma.group(1).equals("L")) {\r
+                                       newdat.put("broadcast", String.valueOf(Integer.valueOf(ma.group(2))+1));        // ライン入力ABC\r
+                               }\r
+                               else {\r
+                                       newdat.put("broadcast","12");           // 地上デジタル\r
+                               }\r
+                               \r
+                               newdat.put("channel_list", channel);\r
+                               newdat.put("channel_no", channel);\r
+                       }\r
+               }\r
+               \r
+               // 録画レート\r
+               newdat.put("enc_type", text2value(encoder, r.getTuner()));\r
+               \r
+               Matcher ma = Pattern.compile("^(\\d+?):(.*?)$").matcher(text2value(vrate, r.getRec_mode()));\r
+               if (ma.find()) {\r
+                       if (ma.group(1).equals("1")) {\r
+                               // VR\r
+                               newdat.put("vrate",ma.group(2));        \r
+                               newdat.put("amode",text2value(arate, r.getRec_audio()));        \r
+                               newdat.put("videotype_digital",ma.group(1));    \r
+                               newdat.put("videomode_digital",ma.group(2));\r
+                       }\r
+                       else if (ma.group(1).equals("2")) {\r
+                               // TSE\r
+                               newdat.put("vrate","1");        \r
+                               newdat.put("amode","1");        \r
+                               newdat.put("videotype_digital",ma.group(1));    \r
+                               newdat.put("videomode_digital",ma.group(2));\r
+                       }\r
+                       else {\r
+                               // TS\r
+                               newdat.put("vrate","1");        \r
+                               newdat.put("amode","1");        \r
+                               newdat.put("videotype_digital",ma.group(1));    \r
+                               //newdat.put("videomode_digital",ma.group(2));\r
+                       }\r
+                       \r
+               }\r
+\r
+               // 繰り返し\r
+               ma = Pattern.compile("^\\d").matcher(r.getRec_pattern());\r
+               if (ma.find()) { \r
+                       newdat.put("maiyoubi_type","0");\r
+                       newdat.put("date", r.getRec_pattern());\r
+               }\r
+               else {\r
+                       newdat.put("maiyoubi_type", "1");\r
+                       int i = 1;\r
+                       for ( String s : RPTPTN ) {\r
+                               if ( s.equals(r.getRec_pattern()) == true ) {\r
+                                       newdat.put("date", String.valueOf(i));\r
+                               }\r
+                               i++;\r
+                       }\r
+               }\r
+\r
+\r
+               newdat.put("start_hour", r.getAhh());\r
+               newdat.put("start_minute", r.getAmm());\r
+               newdat.put("end_hour", r.getZhh());\r
+               newdat.put("end_minute", r.getZmm());\r
+\r
+               // 自動チャプター関連\r
+               newdat.put("bAutoChapter"               , text2value(xchapter, r.getRec_xchapter()));\r
+               newdat.put("MagicChapter"               , text2value(mschapter, r.getRec_mschapter()));\r
+               newdat.put("CM_Chapter"                 , text2value(mvchapter, r.getRec_mvchapter()));\r
+               \r
+               // 記録先\r
+               newdat.put("disc", text2value(device, r.getRec_device()));\r
+               \r
+               // 追加\r
+               newdat.put("start_form"                 , "");\r
+               newdat.put("rec_priority"               , "150");\r
+               newdat.put("title_link"                 , "0");\r
+               newdat.put("auto_delete"                , "0");\r
+               newdat.put("video_es"                   , "00");\r
+               newdat.put("audio_es"                   , "10");\r
+               newdat.put("edge_left"                  , "0");         // 録画のりしろ\r
+               //newdat.put("MagicChapter"             , "0");\r
+               //newdat.put("MagicChapter"             , "0");\r
+               //newdat.put("CM_Chapter"                       , "0");\r
+               newdat.put("add_ch_text"                , "");\r
+               newdat.put("add_ch_value"               , "");\r
+               newdat.put("sport_ext_submit"   , "undefined");\r
+               newdat.put("title_link_submit"  , "undefined");\r
+               newdat.put("end_form"                   , "0");\r
+               \r
+               return(newdat);\r
+       }\r
+\r
+       private String _recstr2genre(String genre)\r
+       {\r
+               if (genre.equals(TVProgram.ProgGenre.MOVIE.toString())) {\r
+                       return "0";\r
+               }\r
+               else if (genre.equals(TVProgram.ProgGenre.MUSIC.toString())) {\r
+                       return "1";\r
+               }\r
+               else if (genre.equals(TVProgram.ProgGenre.DORAMA.toString())) {\r
+                       return "2";\r
+               }\r
+               else if (genre.equals(TVProgram.ProgGenre.ANIME.toString())) {\r
+                       return "3";\r
+               }\r
+               else if (genre.equals(TVProgram.ProgGenre.SPORTS.toString())) {\r
+                       return "4";\r
+               }\r
+               else if (genre.equals(TVProgram.ProgGenre.DOCUMENTARY.toString())) {\r
+                       return "5";\r
+               }\r
+               else if (genre.equals(TVProgram.ProgGenre.THEATER.toString())) {\r
+                       return "6";\r
+               }\r
+               else if (genre.equals(TVProgram.ProgGenre.HOBBY.toString())) {\r
+                       return "7";\r
+               }\r
+               else if (genre.equals(TVProgram.ProgGenre.VARIETY.toString())) {\r
+                       return "8";\r
+               }\r
+               else {\r
+                       return "10";\r
+               }\r
+       }\r
+}\r
diff --git a/TinyBannavi/src/tainavi/PlugIn_RecRD_XS41.java b/TinyBannavi/src/tainavi/PlugIn_RecRD_XS41.java
new file mode 100644 (file)
index 0000000..c6ea3a6
--- /dev/null
@@ -0,0 +1,884 @@
+package tainavi;\r
+\r
+import java.io.File;\r
+import java.io.UnsupportedEncodingException;\r
+import java.net.Authenticator;\r
+import java.net.URLEncoder;\r
+import java.util.ArrayList;\r
+import java.util.Hashtable;\r
+import java.util.regex.Matcher;\r
+import java.util.regex.Pattern;\r
+\r
+/*\r
+ * \r
+ */\r
+\r
+public class PlugIn_RecRD_XS41 extends HDDRecorderUtils implements HDDRecorder,Cloneable {\r
+\r
+       public PlugIn_RecRD_XS41 clone() {\r
+               return (PlugIn_RecRD_XS41) super.clone();\r
+       }\r
+       \r
+       private static final String thisEncoding = "MS932";\r
+\r
+       /* 必須コード  - ここから */\r
+       \r
+       // 種族の特性\r
+       public String getRecorderId() { return "RD-XS41"; }\r
+       public RecType getType() { return RecType.RECORDER; }\r
+       \r
+       // 予約ダイアログのラベル\r
+       public String getLabel_XChapter() { return "無音部分チャプタ分割"; }\r
+       public String getLabel_MsChapter() { return "DVD時チャプタ分割"; }\r
+       public String getLabel_MvChapter() { return "音多連動チャプタ分割"; }\r
+       public String getLabel_DVDCompat() { return "DVD互換モード"; }\r
+       public String getLabel_Aspect() { return "DVD記録時画面比"; }\r
+       public String getLabel_BVperf() { return "高レート節約"; }\r
+       public String getLabel_LVoice() { return "ライン音声選択"; }\r
+       \r
+       // 個体の特性\r
+       private ChannelCode cc = new ChannelCode(getRecorderId());\r
+       private String rsvedFile = "";\r
+       //private String vrateTFile = "";\r
+       //private String arateTFile = "";\r
+       \r
+       private String errmsg = "";\r
+\r
+       // 公開メソッド\r
+       \r
+       /*\r
+        * \r
+        */\r
+       public ChannelCode getChCode() {\r
+               return cc;\r
+       }\r
+       \r
+       /*\r
+        * \r
+        */\r
+       public boolean ChangeChannel(String Channel) {\r
+               return false;\r
+       }\r
+\r
+       /*\r
+        *      レコーダーから予約一覧を取得する \r
+        */\r
+       public boolean GetRdReserve(boolean force)\r
+       {\r
+               errmsg = "";\r
+               \r
+               System.out.println("Run: GetRdReserve("+force+")");\r
+               \r
+               //\r
+               rsvedFile = "env/reserved."+getIPAddr()+"_"+getPortNo()+"_"+getRecorderId()+".xml";\r
+               String vrateTFile = "env/videorate."+getIPAddr()+"_"+getPortNo()+"_"+getRecorderId()+".xml";\r
+               String arateTFile = "env/audiorate."+getIPAddr()+"_"+getPortNo()+"_"+getRecorderId()+".xml";\r
+               String dvdcompatTFile = "env/dvdcompat."+getIPAddr()+"_"+getPortNo()+"_"+getRecorderId()+".xml";\r
+               String xChapterTFile = "env/xchapter."+getIPAddr()+"_"+getPortNo()+"_"+getRecorderId()+".xml";\r
+\r
+               File f = new File(rsvedFile);\r
+               if ( force == false && f.exists()) {\r
+                       // キャッシュから読み出し(録画設定ほか)\r
+                       vrate = TVSload(vrateTFile);\r
+                       if ( vrate.size() > 0 ) {\r
+                               System.out.println("+video rate="+vrateTFile);\r
+                       }\r
+                       arate = TVSload(arateTFile);\r
+                       if ( arate.size() > 0 ) {\r
+                               System.out.println("+audio rate="+arateTFile);\r
+                       }\r
+                       dvdcompat = TVSload(dvdcompatTFile);\r
+                       if ( dvdcompat.size() > 0 ) {\r
+                               System.out.println("+dvdcompat="+dvdcompatTFile);\r
+                       }\r
+                       // 自動チャプタ関連\r
+                       xchapter = TVSload(xChapterTFile);\r
+                       if ( xchapter.size() > 0 ) {\r
+                               System.out.println("+xchapter="+xChapterTFile);\r
+                       }\r
+                       \r
+                       // キャッシュから読み出し(予約一覧)\r
+                       setReservesV1(ReservesFromFile(rsvedFile));\r
+                       if (getReserves().size() > 0) {\r
+                               System.out.println("+read from="+rsvedFile);\r
+                       }\r
+                       \r
+                       // なぜか設定ファイルが空になっている場合があるので、その際は再取得する\r
+                       if (vrate.size()>0 && arate.size()>0 &&\r
+                                       xchapter.size()>0) {\r
+                               return(true);\r
+                       }\r
+                       \r
+                       getReserves().removeAll(getReserves());\r
+               }\r
+\r
+               // おまじない\r
+               Authenticator.setDefault(new MyAuthenticator(getUser(), getPasswd()));\r
+               \r
+               // RDから予約一覧を取り出す\r
+               Matcher ma = null;\r
+               String idx = "";\r
+               String header;\r
+               String response;\r
+               {\r
+                       reportProgress("get reserved list(1/4).");\r
+                       String[] d = reqGET("http://"+getIPAddr()+":"+getPortNo()+"/program/b_prgrm.htm",null);\r
+                       header = d[0];\r
+                       response = d[1];\r
+               }\r
+               ma = Pattern.compile("/program/(\\d+?)/program.htm").matcher(response);\r
+               if (ma.find()) {\r
+                       idx = ma.group(1);\r
+                       reportProgress("get reserved list(2/4).");\r
+                       String[] d = reqGET("http://"+getIPAddr()+":"+getPortNo()+"/program/"+idx+"/program.htm",null);\r
+                       header = d[0];\r
+                       response = d[1];\r
+               }\r
+               \r
+               // (1)録画設定の取得\r
+               {\r
+                       // ハングさせないためのおまじない\r
+                       reportProgress("get reserved list(3/4).");\r
+                       reqGET("http://"+getIPAddr()+":"+getPortNo()+"/program/"+idx+"/b_proginfo.htm?0",null);\r
+               }\r
+               {\r
+                       reportProgress("get reserved list(4/4).");\r
+                       String[] d = reqGET("http://"+getIPAddr()+":"+getPortNo()+"/program/"+idx+"/proginfo.htm",null);\r
+                       String hdr = d[0];\r
+                       String res = d[1];\r
+                       Matcher mb = null; \r
+                       \r
+                       // 画質設定\r
+                       vrate.clear();\r
+                       \r
+                       mb = Pattern.compile("<select name=\"vrate\"[\\s\\S]*?>([\\s\\S]+?)</select>").matcher(res);\r
+                       if (mb.find()) {\r
+                               Matcher mc = Pattern.compile("<option value=\"(.*?)\".*?>(.+?)</option>").matcher(mb.group(1));\r
+                               while (mc.find()) {\r
+                                       TextValueSet t = new TextValueSet();\r
+                                       Matcher md = Pattern.compile(" ").matcher(mc.group(2));\r
+                                       if (md.find()) {\r
+                                               t.setText(md.replaceAll(""));\r
+                                       }\r
+                                       else {\r
+                                               t.setText(mc.group(2));\r
+                                       }\r
+                                       t.setValue(mc.group(1));\r
+                                       vrate.add(t);\r
+                               }\r
+                       }\r
+                       \r
+                       TVSsave(vrate, vrateTFile);\r
+                       \r
+                       // 音質設定\r
+                       arate.clear();\r
+                       \r
+                       mb = Pattern.compile("<select name=\"amode\".+?>([\\s\\S]+?)</select>").matcher(res);\r
+                       if (mb.find()) {\r
+                               Matcher mc = Pattern.compile("<option value=\"(.*?)\".*?>(.+?)</option>").matcher(mb.group(1));\r
+                               while (mc.find()) {\r
+                                       TextValueSet t = new TextValueSet();\r
+                                       t.setText(mc.group(2));\r
+                                       t.setValue(mc.group(1));\r
+                                       arate.add(t);\r
+                               }\r
+                       }\r
+                       \r
+                       TVSsave(arate, arateTFile);\r
+                       \r
+                       // (1-5)DVD互換モード\r
+                       dvdcompat.clear();\r
+                       \r
+                       mb = Pattern.compile("<select name=\"dvdr\" [^>]*?>([\\s\\S]+?)</select>").matcher(res);\r
+                       if (mb.find()) {\r
+                               Matcher mc = Pattern.compile("<option value\\s*=\"(.*?)\"( selected)?>\\s*(.+?)\\s*</option>").matcher(mb.group(1));\r
+                               while (mc.find()) {\r
+                                       TextValueSet t = new TextValueSet();\r
+                                       t.setText(mc.group(3));\r
+                                       t.setValue(mc.group(1));\r
+                                       dvdcompat.add(t);\r
+                               }\r
+                       }\r
+                       \r
+                       TVSsave(dvdcompat, dvdcompatTFile);\r
+                       \r
+                       // (1-7)自動チャプタ関連\r
+                       xchapter.clear();\r
+                       mb = Pattern.compile("<select[^>]+?name=\"bAutoChapter\"[^>]+?>([\\s\\S]+?)</select>").matcher(res);\r
+                       if (mb.find()) {\r
+                               Matcher mc = Pattern.compile("<option value\\s*=\"(.*?)\"\\s*(selected)?>\\s*(.+?)\\s*</option>").matcher(mb.group(1));\r
+                               while (mc.find()) {\r
+                                       TextValueSet t = new TextValueSet();\r
+                                       t.setText(mc.group(3));\r
+                                       t.setValue(mc.group(1));\r
+                                       xchapter.add(t);\r
+                               }\r
+                       }\r
+                       TVSsave(xchapter, xChapterTFile);\r
+               }\r
+               \r
+               // (2)予約一覧の取得\r
+               setReservesV1(GetRdReservedList(response));\r
+                       \r
+               // 取得した情報の表示\r
+               System.out.println("---Reserved List Start---");\r
+               for ( int i = 0; i<getReserves().size(); i++ ) {\r
+                       // 詳細情報の取得\r
+                       ReserveList e = getReserves().get(i);\r
+                       System.out.println(String.format("[%s] %s\t%s\t%s\t%s:%s\t%s:%s\t%sm\t%s\t%s\t%s\t%s",\r
+                                       (i+1), e.getId(), e.getRec_pattern(), e.getRec_nextdate(), e.getAhh(), e.getAmm(), e.getZhh(),  e.getZmm(),     e.getRec_min(), e.getRec_mode(), e.getTitle(), e.getChannel(), e.getCh_name()));\r
+               }\r
+               System.out.println("---Reserved List End---");\r
+               \r
+               // キャッシュに保存\r
+               ReservesToFile(getReserves(), rsvedFile);\r
+               \r
+               return(true);\r
+       }\r
+       \r
+       /*\r
+        *      予約を実行する\r
+        */\r
+       public boolean PostRdEntry(ReserveList r)\r
+       {\r
+               //\r
+               if (cc.getCH_WEB2CODE(r.getCh_name()) == null) {\r
+                       errmsg = "【警告】Web番組表の放送局名「"+r.getCh_name()+"」をCHコードに変換できません。CHコード設定を修正してください。" ;\r
+                       System.out.println(errmsg);\r
+                       return(false);\r
+               }\r
+               \r
+               System.out.println("Run: PostRdEntry("+r.getTitle()+")");\r
+\r
+               // RDから予約一覧を取り出す\r
+               Matcher ma = null;\r
+               String idx = "";\r
+               String header;\r
+               String response;\r
+               {\r
+                       reportProgress("get reserved list(1/7).");\r
+                       String[] d = reqGET("http://"+getIPAddr()+":"+getPortNo()+"/program/b_prgrm.htm",null);\r
+                       header = d[0];\r
+                       response = d[1];\r
+               }\r
+               ma = Pattern.compile("/program/(\\d+?)/program.htm").matcher(response);\r
+               if (ma.find()) {\r
+                       reportProgress("get reserved list(2/7).");\r
+                       idx = ma.group(1);\r
+                       String[] d = reqGET("http://"+getIPAddr()+":"+getPortNo()+"/program/"+idx+"/program.htm",null);\r
+                       header = d[0];\r
+                       response = d[1];\r
+               }\r
+               // 新規予約の行番号を取得する\r
+               int lineno = 0;\r
+               ma = Pattern.compile("\"b_proginfo.htm\\?(\\d+?)\"").matcher(response);\r
+               while (ma.find()) {\r
+                       lineno = Integer.valueOf(ma.group(1));\r
+               }\r
+               //r.setNo(no);\r
+       \r
+               // RDに新規登録要請\r
+               {\r
+                       // ハングさせないためのおまじない\r
+                       reportProgress("get program.(3/7)");\r
+                       reqGET("http://"+getIPAddr()+":"+getPortNo()+"/program/"+idx+"/b_proginfo.htm?"+lineno,null);\r
+\r
+                       reportProgress("get program.(4/7)");\r
+                       reqGET("http://"+getIPAddr()+":"+getPortNo()+"/program/"+idx+"/proginfo.htm",null);\r
+               }\r
+\r
+               // POSTデータを変換する\r
+               Hashtable<String, String> pdat = modPostdata(r);\r
+               \r
+               // RDへの情報作成\r
+               String pstr = joinPoststr(pdat);\r
+\r
+               // RDへ情報送信\r
+               {               \r
+                       reportProgress("send request.(5/7)");\r
+                       String[] d = reqPOST("http://"+getIPAddr()+":"+getPortNo()+"/program/"+idx+"/entry.htm", pstr, null);\r
+                       header = d[0];\r
+                       response = d[1];\r
+               }\r
+\r
+               //System.out.println("#"+response);\r
+\r
+               // 登録結果の確認\r
+               ma = Pattern.compile("msg=\"(.+?)\"").matcher(response);\r
+               if ( ma.find() ) {\r
+                       errmsg = ma.group(1);\r
+                       System.out.printf("\n<<< Message from RD >>> \"%s\"\n\n", errmsg);\r
+                       \r
+                       ma = Pattern.compile("(正常に更新できました。|予約時間が一部重複しています。|現在のHDD残量では入りません。)").matcher(errmsg);\r
+                       if ( ! ma.find()) {\r
+                               return false;\r
+                       }\r
+               }\r
+               \r
+               /*\r
+                * 予約情報の調整 \r
+                */\r
+               // キャッシュ上の予約情報を更新する(ライン入力放送局名引継対応)\r
+               getReserves().add(r);\r
+               \r
+               // 新しい予約一覧を取得する\r
+               {\r
+                       reportProgress("get identifier(6/7).");\r
+                       String[] d = reqGET("http://"+getIPAddr()+":"+getPortNo()+"/program/b_prgrm.htm",null);\r
+                       header = d[0];\r
+                       response = d[1];\r
+               }\r
+               ma = Pattern.compile("/program/(\\d+?)/program.htm").matcher(response);\r
+               if (ma.find()) {\r
+                       idx = ma.group(1);\r
+                       reportProgress("get identifier(7/7).");\r
+                       String[] d = reqGET("http://"+getIPAddr()+":"+getPortNo()+"/program/"+idx+"/program.htm",null);\r
+                       header = d[0];\r
+                       response = d[1];\r
+               }\r
+\r
+               setReservesV1(GetRdReservedList(response));\r
+               \r
+               // キャッシュに保存\r
+               ReservesToFile(getReserves(), rsvedFile);\r
+\r
+               System.out.printf("\n<<< Message from RD >>> \"%s\"\n\n", "正常に登録できました。");\r
+\r
+               return(true);\r
+       }\r
+\r
+       \r
+       /*\r
+        *      予約を更新する\r
+        */\r
+       public boolean UpdateRdEntry(ReserveList o, ReserveList r) {\r
+               //\r
+               if (cc.getCH_WEB2CODE(r.getCh_name()) == null) {\r
+                       errmsg = "【警告】Web番組表の放送局名「"+r.getCh_name()+"」をCHコードに変換できません。CHコード設定を修正してください。" ;\r
+                       System.out.println(errmsg);\r
+                       return(false);\r
+               }\r
+               \r
+               System.out.println("Run: UpdateRdEntry()");\r
+               \r
+               errmsg = "";\r
+\r
+               // 更新対象を探す(内部)\r
+               ReserveList rx = null;\r
+               for ( ReserveList reserve : getReserves() )  {\r
+                       if (reserve.getId().equals(r.getId())) {\r
+                               rx = reserve;\r
+                               break;\r
+                       }\r
+               }\r
+               if (rx == null) {\r
+                       errmsg = "キャッシュ上に操作対象が見つかりません。";\r
+                       return(false);\r
+               }\r
+               \r
+               // 更新準備\r
+               Matcher ma = null;\r
+               String idx = "";\r
+               String header;\r
+               String response;\r
+               {\r
+                       reportProgress("get identifier(1/7).");\r
+                       String[] d = reqGET("http://"+getIPAddr()+":"+getPortNo()+"/program/b_prgrm.htm",null);\r
+                       header = d[0];\r
+                       response = d[1];\r
+               }\r
+               ma = Pattern.compile("/program/(\\d+?)/program.htm").matcher(response);\r
+               if (ma.find()) {\r
+                       idx = ma.group(1);\r
+                       reportProgress("get identifier(2/7).");\r
+                       String[] d = reqGET("http://"+getIPAddr()+":"+getPortNo()+"/program/"+idx+"/program.htm",null);\r
+                       header = d[0];\r
+                       response = d[1];\r
+               }\r
+               \r
+               // 更新対象を探す(外部)\r
+               String id = null;\r
+               for ( ReserveList cr : GetRdReservedList(response) ) {\r
+                       if (cr.getTitle().equals(rx.getTitle()) &&\r
+                                       cr.getChannel().equals(rx.getChannel()) &&\r
+                                       cr.getRec_pattern().equals(rx.getRec_pattern())\r
+                       ) {\r
+                               id = cr.getId();\r
+                               break;\r
+                       }\r
+               }\r
+               if (id == null) {\r
+                       errmsg = "レコーダ上に操作対象が見つかりません。";\r
+                       return(false);\r
+               }\r
+\r
+               // RDに更新要請\r
+               {\r
+                       // ハングさせないためのおまじない\r
+                       reportProgress("get program.(3/7)");\r
+                       reqGET("http://"+getIPAddr()+":"+getPortNo()+"/program/"+idx+"/b_proginfo.htm?"+id,null);\r
+\r
+                       reportProgress("get program.(4/7)");\r
+                       reqGET("http://"+getIPAddr()+":"+getPortNo()+"/program/"+idx+"/proginfo.htm",null);\r
+               }\r
+\r
+               // POSTデータを変換する\r
+               Hashtable<String, String> pdat = modPostdata(r);\r
+               \r
+               // RDへの情報作成\r
+               String pstr = joinPoststr(pdat);\r
+\r
+               // RDへ情報送信\r
+               {               \r
+                       reportProgress("send request.(5/7)");\r
+                       String[] d = reqPOST("http://"+getIPAddr()+":"+getPortNo()+"/program/"+idx+"/entry.htm", pstr, null);\r
+                       header = d[0];\r
+                       response = d[1];\r
+               }\r
+\r
+               // 更新結果の確認\r
+               ma = Pattern.compile("msg=\"(.+?)\"").matcher(response);\r
+               if ( ma.find() ) {\r
+                       errmsg = ma.group(1);\r
+                       System.out.printf("\n<<< Message from RD >>> \"%s\"\n\n", errmsg);\r
+                       \r
+                       ma = Pattern.compile("(正常に更新できました。|予約時間が一部重複しています。|現在のHDD残量では入りません。)").matcher(errmsg);\r
+                       if ( ! ma.find()) {\r
+                               return false;\r
+                       }\r
+               }\r
+\r
+               System.out.printf("\n<<< Message from RD >>> \"%s\"\n\n", "正常に更新できました。");\r
+\r
+               /*\r
+                * 予約情報の調整 \r
+                */\r
+               // キャッシュ上の予約情報を更新する(ライン入力放送局名引継対応)\r
+               getReserves().remove(o);\r
+               getReserves().add(r);\r
+               \r
+               // 新しい予約一覧を取得する\r
+               {\r
+                       reportProgress("get identifier(6/7).");\r
+                       String[] d = reqGET("http://"+getIPAddr()+":"+getPortNo()+"/program/b_prgrm.htm",null);\r
+                       header = d[0];\r
+                       response = d[1];\r
+               }\r
+               ma = Pattern.compile("/program/(\\d+?)/program.htm").matcher(response);\r
+               if (ma.find()) {\r
+                       idx = ma.group(1);\r
+                       reportProgress("get identifier(7/7).");\r
+                       String[] d = reqGET("http://"+getIPAddr()+":"+getPortNo()+"/program/"+idx+"/program.htm",null);\r
+                       header = d[0];\r
+                       response = d[1];\r
+               }\r
+\r
+               setReservesV1(GetRdReservedList(response));\r
+\r
+               // キャッシュに保存\r
+               ReservesToFile(getReserves(), rsvedFile);\r
+               \r
+               return(true);\r
+       }\r
+\r
+       /*\r
+        *      予約を削除する\r
+        */\r
+       public ReserveList RemoveRdEntry(String delid) {\r
+               \r
+               System.out.println("Run: RemoveRdEntry()");\r
+\r
+               errmsg = "";\r
+               \r
+               // 削除対象を探す\r
+               ReserveList rx = null;\r
+               for (  ReserveList reserve : getReserves() )  {\r
+                       if (reserve.getId().equals(delid)) {\r
+                               rx = reserve;\r
+                               break;\r
+                       }\r
+               }\r
+               if (rx == null) {\r
+                       errmsg = "キャッシュ上に操作対象が見つかりません。";\r
+                       return(null);\r
+               }\r
+\r
+               // 削除準備\r
+               Matcher ma = null;\r
+               String idx = "";\r
+               String header;\r
+               String response;\r
+               {\r
+                       reportProgress("get identifier(1/7).");\r
+                       String[] d = reqGET("http://"+getIPAddr()+":"+getPortNo()+"/program/b_prgrm.htm",null);\r
+                       header = d[0];\r
+                       response = d[1];\r
+               }\r
+               ma = Pattern.compile("/program/(\\d+?)/program.htm").matcher(response);\r
+               if (ma.find()) {\r
+                       idx = ma.group(1);\r
+                       reportProgress("get identifier(2/7).");\r
+                       String[] d = reqGET("http://"+getIPAddr()+":"+getPortNo()+"/program/"+idx+"/program.htm",null);\r
+                       header = d[0];\r
+                       response = d[1];\r
+               }\r
+               \r
+               // 削除対象を探す(外部)\r
+               String id = null;\r
+               for ( ReserveList cr : GetRdReservedList(response) ) {\r
+                       if (cr.getTitle().equals(rx.getTitle()) &&\r
+                                       cr.getChannel().equals(rx.getChannel()) &&\r
+                                       cr.getRec_pattern().equals(rx.getRec_pattern())\r
+                       ) {\r
+                               id = cr.getId();\r
+                               break;\r
+                       }\r
+               }\r
+               if (id == null) {\r
+                       errmsg = "レコーダ上に操作対象が見つかりません。";\r
+                       return(null);\r
+               }\r
+\r
+               // RDに削除要請\r
+               {\r
+                       // ハングさせないためのおまじない\r
+                       reportProgress("get program.(3/7)");\r
+                       reqGET("http://"+getIPAddr()+":"+getPortNo()+"/program/"+idx+"/b_proginfo.htm?"+id,null);\r
+\r
+                       reportProgress("get program.(4/7)");\r
+                       reqGET("http://"+getIPAddr()+":"+getPortNo()+"/program/"+idx+"/proginfo.htm",null);\r
+               }\r
+               \r
+               {               \r
+                       reportProgress("send request.(5/7)");\r
+                       String[] d = reqGET("http://"+getIPAddr()+":"+getPortNo()+"/program/"+idx+"/delete.htm", null);\r
+                       header = d[0];\r
+                       response = d[1];\r
+               }\r
+\r
+               // 削除結果の確認\r
+               ma = Pattern.compile("msg=\"(.+?)\"").matcher(response);\r
+               if ( ma.find() ) {\r
+                       errmsg = ma.group(1);\r
+                       System.out.printf("\n<<< Message from RD >>> \"%s\"\n\n", errmsg);\r
+                       \r
+                       ma = Pattern.compile("(正しく削除できました。)").matcher(errmsg);\r
+                       if ( ! ma.find()) {\r
+                               return null;\r
+                       }\r
+               }\r
+\r
+               // 新しい予約一覧を取得する\r
+               {\r
+                       reportProgress("get identifier(6/7).");\r
+                       String[] d = reqGET("http://"+getIPAddr()+":"+getPortNo()+"/program/b_prgrm.htm",null);\r
+                       header = d[0];\r
+                       response = d[1];\r
+               }\r
+               ma = Pattern.compile("/program/(\\d+?)/program.htm").matcher(response);\r
+               if (ma.find()) {\r
+                       idx = ma.group(1);\r
+                       reportProgress("get identifier(7/7).");\r
+                       String[] d = reqGET("http://"+getIPAddr()+":"+getPortNo()+"/program/"+idx+"/program.htm",null);\r
+                       header = d[0];\r
+                       response = d[1];\r
+               }\r
+\r
+               setReservesV1(GetRdReservedList(response));\r
+               \r
+               // キャッシュに保存\r
+               ReservesToFile(getReserves(), rsvedFile);\r
+\r
+               System.out.printf("\n<<< Message from RD >>> \"%s\"\n\n", "正常に削除できました。");\r
+               \r
+               return(rx);\r
+       }\r
+       \r
+       /*\r
+        * \r
+        */\r
+       public String getErrmsg() {\r
+               return(errmsg.replaceAll("\\\\r\\\\n", ""));\r
+       }\r
+\r
+       \r
+       \r
+       /* ここまで */\r
+       \r
+       \r
+       \r
+\r
+       \r
+       \r
+       /* 個別コード-ここから最後まで */\r
+\r
+       /*\r
+        * 非公開メソッド \r
+        */\r
+\r
+       private String joinPoststr(Hashtable<String, String> pdat)\r
+       {\r
+               String[] pkeys = {\r
+                       "bExec",\r
+                       "title_name",\r
+                       "channel",\r
+                       "date",\r
+                       "start_hour",\r
+                       "start_minute",\r
+                       "end_hour",\r
+                       "end_minute",\r
+                       "disc",\r
+                       "vrate",\r
+                       "amode",\r
+                       "genre",\r
+                       "dvdr",\r
+                       "aspect",\r
+                       "bVPerform",\r
+                       "bAutoChapter",\r
+                       "detail",\r
+                       "dtv_sid",\r
+                       "dtv_nid",\r
+                       "end_form"\r
+               };\r
+               \r
+               String pstr = "";\r
+               for ( String key : pkeys ) {\r
+                       if (pdat.get(key) == null) {\r
+                               pstr += key+"=&";\r
+                       }\r
+                       else {\r
+                               pstr += key+"="+pdat.get(key)+"&";\r
+                       }\r
+               }\r
+               pstr = pstr.substring(0, pstr.length()-1);\r
+               System.out.println("POST data: "+pstr);\r
+               return(pstr);\r
+       }\r
+       \r
+       private Hashtable<String, String> modPostdata(ReserveList r) {\r
+\r
+               Hashtable<String, String> newdat = new Hashtable<String, String>();\r
+               \r
+               // 実行するよ\r
+               newdat.put("bExec", (r.getExec())?("ON"):("OFF"));\r
+\r
+               // 予約名\r
+               try {\r
+                       newdat.put("title_name", URLEncoder.encode(r.getTitle(),thisEncoding));\r
+               } catch (UnsupportedEncodingException e1) {\r
+                       // TODO Auto-generated catch block\r
+                       e1.printStackTrace();\r
+               }\r
+\r
+               // 予約詳細\r
+               try {\r
+                       newdat.put("detail", URLEncoder.encode(r.getDetail().replaceAll("\n", Matcher.quoteReplacement("\r\n")),thisEncoding));\r
+               } catch (UnsupportedEncodingException e) {\r
+                       // TODO Auto-generated catch block\r
+                       e.printStackTrace();\r
+               }\r
+               \r
+               // ジャンル\r
+               newdat.put("genre", _recstr2genre(r.getRec_genre()));\r
+               \r
+               // DVD互換モード\r
+               newdat.put("dvdr", _recstr2dvdcompat(r.getRec_dvdcompat()));\r
+               \r
+               // 録画チャンネル\r
+               String channel = cc.getCH_WEB2CODE(r.getCh_name());\r
+               newdat.put("channel", channel);\r
+               String ch_no = getChCode().getCH_CODE2REC(channel);\r
+               r.setChannel(ch_no);\r
+               \r
+               // 録画レート\r
+               newdat.put("vrate", _recstr2mode(r.getRec_mode()));\r
+\r
+               // 録画レート\r
+               newdat.put("amode", _recstr2audio(r.getRec_audio()));\r
+\r
+               // 繰り返し\r
+               Matcher ma = Pattern.compile("^\\d").matcher(r.getRec_pattern());\r
+               if (ma.find()) { \r
+                       newdat.put("date", r.getRec_pattern());\r
+               }\r
+               else {\r
+                       int i = 1;\r
+                       for ( String s : RPTPTN ) {\r
+                               if ( s.equals(r.getRec_pattern()) == true ) {\r
+                                       newdat.put("date", String.valueOf(i));\r
+                               }\r
+                               i++;\r
+                       }\r
+               }\r
+               \r
+               // 番組詳細\r
+               //newdat.put("detail", r.getDetail()); // 上書きしてた…('д`)\r
+\r
+\r
+               newdat.put("start_hour", r.getAhh());\r
+               newdat.put("start_minute", r.getAmm());\r
+               newdat.put("end_hour", r.getZhh());\r
+               newdat.put("end_minute", r.getZmm());\r
+\r
+               // 自動チャプター関連\r
+               newdat.put("bAutoChapter"               , text2value(xchapter, r.getRec_xchapter()));\r
+\r
+               // 追加\r
+               newdat.put("disc"                               , "0");\r
+               newdat.put("aspect"                             , "1");\r
+               newdat.put("bVPerform"                  , "0");\r
+               //newdat.put("bAutoChapter"             , "0");\r
+               newdat.put("dtv_sid"                    , "0");\r
+               newdat.put("dtv_nid"                    , "0");\r
+               newdat.put("end_form"                   , "0");\r
+               \r
+               return(newdat);\r
+       }\r
+\r
+       private String _recstr2audio(String str)\r
+       {\r
+               for ( TextValueSet t : arate ) {\r
+                       if (t.getText().equals(str)) {\r
+                               return(t.getValue());\r
+                       }\r
+               }\r
+               return(null);\r
+       }\r
+       private String _recstr2mode(String str)\r
+       {\r
+               for ( TextValueSet t : vrate ) {\r
+                       if (t.getText().equals(str)) {\r
+                               return(t.getValue());\r
+                       }\r
+               }\r
+               return(null);\r
+       }\r
+       private String _recstr2genre(String genre)\r
+       {\r
+               if (genre.equals(TVProgram.ProgGenre.MOVIE.toString())) {\r
+                       return "0";\r
+               }\r
+               else if (genre.equals(TVProgram.ProgGenre.MUSIC.toString())) {\r
+                       return "1";\r
+               }\r
+               else if (genre.equals(TVProgram.ProgGenre.DORAMA.toString())) {\r
+                       return "2";\r
+               }\r
+               else if (genre.equals(TVProgram.ProgGenre.ANIME.toString())) {\r
+                       return "3";\r
+               }\r
+               else if (genre.equals(TVProgram.ProgGenre.SPORTS.toString())) {\r
+                       return "4";\r
+               }\r
+               else if (genre.equals(TVProgram.ProgGenre.DOCUMENTARY.toString())) {\r
+                       return "5";\r
+               }\r
+               //else if (genre.equals(TVProgram.ProgGenre.THEATER.toString())) {\r
+               //      return "6";\r
+               //}\r
+               else if (genre.equals(TVProgram.ProgGenre.HOBBY.toString())) {\r
+                       return "7";\r
+               }\r
+               else if (genre.equals(TVProgram.ProgGenre.VARIETY.toString())) {\r
+                       return "8";\r
+               }\r
+               else {\r
+                       return "10";\r
+               }\r
+       }\r
+       private String _recstr2dvdcompat(String str)\r
+       {\r
+               for ( TextValueSet t : dvdcompat ) {\r
+                       if (t.getText().equals(str)) {\r
+                               return(t.getValue());\r
+                       }\r
+               }\r
+               return(null);\r
+       }\r
+       \r
+       \r
+       //\r
+       private ArrayList<ReserveList> GetRdReservedList(String response) {\r
+               \r
+               //\r
+               response = response.replaceAll("\n","");\r
+               \r
+               ArrayList<ReserveList> newReserveList = new ArrayList<ReserveList>();\r
+               \r
+               String buf = "";\r
+               Matcher ma = Pattern.compile("<tr [^>]*?>([\\s\\S]*?)</tr>").matcher(response);\r
+               while (ma.find()) {\r
+\r
+                       // 個々のデータを取り出す\r
+                       buf = ma.group(1);\r
+                       \r
+                       Matcher mb = null; \r
+                       mb = Pattern.compile(">新規予約<").matcher(buf);\r
+                       if (mb.find()) {\r
+                               break;\r
+                       }\r
+                       \r
+                       ReserveList entry = new ReserveList();\r
+                       \r
+                       String[] d = new String[9];\r
+                       mb = Pattern.compile("<td .*?>(.*?)</td>").matcher(buf);\r
+                       for (int i=0; i<9; i++) {\r
+                               if ( mb.find()) {\r
+                                       d[i] = mb.group(1);\r
+                               }\r
+                       }\r
+                       \r
+                       // 予約実行ON/OFF\r
+                       mb = Pattern.compile("check_off\\.gif").matcher(d[1]);\r
+                       if (mb.find()) {\r
+                               entry.setExec(false);\r
+                       }\r
+                       \r
+                       // 予約名のエスケープを解除する\r
+                       String title = d[2];\r
+                       mb = Pattern.compile("<a .*?>(.+?)</a>").matcher(title);\r
+                       if (mb.find()) title = mb.group(1);\r
+                       mb = Pattern.compile("<BR>").matcher(title);    // 余計な改行の削除\r
+                       if (mb.find()) title = mb.replaceAll("");\r
+                       mb = Pattern.compile("&quot;").matcher(title);\r
+                       if (mb.find()) title = mb.replaceAll("\"");\r
+                       mb = Pattern.compile("&lt;").matcher(title);\r
+                       if (mb.find()) title = mb.replaceAll("<");\r
+                       mb = Pattern.compile("&gt;").matcher(title);\r
+                       if (mb.find()) title = mb.replaceAll(">");\r
+                       mb = Pattern.compile("&nbsp;").matcher(title);\r
+                       if (mb.find()) title = mb.replaceAll(" ");\r
+                       \r
+                       entry.setId(String.valueOf(Integer.valueOf(d[0])-1));\r
+                       entry.setRec_pattern(d[4]);\r
+                       entry.setRec_pattern_id(getRec_pattern_Id(entry.getRec_pattern()));\r
+                       mb = Pattern.compile("(\\d\\d):(\\d\\d).*?(\\d\\d):(\\d\\d)").matcher(d[5]);\r
+                       if (mb.find()) {\r
+                               entry.setAhh(mb.group(1));\r
+                               entry.setAmm(mb.group(2));\r
+                               entry.setZhh(mb.group(3));\r
+                               entry.setZmm(mb.group(4));\r
+                       }\r
+                       entry.setRec_nextdate(CommonUtils.getNextDate(entry));\r
+                       //entry.setRec_nextdate(getNextDate(entry.getRec_pattern(), entry.getZhh()+":"+entry.getZmm()));\r
+                       entry.setRec_min(CommonUtils.getRecMin(entry.getAhh(), entry.getAmm(), entry.getZhh(), entry.getZmm()));\r
+                       getStartEndDateTime(entry);\r
+                       \r
+                       entry.setTuner("");\r
+                       entry.setRec_mode(d[7]);\r
+                       entry.setTitle(title);\r
+                       entry.setTitlePop(TraceProgram.replacePop(title));\r
+                       entry.setCh_name(cc.getCH_REC2WEB(d[3]));\r
+                       entry.setChannel(d[3]);\r
+\r
+                       entry.setRec_audio(d[8]);\r
+                       //entry.rec_folder = data.get();        // 予約一覧からはとれない\r
+                       //entry.rec_genre = data.get();         // 予約一覧からはとれない\r
+                       \r
+                       // 予約情報を保存\r
+                       newReserveList.add(entry.clone());\r
+               }\r
+               \r
+               return(newReserveList);\r
+       }\r
+}\r
diff --git a/TinyBannavi/src/tainavi/PlugIn_RecRD_XS57.java b/TinyBannavi/src/tainavi/PlugIn_RecRD_XS57.java
new file mode 100644 (file)
index 0000000..806d8fa
--- /dev/null
@@ -0,0 +1,1032 @@
+package tainavi;\r
+\r
+import java.io.File;\r
+import java.io.UnsupportedEncodingException;\r
+import java.net.Authenticator;\r
+import java.net.URLEncoder;\r
+import java.util.ArrayList;\r
+import java.util.Hashtable;\r
+import java.util.regex.Matcher;\r
+import java.util.regex.Pattern;\r
+\r
+/*\r
+ * \r
+ */\r
+\r
+public class PlugIn_RecRD_XS57 extends HDDRecorderUtils implements HDDRecorder,Cloneable {\r
+\r
+       public PlugIn_RecRD_XS57 clone() {\r
+               return (PlugIn_RecRD_XS57) super.clone();\r
+       }\r
+       \r
+       private static final String thisEncoding = "MS932";\r
+\r
+       /* 必須コード  - ここから */\r
+       \r
+       // 種族の特性\r
+       public String getRecorderId() { return "RD-XS57"; }\r
+       public RecType getType() { return RecType.RECORDER; }\r
+       \r
+       @Override\r
+       public String getLabel_XChapter() { return "無音部分チャプタ分割"; }\r
+       @Override\r
+       public String getLabel_MsChapter() { return "DVD時チャプタ分割"; }\r
+       @Override\r
+       public String getLabel_MvChapter() { return "音多連動チャプタ分割"; }\r
+       \r
+       // 個体の特性\r
+       //private GetRDStatus gs = new GetRDStatus();\r
+       private ChannelCode cc = new ChannelCode(getRecorderId());\r
+       private String rsvedFile = "";\r
+       \r
+       private String errmsg = "";\r
+\r
+       // 公開メソッド\r
+       \r
+       /*\r
+        * \r
+        */\r
+       public ChannelCode getChCode() {\r
+               return cc;\r
+       }\r
+       \r
+       /*\r
+        * チャンネルリモコン機能\r
+        */\r
+       public boolean ChangeChannel(String Channel) {\r
+               return false;\r
+       }\r
+\r
+       /*\r
+        *      レコーダーから予約一覧を取得する \r
+        */\r
+       public boolean GetRdReserve(boolean force)\r
+       {\r
+               System.out.println("Run: GetRdReserve("+force+")");\r
+               \r
+               errmsg = "";\r
+               \r
+               //\r
+               rsvedFile = "env/reserved."+getIPAddr()+"_"+getPortNo()+"_"+getRecorderId()+".xml";\r
+               String vrateTFile = "env/videorate."+getIPAddr()+"_"+getPortNo()+"_"+getRecorderId()+".xml";\r
+               String arateTFile = "env/audiorate."+getIPAddr()+"_"+getPortNo()+"_"+getRecorderId()+".xml";\r
+               String folderTFile = "env/folders."+getIPAddr()+"_"+getPortNo()+"_"+getRecorderId()+".xml";\r
+               String encoderTFile = "env/encoders."+getIPAddr()+"_"+getPortNo()+"_"+getRecorderId()+".xml";\r
+               String dvdcompatTFile = "env/dvdcompat."+getIPAddr()+"_"+getPortNo()+"_"+getRecorderId()+".xml";\r
+               String deviceTFile = "env/device."+getIPAddr()+"_"+getPortNo()+"_"+getRecorderId()+".xml";\r
+               String xChapterTFile = "env/xchapter."+getIPAddr()+"_"+getPortNo()+"_"+getRecorderId()+".xml";\r
+               String msChapterTFile = "env/mschapter."+getIPAddr()+"_"+getPortNo()+"_"+getRecorderId()+".xml";\r
+               String mvChapterTFile = "env/mvchapter."+getIPAddr()+"_"+getPortNo()+"_"+getRecorderId()+".xml";\r
+               String chValueTFile = "env/chvalue."+getIPAddr()+"_"+getPortNo()+"_"+getRecorderId()+".xml";\r
+               String chTypeTFile = "env/chtype."+getIPAddr()+"_"+getPortNo()+"_"+getRecorderId()+".xml";\r
+\r
+               File f = new File(rsvedFile);\r
+               if ( force == false && f.exists()) {\r
+                       // キャッシュから読み出し(録画設定ほか)\r
+                       vrate = TVSload(vrateTFile);\r
+                       if ( vrate.size() > 0 ) {\r
+                               System.out.println("+video rate="+vrateTFile);\r
+                       }\r
+                       arate = TVSload(arateTFile);\r
+                       if ( arate.size() > 0 ) {\r
+                               System.out.println("+audio rate="+arateTFile);\r
+                       }\r
+                       folder = TVSload(folderTFile);\r
+                       if ( folder.size() > 0 ) {\r
+                               System.out.println("+folder="+folderTFile);\r
+                       }\r
+                       encoder = TVSload(encoderTFile);\r
+                       if ( encoder.size() > 0 ) {\r
+                               System.out.println("+encoder="+encoderTFile);\r
+                       }\r
+                       dvdcompat = TVSload(dvdcompatTFile);\r
+                       if ( dvdcompat.size() > 0 ) {\r
+                               System.out.println("+dvdcompat="+dvdcompatTFile);\r
+                       }\r
+                       device = TVSload(deviceTFile);\r
+                       if ( device.size() > 0 ) {\r
+                               System.out.println("+device="+deviceTFile);\r
+                       }\r
+                       // 自動チャプタ関連\r
+                       xchapter = TVSload(xChapterTFile);\r
+                       if ( xchapter.size() > 0 ) {\r
+                               System.out.println("+xchapter="+xChapterTFile);\r
+                       }\r
+                       mschapter = TVSload(msChapterTFile);\r
+                       if ( mschapter.size() > 0 ) {\r
+                               System.out.println("+mschapter="+msChapterTFile);\r
+                       }\r
+                       mvchapter = TVSload(mvChapterTFile);\r
+                       if ( mvchapter.size() > 0 ) {\r
+                               System.out.println("+mvchapter="+mvChapterTFile);\r
+                       }\r
+                       // チャンネルコードバリュー\r
+                       chvalue = TVSload(chValueTFile);\r
+                       if ( chvalue.size() > 0 ) {\r
+                               System.out.println("+chvalue="+chValueTFile);\r
+                       }\r
+                       chtype = TVSload(chTypeTFile);\r
+                       if ( chtype.size() > 0 ) {\r
+                               System.out.println("+chtype="+chTypeTFile);\r
+                       }\r
+                       \r
+                       // キャッシュから読み出し(予約一覧)\r
+                       setReserves(ReservesFromFile(rsvedFile));\r
+                       if (getReserves().size() > 0) {\r
+                               System.out.println("+read from="+rsvedFile);\r
+                       }\r
+                       \r
+                       // なぜか設定ファイルが空になっている場合があるので、その際は再取得する\r
+                       if (vrate.size()>0 && arate.size()>0 &&\r
+                                       folder.size()>0 && encoder.size()>0 && device.size()>0 &&\r
+                                       xchapter.size()>0 && mschapter.size()>0 && mvchapter.size()>0) {\r
+                               return(true);\r
+                       }\r
+                       \r
+                       //getReserves().removeAll(getReserves());\r
+               }\r
+               \r
+               // おまじない\r
+               Authenticator.setDefault(new MyAuthenticator(getUser(), getPasswd()));\r
+\r
+               // RDから予約一覧を取り出す\r
+               Matcher ma = null;\r
+               String idx = "";\r
+               String header="";\r
+               String response="";\r
+               {\r
+                       reportProgress("get reserved list(1/4).");\r
+                       String[] d = reqGET("http://"+getIPAddr()+":"+getPortNo()+"/reserve/b_rsv.htm",null);\r
+                       header = d[0];\r
+                       response = d[1];\r
+                       \r
+                       if (response == null) {\r
+                               errmsg = "レコーダーが反応しません";\r
+                               return(false);\r
+                       }\r
+               }\r
+               ma = Pattern.compile("/reserve/(\\d+?)/reserve.htm").matcher(response);\r
+               if (ma.find()) {\r
+                       idx = ma.group(1);\r
+                       reportProgress("get reserved list(2/4).");\r
+                       String[] d = reqGET("http://"+getIPAddr()+":"+getPortNo()+"/reserve/"+idx+"/reserve.htm",null);\r
+                       header = d[0];\r
+                       response = d[1];\r
+                       \r
+                       if (response == null) {\r
+                               errmsg = "レコーダーが反応しません";\r
+                               return(false);\r
+                       }\r
+               }\r
+               \r
+               // 予約エントリー数を取得する\r
+               int RsvCnt = 0;\r
+               ma = Pattern.compile("RsvCnt\\s*=\\s*(\\d+?);").matcher(response);\r
+               if ( ! ma.find()) {\r
+                       errmsg = "レコーダーからの情報取得に失敗しました(予約一覧)";\r
+                       return false;\r
+               }\r
+               RsvCnt = Integer.valueOf(ma.group(1));\r
+               \r
+               // (1)録画設定の取得\r
+               {\r
+                       // ハング防止のおまじない\r
+                       reportProgress("get reserved list(3/4).");\r
+                       reqGET("http://"+getIPAddr()+":"+getPortNo()+"/reserve/"+idx+"/b_rsvinfo.htm?0&"+(RsvCnt+1),null);\r
+               }\r
+               {\r
+                       reportProgress("get reserved list(4/4).");\r
+                       String[] d = reqGET("http://"+getIPAddr()+":"+getPortNo()+"/reserve/"+idx+"/rsvinfo.htm",null);\r
+                       String hdr = d[0];\r
+                       String res = d[1];\r
+                       Matcher mb = null;\r
+                       \r
+                       // 正常取得チェック\r
+                       if ( ! res.matches("[\\s\\S]*var hdd_folder_text[\\s\\S]*")) {\r
+                               errmsg = "レコーダーからの情報取得に失敗しました(録画設定)";\r
+                               return false;\r
+                       }\r
+                       \r
+                       // (1-1)画質設定\r
+                       setSettingEtc(vrate,"vrate",1,res);\r
+                       TVSsave(vrate, vrateTFile);\r
+                       \r
+                       // (1-2)音質設定\r
+                       setSettingEtc(arate,"amode",0,res);\r
+                       TVSsave(arate, arateTFile);\r
+                       \r
+                       // (1-3)フォルダ一覧\r
+                       setSettingEtc(folder,"hdd_folder",1,res);\r
+                       TVSsave(folder, folderTFile);\r
+                       \r
+                       // (1-4)エンコーダ\r
+                       encoder.clear();\r
+                       \r
+                       mb = Pattern.compile("<!--[\\s\\S]*?if\\( double_encode_flg == 1 \\) \\{([\\s\\S]*?)\\}\\s*\\}\\s*-->").matcher(res);\r
+                       if (mb.find()) {\r
+                               Matcher mc = Pattern.compile("if \\( encoder == 1 \\) \\{([\\s\\S]*?)\\}").matcher(mb.group(1));\r
+                               if (mc.find()) {\r
+                                       Matcher md = Pattern.compile("name=enc_type value=\\\\\"(.+?)\\\\\"[\\s\\S]+?toru_(.+?)\\.gif").matcher(mc.group(1));\r
+                                       while (md.find()) {\r
+                                               TextValueSet t = new TextValueSet();\r
+                                               t.setText(md.group(2));\r
+                                               t.setValue(md.group(1));\r
+                                               encoder.add(t);\r
+                                       }\r
+                               }\r
+                       }\r
+                       \r
+                       TVSsave(encoder, encoderTFile);\r
+                       \r
+                       // (1-5)DVD互換モード\r
+                       setSettingEtc(dvdcompat,"dvdr",0,res);\r
+                       TVSsave(dvdcompat, dvdcompatTFile);\r
+                       \r
+                       // (1-6)記録先デバイス\r
+                       setSettingEtc(device,"media",0,res);\r
+                       TVSsave(device, deviceTFile);\r
+                       \r
+                       // (1-7)自動チャプター関連\r
+                       setSettingEtc(xchapter,"mutechapter",0,res);\r
+                       TVSsave(xchapter, xChapterTFile);\r
+                       \r
+                       setSettingEtc(mschapter,"dvdchapter",0,res);\r
+                       TVSsave(mschapter, msChapterTFile);\r
+\r
+                       setSettingEtc(mvchapter,"mvchapter",0,res);\r
+                       TVSsave(mvchapter, mvChapterTFile);\r
+                       \r
+                       // (1-8)チャンネルコードバリュー\r
+                       chvalue.clear();\r
+                       chtype.clear();\r
+                       for ( String typ : new String[] { "uvd","bsd","csd","uva","bsa","l1","l2","l3" } ) {\r
+                               String txtkey = typ+"_ch_text";\r
+                               String valkey = typ+"_ch_value";\r
+                               Matcher mc = Pattern.compile("var "+txtkey+"\\s*= new Array\\(([\\s\\S]+?)\\);").matcher(res);\r
+                               Matcher md = Pattern.compile("var "+valkey+"\\s*= new Array\\(([\\s\\S]+?)\\);").matcher(res);\r
+                               if ( mc.find() && md.find() ) {\r
+                                       Matcher me = Pattern.compile("\"(.+?)\",?").matcher(mc.group(1));\r
+                                       Matcher mf = null;\r
+                                       if ( typ.equals("uva") ) {\r
+                                               mf = Pattern.compile("(\\d+),").matcher(md.group(1));\r
+                                       }\r
+                                       else {\r
+                                               mf = Pattern.compile("\"([^\"]+?)\",").matcher(md.group(1));\r
+                                       }\r
+                                       while ( me.find() && mf.find() ) {\r
+                                               TextValueSet t = new TextValueSet();\r
+                                               t.setText(me.group(1));\r
+                                               t.setValue((typ.matches("^l[123]")?("L"):(""))+mf.group(1));\r
+                                               chvalue.add(t);\r
+                                               \r
+                                               TextValueSet x = new TextValueSet();\r
+                                               x.setText(mf.group(1));\r
+                                               x.setValue(typ);\r
+                                               chtype.add(x);\r
+                                       }\r
+                               }\r
+                       }\r
+                       TVSsave(chvalue, chValueTFile);\r
+                       TVSsave(chtype, chTypeTFile);\r
+               }\r
+               \r
+               // 予約一覧データの分析\r
+               // 情報ブロックをとりだし…\r
+               {\r
+                       //int cnt = 0;\r
+                       ArrayList<ReserveList> newReserveList = new ArrayList<ReserveList>();\r
+                       \r
+                       ma = Pattern.compile("(c1\\[\\d+?\\]=[\\s\\S]+?\";)\\n").matcher(response);\r
+                       while ( ma.find() ) {\r
+                               // 個々のデータを取り出す\r
+                               ReserveList entry = new ReserveList();\r
+                               \r
+                               Matcher mb = null;\r
+                                       \r
+                               String[] d = new String[17];\r
+                               mb = Pattern.compile("c\\d+?\\[\\d+?\\]=\"(.*?)\";").matcher(ma.group(1));\r
+                               for (int i=0; i<d.length; i++) {\r
+                                       if ( mb.find()) {\r
+                                               d[i] = mb.group(1);\r
+                                       }\r
+                                       //System.out.println(i+") "+d[i]);\r
+                               }\r
+                               \r
+                               // 実行ON/OFF\r
+                               if ( ! d[1].equals("1")) {\r
+                                       entry.setExec(false);\r
+                               }\r
+                               \r
+                               // 予約名のエスケープを解除する\r
+                               String title = d[2];\r
+                               mb = Pattern.compile("<BR>").matcher(title);    // 余計な改行の削除\r
+                               if (mb.find()) title = mb.replaceAll("");\r
+                               mb = Pattern.compile("&quot;").matcher(title);\r
+                               if (mb.find()) title = mb.replaceAll("\"");\r
+                               mb = Pattern.compile("&lt;").matcher(title);\r
+                               if (mb.find()) title = mb.replaceAll("<");\r
+                               mb = Pattern.compile("&gt;").matcher(title);\r
+                               if (mb.find()) title = mb.replaceAll(">");\r
+                               mb = Pattern.compile("&nbsp;").matcher(title);\r
+                               if (mb.find()) title = mb.replaceAll(" ");\r
+                               \r
+                               entry.setId(d[0]);\r
+                               entry.setRec_pattern(d[5]);\r
+                               entry.setRec_pattern_id(getRec_pattern_Id(entry.getRec_pattern()));\r
+                               mb = Pattern.compile("(\\d\\d):(\\d\\d).*?(\\d\\d):(\\d\\d)").matcher(d[6]+"-"+d[7]);\r
+                               if (mb.find()) {\r
+                                       entry.setAhh(mb.group(1));\r
+                                       entry.setAmm(mb.group(2));\r
+                                       entry.setZhh(mb.group(3));\r
+                                       entry.setZmm(mb.group(4));\r
+                               }\r
+                               entry.setRec_nextdate(CommonUtils.getNextDate(entry));\r
+                               //entry.setRec_nextdate(getNextDate(entry.getRec_pattern(), entry.getZhh()+":"+entry.getZmm()));\r
+                               entry.setRec_min(CommonUtils.getRecMin(entry.getAhh(), entry.getAmm(), entry.getZhh(), entry.getZmm()));\r
+                               getStartEndDateTime(entry);\r
+                               \r
+                               String enc = value2text(encoder, d[3]);\r
+                               if (enc.length() > 0) {\r
+                                       entry.setTuner(enc);\r
+                               }\r
+                               else {\r
+                                       entry.setTuner("--");\r
+                               }\r
+                               \r
+                               entry.setRec_mode(d[9]);\r
+                               entry.setTitle(title);\r
+                               entry.setTitlePop(TraceProgram.replacePop(title));\r
+                               entry.setCh_name(cc.getCH_REC2WEB(d[4]));\r
+                               entry.setChannel(d[4]);\r
+       \r
+                               //entry.setRec_audio(_recstr2audio(d[10]));\r
+                               entry.setRec_audio(d[10]);\r
+\r
+                               //entry.rec_folder = data.get();        // 予約一覧からはとれない\r
+                               //entry.rec_genre = data.get();         // 予約一覧からはとれない\r
+\r
+                               // タイトル自動補完フラグなど本体からは取得できない情報を引き継ぐ\r
+                               copyAttributes(entry, getReserves());\r
+                               \r
+                               // 予約情報を保存\r
+                               newReserveList.add(entry.clone());\r
+                       }\r
+                       setReserves(newReserveList);\r
+               }\r
+               \r
+               // 取得した情報の表示\r
+               System.out.println("---Reserved List Start---");\r
+               for ( int i = 0; i<getReserves().size(); i++ ) {\r
+                       // 詳細情報の取得\r
+                       ReserveList e = getReserves().get(i);\r
+                       System.out.println(String.format("[%s] %s\t%s\t%s\t%s:%s\t%s:%s\t%sm\t%s\t%s\t%s\t%s",\r
+                                       (i+1), e.getId(), e.getRec_pattern(), e.getRec_nextdate(), e.getAhh(), e.getAmm(), e.getZhh(),  e.getZmm(),     e.getRec_min(), e.getRec_mode(), e.getTitle(), e.getChannel(), e.getCh_name()));\r
+               }\r
+               System.out.println("---Reserved List End---");\r
+               \r
+               // キャッシュに保存\r
+               ReservesToFile(getReserves(), rsvedFile);\r
+               \r
+               return(true);\r
+       }\r
+       \r
+       /*\r
+        *      予約を実行する\r
+        */\r
+       public boolean PostRdEntry(ReserveList r)\r
+       {\r
+               //\r
+               if (cc.getCH_WEB2CODE(r.getCh_name()) == null) {\r
+                       errmsg = "【警告】Web番組表の放送局名「"+r.getCh_name()+"」をCHコードに変換できません。CHコード設定を修正してください。" ;\r
+                       System.out.println(errmsg);\r
+                       return(false);\r
+               }\r
+               \r
+               System.out.println("Run: PostRdEntry("+r.getTitle()+")");\r
+               \r
+               errmsg = "";\r
+\r
+               // おまじない\r
+               Authenticator.setDefault(new MyAuthenticator(getUser(), getPasswd()));\r
+               \r
+               // RDから予約一覧を取り出す\r
+               Matcher ma = null;\r
+               String idx = "";\r
+               String header;\r
+               String response;\r
+               {\r
+                       reportProgress("get reserved list(1/7).");\r
+                       String[] d = reqGET("http://"+getIPAddr()+":"+getPortNo()+"/reserve/b_rsv.htm",null);\r
+                       header = d[0];\r
+                       response = d[1];\r
+                       \r
+                       if ( response == null ) {\r
+                               errmsg = "レコーダーが反応しません。";\r
+                               return(false);\r
+                       }\r
+               }\r
+               ma = Pattern.compile("/reserve/(\\d+?)/reserve.htm").matcher(response);\r
+               if (ma.find()) {\r
+                       reportProgress("get reserved list(2/7).");\r
+                       idx = ma.group(1);\r
+                       String[] d = reqGET("http://"+getIPAddr()+":"+getPortNo()+"/reserve/"+idx+"/reserve.htm",null);\r
+                       header = d[0];\r
+                       response = d[1];\r
+                       \r
+                       if ( response == null ) {\r
+                               errmsg = "レコーダーが反応しません。";\r
+                               return(false);\r
+                       }\r
+               }\r
+               \r
+               // 予約エントリー数を取得する\r
+               int RsvCnt = 0;\r
+               ma = Pattern.compile("RsvCnt\\s*=\\s*(\\d+?);").matcher(response);\r
+               if (ma.find()) {\r
+                       RsvCnt = Integer.valueOf(ma.group(1));\r
+               }\r
+       \r
+               // RDに新規登録要請\r
+               {\r
+                       // ハング防止のおまじない\r
+                       reportProgress("get program(3/7).");\r
+                       reqGET("http://"+getIPAddr()+":"+getPortNo()+"/reserve/"+idx+"/b_rsvinfo.htm?0&"+(RsvCnt+1),null);\r
+                       \r
+                       reportProgress("get program(4/7).");\r
+                       reqGET("http://"+getIPAddr()+":"+getPortNo()+"/reserve/"+idx+"/rsvinfo.htm",null);\r
+               }\r
+\r
+               // POSTデータを変換する\r
+               Hashtable<String, String> pdat = modPostdata(r);\r
+               \r
+               // RDへの情報作成\r
+               String pstr = joinPoststr(pdat);\r
+\r
+               // RDへ情報送信\r
+               {               \r
+                       reportProgress("send request.(5/7)");\r
+                       String[] d = reqPOST("http://"+getIPAddr()+":"+getPortNo()+"/reserve/"+idx+"/entry.htm", pstr, null);\r
+                       header = d[0];\r
+                       response = d[1];\r
+               }\r
+               \r
+               // 登録結果の確認\r
+               ma = Pattern.compile("alert\\(msg\\)").matcher(response);\r
+               if ( ma.find() ) {\r
+                       Matcher mb = Pattern.compile("\\bmsg=\"([\\s\\S]+?)\";").matcher(response);\r
+                       if (mb.find()) {\r
+                               errmsg = mb.group(1);\r
+                               System.out.printf("\n<<< Message from RD >>> \"%s\"\n\n", errmsg);\r
+                               Matcher mc = Pattern.compile("(予約時間が重複しています。|修正しました。)").matcher(errmsg);\r
+                               if ( ! mc.find() ) {\r
+                                       return(false);\r
+                               }\r
+                       }                       \r
+               }\r
+\r
+\r
+               \r
+               /*\r
+                * 予約情報の調整 \r
+                */\r
+               \r
+               // 予約ID番号を取得(キャッシュに存在しない番号が新番号)\r
+               {\r
+                       reportProgress("get identifier(6/7).");\r
+                       String[] d = reqGET("http://"+getIPAddr()+":"+getPortNo()+"/reserve/b_rsv.htm",null);\r
+                       header = d[0];\r
+                       response = d[1];\r
+               }\r
+               ma = Pattern.compile("/reserve/(\\d+?)/reserve.htm").matcher(response);\r
+               if (ma.find()) {\r
+                       idx = ma.group(1);\r
+                       reportProgress("get identifier(7/7).");\r
+                       String[] d = reqGET("http://"+getIPAddr()+":"+getPortNo()+"/reserve/"+idx+"/reserve.htm",null);\r
+                       header = d[0];\r
+                       response = d[1];\r
+               }\r
+               r.setId(getNewId(response));\r
+\r
+               // 予約パターンID\r
+               r.setRec_pattern_id(getRec_pattern_Id(r.getRec_pattern()));\r
+\r
+               // 次回予定日\r
+               r.setRec_nextdate(CommonUtils.getNextDate(r));\r
+               //r.setRec_nextdate(getNextDate(r.getRec_pattern(), r.getZhh()+":"+r.getZmm()));\r
+               \r
+               // 録画長\r
+               r.setRec_min(CommonUtils.getRecMin(r.getAhh(),r.getAmm(),r.getZhh(),r.getZmm()));\r
+               \r
+               // 開始日時・終了日時\r
+               getStartEndDateTime(r);\r
+               \r
+               // 予約リストを更新\r
+               getReserves().add(r);\r
+               \r
+               // キャッシュに保存\r
+               ReservesToFile(getReserves(), rsvedFile);\r
+               \r
+               System.out.printf("\n<<< Message from RD >>> \"%s\"\n\n", "正常に登録できました。");\r
+               return(true);\r
+       }\r
+\r
+       \r
+       /*\r
+        *      予約を更新する\r
+        */\r
+       public boolean UpdateRdEntry(ReserveList o, ReserveList r) {\r
+               //\r
+               if (cc.getCH_WEB2CODE(r.getCh_name()) == null) {\r
+                       errmsg = "【警告】Web番組表の放送局名「"+r.getCh_name()+"」をCHコードに変換できません。CHコード設定を修正してください。" ;\r
+                       System.out.println(errmsg);\r
+                       return(false);\r
+               }\r
+               \r
+               System.out.println("Run: UpdateRdEntry()");\r
+               \r
+               errmsg = "";\r
+\r
+               // おまじない\r
+               Authenticator.setDefault(new MyAuthenticator(getUser(), getPasswd()));\r
+               \r
+               // 更新準備\r
+               Matcher ma = null;\r
+               String idx = "";\r
+               int lineno = 0;\r
+               String header;\r
+               String response;\r
+               {\r
+                       reportProgress("get identifier(1/5).");\r
+                       String[] d = reqGET("http://"+getIPAddr()+":"+getPortNo()+"/reserve/b_rsv.htm",null);\r
+                       header = d[0];\r
+                       response = d[1];\r
+               }\r
+               ma = Pattern.compile("/reserve/(\\d+?)/reserve.htm").matcher(response);\r
+               if (ma.find()) {\r
+                       idx = ma.group(1);\r
+                       reportProgress("get identifier(2/5).");\r
+                       String[] d = reqGET("http://"+getIPAddr()+":"+getPortNo()+"/reserve/"+idx+"/reserve.htm",null);\r
+                       header = d[0];\r
+                       response = d[1];\r
+               }\r
+               ma = Pattern.compile("c1\\[(\\d+?)\\]=\""+r.getId()+"\";").matcher(response);\r
+               if ( ma.find() ) {\r
+                       lineno = Integer.valueOf(ma.group(1))+1;\r
+               }\r
+               \r
+               // RDに更新要請\r
+               {\r
+                       // ハング防止のおまじない\r
+                       reportProgress("get program(3/5).");\r
+                       reqGET("http://"+getIPAddr()+":"+getPortNo()+"/reserve/"+idx+"/b_rsvinfo.htm?"+r.getId()+"&"+lineno,null);\r
+                       \r
+                       reportProgress("get program(4/5).");\r
+                       reqGET("http://"+getIPAddr()+":"+getPortNo()+"/reserve/"+idx+"/rsvinfo.htm",null);\r
+               }\r
+\r
+               // POSTデータを変換する\r
+               Hashtable<String, String> pdat = modPostdata(r);\r
+               \r
+               // RDへの情報作成\r
+               String pstr = joinPoststr(pdat);\r
+\r
+               // RDへ情報送信\r
+               {               \r
+                       reportProgress("send request.(5/5)");\r
+                       String[] d = reqPOST("http://"+getIPAddr()+":"+getPortNo()+"/reserve/"+idx+"/entry.htm", pstr, null);\r
+                       header = d[0];\r
+                       response = d[1];\r
+               }\r
+\r
+               // 更新結果の確認\r
+               ma = Pattern.compile("alert\\(msg\\)").matcher(response);\r
+               if ( ma.find() ) {\r
+                       Matcher mb = Pattern.compile("\\bmsg=\"([\\s\\S]+?)\";").matcher(response);\r
+                       if (mb.find()) {\r
+                               errmsg = mb.group(1);\r
+                               System.out.printf("\n<<< Message from RD >>> \"%s\"\n\n", errmsg);\r
+                               Matcher mc = Pattern.compile("(予約時間が重複しています。|修正しました。)").matcher(errmsg);\r
+                               if ( ! mc.find() ) {\r
+                                       return(false);\r
+                               }\r
+                       }                       \r
+               }\r
+\r
+               System.out.printf("\n<<< Message from RD >>> \"%s\"\n\n", "正常に更新できました。");\r
+\r
+               /*\r
+                * 予約情報の調整 \r
+                */\r
+\r
+               // 予約パターンID\r
+               r.setRec_pattern_id(getRec_pattern_Id(r.getRec_pattern()));\r
+               \r
+               // 次回予定日\r
+               r.setRec_nextdate(CommonUtils.getNextDate(r));\r
+               //r.setRec_nextdate(getNextDate(r.getRec_pattern(), r.getZhh()+":"+r.getZmm()));\r
+               \r
+               // 録画長\r
+               r.setRec_min(CommonUtils.getRecMin(r.getAhh(),r.getAmm(),r.getZhh(),r.getZmm()));\r
+               \r
+               // 開始日時・終了日時\r
+               getStartEndDateTime(r);\r
+               \r
+               // 情報置き換え\r
+               getReserves().remove(o);\r
+               getReserves().add(r);\r
+\r
+               // キャッシュに保存\r
+               ReservesToFile(getReserves(), rsvedFile);\r
+               \r
+               return(true);\r
+       }\r
+\r
+       /*\r
+        *      予約を削除する\r
+        */\r
+       public ReserveList RemoveRdEntry(String delid) {\r
+               System.out.println("Run: RemoveRdEntry()");\r
+               \r
+               errmsg = "";\r
+\r
+               // おまじない\r
+               Authenticator.setDefault(new MyAuthenticator(getUser(), getPasswd()));\r
+\r
+               // 削除対象を探す\r
+               ReserveList rx = null;\r
+               for (  ReserveList reserve : getReserves() )  {\r
+                       if (reserve.getId().equals(delid)) {\r
+                               rx = reserve;\r
+                               break;\r
+                       }\r
+               }\r
+               if (rx == null) {\r
+                       return(null);\r
+               }\r
+\r
+               // 予約行番号を取得\r
+               Matcher ma = null;\r
+               String idx = "";\r
+               int lineno = 0;\r
+               String header;\r
+               String response;\r
+               {\r
+                       reportProgress("get identifier(1/5).");\r
+                       String[] d = reqGET("http://"+getIPAddr()+":"+getPortNo()+"/reserve/b_rsv.htm",null);\r
+                       header = d[0];\r
+                       response = d[1];\r
+               }\r
+               ma = Pattern.compile("/reserve/(\\d+?)/reserve.htm").matcher(response);\r
+               if (ma.find()) {\r
+                       idx = ma.group(1);\r
+                       reportProgress("get identifier(2/5).");\r
+                       String[] d = reqGET("http://"+getIPAddr()+":"+getPortNo()+"/reserve/"+idx+"/reserve.htm",null);\r
+                       header = d[0];\r
+                       response = d[1];\r
+               }\r
+               ma = Pattern.compile("c1\\[(\\d+?)\\]=\""+rx.getId()+"\";").matcher(response);\r
+               if ( ma.find() ) {\r
+                       lineno = Integer.valueOf(ma.group(1))+1;\r
+               }\r
+\r
+               // RDに削除要請\r
+               {\r
+                       // ハング防止のおまじない\r
+                       reportProgress("get program(3/5).");\r
+                       reqGET("http://"+getIPAddr()+":"+getPortNo()+"/reserve/"+idx+"/b_rsvinfo.htm?"+rx.getId()+"&"+lineno,null);\r
+                       \r
+                       reportProgress("get program(4/5).");\r
+                       reqGET("http://"+getIPAddr()+":"+getPortNo()+"/reserve/"+idx+"/rsvinfo.htm",null);\r
+               }\r
+               {               \r
+                       reportProgress("send request.(5/5)");\r
+                       String[] d = reqGET("http://"+getIPAddr()+":"+getPortNo()+"/reserve/"+idx+"/delete.htm", null);\r
+                       header = d[0];\r
+                       response = d[1];\r
+               }\r
+\r
+               // 削除結果の確認\r
+               ma = Pattern.compile("alert\\(msg\\)").matcher(response);\r
+               if ( ma.find() ) {\r
+                       Matcher mb = Pattern.compile("\\bmsg=\"([\\s\\S]+?)\";").matcher(response);\r
+                       if (mb.find()) {\r
+                               errmsg = mb.group(1);\r
+                               System.out.printf("\n<<< Message from RD >>> \"%s\"\n\n", errmsg);\r
+                       }\r
+                       return(null);\r
+               }\r
+               \r
+               // 予約リストを更新\r
+               getReserves().remove(rx);\r
+               \r
+               // キャッシュに保存\r
+               ReservesToFile(getReserves(), rsvedFile);\r
+               \r
+               System.out.printf("\n<<< Message from RD >>> \"%s\"\n\n", "正常に削除できました。");\r
+               return(rx);\r
+       }\r
+       \r
+       /*\r
+        * \r
+        */\r
+       public String getErrmsg() {\r
+               return(errmsg.replaceAll("\\\\r\\\\n", ""));\r
+       }\r
+\r
+       \r
+       \r
+       /* ここまで */\r
+       \r
+       \r
+       \r
+\r
+       \r
+       \r
+       /* 個別コード-ここから最後まで */\r
+\r
+       /*\r
+        * 非公開メソッド \r
+        */\r
+\r
+       private String joinPoststr(Hashtable<String, String> pdat)\r
+       {\r
+               String[] pkeys = {\r
+                       "bExec",\r
+                       "start_form",\r
+                       "title_name",\r
+                       "detail",\r
+                       "genre",\r
+                       "enc_type",\r
+                       "broadcast",\r
+                       "channel_list",\r
+                       "rec_priority",\r
+                       "maiyoubi_type",\r
+                       "date",\r
+                       "start_hour",\r
+                       "start_minute",\r
+                       "end_hour",\r
+                       "end_minute",\r
+                       "disc",\r
+                       "folder",\r
+                       "vrate",\r
+                       "amode",\r
+//                     "title_link",\r
+                       "auto_delete",\r
+//                     "video_es",\r
+//                     "audio_es",\r
+                       "dvdr",\r
+                       "lVoice",\r
+                       "edge_left",\r
+                       "bAutoChapter",\r
+                       "bDvdAutoChapter",\r
+                       "bAudioAutoChapter",\r
+                       "channel_no",\r
+                       "dtv_sid",\r
+                       "dtv_nid",\r
+                       "net_link",\r
+                       "add_ch_text",\r
+                       "add_ch_value",\r
+                       "sport_ext_submit",\r
+                       "title_link_submit",\r
+                       "end_form"\r
+               };\r
+               \r
+               String pstr = "";\r
+               for ( String key : pkeys ) {\r
+                       //$pdat{"$key"} =~ s/(\W)/'%'.unpack("H2", $1)/ego;\r
+                       if (pdat.get(key) == null) {\r
+                               pstr += key+"=&";\r
+                       }\r
+                       else {\r
+                               pstr += key+"="+pdat.get(key)+"&";\r
+                       }\r
+               }\r
+               pstr = pstr.substring(0, pstr.length()-1);\r
+               \r
+               return(pstr);\r
+       }\r
+       \r
+       private Hashtable<String, String> modPostdata(ReserveList r) {\r
+\r
+               Hashtable<String, String> newdat = new Hashtable<String, String>();\r
+               \r
+               try {\r
+                       // 実行するよ\r
+                       newdat.put("bExec", (r.getExec())?("ON"):("OFF"));\r
+       \r
+                       // 予約名\r
+                       try {\r
+                               newdat.put("title_name", URLEncoder.encode(r.getTitle(),thisEncoding));\r
+                       } catch (UnsupportedEncodingException e1) {\r
+                               // TODO Auto-generated catch block\r
+                               e1.printStackTrace();\r
+                       }\r
+       \r
+                       // 予約詳細\r
+                       try {\r
+                               newdat.put("detail", URLEncoder.encode(r.getDetail().replaceAll("\n", Matcher.quoteReplacement("\r\n")),thisEncoding));\r
+                       } catch (UnsupportedEncodingException e1) {\r
+                               // TODO Auto-generated catch block\r
+                               e1.printStackTrace();\r
+                       }\r
+                       \r
+                       // 保存先\r
+                       try {\r
+                               newdat.put("folder", URLEncoder.encode(text2value(folder,r.getRec_folder()),thisEncoding));\r
+                       } catch (UnsupportedEncodingException e1) {\r
+                               // TODO Auto-generated catch block\r
+                               e1.printStackTrace();\r
+                       }\r
+                       \r
+                       // ジャンル\r
+                       newdat.put("genre", _recstr2genre(r.getRec_genre()));\r
+                       \r
+                       // DVD互換モード\r
+                       newdat.put("dvdr", text2value(dvdcompat,r.getRec_dvdcompat()));\r
+                       \r
+                       // 録画チャンネル\r
+                       String channel = cc.getCH_WEB2CODE(r.getCh_name());\r
+                       //System.out.println(r.getCh_name()+"; "+channel);\r
+                       String ch_no = cc.getCH_CODE2REC(channel);\r
+                       if (chtype.size() > 0) {\r
+                               String typ = text2value(chtype, channel);\r
+                               if (typ.equals("uva")) {\r
+                                       newdat.put("broadcast","0");            // 地上アナログ\r
+                               }\r
+                               else if (typ.equals("bsa")) {\r
+                                       newdat.put("broadcast","1");            // BSアナログ\r
+                               }\r
+                               else if (typ.equals("l1")) {\r
+                                       newdat.put("broadcast","2");            // 外部入力(L1)\r
+                               }\r
+                               else if (typ.equals("l2")) {\r
+                                       newdat.put("broadcast","3");            // 外部入力(L2)\r
+                               }\r
+                               else if (typ.equals("l3")) {\r
+                                       newdat.put("broadcast","4");            // 外部入力(L3)\r
+                               }\r
+                               else if (typ.equals("bsd")) {\r
+                                       newdat.put("broadcast","10");           // BSデジタル\r
+                               }\r
+                               else if (typ.equals("csd")) {\r
+                                       newdat.put("broadcast","11");           // 110度CSデジタル\r
+                               }\r
+                               else if (typ.equals("uvd")) {\r
+                                       newdat.put("broadcast","12");           // 地上デジタル\r
+                               }\r
+                               else {\r
+                                       // 普通ここには落ちない\r
+                                       if (ch_no.startsWith("C")) {\r
+                                               newdat.put("broadcast","2");            // "C***"は外部入力(L1)\r
+                                       }\r
+                                       else if (ch_no.startsWith("SP")) {\r
+                                               newdat.put("broadcast","4");            // "SP***"は外部入力(L3)\r
+                                       }\r
+                                       else {\r
+                                               newdat.put("broadcast","3");            // 未定義は全部外部入力(L2)\r
+                                       }\r
+                               }\r
+                               \r
+                               newdat.put("channel_list", channel);\r
+                               newdat.put("channel_no", channel);\r
+                       }\r
+                       else {\r
+                               // 後方互換\r
+                               Matcher ma = Pattern.compile("^(.)(.)").matcher(ch_no);\r
+                               if (ma.find()) {\r
+                                       if (channel.matches("^L.*")) {\r
+                                               newdat.put("broadcast", String.valueOf(Integer.valueOf(channel.substring(1))+1));       // ライン入力ABC\r
+                                       }\r
+                                       else if ((ma.group(1)+ma.group(2)).equals("BS")) {\r
+                                               newdat.put("broadcast","1");            // BSデジタル\r
+                                       }\r
+                                       else {\r
+                                               newdat.put("broadcast","0");            // 地上アナログ\r
+                                       }\r
+                                       \r
+                                       newdat.put("channel_list", channel.replaceFirst("^L", ""));\r
+                                       newdat.put("channel_no", channel.replaceFirst("^L", ""));\r
+                               }\r
+                       }\r
+                       \r
+                       // 録画レート\r
+                       newdat.put("enc_type", text2value(encoder,r.getTuner()));\r
+                       newdat.put("vrate", text2value(vrate,r.getRec_mode()));\r
+       \r
+                       // 録音レートの指定がねえな…\r
+                       newdat.put("amode", text2value(arate, r.getRec_audio()));\r
+                       \r
+                       // 繰り返し\r
+                       Matcher ma = Pattern.compile("^\\d").matcher(r.getRec_pattern());\r
+                       if (ma.find()) { \r
+                               newdat.put("maiyoubi_type","0");\r
+                               newdat.put("date", r.getRec_pattern());\r
+                       }\r
+                       else {\r
+                               newdat.put("maiyoubi_type", "1");\r
+                               int i = 1;\r
+                               for ( String s : RPTPTN ) {\r
+                                       if ( s.equals(r.getRec_pattern()) == true ) {\r
+                                               newdat.put("date", String.valueOf(i));\r
+                                       }\r
+                                       i++;\r
+                               }\r
+                       }\r
+       \r
+       \r
+                       newdat.put("start_hour", r.getAhh());\r
+                       newdat.put("start_minute", r.getAmm());\r
+                       newdat.put("end_hour", r.getZhh());\r
+                       newdat.put("end_minute", r.getZmm());\r
+       \r
+                       // 記録先\r
+                       newdat.put("disc", text2value(device, r.getRec_device()));\r
+       \r
+                       // 自動チャプター関連\r
+                       newdat.put("bAutoChapter"               , text2value(xchapter, r.getRec_xchapter()));\r
+                       newdat.put("bDvdAutoChapter"    , text2value(mschapter, r.getRec_mschapter()));\r
+                       newdat.put("bAudioAutoChapter"  , text2value(mvchapter, r.getRec_mvchapter()));\r
+                       \r
+                       // 追加\r
+                       newdat.put("start_form"                 , "");\r
+                       newdat.put("rec_priority"               , "150");\r
+                       newdat.put("title_link"                 , "0");\r
+                       newdat.put("auto_delete"                , "0");\r
+                       newdat.put("video_es"                   , "00");\r
+                       newdat.put("audio_es"                   , "10");\r
+                       newdat.put("edge_left"                  , "0");         // 録画のりしろ\r
+                       //newdat.put("bAutoChapter"             , "0");\r
+                       //newdat.put("MagicChapter"             , "0");\r
+                       //newdat.put("CM_Chapter"                       , "0");\r
+                       newdat.put("add_ch_text"                , "");\r
+                       newdat.put("add_ch_value"               , "");\r
+                       newdat.put("sport_ext_submit"   , "undefined");\r
+                       newdat.put("title_link_submit"  , "undefined");\r
+                       newdat.put("end_form"                   , "0");\r
+               }\r
+               catch (Exception e) {\r
+                       e.printStackTrace();\r
+               }\r
+               \r
+               return(newdat);\r
+       }\r
+\r
+       private String _recstr2genre(String genre)\r
+       {\r
+               if (genre.equals(TVProgram.ProgGenre.MOVIE.toString())) {\r
+                       return "0";\r
+               }\r
+               else if (genre.equals(TVProgram.ProgGenre.MUSIC.toString())) {\r
+                       return "1";\r
+               }\r
+               else if (genre.equals(TVProgram.ProgGenre.DORAMA.toString())) {\r
+                       return "2";\r
+               }\r
+               else if (genre.equals(TVProgram.ProgGenre.ANIME.toString())) {\r
+                       return "3";\r
+               }\r
+               else if (genre.equals(TVProgram.ProgGenre.SPORTS.toString())) {\r
+                       return "4";\r
+               }\r
+               else if (genre.equals(TVProgram.ProgGenre.DOCUMENTARY.toString())) {\r
+                       return "5";\r
+               }\r
+               else if (genre.equals(TVProgram.ProgGenre.THEATER.toString())) {\r
+                       return "6";\r
+               }\r
+               else if (genre.equals(TVProgram.ProgGenre.HOBBY.toString())) {\r
+                       return "7";\r
+               }\r
+               else if (genre.equals(TVProgram.ProgGenre.VARIETY.toString())) {\r
+                       return "8";\r
+               }\r
+               else {\r
+                       return "10";\r
+               }\r
+       }\r
+       \r
+       private void setSettingEtc(ArrayList<TextValueSet> tvs, String key, int typ, String res) {\r
+               tvs.clear();\r
+               String valExpr = "(\\d+),?";\r
+               if (typ == 1) {\r
+                       valExpr = "\"(.+?)\",?";\r
+               }\r
+               Matcher mc = Pattern.compile("var "+key+"_text\\s*= new Array\\(([\\s\\S]+?)\\);").matcher(res);\r
+               Matcher md = Pattern.compile("var "+key+"_value\\s*= new Array\\(([\\s\\S]+?)\\);").matcher(res);\r
+               if (mc.find() && md.find()) {\r
+                       Matcher me = Pattern.compile("\"(.+?)\",?").matcher(mc.group(1));\r
+                       Matcher mf = Pattern.compile(valExpr).matcher(md.group(1));\r
+                       while (me.find() && mf.find()) {\r
+                               TextValueSet t = new TextValueSet();\r
+                               if (key.equals("vrate")) {\r
+                                       t.setText(me.group(1).replaceAll(" ", ""));\r
+                               }\r
+                               else {\r
+                                       t.setText(me.group(1));\r
+                               }\r
+                               t.setValue(mf.group(1));\r
+                               tvs.add(t);\r
+                       }\r
+               }\r
+       }\r
+}\r
diff --git a/TinyBannavi/src/tainavi/PlugIn_RecRD_Z160.java b/TinyBannavi/src/tainavi/PlugIn_RecRD_Z160.java
new file mode 100644 (file)
index 0000000..ca9379c
--- /dev/null
@@ -0,0 +1,113 @@
+package tainavi;\r
+\r
+import java.util.ArrayList;\r
+\r
+public class PlugIn_RecRD_Z160 extends PlugIn_RecRD_Z260 implements HDDRecorder,Cloneable {\r
+\r
+       public PlugIn_RecRD_Z160 clone() {\r
+               return (PlugIn_RecRD_Z160) super.clone();\r
+       }\r
+\r
+       // 種族の特性\r
+       @Override\r
+       public String getRecorderId() { return "REGZA DBR-Z160"; }\r
+\r
+       /*\r
+        * 録画設定の解読\r
+        */\r
+       @Override\r
+       protected void setSettingVrate(ArrayList<TextValueSet> vrate)\r
+       {\r
+               vrate.clear();\r
+               TextValueSet t = null;\r
+               \r
+               t = new TextValueSet();\r
+               t.setText("[DR]");\r
+               t.setValue("128:6");\r
+               vrate.add(t);\r
+               \r
+               t = new TextValueSet();\r
+               t.setText("[AVC] AF 12.0");\r
+               t.setValue("2:8");\r
+               vrate.add(t);\r
+               t = new TextValueSet();\r
+               t.setText("[AVC] AN 8.0");\r
+               t.setValue("2:9");\r
+               vrate.add(t);\r
+               t = new TextValueSet();\r
+               t.setText("[AVC] AE 2.4");\r
+               t.setValue("2:10");\r
+               vrate.add(t);\r
+               \r
+               t = new TextValueSet();\r
+               t.setText("[AVC] AT 4.7GB");\r
+               t.setValue("2:4");\r
+               vrate.add(t);\r
+               t = new TextValueSet();\r
+               t.setText("[AVC] AT 8.5GB");\r
+               t.setValue("2:7");\r
+               vrate.add(t);\r
+               t = new TextValueSet();\r
+               t.setText("[AVC] AT 9.4GB");\r
+               t.setValue("2:5");\r
+               vrate.add(t);\r
+               t = new TextValueSet();\r
+               t.setText("[AVC] AT 25GB");\r
+               t.setValue("2:11");\r
+               vrate.add(t);\r
+               t = new TextValueSet();\r
+               t.setText("[AVC] AT 50GB");\r
+               t.setValue("2:12");\r
+               vrate.add(t);\r
+               \r
+               for (int br=1400; br<=17000; ) {\r
+                       t = new TextValueSet();\r
+                       t.setText(String.format("[AVC] %d.%d", (br-br%1000)/1000, (br%1000)/100));\r
+                       t.setValue("2:"+String.valueOf(br));\r
+                       vrate.add(t);\r
+                       if (br < 10000) {\r
+                               br += 200;\r
+                       }\r
+                       else {\r
+                               br += 500;\r
+                       }\r
+               }\r
+               \r
+               t = new TextValueSet();\r
+               t.setText("[VR] SP4.4/4.6");\r
+               t.setValue("1:1");\r
+               vrate.add(t);\r
+               t = new TextValueSet();\r
+               t.setText("[VR] LP2.0/2.2");\r
+               t.setValue("1:2");\r
+               vrate.add(t);\r
+               \r
+               t = new TextValueSet();\r
+               t.setText("[VR] AT 4.7GB");\r
+               t.setValue("1:4");\r
+               vrate.add(t);\r
+               t = new TextValueSet();\r
+               t.setText("[VR] AT 8.5GB");\r
+               t.setValue("1:7");\r
+               vrate.add(t);\r
+               t = new TextValueSet();\r
+               t.setText("[VR] AT 9.4GB");\r
+               t.setValue("1:5");\r
+               vrate.add(t);\r
+               \r
+               t = new TextValueSet();\r
+               t.setText("[VR] 1.0");\r
+               t.setValue("1:1000");\r
+               vrate.add(t);\r
+               t = new TextValueSet();\r
+               t.setText("[VR] 1.4");\r
+               t.setValue("1:1400");\r
+               vrate.add(t);\r
+               for (int br=2000; br<=9200; br+=200) {\r
+                       t = new TextValueSet();\r
+                       t.setText(String.format("[VR] %d.%d", (br-br%1000)/1000, (br%1000)/100));\r
+                       t.setValue("1:"+String.valueOf(br));\r
+                       vrate.add(t);\r
+               }\r
+       }\r
+}\r
diff --git a/TinyBannavi/src/tainavi/PlugIn_RecRD_Z160_TSync.java b/TinyBannavi/src/tainavi/PlugIn_RecRD_Z160_TSync.java
new file mode 100644 (file)
index 0000000..1ea72c2
--- /dev/null
@@ -0,0 +1,17 @@
+package tainavi;\r
+\r
+\r
+public class PlugIn_RecRD_Z160_TSync extends PlugIn_RecRD_BZ810_TSync implements HDDRecorder,Cloneable {\r
+       \r
+       public PlugIn_RecRD_Z160_TSync clone() {\r
+               return (PlugIn_RecRD_Z160_TSync) super.clone();\r
+       }\r
+       \r
+       // 種族の特性\r
+       @Override\r
+       public String getRecorderId() { return "REGZA DBR-Z160(TaiSync)"; }\r
+       \r
+       // 個体の特性\r
+       @Override\r
+       protected String getTSyncVersion() { return "z160"; }\r
+}\r
diff --git a/TinyBannavi/src/tainavi/PlugIn_RecRD_Z260.java b/TinyBannavi/src/tainavi/PlugIn_RecRD_Z260.java
new file mode 100644 (file)
index 0000000..e322156
--- /dev/null
@@ -0,0 +1,1853 @@
+package tainavi;\r
+\r
+import java.io.File;\r
+import java.io.UnsupportedEncodingException;\r
+import java.net.Authenticator;\r
+import java.net.URLEncoder;\r
+import java.util.ArrayList;\r
+import java.util.HashMap;\r
+import java.util.LinkedHashMap;\r
+import java.util.regex.Matcher;\r
+import java.util.regex.Pattern;\r
+\r
+\r
+/*\r
+ *\r
+ */\r
+\r
+public class PlugIn_RecRD_Z260 extends HDDRecorderUtils implements HDDRecorder,Cloneable {\r
+\r
+       public PlugIn_RecRD_Z260 clone() {\r
+               return (PlugIn_RecRD_Z260) super.clone();\r
+       }\r
+\r
+       private static final String thisEncoding = "MS932";\r
+       \r
+       /* 必須コード  - ここから */\r
+       \r
+       // 種族の特性\r
+       @Override\r
+       public String getRecorderId() { return "REGZA DBR-Z260"; }\r
+       @Override\r
+       public RecType getType() { return RecType.RECORDER; }\r
+\r
+       @Override\r
+       public String getLabel_XChapter() { return "マジックチャプタ"; }\r
+       @Override\r
+       public String getLabel_MsChapter() { return "持出用録画"; }\r
+       @Override\r
+       public String getLabel_MvChapter() { return "持出用品質"; }\r
+\r
+       @Override\r
+       public String getChDatHelp() { return\r
+                       "「レコーダの放送局名」はネットdeナビの録画予約一覧で表示される「CH」の欄の値を設定してください。\n"+\r
+                       "予約一覧取得が正常に完了していれば設定候補がコンボボックスで選択できるようになります。"\r
+                       ;\r
+       }\r
+       \r
+       // 個体の特性\r
+       private GetRDStatus gs = new GetRDStatus();\r
+       private ChannelCode cc = new ChannelCode(getRecorderId());\r
+       private String rsvedFile = "";\r
+       \r
+       protected void setErrmsg(String s) { errmsg = s; }\r
+       private String errmsg = "";\r
+       \r
+       /*\r
+        * 定数\r
+        */\r
+       \r
+       private final String MSGID = "["+getRecorderId()+"] ";\r
+       private final String ERRID = "[ERROR]"+MSGID;\r
+       private final String DBGID = "[DEBUG]"+MSGID;\r
+       \r
+       //\r
+       private static final String ITEM_VIDEO_TYPE_DR  = "[DR]";\r
+       private static final String ITEM_VIDEO_TYPE_VR  = "[VR] ";\r
+       private static final String ITEM_VIDEO_TYPE_AVC = "[AVC] ";\r
+       \r
+       private static final String ITEM_ENCODER_R1 = "R1";\r
+       private static final String ITEM_ENCODER_R2 = "R2";\r
+\r
+       private static final String ITEM_MEDIA_HDD = "[HDD] ";\r
+       private static final String ITEM_MEDIA_USB = "[USB] ";\r
+\r
+       private static final String ITEM_APPS_RSV_TYPE_NOMAL = "通常のみ"; \r
+       private static final String ITEM_APPS_RSV_TYPE_BRING = "持出のみ"; \r
+       private static final String ITEM_APPS_RSV_TYPE_BOTH  = "通常+持出"; \r
+\r
+       private static final String ITEM_REC_PRIORITY_HIGH   = "最優先";\r
+       private static final String ITEM_REC_PRIORITY_NORMAL = "ふつう";\r
+\r
+       // chvalueを使っていいよ\r
+       @Override\r
+       public boolean isChValueAvailable() { return true; }\r
+       // CHコードは入力しなくていい\r
+       @Override\r
+       public boolean isChCodeNeeded() { return false; }\r
+       \r
+       // 公開メソッド\r
+       \r
+       /*\r
+        * \r
+        */\r
+       public ChannelCode getChCode() {\r
+               return cc;\r
+       }\r
+       \r
+       /*\r
+        * チャンネルリモコン機能\r
+        */\r
+\r
+       public boolean ChangeChannel(String Channel) {\r
+               \r
+               if (Channel == null) {\r
+                       return true;\r
+               }\r
+               \r
+               int curBC;\r
+               int newBC;\r
+               String chNo;\r
+               //byte enc;\r
+               \r
+               errmsg = "";\r
+               \r
+               /*\r
+                * 変更前の放送(地上波/BS/CS)\r
+                */\r
+               {\r
+                       String ch = null;\r
+                       for (int i=0; i<3 && (ch = gs.getCurChannel(getIPAddr())) == null; i++) {\r
+                               CommonUtils.milSleep(500);\r
+                       }\r
+                       if (ch == null) {\r
+                               errmsg = "レコーダへのアクセスに失敗しました(チャンネルリモコン)。";\r
+                               System.err.println(errmsg);\r
+                               return false;\r
+                       }\r
+                       \r
+                       //byte[] ba = ch.getBytes();\r
+                       //enc = ba[0];\r
+                       ch = ch.substring(1,5);\r
+                       \r
+                       if (ch.matches("^\\d.+")) {\r
+                               curBC = 0;\r
+                       }\r
+                       else if (ch.startsWith("BS")) {\r
+                               curBC = 1;\r
+                       }\r
+                       else if (ch.startsWith("CS")) {\r
+                               curBC = 2;\r
+                       }\r
+                       else {\r
+                               errmsg = "現在のチャンネルが認識できません("+ch+")。";\r
+                               System.err.println(errmsg);\r
+                               return false;\r
+                       }\r
+               }\r
+               \r
+               /*\r
+                * 変更後のCH\r
+                */\r
+               {\r
+                       // 放送(地上波/BS/CS)\r
+                       String cd = cc.getCH_WEB2CODE(Channel);\r
+                       String ch = cc.getCH_CODE2REC(cd);\r
+                       String typ = text2value(chtype, cd);\r
+                       if (typ == null) {\r
+                               errmsg = "鯛ナビに情報が同期されていないチャンネルです("+Channel+", "+ch+")。";\r
+                               System.err.println(errmsg);\r
+                               return false;\r
+                       }\r
+                       else if (typ.matches("^l[123]$")) {\r
+                               errmsg = "外部入力にアサインされているチャンネルには変更できません("+Channel+", "+ch+", "+typ+")。";\r
+                               System.err.println(errmsg);\r
+                               return false;\r
+                       }\r
+                       else if (typ.equals("uvd")) {\r
+                               newBC = 0;\r
+                       }\r
+                       else if (typ.equals("bsd")) {\r
+                               newBC = 1;\r
+                       }\r
+                       else if (typ.equals("csd")) {\r
+                               newBC = 2;\r
+                       }\r
+                       else {\r
+                               errmsg = "放送種別が識別できません。プログラム異常です("+Channel+", "+ch+")。";\r
+                               System.err.println(errmsg);\r
+                               return false;\r
+                       }\r
+                       \r
+                       // CH番号\r
+                       Matcher ma = Pattern.compile("(\\d\\d\\d)").matcher(ch);\r
+                       if (ma.find()) {\r
+                               chNo = ma.group(1);\r
+                       }\r
+                       else {\r
+                               errmsg = "CH番号が取得できません("+Channel+", "+ch+")。";\r
+                               System.err.println(errmsg);\r
+                               return false;\r
+                       }\r
+               }\r
+               \r
+               /*\r
+                * CH変更実行\r
+                */\r
+               \r
+               // おまじない\r
+               Authenticator.setDefault(new MyAuthenticator(getUser(), getPasswd()));\r
+               \r
+               int cBC;\r
+               for (int i=0; i<3 && newBC != (cBC = (curBC+i)%3); i++) {\r
+                       // 切り替え実行\r
+                       reqGET("http://"+getIPAddr()+":"+getPortNo()+"/remote/remote.htm?key=21", null);\r
+                       \r
+                       // 地上波→BS・BS→CSは切り替え完了までに時間がかかる\r
+                       CommonUtils.milSleep((cBC == 0 || cBC == 1)?(3000):(1000));\r
+               }\r
+               \r
+               reqGET("http://"+getIPAddr()+":"+getPortNo()+"/remote/remote.htm?key=25", null);\r
+               CommonUtils.milSleep(1000);\r
+               for (int i=0; i<3; i++) {\r
+                       reqGET("http://"+getIPAddr()+":"+getPortNo()+"/remote/remote.htm?key=0"+chNo.substring(i,i+1), null);\r
+                       CommonUtils.milSleep(200);\r
+               }\r
+               reqGET("http://"+getIPAddr()+":"+getPortNo()+"/remote/remote.htm?key=44", null);\r
+               \r
+               return true;\r
+       }\r
+\r
+       /*\r
+        *      レコーダーから予約一覧を取得する \r
+        */\r
+       public boolean GetRdReserve(boolean force)\r
+       {\r
+               System.out.println("レコーダから予約一覧を取得します("+force+"): "+getRecorderId()+"("+getIPAddr()+":"+getPortNo()+")");\r
+\r
+               errmsg = "";\r
+               \r
+               //\r
+               rsvedFile = "env/reserved."+getIPAddr()+"_"+getPortNo()+"_"+getRecorderId()+".xml";\r
+               \r
+               String arateTFile = "env/audiorate."+getIPAddr()+"_"+getPortNo()+"_"+getRecorderId()+".xml";\r
+               String folderTFile = "env/folders."+getIPAddr()+"_"+getPortNo()+"_"+getRecorderId()+".xml";\r
+               String dvdcompatTFile = "env/dvdcompat."+getIPAddr()+"_"+getPortNo()+"_"+getRecorderId()+".xml";\r
+               String deviceTFile = "env/device."+getIPAddr()+"_"+getPortNo()+"_"+getRecorderId()+".xml";\r
+               String xChapterTFile = "env/xchapter."+getIPAddr()+"_"+getPortNo()+"_"+getRecorderId()+".xml";\r
+               String mvChapterTFile = "env/mvchapter."+getIPAddr()+"_"+getPortNo()+"_"+getRecorderId()+".xml";\r
+               String chValueTFile = "env/chvalue."+getIPAddr()+"_"+getPortNo()+"_"+getRecorderId()+".xml";\r
+               String chTypeTFile = "env/chtype."+getIPAddr()+"_"+getPortNo()+"_"+getRecorderId()+".xml";\r
+               String aspectTFile = "env/aspect."+getIPAddr()+"_"+getPortNo()+"_"+getRecorderId()+".xml";\r
+               String bvperfTFile = "env/bvperf."+getIPAddr()+"_"+getPortNo()+"_"+getRecorderId()+".xml";\r
+               String lvoiceTFile = "env/lvoice."+getIPAddr()+"_"+getPortNo()+"_"+getRecorderId()+".xml";\r
+               String autodelTFile = "env/autodel."+getIPAddr()+"_"+getPortNo()+"_"+getRecorderId()+".xml";\r
+               \r
+               File f = new File(rsvedFile);\r
+               if ( force == false && f.exists()) {\r
+                       // ハードコーディング(録画設定ほか)\r
+                       setSettingVrate(vrate);\r
+                       setSettingEncoder(encoder);\r
+                       setSettingApps(mschapter);\r
+                       setSettingGenre(genre);\r
+                       \r
+                       // キャッシュから読み出し(録画設定ほか)\r
+                       arate = TVSload(arateTFile);\r
+                       folder = TVSload(folderTFile);\r
+                       dvdcompat = TVSload(dvdcompatTFile);\r
+                       device = TVSload(deviceTFile);\r
+                       xchapter = TVSload(xChapterTFile);\r
+                       mvchapter = TVSload(mvChapterTFile);\r
+                       chvalue = TVSload(chValueTFile);\r
+                       chtype = TVSload(chTypeTFile);\r
+                       \r
+                       // その他\r
+                       aspect = TVSload(aspectTFile);\r
+                       bvperf = TVSload(bvperfTFile);\r
+                       lvoice = TVSload(lvoiceTFile);\r
+                       autodel = TVSload(autodelTFile);\r
+                       \r
+                       // キャッシュから読み出し(予約一覧)\r
+                       setReserves(ReservesFromFile(rsvedFile));\r
+                       \r
+                       // なぜか設定ファイルが空になっている場合があるので、その際は再取得する\r
+                       if (arate.size()>0 && folder.size()>0 && device.size()>0 &&\r
+                                       xchapter.size()>0 && mvchapter.size()>0 &&\r
+                                       chvalue.size()>0 && chtype.size()>0) {\r
+                               return(true);\r
+                       }\r
+               }\r
+               \r
+               \r
+               // おまじない\r
+               Authenticator.setDefault(new MyAuthenticator(getUser(), getPasswd()));\r
+               \r
+               // RDから予約一覧を取り出す\r
+               Matcher ma = null;\r
+               String idx = "";\r
+               String header="";\r
+               String response="";\r
+               {\r
+                       reportProgress(MSGID+"処理IDを取得します(1/3).");\r
+                       String[] d = reqGET("http://"+getIPAddr()+":"+getPortNo()+"/reserve/b_rsv.htm",null);\r
+                       header = d[0];\r
+                       response = d[1];\r
+                       \r
+                       if (response == null) {\r
+                               errmsg = ERRID+"レコーダーが反応しません";\r
+                               return(false);\r
+                       }\r
+               }\r
+               ma = Pattern.compile("/reserve/(\\d+?)/reserve.htm").matcher(response);\r
+               if ( ! ma.find()) {\r
+                       errmsg = ERRID+"レコーダーからの情報取得に失敗しました(処理ID)";\r
+                       return(false);\r
+               }\r
+               \r
+               idx = ma.group(1);      // 処理ID\r
+               \r
+               {\r
+                       reportProgress(MSGID+"予約一覧を取得します(2/3).");\r
+                       String[] d = reqGET("http://"+getIPAddr()+":"+getPortNo()+"/reserve/"+idx+"/reserve.htm",null);\r
+                       header = d[0];\r
+                       response = d[1];\r
+                       \r
+                       if (response == null) {\r
+                               errmsg = ERRID+"レコーダーが反応しません";\r
+                               return(false);\r
+                       }\r
+               }\r
+               \r
+               // 予約エントリー数を取得する\r
+               int RsvCnt = 0;\r
+               ma = Pattern.compile("RsvCnt\\s*=\\s*(\\d+?);").matcher(response);\r
+               if ( ! ma.find()) {\r
+                       errmsg = ERRID+"レコーダーからの情報取得に失敗しました(予約一覧)";\r
+                       return false;\r
+               }\r
+               RsvCnt = Integer.valueOf(ma.group(1));\r
+               \r
+               boolean isfault = false;\r
+               \r
+               // (1)録画設定の取得\r
+               {\r
+                       // ハング防止のおまじない\r
+                       reportProgress(MSGID+"録画設定を取得します(3/3).");\r
+                       reqGET("http://"+getIPAddr()+":"+getPortNo()+"/reserve/"+idx+"/b_rsvinfo.htm?0&"+(RsvCnt+1),null);\r
+                       \r
+                       String[] d = reqGET("http://"+getIPAddr()+":"+getPortNo()+"/reserve/"+idx+"/rsvinfo.htm",null);\r
+                       String hdr = d[0];\r
+                       String res = d[1];\r
+                       \r
+                       if (res == null) {\r
+                               errmsg = "レコーダーが反応しません";\r
+                               return(false);\r
+                       }\r
+                       \r
+                       //Matcher mb = null;\r
+                       \r
+                       // 正常取得チェック\r
+                       if ( ! res.matches("[\\s\\S]*var hdd_folder_text[\\s\\S]*")) {\r
+                               errmsg = "レコーダーからの情報取得に失敗しました(録画設定)";\r
+                               return false;\r
+                       }\r
+                       \r
+                       ArrayList<TextValueSet> tvsa = null;\r
+                       \r
+                       // (1-1)画質設定 [保存しない]\r
+                       setSettingVrate(vrate);\r
+                       \r
+                       // (1-2)音質設定\r
+                       tvsa = new ArrayList<TextValueSet>();\r
+                       setSettingEtc(tvsa,"amode",0,res);\r
+                       if (tvsa.size() > 0) {\r
+                               TVSsave(arate=tvsa, arateTFile);\r
+                       }\r
+                       else {\r
+                               System.err.println(errmsg = ERRID+"【致命的エラー】 音質設定が取得できません");\r
+                               isfault = true;\r
+                       }\r
+                       \r
+                       // (1-3)フォルダ一覧\r
+                       tvsa = new ArrayList<TextValueSet>();\r
+                       setSettingFolder(tvsa,res);\r
+                       if (tvsa.size() > 0) {\r
+                               TVSsave(folder=tvsa, folderTFile);\r
+                       }\r
+                       else {\r
+                               System.err.println(errmsg = ERRID+"【致命的エラー】 フォルダ一覧が取得できません");\r
+                               isfault = true;\r
+                       }\r
+                       \r
+                       // (1-4)エンコーダ [保存しない]\r
+                       setSettingEncoder(encoder);\r
+                       //TVSsave(encoder, encoderTFile);\r
+                       \r
+                       // (1-5)DVD互換モード\r
+                       tvsa = new ArrayList<TextValueSet>();\r
+                       setSettingEtc(tvsa,"dvdr",1,res);\r
+                       if (tvsa.size() > 0) {\r
+                               TVSsave(dvdcompat=tvsa, dvdcompatTFile);\r
+                       }\r
+                       else {\r
+                               System.err.println(errmsg = ERRID+"【致命的エラー】 フォルダ一覧が取得できません");\r
+                               isfault = true;\r
+                       }\r
+                       \r
+                       // (1-6)記録先デバイス\r
+                       tvsa = new ArrayList<TextValueSet>();\r
+                       setSettingEtc(tvsa,"media",0,res);\r
+                       if (tvsa.size() > 0) {\r
+                               TVSsave(device=tvsa, deviceTFile);\r
+                       }\r
+                       else {\r
+                               System.err.println(errmsg = ERRID+"【致命的エラー】 記録先デバイスが取得できません");\r
+                               isfault = true;\r
+                       }\r
+                       \r
+                       // (1-7)\r
+                       tvsa = new ArrayList<TextValueSet>();\r
+                       setSettingEtc(tvsa,"magicchapter",0,res);\r
+                       if ( tvsa.size() > 0 ) {\r
+                               TVSsave(xchapter=tvsa, xChapterTFile);\r
+                       }\r
+                       else {\r
+                               System.err.println(errmsg = ERRID+"【致命的エラー】 自動チャプタ(2)が取得できません");\r
+                               isfault = true;\r
+                       }\r
+                       \r
+                       // (1-Y) 持出用品質\r
+                       tvsa = new ArrayList<TextValueSet>();\r
+                       setSettingEtc(tvsa,"apps_vrate",0,res);\r
+                       if ( tvsa.size() > 0 ) {\r
+                               TVSsave(mvchapter=tvsa, mvChapterTFile);\r
+                       }\r
+                       else {\r
+                               System.err.println(errmsg = ERRID+"【致命的エラー】 持出用品質が取得できません");\r
+                               isfault = true;\r
+                       }\r
+                       \r
+                       // (1-8)チャンネルコードバリュー  - uva、bsaは廃止 -\r
+                       ArrayList<TextValueSet> tvsb = new ArrayList<TextValueSet>(); \r
+                       ArrayList<TextValueSet> tvsc = new ArrayList<TextValueSet>(); \r
+                       setSettingChCodeValue(tvsb,tvsc,res);\r
+                       if ( tvsb.size() > 0 && tvsc.size() > 0 ) {\r
+                               TVSsave(chvalue = tvsb, chValueTFile);\r
+                               TVSsave(chtype = tvsc, chTypeTFile);\r
+                       }\r
+                       else {\r
+                               System.err.println(errmsg = ERRID+"【致命的エラー】 チャンネルコードバリューが取得できません");\r
+                               isfault = true;\r
+                       }\r
+                       \r
+                       // (1-9)ジャンル [保存しない]\r
+                       setSettingGenre(genre);\r
+                       \r
+                       // (1-10)番組詳細 [関係ない]\r
+                       \r
+                       // (1-11)録画のりしろ\r
+                       tvsa = new ArrayList<TextValueSet>();\r
+                       setSettingEtc(tvsa,"edge",0,res);\r
+                       if ( tvsa.size() > 0 ) {\r
+                               TVSsave(aspect = tvsa, aspectTFile);\r
+                       }\r
+                       else {\r
+                               System.err.println(errmsg = ERRID+"【致命的エラー】 録画のりしろが取得できません");\r
+                               isfault = true;\r
+                       }\r
+                       \r
+                       // (1-12)録画優先度\r
+                       tvsa = new ArrayList<TextValueSet>();\r
+                       setSettingEtc(tvsa,"rec_priority",0,res);\r
+                       if ( tvsa.size() > 0 ) {\r
+                               TVSsave(bvperf = tvsa, bvperfTFile);\r
+                       }\r
+                       else {\r
+                               System.err.println(errmsg = ERRID+"【致命的エラー】 録画優先度が取得できません");\r
+                               isfault = true;\r
+                       }\r
+                       \r
+                       // (1-13)ライン音声選択\r
+                       tvsa = new ArrayList<TextValueSet>();\r
+                       setSettingEtc(tvsa,"lvoice",0,res);\r
+                       if ( tvsa.size() > 0 ) {\r
+                               TVSsave(lvoice = tvsa, lvoiceTFile);\r
+                       }\r
+                       else {\r
+                               System.err.println(errmsg = ERRID+"【致命的エラー】 ライン音声選択が取得できません");\r
+                               isfault = true;\r
+                       }\r
+                       \r
+                       // (1-14)自動削除\r
+                       tvsa = new ArrayList<TextValueSet>();\r
+                       setSettingEtc(tvsa,"auto_del",0,res);\r
+                       if ( tvsa.size() > 0 ) {\r
+                               TVSsave(autodel = tvsa, autodelTFile);\r
+                       }\r
+                       else {\r
+                               System.err.println(errmsg = ERRID+"【致命的エラー】 自動削除が取得できません");\r
+                               isfault = true;\r
+                       }\r
+               }\r
+               \r
+               // 予約一覧データの分析\r
+               setReserves(decodeReservedList(response));\r
+               \r
+               // キャッシュに保存\r
+               ReservesToFile(getReserves(), rsvedFile);\r
+               \r
+               // 取得した情報の表示\r
+               System.out.println("---Reserved List Start---");\r
+               for ( int i = 0; i<getReserves().size(); i++ ) {\r
+                       // 詳細情報の取得\r
+                       ReserveList e = getReserves().get(i);\r
+                       System.out.println(String.format("[%s] %s\t%s\t%s\t%s:%s\t%s:%s\t%sm\t%s\t%s\t%s(%s)\t%s\t%s",\r
+                                       (i+1),\r
+                                       e.getId(),\r
+                                       e.getRec_pattern(), e.getRec_nextdate(), e.getAhh(), e.getAmm(), e.getZhh(), e.getZmm(), e.getRec_min(),\r
+                                       e.getRec_mschapter(), ((e.getAppsRsv())?(e.getRec_mvchapter()):(e.getRec_mode())),\r
+                                       e.getTitle(), e.getTitlePop(),\r
+                                       e.getChannel(), e.getCh_name()));\r
+               }\r
+               System.out.println("---Reserved List End---");\r
+               \r
+               return( ! isfault);\r
+       }\r
+       \r
+       @Override\r
+       public boolean isThereAdditionalDetails() {\r
+               return true;\r
+       }\r
+       @Override\r
+       public boolean GetRdReserveDetails()\r
+       {\r
+               /*\r
+                *  前処理\r
+                */\r
+               \r
+               // おまじない\r
+               Authenticator.setDefault(new MyAuthenticator(getUser(), getPasswd()));\r
+               \r
+               Matcher mx = null;\r
+               String idx = "";\r
+               String header="";\r
+               String response="";\r
+               {\r
+                       reportProgress("処理IDを取得します(1/1).");\r
+                       String[] d = reqGET("http://"+getIPAddr()+":"+getPortNo()+"/reserve/b_rsv.htm",null);\r
+                       header = d[0];\r
+                       response = d[1];\r
+                       \r
+                       if (response == null) {\r
+                               errmsg = "レコーダーが反応しません";\r
+                               return(false);\r
+                       }\r
+               }\r
+               mx = Pattern.compile("/reserve/(\\d+?)/reserve.htm").matcher(response);\r
+               if ( ! mx.find()) {\r
+                       errmsg = "レコーダーからの情報取得に失敗しました(処理ID)";\r
+                       return(false);\r
+               }\r
+               idx = mx.group(1);\r
+               \r
+               /*\r
+                *  詳細確認\r
+                */\r
+               int lineno = 0;\r
+               ArrayList<ReserveList> ra = getReserves();\r
+               for (ReserveList entry : ra) {\r
+                       \r
+                       lineno++;\r
+                       \r
+                       // 詳細情報を引いてみる\r
+                       reportProgress("+番組詳細を取得します("+lineno+"/"+ra.size()+").");\r
+                       reqGET("http://"+getIPAddr()+":"+getPortNo()+"/reserve/"+idx+"/b_rsvinfo.htm?"+entry.getId()+"&"+lineno,null);\r
+                       \r
+                       String[] d = reqGET("http://"+getIPAddr()+":"+getPortNo()+"/reserve/"+idx+"/rsvinfo.htm",null);\r
+                       if (d[1] == null) {\r
+                               reportProgress("レコーダーからの戻り値が不正です");\r
+                               continue;\r
+                       }\r
+                       \r
+                       // (1-1)画質設定\r
+                       mx = Pattern.compile("videotype_digital_value = (\\d+);").matcher(d[1]);\r
+                       if ( ! mx.find()) {\r
+                               reportProgress("レコーダーからの戻り値が不正です(画質1)");\r
+                               continue;\r
+                       }\r
+                       String vtypestr = mx.group(1);\r
+                       mx = Pattern.compile("videomode_digital_value = (\\d+);").matcher(d[1]);\r
+                       if ( ! mx.find()) {\r
+                               reportProgress("レコーダーからの戻り値が不正です(画質2)");\r
+                               continue;\r
+                       }\r
+                       String vmodestr = mx.group(1);\r
+                       entry.setRec_mode(value2text(vrate, vtypestr+":"+vmodestr));\r
+\r
+                       // (1-2)音質設定 [一覧から取得]\r
+                       \r
+                       // (1-6)記録先デバイス [一覧から取得]\r
+                       \r
+                       // (1-3)フォルダ\r
+                       mx = Pattern.compile("var folder_current = \"(.*?)\";").matcher(d[1]);\r
+                       if ( ! mx.find()) {\r
+                               reportProgress("レコーダーからの戻り値が不正です(フォルダ)");\r
+                               continue;\r
+                       }\r
+                       String fTyp = (entry.getRec_device().equals("HDD")) ? (ITEM_MEDIA_HDD) : (ITEM_MEDIA_USB);\r
+                       for ( TextValueSet t : folder ) {\r
+                               if (t.getText().startsWith(fTyp)) {\r
+                                       if (t.getValue().equals(mx.group(1))) {\r
+                                               entry.setRec_folder(t.getText());\r
+                                               break;\r
+                                       }\r
+                               }\r
+                       }\r
+                       \r
+                       // (1-4)エンコーダ [一覧から取得]\r
+                       \r
+                       // (1-5)DVD互換モード\r
+                       mx = Pattern.compile("var dvdr_current = (\\d+?);").matcher(d[1]);\r
+                       if ( ! mx.find()) {\r
+                               reportProgress("レコーダーからの戻り値が不正です(DVD互換モード)");\r
+                               continue;\r
+                       }\r
+                       entry.setRec_dvdcompat(dvdcompat.get(Integer.valueOf(mx.group(1))).getText());\r
+                       \r
+                       // (1-7)自動チャプター関連\r
+                       mx = Pattern.compile("var cmchapter_current = (\\d+?);").matcher(d[1]);\r
+                       if ( ! mx.find()) {\r
+                               reportProgress("レコーダーからの戻り値が不正です(自動チャプタ)");\r
+                               continue;\r
+                       }\r
+                       entry.setRec_xchapter(xchapter.get(Integer.valueOf(mx.group(1))).getText());\r
+                       \r
+                       // (1-Y) 持出用録画関連\r
+                       {\r
+                               mx = Pattern.compile("var apps_rsv_use_current\\s+?=\\s+?(\\d+?);").matcher(d[1]);\r
+                               if ( ! mx.find()) {\r
+                                       reportProgress("レコーダーからの戻り値が不正です(持出用録画有無)");\r
+                                       continue;\r
+                               }\r
+                               int apps_rsv_use = Integer.valueOf(mx.group(1));\r
+                               if ( apps_rsv_use != 0 ) {\r
+                                       mx = Pattern.compile("var apps_rsv_type_current\\s+?=\\s+?(\\d+?);").matcher(d[1]);\r
+                                       if ( ! mx.find()) {\r
+                                               reportProgress("レコーダーからの戻り値が不正です(持出用録画方法)");\r
+                                               continue;\r
+                                       }\r
+                                       apps_rsv_use = Integer.valueOf(mx.group(1)) + 1;\r
+                               }\r
+                               entry.setRec_mschapter(mschapter.get(apps_rsv_use).getText());\r
+\r
+                               mx = Pattern.compile("var apps_vrate_current\\s+?=\\s+?(\\d+?);").matcher(d[1]);\r
+                               if ( ! mx.find()) {\r
+                                       reportProgress("レコーダーからの戻り値が不正です(持出用録画品質)");\r
+                                       continue;\r
+                               }\r
+                               int apps_vrate = Integer.valueOf(mx.group(1));\r
+\r
+                               entry.setRec_mvchapter(mvchapter.get(apps_vrate).getText());\r
+                       }\r
+                       \r
+                       // (1-8)チャンネル [一覧から取得]\r
+                       \r
+                       // (1-9)ジャンル\r
+                       mx = Pattern.compile("var genre_current = (\\d+?);").matcher(d[1]);\r
+                       if ( ! mx.find()) {\r
+                               reportProgress("レコーダーからの戻り値が不正です(ジャンル)");\r
+                               continue;\r
+                       }\r
+                       int gVal = Integer.valueOf(mx.group(1));\r
+                       if (gVal >= genre.size()) {\r
+                               gVal = genre.size()-1;\r
+                       }\r
+                       entry.setRec_genre(genre.get(gVal).getText());\r
+                       \r
+                       // (1-10)番組詳細\r
+                       mx = Pattern.compile("var title_detail = \"(.*?)\";").matcher(d[1]);\r
+                       if ( ! mx.find()) {\r
+                               reportProgress("レコーダーからの戻り値が不正です(番組詳細)");\r
+                               continue;\r
+                       }\r
+                       entry.setDetail(CommonUtils.unEscape(mx.group(1)).replaceAll("\\\\r\\\\n","\n"));\r
+                       \r
+                       // (1-11)録画のりしろ\r
+                       mx = Pattern.compile("var edge_current = (\\d+?);").matcher(d[1]);\r
+                       if ( ! mx.find()) {\r
+                               reportProgress("レコーダーからの戻り値が不正です(録画のりしろ)");\r
+                               continue;\r
+                       }\r
+                       entry.setRec_aspect(aspect.get(Integer.valueOf(mx.group(1))).getText());\r
+                       \r
+                       // (1-12)録画優先度\r
+                       mx = Pattern.compile("var rec_priority_current = (\\d+?);").matcher(d[1]);\r
+                       if ( ! mx.find()) {\r
+                               reportProgress("レコーダーからの戻り値が不正です(録画優先度)");\r
+                               continue;\r
+                       }\r
+                       entry.setRec_bvperf(bvperf.get(Integer.valueOf(mx.group(1))).getText());\r
+                       \r
+                       // (1-13)ライン音声選択\r
+                       mx = Pattern.compile("var lvoice_current = (\\d+?);").matcher(d[1]);\r
+                       if ( ! mx.find()) {\r
+                               reportProgress("レコーダーからの戻り値が不正です(ライン音声選択)");\r
+                               continue;\r
+                       }\r
+                       entry.setRec_lvoice(lvoice.get(Integer.valueOf(mx.group(1))).getText());\r
+                       \r
+                       // (1-14)自動削除\r
+                       mx = Pattern.compile("var auto_del_current = (\\d+?);").matcher(d[1]);\r
+                       if ( ! mx.find()) {\r
+                               reportProgress("レコーダーからの戻り値が不正です(自動削除)");\r
+                               continue;\r
+                       }\r
+                       entry.setRec_autodel(autodel.get(Integer.valueOf(mx.group(1))).getText());\r
+               }\r
+               \r
+               // キャッシュに保存\r
+               ReservesToFile(getReserves(), rsvedFile);\r
+               \r
+               return(true);\r
+       }\r
+       \r
+       /*\r
+        *      予約を実行する\r
+        */\r
+       public boolean PostRdEntry(ReserveList r)\r
+       {\r
+               System.out.println("Run: PostRdEntry("+r.getTitle()+")");\r
+               \r
+               errmsg = "";\r
+               \r
+               //\r
+               if (cc.getCH_WEB2CODE(r.getCh_name()) == null) {\r
+                       errmsg = "【警告】Web番組表の放送局名「"+r.getCh_name()+"」をCHコードに変換できません。CHコード設定を修正してください。" ;\r
+                       System.out.println(errmsg);\r
+                       return(false);\r
+               }\r
+\r
+               \r
+               /*\r
+                * 予約情報の整理 \r
+                */\r
+               \r
+               // 音質(DR/AVCでは音質の設定はできない)\r
+               if (r.getRec_mode().startsWith(ITEM_VIDEO_TYPE_DR) || r.getRec_mode().startsWith(ITEM_VIDEO_TYPE_AVC)) {\r
+                       r.setRec_audio("");\r
+               }\r
+\r
+               // 予約パターンID・次回予定日\r
+               r.setRec_pattern_id(getRec_pattern_Id(r.getRec_pattern()));\r
+               r.setRec_nextdate(CommonUtils.getNextDate(r));\r
+               \r
+               // 録画長\r
+               r.setRec_min(CommonUtils.getRecMin(r.getAhh(),r.getAmm(),r.getZhh(),r.getZmm()));\r
+               \r
+               // 開始日時・終了日時\r
+               getStartEndDateTime(r);\r
+               \r
+               \r
+               /*\r
+                * 予約実行 \r
+                */\r
+               \r
+               // おまじない\r
+               Authenticator.setDefault(new MyAuthenticator(getUser(), getPasswd()));\r
+               \r
+               // RDから予約一覧を取り出す\r
+               Matcher ma = null;\r
+               String idx = "";\r
+               String header;\r
+               String response;\r
+               {\r
+                       reportProgress("処理IDを取得します(1/7).");\r
+                       String[] d = reqGET("http://"+getIPAddr()+":"+getPortNo()+"/reserve/b_rsv.htm",null);\r
+                       header = d[0];\r
+                       response = d[1];\r
+                       \r
+                       if ( response == null ) {\r
+                               errmsg = "レコーダーが反応しません。";\r
+                               return(false);\r
+                       }\r
+               }\r
+               ma = Pattern.compile("/reserve/(\\d+?)/reserve.htm").matcher(response);\r
+               if (ma.find()) {\r
+                       reportProgress("予約実行前の予約一覧を取得します(2/7).");\r
+                       idx = ma.group(1);\r
+                       String[] d = reqGET("http://"+getIPAddr()+":"+getPortNo()+"/reserve/"+idx+"/reserve.htm",null);\r
+                       header = d[0];\r
+                       response = d[1];\r
+                       \r
+                       if ( response == null ) {\r
+                               errmsg = "レコーダーが反応しません。";\r
+                               return(false);\r
+                       }\r
+               }\r
+               ArrayList<String[]> oldids = getIds(response);\r
+               \r
+               // 予約エントリー数を取得する\r
+               int RsvCnt = 0;\r
+               ma = Pattern.compile("RsvCnt\\s*=\\s*(\\d+?);").matcher(response);\r
+               if (ma.find()) {\r
+                       RsvCnt = Integer.valueOf(ma.group(1));\r
+               }\r
+               \r
+               // RDに新規登録要請\r
+               {\r
+                       // ハング防止のおまじない\r
+                       reportProgress("予約ページを開きます(3/7).");\r
+                       reqGET("http://"+getIPAddr()+":"+getPortNo()+"/reserve/"+idx+"/b_rsvinfo.htm?0&"+(RsvCnt+1),null);\r
+                       \r
+                       reportProgress("予約ページを開きます(4/7).");\r
+                       reqGET("http://"+getIPAddr()+":"+getPortNo()+"/reserve/"+idx+"/rsvinfo.htm",null);\r
+               }\r
+\r
+               // POSTデータを変換する\r
+               HashMap<String, String> pdat = modPostdata(r);\r
+               \r
+               // RDへの情報作成\r
+               String pstr = joinPoststr(pdat);\r
+\r
+               // RDへ情報送信\r
+               {               \r
+                       reportProgress("予約を実行します(5/7).");\r
+                       String[] d = reqPOST("http://"+getIPAddr()+":"+getPortNo()+"/reserve/"+idx+"/entry.htm", pstr, null);\r
+                       header = d[0];\r
+                       response = d[1];\r
+               }\r
+               \r
+               // 登録結果の確認\r
+               ma = Pattern.compile("alert\\(msg\\)").matcher(response);\r
+               if ( ma.find() ) {\r
+                       Matcher mb = Pattern.compile("\\bmsg=\"([\\s\\S]+?)\";").matcher(response);\r
+                       if (mb.find()) {\r
+                               errmsg = mb.group(1);\r
+                               System.out.printf("\n<<< Message from RD >>> \"%s\"\n\n", errmsg);\r
+                               Matcher mc = Pattern.compile("(予約時間が重複しています。|W録の振り替えをおこないました)").matcher(errmsg);\r
+                               if ( ! mc.find() ) {\r
+                                       return(false);\r
+                               }\r
+                       }                       \r
+               }\r
+               \r
+               // 予約ID番号を取得(キャッシュに存在しない番号が新番号)\r
+               {\r
+                       reportProgress("処理IDを取得します(6/7).");\r
+                       String[] d = reqGET("http://"+getIPAddr()+":"+getPortNo()+"/reserve/b_rsv.htm",null);\r
+                       header = d[0];\r
+                       response = d[1];\r
+               }\r
+               ma = Pattern.compile("/reserve/(\\d+?)/reserve.htm").matcher(response);\r
+               if (ma.find()) {\r
+                       idx = ma.group(1);\r
+                       reportProgress("予約実行後の予約一覧を取得します(7/7).");\r
+                       //\r
+                       String param = "/reserve.htm";\r
+                       if ( getDebug() ) {\r
+                               param = "/reserve2.htm";\r
+                       }\r
+                       String[] d = reqGET("http://"+getIPAddr()+":"+getPortNo()+"/reserve/"+idx+param,null);\r
+                       //\r
+                       header = d[0];\r
+                       response = d[1];\r
+               }\r
+               ArrayList<String[]> newids = getIds(response);\r
+               \r
+               // 予約IDの取得\r
+               ArrayList<String[]> rids = new ArrayList<String[]>(); \r
+               for ( String[] nid : newids ) {\r
+                       String[] rid = nid;\r
+                       for ( String[] oid : oldids ) {\r
+                               if ( nid[0].equals(oid[0]) ) {\r
+                                       rid = null;\r
+                                       break;\r
+                               }\r
+                       }\r
+                       if ( rid != null ) {\r
+                               rids.add(rid);\r
+                       }\r
+               }\r
+               System.out.println("+追加された予約IDの数: "+rids.size());\r
+               if ( rids.size() > 0 ) {\r
+                       for ( String[] rid : rids ) {\r
+                               System.out.println("+-予約ID: "+rid[0]);\r
+                               ReserveList nr = r.clone();\r
+                               if ( rid[1].equals("0") && (nr.getRec_mschapter().equals(ITEM_APPS_RSV_TYPE_NOMAL)||nr.getRec_mschapter().equals(ITEM_APPS_RSV_TYPE_BOTH)) ) {\r
+                                       // 通常\r
+                                       nr.setAppsRsv(false);\r
+                                       nr.setRec_mschapter(ITEM_APPS_RSV_TYPE_NOMAL);\r
+                                       if ( nr.getRec_mschapter().equals(ITEM_APPS_RSV_TYPE_BOTH) ) {\r
+                                               nr.setTuner(ITEM_ENCODER_R1);\r
+                                       }\r
+                               }\r
+                               else if ( rid[1].equals("1") && (nr.getRec_mschapter().equals(ITEM_APPS_RSV_TYPE_NOMAL)||nr.getRec_mschapter().equals(ITEM_APPS_RSV_TYPE_BOTH)) ) {\r
+                                       // 持出\r
+                                       nr.setAppsRsv(true);\r
+                                       nr.setRec_mschapter(ITEM_APPS_RSV_TYPE_BRING);\r
+                                       nr.setTuner(ITEM_ENCODER_R2);\r
+                               }\r
+                               else {\r
+                                       errmsg = "【警告】予約IDと予約情報が一致しません。";\r
+                                       continue;\r
+                               }\r
+                               nr.setId(rid[0]);\r
+                               getReserves().add(nr);\r
+                       }\r
+               }\r
+               else {\r
+                       errmsg = "【警告】予約IDが取得できませんでした。";\r
+               }\r
+               \r
+               \r
+               // 予約リストをキャッシュに保存\r
+               ReservesToFile(getReserves(), rsvedFile);\r
+               \r
+               \r
+               System.out.printf("\n<<< Message from RD >>> \"%s\"\n\n", "正常に登録できました。");\r
+               return(true);\r
+       }\r
+\r
+       \r
+       /*\r
+        *      予約を更新する\r
+        */\r
+       public boolean UpdateRdEntry(ReserveList o, ReserveList r) {\r
+\r
+               System.out.println("Run: UpdateRdEntry()");\r
+               \r
+               errmsg = "";\r
+\r
+               //\r
+               if (cc.getCH_WEB2CODE(r.getCh_name()) == null) {\r
+                       errmsg = "【警告】Web番組表の放送局名「"+r.getCh_name()+"」をCHコードに変換できません。CHコード設定を修正してください。" ;\r
+                       System.out.println(errmsg);\r
+                       return(false);\r
+               }\r
+               \r
+               if ( ! o.getRec_mschapter().equals(r.getRec_mschapter()) ) {\r
+                       errmsg = String.format("【警告】持出用録画の設定は変更できません。(%s→%s)",o.getRec_mschapter(),r.getRec_mschapter()) ;\r
+                       System.out.println(errmsg);\r
+                       return(false);\r
+               }\r
+               \r
+               /*\r
+                * 予約情報の整理 \r
+                */\r
+               \r
+               // 音質(DR/AVCでは音質の設定はできない)\r
+               if (r.getRec_mode().startsWith(ITEM_VIDEO_TYPE_DR) || r.getRec_mode().startsWith(ITEM_VIDEO_TYPE_AVC)) {\r
+                       r.setRec_audio("");\r
+               }\r
+\r
+               // 予約パターンID・次回予定日\r
+               r.setRec_pattern_id(getRec_pattern_Id(r.getRec_pattern()));\r
+               r.setRec_nextdate(CommonUtils.getNextDate(r));\r
+               \r
+               // 録画長\r
+               r.setRec_min(CommonUtils.getRecMin(r.getAhh(),r.getAmm(),r.getZhh(),r.getZmm()));\r
+               \r
+               // 開始日時・終了日時\r
+               getStartEndDateTime(r);\r
+               \r
+               \r
+               /*\r
+                * 予約実行 \r
+                */\r
+               \r
+               // おまじない\r
+               Authenticator.setDefault(new MyAuthenticator(getUser(), getPasswd()));\r
+               \r
+               // 更新準備\r
+               Matcher ma = null;\r
+               String idx = "";\r
+               String header;\r
+               String response;\r
+               {\r
+                       reportProgress("処理IDを取得します(1/5).");\r
+                       String[] d = reqGET("http://"+getIPAddr()+":"+getPortNo()+"/reserve/b_rsv.htm",null);\r
+                       header = d[0];\r
+                       response = d[1];\r
+                       \r
+                       if ( response == null ) {\r
+                               errmsg = "レコーダーが反応しません。";\r
+                               return(false);\r
+                       }\r
+               }\r
+               ma = Pattern.compile("/reserve/(\\d+?)/reserve.htm").matcher(response);\r
+               if (ma.find()) {\r
+                       idx = ma.group(1);\r
+                       reportProgress("更新対象の予約を確認します(2/5).");\r
+                       String[] d = reqGET("http://"+getIPAddr()+":"+getPortNo()+"/reserve/"+idx+"/reserve.htm",null);\r
+                       header = d[0];\r
+                       response = d[1];\r
+                       \r
+                       if ( response == null ) {\r
+                               errmsg = "レコーダーが反応しません。";\r
+                               return(false);\r
+                       }\r
+               }\r
+               int lineno = getLineNo(response, r.getId());\r
+               \r
+               // RDに更新要請\r
+               {\r
+                       // ハング防止のおまじない\r
+                       reportProgress("予約ページを開きます(3/5).");\r
+                       reqGET("http://"+getIPAddr()+":"+getPortNo()+"/reserve/"+idx+"/b_rsvinfo.htm?"+r.getId()+"&"+lineno,null);\r
+                       \r
+                       reportProgress("予約ページを開きます(4/5).");\r
+                       reqGET("http://"+getIPAddr()+":"+getPortNo()+"/reserve/"+idx+"/rsvinfo.htm",null);\r
+               }\r
+\r
+               // POSTデータを変換する\r
+               HashMap<String, String> pdat = modPostdata(r);\r
+               \r
+               // RDへの情報作成\r
+               String pstr = joinPoststr(pdat);\r
+\r
+               // RDへ情報送信\r
+               {               \r
+                       reportProgress("更新を実行します(5/5).");\r
+                       String[] d = reqPOST("http://"+getIPAddr()+":"+getPortNo()+"/reserve/"+idx+"/entry.htm", pstr, null);\r
+                       header = d[0];\r
+                       response = d[1];\r
+                       \r
+                       if ( response == null ) {\r
+                               errmsg = "レコーダーが反応しません。";\r
+                               return(false);\r
+                       }\r
+               }\r
+\r
+               // 更新結果の確認\r
+               ma = Pattern.compile("alert\\(msg\\)").matcher(response);\r
+               if ( ma.find() ) {\r
+                       Matcher mb = Pattern.compile("\\bmsg=\"([\\s\\S]+?)\";").matcher(response);\r
+                       if (mb.find()) {\r
+                               errmsg = mb.group(1);\r
+                               System.out.printf("\n<<< Message from RD >>> \"%s\"\n\n", errmsg);\r
+                               Matcher mc = Pattern.compile("(予約時間が重複しています。|W録の振り替えをおこないました)").matcher(errmsg);\r
+                               if ( ! mc.find() ) {\r
+                                       return(false);\r
+                               }\r
+                       }                       \r
+               }\r
+\r
+               System.out.printf("\n<<< Message from RD >>> \"%s\"\n\n", "正常に更新できました。");\r
+               \r
+               \r
+               /*\r
+                * 予約情報の調整 \r
+                */\r
+               \r
+               // 情報置き換え\r
+               getReserves().remove(o);\r
+               getReserves().add(r);\r
+\r
+               // キャッシュに保存\r
+               ReservesToFile(getReserves(), rsvedFile);\r
+\r
+               return(true);\r
+       }\r
+\r
+       /*\r
+        *      予約を削除する\r
+        */\r
+       public ReserveList RemoveRdEntry(String delid) {\r
+               \r
+               System.out.println("Run: RemoveRdEntry()");\r
+               \r
+               errmsg = "";\r
+\r
+               // おまじない\r
+               Authenticator.setDefault(new MyAuthenticator(getUser(), getPasswd()));\r
+\r
+               // 削除対象を探す\r
+               ReserveList rx = null;\r
+               for (  ReserveList reserve : getReserves() )  {\r
+                       if (reserve.getId().equals(delid)) {\r
+                               rx = reserve;\r
+                               break;\r
+                       }\r
+               }\r
+               if (rx == null) {\r
+                       return(null);\r
+               }\r
+\r
+               // 予約行番号を取得\r
+               Matcher ma = null;\r
+               String idx = "";\r
+               String header;\r
+               String response;\r
+               {\r
+                       reportProgress("処理IDを取得します(1/5).");\r
+                       String[] d = reqGET("http://"+getIPAddr()+":"+getPortNo()+"/reserve/b_rsv.htm",null);\r
+                       header = d[0];\r
+                       response = d[1];\r
+                       if ( response == null ) {\r
+                               errmsg = "レコーダーが反応しません。";\r
+                               return(null);\r
+                       }\r
+               }\r
+               ma = Pattern.compile("/reserve/(\\d+?)/reserve.htm").matcher(response);\r
+               if (ma.find()) {\r
+                       idx = ma.group(1);\r
+                       reportProgress("削除対象の予約を確認します(2/5).");\r
+                       String[] d = reqGET("http://"+getIPAddr()+":"+getPortNo()+"/reserve/"+idx+"/reserve.htm",null);\r
+                       header = d[0];\r
+                       response = d[1];\r
+               }\r
+               int lineno = getLineNo(response, rx.getId());\r
+\r
+               // RDに削除要請\r
+               {\r
+                       // ハング防止のおまじない\r
+                       reportProgress("予約ページを開きます(3/5).");\r
+                       reqGET("http://"+getIPAddr()+":"+getPortNo()+"/reserve/"+idx+"/b_rsvinfo.htm?"+rx.getId()+"&"+lineno,null);\r
+                       \r
+                       reportProgress("予約ページを開きます(4/5).");\r
+                       reqGET("http://"+getIPAddr()+":"+getPortNo()+"/reserve/"+idx+"/rsvinfo.htm",null);\r
+               }\r
+               {               \r
+                       reportProgress("削除を実行します(5/5).");\r
+                       String[] d = reqGET("http://"+getIPAddr()+":"+getPortNo()+"/reserve/"+idx+"/delete.htm", null);\r
+                       header = d[0];\r
+                       response = d[1];\r
+                       \r
+                       if ( response == null ) {\r
+                               errmsg = "レコーダーが反応しません。";\r
+                               return(null);\r
+                       }\r
+               }\r
+\r
+               // 削除結果の確認\r
+               ma = Pattern.compile("alert\\(msg\\)").matcher(response);\r
+               if ( ma.find() ) {\r
+                       Matcher mb = Pattern.compile("\\bmsg=\"([\\s\\S]+?)\";").matcher(response);\r
+                       if (mb.find()) {\r
+                               errmsg = mb.group(1);\r
+                               System.out.printf("\n<<< Message from RD >>> \"%s\"\n\n", errmsg);\r
+                               Matcher mc = Pattern.compile("W録の振り替えをおこないました").matcher(errmsg);\r
+                               if ( ! mc.find() ) {\r
+                                       return(null);\r
+                               }\r
+                       }                       \r
+               }\r
+               \r
+               // 予約リストを更新\r
+               getReserves().remove(rx);\r
+               \r
+               // キャッシュに保存\r
+               ReservesToFile(getReserves(), rsvedFile);\r
+               \r
+               System.out.printf("\n<<< Message from RD >>> \"%s\"\n\n", "正常に削除できました。");\r
+               return(rx);\r
+       }\r
+       \r
+       /*\r
+        * \r
+        */\r
+       public String getErrmsg() {\r
+               return(errmsg.replaceAll("\\\\r\\\\n", ""));\r
+       }\r
+       \r
+\r
+       \r
+       \r
+       /* ここまで */\r
+       \r
+       \r
+       \r
+\r
+       \r
+       \r
+       /* 個別コード-ここから最後まで */\r
+\r
+       /*\r
+        * 非公開メソッド \r
+        */\r
+\r
+       private String joinPoststr(HashMap<String, String> pdat)\r
+       {\r
+               String[] pkeys = {\r
+                       "bExec",\r
+                       "start_form",\r
+                       "title_name",\r
+                       "detail",\r
+                       "genre",\r
+                       "enc_type",\r
+                       "broadcast",\r
+                       "channel_list",\r
+                       "rec_priority",\r
+                       "maiyoubi_type",\r
+                       "date",\r
+                       "start_hour",\r
+                       "start_minute",\r
+                       "end_hour",\r
+                       "end_minute",\r
+                       "apps_rsv_use",         // new\r
+                       "apps_rsv_type",        // new\r
+                       "apps_vrate",           // new\r
+                       "disc",\r
+                       "folder",\r
+                       "vrate",\r
+                       "amode",\r
+                       "videotype_digital",\r
+                       "videomode_digital",\r
+                       "auto_delete",\r
+                       "dvdr",\r
+                       "lVoice",\r
+                       "edge_left",\r
+                       "CM_Chapter",\r
+                       "channel_no",\r
+                       "dtv_sid",\r
+                       "dtv_nid",\r
+                       "net_link",\r
+                       "add_ch_text",\r
+                       "add_ch_value",\r
+                       "sport_ext_submit",\r
+                       "title_link_submit",\r
+                       "end_form"\r
+               };\r
+               \r
+               String pstr = "";\r
+               for ( String key : pkeys ) {\r
+                       if (pdat.containsKey(key)) {\r
+                               pstr += key+"="+pdat.get(key)+"&";\r
+                       }\r
+               }\r
+               pstr = pstr.substring(0, pstr.length()-1);\r
+               \r
+               System.err.println("poststr: "+pstr);\r
+               \r
+               return(pstr);\r
+       }\r
+       \r
+       private HashMap<String, String> modPostdata(ReserveList r) {\r
+\r
+               HashMap<String, String> newdat = new HashMap<String, String>();\r
+               try {\r
+                       // 実行するよ\r
+                       newdat.put("bExec", (r.getExec())?("ON"):("OFF"));\r
+                       if (r.getUpdateOnlyExec()) {\r
+                               return(newdat);\r
+                       }\r
+       \r
+                       // 予約名・予約詳細\r
+                       if ( r.getAutocomplete() ) {\r
+                               newdat.put("title_name", "");\r
+                               newdat.put("detail", "");\r
+                       }\r
+                       else {\r
+                               try {\r
+                                       newdat.put("title_name", URLEncoder.encode(CommonUtils.substringrb(r.getTitle(),86),thisEncoding));\r
+                               } catch (UnsupportedEncodingException e1) {\r
+                                       // TODO Auto-generated catch block\r
+                                       e1.printStackTrace();\r
+                               }\r
+               \r
+                               // 予約詳細\r
+                               try {\r
+                                       newdat.put("detail", URLEncoder.encode(CommonUtils.substringrb(r.getDetail().replaceAll("\n", Matcher.quoteReplacement("\r\n")),75*5), thisEncoding));\r
+                               } catch (UnsupportedEncodingException e) {\r
+                                       // TODO Auto-generated catch block\r
+                                       e.printStackTrace();\r
+                               }\r
+                       }\r
+                       \r
+                       // ジャンル\r
+                       String gstr = text2value(genre,r.getRec_genre());\r
+                       if (gstr == null || gstr.length() == 0) {\r
+                               gstr = text2value(genre,TVProgram.ProgGenre.NOGENRE.toString());\r
+                       }\r
+                       newdat.put("genre", gstr);\r
+                       \r
+                       // DVD互換モード\r
+                       newdat.put("dvdr", text2value(dvdcompat, r.getRec_dvdcompat()));\r
+                       \r
+                       // 録画チャンネル - uva、bsaは廃止 -\r
+                       String channel = cc.getCH_WEB2CODE(r.getCh_name());\r
+                       String ch_no = cc.getCH_CODE2REC(channel);\r
+                       String typ = text2value(chtype, channel);\r
+                       if (typ.equals("l1")) {\r
+                               newdat.put("broadcast","2");            // 外部入力(L1)\r
+                       }\r
+                       else if (typ.equals("l2")) {\r
+                               newdat.put("broadcast","3");            // 外部入力(L2)\r
+                       }\r
+                       else if (typ.equals("l3")) {\r
+                               newdat.put("broadcast","4");            // 外部入力(L3)\r
+                       }\r
+                       else if (typ.equals("bsd")) {\r
+                               newdat.put("broadcast","10");           // BSデジタル\r
+                       }\r
+                       else if (typ.equals("csd")) {\r
+                               newdat.put("broadcast","11");           // 110度CSデジタル\r
+                       }\r
+                       else if (typ.equals("uvd")) {\r
+                               newdat.put("broadcast","12");           // 地上デジタル\r
+                       }\r
+                       else {\r
+                               // 普通ここには落ちない\r
+                               if (ch_no.startsWith("C")) {\r
+                                       newdat.put("broadcast","2");            // "C***"は外部入力(L1)\r
+                               }\r
+                               else if (ch_no.startsWith("SP")) {\r
+                                       newdat.put("broadcast","4");            // "SP***"は外部入力(L3)\r
+                               }\r
+                               else {\r
+                                       newdat.put("broadcast","3");            // 未定義は全部外部入力(L2)\r
+                               }\r
+                       }\r
+                       \r
+                       try {\r
+                               String ech = URLEncoder.encode(channel,thisEncoding);\r
+                               newdat.put("channel_list", ech);\r
+                               newdat.put("channel_no", ech);\r
+                       } catch (UnsupportedEncodingException e1) {\r
+                               // TODO Auto-generated catch block\r
+                               e1.printStackTrace();\r
+                       }\r
+                       \r
+                       // 開始・終了日時\r
+                       Matcher ma = Pattern.compile("^\\d").matcher(r.getRec_pattern());\r
+                       if (ma.find()) { \r
+                               newdat.put("maiyoubi_type","0");\r
+                               try {\r
+                                       newdat.put("date", URLEncoder.encode(r.getRec_pattern(),thisEncoding));\r
+                               } catch (UnsupportedEncodingException e1) {\r
+                                       // TODO Auto-generated catch block\r
+                                       e1.printStackTrace();\r
+                               }\r
+                       }\r
+                       else {\r
+                               newdat.put("maiyoubi_type", "1");\r
+                               int i = 1;\r
+                               for ( String s : RPTPTN ) {\r
+                                       if ( s.equals(r.getRec_pattern()) == true ) {\r
+                                               newdat.put("date", String.valueOf(i));\r
+                                       }\r
+                                       i++;\r
+                               }\r
+                       }\r
+       \r
+                       newdat.put("start_hour", r.getAhh());\r
+                       newdat.put("start_minute", r.getAmm());\r
+                       newdat.put("end_hour", r.getZhh());\r
+                       newdat.put("end_minute", r.getZmm());\r
+                       \r
+                       String val;\r
+                       \r
+                       val = text2value(aspect, r.getRec_aspect());\r
+                       newdat.put("edge_left", (val!=null)?(val):("0"));\r
+                       \r
+                       val = text2value(lvoice, r.getRec_lvoice());\r
+                       newdat.put("lVoice", (val!=null)?(val):("1"));\r
+                       \r
+                       val = text2value(bvperf, r.getRec_bvperf());\r
+                       newdat.put("rec_priority", (val!=null)?(val):("150"));\r
+                       \r
+                       val = text2value(autodel, r.getRec_autodel());\r
+                       newdat.put("auto_delete", (val!=null)?(val):("0"));\r
+                       \r
+                       /*\r
+                        *  持出用録画のありなしが影響する項目\r
+                        */\r
+                       \r
+                       if ( r.getRec_mschapter().equals(ITEM_APPS_RSV_TYPE_NOMAL) || r.getRec_mschapter().equals(ITEM_APPS_RSV_TYPE_BOTH) ) {\r
+                               // 保存先\r
+                               try {\r
+                                       newdat.put("folder", URLEncoder.encode(text2value(folder, r.getRec_folder()),thisEncoding));\r
+                               } catch (UnsupportedEncodingException e) {\r
+                                       // TODO Auto-generated catch block\r
+                                       e.printStackTrace();\r
+                               }\r
+                               \r
+                               // 記録先\r
+                               newdat.put("disc", text2value(device, r.getRec_device()));\r
+                               \r
+                               // 自動チャプター関連\r
+                               newdat.put("CM_Chapter" , text2value(xchapter, r.getRec_xchapter()));\r
+                       \r
+                               // 録画レート\r
+                               newdat.put("enc_type", text2value(encoder, r.getTuner()));\r
+                               \r
+                               ma = Pattern.compile("^(\\d+?):(.*?)$").matcher(text2value(vrate, r.getRec_mode()));\r
+                               if (ma.find()) {\r
+                                       if (ma.group(1).equals("1")) {\r
+                                               // VR\r
+                                               newdat.put("vrate",ma.group(2));        \r
+                                               newdat.put("amode",text2value(arate, r.getRec_audio()));        \r
+                                               newdat.put("videotype_digital",ma.group(1));    \r
+                                               newdat.put("videomode_digital",ma.group(2));\r
+                                       }\r
+                                       else if (ma.group(1).equals("2")) {\r
+                                               // AVC\r
+                                               newdat.put("vrate","1");        \r
+                                               newdat.put("amode","1");        \r
+                                               newdat.put("videotype_digital",ma.group(1));    \r
+                                               newdat.put("videomode_digital",ma.group(2));\r
+                                       }\r
+                                       else {\r
+                                               // DR\r
+                                               newdat.put("vrate","1");        \r
+                                               newdat.put("amode","1");        \r
+                                               newdat.put("videotype_digital",ma.group(1));    \r
+                                               //newdat.put("videomode_digital",ma.group(2));\r
+                                       }\r
+                               }\r
+                       }\r
+                       \r
+                       // 持出用録画\r
+                       if ( r.getRec_mschapter().equals(ITEM_APPS_RSV_TYPE_BRING) || r.getRec_mschapter().equals(ITEM_APPS_RSV_TYPE_BOTH) ) {\r
+                               newdat.put("apps_rsv_use", "1");\r
+                               newdat.put("apps_rsv_type", text2value(mschapter,r.getRec_mschapter()));\r
+                               newdat.put("apps_vrate", text2value(mvchapter,r.getRec_mvchapter()));\r
+                       }\r
+                       else {\r
+                               newdat.put("apps_rsv_use", "0");\r
+                       }\r
+       \r
+                       \r
+                       // 追加\r
+                       newdat.put("start_form"                 , "");\r
+                       //newdat.put("dtv_sid"                  , "0");         // ?\r
+                       //newdat.put("dtv_nid"                  , "0");         // ?\r
+                       //newdat.put("net_link"                 , "0");         // ?\r
+                       newdat.put("add_ch_text"                , "");\r
+                       newdat.put("add_ch_value"               , "");\r
+                       //newdat.put("sport_ext_submit" , "undefined");         // 本体からの予約の状態を保持する(>>208.)\r
+                       //newdat.put("title_link_submit"        , "undefined"); // 本体からの予約の状態を保持する(>>208.)\r
+                       newdat.put("end_form"                   , "0");\r
+               }\r
+               catch ( Exception e ) {\r
+                       e.printStackTrace();\r
+               }\r
+               \r
+               return(newdat);\r
+       }\r
+       \r
+       \r
+       \r
+       /*\r
+        * 録画設定の解読\r
+        */\r
+       protected void setSettingVrate(ArrayList<TextValueSet> vrate)\r
+       {\r
+               vrate.clear();\r
+               TextValueSet t = null;\r
+               \r
+               t = new TextValueSet();\r
+               t.setText(ITEM_VIDEO_TYPE_DR);\r
+               t.setValue("128:6");\r
+               vrate.add(t);\r
+               \r
+               t = new TextValueSet();\r
+               t.setText(ITEM_VIDEO_TYPE_AVC+"AF 12.0");\r
+               t.setValue("2:8");\r
+               vrate.add(t);\r
+               t = new TextValueSet();\r
+               t.setText(ITEM_VIDEO_TYPE_AVC+"AN 8.0");\r
+               t.setValue("2:9");\r
+               vrate.add(t);\r
+               t = new TextValueSet();\r
+               t.setText(ITEM_VIDEO_TYPE_AVC+"AS 6.0");\r
+               t.setValue("2:10");\r
+               vrate.add(t);\r
+               t = new TextValueSet();\r
+               t.setText(ITEM_VIDEO_TYPE_AVC+"AL 4.0");\r
+               t.setValue("2:11");\r
+               vrate.add(t);\r
+               t = new TextValueSet();\r
+               t.setText(ITEM_VIDEO_TYPE_AVC+"AE 2.4");\r
+               t.setValue("2:12");\r
+               vrate.add(t);\r
+               \r
+               t = new TextValueSet();\r
+               t.setText(ITEM_VIDEO_TYPE_AVC+"AT 4.7GB");\r
+               t.setValue("2:4");\r
+               vrate.add(t);\r
+               t = new TextValueSet();\r
+               t.setText(ITEM_VIDEO_TYPE_AVC+"AT 8.5GB");\r
+               t.setValue("2:7");\r
+               vrate.add(t);\r
+               t = new TextValueSet();\r
+               t.setText(ITEM_VIDEO_TYPE_AVC+"AT 9.4GB");\r
+               t.setValue("2:5");\r
+               vrate.add(t);\r
+               t = new TextValueSet();\r
+               t.setText(ITEM_VIDEO_TYPE_AVC+"AT 25GB");\r
+               t.setValue("2:13");\r
+               vrate.add(t);\r
+               t = new TextValueSet();\r
+               t.setText(ITEM_VIDEO_TYPE_AVC+"AT 50GB");\r
+               t.setValue("2:14");\r
+               vrate.add(t);\r
+               \r
+               for (int br=1400; br<=17000; ) {\r
+                       t = new TextValueSet();\r
+                       t.setText(String.format("%s%d.%d", ITEM_VIDEO_TYPE_AVC, (br-br%1000)/1000, (br%1000)/100));\r
+                       t.setValue("2:"+String.valueOf(br));\r
+                       vrate.add(t);\r
+                       if (br < 10000) {\r
+                               br += 200;\r
+                       }\r
+                       else {\r
+                               br += 500;\r
+                       }\r
+               }\r
+               \r
+               t = new TextValueSet();\r
+               t.setText(ITEM_VIDEO_TYPE_VR+"SP4.4/4.6");\r
+               t.setValue("1:1");\r
+               vrate.add(t);\r
+               t = new TextValueSet();\r
+               t.setText(ITEM_VIDEO_TYPE_VR+"LP2.0/2.2");\r
+               t.setValue("1:2");\r
+               vrate.add(t);\r
+               \r
+               t = new TextValueSet();\r
+               t.setText(ITEM_VIDEO_TYPE_VR+"AT 4.7GB");\r
+               t.setValue("1:4");\r
+               vrate.add(t);\r
+               t = new TextValueSet();\r
+               t.setText(ITEM_VIDEO_TYPE_VR+"AT 8.5GB");\r
+               t.setValue("1:7");\r
+               vrate.add(t);\r
+               t = new TextValueSet();\r
+               t.setText(ITEM_VIDEO_TYPE_VR+"AT 9.4GB");\r
+               t.setValue("1:5");\r
+               vrate.add(t);\r
+               \r
+               t = new TextValueSet();\r
+               t.setText(ITEM_VIDEO_TYPE_VR+"1.0");\r
+               t.setValue("1:1000");\r
+               vrate.add(t);\r
+               t = new TextValueSet();\r
+               t.setText(ITEM_VIDEO_TYPE_VR+"1.4");\r
+               t.setValue("1:1400");\r
+               vrate.add(t);\r
+               for (int br=2000; br<=9200; br+=200) {\r
+                       t = new TextValueSet();\r
+                       t.setText(String.format("%s%d.%d", ITEM_VIDEO_TYPE_VR, (br-br%1000)/1000, (br%1000)/100));\r
+                       t.setValue("1:"+String.valueOf(br));\r
+                       vrate.add(t);\r
+               }\r
+       }\r
+       \r
+       private void setSettingFolder(ArrayList<TextValueSet> folder, String res) {\r
+               folder.clear();\r
+               LinkedHashMap<String,String> folderKey = new LinkedHashMap<String, String>();\r
+               folderKey.put("hdd",ITEM_MEDIA_HDD);\r
+               folderKey.put("dvd",ITEM_MEDIA_USB);\r
+               for (String typ : folderKey.keySet()) {\r
+                       String txtkey = typ+"_folder_text";\r
+                       String valkey = typ+"_folder_value";\r
+                       Matcher mc = Pattern.compile("var "+txtkey+"\\s*= new Array\\(([\\s\\S]+?)\\);").matcher(res);\r
+                       Matcher md = Pattern.compile("var "+valkey+"\\s*= new Array\\(([\\s\\S]+?)\\);").matcher(res);\r
+                       if ( mc.find() && md.find() ) {\r
+                               Matcher me = Pattern.compile("\"(.+?)\",?").matcher(mc.group(1));\r
+                               Matcher mf = Pattern.compile("\"(.+?)\",?").matcher(md.group(1));\r
+                               while (me.find() && mf.find()) {\r
+                                       TextValueSet t = new TextValueSet();\r
+                                       t.setText(folderKey.get(typ)+me.group(1));\r
+                                       t.setValue(mf.group(1));\r
+                                       folder.add(t);\r
+                               }\r
+                       }\r
+               }\r
+       }\r
+       \r
+       protected void setSettingEncoder(ArrayList<TextValueSet> encoder)\r
+       {\r
+               encoder.clear();\r
+               TextValueSet t = null;\r
+               \r
+               t = new TextValueSet();\r
+               t.setText(ITEM_ENCODER_R1);\r
+               t.setValue("3");\r
+               encoder.add(t);\r
+               t = new TextValueSet();\r
+               t.setText(ITEM_ENCODER_R2);\r
+               t.setValue("4");\r
+               encoder.add(t);\r
+       }\r
+       \r
+       private void setSettingChCodeValue(ArrayList<TextValueSet> chvalue, ArrayList<TextValueSet> chtype, String res) {\r
+               chvalue.clear();\r
+               chtype.clear();\r
+               for ( String typ : new String[] { "uvd","bsd","csd","l1","l2","l3" } ) {\r
+                       String txtkey = typ+"_ch_text";\r
+                       String valkey = typ+"_ch_value";\r
+                       Matcher mc = Pattern.compile("var "+txtkey+"\\s*= new Array\\((.+?)\\);",Pattern.DOTALL).matcher(res);\r
+                       Matcher md = Pattern.compile("var "+valkey+"\\s*= new Array\\((.+?)\\);",Pattern.DOTALL).matcher(res);\r
+                       if ( mc.find() && md.find() ) {\r
+                               Matcher me = Pattern.compile("\"(.+?)\",?").matcher(mc.group(1));\r
+                               Matcher mf = Pattern.compile("\"([^\"]+?)\",").matcher(md.group(1));\r
+                               //System.out.println(txtkey+" "+mc.group(1));\r
+                               //System.out.println(valkey+" "+md.group(1));\r
+                               while ( me.find() && mf.find() ) {\r
+                                       TextValueSet t = new TextValueSet();\r
+                                       t.setText(me.group(1));\r
+                                       t.setValue(mf.group(1));\r
+                                       chvalue.add(t);\r
+                                       \r
+                                       TextValueSet x = new TextValueSet();\r
+                                       x.setText(mf.group(1));\r
+                                       x.setValue(typ);\r
+                                       chtype.add(x);\r
+                               }\r
+                       }\r
+               }\r
+       }\r
+       \r
+       private void setSettingGenre(ArrayList<TextValueSet> genre)\r
+       {\r
+               genre.clear();\r
+               TextValueSet t = null;\r
+               \r
+               t = new TextValueSet();\r
+               t.setText(TVProgram.ProgGenre.MOVIE.toString());\r
+               t.setValue("0");\r
+               genre.add(t);\r
+               t = new TextValueSet();\r
+               t.setText(TVProgram.ProgGenre.MUSIC.toString());\r
+               t.setValue("1");\r
+               genre.add(t);\r
+               t = new TextValueSet();\r
+               t.setText(TVProgram.ProgGenre.DORAMA.toString());\r
+               t.setValue("2");\r
+               genre.add(t);\r
+               t = new TextValueSet();\r
+               t.setText(TVProgram.ProgGenre.ANIME.toString());\r
+               t.setValue("3");\r
+               genre.add(t);\r
+               t = new TextValueSet();\r
+               t.setText(TVProgram.ProgGenre.SPORTS.toString());\r
+               t.setValue("4");\r
+               genre.add(t);\r
+               t = new TextValueSet();\r
+               t.setText(TVProgram.ProgGenre.DOCUMENTARY.toString());\r
+               t.setValue("5");\r
+               genre.add(t);\r
+               t = new TextValueSet();\r
+               t.setText(TVProgram.ProgGenre.THEATER.toString());\r
+               t.setValue("6");\r
+               genre.add(t);\r
+               t = new TextValueSet();\r
+               t.setText(TVProgram.ProgGenre.HOBBY.toString());\r
+               t.setValue("7");\r
+               genre.add(t);\r
+               t = new TextValueSet();\r
+               t.setText(TVProgram.ProgGenre.VARIETY.toString());\r
+               t.setValue("8");\r
+               genre.add(t);\r
+               t = new TextValueSet();\r
+               t.setText(TVProgram.ProgGenre.NOGENRE.toString());\r
+               t.setValue("10");\r
+               genre.add(t);\r
+       }\r
+       \r
+       private void setSettingApps(ArrayList<TextValueSet> tvs) {\r
+               tvs.clear();\r
+               TextValueSet t = null;\r
+               \r
+               t = new TextValueSet();\r
+               t.setText(ITEM_APPS_RSV_TYPE_NOMAL);\r
+               t.setValue("");\r
+               tvs.add(t);\r
+               \r
+               t = new TextValueSet();\r
+               t.setText(ITEM_APPS_RSV_TYPE_BRING);\r
+               t.setValue("0");\r
+               tvs.add(t);\r
+               \r
+               t = new TextValueSet();\r
+               t.setText(ITEM_APPS_RSV_TYPE_BOTH);\r
+               t.setValue("1");\r
+               tvs.add(t);\r
+       }\r
+       \r
+       private void setSettingEtc(ArrayList<TextValueSet> tvs, String key, int typ, String res) {\r
+               tvs.clear();\r
+               String valExpr = "(\\d+),?";\r
+               if (typ == 1) {\r
+                       valExpr = "\"(.+?)\",?";\r
+               }\r
+               Matcher mc = Pattern.compile("var "+key+"_text\\s*= new Array\\(([\\s\\S]+?)\\);").matcher(res);\r
+               Matcher md = Pattern.compile("var "+key+"_value\\s*= new Array\\(([\\s\\S]+?)\\);").matcher(res);\r
+               if (mc.find() && md.find()) {\r
+                       Matcher me = Pattern.compile("\"(.+?)\",?").matcher(mc.group(1));\r
+                       Matcher mf = Pattern.compile(valExpr).matcher(md.group(1));\r
+                       while (me.find() && mf.find()) {\r
+                               TextValueSet t = new TextValueSet();\r
+                               t.setText(me.group(1));\r
+                               t.setValue(mf.group(1));\r
+                               tvs.add(t);\r
+                       }\r
+               }\r
+       }\r
+       \r
+       \r
+       \r
+       //\r
+       \r
+       \r
+       \r
+       /***\r
+        *  RDデジタル系の予約一覧の解読\r
+        */\r
+       protected ArrayList<ReserveList> decodeReservedList(String response) {\r
+               \r
+               ArrayList<ReserveList> newReserveList = new ArrayList<ReserveList>();\r
+               \r
+               Matcher ma = Pattern.compile("(c1\\[\\d+?\\]=[\\s\\S]+?\";)\\n").matcher(response);\r
+               while ( ma.find() ) {\r
+                       \r
+                       // 個々のデータを取り出す\r
+                       ReserveList entry = new ReserveList();\r
+                       \r
+                       Matcher mb = null;\r
+                               \r
+                       String[] d = new String[18];\r
+                       for ( int n=0; n < d.length; n++ ) {\r
+                               d[n] = "";\r
+                       }\r
+                       mb = Pattern.compile("c(\\d+?)\\[\\d+?\\]=\"(.*?)\";").matcher(ma.group(1));\r
+                       while ( mb.find() ) {\r
+                               int n = Integer.valueOf(mb.group(1));\r
+                               if ( n >= d.length ) {\r
+                                       continue;\r
+                               }\r
+                               \r
+                               d[n] = mb.group(2);\r
+                               //System.out.println(n+") "+d[n]);\r
+                       }\r
+                       \r
+                       // 予約ID\r
+                       entry.setId(d[1]);\r
+                       \r
+                       // 実行ON/OFF\r
+                       if (d[2].equals("2")) {\r
+                               entry.setExec(false);\r
+                       }\r
+                       \r
+                       // 予約名\r
+                       String title = CommonUtils.unEscape(d[3]).replaceAll("<BR>","");\r
+                       entry.setTitle(title);\r
+                       entry.setTitlePop(TraceProgram.replacePop(title));\r
+\r
+                       // エンコーダ\r
+                       if ( d[4].matches("^(18|14|10|9|5|2)$") ) {\r
+                               entry.setTuner(ITEM_ENCODER_R2);\r
+                       }\r
+                       else {\r
+                               entry.setTuner(ITEM_ENCODER_R1);\r
+                       }\r
+                       \r
+                       // チャンネル\r
+                       //entry.setCh_name(getChCode().getCH_NO2NAME(d[4]));    // 機種固有領域に移動\r
+                       entry.setChannel(d[5]);\r
+                       entry.setCh_name(getChCode().getCH_REC2WEB(entry.getChannel()));\r
+                       \r
+                       // 開始・終了日時\r
+                       entry.setRec_pattern(d[6]);\r
+                       entry.setRec_pattern_id(getRec_pattern_Id(entry.getRec_pattern()));\r
+                       mb = Pattern.compile("(\\d\\d):(\\d\\d).*?(\\d\\d):(\\d\\d)").matcher(d[7]+"-"+d[8]);\r
+                       if (mb.find()) {\r
+                               entry.setAhh(mb.group(1));\r
+                               entry.setAmm(mb.group(2));\r
+                               entry.setZhh(mb.group(3));\r
+                               entry.setZmm(mb.group(4));\r
+                       }\r
+                       entry.setRec_nextdate(CommonUtils.getNextDate(entry));\r
+                       //entry.setRec_nextdate(getNextDate(entry.getRec_pattern(), entry.getZhh()+":"+entry.getZmm()));\r
+                       entry.setRec_min(CommonUtils.getRecMin(entry.getAhh(), entry.getAmm(), entry.getZhh(), entry.getZmm()));\r
+                       getStartEndDateTime(entry);\r
+                       \r
+                       // 記録先デバイス\r
+                       entry.setRec_device(d[9]);\r
+                       \r
+                       // 画質・音質(取得できない)\r
+                       //entry.setRec_mode(d[10]);\r
+                       //entry.setRec_audio(d[11]);\r
+                       \r
+                       // 番組追跡\r
+                       if ( d[15].matches("^(1|2|3|4)$") ) {\r
+                               entry.setPursues(true);\r
+                       }\r
+                       \r
+                       // 持出用フラグ\r
+                       if (d[16].equals("1")) {\r
+                               entry.setAppsRsv(true);\r
+                               entry.setRec_mschapter(ITEM_APPS_RSV_TYPE_BRING);\r
+                       }\r
+                       else {\r
+                               entry.setAppsRsv(false);\r
+                               entry.setRec_mschapter(ITEM_APPS_RSV_TYPE_NOMAL);\r
+                       }\r
+                       \r
+                       // 優先度\r
+                       if (d[17].equals("0")) {\r
+                               entry.setRec_bvperf(ITEM_REC_PRIORITY_HIGH);\r
+                       }\r
+                       else {\r
+                               entry.setRec_bvperf(ITEM_REC_PRIORITY_NORMAL);\r
+                       }\r
+                       \r
+                       // タイトル自動補完フラグなど本体からは取得できない情報を引き継ぐ\r
+                       copyAttributes(entry, getReserves());\r
+                       \r
+                       // 予約情報を保存\r
+                       newReserveList.add(entry);\r
+               }\r
+               return(newReserveList);\r
+       }\r
+       \r
+       // レコーダーから取得できない情報は直接コピー\r
+       @Override\r
+       protected void copyAttributes(ReserveList entry, ArrayList<ReserveList> reserves) {\r
+               for ( ReserveList e : reserves ) {\r
+                       if ( e.getId().equals(entry.getId()) ) {\r
+                               // 鯛ナビの内部フラグ\r
+                               entry.setAutocomplete(e.getAutocomplete());\r
+                               \r
+                               // 予約一覧からは取得できない情報\r
+                               entry.setDetail(e.getDetail());\r
+                               entry.setRec_genre(e.getRec_genre());\r
+                               //entry.setRec_device(e.getRec_device());\r
+                               entry.setRec_folder(e.getRec_folder());\r
+                               entry.setRec_dvdcompat(e.getRec_dvdcompat());\r
+                               entry.setRec_xchapter(e.getRec_xchapter());\r
+                               //entry.setRec_mschapter(e.getRec_mschapter());\r
+                               entry.setRec_mvchapter(e.getRec_mvchapter());\r
+                               //\r
+                               entry.setRec_aspect(e.getRec_aspect());\r
+                               //entry.setRec_bvperf(e.getRec_bvperf());\r
+                               entry.setRec_lvoice(e.getRec_lvoice());\r
+                               entry.setRec_autodel(e.getRec_autodel());\r
+                               entry.setRec_mode(e.getRec_mode());\r
+                               entry.setRec_audio(e.getRec_audio());\r
+                               \r
+                               return;\r
+                       }\r
+               }\r
+       }\r
+       \r
+       private ArrayList<String[]> getIds(String response) {\r
+               ArrayList<String[]> ids = new ArrayList<String[]>();\r
+               Matcher ma = Pattern.compile("c1\\[\\d+?\\]=\"(\\d+?)\";.+?c16\\[\\d+?\\]=\"(\\d+?)\";").matcher(response);\r
+               while (ma.find()) {\r
+                       String data[] = { ma.group(1), ma.group(2) };\r
+                       ids.add(data);\r
+               }\r
+               return(ids);\r
+       }\r
+       \r
+       private int getLineNo(String response, String id) {\r
+               Matcher ma = Pattern.compile("c1\\[(\\d+?)\\]=\""+id+"\";").matcher(response);\r
+               if ( ma.find() ) {\r
+                       return(Integer.valueOf(ma.group(1))+1);\r
+               }\r
+               return(0);\r
+       }\r
+}\r
diff --git a/TinyBannavi/src/tainavi/PlugIn_RecRD_iEPG.java b/TinyBannavi/src/tainavi/PlugIn_RecRD_iEPG.java
new file mode 100644 (file)
index 0000000..055c301
--- /dev/null
@@ -0,0 +1,257 @@
+package tainavi;\r
+\r
+import java.io.BufferedWriter;\r
+import java.io.File;\r
+import java.io.FileNotFoundException;\r
+import java.io.FileOutputStream;\r
+import java.io.IOException;\r
+import java.io.OutputStreamWriter;\r
+import java.io.UnsupportedEncodingException;\r
+import java.util.regex.Matcher;\r
+import java.util.regex.Pattern;\r
+\r
+\r
+/*\r
+ * \r
+ */\r
+\r
+public class PlugIn_RecRD_iEPG extends HDDRecorderUtils implements HDDRecorder,Cloneable {\r
+\r
+       public PlugIn_RecRD_iEPG clone() {\r
+               return (PlugIn_RecRD_iEPG) super.clone();\r
+       }\r
+\r
+       /* 必須コード  - ここから */\r
+       \r
+       // 種族の特性\r
+       public String getRecorderId() { return "iEPG"; }\r
+       public RecType getType() { return RecType.EPG; }\r
+       \r
+       // 個体の特性\r
+       private ChannelCode cc = new ChannelCode(getRecorderId());\r
+       private String rsvedFile = "";\r
+\r
+       private String errmsg = "";\r
+\r
+       // 公開メソッド\r
+       \r
+       /*\r
+        * \r
+        */\r
+       public ChannelCode getChCode() {\r
+               return cc;\r
+       }\r
+       \r
+       // 繰り返し予約をサポートしていない\r
+       @Override\r
+       public boolean isRepeatReserveSupported() { return false; }\r
+       \r
+       /*\r
+        * \r
+        */\r
+       public boolean ChangeChannel(String Channel) {\r
+               return false;\r
+       }\r
+\r
+       /*\r
+        *      レコーダーから予約一覧を取得する \r
+        */\r
+       public boolean GetRdReserve(boolean force)\r
+       {\r
+               System.out.println("Run: GetRdReserve("+force+")");\r
+               \r
+               setRsvedFile("env/reserved."+getIPAddr()+"_"+getPortNo()+"_"+getRecorderId()+".xml");\r
+               \r
+               // チューナー数は自動生成\r
+               {\r
+                       encoder.clear();\r
+                       if ( getTunerNum() >= 2 ) {\r
+                               for ( int i=1; i<=getTunerNum(); i++ ) {\r
+                                       TextValueSet t = new TextValueSet();\r
+                                       t.setText("E"+i);\r
+                                       t.setValue("E"+i);\r
+                                       encoder.add(t);\r
+                               }\r
+                       }\r
+               }\r
+               \r
+               File f = new File(getRsvedFile());\r
+               if ( f.exists()) {\r
+                       // キャッシュから読み出し(予約一覧)\r
+                       setReserves(ReservesFromFile(getRsvedFile()));\r
+               }\r
+               \r
+               return(true);\r
+       }\r
+       \r
+       /*\r
+        *      予約を実行する\r
+        */\r
+       public boolean PostRdEntry(ReserveList r)\r
+       {\r
+               //\r
+               if (getCC().getCH_WEB2CODE(r.getCh_name()) == null) {\r
+                       errmsg = "【警告】Web番組表の放送局名「"+r.getCh_name()+"」をCHコードに変換できません。CHコード設定を修正してください。" ;\r
+                       System.out.println(getErrmsg());\r
+                       return(false);\r
+               }\r
+               \r
+               System.out.println("Run: PostRdEntry("+r.getTitle()+")");\r
+               \r
+               setErrmsg("");\r
+\r
+               String iepgFile = "env/hogehoge.tvpi";\r
+\r
+               //\r
+               BufferedWriter bw = null;\r
+               OutputStreamWriter sw = null;\r
+               FileOutputStream os = null;\r
+               try {\r
+                       Matcher ma = Pattern.compile("^(\\d\\d\\d\\d)/(\\d\\d)/(\\d\\d)").matcher(r.getRec_pattern());\r
+                       if ( ! ma.find()) {\r
+                               setErrmsg("日付指定しか利用出来ません。");\r
+                               return(false) ;\r
+                       }\r
+                       \r
+                       os = new FileOutputStream(iepgFile);\r
+                       sw = new OutputStreamWriter(os,"MS932");\r
+                       bw = new BufferedWriter(sw);\r
+                       bw.write("Content-type: application/x-tv-program-info; charset=Shift_JIS\r\n");\r
+                       bw.write("version: 1\r\n");\r
+                       bw.write("station: "+getCC().getCH_WEB2CODE(r.getCh_name())+"\r\n");\r
+                       bw.write(String.format("year: %04d\r\n", Integer.valueOf(ma.group(1))));\r
+                       bw.write(String.format("month: %02d\r\n", Integer.valueOf(ma.group(2))));\r
+                       bw.write(String.format("date: %02d\r\n", Integer.valueOf(ma.group(3))));\r
+                       bw.write("start: "+r.getAhh()+":"+r.getAmm()+"\r\n");\r
+                       bw.write("end: "+r.getZhh()+":"+r.getZmm()+"\r\n");\r
+                       bw.write("program-title: "+r.getTitle()+"\r\n");\r
+                       \r
+               } catch (UnsupportedEncodingException e) {\r
+                       // TODO Auto-generated catch block\r
+                       e.printStackTrace();\r
+               } catch (FileNotFoundException e) {\r
+                       // TODO Auto-generated catch block\r
+                       e.printStackTrace();\r
+               } catch (IOException e) {\r
+                       // TODO Auto-generated catch block\r
+                       e.printStackTrace();\r
+               }\r
+               finally {\r
+                       if (bw != null) try { bw.close(); } catch (IOException e) {};\r
+                       if (sw != null) try { sw.close(); } catch (IOException e) {};\r
+                       if (os != null) try { os.close(); } catch (IOException e) {};\r
+               }\r
+\r
+               // iEPGファイルを開く\r
+               String emsg = CommonUtils.openFile(iepgFile);\r
+               if (emsg != null) {\r
+                       setErrmsg(emsg);\r
+                       return false;\r
+               }\r
+               \r
+               //\r
+               long no = 0;\r
+               for (ReserveList x : getReserves()) {\r
+                       if (Long.valueOf(x.getId()) > no) {\r
+                               no = Long.valueOf(x.getId());\r
+                       }\r
+               }\r
+               r.setId(String.valueOf(++no));\r
+\r
+               // 予約パターンID\r
+               r.setRec_pattern_id(getRec_pattern_Id(r.getRec_pattern()));\r
+               \r
+               // 次回予定日\r
+               r.setRec_nextdate(CommonUtils.getNextDate(r));\r
+               //r.setRec_nextdate(getNextDate(r.getRec_pattern(), r.getZhh()+":"+r.getZmm()));\r
+               \r
+               // 録画長\r
+               r.setRec_min(CommonUtils.getRecMin(r.getAhh(),r.getAmm(),r.getZhh(),r.getZmm()));\r
+               \r
+               // 開始日時・終了日時\r
+               getStartEndDateTime(r);\r
+               \r
+               // 予約リストを更新\r
+               getReserves().add(r);\r
+               \r
+               // キャッシュに保存\r
+               ReservesToFile(getReserves(), getRsvedFile());\r
+               \r
+               return(true);\r
+       }\r
+\r
+       /*\r
+        *      予約を更新する\r
+        */\r
+       public boolean UpdateRdEntry(ReserveList o, ReserveList r) {\r
+               System.out.println("Through: UpdateRdEntry()");\r
+               \r
+               setErrmsg("更新処理は無効です。");\r
+\r
+               return(false);\r
+       }\r
+\r
+       /*\r
+        *      予約を削除する\r
+        */\r
+       public ReserveList RemoveRdEntry(String delid) {\r
+               \r
+               System.out.println("Run: RemoveRdEntry()");\r
+\r
+               setErrmsg("");\r
+\r
+               // 削除対象を探す\r
+               ReserveList rx = null;\r
+               for (  ReserveList reserve : getReserves() )  {\r
+                       if (reserve.getId().equals(delid)) {\r
+                               rx = reserve;\r
+                               break;\r
+                       }\r
+               }\r
+               if (rx == null) {\r
+                       return(null);\r
+               }\r
+               \r
+               // 予約リストを更新\r
+               getReserves().remove(rx);\r
+               \r
+               // キャッシュに保存\r
+               ReservesToFile(getReserves(), getRsvedFile());\r
+\r
+               System.out.printf("\n<<< Message from RD >>> \"%s\"\n\n", "正常に削除できました。");\r
+\r
+               return(rx);\r
+       }\r
+       \r
+       /*\r
+        * \r
+        */\r
+       public String getErrmsg() {\r
+               return(errmsg.replaceAll("[\r\n]", ""));\r
+       }\r
+       protected void setErrmsg(String s) {\r
+               errmsg = s;\r
+       }\r
+       \r
+       /*\r
+        * \r
+        */\r
+       protected String getRsvedFile() {\r
+               return(rsvedFile);\r
+       }\r
+       protected void setRsvedFile(String s) {\r
+               rsvedFile = s;\r
+       }\r
+       \r
+       /*\r
+        * \r
+        */\r
+       protected ChannelCode getCC() {\r
+               return(cc);\r
+       }\r
+\r
+       public PlugIn_RecRD_iEPG() {\r
+               super();\r
+               this.setTunerNum(4);\r
+       }\r
+}\r
diff --git a/TinyBannavi/src/tainavi/PlugIn_RecRD_iEPG2.java b/TinyBannavi/src/tainavi/PlugIn_RecRD_iEPG2.java
new file mode 100644 (file)
index 0000000..ab7a1bc
--- /dev/null
@@ -0,0 +1,180 @@
+package tainavi;\r
+\r
+import java.util.Calendar;\r
+import java.util.GregorianCalendar;\r
+\r
+/**\r
+ * EPGデジタルのプラグイン\r
+ */\r
+public class PlugIn_RecRD_iEPG2 extends PlugIn_RecRD_iEPG implements HDDRecorder,Cloneable {\r
+\r
+       @Override\r
+       public PlugIn_RecRD_iEPG2 clone() {\r
+               return (PlugIn_RecRD_iEPG2) super.clone();\r
+       }\r
+       \r
+       /* 必須コード  - ここから */\r
+       \r
+       private static final String thisEncoding = "MS932";\r
+       \r
+       // 種族の特性\r
+       @Override\r
+       public String getRecorderId() { return "iEPG2"; }\r
+       \r
+       private String errmsg = "";\r
+       \r
+\r
+       @Override\r
+       public String getChDatHelp() { return\r
+                       "Web番組表にDimora/ザテ/王国/EDCBを、またはレコーダプラグインにEDCB/TVTest/RDデジ系を追加している場合ここの設定は不要です。"+\r
+                       "それ以外の場合はwikiを見て設定してください。";\r
+       }\r
+       \r
+       /*\r
+        *      予約を実行する\r
+        */\r
+       @Override\r
+       public boolean PostRdEntry(ReserveList r)\r
+       {\r
+               //\r
+               errmsg = "";\r
+               \r
+               String station_id = null;\r
+               int event_id = -1;\r
+               \r
+               if ( r.getContentId() != null ) {\r
+                       if ( ContentIdEDCB.isValid(r.getContentId()) ) {\r
+                               ContentIdEDCB.decodeContentId(r.getContentId());\r
+                               station_id = ContentIdEDCB.getStId();\r
+                               event_id = ContentIdEDCB.getEvId();\r
+                       }\r
+                       else if ( ContentIdDIMORA.isValid(r.getContentId()) ) {\r
+                               ContentIdDIMORA.decodeContentId(r.getContentId());\r
+                               station_id = ContentIdDIMORA.getStId();\r
+                               event_id = ContentIdDIMORA.getEvId();\r
+                       }\r
+                       else if ( ContentIdREGZA.isValid(r.getContentId()) ) {\r
+                               ContentIdREGZA.decodeContentId(r.getContentId());\r
+                               station_id = ContentIdREGZA.getStId();\r
+                               event_id = ContentIdREGZA.getEvId();\r
+                       }\r
+               }\r
+               if ( station_id == null ) {\r
+                       if (getCC().getCH_WEB2CODE(r.getCh_name()) == null) {\r
+                               errmsg = "【警告】Web番組表の放送局名「"+r.getCh_name()+"」をCHコードに変換できません。CHコード設定を修正してください。" ;\r
+                               System.out.println(getErrmsg());\r
+                               return(false);\r
+                       }\r
+               }\r
+               \r
+               System.out.println("Run: PostRdEntry("+r.getTitle()+")");\r
+\r
+               setErrmsg("");\r
+               \r
+               String iepgFile = "env/hogehoge.tvpid";\r
+\r
+               //\r
+               try {\r
+                       \r
+                       GregorianCalendar ca = CommonUtils.getCalendar(r.getRec_pattern());\r
+                       if ( ca == null ) {\r
+                               setErrmsg("日付指定しか利用出来ません。");\r
+                               return(false) ;\r
+                       }\r
+                       \r
+                       StringBuilder sb = new StringBuilder();\r
+                       sb.append("Content-type: application/x-tv-program-digital-info; charset=shift_jis\r\n");\r
+                       sb.append("version: 2\r\n");\r
+                       if ( station_id != null ) {\r
+                               sb.append("station: "+station_id+"\r\n");\r
+                               sb.append("station-name: "+r.getCh_name()+"\r\n");\r
+                       }\r
+                       else {\r
+                               sb.append("station: "+getCC().getCH_WEB2REC(r.getCh_name())+"\r\n");\r
+                               sb.append("station-name: "+getCC().getCH_WEB2CODE(r.getCh_name())+"\r\n");\r
+                       }\r
+                       sb.append(String.format("year: %04d\r\n", ca.get(Calendar.YEAR)));\r
+                       sb.append(String.format("month: %02d\r\n", ca.get(Calendar.MONTH)+1));\r
+                       sb.append(String.format("date: %02d\r\n", ca.get(Calendar.DAY_OF_MONTH)));\r
+                       sb.append("start: "+r.getAhh()+":"+r.getAmm()+"\r\n");\r
+                       sb.append("end: "+r.getZhh()+":"+r.getZmm()+"\r\n");\r
+                       sb.append("program-title: "+r.getTitle()+"\r\n");\r
+                       if ( event_id > 0 ) {\r
+                               // futuer use.\r
+                               sb.append("program-id: "+event_id+"\r\n");\r
+                       }\r
+                       TVProgram.ProgGenre gr = TVProgram.ProgGenre.get(r.getRec_genre());\r
+                       if ( gr != null ) {\r
+                               sb.append("genre-1: "+gr.toIEPG()+"\r\n");\r
+                               \r
+                               if (r.getRec_subgenre() != null) {\r
+                                       TVProgram.ProgSubgenre subg = TVProgram.ProgSubgenre.get(gr,r.getRec_subgenre());\r
+                                       if ( subg != null ) {\r
+                                               sb.append("subgenre-1: "+subg.toIEPG()+"\r\n");\r
+                                       }\r
+                               }\r
+                       }\r
+                       sb.append("\r\n");\r
+                       sb.append(r.getDetail());\r
+                       \r
+                       if ( ! CommonUtils.write2file(iepgFile, sb.toString(), thisEncoding) ) {\r
+                               errmsg = "iEPGファイルの書き出しに失敗しました: "+iepgFile;\r
+                               return false;\r
+                       }\r
+               }\r
+               catch ( Exception e ) {\r
+                       // TODO Auto-generated catch block\r
+                       e.printStackTrace();\r
+               }\r
+\r
+               // iEPGファイルを開く\r
+               String emsg = CommonUtils.openFile(iepgFile);\r
+               if (emsg != null) {\r
+                       setErrmsg(emsg);\r
+                       return false;\r
+               }\r
+               \r
+               //\r
+               long no = 0;\r
+               for (ReserveList x : getReserves()) {\r
+                       if (Long.valueOf(x.getId()) > no) {\r
+                               no = Long.valueOf(x.getId());\r
+                       }\r
+               }\r
+               r.setId(String.valueOf(++no));\r
+\r
+               // 予約パターンID\r
+               r.setRec_pattern_id(getRec_pattern_Id(r.getRec_pattern()));\r
+               \r
+               // 次回予定日\r
+               r.setRec_nextdate(CommonUtils.getNextDate(r));\r
+               //r.setRec_nextdate(getNextDate(r.getRec_pattern(), r.getZhh()+":"+r.getZmm()));\r
+               \r
+               // 録画長\r
+               r.setRec_min(CommonUtils.getRecMin(r.getAhh(),r.getAmm(),r.getZhh(),r.getZmm()));\r
+               \r
+               // 開始日時・終了日時\r
+               getStartEndDateTime(r);\r
+               \r
+               // 予約リストを更新\r
+               getReserves().add(r);\r
+               \r
+               // キャッシュに保存\r
+               ReservesToFile(getReserves(), getRsvedFile());\r
+\r
+               return(true);\r
+       }\r
+       \r
+       /*\r
+        * \r
+        */\r
+       @Override\r
+       public String getErrmsg() {\r
+               return(errmsg.replaceAll("[\r\n]", ""));\r
+       }\r
+\r
+       public PlugIn_RecRD_iEPG2() {\r
+               super();\r
+               this.setTunerNum(4);\r
+       }\r
+}\r
diff --git a/TinyBannavi/src/tainavi/PlugIn_TVPDimora.java b/TinyBannavi/src/tainavi/PlugIn_TVPDimora.java
new file mode 100644 (file)
index 0000000..baf199e
--- /dev/null
@@ -0,0 +1,658 @@
+package tainavi;\r
+\r
+import java.io.File;\r
+import java.util.ArrayList;\r
+import java.util.Calendar;\r
+import java.util.Date;\r
+import java.util.GregorianCalendar;\r
+import java.util.HashMap;\r
+import java.util.LinkedHashMap;\r
+import java.util.regex.Matcher;\r
+import java.util.regex.Pattern;\r
+\r
+\r
+public class PlugIn_TVPDimora extends TVProgramUtils implements TVProgram,Cloneable {\r
+\r
+       public PlugIn_TVPDimora clone() {\r
+               return (PlugIn_TVPDimora) super.clone();\r
+       }\r
+       \r
+       private static final String thisEncoding = "UTF-8";\r
+       \r
+       \r
+       /*******************************************************************************\r
+        * 種族の特性\r
+        ******************************************************************************/\r
+       \r
+       @Override\r
+       public String getTVProgramId() { return "Dimora"; }\r
+       \r
+       @Override\r
+       public ProgType getType() { return ProgType.PROG; }\r
+       @Override\r
+       public ProgSubtype getSubtype() { return ProgSubtype.TERRA; }\r
+\r
+       \r
+       /*******************************************************************************\r
+        * 個体の特性\r
+        ******************************************************************************/\r
+       \r
+       @Override\r
+       public int getTimeBarStart() {return 5;}\r
+       \r
+       private int getDogDays() { return ((getExpandTo8())?(8):(7)); }\r
+\r
+       \r
+       /*******************************************************************************\r
+        * 定数\r
+        ******************************************************************************/\r
+       \r
+       private final String MSGID = "["+getTVProgramId()+"] ";\r
+       private final String ERRID = "[ERROR]"+MSGID;\r
+       private final String DBGID = "[DEBUG]"+MSGID;\r
+       \r
+       \r
+       /*******************************************************************************\r
+        * 部品\r
+        ******************************************************************************/\r
+\r
+       // 新しい入れ物の臨時格納場所\r
+       protected final ArrayList<ProgList> newplist = new ArrayList<ProgList>();\r
+       \r
+       // 未定義のフラグの回収場所\r
+       private final HashMap<String,String> nf = new HashMap<String, String>();\r
+\r
+       protected String getCenterInfoId() { return "ChannelTypeDB"; }\r
+       protected String getChType() { return "6"; }\r
+       protected String getCenterCode(String id, String code) { return id.matches("^3.*$")?(bsCode):(code); }\r
+       \r
+       protected String getProgCacheFile(String areacode, String adate) { return String.format(getProgDir()+File.separator+"Dimora_%s_%s.html", areacode, adate.substring(6,8)); }\r
+\r
+       \r
+       /*******************************************************************************\r
+        * コンストラクタ\r
+        ******************************************************************************/\r
+       \r
+       \r
+       /*******************************************************************************\r
+        * 番組情報を取得する\r
+        ******************************************************************************/\r
+\r
+       @Override\r
+       public void loadProgram(String areaCode, boolean force) {\r
+               \r
+               // 入れ物を空にする\r
+               newplist.clear();\r
+               nf.clear(); \r
+               \r
+               // 地域コードごとの参照ページ数の入れ物を用意する\r
+               LinkedHashMap<String,Integer> pages = new LinkedHashMap<String, Integer>();\r
+               \r
+               // 参照する地域コードをまとめる\r
+               if ( areaCode.equals(allCode) ) {\r
+                       // 「全国」\r
+                       for ( Center cr : crlist ) {\r
+                               if ( cr.getOrder() > 0 ) {\r
+                                       // 有効局の地域コードのみ集める\r
+                                       pages.put(cr.getAreaCode(),1);\r
+                               }\r
+                       }\r
+               }\r
+               else if ( ! areaCode.equals(bsCode) ) {\r
+                       // 地域個別\r
+                       for ( Center cr : crlist ) {\r
+                               if ( cr.getOrder() > 0 ) {\r
+                                       // 有効局がある場合のみ集める\r
+                                       pages.put(areaCode,1);\r
+                                       pages.put(bsCode,1);\r
+                                       break;\r
+                               }\r
+                       }\r
+               }\r
+               \r
+               // トップの下に局ごとのリストを生やす\r
+               for ( String ac : pages.keySet() ) {\r
+                       for ( Center cr : crlist ) {\r
+                               if ( cr.getOrder() <= 0 ) {\r
+                                       // 設定上無効な局はいらない\r
+                                       continue;\r
+                               }\r
+                               if ( ac.equals(cr.getAreaCode()) ) {\r
+                                       ProgList pl = new ProgList();\r
+                                       pl.Area = cr.getAreaCode();\r
+                                       pl.SubArea = cr.getType();\r
+                                       pl.Center = cr.getCenter();\r
+                                       pl.BgColor = cr.getBgColor();\r
+                                       pl.enabled = true;\r
+                                       \r
+                                       newplist.add(pl);\r
+                               }\r
+                       }\r
+               }\r
+               \r
+               // 局の下に日付ごとのリストを生やす\r
+               GregorianCalendar cal = CommonUtils.getCalendar(0);\r
+               if ( CommonUtils.isLateNight(cal) ) {\r
+                       // 4時までは当日扱いにする\r
+                       cal.add(Calendar.DATE, -1);\r
+               }\r
+               GregorianCalendar cale = (GregorianCalendar) cal.clone();\r
+               for (int i=0; i<getDogDays(); i++) {\r
+                       String date = CommonUtils.getDate(cale);\r
+                       for ( ProgList pl : newplist ) {\r
+                               ProgDateList cl = new ProgDateList();\r
+                               cl.Date = date;\r
+                               pl.pdate.add(cl);\r
+                       }\r
+                       cale.add(Calendar.DATE, 1);\r
+               }\r
+               \r
+               // 参照する総ページ数を計算\r
+               int counterMax = 0;\r
+               for ( String ac : pages.keySet() ) {\r
+                       if ( ac.equals(bsCode) ) {\r
+                               continue;\r
+                       }\r
+                       counterMax += pages.get(ac)*getDogDays();\r
+               }\r
+               \r
+               clrCookie();\r
+               \r
+               // 日付の下に番組情報ごとのリストを生やす\r
+               int counter = 1;\r
+               for ( String ac : pages.keySet() ) {\r
+                       if ( ac.equals(bsCode) ) {\r
+                               continue;\r
+                       }\r
+                       cale = (GregorianCalendar) cal.clone();\r
+                       for ( int i=0; i<getDogDays(); i++ ) {\r
+                               String adate = CommonUtils.getDateYMD(cale);\r
+                               cale.add(Calendar.DATE, 1);\r
+                               String edate = CommonUtils.getDateYMD(cale);\r
+                               _loadProgram(ac, adate, edate, force, i, counter++, counterMax);\r
+                       }\r
+               }\r
+               \r
+               // 開始・終了日時を正しい値に計算しなおす\r
+               for ( ProgList pl : newplist ) {\r
+                       setAccurateDate(pl.pdate);\r
+               }\r
+               \r
+               // 古いデータから補完できないかな?\r
+               CompensatesPrograms(newplist);\r
+               \r
+               // 解析用\r
+               {\r
+                       for ( String f : nf.keySet() ) {\r
+                               System.out.println(String.format(DBGID+"未定義のフラグです: [%s]",f));\r
+                       }\r
+               }\r
+               \r
+               // 古い番組データを置き換える\r
+               pcenter = newplist;\r
+       }\r
+       \r
+       /* ここまで */\r
+\r
+       \r
+       \r
+       /*\r
+        * 非公開メソッド\r
+        */\r
+       \r
+       protected void _loadProgram(String areacode, String adate, String edate, boolean force, int wdaycol, int counter, int counterMax) {\r
+               //\r
+               final String progCacheFile = getProgCacheFile(areacode,adate);\r
+               String dt = adate.substring(6,8);\r
+               String aname = getArea(areacode);\r
+               //\r
+               try {\r
+                       //\r
+                       String response = null;\r
+                       File f = new File(progCacheFile);\r
+                       if (force == true ||\r
+                                       (f.exists() == true && isCacheOld(progCacheFile) == true) ||\r
+                                       (f.exists() == false && isCacheOld(null) == true)) {\r
+                               \r
+                               GregorianCalendar c = new GregorianCalendar();\r
+                               c.setTime(new Date());\r
+                               \r
+                               String hh = String.valueOf(CommonUtils.getCalendar(0).get(Calendar.HOUR_OF_DAY));\r
+                               String tvParam = "1%2C1%2C1%2CDR%2C10%2C1%2C1%2C1%2C1%2C1%2C1";\r
+                               String url = "http://dimora.jp/dc/pc/P4501.do";\r
+                               String pstr = String.format("c_time=%s&win_id=P4501&isLogin=1&start_time=%s0500&end_time=%s0500&ch_type=%s&args=%s0500%s%s%s",\r
+                                               CommonUtils.getDateTimeYMD(c),\r
+                                               adate,\r
+                                               edate,\r
+                                               getChType(),\r
+                                               adate,\r
+                                               //"%2C5%2C",\r
+                                               "%2C"+hh+"%2C",\r
+                                               getChType(),\r
+                                               "%2C0%2C"+tvParam);\r
\r
+                               // 本体\r
+                               //clrCookie(); -> もっと上に\r
+                               addCookie("KEY_AREA", areacode);\r
+                               addCookie("tvParam", tvParam);\r
+                               //addCookie("CookiePcDmSnsButton", "0");\r
+                               //addCookie("__utma", "95544457.1095879584.1348394165.1352897699.1352948391.32");\r
+                               //addCookie("__utmz", "95544457.1348394165.1.1.utmcsr=(direct)|utmccn=(direct)|utmcmd=(none)");\r
+                               response = webToBuffer(\r
+                                               url,\r
+                                               pstr,\r
+                                               null,\r
+                                               url,                                            \r
+                                               thisEncoding,\r
+                                               false);\r
+                               \r
+                               if ( ! CommonUtils.write2file(progCacheFile, "<!-- "+url+"?"+pstr+" -->\n"+response) ) {\r
+                                       reportProgress(ERRID+"番組表(キャッシュ)の保存に失敗しました: ("+counter+"/"+counterMax+") "+progCacheFile);\r
+                               }\r
+                               \r
+                               reportProgress(MSGID+"番組表(オンライン)を取得しました["+dt+"日/"+aname+"]: ("+counter+"/"+counterMax+") "+url+"?"+pstr+"("+areacode+")");\r
+                       }\r
+                       else if (CommonUtils.isFileAvailable(f,10)) {\r
+                               // キャッシュファイルの読み込み\r
+                               response = CommonUtils.read4file(progCacheFile, false);\r
+                               if ( response == null ) {\r
+                                       reportProgress(ERRID+"番組表(キャッシュ)の取得に失敗しました["+dt+"日/"+aname+"]: ("+counter+"/"+counterMax+") "+progCacheFile);\r
+                                       return;\r
+                               }\r
+                               reportProgress(MSGID+"番組表(キャッシュ)を取得しました["+dt+"日/"+aname+"]: ("+counter+"/"+counterMax+") "+progCacheFile);\r
+                       }\r
+                       else {\r
+                               reportProgress(ERRID+"番組表(キャッシュ)がみつかりません["+dt+"日/"+aname+"]: ("+counter+"/"+counterMax+") "+progCacheFile);\r
+                               return;\r
+                       }\r
+                       \r
+                       // 番組リストの追加\r
+                       getPrograms(areacode, wdaycol, response);\r
+               }\r
+               catch (Exception e) {\r
+                       reportProgress(ERRID+"番組表の取得で例外が発生しました: "+e.toString());\r
+                       e.printStackTrace();\r
+               }\r
+       }\r
+       \r
+       //\r
+       private void getPrograms(String areacode, int wdaycol, String src) {\r
+               \r
+               for ( Center cr : crlist ) {\r
+                       \r
+                       if ( ! cr.getAreaCode().equals(areacode) && ! cr.getAreaCode().equals(bsCode) ) {\r
+                               continue;\r
+                       }\r
+                       \r
+                       Matcher ma = Pattern.compile(String.format("<div\\s+id=\"%s\">([\\s\\S]*?)</div>",cr.getLink())).matcher(src);\r
+                       if ( ! ma.find() ) {\r
+                               continue;\r
+                       }\r
+                       \r
+                       ProgDateList pcl = null;\r
+                       for ( ProgList pl : newplist ) {\r
+                               if ( (pl.Area.equals(areacode)||pl.Area.equals(bsCode)) && pl.Center.equals(cr.getCenter()) ) {\r
+                                       pcl = pl.pdate.get(wdaycol);\r
+                                       break;\r
+                               }\r
+                       }\r
+                       if ( pcl == null ) {\r
+                               continue;\r
+                       }\r
+                       \r
+                       String sdat = String.format("%s %02d:00",pcl.Date,getTimeBarStart());\r
+                       GregorianCalendar cx = CommonUtils.getCalendar(sdat);\r
+                       GregorianCalendar cy = (GregorianCalendar) cx.clone();\r
+                       cy.add(Calendar.DAY_OF_MONTH, 1);\r
+                       \r
+                       GregorianCalendar pcz = null;\r
+                       Matcher mb = Pattern.compile("([\\s\\S]+?)\\|").matcher(ma.group(1));\r
+                       while ( mb.find() && pcl.row < 1440 ) {\r
+                               \r
+                               String[] dat = mb.group(1).split(",");\r
+                               \r
+                               ProgDetailList pdl = new ProgDetailList();\r
+                               \r
+                               // 開始・終了日時\r
+                               GregorianCalendar ca = CommonUtils.getCalendar(dat[1]);\r
+                               if ( ca == null ) {\r
+                                       continue;\r
+                               }\r
+                               pdl.start = CommonUtils.getTime(ca);\r
+                               \r
+                               GregorianCalendar cz = CommonUtils.getCalendar(dat[2]);\r
+                               if ( cz == null ) {\r
+                                       continue;\r
+                               }\r
+                               pdl.end = CommonUtils.getTime(cz);\r
+                               \r
+                               pdl.length = (int)(CommonUtils.getDiffDateTime(dat[1], dat[2])/60000L);\r
+\r
+                               if ( pcz == null ) {\r
+                                       if ( cz.compareTo(cx) <= 0 ) {\r
+                                               // 終了時刻が05:00ちょうど\r
+                                               continue;\r
+                                       }\r
+                                       if ( ca.compareTo(cx) <= 0 ) {\r
+                                               // 開始時刻が05:00よりまえ\r
+                                               pdl.length = (int)(CommonUtils.getDiffDateTime(sdat, dat[2])/60000L);\r
+                                       }\r
+                                       else if ( cx.compareTo(ca) < 0 ) {\r
+                                               // 開始時刻が05:00よりあと\r
+                                               addEnmptyInfo(pcl, sdat, dat[1]);\r
+                                       }\r
+                               }\r
+                               else {\r
+                                       if ( pcz.compareTo(ca) < 0 ) {\r
+                                               // 前の番組との間が空いている\r
+                                               addEnmptyInfo(pcl, CommonUtils.getDateTime(pcz), dat[1]);\r
+                                       }\r
+                               }\r
+                               \r
+                               pcz = (GregorianCalendar) cz.clone();\r
+                               \r
+                               // タイトル&番組詳細\r
+                               pdl.title = CommonUtils.unEscape(dat[3]);\r
+                               \r
+                               dat[4] = CommonUtils.unEscape(dat[4]).trim();\r
+                               dat[5] = CommonUtils.unEscape(dat[5]).trim();\r
+                               dat[6] = CommonUtils.unEscape(dat[6]).trim();\r
+                               pdl.detail  =\r
+                                               (((dat[4].length()>0)?(dat[4]+DETAIL_SEP):(""))\r
+                                               +((dat[5].length()>0)?(dat[5]+DETAIL_SEP):(""))\r
+                                               +dat[6]);\r
+                               pdl.detail = pdl.detail.replaceAll("\\^", " ");\r
+                               // 改行が挟まっているサブタイトルを1行にしたい\r
+                               pdl.detail = Pattern.compile("(#\\s*\\d+\\s*「[^」]*?)[\r\n]+[  ]*(.*?」)",Pattern.DOTALL).matcher(pdl.detail).replaceAll("$1 $2");\r
+                               \r
+                               // タイトルから各種フラグを分離する\r
+                               doSplitFlags(pdl, nf);\r
+                               \r
+                               // ジャンル\r
+                               {\r
+                                       ArrayList<String> genrelist = new ArrayList<String>();\r
+                                       Matcher md = Pattern.compile("([0-9A-F][0-9A-F])").matcher(dat[7]);\r
+                                       while ( md.find() ) {\r
+                                               genrelist.add(md.group(1));\r
+                                       }\r
+                                       setMultiGenre(pdl,genrelist);\r
+                               }\r
+\r
+                               // サブタイトル分離\r
+                               doSplitSubtitle(pdl);\r
+                               \r
+                               // 検索対象外領域にこっそりジャンル文字列を入れる\r
+                               pdl.setGenreStr();\r
+                               \r
+                               // プログラムID?\r
+                               String chid =  ContentIdDIMORA.getChId(cr.getLink());\r
+                               ContentIdDIMORA.decodeChId(chid);\r
+                               pdl.progid = ContentIdDIMORA.getContentId(0,dat[8]);\r
+                               \r
+                               // その他フラグ\r
+                               pdl.extension = false;\r
+                               //pdl.flag = ProgFlags.NOFLAG;\r
+                               pdl.nosyobo = false;\r
+                               \r
+                               //\r
+                               pcl.pdetail.add(pdl);\r
+                               \r
+                               //\r
+                               pcl.row += pdl.length;\r
+                       }\r
+                       if ( pcz == null ) {\r
+                               // 番組情報がないよ\r
+                               addEnmptyInfo(pcl, CommonUtils.getDateTime(cx), CommonUtils.getDateTime(cy));\r
+                       }\r
+                       else if ( pcz.compareTo(cy) < 0 ) {\r
+                               // 終了時刻が29:00より前\r
+                               addEnmptyInfo(pcl, CommonUtils.getDateTime(pcz), CommonUtils.getDateTime(cy));\r
+                       }\r
+               }\r
+       }\r
+       \r
+       private void addEnmptyInfo(ProgDateList pcl, String sdat, String edat) {\r
+               ProgDetailList pdl = new ProgDetailList();\r
+               pdl.title = pdl.splitted_title = "番組情報がありません";\r
+               pdl.detail = "";\r
+               pdl.length = (int)(CommonUtils.getDiffDateTime(sdat, edat)/60000L);\r
+               pdl.genre = ProgGenre.NOGENRE;\r
+               pdl.start = "";\r
+               pcl.pdetail.add(pdl);\r
+               pcl.row += pdl.length;\r
+       }\r
+       \r
+       \r
+       /*******************************************************************************\r
+        * 地域情報を取得する\r
+        ******************************************************************************/\r
+       \r
+       // 普通は東京\r
+       @Override\r
+       public String getDefaultArea() {return "東京";}\r
+       \r
+       //\r
+       @Override\r
+       public void loadAreaCode() {\r
+               \r
+               // 設定ファイルが存在していればファイルから\r
+               File f = new File(getAreaSelectedFile());\r
+               if (f.exists() == true) {\r
+                       @SuppressWarnings("unchecked")\r
+                       ArrayList<AreaCode> tmp = (ArrayList<AreaCode>) CommonUtils.readXML(getAreaSelectedFile());\r
+                       if ( tmp != null ) {\r
+                               aclist = tmp;\r
+                               System.out.println(MSGID+"地域リストを読み込みました: "+getAreaSelectedFile());\r
+                               return;\r
+                       }\r
+                       else {\r
+                               System.err.println(ERRID+"地域リストの読み込みに失敗しました: "+getAreaSelectedFile());\r
+                       }\r
+               }\r
+\r
+               // 存在していなければWeb上から\r
+               String uri = "http://dimora.jp/";\r
+               String response = webToBuffer(uri, thisEncoding, true);;\r
+               if ( response == null ) {\r
+                       System.err.println(ERRID+"地域情報の取得に失敗しました: "+uri);\r
+                       return;\r
+               }\r
+\r
+               // 地域一覧の作成\r
+               ArrayList<AreaCode> newaclist = new ArrayList<AreaCode>();\r
+               \r
+               Matcher ma = Pattern.compile("<div\\s+id=\"includeAreaList\">(.+?)</div>").matcher(response);\r
+               if ( ma.find() ) {\r
+                       Matcher mb = Pattern.compile("(.+?),(.+?)\\|").matcher(ma.group(1));\r
+                       while ( mb.find() ) {\r
+                               AreaCode ac = new AreaCode();\r
+                               ac.setArea(mb.group(2));\r
+                               ac.setCode(mb.group(1));\r
+                               newaclist.add(ac);\r
+                       }\r
+               }\r
+               \r
+               if ( newaclist.size() == 0 ) {\r
+                       System.err.println(ERRID+"地域一覧の取得結果が0件だったため情報を更新しません");\r
+                       return;\r
+               }\r
+               \r
+               {\r
+                       AreaCode ac = new AreaCode();\r
+                       ac.setArea("全国");\r
+                       ac.setCode(allCode);\r
+                       newaclist.add(0,ac);\r
+               }\r
+               {\r
+                       AreaCode ac = new AreaCode();\r
+                       ac.setArea("BS");\r
+                       ac.setCode(bsCode);\r
+                       newaclist.add(ac);\r
+               }\r
+               \r
+               aclist = newaclist;\r
+               saveAreaCode();\r
+       }\r
+       \r
+       \r
+       /*******************************************************************************\r
+        * 放送局情報を取得する\r
+        ******************************************************************************/\r
+\r
+       // 設定ファイルがなければWebから取得\r
+       @Override\r
+       public void loadCenter(String code, boolean force) {\r
+               \r
+               if ( code == null ) {\r
+                       System.out.println(ERRID+"地域コードがnullです.");\r
+                       return;\r
+               }\r
+               \r
+               String centerListFile = getCenterListFile(getTVProgramId(), code);\r
+               \r
+               if (force) {\r
+                       File f = new File(centerListFile);\r
+                       f.delete();\r
+               }\r
+               \r
+               File f = new File(centerListFile);\r
+               if (f.exists() == true) {\r
+                       @SuppressWarnings("unchecked")\r
+                       ArrayList<Center> tmp = (ArrayList<Center>) CommonUtils.readXML(centerListFile);\r
+                       if ( tmp != null ) {\r
+                               \r
+                               crlist = tmp;\r
+                               attachChFilters();      // 放送局名変換\r
+                               \r
+                               System.out.println(MSGID+"放送局リストを読み込みました: "+centerListFile);\r
+                               return;\r
+                       }\r
+                       else {\r
+                               System.err.println(ERRID+"放送局リストの読み込みに失敗しました: "+centerListFile);\r
+                       }\r
+               }\r
+               \r
+               // Web上から放送局の一覧を取得する\r
+               ArrayList<Center> newcrlist = new ArrayList<Center>();\r
+               \r
+               int cntMax = ((code.equals(allCode))?(aclist.size()-2):(1));\r
+               int cnt = 1;\r
+               \r
+               if ( code.equals(allCode) ) {\r
+                       for ( AreaCode ac : aclist ) {\r
+                               if ( ! ac.getCode().equals(allCode) && ! ac.getCode().equals(bsCode) ) {\r
+                                       _loadCenter(newcrlist,ac.getCode(),(cnt!=cntMax));\r
+                                       reportProgress(MSGID+"放送局情報を取得しました: ("+cnt+"/"+cntMax+") ");\r
+                                       cnt++;\r
+                               }\r
+                       }\r
+               }\r
+               else if ( ! code.equals(bsCode) ) {\r
+                       _loadCenter(newcrlist,code,false);\r
+                       reportProgress(MSGID+"放送局情報を取得しました: ("+cnt+"/"+cntMax+") ");\r
+                       cnt++;\r
+               }\r
+               \r
+               if ( newcrlist.size() == 0 ) {\r
+                       System.err.println(ERRID+"放送局情報の取得結果が0件だったため情報を更新しません");\r
+                       return;\r
+               }\r
+\r
+               crlist = newcrlist;\r
+               attachChFilters();      // 放送局名変換\r
+               saveCenter();\r
+       }\r
+       \r
+       private boolean _loadCenter(ArrayList<Center> newcrlist, String code, boolean bsexist) {\r
+               // 本体\r
+               String uri = "http://dimora.jp/dc/pc/P4501.do";\r
+               \r
+               GregorianCalendar c = CommonUtils.getCalendar(0);\r
+               String cdate = CommonUtils.getDateTimeYMD(c);\r
+               String adate = CommonUtils.getDateYMD(c);\r
+               c.add(Calendar.DAY_OF_MONTH, 1);\r
+               String edate = CommonUtils.getDateYMD(c);\r
+               \r
+               clrCookie();\r
+               addCookie("KEY_AREA", code);\r
+               String response = webToBuffer(\r
+                               uri,\r
+                               String.format("c_time=%s&win_id=P4501&isLogin=1&start_time=%s0500&end_time=%s0500&ch_type=%s&args=%s0500%s%s%s",\r
+                                               cdate,\r
+                                               adate,\r
+                                               edate,\r
+                                               getChType(),\r
+                                               adate,\r
+                                               "%2C5%2C",\r
+                                               getChType(),\r
+                                               "%2C0%2C0%2C1%2C1%2CDR%2C10%2C1%2C1%2C1%2C1%2C1%2C1"),\r
+                               null,\r
+                               "http://dimora.jp/dc/pc/P4501.do",\r
+                               thisEncoding,\r
+                               true);\r
+               if ( response == null ) {\r
+                       System.err.println(ERRID+"放送局情報の取得に失敗しました: "+uri);\r
+                       return false;\r
+               }\r
+               \r
+               //CommonUtils.write2file("TMP.htm", response);\r
+               \r
+               String expr = String.format("<div\\s+id=\"%s\"\\s*>(.+?)</div>", getCenterInfoId());\r
+               Matcher ma = Pattern.compile(expr).matcher(response);\r
+               if ( ma.find() ) {\r
+                       Matcher mb = Pattern.compile("(.+?),.+?,.+?,(.+?),(.+?),.+?\\|").matcher(ma.group(1));\r
+                       while ( mb.find() ) {\r
+                               String centerName = CommonUtils.unEscape(mb.group(3));\r
+                               String chNo = mb.group(2);\r
+                               String centerId = mb.group(1);\r
+                               \r
+                               String areacode = getCenterCode(centerId,code);\r
+                               if ( bsexist && areacode.equals(bsCode) ) {\r
+                                       // 重複するBSは排除\r
+                                       continue;\r
+                               }\r
+                               \r
+                               // NHK関連\r
+                               if ( areacode.equals(bsCode) ) {\r
+                                       if ( centerName.startsWith("NHK") ) {\r
+                                               centerName = String.format("%s(%s)", centerName, chNo);\r
+                                       }\r
+                               }\r
+                               else {\r
+                                       Matcher mc = Pattern.compile("^NHK(総合|Eテレ)(\\d+)・?(.+)$").matcher(centerName);\r
+                                       if ( mc.find() ) {\r
+                                               String prefix = "";\r
+                                               if ( mc.group(1).equals("総合") ) {\r
+                                                       prefix = "NHK総合";\r
+                                               }\r
+                                               else if ( mc.group(1).equals("Eテレ") ) {\r
+                                                       prefix = "NHK Eテレ";\r
+                                               }\r
+                                               else {\r
+                                                       prefix = "NHK"+mc.group(1);\r
+                                               }\r
+                                               if ( mc.group(2).equals("1") ) {\r
+                                                       centerName = prefix+"・"+mc.group(3);\r
+                                               }\r
+                                               else {\r
+                                                       centerName = prefix+mc.group(2)+"・"+mc.group(3);\r
+                                               }\r
+                                       }\r
+                               }\r
+                               \r
+                               Center cr = new Center();\r
+                               cr.setAreaCode(areacode);\r
+                               cr.setCenterOrig(centerName);\r
+                               cr.setLink(centerId);\r
+                               cr.setType("");\r
+                               cr.setEnabled(true);\r
+                               if ( response.matches(String.format(".*<div\\s+id=\"%s\">.*",centerId)) ) {\r
+                                       newcrlist.add(cr);\r
+                               }\r
+                       }\r
+               }\r
+               \r
+               return true;\r
+       }\r
+\r
+}\r
diff --git a/TinyBannavi/src/tainavi/PlugIn_TVPEDCB.java b/TinyBannavi/src/tainavi/PlugIn_TVPEDCB.java
new file mode 100644 (file)
index 0000000..5332c2a
--- /dev/null
@@ -0,0 +1,1022 @@
+package tainavi;\r
+\r
+import java.io.File;\r
+import java.util.ArrayList;\r
+import java.util.Calendar;\r
+import java.util.Collections;\r
+import java.util.Date;\r
+import java.util.GregorianCalendar;\r
+import java.util.HashMap;\r
+import java.util.regex.Matcher;\r
+import java.util.regex.Pattern;\r
+\r
+import tainavi.MapCtrl.KeyType;\r
+\r
+import epgdump.EIT_CONTROL;\r
+import epgdump.Epgdump;\r
+import epgdump.SVT_CONTROL;\r
+\r
+\r
+public class PlugIn_TVPEDCB extends TVProgramUtils implements TVProgram,Cloneable {\r
+\r
+       public PlugIn_TVPEDCB clone() {\r
+               return (PlugIn_TVPEDCB) super.clone();\r
+       }\r
+\r
+       private static final String thisEncoding = "UTF-8";\r
+       \r
+       \r
+       /*******************************************************************************\r
+        * 種族の特性\r
+        ******************************************************************************/\r
+       \r
+       @Override\r
+       public String getTVProgramId() { return "EDCB"; }\r
+       \r
+       @Override\r
+       public ProgType getType() { return ProgType.PROG; }\r
+       @Override\r
+       public ProgSubtype getSubtype() { return ProgSubtype.TERRA; }\r
+\r
+       \r
+       /*******************************************************************************\r
+        * 個体の特性\r
+        ******************************************************************************/\r
+       \r
+       @Override\r
+       public int getTimeBarStart() {return 5;}\r
+       \r
+       private int getDogDays() { return ((getExpandTo8())?(8):(7)); }\r
+       \r
+       \r
+       /*******************************************************************************\r
+        * 定数\r
+        ******************************************************************************/\r
+       \r
+       private static final String OPTKEY_DIR = "datdir";\r
+       private static final String OPTVAL_DIR_DEFAULT = "D:/PT2/EpgDataCap_Bon/Setting/EpgData/";\r
+       private static final String fnext = "_epg.dat";\r
+\r
+       private static final String OPTKEY_URL = "url";\r
+       private static final String OPTVAL_URL_DEFAULT = "http://127.0.0.1:5510/(dat参照をやめる場合はこのコメントを削除)";\r
+       \r
+       private static final String TEXT_NANSHICHO_HEADER = "臨)";\r
+       \r
+       private final String MSGID = "["+getTVProgramId()+"] ";\r
+       private final String ERRID = "[ERROR]"+MSGID;\r
+       private final String DBGID = "[DEBUG]"+MSGID;\r
+\r
+       /*******************************************************************************\r
+        * 部品\r
+        ******************************************************************************/\r
+       \r
+       // 新しい入れ物の臨時格納場所\r
+       private ArrayList<ProgList> newplist;\r
+       private ArrayList<SVT_CONTROL> svtlist;\r
+       \r
+       // 未定義のフラグの回収場所\r
+       private HashMap<String,String> nf = null;\r
+       \r
+       private String getProgCacheFile() { return getProgDir()+File.separator+"EDCB.xml"; }\r
+       \r
+       /*\r
+        * フリーオプション用のクラス\r
+        */\r
+\r
+       private OptMap opts = null;\r
+       \r
+       private class OptMap extends MapCtrl {\r
+               @Override\r
+               protected boolean chkOptString() {\r
+                       // ディレクトリ設定があるかしら\r
+                       {\r
+                               String optval = get(OPTKEY_DIR);\r
+                               if ( optval == null ) {\r
+                                       // なければデフォルト\r
+                                       put(OPTKEY_DIR, OPTVAL_DIR_DEFAULT);\r
+                                       optval = get(OPTKEY_DIR);\r
+                                       System.out.println(MSGID+"[EDCB番組表未使用の方は無視してください] EPG.datフォルダはデフォルト値になります: "+optval);\r
+                               }\r
+                               else {\r
+                                       System.out.println(MSGID+"[EDCB番組表未使用の方は無視してください] EPG.datフォルダは設定値になります: "+optval);\r
+                               }\r
+                       }\r
+                       // URL設定があるかしら\r
+                       {\r
+                               String optval = get(OPTKEY_URL);\r
+                               if ( optval == null || ! optval.matches("^https?://[^/]+?:\\d+/$") ) {\r
+                                       /// なければデフォルト\r
+                                       put(OPTKEY_URL, OPTVAL_URL_DEFAULT);\r
+                                       optval = get(OPTKEY_URL);\r
+                                       System.out.println(MSGID+"[EDCB番組表未使用の方は無視してください] EDCBのURLはデフォルト値になります: "+optval);\r
+                               }\r
+                               else {\r
+                                       System.out.println(MSGID+"[EDCB番組表未使用の方は無視してください] EDCBのURLは設定値になります: "+optval);\r
+                               }\r
+                       }\r
+                       \r
+                       return true;\r
+               }\r
+       }\r
+       \r
+       // datファイルをリストアップする\r
+       private ArrayList<String> getDatFiles(String path, String ext) {\r
+               ArrayList<String> dfiles = new ArrayList<String>();\r
+               File d = new File(path);\r
+               if ( d.isDirectory() ) {\r
+                       for ( String fn : d.list() ) {\r
+                               File fdat = new File(path+fn);\r
+                               if ( fdat.isFile() && fn.endsWith(ext) ) {\r
+                                       dfiles.add(path+fn);\r
+                               }\r
+                       }\r
+                       Collections.sort(dfiles);\r
+                       return dfiles;\r
+               }\r
+               else {\r
+                       return new ArrayList<String>();\r
+               }\r
+       }\r
+       \r
+\r
+       /*******************************************************************************\r
+        * コンストラクタ\r
+        ******************************************************************************/\r
+       \r
+       public PlugIn_TVPEDCB() {\r
+               \r
+               super();\r
+               \r
+               opts = new OptMap();\r
+               \r
+               // 保存先の設定\r
+               opts.setFilename("env"+File.separator+"options_"+getTVProgramId()+".xml");\r
+               \r
+               // オプションキーリストの設定\r
+               opts.putdef(OPTKEY_DIR, KeyType.DIR);\r
+               opts.putdef(OPTKEY_URL, KeyType.URL);\r
+\r
+               //\r
+               opts.initOptString();\r
+                       \r
+       }\r
+       \r
+       @Override\r
+       public boolean setOptString(String s) {\r
+               if ( s == null ) {\r
+                       if (getDebug()) System.out.println(DBGID+"フリーオプションがリセットされました.");\r
+                       return opts.initOptString();\r
+               }\r
+               return opts.setOptString(s);\r
+       }\r
+       \r
+       @Override\r
+       public String getOptString() {\r
+               // ちょっとちょーだい\r
+               return opts.toString();\r
+       }\r
+       \r
+       \r
+       /*******************************************************************************\r
+        * 番組情報を取得する\r
+        ******************************************************************************/\r
+       \r
+       @Override\r
+       public void loadProgram(String areaCode, boolean force) {\r
+               \r
+               // 新しい入れ物(トップ)を用意する\r
+               newplist = new ArrayList<ProgList>();\r
+               svtlist = new ArrayList<SVT_CONTROL>();\r
+               nf = new HashMap<String, String>();\r
+               \r
+               // トップの下に局ごとのリストを生やす\r
+               for ( Center cr : crlist ) {\r
+                       \r
+                       if ( cr.getOrder() <= 0 ) {\r
+                               // 設定上無効な局はいらない\r
+                               continue;\r
+                       }\r
+                       \r
+                       // ProgList\r
+                       ProgList pl = new ProgList();\r
+                       pl.Area = cr.getAreaCode();\r
+                       pl.SubArea = cr.getType();\r
+                       pl.Center = cr.getCenter();\r
+                       pl.BgColor = cr.getBgColor();\r
+                       pl.CenterId = cr.getLink();\r
+                       pl.enabled = true;\r
+                       newplist.add(pl);\r
+                       \r
+                       // SVT\r
+                       SVT_CONTROL svt = new SVT_CONTROL();\r
+                       svt.setServicename(pl.Center);\r
+                       setCenterLink(svt,cr.getLink());\r
+                       svt.setEnabled(true);\r
+                       svtlist.add(svt);\r
+                       \r
+                       if (getDebug()) System.err.println(DBGID+"Center added: "+pl.Center+"("+pl.CenterId+")");\r
+               }\r
+               \r
+               // 局の下に日付ごとのリストを生やす(当日前日からの日跨りを考慮して、前日から7+1日分)\r
+               GregorianCalendar cal = new GregorianCalendar();\r
+               cal.setTime(new Date());\r
+               cal.add(Calendar.DATE, -1);\r
+               if ( CommonUtils.isLateNight(cal) ) {\r
+                       // 4時までは当日扱いにする\r
+                       cal.add(Calendar.DATE, -1);\r
+               }\r
+               GregorianCalendar cale = (GregorianCalendar) cal.clone();\r
+               for (int i=0; i<getDogDays()+1; i++) {\r
+                       String date = CommonUtils.getDate(cale);\r
+                       for ( ProgList pl : newplist ) {\r
+                               ProgDateList cl = new ProgDateList();\r
+                               cl.Date = date;\r
+                               pl.pdate.add(cl);\r
+                       }\r
+                       cale.add(Calendar.DATE, 1);\r
+               }\r
+               \r
+               // 日付の下に番組情報ごとのリストを生やす\r
+               if ( opts.get(OPTKEY_URL) != null && opts.get(OPTKEY_URL).matches("^https?://[^/]+?:\\d+/$") ) {\r
+                       //\r
+                       int counterMax = getSortedCRlist().size();\r
+                       int counter=1;\r
+                       for ( Center c : getSortedCRlist() ) {\r
+                               _loadProgramFromAPI(c, force, counter++, counterMax);\r
+                       }\r
+               }\r
+               else {\r
+                       _loadProgramFromDAT(force);\r
+               }\r
+               \r
+               // 隙間を埋める\r
+               for ( ProgList pl : newplist ) {\r
+                       refreshList(pl.pdate);\r
+               }\r
+               \r
+               // 余分に作成した前日分はカット\r
+               for ( ProgList pl : newplist ) {\r
+                       pl.pdate.remove(0);\r
+               }\r
+               \r
+               // 開始・終了日時を正しい値に計算しなおす\r
+               for ( ProgList pl : newplist ) {\r
+                       setAccurateDate(pl.pdate);\r
+               }\r
+               \r
+               // 古いデータから補完できないかな?\r
+               CompensatesPrograms(newplist);\r
+               \r
+               // 解析用\r
+               {\r
+                       for ( String f : nf.keySet() ) {\r
+                               System.err.println(String.format(DBGID+"未定義のフラグです: [%s]",f));\r
+                       }\r
+               }\r
+               \r
+               // 古い番組データを置き換える\r
+               pcenter = newplist;\r
+               svtlist = null;\r
+       }\r
+       \r
+       /* ここまで */\r
+\r
+       \r
+       \r
+       /*\r
+        * 非公開メソッド\r
+        */\r
+       \r
+       private void _loadProgramFromAPI(Center cr, boolean force, int counter, int counterMax) {\r
+               //\r
+               try {\r
+                       \r
+                       // いれるところ\r
+                       SVT_CONTROL svt = new SVT_CONTROL();\r
+                       ContentIdEDCB.decodeChId(cr.getLink());\r
+                       svt.setOriginal_network_id(ContentIdEDCB.getOnId());\r
+                       svt.setTransport_stream_id(ContentIdEDCB.getTSId());\r
+                       svt.setServive_id(ContentIdEDCB.getSId());\r
+                       svt.setServicename(cr.getCenterOrig());\r
+                       svt.setEnabled(true);\r
+                       \r
+                       svtlist = new ArrayList<SVT_CONTROL>();\r
+                       svtlist.add(svt);\r
+                       \r
+                       final String progCacheFile = String.format(getProgDir()+File.separator+"TVEDCB_%s.xml.zip", cr.getLink());      // getLink()はCHコード\r
+                       String response = null;\r
+                       \r
+                       File f = new File(progCacheFile);\r
+                       if (force == true ||\r
+                                       (f.exists() == true && isCacheOld(progCacheFile) == true) ||\r
+                                       (f.exists() == false && isCacheOld(null) == true)) {\r
+                               \r
+                               String url = opts.get(OPTKEY_URL)+"api/EnumEventInfo?ONID="+svt.getOriginal_network_id()+"&TSID="+svt.getTransport_stream_id()+"&SID="+svt.getServive_id()+"&basic=0&count=2000";\r
+                               \r
+                               // Web番組表の読み出し\r
+                               response = webToBuffer(url, thisEncoding, true);\r
+                               \r
+                               // キャッシュファイルの保存\r
+                               if ( ! CommonUtils.write2file(progCacheFile, response) ) {\r
+                                       reportProgress(ERRID+"番組表(キャッシュ)の保存に失敗しました: ("+counter+"/"+counterMax+") "+progCacheFile);\r
+                               }\r
+                               \r
+                               reportProgress(MSGID+"(オンライン)を取得しました: ("+counter+"/"+counterMax+") "+cr.getCenter()+"    "+url);\r
+                       }\r
+                       else if (f.exists()) {\r
+                               // キャッシュファイルの読み込み\r
+                               response = CommonUtils.read4file(progCacheFile, true);\r
+                               if ( response == null ) {\r
+                                       reportProgress(ERRID+"番組表(キャッシュ)の取得に失敗しました: ("+counter+"/"+counterMax+") "+cr.getCenter()+"    "+progCacheFile);\r
+                               }\r
+                               reportProgress(MSGID+"番組表(キャッシュ)を取得しました: ("+counter+"/"+counterMax+") "+cr.getCenter()+"    "+progCacheFile);\r
+                       }\r
+                       else {\r
+                               reportProgress(ERRID+"番組表(キャッシュ)がみつかりません: ("+counter+"/"+counterMax+") "+cr.getCenter()+"    "+progCacheFile);\r
+                               return;\r
+                       }\r
+\r
+                       //\r
+                       \r
+                       Matcher ma = Pattern.compile("<eventinfo>(.+?)</eventinfo>", Pattern.DOTALL).matcher(response);\r
+                       while ( ma.find() ) {\r
+\r
+                               Integer evid = null;\r
+                               GregorianCalendar cal = null;\r
+                               Integer hh = null;\r
+                               Integer hm = null;\r
+                               Integer length = null;\r
+                               String title = null;\r
+                               String subtitle = null;\r
+                               String detail = null; \r
+                               ArrayList<String> content_type = new ArrayList<String>();\r
+                               \r
+                               Matcher mb = Pattern.compile("<(.+?)>(.+?)</\\1>", Pattern.DOTALL).matcher(ma.group(1));\r
+                               while ( mb.find() ) {\r
+                                       if ( mb.group(1).equals("eventID") ) {\r
+                                               evid = Integer.valueOf(mb.group(2));\r
+                                       }\r
+                                       else if ( mb.group(1).equals("startDate") ) {\r
+                                               Matcher mc = Pattern.compile("^(\\d+)/(\\d+)/(\\d+)").matcher(mb.group(2));\r
+                                               if ( mc.find() ) {\r
+                                                       cal = CommonUtils.getCalendar(String.format("%04d/%02d/%02d",Integer.valueOf(mc.group(1)),Integer.valueOf(mc.group(2)),Integer.valueOf(mc.group(3))));\r
+                                               }\r
+                                       }\r
+                                       else if ( mb.group(1).equals("startTime") ) {\r
+                                               Matcher mc = Pattern.compile("^(\\d+):(\\d+)").matcher(mb.group(2));\r
+                                               if ( mc.find() ) {\r
+                                                       hh = Integer.valueOf(mc.group(1));\r
+                                                       hm = Integer.valueOf(mc.group(2));\r
+                                               }\r
+                                       }\r
+                                       else if ( mb.group(1).equals("duration") ) {\r
+                                               length = Integer.valueOf(mb.group(2))/60;\r
+                                       }\r
+                                       else if ( mb.group(1).equals("event_name") ) {\r
+                                               title = CommonUtils.unEscape(mb.group(2));\r
+                                       }\r
+                                       else if ( mb.group(1).equals("event_text") ) {\r
+                                               subtitle = CommonUtils.unEscape(mb.group(2));\r
+                                       }\r
+                                       else if ( mb.group(1).equals("event_ext_text") ) {\r
+                                               detail = CommonUtils.unEscape(mb.group(2));\r
+                                       }\r
+                                       else if ( mb.group(1).equals("contentInfo") ) {\r
+                                               Integer g = null;\r
+                                               Integer subg = null;\r
+                                               Matcher mc = Pattern.compile("<(.+?)>(\\d+?)</\\1>", Pattern.DOTALL).matcher(mb.group(2));\r
+                                               while ( mc.find() ) {\r
+                                                       if ( mc.group(1).equals("nibble1") ) {\r
+                                                               g = Integer.valueOf(mc.group(2));\r
+                                                       }\r
+                                                       else if ( mc.group(1).equals("nibble2") ) {\r
+                                                               subg = Integer.valueOf(mc.group(2));\r
+                                                       }\r
+                                               }\r
+                                               if ( g != null && subg != null ) {\r
+                                                       content_type.add(String.format("%X%X", g, subg));\r
+                                                       /* - 結局API経由でも拡張ジャンルコードは取得できなかった -\r
+                                                       if ( ProgGenre.getByIEPG(String.format("%X", g)) == null ) {\r
+                                                               System.out.println(DBGID+"未定義のジャンルコード "+g+", "+subg);\r
+                                                       }\r
+                                                       */\r
+                                               }\r
+                                       }\r
+                               }\r
+                               \r
+                               //\r
+                               \r
+                               EIT_CONTROL eit = new EIT_CONTROL();\r
+                               \r
+                               eit.setEvent_id(evid);\r
+                               \r
+                               eit.setYy(cal.get(Calendar.YEAR));\r
+                               eit.setMm(cal.get(Calendar.MONTH)+1);\r
+                               eit.setDd(cal.get(Calendar.DAY_OF_MONTH));\r
+                               \r
+                               cal.set(Calendar.HOUR, hh);\r
+                               cal.set(Calendar.MINUTE, hm);\r
+                               eit.setHh(hh);\r
+                               eit.setHm(hm);\r
+                               eit.setSs(0);\r
+                               \r
+                               int dhm = length % 60;\r
+                               int dhh = (length-dhm) / 60;\r
+                               \r
+                               eit.setDhh(dhh);\r
+                               eit.setDhm(dhm);\r
+                               \r
+                               /* - 使わないので要らない -\r
+                               cal.add(Calendar.MINUTE, length);\r
+                               eit.setEhh(cal.get(Calendar.HOUR));\r
+                               eit.setEhm(cal.get(Calendar.DAY_OF_MONTH));\r
+                               eit.setEss(0);\r
+                               */\r
+                               \r
+                               eit.setTitle(title);\r
+                               eit.setSubtitle(subtitle);\r
+                               eit.setDetail(detail);\r
+                               eit.setContent_type(content_type);\r
+                               \r
+                               /* - 使わないので要らない -\r
+                               eit.setPerformer(null)\r
+                               */\r
+                               \r
+                               int n = 0;\r
+                               for ( EIT_CONTROL etmp : svt.getEittop() ) {\r
+                                       String a = String.format("%04d%02d%02d%02d%02d", etmp.getYy(), etmp.getMm(), etmp.getDd(), etmp.getHh(), etmp.getHm());\r
+                                       String b = String.format("%04d%02d%02d%02d%02d", eit.getYy(), eit.getMm(), eit.getDd(), eit.getHh(), eit.getHm());\r
+                                       if ( a.compareTo(b) > 0 ) {\r
+                                               break;\r
+                                       }\r
+                                       n++;\r
+                               }\r
+                               \r
+                               svt.getEittop().add(n,eit);\r
+                       }\r
+                       \r
+                       // データ形式の変換\r
+                       convEpg2Programs();\r
+               }\r
+               catch (Exception e) {\r
+                       reportProgress(ERRID+"番組表の取得で例外が発生しました: "+e.toString());\r
+                       e.printStackTrace();\r
+               }\r
+       }\r
+       \r
+       private void _loadProgramFromDAT(boolean force) {\r
+               //\r
+               final String progCacheFile = getProgCacheFile();\r
+               //\r
+               try {\r
+                       //\r
+                       File f = new File(progCacheFile);\r
+                       if (force == true ||\r
+                                       (f.exists() == true && isCacheOld(progCacheFile) == true) ||\r
+                                       (f.exists() == false && isCacheOld(null) == true)) {\r
+                               \r
+                               // 参照するファイルの一覧\r
+                               ArrayList<String> dfiles = getDatFiles(opts.get(OPTKEY_DIR),fnext);\r
+                               \r
+                               // 参照する総ファイル数\r
+                               int counterMax = dfiles.size();\r
+\r
+                               // 入力クラス\r
+                               Epgdump epg = new Epgdump();\r
+\r
+                               // EPGデータの取得\r
+                               TatCount tcall = new TatCount();\r
+                               TatCount tc = new TatCount();\r
+                               for ( int i=0; i<counterMax; i++ ) {\r
+                                       /*\r
+                                       if ( ! dfiles.get(i).matches(".*000440D0_epg.dat*") ) {\r
+                                               continue;\r
+                                       }\r
+                                       */\r
+                                       tc.restart();\r
+                                       if ( epg.getEitControl(dfiles.get(i), svtlist) == null ) {\r
+                                               reportProgress("+データが取得できませんでした.");\r
+                                               return;\r
+                                       }\r
+                                       reportProgress(String.format("%s(EPGデータ)を取得しました[%5.1fMB,%.2f秒]: (%d/%d) %s",getTVProgramId(),new File(dfiles.get(i)).length()/1000000.0D,tc.end(),i+1,counterMax,dfiles.get(i)));\r
+                               }\r
+                               \r
+                               reportProgress(String.format("%s(EPGデータ)をすべて取得しました[%.2f秒]",getTVProgramId(),tcall.end()));\r
+                               \r
+                               epg = null;\r
+                               \r
+                               tc.restart();\r
+                               reportProgress(String.format("%s(キャッシュ)を保存します: %s",getTVProgramId(),progCacheFile));\r
+                               if ( ! CommonUtils.writeXML(progCacheFile, svtlist) ) {\r
+                                       reportProgress(getTVProgramId()+"(キャッシュ)の保存に失敗しました: "+progCacheFile);\r
+                               }\r
+                               else {\r
+                                       reportProgress(String.format("%s(キャッシュ)を保存しました[%5.1fMB,%.2f秒]: %s",getTVProgramId(),new File(progCacheFile).length()/1000000.0D,tcall.end(),progCacheFile));\r
+                               }\r
+                       }\r
+                       else if (f.exists()) {\r
+                               TatCount tc = new TatCount();\r
+                               reportProgress(String.format("%s(キャッシュ)を取得します(選択した局が多いと時間がかかります): %s (%.2fMB)",getTVProgramId(),progCacheFile,f.length()/1000000.0D));\r
+                               @SuppressWarnings("unchecked")\r
+                               ArrayList<SVT_CONTROL> ns = (ArrayList<SVT_CONTROL>) CommonUtils.readXML(progCacheFile);\r
+                               if ( ns != null ) {\r
+                                       svtlist = ns;\r
+                                       reportProgress(String.format("%s(キャッシュ)を取得しました (%.2f秒): %s",getTVProgramId(),tc.end(),progCacheFile));\r
+                               }\r
+                               else {\r
+                                       reportProgress(getTVProgramId()+"(キャッシュ)が取得できません: "+progCacheFile);\r
+                                       return;\r
+                               }\r
+                       }\r
+                       else {\r
+                               reportProgress(getTVProgramId()+"(キャッシュ)がみつかりません: "+progCacheFile);\r
+                               return;\r
+                       }\r
+                       \r
+                       if (getDebug()) {\r
+                               for ( SVT_CONTROL svt : svtlist ) {\r
+                                       System.err.println("[DEBUG] programs loaded: "+svt.getServicename()+" ("+svt.getEittop().size()+")");\r
+                               }\r
+                       }\r
+                       \r
+                       // データ形式の変換\r
+                       convEpg2Programs();\r
+                       \r
+               }\r
+               catch (Exception e) {\r
+                       // 例外\r
+                       System.out.println("Exception: _loadProgram()");\r
+                       e.printStackTrace();\r
+               }\r
+       }\r
+       \r
+       //\r
+       private void convEpg2Programs() {\r
+               \r
+               for ( SVT_CONTROL svt : svtlist ) {\r
+                       \r
+                       // 不要になる領域は早々に処分する\r
+                       ArrayList<EIT_CONTROL> eittop = svt.getEittop();\r
+                       svt.setEittop(null);\r
+                       \r
+                       if ( eittop.size() == 0 ) {\r
+                               continue;\r
+                       }\r
+                       \r
+                       // 登録先の放送局をみつける\r
+                       ProgList pl = null;\r
+                       for ( int i=0; i<newplist.size(); i++ ) {\r
+                               if ( newplist.get(i).CenterId.equals(getCenterLink(svt)) ) {\r
+                                       pl = newplist.get(i);\r
+                                       break;\r
+                               }\r
+                       }\r
+                       if ( pl == null ) {\r
+                               continue;\r
+                       }\r
+                       \r
+                       for ( EIT_CONTROL eit : eittop ) {\r
+                               \r
+                               ProgDetailList pdl = new ProgDetailList();\r
+                               \r
+                               // 開始日時\r
+                               GregorianCalendar ca = new GregorianCalendar();\r
+                               ca.set(Calendar.YEAR, eit.getYy());\r
+                               ca.set(Calendar.MONTH, eit.getMm()-1);\r
+                               ca.set(Calendar.DATE, eit.getDd());\r
+                               ca.set(Calendar.HOUR_OF_DAY, eit.getHh());\r
+                               ca.set(Calendar.MINUTE, eit.getHm());\r
+                               ca.set(Calendar.SECOND, 0);\r
+                               pdl.start = CommonUtils.getTime(ca);\r
+                               pdl.startDateTime = CommonUtils.getDateTime(ca);\r
+                               \r
+                               // 登録先の日付をみつける\r
+                               GregorianCalendar ct = (GregorianCalendar) ca.clone();\r
+                               if ( CommonUtils.isLateNight(ca) ) {\r
+                                       ct.add(Calendar.DATE,-1);\r
+                               }\r
+                               String tDate = CommonUtils.getDate(ct);\r
+                               ProgDateList pcl = null;\r
+                               for ( int i=0; i<pl.pdate.size(); i++ ) {\r
+                                       if ( pl.pdate.get(i).Date.equals(tDate) ) {\r
+                                               pcl = pl.pdate.get(i);\r
+                                               break;\r
+                                       }\r
+                               }\r
+                               if ( pcl == null ) {\r
+                                       continue;\r
+                               }\r
+                               \r
+                               // 長さ\r
+                               pdl.length = eit.getDhh()*60 + eit.getDhm();\r
+                               \r
+                               // 終了日時\r
+                               GregorianCalendar cz = (GregorianCalendar) ca.clone();\r
+                               cz.add(Calendar.MINUTE, pdl.length);\r
+                               pdl.end = CommonUtils.getTime(cz);\r
+                               pdl.endDateTime = CommonUtils.getDateTime(cz);\r
+\r
+                               // 番組ID\r
+                               pdl.progid = ContentIdEDCB.getContentId(pl.CenterId,eit.getEvent_id());\r
+                               \r
+                               // タイトル&番組詳細\r
+                               pdl.title = (eit.getTitle() == null) ? "" : eit.getTitle();\r
+                               pdl.detail = \r
+                                               ((eit.getSubtitle() != null)   ? (eit.getSubtitle()+DETAIL_SEP):(""))\r
+                                               +((eit.getDetail() != null)    ? (eit.getDetail()+DETAIL_SEP):(""))\r
+                                               +((eit.getPerformer() != null) ? (eit.getPerformer()+DETAIL_SEP):(""));\r
+                               pdl.detail = pdl.detail.replaceFirst("[\r\n]+$", "");\r
+                               \r
+                               // タイトルから各種フラグを分離する\r
+                               doSplitFlags(pdl, nf);\r
+                               \r
+                               // ジャンル\r
+                               setMultiGenre(pdl,eit.getContent_type());\r
+                               \r
+                               // サブタイトル分離\r
+                               doSplitSubtitle(pdl);\r
+                               \r
+                               // 検索対象外領域にこっそりジャンル文字列を入れる\r
+                               pdl.setGenreStr();\r
+                               \r
+                               // 検索対象外領域にこっそりID文字列を入れる(progidもここで入る)\r
+                               pdl.setContentIdStr();\r
+                                \r
+                               // その他フラグ\r
+                               pdl.extension = false;\r
+                               //pdl.flag = ProgFlags.NOFLAG;\r
+                               pdl.nosyobo = false;\r
+                               \r
+                               //\r
+                               pcl.pdetail.add(pdl);\r
+                               \r
+                               //\r
+                               pcl.row += pdl.length;\r
+                       }\r
+               }\r
+       }\r
+       \r
+       //\r
+       private void refreshList(ArrayList<ProgDateList> pcenter) {\r
+               // 日付(29時)をまたいでいるものをコピーする\r
+               for ( int i=0; i<pcenter.size()-1; i++ ) {\r
+                       ProgDateList pl = pcenter.get(i);\r
+                       if ( pl.pdetail.size() <= 0 ) {\r
+                               continue;\r
+                       }\r
+                       // 基準日時\r
+                       GregorianCalendar tbca = CommonUtils.getCalendar(pl.Date+" "+CommonUtils.getTime(getTimeBarStart(),0));\r
+                       tbca.add(Calendar.DATE, 1);\r
+                       // 最終番組の終了日時\r
+                       ProgDetailList pdl = pl.pdetail.get(pl.pdetail.size()-1);\r
+                       GregorianCalendar cz = CommonUtils.getCalendar(pdl.endDateTime);\r
+                       // 29時をまたいでいるか\r
+                       if ( cz.compareTo(tbca) > 0 ) {\r
+                               // またいでいたので翌日の先頭にコピーする\r
+                               pdl = pdl.clone();\r
+                               pdl.length = (int) ((cz.getTimeInMillis() - tbca.getTimeInMillis())/60000L);\r
+                               pcenter.get(i+1).pdetail.add(0,pdl);\r
+                               \r
+                               if (getDebug()) System.err.println("[DEBUG] copy "+pdl.startDateTime+" - "+pdl.endDateTime+" to "+pcenter.get(i+1).Date);\r
+                       }\r
+               }\r
+               // 隙間を埋める\r
+               for ( ProgDateList pl : pcenter ) {\r
+                       ArrayList<ProgDetailList> cur = new ArrayList<ProgDetailList>();\r
+                       String preend = pl.Date.substring(0,10)+" "+CommonUtils.getTime(getTimeBarStart(),0);   // 最初の"前番組のおしり"は05:00\r
+                       for ( int i=0; i<pl.pdetail.size(); i++ ) {\r
+                               ProgDetailList pdl = pl.pdetail.get(i);\r
+                               if ( preend.compareTo(pdl.startDateTime) < 0 ) {\r
+                                       // 前の番組との間に隙間があれば埋める\r
+                                       ProgDetailList npdl = new ProgDetailList();\r
+                                       npdl.title = "番組情報がありません";\r
+                                       npdl.detail = "";\r
+                                       npdl.length = (int)(CommonUtils.getDiffDateTime(preend, pdl.startDateTime)/60000L);\r
+                                       cur.add(npdl);\r
+                               }\r
+                               cur.add(pdl);\r
+                               preend = pdl.endDateTime;\r
+                       }\r
+                       pl.pdetail = cur;\r
+               }\r
+               \r
+               // 総時間数等を整理する\r
+               for ( ProgDateList pl : pcenter ) {\r
+                       // 1日の合計分数を足し合わせる\r
+                       pl.row = 0;\r
+                       for ( ProgDetailList pdl : pl.pdetail ) {\r
+                               pl.row += pdl.length;\r
+                       }\r
+                       // おしりがとどかない場合(デメリット:これをやると、サイト側のエラーで欠けてるのか、そもそも休止なのかの区別がつかなくなる)\r
+                       if ( pl.row < 24*60 ) {\r
+                               ProgDetailList npdl = new ProgDetailList();\r
+                               npdl.title = "番組情報がありません";\r
+                               npdl.detail = "";\r
+                               npdl.length = 24*60 - pl.row;\r
+                               pl.pdetail.add(npdl);\r
+                               pl.row += npdl.length;\r
+                       }\r
+               }\r
+       }\r
+       \r
+       \r
+       /*******************************************************************************\r
+        * 地域情報を取得する\r
+        ******************************************************************************/\r
+       \r
+       // EDCBは地元というか実際に視聴できる局しか情報がとれないね\r
+       @Override\r
+       public String getDefaultArea() { return "すべて"; }\r
+       \r
+       //\r
+       @Override\r
+       public void loadAreaCode() {\r
+               // 地域一覧の作成\r
+               aclist = new ArrayList<AreaCode>();\r
+               {\r
+                       AreaCode ac = new AreaCode();\r
+                       ac.setArea("すべて");\r
+                       ac.setCode(allCode);\r
+                       aclist.add(ac);\r
+               }\r
+               {\r
+                       AreaCode ac = new AreaCode();\r
+                       ac.setArea("地上");\r
+                       ac.setCode(trCode);\r
+                       aclist.add(ac);\r
+               }\r
+               {\r
+                       AreaCode ac = new AreaCode();\r
+                       ac.setArea("BS");\r
+                       ac.setCode(bsCode);\r
+                       aclist.add(ac);\r
+               }\r
+               {\r
+                       AreaCode ac = new AreaCode();\r
+                       ac.setArea("CS");\r
+                       ac.setCode(csCode);\r
+                       aclist.add(ac);\r
+               }\r
+       }\r
+       \r
+       @Override\r
+       public void saveAreaCode() {\r
+       }\r
+       \r
+       \r
+       /*******************************************************************************\r
+        * 放送局情報を取得する\r
+        ******************************************************************************/\r
+       \r
+       // 設定ファイルがなければWebから取得\r
+       @SuppressWarnings("unchecked")\r
+       @Override\r
+       public void loadCenter(String code, boolean force) {\r
+               \r
+               String centerListFile = getCenterListFile(getTVProgramId(), code);\r
+               \r
+               if (force) {\r
+                       File f = new File(centerListFile);\r
+                       f.delete();\r
+               }\r
+               \r
+               File f = new File(centerListFile);\r
+               if (f.exists() == true) {\r
+                       ArrayList<Center> tmp = (ArrayList<Center>) CommonUtils.readXML(centerListFile);\r
+                       if ( tmp != null ) {\r
+                               \r
+                               crlist = tmp;\r
+                                               \r
+                               // 放送局名変換\r
+                               attachChFilters();\r
+                               \r
+                               System.out.println(MSGID+"放送局リストを読み込みました: "+centerListFile);\r
+                               return;\r
+                       }\r
+                       else {\r
+                               System.out.println(ERRID+"放送局リストの読み込みに失敗しました: "+centerListFile);\r
+                       }\r
+               }\r
+               \r
+               // Web上から放送局の一覧を取得する\r
+               ArrayList<Center> tmpCrList = new ArrayList<Center>();\r
+               ArrayList<Center> trCrList = new ArrayList<Center>();\r
+               ArrayList<Center> bsCrList = new ArrayList<Center>();\r
+               ArrayList<Center> csCrList = new ArrayList<Center>();\r
+               ArrayList<Center> otCrList = new ArrayList<Center>();\r
+               \r
+               if ( opts.get(OPTKEY_URL) != null && opts.get(OPTKEY_URL).matches("^https?://[^/]+?:\\d+/$") ) {\r
+                       // APIを使う\r
+                       String url = opts.get(OPTKEY_URL)+"api/EnumService"; \r
+                       String response = webToBuffer(url,thisEncoding,true);\r
+                       if ( response == null ) {\r
+                               System.err.println(ERRID+"放送局情報の取得に失敗しました: "+url);\r
+                               return;\r
+                       }\r
+                       \r
+                       reportProgress(MSGID+"放送局情報を取得しました: "+url);\r
+                       \r
+                       Matcher ma = Pattern.compile("<serviceinfo>(.+?)</serviceinfo>", Pattern.DOTALL).matcher(response);\r
+                       while ( ma.find() ) {\r
+                               Integer onid = null;\r
+                               Integer tsid = null;\r
+                               Integer sid = null;\r
+                               String ch_name = null;\r
+                               Matcher mb = Pattern.compile("<(.+?)>(.+?)</\\1>", Pattern.DOTALL).matcher(ma.group(1));\r
+                               while ( mb.find() ) {\r
+                                       /*\r
+                                        * 解析\r
+                                        */\r
+                                       \r
+                                       if ( mb.group(1).equals("service_name") ) {\r
+                                               ch_name = mb.group(2);\r
+                                       }\r
+                                       else if ( mb.group(1).equals("ONID") ) {\r
+                                               onid = Integer.valueOf(mb.group(2));\r
+                                       }\r
+                                       else if ( mb.group(1).equals("TSID") ) {\r
+                                               tsid = Integer.valueOf(mb.group(2));\r
+                                       }\r
+                                       else if ( mb.group(1).equals("SID") ) {\r
+                                               sid = Integer.valueOf(mb.group(2));\r
+                                       }\r
+                               }\r
+                               \r
+                               String chid = ContentIdEDCB.getChId(onid, tsid, sid);\r
+                               \r
+                               Center cr = new Center();\r
+                               cr.setLink(chid);\r
+                               cr.setAreaCode(getCenterCode(cr.getLink()));\r
+                               \r
+                               if ( tsid == 0x4310 || tsid == 0x4311 ) {\r
+                                       cr.setCenterOrig(TEXT_NANSHICHO_HEADER+ch_name);\r
+                               }\r
+                               else {\r
+                                       cr.setCenterOrig(ch_name);\r
+                               }\r
+                               \r
+                               cr.setCenter(null);\r
+                               cr.setType("");\r
+                               cr.setEnabled(true);\r
+                               \r
+                               tmpCrList.add(cr);\r
+                       }\r
+               }\r
+               else {\r
+                       // datファイルを使う\r
+                       if ( ! new File(opts.get(OPTKEY_DIR)).isDirectory() ) {\r
+                               System.out.println(ERRID+".datファイルの場所が正しくないため放送局リストの読み込みはキャンセルされました: "+opts.get(OPTKEY_DIR));\r
+                               return;\r
+                       }\r
+                       \r
+                       Epgdump epg = new Epgdump();\r
+                       \r
+                       ArrayList<String> dfiles = getDatFiles(opts.get(OPTKEY_DIR),fnext);\r
+                       if ( dfiles != null ) {\r
+                               // SVT\r
+                               ArrayList<SVT_CONTROL> svta = new ArrayList<SVT_CONTROL>();\r
+                               for ( int cnt=0; cnt<dfiles.size(); cnt++ ) {\r
+                                       String fpath = dfiles.get(cnt);\r
+                                       ArrayList<SVT_CONTROL> svttop = epg.getSvtControl(fpath);\r
+                                       for ( SVT_CONTROL svt : svttop ) {\r
+                                               epg.enqueueSVT(svta, svt);\r
+                                       }\r
+                                       reportProgress(MSGID+"放送局情報を取得しました: ("+(cnt+1)+"/"+dfiles.size()+") "+fpath);\r
+                               }\r
+                               //\r
+                               for ( SVT_CONTROL svt : svta ) {\r
+                                       Center cr = new Center();\r
+                                       cr.setLink(getCenterLink(svt));\r
+                                       cr.setAreaCode(getCenterCode(cr.getLink()));\r
+                                       \r
+                                       if ( svt.getTransport_stream_id() == 0x4310 || svt.getTransport_stream_id() == 0x4311 ) {\r
+                                               cr.setCenterOrig(TEXT_NANSHICHO_HEADER+svt.getServicename());\r
+                                       }\r
+                                       else {\r
+                                               cr.setCenterOrig(svt.getServicename());\r
+                                       }\r
+                                       \r
+                                       cr.setCenter(null);\r
+                                       cr.setType("");\r
+                                       cr.setEnabled(true);\r
+                                       \r
+                                       int n = 0;\r
+                                       for ( Center ctmp : tmpCrList ) {\r
+                                               if ( ctmp.getLink().compareTo(cr.getLink()) < 0 ) {\r
+                                                       break;\r
+                                               }\r
+                                               n++;\r
+                                       }\r
+                                       tmpCrList.add(n, cr);\r
+                               }\r
+                       }\r
+               }\r
+               for ( Center cr : tmpCrList ) {\r
+                       // 登録順序を整理する\r
+                       if ( cr.getAreaCode().equals(trCode) ) {\r
+                               addCenter(trCrList,cr);\r
+                               //trCrList.add(cr);\r
+                       }\r
+                       else if ( cr.getAreaCode().equals(bsCode) ) {\r
+                               addCenter(bsCrList,cr);\r
+                               //bsCrList.add(cr);\r
+                       }\r
+                       else if ( cr.getAreaCode().equals(csCode) ) {\r
+                               addCenter(csCrList,cr);\r
+                               //csCrList.add(cr);\r
+                       }\r
+                       else {\r
+                               addCenter(otCrList,cr);\r
+                               //otCrList.add(cr);\r
+                       }\r
+               }\r
+               \r
+               ArrayList<Center> newcrlist = new ArrayList<Center>();\r
+               \r
+               for ( Center cr : trCrList ) {\r
+                       newcrlist.add(cr);\r
+               }\r
+               for ( Center cr : bsCrList ) {\r
+                       newcrlist.add(cr);\r
+               }\r
+               for ( Center cr : csCrList ) {\r
+                       newcrlist.add(cr);\r
+               }\r
+               for ( Center cr : otCrList ) {\r
+                       newcrlist.add(cr);\r
+               }\r
+               \r
+               if ( newcrlist.size() == 0 ) {\r
+                       System.err.println(ERRID+"放送局情報の取得結果が0件だったため情報を更新しません");\r
+                       return;\r
+               }\r
+               \r
+               crlist = newcrlist; \r
+               chkCrNameDuped(crlist); // 放送局名の重複排除\r
+               attachChFilters();              // 放送局名変換\r
+               saveCenter();\r
+       }\r
+       \r
+       private void addCenter(ArrayList<Center> newcrlist, Center cr) {\r
+               int n=0;\r
+               for ( ; n<newcrlist.size(); n++ ) {\r
+                       if ( newcrlist.get(n).getLink().compareTo(cr.getLink()) > 0 ) {\r
+                               break;\r
+                       }\r
+               }\r
+               newcrlist.add(n,cr);\r
+       }\r
+       \r
+       //\r
+       private String getCenterLink(SVT_CONTROL svt) {\r
+               return ContentIdEDCB.getChId(svt.getOriginal_network_id(),svt.getTransport_stream_id(),svt.getServive_id());\r
+       }\r
+       \r
+       //\r
+       private void setCenterLink(SVT_CONTROL svt, String link) {\r
+               if ( ContentIdEDCB.decodeChId(link) ) {\r
+                       svt.setOriginal_network_id(ContentIdEDCB.getOnId());\r
+                       svt.setTransport_stream_id(ContentIdEDCB.getTSId());\r
+                       svt.setServive_id(ContentIdEDCB.getSId());\r
+               }\r
+       }\r
+       \r
+       //\r
+       private void chkCrNameDuped(ArrayList<Center> crList) {\r
+               HashMap<String,String> crNameMap = new HashMap<String, String>();\r
+               for ( Center cr : crList ){\r
+                       if ( crNameMap.get(cr.getCenterOrig()) == null ) {\r
+                               crNameMap.put(cr.getCenterOrig(),"BINGO!");\r
+                       }\r
+                       else {\r
+                               for ( int n=2; n<10; n++ ) {\r
+                                       String nName = cr.getCenterOrig()+"・"+n;\r
+                                       if ( crNameMap.get(nName) == null ) {\r
+                                               crNameMap.put(nName,"BINGO!");\r
+                                               cr.setCenterOrig(nName);\r
+                                               break;\r
+                                       }\r
+                               }\r
+                       }\r
+               }\r
+       }\r
+       \r
+       //\r
+       private String getCenterCode(String id) {\r
+               Matcher ma = Pattern.compile("^(..)(..)").matcher(id);\r
+               if ( ma.find() ) {\r
+                       if ( ma.group(1).equals("00") ) {\r
+                               if ( ma.group(2).equals("04") ) {\r
+                                       return bsCode;\r
+                               }\r
+                               else {\r
+                                       return csCode;\r
+                               }\r
+                       }\r
+                       else {\r
+                               return trCode;\r
+                       }\r
+               }\r
+               return allCode;\r
+       }\r
+}\r
diff --git a/TinyBannavi/src/tainavi/PlugIn_TVPMSN.java b/TinyBannavi/src/tainavi/PlugIn_TVPMSN.java
new file mode 100644 (file)
index 0000000..2c25686
--- /dev/null
@@ -0,0 +1,722 @@
+\r
+package tainavi;\r
+\r
+import java.io.File;\r
+import java.util.ArrayList;\r
+import java.util.Calendar;\r
+import java.util.Date;\r
+import java.util.GregorianCalendar;\r
+import java.util.HashMap;\r
+import java.util.LinkedHashMap;\r
+import java.util.regex.Matcher;\r
+import java.util.regex.Pattern;\r
+\r
+\r
+public class PlugIn_TVPMSN extends TVProgramUtils implements TVProgram,Cloneable {\r
+\r
+       private static final String thisEncoding = "UTF-8"; \r
+       \r
+       /* 必須コード  - ここから */\r
+       \r
+       // 種族の特性\r
+       @Override\r
+       public String getTVProgramId() { return "MSNエンタメ"; }\r
+       \r
+       @Override\r
+       public ProgType getType() { return ProgType.PROG; }\r
+       @Override\r
+       public ProgSubtype getSubtype() { return ProgSubtype.TERRA; }\r
+\r
+       //\r
+       @Override\r
+       public PlugIn_TVPMSN clone() {\r
+               return (PlugIn_TVPMSN) super.clone();\r
+       }\r
+       \r
+       // 個体の特性\r
+\r
+       @Override\r
+       public int getTimeBarStart() {return 5;}\r
+       \r
+       private int getDogDays() { return ((getExpandTo8())?(8):(7)); }\r
+\r
+       private static final int bscntMax = 10;\r
+       private int bscnt = 4;\r
+       public void setBscnt(int n) { bscnt = n; }\r
+       \r
+       private String getBscntFile() { return String.format("env"+File.separator+"bscnt.%s",getTVProgramId()); }\r
+       \r
+       /*******************************************************************************\r
+        * 定数\r
+        ******************************************************************************/\r
+       \r
+       private final String MSGID = "["+getTVProgramId()+"] ";\r
+       private final String ERRID = "[ERROR]"+MSGID;\r
+       private final String DBGID = "[DEBUG]"+MSGID;\r
+       \r
+       // 新しい入れ物の臨時格納場所\r
+       private ArrayList<ProgList> newplist = null;\r
+       \r
+       private HashMap<String,String> nf = null;\r
+       \r
+       \r
+       @Override\r
+       public void loadProgram(String areaCode, boolean force) {\r
+               \r
+               // 新しい入れ物(トップ)を用意する\r
+               newplist = new ArrayList<ProgList>();\r
+               \r
+               nf = new HashMap<String, String>();\r
+               \r
+               // 地域コードごとの参照ページ数の入れ物を用意する\r
+               LinkedHashMap<String,Integer> pages = new LinkedHashMap<String, Integer>();\r
+               \r
+               // 参照する地域コードをまとめる\r
+               if ( areaCode.equals(allCode) ) {\r
+                       // 「全国」\r
+                       for ( Center cr : crlist ) {\r
+                               if ( cr.getOrder() > 0 ) {\r
+                                       // 有効局の地域コードのみ集める\r
+                                       pages.put(cr.getAreaCode(),0);\r
+                               }\r
+                       }\r
+               }\r
+               else {\r
+                       // 地域個別\r
+                       pages.put(areaCode,0);\r
+                       pages.put(bsCode,0);\r
+               }\r
+               \r
+               // トップの下に局ごとのリストを生やす\r
+               for ( String ac : pages.keySet() ) {\r
+                       for ( Center cr : crlist ) {\r
+                               if ( ac.equals(cr.getAreaCode()) ) {\r
+                                       ProgList pl = new ProgList();\r
+                                       pl.Area = cr.getAreaCode();\r
+                                       pl.SubArea = cr.getType();\r
+                                       pl.Center = cr.getCenter();\r
+                                       pl.BgColor = cr.getBgColor();\r
+                                       \r
+                                       // <TABLE>タグの列数を決め打ちで処理するので、設定上無効な局も内部的には列の1つとして必要\r
+                                       pl.enabled = (cr.getOrder()>0)?(true):(false);\r
+                                       \r
+                                       newplist.add(pl);\r
+                                       \r
+                                       int pg = Integer.valueOf(cr.getType());\r
+                                       if ( pl.enabled && pages.get(ac) < pg ) {\r
+                                               // 地域コードごとの最大参照ページ数を格納する\r
+                                               pages.put(ac,pg);\r
+                                       }\r
+                               }\r
+                       }\r
+               }\r
+\r
+               // 局の下に日付ごとのリストを生やす\r
+               GregorianCalendar cal = new GregorianCalendar();\r
+               cal.setTime(new Date());\r
+               if ( CommonUtils.isLateNight(cal) ) {\r
+                       // 4時までは当日扱いにする\r
+                       cal.add(Calendar.DATE, -1);\r
+               }\r
+               GregorianCalendar cale = (GregorianCalendar) cal.clone();\r
+               for (int i=0; i<getDogDays(); i++) {\r
+                       String date = CommonUtils.getDate(cale);\r
+                       for ( ProgList pl : newplist ) {\r
+                               ProgDateList cl = new ProgDateList();\r
+                               cl.Date = date;\r
+                               pl.pdate.add(cl);\r
+                       }\r
+                       cale.add(Calendar.DATE, 1);\r
+               }\r
+\r
+               // 参照する総ページ数を計算\r
+               int counterMax = 0;\r
+               for ( String ac : pages.keySet() ) {\r
+                       counterMax += pages.get(ac)*getDogDays();\r
+               }\r
+               \r
+               // 日付の下に番組情報ごとのリストを生やす(MSNは1ページに複数局存在する)\r
+               int counter = 1;\r
+               for ( String ac : pages.keySet() ) {\r
+                       cale = (GregorianCalendar) cal.clone();\r
+                       for ( int i=0; i<getDogDays(); i++ ) {\r
+                               String date = CommonUtils.getDateYMD(cale);\r
+                               for ( int d=1; d<=pages.get(ac) && d<=REFPAGESMAX; d++ ) {      // 最大{REFPAGESMAX}ページまでしか参照しない\r
+                                       String url;\r
+                                       if ( ac.equals(bsCode) ) {\r
+                                               url = "http://program.tv.jp.msn.com/tv.php?site=032&mode=06&category=d"+d+"&area="+ac+"&template=program&sdate="+date+"&shour=05&lhour=24";\r
+                                       }\r
+                                       else {\r
+                                               if ( d == 1 ) {\r
+                                                       url = "http://program.tv.jp.msn.com/tv.php?site=032&mode=06&category=g&area="+ac+"&template=program&sdate="+date+"&shour=05&lhour=24";\r
+                                               }\r
+                                               else {\r
+                                                       url = "http://program.tv.jp.msn.com/tv.php?site=032&mode=06&category=s&area="+ac+"&template=program&sdate="+date+"&shour=05&lhour=24";\r
+                                               }\r
+                                       }\r
+                                       _loadProgram(ac, String.valueOf(d), url, force, i, cale.get(Calendar.MONTH)+1, cale.get(Calendar.DATE), counter++, counterMax);\r
+                               }\r
+                               \r
+                               cale.add(Calendar.DATE, 1);\r
+                       }\r
+               }\r
+               \r
+               // 開始・終了日時を正しい値に計算しなおす\r
+               for (ProgList pl : newplist) {\r
+                       setAccurateDate(pl.pdate);\r
+               }\r
+               \r
+               // 解析用\r
+               {\r
+                       for ( String f : nf.keySet() ) {\r
+                               System.err.println(String.format("【デバッグ情報】未定義のフラグは[?]と表示されます。: [%s]",f));\r
+                       }\r
+               }\r
+               \r
+               // 古い番組データを置き換える\r
+               pcenter = newplist;\r
+       }\r
+       \r
+       /* ここまで */\r
+       \r
+       \r
+       \r
+       /*\r
+        * 非公開メソッド等\r
+        */\r
+       \r
+       private void _loadProgram(String areacode, String page, String url, boolean force, int wdaycol, int month, int day, int counter, int counterMax) {\r
+               // progfilesの読み出し\r
+               //\r
+               final String progCacheFile = String.format(getProgDir()+File.separator+"TVMSN_%s_%s_%04d.html", areacode, page, day);\r
+               try {\r
+                       File f = new File(progCacheFile);\r
+                       if (force == true ||\r
+                                       (f.exists() == true && isCacheOld(progCacheFile) == true) ||\r
+                                       (f.exists() == false && isCacheOld(null) == true)) {\r
+                               webToFile(url, progCacheFile, thisEncoding);\r
+                               reportProgress(String.format("%s(%s)を取得しました[%s-%02d日-%sページ]: (%d/%d) %s",getTVProgramId(),"オンライン",getArea(areacode),day,page,counter,counterMax,url));\r
+                       }\r
+                       else if (CommonUtils.isFileAvailable(f,10)) {\r
+                               reportProgress(String.format("%s(%s)を取得しました[%s-%02d日-%sページ]: (%d/%d) %s",getTVProgramId(),"キャッシュ",getArea(areacode),day,page,counter,counterMax,url));\r
+                       }\r
+                       else {\r
+                               reportProgress(String.format("%s(%s)がみつかりません[%s-%02d日-%sページ]: (%d/%d) %s",getTVProgramId(),"キャッシュ",getArea(areacode),day,page,counter,counterMax,url));\r
+                               return;\r
+                       }\r
+\r
+                       // キャッシュファイルの読み込み\r
+                       String response = CommonUtils.read4file(progCacheFile, false);\r
+                       \r
+                       // キャッシュが不整合を起こしていたら投げ捨てる\r
+                       Matcher ma = Pattern.compile(String.format("<h1 class=\"headtext\">%d月%d日",month,day)).matcher(response);\r
+                       if ( ! ma.find() ) {\r
+                               reportProgress(getTVProgramId()+"(キャッシュ)が無効です: ("+counter+"/"+counterMax+") "+progCacheFile);\r
+                               return;\r
+                       }\r
+               \r
+                       // 番組リストの追加\r
+                       getPrograms(areacode, page, wdaycol, response);\r
+               }\r
+               catch (Exception e) {\r
+                       // 例外\r
+               }\r
+       }\r
+       \r
+       //\r
+       \r
+       private void getPrograms(String areacode, String page, int wdaycol, String src) {\r
+               Matcher ma = Pattern.compile("\n(<TD ROWSPAN =(\\d+) CLASS = \".*?\">&nbsp;</TD>|<td width=\".*?\" valign=\".*?\" id=\".*?\" ROWSPAN =.*?</A></DIV></TD>)").matcher(src);\r
+               while (ma.find()) {\r
+                       \r
+                       \r
+                       //\r
+                       ProgDetailList pdl = new ProgDetailList();\r
+                       //\r
+                       if (ma.group(1).startsWith("<TD")) {\r
+                               //System.err.println(ma.group(1));\r
+                               pdl.title = "番組情報がありません";\r
+                               pdl.length = Integer.valueOf(ma.group(2));\r
+                       }\r
+                       else {\r
+                               // 1 : length\r
+                               // 2 : genre\r
+                               // 3 : link\r
+                               // 4 : title\r
+                               // 5,6 : start-hour,min\r
+                               // 7,8 : end-hour,min\r
+                               // 9,10 : detail1,detail2\r
+                               Matcher mb = Pattern.compile("<td width=\".*?\" valign=\".*?\" id=\".*?\" ROWSPAN =(\\d+) CLASS = \"(.*?)\"><DIV [^>]*?><a href=.*?onClick=\"xClickACT\\(\'\\.(.+?)\'\\);.*?<h1>(.*?)</h1><h2>.*?(\\d\\d):(\\d\\d)~(\\d\\d):(\\d\\d).*?</h2><p>(.*?)</p><p>(.*?)</p>.*?</A></DIV></TD>").matcher(ma.group(1));\r
+                               if ( ! mb.find() ) {\r
+                                       System.err.println("TVMSN: unexpected string= "+ma.group(1));\r
+                                       continue;\r
+                               }\r
+                               \r
+                               // タイトル\r
+                               {\r
+                                       pdl.title = replaceMarks(pdl, CommonUtils.unEscape(mb.group(4)));\r
+                                       pdl.splitted_title = pdl.title;\r
+                               }\r
+                                       \r
+                               \r
+                               // 番組詳細\r
+                               {\r
+                                       if (mb.group(9).length() > 0) {\r
+                                               pdl.detail = CommonUtils.unEscape(String.format("%s\n%s", mb.group(9),mb.group(10)));\r
+                                       }\r
+                                       else {\r
+                                               pdl.detail = CommonUtils.unEscape(mb.group(10));\r
+                                       }\r
+                                       \r
+                                       pdl.detail = replaceMarks(pdl, pdl.detail);\r
+                                       \r
+                                       //\r
+                                       pdl.detail = pdl.detail.replaceAll("<a[^>]*?>", "");\r
+                                       pdl.detail = pdl.detail.replaceAll("</a>", "");\r
+                                       \r
+                                       pdl.splitted_detail = pdl.detail;\r
+                               }\r
+                               \r
+                               // 検索用インデックス\r
+                               pdl.titlePop = TraceProgram.replacePop(pdl.title);\r
+                               pdl.detailPop = TraceProgram.replacePop(pdl.detail);\r
+                               pdl.SearchStrKeys = TraceProgram.splitKeys(pdl.titlePop);\r
+                               \r
+                               // 詳細へのリンク\r
+                               pdl.link = "http://program.tv.jp.msn.com"+mb.group(3);\r
+                               \r
+                               // 番組長\r
+                               pdl.length = Integer.valueOf(mb.group(1));\r
+                               \r
+                               \r
+                               // ジャンル\r
+                               if (mb.group(2).equals(allCode)) {\r
+                                       pdl.genre = ProgGenre.NOGENRE;\r
+                               }\r
+                               else if (mb.group(2).equals("anime")) {\r
+                                       pdl.genre = ProgGenre.ANIME;\r
+                               }\r
+                               else if (mb.group(2).equals("movie")) {\r
+                                       pdl.genre = ProgGenre.MOVIE;\r
+                               }\r
+                               else if (mb.group(2).equals("dorama")) {\r
+                                       pdl.genre = ProgGenre.DORAMA;\r
+                               }\r
+                               else if (mb.group(2).equals("sports")) {\r
+                                       pdl.genre = ProgGenre.SPORTS;\r
+                               }\r
+                               else {\r
+                                       pdl.genre = ProgGenre.NOGENRE;\r
+                               }\r
+                               \r
+                               // 開始・終了時刻\r
+                               GregorianCalendar c = new GregorianCalendar();\r
+                               c.setTime(new Date());\r
+                               c.set(Calendar.HOUR_OF_DAY, Integer.valueOf(mb.group(5)));\r
+                               c.set(Calendar.MINUTE, Integer.valueOf(mb.group(6)));\r
+                               pdl.start = String.format("%02d:%02d", c.get(Calendar.HOUR_OF_DAY), c.get(Calendar.MINUTE));\r
+                               \r
+                               c.set(Calendar.HOUR_OF_DAY, Integer.valueOf(mb.group(7)));\r
+                               c.set(Calendar.MINUTE, Integer.valueOf(mb.group(8)));\r
+                               pdl.end = String.format("%02d:%02d", c.get(Calendar.HOUR_OF_DAY), c.get(Calendar.MINUTE));\r
+                       }\r
+\r
+                       //\r
+                       int col = -1;\r
+                       int rowMin = 9999;\r
+                       for ( int i=0; i<newplist.size(); i++ ) {\r
+                               ProgList pl = newplist.get(i);\r
+                               if ( ! (pl.Area.equals(areacode) && pl.SubArea.equals(page)) ) {\r
+                                       continue;\r
+                               }\r
+                               if (pl.pdate.get(wdaycol).row < rowMin) {\r
+                                       col = i;\r
+                                       rowMin = pl.pdate.get(wdaycol).row;\r
+                               }\r
+                       }\r
+                       if (col < 0) {\r
+                               continue;\r
+                       }\r
+                       ProgDateList pcl = newplist.get(col).pdate.get(wdaycol);\r
+                       pcl.pdetail.add(pdl);\r
+                       pcl.row += pdl.length;\r
+               }\r
+       }\r
+       \r
+       private String replaceMarks(ProgDetailList pdl, String text) {\r
+               \r
+               HashMap<String, Integer> xf = new HashMap<String, Integer>();\r
+               \r
+               // フラグを拾う\r
+               {\r
+                       String[] marks = new String[] {\r
+                                       "shin",         // 新番組\r
+                                       "saisyu",       // 最終回\r
+                                       "sai",          // 再放送\r
+                                       "hatsu",        // 初回放送\r
+                                       "ppv",          // [PV]ペイパービュー\r
+                                       "moji",         // [字]文字多重放送\r
+                                       "nikakoku",     // [二]二か国語放送\r
+                                       "taju",         // [多]音声多重放送\r
+                                       "dubbed",       // [吹]吹き替え\r
+                                       "data"          // [デ]データ放送\r
+                                                               // [生]生放送(ないよ)\r
+                       };\r
+                       \r
+                       for (int i=0; i<marks.length; i++) {\r
+                               String mark = marks[i];\r
+                               String expr = String.format("/ico_%s\\.gif>", mark);\r
+                               Matcher mx = Pattern.compile(expr, Pattern.DOTALL).matcher(text);\r
+                               if ( mx.find() ) {\r
+                                       xf.put(mark, i);\r
+                               }\r
+                       }\r
+                       \r
+                       for ( String mark : xf.keySet() ) {\r
+                               String expr = String.format("<img src=http://img.tv.msn.co.jp/s/ico_%s\\.gif>", mark);\r
+                               text = text.replaceAll(expr,"");\r
+                               switch (xf.get(mark)) {\r
+                               case 0:\r
+                                       pdl.flag = ProgFlags.NEW;\r
+                                       break;\r
+                               case 1:\r
+                                       pdl.flag = ProgFlags.LAST;\r
+                                       break;\r
+                               case 2:\r
+                                       pdl.addOption(ProgOption.REPEAT);\r
+                                       break;\r
+                               case 3:\r
+                                       pdl.addOption(ProgOption.FIRST);\r
+                                       break;\r
+                               case 4:\r
+                                       pdl.addOption(ProgOption.PV);\r
+                                       break;\r
+                               case 5:\r
+                                       pdl.addOption(ProgOption.SUBTITLE);\r
+                                       break;\r
+                               case 6:\r
+                                       pdl.addOption(ProgOption.BILINGUAL);\r
+                                       break;\r
+                               case 7:\r
+                                       pdl.addOption(ProgOption.MULTIVOICE);\r
+                                       break;\r
+                               case 8:\r
+                                       pdl.addOption(ProgOption.STANDIN);\r
+                                       break;\r
+                               case 9:\r
+                                       pdl.addOption(ProgOption.DATA);\r
+                                       break;\r
+                               }\r
+                       }\r
+               }\r
+\r
+               // 置換する\r
+               {\r
+                       HashMap<String,String> marks = new HashMap<String,String>();\r
+                       // 置換するコード\r
+                       marks.put("director", "[監]");\r
+                       marks.put("guest", "[ゲ]");\r
+                       marks.put("jikkyo", "[実]");\r
+                       marks.put("kaisetsusya", "[解]");\r
+                       marks.put("katari", "[語]");\r
+                       marks.put("koe", "[声]");\r
+                       marks.put("org", "[原]");\r
+                       marks.put("plot", "[脚]");\r
+                       marks.put("shikai", "[司]");\r
+                       marks.put("syutsuen", "[出]");\r
+                       marks.put("n", "[N]");\r
+                       marks.put("tenki", "[天]");\r
+                       marks.put("zen", "(前編)");\r
+                       marks.put("kou", "(後編)");\r
+                       // 無視するコード\r
+                       marks.put("s", "");\r
+                       marks.put("3d", "");\r
+                       marks.put("eiga", "");\r
+                       marks.put("hand", "");\r
+                       \r
+                       for ( String mark : marks.keySet() ) {\r
+                               String expr = String.format("<img src=http://img.tv.msn.co.jp/s/ico_%s\\.gif>", mark);\r
+                               text = text.replaceAll(expr,marks.get(mark));\r
+                       }\r
+               }\r
+               \r
+               // 未解析のマーク\r
+               Matcher mx = Pattern.compile("/ico_(.*?)\\.gif>", Pattern.DOTALL).matcher(text);\r
+               while ( mx.find() ) {\r
+                       nf.put(mx.group(1), null);\r
+               }\r
+               for ( String mark : nf.keySet() ) {\r
+                       String expr = String.format("<img src=http://img.tv.msn.co.jp/s/ico_%s\\.gif>", mark);\r
+                       text = text.replaceAll(expr,"[?]");\r
+               }\r
+\r
+               return text;\r
+       }\r
+       \r
+       /*\r
+        * ★★★★★★★★★★★★★★★★★★★★★★★★★★★★★★★\r
+        * ★★★★★ 放送地域を取得する(TVAreaから降格)-ここから ★★★★★\r
+        * ★★★★★★★★★★★★★★★★★★★★★★★★★★★★★★★\r
+        */\r
+\r
+       /*\r
+        * 公開メソッド\r
+        */\r
+       \r
+       //\r
+       @Override\r
+       public String getDefaultArea() {return "東京";}\r
+       \r
+       //\r
+       @Override\r
+       public void loadAreaCode(){\r
+               \r
+               // 設定ファイルが存在していればファイルから\r
+               File f = new File(getAreaSelectedFile());\r
+               if ( f.exists() ) {\r
+                       @SuppressWarnings("unchecked")\r
+                       ArrayList<AreaCode> tmp = (ArrayList<AreaCode>) CommonUtils.readXML(getAreaSelectedFile());\r
+                       if ( tmp != null ) {\r
+                               System.out.println("地域リストを読み込みました: "+getAreaSelectedFile());\r
+                               aclist = tmp;\r
+                   return;\r
+                       }\r
+\r
+                       System.out.println("地域リストの読み込みに失敗しました: "+getAreaSelectedFile());\r
+               }\r
+               \r
+               // 地域一覧の作成\r
+               ArrayList<AreaCode> newaclist = new ArrayList<AreaCode>();\r
+\r
+               // 存在していなければWeb上から\r
+               String response = "";\r
+               {\r
+                       String uri = "http://program.tv.jp.msn.com/tv.php?site=032&mode=06&template=program&category=g&shour=05&lhour=24";\r
+                       response = webToBuffer(uri, thisEncoding, true);\r
+                       if ( response == null ) {\r
+                               System.out.println("地域情報の取得に失敗しました: "+uri);\r
+                               return;\r
+                       }\r
+               }\r
+               \r
+               Matcher ma = Pattern.compile("<select name=\"area\" size=\"1\">(.+?)</select>").matcher(response);\r
+               if (ma.find()) {\r
+                       Matcher mb = Pattern.compile("<option value=\"([^\"]+?)\" ?(selected=\"selected\")?>(.+?)</option>").matcher(ma.group(1));\r
+                       while (mb.find()) {\r
+                               AreaCode ac = new AreaCode();\r
+                               ac.setArea(mb.group(3));\r
+                               ac.setCode(mb.group(1));\r
+                               newaclist.add(ac);\r
+                       }\r
+               }\r
+               \r
+               if ( newaclist.size() == 0 ) {\r
+                       System.err.println(ERRID+"地域一覧の取得結果が0件だったため情報を更新しません");\r
+                       return;\r
+               }\r
+               \r
+               {\r
+                       {\r
+                               AreaCode ac = new AreaCode();\r
+                               ac.setArea("全国");\r
+                               ac.setCode(allCode);\r
+                               newaclist.add(0,ac);\r
+                       }\r
+                       {\r
+                               AreaCode ac = new AreaCode();\r
+                               ac.setArea("BS");\r
+                               ac.setCode(bsCode);\r
+                               newaclist.add(ac);\r
+                       }\r
+               }\r
+               \r
+               aclist = newaclist;\r
+               saveAreaCode();\r
+       }\r
+\r
+       /*\r
+        * ★★★★★★★★★★★★★★★★★★★★★★★★★★★★★★★\r
+        * ★★★★★ 放送地域を取得する(TVAreaから降格)-ここまで ★★★★★\r
+        * ★★★★★★★★★★★★★★★★★★★★★★★★★★★★★★★\r
+        */\r
+       \r
+       \r
+       \r
+       /*\r
+        * ★★★★★★★★★★★★★★★★★★★★★★★★★★★★★★★\r
+        * ★★★★★ 放送局を選択する(TVCenterから降格)-ここから ★★★★★\r
+        * ★★★★★★★★★★★★★★★★★★★★★★★★★★★★★★★\r
+        */\r
+       \r
+       /*\r
+        * 公開メソッド\r
+        */\r
+\r
+       // 設定ファイルがなければWebから取得\r
+       @Override\r
+       public void loadCenter(String code, boolean force) {\r
+               \r
+               if ( code == null ) {\r
+                       System.out.println(ERRID+"地域コードがnullです.");\r
+                       return;\r
+               }\r
+               \r
+               // BSのページ数の初期化(事前に判明していない場合は2)\r
+               int bscntTmp = CommonUtils.loadCnt(getBscntFile());\r
+               bscntTmp = bscnt = (bscntTmp > 0)?(bscntTmp):(3);\r
+               \r
+               //\r
+               String centerListFile = getCenterListFile(getTVProgramId(), code);\r
+               \r
+               if (force) {\r
+                       File f = new File(centerListFile);\r
+                       f.delete();\r
+               }\r
+               \r
+               File f = new File(centerListFile);\r
+               if (f.exists() == true) {\r
+                       @SuppressWarnings("unchecked")\r
+                       ArrayList<Center> tmp = (ArrayList<Center>) CommonUtils.readXML(centerListFile);\r
+                       if ( tmp != null ) {\r
+                   \r
+                               crlist = tmp;\r
+                               \r
+                       // 放送局名変換\r
+                       attachChFilters();\r
+                       \r
+                               System.out.println("放送局リストを読み込みました: "+centerListFile);\r
+                   return;\r
+               }\r
+\r
+                       System.out.println("放送局リストの読み込みに失敗しました: "+centerListFile);\r
+               }\r
+               \r
+               // 放送局をつくるよ\r
+               ArrayList<Center> newcrlist = new ArrayList<Center>();\r
+               \r
+               // 地上派・UHFは地域別に扱う\r
+\r
+               int cntMax = ((allCode.equals(code))?(aclist.size()-2):(1))*2 + bscnt;\r
+               int cnt = 1;\r
+               for (AreaCode ac : aclist) {\r
+                       if (ac.getCode().equals(bsCode)) {\r
+                               continue;\r
+                       }\r
+                       else if (code.equals(allCode) && ac.getCode().equals(allCode)) {\r
+                               continue;\r
+                       }\r
+                       else if ( ! code.equals(allCode) && ! ac.getCode().equals(code)) {\r
+                               continue;\r
+                       }\r
+                               \r
+                       String url;\r
+                       \r
+                       // 地上波\r
+                       url = "http://program.tv.jp.msn.com/tv.php?site=032&mode=06&template=program&category=g&area="+ac.getCode()+"&shour=05&lhour=24";\r
+                       if ( _loadCenter(newcrlist, ac.getCode(), "1", url) ) {\r
+                               reportProgress(String.format("放送局情報を取得しました[%s%d]: (%d/%d) %s","地上波",cnt,cnt,cntMax,url));\r
+                       }\r
+                       cnt++;\r
+                       \r
+                       // UHF・BSアナログ\r
+                       url = "http://program.tv.jp.msn.com/tv.php?site=032&mode=06&template=program&category=s&area="+ac.getCode()+"&shour=05&lhour=24";\r
+                       if ( _loadCenter(newcrlist, ac.getCode(), "2", url) ) {\r
+                               reportProgress(String.format("放送局情報を取得しました[%s%d]: (%d/%d) %s","地上波",cnt,cnt,cntMax,url));\r
+                       }\r
+                       cnt++;\r
+               }\r
+               \r
+               // BS1・BS2は共通にする(bscntは_loadCenter()中で増加する可能性あり)\r
+               \r
+               for ( int d=1; d<=bscnt; d++ )\r
+               {\r
+                       String url = "http://program.tv.jp.msn.com/tv.php?site=032&mode=06&template=program&category=d"+d+"&shour=05&lhour=24";\r
+                       if ( _loadCenter(newcrlist, bsCode, String.valueOf(d), url) ) {\r
+                               reportProgress(String.format("放送局情報を取得しました[%s%d]: (%d/%d) %s","BS",d,cnt,cntMax,url));\r
+                       }\r
+                       cnt++;\r
+               }\r
+               \r
+               // BSのページ数を記録する\r
+               \r
+               if ( bscntTmp < bscnt ) {\r
+                       reportProgress(String.format("BSのページ数が変更されました: %d→%d (最大%dまで)",bscntTmp,bscnt,bscntMax));\r
+                       CommonUtils.saveCnt(bscnt,getBscntFile());\r
+               }\r
+               \r
+               if ( newcrlist.size() == 0 ) {\r
+                       System.err.println(ERRID+"放送局情報の取得結果が0件だったため情報を更新しません");\r
+                       return;\r
+               }\r
+               \r
+               crlist = newcrlist;\r
+               attachChFilters();      // 放送局名変換\r
+               saveCenter();\r
+       }\r
+               \r
+       private boolean _loadCenter(ArrayList<Center> newcrlist, String code, String page, String uri) {\r
+               String response = null;\r
+               {\r
+                       response = webToBuffer(uri, thisEncoding, true);\r
+                       if ( response == null ) {\r
+                               System.out.println("放送局情報の取得に失敗しました: "+uri);\r
+                               return false;\r
+                       }\r
+               }\r
+               \r
+               // BSのページ数を計算する\r
+               \r
+               for ( int i=bscnt+1; i<=bscntMax; i++ ) {\r
+                       if ( ! response.matches(".*&category=d"+i+"\".*") ) {\r
+                               if ( bscnt < i-1 ) {\r
+                                       bscnt = i-1;\r
+                               }\r
+                               break;\r
+                       }\r
+               }\r
+               \r
+               // 局名リストに追加する\r
+               \r
+               Matcher ma = Pattern.compile("<!-- ↓ Station -->([\\s\\S]+?)<!-- ↑ Station -->").matcher(response);\r
+               if (ma.find()) {\r
+                       Matcher mb = Pattern.compile("<TH width=\".*?\">\\s*(.+?)\\s*</th>").matcher(ma.group(1));\r
+                       while (mb.find()) {\r
+                               String centerName;\r
+                               String link;\r
+                               Matcher mc = Pattern.compile("<a href =\"(.*?)\" id=\'ch01_h\' class=\"th_a\" target=\"_blank\"\\s*>\\s*(.+?)(<BR>|</a>)").matcher(mb.group(1));\r
+                               if (mc.find()) {\r
+                                       centerName = CommonUtils.unEscape(mc.group(2));\r
+                                       link = mb.group(1);\r
+                               }\r
+                               else {\r
+                                       centerName = CommonUtils.unEscape(mb.group(1).replaceAll("<BR>", ""));\r
+                                       link = "";\r
+                               }\r
+                               \r
+                               // NHK総合・NHK教育\r
+                               centerName = centerName.replaceFirst("^NHK$", "NHK総合");\r
+                               centerName = centerName.replaceFirst("^NHK Eテレ", "NHK Eテレ");\r
+                               if ( ! code.startsWith(bsCode) && page.equals("1")) {\r
+                                       if (centerName.startsWith("NHK")) {\r
+                                               centerName = centerName+"・"+getArea(code);\r
+                                       }\r
+                               }\r
+                               \r
+                               Center cr = new Center();\r
+                               cr.setAreaCode(code);\r
+                               cr.setCenterOrig(centerName);\r
+                               cr.setLink(link);\r
+                               cr.setType(page);\r
+                               cr.setEnabled(true);\r
+                               newcrlist.add(cr);\r
+                       }\r
+               }\r
+               return true;\r
+       }\r
+       \r
+       /*\r
+        * ★★★★★★★★★★★★★★★★★★★★★★★★★★★★★★★\r
+        * ★★★★★ 放送局を選択する(TVCenterから降格)-ここまで ★★★★★\r
+        * ★★★★★★★★★★★★★★★★★★★★★★★★★★★★★★★\r
+        */\r
+}\r
diff --git a/TinyBannavi/src/tainavi/PlugIn_TVPTVGuide.java b/TinyBannavi/src/tainavi/PlugIn_TVPTVGuide.java
new file mode 100644 (file)
index 0000000..dd1f878
--- /dev/null
@@ -0,0 +1,672 @@
+package tainavi;\r
+\r
+import java.io.File;\r
+import java.util.ArrayList;\r
+import java.util.Calendar;\r
+import java.util.Date;\r
+import java.util.GregorianCalendar;\r
+import java.util.HashMap;\r
+import java.util.LinkedHashMap;\r
+import java.util.regex.Matcher;\r
+import java.util.regex.Pattern;\r
+\r
+\r
+public class PlugIn_TVPTVGuide extends TVProgramUtils implements TVProgram,Cloneable {\r
+\r
+       public PlugIn_TVPTVGuide clone() {\r
+               return (PlugIn_TVPTVGuide) super.clone();\r
+       }\r
+       \r
+       private static final String thisEncoding = "UTF-8"; \r
+       \r
+       \r
+       /*******************************************************************************\r
+        * 種族の特性\r
+        ******************************************************************************/\r
+       \r
+       @Override\r
+       public String getTVProgramId() { return "インターネットTVガイド"; }\r
+       \r
+       @Override\r
+       public ProgType getType() { return ProgType.PROG; }\r
+       @Override\r
+       public ProgSubtype getSubtype() { return ProgSubtype.TERRA; }\r
+\r
+       \r
+       /*******************************************************************************\r
+        * 個体の特性\r
+        ******************************************************************************/\r
+       \r
+       @Override\r
+       public int getTimeBarStart() {return 5;}\r
+\r
+       private int getDogDays() { return ((getExpandTo8())?(8):(7)); }\r
+       \r
+       \r
+       /*******************************************************************************\r
+        * 定数\r
+        ******************************************************************************/\r
+       \r
+       private final String MSGID = "["+getTVProgramId()+"] ";\r
+       private final String ERRID = "[ERROR]"+MSGID;\r
+       private final String DBGID = "[DEBUG]"+MSGID;\r
+       \r
+       /*******************************************************************************\r
+        * 部品\r
+        ******************************************************************************/\r
+\r
+       // 新しい入れ物の臨時格納場所\r
+       protected final ArrayList<ProgList> newplist = new ArrayList<ProgList>();\r
+       \r
+       // 未定義のフラグの回収場所\r
+       private final HashMap<String,String> nf = new HashMap<String, String>();\r
+       \r
+       \r
+       /*******************************************************************************\r
+        * コンストラクタ\r
+        ******************************************************************************/\r
+       \r
+       \r
+       /*******************************************************************************\r
+        * 番組情報を取得する\r
+        ******************************************************************************/\r
+       @Override\r
+       public void loadProgram(String areaCode, boolean force) {\r
+               \r
+               // 入れ物を空にする\r
+               newplist.clear();\r
+               nf.clear(); \r
+               \r
+               // 地域コードごとの参照ページ数の入れ物を用意する\r
+               LinkedHashMap<String,Integer> pages = new LinkedHashMap<String, Integer>();\r
+               \r
+               // 参照する地域コードをまとめる\r
+               if ( areaCode.equals(allCode) ) {\r
+                       // 「全国」\r
+                       for ( Center cr : crlist ) {\r
+                               if ( cr.getOrder() > 0 ) {\r
+                                       // 有効局の地域コードのみ集める\r
+                                       pages.put(cr.getAreaCode(),0);\r
+                               }\r
+                       }\r
+               }\r
+               else {\r
+                       // 地域個別\r
+                       pages.put(areaCode,0);\r
+                       pages.put(bsCode,0);\r
+               }\r
+               \r
+               // トップの下に局ごとのリストを生やす\r
+               for ( String ac : pages.keySet() ) {\r
+                       for ( Center cr : crlist ) {\r
+                               if ( ac.equals(cr.getAreaCode()) ) {\r
+                                       ProgList pl = new ProgList();\r
+                                       pl.Area = cr.getAreaCode();\r
+                                       pl.SubArea = cr.getType();\r
+                                       pl.Center = cr.getCenter();\r
+                                       pl.BgColor = cr.getBgColor();\r
+                                       \r
+                                       // <TABLE>タグの列数を決め打ちで処理するので、設定上無効な局も内部的には列の1つとして必要\r
+                                       pl.enabled = (cr.getOrder()>0)?(true):(false);\r
+                                       \r
+                                       newplist.add(pl);\r
+                                       \r
+                                       int pg = Integer.valueOf(cr.getType());\r
+                                       if ( pl.enabled && pages.get(ac) < pg ) {\r
+                                               // 地域コードごとの最大参照ページ数を格納する\r
+                                               pages.put(ac,pg);\r
+                                       }\r
+                               }\r
+                       }\r
+               }\r
+               \r
+               // 局の下に日付ごとのリストを生やす\r
+               GregorianCalendar cal = new GregorianCalendar();\r
+               cal.setTime(new Date());\r
+               if ( CommonUtils.isLateNight(cal) ) {\r
+                       // 4時までは当日扱いにする\r
+                       cal.add(Calendar.DATE, -1);\r
+               }\r
+               GregorianCalendar cale = (GregorianCalendar) cal.clone();\r
+               for (int i=0; i<getDogDays(); i++) {\r
+                       String date = CommonUtils.getDate(cale);\r
+                       for ( ProgList pl : newplist ) {\r
+                               ProgDateList cl = new ProgDateList();\r
+                               cl.Date = date;\r
+                               pl.pdate.add(cl);\r
+                       }\r
+                       cale.add(Calendar.DATE, 1);\r
+               }\r
+               \r
+               // 参照する総ページ数を計算\r
+               int counterMax = 0;\r
+               for ( String ac : pages.keySet() ) {\r
+                       counterMax += pages.get(ac)*getDogDays();\r
+               }\r
+               \r
+               // 日付の下に番組情報ごとのリストを生やす(TVガイドシンプル版は1ページに複数局存在する)\r
+               int counter = 1;\r
+               for ( String ac : pages.keySet() ) {\r
+                       cale = (GregorianCalendar) cal.clone();\r
+                       for ( int i=0; i<getDogDays(); i++ ) {\r
+                               String date = CommonUtils.getDateYMD(cale);\r
+                               for ( int d=1; d<=pages.get(ac) && d<=REFPAGESMAX; d++ ) {      // 最大{REFPAGESMAX}ページまでしか参照しない\r
+                                       String url;\r
+                                       if ( ac.equals(bsCode) ) {\r
+                                               url = "http://www.tvguide.or.jp/TF1101LS.php?mediaId=2&date="+date+"&time=05&dispflg=0&page="+String.valueOf(d)+"&time=24";\r
+                                       }\r
+                                       else {\r
+                                               url = "http://www.tvguide.or.jp/TF1101LS.php?regionId="+ac+"&mediaId=1&date="+date+"&time=05&dispflg=0&page="+String.valueOf(d)+"&time=24";\r
+                                       }\r
+                                       _loadProgram(ac, d, pages.get(ac), url, force, i, cale.get(Calendar.MONTH)+1, cale.get(Calendar.DATE), counter++, counterMax);\r
+                               }\r
+                               \r
+                               cale.add(Calendar.DATE, 1);\r
+                       }\r
+               }\r
+               \r
+               // 当日分のプログラムリストのみrowを再計算(現在時刻以前の情報がない関係で)\r
+               for ( ProgList pl : newplist ) {\r
+                       if ( pl.enabled ) {\r
+                               if ( pl.pdate.size() > 0 ) {\r
+                                       ProgDateList pcl = pl.pdate.get(0);\r
+                                       pcl.row = 0;\r
+                                       for ( ProgDetailList pdl : pcl.pdetail ) {\r
+                                               pcl.row += pdl.length;\r
+                                       }\r
+                               }\r
+                       }\r
+               }\r
+               \r
+               // 開始・終了日時を正しい値に計算しなおす\r
+               for ( ProgList pl : newplist ) {\r
+                       setAccurateDate(pl.pdate);\r
+               }\r
+               \r
+               // 古いデータから補完できないかな?\r
+               CompensatesPrograms(newplist);\r
+               \r
+               // 古い番組データを置き換える\r
+               pcenter = newplist;\r
+       }\r
+       \r
+       /* ここまで */\r
+       \r
+       \r
+       \r
+       /*\r
+        * 非公開メソッド\r
+        */\r
+\r
+       private void _loadProgram(String areacode, int page, int pmax, String url, boolean force, int wdaycol, int month, int day, int counter, int counterMax) {\r
+               // progfilesの読み出し\r
+               final String progCacheFile = String.format(getProgDir()+File.separator+"TVGuide_%s_%d_%s.html", areacode, day, page);\r
+               try {\r
+                       File f = new File(progCacheFile);\r
+                       if (force == true ||\r
+                                       (f.exists() == true && isCacheOld(progCacheFile) == true) ||\r
+                                       (f.exists() == false && isCacheOld(null) == true)) {\r
+                               webToFile(url, progCacheFile, thisEncoding);\r
+                               reportProgress(String.format("%s (オンライン)を取得しました: (%d/%d) %d日[%d/%d] %s",getTVProgramId(),counter,counterMax,day,page,pmax,url));\r
+                       }\r
+                       else if (CommonUtils.isFileAvailable(f,10)) {\r
+                               reportProgress(String.format("%s (キャッシュ)を取得しました: (%d/%d) %d日[%d/%d] %s",getTVProgramId(),counter,counterMax,day,page,pmax,progCacheFile));\r
+                       }\r
+                       else {\r
+                               reportProgress(String.format("%s (キャッシュ)がみつかりません: (%d/%d) %d日[%d/%d] %s",getTVProgramId(),counter,counterMax,day,page,pmax,progCacheFile));\r
+                               return;\r
+                       }\r
+\r
+                       // キャッシュファイルの読み込み\r
+                       String response = CommonUtils.read4file(progCacheFile, true);\r
+                       \r
+                       // キャッシュが不整合を起こしていたら投げ捨てる\r
+                       Matcher ma = Pattern.compile(String.format("class=\"txt01 display_now\">%d月%d日",month,day)).matcher(response);\r
+                       if ( ! ma.find() ) {\r
+                               reportProgress(String.format("%s (キャッシュ)が無効です: (%d/%d) %d日[%d/%d] %s",getTVProgramId(),counter,counterMax,day,page,pmax,progCacheFile));\r
+                               return;\r
+                       }\r
+                       \r
+                       // 番組リストの追加\r
+                       getPrograms(areacode, String.valueOf(page), wdaycol, response);\r
+               }\r
+               catch (Exception e) {\r
+                       // 例外\r
+                       System.out.println("Exception: _loadProgram()");\r
+                       e.printStackTrace();\r
+               }\r
+       }\r
+       \r
+       //\r
+       private void getPrograms(String areacode, String page, int wdaycol, String src) {\r
+               \r
+               HashMap<String,ProgGenre> genres = new HashMap<String, TVProgram.ProgGenre>();\r
+               genres.put("ccccff", ProgGenre.SPORTS);\r
+               genres.put("ccffcc", ProgGenre.MUSIC);\r
+               genres.put("ccffff", ProgGenre.ANIME);\r
+               genres.put("ffcccc", ProgGenre.MOVIE);\r
+               genres.put("ffffcc", ProgGenre.DORAMA);\r
+               genres.put("ffcc99", ProgGenre.VARIETY);\r
+               genres.put("", ProgGenre.NOGENRE);\r
+               \r
+               HashMap<String,Integer> marks = new HashMap<String,Integer>();\r
+               marks.put("26", 0);     // 新番組\r
+               marks.put("30", 1);     // 最終回\r
+               marks.put("29", 2);     // 再放送\r
+                                                       // 初回放送(みつからないよ)\r
+               marks.put("53", 4);     // [PV]ペイパービュー\r
+               marks.put("54", 4);     // [PV]ペイパービュー\r
+               marks.put("17", 5);     // [字]文字多重放送\r
+               marks.put("12", 6);     // [二]二か国語放送\r
+               marks.put("11", 7);     // [多]音声多重放送\r
+               marks.put("52", 8);     // [吹]吹き替え\r
+               marks.put("45", 9);     // [デ]データ放送\r
+               marks.put("49", 10);    // [生]生放送\r
+               marks.put("20", 11);    // - ノースクランブル\r
+               \r
+               // 番組枠の開始位置を決定する\r
+               Matcher ma = Pattern.compile("<th rowspan=\"60\" class=\"txt02\"[\\s\\S]*?>\\s*(\\d+)\\s*</th>").matcher(src);\r
+               if ( ! ma.find() ) {\r
+                       return;\r
+               }\r
+               int curHour = Integer.valueOf(ma.group(1));\r
+               if ( CommonUtils.isLateNight(curHour) ) {\r
+                       curHour += 24;\r
+               }\r
+               \r
+               ma = Pattern.compile("<td\\s+rowspan=\"(\\d+?)\"\\s+valign=\"top\"\\s+(class=\"frame\"\\s+)?bgcolor=\"#(.*?)\">([\\s\\S]+?)</table>\\s*?</td>").matcher(src);\r
+               while (ma.find()) {\r
+                       //\r
+                       ProgDetailList pdl = new ProgDetailList();\r
+\r
+                       int hh = 0;\r
+                       int mm = 0;\r
+                       \r
+                       int plen = pdl.length = Integer.valueOf(ma.group(1));\r
+                       \r
+                       String bgcolor = ma.group(3);\r
+                       \r
+                       if ( ma.group(4).indexOf("番組データがありません。") != -1 ) {\r
+                               // 詳細がないよ\r
+                               pdl.title = "番組情報がありません";\r
+                       }\r
+                       else {\r
+                               Matcher mb = Pattern.compile("<div class=\"program_text_1\">\\s*(\\d+):(\\d+)\\s*</div>").matcher(ma.group(4));\r
+                               if ( ! mb.find()) {\r
+                                       continue;\r
+                               }\r
+                               \r
+                               // 開始・終了時刻\r
+                               {\r
+                                       hh = Integer.valueOf(mb.group(1));\r
+                                       mm  = Integer.valueOf(mb.group(2));\r
+                                       pdl.start = CommonUtils.getTime(hh, mm);\r
+                                       \r
+                                       int nn = mm + pdl.length;\r
+                                       mm = nn % 60;\r
+                                       hh = (hh + (nn-mm)/60) % 24;\r
+                                       pdl.end = CommonUtils.getTime(hh, mm);\r
+                               }\r
+                               \r
+                               mb = Pattern.compile("javascript:popup\\('(.+?)'\\);return false;\">([\\s\\S]*?)</A>").matcher(ma.group(4));\r
+                               if ( ! mb.find()) {\r
+                                       continue;\r
+                               }\r
+                               \r
+                               // リンク\r
+                               pdl.link = "http://www.tvguide.or.jp/"+mb.group(1);\r
+                               \r
+                               // タイトル\r
+                               pdl.title = mb.group(2).replaceAll("<br>", "");\r
+                               \r
+                               // ジャンル\r
+                               {\r
+                                       if (genres.containsKey(bgcolor)) {\r
+                                               pdl.genre = genres.get(bgcolor);\r
+                                       }\r
+                                       else {\r
+                                               System.err.println("unexpected genre code: "+bgcolor);\r
+                                               pdl.genre = ProgGenre.NOGENRE;\r
+                                       }\r
+                               }\r
+                               \r
+                               mb = Pattern.compile("<div class=\"program_text_3\">(.*?)</div>").matcher(ma.group(4));\r
+                               if ( ! mb.find()) {\r
+                                       continue;\r
+                               }\r
+                               \r
+                               // 詳細\r
+                               pdl.detail = mb.group(1).replaceAll("<br>", "\n").trim();\r
+                               \r
+                               // 各種マーク\r
+                               mb = Pattern.compile("/image/icon/(\\d+?)\\.gif").matcher(ma.group(4));\r
+                               while (mb.find()) {\r
+                                       if (marks.containsKey(mb.group(1))) {\r
+                                               switch (marks.get(mb.group(1))) {\r
+                                               case 0:\r
+                                                       pdl.flag = ProgFlags.NEW;\r
+                                                       break;\r
+                                               case 1:\r
+                                                       pdl.flag = ProgFlags.LAST;\r
+                                                       break;\r
+                                               case 2:\r
+                                                       pdl.addOption(ProgOption.REPEAT);\r
+                                                       break;\r
+                                               case 3:\r
+                                                       pdl.addOption(ProgOption.FIRST);\r
+                                                       break;\r
+                                               case 4:\r
+                                                       pdl.addOption(ProgOption.PV);\r
+                                                       break;\r
+                                               case 5:\r
+                                                       pdl.addOption(ProgOption.SUBTITLE);\r
+                                                       break;\r
+                                               case 6:\r
+                                                       pdl.addOption(ProgOption.BILINGUAL);\r
+                                                       break;\r
+                                               case 7:\r
+                                                       pdl.addOption(ProgOption.MULTIVOICE);\r
+                                                       break;\r
+                                               case 8:\r
+                                                       pdl.addOption(ProgOption.STANDIN);\r
+                                                       break;\r
+                                               case 9:\r
+                                                       pdl.addOption(ProgOption.DATA);\r
+                                                       break;\r
+                                               case 10:\r
+                                                       pdl.addOption(ProgOption.LIVE);\r
+                                                       break;\r
+                                               case 11:\r
+                                                       pdl.noscrumble = ProgScrumble.NOSCRUMBLE;\r
+                                                       break;\r
+                                               }\r
+                                       }\r
+                               }\r
+                                       \r
+                               // タイトルから各種フラグを分離する\r
+                               doSplitFlags(pdl, nf);\r
+                               \r
+                               // サブタイトル分離\r
+                               doSplitSubtitle(pdl);\r
+                       }\r
+\r
+                       // 挿入位置\r
+                       int col = -1;\r
+                       int rowMin = 9999;\r
+                       for ( int i=0; i<newplist.size(); i++ ) {\r
+                               ProgList pl = newplist.get(i);\r
+                               if ( ! (pl.Area.equals(areacode) && pl.SubArea.equals(page)) ) {\r
+                                       continue;\r
+                               }\r
+                               if (pl.pdate.get(wdaycol).row < rowMin) {\r
+                                       col = i;\r
+                                       rowMin = pl.pdate.get(wdaycol).row;\r
+                               }\r
+                       }\r
+                       if (col < 0) {\r
+                               continue;\r
+                       }\r
+\r
+                       // 挿入…\r
+                       ProgDateList pcl = newplist.get(col).pdate.get(wdaycol);\r
+                       \r
+                       // 1つめの番組は状態によって修正が必要\r
+                       if ( pcl.pdetail.size() == 0 && curHour != getTimeBarStart() ) {\r
+                               if ( pdl.start.length() == 0 ) {\r
+                                       // 番組情報がない、は伸ばすだけでよい\r
+                                       pdl.length += (curHour-getTimeBarStart())*60;\r
+                               }\r
+                               else {\r
+                                       // なにかの番組情報\r
+                                       if ( curHour >= 24 ) {\r
+                                               if ( CommonUtils.isLateNight(hh) ) {\r
+                                                       hh += 24;\r
+                                               }\r
+                                       }\r
+                                       int ttop = getTimeBarStart() * 60;\r
+                                       int tcur = curHour*60;\r
+                                       int tstart = hh*60+mm;\r
+                                       if ( tstart <= ttop ) {\r
+                                               // 日をまたぐ番組()\r
+                                               pdl.length += (tcur-ttop);      // 先頭時刻~現在時刻までのスペース分上に伸ばす\r
+                                       }\r
+                                       else {\r
+                                               // 直前までの情報がない番組\r
+                                               {\r
+                                                       // ダミー挿入\r
+                                                       ProgDetailList NullPdl = new ProgDetailList();\r
+                                                       NullPdl.title = "番組情報がありません";\r
+                                                       NullPdl.length = (tstart-ttop); // 先頭時刻~開始時刻までのスペースを確保\r
+                                                       pcl.pdetail.add(NullPdl);\r
+                                               }\r
+                                               pdl.length += (tcur-tstart);    // 開始時刻~現在時刻までのスペース分上に伸ばす\r
+                                       }\r
+                               }\r
+                       }\r
+                       \r
+                       // 挿入\r
+                       pcl.pdetail.add(pdl);\r
+                       pcl.row += plen;\r
+               }\r
+       }\r
+       \r
+\r
+\r
+       /*******************************************************************************\r
+        * 地域情報を取得する\r
+        ******************************************************************************/\r
+       \r
+       //\r
+       @Override\r
+       public String getDefaultArea() {return "東京";}\r
+       \r
+       //\r
+       public void loadAreaCode(){\r
+               \r
+               // 設定ファイルが存在していればファイルから\r
+               File f = new File(getAreaSelectedFile());\r
+               if (f.exists() == true) {\r
+                       @SuppressWarnings("unchecked")\r
+                       ArrayList<AreaCode> tmp = (ArrayList<AreaCode>) CommonUtils.readXML(getAreaSelectedFile());\r
+                       if ( tmp != null ) {\r
+                               \r
+                               aclist = tmp;\r
+                               \r
+                               // 後方互換\r
+                               for ( AreaCode ac : aclist ) {\r
+                                       String[] aid = ac.getCode().split(",",2);\r
+                                       ac.setCode(aid[0]);\r
+                               }\r
+\r
+                               return;\r
+                       }\r
+                       else {\r
+                               System.err.println(ERRID+"地域リストの読み込みに失敗しました: "+getAreaSelectedFile());\r
+                       }\r
+               }\r
+               \r
+               // 地域一覧の作成\r
+               ArrayList<AreaCode> newaclist = new ArrayList<AreaCode>();\r
+\r
+               // 存在していなければWeb上から\r
+               String uri = "http://www.tvguide.or.jp/TF1101LS.php";\r
+               String response = webToBuffer(uri,thisEncoding,true);\r
+               if ( response == null ) {\r
+                       System.out.println(ERRID+"地域情報の取得に失敗しました: "+uri);\r
+                       return;\r
+               }\r
+               \r
+               Matcher ma = Pattern.compile("<select name=\"area\"([\\s\\S]+?)</select>").matcher(response);\r
+               if ( ma.find() ) {\r
+                       Matcher mb = Pattern.compile("&regionId=(.+?)\" (selected)?>(.+?)</option>").matcher(ma.group(1));\r
+                       while (mb.find()) {\r
+                               AreaCode ac = new AreaCode();\r
+                               ac.setArea(mb.group(3));\r
+                               ac.setCode(mb.group(1));\r
+                               newaclist.add(ac);\r
+                       }\r
+               }\r
+               \r
+               if ( newaclist.size() == 0 ) {\r
+                       System.err.println(ERRID+"地域一覧の取得結果が0件だったため情報を更新しません");\r
+                       return;\r
+               }\r
+               \r
+               {\r
+                       {\r
+                               AreaCode ac = new AreaCode();\r
+                               ac.setArea("全国");\r
+                               ac.setCode(allCode);\r
+                               newaclist.add(0,ac);\r
+                       }\r
+                       {\r
+                               AreaCode ac = new AreaCode();\r
+                               ac.setArea("BS");\r
+                               ac.setCode(bsCode);\r
+                               newaclist.add(ac);\r
+                       }\r
+               }\r
+               \r
+               aclist = newaclist;\r
+               saveAreaCode();\r
+       }\r
+\r
+\r
+       /*******************************************************************************\r
+        * 放送局情報を取得する\r
+        ******************************************************************************/\r
+\r
+       // 設定ファイルがなければWebから取得\r
+       public void loadCenter(String code, boolean force) {\r
+               \r
+               if ( code == null ) {\r
+                       System.out.println(ERRID+"地域コードがnullです.");\r
+                       return;\r
+               }\r
+               \r
+               String centerListFile = getCenterListFile(getTVProgramId(), code);\r
+               \r
+               if (force) {\r
+                       File f = new File(centerListFile);\r
+                       f.delete();\r
+               }\r
+               \r
+               File f = new File(centerListFile);\r
+               if (f.exists() == true) {\r
+                       @SuppressWarnings("unchecked")\r
+                       ArrayList<Center> tmp = (ArrayList<Center>) CommonUtils.readXML(centerListFile);\r
+                       if ( tmp != null ) {\r
+                               \r
+                               crlist = tmp;\r
+                               \r
+                               // 放送局名変換\r
+                               attachChFilters();\r
+                               \r
+                               System.out.println(MSGID+"放送局リストを読み込みました: "+centerListFile);\r
+                               return;\r
+                       }\r
+                       else {\r
+                               System.out.println(MSGID+"放送局リストの読み込みに失敗しました: "+centerListFile);\r
+                       } \r
+               }\r
+               \r
+               // 放送局をつくるよ\r
+               ArrayList<Center> newcrlist = new ArrayList<Center>();\r
+               \r
+               // 地上派・UHFは地域別に扱う\r
+               \r
+               int cntMax = ((code.equals(allCode))?(aclist.size()-2):(1)) + 1;\r
+               int cnt = 1;\r
+               for (AreaCode ac : aclist) {\r
+                       if (ac.getCode().equals(bsCode)) {\r
+                               continue;\r
+                       }\r
+                       else if (code.equals(allCode) && ac.getCode().equals(allCode)) {\r
+                               continue;\r
+                       }\r
+                       else if ( ! code.equals(allCode) && ! ac.getCode().equals(code)) {\r
+                               continue;\r
+                       }\r
+\r
+                       // 地上波\r
+                       String url = "http://www.tvguide.or.jp/TF1101LS.php?regionId="+ac.getCode()+"&mediaId=1";\r
+                       if ( _loadCenter(newcrlist, ac.getCode(), url) ) {\r
+                               reportProgress(MSGID+"放送局情報を取得しました: ("+cnt+"/"+cntMax+") "+url);\r
+                       }\r
+                       cnt++;\r
+               }\r
+               \r
+               // BS1・BS2は共通にする\r
+               \r
+               {\r
+                       // BSデジタル\r
+                       String url = "http://www.tvguide.or.jp/TF1101LS.php?mediaId=2";\r
+                       if ( _loadCenter(newcrlist, bsCode, url) ) {\r
+                               reportProgress(MSGID+"放送局情報を取得しました: ("+cnt+"/"+cntMax+") "+url);\r
+                       }\r
+                       cnt++;\r
+               }\r
+               \r
+               if ( newcrlist.size() == 0 ) {\r
+                       System.err.println(ERRID+"放送局情報の取得結果が0件だったため情報を更新しません");\r
+                       return;\r
+               }\r
+\r
+               crlist = newcrlist;\r
+               attachChFilters();      // 放送局名変換\r
+               saveCenter();\r
+       }\r
+               \r
+       private boolean _loadCenter(ArrayList<Center> newcrlist, String code, String uri) {\r
+               \r
+               String response = webToBuffer(uri,null,null,null,thisEncoding,true);\r
+               if ( response == null ) {\r
+                       System.out.println(ERRID+"放送局情報の取得に失敗しました: "+uri);\r
+                       return false;\r
+               }\r
+               \r
+               // 局名リストに追加する\r
+\r
+               Matcher ma = Pattern.compile("(<select name=\"ch\"[\\s\\S]+?</select>)").matcher(response);\r
+               if ( ma.find() ) {\r
+                       Matcher mb = Pattern.compile("&stationId=(.+?)&page=(.+?)\" (selected)?>([\\s\\S]+?)</option>").matcher(ma.group(1));\r
+                       while ( mb.find() ) {\r
+                               String centerName = CommonUtils.unUniEscape(mb.group(4));\r
+                               String centerId = mb.group(1);\r
+                               String page = mb.group(2);\r
+                               \r
+                               // NHK総合・NHK教育\r
+                               centerName = centerName.replaceFirst("^NHK総合", "NHK総合");\r
+                               centerName = centerName.replaceFirst("^NHK Eテレ", "NHK Eテレ");\r
+                               if ( ! code.startsWith(bsCode)) {\r
+                                       if (centerName.startsWith("NHK")) {\r
+                                               centerName = centerName.replaceFirst("・.*$", "");\r
+                                               centerName = centerName+"・"+getArea(code);\r
+                                       }\r
+                               }\r
+                               else {\r
+                                       if (centerName.equals("放送大学")) {\r
+                                               centerName = "放送大学BS1";\r
+                                       }\r
+                                       else if (centerName.equals("放送大学2")) {\r
+                                               centerName = "放送大学BS2";\r
+                                       }\r
+                                       else if (centerName.equals("放送大学3")) {\r
+                                               centerName = "放送大学BS3";\r
+                                       }\r
+                               }\r
+                               \r
+                               Center cr = new Center();\r
+                               cr.setAreaCode(code);\r
+                               cr.setCenterOrig(centerName);\r
+                               cr.setLink(centerId);\r
+                               cr.setType(page);\r
+                               cr.setEnabled(true);\r
+                               newcrlist.add(cr);\r
+                       }\r
+               }\r
+               \r
+               return true;\r
+       }\r
+\r
+}\r
diff --git a/TinyBannavi/src/tainavi/PlugIn_TVPTVKingdom.java b/TinyBannavi/src/tainavi/PlugIn_TVPTVKingdom.java
new file mode 100644 (file)
index 0000000..42f7e76
--- /dev/null
@@ -0,0 +1,756 @@
+package tainavi;\r
+\r
+import java.io.File;\r
+import java.util.ArrayList;\r
+import java.util.Calendar;\r
+import java.util.Date;\r
+import java.util.GregorianCalendar;\r
+import java.util.HashMap;\r
+import java.util.LinkedHashMap;\r
+import java.util.Locale;\r
+import java.util.regex.Matcher;\r
+import java.util.regex.Pattern;\r
+\r
+\r
+public class PlugIn_TVPTVKingdom extends TVProgramUtils implements TVProgram,Cloneable {\r
+\r
+       public PlugIn_TVPTVKingdom clone() {\r
+               return (PlugIn_TVPTVKingdom) super.clone();\r
+       }\r
+       \r
+       final static String thisEncoding = "UTF-8"; \r
+       \r
+       \r
+       /*******************************************************************************\r
+        * 種族の特性\r
+        ******************************************************************************/\r
+       \r
+       @Override\r
+       public String getTVProgramId() { return "Gガイド.テレビ王国"; }\r
+       \r
+       @Override\r
+       public ProgType getType() { return ProgType.PROG; }\r
+       \r
+       @Override\r
+       public ProgSubtype getSubtype() { return ProgSubtype.TERRA; }\r
+\r
+       \r
+       /*******************************************************************************\r
+        * 個体の特性\r
+        ******************************************************************************/\r
+\r
+       @Override\r
+       public int getTimeBarStart() {return 5;}\r
+       \r
+       private int getDogDays() { return ((getExpandTo8())?(8):(7)); }\r
+\r
+       \r
+       /*******************************************************************************\r
+        * 定数\r
+        ******************************************************************************/\r
+       \r
+       protected final static int accessWait = 1200;\r
+       \r
+       private final String MSGID = "["+getTVProgramId()+"] ";\r
+       private final String ERRID = "[ERROR]"+MSGID;\r
+       private final String DBGID = "[DEBUG]"+MSGID;\r
+       \r
+       \r
+       /*******************************************************************************\r
+        * 部品\r
+        ******************************************************************************/\r
+       \r
+       // 新しい入れ物の臨時格納場所\r
+       protected final ArrayList<ProgList> newplist = new ArrayList<ProgList>();\r
+       \r
+       // 未定義のフラグの回収場所\r
+       protected final HashMap<String,String> nf = new HashMap<String, String>();\r
+       \r
+       protected void debugNF() {\r
+               for ( String f : nf.keySet() ) {\r
+                       System.err.println(String.format("【デバッグ情報】未定義のフラグです: [%s]",f));\r
+               }\r
+       }\r
+\r
+       // BS番組表の総ページ数\r
+       private String getBscntFile() { return String.format("env"+File.separator+"bscnt.%s",getTVProgramId()); }\r
+       private int bscnt = 2;\r
+       \r
+       \r
+       /*******************************************************************************\r
+        * コンストラクタ\r
+        ******************************************************************************/\r
+       \r
+       \r
+       /*******************************************************************************\r
+        * 番組情報を取得する\r
+        ******************************************************************************/\r
+       \r
+       @Override\r
+       public void loadProgram(String areaCode, boolean force) {\r
+               \r
+               // 入れ物を空にする\r
+               newplist.clear();\r
+               nf.clear(); \r
+               \r
+               // 地域コードごとの参照ページ数の入れ物を用意する\r
+               LinkedHashMap<String,Integer> pages = new LinkedHashMap<String, Integer>();\r
+               \r
+               // 参照する地域コードをまとめる\r
+               if ( areaCode.equals(allCode) ) {\r
+                       // 「全国」\r
+                       for ( Center cr : crlist ) {\r
+                               if ( cr.getOrder() > 0 ) {\r
+                                       // 有効局の地域コードのみ集める\r
+                                       pages.put(cr.getAreaCode(),0);\r
+                               }\r
+                       }\r
+               }\r
+               else {\r
+                       // 地域個別\r
+                       pages.put(areaCode,0);\r
+                       pages.put(bsCode,0);\r
+               }\r
+               \r
+               // トップの下に局ごとのリストを生やす\r
+               for ( String ac : pages.keySet() ) {\r
+                       for ( Center cr : crlist ) {\r
+                               if ( ac.equals(cr.getAreaCode()) ) {\r
+                                       ProgList pl = new ProgList();\r
+                                       pl.Area = cr.getAreaCode();\r
+                                       pl.SubArea = cr.getType();\r
+                                       pl.Center = cr.getCenter();\r
+                                       pl.CenterId = cr.getLink();\r
+                                       pl.BgColor = cr.getBgColor();\r
+                                       \r
+                                       // <TABLE>タグの列数を決め打ちで処理するので、設定上無効な局も内部的には列の1つとして必要\r
+                                       pl.enabled = (cr.getOrder()>0)?(true):(false);\r
+                                       \r
+                                       newplist.add(pl);\r
+                                       \r
+                                       int pg = Integer.valueOf(cr.getType());\r
+                                       if ( pl.enabled && pages.get(ac) < pg ) {\r
+                                               // 地域コードごとの最大参照ページ数を格納する\r
+                                               pages.put(ac,pg);\r
+                                       }\r
+                               }\r
+                       }\r
+               }\r
+\r
+               // 局の下に日付ごとのリストを生やす\r
+               GregorianCalendar cal = new GregorianCalendar();\r
+               cal.setTime(new Date());\r
+               if ( CommonUtils.isLateNight(cal) ) {\r
+                       // 4時までは当日扱いにする\r
+                       cal.add(Calendar.DATE, -1);\r
+               }\r
+               GregorianCalendar cale = (GregorianCalendar) cal.clone();\r
+               for (int i=0; i<getDogDays(); i++) {\r
+                       String date = CommonUtils.getDate(cale);\r
+                       for ( ProgList pl : newplist ) {\r
+                               ProgDateList cl = new ProgDateList();\r
+                               cl.Date = date;\r
+                               pl.pdate.add(cl);\r
+                       }\r
+                       cale.add(Calendar.DATE, 1);\r
+               }\r
+\r
+               // 参照する総ページ数を計算\r
+               int counterMax = 0;\r
+               for ( String ac : pages.keySet() ) {\r
+                       counterMax += pages.get(ac)*getDogDays();\r
+               }\r
+\r
+               // 番組リストの追加\r
+               int counter = 1;\r
+               for (String ac : pages.keySet()) {\r
+                       cale = (GregorianCalendar) cal.clone();\r
+                       for (int i=0; i<getDogDays(); i++) {\r
+                               String date = CommonUtils.getDateYMD(cale)+"0500";\r
+                               int dMax = (pages.get(ac)<REFPAGESMAX) ? pages.get(ac) : REFPAGESMAX;\r
+                               for ( int d=1; d<=dMax; d++ ) { // 最大{REFPAGESMAX}ページまでしか参照しない\r
+                                       String url = null;\r
+                                       String cookie = null;\r
+                                       if (ac.equals(bsCode)) {\r
+                                               // BS\r
+                                               url = "http://tv.so-net.ne.jp/chart/bs"+d+".action?head="+date+"&span=24";\r
+                                               cookie = null;\r
+                                       }\r
+                                       else {\r
+                                               // 地上波・UHF\r
+                                               if ( d == 1 ) {\r
+                                                       url = "http://tv.so-net.ne.jp/chart/"+ac+".action?head="+date+"&span=24";\r
+                                                       cookie = null;\r
+                                               }\r
+                                               else if ( d == 2 ) {\r
+                                                       String ck = "";\r
+                                                       for ( ProgList pl : newplist ) {\r
+                                                               if ( pl.Area.equals(ac) && pl.SubArea.equals("2") ) {\r
+                                                                       // サブチャンネルがある\r
+                                                                       String[] id = pl.CenterId.split(",");\r
+                                                                       if (id.length == 2) {\r
+                                                                               ck += "gtv.selectedStationId."+id[0]+"="+id[1]+"; ";\r
+                                                                       }\r
+                                                               }\r
+                                                       }\r
+                                                       if ( ! ck.equals("") ) {\r
+                                                               url = "http://tv.so-net.ne.jp/chart/"+ac+".action?head="+date+"&span=24";\r
+                                                               cookie = ck;\r
+                                                       }\r
+                                               }\r
+                                       }\r
+                                       if ( url != null ) {\r
+                                               _loadProgram(ac, d, dMax, url, cookie, force, i, cale.get(Calendar.MONTH)+1, cale.get(Calendar.DATE), counter++, counterMax);\r
+                                       }\r
+                               }\r
+                               \r
+                               cale.add(Calendar.DATE, 1);\r
+                       }\r
+               }\r
+               \r
+               // 開始・終了日時を正しい値に計算しなおす\r
+               for (ProgList pl : newplist) {\r
+                       setAccurateDate(pl.pdate);\r
+               }\r
+               \r
+               // 解析用\r
+               debugNF();\r
+               \r
+               // 古い番組データを置き換える\r
+               pcenter = newplist;\r
+       }\r
+       \r
+       /* ここまで */\r
+       \r
+       \r
+       \r
+       /*\r
+        * 非公開メソッド等\r
+        */\r
+       \r
+       //\r
+       private void _loadProgram(String ac, int page, int pmax, String url, String cookie, boolean force, int wdaycol, int month, int day, int counter, int counterMax) {\r
+               // progfilesの読み出し\r
+               //\r
+               final String progCacheFile = String.format(getProgDir()+File.separator+"TVK_%s_%s_%04d.html", ac, page, day);\r
+               try {\r
+                       File f = new File(progCacheFile);\r
+                       if (force == true ||\r
+                                       (f.exists() == true && isCacheOld(progCacheFile) == true) ||\r
+                                       (f.exists() == false && isCacheOld(null) == true)) {\r
+                               webToFile(url, null, cookie, null, progCacheFile, thisEncoding);\r
+                               reportProgress(String.format("%s (オンライン)を取得しました: (%d/%d) %s - %d日[%d/%d] %s",getTVProgramId(),counter,counterMax,getArea(ac),day,page,pmax,url));\r
+                               // 連続アクセス規制よけ\r
+                               CommonUtils.milSleep(accessWait);\r
+                       }\r
+                       else if (CommonUtils.isFileAvailable(f,10)) {\r
+                               reportProgress(String.format("%s (キャッシュ)を取得しました: (%d/%d) %s - %d日[%d/%d] %s",getTVProgramId(),counter,counterMax,getArea(ac),day,page,pmax,progCacheFile));\r
+                       }\r
+                       else {\r
+                               reportProgress(String.format("%s (キャッシュ)がみつかりません: (%d/%d) %s - %d日[%d/%d] %s",getTVProgramId(),counter,counterMax,getArea(ac),day,page,pmax,progCacheFile));\r
+                               return;\r
+                       }\r
+\r
+                       // キャッシュファイルの読み込み\r
+                       String response = CommonUtils.read4file(progCacheFile, true);\r
+                       \r
+                       // キャッシュが不整合を起こしていたら投げ捨てる\r
+                       Matcher ma = Pattern.compile(String.format("<title>%d月 %d日",month,day)).matcher(response);\r
+                       if ( ! ma.find() ) {\r
+                               reportProgress(String.format("%s (キャッシュ)が無効です: (%d/%d) %s - %d日[%d/%d] %s",getTVProgramId(),counter,counterMax,getArea(ac),day,page,pmax,progCacheFile));\r
+                               return;\r
+                       }\r
+               \r
+                       // 番組リストの追加\r
+                       getPrograms(ac, String.valueOf(page), wdaycol, response);\r
+               }\r
+               catch (Exception e) {\r
+                       // 例外\r
+                       System.out.println("Exception: _loadProgram()");\r
+                       e.printStackTrace();\r
+               }\r
+       }\r
+       \r
+       //\r
+       private void getPrograms(String areacode, String page, int wdaycol, String src) {\r
+               \r
+               //\r
+               int col = -1;\r
+               for ( int i=0; i<newplist.size(); i++ ) {\r
+                       ProgList pl = newplist.get(i);\r
+                       if ( pl.Area.equals(areacode) && pl.SubArea.equals(page) ) {\r
+                               col = i;\r
+                               break;\r
+                       }\r
+               }\r
+               if (col < 0) {\r
+                       System.out.println("getProgram() error");\r
+                       return;\r
+               }\r
+               \r
+               Matcher ma = Pattern.compile("<div class=\"cell-station cell-top\"(.+?)<div class=\"cell-station\"").matcher(src);\r
+               while ( ma.find() ) {\r
+                       // 一局分取り込む\r
+                       getDetails(newplist.get(col++).pdate.get(wdaycol), ma.group(1));\r
+               }\r
+       }\r
+       //\r
+       protected void getDetails(ProgDateList pcl, String response) {\r
+                       \r
+               Matcher mb = Pattern.compile("<div class=\"cell-schedule( cell-genre-(\\d+?) cell-genre-(\\d+?) )?(.+?)</div>").matcher(response);\r
+               while (mb.find())\r
+               {\r
+                       Matcher mc = Pattern.compile("top: (\\d+)px;.+?(>番組情報がありません<|(/schedule/\\d+(\\d\\d)(\\d\\d)\\.action).+?<span class=\"schedule-title.?\">(.*?)</span>.+?<span class=\"schedule-summary.?\">(.*?)</span>)").matcher(mb.group(4));\r
+                       while (mc.find()) {\r
+                               // なんかしらんが位置が被ってるのがある\r
+                               ProgDetailList pdl = null;\r
+                               if (pcl.pdetail.size() > 0) {\r
+                                       if (pcl.pdetail.get(pcl.pdetail.size()-1).length == Integer.valueOf(mc.group(1))) {\r
+                                               pdl = pcl.pdetail.get(pcl.pdetail.size()-1);\r
+                                       }\r
+                                       else {\r
+                                               pdl = new ProgDetailList();\r
+                                               pcl.pdetail.add(pdl);\r
+                                       }\r
+                               }\r
+                               else {\r
+                                       pdl = new ProgDetailList();\r
+                                       pcl.pdetail.add(pdl);\r
+                               }\r
+                               \r
+                               // 詳細情報の処理\r
+                               if (mc.group(2).equals(">番組情報がありません<")) {\r
+                                       pdl.title = pdl.splitted_title = "番組情報がありません";\r
+                                       pdl.detail = "";\r
+                                       pdl.length = Integer.valueOf(Integer.valueOf(mc.group(1)));\r
+                                       pdl.genre = ProgGenre.NOGENRE;\r
+                                       pdl.start = "";\r
+                               }\r
+                               else {\r
+                                       // タイトルと番組詳細\r
+                                       pdl.title = CommonUtils.unEscape(mc.group(6)).replaceAll("<wbr/>","");\r
+                                       pdl.detail = CommonUtils.unEscape(mc.group(7)).replaceAll("<wbr/>","").replaceAll("<BR>", "\n");\r
+                                       pdl.length = Integer.valueOf(Integer.valueOf(mc.group(1)));\r
+                                       \r
+                                       // タイトルから各種フラグを分離する\r
+                                       doSplitFlags(pdl, nf);\r
+                                       \r
+                                       //\r
+                                       if (mb.group(3) == null) {\r
+                                               pdl.genre = ProgGenre.NOGENRE;\r
+                                       }\r
+                                       else if (mb.group(3).equals("100000")) {\r
+                                               pdl.genre = ProgGenre.NEWS;\r
+                                               pdl.subgenre = getSubgenre(pdl.genre, mb.group(2).substring(4));\r
+                                       }\r
+                                       else if (mb.group(3).equals("101000")) {\r
+                                               pdl.genre = ProgGenre.SPORTS;\r
+                                               pdl.subgenre = getSubgenre(pdl.genre, mb.group(2).substring(4));\r
+                                       }\r
+                                       else if (mb.group(3).equals("102000")) {\r
+                                               pdl.genre = ProgGenre.VARIETYSHOW;\r
+                                               pdl.subgenre = getSubgenre(pdl.genre, mb.group(2).substring(4));\r
+                                       }\r
+                                       else if (mb.group(3).equals("103000")) {\r
+                                               pdl.genre = ProgGenre.DORAMA;\r
+                                               pdl.subgenre = getSubgenre(pdl.genre, mb.group(2).substring(4));\r
+                                       }\r
+                                       else if (mb.group(3).equals("104000")) {\r
+                                               pdl.genre = ProgGenre.MUSIC;\r
+                                               pdl.subgenre = getSubgenre(pdl.genre, mb.group(2).substring(4));\r
+                                       }\r
+                                       else if (mb.group(3).equals("105000")) {\r
+                                               pdl.genre = ProgGenre.VARIETY;\r
+                                               pdl.subgenre = getSubgenre(pdl.genre, mb.group(2).substring(4));\r
+                                       }\r
+                                       else if (mb.group(3).equals("106000")) {\r
+                                               pdl.genre = ProgGenre.MOVIE;\r
+                                               pdl.subgenre = getSubgenre(pdl.genre, mb.group(2).substring(4));\r
+                                       }\r
+                                       else if (mb.group(3).equals("107000")) {\r
+                                               pdl.genre = ProgGenre.ANIME;\r
+                                               pdl.subgenre = getSubgenre(pdl.genre, mb.group(2).substring(4));\r
+                                       }\r
+                                       else if (mb.group(3).equals("108000")) {\r
+                                               pdl.genre = ProgGenre.DOCUMENTARY;\r
+                                               pdl.subgenre = getSubgenre(pdl.genre, mb.group(2).substring(4));\r
+                                       }\r
+                                       else if (mb.group(3).equals("109000")) {\r
+                                               pdl.genre = ProgGenre.THEATER;\r
+                                               pdl.subgenre = getSubgenre(pdl.genre, mb.group(2).substring(4));\r
+                                       }\r
+                                       else if (mb.group(3).equals("110000")) {\r
+                                               pdl.genre = ProgGenre.HOBBY;\r
+                                               pdl.subgenre = getSubgenre(pdl.genre, mb.group(2).substring(4));\r
+                                       }\r
+                                       else if (mb.group(3).equals("111000")) {\r
+                                               pdl.genre = ProgGenre.WELFARE;\r
+                                               pdl.subgenre = getSubgenre(pdl.genre, mb.group(2).substring(4));\r
+                                       }\r
+                                       else {\r
+                                               pdl.genre = ProgGenre.NOGENRE;\r
+                                               pdl.subgenre = getSubgenre(pdl.genre, mb.group(2).substring(4));\r
+                                       }\r
+                                       \r
+                                       // サブタイトル分離\r
+                                       doSplitSubtitle(pdl);\r
+                                       \r
+                                       // 番組ID\r
+                                       Matcher md = Pattern.compile("/iepg\\.tvpi\\?id=(\\d+)\"").matcher(mb.group(4));\r
+                                       if ( md.find() ) {\r
+                                               String chid = null;\r
+                                               if ( md.group(1).startsWith("200") && md.group(1).length() >= 6 ) {\r
+                                                       chid = String.format("%04X%04X%04X", 4,0,Integer.valueOf(md.group(1).substring(3,6)));\r
+                                               }\r
+                                               else if ( md.group(1).startsWith("500") && md.group(1).length() >= 6 ) {\r
+                                                       chid = String.format("%04X%04X%04X", 7,0,Integer.valueOf(md.group(1).substring(3,6)));\r
+                                               }\r
+                                               else if ( md.group(1).startsWith("1") && md.group(1).length() >= 6 ) {\r
+                                                       chid = String.format("%04X%04X%04X", 0x7FFF,0,Integer.valueOf(md.group(1).substring(0,6))-100000);\r
+                                               }\r
+                                               if ( chid != null ) {\r
+                                                       ContentIdDIMORA.decodeChId(chid);\r
+                                                       pdl.progid = ContentIdDIMORA.getContentId(0,"");\r
+                                               }\r
+                                       }\r
+                                       \r
+                                       //\r
+                                       pdl.link = "http://tv.so-net.ne.jp"+mc.group(3);\r
+                                       \r
+                                       //\r
+                                       pdl.start = String.format("%02d:%02d", Integer.valueOf(mc.group(4)), Integer.valueOf(mc.group(5)));\r
+                               }\r
+                       }\r
+               }\r
+               // lengthの調整\r
+               for (int i=1; i<pcl.pdetail.size(); i++) {\r
+                       pcl.pdetail.get(i-1).length = (pcl.pdetail.get(i).length - pcl.pdetail.get(i-1).length)/3;\r
+               }\r
+               if (pcl.pdetail.size() >= 1) {\r
+                       pcl.pdetail.get(pcl.pdetail.size()-1).length = 24*60 - (pcl.pdetail.get(pcl.pdetail.size()-1).length-20)/3;\r
+               }\r
+               \r
+               for (int i=0; i<pcl.pdetail.size(); i++) {\r
+                       pcl.row += pcl.pdetail.get(i).length;\r
+               }\r
+               \r
+               // 終了時刻の調整\r
+               GregorianCalendar c = new GregorianCalendar(Locale.JAPAN);\r
+               c.setTime(new Date());\r
+               for (ProgDetailList pdl : pcl.pdetail) {\r
+                       Matcher mx = Pattern.compile("(\\d\\d):(\\d\\d)").matcher(pdl.start);\r
+                       if (mx.find()) {\r
+                               c.set(Calendar.HOUR_OF_DAY, Integer.valueOf(mx.group(1)));\r
+                               c.set(Calendar.MINUTE, Integer.valueOf(mx.group(2)));\r
+                               c.add(Calendar.MINUTE, pdl.length);\r
+                               pdl.end = String.format("%02d:%02d", c.get(Calendar.HOUR_OF_DAY), c.get(Calendar.MINUTE));\r
+                       }\r
+               }\r
+       }\r
+       //\r
+       private ProgSubgenre getSubgenre(ProgGenre genre, String s) {\r
+               String code = String.format("%X", Integer.valueOf(s));\r
+               for (ProgSubgenre subgenre : ProgSubgenre.values(genre)) {\r
+                       //if (subgenre.getGenre() == genre && subgenre.toIEPG().equals(code)) {\r
+                       if (subgenre.toIEPG().equals(code)) {\r
+                               return(subgenre);\r
+                       }\r
+               }\r
+               return(null);\r
+       }\r
+       \r
+       \r
+       \r
+       /*******************************************************************************\r
+        * 地域情報を取得する\r
+        ******************************************************************************/\r
+       \r
+       //\r
+       @Override\r
+       public String getDefaultArea() {return "東京";}\r
+       \r
+       //\r
+       public void loadAreaCode(){\r
+               \r
+               // 設定ファイルが存在していればファイルから\r
+               File f = new File(getAreaSelectedFile());\r
+               if (f.exists() == true) {\r
+                       @SuppressWarnings("unchecked")\r
+                       ArrayList<AreaCode> tmp = (ArrayList<AreaCode>) CommonUtils.readXML(getAreaSelectedFile());\r
+                       if ( tmp != null ) {\r
+                               \r
+                               aclist = tmp;\r
+                               \r
+                               // 後方互換\r
+                               for ( int i=aclist.size()-1; i>=0; i-- ) {\r
+                                       if (aclist.get(i).getCode().equals("bs2")) {\r
+                                               aclist.remove(i);\r
+                                       }\r
+                                       else if (aclist.get(i).getCode().equals("bs1")) {\r
+                                               aclist.get(i).setArea("BS");\r
+                                               aclist.get(i).setCode(bsCode);\r
+                                       }\r
+                               }\r
+                               \r
+                               return;\r
+                       }\r
+                       else  {\r
+                               System.err.println("地域リストの読み込みに失敗しました: "+getAreaSelectedFile());\r
+                       }\r
+               }\r
+\r
+               // 地域一覧の作成\r
+               ArrayList<AreaCode> newaclist = new ArrayList<AreaCode>();\r
+               \r
+               // 存在していなければWeb上から\r
+               String uri = "http://tv.so-net.ne.jp/chart/23.action";\r
+               String response = webToBuffer(uri,thisEncoding,true);\r
+               if ( response == null ) {\r
+                       System.err.println("地域情報の取得に失敗しました: "+uri);\r
+                       return;\r
+               }\r
+               \r
+               Matcher ma = Pattern.compile("<select name=\"stationAreaId\" id=\"area-selector\" onchange=\"this.form.submit\\(\\);\">(.+?)</select>").matcher(response);\r
+               if (ma.find()) {\r
+                       Matcher mb = Pattern.compile("<option value=\"([^\"]+?)\" ?(selected=\"selected\")?>(.+?)</option>").matcher(ma.group(1));\r
+                       while (mb.find()) {\r
+                               if (mb.group(3).indexOf("CATV") == -1) {\r
+                                       AreaCode ac = new AreaCode();\r
+                                       ac.setArea(mb.group(3));\r
+                                       ac.setCode(mb.group(1));\r
+                                       newaclist.add(ac);\r
+                               }\r
+                       }\r
+               }\r
+               \r
+               if ( newaclist.size() == 0 ) {\r
+                       System.err.println(ERRID+"地域一覧の取得結果が0件だったため情報を更新しません");\r
+                       return;\r
+               }\r
+               \r
+               {\r
+                       {\r
+                               AreaCode ac = new AreaCode();\r
+                               ac.setArea("全国");\r
+                               ac.setCode(allCode);\r
+                               newaclist.add(0,ac);\r
+                       }\r
+                       {\r
+                               AreaCode ac = new AreaCode();\r
+                               ac.setArea("BS");\r
+                               ac.setCode(bsCode);\r
+                               newaclist.add(ac);\r
+                       }\r
+               }\r
+               \r
+               //\r
+               aclist = newaclist;\r
+               saveAreaCode();\r
+       }\r
+       \r
+       //\r
+       public void saveAreaCode() {\r
+               if ( ! CommonUtils.writeXML(getAreaSelectedFile(), aclist) ) {\r
+                       System.err.println("地域リストの保存に失敗しました: "+getAreaSelectedFile());\r
+               }\r
+       }\r
+\r
+       \r
+       /*******************************************************************************\r
+        * 放送局情報を取得する\r
+        ******************************************************************************/\r
+\r
+       // 設定ファイルがなければWebから取得\r
+       public void loadCenter(String code, boolean force) {\r
+               \r
+               if ( code == null ) {\r
+                       System.out.println(ERRID+"地域コードがnullです.");\r
+                       return;\r
+               }\r
+               \r
+               // BSのページ数の初期化(事前に判明していない場合は2)\r
+               int bscntTmp = CommonUtils.loadCnt(getBscntFile());\r
+               bscntTmp = bscnt = (bscntTmp > 0)?(bscntTmp):(2);\r
+               \r
+               //\r
+               String centerListFile = getCenterListFile(getTVProgramId(), code);\r
+               \r
+               if (force) {\r
+                       File f = new File(centerListFile);\r
+                       f.delete();\r
+               }\r
+               \r
+               File f = new File(centerListFile);\r
+               if (f.exists() == true) {\r
+                       //System.out.println("Center Alredy Exist: "+centerListFile);\r
+                       @SuppressWarnings("unchecked")\r
+                       ArrayList<Center> tmp = (ArrayList<Center>) CommonUtils.readXML(centerListFile);\r
+                       if ( tmp != null ) {\r
+                               \r
+                               crlist = tmp;\r
+                               \r
+                               // 放送局名変換\r
+                               attachChFilters();\r
+\r
+                               // 後方互換\r
+                               for (Center c : crlist) {\r
+                                       if (c.getAreaCode().equals("bs2")) {\r
+                                               c.setAreaCode(bsCode);\r
+                                               c.setType("2");\r
+                                       }\r
+                                       else if (c.getAreaCode().equals("bs1")) {\r
+                                               c.setAreaCode(bsCode);\r
+                                               c.setType("1");\r
+                                       }\r
+                                       else if (c.getType().equals("")) {\r
+                                               c.setType("1");\r
+                                       }\r
+                               }\r
+\r
+                               System.out.println("放送局リストを読み込みました: "+centerListFile);\r
+                               \r
+                               return;\r
+                       }\r
+                       else {\r
+                               System.err.println("放送局リストの読み込みに失敗しました: "+centerListFile);\r
+                       } \r
+               }\r
+               \r
+               // 放送局をつくるよ\r
+               ArrayList<Center> newcrlist = new ArrayList<Center>();\r
+               \r
+               // 地上派・UHFは地域別に扱う\r
+               \r
+               int cntMax = ((code.equals(allCode))?(aclist.size()-2):(1))+bscnt;\r
+               int cnt = 1;\r
+               for (AreaCode ac : aclist) {\r
+                       if (ac.getCode().equals(bsCode)) {\r
+                               continue;\r
+                       }\r
+                       else if (code.equals(allCode) && ac.getCode().equals(allCode)) {\r
+                               continue;\r
+                       }\r
+                       else if ( ! code.equals(allCode) && ! ac.getCode().equals(code)) {\r
+                               continue;\r
+                       }\r
+                       \r
+                       String url;\r
+                       \r
+                       // 地上波\r
+                       url = "http://tv.so-net.ne.jp/chart/"+ac.getCode()+".action";\r
+                       if ( _loadCenter(newcrlist, ac.getCode(), "1", url) ) {\r
+                               reportProgress("放送局情報を取得しました: ("+cnt+"/"+cntMax+") "+url);\r
+                       }\r
+                       cnt++;\r
+               }\r
+               \r
+               // BSdは共通にする(bscntは_loadCenter()中で増加する可能性あり)\r
+               \r
+               for ( int d=1; d<=bscnt; d++ )\r
+               {\r
+                       String url = "http://tv.so-net.ne.jp/chart/bs"+d+".action";\r
+                       if ( _loadCenter(newcrlist, bsCode, String.valueOf(d), url) ) {\r
+                               reportProgress("放送局情報を取得しました: ("+cnt+"/"+cntMax+") "+url);\r
+                       }\r
+                       cnt++;\r
+               }\r
+               \r
+               // BSのページ数を記録する\r
+               \r
+               if ( bscntTmp < bscnt ) {\r
+                       reportProgress("BSのページ数が変更されました: "+bscntTmp+"→"+bscnt);\r
+                       CommonUtils.saveCnt(bscnt,getBscntFile());\r
+               }\r
+               \r
+               crlist = newcrlist;\r
+               attachChFilters();      // 放送局名変換\r
+               saveCenter();\r
+       }\r
+               \r
+       private boolean _loadCenter(ArrayList<Center> newcrlist, String code, String page, String uri) {\r
+               \r
+               String response = webToBuffer(uri,thisEncoding,true);\r
+               CommonUtils.milSleep(accessWait);       // 連続アクセス規制よけ\r
+               if ( response == null ) {\r
+                       System.err.println("放送局情報の取得に失敗しました: "+uri);\r
+                       return false;\r
+               }\r
+               \r
+               // BSのページ数を計算する\r
+               \r
+               for ( int i=bscnt+1; i<=10; i++ ) {\r
+                       if ( ! response.matches(".*\"/chart/bs"+i+"\\.action\".*") ) {\r
+                               if ( bscnt < i-1 ) {\r
+                                       bscnt = i-1;\r
+                               }\r
+                               break;\r
+                       }\r
+               }\r
+               \r
+               // 局名リストに追加する\r
+               \r
+               int fCol = newcrlist.size();\r
+               Matcher mb = Pattern.compile("id=\"cell-station-sticky-(\\d+?)\"\\s*title=\"(.+?)\"").matcher(response);\r
+               while (mb.find()) {\r
+                       String centerName = CommonUtils.unEscape(mb.group(2));\r
+                       String centerId = mb.group(1);\r
+                       \r
+                       // NHK総合・NHK教育\r
+                       //centerName = centerName.replaceFirst("^NHK総合1・", "NHK総合・");\r
+                       centerName = centerName.replaceFirst("NHKEテレ1・", "NHK Eテレ・");\r
+                       centerName = centerName.replaceFirst("NHKEテレ", "NHK Eテレ・");\r
+                       \r
+                       Center cr = new Center();\r
+                       cr.setAreaCode(code);\r
+                       cr.setCenterOrig(centerName);\r
+                       cr.setLink(centerId);\r
+                       cr.setType(page);\r
+                       cr.setEnabled(true);\r
+                       newcrlist.add(cr);\r
+               }\r
+               \r
+               // マルチチャンネル情報を取得する\r
+               \r
+               if ( ! code.equals(bsCode) ) {\r
+                       HashMap<String,String> mCH = new HashMap<String, String>(); \r
+                       HashMap<String,String> mNM = new HashMap<String, String>(); \r
+                       mb = Pattern.compile("<div id=\"station-selectors\">([\\s\\S]+?)</div>").matcher(response);\r
+                       if ( mb.find() ) {\r
+                               Matcher mc = Pattern.compile("<ul [\\s\\S]*? id=\"station-selector-(\\d+?)\" [\\s\\S]*?>(.+?)</ul>").matcher(mb.group(1));\r
+                               while ( mc.find() ) {\r
+                                       Matcher md = Pattern.compile("\\?selectedStationId=(\\d+?)\">([\\s\\S]+?)</a>").matcher(mc.group(2));\r
+                                       while ( md.find() ) {\r
+                                               if ( ! mc.group(1).equals(md.group(1)) && ! mCH.containsKey(mc.group(1)) ) {\r
+                                                       mCH.put(mc.group(1), md.group(1));\r
+                                                       mNM.put(md.group(1), md.group(2));\r
+                                                       break;\r
+                                               }\r
+                                       }\r
+                               }\r
+                       }\r
+                       if ( mCH.size() > 0 ) {\r
+                               int eCol = newcrlist.size()-1;\r
+                               for ( int i=fCol; i<=eCol; i++ ) {\r
+                                       String oID = newcrlist.get(i).getLink();\r
+                                       Center cr = new Center();\r
+                                       if ( mCH.containsKey(oID) ) {\r
+                                               String mID = mCH.get(oID);\r
+                                               cr.setCenterOrig(mNM.get(mID));\r
+                                               cr.setLink(oID+","+mID);\r
+                                       }\r
+                                       else {\r
+                                               cr.setCenterOrig("(選択できません)");\r
+                                               cr.setLink("");\r
+                                       }\r
+                                       cr.setAreaCode(code);\r
+                                       cr.setType("2");\r
+                                       cr.setEnabled(true);\r
+                                       newcrlist.add(cr);\r
+                               }\r
+                       }\r
+               }\r
+               \r
+               return true;\r
+       }\r
+       \r
+}\r
diff --git a/TinyBannavi/src/tainavi/PlugIn_TVPtheTelevision.java b/TinyBannavi/src/tainavi/PlugIn_TVPtheTelevision.java
new file mode 100644 (file)
index 0000000..2b55912
--- /dev/null
@@ -0,0 +1,680 @@
+package tainavi;\r
+\r
+import java.io.File;\r
+import java.util.ArrayList;\r
+import java.util.Calendar;\r
+import java.util.GregorianCalendar;\r
+import java.util.HashMap;\r
+import java.util.Locale;\r
+import java.util.regex.Matcher;\r
+import java.util.regex.Pattern;\r
+\r
+\r
+public class PlugIn_TVPtheTelevision extends TVProgramUtils implements TVProgram,Cloneable {\r
+\r
+       public PlugIn_TVPtheTelevision clone() {\r
+               return (PlugIn_TVPtheTelevision) super.clone();\r
+       }\r
+       \r
+       private static final String thisEncoding = "UTF-8";\r
+       \r
+       \r
+       /*******************************************************************************\r
+        * 種族の特性\r
+        ******************************************************************************/\r
+       \r
+       @Override\r
+       public String getTVProgramId() { return "webザテレビジョン"; }\r
+       \r
+       @Override\r
+       public ProgType getType() { return ProgType.PROG; }\r
+       @Override\r
+       public ProgSubtype getSubtype() { return ProgSubtype.TERRA; }\r
+\r
+       \r
+       /*******************************************************************************\r
+        * 個体の特性\r
+        ******************************************************************************/\r
+\r
+       @Override\r
+       public int getTimeBarStart() {return 5;}\r
+       \r
+       //private int getDogDays() { return 7; }\r
+       \r
+       \r
+       /*******************************************************************************\r
+        * 定数\r
+        ******************************************************************************/\r
+\r
+       private final String MSGID = "["+getTVProgramId()+"] ";\r
+       private final String ERRID = "[ERROR]"+MSGID;\r
+       private final String DBGID = "[DEBUG]"+MSGID;\r
+       \r
+       \r
+       /*******************************************************************************\r
+        * 部品\r
+        ******************************************************************************/\r
+       \r
+       // 新しい入れ物の臨時格納場所\r
+       protected ArrayList<ProgList> newplist = new ArrayList<ProgList>();\r
+       \r
+       // 未定義のフラグの回収場所\r
+       private final HashMap<String,String> nf = new HashMap<String, String>();\r
+       \r
+       \r
+       /*******************************************************************************\r
+        * コンストラクタ\r
+        ******************************************************************************/\r
+       \r
+       \r
+       /*******************************************************************************\r
+        * 番組情報を取得する\r
+        ******************************************************************************/\r
+       \r
+       public void loadProgram(String areaCode, boolean force) {\r
+               \r
+               // 入れ物を空にする\r
+               newplist.clear();\r
+               nf.clear(); \r
+               \r
+               //\r
+               int counterMax = getSortedCRlist().size();\r
+               int counter=1;\r
+               for ( Center c : getSortedCRlist() ) {\r
+                       _loadProgram(c, force, counter++, counterMax);\r
+               }\r
+               \r
+               // 古い番組データを置き換える\r
+               pcenter = newplist;\r
+       }\r
+       \r
+       /* ここまで */\r
+\r
+       \r
+       \r
+       /*\r
+        * 非公開メソッド\r
+        */\r
+       \r
+       protected void _loadProgram(Center cr, boolean force, int counter, int counterMax) {\r
+               //\r
+               try {\r
+                       // progfilesの読み出し\r
+                       \r
+                       // 局リストの追加\r
+                       ProgList pl = new ProgList();\r
+                       pl.Center = cr.getCenter();\r
+                       pl.CenterId = cr.getLink();\r
+                       pl.Area = cr.getAreaCode();\r
+                       pl.Type = cr.getType();\r
+                       pl.BgColor = cr.getBgColor();\r
+                       pl.enabled = true;\r
+                       newplist.add(pl);\r
+\r
+                       final String progCacheFile = String.format(getProgDir()+File.separator+"TVTheTV_%s_%s.html", pl.Area, pl.CenterId);\r
+                       String response = null;\r
+                       //\r
+                       File f = new File(progCacheFile);\r
+                       if (force == true ||\r
+                                       (f.exists() == true && isCacheOld(progCacheFile) == true) ||\r
+                                       (f.exists() == false && isCacheOld(null) == true)) {\r
+                               String url = "";\r
+                               if (pl.Type.equals("csa")) {\r
+                                       url = "http://www.television.co.jp/programlist/guide24.php?range=24&type=cs&channel="+pl.CenterId;\r
+                               }\r
+                               else if (pl.Type.equals("csd")) {\r
+                                       url = "http://www.television.co.jp/digitalguide/guide24.php?range=24&type=cs&channel="+pl.CenterId;\r
+                               }\r
+                               else if (pl.Type.equals(bsCode)) {\r
+                                       url = "http://www.television.co.jp/digitalguide/guide24.php?range=24&type=bs&channel="+pl.CenterId;\r
+                               }\r
+                               else {\r
+                                       url = "http://www.television.co.jp/digitalguide/guide24.php?area_pref_key="+pl.Area+"&range=24&type="+pl.Type+"&channel="+pl.CenterId;\r
+                               }\r
+                               \r
+                               // Web番組表の読み出し\r
+                               response = webToBuffer(url, thisEncoding, true);\r
+                               \r
+                               // キャッシュファイルの保存\r
+                               if ( ! CommonUtils.write2file(progCacheFile, response) ) {\r
+                                       reportProgress(ERRID+"番組表(キャッシュ)の保存に失敗しました: ("+counter+"/"+counterMax+") "+pl.Center+"    "+progCacheFile);\r
+                               }\r
+                               \r
+                               reportProgress(MSGID+"(オンライン)を取得しました: ("+counter+"/"+counterMax+") "+pl.Center+"    "+url);\r
+                       }\r
+                       else if (f.exists()) {\r
+                               // キャッシュファイルの読み込み\r
+                               response = CommonUtils.read4file(progCacheFile, true);\r
+                               if ( response == null ) {\r
+                                       reportProgress(ERRID+"番組表(キャッシュ)の取得に失敗しました: ("+counter+"/"+counterMax+") "+pl.Center+"    "+progCacheFile);\r
+                               }\r
+                               reportProgress(MSGID+"番組表(キャッシュ)を取得しました: ("+counter+"/"+counterMax+") "+pl.Center+"    "+progCacheFile);\r
+                       }\r
+                       else {\r
+                               reportProgress(ERRID+"番組表(キャッシュ)がみつかりません: ("+counter+"/"+counterMax+") "+pl.Center+"    "+progCacheFile);\r
+                               return;\r
+                       }\r
+\r
+                       // 日付リストの追加\r
+                       getDateList(pl, response);\r
+                       \r
+                       // 番組リストの追加\r
+                       getPrograms(pl, response);\r
+                       \r
+                       // 日付の調整\r
+                       setAccurateDate(pl.pdate);\r
+               }\r
+               catch (Exception e) {\r
+                       reportProgress(ERRID+"番組表の取得で例外が発生しました: "+e.toString());\r
+                       e.printStackTrace();\r
+               }\r
+       }\r
+       \r
+       //\r
+       private void getDateList(ProgList pl, String response) {\r
+               // 日付の処理\r
+               Matcher ma = Pattern.compile("<tr class=\"head\">(.+?)</tr>").matcher(response);\r
+               if ( ma.find() ) {\r
+                       Matcher mb = Pattern.compile(">(\\d+)/(\\d+)((.))</a>").matcher(ma.group(1));\r
+                       while ( mb.find() ) {\r
+                               ProgDateList cl = new ProgDateList();\r
+                               cl.row = 0;\r
+                               \r
+                               int month = Integer.valueOf(mb.group(1));\r
+                               int day = Integer.valueOf(mb.group(2));\r
+                               int wday = CommonUtils.getWday(mb.group(3));\r
+                               cl.Date = CommonUtils.getDateByMD(month, day, wday, true);\r
+                               \r
+                               pl.pdate.add(cl);\r
+                       }\r
+                       \r
+                       // 8日目は取得できない\r
+               }\r
+       }\r
+       \r
+       //\r
+       private void getPrograms(ProgList pl, String src) {\r
+\r
+               ArrayList<ProgDateList> pdate = pl.pdate;\r
+               \r
+               HashMap<String,ProgOption> marks = new HashMap<String,ProgOption>();\r
+               marks.put("/function-50.", ProgOption.FIRST);\r
+               marks.put("/function-201.", ProgOption.BILINGUAL);\r
+               marks.put("/function-210.", ProgOption.MULTIVOICE);\r
+               marks.put("/function-401.", ProgOption.STANDIN);\r
+               marks.put("/function-501.", ProgOption.SUBTITLE);\r
+               marks.put("/function-601.", ProgOption.DATA);\r
+               marks.put("/function-711.", ProgOption.PV);\r
+               marks.put("/function-nama.", ProgOption.LIVE);\r
+               \r
+               Matcher ma = Pattern.compile("<td class=\"program (category-(\\d*?)|overhead)\" rowspan=\"(\\d+?)\">(.+?)</td>").matcher(src);\r
+               while ( ma.find() ) {\r
+\r
+                       ProgDetailList pdl = new ProgDetailList();\r
+                       \r
+                       // 番組長\r
+                       pdl.length = Integer.valueOf(ma.group(3));\r
+                       \r
+                       // 挿入位置決定\r
+                       int col = -1;\r
+                       int rowMin = 9999;\r
+                       for ( int i=0; i<pdate.size(); i++ ) {\r
+                               if (pdate.get(i).row < rowMin) {\r
+                                       col = i;\r
+                                       rowMin = pdate.get(i).row;\r
+                               }\r
+                       }\r
+                       if ( col == -1 ) {\r
+                               System.err.println(ERRID+"番組情報の挿入先がみつかりません");\r
+                               return;\r
+                       }\r
+                       \r
+                       pdate.get(col).pdetail.add(pdl);\r
+                       pdate.get(col).row += pdl.length;\r
+                       \r
+                       // 詳細設定\r
+                       if ( ma.group(2) == null ) {\r
+                               pdl.title = pdl.splitted_title = "番組情報がありません";\r
+                       }\r
+                       else {\r
+                               // 番組ジャンル\r
+                               if (ma.group(2).equals("0")) {\r
+                                       pdl.genre = ProgGenre.NEWS;\r
+                               }\r
+                               else if (ma.group(2).equals("1")) {\r
+                                       pdl.genre = ProgGenre.SPORTS;\r
+                               }\r
+                               else if (ma.group(2).equals("2")) {\r
+                                       pdl.genre = ProgGenre.VARIETYSHOW;\r
+                               }\r
+                               else if (ma.group(2).equals("3")) {\r
+                                       pdl.genre = ProgGenre.DORAMA;\r
+                               }\r
+                               else if (ma.group(2).equals("4")) {\r
+                                       pdl.genre = ProgGenre.MUSIC;\r
+                               }\r
+                               else if (ma.group(2).equals("5")) {\r
+                                       pdl.genre = ProgGenre.VARIETY;\r
+                               }\r
+                               else if (ma.group(2).equals("6")) {\r
+                                       pdl.genre = ProgGenre.MOVIE;\r
+                               }\r
+                               else if (ma.group(2).equals("7")) {\r
+                                       pdl.genre = ProgGenre.ANIME;\r
+                               }\r
+                               else if (ma.group(2).equals("8")) {\r
+                                       pdl.genre = ProgGenre.DOCUMENTARY;\r
+                               }\r
+                               else if (ma.group(2).equals("9")) {\r
+                                       pdl.genre = ProgGenre.THEATER;\r
+                               }\r
+                               else if (ma.group(2).equals("10")) {\r
+                                       pdl.genre = ProgGenre.HOBBY;\r
+                               }\r
+                               else if (ma.group(2).equals("11")) {\r
+                                       pdl.genre = ProgGenre.WELFARE;\r
+                               }\r
+                               else if (ma.group(2).equals("14")) {\r
+                                       // 拡張エリア - 広帯域CS デジタル放送拡張用情報  映画 - 邦画  \r
+                                       pdl.genre = ProgGenre.MOVIE;\r
+                               }\r
+                               else if (ma.group(2).equals("15")) {\r
+                                       pdl.genre = ProgGenre.NOGENRE;\r
+                               }\r
+                               else {\r
+                                       if ( ma.group(2) != null && ma.group(2).length() > 0 ) {\r
+                                               System.out.println(DBGID+"未定義のジャンルコード: "+ma.group(2)+", "+ma.group(4));\r
+                                       }\r
+                                       pdl.genre = ProgGenre.NOGENRE;\r
+                               }\r
+                               \r
+                               // 番組タイトル+α\r
+                               // 1 : dummy\r
+                               // 2 : id-a\r
+                               // 3 : id-b\r
+                               // 4 : title\r
+                               \r
+                               // マークを取り出してみる\r
+                               Matcher mb = Pattern.compile("(/function-.+?\\.)").matcher(ma.group(4));\r
+                               while ( mb.find() ) {\r
+                                       for ( String mark : marks.keySet() ) {\r
+                                               if ( mb.group(1).equals(mark) ) {\r
+                                                       pdl.addOption(marks.get(mark));\r
+                                                       break;\r
+                                               }\r
+                                       }\r
+                               }\r
+                               \r
+                               // 番組詳細\r
+                               {\r
+                                       mb = Pattern.compile("<div class=\"subtitle\">(.*?)</div>").matcher(ma.group(4));\r
+                                       if ( mb.find() ) {\r
+                                               pdl.detail = mb.group(1);\r
+                                       }\r
+                                       \r
+                                       mb = Pattern.compile("<div name=\"programcontent\" class=\"content\">((【.+?】)*)(.*?)</div>").matcher(ma.group(4));\r
+                                       if ( mb.find() ) {\r
+                                               // 番組詳細の「先頭」に新番組マークなどがある場合がある。ない場合もある。\r
+                                               Matcher mc = Pattern.compile("(【.+?】)").matcher(mb.group(1));\r
+                                               while (mc.find()) {\r
+                                                       if (mc.group(1).equals("【新】")) {\r
+                                                               pdl.flag = ProgFlags.NEW;\r
+                                                       }\r
+                                                       else if (mc.group(1).equals("【終】")) {\r
+                                                               pdl.flag = ProgFlags.LAST;\r
+                                                       }\r
+                                                       else if (mc.group(1).equals("【サブタイトル】")) {\r
+                                                               // 何もしない\r
+                                                       }\r
+                                                       else {\r
+                                                               // その他の不明な【?】はそのままにする\r
+                                                               pdl.detail += mc.group(1);\r
+                                                       }\r
+                                               }\r
+                                               // マーク以降\r
+                                               pdl.detail += mb.group(3);\r
+                                       }\r
+                                       \r
+                                       // 記号を文字化する\r
+                                       pdl.detail = Pattern.compile("(<img .+? alt=\"(.+?)\"/>)",Pattern.DOTALL).matcher(pdl.detail).replaceAll("$2").trim();\r
+                               }\r
+\r
+                               // タイトル・番組詳細ページへのリンク\r
+                               mb = Pattern.compile("<a href=\"(javascript:detail.show\\('(.+?)'\\)|detail\\.php\\?id=(.+?)&ref=guide24)\">[\\s,]*(.+?)[\\s,]*</a>").matcher(ma.group(4));\r
+                               if ( mb.find() ) {\r
+                                       pdl.title =  CommonUtils.unEscape(mb.group(4));\r
+                                       Matcher mc = Pattern.compile("(<img src=\"(.+?)\" alt=\"(.+?)\"/>)").matcher(pdl.title);\r
+                                       while ( mc.find() ) {\r
+                                               pdl.title = pdl.title.replace(mc.group(1),mc.group(3));\r
+                                       }\r
+                                       \r
+                                       if (mb.group(2) != null) {\r
+                                               pdl.link = "http://www.television.co.jp/digitalguide/detail.php?id="+mb.group(2);\r
+                                       }\r
+                                       else if (mb.group(3) != null) {\r
+                                               pdl.link = "http://www.television.co.jp/programlist/detail.php?id="+mb.group(3)+"&ref=guide";\r
+                                       }\r
+                                       else {\r
+                                               pdl.link = "";\r
+                                       }\r
+                               }\r
+\r
+                               // タイトルから各種フラグを分離する\r
+                               doSplitFlags(pdl, nf);\r
+\r
+                               // サブタイトル分離\r
+                               doSplitSubtitle(pdl);\r
+                               \r
+                               if ( ma.group(4).indexOf("\"再放送\"") >= 0 ) {\r
+                                       pdl.addOption(ProgOption.REPEAT);\r
+                               }\r
+                               \r
+                               if ( ma.group(4).indexOf("\"生放送\"") >= 0 ) {\r
+                                       pdl.addOption(ProgOption.LIVE);\r
+                               }\r
+                               \r
+                               if ( ma.group(4).indexOf("\"無料放送\"") >= 0 ) {\r
+                                       pdl.noscrumble = ProgScrumble.NOSCRUMBLE;\r
+                               }\r
+                               \r
+                               if ( ma.group(4).indexOf("function-a.") >= 0 ) {\r
+                                       pdl.flag = ProgFlags.NEW;\r
+                               }\r
+                               else if ( ma.group(4).indexOf("function-l.") >= 0 ) {\r
+                                       pdl.flag = ProgFlags.LAST;\r
+                               }\r
+                               \r
+                               if ( ma.group(4).indexOf("function-50.") >= 0 ) {\r
+                                       pdl.addOption(ProgOption.FIRST);\r
+                               }\r
+                               \r
+                               // 番組ID\r
+                               try {\r
+                                       int hdr = 0;\r
+                                       int chnum = 0;\r
+                                       if ( pl.CenterId.startsWith("CS") ) {\r
+                                               chnum = Integer.valueOf(pl.CenterId.substring(2));\r
+                                               hdr = 7;\r
+                                       }\r
+                                       else if ( pl.CenterId.startsWith("OTD") ) {\r
+                                               chnum = Integer.valueOf(pl.CenterId.substring(3));\r
+                                               hdr = ( chnum < 1024 ) ? (4) : (0x7FFF);\r
+                                       }\r
+                                       if ( chnum > 0 ) {\r
+                                               ContentIdDIMORA.decodeChId(String.format("%04X%04X%04X", hdr,0,chnum));\r
+                                               pdl.progid = ContentIdDIMORA.getContentId(0,"");\r
+                                       }\r
+                               }\r
+                               catch (NumberFormatException e) {\r
+                                       System.err.println("[ERROR] invalid chcode format "+pl.Center+" "+pl.CenterId+" "+e.toString());\r
+                               }\r
+                               \r
+                               // 開始・終了時刻\r
+                               mb = Pattern.compile("<b>(\\d+):(\\d+)</b>").matcher(ma.group(4));\r
+                               if ( mb.find() ) {\r
+                                       GregorianCalendar c = new GregorianCalendar(Locale.JAPAN);\r
+                                       \r
+                                       int ahh = Integer.valueOf(mb.group(1));\r
+                                       int amm = Integer.valueOf(mb.group(2));\r
+                                       pdl.start = String.format("%02d:%02d", ahh, amm);\r
+                                       \r
+                                       c.set(Calendar.HOUR_OF_DAY, ahh);\r
+                                       c.set(Calendar.MINUTE, amm);\r
+                                       c.add(Calendar.MINUTE,pdl.length);\r
+                                       pdl.end = String.format("%02d:%02d", c.get(Calendar.HOUR_OF_DAY), c.get(Calendar.MINUTE));\r
+                               }\r
+                               \r
+                               //\r
+                               pdl.extension = false;\r
+                               //pdl.flag = ProgFlags.NOFLAG;\r
+                               pdl.nosyobo = false;\r
+                       }\r
+               }\r
+       }\r
+       \r
+       \r
+       /*******************************************************************************\r
+        * 地域情報を取得する\r
+        ******************************************************************************/\r
+       \r
+       // 普通は東京\r
+       @Override\r
+       public String getDefaultArea() {return "東京";}\r
+       \r
+       //\r
+       public void loadAreaCode() {\r
+               \r
+               // 設定ファイルが存在していればファイルから\r
+               File f = new File(getAreaSelectedFile());\r
+               if (f.exists() == true) {\r
+                       @SuppressWarnings("unchecked")\r
+                       ArrayList<AreaCode> tmp = (ArrayList<AreaCode>) CommonUtils.readXML(getAreaSelectedFile());\r
+                       if ( tmp != null ) {\r
+                               \r
+                               aclist = tmp;\r
+                               \r
+                               // 後方互換\r
+                               {\r
+                                       boolean bsflg = false;\r
+                                       for (AreaCode ac : aclist) {\r
+                                               if (ac.getCode().equals(bsCode)) {\r
+                                                       bsflg = true;\r
+                                                       break;\r
+                                               }\r
+                                       }\r
+                                       if ( ! bsflg) {\r
+                                               AreaCode ac = new AreaCode();\r
+                                               ac.setArea("BS");\r
+                                               ac.setCode(bsCode);\r
+                                               aclist.add(ac);\r
+                                               \r
+                                               saveAreaCode();\r
+                                       }\r
+                               }\r
+                               \r
+                               System.out.println("地域リストを読み込みました: "+getAreaSelectedFile());\r
+                               return;\r
+                       }\r
+                       else {\r
+                               System.err.println("地域リストの読み込みに失敗しました: "+getAreaSelectedFile());\r
+                       }\r
+               }\r
+\r
+               // 地域一覧の作成\r
+               ArrayList<AreaCode> newaclist = new ArrayList<AreaCode>();\r
+               \r
+               // 存在していなければWeb上から\r
+               String uri = "http://www.television.co.jp/digitalguide/map.html";\r
+               String response = webToBuffer(uri,thisEncoding,true);\r
+               if ( response == null ) {\r
+                       System.out.println("地域情報の取得に失敗しました: "+uri);\r
+                       return;\r
+               }\r
+               \r
+               {\r
+                       // 北海道\r
+                       Matcher ma = Pattern.compile("<option value=\"([^\"]+?)\">(.+?)</option>").matcher(response);\r
+                       while (ma.find()) {\r
+                               AreaCode ac = new AreaCode();\r
+                               ac.setArea(ma.group(2));\r
+                               ac.setCode(ma.group(1));\r
+                               newaclist.add(ac);\r
+                       }\r
+                       \r
+                       // 北海道以外\r
+                       ma = Pattern.compile("\"javascript:setPref\\('(.+?)'\\)\">(.+?)</a>").matcher(response);\r
+                       while (ma.find()) {\r
+                               AreaCode ac = new AreaCode();\r
+                               ac.setArea(ma.group(2));\r
+                               ac.setCode(ma.group(1));\r
+                               newaclist.add(ac);\r
+                       }\r
+               }\r
+               \r
+               if ( newaclist.size() == 0 ) {\r
+                       System.err.println(ERRID+"地域一覧の取得結果が0件だったため情報を更新しません");\r
+                       return;\r
+               }\r
+               \r
+               {\r
+                       {\r
+                               AreaCode ac = new AreaCode();\r
+                               ac.setArea("全国");\r
+                               ac.setCode(allCode);\r
+                               newaclist.add(0,ac);\r
+                       }\r
+                       {\r
+                               AreaCode ac = new AreaCode();\r
+                               ac.setArea("BS");\r
+                               ac.setCode(bsCode);\r
+                               newaclist.add(ac);\r
+                       }\r
+               }\r
+               \r
+               aclist = newaclist;\r
+               saveAreaCode();\r
+       }\r
+       \r
+\r
+       /*******************************************************************************\r
+        * 放送局情報を取得する\r
+        ******************************************************************************/\r
+\r
+       // 設定ファイルがなければWebから取得\r
+       public void loadCenter(String code, boolean force) {\r
+               \r
+               if ( code == null ) {\r
+                       System.out.println(ERRID+"地域コードがnullです.");\r
+                       return;\r
+               }\r
+               \r
+               String centerListFile = getCenterListFile(getTVProgramId(), code);\r
+               \r
+               if (force) {\r
+                       File f = new File(centerListFile);\r
+                       f.delete();\r
+               }\r
+               \r
+               File f = new File(centerListFile);\r
+               if (f.exists() == true) {\r
+                       @SuppressWarnings("unchecked")\r
+                       ArrayList<Center> tmp = (ArrayList<Center>) CommonUtils.readXML(centerListFile);\r
+                       if ( tmp != null ) {\r
+                               \r
+                               crlist = tmp;\r
+                               attachChFilters();      // 放送局名変換\r
+                               \r
+                               // 後方互換\r
+                               for (Center c : crlist) {\r
+                                       if ( c.getType().equals(bsCode) && ! c.getAreaCode().equals(bsCode)) {\r
+                                               c.setAreaCode(bsCode);\r
+                                       }\r
+                               }\r
+\r
+                               System.out.println("放送局リストを読み込みました: "+centerListFile);\r
+                               return;\r
+                       }\r
+                       else {\r
+                               System.err.println("放送局リストの読み込みに失敗しました: "+centerListFile);\r
+                       }\r
+               }\r
+               \r
+               // Web上から放送局の一覧を取得する\r
+               ArrayList<Center> newcrlist = new ArrayList<Center>();\r
+               \r
+               int cntMax = ((code.equals(allCode))?(aclist.size()-2):(1)) + 1;\r
+               int cnt = 1;\r
+               \r
+               // 地上波\r
+               if (code.equals(allCode)) {\r
+                       for (AreaCode ac : aclist) {\r
+                               if ( ! ac.getCode().equals(allCode) && ! ac.getCode().equals(bsCode)) {\r
+                                       String url = "http://www.television.co.jp/digitalguide/guide.php?type=tv&area_pref_key="+ac.getCode();\r
+                                       if ( _loadCenter(newcrlist, ac.getCode(),"tv", url) ) {\r
+                                               reportProgress("放送局情報を取得しました: ("+cnt+"/"+cntMax+") "+url);\r
+                                       }\r
+                                       cnt++;\r
+                               }\r
+                       }\r
+               }\r
+               else {\r
+                       String url = "http://www.television.co.jp/digitalguide/guide.php?type=tv&area_pref_key="+code;\r
+                       if ( _loadCenter(newcrlist, code,"tv",url) ) {\r
+                               reportProgress("放送局情報を取得しました: ("+cnt+"/"+cntMax+") "+url);\r
+                       }\r
+                       cnt++;\r
+               }\r
+               \r
+               // BS\r
+               {\r
+                       String url = "http://www.television.co.jp/digitalguide/guide.php?type=bs";\r
+                       if ( _loadCenter(newcrlist, bsCode,bsCode,url) ) {\r
+                               reportProgress("放送局情報を取得しました: ("+cnt+"/"+cntMax+") "+url);\r
+                       }\r
+                       cnt++;\r
+               }\r
+               \r
+               if ( newcrlist.size() == 0 ) {\r
+                       System.err.println(ERRID+"放送局情報の取得結果が0件だったため情報を更新しません");\r
+                       return;\r
+               }\r
+               \r
+               crlist = newcrlist;\r
+               attachChFilters();      // 放送局名変換\r
+               saveCenter();           // 保存\r
+       }\r
+       \r
+       protected boolean _loadCenter(ArrayList<Center> newcrlist, String code, String type, String uri) {\r
+               \r
+               String response = webToBuffer(uri,thisEncoding,true);\r
+               if ( response == null ) {\r
+                       System.err.println("放送局情報の取得に失敗しました: "+uri);\r
+                       return false;\r
+               }\r
+               \r
+               // 局名リストに追加する\r
+               \r
+               Matcher ma = Pattern.compile("<td><select(.+?)</select>").matcher(response);\r
+               if (ma.find()) {\r
+                       Matcher mb = Pattern.compile("<option value=\"([^\"]+?)\">\\s*(&nbsp;)*(.+?)\\s*</option>").matcher(ma.group(1));\r
+                       while (mb.find()) {\r
+                               String centerName = CommonUtils.unEscape(mb.group(3));\r
+                               String centerId = mb.group(1);\r
+                               \r
+                               // NHK総合・NHK教育\r
+                               if ( type.equals("tv") ) {\r
+                                       Matcher mc = Pattern.compile("^NHK(総合|Eテレ)(\\d+)・?(.+)$").matcher(centerName);\r
+                                       if ( mc.find() ) {\r
+                                               String prefix = "";\r
+                                               if ( mc.group(1).equals("総合") ) {\r
+                                                       prefix = "NHK総合";\r
+                                               }\r
+                                               else if ( mc.group(1).equals("Eテレ") ) {\r
+                                                       prefix = "NHK Eテレ";\r
+                                               }\r
+                                               else {\r
+                                                       prefix = "NHK"+mc.group(1);\r
+                                               }\r
+                                               if ( mc.group(2).equals("1") ) {\r
+                                                       centerName = prefix+"・"+mc.group(3);\r
+                                               }\r
+                                               else {\r
+                                                       centerName = prefix+mc.group(2)+"・"+mc.group(3);\r
+                                               }\r
+                                       }\r
+                               }\r
+                               \r
+                               Center cr = new Center();\r
+                               cr.setAreaCode(code);\r
+                               cr.setCenterOrig(centerName);\r
+                               cr.setLink(centerId);\r
+                               cr.setType(type);\r
+                               cr.setEnabled(true);\r
+                               newcrlist.add(cr);\r
+                       }\r
+               }\r
+               \r
+               return true;\r
+       }\r
+       \r
+}\r
diff --git a/TinyBannavi/src/tainavi/ProgDateList.java b/TinyBannavi/src/tainavi/ProgDateList.java
new file mode 100644 (file)
index 0000000..93f9d52
--- /dev/null
@@ -0,0 +1,15 @@
+package tainavi;\r
+\r
+import java.util.ArrayList;\r
+\r
+public class ProgDateList {\r
+       String Date;\r
+       int row;\r
+       ArrayList<ProgDetailList> pdetail;\r
+       \r
+       public ProgDateList() {\r
+               Date = "";\r
+               row = 0;\r
+               pdetail = new ArrayList<ProgDetailList>();\r
+       }\r
+}\r
diff --git a/TinyBannavi/src/tainavi/ProgDetailList.java b/TinyBannavi/src/tainavi/ProgDetailList.java
new file mode 100644 (file)
index 0000000..13985e2
--- /dev/null
@@ -0,0 +1,675 @@
+package tainavi;\r
+\r
+import java.util.ArrayList;\r
+import java.util.GregorianCalendar;\r
+\r
+import tainavi.TVProgram.ProgFlags;\r
+import tainavi.TVProgram.ProgGenre;\r
+import tainavi.TVProgram.ProgOption;\r
+import tainavi.TVProgram.ProgScrumble;\r
+import tainavi.TVProgram.ProgSubgenre;\r
+import tainavi.TVProgram.ProgSubtype;\r
+import tainavi.TVProgram.ProgType;\r
+\r
+/**\r
+ * 番組の個々の詳細情報です\r
+ */\r
+public class ProgDetailList implements Cloneable {\r
+       \r
+       /*******************************************************************************\r
+        * メンバ\r
+        ******************************************************************************/\r
+       \r
+       /*\r
+        *  ファイルに保存されるもの\r
+        */\r
+       \r
+       String title = "";                              // 分離前タイトル\r
+       String detail = "";                             // 修正前タイトル\r
+       protected String addedDetail = "";\r
+       \r
+       String startDateTime = "";\r
+       String endDateTime = "";\r
+       int length = 0;                                 // 放送時間(05:00を基準とした表示上の長さ)\r
+       \r
+       String link = "";\r
+       \r
+       // ジャンル\r
+       ProgGenre genre = ProgGenre.NOGENRE;\r
+       ProgSubgenre subgenre = ProgSubgenre.NOGENRE_ETC;\r
+       \r
+       // フラグ・オプション関連\r
+       boolean extension = false;                                              // 延長注意\r
+       ProgFlags flag = ProgFlags.NOFLAG;                              // 新番組・最終回\r
+       ProgScrumble noscrumble = ProgScrumble.NONE;    // 有料放送だけ別枠になってしまった!\r
+       protected ArrayList<ProgOption> option = new ArrayList<ProgOption>();\r
+       \r
+       // 複数ジャンル対応\r
+       ArrayList<ProgGenre> genrelist = null;\r
+       ArrayList<ProgSubgenre> subgenrelist = null;\r
+\r
+       /*\r
+        *  ファイルには保存されないもの\r
+        */\r
+       \r
+       /**\r
+        * <P>{@link ProgDateList#Date}が05-29時を基準とした日付を持つのに対して\r
+        * <P>accurateDateは実際の日付である\r
+        * <P>(startDateTimeの日付と同じ、ただし曜日文字がつく)\r
+        */\r
+       \r
+       String accurateDate = "";\r
+       String start = "";                              // hh:mm\r
+       String end = "";                                // hh:mm\r
+       int recmin = 0;                                 // 番組長(実際の長さ)\r
+       \r
+       String progid = "";                             // 番組ID\r
+       \r
+       boolean nosyobo = false;                // しょぼのぼっち判定\r
+       boolean marked = false;                 // 検索に引っかかったマーク \r
+       boolean newarrival = false;             // 新着だなぁ\r
+       boolean modified = false;               // 詳細が更新された\r
+       boolean nonrepeated = false;    // リピート放送の初回かな?\r
+       boolean showinstandby = false;  // 予約待機への表示を行うか?\r
+\r
+       ProgType type = null;                   // Web番組表か、しょぼかるか、過去ログか\r
+       ProgSubtype subtype = null;             // サブタイプ\r
+       String center = "";                             // 放送局名\r
+       String extension_mark = "";             // ★延長注意★\r
+       String prefix_mark = "";                // (移)とか\r
+       String newlast_mark = "";               // [新]とか\r
+       String postfix_mark = "";               // [再]とか\r
+       boolean dontoverlapdown = false;// 終了時間を短縮してはだめ\r
+\r
+       String splitted_title = "";             // 分離済みタイトル→これはtitleへのポインタ\r
+       String splitted_titlePop = "";  // 分離済みタイトルPOP→これはtitlePopへのポインタ\r
+       String splitted_detail = "";    // 分離済み番組詳細→detailはこれへのポインタにするよ\r
+       \r
+       SearchKey dynKey = null;                        // 動的検索用\r
+       String dynMatched = null;                       // 動的検索用\r
+\r
+       // 検索高速化用のデータ\r
+       \r
+       /**\r
+        * キーワード検索で使うよ\r
+        */\r
+       String titlePop = "";\r
+       \r
+       /**\r
+        * キーワード検索で使うよ\r
+        */\r
+       String detailPop = "";\r
+       \r
+       /**\r
+        * 番組追跡で使うよ\r
+        */\r
+       ArrayList<String> SearchStrKeys = null;\r
+       \r
+       /*******************************************************************************\r
+        * NGワード処理\r
+        ******************************************************************************/\r
+       \r
+       public void abon() {\r
+               start = "";\r
+               title = "番組情報がありません";\r
+               detail = "";\r
+               addedDetail = "";\r
+               \r
+               extension = false;\r
+               flag = null;\r
+               option = new ArrayList<ProgOption>();\r
+               noscrumble = ProgScrumble.NONE;\r
+               \r
+               genre = ProgGenre.NOGENRE;\r
+               subgenre = ProgSubgenre.NOGENRE_ETC;\r
+               genrelist = null;\r
+               subgenrelist = null;\r
+\r
+               progid = "";\r
+               nosyobo = false;\r
+               marked = false; \r
+               newarrival = false;\r
+               modified = false;\r
+               nonrepeated = false;\r
+               showinstandby = false;\r
+\r
+               titlePop = "";\r
+               detailPop = "";\r
+               SearchStrKeys = new ArrayList<String>();\r
+       }\r
+       \r
+       /*******************************************************************************\r
+        * 過去ログ処理高速化計画\r
+        ******************************************************************************/\r
+       \r
+       public boolean isEqualsGenre(ProgGenre mGenre, ProgSubgenre mSubgenre) {\r
+               if ( mGenre == null && mSubgenre == null ) {\r
+                       return false;\r
+               }\r
+               if ( this.genrelist == null ) {\r
+                       // 従来の比較\r
+                       if ( mGenre == null || this.genre == mGenre ) {\r
+                               if ( mSubgenre == null ) {\r
+                                       return true;\r
+                               }\r
+                               else {\r
+                                       if ( this.subgenre == mSubgenre ) {\r
+                                               return true;\r
+                                       }\r
+                               }\r
+                       }\r
+               }\r
+               else {\r
+                       // マルチジャンル対応時の比較\r
+                       for ( int n=0; n<this.genrelist.size(); n++ ) {\r
+                               if ( mGenre == null || this.genrelist.get(n) == mGenre ) {\r
+                                       if ( mSubgenre == null ) {\r
+                                               return true;\r
+                                       }\r
+                                       else {\r
+                                               if ( this.subgenrelist.get(n) == mSubgenre ) {\r
+                                                       return true;\r
+                                               }\r
+                                       }\r
+                               }\r
+                       }\r
+               }\r
+               return false;\r
+       }\r
+\r
+       /*\r
+        * addedDetailは直接編集させない\r
+        */\r
+       \r
+       public String getAddedDetail() { return addedDetail; }\r
+       public void setAddedDetail(String s) { addedDetail = s; }\r
+\r
+       /*\r
+        * optionは直接編集させない\r
+        */\r
+       \r
+       public ArrayList<ProgOption> getOption() { return option; }\r
+       public boolean addOption(ProgOption opt) {\r
+               int i = getOptionIndex(opt);\r
+               if ( i == -1 ) {\r
+                       this.option.add(opt);\r
+                       return true;\r
+               }\r
+               return false;\r
+       }\r
+       public boolean removeOption(ProgOption opt) {\r
+               int i = getOptionIndex(opt);\r
+               if ( i >= 0 ) {\r
+                       this.option.remove(i);\r
+                       return true;\r
+               }\r
+               return false;\r
+       }\r
+       public boolean isOptionEnabled(ProgOption opt) {\r
+               return (getOptionIndex(opt)>=0);\r
+       }\r
+       private int getOptionIndex(ProgOption opt) {\r
+               int size=this.option.size();\r
+               for (int i=0; i<size; i++) {\r
+                       if (this.option.get(i) == opt) {\r
+                               return i;\r
+                       }\r
+               }\r
+               return -1;\r
+       }\r
+\r
+       public boolean setGenreStr() {\r
+               if (genrelist == null || genrelist.size() == 0) {\r
+                       return true;\r
+               }\r
+               \r
+               String s = GENREMARK;\r
+               for ( int n=0; n<genrelist.size(); n++ ) {\r
+                       s += genrelist.get(n).toString()+" - "+subgenrelist.get(n).toString()+"\n";\r
+               }\r
+               if ( addedDetail.indexOf(GENREMARK) == -1 ) {\r
+                       addedDetail = s+"\n"+addedDetail;\r
+               }\r
+               else {\r
+                       addedDetail = addedDetail.replace(GENREMARK+".+?\n\n", GENREMARK+s+"\n");\r
+               }\r
+               return true;\r
+       }\r
+       private static final String GENREMARK = "\n【ジャンル】\n";\r
+       \r
+       public boolean setContentIdStr() {\r
+               String pid = null;\r
+               if ( ContentIdEDCB.isValid(progid) ) {\r
+                       pid = ContentIdEDCB.stripMark(progid);\r
+               }\r
+               else if ( ContentIdREGZA.isValid(progid) ) {\r
+                       pid = ContentIdREGZA.stripMark(progid);\r
+               }\r
+               else if ( ContentIdDIMORA.isValid(progid) ) {\r
+                       pid = ContentIdDIMORA.stripMark(progid);\r
+               }\r
+               if ( pid != null )\r
+               {\r
+                       if ( addedDetail.indexOf(CIDMARK) == -1 ) {\r
+                               addedDetail += CIDMARK+pid+"\n";\r
+                       }\r
+                       else {\r
+                               addedDetail = addedDetail.replace(CIDMARK+".+?\n", pid+"\n");\r
+                       }\r
+                       return true;\r
+               }\r
+               return false;\r
+       }\r
+       \r
+       private static final String CIDMARK = "\n【ID】\nid=";\r
+       \r
+       /*******************************************************************************\r
+        * 過去ログ処理高速化計画\r
+        ******************************************************************************/\r
+\r
+       /**\r
+        * \r
+        */\r
+       public static enum WrHeader {\r
+               \r
+               // 順番をかえなければ、どこに追加してもいい\r
+               \r
+               START           ( "$ST$", true ),\r
+               END                     ( "$ED$", true ),\r
+               LENGTH          ( "$LN$", true ),\r
+               GENRE           ( "$GE$", true ),\r
+               GENRELIST       ( "$GL$", true ),\r
+               EXTENTION       ( "$EX$", true ),\r
+               SCRUMBLED       ( "$SC$", true ),\r
+               FLAG            ( "$FL$", true ),\r
+               OPTION          ( "$OP$", true ),\r
+               LINK            ( "$LK$", true ),\r
+               ID                      ( "$ID$", true ),\r
+               \r
+               TITLE           ( "$TI$", true ),\r
+               DETAIL          ( "$DT$", true ),\r
+               ADDED           ( "$AD$", true ),\r
+               \r
+               // ここから下は別の領域なので追加はNG\r
+               \r
+               BEND            ( "$DE#YY$", false ),   // 項目ごとのフッタ\r
+               STARTMARK       ( "$DE#AA$", false ),   // ヘッダ\r
+               ENDMARK         ( "$DE#ZZ$", false ),   // フッタ\r
+               \r
+               ;\r
+               \r
+               private String hdr;\r
+               private boolean marker;\r
+               \r
+               private WrHeader(String hdr, boolean marker) {\r
+                       this.hdr = hdr;\r
+                       this.marker = marker;\r
+               }\r
+               \r
+               // ここはtoString()をOverrideしてよい\r
+               @Override\r
+               public String toString() { return hdr; }\r
+       }\r
+       \r
+       // 区切り文字\r
+       private static final String S_CR = "\n";\r
+       private static final String S_OPT = ";";\r
+       \r
+       private void addHeader(StringBuilder sb, WrHeader header) {\r
+               sb.append(header.toString());\r
+       }\r
+       private void addBody(StringBuilder sb, String body) {\r
+               sb.append(body);\r
+       }\r
+       private void addBodyEnd(StringBuilder sb) {\r
+               sb.append(WrHeader.BEND);\r
+       }\r
+       \r
+       /**\r
+        * @see #ProgDetailList(String)\r
+        */\r
+       public String toString() {\r
+               \r
+               StringBuilder sb = new StringBuilder();\r
+               \r
+               sb.append(WrHeader.STARTMARK);\r
+               sb.append(S_CR);\r
+               \r
+               for ( WrHeader hdr : WrHeader.values() )\r
+               {\r
+                       if ( ! hdr.marker )\r
+                       {\r
+                               break;\r
+                       }\r
+                       \r
+                       switch ( hdr ) {\r
+                       case TITLE:\r
+                               if ( this.title != null && this.title.length() > 0 ) {\r
+                                       addHeader(sb, hdr);\r
+                                       addBody(sb, this.title);\r
+                                       addBodyEnd(sb);\r
+                               }\r
+                               break;\r
+                       case DETAIL:\r
+                               if ( this.detail != null && this.detail.length() > 0 ) {\r
+                                       addHeader(sb, hdr);\r
+                                       addBody(sb, this.detail);\r
+                                       addBodyEnd(sb);\r
+                               }\r
+                               break;\r
+                       case ADDED: \r
+                               if ( this.addedDetail != null && this.addedDetail.length() > 0 ) {\r
+                                       addHeader(sb, hdr);\r
+                                       addBody(sb, this.addedDetail);\r
+                                       addBodyEnd(sb);\r
+                               }\r
+                               break;\r
+                       case START:\r
+                               if ( this.startDateTime != null && this.startDateTime.length() > 0 ) {\r
+                                       addHeader(sb, hdr);\r
+                                       addBody(sb, this.startDateTime);\r
+                                       addBodyEnd(sb);\r
+                               }\r
+                               break;\r
+                       case END:\r
+                               if ( this.endDateTime != null && this.endDateTime.length() > 0 ) {\r
+                                       addHeader(sb, hdr);\r
+                                       addBody(sb, this.endDateTime);\r
+                                       addBodyEnd(sb);\r
+                               }\r
+                               break;\r
+                       case LINK:\r
+                               if ( this.link != null && this.link.length() > 0 ) {\r
+                                       addHeader(sb, hdr);\r
+                                       addBody(sb, this.link);\r
+                                       addBodyEnd(sb);\r
+                               }\r
+                               break;\r
+                       case LENGTH:\r
+                               if ( this.length > 0 ) {\r
+                                       addHeader(sb, hdr);\r
+                                       addBody(sb, String.valueOf(this.length));\r
+                                       addBodyEnd(sb);\r
+                               }\r
+                               break;\r
+                       case GENRE:\r
+                               if ( this.genre != null && this.genre != ProgGenre.NOGENRE )\r
+                               {\r
+                                       addHeader(sb, hdr);\r
+                                       addBody(sb, this.genre.toIEPG());\r
+                                       \r
+                                       if ( this.subgenre != null )\r
+                                       {\r
+                                               addBody(sb, this.subgenre.toIEPG());\r
+                                       }\r
+                                       else\r
+                                       {\r
+                                               addBody(sb, ProgSubgenre.NOGENRE_ETC.toIEPG());\r
+                                       }\r
+                                       \r
+                                       addBodyEnd(sb);\r
+                               }\r
+                               break;\r
+                       case GENRELIST:\r
+                               if ( this.genrelist != null && this.genrelist.size() > 0 )\r
+                               {\r
+                                       addHeader(sb, hdr);\r
+                                       \r
+                                       int n = -1;\r
+                                       for ( ProgGenre g : this.genrelist )\r
+                                       {\r
+                                               ++n;\r
+                                               sb.append(g.toIEPG());\r
+                                               \r
+                                               if ( this.subgenrelist != null && this.subgenrelist.size() > n )\r
+                                               {\r
+                                                       addBody(sb, this.subgenrelist.get(n).toIEPG());\r
+                                               }\r
+                                               else\r
+                                               {\r
+                                                       sb.append(ProgSubgenre.NOGENRE_ETC);\r
+                                               }\r
+                                       }\r
+                                       \r
+                                       addBodyEnd(sb);\r
+                               }\r
+                               break;\r
+                       case EXTENTION:\r
+                               if ( this.extension )\r
+                               {\r
+                                       addHeader(sb, hdr);\r
+                                       // BODYはない\r
+                                       addBodyEnd(sb);\r
+                               }\r
+                               break;\r
+                       case SCRUMBLED:\r
+                               if ( this.noscrumble != null && this.noscrumble != ProgScrumble.NOSCRUMBLE )\r
+                               {\r
+                                       addHeader(sb, hdr);\r
+                                       // BODYはない\r
+                                       addBodyEnd(sb);\r
+                               }\r
+                               break;\r
+                       case FLAG:\r
+                               if ( this.flag != null && this.flag != ProgFlags.NOFLAG )\r
+                               {\r
+                                       addHeader(sb, hdr);\r
+                                       addBody(sb, this.flag.toString());\r
+                                       addBodyEnd(sb);\r
+                               }\r
+                               break;\r
+                       case OPTION:\r
+                               if ( this.option != null && this.option.size() > 0 )\r
+                               {\r
+                                       addHeader(sb, hdr);\r
+                                       \r
+                                       for ( ProgOption o : this.option )\r
+                                       {\r
+                                               addBody(sb, o.toString());\r
+                                               addBody(sb, S_OPT);\r
+                                       }\r
+                                       \r
+                                       addBodyEnd(sb);\r
+                               }\r
+                               break;\r
+                       case ID:\r
+                               if ( this.progid != null && this.progid.length() > 0 ) {\r
+                                       addHeader(sb, hdr);\r
+                                       addBody(sb, this.progid);\r
+                                       addBodyEnd(sb);\r
+                               }\r
+                               break;\r
+                       default:\r
+                               break;\r
+                       }\r
+               }               \r
+\r
+               sb.append(WrHeader.ENDMARK);\r
+               sb.append(S_CR);\r
+               \r
+               return sb.toString();\r
+       }\r
+       \r
+       /*******************************************************************************\r
+        * コンストラクタ2種\r
+        ******************************************************************************/\r
+       \r
+       /**\r
+        * toString()で生成した文字列を入力にして初期化された{@link ProgDetailList}を返します\r
+        * @param s 先頭の{@link WrHeader.STARTMARK}+"\n"とおしりの{@link WrHeader.ENDMARK}+"\n"は外しておいてください\r
+        */\r
+       public ProgDetailList(String s) {\r
+               \r
+               for ( WrHeader hdr : WrHeader.values() )\r
+               {\r
+                       if ( ! hdr.marker )\r
+                       {\r
+                               break;          // ! markerならもう終わり\r
+                       }\r
+                       \r
+                       if ( s == null || s.length() <= 0 )\r
+                       {\r
+                               break;          // なんか変\r
+                       }\r
+                       \r
+                       if ( ! s.startsWith(hdr.toString()) )\r
+                       {\r
+                               continue;       // 次へ\r
+                       }\r
+                       \r
+                       int newtail = s.indexOf(WrHeader.BEND.toString());\r
+                       if ( newtail == -1 )\r
+                       {\r
+                               break;          // なんか変\r
+                       }\r
+                       \r
+                       String body = s.substring(hdr.toString().length(),newtail);\r
+                       s = s.substring(newtail+WrHeader.BEND.toString().length());\r
+                       \r
+                       if ( body == null || body.length() == 0 )\r
+                       {\r
+                               continue;       // いらんわ\r
+                       }\r
+                       \r
+                       switch ( hdr ) {\r
+                       case TITLE:\r
+                               this.title = body;\r
+                               this.titlePop = TraceProgram.replacePop(this.title);\r
+                               this.SearchStrKeys = TraceProgram.splitKeys(this.titlePop);\r
+                               break;\r
+                       case DETAIL:\r
+                               this.detail = body;\r
+                               this.detailPop = TraceProgram.replacePop(this.detail);\r
+                               break;\r
+                       case ADDED:\r
+                               this.addedDetail = body;\r
+                               break;\r
+                       case START:\r
+                               GregorianCalendar ca = CommonUtils.getCalendar(body);\r
+                               if ( ca != null ) \r
+                               {\r
+                                       this.startDateTime = CommonUtils.getDateTime(ca);\r
+                                       this.accurateDate = CommonUtils.getDate(ca);\r
+                                       this.start = CommonUtils.getTime(ca);\r
+                               }\r
+                               break;\r
+                       case END:\r
+                               GregorianCalendar cz = CommonUtils.getCalendar(body);\r
+                               if ( cz != null ) \r
+                               {\r
+                                       this.endDateTime = CommonUtils.getDateTime(cz);\r
+                                       this.end = CommonUtils.getTime(cz);\r
+                                       this.recmin = CommonUtils.getRecMinVal(this.start,this.end);\r
+                               }\r
+                               break;\r
+                       case LINK:\r
+                               this.link = body;\r
+                               break;\r
+                       case LENGTH:\r
+                               try {\r
+                                       this.length = Integer.valueOf(body);\r
+                               }\r
+                               catch (NumberFormatException e) {\r
+                                       //\r
+                               }\r
+                               break;\r
+                       case GENRE:\r
+                               if ( body.length() >= 1 )\r
+                               {\r
+                                       this.genre = ProgGenre.getByIEPG(body.substring(0,1));\r
+                                       if ( this.genre == null )\r
+                                       {\r
+                                               this.genre = ProgGenre.NOGENRE;\r
+                                       }\r
+                                       \r
+                                       if ( body.length() >= 2 )\r
+                                       {\r
+                                               this.subgenre = ProgSubgenre.getByIEPG(this.genre, body.substring(1,2));\r
+                                       }\r
+                                       if ( this.subgenre == null )\r
+                                       {\r
+                                               this.subgenre = ProgSubgenre.NOGENRE_ETC;\r
+                                       }\r
+                               }\r
+                               break;\r
+                       case GENRELIST:\r
+                               while ( body.length() >= 2 )\r
+                               {\r
+                                       if ( this.genrelist == null )\r
+                                       {\r
+                                               this.genrelist = new ArrayList<ProgGenre>();\r
+                                               this.subgenrelist = new ArrayList<ProgSubgenre>(); \r
+                                       }\r
+                                       \r
+                                       ProgGenre g = ProgGenre.getByIEPG(body.substring(0,1));\r
+                                       if ( g == null )\r
+                                       {\r
+                                               g = ProgGenre.NOGENRE;\r
+                                       }\r
+                                       ProgSubgenre sg = ProgSubgenre.getByIEPG(g,body.substring(1,2));\r
+                                       if ( sg == null )\r
+                                       {\r
+                                               sg = ProgSubgenre.NOGENRE_ETC;\r
+                                       }\r
+                                       \r
+                                       this.genrelist.add(g);\r
+                                       this.subgenrelist.add(sg);\r
+                                       \r
+                                       body = body.substring(2);\r
+                               }\r
+                               break;\r
+                       case EXTENTION:\r
+                               this.extension = true;\r
+                               break;\r
+                       case SCRUMBLED:\r
+                               this.noscrumble = ProgScrumble.SCRUMBLED;\r
+                               break;\r
+                       case FLAG:\r
+                               for ( ProgFlags f : ProgFlags.values() )\r
+                               {\r
+                                       if ( f.toString().equals(body) )\r
+                                       {\r
+                                               this.flag = f;\r
+                                               break;\r
+                                       }\r
+                               }\r
+                               break;\r
+                       case OPTION:\r
+                               //this.option = new ArrayList<ProgOption>();    // いらない\r
+                               for ( String opt : body.split(S_OPT) )\r
+                               {\r
+                                       for ( ProgOption o : ProgOption.values() )\r
+                                       {\r
+                                               if ( o.toString().equals(opt) )\r
+                                               {\r
+                                                       this.option.add(o);\r
+                                                       break;\r
+                                               }\r
+                                       }\r
+                               }\r
+                               break;\r
+                       case ID:\r
+                               this.progid = body;\r
+                               break;\r
+                       default:\r
+                               break;\r
+                       }\r
+               }\r
+       }\r
+\r
+       /**\r
+        *  通常のコンストラクタ\r
+        */\r
+       public ProgDetailList() {\r
+       }\r
+\r
+       @Override\r
+       public ProgDetailList clone() {\r
+               try {\r
+                       return (ProgDetailList) super.clone();\r
+               } catch (CloneNotSupportedException e) {\r
+                       throw new InternalError(e.toString());\r
+               }\r
+       }\r
+\r
+}\r
diff --git a/TinyBannavi/src/tainavi/ProgList.java b/TinyBannavi/src/tainavi/ProgList.java
new file mode 100644 (file)
index 0000000..d9f4b21
--- /dev/null
@@ -0,0 +1,29 @@
+package tainavi;\r
+\r
+import java.awt.Color;\r
+import java.util.ArrayList;\r
+import tainavi.ProgDateList;\r
+\r
+public class ProgList {\r
+       String Area;\r
+       String SubArea;\r
+       String Type;\r
+       String Center;\r
+       String CenterId;\r
+       String ChId;\r
+       Color BgColor;\r
+       boolean enabled;\r
+       ArrayList<ProgDateList> pdate;\r
+       \r
+       public ProgList() {\r
+               Area = "";\r
+               SubArea = "";\r
+               Type = "";\r
+               Center = "";\r
+               CenterId = "";\r
+               ChId = "";\r
+               BgColor = new Color(180,180,180);\r
+               enabled = true;\r
+               pdate = new ArrayList<ProgDateList>();\r
+       }\r
+}\r
diff --git a/TinyBannavi/src/tainavi/RecordedInfo.java b/TinyBannavi/src/tainavi/RecordedInfo.java
new file mode 100644 (file)
index 0000000..f9e0600
--- /dev/null
@@ -0,0 +1,74 @@
+package tainavi;\r
+\r
+/**\r
+ * <P>録画結果の情報を保持します。\r
+ */\r
+public class RecordedInfo {\r
+\r
+       private String id;\r
+       \r
+       private String date="";\r
+       private String ahh="";\r
+       private String amm="";\r
+       private String zhh="";\r
+       private String zmm="";\r
+       private String channel;\r
+       private String ch_name;\r
+       private String ch_orig;\r
+       private String title;\r
+       private String detail;\r
+       private String result;\r
+\r
+       private int length;\r
+       private long size;\r
+       private int drop;\r
+       private int drop_mpeg;\r
+       private float sig_a;\r
+       private float sig_z;\r
+       \r
+       private boolean succeeded;\r
+\r
+       public String getId() { return id; }\r
+       public void setId(String s) { id=s; }\r
+       \r
+       public String getDate() { return date; }\r
+       public void setDate(String s) { date=s; }\r
+       public String getAhh() { return ahh; }\r
+       public void setAhh(String s) { ahh=s; }\r
+       public String getAmm() { return amm; }\r
+       public void setAmm(String s) { amm=s; }\r
+       public String getZhh() { return zhh; }\r
+       public void setZhh(String s) { zhh=s; }\r
+       public String getZmm() { return zmm; }\r
+       public void setZmm(String s) { zmm=s; }\r
+       public String getTitle() { return title; }\r
+       public void setTitle(String s) { title=s; }\r
+       public String getChannel() { return channel; }\r
+       public void setChannel(String s) { channel=s; }\r
+       public String getCh_name() { return ch_name; }\r
+       public void setCh_name(String s) { ch_name=s; }\r
+       public String getCh_orig() { return ch_orig; }\r
+       public void setCh_orig(String s) { ch_orig=s; }\r
+       public String getDetail() { return detail; }\r
+       public void setDetail(String s) { detail=s; }\r
+       public String getResult() { return result; }\r
+       public void setResult(String s) { result=s; }\r
+       \r
+       public int getLength() { return length; }\r
+       public void setLength(int n) { length=n; }\r
+       public long getSize() { return size; }\r
+       public void setSize(long l) { size=l; }\r
+       public int getDrop() { return drop; }\r
+       public void setDrop(int n) { drop=n; }\r
+       public int getDrop_mpeg() { return drop_mpeg; }\r
+       public void setDrop_mpeg(int n) { drop_mpeg=n; }\r
+       public float getSig_a() { return sig_a; }\r
+       public void setSig_a(float n) { sig_a=n; }\r
+       public float getSig_z() { return sig_z; }\r
+       public void setSig_z(float n) { sig_z=n; }\r
+       \r
+       public boolean getSucceeded() { return succeeded; }\r
+       public void setSucceeded(boolean b) { succeeded = b; }\r
+       \r
+       \r
+}\r
diff --git a/TinyBannavi/src/tainavi/RecorderInfo.java b/TinyBannavi/src/tainavi/RecorderInfo.java
new file mode 100644 (file)
index 0000000..e3da76d
--- /dev/null
@@ -0,0 +1,177 @@
+package tainavi;\r
+\r
+import java.awt.Color;\r
+import java.util.ArrayList;\r
+import java.util.regex.Matcher;\r
+import java.util.regex.Pattern;\r
+\r
+/**\r
+ * 実際のレコーダの設定(IP、PORTなど)を保持するクラスです.  \r
+ * @since 3.15.4β\r
+ */\r
+public class RecorderInfo implements Cloneable {\r
+\r
+       private String recorderId;\r
+       private String recorderIPAddr;\r
+       private String recorderPortNo;\r
+       private String recorderUser;\r
+       private String recorderPasswd;\r
+       private String recorderMacAddr;\r
+       private String recorderBroadcast;\r
+       private String recorderColor;\r
+       private ArrayList<String> recorderColorList;\r
+       private ArrayList<String> recorderEncoderList;\r
+       private int recordedCheckScope;\r
+       private int recorderTunerNum;\r
+       private boolean useCalendar;\r
+       private boolean useChChange;\r
+       \r
+       public RecorderInfo() {\r
+               recorderId = "";\r
+               recorderIPAddr = "";\r
+               recorderPortNo = "";\r
+               recorderMacAddr = "";\r
+               recorderBroadcast = "";\r
+               recorderUser = "";\r
+               recorderPasswd = "";\r
+               recorderColor = "";\r
+               recorderColorList = new ArrayList<String>();\r
+               recorderEncoderList = new ArrayList<String>();\r
+               recordedCheckScope = 14;\r
+               recorderTunerNum = 0;\r
+               useCalendar = true;\r
+               useChChange = true;\r
+       }\r
+\r
+       public String MySelf() { return recorderIPAddr+":"+recorderPortNo+":"+recorderId; }\r
+       \r
+       public String getRecorderId() { return recorderId; }\r
+       public void setRecorderId(String s) { recorderId = s; }\r
+       \r
+       public String getRecorderIPAddr() { return recorderIPAddr; }\r
+       public void setRecorderIPAddr(String s) { recorderIPAddr = s; }\r
+       \r
+       public String getRecorderPortNo() { return recorderPortNo; }\r
+       public void setRecorderPortNo(String s) { recorderPortNo = s; }\r
+       \r
+       public String getRecorderUser() { return recorderUser; }\r
+       public void setRecorderUser(String s) { recorderUser = s; }\r
+       \r
+       public String getRecorderPasswd() { return recorderPasswd; }\r
+       public void setRecorderPasswd(String s) { recorderPasswd = s; }\r
+       \r
+       public String getRecorderMacAddr() { return recorderMacAddr; }\r
+       public void setRecorderMacAddr(String s) { recorderMacAddr = s; }\r
+       \r
+       public String getRecorderBroadcast() { return recorderBroadcast; }\r
+       public void setRecorderBroadcast(String s) { recorderBroadcast = s; }\r
+       \r
+       public String getRecorderColor() {\r
+               recorderColor = "";\r
+               for (String s : recorderColorList) {\r
+                       recorderColor += s+";";\r
+               }\r
+               return recorderColor;\r
+       }\r
+       public void setRecorderColor(String s) {\r
+               //\r
+               recorderColorList.clear();\r
+               //\r
+               Matcher ma = Pattern.compile("^#......$").matcher(s);\r
+               if (ma.find()) {\r
+                       recorderColorList.add(s);\r
+               }\r
+               else {\r
+                       ma = Pattern.compile("(#......);").matcher(s);\r
+                       while (ma.find()) {\r
+                               recorderColorList.add(ma.group(1));\r
+                       }\r
+               }\r
+               return;\r
+       }\r
+       \r
+       public void clearEncoderColors() {\r
+               recorderColorList.clear();\r
+       }\r
+       public void addEncoderColor(String c) {\r
+               recorderColorList.add(c+";");\r
+       }\r
+       public String getEncoderColor(int n) {\r
+               if ( recorderColorList.size() > n ) {\r
+                       return recorderColorList.get(n);\r
+               }\r
+               return CommonUtils.color2str(Color.RED);\r
+       }\r
+       public String getEncoderColor(String enc) {\r
+               if (enc != null) {\r
+                       for (int i=0; i<recorderEncoderList.size() && i<recorderColorList.size(); i++) {\r
+                               if (enc.equals(recorderEncoderList.get(i))) {\r
+                                       return recorderColorList.get(i);\r
+                               }\r
+                       }\r
+               }\r
+               return recorderColorList.get(0);\r
+       }\r
+       public int clearEncoderColorsSize() {\r
+               return recorderColorList.size();\r
+       }\r
+\r
+       //\r
+       public ArrayList<String> getRecorderEncoderList() { return recorderEncoderList; }\r
+       public void setRecorderEncoderList(ArrayList<String> al) { recorderEncoderList = al; }\r
+       \r
+       public void clearEncoders() {\r
+               recorderEncoderList.clear();\r
+       }\r
+       public void addEncoder(String enc) {\r
+               recorderEncoderList.add(enc);\r
+       }\r
+       public String getEncoder(int n) {\r
+               if ( recorderEncoderList.size() == 0 && n == 0) {\r
+                       // エンコーダ情報を持たないものはデフォルト文字列\r
+                       return "■";\r
+               }\r
+               else if ( recorderEncoderList.size() > n ) {\r
+                       return recorderEncoderList.get(n);\r
+               }\r
+               return "";\r
+       }\r
+       public int getEncoderSize() {\r
+               return recorderEncoderList.size();\r
+       }\r
+       \r
+       // 主にTvRock./EDCB用\r
+       public int getRecordedCheckScope() {\r
+               return recordedCheckScope;\r
+       }\r
+       public void setRecordedCheckScope(int n) {\r
+               recordedCheckScope = n;\r
+       }\r
+       \r
+       // 主にDIGA用\r
+       public int getTunerNum() {\r
+               return recorderTunerNum;\r
+       }\r
+       public void setTunerNum(int n) {\r
+               recorderTunerNum = n;\r
+       }\r
+       \r
+       public boolean getUseCalendar() { return useCalendar; }\r
+       public void setUseCalendar(boolean b) { useCalendar = b; }\r
+       \r
+       public boolean getUseChChange() { return useChChange; }\r
+       public void setUseChChange(boolean b) { useChChange = b; }\r
+       \r
+       /*\r
+        * (non-Javadoc)\r
+        * @see java.lang.Object#clone()\r
+        */\r
+       @Override\r
+       public Object clone() {\r
+               try {\r
+                       return super.clone();\r
+               } catch (CloneNotSupportedException e) {\r
+                       throw new InternalError(e.toString());\r
+               }\r
+       }\r
+}\r
diff --git a/TinyBannavi/src/tainavi/RecorderInfoList.java b/TinyBannavi/src/tainavi/RecorderInfoList.java
new file mode 100644 (file)
index 0000000..d2e6c52
--- /dev/null
@@ -0,0 +1,98 @@
+package tainavi;\r
+\r
+import java.io.File;\r
+import java.util.ArrayList;\r
+\r
+/**\r
+ * {@link RecorderInfo} のリストを実現するクラスです. \r
+ * @version 3.15.4β~\r
+ */\r
+\r
+public class RecorderInfoList extends ArrayList<RecorderInfo> {\r
+       \r
+       private static final long serialVersionUID = 1L;\r
+       \r
+       private final String rFileOld = "env"+File.separator+"recorderlist.xml";\r
+       private final String rFile = "env"+File.separator+"recorderinfolist.xml";\r
+       private final String passEncMark = "Encrypted:";\r
+       \r
+       // セーブ・ロード\r
+       public boolean save() {\r
+               System.out.println("レコーダ一覧を保存します: "+rFile);\r
+               \r
+               RecorderInfoList rl = new RecorderInfoList();\r
+               \r
+               for (RecorderInfo r : this) {\r
+                       // パスワードを隠す\r
+                       RecorderInfo rc = (RecorderInfo) r.clone();\r
+                       rc.setRecorderPasswd(passEncMark+b64.enc(EncryptPassword.enc(r.getRecorderPasswd())));\r
+                       rl.add(rc);\r
+               }\r
+               \r
+               //\r
+               if ( ! CommonUtils.writeXML(rFile, rl) ) {\r
+                       System.out.println("レコーダ一覧の保存に失敗しました: "+rFile);\r
+                       return false;\r
+               }\r
+               \r
+               return true;\r
+       }\r
+       \r
+       @SuppressWarnings("deprecation")\r
+       public boolean load() {\r
+               \r
+               System.out.println("レコーダ一覧を読み込みます: "+rFile);\r
+\r
+               boolean isoldclass = false;\r
+               ArrayList<RecorderInfo> rl = null;\r
+               \r
+               if ( ! new File(rFile).exists() ) {\r
+                       if ( ! new File(rFileOld).exists() ) {\r
+                               System.err.println("レコーダ一覧が読み込めなかったので登録なしで起動します.");\r
+                               return false;\r
+                       }\r
+                               \r
+                       // 旧RecorderInfoList対策\r
+                       isoldclass = true;\r
+                       rl = new ArrayList<RecorderInfo>();\r
+                       ArrayList<RecorderList> rlx = (ArrayList<RecorderList>) CommonUtils.readXML(rFileOld);\r
+                       for ( RecorderList rx : rlx ) {\r
+                               RecorderInfo r = new RecorderInfo();\r
+                               CommonUtils.FieldCopy(r, rx);\r
+                               rl.add(r);\r
+                       }\r
+               }\r
+               else {\r
+                       rl = (RecorderInfoList) CommonUtils.readXML(rFile);\r
+               }\r
+               if ( rl == null ) {\r
+                       System.err.println("レコーダ一覧が読み込めなかったので登録なしで起動します.");\r
+                       return false;\r
+               }\r
+               \r
+               this.clear();\r
+               for (RecorderInfo r : rl) {\r
+                       if (r.getRecorderPasswd() != null) {\r
+                               if (r.getRecorderPasswd().indexOf(passEncMark) == 0) {\r
+                                       // パスワードを復元する\r
+                                       r.setRecorderPasswd(EncryptPassword.dec(b64.dec(r.getRecorderPasswd().substring(passEncMark.length()))));\r
+                               }\r
+                       }\r
+                       \r
+                       this.add(r);\r
+               }\r
+               \r
+               if ( isoldclass && this.save() ) {\r
+                       System.err.println("レコーダ一覧ファイルを置き換えます: "+rFileOld+"->"+rFile);\r
+                       new File(rFileOld).delete();\r
+               }\r
+               \r
+               return true;\r
+       }\r
+       \r
+       @Override\r
+       public boolean add(RecorderInfo r) {\r
+               if ( r == null ) throw new NullPointerException();\r
+               return super.add(r);\r
+       }\r
+}
\ No newline at end of file
diff --git a/TinyBannavi/src/tainavi/RecorderList.java b/TinyBannavi/src/tainavi/RecorderList.java
new file mode 100644 (file)
index 0000000..4028f15
--- /dev/null
@@ -0,0 +1,12 @@
+package tainavi;\r
+\r
+/**\r
+ * {@link RecorderInfo} に置き換えられました.(名前がアンマッチだったため)\r
+ * @deprecated\r
+ * @version ~3.15.4β\r
+ */\r
+public class RecorderList extends RecorderInfo implements Cloneable {\r
+       \r
+       // RecorderInfoに移行\r
+       \r
+}\r
diff --git a/TinyBannavi/src/tainavi/ReserveList.java b/TinyBannavi/src/tainavi/ReserveList.java
new file mode 100644 (file)
index 0000000..033e02e
--- /dev/null
@@ -0,0 +1,196 @@
+package tainavi;\r
+\r
+import java.util.HashMap;\r
+\r
+/**\r
+ * <P>個々の予約の情報を保持します。\r
+ * <P>本当はReserveInfoに名前を変えたいのだけど、影響範囲が大きすぎてできなかった。\r
+ */\r
+public class ReserveList implements Cloneable {\r
+       private int no=0;       // idに移行(2.4β~)\r
+       private String id=null;\r
+       private String rec_pattern="";\r
+       private int rec_pattern_id=0;\r
+       private String rec_nextdate="";\r
+       private String ahh="";\r
+       private String amm="";\r
+       private String zhh="";\r
+       private String zmm="";\r
+       private String rec_min="";\r
+       private String tuner="";\r
+       private String rec_mode="";\r
+       private String title="";\r
+       private String titlePop="";\r
+       private String channel="";\r
+       private String ch_name="";\r
+       \r
+       private String detail="";\r
+       \r
+       private String content_id="";\r
+       \r
+       private String rec_audio="";\r
+       private String rec_folder="";\r
+       private String rec_genre="";\r
+       private String rec_subgenre="";\r
+       private String rec_dvdcompat="";\r
+       private String rec_device="";\r
+       \r
+       private String rec_xchapter="";\r
+       private String rec_mschapter="";\r
+       private String rec_mvchapter="";\r
+       \r
+       private String rec_aspect = "";\r
+       private String rec_bvperf = "";\r
+       private String rec_lvoice = "";\r
+       private String rec_autodel = "";\r
+       \r
+       private String startDateTime="";\r
+       private String endDateTime="";\r
+       \r
+       private boolean exec=true;              // 予約実行ON/OFF\r
+       \r
+       private boolean pursues=false;  // 番組追跡\r
+       \r
+       private boolean autocomplete=false;     // タイトル自動補完\r
+       \r
+       private boolean updateonlyexec=false;   // 予約実行ON/OFFのみの更新\r
+       \r
+       private boolean appsrsv=false;  // 持出\r
+       \r
+       private boolean tunershort=false;       // チューナー不足警告\r
+       \r
+       private boolean modenabled=true;\r
+       \r
+       private boolean recorded=false;         // 成功した録画結果が存在しているかどうか\r
+       \r
+       private boolean autoreserved=false;     // 自動予約かどうか\r
+\r
+       private HashMap<String, String> hidden_params = new HashMap<String, String>();\r
+\r
+       //\r
+       @Override\r
+       public ReserveList clone() {\r
+               try {\r
+                       ReserveList p = (ReserveList) super.clone();\r
+                       p.rec_device = (this.rec_device!=null)?(this.rec_device):("");\r
+                       return p;\r
+               } catch (CloneNotSupportedException e) {\r
+                       throw new InternalError(e.toString());\r
+               }\r
+       }\r
+       \r
+       \r
+       \r
+       /*\r
+        * ほげほげ\r
+        */\r
+       \r
+       /**\r
+        * @deprecated 大昔、予約IDが数値だけだったころの名残\r
+        * @see #getId() \r
+        */\r
+       public int getNo() {return no;}\r
+       /**\r
+        * @deprecated 大昔、予約IDが数値だけだったころの名残\r
+        * @see #setId(String) \r
+        */\r
+       public void setNo(int d) { no=d;}\r
+       \r
+       public String getId() {return id;}\r
+       public void setId(String s) { id=s;}\r
+       \r
+       public String getRec_pattern() {return rec_pattern;}\r
+       public void setRec_pattern(String s) { rec_pattern=s;}\r
+       public int getRec_pattern_id() {return rec_pattern_id;}\r
+       public void setRec_pattern_id(int i) { rec_pattern_id=i;}\r
+       public String getRec_nextdate() {return rec_nextdate;}\r
+       public void setRec_nextdate(String s) { rec_nextdate=s;}\r
+       public String getAhh() {return ahh;}\r
+       public void setAhh(String s) { ahh=s;}\r
+       public String getAmm() {return amm;}\r
+       public void setAmm(String s) { amm=s;}\r
+       public String getZhh() {return zhh;}\r
+       public void setZhh(String s) { zhh=s;}\r
+       public String getZmm() {return zmm;}\r
+       public void setZmm(String s) { zmm=s;}\r
+       public String getRec_min() {return rec_min;}\r
+       public void setRec_min(String s) { rec_min=s;}\r
+       public String getTuner() {return tuner;}\r
+       public void setTuner(String s) { tuner=s;}\r
+       public String getRec_mode() {return rec_mode;}\r
+       public void setRec_mode(String s) { rec_mode=s;}\r
+       public String getTitle() {return title;}\r
+       public void setTitle(String s) { title=s;}\r
+       public String getTitlePop() {return titlePop;}\r
+       public void setTitlePop(String s) { titlePop=s;}\r
+       public String getChannel() {return channel;}\r
+       public void setChannel(String s) { channel=s;}\r
+       public String getCh_name() {return ch_name;}\r
+       public void setCh_name(String s) { ch_name=s;}\r
+       \r
+       public String getDetail() {return detail;}\r
+       public void setDetail(String s) { detail=s;}\r
+\r
+       public String getContentId() {return content_id;}\r
+       public void setContentId(String s) { content_id=s;}\r
+\r
+       public String getRec_audio() {return rec_audio;}\r
+       public void setRec_audio(String s) { rec_audio=s;}\r
+       public String getRec_folder() {return rec_folder;}\r
+       public void setRec_folder(String s) { rec_folder=s;}\r
+       public String getRec_genre() {return rec_genre;}\r
+       public void setRec_genre(String s) { rec_genre=s;}\r
+       public String getRec_subgenre() {return rec_subgenre;}\r
+       public void setRec_subgenre(String s) { rec_subgenre=s;}\r
+       public String getRec_dvdcompat() {return rec_dvdcompat;}\r
+       public void setRec_dvdcompat(String s) { rec_dvdcompat=s;}\r
+       public String getRec_device() {return rec_device;}\r
+       public void setRec_device(String s) { rec_device=s;}\r
+       \r
+       public String getRec_xchapter() {return rec_xchapter;}\r
+       public void setRec_xchapter(String s) { rec_xchapter=s;}\r
+       public String getRec_mschapter() {return rec_mschapter;}\r
+       public void setRec_mschapter(String s) { rec_mschapter=s;}\r
+       public String getRec_mvchapter() {return rec_mvchapter;}\r
+       public void setRec_mvchapter(String s) { rec_mvchapter=s;}\r
+       \r
+       public String getRec_aspect() { return rec_aspect; }\r
+       public void setRec_aspect(String s) { rec_aspect = s; }\r
+       public String getRec_bvperf() { return rec_bvperf; }\r
+       public void setRec_bvperf(String s) { rec_bvperf = s; }\r
+       public String getRec_lvoice() { return rec_lvoice; }\r
+       public void setRec_lvoice(String s) { rec_lvoice = s; }\r
+       public String getRec_autodel() { return rec_autodel; }\r
+       public void setRec_autodel(String s) { rec_autodel = s; }\r
+       \r
+       public String getStartDateTime() {return startDateTime;}\r
+       public void setStartDateTime(String s) { startDateTime=s;}\r
+       public String getEndDateTime() {return endDateTime;}\r
+       public void setEndDateTime(String s) { endDateTime=s;}\r
+       \r
+       public boolean getExec() {return exec;}\r
+       public void setExec(boolean b) { exec=b;}\r
+       public boolean getPursues() {return pursues;}\r
+       public void setPursues(boolean b) { pursues=b;}\r
+       public boolean getAutocomplete() {return autocomplete;}\r
+       public void setAutocomplete(boolean b) { autocomplete=b;}\r
+       public boolean getUpdateOnlyExec() {return updateonlyexec;}\r
+       public void setUpdateOnlyExec(boolean b) { updateonlyexec=b;}\r
+       public boolean getAppsRsv() {return appsrsv;}\r
+       public void setAppsRsv(boolean b) { appsrsv=b;}\r
+       public boolean getModEnabled() {return modenabled;}\r
+       public void setModEnabled(boolean b) { modenabled=b;}\r
+       \r
+       public boolean getRecorded() {return recorded;}\r
+       public void setRecorded(boolean b) { recorded=b;}\r
+\r
+       public boolean getAutoreserved() {return autoreserved;}\r
+       public void setAutoreserved(boolean b) { autoreserved=b;}\r
+\r
+       public boolean getTunershort() { return tunershort; }\r
+       public void setTunershort(boolean b) { tunershort = b; }\r
+       \r
+       public HashMap<String,String> getHidden_params() { return hidden_params; }\r
+       public void setHidden_params(HashMap<String,String> a) { hidden_params = a; }\r
+\r
+}\r
diff --git a/TinyBannavi/src/tainavi/RowHeightChangeListener.java b/TinyBannavi/src/tainavi/RowHeightChangeListener.java
new file mode 100644 (file)
index 0000000..7ad6465
--- /dev/null
@@ -0,0 +1,27 @@
+package tainavi;\r
+\r
+import java.beans.PropertyChangeEvent;\r
+import java.beans.PropertyChangeListener;\r
+\r
+import javax.swing.JTable;\r
+import javax.swing.JTree;\r
+\r
+public class RowHeightChangeListener implements PropertyChangeListener {\r
+\r
+       public int h = 4;\r
+       \r
+       public RowHeightChangeListener(int h) { this.h = h; }\r
+       \r
+       @Override\r
+       public void propertyChange(PropertyChangeEvent e) {\r
+               if ( e.getSource() instanceof JTable ) {\r
+                       JTable comp = (JTable) e.getSource();\r
+                       comp.setRowHeight(comp.getFont().getSize()+h);\r
+               }\r
+               else if ( e.getSource() instanceof JTree ) {\r
+                       JTree comp = (JTree) e.getSource();\r
+                       comp.setRowHeight(comp.getFont().getSize()+h);\r
+               }\r
+       }\r
+\r
+}\r
diff --git a/TinyBannavi/src/tainavi/RowItem.java b/TinyBannavi/src/tainavi/RowItem.java
new file mode 100644 (file)
index 0000000..896a346
--- /dev/null
@@ -0,0 +1,52 @@
+package tainavi;\r
+\r
+import java.util.ArrayList;\r
+\r
+/**\r
+ * <P>- JTableの実態のデータを統一したインタフェースであつかうようにできればいーんじゃない? -\r
+ * <P>テーブルの各行のデータを保持するもの\r
+ */\r
+public abstract class RowItem implements Cloneable {\r
+\r
+       /*\r
+        *  抽象メソッド\r
+        */\r
+       \r
+       abstract protected void myrefresh(RowItem o);\r
+       \r
+       /*\r
+        * 共用部\r
+        */\r
+       \r
+       // JTableからの参照用にインデックスを張る\r
+       private ArrayList<Object> data = new ArrayList<Object>();\r
+       \r
+       public int getColumnCount() { return (data == null)?(-1):(data.size()); }\r
+       \r
+       public Object get(int index) { return data.get(index); }\r
+       \r
+       public int size() { return data.size(); }\r
+       \r
+       // メンバを更新したらインデックスを張り替える\r
+       public void fireChanged() {\r
+               data = new ArrayList<Object>();\r
+               myrefresh(this);\r
+               //System.err.println("RowItem#refresh: "+data.size());\r
+       }\r
+       \r
+       protected void addData(Object o) { data.add(o); }\r
+       \r
+       protected void clean() { data = new ArrayList<Object>(); }\r
+       \r
+       @Override\r
+       public RowItem clone() {\r
+               try {\r
+                       RowItem o = (RowItem) super.clone();\r
+                       o.clean();\r
+                       myrefresh(o);\r
+                       return o;\r
+               } catch (CloneNotSupportedException e) {\r
+                       throw new InternalError(e.toString());\r
+               }\r
+       }\r
+}\r
diff --git a/TinyBannavi/src/tainavi/RowItemList.java b/TinyBannavi/src/tainavi/RowItemList.java
new file mode 100644 (file)
index 0000000..85f42ff
--- /dev/null
@@ -0,0 +1,44 @@
+package tainavi;\r
+\r
+import java.util.ArrayList;\r
+\r
+/**\r
+ * <P>- JTableの実態のデータを統一したインタフェースであつかうようにできればいーんじゃない? -\r
+ * <P>テーブルの操作をまとめたもの\r
+ * <P>\r
+ * <P> ソートが必要な場合は、RowItemListの操作はTableModelのサブクラスで実装する。ただし、その場合Viewのrowがわからないので行の入れ替えが行えない\r
+ * <P> ソートが必要がないならRowItemListの操作はJTableのサブクラスを作ればよい\r
+ */\r
+public class RowItemList<T> extends ArrayList<T> {\r
+\r
+       private static final long serialVersionUID = 1L;\r
+\r
+       public boolean up(int top, int length) {\r
+               if ( top < 1 ) {\r
+                       // 2行目より上ならもうなにもできない\r
+                       return false;\r
+               }\r
+               T a = this.get(top-1);\r
+               for ( int i=0; i<length; i++ ) {\r
+                       this.set(top+i-1,this.get(top+i));\r
+               }\r
+               this.set(top+length-1,a);\r
+               \r
+               return true;\r
+       }\r
+       \r
+       public boolean down(int top, int length) {\r
+               if ( (top+length) > (this.size()-1) ) {\r
+                       // 最終行までいってるならもうなにもできない\r
+                       return false;\r
+               }\r
+                       \r
+               T a = this.get(top+length);\r
+               for ( int i=length-1; i>=0; i-- ) {\r
+                       this.set(top+i+1,this.get(top+i));\r
+               }\r
+               this.set(top,a);\r
+               \r
+               return true;\r
+       }\r
+}\r
diff --git a/TinyBannavi/src/tainavi/SearchGroup.java b/TinyBannavi/src/tainavi/SearchGroup.java
new file mode 100644 (file)
index 0000000..9c4c0f8
--- /dev/null
@@ -0,0 +1,47 @@
+package tainavi;\r
+\r
+import java.util.ArrayList;\r
+import java.util.Iterator;\r
+\r
+public class SearchGroup implements Iterator<String>,Iterable<String> {\r
+       \r
+       private String name;\r
+       private ArrayList<String> members = new ArrayList<String>();\r
+       \r
+       private int idx;\r
+       \r
+       public void setName(String s) { name = s; }\r
+       public String getName() { return name; }\r
+       public void setMembers(ArrayList<String> a) { members = a; }\r
+       public ArrayList<String> getMembers() { return members; }\r
+       \r
+       public void add(String s) { members.add(s); }\r
+       \r
+       public boolean replace(String oldMember, String newMember) {\r
+               for ( int i=0; i<members.size(); i++ ) {\r
+                       if ( members.get(i).equals(oldMember) ) {\r
+                                members.set(i, newMember);\r
+                                return true;\r
+                       }\r
+               }\r
+               return false;\r
+       }\r
+       \r
+       @Override\r
+       public Iterator<String> iterator() {\r
+               idx = -1;\r
+               return this;\r
+       }\r
+       @Override\r
+       public boolean hasNext() {\r
+               return (members.size() > (idx+1));\r
+       }\r
+       @Override\r
+       public String next() {\r
+               return members.get(++idx);\r
+       }\r
+       @Override\r
+       public void remove() {\r
+               members.remove(idx);\r
+       }\r
+}\r
diff --git a/TinyBannavi/src/tainavi/SearchGroupList.java b/TinyBannavi/src/tainavi/SearchGroupList.java
new file mode 100644 (file)
index 0000000..9d21c6e
--- /dev/null
@@ -0,0 +1,214 @@
+package tainavi;\r
+\r
+import java.io.File;\r
+import java.util.ArrayList;\r
+import java.util.Iterator;\r
+\r
+public class SearchGroupList implements Iterator<SearchGroup>, Iterable<SearchGroup>{\r
+       \r
+       private final ArrayList<SearchGroup> gList = new ArrayList<SearchGroup>();\r
+       \r
+       private int idx;\r
+       \r
+       private String gFile = "env"+File.separator+"keywordgroup.xml";\r
+       \r
+       \r
+       // コンストラクタ\r
+       public SearchGroupList() {\r
+               //\r
+       }\r
+       \r
+       \r
+       // 検索系\r
+       @Override\r
+       public Iterator<SearchGroup> iterator() {\r
+               idx = -1;\r
+               return this;\r
+       }\r
+       @Override\r
+       public boolean hasNext() {\r
+               return (gList.size() > (idx+1));\r
+       }\r
+       @Override\r
+       public SearchGroup next() {\r
+               return gList.get(++idx);\r
+       }\r
+       @Override\r
+       public void remove() {\r
+               gList.remove(idx);\r
+       }\r
+       \r
+       public int size() {\r
+               return gList.size();\r
+       }\r
+       \r
+       // グループ・メンバー追加\r
+       public boolean add(String name) {\r
+               for ( SearchGroup gl : gList ) {\r
+                       if ( gl.getName().equals(name) ) {\r
+                               // 既に存在している\r
+                               return false;\r
+                       }\r
+               }\r
+               SearchGroup gl = new SearchGroup();\r
+               gl.setName(name);\r
+               gList.add(gl);\r
+               return true;\r
+       }\r
+       public boolean add(String name, String member) {\r
+               \r
+               if ( name == null || member == null ) {\r
+                       // グループとメンバーの指定は必須\r
+                       return false;\r
+               }\r
+               \r
+               for ( SearchGroup gl : gList ) {\r
+                       if ( gl.getName().equals(name) ) {\r
+                               gList.iterator();\r
+                               for ( String gmember : gl ) {\r
+                                       if ( gmember.equals(member) ) {\r
+                                               // 既に存在している\r
+                                               return false;\r
+                                       }\r
+                               }\r
+                               // メンバーを追加する\r
+                               gl.add(member);\r
+                               return true;\r
+                       }\r
+               }\r
+               // グループごと追加する\r
+               SearchGroup gl = new SearchGroup();\r
+               gl.setName(name);\r
+               gl.add(member);\r
+               return true;\r
+       }\r
+       \r
+       // 削除する\r
+       public boolean remove(String name) {\r
+               for ( SearchGroup gl : gList ) {\r
+                       if ( gl.getName().equals(name) ) {\r
+                               // グループごと削除\r
+                               gList.remove(gl);\r
+                               return true;\r
+                       }\r
+               }\r
+               return false;\r
+       }\r
+       public boolean remove(String name, String member) {\r
+               \r
+               if ( member == null ) {\r
+                       // グループ指定は必須\r
+                       return false;\r
+               }\r
+               \r
+               if ( name == null ) {\r
+                       // グループ横断削除\r
+                       for ( SearchGroup gl : gList ) {\r
+                               for ( String gmember : gl ) {\r
+                                       if ( gmember.equals(member) ) {\r
+                                               // メンバー削除\r
+                                               gl.remove();\r
+                                               break;\r
+                                       }\r
+                               }\r
+                       }\r
+                       // あってもなくてもよしとする\r
+                       return true;\r
+               }\r
+               else {\r
+                       // 単独グループ削除\r
+                       for ( SearchGroup gl : gList ) {\r
+                               if ( gl.getName().equals(name) ) {\r
+                                       for ( String gmember : gl ) {\r
+                                               if ( gmember.equals(member) ) {\r
+                                                       // メンバー削除\r
+                                                       gl.remove();\r
+                                                       return true;\r
+                                               }\r
+                                       }\r
+                                       // メンバーが存在しない\r
+                                       return false;\r
+                               }\r
+                       }\r
+                       // グループが存在しない\r
+                       return false;\r
+               }\r
+       }\r
+       \r
+       // 検索する\r
+       public boolean isFind(String name, String member) {\r
+               \r
+               if ( name == null || member == null ) {\r
+                       return false;\r
+               }\r
+               \r
+               for ( SearchGroup gl : gList ) {\r
+                       if ( gl.getName().equals(name) ) {\r
+                               for ( String gmember : gl ) {\r
+                                       if ( gmember.equals(member) ) {\r
+                                               // 存在している\r
+                                               return true;\r
+                                       }\r
+                               }\r
+                       }\r
+               }\r
+               return false;\r
+       }\r
+       \r
+       // 改名する\r
+       public boolean rename(String oldName, String newName) {\r
+               for ( SearchGroup gl : gList ) {\r
+                       if ( gl.getName().equals(oldName) ) {\r
+                               gl.setName(newName);\r
+                               return true;\r
+                       }\r
+               }\r
+               return false;\r
+       }\r
+       public boolean rename(String name, String oldMember, String newMember) {\r
+               if ( name == null ) {\r
+                       boolean f = false;\r
+                       for ( SearchGroup gl : gList ) {\r
+                               if ( gl.replace(oldMember, newMember) ) {\r
+                                       f = true;\r
+                               }\r
+                       }\r
+                       return f;\r
+               }\r
+               else {\r
+                       for ( SearchGroup gl : gList ) {\r
+                               if ( gl.getName().equals(name) ) {\r
+                                       if ( gl.replace(oldMember, newMember) ) {\r
+                                               return true;\r
+                                       }\r
+                               }\r
+                       }\r
+               }\r
+               return false;\r
+       }\r
+       \r
+       // セーブ・ロード\r
+       public boolean save() {\r
+               System.out.println("検索キーワードグループ設定を保存します: "+gFile);\r
+               if ( ! CommonUtils.writeXML(gFile, gList) ) {\r
+                       System.err.println("検索キーワードグループ設定の保存に失敗しました: "+gFile);\r
+                       return false;\r
+               }\r
+               return true;\r
+       }\r
+       \r
+       public boolean load() {\r
+               System.out.println("検索キーワードグループ設定を読み込みます: "+gFile);\r
+               if ( new File(gFile).exists() ) {\r
+                       @SuppressWarnings("unchecked")\r
+                       ArrayList<SearchGroup> tmpList = (ArrayList<SearchGroup>) CommonUtils.readXML(gFile);\r
+                       if ( tmpList != null ) {\r
+                               CommonUtils.FieldCopy(gList, tmpList);\r
+                               return true;\r
+                       }\r
+               }\r
+               \r
+               System.err.println("検索キーワードグループ設定が読み込みに失敗しました: "+gFile);\r
+               return false;\r
+       }\r
+}\r
diff --git a/TinyBannavi/src/tainavi/SearchKey.java b/TinyBannavi/src/tainavi/SearchKey.java
new file mode 100644 (file)
index 0000000..1a91e6e
--- /dev/null
@@ -0,0 +1,136 @@
+package tainavi;\r
+\r
+import java.util.ArrayList;\r
+import java.util.regex.Pattern;\r
+\r
+\r
+public class SearchKey {\r
+       private String label;\r
+       \r
+       // 0:"次のすべての条件に一致"\r
+       // 1:"次のいずれかの条件に一致"\r
+       private String condition;\r
+       \r
+       // 0:"延長感染源にする"\r
+       // 1:"延長感染源にしない"\r
+       private String infection;\r
+       \r
+       public static enum TargetId {\r
+               TITLEANDDETAIL  ("0",   true,   true,   "番組名、内容に"),\r
+               TITLE                   ("1",   true,   true,   "番組名に"),\r
+               DETAIL                  ("2",   true,   true,   "番組内容に"),\r
+               CHANNEL                 ("3",   true,   true,   "チャンネル名に"),\r
+               GENRE                   ("4",   false,  true,   "ジャンルに"),\r
+               NEW                             ("5",   false,  false,  "新番組"),\r
+               LAST                    ("6",   false,  false,  "最終回"),\r
+               REPEAT                  ("7",   false,  false,  "再放送"),\r
+               FIRST                   ("8",   false,  false,  "初回放送"),\r
+               LENGTH                  ("9",   false,  false,  "番組長が"),\r
+               STARTA                  ("10",  false,  true,   "開始時刻(上限)が"),\r
+               STARTZ                  ("11",  false,  true,   "開始時刻(下限)が"),\r
+               SPECIAL                 ("12",  false,  false,  "特番"),\r
+               NOSCRUMBLE              ("13",  false,  false,  "無料放送"),\r
+               STARTDATETIME   ("14",  true,   true,   "開始日時に"),\r
+               SUBGENRE                ("15",  false,  true,   "サブジャンルに"),\r
+               LIVE                    ("16",  false,  false,  "生放送"),\r
+               BILINGUAL               ("17",  false,  false,  "二か国語放送"),\r
+               STANDIN                 ("18",  false,  false,  "吹替放送"),\r
+               RATING                  ("19",  false,  false,  "視聴制限"),\r
+               ;\r
+               \r
+               private String id;\r
+               private boolean useregexpr;\r
+               private boolean usekeyword;\r
+               private String name;\r
+               \r
+               private TargetId(String id, boolean useregexpr, boolean usekeyword, String name) {\r
+                       this.id = id;\r
+                       this.useregexpr = useregexpr;\r
+                       this.usekeyword = usekeyword;\r
+                       this.name = name;\r
+               }\r
+               \r
+               @Override\r
+               public String toString() {\r
+                       return name;\r
+               }\r
+               \r
+               public String getId() {\r
+                       return id;\r
+               }\r
+               \r
+               public boolean getUseRegexpr() {\r
+                       return useregexpr;\r
+               }\r
+               \r
+               public boolean getUseKeyword() {\r
+                       return usekeyword;\r
+               }\r
+               \r
+               public static TargetId getTargetId(String id) {\r
+                       for ( TargetId ti : TargetId.values() ) {\r
+                               if ( ti.id.equals(id) ) {\r
+                                       return ti;\r
+                               }\r
+                       }\r
+                       return null;\r
+               }\r
+       }\r
+       \r
+       private String target;\r
+       \r
+       // s\t..:キーワード\r
+       private String keyword;\r
+       \r
+       // 0\t..:"を含む番組"\r
+       // 1\t..:"を含む番組を除く"\r
+       private String contain;\r
+       \r
+       // 1:レベル1\r
+       // 2:レベル2\r
+       // 3:レベル3\r
+       // 4:レベル4\r
+       // 5:レベル5\r
+       private String okiniiri;\r
+       \r
+       // 大小同一視無効\r
+       private boolean caseSensitive;\r
+       \r
+       // 番組追跡表示あり\r
+       private boolean showInStandby = true;\r
+       \r
+       // 正規表現はプリコンパイルしておくべきだ!\r
+       ArrayList<TargetId> alTarget = new ArrayList<TargetId>();\r
+       ArrayList<Pattern> alKeyword_regex = new ArrayList<Pattern>();\r
+       ArrayList<String> alKeyword = new ArrayList<String>();\r
+       ArrayList<String> alKeyword_plane = new ArrayList<String>();\r
+       ArrayList<String> alKeyword_pop = new ArrayList<String>();\r
+       ArrayList<String> alContain = new ArrayList<String>();\r
+       ArrayList<Integer> alLength = new ArrayList<Integer>();\r
+\r
+       //\r
+       public void setLabel(String s) { label = s; }\r
+       public String getLabel() { return label; }\r
+       \r
+       public void setCondition(String s) { condition = s; }\r
+       public String getCondition() { return condition; }\r
+       \r
+       public void setInfection(String s) { infection = s; }\r
+       public String getInfection() { return infection; }\r
+       \r
+       public void setTarget(String s) { target = s; }\r
+       public String getTarget() { return target; }\r
+       public void setKeyword(String s) { keyword = s; }\r
+       public String getKeyword() { return keyword; }\r
+       public void setContain(String s) { contain = s; }\r
+       public String getContain() { return contain; }\r
+\r
+       public void setOkiniiri(String s) { okiniiri = s; }\r
+       public String getOkiniiri() { return okiniiri; }\r
+\r
+       public void setCaseSensitive(boolean b) { caseSensitive = b; }\r
+       public boolean getCaseSensitive() { return caseSensitive; }\r
+\r
+       public void setShowInStandby(boolean b) { showInStandby = b; }\r
+       public boolean getShowInStandby() { return showInStandby; }\r
+}\r
diff --git a/TinyBannavi/src/tainavi/SearchProgram.java b/TinyBannavi/src/tainavi/SearchProgram.java
new file mode 100644 (file)
index 0000000..f969d4e
--- /dev/null
@@ -0,0 +1,478 @@
+package tainavi;\r
+\r
+import java.io.File;\r
+import java.util.ArrayList;\r
+import java.util.regex.Matcher;\r
+import java.util.regex.Pattern;\r
+\r
+import tainavi.SearchKey.TargetId;\r
+import tainavi.TVProgram.ProgFlags;\r
+import tainavi.TVProgram.ProgGenre;\r
+import tainavi.TVProgram.ProgOption;\r
+import tainavi.TVProgram.ProgScrumble;\r
+import tainavi.TVProgram.ProgSubgenre;\r
+\r
+\r
+public class SearchProgram {\r
+\r
+       //\r
+       private ArrayList<SearchKey> searchKeys = new ArrayList<SearchKey>();\r
+\r
+       private String searchKeyFile = null;\r
+       protected void setSearchKeyFile(String s) { searchKeyFile = s; }\r
+       \r
+       private String searchKeyLabel = null;\r
+       protected void setSearchKeyLabel(String s) { searchKeyLabel = s; }\r
+       \r
+       // 設定ファイルに書き出し\r
+       public void save() {\r
+               System.out.println(searchKeyLabel+"設定を保存します: "+searchKeyFile);\r
+               if ( ! CommonUtils.writeXML(searchKeyFile, searchKeys) ) {\r
+                       System.err.println(searchKeyLabel+"設定の保存に失敗しました.");\r
+                       return;\r
+               }\r
+       }\r
+       \r
+       // 設定ファイルから読み出し\r
+       public void load() {\r
+               System.out.println(searchKeyLabel+"設定を読み込みます: "+searchKeyFile);\r
+               searchKeys = (ArrayList<SearchKey>) CommonUtils.readXML(searchKeyFile);\r
+               if ( searchKeys == null ) {\r
+                       System.err.println(searchKeyLabel+"設定を読み込めなかったので登録なしで起動します.");\r
+                       searchKeys = new ArrayList<SearchKey>();\r
+                       return;\r
+               }\r
+\r
+               // もういらないのでは…\r
+               for (SearchKey sk : searchKeys) {\r
+                       // 後方互換用・その1\r
+                       compile(sk);\r
+                       // 後方互換用・その2\r
+                       if (sk.getOkiniiri() == null) {\r
+                               sk.setOkiniiri("★");\r
+                       }\r
+               }\r
+       }\r
+       \r
+       // 検索用\r
+       public ArrayList<SearchKey> getSearchKeys() {\r
+               return(searchKeys);\r
+       }\r
+       \r
+       // キーワード検索の追加\r
+       public void add(SearchKey newkey) {\r
+               searchKeys.add(newkey);\r
+       }\r
+       \r
+       public void add(int index, SearchKey newkey) {\r
+               searchKeys.add(index, newkey);\r
+       }\r
+       \r
+       // キーワード検索の削除\r
+       public void remove(String key) {\r
+               for ( SearchKey k : searchKeys ) {\r
+                       if (k.getLabel().equals(key)) {\r
+                               searchKeys.remove(k);\r
+                               break;\r
+                       }\r
+               }\r
+       }\r
+       \r
+       // キーワード検索の置き換え\r
+       public SearchKey replace(String key, SearchKey sk) {\r
+               int i = 0;\r
+               for ( SearchKey k : searchKeys ) {\r
+                       if (k.getLabel().equals(key)) {\r
+                               searchKeys.add(i,sk);\r
+                               searchKeys.remove(i+1);\r
+                               return(sk);\r
+                       }\r
+                       i++;\r
+               }\r
+               return(null);\r
+       }\r
+       \r
+       // キーワード検索の整理\r
+       public void compile(SearchKey keyword) {\r
+               //\r
+               keyword.alTarget = new ArrayList<TargetId>();\r
+               keyword.alKeyword_regex = new ArrayList<Pattern>();\r
+               keyword.alKeyword = new ArrayList<String>();\r
+               keyword.alContain = new ArrayList<String>();\r
+               \r
+               //\r
+               ArrayList<String> sTtmp = new ArrayList<String>();\r
+               ArrayList<String> mRtmp = new ArrayList<String>();\r
+               ArrayList<String> sCtmp = new ArrayList<String>();\r
+               \r
+               Matcher ma = null;\r
+               \r
+               //\r
+               ma = Pattern.compile("(.*?)\t").matcher(keyword.getTarget());\r
+               while (ma.find()) {\r
+                       sTtmp.add(String.valueOf(ma.group(1)));\r
+               }\r
+               //\r
+               ma = Pattern.compile("(.*?)\t").matcher(keyword.getKeyword());\r
+               while (ma.find()) {\r
+                       mRtmp.add(ma.group(1));\r
+               }\r
+               //\r
+               ma = Pattern.compile("(.*?)\t").matcher(keyword.getContain());\r
+               while (ma.find()) {\r
+                       sCtmp.add(String.valueOf(ma.group(1)));\r
+               }\r
+               \r
+               // 「含む」を前に、「除く」を後に\r
+               for (int x=0; x<sTtmp.size(); x++) {\r
+                       if (sCtmp.get(x).equals("0")) {\r
+                               setAlKeyword(keyword, x, sTtmp.get(x), mRtmp.get(x), sCtmp.get(x));\r
+                       }\r
+               }\r
+               for (int x=0; x<sTtmp.size(); x++) {\r
+                       if (sCtmp.get(x).equals("1")) {\r
+                               setAlKeyword(keyword, x, sTtmp.get(x), mRtmp.get(x), sCtmp.get(x));\r
+                       }\r
+               }\r
+       }\r
+       private void setAlKeyword(SearchKey keyword, int x, String sT, String mR, String sC) {\r
+               \r
+               TargetId ti = TargetId.getTargetId(sT);\r
+               keyword.alTarget.add(ti);\r
+\r
+               // 正規表現なターゲット\r
+               switch (ti) {\r
+               case TITLEANDDETAIL:\r
+               case TITLE:\r
+               case DETAIL:\r
+               case CHANNEL:\r
+                       if (keyword.getCaseSensitive() == false) {\r
+                               keyword.alKeyword_regex.add(Pattern.compile("("+TraceProgram.replacePop(mR)+")"));\r
+                       }\r
+                       else {\r
+                               keyword.alKeyword_regex.add(Pattern.compile("("+mR+")"));\r
+                       }\r
+                       break;\r
+               case STARTDATETIME:\r
+                       keyword.alKeyword_regex.add(Pattern.compile("("+mR+")"));\r
+                       break;\r
+               default:\r
+                       keyword.alKeyword_regex.add(null);\r
+                       break;\r
+               }\r
+               \r
+               // 数値なターゲット\r
+               switch (ti) {\r
+               case LENGTH:\r
+                       Matcher ma = Pattern.compile("^(\\d+) ").matcher(mR);\r
+                       if (ma.find()) {\r
+                               keyword.alLength.add(Integer.valueOf(ma.group(1)));\r
+                       }\r
+                       else {\r
+                               keyword.alLength.add(0);\r
+                       }\r
+                       break;\r
+               default:\r
+                       keyword.alLength.add(0);\r
+                       break;\r
+               }\r
+               \r
+               // 時刻なターゲット\r
+               switch (ti) {\r
+               case STARTA:\r
+               case STARTZ:\r
+                       Matcher ma = Pattern.compile("^(\\d\\d)(:\\d\\d)").matcher(mR);\r
+                       if (ma.find()) {\r
+                               Integer t = Integer.valueOf(ma.group(1));\r
+                               t += (CommonUtils.isLateNight(t)?(24):(0));\r
+                               keyword.alKeyword_plane.add(String.format("%02d%s", t, ma.group(2)));\r
+                               //System.out.println(keyword.alKeyword_plane.get(keyword.alKeyword_plane.size()-1));\r
+                       }\r
+                       else {\r
+                               keyword.alKeyword_plane.add("");\r
+                       }\r
+                       break;\r
+               default:\r
+                       keyword.alKeyword_plane.add(mR);\r
+                       break;\r
+               }\r
+               \r
+               keyword.alKeyword.add(TraceProgram.replacePop(mR));\r
+               keyword.alKeyword_pop.add(TraceProgram.replacePop(mR));\r
+               keyword.alContain.add(sC);\r
+       }\r
+       \r
+       \r
+       \r
+       //\r
+       private static int compareDT(String target, String limit)\r
+       {\r
+               Matcher ma = Pattern.compile("^(\\d\\d)(:\\d\\d)").matcher(target);\r
+               if (ma.find()) {\r
+                       Integer t = Integer.valueOf(ma.group(1));\r
+                       t += (CommonUtils.isLateNight(t)?(24):(0));\r
+                       String ts = String.format("%02d%s", t, ma.group(2));\r
+                       return ts.compareTo(limit);\r
+               }\r
+               else {\r
+                       return 0;\r
+               }\r
+       }\r
+       \r
+       private static String matchedString = null;\r
+       public static String getMatchedString() { return matchedString; }\r
+       \r
+       public static boolean isMatchKeyword(SearchKey keyword, String center, ProgDetailList tvd)\r
+       {\r
+               //\r
+               ArrayList<TargetId> sT = keyword.alTarget;\r
+               ArrayList<Pattern> mRe = keyword.alKeyword_regex;\r
+               //ArrayList<String> mR = keyword.alKeyword;\r
+               ArrayList<String> mRp = keyword.alKeyword_plane;\r
+               //ArrayList<String> mRpop = keyword.alKeyword_pop;\r
+               ArrayList<String> sC = keyword.alContain;\r
+               ArrayList<Integer> sL = keyword.alLength;\r
+               \r
+               matchedString = null;\r
+\r
+               //\r
+               Matcher mx = null;\r
+               boolean isOrMatch = false;\r
+               for (int x=0; x<sT.size(); x++) {\r
+                       //\r
+                       boolean isCurMatch = false;\r
+                       TargetId ti = sT.get(x);\r
+                       \r
+                       // 0:"番組名、内容に"\r
+                       // 1:"番組名に"\r
+                       if (ti == TargetId.TITLE || ti == TargetId.TITLEANDDETAIL) {\r
+                               if (keyword.getCaseSensitive() == false) {\r
+                                       mx = mRe.get(x).matcher(tvd.titlePop);\r
+                                       if (mx.find()) {\r
+                                               isCurMatch = true;\r
+                                               if (matchedString == null) {\r
+                                                       int srcLen = tvd.title.length();\r
+                                                       int keyLen = mx.group(1).length();\r
+                                                       int keyIdx = tvd.titlePop.indexOf(mx.group(1));\r
+                                                       char[] ch = tvd.title.toCharArray();\r
+                                                       int cnt = 0;\r
+                                                       int matchIdx = -1;\r
+                                                       int matchLen = 0;\r
+                                                       for (int i=0; i<srcLen; i++) {\r
+                                                               if ( ! TraceProgram.isOmittedChar(ch[i])) {\r
+                                                                       cnt++;\r
+                                                               }\r
+                                                               if (matchIdx < 0 && keyIdx == cnt-1) {\r
+                                                                       matchIdx = i;\r
+                                                                       cnt = 0;\r
+                                                               }\r
+                                                               if (matchIdx >= 0 && cnt < keyLen) {\r
+                                                                       matchLen++;\r
+                                                               }\r
+                                                       }\r
+                                                       matchedString = tvd.title.substring(matchIdx,matchIdx+matchLen);\r
+                                               }\r
+                                       }\r
+                               }\r
+                               else {\r
+                                       if (tvd.title.indexOf(mRp.get(x)) >= 0) {\r
+                                               isCurMatch = true;\r
+                                               if (matchedString == null) {\r
+                                                       matchedString = mRp.get(x);\r
+                                               }\r
+                                       }\r
+                               }\r
+                       }\r
+                       \r
+                       // 0:"番組名、内容に"\r
+                       // 2:"番組内容に"\r
+                       if (ti == TargetId.DETAIL || ti == TargetId.TITLEANDDETAIL) {\r
+                               if (keyword.getCaseSensitive() == false) {\r
+                                       mx = mRe.get(x).matcher(tvd.detailPop);\r
+                                       if (mx.find()) {\r
+                                               isCurMatch = true;\r
+                                       }\r
+                               }\r
+                               else {\r
+                                       if (tvd.detail.indexOf(mRp.get(x)) >= 0) {\r
+                                               isCurMatch = true;\r
+                                       }\r
+                               }\r
+                       }\r
+\r
+                       switch ( ti ) {\r
+                       case CHANNEL:   // 3\r
+                               if (keyword.getCaseSensitive() == false) {\r
+                                       mx = mRe.get(x).matcher(center);\r
+                                       if (mx.find()) {\r
+                                               isCurMatch = true;\r
+                                       }\r
+                               }\r
+                               else {\r
+                                       if (center.indexOf(mRp.get(x)) >= 0) {\r
+                                               isCurMatch = true;\r
+                                       }\r
+                               }\r
+                               break;\r
+                       \r
+                       case GENRE:     //4,15\r
+                               ProgGenre gr = ProgGenre.get(mRp.get(x));\r
+                               if ( gr != null ) {\r
+                                       isCurMatch = tvd.isEqualsGenre(gr, null);\r
+                               }\r
+                               break;\r
+                       case SUBGENRE:\r
+                               ProgSubgenre sgr = ProgSubgenre.get(mRp.get(x));\r
+                               if ( sgr != null ) {\r
+                                       isCurMatch = tvd.isEqualsGenre(null, sgr);\r
+                               }\r
+                               break;\r
+                       \r
+                       case NEW:       // 5,6,7,8,12,13\r
+                               if (tvd.flag == ProgFlags.NEW) {\r
+                                       isCurMatch = true;\r
+                               }\r
+                               break;\r
+                       case LAST:\r
+                               if (tvd.flag == ProgFlags.LAST) {\r
+                                       isCurMatch = true;\r
+                               }\r
+                               break;\r
+                       case REPEAT:\r
+                               for (ProgOption o : tvd.getOption()) {\r
+                                       if (o == ProgOption.REPEAT) {\r
+                                               isCurMatch = true;\r
+                                               break;\r
+                                       }\r
+                               }\r
+                               break;\r
+                       case FIRST:\r
+                               for (ProgOption o : tvd.getOption()) {\r
+                                       if (o == ProgOption.FIRST) {\r
+                                               isCurMatch = true;\r
+                                               break;\r
+                                       }\r
+                               }\r
+                               break;\r
+                       case SPECIAL:\r
+                               for (ProgOption o : tvd.getOption()) {\r
+                                       if (o == ProgOption.SPECIAL) {\r
+                                               isCurMatch = true;\r
+                                               break;\r
+                                       }\r
+                               }\r
+                               break;\r
+                       case RATING:\r
+                               for (ProgOption o : tvd.getOption()) {\r
+                                       if (o == ProgOption.RATING) {\r
+                                               isCurMatch = true;\r
+                                               break;\r
+                                       }\r
+                               }\r
+                               break;\r
+                       case NOSCRUMBLE:\r
+                               if (tvd.noscrumble == ProgScrumble.NOSCRUMBLE) {\r
+                                       isCurMatch = true;\r
+                               }\r
+                               break;\r
+                       case LIVE:\r
+                               for (ProgOption o : tvd.getOption()) {\r
+                                       if (o == ProgOption.LIVE) {\r
+                                               isCurMatch = true;\r
+                                               break;\r
+                                       }\r
+                               }\r
+                               break;\r
+                       case BILINGUAL:\r
+                               for (ProgOption o : tvd.getOption()) {\r
+                                       if (o == ProgOption.BILINGUAL) {\r
+                                               isCurMatch = true;\r
+                                               break;\r
+                                       }\r
+                               }\r
+                               break;\r
+                       case STANDIN:\r
+                               for (ProgOption o : tvd.getOption()) {\r
+                                       if (o == ProgOption.STANDIN) {\r
+                                               isCurMatch = true;\r
+                                               break;\r
+                                       }\r
+                               }\r
+                               break;\r
+                       \r
+                       case LENGTH:    // 9\r
+                               if (tvd.length >= sL.get(x)) {\r
+                                       isCurMatch = true;\r
+                               }\r
+                               break;\r
+\r
+                       case STARTA:    // 10,11,14\r
+                               if (compareDT(tvd.start,mRp.get(x)) >= 0) {\r
+                                       isCurMatch = true;\r
+                               }\r
+                               break;\r
+                       case STARTZ:\r
+                               if (compareDT(tvd.start,mRp.get(x)) <= 0) {\r
+                                       isCurMatch = true;\r
+                               }\r
+                               break;\r
+                       case STARTDATETIME:\r
+                               mx = mRe.get(x).matcher(tvd.startDateTime);\r
+                               if (mx.find()) {\r
+                                       isCurMatch = true;\r
+                               }\r
+                               break;\r
+                               \r
+                       default:\r
+                               break;\r
+                       }\r
+                       \r
+                       \r
+                       //\r
+                       if (keyword.getCondition().equals("0")) {\r
+                               // 0:次のすべての条件に一致\r
+                               if (sC.get(x).equals("0") && isCurMatch == false) {\r
+                                       // 0:"を含む番組"\r
+                                       return(false);\r
+                               }\r
+                               else if (sC.get(x).equals("1") && isCurMatch == true) {\r
+                                       // 1:"を含む番組を除く"\r
+                                       return(false);\r
+                               }\r
+                       }\r
+                       else {\r
+                               // 1:次のいずれかの条件に一致\r
+                               if (sC.get(x).equals("0") && isCurMatch == true) {\r
+                                       // 0:"を含む番組"\r
+                                       isOrMatch = true;\r
+                               }\r
+                               else if (sC.get(x).equals("1") && isCurMatch == true) {\r
+                                       // 1:"を含む番組を除く"\r
+                                       \r
+                                       // ★★★ 特殊動作注意!番ナビスレ Part.11 No.274付近参照 ★★★\r
+                                       // 「含む ∪ 含む ∪ 含まない ∪ 含まない」ではなく\r
+                                       // 「( 含む ∪ 含む ) ∩ ~(含まない ∪ 含まない)」となる\r
+                                       \r
+                                       return(false);\r
+                               }\r
+                       }\r
+               }\r
+               \r
+               if (keyword.getCondition().equals("0")) {\r
+                       // 0:次のすべての条件に一致で、すべての条件に適合した場合\r
+                       return(true);\r
+               }\r
+               else {\r
+                       // 1:次のいずれかの条件に一致、の場合\r
+                       return(isOrMatch);\r
+               }\r
+       }\r
+       \r
+       \r
+       \r
+       // コンストラクタ\r
+       public SearchProgram() {\r
+               setSearchKeyFile("env"+File.separator+"keyword.xml");\r
+               setSearchKeyLabel("キーワード検索");\r
+       }\r
+}\r
diff --git a/TinyBannavi/src/tainavi/SearchResult.java b/TinyBannavi/src/tainavi/SearchResult.java
new file mode 100644 (file)
index 0000000..8d843b8
--- /dev/null
@@ -0,0 +1,122 @@
+package tainavi;\r
+\r
+import java.util.ArrayList;\r
+\r
+\r
+public class SearchResult extends TVProgramUtils implements TVProgram,Cloneable {\r
+\r
+       public void setDebug(boolean b) { debug = b; }\r
+       private static boolean debug = false;\r
+\r
+       /*******************************************************************************\r
+        * 種族の特性\r
+        ******************************************************************************/\r
+       \r
+       @Override\r
+       public String getTVProgramId() { return tvProgId; }\r
+       private static final String tvProgId = "SearchResult";\r
+       \r
+       @Override\r
+       public boolean isAreaSelectSupported() { return false; }\r
+       \r
+       @Override\r
+       public ProgType getType() { return ProgType.SEARCHED; }\r
+       \r
+       @Override\r
+       public ProgSubtype getSubtype() { return ProgSubtype.NONE; }\r
+       \r
+       public PassedProgram clone() {\r
+               return (PassedProgram) super.clone();\r
+       }\r
+       \r
+       \r
+       /*******************************************************************************\r
+        * 個体の特性\r
+        ******************************************************************************/\r
+       \r
+       @Override\r
+       public int getTimeBarStart() { return 5; }\r
+       \r
+       // 検索結果を何件保存するか\r
+       public void setResultBufferMax(int n) { resultBufferMax = n; }\r
+       private int resultBufferMax = 5;\r
+       \r
+       \r
+       /*******************************************************************************\r
+        * 定数\r
+        ******************************************************************************/\r
+       \r
+       \r
+       /*******************************************************************************\r
+        * コンストラクタ\r
+        ******************************************************************************/\r
+       \r
+       public SearchResult() {\r
+               \r
+               super();\r
+               \r
+               // ガワ\r
+               crlist = new ArrayList<Center>();               // 番組情報を全部破棄\r
+               sortedcrlist = new ArrayList<Center>(); // 番組情報を全部破棄\r
+               pcenter = new ArrayList<ProgList>();    // 番組情報を全部破棄\r
+               \r
+       }\r
+\r
+\r
+       /*******************************************************************************\r
+        * 検索結果固有\r
+        ******************************************************************************/\r
+       \r
+       public ArrayList<ProgDetailList> getResultBuffer(final String label) {\r
+               \r
+               // 履歴保存先を指定サイズまで縮小\r
+               for ( int i=pcenter.size(); i>=resultBufferMax && i>1; i-- ) {\r
+                       crlist.remove(i-1);\r
+                       pcenter.remove(i-1);\r
+               }\r
+               \r
+               // 追加\r
+               {\r
+                       Center srchcr = new Center();\r
+                       srchcr.setCenter(label);\r
+                       \r
+                       ProgList srchpl = new ProgList();\r
+                       srchpl.enabled = true;\r
+                       srchpl.Center = srchcr.getCenter();\r
+                       srchpl.pdate = new ArrayList<ProgDateList>();\r
+                       \r
+                       ProgDateList srchpdt = new ProgDateList();\r
+                       srchpdt.Date = CommonUtils.getDateTime(CommonUtils.getCalendar("2999/12/31 23:59"));\r
+                       srchpl.pdate.add(srchpdt);\r
+                       \r
+                       crlist.add(0,srchcr);\r
+                       //sortedcrlist.add(srchcr);\r
+                       pcenter.add(0,srchpl);\r
+               }\r
+               \r
+               return pcenter.get(0).pdate.get(0).pdetail;\r
+       }\r
+\r
+       public int getResultBufferSize() { return pcenter.size(); }\r
+       \r
+       public String getLabel(int index) { return pcenter.get(index).Center; }\r
+       \r
+       public ArrayList<ProgDetailList> getResult(int index) { return pcenter.get(index).pdate.get(0).pdetail; }\r
+       \r
+       /*******************************************************************************\r
+        * 本体\r
+        ******************************************************************************/\r
+       \r
+       @Override\r
+       public void loadProgram(String areaCode, boolean force) {\r
+       }\r
+\r
+       @Override\r
+       public void loadAreaCode() {\r
+       }\r
+\r
+       @Override\r
+       public void loadCenter(String code, boolean force) {\r
+       }\r
+\r
+}\r
diff --git a/TinyBannavi/src/tainavi/SemLocker.java b/TinyBannavi/src/tainavi/SemLocker.java
new file mode 100644 (file)
index 0000000..3f126a0
--- /dev/null
@@ -0,0 +1,33 @@
+package tainavi;\r
+\r
+import java.util.concurrent.Semaphore;\r
+\r
+/**\r
+ * {@link Locker}を{@link Semaphore}をつかって実装。しかし使えなかった。\r
+ * @since 3.15.4β\r
+ */\r
+public class SemLocker extends Locker {\r
+\r
+       private Semaphore sem;\r
+       \r
+       public void unlock() {\r
+               sem.release(1);\r
+       }\r
+       \r
+       public boolean waitfor() {\r
+               try {\r
+                       sem.acquire();\r
+                       return true;\r
+               }\r
+               catch (InterruptedException e) {\r
+                       System.err.println("[ERROR] Semaphore: "+e.toString());\r
+               }\r
+               return false;\r
+       }\r
+\r
+       public SemLocker() {\r
+               super();\r
+               sem = new Semaphore(0);\r
+       }\r
+\r
+}\r
diff --git a/TinyBannavi/src/tainavi/StatusTextArea.java b/TinyBannavi/src/tainavi/StatusTextArea.java
new file mode 100644 (file)
index 0000000..2c6b1c7
--- /dev/null
@@ -0,0 +1,19 @@
+package tainavi;\r
+\r
+public interface StatusTextArea {\r
+\r
+       public void clear();\r
+       \r
+       /**\r
+        * 基本形\r
+        * @see #appendMessage(String)\r
+        * @see #appendError(String)\r
+        */\r
+       //public void append(String message);\r
+       public void appendMessage(String message);\r
+       public void appendError(String message);\r
+\r
+       public void setVisible(boolean b);\r
+       public int getRows();\r
+       public void setRows(int rows);\r
+}\r
diff --git a/TinyBannavi/src/tainavi/StatusWindow.java b/TinyBannavi/src/tainavi/StatusWindow.java
new file mode 100644 (file)
index 0000000..179bc3e
--- /dev/null
@@ -0,0 +1,17 @@
+package tainavi;\r
+\r
+public interface StatusWindow {\r
+       \r
+       public void clear();\r
+       \r
+       /**\r
+        * 基本形\r
+        * @see #appendMessage(String)\r
+        * @see #appendError(String)\r
+        */\r
+       public void append(String message);\r
+       public void appendMessage(String message);\r
+       public void appendError(String message);\r
+       \r
+       public void setVisible(boolean b);\r
+}\r
diff --git a/TinyBannavi/src/tainavi/SwingBackgroundWorker.java b/TinyBannavi/src/tainavi/SwingBackgroundWorker.java
new file mode 100644 (file)
index 0000000..9617715
--- /dev/null
@@ -0,0 +1,88 @@
+package tainavi;\r
+\r
+import java.beans.PropertyChangeEvent;\r
+import java.beans.PropertyChangeListener;\r
+import java.util.concurrent.ExecutionException;\r
+\r
+import javax.swing.SwingWorker;\r
+\r
+/**\r
+ * {@link BackgroundWorker}を{@link SwingWorker}をつかって実装\r
+ * @since 3.15.4β\r
+ */\r
+public abstract class SwingBackgroundWorker extends BackgroundWorker {\r
+\r
+       public static void setDebug(boolean b) { debug = b; }\r
+       private static boolean debug = false;\r
+       \r
+       // 抽象メソッド\r
+       protected abstract Object doWorks() throws Exception;   // 主な処理\r
+       protected abstract void doFinally();                                    // 終了時に1回だけ実行する処理\r
+       \r
+       // 本体\r
+       private boolean locking = true; // falseならブロッキングしなくてもいいよ\r
+\r
+       private SwingWorker<Object, Object> worker = null;\r
+       \r
+       /*\r
+        * コンストラクタ\r
+        */\r
+       public SwingBackgroundWorker(final boolean locking) {\r
+               this.locking = locking;\r
+       }\r
+       \r
+       /*\r
+        * メソッド\r
+        */\r
+       \r
+       public void execute() {\r
+               \r
+               final Locker lock = new SwingLocker();\r
+               \r
+               worker = new SwingWorker<Object, Object>() {\r
+                       @Override\r
+                       protected Object doInBackground() throws Exception {\r
+                               Object retval = doWorks();\r
+                               //if (debug) System.out.println("DOWORK");\r
+                               return retval;\r
+                       }\r
+                       \r
+                       @Override\r
+                       protected void done() {\r
+                               //if (debug) System.out.println("DONE");\r
+                               doFinally();\r
+                               if (locking) lock.unlock();\r
+                       }\r
+               };\r
+               \r
+               if (debug) {\r
+                       worker.addPropertyChangeListener(new PropertyChangeListener() {\r
+                               @Override\r
+                               public void propertyChange(PropertyChangeEvent e) {\r
+                                       System.out.println("SwingBackgroundWorker CHANGE "+e.getPropertyName()+"="+e.getOldValue()+"->"+e.getNewValue());\r
+                               }\r
+                       });\r
+               }\r
+               \r
+               worker.execute();\r
+               \r
+               if (locking) lock.waitfor();\r
+               \r
+               // Semaphoreは、親スレッドが止まってしまってdone()がディスパッチされなくなり動作が停止するので使えない\r
+               // SwingWorker.get()は、Swingのディスパッチが止まってしって画面更新がなくなるのでバックグランド処理をしては使いづらい\r
+               // JDialog.setVisible(true)以外ありえないのか…?\r
+               //\r
+               // JRE6では\r
+               //  dialog.setModal(true); ではなく\r
+               //  dialog.setModalityType(ModalityType.DOCUMENT_MODAL); を使うことで\r
+               // 他のダイアログへのブロッキングが発生しない!これはいい!!\r
+       }\r
+       \r
+       public Object get() throws InterruptedException, ExecutionException {\r
+               return worker.get();\r
+       }\r
+       \r
+       public boolean cancel(boolean mayInterruptIfRunning) {\r
+               return worker.cancel(mayInterruptIfRunning);\r
+       }\r
+}\r
diff --git a/TinyBannavi/src/tainavi/SwingLocker.java b/TinyBannavi/src/tainavi/SwingLocker.java
new file mode 100644 (file)
index 0000000..e11e7c2
--- /dev/null
@@ -0,0 +1,48 @@
+package tainavi;\r
+\r
+import java.awt.Dialog.ModalityType;\r
+import java.awt.Frame;\r
+\r
+import javax.swing.JDialog;\r
+import javax.swing.SwingWorker;\r
+\r
+/**\r
+ * {@link Locker}を{@link SwingWorker}をつかって実装\r
+ * @since 3.15.4β\r
+ */\r
+public class SwingLocker extends Locker {\r
+\r
+       // クラス共有設定\r
+       private static Frame ownerframe = null;\r
+       public static void setOwner(Frame owner) { ownerframe = owner; }\r
+\r
+       private JDialog dialog;\r
+       \r
+       @Override\r
+       public void unlock() {\r
+               dialog.setVisible(false);\r
+       }\r
+       \r
+       @Override\r
+       public boolean waitfor() {\r
+               dialog.setVisible(true);\r
+               return true;\r
+       }\r
+\r
+       public SwingLocker() {\r
+               \r
+               super();\r
+               \r
+               if ( ownerframe != null ) {\r
+                       dialog = new JDialog(ownerframe);       // フォーカスが移動してしまうのが難\r
+               }\r
+               else {\r
+                       dialog = new JDialog();\r
+               }\r
+               //dialog.setModal(true);                        // モーダル\r
+               dialog.setModalityType(ModalityType.DOCUMENT_MODAL);    // 親しかブロックしないモーダル\r
+               dialog.setUndecorated(true);            // 見えないダイアログ\r
+               dialog.setBounds(0,0,0,0);\r
+               dialog.setEnabled(false);\r
+       }\r
+}\r
diff --git a/TinyBannavi/src/tainavi/Syobocal.java b/TinyBannavi/src/tainavi/Syobocal.java
new file mode 100644 (file)
index 0000000..46db8f0
--- /dev/null
@@ -0,0 +1,518 @@
+package tainavi;\r
+\r
+import java.io.File;\r
+import java.util.ArrayList;\r
+import java.util.Calendar;\r
+import java.util.GregorianCalendar;\r
+import java.util.regex.Matcher;\r
+import java.util.regex.Pattern;\r
+\r
+/**\r
+ * しょぼかるから番組表と放送局リストを取得する。プラグインではないよ。\r
+ */\r
+public class Syobocal extends TVProgramUtils implements TVProgram,Cloneable {\r
+\r
+       private final String thisEncoding = "UTF-8";\r
+       \r
+       public void setDebug(boolean b) { debug = b; }\r
+       \r
+       private boolean debug = false;\r
+       \r
+       private boolean rss2 = false;\r
+\r
+       /* 必須コード  - ここから */\r
+       \r
+       // 種族の特性\r
+       private static final String tvProgId = "Syobocal";\r
+       \r
+       //private final String progCacheFile = getProgDir()+File.separator+"syobocal.xml";\r
+       private final String centerFile = "env"+File.separator+"center."+getTVProgramId()+".xml";  \r
+       \r
+       private final String MSGID = "[しょぼかる] ";\r
+       private final String ERRID = "[ERROR]"+MSGID;\r
+       private final String DBGID = "[DEBUG]"+MSGID;\r
+       \r
+       @Override\r
+       public String getTVProgramId() { return tvProgId; }\r
+       \r
+       @Override\r
+       public boolean isAreaSelectSupported() { return false; }\r
+       \r
+       @Override\r
+       public ProgType getType() { return ProgType.SYOBO; }\r
+       public ProgSubtype getSubtype() { return ProgSubtype.NONE; }\r
+\r
+       @Override\r
+       public Syobocal clone() {\r
+               return (Syobocal) super.clone();\r
+       }\r
+\r
+       // 個体の特性\r
+       \r
+       //\r
+       @Override\r
+       public int getTimeBarStart() {return 5;}\r
+\r
+       private int getDogDays() { return ((getExpandTo8())?(8):(7)); }\r
+       \r
+       //\r
+       @Override\r
+       public void loadProgram(String areaCode, boolean force) {\r
+\r
+               String progCacheFile = null;\r
+               if ( rss2 ) {\r
+                       progCacheFile = getProgDir()+File.separator+"syobocal.xml";\r
+               }\r
+               else {\r
+                       progCacheFile = getProgDir()+File.separator+"syobocal.rss";\r
+               }\r
+               \r
+               // 新しい番組データの入れ物を作る\r
+               ArrayList<ProgList> newplist = new ArrayList<ProgList>();\r
+\r
+               int cnt = 0;\r
+               \r
+               try {\r
+                       String response = null;\r
+                       \r
+                       String cirtDateTimeYMD = CommonUtils.getCritDateTime().replaceAll("[/: ]", "");\r
+                       \r
+                       File f = new File(progCacheFile);\r
+                       if (force == true ||\r
+                                       (f.exists() == true && isCacheOld(progCacheFile) == true) ||\r
+                                       (f.exists() == false && isCacheOld(null) == true)) {\r
+                               \r
+                               String url = null;\r
+                               if ( rss2 ) {\r
+                                       url = "http://cal.syoboi.jp/rss2.php?start="+cirtDateTimeYMD+"&days="+getDogDays()+"&titlefmt=$(Flag)^^^$(FlagW)^^^$(Cat)^^^$(ChName)^^^$(EdTime)^^^$(Title)^^^$(SubTitleB)";\r
+                               }\r
+                               else {\r
+                                       url = "http://cal.syoboi.jp/rss.php?start=today&count=1500&days=8&days="+getDogDays()+"&titlefmt=$(Flag)^^^$(FlagW)^^^$(Cat)^^^$(ChName)^^^$(EdTime)^^^$(Title)^^^$(SubTitleB)";\r
+                               }\r
+                               response = webToBuffer(url, thisEncoding, true);\r
+                               if ( response == null ) {\r
+                                       reportProgress(ERRID+"RSS2.0(オンライン)の取得に失敗しました: "+url);\r
+                                       return;\r
+                               }\r
+                               \r
+                               reportProgress(MSGID+"RSS2.0(オンライン)を取得しました: "+url);\r
+                               CommonUtils.write2file(progCacheFile, response);\r
+                       }\r
+                       else if (f.exists()) {\r
+                               //\r
+                               response = CommonUtils.read4file(progCacheFile, true);\r
+                               if ( response == null ) {\r
+                                       reportProgress(ERRID+"RSS2.0(キャッシュ)の取得に失敗しました: "+progCacheFile);\r
+                                       return;\r
+                               }\r
+                               reportProgress(MSGID+"RSS2.0(キャッシュ)を取得しました: "+progCacheFile);\r
+                       }\r
+                       else {\r
+                               reportProgress(ERRID+"RSS2.0(キャッシュ)がみつかりません: "+progCacheFile);\r
+                               return;\r
+                       }\r
+                       \r
+                       // 情報解析\r
+                       \r
+                       Matcher ma = Pattern.compile("<item(.+?)</item>",Pattern.DOTALL).matcher(response);\r
+                       while (ma.find()) {\r
+\r
+                               // 入れ物\r
+                               ProgDetailList pDetail = new ProgDetailList();\r
+                               \r
+                               // <title>金曜ロードショー ヱヴァンゲリヲン新劇場版:破 TV版</title> \r
+                               Matcher mb = Pattern.compile("<title>(.+?)</title>",Pattern.DOTALL).matcher(ma.group(1));\r
+                               if ( ! mb.find()) {\r
+                                       continue;\r
+                               }\r
+                               \r
+                               String[] t = mb.group(1).split("\\^\\^\\^",7);\r
+                               \r
+                               if (t.length < 7) {\r
+                                       System.err.println(ERRID+"書式が不正: "+mb.group(1));\r
+                               }\r
+                               \r
+                               pDetail.title = CommonUtils.unEscape(t[5]);\r
+                               \r
+                               pDetail.detail = CommonUtils.unEscape(t[6]);\r
+                               if ( pDetail.detail.matches("^#1$|^#1[^0-9].*$") ) {\r
+                                       // まあなにもしなくていいか\r
+                               }\r
+                               \r
+                               if (t[0] != null && t[0].length() > 0) {\r
+                                       int flag = Integer.valueOf(t[0]);\r
+                                       if (flag != 0) {\r
+                                               if ((flag & 0x01) != 0x00) {\r
+                                                       pDetail.addOption(ProgOption.SPECIAL);\r
+                                                       flag ^= 0x01;\r
+                                               }\r
+                                               if ((flag & 0x02) != 0x00) {\r
+                                                       pDetail.flag = ProgFlags.NEW;\r
+                                                       flag ^= 0x02;\r
+                                               }\r
+                                               if ((flag & 0x04) != 0x00) {\r
+                                                       pDetail.flag = ProgFlags.LAST;\r
+                                                       flag ^= 0x04;\r
+                                               }\r
+                                               if ((flag & 0x08) != 0x00) {\r
+                                                       pDetail.addOption(ProgOption.REPEAT);\r
+                                                       flag ^= 0x08;\r
+                                               }\r
+                                               if (flag != 0) {\r
+                                                       System.out.println(DBGID+"未対応のマーク: "+flag);\r
+                                               }\r
+                                       }\r
+                               }\r
+                               if (t[1] != null && t[1].length() > 0) {\r
+                                       int flagw = Integer.valueOf(t[1]);\r
+                                       if (flagw != 0) {\r
+                                               if ((flagw & 0x01) != 0x00) {\r
+                                                       if (pDetail.flag != ProgFlags.NEW && ! pDetail.isOptionEnabled(ProgOption.SPECIAL)) {\r
+                                                               pDetail.addOption(ProgOption.MOVED);    // 新番組や特番なら(移)はいらんやろ\r
+                                                       }\r
+                                                       flagw ^= 0x01;\r
+                                               }\r
+                                               if (flagw != 0) {\r
+                                                       System.out.println(DBGID+"未対応の警告フラグ: "+flagw);\r
+                                               }\r
+                                       }\r
+                               }\r
+                               //  <tv:genre>アニメ(終了/再放送)</tv:genre>\r
+                               pDetail.genre = null;\r
+                               pDetail.subgenre = null;\r
+                               pDetail.genrelist = new ArrayList<TVProgram.ProgGenre>();\r
+                               pDetail.subgenrelist = new ArrayList<TVProgram.ProgSubgenre>();\r
+                               boolean anime_etc = true;\r
+                               \r
+                               if (t[2] != null && t[2].length() > 0) {\r
+                                       // ジャンル\r
+                                       if (t[2].equals("1") || t[2].equals("10")  || t[2].equals("5")) {\r
+                                               // 1:アニメ 10:アニメ(終了/再放送) 5:アニメ関連\r
+                                       }\r
+                                       else if (t[2].equals("7")) {\r
+                                               // 7:OVA\r
+                                               pDetail.genre = ProgGenre.ANIME;\r
+                                               pDetail.subgenre = ProgSubgenre.ANIME_KOKUNAI;\r
+                                               pDetail.genrelist.add(ProgGenre.ANIME);\r
+                                               pDetail.subgenrelist.add(ProgSubgenre.ANIME_KOKUNAI);\r
+                                               anime_etc = false;\r
+                                       }\r
+                                       else if (t[2].equals("4")) {\r
+                                               // 4:特撮\r
+                                               pDetail.genre = ProgGenre.ANIME;\r
+                                               pDetail.subgenre = ProgSubgenre.ANIME_TOKUSATSU;\r
+                                               pDetail.genrelist.add(ProgGenre.ANIME);\r
+                                               pDetail.subgenrelist.add(ProgSubgenre.ANIME_TOKUSATSU);\r
+                                               anime_etc = false;\r
+                                       }\r
+                                       else if (t[2].equals("8")) {\r
+                                               // 8:映画\r
+                                               pDetail.genre = ProgGenre.MOVIE;\r
+                                               pDetail.subgenre = ProgSubgenre.MOVIE_ETC;\r
+                                               pDetail.genrelist.add(ProgGenre.MOVIE);\r
+                                               pDetail.subgenrelist.add(ProgSubgenre.MOVIE_ETC);\r
+                                               pDetail.removeOption(ProgOption.MOVED); // 映画なら(移)はいらんやろ\r
+                                       }\r
+                                       else if (t[2].equals("0")) {\r
+                                               // 0:その他\r
+                                               pDetail.genrelist.add(ProgGenre.NOGENRE);\r
+                                               pDetail.subgenrelist.add(ProgSubgenre.NOGENRE_ETC);\r
+                                       }\r
+                                       else if (t[2].equals("3") || t[2].equals("2") || t[2].equals("6")) {\r
+                                               // 3:テレビ 2:ラジオ 6:メモ\r
+                                       }\r
+                                       else {\r
+                                               System.out.println(DBGID+"未対応のジャンル: "+t[2]);\r
+                                       }\r
+                                       \r
+                                       // 最後に\r
+                                       if (pDetail.genre == null) {\r
+                                               pDetail.genre = ProgGenre.ANIME;\r
+                                               pDetail.subgenre = ProgSubgenre.ANIME_ETC;\r
+                                       }\r
+                                       if (pDetail.genrelist.size() == 0 || anime_etc) {\r
+                                               pDetail.genrelist.add(ProgGenre.ANIME);\r
+                                               pDetail.subgenrelist.add(ProgSubgenre.ANIME_ETC);\r
+                                       }\r
+                               }\r
+                               \r
+                               // <dc:publisher>日本テレビ</dc:publisher>\r
+                               if ( t[3] == null || t[3].length() == 0) {\r
+                                       System.err.println(ERRID+"放送局名がない: "+mb.group(1));\r
+                                       continue;\r
+                               }\r
+                               \r
+                               // ベアな放送局名と、ChannelConvert.datを適用した結果の放送局名\r
+                               String location = CommonUtils.unEscape(t[3]);\r
+                               String modifiedloc = getChName(location);\r
+\r
+                               // <tv:startDatetime>2011-08-29T00:00:00+09:00</tv:startDatetime>\r
+                               if ( rss2 ) {\r
+                                       mb = Pattern.compile("<pubDate>(.+?)\\+09:00</pubDate>",Pattern.DOTALL).matcher(ma.group(1));\r
+                               }\r
+                               else {\r
+                                       mb = Pattern.compile("<tv:startDatetime>(.+?)\\+09:00</tv:startDatetime>",Pattern.DOTALL).matcher(ma.group(1));\r
+                               }\r
+                               if ( ! mb.find()) {\r
+                                       System.err.println(ERRID+"開始日時がない");\r
+                                       continue;\r
+                               }\r
+                               GregorianCalendar ca = CommonUtils.getCalendar(mb.group(1));\r
+                               if ( ca == null ) {\r
+                                       System.err.println(ERRID+"開始日時が不正: "+mb.group(1));\r
+                                       continue;\r
+                               }\r
+                               \r
+                               pDetail.startDateTime = CommonUtils.getDateTime(ca);\r
+                               pDetail.start = pDetail.startDateTime.substring(11, 16);\r
+                               pDetail.accurateDate = CommonUtils.getDate(ca);\r
+                               \r
+                               if ( t[4] == null || t[4].length() == 0) {\r
+                                       System.err.println(ERRID+"終了時刻がない: "+mb.group(1));\r
+                                       continue;\r
+                               }\r
+                               \r
+                               GregorianCalendar cz = CommonUtils.getCalendar(CommonUtils.getDate(ca)+" "+t[4]);\r
+                               if (ca.compareTo(cz) > 0) {\r
+                                       cz.add(Calendar.DAY_OF_MONTH, 1);\r
+                               }\r
+                               \r
+                               pDetail.endDateTime = CommonUtils.getDateTime(cz);\r
+                               pDetail.end = pDetail.endDateTime.substring(11, 16);\r
+                               \r
+                               // 24:00~28:59までは前日なんだニャー\r
+                               if (CommonUtils.isLateNight(ca.get(Calendar.HOUR_OF_DAY))) {\r
+                                       ca.add(Calendar.DAY_OF_MONTH, -1);\r
+                               }\r
+                               \r
+                               // 番組情報を入れるべき日付\r
+                               String progdate = CommonUtils.getDate(ca);\r
+                               \r
+                               // <description>HD放送</description>\r
+                               mb = Pattern.compile("<description>(.+?)</description>").matcher(ma.group(1));\r
+                               if (mb.find()) {\r
+                                       pDetail.detail += " <" + CommonUtils.unEscape(mb.group(1)) + ">";\r
+                                       if (pDetail.detail.contains("無料放送")) {\r
+                                               pDetail.noscrumble = ProgScrumble.NOSCRUMBLE;\r
+                                       }\r
+                                       if (pDetail.detail.contains("先行放送")) {\r
+                                               pDetail.addOption(ProgOption.PRECEDING);\r
+                                       }\r
+                                       if (pDetail.detail.contains("変更の可能性")) {\r
+                                               pDetail.extension = true;\r
+                                       }\r
+                                       if ( pDetail.detail.contains("繰り下げ") ) {\r
+                                               pDetail.extension = true;\r
+                                       }\r
+                                       if (pDetail.detail.contains("副音声")) {\r
+                                               pDetail.addOption(ProgOption.MULTIVOICE);\r
+                                       }\r
+                               }\r
+                               \r
+                               // <link>http://cal.syoboi.jp/tid/44#198593</link> \r
+                               mb = Pattern.compile("<link>(.+?)</link>",Pattern.DOTALL).matcher(ma.group(1));\r
+                               if (mb.find()) {\r
+                                       pDetail.link = mb.group(1);\r
+                                       if ( ! ContentIdSyobo.isValid(pDetail.link)) {\r
+                                               System.out.println(DBGID+"TIDとPIDが取得できない: "+pDetail.link);\r
+                                       }\r
+                               }\r
+                               \r
+                               // 追加詳細\r
+                               pDetail.setGenreStr();\r
+                               \r
+                               // 統合\r
+                               {\r
+                                       // 放送局が存在するか\r
+                                       ProgList prog = null;\r
+                                       for (ProgList pl : newplist) {\r
+                                               if (pl.Center.equals(modifiedloc)) {\r
+                                                       prog = pl;\r
+                                                       break;\r
+                                               }\r
+                                       }\r
+                                       if (prog == null) {\r
+                                               // 番組表\r
+                                               prog = new ProgList();\r
+                                               prog.Center = modifiedloc;\r
+                                               prog.enabled = true;\r
+                                               prog.pdate = new ArrayList<ProgDateList>();\r
+                                               \r
+                                               newplist.add(prog);\r
+                                       }\r
+                                       \r
+                                       // 日付が存在するか\r
+                                       ProgDateList pCenter = null;\r
+                                       for (ProgDateList pcl : prog.pdate) {\r
+                                               if (pcl.Date.equals(progdate)) {\r
+                                                       pCenter = pcl;\r
+                                                       break;\r
+                                               }\r
+                                       }\r
+                                       if (pCenter == null) {\r
+                                               pCenter = new ProgDateList();\r
+                                               pCenter.Date = progdate;\r
+                                               pCenter.pdetail = new ArrayList<ProgDetailList>();\r
+                                               prog.pdate.add(pCenter);\r
+                                       }\r
+                                       \r
+                                       // 連結\r
+                                       pCenter.pdetail.add(pDetail);\r
+                                       \r
+                                       cnt++;\r
+                               }\r
+                               \r
+                               //\r
+                               pDetail.splitted_title = pDetail.title;\r
+                               pDetail.splitted_detail = pDetail.detail;\r
+                               \r
+                               // 詳細を登録する\r
+                               pDetail.titlePop = TraceProgram.replacePop(pDetail.title);\r
+                               pDetail.SearchStrKeys = TraceProgram.splitKeys(pDetail.titlePop);\r
+                               pDetail.detailPop = TraceProgram.replacePop(pDetail.detail);\r
+                               pDetail.length = Integer.valueOf(CommonUtils.getRecMin(pDetail.start.substring(0,2),pDetail.start.substring(3,5),pDetail.end.substring(0,2),pDetail.end.substring(3,5)));\r
+                       }\r
+               }\r
+               catch (Exception e) {\r
+                       e.printStackTrace();\r
+                       return;\r
+               }\r
+               \r
+               pcenter = newplist;\r
+               System.out.println(DBGID+"番組の数: "+cnt);\r
+       }\r
+\r
+       /* ここまで */\r
+\r
+       /*\r
+        * ★★★★★★★★★★★★★★★★★★★★★★★★★★★★★★★\r
+        * ★★★★★ 放送地域を取得する(TVAreaから降格)-ここから ★★★★★\r
+        * ★★★★★★★★★★★★★★★★★★★★★★★★★★★★★★★\r
+        */\r
+\r
+       @Override\r
+       public void loadAreaCode() {\r
+       }\r
+\r
+       @Override\r
+       public void saveAreaCode() {\r
+       }\r
+       \r
+       @Override\r
+       public String getCode(String area) {\r
+               return "1";\r
+       }\r
+\r
+       @Override\r
+       public String getDefaultArea() {\r
+               return "しょぼかる";\r
+       }\r
+\r
+       @Override\r
+       public String getSelectedArea() {\r
+               return "しょぼかる";\r
+       }\r
+\r
+       @Override\r
+       public String getSelectedCode() {\r
+               return "1";\r
+       }\r
+       \r
+       /*\r
+        * ★★★★★★★★★★★★★★★★★★★★★★★★★★★★★★★\r
+        * ★★★★★ 放送地域を取得する(TVAreaから降格)-ここまで ★★★★★\r
+        * ★★★★★★★★★★★★★★★★★★★★★★★★★★★★★★★\r
+        */\r
+\r
+       /*\r
+        * ★★★★★★★★★★★★★★★★★★★★★★★★★★★★★★★\r
+        * ★★★★★ 放送局を選択する(TVCenterから降格)-ここから ★★★★★\r
+        * ★★★★★★★★★★★★★★★★★★★★★★★★★★★★★★★\r
+        */\r
+\r
+       @Override\r
+       public void loadCenter(String code, boolean force) {\r
+               \r
+               if ( code == null ) {\r
+                       System.out.println(ERRID+"地域コードがnullです.");\r
+                       return;\r
+               }\r
+               \r
+               if ( ! force && new File(centerFile).exists() ) {\r
+                       // NOT FORFCEならキャッシュからどうぞ\r
+                       @SuppressWarnings("unchecked")\r
+                       ArrayList<Center> tmp = (ArrayList<Center>) CommonUtils.readXML(centerFile);\r
+                       if ( tmp != null ) {\r
+                               crlist = tmp;\r
+                               return;\r
+                       }\r
+               }\r
+               \r
+               String uri = "http://cal.syoboi.jp/mng?Action=ShowChList";\r
+               String response = webToBuffer(uri,thisEncoding,true);\r
+               if ( response == null ) {\r
+                       System.err.println(ERRID+"放送局リストの取得に失敗: "+uri);\r
+                       return;\r
+               }\r
+               System.out.println(MSGID+"放送局リストを取得: "+uri);\r
+               \r
+               Matcher ma = Pattern.compile("<table class=\"tframe output\".*?>(.+?)</table>",Pattern.DOTALL).matcher(response);\r
+               if ( ! ma.find() ) {\r
+                       System.err.println(ERRID+"放送局情報がない: "+uri);\r
+                       return;\r
+               }\r
+               \r
+               // 新しい放送局リストの入れ物を作る\r
+               ArrayList<Center> newcrlist = new ArrayList<Center>();\r
+               \r
+               int cnt = 1;\r
+               Matcher mb = Pattern.compile("<tr>(.+?)</tr>",Pattern.DOTALL).matcher(ma.group(1));\r
+               while ( mb.find() ) {\r
+                       String[] d = mb.group(1).split("<.*?>",9);\r
+                       if ( d.length != 9 ) {\r
+                               System.err.println(ERRID+"書式不正(カラム数が足りない): "+d.length);\r
+                               continue;\r
+                       }\r
+                       if ( ! d[1].matches("^\\d+$") ) {\r
+                               continue;\r
+                       }\r
+                       \r
+                       // 放送局リスト\r
+                       Center cr = new Center();\r
+                       cr.setLink(d[5]);\r
+                       cr.setAreaCode("1");\r
+                       cr.setCenterOrig(CommonUtils.unEscape(d[7]));\r
+                       //cr.setCenter(this.chconv.get(cr.getCenterOrig()));    // ChannelConvert.datで入れ替えたもの\r
+                       cr.setType("");\r
+                       cr.setEnabled(true);\r
+                       cr.setOrder(cnt++);\r
+                       \r
+                       newcrlist.add(cr);\r
+                       \r
+                       if (debug) System.out.println(MSGID+"放送局を追加: "+cr.getCenterOrig()+"  ->  "+cr.getCenter());\r
+               }\r
+               \r
+               if ( newcrlist.size() == 0 ) {\r
+                       System.err.println(ERRID+"放送局情報の取得結果が0件だったため情報を更新しません");\r
+                       return;\r
+               }\r
+\r
+               System.out.println(DBGID+"放送局の数: "+newcrlist.size());\r
+               \r
+               crlist = newcrlist;\r
+               attachChFilters();\r
+               setSortedCRlist();\r
+               CommonUtils.writeXML(centerFile, crlist);\r
+       }\r
+       \r
+       @Override\r
+       public boolean saveCenter() {\r
+               return false;\r
+       }\r
+       \r
+       /*\r
+        * ★★★★★★★★★★★★★★★★★★★★★★★★★★★★★★★\r
+        * ★★★★★ 放送局を選択する(TVCenterから降格)-ここまで ★★★★★\r
+        * ★★★★★★★★★★★★★★★★★★★★★★★★★★★★★★★\r
+        */\r
+}\r
diff --git a/TinyBannavi/src/tainavi/TVProgram.java b/TinyBannavi/src/tainavi/TVProgram.java
new file mode 100644 (file)
index 0000000..b26e2cd
--- /dev/null
@@ -0,0 +1,374 @@
+package tainavi;\r
+\r
+import java.util.ArrayList;\r
+\r
+/**\r
+ * Web番組表の設定や、実際の番組情報などを保持するクラスです。\r
+ * @see TVProgramList\r
+ * @see TVProgramUtils\r
+ * @see TVProgramIterator\r
+ */\r
+public interface TVProgram {\r
+\r
+       // 定数\r
+       public enum ProgScrumble { NONE, NOSCRUMBLE, SCRUMBLED };\r
+       public enum ProgFlags { NOFLAG, NEW, LAST };\r
+       public enum ProgType { PROG, SYOBO, PASSED, PICKED, SEARCHED, OTHERS };\r
+       public enum ProgSubtype { TERRA, CS, CS2, CS3, RADIO, NONE };\r
+\r
+       /**\r
+        *  NOSCRUMBLEは間違って(?)ProgScrumbleを使うこととなってしまったのだ。ここにあるのはマーク表示のための一覧用なのだ。\r
+        */\r
+       public enum ProgOption { HIDDEN_NEW, HIDDEN_LAST, HIDDEN_NOSCRUMBLE, FIRST, REPEAT, LIVE, SPECIAL, NOSYOBO, SUBTITLE, BILINGUAL, STANDIN, PV, MULTIVOICE, DATA, SURROUND, NEWARRIVAL, MODIFIED, NONREPEATED, MOVED, PRECEDING, RATING };\r
+\r
+       public static enum ProgGenre { \r
+               NEWS            ("ニュース/報道",         "0"),\r
+               SPORTS          ("スポーツ",                        "1"),\r
+               VARIETYSHOW     ("情報/ワイドショー",   "2"),\r
+               DORAMA          ("ドラマ",                   "3"), \r
+               MUSIC           ("音楽",                              "4"),\r
+               VARIETY         ("バラエティー",          "5"),\r
+               MOVIE           ("映画",                              "6"), \r
+               ANIME           ("アニメ/特撮",            "7"), \r
+               DOCUMENTARY     ("ドキュメンタリー/教養",     "8"),\r
+               THEATER         ("劇場/公演",               "9"),\r
+               HOBBY           ("趣味/教育",               "A"),\r
+               WELFARE         ("福祉",                              "B"),\r
+               //KIDS          ("キッズ",                   "XXX"), // もとからなかったらしい\r
+               //EXTENTION     ("拡張",                      "E"),   // 非対応とする\r
+               NOGENRE         ("その他",                   "F");\r
+               \r
+               private String name;\r
+               private String iepg;\r
+               \r
+               private ProgGenre(String name, String iepg) {\r
+                       this.name = name;\r
+                       this.iepg = iepg;\r
+               }\r
+               \r
+               @Override\r
+               public String toString() {\r
+                       return(name);\r
+               }\r
+               \r
+               public String toIEPG() {\r
+                       return(iepg);\r
+               }\r
+               \r
+               /**\r
+                * ジャンル名文字列に一致するものを返す\r
+                */\r
+               public static ProgGenre get(String name) {\r
+                       for ( ProgGenre g : ProgGenre.values() ) {\r
+                               if ( g.name.equals(name) ) {\r
+                                       return g;\r
+                               }\r
+                       }\r
+                       return null;\r
+               }\r
+               \r
+               /**\r
+                * IEPGで\r
+                */\r
+               public static ProgGenre getByIEPG(String iepg) {\r
+                       for ( ProgGenre g : ProgGenre.values() ) {\r
+                               if ( g.iepg.equals(iepg) ) {\r
+                                       return g;\r
+                               }\r
+                       }\r
+                       return null;\r
+               }\r
+       };\r
+       \r
+       public static enum ProgSubgenre {\r
+               NEWS_TEIJI              (ProgGenre.NEWS,        "定時・総合",              "0"),\r
+               NEWS_TENKI              (ProgGenre.NEWS,        "天気",                       "1"),\r
+               NEWS_TOKUSHU    (ProgGenre.NEWS,        "特集・ドキュメント",  "2"),\r
+               NEWS_SEIJI              (ProgGenre.NEWS,        "政治・国会",              "3"),\r
+               NEWS_KEIZAI             (ProgGenre.NEWS,        "経済・市況",              "4"),\r
+               NEWS_KAIGAI             (ProgGenre.NEWS,        "海外・国際",              "5"),\r
+               NEWS_KAISETU    (ProgGenre.NEWS,        "解説",                       "6"),\r
+               NEWS_TOURON             (ProgGenre.NEWS,        "討論・会談",              "7"),\r
+               NEWS_HOUDOU             (ProgGenre.NEWS,        "報道特番",         "8"),\r
+               NEWS_LOCAL              (ProgGenre.NEWS,        "ローカル・地域",        "9"),\r
+               NEWS_KOTSU              (ProgGenre.NEWS,        "交通",                       "A"),\r
+               NEWS_ETC                (ProgGenre.NEWS,        "その他",                    "F"),\r
+               \r
+               SPORTS_NEWS             (ProgGenre.SPORTS,      "スポーツニュース",             "0"),\r
+               SPORTS_BASEBALL (ProgGenre.SPORTS,      "野球",                               "1"),\r
+               SPORTS_FOOTBALL (ProgGenre.SPORTS,      "サッカー",                 "2"),\r
+               SPORTS_GOLF             (ProgGenre.SPORTS,      "ゴルフ",                            "3"),\r
+               SPORTS_BALLGAME (ProgGenre.SPORTS,      "その他の球技",           "4"),\r
+               SPORTS_SUMO             (ProgGenre.SPORTS,      "相撲・格闘技",           "5"),\r
+               SPORTS_OLYNPIC  (ProgGenre.SPORTS,      "オリンピック・国際大会",    "6"),\r
+               SPORTS_MARATHON (ProgGenre.SPORTS,      "マラソン・陸上・水泳",       "7"),\r
+               SPORTS_MORTAR   (ProgGenre.SPORTS,      "モータースポーツ",             "8"),\r
+               SPORTS_MARINE   (ProgGenre.SPORTS,      "マリン・ウィンタースポーツ","9"),\r
+               SPORTS_KEIBA    (ProgGenre.SPORTS,      "競馬・公営競技",                "A"),\r
+               SPORTS_ETC              (ProgGenre.SPORTS,      "その他",                            "F"),\r
+               \r
+               VSHOW_GEINO             (ProgGenre.VARIETYSHOW, "芸能・ワイドショー",          "0"),\r
+               VSHOW_FASHION   (ProgGenre.VARIETYSHOW, "ファッション",                   "1"),\r
+               VSHOW_LIFE              (ProgGenre.VARIETYSHOW, "暮らし・住まい",                "2"),\r
+               VSHOW_HEALTH    (ProgGenre.VARIETYSHOW, "健康・医療",                      "3"),\r
+               VSHOW_SHOPPING  (ProgGenre.VARIETYSHOW, "ショッピング・通販",          "4"),\r
+               VSHOW_GOURMET   (ProgGenre.VARIETYSHOW, "グルメ・料理",           "5"),\r
+               VSHOW_EVENT             (ProgGenre.VARIETYSHOW, "イベント",                 "6"),\r
+               VSHOW_INFO              (ProgGenre.VARIETYSHOW, "番組紹介・お知らせ",  "7"),\r
+               VSHOW_ETC               (ProgGenre.VARIETYSHOW, "その他",                            "F"),\r
+               \r
+               DRAMA_KOKUNAI   (ProgGenre.DORAMA,      "国内ドラマ",      "0"),\r
+               DRAMA_KAIGAI    (ProgGenre.DORAMA,      "海外ドラマ",      "1"),\r
+               DRAMA_JIDAI             (ProgGenre.DORAMA,      "時代劇",    "2"),\r
+               DRAMA_ETC               (ProgGenre.DORAMA,      "その他",            "F"),\r
+               \r
+               MUSIC_KOKUNAI   (ProgGenre.MUSIC,       "国内ロック・ポップス",                       "0"),\r
+               MUSIC_KAIGAI    (ProgGenre.MUSIC,       "海外ロック・ポップス",                       "1"),\r
+               MUSIC_CLASSIC   (ProgGenre.MUSIC,       "クラシック・オペラ",                          "2"),\r
+               MUSIC_JAZZ              (ProgGenre.MUSIC,       "ジャズ・フュージョン",                       "3"),\r
+               MUSIC_KAYOKYOKU (ProgGenre.MUSIC,       "歌謡曲・演歌",                           "4"),\r
+               MUSIC_LIVE              (ProgGenre.MUSIC,       "ライブ・コンサート",                          "5"),\r
+               MUSIC_RANKING   (ProgGenre.MUSIC,       "ランキング・リクエスト",                    "6"),\r
+               MUSIC_KARAOKE   (ProgGenre.MUSIC,       "カラオケ・のど自慢",                  "7"),\r
+               MUSIC_MINYO             (ProgGenre.MUSIC,       "民謡・邦楽",                                      "8"),\r
+               MUSIC_DOYO              (ProgGenre.MUSIC,       "童謡・キッズ",                                   "9"),\r
+               MUSIC_MINZOKU   (ProgGenre.MUSIC,       "民族音楽・ワールドミュージック",        "A"),\r
+               MUSIC_ETC               (ProgGenre.MUSIC,       "その他",                                            "F"),\r
+               \r
+               VARIETY_QUIZ    (ProgGenre.VARIETY,     "クイズ",                    "0"),\r
+               VARIETY_GAME    (ProgGenre.VARIETY,     "ゲーム",                    "1"),\r
+               VARIETY_TALK    (ProgGenre.VARIETY,     "トークバラエティ",     "2"),\r
+               VARIETY_OWARAI  (ProgGenre.VARIETY,     "お笑い・コメディ",     "3"),\r
+               VARIETY_MUSIC   (ProgGenre.VARIETY,     "音楽バラエティ",        "4"),\r
+               VARIETY_TABI    (ProgGenre.VARIETY,     "旅バラエティ",           "5"),\r
+               VARIETY_RYORI   (ProgGenre.VARIETY,     "料理バラエティ",        "6"),\r
+               VARIETY_ETC             (ProgGenre.VARIETY,     "その他",                    "F"),\r
+               \r
+               MOVIE_YOGA              (ProgGenre.MOVIE,       "洋画",                       "0"),\r
+               MOVIE_HOGA              (ProgGenre.MOVIE,       "邦画",                       "1"),\r
+               MOVIE_ANIME             (ProgGenre.MOVIE,       "アニメ",                    "2"),\r
+               MOVIE_ETC               (ProgGenre.MOVIE,       "その他",                    "F"),\r
+               \r
+               ANIME_KOKUNAI   (ProgGenre.ANIME,       "国内アニメ",              "0"),\r
+               ANIME_KAIGAI    (ProgGenre.ANIME,       "海外アニメ",              "1"),\r
+               ANIME_TOKUSATSU (ProgGenre.ANIME,       "特撮",                       "2"),\r
+               ANIME_ETC               (ProgGenre.ANIME,       "その他",                    "F"),\r
+               \r
+               DOC_SOCIAL              (ProgGenre.DOCUMENTARY, "社会・時事",                      "0"),\r
+               DOC_HISTORY             (ProgGenre.DOCUMENTARY, "歴史・紀行",                      "1"),\r
+               DOC_NATURE              (ProgGenre.DOCUMENTARY, "自然・動物・環境",     "2"),\r
+               DOC_SPACE               (ProgGenre.DOCUMENTARY, "宇宙・科学・医学",     "3"),\r
+               DOC_CULTURE             (ProgGenre.DOCUMENTARY, "カルチャー・伝統文化",       "4"),\r
+               DOC_BUNGEI              (ProgGenre.DOCUMENTARY, "文学・文芸",                      "5"),\r
+               DOC_SPORTS              (ProgGenre.DOCUMENTARY, "スポーツ",                 "6"),\r
+               DOC_DOCUMENTARY (ProgGenre.DOCUMENTARY, "ドキュメンタリー全般",       "7"),\r
+               DOC_INTERVIEW   (ProgGenre.DOCUMENTARY, "インタビュー・討論",          "8"),\r
+               DOC_ETC                 (ProgGenre.DOCUMENTARY, "その他",                            "F"),\r
+               \r
+               THEATER_GENDAI  (ProgGenre.THEATER,     "現代劇・新劇",   "0"),\r
+               THEATER_MUSICAL (ProgGenre.THEATER,     "ミュージカル",           "1"),\r
+               THEATER_DANCE   (ProgGenre.THEATER,     "ダンス・バレエ",        "2"),\r
+               THEATER_RAKUGO  (ProgGenre.THEATER,     "落語・演芸",              "3"),\r
+               THEATER_KABUKI  (ProgGenre.THEATER,     "歌舞伎・古典",   "4"),\r
+               THEATER_ETC             (ProgGenre.THEATER,     "その他",                    "F"),\r
+               \r
+               HOBBY_TABI              (ProgGenre.HOBBY,       "旅・釣り・アウトドア",       "0"),\r
+               HOBBY_ENGEI             (ProgGenre.HOBBY,       "園芸・ペット・手芸",  "1"),\r
+               HOBBY_MUSIC             (ProgGenre.HOBBY,       "音楽・美術・工芸",     "2"),\r
+               HOBBY_IGO               (ProgGenre.HOBBY,       "囲碁・将棋",                      "3"),\r
+               HOBBY_MAHJONG   (ProgGenre.HOBBY,       "麻雀・パチンコ",                "4"),\r
+               HOBBY_CAR               (ProgGenre.HOBBY,       "車・オートバイ",                "5"),\r
+               HOBBY_COMPUTER  (ProgGenre.HOBBY,       "コンピュータ・TVゲーム", "6"),\r
+               HOBBY_KAIWA             (ProgGenre.HOBBY,       "会話・語学",                      "7"),\r
+               HOBBY_YOJI              (ProgGenre.HOBBY,       "幼児・小学生",           "8"),\r
+               HOBBY_CHUGAKU   (ProgGenre.HOBBY,       "中学生・高校生",                "9"),\r
+               HOBBY_DAIGAKU   (ProgGenre.HOBBY,       "大学生・受験",           "A"),\r
+               HOBBY_SHOGAI    (ProgGenre.HOBBY,       "生涯教育・資格",                "B"),\r
+               HOBBY_KYOIKU    (ProgGenre.HOBBY,       "教育問題",                 "C"),\r
+               HOBBY_ETC               (ProgGenre.HOBBY,       "その他",                            "F"),\r
+               \r
+               WELFARE_KOUREI  (ProgGenre.WELFARE,     "高齢者",            "0"),\r
+               WELFARE_SHOGAI  (ProgGenre.WELFARE,     "障害者",            "1"),\r
+               WELFARE_HUKUSHI (ProgGenre.WELFARE,     "社会福祉",         "2"),\r
+               WELFARE_VULNTEER(ProgGenre.WELFARE,     "ボランティア",           "3"),\r
+               WELFARE_SHUWA   (ProgGenre.WELFARE,     "手話",                       "4"),\r
+               WELFARE_MOJI    (ProgGenre.WELFARE,     "文字(字幕)",   "5"),\r
+               WELFARE_ONSEI   (ProgGenre.WELFARE,     "音声解説",         "6"),\r
+               WELFARE_ETC             (ProgGenre.WELFARE,     "その他",                    "F"),\r
+               \r
+               NOGENRE_ETC             (ProgGenre.NOGENRE,     "その他",                    "F"),\r
+               \r
+               ;\r
+               \r
+               private ProgGenre genre;\r
+               private String name;\r
+               private String iepg;\r
+               \r
+               private ProgSubgenre(ProgGenre genre, String name, String iepg) {\r
+                       this.genre = genre;\r
+                       this.name = name;\r
+                       this.iepg = iepg;\r
+               }\r
+               \r
+               @Override\r
+               public String toString() {\r
+                       return(name);\r
+               }\r
+               \r
+               public String toIEPG() {\r
+                       return(iepg);\r
+               }\r
+               \r
+               public ProgGenre getGenre() {\r
+                       return(genre);\r
+               }\r
+               \r
+               public static ArrayList<ProgSubgenre> values(ProgGenre gr) {\r
+                       ArrayList<ProgSubgenre> ga = new ArrayList<TVProgram.ProgSubgenre>();\r
+                       for ( ProgSubgenre g : ProgSubgenre.values() ) {\r
+                               if ( g.genre == gr ) {\r
+                                       ga.add(g);\r
+                               }\r
+                       }\r
+                       return ga;\r
+               }\r
+               public static ProgSubgenre get(ProgGenre gr, String s) {\r
+                       for ( ProgSubgenre g : ProgSubgenre.values(gr) ) {\r
+                               if ( g.name.equals(s) ) {\r
+                                       return g;\r
+                               }\r
+                       }\r
+                       return null;\r
+               }\r
+               public static ProgSubgenre get(String s) {\r
+                       for ( ProgSubgenre g : ProgSubgenre.values() ) {\r
+                               if ( g.name.equals(s) ) {\r
+                                       return g;\r
+                               }\r
+                       }\r
+                       return null;\r
+               }\r
+               \r
+               public static ProgSubgenre getByIEPG(ProgGenre gr, String iepg) {\r
+                       for ( ProgSubgenre g : ProgSubgenre.values(gr) ) {\r
+                               if ( g.iepg.equals(iepg) ) {\r
+                                       return g;\r
+                               }\r
+                       }\r
+                       return null;\r
+               }\r
+       };\r
+       \r
+       public static final Object[][] optMarks = {\r
+               { ProgOption.HIDDEN_NEW, "【新】新番組" },\r
+               { ProgOption.HIDDEN_LAST, "【終】最終回" },\r
+               { ProgOption.HIDDEN_NOSCRUMBLE, "【無料】無料放送" },\r
+               { ProgOption.FIRST, "【初】初回放送" },\r
+               { ProgOption.PRECEDING, "【先】先行放送" },\r
+               { ProgOption.NONREPEATED, "[初]リピート放送の初回放送回" },\r
+               //{ false, ProgOption.REPEAT, "[再]再放送" },\r
+               { ProgOption.LIVE, "[生]生放送" },\r
+               { ProgOption.SPECIAL, "[特]特番" },\r
+               { ProgOption.RATING, "[R]視聴制限あり" },\r
+               { ProgOption.SUBTITLE, "[字]文字多重放送" },\r
+               { ProgOption.MULTIVOICE, "[多]音声多重放送" },\r
+               { ProgOption.BILINGUAL, "[二]二か国語放送" },\r
+               { ProgOption.STANDIN, "[吹]吹替放送" },\r
+               { ProgOption.SURROUND, "[5.1]5.1chサラウンド" },\r
+               { ProgOption.DATA, "[デ]データ放送" },\r
+               { ProgOption.PV, "[PV]ペイパービュー" },\r
+               { ProgOption.NOSYOBO, "[!]しょぼかる未定義" },\r
+               { ProgOption.NEWARRIVAL, "[NEW]予約待機の新着" },\r
+               { ProgOption.MODIFIED, "(更)番組詳細に更新あり - 予約待機分のみ" },\r
+               { ProgOption.MOVED, "(移)先週無かったか時間が違う" },\r
+       };\r
+       \r
+       public static final String[] OKINIIRI = {"★★★★★","★★★★","★★★","★★","★",""};\r
+\r
+       // エリアコード\r
+       public static final String allCode = "all";\r
+       public static final String trCode = "tr";\r
+       public static final String bsCode = "bs";\r
+       public static final String csCode = "cs";\r
+       \r
+       // タイトルの頭の邪魔な文字\r
+       public static final String titlePrefixRemoveExpr = "^(\\[(新|無|字|終|HV|無料)\\]|無料≫|【無料】)+\\s*"; \r
+       public static final String epnoNormalizeExpr = "([第#(])(\\d\\D|\\d$)";\r
+\r
+       // 種族の特性\r
+       public String getTVProgramId();\r
+       public TVProgram clone();\r
+       //public void setProperties(TVProgramUtils from);\r
+       public boolean isAreaSelectSupported();\r
+\r
+       // 個体の特性\r
+       // なし\r
+       \r
+       /*\r
+        * TVProgramRefreshクラスを継承する部分 ←そんなクラスあったっけ…?\r
+        */\r
+       public ArrayList<ProgList> getCenters();\r
+       public ArrayList<Center> getCRlist();\r
+       public ArrayList<Center> getSortedCRlist();\r
+       public void setSortedCRlist();\r
+       public void refresh();\r
+       public void setExtension(String spoexSearchStart, String spoexSearchEnd, boolean spoexLimitation, ArrayList<SearchKey> extKeys);\r
+       public void abon(ArrayList<String> ngword);\r
+       public String chkComplete();\r
+       \r
+       //public void setAbnormal(boolean b);\r
+       //public boolean getAbnormal();\r
+       \r
+       public ProgType getType();\r
+       public ProgSubtype getSubtype();\r
+\r
+       public void setDebug(boolean b);\r
+       //public boolean setProxy(String host, String port);\r
+       \r
+       /*\r
+        * 公開メソッドP(番組表)\r
+        */\r
+       public void loadProgram(String areaCode, boolean force);\r
+       public int getTimeBarStart();\r
+       public void setExpandTo8(boolean b);\r
+       public void setUseDetailCache(boolean b);\r
+       \r
+       /*\r
+        * 公開メソッドA(地域設定)\r
+        */\r
+       public ArrayList<AreaCode> getAClist();\r
+       public void loadAreaCode();\r
+       public void saveAreaCode();\r
+       public String getDefaultArea();\r
+       public String getArea(String code);\r
+       public String getCode(String Area);\r
+       public String setSelectedAreaByName(String area);\r
+       public String setSelectedAreaByCode(String code);\r
+       public String getSelectedArea();\r
+       public String getSelectedCode();\r
+       \r
+       /*\r
+        * 公開メソッドC(放送局設定)\r
+        */\r
+       //public void loadCenter();\r
+       public void loadCenter(String code, boolean force);\r
+       public boolean saveCenter();\r
+       \r
+       /*\r
+        * \r
+        */\r
+       public void setUserAgent(String s);\r
+       public void setProgDir(String s);\r
+       public void setCacheExpired(int h);\r
+       //public void setProgressArea(StatusWindow o);\r
+       //public void setChConv(ChannelConvert chconv);\r
+       public void setContinueTomorrow(boolean b);\r
+       public void setSplitEpno(boolean b);\r
+       //public String[] doSplitEpno(ProgGenre genre, String title);\r
+       \r
+       // 拡張機能!\r
+       public boolean setOptString(String s);\r
+       public String getOptString();\r
+       \r
+       \r
+}\r
diff --git a/TinyBannavi/src/tainavi/TVProgramIterator.java b/TinyBannavi/src/tainavi/TVProgramIterator.java
new file mode 100644 (file)
index 0000000..017ebe1
--- /dev/null
@@ -0,0 +1,228 @@
+\r
+package tainavi;\r
+\r
+import java.util.ArrayList;\r
+import java.util.Iterator;\r
+import java.util.NoSuchElementException;\r
+\r
+import tainavi.TVProgram.ProgSubtype;\r
+import tainavi.TVProgram.ProgType;\r
+\r
+/**\r
+ * Web番組表をまたいだソートのために無理やり組んだクラス。\r
+ * @see TVProgram\r
+ * @see TVProgramList\r
+ * @version 3.15.4β TVProgramListのメンバにするため抽象クラス化した。\r
+ * @version 3.16.3β プラグイン種別に加えてエリアコードによる分類も追加(IterationType.BSも追加)\r
+ */\r
+abstract class TVProgramIterator implements Iterator<ProgList>,Iterable<ProgList> {\r
+\r
+       // 抽象メソッド\r
+       \r
+       /**\r
+        * ソートしたリストを作るよ。\r
+        * @param tvprogs : 有効なプラグインをどうぞ。\r
+        * @param clst : 放送局の順番をしていしたければどうぞ。nullだとソートしない。\r
+        * @param tuner : {@link IterationType}\r
+        */\r
+       public abstract TVProgramIterator build(ArrayList<Center> clst, IterationType tuner);\r
+       \r
+       \r
+       /**\r
+        * どの種類のプラグイン(地上&BS、CS、ラジオ、または全部)に絞るか。\r
+        */\r
+       public static enum IterationType { ALL, TERRA, BS, CS, PASSED, RADIO };\r
+       \r
+       private ArrayList<ProgList> proglist;\r
+       \r
+       private int idx = -1;\r
+       \r
+       \r
+       // 公開メソッド\r
+       \r
+       @Override\r
+    public Iterator<ProgList> iterator() {\r
+        return this;\r
+    }\r
+       \r
+       @Override\r
+       public boolean hasNext() {\r
+               if ((idx+1) < proglist.size()) {\r
+                       return true;\r
+               }\r
+               return false;\r
+       }\r
+       \r
+       @Override\r
+       public ProgList next() {\r
+               if (++idx >= proglist.size()) {\r
+                       throw new NoSuchElementException();\r
+               }\r
+               return getP();\r
+       }\r
+       \r
+       @Override\r
+       public void remove() {\r
+               throw new UnsupportedOperationException();\r
+       }\r
+       \r
+       // 独自公開メソッド\r
+       \r
+       public ProgList getP() {\r
+               return proglist.get(idx);\r
+       }\r
+       \r
+       public void rewind() {\r
+               idx = -1;\r
+       }\r
+       \r
+       public int size() {\r
+               return proglist.size();\r
+       }\r
+       \r
+       /**\r
+        * <P>内部ポインタを指定した放送局まで移動させます\r
+        * <P><B>iterator.get(iterator.getIndex())とかできるわけではないので注意</B>\r
+        * @return -1 移動に失敗しました。ポインタは変更されません\r
+        */\r
+       public int getIndex(String Center) {\r
+               int n = 0;\r
+               for ( ProgList pl : proglist ) {\r
+                       if ( pl.Center.equals(Center) ) {\r
+                               return idx = n;\r
+                       }\r
+                       n++;\r
+               }\r
+               return -1;\r
+       }\r
+       \r
+       \r
+       \r
+       // 非公開メソッド\r
+       \r
+       /**\r
+        * {@link #build(ArrayList, IterationType)} の本体\r
+        */\r
+       protected TVProgramIterator _build(TVProgramList tvprograms, ArrayList<Center> clst, IterationType tuner) {\r
+               \r
+               this.rewind();\r
+               \r
+               proglist = new ArrayList<ProgList>();\r
+               \r
+               if (clst != null && clst.size() > 0) {\r
+                       // 局順リストあり\r
+                       for (Center cr : clst) {\r
+                               for ( TVProgram p : tvprograms ) {\r
+                                       if (isProgramEnabled(p,tuner)) {\r
+                                               int centerid;\r
+                                               if ((centerid = findCenterId(p.getCenters(),cr)) >= 0) {\r
+                                                       \r
+                                                       if ( ! isAreaCodeEnabled(p,cr,tuner)) {\r
+                                                               // エリアコードによる判定を追加\r
+                                                               continue;\r
+                                                       }\r
+                                                       \r
+                                                       proglist.add(p.getCenters().get(centerid));\r
+                                                       break;\r
+                                               }\r
+                                       }\r
+                               }\r
+                       }\r
+                       \r
+                       // 過去ログはリストにない局もリストアップして追加する\r
+                       if ( tuner == IterationType.PASSED ) {\r
+                               for ( TVProgram p : tvprograms ) {\r
+                                       if (isProgramEnabled(p,tuner)) {\r
+                                               int centerid=0;\r
+                                               for ( ProgList pl : p.getCenters() ) {\r
+                                                       if ( ! proglist.contains(pl) ) {\r
+                                                               proglist.add(pl);\r
+                                                       }\r
+                                                       centerid++;\r
+                                               }\r
+                                       }\r
+                               }\r
+                       }\r
+                       \r
+               }\r
+               else {\r
+                       // 局順リストなし(後方互換)\r
+                       int centerid;\r
+                       for (int siteid=0; siteid<tvprograms.size(); siteid++) {\r
+                               TVProgram p = tvprograms.get(siteid);\r
+                               if (isProgramEnabled(p,tuner)) {\r
+                                       for (Center cr : p.getSortedCRlist()) {\r
+                                               if ((centerid = findCenterId(p.getCenters(),cr)) >= 0) {\r
+                                                       \r
+                                                       if ( ! isAreaCodeEnabled(p,cr,tuner)) {\r
+                                                               // エリアコードによる判定を追加\r
+                                                               continue;\r
+                                                       }\r
+                                                       \r
+                                                       proglist.add(p.getCenters().get(centerid));\r
+                                               }\r
+                                       }\r
+                               }\r
+                       }\r
+               }\r
+               \r
+               return this;\r
+       }\r
+       \r
+       private int findCenterId(ArrayList<ProgList> pl, Center cr) {\r
+               for (int x=0; x<pl.size(); x++) {\r
+                       if (pl.get(x).enabled && pl.get(x).Center.equals(cr.getCenter())) {\r
+                               return x;\r
+                       }\r
+               }\r
+               return -1;\r
+       }\r
+       \r
+       private boolean isProgramEnabled(TVProgram p, IterationType tuner) {\r
+               if (p.getType() == ProgType.PROG) {\r
+                       if (tuner == IterationType.TERRA || tuner == IterationType.BS || tuner == IterationType.ALL) { \r
+                               if (p.getSubtype() == ProgSubtype.TERRA) {\r
+                                       return true;\r
+                               }\r
+                       }\r
+                       if (tuner == IterationType.CS || tuner == IterationType.ALL) {\r
+                               if (p.getSubtype() == ProgSubtype.TERRA || p.getSubtype() == ProgSubtype.CS || p.getSubtype() == ProgSubtype.CS2) {\r
+                                       return true;\r
+                               }\r
+                       }\r
+                       if (tuner == IterationType.RADIO || tuner == IterationType.ALL) {\r
+                               if (p.getSubtype() == ProgSubtype.RADIO) {\r
+                                       return true;\r
+                               }\r
+                       }\r
+               }\r
+               else if (p.getType() == ProgType.PASSED && tuner == IterationType.PASSED) {\r
+                       return true;\r
+               }\r
+               return false;\r
+       }\r
+       \r
+       private boolean isAreaCodeEnabled(TVProgram p, Center cr, IterationType tuner) {\r
+               if (tuner == IterationType.TERRA) {\r
+                       if (cr.getAreaCode().equals(TVProgram.bsCode) || cr.getAreaCode().equals(TVProgram.csCode)) {\r
+                               // TERRAとBSをわけてみたい\r
+                               return false;\r
+                       }\r
+               }\r
+               else if (tuner == IterationType.BS) {\r
+                       if ( ! cr.getAreaCode().equals(TVProgram.bsCode)) {\r
+                               // TERRAとBSをわけてみたい\r
+                               return false;\r
+                       }\r
+               }\r
+               else if (tuner == IterationType.CS) {\r
+                       if (p.getSubtype() == ProgSubtype.TERRA) {\r
+                               if ( ! cr.getAreaCode().equals(TVProgram.csCode)) {\r
+                                       // EDCBみたいに、TERRAの中にTERRA/BS/CSが混在しているとか\r
+                                       return false;\r
+                               }\r
+                       }\r
+               }\r
+               return true;\r
+       }\r
+}\r
diff --git a/TinyBannavi/src/tainavi/TVProgramList.java b/TinyBannavi/src/tainavi/TVProgramList.java
new file mode 100644 (file)
index 0000000..a64dafd
--- /dev/null
@@ -0,0 +1,176 @@
+package tainavi;\r
+\r
+import java.util.ArrayList;\r
+\r
+import tainavi.TVProgram.ProgType;\r
+\r
+/**\r
+ * <P>{@link TVProgram} のリストを実現するクラスです.\r
+ * <P><B>使用の前に必ずclear()を実行すること!!</B> \r
+ * @version 3.15.4β~\r
+ */\r
+public class TVProgramList extends ArrayList<TVProgram> implements Cloneable {\r
+       \r
+       private static final long serialVersionUID = 1L;\r
+       \r
+       /**\r
+        * 放送局をソートした結果が得られるはずだ。\r
+        */\r
+       public TVProgramIterator getIterator() { return iterator; }\r
+       \r
+       private final TVProgramIterator iterator = new TVProgramIterator() {\r
+               @Override\r
+               public TVProgramIterator build(ArrayList<Center> clst, IterationType tuner) {\r
+                       return super._build(TVProgramList.this,clst,tuner);\r
+               }\r
+       };\r
+       \r
+       \r
+       // ここから本体\r
+       \r
+       public TVProgramList() {\r
+               super();\r
+       }\r
+       \r
+       private TVProgramList progs = null;\r
+       private TVProgramList tv = null;\r
+       private TVProgramList cs = null;\r
+       private TVProgramList cs2 = null;\r
+       private TVProgramList radio = null;\r
+       private Syobocal syobo = null;\r
+       private PassedProgram passed = null;\r
+       private PickedProgram picked = null;\r
+       private SearchResult searched = null;   // これだけ別枠\r
+\r
+       /**\r
+        * {@link ProgType#PROG}だけのリストを返す\r
+        */\r
+       public TVProgramList getProgPlugins() {\r
+               return progs;\r
+       }\r
+       \r
+       /**\r
+        * @param progId : nullの場合は先頭の要素を返す。\r
+        */\r
+       public TVProgram getTvProgPlugin(String progId) { return (tv.size()>0)?(getProgPlugin(tv, progId)):(null); }\r
+       /**\r
+        * @param progId : nullの場合は先頭の要素を返す。\r
+        */\r
+       public TVProgram getCsProgPlugin(String progId) { return (cs.size()>0)?(getProgPlugin(cs, progId)):(null); }\r
+       /**\r
+        * @param progId : nullの場合は先頭の要素を返す。\r
+        */\r
+       public TVProgram getCs2ProgPlugin(String progId) { return (cs2.size()>0)?(getProgPlugin(cs2, progId)):(null); }\r
+       /**\r
+        * @param progId : nullの場合は先頭の要素を返す。\r
+        */\r
+       public TVProgram getRadioProgPlugin(String progId) { return (radio.size()>0)?(getProgPlugin(radio, progId)):(null); }\r
+       /**\r
+        * @param progId : nullの場合は先頭の要素を返す。\r
+        */\r
+       public TVProgram getProgPlugin(String progId) {\r
+               return getProgPlugin(this, progId);\r
+       }\r
+       \r
+       public Syobocal getSyobo() { return syobo; }\r
+       public PassedProgram getPassed() { return passed; }\r
+       public PickedProgram getPickup() { return picked; }\r
+       public SearchResult getSearched() { return searched; }\r
+       \r
+       private TVProgram getProgPlugin(TVProgramList pList, String progId) {\r
+               if ( pList.size() == 0 ) {\r
+                       return null;\r
+               }\r
+               if ( progId == null ) {\r
+                       return pList.get(0);\r
+               }\r
+               for ( TVProgram prog : pList ) {\r
+                       if ( prog.getTVProgramId().equals(progId) ) {\r
+                               return prog;\r
+                       }\r
+               }\r
+               return null;\r
+       }\r
+\r
+       public TVProgramList getTvProgPlugins() { return tv; }\r
+       public TVProgramList getCsProgPlugins() { return cs; }\r
+       public TVProgramList getCs2ProgPlugins() { return cs2; }\r
+       public TVProgramList getRadioProgPlugins() { return radio; }\r
+\r
+       //\r
+       @Override\r
+       public boolean add(TVProgram prog) {\r
+               \r
+               if ( prog.getType() != ProgType.SEARCHED ) {\r
+                       // 検索結果は全体リストに含めない\r
+                       super.add(prog);\r
+               }\r
+               \r
+               if ( tv == null ) {\r
+                       return true;    // ループ防止\r
+               }\r
+               \r
+               if ( prog.getType() == ProgType.PROG ) {\r
+                       \r
+                       progs.add(prog);\r
+                       \r
+                       switch (prog.getSubtype()) {\r
+                       case TERRA:\r
+                               tv.add(prog);\r
+                               break;\r
+                       case CS:\r
+                               cs.add(prog);\r
+                               break;\r
+                       case CS2:\r
+                               cs2.add(prog);\r
+                               break;\r
+                       case RADIO:\r
+                               radio.add(prog);\r
+                               break;\r
+                       default:\r
+                               break;\r
+                       }\r
+               }\r
+               else if ( prog.getType() == ProgType.SYOBO ) {\r
+                       syobo = (Syobocal) prog;\r
+               }\r
+               else if ( prog.getType() == ProgType.PASSED ) {\r
+                       passed = (PassedProgram) prog;\r
+               }\r
+               else if ( prog.getType() == ProgType.PICKED ) {\r
+                       picked = (PickedProgram) prog;\r
+               }\r
+               else if ( prog.getType() == ProgType.SEARCHED ) {\r
+                       searched = (SearchResult) prog;\r
+               }\r
+               else {\r
+                       System.err.println("[DEBUG] 不正なWeb番組表プラグインです: type="+prog.getType()+" subtype="+prog.getSubtype()+" id="+prog.getTVProgramId());\r
+                       return false;\r
+               }\r
+               \r
+               return true;\r
+       }\r
+       \r
+       //\r
+       @Override\r
+       public void clear() {\r
+               super.clear();\r
+               progs = new TVProgramList();\r
+               tv = new TVProgramList();\r
+               cs = new TVProgramList();\r
+               cs2 = new TVProgramList();\r
+               radio = new TVProgramList();\r
+               syobo = null;\r
+               passed = null;\r
+               picked = null;\r
+               searched = null;\r
+       }\r
+\r
+       //\r
+       @Override\r
+       public TVProgramList clone() {\r
+               TVProgramList p = (TVProgramList) super.clone();\r
+               CommonUtils.FieldCopy(p, this); // ディープコピーするよ\r
+               return p;\r
+       }\r
+}\r
diff --git a/TinyBannavi/src/tainavi/TVProgramUtils.java b/TinyBannavi/src/tainavi/TVProgramUtils.java
new file mode 100644 (file)
index 0000000..086f79f
--- /dev/null
@@ -0,0 +1,1491 @@
+package tainavi;\r
+\r
+import java.io.BufferedInputStream;\r
+import java.io.BufferedOutputStream;\r
+import java.io.BufferedReader;\r
+import java.io.BufferedWriter;\r
+import java.io.File;\r
+import java.io.FileOutputStream;\r
+import java.io.FileWriter;\r
+import java.io.IOException;\r
+import java.io.InputStreamReader;\r
+import java.io.OutputStream;\r
+import java.net.HttpURLConnection;\r
+import java.net.InetSocketAddress;\r
+import java.net.MalformedURLException;\r
+import java.net.Proxy;\r
+import java.net.URL;\r
+import java.util.ArrayList;\r
+import java.util.Calendar;\r
+import java.util.Collections;\r
+import java.util.Date;\r
+import java.util.GregorianCalendar;\r
+import java.util.HashMap;\r
+import java.util.List;\r
+import java.util.regex.Matcher;\r
+import java.util.regex.Pattern;\r
+\r
+import tainavi.TVProgram.ProgFlags;\r
+import tainavi.TVProgram.ProgGenre;\r
+import tainavi.TVProgram.ProgOption;\r
+import tainavi.TVProgram.ProgScrumble;\r
+import tainavi.TVProgram.ProgSubgenre;\r
+\r
+\r
+/**\r
+ * {@link TVProgram}インタフェース をインプルメントしたWeb番組表プラグインのクラスで利用できる、共有部品の集合です。\r
+ */\r
+public class TVProgramUtils implements Cloneable {\r
+\r
+       /*******************************************************************************\r
+        * ディープコピーが意外と大変\r
+        ******************************************************************************/\r
+       \r
+       @Override\r
+       public TVProgramUtils clone() {\r
+               try {\r
+                       TVProgramUtils p = (TVProgramUtils) super.clone();\r
+                       \r
+                       // フィールドコピーしてもらいたくないもの\r
+                       p.pcenter = null;\r
+                       \r
+                       // static化したのでコピー抑制を必要としなくなったものたち\r
+                       //p.setProgressArea(null);\r
+                       //p.setChConv(null);\r
+                       \r
+                       CommonUtils.FieldCopy(p, this); // ディープコピーするよ\r
+                       \r
+                       p.pcenter = new ArrayList<ProgList>();\r
+                       \r
+                       /*\r
+                       // 地域設定をコピー\r
+                       p.aclist = new ArrayList<AreaCode>();\r
+                       for ( AreaCode ac : aclist ) {\r
+                               p.aclist.add(ac.clone());\r
+                       }\r
+                       \r
+                       // 放送局設定をコピー\r
+                       p.crlist = new ArrayList<Center>();\r
+                       for ( Center cr : crlist ) {\r
+                               p.crlist.add(cr.clone());\r
+                       }\r
+                       */\r
+\r
+                       p.setSortedCRlist();\r
+\r
+                       return p;\r
+                       \r
+               } catch (CloneNotSupportedException e) {\r
+                       throw new InternalError(e.toString());\r
+               }\r
+       }\r
+       \r
+       \r
+       /*******************************************************************************\r
+        * 共通情報\r
+        ******************************************************************************/\r
+       \r
+       /**\r
+        * Proxy\r
+        */\r
+       public static boolean setProxy(String host, String port) {\r
+               Proxy newproxy = null;\r
+               if ( host != null ) {\r
+                       try {\r
+                               newproxy = new Proxy(Proxy.Type.HTTP, new InetSocketAddress(host, Integer.valueOf(port)));\r
+                       }\r
+                       catch (Exception e) {\r
+                               e.printStackTrace();\r
+                               return false;\r
+                       }\r
+               }\r
+               proxy = newproxy;\r
+               return true;\r
+       }\r
+       \r
+       private Proxy getProxy() { return proxy; }\r
+       \r
+       private static Proxy proxy = null;\r
+       \r
+       /**\r
+        * ChannelConvert.dat\r
+        */\r
+       public static void setChConv(ChannelConvert cc) { chconv = cc; }\r
+       \r
+       private ChannelConvert getChConv() { return chconv; }\r
+       \r
+       private static ChannelConvert chconv = null;\r
+       \r
+       /**\r
+        *  ログと進捗ダイアログ\r
+        */\r
+       public static void setProgressArea(StatusWindow o) { stw = o; }\r
+       \r
+       protected void reportProgress(String msg) {\r
+               if (stw != null) {\r
+                       stw.append(msg);\r
+               }\r
+               System.out.println(msg);\r
+       }\r
+\r
+       private static StatusWindow stw = null;\r
+       \r
+       \r
+       /*******************************************************************************\r
+        * オプション確認\r
+        ******************************************************************************/\r
+       \r
+       public boolean isAreaSelectSupported() { return true; }         // デフォルトはエリアを選べる\r
+       \r
+       \r
+       /*******************************************************************************\r
+        * 定数\r
+        ******************************************************************************/\r
+       \r
+       // コネクションタイムアウト\r
+       private static final int conntout = 5;\r
+       \r
+       // read()タイムアウト\r
+       private static final int readtout = 30;\r
+\r
+       // 番組表が複数ページに跨っている場合、何ページまで追いかけるか\r
+       protected static final int REFPAGESMAX = 10;\r
+       \r
+       // 番組詳細に情報をconcatする時のセパレータ\r
+       protected final String DETAIL_SEP = "\n\n";\r
+       \r
+       // メッセージID\r
+       private static final String MSGID = "[番組表共通] ";\r
+       private static final String DBGID = "[DEBUG]"+MSGID;\r
+       private static final String ERRID = "[ERROR]"+MSGID;\r
+       \r
+       /*******************************************************************************\r
+        * メンバ変数関連\r
+        ******************************************************************************/\r
+       \r
+       // デバッグフラグ\r
+       public void setDebug(boolean b) { debug = b; }\r
+       protected boolean getDebug() { return debug; }  // 参照はサブクラスのみに許可\r
+       private boolean debug = false;\r
+       \r
+       // 通信エラー時のリトライ回数\r
+       protected void setRetryCount(int n) { retrycount = n; } // サブクラスのみに許可(特殊)\r
+       private int retrycount = 1;\r
+\r
+       // User-Agent\r
+       public String getUserAgent() { return userAgent; }\r
+       public void setUserAgent(String s) { userAgent = s; }\r
+       private String userAgent = "";\r
+\r
+       // 番組表キャッシュの格納フォルダ\r
+       public String getProgDir() { return progDir; }\r
+       public void setProgDir(String s) { progDir = s; }\r
+       private String progDir = "progcache";\r
+       \r
+       // 番組表キャッシュの有効時間(H)\r
+       public int getCacheExpired() { return cacheExpired; }\r
+       public void setCacheExpired(int h) { cacheExpired = h; }\r
+       private int cacheExpired = 6;\r
+       \r
+       // 番組詳細取得を行なうかどうか\r
+       public boolean getUseDetailCache() { return useDetailCache; }\r
+       public void setUseDetailCache(boolean b) { useDetailCache = b; }\r
+       private boolean useDetailCache = true;\r
+       \r
+       // 29時跨ぎで2つに分かれた番組情報をくっつけるかどうか\r
+       public boolean getContinueTomorrow() { return continueTomorrow; }\r
+       public void setContinueTomorrow(boolean b) { continueTomorrow = b; }\r
+       private boolean continueTomorrow = false;\r
+       \r
+       // タイトルから話数以降を分離する\r
+       public void setSplitEpno(boolean b) { splitEpno = b; }\r
+       public boolean isSplitEpno() { return splitEpno; }\r
+       private boolean splitEpno = true;\r
+       \r
+       // 可能なら8日分取得する\r
+       public boolean getExpandTo8() { return expandTo8; }\r
+       public void setExpandTo8(boolean b) { expandTo8 = b; }\r
+       private boolean expandTo8 = false;\r
+       \r
+       // 番組詳細キャッシュをオンライン取得するかどうか\r
+       @Deprecated\r
+       protected boolean isForceLoadDetInfo() { return forceLoadDetInfo; }\r
+       @Deprecated\r
+       private boolean forceLoadDetInfo = false;\r
+       \r
+       // もう使っていないようだ\r
+       //public void setAbnormal(boolean b) { abnormal = b; }\r
+       //public boolean getAbnormal() { return abnormal; }\r
+       //private boolean abnormal = false;\r
+\r
+       \r
+       /*******************************************************************************\r
+        * 番組表固有の情報\r
+        ******************************************************************************/\r
+\r
+       \r
+       /*******************************************************************************\r
+        * 番組情報\r
+        ******************************************************************************/\r
+       \r
+       /**\r
+        * <P>番組表が格納される場所\r
+        * <P>上から順の{@link ProgList}(pcenter/放送局別)->{@link ProgDateList}(pdate/日付別)→{@link ProgDetailList}pdetail/(番組詳細)\r
+        */\r
+       public ArrayList<ProgList> getCenters() { return(pcenter); }\r
+       public ArrayList<ProgList> pcenter = new ArrayList<ProgList>();\r
+       \r
+       \r
+       /*******************************************************************************\r
+        * 放送局情報\r
+        ******************************************************************************/\r
+       \r
+       /**\r
+        * 放送局名リスト\r
+        * @see ProgList#Center\r
+        * @see #sortedcrlist\r
+        */\r
+       public ArrayList<Center> getCRlist() { return(crlist); }\r
+       public ArrayList<Center> crlist = new ArrayList<Center>();\r
+       \r
+       /**\r
+        * ソート済み放送局名リスト\r
+        * @see ProgList#Center\r
+        * @see #crlist\r
+        */\r
+       public ArrayList<Center> getSortedCRlist() { return(sortedcrlist); }\r
+       public void setSortedCRlist() {\r
+               sortedcrlist = new ArrayList<Center>();\r
+               for (int order=1; order<=crlist.size(); order++) {\r
+                       for (Center center : crlist) {\r
+                               if (center.getOrder() == order) {\r
+                                       sortedcrlist.add(center);\r
+                                       break;\r
+                               }\r
+                       }\r
+               }\r
+       }\r
+       public ArrayList<Center> sortedcrlist = new ArrayList<Center>();\r
+       \r
+       // 設定ファイルへ書き出し\r
+       public boolean saveCenter() {\r
+               String centerListFile = getCenterListFile(getTVProgramId(), getSelectedCode());\r
+               if ( ! CommonUtils.writeXML(centerListFile,crlist) ) {\r
+                       System.out.println("放送局リストの保存に失敗しました: "+centerListFile+", "+getSelectedCode());\r
+                       return false;\r
+               }\r
+               System.out.println("放送局リストを保存しました: "+centerListFile+", "+getSelectedCode());\r
+               return true;\r
+       }\r
+       \r
+       // 放送局リストファイル名\r
+       protected String getCenterListFile(String id, String code) {\r
+               return String.format("env%scenter.%s.%s.xml", File.separator, id, code);\r
+       }\r
+       \r
+       // 放送局名に一括してフィルタをかける\r
+       protected void attachChFilters() {\r
+               for ( Center c : crlist ) {\r
+                       if ( c.getCenterOrig() == null ) {\r
+                               c.setCenterOrig(c.getCenter());\r
+                       }\r
+                       c.setCenter(getChConv().get(c.getCenterOrig()));\r
+               }\r
+       }\r
+       \r
+       // 放送局名個別にフィルタをかけたい\r
+       protected String getChName(String chorig) {\r
+               return getChConv().get(chorig);\r
+       }\r
+\r
+\r
+       /*******************************************************************************\r
+        * 地域情報\r
+        ******************************************************************************/\r
+       \r
+       /**\r
+        * 地域コード のリストを取得する\r
+        */\r
+       public ArrayList<AreaCode> getAClist() { return(aclist); }\r
+       public ArrayList<AreaCode> aclist = new ArrayList<AreaCode>();\r
+       \r
+       /**\r
+        * 地域コードから地域名を取得\r
+        */\r
+       public String getArea(String code) {\r
+               for (AreaCode ac : aclist) {\r
+                       if (ac.getCode().equals(code)) {\r
+                               return(ac.getArea());\r
+                       }\r
+               }\r
+               return(null);\r
+       }\r
+\r
+       /**\r
+        * 地域名から地域コードを取得\r
+        */\r
+       public String getCode(String area) {\r
+               for (AreaCode ac : aclist) {\r
+                       if (ac.getArea().equals(area)) {\r
+                               return(ac.getCode());\r
+                       }\r
+               }\r
+               return(null);\r
+       }\r
+       \r
+       /**\r
+        * 地域名を指定して選択中の地域を変更し、ついでにその地域コードを返す\r
+        * @see #getSelectedCode()\r
+        */\r
+       public String setSelectedAreaByName(String area) {\r
+               AreaCode ac = null;\r
+               for (AreaCode atmp : aclist) {\r
+                       if (atmp.getArea().equals(area)) {\r
+                       atmp.setSelected(true);\r
+                       ac = atmp;\r
+                       }\r
+                       else {\r
+                       atmp.setSelected(false);\r
+                       }\r
+               }\r
+               if (ac != null) {\r
+                       return(ac.getCode());\r
+               }\r
+               return(null);\r
+       }\r
+       \r
+       /**\r
+        * 地域コードを指定して選択中の地域を変更し、ついでにその地域名を返す\r
+        * @see #getSelectedArea()\r
+        */\r
+       public String setSelectedAreaByCode(String code) {\r
+               AreaCode ac = null;\r
+               for (AreaCode atmp : aclist) {\r
+                       if (atmp.getCode().equals(code)) {\r
+                       atmp.setSelected(true);\r
+                       ac = atmp;\r
+                       }\r
+                       else {\r
+                       atmp.setSelected(false);\r
+                       }\r
+               }\r
+               if (ac != null) {\r
+                       return(ac.getArea());\r
+               }\r
+               return(null);\r
+       }\r
+       \r
+       /**\r
+        * 選択中の地域名を返す\r
+        */\r
+       public String getSelectedArea() {\r
+               for (AreaCode ac : aclist) {\r
+                       if (ac.getSelected() == true) {\r
+                               return(ac.getArea());\r
+                       }\r
+               }\r
+               return(getDefaultArea());\r
+       }\r
+\r
+       /**\r
+        * 選択中の地域コードを返す\r
+        */\r
+       public String getSelectedCode() {\r
+        for (AreaCode ac : aclist) {\r
+               if (ac.getSelected() == true) {\r
+                       return(ac.getCode());\r
+               }\r
+        }\r
+               return(getCode(getDefaultArea()));\r
+       }\r
+       \r
+       // 設定ファイルへ書き出し\r
+       public void saveAreaCode() {\r
+               if ( CommonUtils.writeXML(getAreaSelectedFile(), aclist) ) {\r
+                       System.out.println(MSGID+"地域リストを保存しました: "+getAreaSelectedFile());\r
+               }\r
+               else {\r
+                       System.err.println(ERRID+"地域リストの保存に失敗しました: "+getAreaSelectedFile());\r
+               }\r
+       }\r
+       \r
+       // 地域情報ファイル名\r
+       protected String getAreaSelectedFile() {\r
+               return String.format("env%sarea.%s.xml", File.separator, getTVProgramId());\r
+       }\r
+       \r
+       \r
+       /*******************************************************************************\r
+        * タイトル操作関連\r
+        ******************************************************************************/\r
+       \r
+       /**\r
+        * 話数重複排除\r
+        */\r
+       protected String doCutDupEpno(String title, String detail) {\r
+               // タイトルの末尾に話数がついているかな?\r
+               Matcher md = Pattern.compile("[  ]*?(#[#  -・0-9]+|第[0-9]+話)$").matcher(title);\r
+               if ( ! md.find() ) {\r
+                       return title;\r
+               }\r
+               \r
+               ArrayList<String> tnoa = new ArrayList<String>();\r
+               {\r
+                       Matcher me = Pattern.compile("(\\d+)").matcher(md.group(1));\r
+                       while ( me.find() ) {\r
+                               tnoa.add(me.group(1));\r
+                       }\r
+                       if ( tnoa.size() == 0 ) {\r
+                               return title;\r
+                       }\r
+               }\r
+               \r
+               // 番組詳細と重複しているかな?\r
+               {\r
+                       ArrayList<String> dnoa = new ArrayList<String>();\r
+                       Matcher me = Pattern.compile("#[  ]*([0-9]+)|第([0-9]+)話").matcher(detail);\r
+                       while ( me.find() ) {\r
+                               if ( me.group(1) != null ) {\r
+                                       dnoa.add(me.group(1));\r
+                               }\r
+                               else if ( me.group(2) != null ) {\r
+                                       dnoa.add(me.group(2));\r
+                               }\r
+                       }\r
+                       if ( dnoa.size() == 0 ) {\r
+                               return title;\r
+                       }\r
+                       \r
+                       for ( String tno : tnoa ) {\r
+                               for ( String dno : dnoa ) {\r
+                                       if ( dno.equals(tno) ) {\r
+                                               dnoa.remove(dno);\r
+                                               break;\r
+                                       }\r
+                               }\r
+                       }\r
+                       if ( dnoa.size() == 0 ) {\r
+                               title = md.replaceFirst("");\r
+                       }\r
+               }\r
+               \r
+               return title;\r
+       }\r
+       \r
+       /**\r
+        * サブタイトルの分離\r
+        * @param pdl\r
+        */\r
+       protected void doSplitSubtitle(ProgDetailList pdl)      {\r
+               \r
+               pdl.splitted_title = doCutDupEpno(pdl.title, pdl.detail);       // タイトルと番組詳細中の話数の重複排除\r
+               \r
+               String [] d = doSplitEpno(pdl.genre, pdl.splitted_title);       // 分離!\r
+               \r
+               pdl.splitted_title = pdl.title.substring(0,d[0].length());\r
+               \r
+               if ( d[1].length() > 0 ) {\r
+                       // 番組詳細はサブタイトル分離番組詳細へのポインタでいいよ\r
+                       pdl.splitted_detail = d[1]+DETAIL_SEP+pdl.detail;\r
+                       pdl.detail = pdl.splitted_detail.substring(d[1].length()+DETAIL_SEP.length());\r
+               }\r
+               else {\r
+                       // サブタイトルが分離されなかったから同じでいいよ\r
+                       pdl.splitted_detail = pdl.detail;\r
+               }\r
+               \r
+               // タイトル&番組詳細のキーワード検索情報の設定\r
+               String key_title;\r
+               String key_detail;\r
+               if (isSplitEpno()) {\r
+                       // サブタイトルを分離するならばそれを考慮した値を使う\r
+                       key_title = pdl.splitted_title;\r
+                       key_detail = pdl.splitted_detail;\r
+               }\r
+               else {\r
+                       key_title = pdl.title;\r
+                       key_detail = pdl.detail;\r
+               }\r
+               pdl.titlePop = TraceProgram.replacePop(key_title);\r
+               pdl.detailPop = TraceProgram.replacePop(key_detail);\r
+               \r
+               pdl.SearchStrKeys = TraceProgram.splitKeys(pdl.titlePop);\r
+       }\r
+       \r
+       /**\r
+        * サブタイトル分離(Part.11 444-)\r
+        */\r
+       private String[] doSplitEpno(ProgGenre genre, String title) {\r
+               if ( genre == ProgGenre.MOVIE || genre == ProgGenre.DOCUMENTARY ) {\r
+                       // 映画とドキュメンタリーは何もしない\r
+               }\r
+               else {\r
+                       if ( genre == ProgGenre.DORAMA ) {\r
+                               // ドラマの場合は、"「*」"での分割をしない(土曜ドラマ「タイトル」とかあるため)\r
+                               Matcher mc = Pattern.compile(spep_expr_dorama).matcher(title);\r
+                               if ( mc.find() ) {\r
+                                       return(new String[] { mc.group(1),mc.group(2)+" " });\r
+                               }\r
+                       }\r
+                       else {\r
+                               // いきなり「で始まる場合や、タイトル中に『TOKYO「萌」探偵』のように「ほげほげ」を含む場合\r
+                               Matcher mc = Pattern.compile("^([^  ]*「.+?」[^  ]+)(.*)$").matcher(title);\r
+                               if ( mc.find() ) {\r
+                                       Matcher md = Pattern.compile("^[  ]*(.*?)[  ]*?"+spep_expr).matcher(mc.group(2));\r
+                                       if ( md.find() ) {\r
+                                               if ( md.group(1).length() == 0 ) {\r
+                                                       return(new String[] { mc.group(1),md.group(2)+" " });\r
+                                               }\r
+                                               else {\r
+                                                       return(new String[] { mc.group(1)+" "+md.group(1),md.group(2)+" " });\r
+                                               }\r
+                                       }\r
+                                       else {\r
+                                               return(new String[] { title,"" });\r
+                                       }\r
+                               }\r
+                               // まあこれが普通\r
+                               mc = Pattern.compile("^(.+?)[  ]*?"+spep_expr).matcher(title);\r
+                               if ( mc.find() ) {\r
+                                       return(new String[] { mc.group(1),mc.group(2)+" " });\r
+                               }\r
+                       }\r
+               }\r
+               return(new String[] { title,"" });\r
+       }\r
+       \r
+       // サブタイトル判定条件\r
+       private static final String spep_expr = "(([<<]?[((##♯第全「][第]?[12345678901234567890一二三四五六七八九十百千]+?[回話章]?|「).*)$";\r
+       \r
+       // サブタイトル判定条件(ジャンル=ドラマ専用)\r
+       private static final String spep_expr_dorama = "^(.+?)[  ]*?(([<<]?[((##♯第全「][第]?[12345678901234567890一二三四五六七八九十百千]+?[回話章]?).*)$";\r
+       \r
+       \r
+       /**\r
+        * NGワード\r
+        */\r
+       public void abon(ArrayList<String> ngword) {\r
+               //\r
+               if (ngword.size() == 0) {\r
+                       return;\r
+               }\r
+               for ( ProgList p : pcenter ) {\r
+                       for ( ProgDateList c : p.pdate ) {\r
+                               for (ProgDetailList d : c.pdetail) {\r
+                                       for (String ngs : ngword) {\r
+                                               if (d.title.indexOf(ngs) != -1 || d.detail.indexOf(ngs) != -1) {\r
+                                                       d.abon();\r
+                                                       break;\r
+                                               }\r
+                                       }\r
+                               }\r
+                       }\r
+               }\r
+       }\r
+       \r
+       \r
+       \r
+       /*******************************************************************************\r
+        * 番組情報キャッシュ\r
+        ******************************************************************************/\r
+       \r
+       // キャッシュファイルが有効期限内か確認する\r
+       public boolean isCacheOld(String fname) {\r
+               // キャッシュ期限が無期限の場合はDL禁止\r
+               if (cacheExpired == 0) {\r
+                       return(false);\r
+               }\r
+               // キャッシュ期限があってファイルがない場合はDLしなさい\r
+               //if (cacheExpired > 0 && fname == null) {\r
+               if (fname == null) {    // あれだ、期限に関係なくファイル名の指定がなきゃDLするしかないよ\r
+                       return(true);\r
+               }\r
+               // 実際のファイルのタイムスタンプを確認する\r
+               try {\r
+                       File f = new File(fname);\r
+                       if (f.exists() == true) {\r
+                               long t = System.currentTimeMillis();\r
+                               if (f.lastModified() < (t - cacheExpired*3600000L) ) {\r
+                                       // ファイルを更新\r
+                                       f.delete();\r
+                                       f.createNewFile();\r
+                                       return(true);\r
+                               }\r
+                       }\r
+                       else {\r
+                               // ファイルを作成        \r
+                               f.createNewFile();\r
+                               return(true);\r
+                       }\r
+               }\r
+               catch (Exception e) {\r
+                       // 例外\r
+                       System.out.println("Exception: isCacheOld() "+e);\r
+               }\r
+               return(false);\r
+       }\r
+       \r
+       \r
+       /*******************************************************************************\r
+        * 番組詳細キャッシュ(現在は使われていない)\r
+        ******************************************************************************/\r
+       \r
+       /**\r
+        * 番組詳細をオンライン取得するかどうか\r
+        */\r
+       @Deprecated\r
+       protected void chkForceLoadDetInfo(boolean force) {\r
+               File f = new File(getDetCacheFile());\r
+               if (force == true ||\r
+                               (f.exists() == true && isCacheOld(getDetCacheFile()) == true) ||\r
+                               (f.exists() == false && isCacheOld(null) == true)) {\r
+                       // Webからロードします\r
+                       forceLoadDetInfo = true;\r
+               }\r
+               else if (f.exists()) {\r
+                       // キャッシュファイルが有効なようなので利用します\r
+                       forceLoadDetInfo = false;\r
+               }\r
+               else {\r
+                       // 無くても平気…\r
+                       forceLoadDetInfo = false;\r
+               }\r
+       }\r
+       \r
+       // 番組詳細キャッシュのLOAD\r
+       @Deprecated\r
+       protected HashMap<String,String> loadDetCache(){\r
+               \r
+               // 設定ファイルが存在していればファイルから\r
+               File f = new File(getDetCacheFile());\r
+               if (f.exists() == true) {\r
+                       @SuppressWarnings("unchecked")\r
+                       HashMap<String,String> cache = (HashMap<String, String>) CommonUtils.readXML(getDetCacheFile());\r
+                       if ( cache != null ) {\r
+                               return cache;\r
+                       }\r
+                       \r
+                       System.out.println(ERRID+"【致命的エラー】番組詳細キャッシュが読み込めません: "+getDetCacheFile());\r
+               }\r
+               \r
+               // キャッシュなし&エラーは空配列を返す\r
+               return new HashMap<String, String>();\r
+       }\r
+       \r
+       // 番組詳細キャッシュのSAVE\r
+       @Deprecated\r
+       protected void saveDetCache(HashMap<String,String> cache) {\r
+               if ( ! CommonUtils.writeXML(getDetCacheFile(), cache) ) {\r
+                       System.err.println(ERRID+"【致命的エラー】番組詳細キャッシュが書き込めません: "+getDetCacheFile());\r
+               }\r
+       }\r
+       \r
+       \r
+       /*******************************************************************************\r
+        * 番組情報のリフレッシュ関連\r
+        ******************************************************************************/\r
+       \r
+       /**\r
+        * 日付変更線(29:00)をまたいだら過去のデータはカットする\r
+        * <B> PassedProgramでは使わない\r
+        */\r
+       public void refresh() {\r
+               \r
+               String critDate = CommonUtils.getDate529(0, true);\r
+               for ( ProgList p : pcenter ) {\r
+                       int i = 0;\r
+                       for ( ProgDateList c : p.pdate ) {\r
+                               if ( c.Date.compareTo(critDate) >= 0 ) {\r
+                                       break;\r
+                               }\r
+                               i++;\r
+                       }\r
+                       for ( int j=0; j<i; j++) {\r
+                               p.pdate.remove(0);\r
+                       }\r
+               }\r
+       }\r
+       \r
+       /**\r
+        * 24時間分番組枠が埋まっているかどうか確認する\r
+        */\r
+       public String chkComplete() {\r
+               for ( ProgList p : pcenter ) {\r
+                       if (p.enabled) {\r
+                               for ( ProgDateList c : p.pdate ) {\r
+                                       if (c.pdetail.size()<=1) {\r
+                                               String msg = "番組枠が存在しません.("+p.Center+","+c.Date+")";\r
+                                               System.out.println(msg);\r
+                                               return(msg);\r
+                                       }\r
+                                       if (c.row < 24*60) {\r
+                                               String msg = "番組枠が24時間分取得できませんでした.("+p.Center+","+c.Date+","+c.row+")";\r
+                                               System.out.println(msg);\r
+                                               return(msg);\r
+                                       }\r
+                               }\r
+                               if (p.pdate.size() < 7) {\r
+                                       String msg = "番組表が一週間分取得できませんでした.("+p.Center+")";\r
+                                       System.out.println(msg);\r
+                                       return(msg);\r
+                               }\r
+                       }\r
+               }\r
+               return null;\r
+       }\r
+       \r
+       /**\r
+        * 開始終了日時の整理\r
+        */\r
+       public void setAccurateDate(ArrayList<ProgDateList> pcenter) {\r
+       \r
+               // 先頭のエントリの開始時刻が 5:00 以前の場合\r
+               for ( ProgDateList pcl : pcenter ) {\r
+                       if (pcl.pdetail.size() <= 0) {\r
+                               continue;\r
+                       }\r
+                       \r
+                       ProgDetailList pd = pcl.pdetail.get(0);\r
+                       Matcher ma = Pattern.compile("(\\d\\d):(\\d\\d)").matcher(pd.start);\r
+                       if (ma.find()) {\r
+                               int prelength = 0;\r
+                               int ahh = Integer.valueOf(ma.group(1));\r
+                               int amm = Integer.valueOf(ma.group(2));\r
+                               \r
+                               GregorianCalendar c = new GregorianCalendar();\r
+                               c.setTime(new Date());\r
+                               c.set(Calendar.HOUR_OF_DAY, ahh);\r
+                               c.set(Calendar.MINUTE, amm);\r
+                               \r
+                               if ( pd.start.compareTo("05:00") < 0 ) {\r
+                                       // 5:00以前\r
+                                       prelength = (5*60+0)-(ahh*60+amm);\r
+                                       c.add(Calendar.MINUTE,prelength+pd.length);\r
+                                       pd.end = CommonUtils.getTime(c);\r
+                               }\r
+                               else if ( pd.start.compareTo("18:00") >= 0 && pd.start.compareTo("24:00") < 0 ) {\r
+                                       // 前日の24:00以前\r
+                                       prelength = (24*60+0)-(ahh*60+amm)+(5*60);\r
+                                       c.add(Calendar.MINUTE,prelength+pd.length);\r
+                                       pd.end = CommonUtils.getTime(c);\r
+                               }\r
+                       }\r
+               }\r
+\r
+               for ( ProgDateList pcl : pcenter ) {\r
+       \r
+                       GregorianCalendar c = CommonUtils.getCalendar(pcl.Date);\r
+       \r
+                       boolean extend = false;\r
+                       boolean overtwodays = false;\r
+                       for ( int i=0; i<pcl.pdetail.size(); i++ ) {\r
+                               \r
+                               ProgDetailList pdl = pcl.pdetail.get(i);\r
+                               \r
+                               // 番組情報がありません\r
+                               if (pdl.start.compareTo("") == 0) {\r
+                                       continue;\r
+                               }\r
+                               \r
+                               // 表示上の開始日時\r
+                               if ( i == 0 ) {\r
+                                       if ( pdl.start.compareTo("18:00") >= 0 && pdl.start.compareTo("24:00") < 0 ) {\r
+                                               // いったい何時間放送するんだよ(--#\r
+                                               c.add(Calendar.DAY_OF_MONTH, -1);\r
+                                               overtwodays = true;\r
+                                       }\r
+                               }\r
+                               else {\r
+                                       if ( (pdl.start.compareTo("00:00") >= 0 && pdl.start.compareTo("05:00") < 0 && pdl.end.compareTo("05:00") < 0) && extend == false) {\r
+                                               c.add(Calendar.DAY_OF_MONTH, 1);\r
+                                               extend = true;\r
+                                       }\r
+                               }\r
+                               pdl.startDateTime = String.format("%s %s", CommonUtils.getDate(c,false), pdl.start);\r
+       \r
+                               // 正確な開始日\r
+                               pdl.accurateDate = CommonUtils.getDate(c);\r
+                               \r
+                               // 表示上の終了日時\r
+                               if ( overtwodays ) {\r
+                                       c.add(Calendar.DAY_OF_MONTH, 1);\r
+                                       overtwodays = false;\r
+                               }\r
+                               else {\r
+                                       if ( pdl.start.compareTo(pdl.end) > 0  && extend == false) {\r
+                                               c.add(Calendar.DAY_OF_MONTH, 1);\r
+                                               extend = true;\r
+                                       }\r
+                               }\r
+                               \r
+                               pdl.endDateTime = String.format("%s %s", CommonUtils.getDate(c,false), pdl.end);\r
+                       }\r
+               }\r
+               \r
+               // 29時をまたいで同タイトルが続いている場合は同一番組とみなす\r
+               if ( continueTomorrow ) {\r
+                       for (int w=0; w<pcenter.size()-1; w++) {\r
+                               if (pcenter.get(w).pdetail.size() > 0 && pcenter.get(w+1).pdetail.size() > 0) {\r
+                                       ProgDetailList pd1 = pcenter.get(w).pdetail.get(pcenter.get(w).pdetail.size()-1);\r
+                                       ProgDetailList pd2 = pcenter.get(w+1).pdetail.get(0);\r
+                                       if (pd1.title.equals(pd2.title)) {\r
+                                               pd1.end = pd2.end;\r
+                                               pd1.endDateTime = pd2.endDateTime;\r
+                                               \r
+                                               pd2.start = pd1.start;\r
+                                               pd2.startDateTime = pd1.startDateTime;\r
+                                               pd2.accurateDate = pd1.accurateDate;\r
+                                       }\r
+                                       else if (pd2.title.equals("承前")) {\r
+                                               pd1.end = pd2.end;\r
+                                               pd1.endDateTime = pd2.endDateTime;\r
+                                               \r
+                                               pd2.start = pd1.start;\r
+                                               pd2.startDateTime = pd1.startDateTime;\r
+                                               pd2.accurateDate = pd1.accurateDate;\r
+                                               \r
+                                               pd2.title = pd1.title;\r
+                                               pd2.detail = pd1.detail;\r
+                                               pd2.setAddedDetail(pd1.getAddedDetail());\r
+                                               pd2.link = pd1.link;\r
+                                               pd2.titlePop = pd1.titlePop;\r
+                                               pd2.detailPop = pd1.detailPop;\r
+                                               pd2.SearchStrKeys = pd1.SearchStrKeys;\r
+                                               pd2.nosyobo = pd1.nosyobo;\r
+                                               pd2.extension = pd1.extension;\r
+                                               pd2.flag = pd1.flag;\r
+                                               pd2.genre = pd1.genre;\r
+                                       }\r
+                               }\r
+                       }\r
+               }\r
+       }\r
+       \r
+       // 以前に取得したデータから当日の取得不能領域のデータを補完する\r
+       protected void CompensatesPrograms(ArrayList<ProgList> newplist) {\r
+               //\r
+               for ( ProgList newpl : newplist ) {\r
+                       \r
+                       if ( newpl.enabled != true ) {\r
+                               // 無効局は処理なし\r
+                               continue;\r
+                       }\r
+                       \r
+                       ArrayList<ProgDateList> newpcla = newpl.pdate; \r
+                       if ( newpcla.size() == 0 ) {\r
+                               // 日付リストが存在しない場合は処理なし\r
+                               continue;\r
+                       }\r
+                       ProgDateList newpcl = newpcla.get(0);\r
+                       \r
+                       ArrayList<ProgDetailList> newpdla = newpcl.pdetail;\r
+                       if ( newpdla.size() == 0 ) {\r
+                               // 番組情報が存在しない場合は処理なし\r
+                               if (debug) System.out.println(DBGID+"番組表情報がないので過去ログは参照しない: "+newpcl.Date+" "+newpl.Center);\r
+                               continue;\r
+                       }\r
+                       \r
+                       if ( newpdla.get(0).start.length() != 0 ) {\r
+                               // 先頭の番組情報が「番組情報がありません」以外の場合は処理なし\r
+                               if (debug) System.out.println(DBGID+"先頭から有効な情報なので過去ログは参照しない: "+newpcl.Date+" "+newpl.Center+" "+newpdla.get(0).start+" "+newpdla.get(0).title);\r
+                               continue;\r
+                       }\r
+                       \r
+                       PassedProgram oldplist = new PassedProgram();\r
+                       if ( ! oldplist.loadByCenter(newpcl.Date, newpl.Center) || oldplist.getProgCount() == 0 ) {\r
+                               // 過去情報が取得できなければ処理なし\r
+                               System.out.println(DBGID+"過去ログに情報はありませんでした");\r
+                               continue;\r
+                       }\r
+                       ProgDateList oldpcl = oldplist.pcenter.get(0).pdate.get(0);\r
+                       \r
+                       // 補填候補抽出\r
+                       ArrayList<ProgDetailList> tmppdla = new ArrayList<ProgDetailList>();\r
+                       if ( newpdla.size() == 1 ) {\r
+                               // 「番組情報がありません」しかない場合は全面複写\r
+                               for ( ProgDetailList oldpdl : oldpcl.pdetail ) {\r
+                                       tmppdla.add(oldpdl.clone());\r
+                               }\r
+                       }\r
+                       else {\r
+                               int idx = 0;\r
+                               for ( ProgDetailList oldpdl : oldpcl.pdetail ) {\r
+                                       if ( idx == 0 ) {\r
+                                               // 過去ログの最初は無条件に追加してよい\r
+                                               tmppdla.add(oldpdl.clone());\r
+                                       }\r
+                                       else if ( oldpdl.startDateTime.compareTo(newpdla.get(1).startDateTime) < 0 ) {\r
+                                               // 2個目以降は当日の有効情報の前まで(「番組情報がありません」は無条件追加)\r
+                                               tmppdla.add(oldpdl.clone());\r
+                                       }\r
+                                       else {\r
+                                               // 有効情報を越えたら終了\r
+                                               break;\r
+                                       }\r
+                                       idx++;\r
+                               }\r
+                       }\r
+                       \r
+                       // 先頭の「番組情報はありません」と差し替えて補填\r
+                       newpdla.remove(0);\r
+                       for ( int i=0; i<tmppdla.size(); i++ ) {\r
+                               newpdla.add(i,tmppdla.get(i));\r
+                       }\r
+                       tmppdla = null;\r
+               }\r
+       }\r
+\r
+       \r
+       /*******************************************************************************\r
+        * フラグ処理関連\r
+        ******************************************************************************/\r
+       \r
+       /**\r
+        * 延長警告を設定する\r
+        */\r
+       public void setExtension(String spoexSearchStart, String spoexSearchEnd, boolean spoexLimitation, ArrayList<SearchKey> extKeys) {\r
+               //\r
+               for ( ProgList p : pcenter ) {\r
+                       // 局ごと\r
+                       \r
+                       // キーワード検索用\r
+                       String centerPop = TraceProgram.replacePop(p.Center);\r
+                       \r
+                       for ( ProgDateList c : p.pdate ) {\r
+                               // 日ごと\r
+                               boolean poisoned = false;\r
+                               for (ProgDetailList d : c.pdetail) {\r
+                                       // 番組ごと\r
+                                       boolean soi = false;\r
+                                       for ( SearchKey k : extKeys ) {\r
+                                               // 個別設定による延長可否\r
+                                               boolean isMatch = SearchProgram.isMatchKeyword(k, ((k.getCaseSensitive()==false)?(centerPop):(p.Center)), d);\r
+                                               if (isMatch) {\r
+                                                       if (k.getInfection().equals("0")) {\r
+                                                               // 延長感染源にする\r
+                                                               soi = true;\r
+                                                       }\r
+                                                       else {\r
+                                                               // 延長感染源にしない(優先)\r
+                                                               soi = false;\r
+                                                               break;\r
+                                                       }\r
+                                               }\r
+                                       }\r
+                                       if (soi) {\r
+                                               poisoned = true;\r
+                                       }\r
+                                       \r
+                                       d.extension = poisoned;\r
+                               }\r
+                       }\r
+               }\r
+       }\r
+       \r
+       /*\r
+        * タイトルからフラグを抽出する\r
+        */\r
+       protected void doSplitFlags(ProgDetailList pdl, HashMap<String, String> nf) {\r
+               \r
+               Matcher md = Pattern.compile("(#1|第1話)\\b").matcher(pdl.title);\r
+               if ( md.find() ) {\r
+                       pdl.flag = ProgFlags.NEW;\r
+               }\r
+               \r
+               md = Pattern.compile("([  ]?[<<]新[>>]| 新$| NEW$)").matcher(pdl.title);\r
+               if ( md.find() ) {\r
+                       pdl.flag = ProgFlags.NEW;\r
+                       pdl.title = md.replaceAll("");\r
+               }\r
+               md = Pattern.compile("([  ]?[<<]終[>>]| 終$| END$)").matcher(pdl.title);\r
+               if ( md.find() ) {\r
+                       pdl.flag = ProgFlags.LAST;\r
+                       pdl.title = md.replaceAll("");  \r
+               }\r
+               md = Pattern.compile("[((]終[))]",Pattern.DOTALL).matcher(pdl.detail);\r
+               if ( md.find() ) {\r
+                       pdl.flag = ProgFlags.LAST;\r
+               }\r
+               md = Pattern.compile("^無料≫").matcher(pdl.title);\r
+               if ( md.find() ) {\r
+                       pdl.noscrumble = ProgScrumble.NOSCRUMBLE;\r
+                       pdl.title = md.replaceAll("");  \r
+               }\r
+               \r
+               Pattern pat = Pattern.compile("初放送",Pattern.DOTALL);\r
+               if ( pat.matcher(pdl.detail).find() ) {\r
+                       pdl.addOption(ProgOption.FIRST);\r
+               }\r
+               else if ( pat.matcher(pdl.title).find() ) {\r
+                       pdl.addOption(ProgOption.FIRST);\r
+               }\r
+\r
+               pat = Pattern.compile("(視聴(..)?制限|[RR]([--]?[11][5588][++]?|指定))",Pattern.DOTALL);\r
+               if ( pat.matcher(pdl.detail).find() ) {\r
+                       pdl.addOption(ProgOption.RATING);\r
+               }\r
+               else if ( pat.matcher(pdl.title).find() ) {\r
+                       pdl.addOption(ProgOption.RATING);\r
+               }\r
+               \r
+               if ( pdl.detail.indexOf("5.1サラウンド") != -1 ) {\r
+                       pdl.addOption(ProgOption.SURROUND);\r
+               }\r
+\r
+               HashMap<String, String> xf = new HashMap<String, String>();\r
+               \r
+               String flagExpr = "[\\[[((【](.{1,3})[\\]]))】]";\r
+               Matcher mx = Pattern.compile(flagExpr).matcher(pdl.title);\r
+               while (mx.find()) {\r
+                       if (mx.group(1).equals("新") || mx.group(1).equals("新番組")) {\r
+                               pdl.flag = ProgFlags.NEW;\r
+                       }\r
+                       else if (mx.group(1).equals("終") || mx.group(1).equals("完") || mx.group(1).equals("最終回")) {\r
+                               pdl.flag = ProgFlags.LAST;\r
+                       }\r
+                       else if (mx.group(1).equals("再")) {\r
+                               pdl.addOption(ProgOption.REPEAT);\r
+                       }\r
+                       else if (mx.group(1).equals("初")) {\r
+                               pdl.addOption(ProgOption.FIRST);\r
+                       }\r
+                       else if (mx.group(1).equals("生")) {\r
+                               pdl.addOption(ProgOption.LIVE);\r
+                       }\r
+                       \r
+                       else if (mx.group(1).equals("二/吹")) {\r
+                               pdl.addOption(ProgOption.BILINGUAL);\r
+                               pdl.addOption(ProgOption.STANDIN);\r
+                       }\r
+                       else if (mx.group(1).equals("字") || mx.group(1).equals("字幕") || mx.group(1).equals("字幕版")) {\r
+                               pdl.addOption(ProgOption.SUBTITLE);\r
+                       }\r
+                       else if (mx.group(1).equals("二")) {\r
+                               pdl.addOption(ProgOption.BILINGUAL);\r
+                       }\r
+                       else if (mx.group(1).equals("多")) {\r
+                               pdl.addOption(ProgOption.MULTIVOICE);\r
+                       }\r
+                       else if (mx.group(1).equals("SS") || mx.group(1).equals("5.1")) {\r
+                               pdl.addOption(ProgOption.SURROUND);\r
+                       }\r
+                       else if (mx.group(1).equals("吹") || mx.group(1).equals("吹替") || mx.group(1).equals("吹替版")) {\r
+                               pdl.addOption(ProgOption.STANDIN);      // (ないよ)\r
+                       }\r
+                       else if (mx.group(1).equals("デ")) {\r
+                               pdl.addOption(ProgOption.DATA);\r
+                       }\r
+                       else if (mx.group(1).equals("無") || mx.group(1).equals("無料")) {\r
+                               //pdl.addOption(ProgOption.NOSCRUMBLE);\r
+                               pdl.noscrumble = ProgScrumble.NOSCRUMBLE;\r
+                       }\r
+                       \r
+                       else if (mx.group(1).matches("^(S|N|B|映|双|解|手|天|英|日|録|HV)$")) {\r
+                               // 無視するフラグ\r
+                               if ( mx.group(1).equals("日") && ( ! pdl.title.matches(String.format("^(%s)*[((]日[))].*", flagExpr)) && ! pdl.title.matches(".*[\\[[]日[\\]]].*")) ) {\r
+                                       // 削除しないフラグ(特殊)\r
+                                       continue;\r
+                               }\r
+                       }\r
+                       else if (mx.group(1).matches("^(韓|仮|[前後][編篇半]|[月火水木金土]|[0-90-9]+)$")) {\r
+                               // 削除しないフラグ\r
+                               continue;\r
+                       }\r
+                       \r
+                       else {\r
+                               // 未知のフラグ\r
+                               nf.put(mx.group(1),null);\r
+                               continue;\r
+                       }\r
+                       \r
+                       // 削除するフラグ\r
+                       xf.put(mx.group(1), null);\r
+               }\r
+               {\r
+                       // 認識されたフラグだけ削除する.\r
+                       String repExpr = "";\r
+                       for ( String f : xf.keySet() ) {\r
+                               repExpr += String.format("%s|",f);\r
+                       }\r
+                       if ( repExpr.length() > 0 ) {\r
+                               repExpr = "[\\[[((【]("+repExpr.substring(0, repExpr.length()-1)+")[\\]]))】]";\r
+                               pdl.title = pdl.title.replaceAll(repExpr, "");\r
+                       }\r
+               }\r
+               \r
+               if ( pdl.title.matches("^特[::].*") ) {\r
+                       pdl.option.add(ProgOption.SPECIAL);\r
+                       pdl.title = pdl.title.substring(2);\r
+               }\r
+               else if ( pdl.detail.contains("OVA") && ! pdl.detail.contains("+OVA") ) {\r
+                       pdl.option.add(ProgOption.SPECIAL);\r
+               }\r
+               else if ( pdl.detail.contains("未放送") ) {\r
+                       pdl.option.add(ProgOption.SPECIAL);\r
+               }\r
+       }\r
+       \r
+       /**\r
+        * マルチジャンル処理\r
+        */\r
+       protected void setMultiGenre(ProgDetailList pdl, ArrayList<String> genrelist) {\r
+               // コード順にならべかえる\r
+               Collections.sort(genrelist);\r
+\r
+               // ここに入ってこない限り genrelist == null なので対応プラグイン以外ではマルチジャンルは機能しない\r
+               pdl.genrelist = new ArrayList<TVProgram.ProgGenre>();\r
+               pdl.subgenrelist = new ArrayList<TVProgram.ProgSubgenre>(); \r
+                               \r
+               String gcode = ProgGenre.NOGENRE.toIEPG();\r
+               String subgcode = ProgSubgenre.NOGENRE_ETC.toIEPG();\r
+               \r
+               // マルチジャンルコードを設定する\r
+               for ( String gstr : genrelist ) {\r
+                       // ジャンルコードが複数ある場合は基本的に最初のものを代表にするが、一部例外をもうける(ニュースよりドキュメンタリー優先、など)\r
+                       // 鯛ナビの一覧で表示されるジャンルは代表のものだけである\r
+                       String gv = gstr.substring(0,1); \r
+                       String subgv = gstr.substring(1,2); \r
+                       if ( gcode.equals(ProgGenre.NOGENRE.toIEPG()) ) {\r
+                               gcode = gv;\r
+                               subgcode = subgv;\r
+                       }\r
+                       else if ( gcode.equals(ProgGenre.NEWS.toIEPG()) && gv.equals(ProgGenre.DOCUMENTARY.toIEPG())) {\r
+                               gcode = gv;\r
+                               subgcode = subgv;\r
+                       }\r
+                       /*\r
+                       else if ( gcode.equals(ProgGenre.MUSIC.toIEPG()) && md.group(1).equals(ProgGenre.VARIETY.toIEPG())) {\r
+                               gcode = md.group(1);\r
+                               subgcode = md.group(2);\r
+                       }\r
+                       */\r
+                       \r
+                       // 3.14.12βでマルチジャンル対応を追加した\r
+                       // 一覧では代表しか見えないが、検索処理ではすべてのジャンルコードが対象になる\r
+                       {\r
+                               ProgGenre ng = ProgGenre.NOGENRE;\r
+                               ProgSubgenre nsubg = ProgSubgenre.NOGENRE_ETC;\r
+                               for ( ProgGenre g : ProgGenre.values() ) {\r
+                                       if ( g.toIEPG().equals(gv) ) {\r
+                                               ng = g;\r
+                                               for ( ProgSubgenre subg : ProgSubgenre.values() ) {\r
+                                                       if ( subg.getGenre().equals(g) && subg.toIEPG().equals(subgv) ) {\r
+                                                               nsubg = subg;\r
+                                                               break;\r
+                                                       }\r
+                                               }\r
+                                               break;\r
+                                       }\r
+                               }\r
+                               if ( ng != ProgGenre.NOGENRE ) {\r
+                                       pdl.genrelist.add(ng);\r
+                                       pdl.subgenrelist.add(nsubg);\r
+                               }\r
+                       }\r
+                       if ( pdl.genrelist.size() == 0 ) {\r
+                               pdl.genrelist.add(ProgGenre.NOGENRE);\r
+                               pdl.subgenrelist.add(ProgSubgenre.NOGENRE_ETC);\r
+                       }\r
+               }\r
+               \r
+               // 代表ジャンルコードを設定する\r
+               for ( ProgGenre g : ProgGenre.values() ) {\r
+                       if ( g.toIEPG().equals(gcode) ) {\r
+                               pdl.genre = g;\r
+                               for ( ProgSubgenre subg : ProgSubgenre.values() ) {\r
+                                       if ( subg.getGenre().equals(g) && subg.toIEPG().equals(subgcode) ) {\r
+                                               pdl.subgenre = subg;\r
+                                               break;\r
+                                       }\r
+                               }\r
+                               break;\r
+                       }\r
+               }\r
+       }\r
+       \r
+       \r
+       /*******************************************************************************\r
+        * 通信系\r
+        ******************************************************************************/\r
+       \r
+       // Web上から取得してファイルにキャッシュする\r
+       \r
+       // GET型\r
+       public void webToFile(String uri, String fname, String thisEncoding) {\r
+               webToFile(uri, null, null, null, fname, thisEncoding);\r
+       }\r
+       \r
+       // POST型\r
+       public void webToFile(String uri, String pstr, String cookie, String referer, String fname, String thisEncoding) {\r
+               int retry = 0;\r
+               while (true) {\r
+                       if ( _webToFile(uri, pstr, cookie, referer, fname, thisEncoding) == true ) {\r
+                               break;\r
+                       }\r
+                       if ( ++retry > retrycount ) {\r
+                               break;\r
+                       }\r
+                       System.out.println("wait for retry...");\r
+                       CommonUtils.milSleep(1000);\r
+               }\r
+       }\r
+       \r
+       // GET/POST本体\r
+       private boolean _webToFile(String uri, String pstr, String cookie, String referer, String fname, String thisEncoding) {\r
+               \r
+               HttpURLConnection ucon = null;\r
+               BufferedWriter filewriter = null;\r
+               BufferedReader filereader = null;\r
+               BufferedOutputStream streamwriter = null;\r
+               BufferedInputStream streamreader = null;\r
+               try {\r
+                       ucon = _webToXXX(uri,pstr,cookie,referer,thisEncoding);\r
+                       if (ucon == null) {\r
+                               return false;\r
+                       }\r
+                       \r
+                       // 一時ファイルに書き出し\r
+                       if (thisEncoding != null) {\r
+                               filewriter = new BufferedWriter(new FileWriter(fname+".tmp"));\r
+                               filereader = new BufferedReader(new InputStreamReader(ucon.getInputStream(), thisEncoding));\r
+                               String str;\r
+                           while((str = filereader.readLine()) != null){\r
+                               filewriter.write(str);\r
+                               filewriter.write("\n");\r
+                           }\r
+                           filewriter.close();\r
+                           filewriter = null;\r
+                           filereader.close();\r
+                           filereader = null;\r
+                       }\r
+                       else {\r
+                               streamwriter = new BufferedOutputStream(new FileOutputStream(fname+".tmp"));\r
+                               streamreader = new BufferedInputStream(ucon.getInputStream());\r
+                               byte[] buf = new byte[65536];\r
+                               int len;\r
+                           while((len = streamreader.read(buf,0,buf.length)) != -1){\r
+                               streamwriter.write(buf,0,len);\r
+                           }\r
+                           streamwriter.close();\r
+                           streamwriter = null;\r
+                           streamreader.close();\r
+                           streamreader = null;\r
+                       }\r
+                   ucon.disconnect();\r
+                   ucon = null;\r
+                       \r
+                   // クローズしてからじゃないと失敗するよ \r
+                   \r
+                       // キャッシュファイルに変換\r
+                   File o = new File(fname);\r
+                   if ( o.exists() && ! o.delete() ) {\r
+                       System.err.println("削除できないよ: "+fname);\r
+                   }\r
+                   File n = new File(fname+".tmp");\r
+                   if ( ! n.renameTo(o) ) {\r
+                       System.err.println("リネームできないよ: "+fname+".tmp to "+fname);\r
+                       return false;\r
+                   }\r
+                       \r
+                   return true;\r
+               }\r
+               catch (Exception e) {\r
+                       // 例外\r
+                       System.out.println("Webアクセスに失敗しました("+uri+"): "+e);\r
+               }\r
+               finally {\r
+                       CommonUtils.closing(filewriter);\r
+                       CommonUtils.closing(filereader);\r
+                       CommonUtils.closing(streamwriter);\r
+                       CommonUtils.closing(streamreader);\r
+                       CommonUtils.closing(ucon);\r
+               }\r
+               return false;\r
+       }\r
+\r
+       // Web上から取得してバッファに格納する\r
+       \r
+       // GET型\r
+       public String webToBuffer(String uri, String thisEncoding, boolean nocr) {\r
+               return webToBuffer(uri, null, null, null, thisEncoding, nocr);\r
+       }\r
+       \r
+       // POST型\r
+       public String webToBuffer(String uri, String pstr, String cookie, String referer, String thisEncoding, boolean nocr) {\r
+               int retry = 0;\r
+               while (true) {\r
+                       String response = _webToBuffer(uri, pstr, cookie, referer, thisEncoding, nocr); \r
+                       if ( response != null ) {\r
+                               return response;\r
+                       }\r
+                       if ( ++retry > retrycount ) {\r
+                               break;\r
+                       }\r
+                       System.out.println("wait for retry...");\r
+                       CommonUtils.milSleep(1000);\r
+               }\r
+               return null;\r
+       }\r
+       \r
+       // 本体\r
+       private String _webToBuffer(String uri, String pstr, String cookie, String referer, String thisEncoding, boolean nocr) {\r
+\r
+               if ( thisEncoding == null ) {\r
+                       return null;\r
+               }\r
+               \r
+               try {\r
+                       HttpURLConnection ucon = null;\r
+                       BufferedReader reader = null;\r
+                       try {\r
+                               ucon = _webToXXX(uri,pstr,cookie,referer,thisEncoding);\r
+                               if (ucon == null) {\r
+                                       return null;\r
+                               }\r
+                               \r
+                               // バッファ作成\r
+                               StringBuilder sb = new StringBuilder();\r
+                               reader = new BufferedReader(new InputStreamReader(ucon.getInputStream(), thisEncoding));\r
+                               String str;\r
+                           while((str = reader.readLine()) != null){\r
+                               sb.append(str);\r
+                               if ( ! nocr) sb.append("\n");\r
+                           }\r
+                           return sb.toString();\r
+                       }\r
+                       catch (Exception e) {\r
+                               System.out.println("Webアクセスに失敗しました("+uri+"): "+e.toString());\r
+                               //e.printStackTrace();\r
+                       }\r
+                       finally {\r
+                               if ( reader != null ) {\r
+                                       reader.close();\r
+                                       reader = null;\r
+                               }\r
+                               if ( ucon != null ) {\r
+                                       ucon.disconnect();\r
+                                       ucon = null;\r
+                               }\r
+                       }\r
+               }\r
+               catch ( Exception e ) {\r
+                       // close()の例外は無視\r
+               }\r
+               return null;\r
+       }\r
+       \r
+       /*\r
+        * File/Bufferの共通部品\r
+        */\r
+       \r
+       private HttpURLConnection _webToXXX(String uri, String pstr, String cookie, String referer, String thisEncoding) {\r
+               try {\r
+                       URL url = new URL(uri);\r
+                       HttpURLConnection ucon;\r
+                       if ( getProxy() == null ) {\r
+                               ucon = (HttpURLConnection)url.openConnection();\r
+                       }\r
+                       else {\r
+                               ucon = (HttpURLConnection)url.openConnection(getProxy());\r
+                       }\r
+                       ucon.setConnectTimeout(conntout*1000);\r
+                       ucon.setReadTimeout(readtout*1000);\r
+       \r
+                       ucon.addRequestProperty("User-Agent", getUserAgent());\r
+                       \r
+                       if ( referer != null ) {\r
+                               ucon.addRequestProperty("Referer", referer);\r
+                       }\r
+                       \r
+                       if ( cookie != null ) {\r
+                               ucon.addRequestProperty("Cookie", cookie);\r
+                       }\r
+                       \r
+                       // Cookie処理(CookieManagerはなぜうまく動かないんだ…orz)\r
+                       if ( cookie_cache.size() > 0 ) {\r
+                               String cStr = "";\r
+                               for ( String key : cookie_cache.keySet() ) {\r
+                                       cStr += key+"="+cookie_cache.get(key)+"; ";\r
+                               }\r
+                               ucon.addRequestProperty("Cookie", cStr);\r
+                       }\r
+       \r
+                       if (pstr != null) {\r
+                               ucon.setRequestMethod("POST");\r
+                               ucon.setDoOutput(true);\r
+                               ucon.setDoInput(true);\r
+                       }\r
+                       else {\r
+                               ucon.setRequestMethod("GET");\r
+                               ucon.connect();\r
+                       }\r
+\r
+                       // POSTの場合は別途データを送る\r
+                       if (pstr != null && thisEncoding != null) {\r
+                               OutputStream writer = ucon.getOutputStream();\r
+                               writer.write(pstr.getBytes(thisEncoding));\r
+                               writer.flush();\r
+                               writer.close();\r
+                       }\r
+                       \r
+                       // Cookie処理\r
+                       if ( uri.matches(".*dimora.jp.*") || uri.matches(".*\\.yahoo\\.co\\.jp.*") ) {\r
+                               List<String> hf = ucon.getHeaderFields().get("Set-Cookie");\r
+                               if ( hf != null ) {\r
+                               for ( String rcookie :  hf ) {\r
+                                       String[] rc1 = rcookie.split(";",2);\r
+                                       String[] rc2 = rc1[0].split("=",2);\r
+                                       cookie_cache.put(rc2[0], rc2[1]);\r
+                               }\r
+                               }\r
+                   }\r
+                       \r
+                       return ucon;\r
+                       \r
+               } catch (MalformedURLException e) {\r
+                       e.printStackTrace();\r
+               } catch (IOException e) {\r
+                       e.printStackTrace();\r
+               }\r
+               \r
+               return null;\r
+       }\r
+       \r
+       protected String getCookie(String key) {\r
+               return cookie_cache.get(key);\r
+       }\r
+       protected void addCookie(String key, String val) {\r
+               cookie_cache.put(key,val);\r
+       }\r
+       protected void delCookie(String key) {\r
+               cookie_cache.remove(key);\r
+       }\r
+       protected void clrCookie() {\r
+               cookie_cache.clear();\r
+       }\r
+       \r
+       // Cookieの情報をキャッシュするよ\r
+       private HashMap<String,String> cookie_cache = new HashMap<String, String>();\r
+               \r
+       \r
+       /*******************************************************************************\r
+        * ここから下は該当機能が無効なプラグイン用のダミー\r
+        ******************************************************************************/\r
+       \r
+       public String getTVProgramId() { return "DUMMY"; }\r
+       \r
+       public String getDefaultArea() { return "東京"; }\r
+       \r
+       // 番組詳細キャッシュファイル名\r
+       protected String getDetCacheFile() { return ""; }\r
+       \r
+       // 番組詳細をattachする\r
+       protected void attachDetails(ArrayList<ProgList> plist, HashMap<String,String> oldcache, HashMap<String,String> newcache) {\r
+               // ダミー\r
+       }\r
+\r
+       // フリーテキストによるオプション指定\r
+       public boolean setOptString(String s) { return true; }  // ダミー\r
+       public String getOptString() { return null; }                   // ダミー\r
+       \r
+}\r
diff --git a/TinyBannavi/src/tainavi/TatCount.java b/TinyBannavi/src/tainavi/TatCount.java
new file mode 100644 (file)
index 0000000..ca98a02
--- /dev/null
@@ -0,0 +1,23 @@
+package tainavi;\r
+\r
+import java.util.Date;\r
+import java.util.GregorianCalendar;\r
+\r
+public class TatCount {\r
+\r
+       private GregorianCalendar ca = new GregorianCalendar();\r
+       private GregorianCalendar cz = new GregorianCalendar();\r
+       \r
+       public void restart() {\r
+               ca.setTime(new Date());\r
+       }\r
+       \r
+       public double end() {\r
+               cz.setTime(new Date());\r
+               return (cz.getTimeInMillis() - ca.getTimeInMillis())/1000.0D;\r
+       }\r
+       \r
+       public TatCount() {\r
+               restart();\r
+       }\r
+}\r
diff --git a/TinyBannavi/src/tainavi/TextEditPopupMenu.java b/TinyBannavi/src/tainavi/TextEditPopupMenu.java
new file mode 100644 (file)
index 0000000..5e58323
--- /dev/null
@@ -0,0 +1,84 @@
+package tainavi;\r
+\r
+import java.awt.event.KeyEvent;\r
+import java.awt.event.MouseAdapter;\r
+import java.awt.event.MouseEvent;\r
+\r
+import javax.swing.Action;\r
+import javax.swing.ActionMap;\r
+import javax.swing.JComponent;\r
+import javax.swing.JMenuItem;\r
+import javax.swing.JPopupMenu;\r
+import javax.swing.KeyStroke;\r
+import javax.swing.text.DefaultEditorKit;\r
+\r
+public class TextEditPopupMenu extends MouseAdapter {\r
+\r
+       @Override\r
+       public void mouseReleased(MouseEvent e) {\r
+               mousePopup(e);\r
+       }\r
+       \r
+       @Override\r
+       public void mousePressed(MouseEvent e) {\r
+               mousePopup(e);\r
+       }\r
+       \r
+       @Override\r
+       public void mouseExited(MouseEvent e) {\r
+       }\r
+       \r
+       @Override\r
+       public void mouseEntered(MouseEvent e) {\r
+       }\r
+       \r
+       @Override\r
+       public void mouseClicked(MouseEvent e) {\r
+       }\r
+       \r
+       //\r
+       private void mousePopup(MouseEvent e) {\r
+               if (e.isPopupTrigger()) {\r
+                       JComponent c = (JComponent)e.getSource();\r
+                       showPopup(c, e.getX(), e.getY());\r
+                       e.consume();\r
+               }\r
+       }\r
+       \r
+       private void showPopup(JComponent c, int x, int y) {\r
+               \r
+        JPopupMenu pmenu = new JPopupMenu();\r
+        \r
+               ActionMap am = c.getActionMap();\r
+               \r
+               Action cut = am.get(DefaultEditorKit.cutAction);\r
+               addMenu(pmenu, "切り取り(X)", cut, 'X', KeyStroke.getKeyStroke(KeyEvent.VK_X, KeyEvent.CTRL_DOWN_MASK));\r
+\r
+               Action copy = am.get(DefaultEditorKit.copyAction);\r
+               addMenu(pmenu, "コピー(C)", copy, 'C', KeyStroke.getKeyStroke(KeyEvent.VK_C, KeyEvent.CTRL_DOWN_MASK));\r
+\r
+               Action paste = am.get(DefaultEditorKit.pasteAction);\r
+               addMenu(pmenu, "貼り付け(V)", paste, 'V', KeyStroke.getKeyStroke(KeyEvent.VK_V, KeyEvent.CTRL_DOWN_MASK));\r
+\r
+               Action all = am.get(DefaultEditorKit.selectAllAction);\r
+               addMenu(pmenu, "すべて選択(A)", all, 'A', KeyStroke.getKeyStroke(KeyEvent.VK_A, KeyEvent.CTRL_DOWN_MASK));\r
+        \r
+               c.requestFocusInWindow();\r
+               pmenu.show(c, x, y);\r
+       }\r
+       \r
+       private void addMenu(JPopupMenu pmenu, String text, Action action, int mnemonic, KeyStroke ks) {\r
+               if (action != null) {\r
+                       JMenuItem mi = pmenu.add(action);\r
+                       if (text != null) {\r
+                               mi.setText(text);\r
+                       }\r
+                       if (mnemonic != 0) {\r
+                               mi.setMnemonic(mnemonic);\r
+                       }\r
+                       if (ks != null) {\r
+                               mi.setAccelerator(ks);\r
+                       }\r
+               }\r
+       }\r
+}\r
diff --git a/TinyBannavi/src/tainavi/TextValueSet.java b/TinyBannavi/src/tainavi/TextValueSet.java
new file mode 100644 (file)
index 0000000..a89bc1e
--- /dev/null
@@ -0,0 +1,14 @@
+package tainavi;\r
+\r
+public class TextValueSet {\r
+       private String Text;\r
+       private String Value;\r
+       private boolean defval = false;\r
+       \r
+       public String getText() { return Text; }\r
+       public void setText(String t) { Text = t; } \r
+       public String getValue() { return Value; }\r
+       public void setValue(String v) { Value = v; }\r
+       public boolean getDefval() { return defval; }\r
+       public void setDefval(boolean b) { defval = b; }\r
+}\r
diff --git a/TinyBannavi/src/tainavi/TraceKey.java b/TinyBannavi/src/tainavi/TraceKey.java
new file mode 100644 (file)
index 0000000..53196e0
--- /dev/null
@@ -0,0 +1,35 @@
+package tainavi;\r
+\r
+import java.util.ArrayList;\r
+\r
+public class TraceKey {\r
+       \r
+       public static final int defaultFazzyThreshold = 35;\r
+       public static final int noFazzyThreshold = 0;\r
+       \r
+       private String label = null;\r
+       private String titlePop = null;\r
+       private String center = null;\r
+       private int fazzyThreshold = 0;\r
+       private String okiniiri = null;\r
+       private ArrayList<String> SearchStrKeys = null;\r
+       private boolean disableRepeat = false;\r
+       private boolean showLatestOnly = false;\r
+       \r
+       public void setLabel(String s) { label = s; }\r
+       public String getLabel() { return label; }\r
+       public void setTitlePop(String s) { titlePop = s; }\r
+       public String getTitlePop() { return titlePop; }\r
+       public void setCenter(String s) { center = s; }\r
+       public String getCenter() { return center; }\r
+       public void setFazzyThreshold(int n) { fazzyThreshold = n; }\r
+       public int getFazzyThreshold() { return fazzyThreshold; };\r
+       public void setOkiniiri(String s) { okiniiri = s; }\r
+       public String getOkiniiri() { return okiniiri; }\r
+       public void setSearchStrKeys(ArrayList<String> sa) { SearchStrKeys = sa; }\r
+       public ArrayList<String> getSearchStrKeys() { return SearchStrKeys; }\r
+       public void setDisableRepeat(boolean b) { disableRepeat = b; }\r
+       public boolean getDisableRepeat() { return disableRepeat; }\r
+       public void setShowLatestOnly(boolean b) { showLatestOnly = b; }\r
+       public boolean getShowLatestOnly() { return showLatestOnly; }\r
+}\r
diff --git a/TinyBannavi/src/tainavi/TraceProgram.java b/TinyBannavi/src/tainavi/TraceProgram.java
new file mode 100644 (file)
index 0000000..b60a485
--- /dev/null
@@ -0,0 +1,173 @@
+package tainavi;\r
+\r
+import java.io.File;\r
+import java.util.ArrayList;\r
+\r
+public class TraceProgram {\r
+\r
+       private static final String MSGID = "[番組追跡設定] ";\r
+       private static final String ERRID = "[ERROR]"+MSGID;\r
+       \r
+       //\r
+       private final String traceKeyFile = "env"+File.separator+"tracekey.xml";\r
+       private ArrayList<TraceKey> traceKeys = new ArrayList<TraceKey>();\r
+       \r
+       // 設定ファイルに書き出し\r
+       public void save() {\r
+               System.out.println(MSGID+"保存します: "+traceKeyFile);\r
+               if ( ! CommonUtils.writeXML(traceKeyFile, traceKeys) ) {\r
+                       System.err.println(ERRID+"保存に失敗しました: "+traceKeyFile);\r
+               }\r
+               return;\r
+       }\r
+       \r
+       // 設定ファイルから読み出し\r
+       @SuppressWarnings("unchecked")\r
+       public void load() {\r
+               if ( ! new File(traceKeyFile).exists() ) {\r
+                       System.out.println(MSGID+"設定を読み込めなかったので登録なしで起動します: "+traceKeyFile);\r
+                       return;\r
+               }\r
+               \r
+               System.out.println(MSGID+"読み込みます: "+traceKeyFile);\r
+               \r
+               traceKeys = (ArrayList<TraceKey>)CommonUtils.readXML(traceKeyFile);\r
+               if ( traceKeys != null ) {\r
+               for (TraceKey tr : traceKeys) {\r
+                       // 後方互換用\r
+                       if (tr.getFazzyThreshold() == 0) {\r
+                               tr.setFazzyThreshold(TraceKey.defaultFazzyThreshold);\r
+                       }\r
+                       if (tr.getOkiniiri() == null) {\r
+                               tr.setOkiniiri("★");\r
+                       }\r
+                       if (tr.getSearchStrKeys() == null) {\r
+                               tr.setSearchStrKeys(splitKeys(tr.getTitlePop()));\r
+                       }\r
+               }\r
+               }\r
+               if ( traceKeys == null ) {\r
+               System.out.println(ERRID+"設定を読み込めなかったので登録なしで起動します: "+traceKeyFile);\r
+               }\r
+       }\r
+       \r
+       // 検索用\r
+       public ArrayList<TraceKey> getTraceKeys() {\r
+               return(traceKeys);\r
+       }\r
+       \r
+       // 番組追跡の追加\r
+       public void add(String label, String key, String value, int fazzyThreshold) {\r
+               TraceKey newkey = new TraceKey();\r
+               newkey.setLabel(label);\r
+               newkey.setTitlePop(replacePop(key));\r
+           newkey.setSearchStrKeys(splitKeys(newkey.getTitlePop()));\r
+               newkey.setCenter(value);\r
+               newkey.setFazzyThreshold(fazzyThreshold);\r
+               newkey.setOkiniiri(TVProgram.OKINIIRI[0]);\r
+               newkey.setDisableRepeat(false);\r
+               newkey.setShowLatestOnly(false);\r
+               \r
+               traceKeys.add(newkey);\r
+       }\r
+       public void add(TraceKey newkey) {\r
+               traceKeys.add(newkey);\r
+       }\r
+       \r
+       // 番組追跡の削除\r
+       public void remove(String key) {\r
+               for ( TraceKey k : traceKeys ) {\r
+                       if (k.getLabel().equals(key)) {\r
+                               traceKeys.remove(k);\r
+                       break;\r
+                       }\r
+               }\r
+       }\r
+       \r
+       private static final String popSrc = "あいうえおかきくけこさしすせそたちつてとなにぬねのはひふへほまみむめもやゐゆゑよらりるれろわをんぁぃぅぇぉっゃゅょがぎぐげござじずぜぞだぢづでどばびぶべぼぱぴぷぺぽァィゥェォッャュョガギグゲゴザジズゼゾダヂヅデドバビブベボパピプペポヴアイウエオカキクケコサシスセソタチツテトナニヌネノハヒフヘホマミムメモヤユヨラリルレロワヲンァィゥェォッャュョabcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz1234567890!@#$%^&*()-_=+「{」}¥|;:’”、<。>・?‘~ 一二三四五六七八九〇ⅠⅡⅢⅣⅤⅥⅦⅧⅨ①②③④⑤⑥⑦⑧⑨壱弐肆零";\r
+       private static final String popDst = "アイウエオカキクケコサシスセソタチツテトナニヌネノハヒフヘホマミムメモヤイユエヨラリルレロワヲンアイウエオツヤユヨカキクケコサシスセソタチツテトハヒフヘホハヒフヘホアイウエオツヤユヨカキクケコサシスセソタチツテトハヒフヘホハヒフヘホウアイウエオカキクケコサシスセソタチツテトナニヌネノハヒフヘホマミムメモヤユヨラリルレロワヲンアイウエオツヤユヨABCDEFGHIJKLMNOPQRSTUVWXYZABCDEFGHIJKLMNOPQRSTUVWXYZABCDEFGHIJKLMNOPQRSTUVWXYZ1234567890!@#$%^&*()-_=+[{]}\\|;:'\",<.>/?`~ 12345678901234567891234567891240";\r
+       private static char[] popDstA = null;\r
+       \r
+       // 文字列を比較しやすい値にする\r
+       public static boolean isOmittedChar(char ch) {\r
+               return (ch == ' ' || ch == ' ' || ch == 'ー' || ch == '゙' || ch == '゚');\r
+       }\r
+       public static String replacePop(String src)\r
+       {\r
+               if (popDstA == null) {\r
+                       popDstA = popDst.toCharArray();\r
+               }\r
+               \r
+               src = src.replaceAll("[  ー゙゚]", "");\r
+               \r
+               StringBuilder sb = new StringBuilder();\r
+               for ( char c : src.toCharArray() ) {\r
+                       int index = popSrc.indexOf(c);\r
+                       if ( index >= 0 ) {\r
+                               sb.append(popDstA[index]);\r
+                       }\r
+                       else {\r
+                               sb.append(c);\r
+                       }\r
+               }\r
+               return(sb.toString());\r
+       }\r
+\r
+       // あいまい検索用キー文字列群の生成\r
+       public static ArrayList<String> splitKeys(String s)\r
+       {\r
+               ArrayList<String> SearchStrKeys = new ArrayList<String>();\r
+           \r
+           int countStr=s.length();\r
+           if (countStr == 1) {\r
+               SearchStrKeys.add(s);\r
+           }\r
+           else {\r
+                   for (int i=1; i<countStr; i++) {\r
+                       SearchStrKeys.add(s.substring(i-1, i+1));\r
+                   }\r
+           }\r
+           \r
+           return(SearchStrKeys);\r
+       }\r
+       \r
+       // 2つの文字を比較してスコアを計算する(special thanks to ◆kzz0PzTAMM)\r
+       public static int sumScore(String SearchStr1, String SearchStr2)\r
+       {\r
+           // 検索ワードが空なら検索終了\r
+               if (SearchStr1.equals("") || SearchStr2.equals("")) {\r
+                       return 0;\r
+               }\r
+               \r
+           // 検索ワードを基準に検索する\r
+           return sumScore(splitKeys(SearchStr1), SearchStr2);\r
+       }\r
+       public static int sumScore(ArrayList<String> SearchStr1Keys, String SearchStr2)\r
+       {\r
+       \r
+           // 検索ワードが空なら検索終了\r
+               if (SearchStr1Keys.size() == 0 || "".equals(SearchStr2)) {\r
+                       return 0;\r
+               }\r
+\r
+           // 検索ワードを基準に検索する\r
+           int searchCount=0;\r
+           int score=0;\r
+           for (String key : SearchStr1Keys) {\r
+               if (SearchStr2.indexOf(key) >= 0) {\r
+                       score++;\r
+               }\r
+               searchCount++;\r
+           }\r
+\r
+           score=Math.round(score*100/searchCount);\r
+           return(score);\r
+    }\r
+       \r
+       \r
+       \r
+       // コンストラクタ\r
+       public TraceProgram() {\r
+               traceKeys = new ArrayList<TraceKey>();\r
+       }\r
+}\r
diff --git a/TinyBannavi/src/tainavi/TreeExpansionReg.java b/TinyBannavi/src/tainavi/TreeExpansionReg.java
new file mode 100644 (file)
index 0000000..44cd238
--- /dev/null
@@ -0,0 +1,190 @@
+package tainavi;\r
+\r
+import java.util.ArrayList;\r
+\r
+import javax.swing.JTree;\r
+import javax.swing.tree.DefaultMutableTreeNode;\r
+import javax.swing.tree.TreeModel;\r
+import javax.swing.tree.TreeNode;\r
+import javax.swing.tree.TreePath;\r
+\r
+/**\r
+ * リスト形式・新聞形式のサイドツリーのノード展開情報を記録します。\r
+ */\r
+public class TreeExpansionReg {\r
+\r
+       private static final String MSGID = "[ツリー展開情報] ";\r
+       private static final String ERRID = "[ERROR]"+MSGID;\r
+       private static final String DBGID = "[DEBUG]"+MSGID;\r
+       \r
+       private JTree tree;\r
+       private String fname;\r
+       private ArrayList<String[]> pathlist;\r
+       \r
+       private TreeNode[] getSelectedPathSub(DefaultMutableTreeNode node, String[] names, int idx) {\r
+               if (names.length > idx) {\r
+                       for (int i=0; i<node.getChildCount(); i++) {\r
+                               if (node.getChildAt(i).toString().equals(names[idx])) {\r
+                                       TreeNode[] childs = getSelectedPath((DefaultMutableTreeNode)node.getChildAt(i), names, idx+1);\r
+                                       if (childs == null) {\r
+                                               // もう子供がいない\r
+                                               if (idx != 0) {\r
+                                                       return(new TreeNode[] {node.getChildAt(i)});\r
+                                               }\r
+                                               else {\r
+                                                       return(new TreeNode[] {node, node.getChildAt(i)});\r
+                                               }\r
+                                       }\r
+                                       else {\r
+                                               // 子供がいた\r
+                                               if (idx != 0) {\r
+                                                       TreeNode[] tmp = new TreeNode[childs.length+1];\r
+                                                       tmp[0] = node.getChildAt(i);\r
+                                                       for (int j=0; j<childs.length; j++) {\r
+                                                               tmp[j+1] = childs[j];\r
+                                                       }\r
+                                                       return(tmp);\r
+                                               }\r
+                                               else {\r
+                                                       TreeNode[] tmp = new TreeNode[childs.length+2];\r
+                                                       tmp[0] = node;\r
+                                                       tmp[1] = node.getChildAt(i);\r
+                                                       for (int j=0; j<childs.length; j++) {\r
+                                                               tmp[j+2] = childs[j];\r
+                                                       }\r
+                                                       return(tmp);\r
+                                               }\r
+                                       }\r
+                               }\r
+                       }\r
+               }\r
+               return null;\r
+       }\r
+       \r
+       private void reg(DefaultMutableTreeNode node) {\r
+               for ( int i=0; i<node.getChildCount(); i++ ) {\r
+                       DefaultMutableTreeNode child = (DefaultMutableTreeNode) node.getChildAt(i);\r
+                       if ( ! child.isLeaf() ) {\r
+                               TreePath path = new TreePath(child.getPath());\r
+                               if ( tree.isExpanded(path) ) {\r
+                                       String[] names = new String[path.getPathCount()];\r
+                                       int j=0;\r
+                                       for ( Object o : path.getPath() ) {\r
+                                               names[j++] = o.toString();\r
+                                       }\r
+                                       pathlist.add(names);\r
+                               }\r
+                               reg(child);\r
+                       }\r
+               }\r
+       }\r
+\r
+       // リストを取得する\r
+       private TreeNode getChild(TreeModel model, Object parent, String q) {\r
+               //\r
+               for ( int i=model.getChildCount(parent), index=0; index<i; index++ ) {\r
+                       TreeNode child = (DefaultMutableTreeNode)model.getChild(parent, index);\r
+                       if ( child != null && child.toString().equals(q) ) {\r
+                               return child;\r
+                       }\r
+               }\r
+               return null;\r
+       }\r
+       private Object[] getChildren(TreeModel model, String[] nodenames) {\r
+               //\r
+               ArrayList<TreeNode> nodes =  new ArrayList<TreeNode>();\r
+               Object parent = model.getRoot();\r
+               nodes.add((TreeNode)parent);\r
+               for ( int i=1; i<nodenames.length; i++ ) {\r
+                       TreeNode child = getChild(model,parent,nodenames[i]);\r
+                       if ( child == null ) {\r
+                               return null;\r
+                       }\r
+                       nodes.add(child);\r
+                       parent = child;\r
+               }\r
+               if ( nodenames.length == nodes.size() ) {\r
+                       return nodes.toArray();\r
+               }\r
+               return null;\r
+       }\r
+       \r
+       /*\r
+        * 公開メソッド\r
+        */\r
+       \r
+       /**\r
+        * 展開されているツリーの情報を保存する\r
+        */\r
+       public void reg() {\r
+               pathlist.clear();\r
+               try {\r
+                       reg((DefaultMutableTreeNode) tree.getModel().getRoot());\r
+               }\r
+               catch ( Exception e ) {\r
+                       System.err.println(ERRID+"展開情報の記録に失敗しました");\r
+               }\r
+       }\r
+       \r
+       /**\r
+        * ツリーの再表示のため選択中だったパスを返却する\r
+        */\r
+       public TreeNode[] getSelectedPath(DefaultMutableTreeNode node, String[] names, int idx) {\r
+               try {\r
+                       return getSelectedPathSub(node, names, idx);\r
+               }\r
+               catch ( Exception e ) {\r
+                       System.err.println(ERRID+"展開情報の検索に失敗しました");\r
+               }\r
+               return null;\r
+       }\r
+       \r
+       /**\r
+        * ツリーの展開状態をすべて吐き出すっ\r
+        */\r
+       public ArrayList<TreePath> get() {\r
+               //\r
+               try{\r
+                       ArrayList<TreePath> newpaths = new ArrayList<TreePath>();\r
+                       TreeModel model = tree.getModel();\r
+                       for ( String[] nodenames : pathlist ) {\r
+                               Object[] nodes =  getChildren(model,nodenames);\r
+                               if ( nodes != null ) {\r
+                                       newpaths.add(new TreePath(nodes));\r
+                               }\r
+                       }\r
+                       return newpaths;\r
+               }\r
+               catch ( Exception e ) {\r
+                       System.err.println(ERRID+"展開情報の検索に失敗しました");\r
+               }\r
+               return null;\r
+       }\r
+\r
+       public void save() {\r
+               System.out.println(MSGID+"展開状態を保存します: "+fname);\r
+               if ( ! CommonUtils.writeXML(fname, pathlist) ) {\r
+                       System.err.println(ERRID+"展開情報を保存できませんでした: "+fname);\r
+               }\r
+       }\r
+       \r
+       public void load() {\r
+               System.out.println(MSGID+"展開状態を読み込みます: "+fname);\r
+               @SuppressWarnings("unchecked")\r
+               ArrayList<String[]> nameslist = (ArrayList<String[]>) CommonUtils.readXML(fname);\r
+               if ( nameslist == null ) {\r
+                       System.err.println(ERRID+"展開情報を取得できませんでした: "+fname);\r
+                       return;\r
+               }\r
+               pathlist = nameslist;\r
+       }\r
+       \r
+       /**\r
+        *  コンストラクタ\r
+        */\r
+       public TreeExpansionReg(JTree tree, String fname) {\r
+               this.tree = tree;\r
+               this.fname = fname;\r
+               this.pathlist = new ArrayList<String[]>();\r
+       }\r
+}\r
diff --git a/TinyBannavi/src/tainavi/VWColorCellRenderer.java b/TinyBannavi/src/tainavi/VWColorCellRenderer.java
new file mode 100644 (file)
index 0000000..8c6c68b
--- /dev/null
@@ -0,0 +1,23 @@
+package tainavi;\r
+\r
+import java.awt.Color;\r
+\r
+import javax.swing.JLabel;\r
+import javax.swing.JTable;\r
+\r
+/**\r
+ * 後方互換のため残してあるだけ\r
+ * @see VWColorCharCellRenderer\r
+ */\r
+public class VWColorCellRenderer extends VWColorCharCellRenderer {\r
+\r
+       private static final long serialVersionUID = 1L;\r
+\r
+       public VWColorCellRenderer() {\r
+               super(JLabel.CENTER,true);\r
+       }\r
+       \r
+       @Override\r
+       protected Color getInvertedForeground(JTable table) { return table.getForeground(); }\r
+       \r
+}\r
diff --git a/TinyBannavi/src/tainavi/VWColorCharCellRenderer.java b/TinyBannavi/src/tainavi/VWColorCharCellRenderer.java
new file mode 100644 (file)
index 0000000..97fd721
--- /dev/null
@@ -0,0 +1,111 @@
+package tainavi;\r
+\r
+import java.awt.Color;\r
+import java.awt.Component;\r
+import java.awt.Font;\r
+\r
+import javax.swing.JLabel;\r
+import javax.swing.JTable;\r
+import javax.swing.table.DefaultTableCellRenderer;\r
+\r
+/**\r
+ * 文字色をつけられるセルのレンダラ。反転して背景色を変えることもできる。\r
+ */\r
+public class VWColorCharCellRenderer extends DefaultTableCellRenderer {\r
+       \r
+       private static final long serialVersionUID = 1L;\r
+       \r
+       private boolean invert = false;\r
+       \r
+       private JLabel renderer = null;\r
+       private Font macFont = null;\r
+       \r
+       public VWColorCharCellRenderer() {\r
+               this(JLabel.CENTER,false);\r
+       }\r
+       public VWColorCharCellRenderer(int align) {\r
+               this(align,false);\r
+       }\r
+       public VWColorCharCellRenderer(int align, boolean invert) {\r
+               super();\r
+               this.invert = invert;\r
+               \r
+               renderer = new JLabel();\r
+               renderer.setHorizontalAlignment(align);\r
+               renderer.setOpaque(true);\r
+       }\r
+       \r
+       public void setMacMarkFont() {\r
+               Font f = super.getFont();\r
+               macFont = new Font("Osaka",f.getStyle(),f.getSize());\r
+       }\r
+       \r
+       @Override\r
+       public Component getTableCellRendererComponent(JTable table, Object value,\r
+                       boolean isSelected, boolean hasFocus, int row, int column) {\r
+               \r
+               Color c = Color.BLACK;\r
+               String es = null;\r
+               String val = (String)value;\r
+               if ( val != null ) {\r
+                       if ( val.length() > 8 ) {\r
+                               String cs = val.substring(val.length()-8);\r
+                               if ( cs.startsWith("\0") && (c = CommonUtils.str2color(cs)) != null ) {\r
+                                       es = val.substring(0,val.length()-8);\r
+                               }\r
+                       }\r
+                       else if ( val.length() == 8 ) {\r
+                               if ( val.startsWith("\0") ) {\r
+                                       es = "";\r
+                               }\r
+                       }\r
+                       if ( es == null ) {\r
+                               es = val;\r
+                       }\r
+               }\r
+               else {\r
+                       es = "";\r
+               }\r
+               \r
+               // 文字の設定\r
+               renderer.setText(es);\r
+               \r
+               // フォントの設定\r
+               Component comp = super.getTableCellRendererComponent(table, value, isSelected, hasFocus, row, column);\r
+               Font f = comp.getFont();\r
+               if ( macFont != null ) {\r
+                       renderer.setFont(macFont.deriveFont((f.getStyle()|Font.BOLD) ^ Font.BOLD, f.getSize()));\r
+               }\r
+               else {\r
+                       renderer.setFont(f.deriveFont((f.getStyle()|Font.BOLD) ^ Font.BOLD, f.getSize()));\r
+               }\r
+               \r
+               // セルレンダラの構成\r
+               if ( invert ) {\r
+                       renderer.setBackground(c);\r
+                       \r
+                       if (isSelected) {\r
+                               renderer.setForeground(table.getSelectionBackground());\r
+                       }\r
+                       else {\r
+                               renderer.setForeground(getInvertedForeground(table));\r
+                       }\r
+               }\r
+               else {\r
+                       renderer.setForeground(c);\r
+                       \r
+                       // 選択・非選択時の「背景」色が他のセルと同期するようにする\r
+                       if (isSelected) {\r
+                               renderer.setBackground(table.getSelectionBackground());\r
+                       }\r
+                       else {\r
+                               renderer.setBackground(table.getBackground());\r
+                       }\r
+               }\r
+               \r
+               return renderer;\r
+       }\r
+       \r
+       protected Color getInvertedForeground(JTable table) { return table.getBackground(); }\r
+       \r
+}\r
diff --git a/TinyBannavi/src/tainavi/VWColorCharCellRenderer2.java b/TinyBannavi/src/tainavi/VWColorCharCellRenderer2.java
new file mode 100644 (file)
index 0000000..dd0d157
--- /dev/null
@@ -0,0 +1,110 @@
+package tainavi;\r
+\r
+import java.awt.Color;\r
+import java.awt.Component;\r
+import java.awt.Font;\r
+\r
+import javax.swing.BoxLayout;\r
+import javax.swing.JLabel;\r
+import javax.swing.JPanel;\r
+import javax.swing.JTable;\r
+import javax.swing.table.DefaultTableCellRenderer;\r
+\r
+/**\r
+ * <P>番組表のタイトルの色強調をおこなうためのレンダラ\r
+ * <P>[マーク]\0[テキスト1]\0[テキスト2(強調部分)]\0[テキスト3]\r
+ * <P>強調色は{@link VWColorCharCellRenderer2#setMatchedKeywordColor(Color)}で指定する\r
+ * <P>各項目は省略しても構わない\r
+ */\r
+public class VWColorCharCellRenderer2 extends DefaultTableCellRenderer {\r
+\r
+       private static final long serialVersionUID = 1L;\r
+       \r
+       private final JPanel renderer = new JPanel();\r
+       private final JLabel mark = new JLabel();\r
+       private final JLabel t1 = new JLabel();\r
+       private final JLabel t2 = new JLabel();\r
+       private final JLabel t3 = new JLabel();\r
+       \r
+       private Color matchedc = Color.PINK;\r
+       \r
+       public void setMatchedKeywordColor(Color c) {\r
+               matchedc = c;\r
+       }\r
+       \r
+       public VWColorCharCellRenderer2() {\r
+               //\r
+               renderer.setLayout(new BoxLayout(renderer, BoxLayout.LINE_AXIS));\r
+               renderer.setOpaque(true);\r
+               //\r
+               //\r
+               //Font fm = mark.getFont();\r
+           mark.setForeground(Color.RED);\r
+               mark.setHorizontalAlignment(JLabel.LEFT);\r
+               //mark.setFont(fm.deriveFont(fm.getStyle() | Font.BOLD));\r
+           renderer.add(mark);\r
+               //\r
+               //Font f1 = t1.getFont();\r
+           t1.setForeground(Color.BLACK);\r
+               t1.setHorizontalAlignment(JLabel.LEFT);\r
+               //t1.setFont(f1.deriveFont((f1.getStyle()|Font.BOLD) ^ Font.BOLD));\r
+           renderer.add(t1);\r
+               //\r
+               //Font f2 = t2.getFont();\r
+           t2.setForeground(matchedc);\r
+               t2.setHorizontalAlignment(JLabel.LEFT);\r
+               //t2.setFont(f2.deriveFont(f2.getStyle() | Font.BOLD));\r
+           renderer.add(t2);\r
+               //\r
+               //Font f3 = t3.getFont();\r
+           t3.setForeground(Color.BLACK);\r
+               t3.setHorizontalAlignment(JLabel.LEFT);\r
+               //t3.setFont(f3.deriveFont((f3.getStyle()|Font.BOLD) ^ Font.BOLD));\r
+           renderer.add(t3);\r
+       }\r
+       \r
+       @Override\r
+       public void setForeground(Color c) {\r
+               super.setForeground(c);\r
+               if ( t1 != null ) {\r
+                       t1.setForeground(c);\r
+                       t3.setForeground(c);\r
+               }\r
+       };\r
+\r
+       @Override\r
+       public Component getTableCellRendererComponent(JTable table, Object value,\r
+                       boolean isSelected, boolean hasFocus, int row, int column) {\r
+               \r
+               String[] xs = ((String)value).split("\0",4);\r
+               \r
+               // 文字の設定\r
+               mark.setText((xs.length>=1)?(xs[0]):(""));\r
+               t1.setText((xs.length>=2)?(xs[1]):(""));\r
+               t2.setText((xs.length>=3)?(xs[2]):(""));\r
+               t3.setText((xs.length>=4)?(xs[3]):(""));\r
+               \r
+               // フォントの設定\r
+               Component comp = super.getTableCellRendererComponent(table, value, isSelected, hasFocus, row, column);\r
+               Font f = comp.getFont();\r
+               mark.setFont(f.deriveFont(f.getStyle()|Font.BOLD));\r
+               t1.setFont(f.deriveFont((f.getStyle()|Font.BOLD) ^ Font.BOLD));\r
+               t2.setFont(f.deriveFont(f.getStyle()|Font.BOLD));\r
+               t3.setFont(f.deriveFont((f.getStyle()|Font.BOLD) ^ Font.BOLD));\r
+               \r
+               // 選択・非選択時の文字色が他のセルと同期するようにする\r
+               if (isSelected) {\r
+                       t1.setForeground(table.getSelectionForeground());\r
+                       t3.setForeground(table.getSelectionForeground());\r
+               }\r
+               else {\r
+                       t1.setForeground(table.getForeground());\r
+                       t3.setForeground(table.getForeground());\r
+               }\r
+               \r
+               // キーワードにマッチした箇所の強調色\r
+               t2.setForeground(matchedc);\r
+               \r
+               return renderer;\r
+       }\r
+}\r
diff --git a/TinyBannavi/src/tainavi/VWColorChooserDialog.java b/TinyBannavi/src/tainavi/VWColorChooserDialog.java
new file mode 100644 (file)
index 0000000..3a3587a
--- /dev/null
@@ -0,0 +1,398 @@
+package tainavi;\r
+\r
+import java.awt.BorderLayout;\r
+import java.awt.Color;\r
+import java.awt.Component;\r
+import java.awt.Dimension;\r
+import java.awt.Point;\r
+import java.awt.event.ActionEvent;\r
+import java.awt.event.ActionListener;\r
+import java.awt.event.MouseAdapter;\r
+import java.awt.event.MouseEvent;\r
+import java.awt.event.MouseListener;\r
+\r
+import javax.swing.Box;\r
+import javax.swing.BoxLayout;\r
+import javax.swing.Icon;\r
+import javax.swing.JButton;\r
+import javax.swing.JCheckBox;\r
+import javax.swing.JColorChooser;\r
+import javax.swing.JComponent;\r
+import javax.swing.JDialog;\r
+import javax.swing.JLabel;\r
+import javax.swing.JPanel;\r
+import javax.swing.JTable;\r
+import javax.swing.colorchooser.AbstractColorChooserPanel;\r
+import javax.swing.table.TableCellRenderer;\r
+\r
+public class VWColorChooserDialog extends JDialog {\r
+\r
+       private static final long serialVersionUID = 1L;\r
+\r
+       //\r
+       private Color selectedColor = null;\r
+       \r
+       // コンストラクタ\r
+       \r
+       public VWColorChooserDialog() {\r
+               \r
+               super();\r
+               \r
+               this.setModal(true);\r
+               this.setContentPane(getJPanel());\r
+               \r
+               // タイトルバーの高さも考慮する必要がある\r
+               Dimension d = getJPanel().getPreferredSize();\r
+               this.pack();\r
+               this.setPreferredSize(new Dimension(d.width, d.height+this.getInsets().top));\r
+               this.setResizable(false);\r
+               \r
+               //\r
+               this.setTitle("色見本");\r
+       }\r
+       \r
+       // 公開メソッド\r
+       \r
+       @Override\r
+       public void setVisible(boolean b) {\r
+               super.setVisible(b);\r
+       }\r
+       \r
+       public Color getSelectedColor() {\r
+               return selectedColor;\r
+       }\r
+       \r
+       public void setColor(Color c) {\r
+               jColorChooser.setColor(c);\r
+       selectedColor = null;\r
+       }\r
+       \r
+       /*\r
+       public void setPosition(int x, int y) {\r
+               Rectangle r = this.getBounds();\r
+               r.x = x;\r
+               r.y = y;\r
+               this.setBounds(r);\r
+       }\r
+       */\r
+       \r
+       // 録画予約ウィンドウのコンポーネント\r
+       \r
+       private JPanel jPanel = null;\r
+       private JColorChooser jColorChooser = null;\r
+       private JPanel jPanel_buttons = null;\r
+       private JButton jButton = null;\r
+       private JButton jButton_cancel = null;\r
+       \r
+       // ほげほげ\r
+       \r
+       private JPanel getJPanel() {\r
+               if (jPanel == null) {\r
+                       jPanel = new JPanel();\r
+                       jPanel.setLayout(new BorderLayout());\r
+                       jPanel.add(getJColorChooser(), BorderLayout.CENTER);\r
+                       jPanel.add(getJPanel_buttons(), BorderLayout.SOUTH);\r
+\r
+                       Dimension d;\r
+                       if ( CommonUtils.isLinux() ) {\r
+                               d = new Dimension(600, 400);\r
+                       }\r
+                       else {\r
+                               d = new Dimension(450, 400);\r
+                       }\r
+                       jPanel.setPreferredSize(d);\r
+               }\r
+               return(jPanel);\r
+       }\r
+       \r
+       private JPanel getJPanel_buttons() {\r
+               if (jPanel_buttons == null) {\r
+                       jPanel_buttons = new JPanel();\r
+                       jPanel_buttons.setLayout(new BorderLayout());\r
+                       jPanel_buttons.add(getJButton("決定"), BorderLayout.CENTER);\r
+                       jPanel_buttons.add(getJButton_cancel("キャンセル"), BorderLayout.EAST);\r
+               }\r
+               return(jPanel_buttons);\r
+       }\r
+       \r
+       //\r
+       private JColorChooser getJColorChooser() {\r
+               if (jColorChooser == null) {\r
+                       if ( System.getProperty("java.version").startsWith("1.6.") && ! CommonUtils.isLinux() ) {\r
+                               System.out.println("[色選択ダイアログ] カスタマイズダイアログを使う: "+System.getProperty("java.version"));\r
+                               jColorChooser = new XColorChooser();\r
+                       }\r
+                       else {\r
+                               System.out.println("[色選択ダイアログ] 標準ダイアログを使う: "+System.getProperty("java.version"));\r
+                               jColorChooser = new JColorChooser();\r
+                       }\r
+               }\r
+               return jColorChooser;\r
+       }\r
+       \r
+       private JButton getJButton(String s) {\r
+               if (jButton == null) {\r
+                       jButton = new JButton(s);\r
+                       final JDialog jd = this;\r
+                       jButton.addMouseListener(new MouseAdapter() {\r
+                               @Override\r
+                               public void mouseClicked(MouseEvent e) {\r
+                                       selectedColor = jColorChooser.getColor();\r
+                                       jd.setVisible(false);\r
+                               }\r
+                       });\r
+               }\r
+               return(jButton);\r
+       }\r
+       \r
+       private JButton getJButton_cancel(String s) {\r
+               if (jButton_cancel == null) {\r
+                       jButton_cancel = new JButton(s);\r
+                       final JDialog jd = this;\r
+                       jButton_cancel.addMouseListener(new MouseAdapter() {\r
+                               @Override\r
+                               public void mouseClicked(MouseEvent e) {\r
+                                       selectedColor = null;\r
+                                       jd.setVisible(false);\r
+                               }\r
+                       });\r
+               }\r
+               return(jButton_cancel);\r
+       }\r
+       \r
+       /*\r
+        * カスタムカラーチューザー\r
+        */\r
+\r
+       /**\r
+        * LookAndFeelの変更を行うとカスタマイズした内容がデフォルトのJColorChooserに戻ってしまうことが今更ながら判明\r
+        * @version 3.16\r
+        */\r
+       private class XColorChooser extends JColorChooser {\r
+\r
+               private static final long serialVersionUID = 1L;\r
+\r
+               public XColorChooser() {\r
+                       super();\r
+               }\r
+               \r
+               @Override\r
+               public void updateUI() {\r
+                       super.updateUI();\r
+                       customize();\r
+               }\r
+               \r
+               @Override\r
+               public void setPreviewPanel(JComponent preview) {\r
+                       // LookAndFeel変更でプレビューパネルが消えてしまうので\r
+                       if ( CommonUtils.isLinux() ) super.setPreviewPanel(preview);    // Linuxでは死んでしまう\r
+               }\r
+               \r
+               private void customize() {\r
+                       if ( CommonUtils.isLinux() ) return;    // Linuxでは死んでしまう\r
+                       \r
+                       AbstractColorChooserPanel[] accp = this.getChooserPanels();\r
+                       for ( AbstractColorChooserPanel a : accp ) {\r
+                               this.removeChooserPanel(a);\r
+                       }\r
+\r
+                       for ( AbstractColorChooserPanel a : accp ) {\r
+                               if ( ! "RGB".equals(a.getDisplayName())) {\r
+                                       this.addChooserPanel(a);\r
+                               }\r
+                       }\r
+                       this.addChooserPanel(new customColorChooserPanel2());\r
+                       this.addChooserPanel(new customColorChooserPanel());\r
+               }\r
+       \r
+               private class customColorChooserPanel extends AbstractColorChooserPanel implements MouseListener {\r
+       \r
+                       private static final long serialVersionUID = 1L;\r
+       \r
+                       JTable jTable = null;\r
+                       \r
+                       @Override\r
+                       protected void buildChooser() {\r
+                               if (jTable == null) {\r
+                                       jTable = new JTable(8,8) {\r
+       \r
+                                               private static final long serialVersionUID = 1L;\r
+       \r
+                                               @Override\r
+                                                 public boolean isCellEditable(int row, int column) {\r
+                                                       return false;\r
+                                                 }\r
+                                       };\r
+                                       jTable.setDefaultRenderer(Object.class, new labelRenderer());\r
+                                       jTable.setPreferredSize(new Dimension(300,160));\r
+                                       jTable.setRowHeight(20);\r
+                                       jTable.addMouseListener(this);\r
+                                       add(jTable);\r
+                               }\r
+                       }\r
+       \r
+                       @Override\r
+                       public String getDisplayName() {\r
+                               return "鯛ナビ";\r
+                       }\r
+       \r
+                       @Override\r
+                       public Icon getLargeDisplayIcon() {\r
+                               return null;\r
+                       }\r
+       \r
+                       @Override\r
+                       public Icon getSmallDisplayIcon() {\r
+                               return null;\r
+                       }\r
+       \r
+                       @Override\r
+                       public void updateChooser() {\r
+                       }\r
+       \r
+                       @Override\r
+                       public void mouseClicked(MouseEvent e) {\r
+                               Point p = e.getPoint();\r
+                               int row = jTable.rowAtPoint(p);\r
+                               int column = jTable.columnAtPoint(p);\r
+                               Color c = getColorFromTable(row, column);\r
+                               jColorChooser.setColor(c);\r
+                       }\r
+       \r
+                       @Override\r
+                       public void mouseEntered(MouseEvent e) {\r
+                       }\r
+       \r
+                       @Override\r
+                       public void mouseExited(MouseEvent e) {\r
+                       }\r
+       \r
+                       @Override\r
+                       public void mousePressed(MouseEvent e) {\r
+                       }\r
+       \r
+                       @Override\r
+                       public void mouseReleased(MouseEvent e) {\r
+                       }\r
+               }\r
+               \r
+               private class customColorChooserPanel2 extends AbstractColorChooserPanel {\r
+       \r
+                       private static final long serialVersionUID = 1L;\r
+       \r
+                       JColorChooseSlider jCCS_Red = null;\r
+                       JColorChooseSlider jCCS_Blue = null;\r
+                       JColorChooseSlider jCCS_Green = null;\r
+                       JCheckBox jCB_Hex = null;\r
+                       \r
+                       @Override\r
+                       protected void buildChooser() {\r
+                               if (jCCS_Red == null) {\r
+                                       \r
+                                       jCCS_Red = new JColorChooseSlider("赤") {\r
+       \r
+                                               private static final long serialVersionUID = 1L;\r
+       \r
+                                               @Override\r
+                                               void evHandle(int n) {\r
+                                                       myEvHandle(n);\r
+                                               }\r
+                                       };\r
+                                       \r
+                                       jCCS_Green = new JColorChooseSlider("緑") {\r
+       \r
+                                               private static final long serialVersionUID = 1L;\r
+       \r
+                                               @Override\r
+                                               void evHandle(int n) {\r
+                                                       myEvHandle(n);\r
+                                               }\r
+                                       };\r
+                                       \r
+                                       jCCS_Blue = new JColorChooseSlider("青") {\r
+       \r
+                                               private static final long serialVersionUID = 1L;\r
+       \r
+                                               @Override\r
+                                               void evHandle(int n) {\r
+                                                       myEvHandle(n);\r
+                                               }\r
+                                       };\r
+                                       \r
+                                       jCB_Hex = new JCheckBox("16進数(00-ff)で入力", false);\r
+                                       \r
+                                       jCB_Hex.addActionListener(new ActionListener() {\r
+                                               @Override\r
+                                               public void actionPerformed(ActionEvent arg0) {\r
+                                                       jCCS_Red.setHex(jCB_Hex.isSelected());\r
+                                                       jCCS_Green.setHex(jCB_Hex.isSelected());\r
+                                                       jCCS_Blue.setHex(jCB_Hex.isSelected());\r
+                                               }\r
+                                       });\r
+                                       \r
+                                       setLayout(new BoxLayout(this, BoxLayout.Y_AXIS));\r
+                                       add(jCCS_Red);\r
+                                       add(Box.createRigidArea(new Dimension(10,10)));\r
+                                       add(jCCS_Green);\r
+                                       add(Box.createRigidArea(new Dimension(10,10)));\r
+                                       add(jCCS_Blue);\r
+                                       add(Box.createRigidArea(new Dimension(10,20)));\r
+                                       add(jCB_Hex);\r
+                               }\r
+                       }\r
+       \r
+                       @Override\r
+                       public String getDisplayName() {\r
+                               return "RGB";\r
+                       }\r
+       \r
+                       @Override\r
+                       public Icon getLargeDisplayIcon() {\r
+                               return null;\r
+                       }\r
+       \r
+                       @Override\r
+                       public Icon getSmallDisplayIcon() {\r
+                               return null;\r
+                       }\r
+       \r
+                       @Override\r
+                       public void updateChooser() {\r
+                               if ( jColorChooser != null ) {\r
+                                       Color c = jColorChooser.getColor();\r
+                                       jCCS_Red.setValue(c.getRed());\r
+                                       jCCS_Green.setValue(c.getGreen());\r
+                                       jCCS_Blue.setValue(c.getBlue());\r
+                               }\r
+                       }\r
+                       \r
+                       private void myEvHandle(int n) {\r
+                               jColorChooser.setColor(new Color(jCCS_Red.getValue(),jCCS_Green.getValue(),jCCS_Blue.getValue()));\r
+                       }\r
+               }\r
+               \r
+               private class labelRenderer extends JLabel implements TableCellRenderer {\r
+                       private static final long serialVersionUID = 1L;\r
+       \r
+                       @Override\r
+                       public Component getTableCellRendererComponent(JTable table,\r
+                                       Object value, boolean isSelected, boolean hasFocus, int row, int column) {\r
+                               this.setOpaque(true);\r
+                               \r
+                               Color c = getColorFromTable(row,column);\r
+                               this.setBackground(c);\r
+                               \r
+                               this.setToolTipText("("+c.getRed()+","+c.getGreen()+","+c.getBlue()+")");\r
+       \r
+                               return this;\r
+                       }\r
+               }\r
+               \r
+               private Color getColorFromTable(int row, int column) {\r
+                       int b = ((int)(column / 4)+((int)(row / 4))*2) * 85;\r
+                       int g = (column % 4) * 85;\r
+                       int r = (row % 4) * 85;\r
+                       return new Color(r,g,b);\r
+               }\r
+       }\r
+}\r
diff --git a/TinyBannavi/src/tainavi/VWDetailCellRenderer.java b/TinyBannavi/src/tainavi/VWDetailCellRenderer.java
new file mode 100644 (file)
index 0000000..a0869a1
--- /dev/null
@@ -0,0 +1,20 @@
+package tainavi;\r
+\r
+import java.awt.Component;\r
+\r
+import javax.swing.JTable;\r
+import javax.swing.table.DefaultTableCellRenderer;\r
+\r
+public class VWDetailCellRenderer extends DefaultTableCellRenderer {\r
+\r
+       private static final long serialVersionUID = 1L;\r
+\r
+       @Override\r
+       public Component getTableCellRendererComponent(JTable table, Object value,\r
+                       boolean isSelected, boolean hasFocus, int row, int column) {\r
+               \r
+               super.getTableCellRendererComponent(table,((String)value).replaceFirst("^[  ]+", ""),isSelected,hasFocus,row,column);\r
+               \r
+               return this;\r
+       }\r
+}\r
diff --git a/TinyBannavi/src/tainavi/VWFont.java b/TinyBannavi/src/tainavi/VWFont.java
new file mode 100644 (file)
index 0000000..46ab124
--- /dev/null
@@ -0,0 +1,149 @@
+package tainavi;\r
+\r
+import java.awt.GraphicsEnvironment;\r
+import java.util.ArrayList;\r
+import java.util.Enumeration;\r
+\r
+import javax.swing.JLabel;\r
+import javax.swing.UIDefaults;\r
+import javax.swing.UIManager;\r
+import javax.swing.plaf.FontUIResource;\r
+\r
+\r
+public class VWFont {\r
+\r
+       private static boolean debug = false;\r
+       \r
+       \r
+       /*******************************************************************************\r
+        * 定数\r
+        ******************************************************************************/\r
+       \r
+       private static final String MSGID = "[フォント変更] ";\r
+       private static final String DBGID = "[DEBUG]"+MSGID;\r
+       private static final String ERRID = "[ERROR]"+MSGID;\r
+       \r
+       /*******************************************************************************\r
+        * メンバ変数関連\r
+        ******************************************************************************/\r
+       \r
+       public ArrayList<String> getNames() {\r
+               return fontnames;\r
+       }\r
+\r
+       ArrayList<String> fontnames = null;\r
+\r
+       /*******************************************************************************\r
+        * メソッド\r
+        ******************************************************************************/\r
+       \r
+       public String update(final String fn, final int fontSize) {\r
+               \r
+               //System.err.println("[DEBUG] VWFont#update "+fn+" "+fontSize);\r
+               \r
+               String font = getFontName(fn);\r
+               \r
+               UIDefaults defaults = UIManager.getDefaults();\r
+               Enumeration<Object> keys = defaults.keys();\r
+               \r
+        while ( keys.hasMoreElements() ) {\r
+               Object key = keys.nextElement();\r
+               Object value = UIManager.get (key);\r
+               if ( value instanceof FontUIResource ) {\r
+                       int fontstyle = ((FontUIResource) value).getStyle();\r
+                       if ( font != null ) {\r
+                               \r
+                               FontUIResource to = new FontUIResource(font, fontstyle, fontSize);\r
+                       if (debug) System.out.println(DBGID+"UIManagerの変更: key="+key.toString()+", from="+value.toString()+", to="+to.toString());\r
+                       \r
+                       defaults.put(key, to);\r
+                       }\r
+                       else {\r
+                               \r
+                       if (debug) System.out.println(DBGID+"変更されないUIManager: key="+key.toString()+", value="+value.toString());\r
+                       \r
+                       defaults.put(key, value);\r
+                       }\r
+               }\r
+        }\r
+               \r
+               return font;\r
+       }\r
+       \r
+       /**\r
+        * fnで指定したフォントがフォントリストにあればそれを返す。なければデフォルトのフォント名を返す。\r
+        */\r
+       private String getFontName(final String fn) {\r
+               \r
+               final String fWin7 = "Meiryo UI";\r
+               final String fVista = "メイリオ";\r
+               final String fWindows = "MS UI Gothic";\r
+               final String fLinux = "Takao Pゴシック";\r
+               //final String fMac = "Osaka";\r
+               \r
+               String fName = null;\r
+               if ( ! fn.equals("")) {\r
+                       for ( String f : fontnames ) {\r
+                               if (f.equals(fn)) {\r
+                                       fName = fn;\r
+                                       System.out.println(MSGID+"指定のフォントは有効です="+fn);\r
+                                       break;\r
+                               }\r
+                       }\r
+               }\r
+               if (fName == null) {\r
+                       for ( String f : fontnames ) {\r
+                               if ( CommonUtils.isWindowsXP() ) {\r
+                                       if (fName == null && f.equals(fWindows)) {\r
+                                               fName = fWindows;\r
+                                       }\r
+                               }\r
+                               else if ( CommonUtils.isWindows() ) {\r
+                                       if (f.equals(fWin7)) {\r
+                                               fName = fWin7;\r
+                                               break;\r
+                                       }\r
+                                       else if ((fName == null || ! fName.equals(fWin7)) && f.equals(fVista)) {\r
+                                               fName = fVista; // 優先度:低\r
+                                       }\r
+                                       else if (fName == null && f.equals(fWindows)) {\r
+                                               fName = fWindows;       // 優先度:低\r
+                                       }\r
+                               }\r
+                               else if ( CommonUtils.isLinux() ) {\r
+                                       if (f.equals(fLinux)) {\r
+                                               fName = fLinux;\r
+                                               break;\r
+                                       }\r
+                               }\r
+                               else if ( CommonUtils.isMac() ) {\r
+                                       fName = new JLabel().getFont().getFontName();\r
+                                       break;\r
+                               }\r
+                       }\r
+                       \r
+                       System.out.println(ERRID+"フォントリスト中に指定のフォントが見つからないのでデフォルトフォントを利用します:  指定="+fn+", デフォルト="+fName);\r
+               }\r
+               \r
+               return fName;\r
+       }\r
+       \r
+       //\r
+       private void findFont() {\r
+               \r
+               fontnames = new ArrayList<String>();\r
+               \r
+               for ( String f : GraphicsEnvironment.getLocalGraphicsEnvironment().getAvailableFontFamilyNames() ) {\r
+                       fontnames.add(f);\r
+               }\r
+               \r
+       }\r
+       \r
+       /*******************************************************************************\r
+        * コンストラクタ\r
+        ******************************************************************************/\r
+       \r
+       public VWFont() {\r
+               this.findFont();\r
+       }\r
+}\r
diff --git a/TinyBannavi/src/tainavi/VWKeywordGroupDialog.java b/TinyBannavi/src/tainavi/VWKeywordGroupDialog.java
new file mode 100644 (file)
index 0000000..d6f2fe0
--- /dev/null
@@ -0,0 +1,158 @@
+package tainavi;\r
+\r
+import java.awt.Dimension;\r
+import java.awt.event.ActionEvent;\r
+import java.awt.event.ActionListener;\r
+\r
+import javax.swing.JButton;\r
+import javax.swing.JComponent;\r
+import javax.swing.JDialog;\r
+import javax.swing.JLabel;\r
+import javax.swing.JPanel;\r
+import javax.swing.JTextField;\r
+import javax.swing.SpringLayout;\r
+\r
+/***\r
+ * \r
+ * 番組追跡検索の設定のクラス\r
+ * \r
+ */\r
+\r
+public class VWKeywordGroupDialog extends JDialog {\r
+\r
+       private static final long serialVersionUID = 1L;\r
+\r
+       private boolean reg = false;\r
+       private String oldName = "";\r
+       \r
+       public String getNewName() { return jTextField_title.getText(); }\r
+       \r
+       // キーワード検索の設定ウィンドウのコンポーネント\r
+       \r
+       private JPanel jPanel = null;\r
+       \r
+       private JLabel jLabel_title = null;\r
+       private JTextField jTextField_title = null;\r
+       private JButton jButton_label = null;\r
+       private JButton jButton_cancel = null;\r
+\r
+       // ほげほげ\r
+\r
+       public boolean isRegistered() { return reg; }\r
+       \r
+       public void reopen(String name) {\r
+               //\r
+               oldName = name;\r
+               jTextField_title.setText(oldName);\r
+               jTextField_title.setCaretPosition(0);\r
+       }\r
+       \r
+       //\r
+       private JPanel getJPanel() {\r
+               if (jPanel == null) {\r
+                       jPanel = new JPanel();\r
+\r
+                       jPanel.setLayout(new SpringLayout());\r
+                       \r
+                       int y = 10;\r
+                       _getJComponent(jPanel, getJLabel_title("グループ名"), 80, 25, 10, y);\r
+                       _getJComponent(jPanel, getJTextField_title(), 200, 25, 100, y);\r
+                       \r
+                       y += 50;\r
+                       _getJComponent(jPanel, getJButton_label("登録"), 75, 25, 110, y);\r
+                       _getJComponent(jPanel, getJButton_cancel("キャンセル"), 75, 25, 195, y);\r
+                       \r
+                       y += 30;\r
+                       Dimension d = new Dimension(330,y+10);\r
+                       jPanel.setPreferredSize(d);\r
+               }\r
+               return jPanel;\r
+       }\r
+       \r
+       private void _getJComponent(JPanel p, JComponent c, int width, int height, int x, int y) {\r
+           c.setPreferredSize(new Dimension(width, height));\r
+           ((SpringLayout)p.getLayout()).putConstraint(SpringLayout.NORTH, c, y, SpringLayout.NORTH, p);\r
+           ((SpringLayout)p.getLayout()).putConstraint(SpringLayout.WEST, c, x, SpringLayout.WEST, p);\r
+           p.add(c);\r
+       }\r
+       \r
+       \r
+       \r
+       //\r
+       private JLabel getJLabel_title(String s) {\r
+               if (jLabel_title == null) {\r
+                       jLabel_title = new JLabel(s);\r
+               }\r
+               return(jLabel_title);\r
+       }\r
+       \r
+       //\r
+       private JTextField getJTextField_title() {\r
+               if (jTextField_title == null) {\r
+                       jTextField_title = new JTextField();\r
+               }\r
+               return(jTextField_title);\r
+       }\r
+       \r
+       //\r
+       private JButton getJButton_label(String s) {\r
+               if (jButton_label == null) {\r
+                       jButton_label = new JButton();\r
+                       jButton_label.setText(s);\r
+                       \r
+                       jButton_label.addActionListener(new ActionListener() {\r
+                               @Override\r
+                               public void actionPerformed(ActionEvent e) {\r
+                                       if (jTextField_title.getText().equals("")) {\r
+                                               return;\r
+                                       }\r
+                                       if ( ! jTextField_title.getText().equals(oldName)) {\r
+                                               reg = true;\r
+                                       }\r
+                                       \r
+                                       // ウィンドウを閉じる\r
+                                       dispose();\r
+                               }\r
+                       });\r
+               }\r
+               return(jButton_label);\r
+       }\r
+       \r
+       //\r
+       private JButton getJButton_cancel(String s) {\r
+               if (jButton_cancel == null) {\r
+                       jButton_cancel = new JButton();\r
+                       jButton_cancel.setText(s);\r
+                       \r
+                       jButton_cancel.addActionListener(new ActionListener() {\r
+\r
+                               @Override\r
+                               public void actionPerformed(ActionEvent e) {\r
+                                       dispose();\r
+                               }\r
+                       });\r
+               }\r
+               return jButton_cancel;\r
+       }\r
+       \r
+       \r
+       // コンストラクタ\r
+       public VWKeywordGroupDialog() {\r
+               \r
+               super();\r
+\r
+               //\r
+               reg = false;\r
+               \r
+               //\r
+               this.setModal(true);\r
+               this.setContentPane(getJPanel());\r
+               // タイトルバーの高さも考慮する必要がある\r
+               Dimension d = getJPanel().getPreferredSize();\r
+               this.pack();\r
+               this.setPreferredSize(new Dimension(d.width, d.height+this.getInsets().top));\r
+               this.setResizable(false);\r
+               //\r
+               this.setTitle("キーワードグループの設定");\r
+       }\r
+}\r
diff --git a/TinyBannavi/src/tainavi/VWListedTreeNode.java b/TinyBannavi/src/tainavi/VWListedTreeNode.java
new file mode 100644 (file)
index 0000000..115e206
--- /dev/null
@@ -0,0 +1,23 @@
+package tainavi;\r
+\r
+import javax.swing.tree.DefaultMutableTreeNode;\r
+\r
+public class VWListedTreeNode extends DefaultMutableTreeNode {\r
+\r
+       private static final long serialVersionUID = 1L;\r
+\r
+       private boolean used = true;\r
+       \r
+       public final boolean isUsed() { return used; }\r
+       public final boolean isUnUsed() { return ! used; }\r
+       \r
+       public VWListedTreeNode(Object userObject) {\r
+               super(userObject);\r
+               this.used = true;\r
+       }\r
+       \r
+       public VWListedTreeNode(Object userObject, boolean used) {\r
+               super(userObject);\r
+               this.used = used;\r
+       }\r
+}\r
diff --git a/TinyBannavi/src/tainavi/VWLookAndFeel.java b/TinyBannavi/src/tainavi/VWLookAndFeel.java
new file mode 100644 (file)
index 0000000..9a0d614
--- /dev/null
@@ -0,0 +1,119 @@
+package tainavi;\r
+\r
+import java.awt.Color;\r
+import java.io.File;\r
+import java.io.IOException;\r
+import java.util.ArrayList;\r
+import java.util.Enumeration;\r
+import java.util.jar.JarEntry;\r
+import java.util.jar.JarFile;\r
+\r
+import javax.swing.UIDefaults;\r
+import javax.swing.UIManager;\r
+import javax.swing.UnsupportedLookAndFeelException;\r
+import javax.swing.UIManager.LookAndFeelInfo;\r
+import javax.swing.plaf.InsetsUIResource;\r
+\r
+public class VWLookAndFeel {\r
+\r
+       ArrayList<String> lafnames = null;\r
+       //Component comp = null;\r
+       \r
+       //\r
+       public ArrayList<String> getNames() {\r
+               return lafnames;\r
+       }\r
+       \r
+       public String update(String lafname) {\r
+               \r
+               String name = "";\r
+               if ( ! lafname.equals("") ) {\r
+                       name = lafname;\r
+               }\r
+               else if ( CommonUtils.isWindows() ) {\r
+                       name = "Windows";\r
+               }\r
+               else if ( CommonUtils.isLinux() ) {\r
+                       name = "GTK";\r
+               }\r
+               \r
+               if (/*comp != null &&*/ ! name.equals("")) {\r
+                       for ( String className : lafnames ) {\r
+                               if ( className.endsWith("."+name+"LookAndFeel") ) {\r
+                                       try {\r
+                                               UIManager.setLookAndFeel(className);\r
+                                               //SwingUtilities.updateComponentTreeUI(comp);\r
+                                               \r
+                                               // テーブルの色だけ固定\r
+                                               UIDefaults defaultTable = UIManager.getLookAndFeelDefaults();\r
+                                               for(Object o: defaultTable.keySet()) {\r
+                                                       if (o.toString().equals("Table.selectionBackground")) {\r
+                                                               UIManager.put(o, new Color(182,207,229));\r
+                                                       }\r
+                                                       else if (o.toString().equals("Table.selectionForeground")) {\r
+                                                               UIManager.put(o, new Color(0,0,0));\r
+                                                       }\r
+                                                       else if (o.toString().equals("Button.margin")) {\r
+                                                               InsetsUIResource ins = (InsetsUIResource) UIManager.get(o);\r
+                                                               ins.left = ins.right = 4;\r
+                                                               UIManager.put(o,ins);\r
+                                                       }\r
+                                               }\r
+                                       } catch (ClassNotFoundException e) {\r
+                                               e.printStackTrace();\r
+                                       } catch (InstantiationException e) {\r
+                                               e.printStackTrace();\r
+                                       } catch (IllegalAccessException e) {\r
+                                               e.printStackTrace();\r
+                                       } catch (UnsupportedLookAndFeelException e) {\r
+                                               e.printStackTrace();\r
+                                       }\r
+                                       break;\r
+                               }\r
+                       }\r
+               }\r
+               \r
+               return name;\r
+       }\r
+       \r
+       \r
+       //\r
+       private void findLookAndFeel() {\r
+               \r
+               lafnames = new ArrayList<String>();\r
+\r
+               // 組み込みLAF\r
+               for ( LookAndFeelInfo laf : UIManager.getInstalledLookAndFeels() ) {\r
+                       if ( laf.getClassName().endsWith("LookAndFeel") ) {\r
+                               lafnames.add(laf.getClassName());\r
+                       }\r
+               }\r
+               \r
+               // 追加LAF\r
+               File skinFolder = new File("skin");\r
+               if ( ! skinFolder.exists()) {\r
+                       skinFolder.mkdir();\r
+               }\r
+               for ( File file : skinFolder.listFiles() ) {\r
+                       try {\r
+                               JarFile jarfile = new JarFile(file) ;\r
+                               for ( Enumeration<JarEntry> en = jarfile.entries(); en.hasMoreElements(); ) {\r
+                                       JarEntry entry = (JarEntry)en.nextElement() ;\r
+                                       if ( entry.getName().endsWith(".class") ) {\r
+                                               String className = entry.getName().replaceAll("/", "\\.").replaceAll("\\.class$", "");\r
+                                               lafnames.add(className);\r
+                                       }\r
+                               }\r
+                       } catch (IOException e) {\r
+                               e.printStackTrace();\r
+                       }\r
+               }\r
+       }\r
+       \r
+       \r
+       // コンストラクタ\r
+       public VWLookAndFeel(/*Component c*/) {\r
+               //comp = c;\r
+               findLookAndFeel();\r
+       }\r
+}\r
diff --git a/TinyBannavi/src/tainavi/VWMainWindow.java b/TinyBannavi/src/tainavi/VWMainWindow.java
new file mode 100644 (file)
index 0000000..006478f
--- /dev/null
@@ -0,0 +1,191 @@
+package tainavi;\r
+\r
+import java.awt.BorderLayout;\r
+import java.awt.Component;\r
+\r
+import javax.swing.JPanel;\r
+import javax.swing.JTabbedPane;\r
+\r
+\r
+public class VWMainWindow extends JPanel {\r
+\r
+       private static final long serialVersionUID = 1L;\r
+\r
+       /*\r
+        * 定数\r
+        */\r
+       public static enum MWinTab {\r
+               LISTED  ("リスト形式"),\r
+               PAPER   ("新聞形式"),\r
+               RSVED   ("本体予約一覧"),\r
+               RECED   ("録画結果一覧"),\r
+               SETTING ("各種設定"),\r
+               RECSET  ("レコーダ設定"),\r
+               CHSET   ("CH設定"),\r
+               CHSORT  ("CHソート設定"),\r
+               CHCONV  ("CHコンバート設定"),\r
+               CHDAT   ("CHコード設定"),\r
+               ;\r
+               \r
+               String name;\r
+               \r
+               private MWinTab(String name) {\r
+                       this.name = name;\r
+               }\r
+               \r
+               public String getName() {\r
+                       return name;\r
+               }\r
+               \r
+               public int getIndex() {\r
+                       return ordinal();\r
+               }\r
+               \r
+               public static MWinTab getAt(int index) {\r
+                       for ( MWinTab tab : MWinTab.values() ) {\r
+                               if ( tab.ordinal() == index ) {\r
+                                       return tab;\r
+                               }\r
+                       }\r
+                       return null;\r
+               }\r
+               \r
+               public static int size() { return MWinTab.values().length; }\r
+               \r
+       }\r
+       \r
+       /*\r
+        * 部品\r
+        */\r
+       \r
+       private JTabbedPane jTabbedPane = null;\r
+\r
+       \r
+       /*\r
+        *  コンストラクタ\r
+        */\r
+       \r
+       public VWMainWindow() {\r
+               this.setLayout(new BorderLayout());\r
+               this.add(getJTabbedPane(), BorderLayout.CENTER);\r
+               \r
+               // タブを全部準備する\r
+               for ( MWinTab tab : MWinTab.values() ) {\r
+                       addTab(null, tab);\r
+               }\r
+       }\r
+       \r
+       \r
+       /*\r
+        * 公開メソッド\r
+        */\r
+       \r
+       // ツールバーを追加する\r
+       public void addToolBar(Component comp){\r
+               this.add(comp, BorderLayout.PAGE_START);\r
+       }\r
+       \r
+       \r
+       public void addStatusArea(Component comp) {\r
+               this.add(comp, BorderLayout.PAGE_END);\r
+       }\r
+       \r
+       // タブを追加する\r
+       public boolean addTab(Component comp, MWinTab tab) {\r
+               if ( jTabbedPane.getTabCount() < tab.getIndex() ) {\r
+                       System.err.println(String.format("[DEBUG][メインウィンドウ] タブの数があわない: %d/%d",jTabbedPane.getTabCount(),tab.getIndex()));\r
+                       return false;\r
+               }\r
+               if ( jTabbedPane.getTabCount() > tab.getIndex() ) {\r
+                       jTabbedPane.remove(tab.getIndex());\r
+               }\r
+               jTabbedPane.add(comp, tab.getName(), tab.getIndex());\r
+               return true;\r
+       }\r
+\r
+       // タブを切り替える\r
+       public void setSelectedTab(MWinTab tab) {\r
+               if ( tab == null ) {\r
+                       jTabbedPane.setSelectedIndex(-1);\r
+               }\r
+               else if (jTabbedPane.getTabCount() > tab.getIndex()) {\r
+                       jTabbedPane.setSelectedIndex(tab.getIndex());\r
+               }\r
+       }\r
+       \r
+       //\r
+       public Component getTab(MWinTab tab) {\r
+               return this.getComponent(tab.getIndex());\r
+       }\r
+       \r
+       // タブが選択されているか確認する\r
+       public boolean isTabSelected(MWinTab tab) {\r
+               return (jTabbedPane.getSelectedIndex() == tab.getIndex());\r
+       }\r
+       \r
+       // どのタブが選択されているのやら\r
+       public MWinTab getSelectedTab() { return MWinTab.getAt(jTabbedPane.getSelectedIndex()); }\r
+       \r
+       // 設定タブをトグル切り替え\r
+       private final int firstSettingTab = MWinTab.SETTING.ordinal();\r
+       private final int countSettingTab = MWinTab.size()-firstSettingTab;\r
+       private Component[] st_comp = new Component[countSettingTab];\r
+       private String[] st_title = new String[countSettingTab];\r
+       public boolean toggleShowSettingTabs() {\r
+               if (st_comp[0] == null) {\r
+                       for (int i=countSettingTab-1; i>=0; i--) {\r
+                               st_comp[i] = this.jTabbedPane.getComponentAt(firstSettingTab+i);\r
+                               st_title[i] = this.jTabbedPane.getTitleAt(firstSettingTab+i);\r
+                               this.jTabbedPane.remove(firstSettingTab+i);\r
+                       }\r
+                       return false;\r
+               }\r
+               else {\r
+                       for (int i=0; i<countSettingTab; i++) {\r
+                               this.jTabbedPane.add(st_comp[i]);\r
+                               this.jTabbedPane.setTitleAt(firstSettingTab+i, st_title[i]);\r
+                               st_comp[i] = null;\r
+                       }\r
+                       this.jTabbedPane.setSelectedIndex(firstSettingTab);\r
+                       return true;\r
+               }\r
+       }\r
+       \r
+       public boolean getShowSettingTabs() {\r
+               return (jTabbedPane.getTabCount() > firstSettingTab);\r
+       }\r
+       public void setShowSettingTabs(boolean b) {\r
+               if ((b && jTabbedPane.getTabCount() <= firstSettingTab) ||\r
+                               ( ! b && jTabbedPane.getTabCount() > firstSettingTab)) {\r
+                       toggleShowSettingTabs();\r
+               }\r
+       }\r
+       \r
+       \r
+       /*\r
+        * \r
+        */\r
+       \r
+       private JTabbedPane getJTabbedPane() {\r
+               if (jTabbedPane == null) {\r
+                       jTabbedPane = new JTabbedPane();\r
+               }\r
+               return jTabbedPane;\r
+       }\r
+       \r
+       /**\r
+        * @deprecated\r
+        */\r
+       public void appendStatusMessage(String s) {\r
+               throw new UnsupportedOperationException();\r
+       }\r
+       \r
+       /**\r
+        * @deprecated\r
+        * @see Viewer#setStatusVisible(boolean)\r
+        */\r
+       public void setStatusVisible(boolean b) {\r
+               throw new UnsupportedOperationException();\r
+       }\r
+       \r
+}\r
diff --git a/TinyBannavi/src/tainavi/VWStatusTextArea.java b/TinyBannavi/src/tainavi/VWStatusTextArea.java
new file mode 100644 (file)
index 0000000..e866600
--- /dev/null
@@ -0,0 +1,86 @@
+package tainavi;\r
+\r
+import java.awt.BorderLayout;\r
+\r
+import javax.swing.JPanel;\r
+import javax.swing.JScrollPane;\r
+import javax.swing.JTextArea;\r
+\r
+public class VWStatusTextArea extends JPanel implements StatusTextArea {\r
+\r
+       private static final long serialVersionUID = 1L;\r
+\r
+       /*\r
+        * 部品\r
+        */\r
+       \r
+       private JScrollPane jsp = null;\r
+       private JTextArea jta = null;\r
+       \r
+       /*\r
+        * コンストラクタ\r
+        */\r
+       \r
+       public VWStatusTextArea() {\r
+               \r
+               super();\r
+               \r
+               this.setLayout(new BorderLayout());\r
+               \r
+               this.add(getJScrollPane_statusarea());\r
+               \r
+       }\r
+\r
+       private JScrollPane getJScrollPane_statusarea() {\r
+               if (jsp == null) {\r
+                       jsp = new JScrollPane(getJTextArea_statusarea(),JScrollPane.VERTICAL_SCROLLBAR_ALWAYS,JScrollPane.HORIZONTAL_SCROLLBAR_NEVER);\r
+               }\r
+               return(jsp);\r
+       }\r
+       \r
+       private JTextArea getJTextArea_statusarea() {\r
+               if (jta == null) {\r
+                       jta = new JTextAreaWithPopup(4,0);\r
+                       jta.setLineWrap(true);                  // 改行、する!\r
+                       jta.setWrapStyleWord(false);            // 画面端で折り返し、する!\r
+                       jta.setEditable(false);                 // 編集、させない!\r
+               }\r
+               return jta;\r
+       }\r
+       \r
+       /*\r
+        * StatusTextArea用のメソッド(non-Javadoc)\r
+        */\r
+\r
+       @Override\r
+       public void clear() {\r
+               jta.setText("");\r
+       }\r
+\r
+       private void append(String message) {\r
+               jta.append("\n"+message);\r
+               jta.setCaretPosition(jta.getText().length());\r
+       }\r
+\r
+       @Override\r
+       public void appendMessage(String message) {\r
+               this.append(message);\r
+               System.out.println(message);\r
+       }\r
+\r
+       @Override\r
+       public void appendError(String message) {\r
+               this.append(message);\r
+               System.err.println(message);\r
+       }\r
+\r
+       @Override\r
+       public int getRows() {\r
+               return jta.getRows();\r
+       }\r
+       \r
+       @Override\r
+       public void setRows(int rows) {\r
+               jta.setRows(rows);\r
+       }\r
+}\r
diff --git a/TinyBannavi/src/tainavi/VWStatusWindow.java b/TinyBannavi/src/tainavi/VWStatusWindow.java
new file mode 100644 (file)
index 0000000..99e6632
--- /dev/null
@@ -0,0 +1,133 @@
+package tainavi;\r
+\r
+import java.awt.event.WindowAdapter;\r
+import java.awt.event.WindowEvent;\r
+\r
+import javax.swing.JDialog;\r
+import javax.swing.JScrollPane;\r
+import javax.swing.JTextArea;\r
+import javax.swing.ScrollPaneConstants;\r
+import javax.swing.WindowConstants;\r
+\r
+\r
+/**\r
+ *  <P>ステータスの小窓\r
+ *  <P><B>【注意】インスタンスは1個のみとしているので、clone()を実行しても自分自身が返ってきます!</B>\r
+ */\r
+public class VWStatusWindow extends JDialog implements StatusWindow,Cloneable {\r
+\r
+       private static final long serialVersionUID = 1L;\r
+       \r
+       private static final int WIN_COLS = 70;\r
+       private static final int WIN_ROWS = 8;\r
+\r
+       private final JScrollPane jsp = new JScrollPane();\r
+       private final JTextArea jta = new JTextAreaWithPopup();\r
+       \r
+       private WindowAdapter wl_closing = null;\r
+       \r
+       /**\r
+        * 鯛ナビでは1個インスタンスを作ったらずっと使いまわすので自身を返す。まあ、特殊。\r
+        */\r
+       @Override\r
+       public VWStatusWindow clone() {\r
+               return this;\r
+       }\r
+\r
+       // 内部用メソッド\r
+       private JScrollPane getJScrollPane() {\r
+               jta.setRows(WIN_ROWS);\r
+               jta.setColumns(WIN_COLS);\r
+               jta.setLineWrap(false);\r
+               jta.setEditable(false);\r
+                       \r
+               jsp.setViewportView(jta);\r
+               jsp.setVerticalScrollBarPolicy(ScrollPaneConstants.VERTICAL_SCROLLBAR_ALWAYS);\r
+               jsp.setHorizontalScrollBarPolicy(ScrollPaneConstants.HORIZONTAL_SCROLLBAR_ALWAYS);\r
+               \r
+               return jsp;\r
+       }\r
+       \r
+       // デフォルトコンストラクタ\r
+       public VWStatusWindow() {\r
+               //\r
+               super();\r
+               //\r
+               this.setDefaultCloseOperation(WindowConstants.DO_NOTHING_ON_CLOSE);\r
+               this.setModal(true);\r
+               \r
+               setClosingEnabled(true);\r
+               \r
+               this.setContentPane(getJScrollPane());\r
+               this.pack();\r
+               \r
+               this.setResizable(false);\r
+\r
+               //this.setLocationRelativeTo(owner);    // 画面の真ん中に\r
+               \r
+               //\r
+               this.setTitle("タイニー番組ナビゲータ ステータスウィンドウ");\r
+       }\r
+       \r
+       private final WindowAdapter wl_closing_exitdisabled = new WindowAdapter() {\r
+               @Override\r
+               public void windowClosing(WindowEvent e) {\r
+                       jta.append("処理中です。しばらくお待ちください…\n");\r
+               }\r
+       };\r
+\r
+       private final WindowAdapter wl_closing_exitenabled = new WindowAdapter() {\r
+               @Override\r
+               public void windowClosing(WindowEvent e) {\r
+                       System.out.println("強制終了します。");\r
+                       System.exit(1);\r
+               }\r
+       };\r
+\r
+       /*\r
+        * StatusWindow用のメソッド\r
+        */\r
+       \r
+       public void setClosingEnabled(boolean b) {\r
+               if ( wl_closing != null ) this.removeWindowListener(wl_closing);\r
+               if ( b ) {\r
+                       this.addWindowListener(wl_closing = wl_closing_exitenabled);\r
+               }\r
+               else {\r
+                       this.addWindowListener(wl_closing = wl_closing_exitdisabled);\r
+               }\r
+       }\r
+       \r
+       @Override\r
+       public void clear() {\r
+               jta.setText("");\r
+       }\r
+       \r
+       @Override\r
+       public void append(String message) {\r
+               jta.append(message+"\n");\r
+               jta.setCaretPosition(jta.getText().length());\r
+       }\r
+       \r
+       @Override\r
+       public void appendMessage(String message) {\r
+               this.append(message);\r
+               System.out.println(message);\r
+       }\r
+       \r
+       @Override\r
+       public void appendError(String message) {\r
+               this.append(message);\r
+               System.err.println(message);\r
+       }\r
+       \r
+       @Override\r
+       public void setVisible(boolean b) {\r
+               try {\r
+                       super.setVisible(b);\r
+               }\r
+               catch (NullPointerException e) {\r
+                       System.err.println("HOGEHOEG");\r
+               }\r
+       }\r
+}\r
diff --git a/TinyBannavi/src/tainavi/VWTimer.java b/TinyBannavi/src/tainavi/VWTimer.java
new file mode 100644 (file)
index 0000000..622c7bd
--- /dev/null
@@ -0,0 +1,110 @@
+package tainavi;\r
+\r
+import java.awt.event.ActionEvent;\r
+import java.awt.event.ActionListener;\r
+import java.util.ArrayList;\r
+import java.util.Date;\r
+\r
+import javax.swing.Timer;\r
+\r
+/**\r
+ * 毎分00秒に処理をキックするタイマー\r
+ */\r
+public class VWTimer {\r
+\r
+       public void setInterval(int min) { timer_interval = min; }\r
+       private int timer_interval = 1;                         // 何分ごとにキックするか\r
+       \r
+       public void setDelay(int n) { timer_delay_add = n; }\r
+       private int timer_delay_add = 100;                      // 実際には毎分00秒ではなく00.1秒にキックするよ\r
+       \r
+       private Timer timer_now = null;                         // タイマーのオブジェクト\r
+       \r
+       private ArrayList<VWTimerRiseListener> listener_list = new ArrayList<VWTimerRiseListener>(); \r
+       \r
+       /**\r
+        * 次の00秒までの時間(ミリ秒)を計算する\r
+        */\r
+       private int getNextDelay() {\r
+               return timer_interval*60000 - (int) (new Date().getTime() % (long) (timer_interval*60000));\r
+       }\r
+       \r
+       /**\r
+        * タイマー起動\r
+        */\r
+       public int start() {\r
+               \r
+               if ( timer_now == null ) {\r
+                       // 新規のタイマー\r
+                       timer_now = new Timer(0, al_nowtimer);  // TIMER_INTERVALの値はなんでもいい\r
+                       timer_now.setRepeats(false);    // 一回しか実行しないよ\r
+               }\r
+               \r
+               int delay = getNextDelay()+timer_delay_add;\r
+               timer_now.setInitialDelay(delay);\r
+               \r
+               timer_now.start();\r
+               \r
+               return delay;\r
+       }\r
+       \r
+       /**\r
+        * タイマー停止\r
+        */\r
+       public boolean stop() {\r
+               if ( timer_now != null ) {\r
+                       timer_now.stop();\r
+                       timer_now = null;\r
+                       \r
+                       return true;\r
+               }\r
+               return false;\r
+       }\r
+       \r
+       /**\r
+        * タイマー一時停止\r
+        */\r
+       public boolean pause() {\r
+               if ( timer_now != null && timer_now.isRunning() ) {\r
+                       timer_now.stop();\r
+                       \r
+                       return true;\r
+               }\r
+               return false;\r
+       }\r
+       \r
+       /**\r
+        * タイマーで実行する内容を追加\r
+        */\r
+       public void addVWTimerRiseListener(VWTimerRiseListener l) {\r
+               if ( ! listener_list.contains(l) ) {\r
+                       listener_list.add(l);\r
+               }\r
+       }\r
+       \r
+       /**\r
+        * タイマーで実行する内容を削除\r
+        */\r
+       public void removeVWTimerRiseListener(VWTimerRiseListener l) {\r
+               listener_list.remove(l);\r
+       }\r
+       \r
+       /**\r
+        * タイマーで実行する内容\r
+        */\r
+       private final ActionListener al_nowtimer = new ActionListener() {\r
+               @Override\r
+               public void actionPerformed(ActionEvent e) {\r
+                       \r
+                       VWTimerRiseEvent ev = new VWTimerRiseEvent(this);\r
+                       \r
+                       for ( VWTimerRiseListener l : listener_list ) {\r
+                               l.timerRised(ev);\r
+                       }\r
+                       \r
+                       int delay = start();\r
+                       //System.out.println("Timer Rised: now="+CommonUtils.getDateTimeYMD(ev.getCalendar())+" delay="+delay);\r
+               }\r
+       };\r
+\r
+}\r
diff --git a/TinyBannavi/src/tainavi/VWTimerRiseEvent.java b/TinyBannavi/src/tainavi/VWTimerRiseEvent.java
new file mode 100644 (file)
index 0000000..27eb9a7
--- /dev/null
@@ -0,0 +1,17 @@
+package tainavi;\r
+\r
+import java.util.EventObject;\r
+import java.util.GregorianCalendar;\r
+\r
+public class VWTimerRiseEvent extends EventObject {\r
+\r
+       private final GregorianCalendar calendar;\r
+       \r
+       public GregorianCalendar getCalendar() { return calendar; }\r
+       \r
+       public VWTimerRiseEvent(Object source) {\r
+               super(source);\r
+               this.calendar = new GregorianCalendar();\r
+       }\r
+\r
+}\r
diff --git a/TinyBannavi/src/tainavi/VWTimerRiseListener.java b/TinyBannavi/src/tainavi/VWTimerRiseListener.java
new file mode 100644 (file)
index 0000000..6445180
--- /dev/null
@@ -0,0 +1,9 @@
+package tainavi;\r
+\r
+import java.util.EventListener;\r
+\r
+public interface VWTimerRiseListener extends EventListener {\r
+       \r
+       public void timerRised(VWTimerRiseEvent e);\r
+       \r
+}\r
diff --git a/TinyBannavi/src/tainavi/VWTraceKeyDialog.java b/TinyBannavi/src/tainavi/VWTraceKeyDialog.java
new file mode 100644 (file)
index 0000000..277cb2c
--- /dev/null
@@ -0,0 +1,332 @@
+package tainavi;\r
+\r
+import java.awt.Dimension;\r
+import java.awt.event.ActionEvent;\r
+import java.awt.event.ActionListener;\r
+import java.util.ArrayList;\r
+import java.util.regex.Matcher;\r
+import java.util.regex.Pattern;\r
+\r
+import javax.swing.DefaultComboBoxModel;\r
+import javax.swing.JButton;\r
+import javax.swing.JCheckBox;\r
+import javax.swing.JComboBox;\r
+import javax.swing.JComponent;\r
+import javax.swing.JDialog;\r
+import javax.swing.JLabel;\r
+import javax.swing.JPanel;\r
+import javax.swing.JSlider;\r
+import javax.swing.JTextField;\r
+import javax.swing.SpringLayout;\r
+import javax.swing.event.ChangeEvent;\r
+import javax.swing.event.ChangeListener;\r
+\r
+/***\r
+ * \r
+ * 番組追跡検索の設定のクラス\r
+ * \r
+ */\r
+\r
+public class VWTraceKeyDialog extends JDialog {\r
+\r
+       private static final long serialVersionUID = 1L;\r
+\r
+       private TraceProgram xKeys = null;\r
+       private TraceKey xKey = null;\r
+       \r
+       private boolean reg = false;\r
+       \r
+       public String getNewLabel() { return jTextField_title.getText()+" ("+jTextField_channel.getText()+")"; }\r
+       \r
+       private ArrayList<String> okiniiri_items = new ArrayList<String>(); \r
+       public void clean_okiniiri_items() { okiniiri_items.clear(); }\r
+       public void add_okiniiri_item(String s) { okiniiri_items.add(s); }\r
+\r
+       // キーワード検索の設定ウィンドウのコンポーネント\r
+       \r
+       private JPanel jPanel = null;\r
+       \r
+       private JLabel jLabel_title = null;\r
+       private JTextField jTextField_title = null;\r
+       private JLabel jLabel_channel = null;\r
+       private JTextField jTextField_channel = null;\r
+       private JLabel jLabel_fazzyThreshold = null;\r
+       private JSlider jSlider_fazzyThreshold = null;\r
+       private JLabel jLabel_okiniiri = null;\r
+       private JComboBox jComboBox_okiniiri = null;\r
+       private JLabel jLabel_disableRepeat = null;\r
+       private JCheckBox jCheckBox_disableRepeat = null;\r
+       private JLabel jLabel_showLatestOnly = null;\r
+       private JCheckBox jCheckBox_showLatestOnly = null;\r
+       private JButton jButton_label = null;\r
+       private JButton jButton_cancel = null;\r
+\r
+       // ほげほげ\r
+\r
+       public boolean isRegistered() { return reg; }\r
+       \r
+       public void reopen(String s, TraceProgram sKeys) {\r
+               //\r
+               xKeys = sKeys;\r
+               //\r
+               for (TraceKey k : xKeys.getTraceKeys()) {\r
+                       if (k.getLabel().equals(s)) {\r
+                               //\r
+                               xKey = k;\r
+                               //\r
+                               Matcher ma = Pattern.compile("^(.+) \\(.+?\\)$",Pattern.DOTALL).matcher(k.getLabel());\r
+                               if (ma.find()) {\r
+                                       jTextField_title.setText(ma.group(1));\r
+                                       jTextField_title.setCaretPosition(0);\r
+                               }\r
+                               else {\r
+                                       jTextField_title.setText(k.getLabel());\r
+                               }\r
+                               jTextField_channel.setText(k.getCenter());\r
+                               jSlider_fazzyThreshold.setValue(k.getFazzyThreshold());\r
+                               jComboBox_okiniiri.setSelectedItem(k.getOkiniiri());\r
+                               jCheckBox_disableRepeat.setSelected(k.getDisableRepeat());\r
+                               jCheckBox_showLatestOnly.setSelected(k.getShowLatestOnly());\r
+                       }\r
+               }\r
+       }\r
+       \r
+       \r
+       //\r
+       private JPanel getJPanel() {\r
+               if (jPanel == null) {\r
+                       jPanel = new JPanel();\r
+\r
+                       jPanel.setLayout(new SpringLayout());\r
+                       \r
+                       int lw = 90;\r
+                       int iw = 200;\r
+                       int ix = 10+lw+10;\r
+                       int y = 10;\r
+                       _getJComponent(jPanel, getJLabel_title("番組タイトル"), lw, 25, 10, y);\r
+                       _getJComponent(jPanel, getJTextField_title(), iw, 25, ix, y);\r
+                       \r
+                       y += 30;\r
+                       _getJComponent(jPanel, getJLabel_channel("チャンネル名"), lw, 25, 10, y);\r
+                       _getJComponent(jPanel, getJTextField_channel(), iw, 25, ix, y);\r
+                       \r
+                       y += 30;\r
+                       _getJComponent(jPanel, getJLabel_fazzyThreshold("あいまい閾値"), lw, 25, 10, y);\r
+                       _getJComponent(jPanel, getJSlider_fazzyThreshold(), iw, 25, ix, y);\r
+                       \r
+                       y += 30;\r
+                       _getJComponent(jPanel, getJLabel_okiniiri("お気に入り度"), lw, 25, 10, y);\r
+                       _getJComponent(jPanel, getJComboBox_okiniiri(), iw, 25, ix, y);\r
+                       \r
+                       y += 30;\r
+                       _getJComponent(jPanel, getJLabel_disableRepeat("再放送を除く"), lw, 25, 10, y);\r
+                       _getJComponent(jPanel, getJCheckBox_disableRepeat(), iw, 25, ix, y);\r
+                       \r
+                       y += 30;\r
+                       _getJComponent(jPanel, getJLabel_showLatestOnly("リピート放送を検出"), lw, 25, 10, y);\r
+                       _getJComponent(jPanel, getJCheckBox_showLatestOnly(), iw, 25, ix, y);\r
+                       \r
+                       int wd = 10+lw+10+iw+20;\r
+                       \r
+                       y += 50;\r
+                       int bw = 75;\r
+                       _getJComponent(jPanel, getJButton_label("登録"), bw, 25, (wd/2)-bw-5, y);\r
+                       _getJComponent(jPanel, getJButton_cancel("キャンセル"), bw, 25, (wd/2)+5, y);\r
+                       \r
+                       y += 30;\r
+                       \r
+                       Dimension d = new Dimension(wd,y+10);\r
+                       jPanel.setPreferredSize(d);\r
+               }\r
+               return jPanel;\r
+       }\r
+       \r
+       private void _getJComponent(JPanel p, JComponent c, int width, int height, int x, int y) {\r
+           c.setPreferredSize(new Dimension(width, height));\r
+           ((SpringLayout)p.getLayout()).putConstraint(SpringLayout.NORTH, c, y, SpringLayout.NORTH, p);\r
+           ((SpringLayout)p.getLayout()).putConstraint(SpringLayout.WEST, c, x, SpringLayout.WEST, p);\r
+           p.add(c);\r
+       }\r
+       \r
+       \r
+       \r
+       //\r
+       private JLabel getJLabel_title(String s) {\r
+               if (jLabel_title == null) {\r
+                       jLabel_title = new JLabel(s);\r
+               }\r
+               return(jLabel_title);\r
+       }\r
+       \r
+       //\r
+       private JTextField getJTextField_title() {\r
+               if (jTextField_title == null) {\r
+                       jTextField_title = new JTextField();\r
+               }\r
+               return(jTextField_title);\r
+       }\r
+       \r
+       //\r
+       private JLabel getJLabel_channel(String s) {\r
+               if (jLabel_channel == null) {\r
+                       jLabel_channel = new JLabel(s);\r
+               }\r
+               return(jLabel_channel);\r
+       }\r
+       private JTextField getJTextField_channel() {\r
+               if (jTextField_channel == null) {\r
+                       jTextField_channel = new JTextField();\r
+                       jTextField_channel.setEnabled(false);\r
+               }\r
+               return(jTextField_channel);\r
+       }\r
+       \r
+       //\r
+       private JLabel getJLabel_fazzyThreshold(String s) {\r
+               if (jLabel_fazzyThreshold == null) {\r
+                       jLabel_fazzyThreshold = new JLabel(s);\r
+               }\r
+               return(jLabel_fazzyThreshold);\r
+       }\r
+       private JSlider getJSlider_fazzyThreshold() {\r
+               if (jSlider_fazzyThreshold == null) {\r
+                       jSlider_fazzyThreshold = new JSlider(1,99);\r
+                       \r
+                       jSlider_fazzyThreshold.addChangeListener(new ChangeListener() {\r
+                               @Override\r
+                               public void stateChanged(ChangeEvent e) {\r
+                                       \r
+                                       jLabel_fazzyThreshold.setText("あいまい閾値"+jSlider_fazzyThreshold.getValue());\r
+                               }\r
+                       });\r
+               }\r
+               return(jSlider_fazzyThreshold);\r
+       }\r
+       \r
+       //\r
+       private JLabel getJLabel_okiniiri(String s) {\r
+               if (jLabel_okiniiri == null) {\r
+                       jLabel_okiniiri = new JLabel(s);\r
+               }\r
+               return(jLabel_okiniiri);\r
+       }\r
+       private JComboBox getJComboBox_okiniiri() {\r
+               if (jComboBox_okiniiri == null) {\r
+                       jComboBox_okiniiri = new JComboBox();\r
+                       jComboBox_okiniiri.setEditable(false);\r
+                       \r
+                       DefaultComboBoxModel aModel = new DefaultComboBoxModel();\r
+                       jComboBox_okiniiri.setModel(aModel);\r
+                       for (String k : okiniiri_items) {\r
+                               aModel.addElement(k);\r
+                       }\r
+               }\r
+               return(jComboBox_okiniiri);\r
+       }\r
+       \r
+       //\r
+       private JLabel getJLabel_disableRepeat(String s) {\r
+               if (jLabel_disableRepeat == null) {\r
+                       jLabel_disableRepeat = new JLabel(s);\r
+               }\r
+               return(jLabel_disableRepeat);\r
+       }\r
+       private JCheckBox getJCheckBox_disableRepeat() {\r
+               if (jCheckBox_disableRepeat == null) {\r
+                       jCheckBox_disableRepeat = new JCheckBox();\r
+               }\r
+               return(jCheckBox_disableRepeat);\r
+       }\r
+       \r
+       //\r
+       private JLabel getJLabel_showLatestOnly(String s) {\r
+               if (jLabel_showLatestOnly == null) {\r
+                       jLabel_showLatestOnly = new JLabel(s);\r
+               }\r
+               return(jLabel_showLatestOnly);\r
+       }\r
+       private JCheckBox getJCheckBox_showLatestOnly() {\r
+               if (jCheckBox_showLatestOnly == null) {\r
+                       jCheckBox_showLatestOnly = new JCheckBox();\r
+               }\r
+               return(jCheckBox_showLatestOnly);\r
+       }\r
+       \r
+       //\r
+       private JButton getJButton_label(String s) {\r
+               if (jButton_label == null) {\r
+                       jButton_label = new JButton();\r
+                       jButton_label.setText(s);\r
+                       \r
+                       jButton_label.addActionListener(new ActionListener() {\r
+                               @Override\r
+                               public void actionPerformed(ActionEvent e) {\r
+                                       if (jTextField_title.getText().equals("")) {\r
+                                               return;\r
+                                       }\r
+                                       \r
+                                       //xKey.setLabel(jTextField_title.getText()+" ("+jTextField_channel.getText()+")");\r
+                                       xKey.setLabel(getNewLabel());\r
+                                       xKey.setTitlePop(TraceProgram.replacePop(jTextField_title.getText()));\r
+                                       xKey.setSearchStrKeys(TraceProgram.splitKeys(xKey.getTitlePop()));\r
+                                       xKey.setFazzyThreshold(jSlider_fazzyThreshold.getValue());\r
+                                       xKey.setOkiniiri((String) jComboBox_okiniiri.getSelectedItem());\r
+                                       xKey.setDisableRepeat(jCheckBox_disableRepeat.isSelected());\r
+                                       xKey.setShowLatestOnly(jCheckBox_showLatestOnly.isSelected());\r
+                                       xKeys.save();\r
+                                       \r
+                                       //\r
+                                       reg = true;\r
+                                       \r
+                                       // ウィンドウを閉じる\r
+                                       dispose();\r
+                       }\r
+                       });\r
+               }\r
+               return(jButton_label);\r
+       }\r
+       \r
+       //\r
+       private JButton getJButton_cancel(String s) {\r
+               if (jButton_cancel == null) {\r
+                       jButton_cancel = new JButton();\r
+                       jButton_cancel.setText(s);\r
+                       \r
+                       jButton_cancel.addActionListener(new ActionListener() {\r
+\r
+                               @Override\r
+                               public void actionPerformed(ActionEvent e) {\r
+                                       dispose();\r
+                               }\r
+                       });\r
+               }\r
+               return jButton_cancel;\r
+       }\r
+       \r
+       \r
+       // コンストラクタ\r
+       public VWTraceKeyDialog(int x, int y) {\r
+               \r
+               super();\r
+\r
+               //\r
+               reg = false;\r
+               \r
+               // 初期設定\r
+               clean_okiniiri_items();\r
+               for (String okini : TVProgram.OKINIIRI) {\r
+                       add_okiniiri_item(okini);\r
+               }\r
+               \r
+               //\r
+               this.setModal(true);\r
+               this.setContentPane(getJPanel());\r
+               // タイトルバーの高さも考慮する必要がある\r
+               Dimension d = getJPanel().getPreferredSize();\r
+               this.pack();\r
+               this.setBounds(x, y, d.width, d.height+this.getInsets().top);\r
+               this.setResizable(false);\r
+               //\r
+               this.setTitle("番組追跡の設定");\r
+       }\r
+}\r
diff --git a/TinyBannavi/src/tainavi/VWTreeCellRenderer.java b/TinyBannavi/src/tainavi/VWTreeCellRenderer.java
new file mode 100644 (file)
index 0000000..a9591da
--- /dev/null
@@ -0,0 +1,31 @@
+package tainavi;\r
+\r
+import java.awt.Color;\r
+import java.awt.Component;\r
+\r
+import javax.swing.JTree;\r
+import javax.swing.tree.DefaultMutableTreeNode;\r
+import javax.swing.tree.DefaultTreeCellRenderer;\r
+\r
+public class VWTreeCellRenderer extends DefaultTreeCellRenderer {\r
+\r
+       private static final long serialVersionUID = 1L;\r
+\r
+       @Override\r
+       public Component getTreeCellRendererComponent(JTree tree, Object value,\r
+                       boolean selected, boolean expanded, boolean leaf, int row,\r
+                       boolean hasFocus) {\r
+               Component c = super.getTreeCellRendererComponent(tree, value, selected, expanded, leaf, row, hasFocus);\r
+               // 'instanceof'は使っていいものやらわるいものやら\r
+               if ( value instanceof VWListedTreeNode ) {\r
+                       if ( leaf && ((VWListedTreeNode)value).isUnUsed() ) {\r
+                               c.setForeground(Color.RED);\r
+                       }\r
+               }\r
+               else {\r
+                       System.err.println("[error] VWListedTreeNode じゃないよ. "+((DefaultMutableTreeNode)value).toString());\r
+               }\r
+               return c;\r
+       }\r
+\r
+}\r
diff --git a/TinyBannavi/src/tainavi/VWUpdate.java b/TinyBannavi/src/tainavi/VWUpdate.java
new file mode 100644 (file)
index 0000000..b3fd64e
--- /dev/null
@@ -0,0 +1,407 @@
+package tainavi;\r
+\r
+import java.io.File;\r
+import java.io.IOException;\r
+import java.util.Calendar;\r
+import java.util.GregorianCalendar;\r
+import java.util.regex.Matcher;\r
+import java.util.regex.Pattern;\r
+\r
+import javax.swing.JDialog;\r
+import javax.swing.JOptionPane;\r
+\r
+\r
+public class VWUpdate {\r
+\r
+       private final String thisEncoding = "UTF-8";\r
+       \r
+       public enum UpdateResult { DONE, FAIL, PASS, NOUPDATE };\r
+       \r
+       private static final String site =  "http://sourceforge.jp";\r
+       private static final String updDir = "update";\r
+       \r
+       private static final TVProgramUtils utils = new TVProgramUtils();\r
+       \r
+       private StatusWindow stwin = null;\r
+       \r
+       private static UpdateResult isDone = UpdateResult.NOUPDATE;\r
+       \r
+       private static final String MSG_DONEUPDATE = "更新が完了しました。再起動してください。"+\r
+                       "\n\n★バックアップはbin.old、env.oldにあります。"+\r
+                       "\n\n☆再起動に失敗するとの報告がよせられています。"+\r
+                       "\n☆調査にご協力いただける方、事象発生時の"+\r
+                       "\n☆log.txtを提供いただけると助かります。";\r
+       \r
+       private static final String MSGID = "[アップデート] ";\r
+       private static final String ERRID = "[ERROR]"+MSGID;\r
+       private static final String DBGID = "[DEBUG]"+MSGID;\r
+       \r
+       /*\r
+        * 公開メソッド\r
+        */\r
+       \r
+       public VWUpdate(StatusWindow sw) {\r
+               stwin = sw;\r
+       }\r
+       \r
+       public UpdateResult checkUpdate(final String versioninfo)       {\r
+               \r
+               new SwingBackgroundWorker(true) {\r
+                       \r
+                       @Override\r
+                       protected Object doWorks() throws Exception {\r
+                               checkUpdateSub(versioninfo);\r
+                               return null;\r
+                       }\r
+                       \r
+                       @Override\r
+                       protected void doFinally() {\r
+                       }\r
+               }.execute();\r
+               \r
+               return isDone;\r
+       }\r
+       \r
+       \r
+       \r
+       /*\r
+        * 非公開メソッド \r
+        */\r
+       \r
+       // うーん… \r
+       private void StWinAppend(String message) {\r
+               if (stwin!=null) stwin.append(message);\r
+       }\r
+       private void StWinAppendMessage(String message) {\r
+               if (stwin!=null) stwin.append(message);\r
+               System.out.println(message);\r
+       }\r
+       private void StWinAppendError(String message) {\r
+               if (stwin!=null) stwin.append(message);\r
+               System.err.println(message);\r
+       }\r
+       \r
+       \r
+       //\r
+       private void checkUpdateSub(String versioninfo) {\r
+               \r
+               boolean isFull = new File("jre").exists() || new File("jre6").exists();\r
+               \r
+               Matcher ma = null;\r
+               \r
+               // 自分のバージョンを確認する\r
+               String currentver = "";\r
+               ma = Pattern.compile("([0-9]+)\\.([0-9]+)(\\.([0-9]+))?(.)?").matcher(versioninfo);\r
+               if (ma.find()) {\r
+                       if (ma.group(4) == null) {\r
+                               currentver = String.format("%03d_%03d", Integer.valueOf(ma.group(1)), Integer.valueOf(ma.group(2)));\r
+                       }\r
+                       else {\r
+                               String ab = "b";\r
+                               if (ma.group(5) != null && ma.group(5).equals("α")) {\r
+                                       ab = "a";\r
+                               }\r
+                               currentver = String.format("%03d_%03d_%03d%s", Integer.valueOf(ma.group(1)), Integer.valueOf(ma.group(2)), Integer.valueOf(ma.group(4)),ab);\r
+                       }\r
+               }\r
+               StWinAppendMessage("=== アップデートの確認を行います ===");\r
+               StWinAppendMessage("お使いのバージョンは "+currentver+" です.");\r
+               \r
+               String latestlink = "";\r
+               String latestfile = "";\r
+               String latestver = "";\r
+               String updatelink = "";\r
+               String updatefile = "";\r
+               String updatever = "";\r
+               \r
+               // Web上の最新バージョンを確認する\r
+               String response = utils.webToBuffer(site+"/projects/tainavi/releases/",thisEncoding,false);\r
+               if ( response != null ) {\r
+                       // 最新版\r
+                       ma = Pattern.compile("href=\"(/projects/tainavi/downloads/\\d+/(TinyBannavi.?(\\d+)_(\\d+)_"+((isFull)?("with"):("no"))+"_JRE\\.zip)/)").matcher(response);\r
+                       if (ma.find()) {\r
+                               latestlink = site+ma.group(1);\r
+                               latestfile = ma.group(2);\r
+                               latestver = String.format("%03d_%03d", Integer.valueOf(ma.group(3)), Integer.valueOf(ma.group(4)));\r
+                       }\r
+                       StWinAppendMessage("最新のバージョンは "+latestver+" です.");\r
+                       \r
+                       // アップデート差分\r
+                       ma = Pattern.compile("href=\"(/projects/tainavi/downloads/\\d+/(TinyBannavi.?(\\d+)_(\\d+)_(\\d+)b\\.zip)/)").matcher(response);\r
+                       if (ma.find()) {\r
+                               updatelink = site+ma.group(1);\r
+                               updatefile = ma.group(2);\r
+                               updatever = String.format("%03d_%03d_%03db", Integer.valueOf(ma.group(3)), Integer.valueOf(ma.group(4)), Integer.valueOf(ma.group(5)));\r
+                       }\r
+                       StWinAppendMessage("最新のアップデートは "+updatever+" です.");\r
+               }\r
+               \r
+               if (response == null || (latestver.length() == 0 && updatever.length() == 0)) {\r
+                       // サイト構成が変わったかな?\r
+                       StWinAppend("");\r
+                       \r
+                       String msg = "【警告】アップデート情報を取得できませんでした。プロジェクトサイト(http://sourceforge.jp/projects/tainavi/)を確認してください。";\r
+                       StWinAppendError(msg);\r
+                       \r
+                       StWinAppend("");\r
+                       \r
+                       msg = "★★★★ 起動時のアップデート確認が不要な場合は各種設定タブで無効にしてください。 ★★★";\r
+                       StWinAppendMessage(msg);\r
+                       \r
+                       isDone = UpdateResult.PASS;\r
+                       \r
+                       CommonUtils.milSleep(5000);\r
+               }\r
+               else {\r
+                       // 更新対象を確認する\r
+                       if (latestver.compareTo(currentver) > 0) {\r
+                               {\r
+                                       String msg = latestver+" にバージョンアップします.";\r
+                                       StWinAppendMessage(msg);\r
+                               }\r
+                               if ( isFull ) {\r
+                                       StWinAppend("");\r
+                                       \r
+                                       String msg = "★★★JRE同梱版はサイズが大きいためダウンロードに時間がかかります★★★";\r
+                                       StWinAppendMessage(msg);\r
+                               }\r
+                               \r
+                               if (confirmUpdate(latestfile)) {\r
+                                       // メジャーorマイナーバージョンが更新された場合\r
+                                       cleanup(updDir);\r
+                                       if (doUpdate(latestlink,latestfile)) {\r
+                                               isDone = UpdateResult.DONE;\r
+                                               showMessage(MSG_DONEUPDATE);\r
+                                       }\r
+                                       else {\r
+                                               isDone = UpdateResult.FAIL;\r
+                                               showMessage("バージョンアップが失敗しました.");\r
+                                       }\r
+                               }\r
+                               else {\r
+                                       isDone = UpdateResult.PASS;\r
+                                       StWinAppendMessage("バージョンアップはキャンセルされました.");\r
+                               }\r
+                       }\r
+                       else if (updatever.length()>0 && updatever.compareTo(currentver) > 0) {\r
+                               String msg = updatever+" にアップデートします.";\r
+                               StWinAppendMessage(msg);\r
+                               \r
+                               if (confirmUpdate(updatefile)) {\r
+                                       // リビジョンが更新された場合\r
+                                       cleanup(updDir);\r
+                                       if (doUpdate(updatelink,updatefile)) {\r
+                                               isDone = UpdateResult.DONE;\r
+                                               showMessage(MSG_DONEUPDATE);\r
+                                       }\r
+                                       else {\r
+                                               isDone = UpdateResult.FAIL;\r
+                                               showMessage("アップデートが失敗しました.");\r
+                                       }\r
+                               }\r
+                               else {\r
+                                       isDone = UpdateResult.PASS;\r
+                                       StWinAppendMessage("アップデートはキャンセルされました.");\r
+                               }\r
+                       }\r
+                       else {\r
+                               isDone = UpdateResult.NOUPDATE;\r
+                               String msg = "お使いのタイニー番組ナビゲータは最新です.";\r
+                               StWinAppendMessage(msg);\r
+                               \r
+                               CommonUtils.milSleep(1000);\r
+                       }\r
+               }\r
+               StWinAppendMessage("=== アップデートを確認しました ===");\r
+       }\r
+       \r
+       // 掃除する\r
+       private boolean cleanup(String dir) {\r
+               File d = new File(dir);\r
+               if (d.exists() && ! CommonUtils.rmdir(d)) {\r
+                       return false;\r
+               }\r
+               return d.mkdir();\r
+       }\r
+       \r
+       // 更新しますか\r
+       private boolean confirmUpdate(String newfile) {\r
+               String msg = "タイニー番組ナビゲータを("+newfile+")に更新しますか?";\r
+               \r
+               // JOptionPaneにsetAlwaysOnTopを設定するのは大変\r
+               JOptionPane jOptPane = new JOptionPane();\r
+               jOptPane.setMessage(msg);\r
+               jOptPane.setOptionType(JOptionPane.YES_NO_OPTION);\r
+               JDialog jdialog = jOptPane.createDialog("更新");\r
+               jdialog.setAlwaysOnTop(true);\r
+               jdialog.setDefaultCloseOperation(JDialog.DISPOSE_ON_CLOSE);\r
+               jdialog.setVisible(true);\r
+               jdialog.dispose();\r
+               \r
+               int ret = (Integer) jOptPane.getValue();\r
+               if (ret == JOptionPane.YES_OPTION) {\r
+                       return true;\r
+               }\r
+               return false;\r
+       }\r
+       \r
+       // 再起動してください\r
+       private boolean showMessage(String msg) {\r
+               // JOptionPaneにsetAlwaysOnTopを設定するのは大変\r
+               JOptionPane jOptPane = new JOptionPane();\r
+               jOptPane.setMessage(msg);\r
+               jOptPane.setOptionType(JOptionPane.PLAIN_MESSAGE);\r
+               JDialog jdialog = jOptPane.createDialog("メッセージ");\r
+               jdialog.setAlwaysOnTop(true);\r
+               jdialog.setDefaultCloseOperation(JDialog.DISPOSE_ON_CLOSE);\r
+               jdialog.setVisible(true);\r
+               jdialog.dispose();\r
+               \r
+               return false;\r
+       }\r
+       \r
+       // 更新を実行する\r
+       private boolean doUpdate(String releaselink, String releasefile) {\r
+               //\r
+               String releasepath = updDir+File.separator+releasefile;\r
+               \r
+               // リリースファイルを取得する\r
+               if ( ! download(releaselink,releasepath)) {\r
+                       String msg = "リリースファイルのダウンロードに失敗しました.";\r
+                       StWinAppendError(msg);\r
+                       \r
+                       return false;\r
+               }\r
+               \r
+               // リリースファイルを展開する\r
+               try {\r
+                       String msg = "リリースファイルを解凍します.";\r
+                       StWinAppendMessage(msg);\r
+                       unzip.doUnzip(updDir,releasepath);\r
+               }\r
+               catch (IOException e) {\r
+                       String msg = "zipファイルの解凍に失敗しました: "+e;\r
+                       StWinAppendError(msg);\r
+                       \r
+                       return false;\r
+               }\r
+               catch (Exception e) {\r
+                       String msg = "zipファイルの解凍に失敗しました: "+e;\r
+                       StWinAppendError(msg);\r
+                       \r
+                       return false;\r
+               }\r
+               \r
+               // アップデートを実施する\r
+               try {\r
+                       String msg = "アップデートを実行します.";\r
+                       StWinAppendMessage(msg);\r
+                       \r
+                       int exitvalue;\r
+                       if (CommonUtils.isWindows()) {\r
+                               exitvalue = CommonUtils.executeCommand("cmd /c call "+updDir+"\\TinyBannavi\\_update.cmd");\r
+                       }\r
+                       else {\r
+                               exitvalue = CommonUtils.executeCommand("sh "+updDir+"/TinyBannavi/_update.sh");\r
+                       }\r
+                       msg = "アップデートスクリプトの終了値: "+exitvalue;\r
+                       StWinAppendMessage(msg);\r
+               }\r
+               catch (Exception e) {\r
+                       e.printStackTrace();\r
+                       return false;\r
+               }\r
+               \r
+               return true;\r
+       }\r
+       \r
+       // リリースファイルを取得する\r
+       private boolean download(String releaselink, String fname) {\r
+               \r
+               // リリースページを取得する\r
+               String response = utils.webToBuffer(releaselink,thisEncoding,false);\r
+               \r
+               // リリースファイルのURLを取得する\r
+               String flink = "";\r
+               Matcher ma = Pattern.compile("<meta http-equiv=\"refresh\" content=\"1; url=(.+?)\">").matcher(response);\r
+               if ( ! ma.find()) {\r
+                       String msg = "リリースファイルの情報が取得できませんでした。プロジェクトサイト(http://sourceforge.jp/projects/tainavi/)を確認してください。";\r
+                       StWinAppendError(msg);\r
+                       return false;\r
+               }\r
+               \r
+               flink = CommonUtils.unEscape(site+ma.group(1));\r
+               \r
+               //\r
+               String msg = "リリースファイルをダウンロードします: "+flink;\r
+               StWinAppendMessage(msg);\r
+               \r
+               // リリースファイルを取得する\r
+               utils.webToFile(flink, fname, null);\r
+               \r
+               return true;\r
+       }\r
+       \r
+       \r
+       \r
+       /*\r
+        * アップデート履歴\r
+        */\r
+       \r
+       private static final String histfile = "env"+File.separator+"updatehistory.txt";\r
+       \r
+       public boolean isExpired(Env.UpdateOn u) {\r
+               if (u == Env.UpdateOn.DISABLE) {\r
+                       // アップデートしてはいけない\r
+                       StWinAppendMessage(MSGID+"アップデートは無効です.");\r
+                       return false;\r
+               }\r
+               File hf = new File(histfile);\r
+               if ( ! hf.exists() ) {\r
+                       // 履歴がないならアップデートしてください\r
+                       StWinAppendMessage(MSGID+"プロジェクトポータルに確認を行います(アップデート履歴ファイルは存在しません)");\r
+                       return true;\r
+               }\r
+               \r
+               // 前はいつ更新したっけなー\r
+               String lastdt = CommonUtils.read4file(histfile, true);\r
+               if ( lastdt == null ) {\r
+                       // 履歴がおかしいんでアップデートしちゃってください\r
+                       StWinAppendError(ERRID+"アップデート履歴ファイルが読み込めません:"+histfile);\r
+                       return true;\r
+               }\r
+               GregorianCalendar cala = CommonUtils.getCalendar(lastdt);\r
+               if ( cala == null ) {\r
+                       // 履歴がおかしいんでアップデートしちゃってください\r
+                       StWinAppendError(ERRID+"アップデート履歴ファイルが読み込めません:"+histfile);\r
+                       return true;\r
+               }\r
+               lastdt = CommonUtils.getDate(cala,true);\r
+\r
+               // 今日は何の日?\r
+               GregorianCalendar calb = CommonUtils.getCalendar(0);\r
+               String curdt = CommonUtils.getDate(calb,true);\r
+               \r
+               // この日までには更新したいなー\r
+               cala.add(Calendar.DATE, u.getInterval());\r
+               String nextdt = CommonUtils.getDate(cala,true);\r
+               \r
+               StWinAppendError(String.format(MSGID+"アップデート履歴のチェック: 最終確認日=%s 次回確認予定日=%s 本日=%s",lastdt,nextdt,curdt));\r
+               \r
+               if (cala.compareTo(calb) >= 0) {\r
+                       StWinAppendMessage(MSGID+"確認は保留されました.");\r
+                       return false;\r
+               }\r
+               \r
+               StWinAppendMessage(MSGID+"プロジェクトポータルに確認を行います(保留期限切れ)");\r
+               return true;\r
+       }\r
+       public boolean updateHistory() {\r
+               if ( ! CommonUtils.write2file(histfile, CommonUtils.getDate529(0,false)) ) {\r
+                       StWinAppendError(ERRID+"アップデート履歴ファイルが書き込めません:"+histfile);\r
+                       return false;\r
+               }\r
+               \r
+               return true;\r
+       }\r
+}\r
diff --git a/TinyBannavi/src/tainavi/VersionInfo.java b/TinyBannavi/src/tainavi/VersionInfo.java
new file mode 100644 (file)
index 0000000..7db0dce
--- /dev/null
@@ -0,0 +1,55 @@
+package tainavi;\r
+\r
+import java.util.regex.Matcher;\r
+import java.util.regex.Pattern;\r
+\r
+\r
+public class VersionInfo {\r
+       private static final String Version = "タイニー番組ナビゲータ 3.22.1β";\r
+       \r
+       private static final String OSname = System.getProperty("os.name");\r
+       private static final String OSvers = System.getProperty("os.version");\r
+       //private static final String OSarch = System.getProperty("sun.arch.data.model", "?");\r
+       private static final String VMvers = System.getProperty("java.version");\r
+       private static final String VMarch = System.getProperty("os.arch");                     // なんと!OSarchでなくJREarchが返る!\r
+       private static final String VMvend = System.getProperty("java.vendor");\r
+       \r
+       \r
+       public static String getVersion() {\r
+               return(Version);\r
+       }\r
+\r
+       public static String getEnvironment() {\r
+               String osarch = "";\r
+               if ( CommonUtils.isWindows() ) {\r
+                       osarch = (System.getenv("ProgramFiles(x86)") != null) ? "x64" : "x86";\r
+               }\r
+               else {\r
+                       CommonUtils.executeCommand("arch");\r
+                       String oa = CommonUtils.getCommandResult();\r
+                       if ( oa != null ) {\r
+                               osarch = Pattern.compile("[\r\n]+$",Pattern.DOTALL).matcher(oa).replaceFirst("");\r
+                       }\r
+               }\r
+               return(String.format("%s %s (%s) & Java %s (%s) [%s]",OSname,OSvers,osarch,VMvers,VMarch,VMvend));\r
+       }\r
+\r
+       public static String getVersionNumber() {\r
+               \r
+               Matcher ma = Pattern.compile("([0-9]+)\\.([0-9]+)(\\.([0-9]+))?(.)?").matcher(Version);\r
+               if (ma.find()) {\r
+                       if (ma.group(4) == null) {\r
+                               return String.format("%03d_%03d", Integer.valueOf(ma.group(1)), Integer.valueOf(ma.group(2)));\r
+                       }\r
+                       else {\r
+                               String ab = "b";\r
+                               if (ma.group(5) != null && ma.group(5).equals("α")) {\r
+                                       ab = "a";\r
+                               }\r
+                               return String.format("%03d_%03d_%03d%s", Integer.valueOf(ma.group(1)), Integer.valueOf(ma.group(2)), Integer.valueOf(ma.group(4)),ab);\r
+                       }\r
+               }\r
+               \r
+               return null;\r
+       }\r
+}\r
diff --git a/TinyBannavi/src/tainavi/Viewer.java b/TinyBannavi/src/tainavi/Viewer.java
new file mode 100644 (file)
index 0000000..4226707
--- /dev/null
@@ -0,0 +1,4756 @@
+package tainavi;\r
+\r
+import java.awt.AWTException;\r
+import java.awt.BorderLayout;\r
+import java.awt.Color;\r
+import java.awt.Component;\r
+import java.awt.Container;\r
+import java.awt.Desktop;\r
+import java.awt.Dimension;\r
+import java.awt.Font;\r
+import java.awt.Frame;\r
+import java.awt.Image;\r
+import java.awt.Insets;\r
+import java.awt.MenuItem;\r
+import java.awt.Point;\r
+import java.awt.PopupMenu;\r
+import java.awt.Rectangle;\r
+import java.awt.SystemTray;\r
+import java.awt.Toolkit;\r
+import java.awt.TrayIcon;\r
+import java.awt.datatransfer.Clipboard;\r
+import java.awt.datatransfer.StringSelection;\r
+import java.awt.event.ActionEvent;\r
+import java.awt.event.ActionListener;\r
+import java.awt.event.ComponentAdapter;\r
+import java.awt.event.ComponentEvent;\r
+import java.awt.event.MouseAdapter;\r
+import java.awt.event.MouseEvent;\r
+import java.awt.event.WindowAdapter;\r
+import java.awt.event.WindowEvent;\r
+import java.io.File;\r
+import java.io.IOException;\r
+import java.io.UnsupportedEncodingException;\r
+import java.lang.management.ManagementFactory;\r
+import java.lang.management.MemoryMXBean;\r
+import java.lang.management.MemoryUsage;\r
+import java.lang.reflect.InvocationTargetException;\r
+import java.net.URI;\r
+import java.net.URISyntaxException;\r
+import java.net.URLEncoder;\r
+import java.security.NoSuchAlgorithmException;\r
+import java.util.ArrayList;\r
+import java.util.Arrays;\r
+import java.util.Calendar;\r
+import java.util.GregorianCalendar;\r
+import java.util.HashMap;\r
+import java.util.LinkedHashMap;\r
+import java.util.ServiceLoader;\r
+import java.util.regex.Matcher;\r
+import java.util.regex.Pattern;\r
+\r
+import javax.imageio.ImageIO;\r
+import javax.swing.ImageIcon;\r
+import javax.swing.JComponent;\r
+import javax.swing.JFrame;\r
+import javax.swing.JLabel;\r
+import javax.swing.JMenuItem;\r
+import javax.swing.JOptionPane;\r
+import javax.swing.JPopupMenu;\r
+import javax.swing.SwingUtilities;\r
+import javax.swing.ToolTipManager;\r
+import javax.swing.UIManager;\r
+import javax.swing.event.ChangeEvent;\r
+import javax.swing.event.ChangeListener;\r
+\r
+import tainavi.HDDRecorder.RecType;\r
+import tainavi.SearchKey.TargetId;\r
+import tainavi.TVProgram.ProgFlags;\r
+import tainavi.TVProgram.ProgGenre;\r
+import tainavi.TVProgram.ProgOption;\r
+import tainavi.TVProgram.ProgSubgenre;\r
+import tainavi.TVProgram.ProgSubtype;\r
+import tainavi.TVProgram.ProgType;\r
+import tainavi.VWMainWindow.MWinTab;\r
+import tainavi.VWUpdate.UpdateResult;\r
+\r
+\r
+/**\r
+ * メインな感じ\r
+ */\r
+public class Viewer extends JFrame implements ChangeListener,VWTimerRiseListener {\r
+\r
+       private static final long serialVersionUID = 1L;\r
+       \r
+       @Override\r
+       public void stateChanged(ChangeEvent e){\r
+               StdAppendMessage("イベント発生");\r
+       }\r
+\r
+       \r
+       /*\r
+        * メソッド的な\r
+        */\r
+       \r
+       private void StdAppendMessage(String message)   { System.out.println(message); }\r
+       private void StdAppendError(String message)             { System.err.println(message); }\r
+       // \r
+       private void MWinSetVisible(boolean b)                  { mwin.setVisible(b); }\r
+       // \r
+       private void StWinClear()                                               { stwin.clear(); }\r
+       private void StWinSetVisible(boolean b)                 { stwin.setVisible(b); }\r
+       private void StWinSetLocationCenter(Component frame) { CommonSwingUtils.setLocationCenter(frame, (VWStatusWindow)stwin); }\r
+       private void StWinSetLocationUnder(Component frame)  { CommonSwingUtils.setLocationUnder(frame, (VWStatusWindow)stwin); }\r
+       \r
+       private void ringBeep() { if (env!=null && ! env.getDisableBeep()) { Toolkit.getDefaultToolkit().beep(); if ( env.getDebug() ) CommonUtils.printStackTrace(); } }\r
+       \r
+       \r
+       /*\r
+        * オブジェクト的な\r
+        */\r
+       \r
+       // 設定値をいれるところ\r
+       private final Env env = new Env();                                                                                      // 主要な設定\r
+       private final Bounds bounds = new Bounds();                                                                     // ウィンドウサイズとか動的に変化するもの\r
+       private final ClipboardInfoList cbitems = new ClipboardInfoList();                      // クリップボード対応機能でどの項目をコピーするかとかの設定 \r
+       private final PaperColorsMap pColors = new PaperColorsMap();                            // 新聞形式のジャンル別背景色の設定\r
+       private final AVSetting avs = new AVSetting();                                                          // ジャンル別録画画質・音質等設定\r
+       private final CHAVSetting chavs = new CHAVSetting();                                            // CH別録画画質・音質等設定\r
+       private final ChannelSort chsort = new ChannelSort();                                           // CHソート設定\r
+       private final ChannelConvert chconv = new ChannelConvert();                                     // ChannelConvert.dat\r
+       private final MarkChar markchar = new MarkChar(env);                                            // タイトルにつけるマークを操作する\r
+       \r
+       private final MarkedProgramList mpList = new MarkedProgramList();                       // 検索結果のキャッシュ(表示高速化用)\r
+       private final TraceProgram trKeys = new TraceProgram();                                         // 番組追跡の設定\r
+       private final SearchProgram srKeys = new SearchProgram();                                       // キーワード検索の設定\r
+       private final SearchGroupList srGrps = new SearchGroupList();                           // キーワード検索グループの設定\r
+       private final ExtProgram extKeys = new ExtProgram();                                            // 延長警告管理の設定\r
+       \r
+       private final RecorderInfoList recInfoList = new RecorderInfoList();            // レコーダ一覧の設定\r
+       \r
+       private final HDDRecorderList recPlugins = new HDDRecorderList();                       // レコーダプラグイン(テンプレート)\r
+       private final HDDRecorderList recorders = new HDDRecorderList();                        // レコーダプラグイン(実際に利用するもの)\r
+       \r
+       private final TVProgramList progPlugins = new TVProgramList();                          // Web番組表プラグイン(テンプレート)\r
+       private final TVProgramList tvprograms = new TVProgramList();                           // Web番組表プラグイン(実際に利用するもの)\r
+       \r
+       private final VWTimer timer_now = new VWTimer();                                                        // 毎分00秒に起動して処理をキックするタイマー\r
+       \r
+       // 初期化的な\r
+       private boolean logging = true;                                                                                 // ログ出力する\r
+       private boolean runRecWakeup = false;                                                                           // 起動時にレコーダを起こす\r
+       private boolean runRecLoad = false;                                                                                     // 起動時にレコーダから予約一覧を取得する\r
+       private boolean enableWebAccess = true;                                                                         // 起動時のWeb番組表へのアクセスを禁止する\r
+       private boolean onlyLoadProgram = false;\r
+       private String pxaddr = null;                                                                                           // ProxyAddress指定\r
+       private String pxport = null;                                                                                           // ProxtPort指定\r
+       \r
+       \r
+       /*******************************************************************************\r
+        * 定数\r
+        ******************************************************************************/\r
+       \r
+       public static final String LOG_FILE = "log.txt";                                                        // ログファイル名\r
+       public static final String HISTORY_FILE = "05_history.txt";                             // 更新履歴だよ\r
+       \r
+       private static final String ICONFILE_SYSTRAY = "icon"+File.separator+"tainavi16.png";\r
+       private static final String ICONFILE_TAINAVI = "icon"+File.separator+"tainavi.png";\r
+       \r
+       public static final int TIMEBAR_START = 5;                                      // 新聞形式の開始時刻\r
+       private static final int OPENING_WIAT = 500;                            // まあ起動時しか使わないんですけども\r
+\r
+       private static final String MSGID = "[鯛ナビ] ";\r
+       private static final String ERRID = "[ERROR]"+MSGID;\r
+       private static final String DBGID = "[DEBUG]"+MSGID;\r
+       \r
+       /**\r
+        * Web番組表のどれとどれを読めばいいのか\r
+        */\r
+       public static enum LoadFor {\r
+               TERRA   ("地上波&BSのみ取得"),\r
+               CS              ("CSのみ取得"),\r
+               CSo1    ("CS[プライマリ]のみ取得"),\r
+               CSo2    ("CS[セカンダリ]のみ取得"),\r
+               CSwSD   ("CSのみ取得(取得後シャットダウン)"),\r
+               RADIO   ("ラジオのみ取得"),\r
+               SYOBO   ("しょぼかるのみ取得"),\r
+               ALL             ("すべて取得");\r
+               \r
+               private String name;\r
+               \r
+               private LoadFor(String name) {\r
+                       this.name = name;\r
+               }\r
+\r
+               public String getName() {\r
+                       return this.name;\r
+               }\r
+               \r
+               public static LoadFor get(String s) {\r
+                       for ( LoadFor lf : LoadFor.values() ) {\r
+                               if ( lf.name.equals(s) ) {\r
+                                       return lf;\r
+                               }\r
+                       }\r
+                       return null;\r
+               }\r
+       };\r
+\r
+       /**\r
+        * レコーダ情報のどれとどれを読めばいいのか\r
+        */\r
+       public static enum LoadRsvedFor {\r
+               SETTING         ( "設定情報のみ取得(future use.)" ),\r
+               RECORDED        ( "録画結果のみ取得" ),\r
+               ;\r
+               \r
+               private String name;\r
+               \r
+               private LoadRsvedFor(String name) {\r
+                       this.name = name;\r
+               }\r
+               \r
+               public String getName() {\r
+                       return name;\r
+               }\r
+               \r
+               public static LoadRsvedFor get(String s) {\r
+                       for ( LoadRsvedFor lrf : LoadRsvedFor.values() ) {\r
+                               if ( lrf.name.equals(s) ) {\r
+                                       return lrf;\r
+                               }\r
+                       }\r
+                       return null;\r
+               }\r
+       }\r
+       \r
+       /**\r
+        *  リスト形式のカラム定義\r
+        * @deprecated しっぱいした 半年くらいしたら削除する\r
+        */\r
+       public static enum ListedColumn {\r
+               RSVMARK         ("予約",                      35),\r
+               DUPMARK         ("重複",                      35),\r
+               CHNAME          ("チャンネル名",  100),\r
+               TITLE           ("番組タイトル",  300),\r
+               DETAIL          ("番組詳細",                200),\r
+               START           ("開始時刻",                150),\r
+               END                     ("終了",                      50),\r
+               LENGTH          ("長さ",                      50),\r
+               GENRE           ("ジャンル",                85),\r
+               SITEM           ("検索アイテム名",       100),\r
+               STAR            ("お気に入り度",  100),\r
+               SCORE           ("スコア",                   35),\r
+               THRESHOLD       ("閾値",                      35),\r
+               HID_PRGID       ("PRGID",               -1),\r
+               HID_STIME       ("STIME",               -1),\r
+               HID_ETIME       ("ETIME",               -1),\r
+               HID_EXFLG       ("EXFLG",               -1),\r
+               HID_TITLE       ("TITLE",               -1),\r
+               ;\r
+\r
+               @SuppressWarnings("unused")\r
+               private String name;\r
+               private int iniWidth;\r
+\r
+               private ListedColumn(String name, int iniWidth) {\r
+                       this.name = name;\r
+                       this.iniWidth = iniWidth;\r
+               }\r
+\r
+               /* なんだかなー\r
+               @Override\r
+               public String toString() {\r
+                       return name;\r
+               }\r
+               */\r
+\r
+               public int getIniWidth() {\r
+                       return iniWidth;\r
+               }\r
+               \r
+               public int getColumn() {\r
+                       return ordinal();\r
+               }\r
+       };\r
+       \r
+       /**\r
+        *  本体予約一覧のカラム定義\r
+        * @deprecated しっぱいした 半年くらいしたら削除する\r
+        */\r
+       public static enum RsvedColumn {\r
+               PATTERN         ("パタン",                   110),\r
+               DUPMARK         ("重複",                      35),\r
+               EXEC            ("実行",                      35),\r
+               TRACE           ("追跡",                      35),\r
+               NEXTSTART       ("次回実行予定",  150),\r
+               END                     ("終了",                      50),\r
+               LENGTH          ("長さ",                      50),\r
+               ENCODER         ("エンコーダ",          50),\r
+               VRATE           ("画質",                      100),\r
+               ARATE           ("音質",                      50),\r
+               TITLE           ("番組タイトル",  300),\r
+               CHNAME          ("チャンネル名",  150),\r
+               RECORDER        ("レコーダ",                200),\r
+               HID_INDEX       ("INDEX",               -1),\r
+               HID_RSVID       ("RSVID",               -1),\r
+               ;\r
+\r
+               @SuppressWarnings("unused")\r
+               private String name;\r
+               private int iniWidth;\r
+\r
+               private RsvedColumn(String name, int iniWidth) {\r
+                       this.name = name;\r
+                       this.iniWidth = iniWidth;\r
+               }\r
+\r
+               /*\r
+               @Override\r
+               public String toString() {\r
+                       return name;\r
+               }\r
+               */\r
+\r
+               public int getIniWidth() {\r
+                       return iniWidth;\r
+               }\r
+               \r
+               public int getColumn() {\r
+                       return ordinal();\r
+               }\r
+       };\r
+       \r
+       \r
+       \r
+       /*\r
+        * コンポーネント\r
+        */\r
+       \r
+       // 起動時に固定で用意しておくもの\r
+       private final VWStatusWindow stwin = new VWStatusWindow();\r
+       private final VWStatusTextArea mwin = new VWStatusTextArea();\r
+       private final VWColorChooserDialog ccwin = new VWColorChooserDialog();\r
+       private final VWPaperColorsDialog pcwin = new VWPaperColorsDialog();\r
+       private final VWReserveDialog rdialog = new VWReserveDialog(0, 0);\r
+       \r
+       // 初期化処理の中で生成していくもの\r
+       private VWMainWindow mainWindow = null;\r
+       private VWToolBar toolBar = null;\r
+       private VWListedView listed = null;\r
+       private VWPaperView paper = null;\r
+       private VWReserveListView reserved = null;\r
+       private VWRecordedListView recorded = null;\r
+       private VWSettingView setting = null;\r
+       private VWRecorderSettingView recsetting = null;\r
+       private VWChannelSettingView chsetting = null;\r
+       private VWChannelDatSettingView chdatsetting = null;\r
+       private VWChannelSortView chsortsetting = null;\r
+       private VWChannelConvertView chconvsetting = null;\r
+       private VWLookAndFeel vwlaf = null;\r
+       private VWFont vwfont = null;\r
+       \r
+       private TrayIcon trayicon = null;\r
+       \r
+       \r
+       \r
+       /*******************************************************************************\r
+        * タブやダイアログのインスタンス作成用クラス定義\r
+        ******************************************************************************/\r
+       \r
+       /***\r
+        * リスト形式の内部クラス\r
+        */\r
+       private class VWListedView extends AbsListedView {\r
+\r
+               private static final long serialVersionUID = 1L;\r
+\r
+               // 環境設定の入れ物を渡す\r
+               @Override\r
+               protected Env getEnv() { return env; }\r
+               @Override\r
+               protected Bounds getBoundsEnv() { return bounds; }\r
+               @Override\r
+               protected ChannelSort getChannelSort() { return chsort; }\r
+\r
+               @Override\r
+               protected MarkedProgramList getMarkedProgramList() { return mpList; }\r
+               @Override\r
+               protected TraceProgram getTraceProgram() { return trKeys; }\r
+               @Override\r
+               protected SearchProgram getSearchProgram() { return srKeys; }\r
+               @Override\r
+               protected SearchGroupList getSearchGroupList() { return srGrps; }\r
+               @Override\r
+               protected ExtProgram getExtProgram() { return extKeys; }\r
+\r
+               @Override\r
+               protected TVProgramList getTVProgramList() { return tvprograms; }\r
+               @Override\r
+               protected HDDRecorderList getRecorderList() { return recorders; }\r
+\r
+               // メッセージ出力関連\r
+               @Override\r
+               protected StatusWindow getStWin() { return stwin; }\r
+               @Override\r
+               protected StatusTextArea getMWin() { return mwin; }\r
+               \r
+               // コンポーネントを渡す\r
+               @Override\r
+               protected AbsReserveDialog getReserveDialog() { return rdialog; }\r
+               @Override\r
+               protected Component getParentComponent() { return Viewer.this; }\r
+\r
+               @Override\r
+               protected void ringBeep() { Viewer.this.ringBeep(); }\r
+               \r
+               /*\r
+                * AbsListedView内でのイベントから呼び出されるメソッド群\r
+                */\r
+\r
+               @Override\r
+               protected void onShown() {\r
+                       // キーワード登録ボタンはリスト形式のみ\r
+                       toolBar.setAddkeywordEnabled(true);\r
+                       // 一括予約はリスト形式のみ\r
+                       toolBar.setBatchReservationEnabled(true);\r
+                       // スナップショットを有効にする\r
+                       toolBar.setSnapShotEnabled(true);\r
+               }\r
+\r
+               @Override\r
+               protected void onHidden() {\r
+                       // キーワード登録ボタンはリスト形式のみ\r
+                       toolBar.setAddkeywordEnabled(false);\r
+                       // 一括予約はリスト形式のみ\r
+                       toolBar.setBatchReservationEnabled(false);\r
+                       // スナップショットを無効にする\r
+                       toolBar.setSnapShotEnabled(false);\r
+               }\r
+\r
+               @Override\r
+               protected void showPopupForTraceProgram(\r
+                               final JComponent comp,\r
+                               final ProgDetailList tvd, final String keyword, final int threshold,\r
+                               final int x, final int y, final int h) {\r
+                       \r
+                       timer_now.pause();\r
+                       Viewer.this.showPopupForTraceProgram(comp, tvd, keyword, threshold, x, y, h);\r
+                       timer_now.start();\r
+               }\r
+\r
+               @Override\r
+               protected void updateReserveDisplay(String chname) {\r
+                       timer_now.pause();\r
+                       paper.updateReserveBorder(chname);\r
+                       reserved.redrawReservedList();\r
+                       timer_now.start();\r
+               }\r
+\r
+               @Override\r
+               protected void updateBangumiColumns() {\r
+                       timer_now.pause();\r
+                       paper.updateBangumiColumns();\r
+                       timer_now.start();\r
+               }\r
+\r
+               @Override\r
+               protected void clearPaper() {\r
+                       timer_now.pause();\r
+                       paper.clearPanel();\r
+                       timer_now.start();\r
+               }\r
+\r
+               @Override\r
+               protected void previewKeywordSearch(SearchKey search) {\r
+                       //timer_now.pause();\r
+                       if (search.alTarget.size() > 0) {\r
+                               mainWindow.setSelectedTab(MWinTab.LISTED);\r
+                               listed.redrawListByPreview(search);\r
+                       }\r
+                       //timer_now.start();\r
+               }\r
+\r
+               @Override\r
+               protected void jumpToPaper(String Center, String StartDateTime) {\r
+                       //timer_now.pause();\r
+                       paper.jumpToBangumi(Center,StartDateTime);\r
+                       //timer_now.start();\r
+               }\r
+\r
+               @Override\r
+               protected boolean addToPickup(ProgDetailList tvd) { return Viewer.this.addToPickup(tvd); }\r
+\r
+               @Override\r
+               protected boolean isTabSelected(MWinTab tab) { return mainWindow.isTabSelected(tab); }\r
+               @Override\r
+               protected void setSelectedTab(MWinTab tab) { mainWindow.setSelectedTab(tab); }\r
+\r
+               @Override\r
+               protected String getSelectedRecorderOnToolbar() { return toolBar.getSelectedRecorder(); }\r
+               @Override\r
+               protected boolean isFullScreen() { return toolBar.isFullScreen(); }\r
+               @Override\r
+               protected void setPagerEnabled(boolean b) { toolBar.setPagerEnabled(b); }\r
+               @Override\r
+               protected int getPagerCount() { return toolBar.getPagerCount(); }\r
+               @Override\r
+               protected int getSelectedPagerIndex() { return toolBar.getSelectedPagerIndex(); }\r
+\r
+               @Override\r
+               protected void setDividerEnvs(int loc) {\r
+                       if ( ! toolBar.isFullScreen() && mainWindow.isTabSelected(MWinTab.LISTED) ) {\r
+                               if (env.getSyncTreeWidth()) {\r
+                                       bounds.setTreeWidth(loc);\r
+                                       bounds.setTreeWidthPaper(loc);\r
+                               }\r
+                               else {\r
+                                       bounds.setTreeWidth(loc);\r
+                               }\r
+                       }\r
+               }\r
+       }\r
+       \r
+       \r
+       \r
+       /**\r
+        * 新聞形式の内部クラス\r
+        */\r
+       private class VWPaperView extends AbsPaperView {\r
+\r
+               private static final long serialVersionUID = 1L;\r
+\r
+               // 環境設定の入れ物を渡す\r
+               @Override\r
+               protected Env getEnv() { return env; }\r
+               @Override\r
+               protected Bounds getBoundsEnv() { return bounds; }\r
+               @Override\r
+               protected PaperColorsMap getPaperColorMap() { return pColors; }\r
+               @Override\r
+               protected ChannelSort getChannelSort() { return chsort; }\r
+               \r
+               @Override\r
+               protected TVProgramList getTVProgramList() { return tvprograms; }\r
+               @Override\r
+               protected HDDRecorderList getRecorderList() { return recorders; }\r
+\r
+               // メッセージ出力関連\r
+               @Override\r
+               protected StatusWindow getStWin() { return stwin; }\r
+               @Override\r
+               protected StatusTextArea getMWin() { return mwin; }\r
+               \r
+               // コンポーネントを渡す\r
+               @Override\r
+               protected AbsReserveDialog getReserveDialog() { return rdialog; }\r
+               @Override\r
+               protected Component getParentComponent() { return Viewer.this; }\r
+\r
+               @Override\r
+               protected void ringBeep() { Viewer.this.ringBeep(); }\r
+               \r
+               /*\r
+                * AbsPaperView内でのイベントから呼び出されるメソッド群\r
+                */\r
+\r
+               @Override\r
+               protected void onShown() {\r
+                       // ページャーコンボボックスを有効にする(状況次第で有効にならない場合もある)(ツリーの選択次第で変わるのでもどし)\r
+                       //toolBar.setPagerEnabled(true);\r
+                       // スナップショットを有効にする\r
+                       toolBar.setSnapShotEnabled(true);\r
+                       // ジャンル別背景色を有効にする\r
+                       toolBar.setPaperColorDialogEnabled(true);\r
+                       // マッチ枠を有効にする\r
+                       toolBar.setBorderToggleEnabled(true);\r
+               }\r
+\r
+               @Override\r
+               protected void onHidden() {\r
+                       // 新聞形式以外ではページャーコンボボックスを無効にする(ツリーの選択次第で変わるのでもどし)\r
+                       //toolBar.setPagerEnabled(false);\r
+                       // 新聞形式以外ではスナップショットを無効にする\r
+                       toolBar.setSnapShotEnabled(false);\r
+                       // 新聞形式以外ではジャンル別背景色を無効にする\r
+                       toolBar.setPaperColorDialogEnabled(false);\r
+                       // 新聞形式以外ではマッチ枠を無効にする\r
+                       toolBar.setBorderToggleEnabled(false);\r
+               }\r
+\r
+               @Override\r
+               protected void showPopupForTraceProgram(\r
+                               final JComponent comp,\r
+                               final ProgDetailList tvd, final String keyword, final int threshold,\r
+                               final int x, final int y, final int h) {\r
+                       \r
+                       timer_now.pause();\r
+                       Viewer.this.showPopupForTraceProgram(comp, tvd, keyword, threshold, x, y, h);\r
+                       timer_now.start();\r
+               }\r
+\r
+               @Override\r
+               protected void updateReserveDisplay() {\r
+                       timer_now.pause();\r
+                       listed.updateReserveMark();\r
+                       reserved.redrawReservedList();\r
+                       timer_now.start();\r
+               }\r
+\r
+               @Override\r
+               protected void addToPickup(ProgDetailList tvd) { Viewer.this.addToPickup(tvd); }\r
+\r
+               @Override\r
+               protected boolean isTabSelected(MWinTab tab) { return mainWindow.isTabSelected(tab); }\r
+               @Override\r
+               protected void setSelectedTab(MWinTab tab) { mainWindow.setSelectedTab(tab); }\r
+\r
+               @Override\r
+               protected String getSelectedRecorderOnToolbar() { return toolBar.getSelectedRecorder(); }\r
+               @Override\r
+               protected boolean isFullScreen() { return toolBar.isFullScreen(); }\r
+               @Override\r
+               protected void setSelectedPagerIndex(int idx) {\r
+                       toolBar.setSelectedPagerIndex(idx);\r
+               }\r
+               @Override\r
+               protected void setPagerEnabled(boolean b) { toolBar.setPagerEnabled(b); }\r
+               @Override\r
+               protected int getPagerCount() { return toolBar.getPagerCount(); }\r
+               @Override\r
+               protected int getSelectedPagerIndex() { return toolBar.getSelectedPagerIndex(); }\r
+               @Override\r
+               protected void setPagerItems(TVProgramIterator pli, int curindex) {\r
+                       toolBar.setPagerItems(pli,curindex);\r
+               }\r
+\r
+               @Override\r
+               protected String getExtensionMark(ProgDetailList tvd) { return markchar.getExtensionMark(tvd); }\r
+               @Override\r
+               protected String getOptionMark(ProgDetailList tvd) { return markchar.getOptionMark(tvd)+markchar.getNewLastMark(tvd); }\r
+               @Override\r
+               protected String getPostfixMark(ProgDetailList tvd) { return markchar.getPostfixMark(tvd); }\r
+\r
+               @Override\r
+               protected void setDividerEnvs(int loc) {\r
+                       if ( ! toolBar.isFullScreen() && mainWindow.isTabSelected(MWinTab.PAPER) ) {\r
+                               if (env.getSyncTreeWidth()) {\r
+                                       bounds.setTreeWidth(loc);\r
+                                       bounds.setTreeWidthPaper(loc);\r
+                               }\r
+                               else {\r
+                                       bounds.setTreeWidthPaper(loc);\r
+                               }\r
+                       }\r
+               }\r
+       }\r
+       \r
+       \r
+       \r
+       /**\r
+        * \r
+        * 本体予約一覧の内部クラス\r
+        * \r
+        */\r
+       private class VWReserveListView extends AbsReserveListView {\r
+\r
+               private static final long serialVersionUID = 1L;\r
+\r
+               // 環境設定の入れ物を渡す\r
+               @Override\r
+               protected Env getEnv() { return env; }\r
+               @Override\r
+               protected Bounds getBoundsEnv() { return bounds; }\r
+\r
+               @Override\r
+               protected HDDRecorderList getRecorderList() { return recorders; }\r
+\r
+               // ログ関係はないのか\r
+               \r
+               // コンポーネントを渡す\r
+               @Override\r
+               protected AbsReserveDialog getReserveDialog() { return rdialog; }\r
+               @Override\r
+               protected Component getParentComponent() { return Viewer.this; }\r
+\r
+               @Override\r
+               protected void ringBeep() { Viewer.this.ringBeep(); }\r
+\r
+               /*\r
+                * AbsReserveListView内でのイベントから呼び出されるメソッド群\r
+                */\r
+               \r
+               @Override\r
+               protected void updateReserveDisplay(String chname) {\r
+                       timer_now.pause();\r
+                       listed.updateReserveMark();\r
+                       paper.updateReserveBorder(chname);\r
+                       timer_now.start();\r
+               }\r
+\r
+               @Override\r
+               protected boolean doExecOnOff(boolean fexec, String title, String chnam, String rsvId, String recId) {\r
+                       return Viewer.this.doExecOnOff(fexec, title, chnam, rsvId, recId);\r
+               }\r
+               \r
+               @Override\r
+               protected JMenuItem getExecOnOffMenuItem(boolean fexec, String title,\r
+                               String chnam, String rsvId, String recId) {\r
+\r
+                       return Viewer.this.getExecOnOffMenuItem(fexec, title, chnam, rsvId, recId, 0);\r
+               }\r
+\r
+               @Override\r
+               protected JMenuItem getRemoveRsvMenuItem(String title, String chnam,\r
+                               String rsvId, String recId) {\r
+                       \r
+                       return Viewer.this.getRemoveRsvMenuItem(title, chnam, rsvId, recId, 0);\r
+               }\r
+\r
+               @Override\r
+               protected JMenuItem getJumpMenuItem(String title, String chnam,\r
+                               String startDT) {\r
+                       \r
+                       return Viewer.this.getJumpMenuItem(title, chnam, startDT);\r
+               }\r
+\r
+               @Override\r
+               protected JMenuItem getJumpToLastWeekMenuItem(String title,\r
+                               String chnam, String startDT) {\r
+                       \r
+                       return Viewer.this.getJumpToLastWeekMenuItem(title, chnam, startDT);\r
+               }\r
+\r
+               @Override\r
+               protected String getSelectedRecorderOnToolbar() { return toolBar.getSelectedRecorder(); }\r
+       }\r
+       \r
+       \r
+       \r
+       /**\r
+        * \r
+        * 録画結果一覧の内部クラス\r
+        * \r
+        */\r
+       private class VWRecordedListView extends AbsRecordedListView {\r
+\r
+               private static final long serialVersionUID = 1L;\r
+\r
+               // 環境設定の入れ物を渡す\r
+               @Override\r
+               protected Env getEnv() { return env; }\r
+               @Override\r
+               protected Bounds getBoundsEnv() { return bounds; }\r
+\r
+               @Override\r
+               protected HDDRecorderList getRecorderList() { return recorders; }\r
+\r
+               // ログ関係はないのか\r
+               \r
+               // コンポーネントを渡す\r
+               @Override\r
+               protected Component getParentComponent() { return Viewer.this; }\r
+\r
+               @Override\r
+               protected void ringBeep() { Viewer.this.ringBeep(); }\r
+\r
+               /*\r
+                * AbsReserveListView内でのイベントから呼び出されるメソッド群\r
+                */\r
+               \r
+               @Override\r
+               protected String getSelectedRecorderOnToolbar() { return toolBar.getSelectedRecorder(); }\r
+       }\r
+\r
+       \r
+       /***\r
+        * 各種設定の内部クラス\r
+        */\r
+       private class VWSettingView extends AbsSettingView {\r
+\r
+               private static final long serialVersionUID = 1L;\r
+\r
+               // 環境設定の入れ物を渡す\r
+               @Override\r
+               protected Env getEnv() { return env; }\r
+               @Override\r
+               protected ClipboardInfoList getCbItemEnv() { return cbitems; }\r
+               @Override\r
+               protected VWLookAndFeel getLAFEnv() { return vwlaf; }\r
+               @Override\r
+               protected VWFont getFontEnv() { return vwfont; }\r
+               \r
+               // メッセージ出力関連\r
+               @Override\r
+               protected StatusWindow getStWin() { return stwin; }\r
+               @Override\r
+               protected StatusTextArea getMWin() { return mwin; }\r
+               \r
+               // コンポーネントを渡す\r
+               @Override\r
+               protected Component getParentComponent() { return Viewer.this; }\r
+               @Override\r
+               protected VWColorChooserDialog getCcWin() { return ccwin; }\r
+\r
+               /*\r
+                * AbsSettingView内でのイベントから呼び出されるメソッド群\r
+                */\r
+               \r
+               @Override\r
+               protected void lafChanged(String lafname) {\r
+                       vwlaf.update(lafname);\r
+                       Viewer.this.updateComponentTreeUI();\r
+                       StdAppendMessage("Set LookAndFeel="+lafname);\r
+               }\r
+\r
+               @Override\r
+               protected void fontChanged(String fn, int fontSize) {\r
+                       vwfont.update(fn, fontSize);\r
+                       Viewer.this.updateComponentTreeUI();\r
+                       StdAppendMessage("システムのフォントを変更しました: "+fn+", size="+fontSize);\r
+               }\r
+\r
+               @Override\r
+               protected void setEnv(final boolean reload_prog) {\r
+                       \r
+                       //listed.pauseTimer();\r
+                       timer_now.pause();\r
+                       \r
+                       bounds.save();\r
+                       cbitems.save();\r
+                       env.save();\r
+\r
+                       // CommonUtilsの設定変更\r
+                       CommonUtils.setAdjLateNight(env.getAdjLateNight());\r
+                       CommonUtils.setExpandTo8(env.getExpandTo8());\r
+                       CommonUtils.setUseRundll32(env.getUseRundll32());\r
+                       CommonUtils.setDisplayPassedReserve(env.getDisplayPassedReserve());\r
+                       CommonUtils.setDebug(env.getDebug());\r
+                       \r
+                       SwingBackgroundWorker.setDebug(env.getDebug());\r
+                       \r
+                       // PassedProgramListの設定変更\r
+                       tvprograms.getPassed().setPassedDir(env.getPassedDir());\r
+\r
+                       // レコーダプラグインの設定変更\r
+                       for ( HDDRecorder rec : recorders ) {\r
+                               // 拡張設定だけ\r
+                               setSettingRecPluginExt(rec, env);\r
+                       }\r
+\r
+                       // Web番組表共通設定\r
+                       setSettingProgPluginCommon(env);\r
+                       \r
+                       // web番組表のリフレッシュ\r
+                       setSettingProgPluginAll(env);\r
+                       \r
+                       // リロードメニューの書き換え\r
+                       toolBar.updateReloadProgramExtention();\r
+                       \r
+                       // ページャーコンボボックスの書き換え\r
+                       toolBar.setPagerItems();\r
+                       \r
+                       // 列の表示・非表示\r
+                       listed.setMarkColumnVisible(env.getSplitMarkAndTitle());\r
+                       listed.setDetailColumnVisible(env.getShowDetailOnList());\r
+                       listed.setRowHeaderVisible(env.getRowHeaderVisible());\r
+                       reserved.setRowHeaderVisible(env.getRowHeaderVisible());\r
+                       \r
+                       // 強調色\r
+                       listed.setMatchedKeywordColor(env.getMatchedKeywordColor());\r
+                       listed.setRsvdLineColor((env.getRsvdLineEnhance())?(env.getRsvdLineColor()):(null));\r
+                       listed.setPickedLineColor((env.getRsvdLineEnhance())?(env.getPickedLineColor()):(null));\r
+                       listed.setCurrentLineColor((env.getCurrentLineEnhance())?(env.getCurrentLineColor()):(null));\r
+                       \r
+                       // システムトレイアイコン\r
+                       setTrayIconVisible(env.getShowSysTray());\r
+                       setXButtonAction(env.getShowSysTray() && env.getHideToTray());\r
+                       \r
+                       // 新聞形式のツールチップの表示時間を変更する\r
+                       setTooltipDelay();\r
+                       \r
+                       // Web番組表の再構築\r
+                       mpList.setHistoryOnlyUpdateOnce(env.getHistoryOnlyUpdateOnce());\r
+                       mpList.setShowOnlyNonrepeated(env.getShowOnlyNonrepeated());\r
+                       \r
+                       // 番組情報の再取得\r
+                       if ( reload_prog ) {\r
+                               loadTVProgram(false,LoadFor.ALL);       // 部品呼び出し\r
+                       }\r
+                       \r
+                       // 新聞描画枠のリセット\r
+                       paper.clearPanel();\r
+                       paper.buildMainViewByDate();\r
+                       \r
+                       // 再度ツリーの再構築\r
+                       paper.redrawTreeByDate();\r
+                       paper.redrawTreeByPassed();\r
+                       \r
+                       listed.redrawTreeByHistory();\r
+                       listed.redrawTreeByCenter();\r
+                       \r
+                       // 再描画\r
+                       paper.reselectTree();\r
+                       listed.reselectTree();\r
+\r
+                       //listed.continueTimer();       // まあreselectTree()で再開しているはずだが\r
+                       timer_now.start();\r
+               }\r
+       }\r
+       \r
+       /**\r
+        * レコーダ設定タブの内部クラス\r
+        * @see AbsRecorderSettingView\r
+        */\r
+       private class VWRecorderSettingView extends AbsRecorderSettingView {\r
+\r
+               private static final long serialVersionUID = 1L;\r
+\r
+               // 環境設定の入れ物を渡す\r
+               @Override\r
+               protected Env getEnv() { return env; }\r
+               @Override\r
+               protected RecorderInfoList getRecInfos() { return recInfoList; }\r
+               @Override\r
+               protected HDDRecorderList getRecPlugins() { return recPlugins; }\r
+\r
+               // ログ関連\r
+               @Override\r
+               protected VWStatusWindow getStWin() { return stwin; }\r
+               @Override\r
+               protected StatusTextArea getMWin() { return mwin; }\r
+\r
+               // コンポーネントを渡す\r
+               @Override\r
+               protected Component getParentComponent() { return Viewer.this; }\r
+               @Override\r
+               protected VWColorChooserDialog getCcWin() { return ccwin; }\r
+\r
+               @Override\r
+               protected void ringBeep() { Viewer.this.ringBeep(); }\r
+\r
+               /*\r
+                * AbsRecorderSettingView内でのイベントから呼び出されるメソッド群\r
+                */\r
+               \r
+               @Override\r
+               protected void setRecInfos() {\r
+                       \r
+                       timer_now.pause();\r
+                       \r
+                       // 設定を保存\r
+                       recInfoList.save();\r
+                       \r
+                       // レコーダプラグインのリフレッシュ\r
+                       initRecPluginAll();\r
+                       \r
+                       // レコーダ一覧をツールバーに設定\r
+                       toolBar.updateRecorderComboBox();\r
+                       \r
+                       // 予約一覧のリフレッシュ\r
+                       loadRdReserve(false, null);             // toolBarの内容がリセットされているので recId = null で\r
+                       \r
+                       // レコーダのエンコーダ表示の更新\r
+                       this.redrawRecorderEncoderEntry();\r
+\r
+                       // レコーダ一覧をCHコード設定のコンボボックスに設定 \r
+                       chdatsetting.updateRecorderComboBox();\r
+                       \r
+                       // Web番組表の再構築(予約マークのリフレッシュ)\r
+                       paper.updateReserveBorder(null);\r
+                       listed.updateReserveMark();\r
+                       \r
+                       timer_now.start();\r
+               }\r
+\r
+       }\r
+\r
+       \r
+       /***\r
+        * CH設定の内部クラス\r
+        */\r
+       private class VWChannelSettingView extends AbsChannelSettingView {\r
+\r
+               private static final long serialVersionUID = 1L;\r
+               \r
+               // 環境設定の入れ物を渡す\r
+               @Override\r
+               protected Env getEnv() { return Viewer.this.env; }\r
+               @Override\r
+               protected TVProgramList getProgPlugins() { return progPlugins; }\r
+\r
+               // ログ関連\r
+               @Override\r
+               protected StatusWindow getStWin() { return stwin; }\r
+               @Override\r
+               protected StatusTextArea getMWin() { return mwin; }\r
+\r
+               // コンポーネントを渡す\r
+               @Override\r
+               protected Component getParentComponent() { return Viewer.this; }\r
+               @Override\r
+               protected VWColorChooserDialog getCcWin() { return ccwin; }\r
+\r
+               @Override\r
+               protected void ringBeep() {\r
+                       Viewer.this.ringBeep();\r
+               }\r
+               @Override\r
+               protected void updateProgPlugin() {\r
+                       \r
+                       timer_now.pause();\r
+                       \r
+                       // 設定を保存(プラグイン内部の設定はChannelSettingPanel内で実施)\r
+                       env.save();\r
+                       \r
+                       // Web番組表プラグインのリフレッシュ\r
+                       setSelectedProgPlugin();\r
+                       initProgPluginAll();\r
+                       \r
+                       // CHソート設定に反映\r
+                       chsortsetting.updateChannelSortTable();\r
+                       \r
+                       // CHコンバート設定をリフレッシュ\r
+                       chconvsetting.updateChannelConvertTable();\r
+                       \r
+                       // CHコード設定にも反映\r
+                       chdatsetting.updateChannelDatTable();\r
+\r
+                       // 番組情報の再取得\r
+                       loadTVProgram(false,LoadFor.ALL);       // 部品呼び出し\r
+                       \r
+                       // ツールバーに反映\r
+                       toolBar.setPagerItems();\r
+                       \r
+                       // 新聞描画枠のリセット\r
+                       paper.clearPanel();\r
+                       paper.buildMainViewByDate();\r
+                       \r
+                       // サイドツリーの再構築\r
+                       paper.redrawTreeByCenter();\r
+                       \r
+                       listed.redrawTreeByCenter();\r
+                       \r
+                       // 再構築\r
+                       paper.reselectTree();\r
+                       listed.reselectTree();\r
+                       \r
+                       timer_now.start();\r
+               }\r
+               \r
+       }\r
+       \r
+       /***\r
+        * CHコード設定の内部クラス\r
+        */\r
+       private class VWChannelDatSettingView extends AbsChannelDatSettingView {\r
+\r
+               private static final long serialVersionUID = 1L;\r
+\r
+               // 環境設定の入れ物を渡す\r
+               @Override\r
+               protected Env getEnv() { return Viewer.this.env; }\r
+               @Override\r
+               protected TVProgramList getTVProgramList() { return tvprograms; }\r
+               @Override\r
+               protected ChannelSort getChannelSort() { return chsort; }\r
+               @Override\r
+               protected HDDRecorderList getHDDRecorderList() { return recorders; }\r
+\r
+               // ログ関連\r
+               @Override\r
+               protected StatusWindow getStWin() { return stwin; }\r
+               @Override\r
+               protected StatusTextArea getMWin() { return mwin; }\r
+\r
+               // コンポーネントを渡す\r
+               @Override\r
+               protected Component getParentComponent() { return Viewer.this; }\r
+               \r
+               @Override\r
+               protected void ringBeep() {\r
+                       Viewer.this.ringBeep();\r
+               }\r
+               \r
+       }\r
+\r
+       /**\r
+        * CHソート設定タブの内部クラス\r
+        */\r
+       private class VWChannelSortView extends AbsChannelSortView {\r
+\r
+               private static final long serialVersionUID = 1L;\r
+\r
+               @Override\r
+               protected Env getEnv() { return Viewer.this.env; }\r
+               @Override\r
+               protected TVProgramList getTVProgramList() { return tvprograms; }\r
+               @Override\r
+               protected ChannelSort getChannelSort() { return chsort; }\r
+               \r
+               // ログ関連\r
+               @Override\r
+               protected StatusTextArea getMWin() { return mwin; }\r
+               \r
+               @Override\r
+               protected void updProc() {\r
+                       \r
+                       timer_now.pause();\r
+                       \r
+                       env.save();\r
+                       \r
+                       toolBar.setPagerItems();\r
+                       toolBar.setSelectedPagerIndex(toolBar.getSelectedPagerIndex());\r
+                       \r
+                       // 新聞描画枠のリセット\r
+                       paper.clearPanel();\r
+                       paper.buildMainViewByDate();\r
+                       \r
+                       // サイドツリーの再構築\r
+                       paper.redrawTreeByCenter();\r
+                       \r
+                       listed.redrawTreeByCenter();\r
+                       \r
+                       // 再描画 \r
+                       paper.reselectTree();\r
+                       listed.reselectTree();\r
+                       \r
+                       timer_now.start();\r
+               }\r
+       }\r
+       \r
+       /**\r
+        * CHコンバート設定タブの内部クラス\r
+        */\r
+       private class VWChannelConvertView extends AbsChannelConvertView {\r
+\r
+               private static final long serialVersionUID = 1L;\r
+\r
+               // 環境設定の入れ物を渡す\r
+               @Override\r
+               protected Env getEnv() { return env; }\r
+               @Override\r
+               protected TVProgramList getProgPlugins() { return progPlugins; }\r
+               @Override\r
+               protected ChannelConvert getChannelConvert() { return chconv; }\r
+               \r
+       }\r
+       \r
+       /***\r
+        * 予約ウィンドウの内部クラス\r
+        */\r
+       private class VWReserveDialog extends AbsReserveDialog {\r
+\r
+               private static final long serialVersionUID = 1L;\r
+\r
+               // コンストラクタ\r
+               public VWReserveDialog(int x, int y) {\r
+                       super(x, y);\r
+               }\r
+\r
+               // 環境設定の入れ物を渡す\r
+               @Override\r
+               protected Env getEnv() { return env; }\r
+               @Override\r
+               protected TVProgramList getTVProgramList() { return tvprograms; }\r
+               @Override\r
+               protected HDDRecorderList getRecorderList() { return recorders; }\r
+               @Override\r
+               protected AVSetting getAVSetting() { return avs; }\r
+               @Override\r
+               protected CHAVSetting getCHAVSetting() { return chavs; }\r
+\r
+               // ログ関連\r
+               @Override\r
+               protected StatusWindow getStWin() { return stwin; }\r
+               @Override\r
+               protected StatusTextArea getMWin() { return mwin; }\r
+               \r
+               // コンポーネントを渡す\r
+               @Override\r
+               protected Component getParentComponent() { return Viewer.this; }\r
+\r
+               @Override\r
+               protected void ringBeep() { Viewer.this.ringBeep(); }\r
+\r
+               /*\r
+                * ReserveDialog内でのイベントから呼び出されるメソッド群\r
+                */\r
+               \r
+               @Override\r
+               protected void searchLikeRsv(LikeReserveList likeRsvList, ProgDetailList tvd, String keyword, int threshold) {\r
+                       Viewer.this.searchLikeRsv(likeRsvList, tvd, keyword, threshold);\r
+               }\r
+\r
+               @Override\r
+               protected String getSelectedRecorderOnToolbar() { return toolBar.getSelectedRecorder(); }\r
+       }\r
+       \r
+       /**\r
+        * 新聞の表示形式を操作するダイアログ\r
+        */\r
+       private class VWPaperColorsDialog extends AbsPaperColorsDialog {\r
+\r
+               private static final long serialVersionUID = 1L;\r
+\r
+               @Override\r
+               protected Env getEnv() { return env; }\r
+               @Override\r
+               protected Bounds getBoundsEnv() { return bounds; }\r
+               @Override\r
+               protected PaperColorsMap getPaperColorMap() { return pColors; }\r
+               \r
+               @Override\r
+               protected VWColorChooserDialog getCCWin() { return ccwin; }\r
+               \r
+               /*\r
+                * PaperColorsDialog内でのイベントから呼び出されるメソッド群\r
+                */\r
+               \r
+               // 背景色設定の反映\r
+               @Override\r
+               protected void updatePaperColors(Env ec,PaperColorsMap pc) {\r
+                       paper.updateColors(ec,pc);\r
+               }\r
+\r
+               // フォント設定の反映\r
+               @Override\r
+               protected void updatePaperFonts(Env ec) {\r
+                       paper.updateFonts(ec);\r
+               }\r
+\r
+               // サイズ設定の反映\r
+               @Override\r
+               protected void updatePaperBounds(Env ec, Bounds bc) {\r
+                       paper.updateBounds(ec,bc);\r
+               }\r
+               \r
+               // 再描画?\r
+               @Override\r
+               protected void updatePaperRepaint() {\r
+                       paper.updateRepaint();\r
+               }\r
+       }\r
+       \r
+       /**\r
+        * キーワード検索ウィンドウの内部クラス\r
+        */\r
+       private class VWKeywordDialog extends AbsKeywordDialog {\r
+\r
+               private static final long serialVersionUID = 1L;\r
+\r
+               @Override\r
+               void preview(SearchKey search) {\r
+                       // 検索実行\r
+                       if (search.alTarget.size() > 0) {\r
+                               mainWindow.setSelectedTab(MWinTab.LISTED);\r
+                               listed.redrawListByPreview(search);\r
+                       }\r
+               }\r
+       }\r
+\r
+       /**\r
+        * 延長警告管理ウィンドウの内部クラス\r
+        */\r
+       private class VWExtensionDialog extends AbsExtensionDialog {\r
+\r
+               private static final long serialVersionUID = 1L;\r
+\r
+               @Override\r
+               void preview(SearchKey search) {\r
+                       // 検索実行\r
+                       if (search.alTarget.size() > 0) {\r
+                               mainWindow.setSelectedTab(MWinTab.LISTED);\r
+                               listed.redrawListByPreview(search);\r
+                       }\r
+               }\r
+       }\r
+       \r
+       /***\r
+        * \r
+        * ツールバーの内部クラス\r
+        * \r
+        */\r
+       private class VWToolBar extends AbsToolBar {\r
+\r
+               private static final long serialVersionUID = 1L;\r
+\r
+               @Override\r
+               protected Env getEnv() { return env; }\r
+               @Override\r
+               protected Bounds getBoundsEnv() { return bounds; }\r
+               @Override\r
+               protected TVProgramList getTVPrograms() { return tvprograms; }\r
+               @Override\r
+               protected ChannelSort getChannelSort() { return chsort; }\r
+               @Override\r
+               protected HDDRecorderList getHDDRecorders() { return recorders; }\r
+\r
+               @Override\r
+               protected StatusWindow getStWin() { return stwin; }\r
+               @Override\r
+               protected StatusTextArea getMWin() { return mwin; }\r
+               @Override\r
+               protected Component getParentComponent() { return Viewer.this; }\r
+\r
+               @Override\r
+               protected void ringBeep() { Viewer.this.ringBeep(); }\r
+\r
+               @Override\r
+               protected boolean doKeywordSerach(SearchKey search, String kStr, String sStr, boolean doFilter) {\r
+                       \r
+                       timer_now.pause();\r
+                       \r
+                       if ( mainWindow.getSelectedTab() == MWinTab.RSVED ) {\r
+                               reserved.redrawListByKeywordFilter(search, kStr);\r
+                       }\r
+                       else if ( mainWindow.getSelectedTab() == MWinTab.RECED ) {\r
+                               recorded.redrawListByKeywordFilter(search, kStr);\r
+                       }\r
+                       else {\r
+                               if ( search != null ) {\r
+                                       mainWindow.setSelectedTab(MWinTab.LISTED);\r
+                                       if ( doFilter ) {\r
+                                               // 絞り込み検索\r
+                                               listed.clearSelection();\r
+                                               listed.redrawListByKeywordFilter(search, kStr);\r
+                                       }\r
+                                       else if (sStr != null) {\r
+                                               // 過去ログ検索\r
+                                               searchPassedProgram(search, sStr);\r
+                                               listed.clearSelection();\r
+                                               listed.redrawListBySearched(ProgType.PASSED, 0);\r
+                                               \r
+                                               listed.redrawTreeByHistory();\r
+                                       }\r
+                                       else {\r
+                                               // キーワード検索\r
+                                               listed.clearSelection();\r
+                                               listed.redrawListByKeywordDyn(search, kStr);\r
+                                       }\r
+                               }\r
+                       }\r
+                       \r
+                       timer_now.start();\r
+                       \r
+                       return true;\r
+               }\r
+\r
+               @Override\r
+               protected boolean doBatchReserve() {\r
+                       timer_now.pause();\r
+                       listed.doBatchReserve();\r
+                       timer_now.start();\r
+                       return true;\r
+               }\r
+\r
+               @Override\r
+               protected boolean jumpToNow() {\r
+                       timer_now.pause();\r
+                       if ( ! mainWindow.isTabSelected(MWinTab.PAPER) ) {\r
+                               mainWindow.setSelectedTab(MWinTab.PAPER);\r
+                       }\r
+                       paper.jumpToNow();\r
+                       timer_now.start();\r
+                       return true;\r
+               }\r
+\r
+               @Override\r
+               protected boolean jumpToPassed(String passed) {\r
+                       timer_now.pause();\r
+                       boolean b = paper.jumpToBangumi(null,passed);\r
+                       timer_now.start();\r
+                       return b;\r
+               }\r
+\r
+               @Override\r
+               protected boolean redrawByPager() {\r
+                       timer_now.pause();\r
+                       boolean b = paper.redrawByPager();\r
+                       timer_now.start();\r
+                       return b;\r
+               }\r
+\r
+               @Override\r
+               protected void toggleMatchBorder() {\r
+                       timer_now.pause();\r
+                       paper.toggleMatchBorder();\r
+                       timer_now.start();\r
+               }\r
+\r
+               @Override\r
+               protected void setPaperColorDialogVisible(boolean b) {\r
+                       //paper.stopTimer(); xxxx\r
+                       timer_now.pause();\r
+                       CommonSwingUtils.setLocationCenter(Viewer.this,pcwin);\r
+                       pcwin.setVisible(true);\r
+                       timer_now.start();\r
+               }\r
+\r
+               @Override\r
+               protected void setPaperZoom(int n) {\r
+                       timer_now.pause();\r
+                       paper.setZoom(n);\r
+                       timer_now.start();\r
+               }\r
+\r
+               @Override\r
+               protected boolean recorderSelectorChanged() {\r
+                       \r
+                       timer_now.pause();\r
+                       \r
+                       if (mainWindow.isTabSelected(MWinTab.LISTED)) {\r
+                               listed.updateReserveMark();\r
+                               listed.selectBatchTarget();\r
+                       }\r
+                       else if (mainWindow.isTabSelected(MWinTab.RSVED)) {\r
+                               reserved.redrawReservedList();\r
+                       }\r
+                       else if (mainWindow.isTabSelected(MWinTab.RECED)) {\r
+                               recorded.redrawRecordedList();\r
+                       }\r
+                       \r
+                       // 新聞形式の予約枠を書き換えるかもよ?\r
+                       if (env.getEffectComboToPaper()) {\r
+                               paper.updateReserveBorder(null);\r
+                       }\r
+                       \r
+                       timer_now.start();\r
+                       \r
+                       return true;\r
+               }\r
+\r
+               @Override\r
+               protected void takeSnapShot() {\r
+                       \r
+                       timer_now.pause();\r
+                       \r
+                       try {\r
+                               String fname;\r
+                               if ( mainWindow.isTabSelected(MWinTab.LISTED) ) {\r
+                                       fname = String.format("snapshot.%s",env.getSnapshotFmt().getExtension());\r
+                                       CommonSwingUtils.saveComponentAsJPEG(listed.getCurrentView(), listed.getTableHeader(), null, listed.getTableBody(), fname, env.getSnapshotFmt(), Viewer.this);\r
+                               }\r
+                               else if ( mainWindow.isTabSelected(MWinTab.PAPER) ){\r
+                                       if ( env.getDrawcacheEnable() || ! env.isPagerEnabled() ) {\r
+                                               fname = String.format("snapshot.%s",env.getSnapshotFmt().getExtension());\r
+                                       }\r
+                                       else {\r
+                                               int pcur = getSelectedPagerIndex();\r
+                                               int pmax = getPagerCount();\r
+                                               if ( env.getAllPageSnapshot() ) {\r
+                                                       for ( int i=0; i<pmax; i++ ) {\r
+                                                               if ( i != pcur ) {\r
+                                                                       setSelectedPagerIndex(i);\r
+                                                                       fname = String.format("snapshot%02d.%s",i+1,env.getSnapshotFmt().getExtension());\r
+                                                                       CommonSwingUtils.saveComponentAsJPEG(paper.getCurrentView(), paper.getCenterPane(), paper.getTimebarPane(), paper.getCurrentPane(), fname, env.getSnapshotFmt(), Viewer.this);\r
+                                                               }\r
+                                                       }\r
+                                               }\r
+                                               fname = String.format("snapshot%02d.%s",pcur+1,env.getSnapshotFmt().getExtension());\r
+                                               setSelectedPagerIndex(pcur);\r
+                                       }\r
+                                       CommonSwingUtils.saveComponentAsJPEG(paper.getCurrentView(), paper.getCenterPane(), paper.getTimebarPane(), paper.getCurrentPane(), fname, env.getSnapshotFmt(), Viewer.this);\r
+                               }\r
+                               else {\r
+                                       return; // おかしーよ\r
+                               }\r
+                               Desktop desktop = Desktop.getDesktop();\r
+                               if (env.getPrintSnapshot()) {\r
+                                       desktop.print(new File(fname));\r
+                               }\r
+                               else {\r
+                                       String emsg = CommonUtils.openFile(fname);\r
+                                       if (emsg != null) {\r
+                                               mwin.appendError(emsg);\r
+                                       }\r
+                               }\r
+                       } catch (IOException e1) {\r
+                               e1.printStackTrace();\r
+                       }\r
+                       finally {\r
+                               timer_now.start();\r
+                       }\r
+               }\r
+\r
+               @Override\r
+               protected void setStatusVisible(boolean b) {\r
+                       Viewer.this.setStatusVisible(b);\r
+               }\r
+\r
+               @Override\r
+               protected void setFullScreen(boolean b) {\r
+                       Viewer.this.setFullScreen(b);\r
+               }\r
+\r
+               @Override\r
+               protected void toggleSettingTabVisible() {\r
+                       mainWindow.toggleShowSettingTabs();\r
+               }\r
+\r
+               @Override\r
+               protected boolean isTabSelected(MWinTab tab) {\r
+                       return mainWindow.isTabSelected(tab);\r
+               }\r
+\r
+               @Override\r
+               protected boolean addKeywordSearch(SearchKey search) {\r
+                       \r
+                       timer_now.pause();\r
+                       \r
+                       AbsKeywordDialog kD = new VWKeywordDialog();\r
+                       CommonSwingUtils.setLocationCenter(Viewer.this,kD);\r
+                       \r
+                       kD.open(search.getLabel(), search, srKeys, srGrps);\r
+                       kD.setVisible(true);\r
+                       \r
+                       if (kD.isRegistered()) {\r
+                               // 検索結果の再構築\r
+                               mpList.clear(env.getDisableFazzySearch(), env.getDisableFazzySearchReverse());\r
+                               mpList.build(tvprograms, trKeys.getTraceKeys(), srKeys.getSearchKeys());\r
+                               \r
+                               // ツリーに反映する\r
+                               listed.redrawTreeByKeyword();\r
+                               \r
+                               mainWindow.setSelectedTab(MWinTab.LISTED);\r
+                       }\r
+                       \r
+                       timer_now.start();\r
+                       \r
+                       return true;\r
+               }\r
+\r
+               @Override\r
+               protected boolean reLoadTVProgram(LoadFor lf) {\r
+                       timer_now.pause();\r
+                       boolean b = Viewer.this.reLoadTVProgram(lf);\r
+                       timer_now.start();\r
+                       return b;\r
+               }\r
+\r
+               @Override\r
+               protected boolean reLoadRdReserve(String myself) {\r
+                       timer_now.pause();\r
+                       boolean b = Viewer.this.reLoadRdReserve(myself);\r
+                       timer_now.start();\r
+                       return b;\r
+               }\r
+\r
+               @Override\r
+               protected boolean reLoadRdRecorded(String myself) {\r
+                       timer_now.pause();\r
+                       boolean b = Viewer.this.reLoadRdRecorded(myself);\r
+                       timer_now.start();\r
+                       return b;\r
+               }\r
+\r
+       }\r
+       \r
+       \r
+       \r
+       /*******************************************************************************\r
+        * 共通メソッド群\r
+        ******************************************************************************/\r
+       \r
+       /**\r
+        * 類似予約をさがす\r
+        */\r
+       private void searchLikeRsv(LikeReserveList likeRsvList, ProgDetailList tvd, String keyword, int threshold) {\r
+               \r
+               likeRsvList.clear();\r
+               \r
+               // 曖昧検索のための初期化\r
+               String keywordPop = null;\r
+               int thresholdVal = 0;\r
+               if (threshold > 0) {\r
+                       // キーワード指定がある場合\r
+                       keywordPop = TraceProgram.replacePop(keyword);\r
+                       thresholdVal = threshold;\r
+               }\r
+               else {\r
+                       // キーワード指定がない場合\r
+                       keywordPop = tvd.titlePop;\r
+                       thresholdVal = env.getDefaultFazzyThreshold();\r
+               }\r
+\r
+               // 検索範囲\r
+               long rangeLikeRsv = env.getRangeLikeRsv()*3600000;\r
+               \r
+               HashMap<String,Boolean> misCN = new HashMap<String, Boolean>();\r
+               for ( HDDRecorder recorder : recorders ) {\r
+                       \r
+                       // 終了した予約を整理する\r
+                       recorder.refreshReserves();\r
+                       \r
+                       for ( ReserveList r : recorder.getReserves() ) {\r
+                               \r
+                               // タイトルのマッチング\r
+                               boolean isExist = false;\r
+                               if (env.getDisableFazzySearch() == false) {\r
+                                       // 双方向の比較を行う・正引き\r
+                                       int fazScore = TraceProgram.sumScore(keywordPop, r.getTitlePop());\r
+                                       if ( fazScore >= thresholdVal) {\r
+                                               isExist = true;\r
+                                       }\r
+                                       else if ( ! env.getDisableFazzySearchReverse()) {\r
+                                               // 逆引き\r
+                                               fazScore = TraceProgram.sumScore(r.getTitlePop(), keywordPop);\r
+                                               if ( fazScore >= thresholdVal) {\r
+                                                       isExist = true;\r
+                                               }\r
+                                       }\r
+                               }\r
+                               else {\r
+                                       if (r.getTitlePop().equals(tvd.titlePop)) {\r
+                                               isExist = true;\r
+                                       }\r
+                               }\r
+                               if ( ! isExist) {\r
+                                       continue;\r
+                               }\r
+                               \r
+                               // 放送局のマッチング\r
+                               if (r.getCh_name() == null) {\r
+                                       if(r.getChannel().length() > 0) {\r
+                                               misCN.put(r.getChannel(),true);\r
+                                       }\r
+                                       continue;\r
+                               }\r
+                               if ( ! r.getCh_name().equals(tvd.center)) {\r
+                                       continue;\r
+                               }\r
+                               \r
+                               // 近接時間チェック\r
+                               boolean inRange = true;\r
+                               if (rangeLikeRsv > 0) {\r
+                                       \r
+                                       inRange = false;\r
+                                       \r
+                                       ArrayList<String> starts = new ArrayList<String>();\r
+                                       ArrayList<String> ends = new ArrayList<String>();\r
+                                       CommonUtils.getStartEndList(starts, ends, r);\r
+                                       for (int j=0; j<starts.size(); j++) {\r
+                                               long d = CommonUtils.getDiffDateTime(tvd.startDateTime, starts.get(j));\r
+                                               //StdAppendMessage(String.format("%s %s %d", tvd.startDateTime, starts.get(j),d));\r
+                                               if (d <= rangeLikeRsv) {\r
+                                                       inRange = true;\r
+                                                       break;\r
+                                               }\r
+                                       }\r
+                               }\r
+                               if ( ! inRange) {\r
+                                       continue;\r
+                               }\r
+                               \r
+                               // 類似予約あり!\r
+                               likeRsvList.add(new LikeReserveItem(recorder, r));\r
+                       }\r
+               }\r
+               \r
+               return;\r
+       }\r
+       \r
+       \r
+       /***\r
+        * \r
+        * リスト・新聞形式共通\r
+        * \r
+        */\r
+\r
+       /**\r
+        *  番組追跡への追加とgoogle検索\r
+        */\r
+       public void showPopupForTraceProgram(\r
+                       final JComponent comp,\r
+                       final ProgDetailList tvd, final String keyword, final int threshold,\r
+                       final int x, final int y, final int h)\r
+       {\r
+               JPopupMenu pop = new JPopupMenu();\r
+       \r
+               // 予約する\r
+               if ( tvd.type == ProgType.PASSED ||\r
+                               (tvd.type == ProgType.PROG && tvd.subtype == ProgSubtype.RADIO) ||\r
+                               recorders.size() == 0 ) {\r
+                       // 過去ログは処理対象外です\r
+               }\r
+               else {\r
+                       JMenuItem menuItem = new JMenuItem("予約する【"+tvd.title+" ("+tvd.center+")】");\r
+                       \r
+                       menuItem.addActionListener(new ActionListener() {\r
+                               public void actionPerformed(ActionEvent e) {\r
+\r
+                                       CommonSwingUtils.setLocationCenter(mainWindow,rdialog);\r
+\r
+                                       if ( rdialog.open(tvd) ) {\r
+                                               rdialog.setVisible(true);\r
+                                       }\r
+                                       else {\r
+                                               rdialog.setVisible(false);\r
+                                       }\r
+                                       \r
+                                       //\r
+                                       if (rdialog.isReserved()) {\r
+                                               listed.updateReserveMark();\r
+                                               paper.updateReserveBorder(tvd.center);\r
+                                               reserved.redrawReservedList();\r
+                                       }\r
+                               }\r
+                       });\r
+                       pop.add(menuItem);\r
+               }\r
+               \r
+               pop.addSeparator();\r
+               \r
+               // 類似予約検索\r
+               LikeReserveList likeRsvList = new LikeReserveList();\r
+               searchLikeRsv(likeRsvList, tvd, "", 0);\r
+               \r
+               // 重複予約検索\r
+               LikeReserveList overlapRsvList = new LikeReserveList();\r
+               searchOverlapRsv(overlapRsvList, tvd, h);\r
+               \r
+               // 類似と重複で被るものを重複から除外\r
+               for ( LikeReserveItem ll : likeRsvList ) {\r
+                       int i=0;\r
+                       for ( ; i<overlapRsvList.size(); i++ ) {\r
+                               if ( ll.getRsv() == overlapRsvList.getRsv(i) ) {\r
+                                       break;\r
+                               }\r
+                       }\r
+                       if ( i < overlapRsvList.size() ) {\r
+                               overlapRsvList.remove(i);\r
+                       }\r
+               }\r
+               \r
+               // 予約実行ON・OFF\r
+               if ( tvd.type != ProgType.PASSED )\r
+               {\r
+                       for ( int n=0; n<2; n++ ) {\r
+                               \r
+                               LikeReserveList rsvList = null;\r
+                               if ( n == 0 ) {\r
+                                       rsvList = likeRsvList;\r
+                               }\r
+                               else {\r
+                                       rsvList = overlapRsvList;\r
+                               }\r
+                               \r
+                               for ( int i=0; i<rsvList.size(); i++ ) {\r
+                                       \r
+                                       final boolean fexec = rsvList.getRsv(i).getExec();\r
+                                       final String title = rsvList.getRsv(i).getTitle();\r
+                                       final String chnam = rsvList.getRsv(i).getCh_name();\r
+                                       final String rsvId = rsvList.getRsv(i).getId();\r
+                                       final String recId = rsvList.getRec(i).Myself();\r
+                                       \r
+                                       pop.add(getExecOnOffMenuItem(fexec,title,chnam,rsvId,recId,n));\r
+                               }\r
+                               \r
+                               pop.addSeparator();\r
+                       }\r
+               }\r
+               \r
+               pop.addSeparator();\r
+               \r
+               // 削除する\r
+               if ( tvd.type != ProgType.PASSED )      // 過去ログは処理対象外です\r
+               {\r
+                       for ( int n=0; n<2; n++ ) {\r
+                               \r
+                               LikeReserveList rsvList = null;\r
+                               if ( n == 0 ) {\r
+                                       rsvList = likeRsvList;\r
+                               }\r
+                               else {\r
+                                       rsvList = overlapRsvList;\r
+                               }\r
+                               \r
+                               for (int i=0; i<rsvList.size(); i++) {\r
+                                       \r
+                                       final String title = rsvList.getRsv(i).getTitle();\r
+                                       final String chnam = rsvList.getRsv(i).getCh_name();\r
+                                       final String rsvId = rsvList.getRsv(i).getId();\r
+                                       final String recId = rsvList.getRec(i).Myself();\r
+                                       \r
+                                       pop.add(getRemoveRsvMenuItem(title,chnam,rsvId,recId,n));\r
+                               }\r
+                               \r
+                               pop.addSeparator();\r
+                       }\r
+               }\r
+               else {\r
+                       pop.addSeparator();\r
+                       pop.addSeparator();\r
+               }\r
+               \r
+               // ジャンプする\r
+               {\r
+                       if ( mainWindow.isTabSelected(MWinTab.LISTED) ) {\r
+                               pop.add(getJumpMenuItem(tvd.title,tvd.center,tvd.startDateTime));\r
+                       }\r
+                       if ( mainWindow.isTabSelected(MWinTab.LISTED) || mainWindow.isTabSelected(MWinTab.PAPER) ) {\r
+                               JMenuItem mi = getJumpToLastWeekMenuItem(tvd.title,tvd.center,tvd.startDateTime);\r
+                               if ( mi != null ) {\r
+                                       pop.add(mi);\r
+                               }\r
+                       }\r
+               }\r
+               \r
+               pop.addSeparator();\r
+               \r
+               // 番組追跡へ追加する\r
+               {\r
+                       final String label = tvd.title+" ("+tvd.center+")";\r
+                       JMenuItem menuItem = new JMenuItem("番組追跡への追加【"+label+"】");\r
+                       menuItem.addActionListener(new ActionListener() {\r
+                               public void actionPerformed(ActionEvent e) {\r
+                                       //\r
+                                       for (TraceKey tr : trKeys.getTraceKeys()) {\r
+                                               if (tr.getLabel().equals(label)) {\r
+                                                       mwin.appendMessage("【警告】すでに番組追跡に登録されています:"+label);\r
+                                                       ringBeep();\r
+                                                       return;\r
+                                               }\r
+                                       }\r
+                                       \r
+                                       //\r
+                                       trKeys.add(label, tvd.title, tvd.center, env.getDefaultFazzyThreshold());\r
+\r
+                                       VWTraceKeyDialog tD = new VWTraceKeyDialog(0,0);\r
+                                       CommonSwingUtils.setLocationCenter(mainWindow,tD);\r
+                                       \r
+                                       tD.reopen(label, trKeys);\r
+                                       tD.setVisible(true);\r
+                                       \r
+                                       if (tD.isRegistered()) { \r
+                                               //\r
+                                               trKeys.save();\r
+                                               \r
+                                               // 検索結果の再構築\r
+                                               mpList.clear(env.getDisableFazzySearch(), env.getDisableFazzySearchReverse());\r
+                                               mpList.build(tvprograms, trKeys.getTraceKeys(), srKeys.getSearchKeys());\r
+                                               \r
+                                               // ツリーに反映する\r
+                                               listed.redrawTreeByTrace();\r
+\r
+                                               // 表示を更新する\r
+                                               paper.updateBangumiColumns();\r
+                                               listed.reselectTree();\r
+                                               \r
+                                               mwin.appendMessage("番組追跡へ追加しました【"+label+"】");\r
+                                       }\r
+                                       else {\r
+                                               trKeys.remove(label);\r
+                                       }\r
+                               }\r
+                       });\r
+                       pop.add(menuItem);\r
+               }\r
+               \r
+               // キーワード検索へ追加する\r
+               {\r
+                       final String label = tvd.title+" ("+tvd.center+")";\r
+                       JMenuItem menuItem = new JMenuItem("キーワード検索への追加【"+label+"】");\r
+                       menuItem.addActionListener(new ActionListener(){\r
+                               public void actionPerformed(ActionEvent e){\r
+                                       //\r
+                                       for (SearchKey sr : srKeys.getSearchKeys()) {\r
+                                               if (sr.getLabel().equals(tvd.title)) {\r
+                                                       ringBeep();\r
+                                                       String msg = "すでにキーワード検索に登録されています: "+tvd.title;\r
+                                                       mwin.appendMessage(msg);\r
+                                                       JOptionPane.showConfirmDialog(null, msg, "警告", JOptionPane.CLOSED_OPTION);                                                  // キーワード検索の追加ではダイアログで修正できるので止めない\r
+                                                       //return;\r
+                                               }\r
+                                       }\r
+                                       // 「キーワード検索の設定」ウィンドウを開く\r
+                                       SearchKey search = new SearchKey();\r
+                                       {\r
+                                               search.setCondition("0");\r
+                                               search.alTarget.add(TargetId.TITLE);\r
+                                               search.alContain.add("0");\r
+                                               search.alKeyword.add(tvd.title);\r
+                                       }\r
+                                       {\r
+                                               search.setCondition("0");\r
+                                               search.alTarget.add(TargetId.CHANNEL);\r
+                                               search.alContain.add("0");\r
+                                               search.alKeyword.add(tvd.center);\r
+                                       }\r
+                                       {\r
+                                               AbsKeywordDialog kD = new VWKeywordDialog();\r
+                                               CommonSwingUtils.setLocationCenter(mainWindow,kD);\r
+                                               \r
+                                               kD.open(tvd.title, search, srKeys, srGrps);\r
+                                               kD.setVisible(true);\r
+                                               \r
+                                               if (kD.isRegistered()) {\r
+                                                       // 検索結果の再構築\r
+                                                       mpList.clear(env.getDisableFazzySearch(), env.getDisableFazzySearchReverse());\r
+                                                       mpList.build(tvprograms, trKeys.getTraceKeys(), srKeys.getSearchKeys());\r
+                                                       \r
+                                                       // ツリーに反映する\r
+                                                       listed.redrawTreeByKeyword();\r
+\r
+                                                       // 表示を更新する\r
+                                                       paper.updateBangumiColumns();\r
+                                                       listed.reselectTree();\r
+                                                       \r
+                                                       mwin.appendMessage("キーワード検索へ追加しました【"+label+"】");\r
+                                               }\r
+                                       }\r
+                               }\r
+                       });\r
+                       pop.add(menuItem);\r
+               }\r
+               \r
+               // ピックアップへ追加する\r
+               {\r
+                       boolean isRemoveItem = false;\r
+                       if ( mainWindow.isTabSelected(MWinTab.LISTED) && tvd.type == ProgType.PICKED ) {\r
+                               isRemoveItem = true;\r
+                       }\r
+                       else {\r
+                               PickedProgram tvp = tvprograms.getPickup();\r
+                               if ( tvp != null ) {\r
+                                       isRemoveItem = tvp.remove(tvd, tvd.center, tvd.accurateDate, false);\r
+                               }\r
+                       }\r
+                       \r
+                       if ( ! isRemoveItem )   // 過去ログは処理対象外です\r
+                       {\r
+                               final String label = tvd.title+" ("+tvd.center+")";\r
+                               JMenuItem menuItem = new JMenuItem("ピックアップへの追加【"+label+"】");\r
+                               menuItem.addActionListener(new ActionListener() {\r
+                                       public void actionPerformed(ActionEvent e) {\r
+                                               //\r
+                                               PickedProgram tvp = tvprograms.getPickup();\r
+                                               if ( tvp != null ) {\r
+                                                       tvp.refresh();\r
+                                                       tvp.add(tvd);\r
+                                                       tvp.save();\r
+                                                       /*\r
+                                                       if ( listed.isNodeSelected(ListedTreeNode.PICKUP) ) {\r
+                                                               // ピックアップノードが選択されていたらリストを更新する\r
+                                                               listed.reselectTree();\r
+                                                       }\r
+                                                       */\r
+                                                       listed.updateReserveMark();\r
+                                                       listed.refocus();\r
+                                                       paper.updateReserveBorder(tvd.center);\r
+                                                       mwin.appendMessage("【ピックアップ】追加しました: "+tvd.title+" ("+tvd.center+")");\r
+                                                       return;\r
+                                               }\r
+                                       }\r
+                               });\r
+                               pop.add(menuItem);\r
+                       }\r
+                       else {\r
+                               final String label = tvd.title+" ("+tvd.center+")";\r
+                               JMenuItem menuItem = new JMenuItem("ピックアップからの削除【"+label+"】");\r
+                               menuItem.setForeground(Color.RED);\r
+                               menuItem.addActionListener(new ActionListener() {\r
+                                       public void actionPerformed(ActionEvent e) {\r
+                                               //\r
+                                               PickedProgram tvp = tvprograms.getPickup();\r
+                                               if ( tvp != null ) {\r
+                                                       tvp.refresh();\r
+                                                       tvp.remove(tvd, tvd.center, tvd.accurateDate, true);\r
+                                                       tvp.save();\r
+                                                       /*\r
+                                                       if ( listed.isNodeSelected(ListedTreeNode.PICKUP) || listed.isNodeSelected(ListedTreeNode.STANDBY) ) {\r
+                                                               // ピックアップノードが選択されていたらリストを更新する\r
+                                                               listed.reselectTree();\r
+                                                       }\r
+                                                       */\r
+                                                       listed.updateReserveMark();\r
+                                                       paper.updateReserveBorder(tvd.center);\r
+                                                       mwin.appendMessage("【ピックアップ】削除しました: "+tvd.title+" ("+tvd.center+")");\r
+                                                       return;\r
+                                               }\r
+                                       }\r
+                               });\r
+                               pop.add(menuItem);\r
+                       }\r
+               }\r
+\r
+               pop.addSeparator();\r
+               \r
+               // googleで検索する\r
+               {\r
+                       for (final TextValueSet tv : env.getTvCommand()) {\r
+                               JMenuItem menuItem = new JMenuItem(tv.getText());\r
+                               String escepedTitle = "";\r
+                               String escepedChName = "";\r
+                               String escepedDetail = "";\r
+                               try {\r
+                                       escepedTitle = URLEncoder.encode(tvd.title,"UTF-8");\r
+                                       escepedDetail = URLEncoder.encode(tvd.detail,"UTF-8");\r
+                                       escepedChName = URLEncoder.encode(tvd.center,"UTF-8");\r
+                               } catch (UnsupportedEncodingException e2) {\r
+                                       //\r
+                               }\r
+                               \r
+                               String cmd = tv.getValue();\r
+                               if ( cmd.matches(".*%DETAILURL%.*") ) {\r
+                                       if ( tvd.link == null || tvd.link.length() == 0 ) {\r
+                                               // このメニューは利用できません!\r
+                                               menuItem.setEnabled(false);\r
+                                               menuItem.setForeground(Color.lightGray);\r
+                                       }\r
+                               }\r
+                               cmd = cmd.replaceAll("%ENCTITLE%", escepedTitle);\r
+                               cmd = cmd.replaceAll("%ENCDETAIL%", escepedDetail);\r
+                               cmd = cmd.replaceAll("%ENCCHNAME%", escepedChName);\r
+                               cmd = cmd.replaceAll("%TITLE%", tvd.title);\r
+                               cmd = cmd.replaceAll("%DETAIL%", tvd.detail);\r
+                               cmd = cmd.replaceAll("%CHNAME%", tvd.center);\r
+                               cmd = cmd.replaceAll("%DATE%", tvd.accurateDate);\r
+                               cmd = cmd.replaceAll("%START%", tvd.start);\r
+                               cmd = cmd.replaceAll("%END%", tvd.end);\r
+                               cmd = cmd.replaceAll("%DETAILURL%", tvd.link); \r
+                               \r
+                               // CHAN-TORU対応\r
+                               if ( cmd.matches(".*%TVKAREACODE%.*") && cmd.matches(".*%TVKPID%.*") ) {\r
+                                       Center cr = null;\r
+                                       for ( TVProgram tvp : progPlugins ) {\r
+                                               if ( tvp.getTVProgramId().startsWith("Gガイド.テレビ王国") ) {\r
+                                                       for ( Center tempcr : tvp.getCRlist() ) {\r
+                                                               // CH設定が完了している必要がある\r
+                                                               if ( tvp.getSubtype() == ProgSubtype.TERRA && tvp.getSelectedCode().equals(TVProgram.allCode) && ! tempcr.getAreaCode().equals(TVProgram.bsCode) ) {\r
+                                                                       // 地域が全国の地デジの場合のみ、有効局かどうかを確認する必要がある\r
+                                                                       if ( tempcr.getCenter().equals(tvd.center) && tempcr.getOrder() > 0 ) {\r
+                                                                               // このメニューは利用できます!\r
+                                                                               cr = tempcr;\r
+                                                                               break;\r
+                                                                       }\r
+                                                               }\r
+                                                               else {\r
+                                                                       if ( tempcr.getCenter().equals(tvd.center) ) {\r
+                                                                               // このメニューは利用できます!\r
+                                                                               cr = tempcr;\r
+                                                                               break;\r
+                                                                       }\r
+                                                               }\r
+                                                       }\r
+                                                       \r
+                                                       if ( cr != null ) {\r
+                                                               break;\r
+                                                       }\r
+                                               }\r
+                                       }\r
+                                       if ( cr != null ) {\r
+                                               String areacode = null;\r
+                                               String centercode = cr.getLink();\r
+                                               String cat = cr.getLink().substring(0,1);\r
+                                               if ( cat.equals("1") ) {\r
+                                                       areacode = cr.getAreaCode();\r
+                                               }\r
+                                               else {\r
+                                                       if ( cat.equals("4") ) {\r
+                                                               cat = "5";\r
+                                                       }\r
+                                                       else if ( cat.equals("5") ) {\r
+                                                               cat = "4";\r
+                                                       }\r
+                                                       areacode = "10";\r
+                                               }\r
+                                               \r
+                                               cmd = cmd.replaceAll("%TVKAREACODE%", areacode);\r
+                                               cmd = cmd.replaceAll("%TVKCAT%", cat);\r
+                                               cmd = cmd.replaceAll("%TVKPID%", centercode+CommonUtils.getDateTimeYMD(CommonUtils.getCalendar(tvd.startDateTime)).replaceFirst("..$", ""));\r
+                                               System.out.println("[DEBUG] "+cmd);\r
+                                               \r
+                                               menuItem.setEnabled(true);\r
+                                               menuItem.setForeground(Color.BLACK);\r
+                                       }\r
+                                       else {\r
+                                               menuItem.setEnabled(false);\r
+                                               menuItem.setForeground(Color.lightGray);\r
+                                       }\r
+                               }\r
+                               \r
+                               final String run = cmd;\r
+                               \r
+                               menuItem.addActionListener(new ActionListener() {\r
+                                       @Override\r
+                                       public void actionPerformed(ActionEvent e) {\r
+                                               try {\r
+                                                       if (run.indexOf("http") == 0) {\r
+                                                               Desktop desktop = Desktop.getDesktop();\r
+                                                               desktop.browse(new URI(run));\r
+                                                       }\r
+                                                       else {\r
+                                                               CommonUtils.executeCommand(run);\r
+                                                       }\r
+                                               } catch (IOException e1) {\r
+                                                       e1.printStackTrace();\r
+                                               } catch (URISyntaxException e1) {\r
+                                                       e1.printStackTrace();\r
+                                               }\r
+                                       }\r
+                               });\r
+\r
+                               pop.add(menuItem);\r
+                       }\r
+               }\r
+               \r
+               pop.addSeparator();\r
+               \r
+               // クリップボードへコピーする\r
+               {\r
+                       JMenuItem menuItem = new JMenuItem("番組名をコピー【"+tvd.title+"】");\r
+                       menuItem.addActionListener(new ActionListener() {\r
+                               public void actionPerformed(ActionEvent e) {\r
+                                       String msg = tvd.title;\r
+                                       Clipboard cb = Toolkit.getDefaultToolkit().getSystemClipboard();\r
+                                       StringSelection s = new StringSelection(msg);\r
+                                       cb.setContents(s, null);\r
+                               }\r
+                       });\r
+                       pop.add(menuItem);\r
+               }\r
+               {\r
+                       JMenuItem menuItem = new JMenuItem("番組名と詳細をコピー【"+tvd.title+"】");\r
+                       menuItem.addActionListener(new ActionListener() {\r
+                               public void actionPerformed(ActionEvent e) {\r
+                                       String msg = tvd.title+System.getProperty("line.separator")+tvd.detail+"\0"+tvd.getAddedDetail();\r
+                                       Clipboard cb = Toolkit.getDefaultToolkit().getSystemClipboard();\r
+                                       StringSelection s = new StringSelection(msg);\r
+                                       cb.setContents(s, null);\r
+                               }\r
+                       });\r
+                       pop.add(menuItem);\r
+               }\r
+               {\r
+                       JMenuItem menuItem = new JMenuItem("番組情報をコピー【"+tvd.title+"】");\r
+                       menuItem.addActionListener(new ActionListener() {\r
+                               public void actionPerformed(ActionEvent e) {\r
+                                       String msg = "";\r
+                                       int preId = 0;\r
+                                       for (ClipboardInfo cb : cbitems) {\r
+                                               if (cb.getB()) {\r
+                                                       switch (cb.getId()) {\r
+                                                       case 1:\r
+                                                               msg += tvd.title+"\t";\r
+                                                               break;\r
+                                                       case 2:\r
+                                                               msg += tvd.center+"\t";\r
+                                                               break;\r
+                                                       case 3:\r
+                                                               msg += tvd.accurateDate+"\t";\r
+                                                               break;\r
+                                                       case 4:\r
+                                                               msg += tvd.start+"\t";\r
+                                                               break;\r
+                                                       case 5:\r
+                                                               if (preId == 4) {\r
+                                                                       msg = msg.substring(0,msg.length()-1)+"-";\r
+                                                               }\r
+                                                               msg += tvd.end+"\t";\r
+                                                               break;\r
+                                                       case 6:\r
+                                                               msg += tvd.genre+"\t";\r
+                                                               break;\r
+                                                       case 7:\r
+                                                               msg += tvd.detail+"\0"+tvd.getAddedDetail()+"\t";\r
+                                                               break;\r
+                                                       }\r
+                                               }\r
+                                               preId = cb.getId();\r
+                                       }\r
+                                       if (msg.length() > 0) {\r
+                                               msg = msg.substring(0,msg.length()-1);\r
+                                       }\r
+                                       Clipboard cb = Toolkit.getDefaultToolkit().getSystemClipboard();\r
+                                       StringSelection s = new StringSelection(msg);\r
+                                       cb.setContents(s, null);\r
+                               }\r
+                       });\r
+                       pop.add(menuItem);\r
+               }\r
+\r
+               pop.addSeparator();\r
+               \r
+               // 延長感染源へ追加する\r
+               if (\r
+                               tvd.type == ProgType.SYOBO ||\r
+                               tvd.type == ProgType.PASSED ||\r
+                               tvd.type == ProgType.PICKED ||\r
+                               (tvd.type == ProgType.PROG && tvd.subtype != ProgSubtype.RADIO) )       // ラジオは処理対象外です\r
+               {\r
+                       JMenuItem menuItem = new JMenuItem("延長感染源にしない【"+tvd.title+" ("+tvd.center+")】");\r
+                       menuItem.addActionListener(new ActionListener() {\r
+                               public void actionPerformed(ActionEvent e) {\r
+                                       //\r
+                                       mwin.appendMessage("延長感染源を隔離します【"+tvd.title+"("+tvd.center+")】");\r
+                                       //\r
+                                       AbsExtensionDialog eD = new VWExtensionDialog();\r
+                                       CommonSwingUtils.setLocationCenter(mainWindow,eD);\r
+                                       \r
+                                       eD.open(tvd.title,tvd.center,false,extKeys);\r
+                                       eD.setVisible(true);\r
+                                       \r
+                                       if (eD.isRegistered()) {\r
+                                               // 番組表の状態を更新する\r
+                                               for (TVProgram tvp : tvprograms) {\r
+                                                       if (tvp.getType() == ProgType.PROG) {\r
+                                                               tvp.setExtension(null, null, false, extKeys.getSearchKeys());\r
+                                                       }\r
+                                               }\r
+                                               \r
+                                               // ツリーに反映する\r
+                                               listed.redrawTreeByExtension();\r
+                                               \r
+                                               mainWindow.setSelectedTab(MWinTab.LISTED);\r
+                                       }\r
+                               }\r
+                       });\r
+                       pop.add(menuItem);\r
+               }\r
+               if ( tvd.type == ProgType.PASSED || (tvd.type == ProgType.PROG && tvd.subtype != ProgSubtype.RADIO) )   // ラジオは処理対象外です\r
+               {\r
+                       JMenuItem menuItem = new JMenuItem("延長感染源にする【"+tvd.title+" ("+tvd.center+")】");\r
+                       menuItem.addActionListener(new ActionListener() {\r
+                               public void actionPerformed(ActionEvent e) {\r
+                                       //\r
+                                       AbsExtensionDialog eD = new VWExtensionDialog();\r
+                                       CommonSwingUtils.setLocationCenter(mainWindow,eD);\r
+                                       \r
+                                       eD.open(tvd.title,tvd.center,true,extKeys);\r
+                                       eD.setVisible(true);\r
+                                       \r
+                                       if (eD.isRegistered()) {\r
+                                               // 番組表の状態を更新する\r
+                                               for (TVProgram tvp : tvprograms) {\r
+                                                       if (tvp.getType() == ProgType.PROG) {\r
+                                                               tvp.setExtension(null, null, false, extKeys.getSearchKeys());\r
+                                                       }\r
+                                               }\r
+                                               \r
+                                               // ツリーに反映する\r
+                                               listed.redrawTreeByExtension();\r
+                                               \r
+                                               mainWindow.setSelectedTab(MWinTab.LISTED);\r
+                                       }\r
+                               }\r
+                       });\r
+                       pop.add(menuItem);\r
+               }\r
+               \r
+               pop.addSeparator();\r
+               \r
+               // 視聴する\r
+               if ( tvd.type == ProgType.PROG && tvd.subtype != ProgSubtype.RADIO)     // ラジオは処理対象外です\r
+               {\r
+                       for (HDDRecorder recorder : recorders ) {\r
+                               \r
+                               if (recorder.ChangeChannel(null) == false) {\r
+                                       continue;\r
+                               }\r
+                               \r
+                               final String recorderName = recorder.Myself();\r
+                               JMenuItem menuItem = new JMenuItem("【"+recorderName+"】で【"+tvd.center+"】を視聴する");\r
+                               \r
+                               menuItem.addActionListener(new ActionListener() {\r
+                                       public void actionPerformed(ActionEvent e) {\r
+                                               for (HDDRecorder recorder : recorders ) {\r
+                                                       if (recorder.isMyself(recorderName)) {\r
+                                                               if (recorder.ChangeChannel(tvd.center) == false) {\r
+                                                                       ringBeep();\r
+                                                                       mwin.appendError("【警告】チャンネルを変更できませんでした:"+recorder.getErrmsg());\r
+                                                               }\r
+                                                               else if (recorder.getErrmsg() !=null && recorder.getErrmsg().length() > 0) {\r
+                                                                       mwin.appendError("[追加情報] "+recorder.getErrmsg());\r
+                                                               }\r
+                                                       }\r
+                                               }\r
+                                       }\r
+                               });\r
+                               \r
+                               menuItem.setEnabled(recorder.getUseChChange());\r
+                               \r
+                               pop.add(menuItem);\r
+                       }\r
+               }\r
+               \r
+               pop.show(comp, x, y);\r
+       }\r
+       \r
+       // ピックアップへ追加する\r
+       public boolean addToPickup(final ProgDetailList tvd) {\r
+               \r
+               if (tvd.start.equals("")) {\r
+                       // 番組情報がありません\r
+                       return false;\r
+               }\r
+               \r
+               PickedProgram tvp = tvprograms.getPickup();\r
+               if ( tvp == null ) {\r
+                       // ピックアップ先がありません\r
+                       return true;\r
+               }\r
+               \r
+               // 削除かな?\r
+               if ( tvp.remove(tvd, tvd.center, tvd.accurateDate, true) ) {\r
+                       tvp.save();\r
+                       if ( listed.isNodeSelected(JTreeLabel.Nodes.PICKUP) || listed.isNodeSelected(JTreeLabel.Nodes.STANDBY) ) {\r
+                               // ピックアップノードor予約待機ノードが選択されていたらリストを更新する\r
+                               listed.reselectTree();\r
+                               //listed.updateReserveMark();\r
+                       }\r
+                       else {\r
+                               // 予約マークだけ変えておけばいいよね\r
+                               listed.updateReserveMark();\r
+                               listed.refocus();\r
+                       }\r
+                       paper.updateReserveBorder(tvd.center);\r
+                       mwin.appendMessage("【ピックアップ】削除しました: "+tvd.title+" ("+tvd.center+")");\r
+                       return false;\r
+               }\r
+               \r
+               // 追加です\r
+               if ( tvd.endDateTime.compareTo(CommonUtils.getDateTime(0)) > 0 ) {\r
+                       tvp.refresh();\r
+                       tvp.add(tvd);\r
+                       tvp.save();\r
+                       if ( listed.isNodeSelected(JTreeLabel.Nodes.PICKUP) ) {\r
+                               // ピックアップノードが選択されていたらリストを更新する\r
+                               listed.reselectTree();\r
+                               //listed.updateReserveMark();\r
+                       }\r
+                       else {\r
+                               listed.updateReserveMark();\r
+                               listed.refocus();\r
+                       }\r
+                       paper.updateReserveBorder(tvd.center);\r
+                       mwin.appendMessage("【ピックアップ】追加しました: "+tvd.title+" ("+tvd.center+")");\r
+                       return true;\r
+               }\r
+\r
+               // 過去ログは登録できないよ\r
+               mwin.appendMessage("【ピックアップ】過去情報はピックアップできません.");\r
+               return false;\r
+       }\r
+       \r
+       /**\r
+        *  予約を削除するメニューアイテム\r
+        */\r
+       private JMenuItem getRemoveRsvMenuItem(final String title, final String chnam, final String rsvId, final String recId, int n) {\r
+               //\r
+               JMenuItem menuItem = new JMenuItem(((n==0)?"予約を削除する【":"隣接予約を削除する【")+title+"("+chnam+")/"+recId+"】");\r
+               menuItem.setForeground(Color.RED);\r
+               if ( recId.equals(toolBar.getSelectedRecorder()) ) {\r
+                       // 選択中のレコーダのものは太字に\r
+                       Font f = menuItem.getFont();\r
+                       menuItem.setFont(f.deriveFont(f.getStyle()|Font.BOLD));\r
+               }\r
+               \r
+               menuItem.addActionListener(new ActionListener() {\r
+                       public void actionPerformed(ActionEvent e) {\r
+                               \r
+                               if (env.getShowWarnDialog()) {\r
+                                       Container cp = getContentPane();\r
+                                       int ret = JOptionPane.showConfirmDialog(cp, "削除しますか?【"+title+"("+chnam+")】("+recId+")", "確認", JOptionPane.YES_NO_OPTION);\r
+                                       if (ret != JOptionPane.YES_OPTION) {\r
+                                               return;\r
+                                       }\r
+                               }\r
+                               \r
+                               stwin.clear();\r
+                               \r
+                               // 削除本体\r
+                               new SwingBackgroundWorker(false) {\r
+                                       \r
+                                       @Override\r
+                                       protected Object doWorks() throws Exception {\r
+                                               \r
+                                               for (HDDRecorder recorder : recorders) {\r
+                                                       if (recorder.isMyself(recId)) { // IPAddr:PortNo:RecorderIdで比較\r
+                                                               \r
+                                                               String title = "";\r
+                                                               for (ReserveList r : recorder.getReserves()) {\r
+                                                                       if (r.getId().equals(rsvId)) {\r
+                                                                               title = r.getTitle();\r
+                                                                               break;\r
+                                                                       }\r
+                                                               }\r
+                                                               \r
+                                                               stwin.appendMessage("予約を削除します:"+title+"("+rsvId+")");\r
+                                                               //recorder.setProgressArea(stwin);\r
+                                                               ReserveList r = recorder.RemoveRdEntry(rsvId);  // Noで検索\r
+                                                               if (r != null) {\r
+                                                                       mwin.appendMessage("正常に削除できました:"+r.getTitle()+"("+r.getCh_name()+")");\r
+                                                                       \r
+                                                                       if ( ! r.getTitle().equals(title) || ! r.getId().equals(rsvId)) {\r
+                                                                               mwin.appendError("【警告】削除結果が一致しません!:"+title+"/"+r.getTitle());\r
+                                                                       }\r
+                                                                       \r
+                                                                       if ( recorder.getUseCalendar()) {\r
+                                                                               // カレンダーから削除する\r
+                                                                               for ( HDDRecorder calendar : recorders ) {\r
+                                                                                       if (calendar.getType() == RecType.CALENDAR) {\r
+                                                                                               stwin.appendMessage("カレンダーから予約情報を削除します");\r
+                                                                                               //calendar.setProgressArea(stwin);\r
+                                                                                               if ( ! calendar.UpdateRdEntry(r, null)) {\r
+                                                                                                       mwin.appendError("【カレンダー】"+calendar.getErrmsg());\r
+                                                                                                       ringBeep();\r
+                                                                                               }\r
+                                                                                       }\r
+                                                                               }\r
+                                                                       }\r
+                                                                       \r
+                                                                       r = null;\r
+                                                               }\r
+                                                               else {\r
+                                                                       mwin.appendError("削除に失敗しました:"+title);\r
+                                                               }\r
+                                                               \r
+                                                               //\r
+                                                               if ( ! recorder.getErrmsg().equals("")) {\r
+                                                                       mwin.appendError("【追加情報】"+recorder.getErrmsg());\r
+                                                                       ringBeep();\r
+                                                               }\r
+                                                               break;\r
+                                                       }\r
+                                               }\r
+                                               return null;\r
+                                       }\r
+                                       \r
+                                       @Override\r
+                                       protected void doFinally() {\r
+                                               stwin.setVisible(false);\r
+                                       }\r
+                               }.execute();\r
+                               \r
+                               CommonSwingUtils.setLocationCenter(Viewer.this, stwin);\r
+                               stwin.setVisible(true);\r
+                               \r
+                               // 予約状況を更新\r
+                               listed.updateReserveMark();\r
+                               paper.updateReserveBorder(chnam);\r
+                               reserved.redrawReservedList();\r
+                       }\r
+               });\r
+               \r
+               return menuItem;\r
+       }\r
+       \r
+       \r
+       \r
+       \r
+       /*\r
+        * 他のクラスに分離できなかったというか、しなかったというか、そんなメソッド群\r
+        */\r
+       \r
+       /**\r
+        * \r
+        */\r
+       private boolean doExecOnOff(final boolean fexec, final String title, final String chnam, final String rsvId, final String recId) {\r
+               \r
+               CommonSwingUtils.setLocationCenter(mainWindow,rdialog);\r
+               \r
+               String mode = (fexec ? "ON" : "OFF");\r
+               \r
+               if ( rdialog.open(recId,rsvId) ) {\r
+                       rdialog.setOnlyUpdateExec(fexec);\r
+                       rdialog.doUpdate();\r
+                       \r
+                       if (rdialog.isReserved()) {\r
+                               // 予約状況を更新\r
+                               listed.updateReserveMark();\r
+                               paper.updateReserveBorder(chnam);\r
+                               reserved.redrawReservedList();\r
+                               \r
+                               {\r
+                                       String msg = "予約を"+mode+"にしました【"+title+"("+chnam+")/"+recId+"】";\r
+                                       //StdAppendMessage(msg);\r
+                                       mwin.appendMessage(msg);\r
+                               }\r
+                               \r
+                               return true;\r
+                       }\r
+               }\r
+               \r
+               return false;\r
+       }\r
+       \r
+       /**\r
+        *  予約実行をONOFFするメニューアイテム\r
+        */\r
+       private JMenuItem getExecOnOffMenuItem(final boolean fexec, final String title, final String chnam, final String rsvId, final String recId, int n) {\r
+               \r
+               JMenuItem menuItem = new JMenuItem();\r
+               \r
+               String mode;\r
+               if ( ! fexec ) {\r
+                       mode = "ON";\r
+                       menuItem.setForeground(Color.BLUE);\r
+               }\r
+               else {\r
+                       mode = "OFF";\r
+               }\r
+               \r
+               menuItem.setText(((n==0)?"予約を":"隣接予約を")+mode+"にする【"+title+"("+chnam+")/"+recId+")】");\r
+               \r
+               if ( recId.equals(toolBar.getSelectedRecorder()) ) {\r
+                       // 選択中のレコーダのものは太字に\r
+                       Font f = menuItem.getFont();\r
+                       menuItem.setFont(f.deriveFont(f.getStyle()|Font.BOLD));\r
+               }\r
+\r
+               final String xmode = mode;\r
+               menuItem.addActionListener(new ActionListener() {\r
+                       public void actionPerformed(ActionEvent e) {\r
+                               \r
+                               //VWReserveDialog rD = new VWReserveDialog(0, 0, env, tvprograms, recorders, avs, chavs, stwin);\r
+                               //rdialog.clear();\r
+                               CommonSwingUtils.setLocationCenter(mainWindow,rdialog);\r
+                               \r
+                               if ( rdialog.open(recId,rsvId) ) {\r
+                                       rdialog.setOnlyUpdateExec( ! fexec);\r
+                                       rdialog.doUpdate();\r
+                                       \r
+                                       if (rdialog.isReserved()) {\r
+                                               // 予約状況を更新\r
+                                               listed.updateReserveMark();\r
+                                               paper.updateReserveBorder(chnam);\r
+                                               reserved.redrawReservedList();\r
+                                               \r
+                                               {\r
+                                                       String msg = "予約を"+xmode+"にしました【"+title+"("+chnam+")/"+recId+"】";\r
+                                                       StdAppendMessage(msg);\r
+                                                       mwin.appendMessage(msg);\r
+                                               }\r
+                                       }\r
+                               }\r
+                               else {\r
+                                       //rdialog.setVisible(false);\r
+                               }\r
+                       }\r
+               });\r
+               \r
+               return menuItem;\r
+       }\r
+       \r
+       /**\r
+        *  新聞形式へジャンプするメニューアイテム\r
+        */\r
+       private JMenuItem getJumpMenuItem(final String title, final String chnam, final String startDT) {\r
+               JMenuItem menuItem = new JMenuItem("番組欄へジャンプする【"+title+" ("+chnam+")】");\r
+               menuItem.addActionListener(new ActionListener() {\r
+                       public void actionPerformed(ActionEvent e) {\r
+                               paper.jumpToBangumi(chnam,startDT);\r
+                       }\r
+               });\r
+               return menuItem;\r
+       }\r
+       private JMenuItem getJumpToLastWeekMenuItem(final String title, final String chnam, final String startDT) {\r
+               GregorianCalendar cal = CommonUtils.getCalendar(startDT);\r
+               if ( cal != null ) {\r
+                       JMenuItem menuItem = new JMenuItem("先週の番組欄へジャンプする【"+title+" ("+chnam+")】");\r
+                       cal.add(Calendar.DATE, -7);\r
+                       final String lastweek = CommonUtils.getDateTime(cal);\r
+                       menuItem.addActionListener(new ActionListener() {\r
+                               public void actionPerformed(ActionEvent e) {\r
+                                       paper.jumpToBangumi(chnam,lastweek);\r
+                               }\r
+                       });\r
+                       return menuItem;\r
+               }\r
+               return null;\r
+       }\r
+       \r
+       // カーソル位置にかかる予約枠の検索\r
+       private void searchOverlapRsv(LikeReserveList overlapRsvList, ProgDetailList tvd, int h)\r
+       {\r
+               String clicked = "";\r
+               if ( h >= 0 && tvd.start.length() != 0 ) {\r
+                       // 新聞形式ならクリック位置の日時を算出する\r
+                       GregorianCalendar cala = CommonUtils.getCalendar(tvd.startDateTime);\r
+                       if ( CommonUtils.isLateNight(cala.get(Calendar.HOUR_OF_DAY)) ) {\r
+                               cala.set(Calendar.HOUR_OF_DAY, TIMEBAR_START);\r
+                               cala.set(Calendar.MINUTE, 0);\r
+                       }\r
+                       cala.add(Calendar.MINUTE, Math.round(h/bounds.getPaperHeightMultiplier()));\r
+                       clicked = CommonUtils.getDateTime(cala);\r
+                       //StdAppendError("clicked:"+clicked);\r
+               }\r
+               \r
+               HashMap<String,Boolean> misCN = new HashMap<String, Boolean>();\r
+               for ( HDDRecorder recorder : recorders ) {\r
+                       \r
+                       // 終了した予約を整理する\r
+                       recorder.refreshReserves();\r
+                       \r
+                       for ( ReserveList r : recorder.getReserves() ) {\r
+                               \r
+                               // 放送局のマッチング\r
+                               if (r.getCh_name() == null) {\r
+                                       if(r.getChannel().length() > 0) {\r
+                                               misCN.put(r.getChannel(),true);\r
+                                       }\r
+                                       continue;\r
+                               }\r
+                               if ( ! r.getCh_name().equals(tvd.center)) {\r
+                                       continue;\r
+                               }\r
+                               \r
+                               // 重複時間チェック\r
+                               boolean inRange = false;\r
+                               {\r
+                                       ArrayList<String> starts = new ArrayList<String>();\r
+                                       ArrayList<String> ends = new ArrayList<String>();\r
+                                       CommonUtils.getStartEndList(starts, ends, r);\r
+                                       if ( h >= 0 ) {\r
+                                               // 新聞形式はピンポイント(マウスポインタのある位置の時刻)\r
+                                               for (int j=0; j<starts.size(); j++) {\r
+                                                       if ( clicked.compareTo(starts.get(j)) >= 0 && clicked.compareTo(ends.get(j)) <= 0 ) {\r
+                                                               inRange = true;\r
+                                                               break;\r
+                                                       }\r
+                                               }\r
+                                       }\r
+                                       else {\r
+                                               // リスト形式は幅がある(開始~終了までの間のいずれかの時刻)\r
+                                               for (int j=0; j<starts.size(); j++) {\r
+                                                       if ( CommonUtils.isOverlap(tvd.startDateTime, tvd.endDateTime, starts.get(j), ends.get(j), false) ) {\r
+                                                               inRange = true;\r
+                                                               break;\r
+                                                       }\r
+                                               }\r
+                                       }\r
+                               }\r
+                               if ( ! inRange) {\r
+                                       continue;\r
+                               }\r
+                               \r
+                               // 類似予約あり!\r
+                               overlapRsvList.add(new LikeReserveItem(recorder, r));\r
+                       }\r
+               }\r
+               \r
+               return;\r
+       }\r
+       \r
+       \r
+       /*******************************************************************************\r
+        * タイマー関連\r
+        ******************************************************************************/\r
+       \r
+               \r
+       /*******************************************************************************\r
+        * ここからおおむね初期化処理にかかわるメソッド群\r
+        ******************************************************************************/\r
+       \r
+       // レコーダから取得したエンコーダ情報で、登録済みレコーダ一覧を更新する\r
+       private void setEncoderInfo2RecorderList(HDDRecorder recorder) {\r
+               for (RecorderInfo ri : recInfoList ) {\r
+                       //if (rl.getRecorderEncoderList().size() == 0)\r
+                       {\r
+                               String mySelf = ri.getRecorderIPAddr()+":"+ri.getRecorderPortNo()+":"+ri.getRecorderId();\r
+                               String myMail = "MAIL"+":"+ri.getRecorderMacAddr()+":"+ri.getRecorderId();\r
+                               if (recorder.isMyself(mySelf) || recorder.isMyself(myMail)) {\r
+                                       ri.clearEncoders();\r
+                                       for (TextValueSet enc : recorder.getEncoderList()) {\r
+                                               ri.addEncoder(enc.getText());\r
+                                       }\r
+                                       break;\r
+                               }\r
+                       }\r
+               }\r
+       }\r
+       \r
+       /**\r
+        *  レコーダの予約情報をDLする\r
+        */\r
+       private boolean reLoadRdReserve(final String recId) {\r
+               //\r
+               StWinClear();\r
+               \r
+               new SwingBackgroundWorker(false) {\r
+                       \r
+                       @Override\r
+                       protected Object doWorks() throws Exception {\r
+                               \r
+                               TatCount tc = new TatCount();\r
+                               \r
+                               loadRdReserve(true, recId);\r
+                               \r
+                               // エンコーダ情報が更新されるかもしれないので、一覧のエンコーダ表示にも反映する\r
+                               recsetting.redrawRecorderEncoderEntry();\r
+                               \r
+                               // 各タブに反映する\r
+                               paper.updateReserveBorder(null);\r
+                               listed.updateReserveMark();\r
+                               reserved.redrawReservedList();\r
+                               recorded.redrawRecordedList();\r
+                               \r
+                               mwin.appendMessage(String.format("【予約一覧の取得処理が完了しました】 所要時間: %.2f秒",tc.end()));\r
+                               return null;\r
+                       }\r
+                       \r
+                       @Override\r
+                       protected void doFinally() {\r
+                               StWinSetVisible(false);\r
+                       }\r
+               }.execute();\r
+               \r
+               StWinSetLocationCenter(this);\r
+               StWinSetVisible(true);\r
+               \r
+               return true;\r
+       }\r
+       private void loadRdReserve(final boolean force, final String myself) {\r
+\r
+               //\r
+               new SwingBackgroundWorker(true) {\r
+                       \r
+                       @Override\r
+                       protected Object doWorks() throws Exception {\r
+                               \r
+                               HDDRecorderList recs;\r
+                               if ( myself != null ) {\r
+                                       recs = recorders.getMyself(myself);\r
+                               }\r
+                               else {\r
+                                       recs = recorders;\r
+                               }\r
+                               for ( HDDRecorder recorder : recs ) {\r
+                                       switch ( recorder.getType() ) {\r
+                                       case RECORDER:\r
+                                       case EPG:\r
+                                       case MAIL:\r
+                                       case NULL:\r
+                                       case TUNER:\r
+                                               loadRdReserveOnce(recorder, force);\r
+                                               break;\r
+                                       default:\r
+                                               break;\r
+                                       }\r
+                               }\r
+                               \r
+                               return null;\r
+                       }\r
+                       \r
+                       @Override\r
+                       protected void doFinally() {\r
+                       }\r
+               }.execute();\r
+       }\r
+       \r
+       private boolean loadRdReserveOnce(HDDRecorder recorder, boolean force) {\r
+               \r
+               mwin.appendMessage("【レコーダ情報取得】レコーダから情報を取得します: "+recorder.getRecorderId()+"("+recorder.getIPAddr()+":"+recorder.getPortNo()+")");\r
+               if ( recorder.isThereAdditionalDetails() && ! env.getForceGetRdReserveDetails() ) {\r
+                       mwin.appendMessage("<<<注意!>>>このレコーダでは予約詳細の個別取得を実行しないと正確な情報を得られない場合があります。");\r
+               }\r
+               \r
+               try {\r
+                       \r
+                       // 各種設定の取得\r
+                       if ( ! recorder.GetRdSettings(force) ) {\r
+                               // 取得に失敗\r
+                               mwin.appendError(recorder.getErrmsg()+" "+recorder.getIPAddr()+":"+recorder.getPortNo()+":"+recorder.getRecorderId());\r
+                               ringBeep();\r
+                               return false;\r
+                       }\r
+                       \r
+                       // 予約一覧の取得\r
+                       if ( ! recorder.GetRdReserve(force) ) {\r
+                               // 取得に失敗\r
+                               mwin.appendError(recorder.getErrmsg()+" "+recorder.getIPAddr()+":"+recorder.getPortNo()+":"+recorder.getRecorderId());\r
+                               ringBeep();\r
+                               return false;\r
+                       }\r
+                       \r
+                       // レコーダから取得したエンコーダ情報で、登録済みレコーダ一覧を更新する\r
+                       setEncoderInfo2RecorderList(recorder);\r
+                       if ( force ) {\r
+                               recInfoList.save();\r
+                       }\r
+                       \r
+                       // 予約詳細の取得\r
+                       if ( env.getNeverGetRdReserveDetails() ) {\r
+                               mwin.appendMessage("【!】予約詳細情報の取得はスキップされました");\r
+                       }\r
+                       else if ( force && recorder.isThereAdditionalDetails() ) {\r
+                               boolean getDetails = true;\r
+                               if ( ! env.getForceGetRdReserveDetails() ) {\r
+                                       int ret = JOptOptionPane.showConfirmDialog(null, "詳細情報を取得しますか?(時間がかかります)", "今回の選択を既定の動作とする", "確認", JOptionPane.YES_NO_OPTION);\r
+                                       getDetails = (ret == JOptOptionPane.YES_OPTION);\r
+                                       if ( JOptOptionPane.isSelected() ) {\r
+                                               // 今回の選択を既定の動作とする\r
+                                               env.setForceGetRdReserveDetails(getDetails);\r
+                                               env.setNeverGetRdReserveDetails( ! getDetails);\r
+                                               env.save();\r
+                                               setting.updateSelections();\r
+                                       }\r
+                               }\r
+                               if ( ! getDetails ) {\r
+                                       mwin.appendMessage("【!】予約詳細情報の取得はスキップされました");\r
+                               }\r
+                               else {\r
+                                       if ( ! recorder.GetRdReserveDetails()) {\r
+                                               // 取得に失敗\r
+                                               mwin.appendError(recorder.getErrmsg()+" "+recorder.getIPAddr()+":"+recorder.getPortNo()+":"+recorder.getRecorderId());\r
+                                               ringBeep();\r
+                                       }\r
+                               }\r
+                       }\r
+                       \r
+                       // レコーダの放送局名をWeb番組表の放送局名に置き換え\r
+                       {       \r
+                               HashMap<String,String> misCN = new HashMap<String,String>();\r
+                               for ( ReserveList r : recorder.getReserves() ) {\r
+                                       if ( r.getCh_name() == null ) {\r
+                                               misCN.put(r.getChannel(),recorder.getRecorderId());\r
+                                       }\r
+                               }\r
+                               if ( misCN.size() > 0 ) {\r
+                                       for ( String cn : misCN.keySet() ) {\r
+                                               String msg = "【警告(予約一覧)】 <"+misCN.get(cn)+"> \"レコーダの放送局名\"を\"Web番組表の放送局名\"に変換できません。CHコード設定に設定を追加してください:\"レコーダの放送局名\"="+cn;\r
+                                               mwin.appendMessage(msg);\r
+                                       }\r
+                                       ringBeep();\r
+                               }\r
+                       }\r
+                       \r
+                       // 録画結果一覧の取得\r
+                       if ( env.getSkipGetRdRecorded() ) {\r
+                               mwin.appendMessage("【!】録画結果一覧の取得はスキップされました");\r
+                       }\r
+                       else {\r
+                               if ( ! recorder.GetRdRecorded(force) ) {\r
+                                       // 取得に失敗\r
+                                       mwin.appendError(recorder.getErrmsg()+" "+recorder.getIPAddr()+":"+recorder.getPortNo()+":"+recorder.getRecorderId());\r
+                                       ringBeep();\r
+                                       return false;\r
+                               }\r
+                       }\r
+                       \r
+               }\r
+               catch (Exception e) {\r
+                       e.printStackTrace();\r
+                       mwin.appendError("【致命的エラー】予約一覧の取得で例外が発生 "+recorder.getIPAddr()+":"+recorder.getPortNo()+":"+recorder.getRecorderId());\r
+                       ringBeep();\r
+                       return false;\r
+               }\r
+               return true;\r
+       }\r
+\r
+       /**\r
+        * 録画結果一覧をDLする\r
+        */\r
+       private boolean reLoadRdRecorded(final String myself) {\r
+               //\r
+               StWinClear();\r
+               \r
+               new SwingBackgroundWorker(false) {\r
+                       \r
+                       @Override\r
+                       protected Object doWorks() throws Exception {\r
+                               \r
+                               TatCount tc = new TatCount();\r
+                       \r
+                               boolean succeeded = true;\r
+                               \r
+                               HDDRecorderList recs;\r
+                               if ( myself != null ) {\r
+                                       recs = recorders.getMyself(myself);\r
+                               }\r
+                               else {\r
+                                       recs = recorders;\r
+                               }\r
+                               for ( HDDRecorder recorder : recs ) {\r
+                                       switch ( recorder.getType() ) {\r
+                                       case RECORDER:\r
+                                       case EPG:\r
+                                       case MAIL:\r
+                                       case NULL:\r
+                                       case TUNER:\r
+                                               if ( ! recorder.GetRdSettings(true) ) {\r
+                                                       succeeded = false;\r
+                                               }\r
+                                               if ( ! recorder.GetRdRecorded(true) ) {\r
+                                                       succeeded = false;\r
+                                               }\r
+                                               break;\r
+                                       default:\r
+                                               break;\r
+                                       }\r
+                               }\r
+                               \r
+                               if ( succeeded ) {\r
+                                       reserved.redrawReservedList();\r
+                                       recorded.redrawRecordedList();\r
+                                       \r
+                                       mwin.appendMessage(String.format("【録画結果一覧の取得処理が完了しました】 所要時間: %.2f秒",tc.end()));\r
+                               }\r
+                               else {\r
+                                       ringBeep();\r
+                                       mwin.appendMessage(String.format("【録画結果一覧の取得処理に失敗しました】 所要時間: %.2f秒",tc.end()));\r
+                               }\r
+                               return null;\r
+                       }\r
+                       \r
+                       @Override\r
+                       protected void doFinally() {\r
+                               StWinSetVisible(false);\r
+                       }\r
+               }.execute();\r
+               \r
+               StWinSetLocationCenter(this);\r
+               StWinSetVisible(true);\r
+               \r
+               return true;\r
+       }\r
+\r
+       /**\r
+        * Web番組表をDLする\r
+        * <P>単体実行の場合はこちらを呼び出す\r
+        * <P>部品実行の場合はこちらを呼び出す:{@link #loadTVProgram(boolean, LoadFor)}\r
+        */\r
+       private boolean reLoadTVProgram(final LoadFor lf) {\r
+               //\r
+               StWinClear();\r
+               \r
+               new SwingBackgroundWorker(false) {\r
+                       \r
+                       @Override\r
+                       protected Object doWorks() throws Exception {\r
+                               \r
+                               TatCount tc = new TatCount();\r
+                               \r
+                               loadTVProgram(true, lf);\r
+                               \r
+                               // 新聞描画枠のリセット\r
+                               paper.clearPanel();\r
+                               paper.buildMainViewByDate();\r
+                               \r
+                               // サイドツリーの再構築\r
+                               paper.redrawTreeByPassed();\r
+                               \r
+                               // 再描画\r
+                               paper.reselectTree();\r
+                               listed.reselectTree();\r
+                               \r
+                               mwin.appendMessage(String.format("[Web番組表取得] 【完了しました】 所要時間: %.2f秒",tc.end()));\r
+                               return null;\r
+                       }\r
+                       \r
+                       @Override\r
+                       protected void doFinally() {\r
+                               StWinSetVisible(false);\r
+                       }\r
+               }.execute();\r
+               \r
+               StWinSetLocationCenter(this);\r
+               StWinSetVisible(true);\r
+               \r
+               return true;\r
+       }\r
+       \r
+       /**\r
+        * Web番組表をDLする\r
+        * <P>単体実行の場合はこちらを呼び出す:{@link #reLoadTVProgram(LoadFor)}\r
+        * <P>部品実行の場合はこちらを呼び出す\r
+        */\r
+       private void loadTVProgram(final boolean b, final LoadFor lf) {\r
+               \r
+               final String FUNCID = "[Web番組表取得] ";\r
+               final String ERRID = "[ERROR]"+FUNCID;\r
+               //\r
+               new SwingBackgroundWorker(true) {\r
+                       \r
+                       @Override\r
+                       protected Object doWorks() throws Exception {\r
+                               try {\r
+                                       String msg;\r
+                                       TVProgram tvp;\r
+                                       \r
+                                       tvp = tvprograms.getTvProgPlugin(null);\r
+                                       if ( tvp != null )\r
+                                       {\r
+                                               String sType = "地上波&BS番組表";\r
+                                               if (lf == LoadFor.ALL || lf == LoadFor.TERRA) {\r
+                                                       loadTVProgramOnce(tvp, sType, tvp.getSelectedArea(), false, b);\r
+                                               }\r
+                                               else {\r
+                                                       stwin.appendMessage(FUNCID+sType+"へのアクセスはスキップされました: "+tvp.getTVProgramId());\r
+                                               }\r
+                                       }\r
+                                       \r
+                                       tvp = tvprograms.getCsProgPlugin(null);\r
+                                       if ( tvp != null )\r
+                                       {\r
+                                               String sType = "CS番組表[プライマリ]";\r
+                                               if (lf == LoadFor.ALL || lf == LoadFor.CS || lf == LoadFor.CSo1) {\r
+                                                       loadTVProgramOnce(tvp, sType, tvp.getSelectedArea(), false, b);\r
+                                               }\r
+                                               else {\r
+                                                       stwin.appendMessage(FUNCID+sType+"へのアクセスはスキップされました: "+tvp.getTVProgramId());\r
+                                               }\r
+                                       }\r
+                                       \r
+                                       tvp = tvprograms.getCs2ProgPlugin(null);\r
+                                       if ( tvp != null )\r
+                                       {\r
+                                               String sType = "CS番組表[セカンダリ]";\r
+                                               if (lf == LoadFor.ALL || lf == LoadFor.CS || lf == LoadFor.CSo2) {\r
+                                                       loadTVProgramOnce(tvp, sType, tvp.getSelectedArea(), false, b);\r
+                                               }\r
+                                               else {\r
+                                                       stwin.appendMessage(FUNCID+sType+"へのアクセスはスキップされました: "+tvp.getTVProgramId());\r
+                                               }\r
+                                       }\r
+                                       \r
+                                       tvp = tvprograms.getSyobo();\r
+                                       if ( tvp != null ) {\r
+                                               String sType = "しょぼかる";\r
+                                               if ( (lf == LoadFor.ALL || lf == LoadFor.SYOBO) && enableWebAccess && env.getUseSyobocal()) {\r
+                                                       tvp.loadCenter(tvp.getSelectedCode(), b);       // しょぼかるには放送局リストを取得するイベントが他にないので\r
+                                                       loadTVProgramOnce(tvp, sType, null, true, b);\r
+                                               }\r
+                                               else {\r
+                                                       stwin.appendMessage(FUNCID+sType+"へのアクセスはスキップされました.");\r
+                                               }\r
+                                               \r
+                                               // しょぼかるの新番組マークを引き継ぐ\r
+                                               attachSyoboNew();\r
+                                       }\r
+                               \r
+                                       PickedProgram pickup = tvprograms.getPickup();\r
+                                       if ( tvp != null ) {\r
+                                               pickup.refresh();\r
+                                               //pickup.save();\r
+                                       }\r
+                                       \r
+                                       // 番組タイトルを整形する\r
+                                       fixTitle();\r
+                                       fixDetail();\r
+                                       \r
+                                       // 検索結果の再構築\r
+                                       stwin.appendMessage(FUNCID+"検索結果を生成します.");\r
+                                       mpList.clear(env.getDisableFazzySearch(), env.getDisableFazzySearchReverse());\r
+                                       mpList.build(tvprograms, trKeys.getTraceKeys(), srKeys.getSearchKeys());\r
+                                       \r
+                                       // 過去ローグ\r
+                                       if ( env.getUsePassedProgram() ) {\r
+                                               TatCount tc = new TatCount();\r
+                                               stwin.appendMessage(FUNCID+"過去ログを生成します.");\r
+                                               if ( tvprograms.getPassed().save(tvprograms.getIterator(), chsort.getClst(), env.getPrepPassedProgramCount()) ) {\r
+                                                       msg = String.format(FUNCID+"過去ログを生成しました [%.2f秒].",tc.end());\r
+                                                       StdAppendMessage(msg);\r
+                                               }\r
+                                               //PassedProgramList.getDateList(env.getPassedLogLimit());\r
+                                       }\r
+                                       else {\r
+                                               stwin.appendMessage(FUNCID+"過去ログは記録されません.");\r
+                                       }\r
+                               }\r
+                               catch (Exception e) {\r
+                                       e.printStackTrace();\r
+                                       mwin.appendError(ERRID+"番組情報の取得で例外が発生");\r
+                                       ringBeep();\r
+                                       return null;\r
+                               }\r
+                               return null;\r
+                       }\r
+                       \r
+                       @Override\r
+                       protected void doFinally() {\r
+                       }\r
+               }.execute();\r
+       }\r
+       \r
+       private void loadTVProgramOnce(TVProgram tvp, String sType, String aName, boolean loadonly, boolean force) {\r
+               \r
+               final String FUNCID = "[Web番組表取得] ";\r
+               final String ERRID = "[ERROR]"+FUNCID;\r
+               \r
+               // ログ\r
+               String msg = FUNCID+sType+"を取得します: "+tvp.getTVProgramId();\r
+               stwin.appendMessage(msg);\r
+               if (aName!=null) stwin.appendMessage(FUNCID+"+選択されているエリア="+aName);\r
+               \r
+               // 読み込み\r
+               //tvp.setProgressArea(stwin);\r
+               tvp.loadProgram(tvp.getSelectedCode(), force);\r
+               \r
+               if (loadonly) {\r
+                       return;\r
+               }\r
+               \r
+               // 延長警告\r
+               tvp.setExtension(null, null, false, extKeys.getSearchKeys());   // 最初の3引数は盲腸。ダミー\r
+               // NGワード\r
+               tvp.abon(env.getNgword());\r
+               // 抜けチェック\r
+               String errmsg = tvp.chkComplete(); \r
+               if (errmsg != null) {\r
+                       stwin.appendError(FUNCID+"取得した情報が不正です:"+errmsg);\r
+                       if (mainWindow!=null) mwin.appendMessage(msg);\r
+                       ringBeep();\r
+               }\r
+       }\r
+       \r
+       // しょぼかるの番組詳細を番組表に反映する\r
+       private void attachSyoboNew() {\r
+               TVProgram syobo = tvprograms.getSyobo();\r
+               if (syobo == null) {\r
+                       return;\r
+               }\r
+               \r
+               for ( TVProgram tvp : tvprograms ) {\r
+                       \r
+                       if ( tvp.getType() != ProgType.PROG ) {\r
+                               continue;\r
+                       }\r
+                       if ( ! (tvp.getSubtype() == ProgSubtype.TERRA || tvp.getSubtype() == ProgSubtype.CS || tvp.getSubtype() == ProgSubtype.CS2) ) {\r
+                               continue;\r
+                       }\r
+                       \r
+                       for ( ProgList tvpl : tvp.getCenters() ) {\r
+                               if ( ! tvpl.enabled) {\r
+                                       continue;\r
+                               }\r
+                               for ( ProgList svpl : syobo.getCenters() ) {\r
+                                       if ( ! tvpl.Center.equals(svpl.Center)) {\r
+                                               continue;\r
+                                       }\r
+                                       for ( ProgDateList tvc : tvpl.pdate ) {\r
+                                               \r
+                                               ProgDateList mSvc = null;\r
+                                               for ( ProgDateList svc : svpl.pdate ) {\r
+                                                       if (tvc.Date.equals(svc.Date) ) {\r
+                                                               mSvc = svc;\r
+                                                               break;\r
+                                                       }\r
+                                               }\r
+                                               if (mSvc == null) {\r
+                                                       // しょぼかる側に該当する日付自体ないので全部フラグを立てっぱなしでいい\r
+                                                       for ( ProgDetailList tvd : tvc.pdetail ) {\r
+                                                               if ( tvd.isEqualsGenre(ProgGenre.ANIME, null) ) {\r
+                                                                       tvd.addOption(ProgOption.NOSYOBO);\r
+                                                               }\r
+                                                       }\r
+                                               }\r
+                                               else {\r
+                                                       // しょぼかる側に該当する日付があるのでマッチング。アニメと映画と音楽\r
+                                                       for ( ProgDetailList tvd : tvc.pdetail ) {\r
+                                                               \r
+                                                               // アニメはいったんフラグを立てる\r
+                                                               if ( tvd.isEqualsGenre(ProgGenre.ANIME, null) ) {\r
+                                                                       tvd.addOption(ProgOption.NOSYOBO);\r
+                                                               }\r
+                                                               \r
+                                                               boolean isFind = false;\r
+                                                               for ( ProgDetailList svd : mSvc.pdetail ) {\r
+                                                                       if ( tvd.start.equals(svd.start) ) {\r
+                                                                               \r
+                                                                               // 番組ID\r
+                                                                               {\r
+                                                                                       //svd.progid = tvd.progid;\r
+                                                                                       svd.setContentIdStr();\r
+                                                                               }\r
+                                                                               \r
+                                                                               boolean isAnime = tvd.isEqualsGenre(ProgGenre.ANIME, null);\r
+                                                                               if ( ! isAnime && ! tvd.isEqualsGenre(ProgGenre.MOVIE, null) && ! tvd.isEqualsGenre(ProgGenre.MUSIC, null) ) {\r
+                                                                                       break;\r
+                                                                               }\r
+                                                                               \r
+                                                                               // みつけた\r
+                                                                               isFind = true;\r
+                                                                               \r
+                                                                               // しょぼかるとWeb番組表の両方に存在する\r
+                                                                               svd.nosyobo = true;\r
+                       \r
+                                                                               // 各種フラグ\r
+                                                                               {\r
+                                                                                       boolean isAttached = false;\r
+                                                                                       \r
+                                                                                       // 新番組フラグ\r
+                                                                                       if ( svd.flag == ProgFlags.NEW && tvd.flag != ProgFlags.NEW ) {\r
+                                                                                               tvd.flag = ProgFlags.NEW;\r
+                                                                                               isAttached = true;\r
+                                                                                       }\r
+                                                                                       \r
+                                                                                       // 最終回フラグ\r
+                                                                                       if ( svd.flag == ProgFlags.LAST && tvd.flag != ProgFlags.LAST ) {\r
+                                                                                               tvd.flag = ProgFlags.LAST;\r
+                                                                                               isAttached = true;\r
+                                                                                       }\r
+                                                                                       \r
+                                                                                       // ジャンル\r
+                                                                                       if ( tvd.isEqualsGenre(ProgGenre.MOVIE, null) && ! tvd.isEqualsGenre(ProgGenre.MOVIE, ProgSubgenre.MOVIE_ANIME) ) {\r
+                                                                                               if ( tvd.genrelist == null ) {\r
+                                                                                                       tvd.genrelist = new ArrayList<ProgGenre>();\r
+                                                                                                       tvd.genrelist.add(tvd.genre);\r
+                                                                                                       tvd.genrelist.add(ProgGenre.MOVIE);\r
+                                                                                                       tvd.subgenrelist = new ArrayList<ProgSubgenre>();\r
+                                                                                                       tvd.subgenrelist.add(tvd.subgenre);\r
+                                                                                                       tvd.subgenrelist.add(ProgSubgenre.MOVIE_ANIME);\r
+                                                                                               }\r
+                                                                                               else {\r
+                                                                                                       tvd.genrelist.add(ProgGenre.MOVIE);\r
+                                                                                                       tvd.subgenrelist.add(ProgSubgenre.MOVIE_ANIME);\r
+                                                                                               }\r
+                                                                                               isAttached = true;\r
+                                                                                       }\r
+                                                                                       \r
+                                                                                       // その他のフラグ\r
+                                                                                       for ( ProgOption sopt : svd.getOption() ) {\r
+                                                                                               if ( tvd.addOption(sopt) && isAttached == false ) {\r
+                                                                                                       isAttached = true;\r
+                                                                                               }\r
+                                                                                       }\r
+                                                                                       \r
+                                                                                       // ログ\r
+                                                                                       if (isAttached && env.getDebug()) {\r
+                                                                                               StdAppendMessage("しょぼかるのフラグを引き継ぎました: ("+tvpl.Center+") "+tvd.title);\r
+                                                                                       }\r
+                                                                               }\r
+                                                                               \r
+                                                                               // 番組詳細\r
+                                                                               if ( tvd.detail.length() < svd.detail.length() ) {\r
+                                                                                       tvd.detail = svd.detail;\r
+                                                                               }\r
+                                                                               else {\r
+                                                                                       int idx = svd.detail.indexOf("<!");\r
+                                                                                       if (idx != -1) {\r
+                                                                                               tvd.detail += svd.detail.substring(idx);\r
+                                                                                       }\r
+                                                                               }\r
+                                                                               \r
+                                                                               // 「しょぼかるにのみ存在」フラグの上げ下げ(これはアニメ限定)\r
+                                                                               if ( isAnime ) {\r
+                                                                                       if ( isFind ) {\r
+                                                                                               tvd.removeOption(ProgOption.NOSYOBO);   // NOSYOBOって…\r
+                                                                                       }\r
+                                                                                       else {\r
+                                                                                               //tvd.addOption(ProgOption.NOSYOBO);\r
+                                                                                       }\r
+                                                                               }\r
+                                                                               \r
+                                                                               break;\r
+                                                                       }\r
+                                                               }\r
+                                                       }\r
+                                               }\r
+                                       }\r
+                                       break;\r
+                               }\r
+                       }\r
+               }\r
+       }\r
+       \r
+       // 番組タイトルを整形する\r
+       private void fixTitle() {\r
+               //\r
+               if ( ! env.getFixTitle()) {\r
+                       return;\r
+               }\r
+               //\r
+               for ( TVProgram tvp : tvprograms ) {\r
+                       //if ( ! (tvp.getType() == ProgType.PROG && tvp.getSubtype() == ProgSubtype.TERRA) ) {\r
+                       if ( tvp.getType() != ProgType.PROG ) {\r
+                               continue;\r
+                       }\r
+                       //\r
+                       for ( ProgList pl : tvp.getCenters() ) {\r
+                               if ( ! pl.enabled ) {\r
+                                       continue;\r
+                               }\r
+\r
+                               for ( ProgDateList pcl : pl.pdate ) {\r
+                                       //\r
+                                       for ( ProgDetailList tvd : pcl.pdetail ) {\r
+                                               if ( tvd.isEqualsGenre(ProgGenre.ANIME, null) ) {\r
+                                                       if ( pl.Center.startsWith("NHK") || pl.Center.startsWith("NHK") ) {\r
+                                                               // NHK系で先頭が「アニメ 」ではじまるものから「アニメ 」を削除する\r
+                                                               tvd.title = tvd.title.replaceFirst("^アニメ[  ・]+","");\r
+                                                               tvd.titlePop = TraceProgram.replacePop(tvd.title);\r
+                                                               tvd.SearchStrKeys = TraceProgram.splitKeys(tvd.titlePop);\r
+                                                       }\r
+                                                       if ( (tvd.title.contains("劇場版") || tvd.detail.contains("映画")) && ! tvd.isEqualsGenre(ProgGenre.MOVIE, ProgSubgenre.MOVIE_ANIME) ) {\r
+                                                               // ジャンル=アニメだがタイトルに「劇場版」が含まれるならジャンル=映画(アニメ映画)を追加する\r
+                                                               if ( tvd.genrelist == null ) {\r
+                                                                       tvd.genrelist = new ArrayList<ProgGenre>();\r
+                                                                       tvd.genrelist.add(tvd.genre);\r
+                                                                       tvd.genrelist.add(ProgGenre.MOVIE);\r
+                                                                       tvd.subgenrelist = new ArrayList<ProgSubgenre>();\r
+                                                                       tvd.subgenrelist.add(tvd.subgenre);\r
+                                                                       tvd.subgenrelist.add(ProgSubgenre.MOVIE_ANIME);\r
+                                                               }\r
+                                                               else {\r
+                                                                       tvd.genrelist.add(ProgGenre.MOVIE);\r
+                                                                       tvd.subgenrelist.add(ProgSubgenre.MOVIE_ANIME);\r
+                                                               }\r
+                                                       }\r
+                                               }\r
+                                               else if ( tvd.isEqualsGenre(ProgGenre.MOVIE, ProgSubgenre.MOVIE_ANIME) && tvd.subgenre != ProgSubgenre.MOVIE_ANIME ) {\r
+                                                       // ジャンル=映画でサブジャンルが複数ありアニメが優先されてないものはアニメを優先する\r
+                                                       tvd.subgenre = ProgSubgenre.MOVIE_ANIME;\r
+                                               }\r
+                                               \r
+                                               // サブタイトルを番組追跡の対象から外す\r
+                                               if ( env.getTraceOnlyTitle() && tvd.title != tvd.splitted_title ) {\r
+                                                       tvd.SearchStrKeys = TraceProgram.splitKeys(TraceProgram.replacePop(tvd.splitted_title));        // 番組追跡の検索用インデックスは、サブタイトルを削除したもので置き換える\r
+                                               }\r
+                                       }\r
+                               }\r
+                       }\r
+               }\r
+       }\r
+       \r
+       /**\r
+        * {@link ProgDetailList} の情報を整形する\r
+        */\r
+       private void fixDetail() {\r
+               for ( TVProgram tvp : tvprograms ) {\r
+                       for ( ProgList pl : tvp.getCenters() ) {\r
+                               if ( ! pl.enabled ) {\r
+                                       continue;\r
+                               }\r
+                               for ( ProgDateList pcl : pl.pdate ) {\r
+                                       for ( ProgDetailList tvd : pcl.pdetail ) {\r
+                                               if ( tvd.start == null || tvd.start.length() == 0 ) {\r
+                                                       continue;\r
+                                               }\r
+                                               \r
+                                               fixDetailSub(tvp, pl, tvd);\r
+                                       }\r
+                               }\r
+                       }\r
+               }\r
+       }\r
+       \r
+       private void fixDetailSub(TVProgram tvp, ProgList pl, ProgDetailList tvd) {\r
+               tvd.type = tvp.getType();\r
+               tvd.subtype = tvp.getSubtype();\r
+               tvd.center = pl.Center;\r
+               \r
+               tvd.recmin = CommonUtils.getRecMinVal(tvd.startDateTime, tvd.endDateTime);\r
+               \r
+               tvd.extension_mark = markchar.getExtensionMark(tvd);\r
+               tvd.prefix_mark = markchar.getOptionMark(tvd);\r
+               tvd.newlast_mark = markchar.getNewLastMark(tvd);\r
+               tvd.postfix_mark = markchar.getPostfixMark(tvd);\r
+               \r
+               tvd.dontoverlapdown = (tvd.center.startsWith("NHK") || tvd.center.startsWith("NHK"));\r
+       }\r
+       \r
+       /**\r
+        * <P>過去ログから検索キーワードにマッチする情報を取得する\r
+        * <P>全部検索がヒットした結果がかえるのだから {@link ProgDetailList} ではなく {@link MarkedProgramList} を使うべきなのだが…\r
+        */\r
+       private boolean searchPassedProgram(final SearchKey sKey, final String target) {\r
+               \r
+               Matcher ma = Pattern.compile("^(\\d\\d\\d\\d/\\d\\d/\\d\\d)-(\\d\\d\\d\\d/\\d\\d/\\d\\d)$").matcher(target);\r
+               if ( ! ma.find() ) {\r
+                       return false;\r
+               }\r
+               \r
+               final GregorianCalendar s = CommonUtils.getCalendar(ma.group(1));\r
+               final GregorianCalendar e = CommonUtils.getCalendar(ma.group(2));\r
+               final long dDays = (e.getTimeInMillis() - s.getTimeInMillis())/86400000 + 1;\r
+\r
+               final ArrayList<ProgDetailList> srchpdl = tvprograms.getSearched().getResultBuffer(sKey.getLabel()) ;\r
+\r
+               stwin.clear();\r
+               \r
+               // 検索実行(時間がかかるので状況表示する)\r
+               new SwingBackgroundWorker(false) {\r
+                       \r
+                       @Override\r
+                       protected Object doWorks() throws Exception {\r
+                               \r
+                               TatCount tc = new TatCount();\r
+                               \r
+                               // 検索中\r
+                               int resultCnt = 0;\r
+                               for (int cnt=1; cnt<=dDays; cnt++) {\r
+                                       \r
+                                       String passdt = CommonUtils.getDate(e);\r
+                                       stwin.appendMessage(String.format("[過去ログ検索] 検索中:(%d/%d) %s", cnt, dDays, passdt));\r
+                                       \r
+                                       PassedProgram tvp = new PassedProgram();\r
+                                       if ( tvp.loadAllCenters(passdt) ) {\r
+                                               for ( ProgList pl : tvp.getCenters() ) {\r
+                                                       if ( ! pl.enabled ) {\r
+                                                               continue;\r
+                                                       }\r
+                                                       \r
+                                                       for ( ProgDateList pcl : pl.pdate ) {\r
+                                                               for ( ProgDetailList tvd : pcl.pdetail ) {\r
+                                                                       if ( tvd.start == null || tvd.start.length() == 0 ) {\r
+                                                                               continue;\r
+                                                                       }\r
+                                                                       \r
+                                                                       if ( SearchProgram.isMatchKeyword(sKey, pl.Center, tvd) ) {\r
+                                                                               tvd.dynKey = sKey;\r
+                                                                               tvd.dynMatched = SearchProgram.getMatchedString();\r
+                                                                               fixDetailSub(tvp, pl, tvd);\r
+                                                                               srchpdl.add(tvd);\r
+                                                                               if ( ++resultCnt >= env.getSearchResultMax() ) {\r
+                                                                                       mwin.appendMessage(String.format("[過去ログ検索] 検索件数の上限に到達しました。所要時間: %.2f秒",tc.end()));\r
+                                                                                       return null;\r
+                                                                               }\r
+                                                                       }\r
+                                                               }\r
+                                                       }\r
+                                               }\r
+                                       }\r
+                                       \r
+                                       e.add(Calendar.DATE,-1);\r
+                               }\r
+\r
+                               mwin.appendMessage(String.format("[過去ログ検索] 検索完了。所要時間: %.2f秒",tc.end()));\r
+                               return null;\r
+                       }\r
+                       \r
+                       @Override\r
+                       protected void doFinally() {\r
+                               StWinSetVisible(false);\r
+                       }\r
+               }.execute();\r
+               \r
+               StWinSetLocationCenter(this);\r
+               StWinSetVisible(true);\r
+\r
+               return true;\r
+       }\r
+\r
+       // システムトレイ関係\r
+       private void getTrayIcon() {\r
+               if ( trayicon != null ) {\r
+                       return;\r
+               }\r
+               \r
+               try {\r
+                       Image image = ImageIO.read(new File(ICONFILE_SYSTRAY));\r
+                       trayicon = new TrayIcon(image,"Tainavi");\r
+                       \r
+                       final Viewer thisClass = this;\r
+                       \r
+                       // メニューの追加\r
+                       PopupMenu popup = new PopupMenu();\r
+                       {\r
+                               MenuItem item = new MenuItem("開く");\r
+                               item.addActionListener(new ActionListener() {\r
+                                       @Override\r
+                                       public void actionPerformed(ActionEvent e) {\r
+                                               thisClass.setVisible(true);\r
+                                               thisClass.setState(Frame.NORMAL);\r
+                                       }\r
+                               });\r
+                               popup.add(item);\r
+                       }\r
+                       {\r
+                               MenuItem item = new MenuItem("終了する");\r
+                               item.addActionListener(new ActionListener() {\r
+                                       @Override\r
+                                       public void actionPerformed(ActionEvent e) {\r
+                                               ExitOnClose();\r
+                                               System.exit(0);\r
+                                       }\r
+                               });\r
+                               popup.add(item);\r
+                       }\r
+                       trayicon.setPopupMenu(popup);\r
+                       \r
+                       // 左クリックで復帰\r
+                       trayicon.addMouseListener(new MouseAdapter() {\r
+                               //\r
+                               public void mouseClicked(MouseEvent e) {\r
+                                       if (e.getButton() == MouseEvent.BUTTON1) {\r
+                                               thisClass.setVisible(true);\r
+                                               thisClass.setState(Frame.NORMAL);\r
+                                       }\r
+                               }\r
+                       });\r
+               \r
+               } catch (IOException e) {\r
+                       StdAppendError("アイコンファイルが読み込めませんでした: "+ICONFILE_SYSTRAY);\r
+                       e.printStackTrace();\r
+               }\r
+       }\r
+       private void setTrayIconVisible(boolean b) {\r
+               \r
+               if ( ! SystemTray.isSupported() || trayicon == null ) {\r
+                       return;\r
+               }\r
+               \r
+               try {\r
+                       if ( b ) {\r
+                               // システムトレイに追加\r
+                               SystemTray.getSystemTray().remove(trayicon);\r
+                               SystemTray.getSystemTray().add(trayicon);\r
+                       }\r
+                       else {\r
+                               // システムトレイから削除\r
+                               SystemTray.getSystemTray().remove(trayicon);\r
+                       }\r
+               } catch (AWTException e) {\r
+                       e.printStackTrace();\r
+               }\r
+       }\r
+       private void HideToTray() {\r
+               if ( SystemTray.isSupported() && trayicon != null && (env.getShowSysTray() && env.getHideToTray()) ) {\r
+                       this.setVisible(false);\r
+               }\r
+       }\r
+       private void setXButtonAction(boolean b) {\r
+               if ( b ) {\r
+                       this.setDefaultCloseOperation(JFrame.ICONIFIED);\r
+               }\r
+               else {\r
+                       this.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);\r
+               }\r
+       }\r
+       \r
+       // コマンドライン引数の処理\r
+       private void procArgs(String[] args) {\r
+               int flag = 0;\r
+               for (String arg : args) {\r
+                       switch (flag) {\r
+                       case 0:\r
+                               if (arg.compareTo("-L") == 0) {\r
+                                       // -l : ロギング\r
+                                       //logging = false;\r
+                               }\r
+                               else if (arg.compareTo("-L") == 0) {\r
+                                       // -L : ロギング不可\r
+                                       logging = false;\r
+                               }\r
+                               else if (arg.compareTo("-w") == 0) {\r
+                                       // -w : レコーダ起動\r
+                                       runRecWakeup = true;\r
+                               }\r
+                               else if (arg.compareTo("-nowebaccess") == 0) {\r
+                                       // -nowebaccess : 起動時のWeb番組表へのアクセス無効\r
+                                       enableWebAccess = false;\r
+                               }\r
+                               else if (arg.compareTo("-proxy") == 0) {\r
+                                       // -proxy : Web番組表へのアクセスにProxy経由を強制する\r
+                                       flag = 1;\r
+                               }\r
+                               else if (arg.compareTo("-loadrec") == 0) {\r
+                                       // -loadrec : 起動時にレコーダにアクセスする\r
+                                       runRecLoad = true;\r
+                               }\r
+                               else if (arg.compareTo("-onlyLoadProgram") == 0) {\r
+                                       // -onlyLoadProgram : 番組表の取得だけ行う\r
+                                       onlyLoadProgram = true;\r
+                               }\r
+                               break;\r
+                       case 1:\r
+                               String[] dat = arg.split(":");\r
+                               if (dat.length == 1 ) {\r
+                                       pxaddr = dat[0];\r
+                                       pxport = "8080";\r
+                               } if (dat.length >= 2 ) {\r
+                                       pxaddr = dat[0];\r
+                                       pxport = dat[1];\r
+                               }\r
+                               flag = 0;\r
+                               break;\r
+                       }\r
+               }\r
+       }\r
+       \r
+       // メインの環境設定ファイルを読みだす\r
+       private void loadEnvfile() {\r
+               StdAppendMessage("【環境設定】環境設定ファイルを読み込みます.");\r
+               env.load();\r
+       }\r
+       \r
+       // 引き続きその他の環境設定ファイルも読みだす\r
+       private void procEnvs() {\r
+               \r
+               StdAppendMessage("【環境設定】環境設定ファイル類を読み込みます.");\r
+\r
+               // 各種設定\r
+               env.makeEnvDir();\r
+               \r
+               // レコーダ一覧\r
+               recInfoList.load();\r
+\r
+               // Proxyサーバ\r
+               if (pxaddr != null) {\r
+                       env.setUseProxy(true);\r
+                       env.setProxyAddr(pxaddr);\r
+                       env.setProxyPort(pxport);\r
+               }\r
+               \r
+               // Cookieの処理を入れようとしたけど無理だった\r
+               /*\r
+               {\r
+                       CookieManager manager = new CookieManager();\r
+                       manager.setCookiePolicy(CookiePolicy.ACCEPT_ALL);\r
+                       CookieHandler.setDefault(manager);\r
+               }\r
+               */\r
+               \r
+               // ジャンル別背景色\r
+               pColors.load();\r
+               \r
+               // 深夜の帯予約の補正(一日前にずらす)\r
+               // 可能なら番組表を8日分取得する\r
+               // 【WIN】ファイルオープンにrundll32を使用する\r
+               CommonUtils.setAdjLateNight(env.getAdjLateNight());\r
+               CommonUtils.setExpandTo8(env.getExpandTo8());\r
+               CommonUtils.setUseRundll32(env.getUseRundll32());\r
+               CommonUtils.setDisplayPassedReserve(env.getDisplayPassedReserve());\r
+               CommonUtils.setDebug(env.getDebug());\r
+               \r
+               SwingBackgroundWorker.setDebug(env.getDebug());\r
+               \r
+               // クリップボードアイテム\r
+               cbitems.load();\r
+               \r
+               // サイズ・位置情報取得\r
+               bounds.setLoaded(bounds.load());\r
+               \r
+               // 番組追跡キーワード取得\r
+               trKeys.load();\r
+               \r
+               // 検索キーワード取得\r
+               srKeys.load();\r
+               \r
+               // 検索キーワードグループ取得\r
+               srGrps.load();\r
+               \r
+               // 延長警告源設定取得\r
+               extKeys.load();\r
+               \r
+               // デフォルトAV設定取得\r
+               avs.load();\r
+               chavs.load();\r
+\r
+               // スポーツ延長警告のデフォルト設定のコードはもういらないので削除(3.15.4β) \r
+               \r
+               // 簡易描画はもういらないので削除\r
+               \r
+               // ChannelConvert\r
+               chconv.load();\r
+       }\r
+       \r
+       // 二重起動チェック\r
+       private void chkDualBoot() {\r
+               if ( ! env.getOnlyOneInstance() ) {\r
+                       return;\r
+               }\r
+               \r
+               if ( ! CommonUtils.getLock() ) {\r
+                       // 既にロックされている\r
+                       ringBeep();\r
+                       System.exit(1);\r
+               }\r
+               \r
+               Runtime.getRuntime().addShutdownHook(new Thread() {\r
+                       public void run() {\r
+                               // 鯛ナビ終了時にロックを解除する\r
+                               CommonUtils.getUnlock();\r
+                       }\r
+               });\r
+       }\r
+       \r
+       // アップデートの有無チェック\r
+       private void chkVerUp() {\r
+               if ( ! enableWebAccess || onlyLoadProgram ) {\r
+                       stwin.appendError("【オンラインアップデート】オンラインアップデートは無効です");\r
+                       return;\r
+               }\r
+               \r
+               VWUpdate vu = new VWUpdate(stwin);\r
+               if ( ! vu.isExpired(env.getUpdateMethod()) ) {\r
+                       // メッセージはVWUpdate内で出力されます\r
+                       return;\r
+               }\r
+               if ( doVerUp(vu) ) {\r
+                       System.exit(0);\r
+               }\r
+       }\r
+       \r
+       private boolean doVerUp(VWUpdate vu) {\r
+               UpdateResult res = vu.checkUpdate(VersionInfo.getVersion());\r
+               switch ( res ) {\r
+               case DONE:\r
+                       // 成功\r
+                       // 履歴は更新しない(連続アップデートがあるかも知れないので)\r
+                       LogViewer lv = new LogViewer(HISTORY_FILE);\r
+                       lv.setModal(true);\r
+                       lv.setCaretPosition(0);\r
+                       lv.setVisible(true);\r
+                       return true;\r
+               case PASS:\r
+                       // キャンセル\r
+                       // 履歴は更新しない(次回に持ち越し)\r
+                       break;\r
+               case NOUPDATE:\r
+                       // アップデートなし\r
+                       vu.updateHistory();\r
+                       break;\r
+               default:\r
+                       // 失敗\r
+                       // 履歴は更新しない(次回再挑戦)\r
+                       break;\r
+               }\r
+               return false;\r
+       }\r
+       \r
+       /**\r
+        *  レコーダプラグインをすべて読み込みます。\r
+        */\r
+       private boolean loadRecPlugins() {\r
+               \r
+               stwin.appendMessage("【レコーダプラグイン】プラグインを読み込みます.");\r
+               \r
+               boolean isMailPluginEnabled = false;\r
+               try {\r
+                       Class.forName("javax.mail.Session");\r
+                       isMailPluginEnabled = true;\r
+               }\r
+               catch ( Exception e ) {\r
+                       System.err.println("【レコーダプラグイン】メール系プラグイン用の外部ライブラリがみつかりません: "+e.toString());\r
+               }\r
+\r
+               boolean isCalendarPluginEnabled = false;\r
+               try {\r
+                       Class.forName("com.google.gdata.client.calendar.CalendarService");\r
+                       isCalendarPluginEnabled = true;\r
+               }\r
+               catch ( Exception e ) {\r
+                       System.err.println("【レコーダプラグイン】カレンダー系プラグイン用の外部ライブラリがみつかりません: "+e.toString());\r
+               }\r
+               \r
+               //\r
+               ArrayList<String> recIda = new ArrayList<String>();\r
+               for ( File f : new File(CommonUtils.joinPath(new String[]{"bin","tainavi"})).listFiles() ) {\r
+                       Matcher ma = Pattern.compile("^(PlugIn_Rec[^$]+)[^$]*\\.class$").matcher(f.getName());\r
+                       if ( ma.find() ) {\r
+                               if ( ! isMailPluginEnabled && f.getName().toLowerCase().contains("mail") ) {\r
+                                       System.out.println("【レコーダプラグイン】メール系プラグインは無効です: "+f.getName());\r
+                                       continue;\r
+                               }\r
+                               if ( ! isCalendarPluginEnabled && f.getName().toLowerCase().contains("calendar") ) {\r
+                                       System.out.println("【レコーダプラグイン】カレンダー系プラグインは無効です: "+f.getName());\r
+                                       continue;\r
+                               }\r
+                               \r
+                               recIda.add(ma.group(1));\r
+                       }\r
+               }\r
+               String[] recIdd = recIda.toArray(new String[0]);\r
+               Arrays.sort(recIdd);\r
+               \r
+               // servicesに追記\r
+               StringBuilder sb = new StringBuilder();\r
+               for ( String recId : recIdd ) {\r
+                       sb.append("tainavi.");\r
+                       sb.append(recId);\r
+                       sb.append("\n");\r
+               }\r
+               if ( ! CommonUtils.write2file(CommonUtils.joinPath(new String[] {"bin","META-INF","services","tainavi.HDDRecorder"}), sb.toString()) ) {\r
+                       stwin.appendError("【レコーダプラグイン】プラグインの読み込みに失敗しました: ");\r
+                       return false;\r
+               }\r
+\r
+               // ここで例外が起きてもトラップできない、スレッドが落ちる\r
+               ServiceLoader<HDDRecorder> r = ServiceLoader.load(HDDRecorder.class);\r
+               \r
+               recPlugins.clear();\r
+               for ( HDDRecorder recorder : r ) {\r
+                       if (env.getDebug()) StdAppendMessage("+追加します: "+recorder.getRecorderId());\r
+                       recPlugins.add(recorder.clone());\r
+                       StdAppendMessage("+追加しました: "+recorder.getRecorderId());\r
+               }\r
+               \r
+               return true;\r
+       }\r
+       \r
+       /**\r
+        * レコーダ設定をもとにレコーダプラグインから実レコーダのインスタンスを生成します。\r
+        */\r
+       private void initRecPluginAll() {\r
+               //\r
+               recorders.clear();\r
+               for ( RecorderInfo ri : recInfoList ) {\r
+                       ArrayList<HDDRecorder> rl = recPlugins.get(ri.getRecorderId());\r
+                       if ( rl.size() == 0 ) {\r
+                               stwin.appendError("【レコーダプラグイン】プラグインがみつかりません: "+ri.getRecorderId()+"("+ri.getRecorderIPAddr()+":"+ri.getRecorderPortNo()+")");\r
+                       }\r
+                       else { \r
+                               stwin.appendMessage("【レコーダプラグイン】プラグインを初期化します: "+ri.getRecorderId()+"("+ri.getRecorderIPAddr()+":"+ri.getRecorderPortNo()+")");\r
+                               for ( HDDRecorder rPlugin : rl ) {\r
+                                       initRecPlugin(rPlugin, ri);\r
+                               }\r
+                       }\r
+               }\r
+       }\r
+       protected HDDRecorder initRecPlugin(HDDRecorder rPlugin, RecorderInfo ri) {\r
+               HDDRecorder rec = rPlugin.clone();\r
+               recorders.add(rec);\r
+               \r
+               rec.getChCode().load(true);     // true : ログ出力あり\r
+               setSettingRecPluginBase(rec, ri);\r
+               setSettingRecPluginExt(rec,env);\r
+               rec.setProgressArea(stwin);\r
+               return rec;\r
+       }\r
+       protected void setSettingRecPluginBase(HDDRecorder to, RecorderInfo from) {\r
+               to.setIPAddr(from.getRecorderIPAddr());\r
+               to.setPortNo(from.getRecorderPortNo());\r
+               to.setUser(from.getRecorderUser());\r
+               to.setPasswd(from.getRecorderPasswd());\r
+               to.setMacAddr(from.getRecorderMacAddr());\r
+               to.setBroadcast(from.getRecorderBroadcast());\r
+               to.setUseCalendar(from.getUseCalendar());\r
+               to.setUseChChange(from.getUseChChange());\r
+               to.setRecordedCheckScope(from.getRecordedCheckScope());\r
+               to.setTunerNum(from.getTunerNum());\r
+               to.setColor(from.getRecorderColor());\r
+       }\r
+       protected void setSettingRecPluginExt(HDDRecorder recorder, Env nEnv) {\r
+               recorder.setUserAgent(nEnv.getUserAgent());\r
+               recorder.setDebug(nEnv.getDebug());\r
+               recorder.setAdjNotRep(nEnv.getAdjoiningNotRepetition());\r
+               recorder.setRecordedSaveScope(nEnv.getRecordedSaveScope());\r
+       }\r
+       \r
+       //\r
+       protected void doRecWakeup() {\r
+               for ( HDDRecorder rec : recorders ) {\r
+                       if ( ! rec.getMacAddr().equals("") && ! rec.getBroadcast().equals("") ) {\r
+                               rec.wakeup();\r
+                       }\r
+               }\r
+       }\r
+       \r
+       /**\r
+        * 一時間は再実行させないんだ\r
+        */\r
+       private boolean isOLPExpired(int expire) {\r
+               String fname = "env"+File.separator+"olp.history";\r
+               if ( ! new File(fname).exists() || ! new File(fname).canWrite() ) {\r
+                       stwin.appendError("【警告】実行履歴ファイルがないから実行させないよ!");\r
+                       ringBeep();\r
+                       return false;\r
+               }\r
+               String dat = CommonUtils.read4file(fname, true);\r
+               if ( dat == null ) {\r
+                       stwin.appendError("【警告】実行履歴を取得できなかったから実行させないよ!");\r
+                       ringBeep();\r
+                       return false;\r
+               }\r
+               GregorianCalendar ca = null;\r
+               dat = EncryptPassword.dec(b64.dec(dat));\r
+               if ( dat != null ) {\r
+                       ca = CommonUtils.getCalendar(dat);\r
+               }\r
+               if ( ca == null ) {\r
+                       stwin.appendError("【警告】実行履歴の内容が不正だったから実行させないよ! "+dat);\r
+                       ringBeep();\r
+                       return false;\r
+               }\r
+               if ( CommonUtils.getCompareDateTime(ca, CommonUtils.getCalendar(-expire*3600)) >= 0 ) {\r
+                       ca.add(Calendar.HOUR,expire);\r
+                       stwin.appendError("【警告】"+expire+"時間以内の再実行は許さないよ!"+CommonUtils.getDateTime(ca)+"まで待って!");\r
+                       ringBeep();\r
+                       return false;\r
+               }\r
+               if ( ! CommonUtils.write2file(fname, b64.enc(EncryptPassword.enc(CommonUtils.getDateTime(0)))) ) {\r
+                       stwin.appendError("【警告】実行履歴を保存できなかったから実行させないよ!");\r
+                       ringBeep();\r
+                       return false;\r
+               }\r
+               \r
+               return true;\r
+       }\r
+       \r
+       /**\r
+        *  Web番組表プラグインをすべて読み込みます。\r
+        */\r
+       private boolean loadProgPlugins() {\r
+       \r
+               final String FUNCID = "[Web番組表プラグイン組込] ";\r
+               final String ERRID = "[ERROR]"+FUNCID;\r
+               \r
+               // Web番組表プラグインの処理\r
+               stwin.appendMessage(FUNCID+"プラグインを読み込みます.");\r
+               \r
+               // Web番組表共通設定\r
+               setSettingProgPluginCommon(env);\r
+               \r
+               /*\r
+                * 重要 - ここから\r
+                */\r
+               \r
+               // TVProgramListのインスタンスは別途初期化が必要\r
+               progPlugins.clear();\r
+               tvprograms.clear();\r
+\r
+               /*\r
+                * 重要 - ここまで\r
+                */\r
+\r
+               ArrayList<String> prgIda = new ArrayList<String>();\r
+               for ( File f : new File(CommonUtils.joinPath("bin","tainavi")).listFiles() ) {\r
+                       Matcher ma = Pattern.compile("^(PlugIn_(TV|CS|RAD)P[^$]+)\\.class$").matcher(f.getName());\r
+                       if (ma.find()) {\r
+                               prgIda.add(ma.group(1));\r
+                       }\r
+               }\r
+               String[] prgIdd = prgIda.toArray(new String[0]);\r
+               Arrays.sort(prgIdd);\r
+               \r
+               // servicesに追記\r
+               StringBuilder sb = new StringBuilder();\r
+               for ( String prgId : prgIdd ) {\r
+                       sb.append("tainavi.");\r
+                       sb.append(prgId);\r
+                       sb.append("\n");\r
+               }\r
+               if ( ! CommonUtils.write2file(CommonUtils.joinPath("bin","META-INF","services","tainavi.TVProgram"), sb.toString()) ) {\r
+                       stwin.appendError(ERRID+"プラグインの読み込みに失敗しました: ");\r
+                       return false;\r
+               }\r
+               \r
+               ServiceLoader<TVProgram> p = ServiceLoader.load(TVProgram.class);\r
+\r
+               // 実際必要ないのだが、プラグインのインスタンスはclone()して使う\r
+               for ( TVProgram pg : p ) {\r
+                       TVProgram prog = pg.clone();\r
+                       \r
+                       stwin.appendMessage("+追加しました: "+prog.getTVProgramId());\r
+                       \r
+                       // CH設定タブではプラグイン側のインスタンスを使うので情報を追加してやる必要があるのであった\r
+                       setSettingProgPlugin(prog, env);\r
+                       \r
+                       progPlugins.add(prog);\r
+               }\r
+               \r
+               p = null;\r
+               \r
+               return true;\r
+       }\r
+       \r
+       /**\r
+        *  設定にあわせてWeb番組表プラグインを絞り込みます。\r
+        */\r
+       private void setSelectedProgPlugin() {\r
+               \r
+               // この3つは保存しておく\r
+               Syobocal syobo = tvprograms.getSyobo();\r
+               PassedProgram passed = tvprograms.getPassed();\r
+               PickedProgram pickup = tvprograms.getPickup();\r
+               SearchResult searched = tvprograms.getSearched();\r
+               \r
+               tvprograms.clear();\r
+               \r
+               {\r
+                       TVProgram tvp = progPlugins.getTvProgPlugin(env.getTVProgramSite());\r
+                       if ( tvp == null ) {\r
+                               // デフォルトもなければ先頭にあるもの\r
+                               tvp = progPlugins.getTvProgPlugin(null);\r
+                       }\r
+                       if ( tvp == null ) {\r
+                               // てか一個もなくね?\r
+                               StdAppendError("【Web番組表選択】地上波&BS番組表が選択されていません: "+env.getTVProgramSite());\r
+                       }\r
+                       else {\r
+                               StdAppendMessage("【Web番組表選択】地上波&BS番組表が選択されました: "+tvp.getTVProgramId());\r
+                               tvprograms.add(tvp.clone());\r
+                       }\r
+               }\r
+               {\r
+                       TVProgram tvp = progPlugins.getCsProgPlugin(env.getCSProgramSite());\r
+                       if ( tvp == null ) {\r
+                               tvp = progPlugins.getCsProgPlugin(null);\r
+                       }\r
+                       if ( tvp == null ) {\r
+                               StdAppendError("【Web番組表選択】CS番組表[プライマリ]が選択されていません: "+env.getCSProgramSite());\r
+                       }\r
+                       else {\r
+                               StdAppendMessage("【Web番組表選択】CS番組表[プライマリ]が選択されました: "+tvp.getTVProgramId());\r
+                               tvprograms.add(tvp.clone());\r
+                       }\r
+               }\r
+               {\r
+                       TVProgram tvp = progPlugins.getCs2ProgPlugin(env.getCS2ProgramSite());\r
+                       if ( tvp == null ) {\r
+                               tvp = progPlugins.getCs2ProgPlugin(null);\r
+                       }\r
+                       if ( tvp == null ) {\r
+                               StdAppendError("【Web番組表選択】CS番組表[プライマリ]が選択されていません: "+env.getCS2ProgramSite());\r
+                       }\r
+                       else {\r
+                               StdAppendMessage("【Web番組表選択】CS番組表[プライマリ]が選択されました: "+tvp.getTVProgramId());\r
+                               tvprograms.add(tvp.clone());\r
+                       }\r
+               }\r
+               /*\r
+               if ( progPlugins.getRadioProgPlugins().size() > 0 )\r
+               {\r
+                       TVProgram tvp = progPlugins.getCsProgPlugin(env.getRadioProgramSite());\r
+                       if ( tvp == null ) {\r
+                               tvp = progPlugins.getCsProgPlugin(null);\r
+                       }\r
+                       if ( tvp == null ) {\r
+                               StdAppendError("【Web番組表選択】ラジオ番組表が選択されていません: "+env.getRadioProgramSite());\r
+                       }\r
+                       else {\r
+                               StdAppendMessage("【Web番組表選択】ラジオ番組表が選択されました: "+tvp.getTVProgramId());\r
+                               tvprograms.add(tvp.clone());\r
+                       }\r
+               }\r
+               */\r
+               \r
+               {\r
+                       if ( syobo == null ) {\r
+                               syobo = new Syobocal();\r
+                       }\r
+                       tvprograms.add(syobo);\r
+               }\r
+               {\r
+                       if ( passed == null ) {\r
+                               passed = new PassedProgram();\r
+                       }\r
+                       tvprograms.add(passed);\r
+               }\r
+               {\r
+                       if ( pickup == null ) {\r
+                               pickup = new PickedProgram();\r
+                               pickup.loadProgram(null, false);\r
+                       }\r
+                       tvprograms.add(pickup);\r
+               }\r
+               {\r
+                       if ( searched == null ) {\r
+                               searched = new SearchResult();\r
+                       }\r
+                       tvprograms.add(searched);\r
+               }\r
+       }\r
+       \r
+       /**\r
+        * Web番組表設定をもとにレコーダプラグインのインスタンスを生成します。\r
+        */\r
+       private void initProgPluginAll() {\r
+\r
+               final String FUNCID = "[Web番組表プラグイン初期化] ";\r
+               final LinkedHashMap<ArrayList<TVProgram>,String> map = new LinkedHashMap<ArrayList<TVProgram>, String>();\r
+               map.put(tvprograms.getTvProgPlugins(), "地上波&BS番組表");\r
+               map.put(tvprograms.getCsProgPlugins(), "CS番組表[プライマリ]");\r
+               map.put(tvprograms.getCs2ProgPlugins(), "CS番組表[セカンダリ]");\r
+               //map.put(progPlugins.getRadioProgPlugins(), "ラジオ番組表");\r
+               \r
+               new SwingBackgroundWorker(true) {\r
+                       \r
+                       @Override\r
+                       protected Object doWorks() throws Exception {\r
+\r
+                               for ( ArrayList<TVProgram> tvpa : map.keySet() ) {\r
+                                       stwin.appendMessage(FUNCID+map.get(tvpa)+"のベース情報(放送局リストなど)を取得します.");\r
+                                       for ( TVProgram p : tvpa ) {\r
+                                               stwin.appendMessage(FUNCID+"プラグインを初期化します: "+p.getTVProgramId());\r
+                                               \r
+                                               try {\r
+                                                       // 個別設定(2) …(1)と(2)の順番が逆だったので前に移動してきました(3.17.3β)\r
+                                                       setSettingProgPlugin(p,env);                            // 他からも呼び出される部分だけ分離\r
+                                                       \r
+                                                       // 個別設定(1)\r
+                                                       p.setOptString(null);                                           // フリーオプション初期化\r
+                                                       p.loadAreaCode();                                                       // 放送エリア情報取得\r
+                                                       p.loadCenter(p.getSelectedCode(),false);        // 放送局情報取得\r
+                                                       p.setSortedCRlist();                                            // 有効放送局だけよりわける\r
+                                               }\r
+                                               catch (Exception e) {\r
+                                                       stwin.appendError(FUNCID+"ベース情報の取得に失敗しました.");\r
+                                                       e.printStackTrace();\r
+                                               }\r
+                                       }\r
+                               }\r
+                               \r
+                               // 共通設定部分の一斉更新\r
+                               //setSettingProgPluginAll(env);\r
+                               \r
+                               if ( env.getUseSyobocal() ) {\r
+                                       TVProgram syobo = tvprograms.getSyobo();\r
+                                       if ( syobo != null ) {\r
+                                               stwin.appendMessage(FUNCID+"しょぼかるを初期化します.");\r
+                                               setSettingProgPlugin(syobo,env);                                // 他からも呼び出される部分だけ分離\r
+                                               syobo.setUserAgent("tainavi");\r
+                                               syobo.setOptString(null);                                               // フリーオプション初期化\r
+                                               syobo.loadCenter(syobo.getSelectedCode(), false);\r
+                                       }\r
+                               }\r
+                               \r
+                               return null;\r
+                       }\r
+                       \r
+                       @Override\r
+                       protected void doFinally() {\r
+                       }\r
+               }.execute();\r
+       }\r
+       protected void setSettingProgPluginAll(Env nEnv) {\r
+               // 通常\r
+               setSettingProgPlugin(tvprograms.getTvProgPlugin(null),nEnv);\r
+               setSettingProgPlugin(tvprograms.getCsProgPlugin(null),nEnv);\r
+               setSettingProgPlugin(tvprograms.getCs2ProgPlugin(null),nEnv);\r
+               //setSettingProgPlugin(tvprograms.getRadioProgPlugin(null),nEnv);\r
+               setSettingProgPlugin(tvprograms.getSyobo(),nEnv);\r
+               \r
+               // しょぼかるは特殊\r
+               tvprograms.getSyobo().setUserAgent("tainavi");\r
+               // 検索結果も特殊\r
+               tvprograms.getSearched().setResultBufferMax(nEnv.getSearchResultBufferMax());\r
+       }\r
+       protected void setSettingProgPlugin(TVProgram p, Env nEnv) {\r
+               if ( p == null ) {\r
+                       return;\r
+               }               \r
+               p.setUserAgent(nEnv.getUserAgent());\r
+               p.setProgDir(nEnv.getProgDir());\r
+               p.setCacheExpired((enableWebAccess)?(nEnv.getCacheTimeLimit()):(0));\r
+               p.setContinueTomorrow(nEnv.getContinueTomorrow());\r
+               p.setExpandTo8(nEnv.getExpandTo8());\r
+               //p.setUseDetailCache(nEnv.getUseDetailCache());\r
+               p.setUseDetailCache(false);\r
+               p.setSplitEpno(nEnv.getSplitEpno());\r
+       }\r
+       \r
+       /**\r
+        * staticで持っている共通設定の更新\r
+        */\r
+       protected void setSettingProgPluginCommon(Env nEnv) {\r
+               \r
+               if ( nEnv.getUseProxy() && (nEnv.getProxyAddr().length() > 0 && nEnv.getProxyPort().length() > 0) ) {\r
+                       stwin.appendMessage("+Web番組表へのアクセスにProxyが設定されています: "+nEnv.getProxyAddr()+":"+nEnv.getProxyPort());\r
+                       TVProgramUtils.setProxy(nEnv.getProxyAddr(),nEnv.getProxyPort());\r
+               }\r
+               else {\r
+                       TVProgramUtils.setProxy(null,null);\r
+               }\r
+               \r
+               TVProgramUtils.setProgressArea(stwin);\r
+               TVProgramUtils.setChConv(chconv);\r
+       }\r
+       \r
+       //\r
+       private void initMpList() {\r
+               //mpList = new MarkedProgramList();                     // 検索結果リスト\r
+               mpList.setHistoryOnlyUpdateOnce(env.getHistoryOnlyUpdateOnce());\r
+               mpList.setShowOnlyNonrepeated(env.getShowOnlyNonrepeated());\r
+       }\r
+       \r
+       // L&FとFontを設定\r
+       private void initLookAndFeelAndFont() {\r
+\r
+               try {\r
+                       {\r
+                               vwlaf = new VWLookAndFeel();\r
+                               \r
+                               String lafname = vwlaf.update(env.getLookAndFeel());\r
+                               if ( lafname != null && ! lafname.equals(env.getLookAndFeel())) {\r
+                                       env.setLookAndFeel(lafname);\r
+                               }\r
+                               \r
+                               if ( CommonUtils.isMac() ) {\r
+                                       UIManager.getDefaults().put("Table.gridColor", new Color(128,128,128));\r
+                                       //UIManager.getDefaults().put("Table.selectionBackground", new Color(182,207,229));\r
+                                       //UIManager.getDefaults().put("Table.selectionForeground", new Color(0,0,0));\r
+                               }\r
+                       }\r
+                       \r
+                       {\r
+                               vwfont = new VWFont();\r
+                               \r
+                               String fname = vwfont.update(env.getFontName(),env.getFontSize());\r
+                               if ( fname != null && ! fname.equals(env.getFontName())) {\r
+                                       env.setFontName(fname);\r
+                               }\r
+                       }\r
+               }\r
+               catch ( Exception e ) {\r
+                       // 落ちられると困るからトラップしておこうぜ\r
+                       e.printStackTrace();\r
+               }\r
+       }\r
+       \r
+       // L&FやFontを変えたらコンポーネントに通知が必要\r
+       protected void updateComponentTreeUI() {\r
+               try {\r
+                       SwingUtilities.updateComponentTreeUI(this);\r
+                       SwingUtilities.updateComponentTreeUI(stwin);\r
+                       SwingUtilities.updateComponentTreeUI(mwin);\r
+                       SwingUtilities.updateComponentTreeUI(pcwin);\r
+                       SwingUtilities.updateComponentTreeUI(rdialog);\r
+                       SwingUtilities.updateComponentTreeUI(ccwin);\r
+               }\r
+               catch ( Exception e ) {\r
+                       // 落ちられると困るからトラップしておこうぜ\r
+                       e.printStackTrace();\r
+               }\r
+       }\r
+\r
+       // ツールチップの表示遅延時間を設定する\r
+       private void setTooltipDelay() {\r
+               ToolTipManager tp = ToolTipManager.sharedInstance();\r
+               tp.setInitialDelay(env.getTooltipInitialDelay()*100);\r
+               tp.setDismissDelay(env.getTooltipDismissDelay()*100);\r
+       }\r
+       \r
+       /**\r
+        * \r
+        * @return true:前回終了時の設定がある場合\r
+        */\r
+       private boolean buildMainWindow() {\r
+               //\r
+               mainWindow.addToolBar(toolBar);\r
+               mainWindow.addStatusArea(mwin);\r
+               \r
+               mainWindow.addTab(listed, MWinTab.LISTED);\r
+               mainWindow.addTab(paper, MWinTab.PAPER);\r
+               mainWindow.addTab(reserved, MWinTab.RSVED);\r
+               mainWindow.addTab(recorded, MWinTab.RECED);\r
+               mainWindow.addTab(setting, MWinTab.SETTING);\r
+               mainWindow.addTab(recsetting, MWinTab.RECSET);\r
+               mainWindow.addTab(chsetting, MWinTab.CHSET);\r
+               mainWindow.addTab(chsortsetting, MWinTab.CHSORT);\r
+               mainWindow.addTab(chconvsetting, MWinTab.CHCONV);\r
+               mainWindow.addTab(chdatsetting, MWinTab.CHDAT);\r
+               \r
+               //新聞描画枠のリセット\r
+               paper.clearPanel();\r
+               paper.buildMainViewByDate();\r
+               \r
+               // サイドツリーのデフォルトノードの選択\r
+               paper.selectTreeDefault();\r
+               listed.selectTreeDefault();\r
+               \r
+               if ( recInfoList.size() > 0 ) {\r
+                       // 前回終了時設定が存在する場合\r
+                       \r
+                       // 開いていたタブ\r
+                       mainWindow.setShowSettingTabs(bounds.getShowSettingTabs());\r
+                       \r
+                       // 選択していたレコーダ\r
+                       toolBar.setSelectedRecorder(bounds.getSelectedRecorderId());\r
+                       \r
+                       // ステータスエリアの高さ\r
+                       mwin.setRows(bounds.getStatusRows());\r
+                       \r
+                       return false;\r
+               }\r
+               \r
+               // 前回終了時設定が存在しない場合\r
+               return true;\r
+       }\r
+       \r
+       private void ShowInitTab() {\r
+               \r
+               // いったん無選択状態にしてから\r
+               mainWindow.setSelectedTab(null);\r
+               \r
+               if ( recInfoList.size() <= 0 ) {\r
+                       // 設定が存在しない場合\r
+                       mainWindow.setSelectedTab(MWinTab.RECSET);\r
+               }\r
+               else {\r
+                       // 設定が存在する場合\r
+                       mainWindow.setSelectedTab(MWinTab.getAt(bounds.getSelectedTab()));\r
+               }\r
+       }\r
+       \r
+       //\r
+       private void setInitBounds() {\r
+               // ウィンドウのサイズと表示位置を設定する\r
+               Rectangle window = bounds.getWinRectangle();\r
+               if (bounds.isLoaded()) {\r
+                       // 設定ファイルを読み込んであったらそれを設定する\r
+                       System.out.println(DBGID+"set bounds "+window);\r
+                       this.setBounds(window.x, window.y, window.width, window.height);\r
+               }\r
+               else {\r
+                       // 設定ファイルがなければ自動設定する\r
+                       Rectangle screen = this.getGraphicsConfiguration().getBounds();\r
+                       int x = 0;\r
+                       int w = window.width;\r
+                       if (window.width > screen.width) {\r
+                               x = 0;\r
+                               w = screen.width;\r
+                       }\r
+                       else {\r
+                               x = (screen.width - window.width)/2;\r
+                       }\r
+                       int y = 0;\r
+                       int h = window.height;\r
+                       if (window.height > screen.height) {\r
+                               y = 0;\r
+                               h = screen.height;\r
+                       }\r
+                       else {\r
+                               y = (screen.height - window.height)/2;\r
+                       }\r
+                       this.setBounds(x, y, w, h);\r
+               }\r
+       }\r
+\r
+       /**\r
+        * <P>ステータスエリアを隠す\r
+        * {@link VWMainWindow#setStatusVisible(boolean)}の置き換え\r
+        */\r
+       private void setStatusVisible(boolean b) {\r
+               \r
+               if (b) {\r
+                       listed.setDetailVisible(true);\r
+                       paper.setDetailVisible(true);\r
+                       MWinSetVisible(true);\r
+               }\r
+               else {\r
+                       listed.setDetailVisible(false);\r
+                       paper.setDetailVisible(false);\r
+                       MWinSetVisible(false);\r
+               }\r
+       }\r
+       \r
+       // フルスクリーンモードをトグル切り替え\r
+       private Dimension f_dim;\r
+       private Point f_pnt;\r
+       private int divloc_l = 0;\r
+       private int divloc_p = 0;\r
+       \r
+       private void setFullScreen(boolean b) {\r
+               \r
+               if ( b == true ) {\r
+                       // 枠の撤去\r
+                       this.dispose();\r
+                       this.setUndecorated(true);\r
+                       this.setVisible(true);\r
+                       \r
+                       //全画面表示へ\r
+                       Toolkit tk = getToolkit();\r
+                       Insets in = tk.getScreenInsets(getGraphicsConfiguration());\r
+                       Dimension d = tk.getScreenSize();\r
+                       f_dim = this.getSize();\r
+                       f_pnt = this.getLocation();\r
+                       this.setBounds(in.left, in.top, d.width-(in.left+in.right), d.height-(in.top+in.bottom));\r
+                       \r
+                       divloc_l = bounds.getTreeWidth();\r
+                       divloc_p = bounds.getTreeWidthPaper();\r
+                       \r
+                       // ツリーを閉じる\r
+                       paper.setCollapseTree();\r
+                       listed.setCollapseTree();\r
+               }\r
+               else {\r
+                       if ( f_pnt != null && f_dim != null ) { // 起動直後などは値がないですしね\r
+                               \r
+                               // 枠の復帰\r
+                               this.dispose();\r
+                               this.setUndecorated(false);\r
+                               this.setVisible(true);\r
+                               \r
+                               //全画面表示終了\r
+                               this.setBounds(f_pnt.x, f_pnt.y, f_dim.width, f_dim.height);\r
+                               \r
+                               bounds.setTreeWidth(divloc_l);\r
+                               bounds.setTreeWidthPaper(divloc_p);\r
+                               \r
+                               // ツリーの幅を元に戻す\r
+                               paper.setExpandTree();\r
+                               listed.setExpandTree();\r
+                       }\r
+               }\r
+       }\r
+       \r
+       // タイトルバー\r
+       private void setTitleBar() {\r
+               MemoryMXBean mbean = ManagementFactory.getMemoryMXBean();\r
+               MemoryUsage heapUsage = mbean.getHeapMemoryUsage();\r
+               \r
+               this.setTitle(\r
+                               String.format(\r
+                                               "%s - %s - Memory Usage Max:%dM Committed:%dM Used:%dM - FrameBuffer Status:%s",\r
+                                               VersionInfo.getVersion(),\r
+                                               CommonUtils.getDateTime(0),\r
+                                               heapUsage.getMax()/(1024*1024),\r
+                                               heapUsage.getCommitted()/(1024*1024),\r
+                                               heapUsage.getUsed()/(1024*1024),\r
+                                               (paper!=null)?(paper.getFrameBufferStatus()):("N/A")\r
+                               )\r
+               );\r
+       }\r
+       \r
+       @Override\r
+       public void timerRised(VWTimerRiseEvent e) {\r
+               if (env.getDebug()) System.out.println("Timer Rised: now="+CommonUtils.getDateTimeYMDx(e.getCalendar()));\r
+               setTitleBar();\r
+       }\r
+       \r
+       // 終了処理関連\r
+       private void ExitOnClose() {\r
+               // 座標・サイズ\r
+               if ( ! this.toolBar.isFullScreen()) {\r
+                       Rectangle r = this.getBounds();\r
+                       bounds.setWinRectangle(r);\r
+               }\r
+               else {\r
+                       Rectangle r = new Rectangle();\r
+                       r.x = this.f_pnt.x;\r
+                       r.y = this.f_pnt.y;\r
+                       r.width = this.f_dim.width;\r
+                       r.height = this.f_dim.height;\r
+                       bounds.setWinRectangle(r);\r
+               }\r
+               listed.copyColumnWidth();\r
+               reserved.copyColumnWidth();\r
+               \r
+               bounds.setStatusRows(mwin.getRows());\r
+\r
+               // 動作状態\r
+               bounds.setSelectedTab(mainWindow.getSelectedTab().getIndex());\r
+               bounds.setShowSettingTabs(mainWindow.getShowSettingTabs());\r
+               bounds.setSelectedRecorderId(toolBar.getSelectedRecorder());\r
+               bounds.setShowStatus(toolBar.isStatusShown());\r
+               \r
+               // 保存する\r
+               bounds.save();\r
+               \r
+               // ツリーの展開状態の保存\r
+               listed.saveTreeExpansion();\r
+               paper.saveTreeExpansion();\r
+       }\r
+       \r
+\r
+       /*******************************************************************************\r
+        * main()\r
+        ******************************************************************************/\r
+       \r
+       // 初期化が完了したら立てる\r
+       private static boolean initialized = false;\r
+       private static Viewer myClass = null;\r
+       \r
+       /**\r
+        * めいーん\r
+        * @param args\r
+        * @throws NoSuchAlgorithmException\r
+        * @version 今まで初期化を行ってからウィンドウを作成していたが<BR>\r
+        * 途中で例外が起こるとダンマリの上にゾンビになってたりとヒドかったので<BR>\r
+        * 先にウィンドウを作成してから初期化を行うように変えました\r
+        * @throws InterruptedException \r
+        * @throws InvocationTargetException \r
+        */\r
+       public static void main(final String[] args) throws NoSuchAlgorithmException, InvocationTargetException, InterruptedException {\r
+               \r
+               if ( myClass != null ) {\r
+                       // 既に起動していたらフォアグラウンドにする\r
+                       SwingUtilities.invokeAndWait(new Runnable() {\r
+                               @Override\r
+                               public void run() {\r
+                                       // うーん、いいのかこのコード?\r
+                                       myClass.setVisible(true);\r
+                                       myClass.setState(Frame.NORMAL);\r
+                               }\r
+                       });\r
+                       return;\r
+               }\r
+               \r
+               SwingUtilities.invokeLater(new Runnable() {\r
+                       public void run() {\r
+                               \r
+                               final Viewer thisClass = myClass = new Viewer(args);\r
+                               \r
+                               thisClass.addComponentListener(new ComponentAdapter() {\r
+                                       @Override\r
+                                       public void componentShown(ComponentEvent e) {\r
+                                               \r
+                                               // 一回実行したらもういらないよ\r
+                                               thisClass.removeComponentListener(this);\r
+                                               \r
+                                               // 初期化するよ\r
+                                               thisClass.initialize(args);\r
+                                               \r
+                                       }\r
+                               });\r
+                               \r
+                               thisClass.setVisible(true);\r
+                       }\r
+               });\r
+       }\r
+\r
+       \r
+       \r
+       /*******************************************************************************\r
+        * コンストラクタ\r
+        ******************************************************************************/\r
+\r
+       /**\r
+        * デフォルトコンストラクタ\r
+        */\r
+       public Viewer(final String[] args) {\r
+               \r
+               super();\r
+               \r
+               env.loadText();\r
+               bounds.loadText();\r
+               \r
+               \r
+               // 初期化が終わるまでは閉じられないよ → どうせステータスウィンドウにブロックされて操作できない\r
+               //setDefaultCloseOperation(JFrame.DO_NOTHING_ON_CLOSE);\r
+               //setResizable(false);\r
+               \r
+               setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);\r
+               \r
+               setTitleBar();\r
+               \r
+               try {\r
+                       Image image = ImageIO.read(new File(ICONFILE_TAINAVI));\r
+                       setIconImage(image);\r
+               }\r
+               catch (IOException e) {\r
+                       StdAppendError("[ERROR] アイコンが設定できない: "+e.toString());\r
+               }\r
+               \r
+               JLabel jLabel_splash_img = new JLabel(new ImageIcon("splash.gif"));\r
+               jLabel_splash_img.setPreferredSize(new Dimension(400,300));\r
+               //getContentPane().setLayout(new BorderLayout());\r
+               getContentPane().add(jLabel_splash_img, BorderLayout.CENTER);\r
+               pack();\r
+               \r
+               setLocationRelativeTo(null);    // 画面の真ん中に\r
+               \r
+               // SwingLocker共有設定\r
+               SwingLocker.setOwner(this);\r
+               \r
+               // とりあえずルックアンドフィールはリセットしておかないとだめっぽいよ\r
+               initLookAndFeelAndFont();\r
+               updateComponentTreeUI();\r
+       }\r
+       \r
+       // 初期化をバックグラウンドで行う\r
+       private void initialize(final String[] args) {\r
+               \r
+               StWinClear();\r
+               \r
+               // 初期化処理はバックグラウンドで行う\r
+               new SwingBackgroundWorker(false) {\r
+                       \r
+                       @Override\r
+                       protected Object doWorks() throws Exception {\r
+                               \r
+                               TatCount tc = new TatCount();\r
+                               \r
+                               // 初期化処理\r
+                               _initialize(args);\r
+                               \r
+                               // 終わったら閉じられるようにするよ\r
+                               //setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);\r
+                               //setResizable(true);\r
+\r
+                               stwin.append("");\r
+                               stwin.appendMessage(String.format("【タイニー番組ナビゲータが起動しました】 所要時間: %.2f秒",tc.end()));\r
+                               return null;\r
+                       }\r
+                       \r
+                       @Override\r
+                       protected void doFinally() {\r
+                               if ( ! initialized ) System.err.println("[ERROR][鯛ナビ] 【致命的エラー】 初期化処理を行っていたスレッドが異常終了しました。");\r
+                               stwin.setClosingEnabled(false);\r
+                               CommonUtils.milSleep(OPENING_WIAT);\r
+                               StWinSetVisible(false);\r
+                       }\r
+               }.execute();\r
+               \r
+               StWinSetLocationUnder(this);\r
+               StWinSetVisible(true);\r
+       }\r
+       \r
+       // 初期化の本体\r
+       private void _initialize(final String[] args) {\r
+               \r
+               // コマンドライン引数を処理する\r
+               procArgs(args);\r
+               \r
+               // ログ出力を設定する(Windowsの場合は文字コードをMS932にする) →DOS窓を殺したので終了\r
+               System.setOut(new DebugPrintStream(System.out,LOG_FILE,logging));\r
+               System.setErr(new DebugPrintStream(System.err,LOG_FILE,logging));\r
+               \r
+               // 起動メッセージ\r
+               StdAppendMessage("================================================================================");\r
+               StdAppendMessage("以下のメッセージは無視してください(原因調査中)");\r
+               StdAppendMessage("Exception occurred during event dispatching:");\r
+               StdAppendMessage("      java.lang.NullPointerException");\r
+               StdAppendMessage("              at javax.swing.plaf.basic.BasicScrollBarUI.layoutHScrollbar(Unknown Source)");\r
+               StdAppendMessage("              (以下略)");\r
+               StdAppendMessage("================================================================================");\r
+               stwin.appendMessage(CommonUtils.getDateTime(0));\r
+               stwin.appendMessage(String.format("タイニー番組ナビゲータが起動を開始しました(VersionInfo:%s on %s)",VersionInfo.getVersion(),VersionInfo.getEnvironment()));\r
+               \r
+               // 起動時にアップデートを確認する\r
+               chkVerUp();\r
+               \r
+               try {\r
+                       // メインの環境設定ファイルを読み込む\r
+                       loadEnvfile();\r
+                       \r
+                       // 二重起動防止\r
+                       chkDualBoot();\r
+                       \r
+                       // その他の環境設定ファイルを読み込む\r
+                       procEnvs();\r
+                       \r
+                       if ( onlyLoadProgram ) {\r
+                               if ( ! isOLPExpired(4) ) {\r
+                                       CommonUtils.milSleep(3000);\r
+                                       System.exit(1);\r
+                               }\r
+                               // プラグインのロード\r
+                               loadProgPlugins();\r
+                               // プラグインの初期化\r
+                               setSelectedProgPlugin();\r
+                               initProgPluginAll();\r
+                               // 検索結果リストの初期化(loadTVProgram()中で使うので)\r
+                               initMpList();\r
+                               // データのロード\r
+                               loadTVProgram(true,LoadFor.ALL);\r
+                               stwin.appendMessage("番組表を取得したので終了します");\r
+                               CommonUtils.milSleep(3000);\r
+                               System.exit(1);\r
+                       }\r
+                       \r
+                       // プラグインのロード\r
+                       loadProgPlugins();\r
+                       loadRecPlugins();\r
+\r
+                       // プラグインの初期化\r
+                       setSelectedProgPlugin();\r
+                       initProgPluginAll();\r
+\r
+                       initRecPluginAll();\r
+\r
+                       // WOL指定があったなら\r
+                       if ( runRecWakeup ) {\r
+                               doRecWakeup();\r
+                       }\r
+\r
+                       // 検索結果リストの初期化(loadTVProgram()中で使うので)\r
+                       initMpList();\r
+                       \r
+                       // データのロード\r
+                       loadTVProgram(false,LoadFor.ALL);\r
+                       \r
+                       // 放送局の並び順もロード\r
+                       chsort.load();\r
+                       \r
+                       loadRdReserve(runRecLoad, null);\r
+               }\r
+               catch ( Exception e ) {\r
+                       System.err.println("【致命的エラー】設定の初期化に失敗しました");\r
+                       e.printStackTrace();\r
+                       System.exit(1);\r
+               }\r
+               \r
+               // LookAndFeelとフォントを変更する\r
+               //initLookAndFeelAndFont();\r
+               //updateComponentTreeUI();\r
+               \r
+               // 背景色設定ダイアログにフォント名の一覧を設定する\r
+               pcwin.setFontList(vwfont);\r
+               \r
+               // (新聞形式の)ツールチップの表示時間を変更する\r
+               setTooltipDelay();\r
+\r
+               boolean firstRun = true;\r
+               try {\r
+                       // メインウィンドウの作成\r
+                       mainWindow = new VWMainWindow();\r
+               \r
+                       // 内部クラスのインスタンス生成\r
+                       toolBar = new VWToolBar();\r
+                       listed = new VWListedView();\r
+                       paper = new VWPaperView();\r
+                       reserved = new VWReserveListView();\r
+                       recorded = new VWRecordedListView();\r
+                       setting = new VWSettingView();\r
+                       recsetting = new VWRecorderSettingView();\r
+                       chsetting = new VWChannelSettingView();\r
+                       chdatsetting = new VWChannelDatSettingView();\r
+                       chsortsetting = new VWChannelSortView();\r
+                       chconvsetting = new VWChannelConvertView();\r
+                       \r
+                       // ページャーの設定\r
+                       toolBar.setPagerItems();\r
+                       \r
+                       // ウィンドウを構築\r
+                       firstRun = buildMainWindow();\r
+                       \r
+                       // ステータスエリアを開く\r
+                       setStatusVisible(bounds.getShowStatus());\r
+               }\r
+               catch ( Exception e ) {\r
+                       System.err.println("【致命的エラー】ウィンドウの構築に失敗しました");\r
+                       e.printStackTrace();\r
+                       System.exit(1);\r
+               }\r
+               \r
+               // ステータスエリアの開閉(不要)\r
+               \r
+               // トレイアイコンを作る\r
+               getTrayIcon();\r
+               setTrayIconVisible(env.getShowSysTray());\r
+               \r
+               // ウィンドウを閉じたときの処理\r
+               setXButtonAction(env.getShowSysTray() && env.getHideToTray());\r
+               \r
+               // ウィンドウ操作のリスナー登録\r
+               this.addWindowListener(new WindowAdapter() {\r
+                       // ウィンドウを最小化したときの処理\r
+                       @Override\r
+                       public void windowIconified(WindowEvent e) {\r
+                               HideToTray();\r
+                       }\r
+               \r
+                       // ウィンドウを閉じたときの処理\r
+                       @Override\r
+                       public void windowClosing(WindowEvent e) {\r
+                               ExitOnClose();\r
+                       }\r
+               });\r
+               \r
+               // タブを選択\r
+               ShowInitTab();\r
+               \r
+               // 初回起動時はレコーダの登録を促す\r
+               if (firstRun) {\r
+                       Container cp = getContentPane();\r
+                       JOptionPane.showMessageDialog(cp, "レコーダが登録されていません。\n最初に登録を行ってください。\n番組表だけを使いたい場合は、\nNULLプラグインを登録してください。");\r
+               }\r
+               \r
+               // メインウィンドウを入れ替える\r
+               this.setVisible(false);\r
+               this.setContentPane(mainWindow);\r
+               setInitBounds();\r
+               this.setVisible(true);\r
+               \r
+               // タイトル更新\r
+               setTitleBar();\r
+               \r
+               // [タイマー] タイトルバー更新/リスト形式の現在時刻ノード/新聞形式の現在時刻ノード\r
+               timer_now.addVWTimerRiseListener(this);\r
+               timer_now.addVWTimerRiseListener(listed);\r
+               timer_now.addVWTimerRiseListener(paper);\r
+               \r
+               // タイマー起動\r
+               timer_now.start();\r
+               \r
+               // メッセージだ\r
+               mwin.appendMessage(String.format("タイニー番組ナビゲータが起動しました (VersionInfo:%s on %s)",VersionInfo.getVersion(),VersionInfo.getEnvironment()));\r
+               \r
+               initialized = true;\r
+       }\r
+}\r
diff --git a/TinyBannavi/src/tainavi/b64.java b/TinyBannavi/src/tainavi/b64.java
new file mode 100644 (file)
index 0000000..a8b6d96
--- /dev/null
@@ -0,0 +1,183 @@
+package tainavi;\r
+\r
+import java.util.ArrayList;\r
+\r
+\r
+\r
+public class b64 {\r
+       \r
+       private static final String list = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/";\r
+\r
+       public static String enc(byte[] src) {\r
+               String out = "";\r
+               \r
+               if (src == null) {\r
+                       return null;\r
+               }\r
+               \r
+               int n = src.length;\r
+               byte[] b = new byte[n+1];\r
+               b[n] = 0x00;\r
+               System.arraycopy(src, 0, b, 0, n);\r
+               \r
+               int x;\r
+               int y;\r
+               int j;\r
+               byte c = 0;\r
+               for (int i=0; i<n*8; i+=6) {\r
+                       j = (i-i%8)/8;\r
+                       switch (i%8) {\r
+                       case 0:\r
+                               x = (int)b[j];\r
+                               x &= 0x000000fc;\r
+                               x >>= 2;\r
+                               c = (byte)x;\r
+                               break;\r
+                       case 1:\r
+                               x = (int)b[j];\r
+                               x &= 0x0000007e;\r
+                               x >>= 1;\r
+                               c = (byte)x;\r
+                               break;\r
+                       case 2:\r
+                               x = (int)b[j];\r
+                               x &= 0x0000003f;\r
+                               c = (byte)x;\r
+                               break;\r
+                       case 3:\r
+                               x = (int)b[j];\r
+                               x &= 0x0000001f;\r
+                               x <<= 1;\r
+                               y = (int)b[j+1];\r
+                               y &= 0x00000080;\r
+                               y >>= 7;\r
+                               c = (byte)(x|y);\r
+                               break;\r
+                       case 4:\r
+                               x = (int)b[j];\r
+                               x &= 0x0000000f;\r
+                               x <<= 2;\r
+                               y = (int)b[j+1];\r
+                               y &= 0x000000C0;\r
+                               y >>= 6;\r
+                               c = (byte)(x|y);\r
+                               break;\r
+                       case 5:\r
+                               x = (int)b[j];\r
+                               x &= 0x00000007;\r
+                               x <<= 3;\r
+                               y = (int)b[j+1];\r
+                               y &= 0x000000E0;\r
+                               y >>= 5;\r
+                               c = (byte)(x|y);\r
+                               break;\r
+                       case 6:\r
+                               x = (int)b[j];\r
+                               x &= 0x00000003;\r
+                               x <<= 4;\r
+                               y = (int)b[j+1];\r
+                               y &= 0x000000F0;\r
+                               y >>= 4;\r
+                               c = (byte)(x|y);\r
+                               break;\r
+                       case 7:\r
+                               x = (int)b[j];\r
+                               x &= 0x00000001;\r
+                               x <<= 5;\r
+                               y = (int)b[j+1];\r
+                               y &= 0x000000F8;\r
+                               y >>= 3;\r
+                               c = (byte)(x|y);\r
+                               break;\r
+                       }\r
+                       out += list.substring((int)c,(int)c+1);\r
+               }\r
+               for (int i=out.length(); i%4 != 0; i++) {\r
+                       out += "=";\r
+               }\r
+               return(out);\r
+       }\r
+               \r
+       public static byte[] dec(String src){\r
+               \r
+               if (src == null) {\r
+                       return null;\r
+               }\r
+               \r
+               ArrayList<Byte> ba = new ArrayList<Byte>();\r
+               \r
+               int x;\r
+               int y;\r
+               int m = 0;\r
+               byte c = 0;\r
+               int n = src.length();\r
+               for (int i=0; i<n; i++) {\r
+                       //\r
+                       int b = list.indexOf(src.charAt(i));\r
+                       if (b == -1) {\r
+                               break;\r
+                       }\r
+                       \r
+                       //\r
+                       //int j = i / 4;\r
+                       switch (m = i%4) {\r
+                       case 0:\r
+                               x = b;\r
+                               x &= 0x0000003f;\r
+                               x <<= 2;\r
+                               c = (byte)x;\r
+                               ba.add(c);\r
+                               break;\r
+                       case 1:\r
+                               x = b;\r
+                               x &= 0x00000030;\r
+                               x >>= 4;\r
+                               c = ba.remove(ba.size()-1);\r
+                               c |= (byte)x;\r
+                               ba.add(c);\r
+                               //\r
+                               y = b;\r
+                               y &= 0x0000000f;\r
+                               y <<= 4;\r
+                               c = (byte)y;\r
+                               ba.add(c);\r
+                               break;\r
+                       case 2:\r
+                               x = b;\r
+                               x &= 0x0000003C;\r
+                               x >>= 2;\r
+                               c = ba.remove(ba.size()-1);\r
+                               c |= (byte)x;\r
+                               ba.add(c);\r
+                               //\r
+                               y = b;\r
+                               y &= 0x00000003;\r
+                               y <<= 6;\r
+                               c = (byte)y;\r
+                               ba.add(c);\r
+                               break;\r
+                       case 3:\r
+                               x = b;\r
+                               x &= 0x0000003f;\r
+                               c = ba.remove(ba.size()-1);\r
+                               c |= (byte)x;\r
+                               ba.add(c);\r
+                               break;\r
+                       }\r
+               }\r
+               \r
+               // 端数切捨て\r
+               if (ba.size() > 0 && m != 3) {\r
+                       ba.remove(ba.size()-1);\r
+               }\r
+               \r
+               // リスト→配列\r
+               byte[] bb = new byte[ba.size()];\r
+               int i = 0;\r
+               for (Byte b : ba) {\r
+                       bb[i++] = b;\r
+               }\r
+               \r
+               return bb;\r
+       }\r
+}\r
diff --git a/TinyBannavi/src/tainavi/clipboardItem.java b/TinyBannavi/src/tainavi/clipboardItem.java
new file mode 100644 (file)
index 0000000..7f355df
--- /dev/null
@@ -0,0 +1,10 @@
+package tainavi;\r
+\r
+/**\r
+ * {@link ClipboardInfo} に置き換えられました(名前が小文字で始まっていたため) \r
+ * @deprecated\r
+ * @version ~3.15.4β\r
+ */\r
+public class clipboardItem extends ClipboardInfo {\r
+       \r
+}\r
diff --git a/TinyBannavi/src/tainavi/paperColors.java b/TinyBannavi/src/tainavi/paperColors.java
new file mode 100644 (file)
index 0000000..f2f3f5b
--- /dev/null
@@ -0,0 +1,9 @@
+package tainavi;\r
+\r
+/**\r
+ * @deprecated\r
+ * @version 3.15.4β 廃止(先頭が大文字でないとワーニングが出るので)\r
+ * @see PaperColorsMap\r
+ */\r
+public class paperColors {\r
+}\r
diff --git a/TinyBannavi/src/tainavi/unzip.java b/TinyBannavi/src/tainavi/unzip.java
new file mode 100644 (file)
index 0000000..7fb6263
--- /dev/null
@@ -0,0 +1,72 @@
+package tainavi;\r
+\r
+import java.io.File;\r
+import java.io.FileInputStream;\r
+import java.io.FileOutputStream;\r
+import java.io.IOException;\r
+import java.util.zip.ZipEntry;\r
+import java.util.zip.ZipInputStream;\r
+\r
+public class unzip {\r
+\r
+       public static void doUnzip(String dir, String zip) throws IOException,Exception {\r
+               ZipInputStream zis = new ZipInputStream(new FileInputStream(new File(zip)));\r
+               ZipEntry entry = null;\r
+               try {\r
+                       while ((entry = zis.getNextEntry()) != null) {\r
+                               try {\r
+                                       unzipEntry(entry, dir, zis);\r
+                               }\r
+                               finally {\r
+                                       zis.closeEntry();\r
+                               }\r
+                       }\r
+               }\r
+               finally {\r
+                       if (zis != null) {\r
+                               try {\r
+                                       zis.close();\r
+                               }\r
+                               catch (IOException e) {}\r
+                       }\r
+               }\r
+       }\r
+       \r
+       private static final void unzipEntry( ZipEntry entry, String dir, ZipInputStream zis ) throws IOException {\r
+               File f  = null;\r
+               if (entry.isDirectory()) {\r
+                       // フォルダ\r
+                       f = new File(dir, entry.getName());\r
+                       if ( ! f.exists() && ! f.mkdirs()) {\r
+                               throw new IOException( "create directory faild. directory="+f.getAbsolutePath() );\r
+                       }\r
+               }\r
+               else {\r
+                       // ファイル\r
+                       f = new File(dir, entry.getName());\r
+                       File parent = new File(f.getParent());\r
+                       if ( ! parent.exists() && ! new File(f.getParent()).mkdirs()) {\r
+                               throw new IOException( "create directory faild. directory="+parent.getAbsolutePath() );\r
+                       }\r
+                       FileOutputStream out = new FileOutputStream(f);\r
+                       byte[] buf = new byte[65536];\r
+                       int size = 0;\r
+                       try {\r
+                               while ((size = zis.read(buf,0,buf.length)) != -1) {\r
+                                       out.write(buf, 0, size);\r
+                               }\r
+                               out.close();\r
+                               long l = entry.getTime();\r
+                               f.setLastModified(l);\r
+                       }\r
+                       finally {\r
+                               if (out != null) {\r
+                                       try {\r
+                                               out.close();\r
+                                       }\r
+                                       catch (IOException e) { }\r
+                               }\r
+                       }\r
+               }\r
+       }\r
+}\r
diff --git a/TinyBannavi/src/tainaviboot/Boot.java b/TinyBannavi/src/tainaviboot/Boot.java
new file mode 100644 (file)
index 0000000..4b03cda
--- /dev/null
@@ -0,0 +1,18 @@
+package tainaviboot;\r
+\r
+import java.lang.reflect.InvocationTargetException;\r
+import java.security.NoSuchAlgorithmException;\r
+\r
+public class Boot {\r
+\r
+       /**\r
+        * @param args\r
+        * @throws NoSuchAlgorithmException \r
+        * @throws InterruptedException \r
+        * @throws InvocationTargetException \r
+        */\r
+       public static void main(String[] args) throws NoSuchAlgorithmException, InvocationTargetException, InterruptedException {\r
+               tainavi.Viewer.main(args);\r
+       }\r
+\r
+}\r
diff --git a/TinyBannavi/src/tainaviboot/Boot.txt b/TinyBannavi/src/tainaviboot/Boot.txt
new file mode 100644 (file)
index 0000000..76fd789
--- /dev/null
@@ -0,0 +1,3 @@
+Manifest-Version: 1.0\r
+Main-Class: tainaviboot.Boot\r
+Class-Path: bin;javamail/mail.jar;javamail/activation.jar;calendar/gdata-calendar-1.0.jar;calendar/gdata-client-1.0.jar;skin/*\r
diff --git a/TinyBannavi/src/todo.txt b/TinyBannavi/src/todo.txt
new file mode 100644 (file)
index 0000000..14f8bef
--- /dev/null
@@ -0,0 +1,62 @@
+---バグとおぼしき現象\r
+\r
+【録画結果一覧】TvRockに上限設定が反映されていない?\r
+【レコーダ対応】RD-X5プラグインのバグ調査依頼への対応\r
+【新聞形式】類似予約を正しく抽出できない問題→予約がタイトルのみで、番組情報はタイトル+サブタイトルの場合スコアが足りなくなる\r
+【新聞形式】snapshotで落ちる\r
+【新聞形式】日跨りイベント時に「現在放送中」が自動で翌日に切り替わらなくなった\r
+【予約ダイアログ】類似予約対応で番組IDの扱いが変\r
+【リスト形式】放送局別表示で、予約が多いと描画が重くなる件(AT-Xで大量に自動録画が入っているとか)\r
+\r
+---優先度高\r
+\r
+【自動予約登録】EDCBとTVRockの自動予約登録編集機能の追加(~6月中旬厳守) \r
+【CHコンバート設定】さっさと編集を実装する\r
+【番組追跡】リピート放送検出の改善>番組追跡履歴の保存 \r
+【レコーダ対応(EDCB/TvRock)】番組IDの取得をレコーダから取得するように変更\r
+【レコーダ対応(TVRock)】UTF-8→ShiftJISに変換できない文字が化けるので、他の文字で置換するようにする\r
+\r
+【予約一覧】番組表にマッチする番組が存在しているかどうかわかるようにする(繰り返し予約で改変期に番組が終了したとかがわかるようにする)\r
+【予約一覧】録画結果一覧に正常録画済みの記録がある場合はその情報が簡単に参照できるようにする(無効化操作のときの目視確認作業を省力化)\r
+\r
+---優先度並\r
+\r
+【予約ダイアログ】EPG予約未対応のレコーダの場合は番組ID取得設定になっても取得にいかないようにする? →スカパープレミアムの例外処理が現実的?\r
+【予約ダイアログ】鯛ナビの番組表とレコーダの番組表で時間が合わない番組への追従対応\r
+\r
+【キーワード検索】検索処理と表示処理の分離>過去ログ検索については完了\r
+【キーワード検索】検索履歴の保存(結果の保存までやる?)\r
+\r
+【その他】TaiNavi.exeで起動できない場合がある問題の原因調査\r
+【その他】初期化処理中にExceptionをcatchできない場合がある問題の原因調査\r
+\r
+【レコーダ対応】DIGA本体から番組IDをとれないか、DIMORAとの通信にsnifferかまして調べる\r
+\r
+【新聞形式】番組枠のbitmap描画の高速化 \r
+ 案1)テキスト描画をjniでnaitiveコードにする\r
+ 案2)番組枠を1番組ずつjlabelで描画してるが、テキストはでかい1枚のbitmapにして、jlabelはmouselistenerを拾うための枠だけにする\r
+\r
+---優先度低\r
+\r
+【予約一覧】フィルタリングを、予約操作後も維持できるようにする\r
+【録画結果一覧】各項目の横幅の記憶\r
+【新聞形式】過去ログの放送局別一週間表示\r
+【予約ダイアログ】類似予約をグラフィカルに表示(横に縦棒何本かひくとか) \r
+\r
+---暫定対処まで済\r
+\r
+【番組予約】類似予約存在時の番組ID等の引き継ぎ問題への対応\r
+\r
+---済\r
+\r
+【新聞形式】予約待機枠の描画処理の修正\r
+【その他】Web番組表データと新聞形式/リスト形式表示データ間の齟齬と重複の解消\r
+【その他】Web番組表データは各種設定のいかんにかかわらずサブタイトルを分離しない形で保持し、表示部で切り替えるように変更(バージョンアップ以前の過去ログのデータには遡り適用されない)\r
+【新聞形式】一時的に表示範囲を1~2時間程度にするボタン →100%~300%で\r
+【新聞形式】新聞形式以外からジャンプしてくるとページャーコンボボックスがdisabledになる\r
+【新聞形式】高さ倍率次第で番組枠の枠線が太くなったりする問題\r
+【新聞形式/リスト形式】現在状態の確認をツリーの上部のラベルの文字列でやっているのをやめる\r
+【新聞形式】拡縮すると描画開始位置がずれる\r
+【新聞形式/リスト形式】タブを開くたびにツリーを全部生成しなおしているのをやめる \r
+\r
+---以上\r
diff --git a/TinyBannavi/statviewer.cmd b/TinyBannavi/statviewer.cmd
new file mode 100644 (file)
index 0000000..1bb158a
--- /dev/null
@@ -0,0 +1,4 @@
+CD /D %~dp0\r
+SET JAVA=javaw\r
+IF EXIST JRE6 SET JAVA=JRE6\BIN\JAVA\r
+START %JAVA% -cp bin -Dfile.encoding=UTF-8 statusView.Viewer\r
diff --git a/TinyBannavi/tinybannavi.cmd b/TinyBannavi/tinybannavi.cmd
new file mode 100644 (file)
index 0000000..afaf9fc
--- /dev/null
@@ -0,0 +1,102 @@
+@ECHO OFF\r
+\r
+CD /D %~dp0\r
+\r
+IF NOT EXIST .\bin\tainavi\Viewer.class GOTO ERROREXIT\r
+\r
+REM JavaRuntime\82Ì\83o\81[\83W\83\87\83\93\83A\83b\83v\r
+\r
+IF EXIST jre6 THEN REN jre6 jre\r
+IF EXIST jre (\r
+       IF EXIST jre.new (\r
+               ECHO JRE\82ð\83o\81[\83W\83\87\83\93\83A\83b\83v\82µ\82Ü\82·.\r
+               REN jre jre.old\r
+               REN jre.new jre\r
+       )\r
+)\r
+\r
+REM \83o\83C\83i\83\8a\82Ì\83o\81[\83W\83\87\83\93\83A\83b\83v\r
+\r
+IF EXIST bin.new (\r
+       ECHO \83o\83C\83i\83\8a[bin]\82Æ\90Ý\92è\83t\83@\83C\83\8b[env]\82ð\8dX\90V\82µ\82Ü\82·.\r
+       IF EXIST bin.old REN bin.old bin.old2\r
+       REN bin bin.old\r
+       REN bin.new bin\r
+)\r
+\r
+REM \8eg\97p\82·\82éJava\82ð\8c\9f\8dõ\82·\82é\r
+\r
+SET JPATH=%PATH%;C:\Program Files\Java\jre6\bin;C:\Program Files (x86)\Java\jre6\bin;C:\Program Files\Java\jre7\bin;C:\Program Files (x86)\Java\jre7\bin\r
+SET JEXE=javaw.exe\r
+SET JAVA=\r
+SET CLASSPATH=bin;javamail\mail.jar;javamail\activation.jar;calendar\gdata-calendar-1.0.jar;calendar\gdata-client-1.0.jar;skin\*\r
+IF EXIST jre (\r
+       SET JAVA=jre\bin\javaw.exe\r
+) ELSE (\r
+       FOR %%I IN ( %JEXE% ) DO (\r
+               SET JAVA=%%~$JPATH:I\r
+       )\r
+)\r
+IF NOT EXIST "%JAVA%" (\r
+       ECHO.\r
+       ECHO Java\82ª\82Ý\82Â\82©\82è\82Ü\82¹\82ñ\81B\88È\89º\82ð\8am\94F\82µ\82Ä\82­\82¾\82³\82¢\81B\r
+       ECHO.\r
+       ECHO \87@Java\82ª\83C\83\93\83X\83g\81[\83\8b\82³\82ê\82Ä\82¢\82é\82©\81B\r
+       ECHO \87A%JEXE%\82ªPATH\8aÂ\8b«\95Ï\90\94\82Å\8ew\92è\82³\82ê\82é\8fê\8f\8a\82É\91\8dÝ\82µ\82Ä\82¢\82é\82©\81B\r
+       ECHO.\r
+       PAUSE\r
+       GOTO :EOF\r
+)\r
+ECHO \8e\9f\82ÌJava\82ª\8eg\97p\82³\82ê\82Ü\82·\81F"%JAVA%"\r
+\r
+REM \83q\81[\83v\82Ì\8am\95Û\89Â\94\\82È\8dÅ\91å\83T\83C\83Y\82ð\92²\82×\82é\r
+\r
+CALL :BASENAME "%JAVA%"\r
+SET JAVACMD=%RESULT%java.exe\r
+SET MAXHEAP=1024m\r
+"%JAVACMD%" -Xmx%MAXHEAP% -version > NUL 2> NUL\r
+IF NOT ERRORLEVEL 1 (\r
+       GOTO :RUNTAI\r
+)\r
+SET MAXHEAP=768m\r
+"%JAVACMD%" -Xmx%MAXHEAP% -version > NUL 2> NUL\r
+IF NOT ERRORLEVEL 1 (\r
+       GOTO :RUNTAI\r
+)\r
+SET MAXHEAP=512m\r
+"%JAVACMD%" -Xmx%MAXHEAP% -version > NUL 2> NUL\r
+IF NOT ERRORLEVEL 1 (\r
+       GOTO :RUNTAI\r
+)\r
+SET MAXHEAP=256m\r
+"%JAVACMD%" -Xmx%MAXHEAP% -version  > NUL 2> NUL\r
+IF ERRORLEVEL 1 (\r
+       ECHO \97\\96ñ\89Â\94\\82È\83q\81[\83v\83T\83C\83Y\82ª%MAXHEAP%\82ð\89º\89ñ\82è\82Ü\82µ\82½.\r
+       ECHO \83\81\83\82\83\8a\82ª\95s\91«\82µ\82Ä\82¢\82é\82æ\82¤\82É\8ev\82í\82ê\82Ü\82·.\r
+       PAUSE\r
+       GOTO :EOF\r
+)\r
+\r
+REM IF ERRORLEVEL x \82Í\81AERRORLEVEL==x\82Å\82Í\82È\82­\82ÄERRORLEVEL>=x\82È\82ñ\82¾\82»\82¤\82È\81B\92m\82ç\82ñ\82í\81I\82\97\82\97\82\97\r
+REM "()"\93à\82Í\83\8d\81[\83J\83\8b\83X\83R\81[\83v\82É\82È\82Á\82Ä\82é\82Ý\82½\82¢\81B\92m\82ç\82ñ\82È\81[\81c\r
+\r
+:RUNTAI\r
+ECHO \81\9a\81\9a\81\9a\81@TaiNavi.exe\82©\82ç\82Ì\8bN\93®\82É\95Ï\8dX\82ð\82¨\8aè\82¢\82µ\82Ü\82·\81@\81\9a\81\9a\81\9a\r
+ECHO \81\9a\81\9a\81\9a\81@\83E\83B\83\8b\83X\83`\83F\83b\83N\82ð\96Y\82ê\82¸\82É\81\81@\81@\81@\81@\81@\81@\81@\81\9a\81\9a\81\9a\r
+ECHO.\r
+ECHO \97\98\97p\89Â\94\\82È\8dÅ\91å\83q\81[\83v\83T\83C\83Y\81F%MAXHEAP%\r
+ECHO.\r
+ECHO \83^\83C\83j\81[\94Ô\91g\83i\83r\83Q\81[\83^\82ð\8bN\93®\92\86\82Å\82·.\81@\81¦\82±\82Ì\91\8b\82Í\82P\82O\95b\82Å\95Â\82\82Ü\82·.\r
+START "TaiNavi" "%JAVA%" -Xrs -Xms64m -Xmx%MAXHEAP% -Dfile.encoding=UTF-8 -Duser.timezone=Asia/Tokyo tainavi.Viewer -l %*\r
+PING localhost -n 10 > NUL\r
+GOTO :EOF\r
+\r
+REM \88ø\90\94\82Ì\83p\83X\82Ì\82Ý\82ð\8eæ\93¾\82·\82é\83T\83u\83\8b\81[\83`\83\93\r
+\r
+:BASENAME\r
+SET RESULT=%~dp1\r
+GOTO :EOF\r
+\r
+:ERROREXIT\r
+ECHO \8eÀ\8ds\82Å\82«\82Ü\82¹\82ñ.\r
+PAUSE\r
diff --git a/TinyBannavi/tinybannavi.sh b/TinyBannavi/tinybannavi.sh
new file mode 100644 (file)
index 0000000..5e9229c
--- /dev/null
@@ -0,0 +1,58 @@
+#!/bin/sh
+WKDIR=`dirname "$0"`
+if [ "`uname -s`" = "Darwin" ]; then
+       cd "$WKDIR/../../../"
+else
+       cd "$WKDIR"
+fi
+
+if [ ! -f ./bin/tainavi/Viewer.class ]; then
+       echo "NOT FOUND INSTALL DEST."
+       exit 1
+fi
+
+if [ -d bin.new ]; then
+       if [ -d bin.old ]; then
+               rm -rf bin.old
+       fi
+       mv -f bin bin.old
+       mv -f bin.new bin
+       if [ -d env.old ]; then
+               rm -rf env.old
+       fi
+       mkdir env.old
+       ( cd env; tar cf - . ) | ( cd env.old; tar xf - )
+fi
+
+MAXHEAP=1024m
+java -Xmx$MAXHEAP -version
+if [ "$?" -ne "0" ]; then
+       MAXHEAP=768m
+       java -Xmx$MAXHEAP -version
+       if [ "$?" -ne "0" ]; then
+               MAXHEAP=512m
+               java -Xmx$MAXHEAP -version
+               if [ "$?" -ne "0" ]; then
+                       MAXHEAP=256m
+                       java -Xmx$MAXHEAP -version
+                       if [ "$?" -ne "0" ]; then
+                               exit 1
+                       fi
+               fi
+       fi
+fi
+
+echo "MAXHEAP=$MAXHEAP"
+
+GTKRC=env/_gtkrc-2.0
+if [ -f "$GTKRC" ]; then
+       export GTK2_RC_FILES="$GTKRC"
+fi
+
+CLASSPATH=bin:javamail/mail.jar:javamail/activation.jar:calendar/gdata-calendar-1.0.jar:calendar/gdata-client-1.0.jar
+VMARGS="-Xms64m -Xmx$MAXHEAP -Dfile.encoding=UTF-8 -Duser.timezone=Asia/Tokyo"
+if [ "`uname -s`" = "Darwin" ]; then
+       java $VMARGS -cp "$CLASSPATH" -Xdock:icon="$WKDIR/../Resources/tainavi.icns" -Xdock:name=TinyBangumiNavigator tainavi.Viewer -l $* &
+else
+       java $VMARGS -cp "$CLASSPATH" -Dawt.useSystemAAFontSettings=on tainavi.Viewer -l $*
+fi
diff --git a/TinyBannavi/tinysync.cmd b/TinyBannavi/tinysync.cmd
new file mode 100644 (file)
index 0000000..9218b4e
--- /dev/null
@@ -0,0 +1,5 @@
+CD /D %~dp0\r
+SET JAVA=javaw\r
+SET CLASSPATH=bin\r
+IF EXIST JRE6 SET JAVA=JRE6\BIN\JAVAW\r
+START %JAVA% -Dfile.encoding=UTF-8 taiSync.Viewer %*\r
diff --git a/TinyBannavi/tinysync.sh b/TinyBannavi/tinysync.sh
new file mode 100644 (file)
index 0000000..505fdd4
--- /dev/null
@@ -0,0 +1,3 @@
+#!/bin/sh
+export CLASSPATH=bin
+java -Dfile.encoding=UTF-8 taiSync.Viewer $*
diff --git a/TinyBannavi/削除.cm_ b/TinyBannavi/削除.cm_
new file mode 100644 (file)
index 0000000..94b4dfd
--- /dev/null
@@ -0,0 +1,78 @@
+CD .\bin\r
+RMDIR /S /Q CVS\r
+RMDIR /S /Q META-INF\r
+RMDIR /S /Q niseRD\r
+RMDIR /S /Q testfield\r
+CD ..\\r
+\r
+CD .\env\r
+RMDIR /S /Q CVS\r
+DEL /Q \83e\83X\83g\8dí\8f\9c.cmd\r
+DEL /Q hogehoge.*\r
+CALL _initialize.cmd\r
+CD ..\\r
+\r
+CD .\calendar\r
+DEL /Q gdata-calendar-1.0.jar\r
+DEL /Q gdata-client-1.0.jar\r
+CD ..\\r
+\r
+CD .\javamail\r
+DEL /Q activation.jar\r
+DEL /Q mail.jar\r
+CD ..\\r
+\r
+CD .\icon\r
+RMDIR /S /Q CVS\r
+CD ..\\r
+\r
+CD .\skin\r
+DEL /Q *.jar\r
+CD ..\\r
+\r
+CD ".\TaiNavi for Mac.app"\r
+RMDIR /S /Q CVS\r
+CD .\Contents\r
+RMDIR /S /Q CVS\r
+CD .\MacOS\r
+RMDIR /S /Q CVS\r
+CD ..\Resources\r
+RMDIR /S /Q CVS\r
+CD ..\..\..\\r
+\r
+COPY tinybannavi.sh "TaiNavi for Mac.app\Contents\MacOS\"\r
+\r
+rem MOVE .\bin\tainavi\PlugIn_RecGoogleCalendar.class .\calendar\r
+rem MOVE .\bin\tainavi\PlugIn_RecRD_MAIL*.class .\javamail\r
+rem MOVE .\bin\tainavi\PlugIn_RecRD_MAIL.class .\javamail\r
+rem MOVE .\bin\tainavi\PlugIn_RecRD_MAIL$1.class .\javamail\r
+rem MOVE .\bin\tainavi\PlugIn_RecRD_MAIL_Z9500.class .\javamail\r
+rem MOVE .\bin\tainavi\PlugIn_RecRD_MAIL_Z9500$1.class .\javamail\r
+rem MOVE .\bin\tainavi\PlugIn_RecRD_MAIL_Z1.class .\javamail\r
+DEL /Q .\bin\tainavi\PlugIn_CSPYahoo.class\r
+DEL /Q .\bin\tainavi\PlugIn_RecRD_S304K_AVC.class\r
+DEL /Q .\bin\tainavi\TVProgramOrder.class\r
+\r
+RMDIR /S /Q .settings\r
+RMDIR /S /Q CVS\r
+RMDIR /S /Q debug\r
+RMDIR /S /Q passed\r
+RMDIR /S /Q progcache\r
+RMDIR /S /Q skin\r
+RMDIR /S /Q src\r
+DEL /Q .classpath\r
+DEL /Q .project\r
+DEL /Q cmd.exe.lnk\r
+DEL /Q dump.txt\r
+DEL /Q log.txt\r
+DEL /Q log.txt.bak\r
+DEL /Q log_taiSync.txt\r
+DEL /Q log_taiSync.txt.bak\r
+DEL /Q hs_err_pid*.log\r
+DEL /Q snapshot*.*\r
+DEL /Q _lock_\r
+DEL /Q *.htm\r
+DEL /Q tainaviboot.jar\r
+DEL /Q TaiNavi.log\r
+\r
+DEL /Q \8dí\8f\9c.cmd\r