1 /////////////////////////////////////////////////////////////////////////////
2 // Name:        ChaptersDlg.cpp
3 // Purpose:     The chapters dialog
4 // Author:      Alex Thuering
5 // Created:     19.04.2010
6 // RCS-ID:      $Id: ChaptersDlg.cpp,v 1.13 2016/05/16 21:13:35 ntalex Exp $
7 // Copyright:   (c) Alex Thuering
8 // Licence:     GPL
9 /////////////////////////////////////////////////////////////////////////////
10 
11 #include "ChaptersDlg.h"
12 #include "TitlePropDlg.h"
13 #include "Utils.h"
14 #include "Config.h"
15 #include <wx/regex.h>
16 #include <wx/imaglist.h>
17 #include <wxVillaLib/utils.h>
18 #include <wxVillaLib/rc/video.png.h>
19 #include "rc/add.png.h"
20 #include "rc/remove.png.h"
21 #include "rc/preferences.png.h"
22 #include "Utils.h"
23 
24 //(*InternalHeaders(ChaptersDlg)
25 #include <wx/bitmap.h>
26 #include <wx/intl.h>
27 #include <wx/button.h>
28 #include <wx/image.h>
29 #include <wx/string.h>
30 //*)
31 
32 //(*IdInit(ChaptersDlg)
33 const long ChaptersDlg::ID_THUMBNAILS = wxNewId();
34 const long ChaptersDlg::ID_ADD_BT = wxNewId();
35 const long ChaptersDlg::ID_DEL_BT = wxNewId();
36 const long ChaptersDlg::ID_PANEL1 = wxNewId();
37 const long ChaptersDlg::ID_MEDIA_CTRL = wxNewId();
38 const long ChaptersDlg::ID_SLIDER = wxNewId();
39 const long ChaptersDlg::ID_TIME_CTRL = wxNewId();
40 const long ChaptersDlg::ID_TIME_SPINB = wxNewId();
41 const long ChaptersDlg::ID_FRAME_SPINBT = wxNewId();
42 const long ChaptersDlg::ID_RADIOBUTTON1 = wxNewId();
43 const long ChaptersDlg::ID_RADIOBUTTON2 = wxNewId();
44 const long ChaptersDlg::ID_RADIOBUTTON3 = wxNewId();
45 const long ChaptersDlg::ID_STATICTEXT1 = wxNewId();
46 const long ChaptersDlg::ID_CHOICE1 = wxNewId();
47 const long ChaptersDlg::ID_END_CTRL = wxNewId();
48 const long ChaptersDlg::ID_END_SPIN = wxNewId();
49 const long ChaptersDlg::ID_END_FRAME_SPINBT = wxNewId();
50 const long ChaptersDlg::ID_DURATION_CTRL = wxNewId();
51 const long ChaptersDlg::ID_COMMANDS_CTRL = wxNewId();
52 const long ChaptersDlg::ID_PANEL2 = wxNewId();
53 const long ChaptersDlg::ID_SPLITTERWINDOW = wxNewId();
54 const long ChaptersDlg::ID_ADD_CELL_CHECK = wxNewId();
55 //*)
56 
BEGIN_EVENT_TABLE(ChaptersDlg,wxDialog)57 BEGIN_EVENT_TABLE(ChaptersDlg, wxDialog)
58 	//(*EventTable(ChaptersDlg)
59 	//*)
60 	EVT_COMMAND_SCROLL(ID_SLIDER, ChaptersDlg::OnSliderScroll)
61 END_EVENT_TABLE()
62 
63 ChaptersDlg::ChaptersDlg(wxWindow* parent, DVD* dvd, int tsi, int pgci, int vobi, Vob* vob) {
64 	m_dvd = dvd;
65 	m_tsi = tsi;
66 	m_pgci = pgci;
67 	m_vobi = vobi;
68 	m_vob = vob;
69 	VECTOR_COPY(m_vob->GetCells(), m_cells, Cell);
70 	m_selectedCell = NULL;
71 
72 	//(*Initialize(ChaptersDlg)
73 	wxBoxSizer* BoxSizer4;
74 	wxBoxSizer* BoxSizer6;
75 	wxPanel* panel1;
76 	wxStaticText* staticText2;
77 	wxBoxSizer* BoxSizer5;
78 	wxBoxSizer* boxSizer1;
79 	wxStaticText* staticText5;
80 	wxStaticText* staticText4;
81 	wxSplitterWindow* splitterWindow;
82 	wxBoxSizer* boxSizer2;
83 	wxStaticText* staticText3;
84 	wxStaticText* StaticText1;
85 	wxBoxSizer* BoxSizer2;
86 	wxFlexGridSizer* flexGridSizer1;
87 	wxStdDialogButtonSizer* stdDialogButtonSizer;
88 	wxStaticText* staticText1;
89 	wxBoxSizer* BoxSizer1;
90 	wxPanel* panel2;
91 	wxBoxSizer* BoxSizer3;
92 	wxBoxSizer* mainSizer;
93 
94 	Create(parent, wxID_ANY, _("Chapters"), wxDefaultPosition, wxDefaultSize, wxDEFAULT_DIALOG_STYLE|wxRESIZE_BORDER, _T("wxID_ANY"));
95 	SetClientSize(wxSize(800,450));
96 	mainSizer = new wxBoxSizer(wxVERTICAL);
97 	splitterWindow = new wxSplitterWindow(this, ID_SPLITTERWINDOW, wxDefaultPosition, wxDefaultSize, wxSP_3D|wxSP_3DSASH, _T("ID_SPLITTERWINDOW"));
98 	splitterWindow->SetMinSize(wxSize(50,50));
99 	splitterWindow->SetMinimumPaneSize(50);
100 	splitterWindow->SetSashGravity(0.5);
101 	panel1 = new wxPanel(splitterWindow, ID_PANEL1, wxPoint(47,173), wxDefaultSize, wxTAB_TRAVERSAL, _T("ID_PANEL1"));
102 	BoxSizer2 = new wxBoxSizer(wxVERTICAL);
103 	staticText1 = new wxStaticText(panel1, wxID_ANY, _("Chapters:"), wxDefaultPosition, wxDefaultSize, 0, _T("wxID_ANY"));
104 	BoxSizer2->Add(staticText1, 0, wxTOP|wxLEFT|wxRIGHT|wxEXPAND, 5);
105 	BoxSizer3 = new wxBoxSizer(wxHORIZONTAL);
106 	m_thumbnails = new wxThumbnails(panel1,ID_THUMBNAILS);
107 	BoxSizer3->Add(m_thumbnails, 1, wxEXPAND, 5);
108 	boxSizer2 = new wxBoxSizer(wxVERTICAL);
109 	m_addBt = new wxBitmapButton(panel1, ID_ADD_BT, wxBITMAP_FROM_MEMORY(add), wxDefaultPosition, wxDefaultSize, wxBU_AUTODRAW, wxDefaultValidator, _T("ID_ADD_BT"));
110 	boxSizer2->Add(m_addBt, 0, wxALL|wxALIGN_CENTER_HORIZONTAL|wxALIGN_CENTER_VERTICAL, 4);
111 	m_delBt = new wxBitmapButton(panel1, ID_DEL_BT, wxBITMAP_FROM_MEMORY(remove), wxDefaultPosition, wxDefaultSize, wxBU_AUTODRAW, wxDefaultValidator, _T("ID_DEL_BT"));
112 	boxSizer2->Add(m_delBt, 0, wxALL|wxALIGN_CENTER_HORIZONTAL|wxALIGN_CENTER_VERTICAL, 4);
113 	BoxSizer3->Add(boxSizer2, 0, wxEXPAND, 5);
114 	BoxSizer2->Add(BoxSizer3, 1, wxTOP|wxBOTTOM|wxLEFT|wxEXPAND, 5);
115 	panel1->SetSizer(BoxSizer2);
116 	BoxSizer2->Fit(panel1);
117 	BoxSizer2->SetSizeHints(panel1);
118 	panel2 = new wxPanel(splitterWindow, ID_PANEL2, wxDefaultPosition, wxDefaultSize, wxTAB_TRAVERSAL, _T("ID_PANEL2"));
119 	BoxSizer4 = new wxBoxSizer(wxVERTICAL);
120 	m_mediaCtrl = new MediaCtrlFF(panel2, ID_MEDIA_CTRL, wxT(""), wxDefaultPosition,wxDefaultSize, 0, wxDefaultValidator, _T("ID_MEDIA_CTRL"));
121 	m_mediaCtrl->SetMinSize(wxSize(300, 200));
122 	m_mediaCtrl->SetWindowStyle(wxBORDER_NONE);
123 	BoxSizer4->Add(m_mediaCtrl, 1, wxALL|wxEXPAND, 4);
124 	m_slider = new wxSlider(panel2, ID_SLIDER, 0, 0, 100, wxDefaultPosition, wxDefaultSize, 0, wxDefaultValidator, _T("ID_SLIDER"));
125 	BoxSizer4->Add(m_slider, 0, wxEXPAND, 2);
126 	flexGridSizer1 = new wxFlexGridSizer(0, 2, 2, 4);
127 	flexGridSizer1->AddGrowableCol(1);
128 	StaticText1 = new wxStaticText(panel2, wxID_ANY, _("Time:"), wxDefaultPosition, wxDefaultSize, 0, _T("wxID_ANY"));
129 	flexGridSizer1->Add(StaticText1, 0, wxLEFT|wxALIGN_LEFT|wxALIGN_CENTER_VERTICAL, 4);
130 	BoxSizer5 = new wxBoxSizer(wxHORIZONTAL);
131 	m_timeCtrl = new wxTextCtrl(panel2, ID_TIME_CTRL, wxEmptyString, wxDefaultPosition, wxDefaultSize, 0, wxDefaultValidator, _T("ID_TIME_CTRL"));
132 	BoxSizer5->Add(m_timeCtrl, 0, wxEXPAND, 5);
133 	m_timeSpinBt = new wxSpinButton(panel2, ID_TIME_SPINB, wxDefaultPosition, wxDefaultSize, wxSP_VERTICAL|wxSP_ARROW_KEYS, _T("ID_TIME_SPINB"));
134 	m_timeSpinBt->SetRange(0, 100);
135 	m_timeSpinBt->SetMinSize(wxSize(16,12));
136 	BoxSizer5->Add(m_timeSpinBt, 0, wxEXPAND, 5);
137 	m_frameSpinBt = new wxSpinButton(panel2, ID_FRAME_SPINBT, wxDefaultPosition, wxDefaultSize, wxSP_VERTICAL|wxSP_ARROW_KEYS, _T("ID_FRAME_SPINBT"));
138 	m_frameSpinBt->SetRange(-9999, 9999);
139 	m_frameSpinBt->SetMinSize(wxSize(16,12));
140 	BoxSizer5->Add(m_frameSpinBt, 0, wxEXPAND, 5);
141 	flexGridSizer1->Add(BoxSizer5, 0, wxALL|wxEXPAND, 0);
142 	staticText5 = new wxStaticText(panel2, wxID_ANY, _("Type:"), wxDefaultPosition, wxDefaultSize, 0, _T("wxID_ANY"));
143 	flexGridSizer1->Add(staticText5, 0, wxLEFT|wxALIGN_LEFT|wxALIGN_CENTER_VERTICAL, 4);
144 	boxSizer1 = new wxBoxSizer(wxHORIZONTAL);
145 	m_chapterBt = new wxRadioButton(panel2, ID_RADIOBUTTON1, _("Chapter"), wxDefaultPosition, wxDefaultSize, wxRB_GROUP, wxDefaultValidator, _T("ID_RADIOBUTTON1"));
146 	boxSizer1->Add(m_chapterBt, 0, wxALIGN_CENTER_HORIZONTAL|wxALIGN_CENTER_VERTICAL, 4);
147 	m_programBt = new wxRadioButton(panel2, ID_RADIOBUTTON2, _("Program"), wxDefaultPosition, wxDefaultSize, 0, wxDefaultValidator, _T("ID_RADIOBUTTON2"));
148 	boxSizer1->Add(m_programBt, 0, wxALL|wxALIGN_CENTER_HORIZONTAL|wxALIGN_CENTER_VERTICAL, 0);
149 	m_regularBt = new wxRadioButton(panel2, ID_RADIOBUTTON3, _("Regular cell"), wxDefaultPosition, wxDefaultSize, 0, wxDefaultValidator, _T("ID_RADIOBUTTON3"));
150 	boxSizer1->Add(m_regularBt, 0, wxALL|wxALIGN_CENTER_HORIZONTAL|wxALIGN_CENTER_VERTICAL, 0);
151 	flexGridSizer1->Add(boxSizer1, 1, wxALIGN_LEFT|wxALIGN_CENTER_VERTICAL, 5);
152 	StaticText2 = new wxStaticText(panel2, ID_STATICTEXT1, _("End:"), wxDefaultPosition, wxDefaultSize, 0, _T("ID_STATICTEXT1"));
153 	flexGridSizer1->Add(StaticText2, 1, wxLEFT|wxALIGN_LEFT|wxALIGN_CENTER_VERTICAL, 5);
154 	BoxSizer6 = new wxBoxSizer(wxHORIZONTAL);
155 	m_endChoice = new wxChoice(panel2, ID_CHOICE1, wxDefaultPosition, wxDefaultSize, 0, 0, 0, wxDefaultValidator, _T("ID_CHOICE1"));
156 	m_endChoice->SetSelection( m_endChoice->Append(_("Auto")) );
157 	m_endChoice->Append(_("Custom"));
158 	BoxSizer6->Add(m_endChoice, 0, wxALIGN_CENTER_HORIZONTAL|wxALIGN_CENTER_VERTICAL, 0);
159 	m_endCtrl = new wxTextCtrl(panel2, ID_END_CTRL, wxEmptyString, wxDefaultPosition, wxDefaultSize, 0, wxDefaultValidator, _T("ID_END_CTRL"));
160 	BoxSizer6->Add(m_endCtrl, 0, wxLEFT|wxALIGN_CENTER_HORIZONTAL|wxALIGN_CENTER_VERTICAL, 2);
161 	m_endSpinBt = new wxSpinButton(panel2, ID_END_SPIN, wxDefaultPosition, wxDefaultSize, wxSP_VERTICAL|wxSP_ARROW_KEYS, _T("ID_END_SPIN"));
162 	m_endSpinBt->SetRange(0, 100);
163 	m_endSpinBt->SetMinSize(wxSize(16,12));
164 	BoxSizer6->Add(m_endSpinBt, 0, wxEXPAND, 0);
165 	m_endFrameSpinBt = new wxSpinButton(panel2, ID_END_FRAME_SPINBT, wxDefaultPosition, wxDefaultSize, wxSP_VERTICAL|wxSP_ARROW_KEYS, _T("ID_END_FRAME_SPINBT"));
166 	m_endFrameSpinBt->SetRange(-9999, 9999);
167 	m_endFrameSpinBt->SetMinSize(wxSize(16,12));
168 	BoxSizer6->Add(m_endFrameSpinBt, 0, wxEXPAND, 5);
169 	staticText2 = new wxStaticText(panel2, wxID_ANY, _("Pause:"), wxDefaultPosition, wxDefaultSize, 0, _T("wxID_ANY"));
170 	BoxSizer6->Add(staticText2, 0, wxLEFT|wxALIGN_CENTER_HORIZONTAL|wxALIGN_CENTER_VERTICAL, 8);
171 	m_pauseSpin = new wxSpinCtrl(panel2, ID_DURATION_CTRL, _T("0"), wxDefaultPosition, wxSize(60,-1), 0, -1, 999, 0, _T("ID_DURATION_CTRL"));
172 	m_pauseSpin->SetValue(_T("0"));
173 	BoxSizer6->Add(m_pauseSpin, 0, wxLEFT|wxEXPAND, 4);
174 	staticText3 = new wxStaticText(panel2, wxID_ANY, _("sec"), wxDefaultPosition, wxDefaultSize, 0, _T("wxID_ANY"));
175 	BoxSizer6->Add(staticText3, 0, wxLEFT|wxALIGN_CENTER_VERTICAL, 2);
176 	flexGridSizer1->Add(BoxSizer6, 1, wxALL|wxEXPAND, 0);
177 	staticText4 = new wxStaticText(panel2, wxID_ANY, _("Commands:"), wxDefaultPosition, wxDefaultSize, 0, _T("wxID_ANY"));
178 	flexGridSizer1->Add(staticText4, 0, wxLEFT|wxALIGN_LEFT|wxALIGN_CENTER_VERTICAL, 4);
179 	m_commandsCtrl = new wxComboBox(panel2, ID_COMMANDS_CTRL, wxEmptyString, wxDefaultPosition, wxDefaultSize, 0, 0, 0, wxDefaultValidator, _T("ID_COMMANDS_CTRL"));
180 	flexGridSizer1->Add(m_commandsCtrl, 1, wxEXPAND, 4);
181 	BoxSizer4->Add(flexGridSizer1, 0, wxBOTTOM|wxLEFT|wxRIGHT|wxEXPAND, 5);
182 	panel2->SetSizer(BoxSizer4);
183 	BoxSizer4->Fit(panel2);
184 	BoxSizer4->SetSizeHints(panel2);
185 	splitterWindow->SplitVertically(panel1, panel2);
186 	mainSizer->Add(splitterWindow, 1, wxEXPAND, 5);
187 	BoxSizer1 = new wxBoxSizer(wxHORIZONTAL);
188 	m_addCellAtBegin = new wxCheckBox(this, ID_ADD_CELL_CHECK, _("Add cell at begin if it doesn\'t exist"), wxDefaultPosition, wxDefaultSize, 0, wxDefaultValidator, _T("ID_ADD_CELL_CHECK"));
189 	m_addCellAtBegin->SetValue(false);
190 	BoxSizer1->Add(m_addCellAtBegin, 0, wxALIGN_CENTER_VERTICAL, 5);
191 	stdDialogButtonSizer = new wxStdDialogButtonSizer();
192 	stdDialogButtonSizer->AddButton(new wxButton(this, wxID_OK, wxEmptyString));
193 	stdDialogButtonSizer->AddButton(new wxButton(this, wxID_CANCEL, wxEmptyString));
194 	stdDialogButtonSizer->Realize();
195 	BoxSizer1->Add(stdDialogButtonSizer, 1, wxTOP|wxEXPAND, 5);
196 	mainSizer->Add(BoxSizer1, 0, wxBOTTOM|wxLEFT|wxRIGHT|wxEXPAND, 5);
197 	SetSizer(mainSizer);
198 	SetSizer(mainSizer);
199 	Layout();
200 	Center();
201 
202 	Connect(ID_ADD_BT,wxEVT_COMMAND_BUTTON_CLICKED,(wxObjectEventFunction)&ChaptersDlg::OnAddBt);
203 	Connect(ID_DEL_BT,wxEVT_COMMAND_BUTTON_CLICKED,(wxObjectEventFunction)&ChaptersDlg::OnDelBt);
204 	Connect(ID_TIME_CTRL,wxEVT_COMMAND_TEXT_UPDATED,(wxObjectEventFunction)&ChaptersDlg::OnChangeTime);
205 	Connect(ID_TIME_SPINB,wxEVT_SCROLL_THUMBTRACK,(wxObjectEventFunction)&ChaptersDlg::OnTimeSpin);
206 	Connect(ID_FRAME_SPINBT,wxEVT_SCROLL_LINEUP,(wxObjectEventFunction)&ChaptersDlg::OnFrameSpin);
207 	Connect(ID_FRAME_SPINBT,wxEVT_SCROLL_LINEDOWN,(wxObjectEventFunction)&ChaptersDlg::OnFrameSpinDown);
208 	Connect(ID_CHOICE1,wxEVT_COMMAND_CHOICE_SELECTED,(wxObjectEventFunction)&ChaptersDlg::OnEndChoice);
209 	Connect(ID_END_CTRL,wxEVT_COMMAND_TEXT_UPDATED,(wxObjectEventFunction)&ChaptersDlg::OnChangeEnd);
210 	Connect(ID_END_SPIN,wxEVT_SCROLL_THUMBTRACK,(wxObjectEventFunction)&ChaptersDlg::OnEndSpin);
211 	Connect(ID_END_FRAME_SPINBT,wxEVT_SCROLL_LINEUP,(wxObjectEventFunction)&ChaptersDlg::OnEndFrameSpin);
212 	Connect(ID_END_FRAME_SPINBT,wxEVT_SCROLL_LINEDOWN,(wxObjectEventFunction)&ChaptersDlg::OnEndFrameSpinDown);
213 	//*)
214 	Connect(ID_THUMBNAILS, EVT_COMMAND_THUMBNAILS_SEL_CHANGED, (wxObjectEventFunction)&ChaptersDlg::OnSelectionChanged);
215 	m_thumbnails->Connect(wxID_ANY, wxEVT_KEY_DOWN, wxKeyEventHandler(ChaptersDlg::OnKeyDown), (wxObject*) NULL, this);
216 	Connect(wxID_OK, wxEVT_COMMAND_BUTTON_CLICKED,(wxObjectEventFunction)&ChaptersDlg::OnOkBt);
217 	stdDialogButtonSizer->GetAffirmativeButton()->SetDefault();
218 
219     SetIcon(((wxTopLevelWindow*)wxGetTopLevelParent(parent))->GetIcon());
220 
221     int x = 0;
222 	m_timeCtrl->GetTextExtent(wxT("00:00:00.000"), &x, NULL);
223 	m_timeCtrl->SetMinSize(wxSize(x + 10, -1));
224 	m_endCtrl->SetMinSize(wxSize(x + 10, -1));
225 	m_pauseSpin->GetTextExtent(wxT("999"), &x, NULL);
226 	m_pauseSpin->SetMinSize(wxSize(x + 32, -1));
227 	m_mediaCtrl->Load(m_vob->GetFilename());
228 	m_slider->SetMax(m_vob->GetResultDuration());
229 	m_timeSpinBt->SetMax(m_vob->GetResultDuration());
230 	m_endSpinBt->SetMax(m_vob->GetResultDuration());
231 	m_commandsCtrl->Append(TitlePropDlg::GetCommandList(m_dvd, m_tsi));
232 
233 	m_thumbnails->Clear();
234 	for (vector<Cell*>::const_iterator it = m_cells.begin(); it != m_cells.end(); it++) {
235 		AddThumbnail((*it));
236 	}
237 
238 	m_addCellAtBegin->SetValue(m_cells.size() == 0 || m_cells.front()->GetStart() == 0);
239 
240 	if (m_thumbnails->GetItemCount() > 0)
241 		m_thumbnails->SetSelected(0);
242 	wxCommandEvent evt;
243 	OnSelectionChanged(evt);
244     m_thumbnails->SetFocus();
245 }
246 
~ChaptersDlg()247 ChaptersDlg::~ChaptersDlg() {
248 	//(*Destroy(ChaptersDlg)
249 	//*)
250 }
251 
LoadFrame(long pos)252 wxImage ChaptersDlg::LoadFrame(long pos) {
253 	double dpos = ((double) pos)/1000;
254 	wxImage image;
255 	wxFfmpegMediaDecoder& decoder = m_mediaCtrl->GetDecoder();
256 	if (decoder.SetPosition(dpos >= 1 ? dpos - 1 : 0)) {
257 		for (int i = 0; i < 60; i++) {
258 			image = decoder.GetNextFrame();
259 			if (decoder.GetPosition() >= dpos)
260 				break;
261 		}
262 	}
263 	return image.Ok() ? image : wxBITMAP_FROM_MEMORY(video).ConvertToImage();
264 }
265 
AddThumbnail(Cell * cell,int pos)266 void ChaptersDlg::AddThumbnail(Cell* cell, int pos) {
267 	wxThumb* thumb = new wxThumb(wxT(""), cell->GetStartStr());
268 	thumb->SetImage(LoadFrame(cell->GetStart() + (m_vob->GetStartTime()*1000)));
269 	m_thumbnails->InsertItem(thumb, pos);
270 }
271 
OnAddBt(wxCommandEvent & event)272 void ChaptersDlg::OnAddBt(wxCommandEvent& event) {
273 	long start = m_cells.size() > 0 ? m_cells.back()->GetStart() + s_config.GetDefChapterLength() * 60000 : 0;
274 	long duration = m_vob->GetResultDuration()*1000;
275 	Cell* cell = new Cell(start < duration ? start : (duration > 1000 ? duration - 1000 - (duration % 1000): 0));
276 	m_cells.push_back(cell);
277 	AddThumbnail(cell);
278 	m_thumbnails->Refresh();
279 }
280 
OnDelBt(wxCommandEvent & event)281 void ChaptersDlg::OnDelBt(wxCommandEvent& event) {
282 	if (m_thumbnails->GetSelected() < 0)
283 		return;
284 	m_selectedCell = NULL;
285 	m_thumbnails->RemoveItemAt(m_thumbnails->GetSelected());
286 	m_cells.erase(m_cells.begin() + m_thumbnails->GetSelected());
287 	if (m_thumbnails->GetSelected() >= m_thumbnails->GetItemCount()) {
288 		m_thumbnails->SetSelected(m_thumbnails->GetItemCount() - 1);
289 	}
290 	wxCommandEvent evt;
291 	OnSelectionChanged(evt);
292 	m_thumbnails->Refresh();
293 }
294 
OnSelectionChanged(wxCommandEvent & event)295 void ChaptersDlg::OnSelectionChanged(wxCommandEvent& event) {
296 	if (m_selectedCell != NULL && !CheckCellValues()) {
297 		for (unsigned int idx = 0; idx < m_cells.size(); idx++) {
298 			if (m_cells[idx] == m_selectedCell) {
299 				m_thumbnails->SetSelected(idx);
300 				break;
301 			}
302 		}
303 		return;
304 	}
305 
306 	int idx = m_thumbnails->GetSelected();
307 	m_selectedCell = idx >= 0 ? m_cells[idx] : NULL;
308 
309 	bool enable = idx >= 0;
310 	m_delBt->Enable(enable);
311 	m_slider->Enable(enable);
312 	m_timeCtrl->Enable(enable);
313 	m_timeSpinBt->Enable(enable);
314 	m_frameSpinBt->Enable(enable);
315 	m_endChoice->Enable(enable);
316 	m_endSpinBt->Enable(enable);
317 	m_endCtrl->Enable(enable);
318 	m_pauseSpin->Enable(enable);
319 	m_regularBt->Enable(enable);
320 	m_chapterBt->Enable(enable);
321 	m_programBt->Enable(enable);
322 	m_commandsCtrl->Enable(enable);
323 	if (enable) {
324 		m_endChoice->SetSelection(m_selectedCell->GetEnd() == -1 ? 0 : 1);
325 		m_endSpinBt->SetValue(m_selectedCell->GetEnd() >= 0 ? m_selectedCell->GetEnd() / 1000 : 0);
326 		m_endCtrl->ChangeValue(m_selectedCell->GetEnd() >= 0 ? Time2String(m_selectedCell->GetEnd(), true) : wxString(wxT("")));
327 		m_pauseSpin->SetValue(m_selectedCell->GetPause());
328 		m_regularBt->SetValue(true);
329 		m_chapterBt->SetValue(m_selectedCell->IsChapter());
330 		m_programBt->SetValue(m_selectedCell->IsProgram());
331 		m_commandsCtrl->SetValue(m_selectedCell->GetCommands());
332 		SeekVideo(m_selectedCell->GetStart());
333 	} else {
334 		SeekVideo(0);
335 	}
336 }
337 
OnKeyDown(wxKeyEvent & event)338 void ChaptersDlg::OnKeyDown(wxKeyEvent& event) {
339 	switch (event.GetKeyCode()) {
340 	case WXK_DELETE: {
341 		wxCommandEvent evt;
342 		OnDelBt(evt);
343 		break;
344 	}
345 	default:
346 		event.Skip();
347 		break;
348 	}
349 }
350 
SeekVideo(long pos,bool updateTimeCtrl)351 void ChaptersDlg::SeekVideo(long pos, bool updateTimeCtrl) {
352 	m_slider->SetValue(lround(pos / 1000));
353 	m_timeSpinBt->SetValue(lround(pos / 1000));
354 	m_mediaCtrl->Seek((wxFileOffset) (pos + (m_vob->GetStartTime()*1000)));
355 	if (updateTimeCtrl)
356 		m_timeCtrl->ChangeValue(Time2String(pos, true));
357 
358 	int idx = m_thumbnails->GetSelected();
359 	if (idx < 0)
360 		return;
361 	Cell* cell = m_cells[idx];
362 	cell->SetStart(pos);
363 	int idx2 = idx;
364 	while (idx2 > 0 && cell->GetStart() < m_cells[idx2-1]->GetStart())
365 		idx2--;
366 	while (idx2 < (int) m_cells.size() - 1 && cell->GetStart() > m_cells[idx2+1]->GetStart())
367 		idx2++;
368 	if (idx2 != idx) {
369 		m_cells.erase(m_cells.begin() + idx);
370 		m_cells.insert(m_cells.begin() + idx2, cell);
371 		m_thumbnails->RemoveItemAt(idx);
372 		AddThumbnail(cell, idx2);
373 		m_thumbnails->SetSelected(idx2);
374 	}
375 	m_thumbnails->GetSelectedItem()->SetCaption(cell->GetStartStr());
376 	wxImage image = m_mediaCtrl->GetImage();
377 	m_thumbnails->GetSelectedItem()->SetImage(image.Ok() ? image : wxBITMAP_FROM_MEMORY(video).ConvertToImage());
378 	m_thumbnails->GetSelectedItem()->Update();
379 	m_thumbnails->Refresh();
380 	wxCommandEvent evt;
381 	OnEndChoice(evt); // update end time if selected "auto"
382 }
383 
OnChangeTime(wxCommandEvent & event)384 void ChaptersDlg::OnChangeTime(wxCommandEvent& event) {
385 	if (s_timeRE.Matches(m_timeCtrl->GetValue())) {
386 		SeekVideo(String2Time(m_timeCtrl->GetValue(), m_mediaCtrl->GetFps()), false);
387 	}
388 }
389 
OnTimeSpin(wxSpinEvent & event)390 void ChaptersDlg::OnTimeSpin(wxSpinEvent& event) {
391 	SeekVideo(((long)m_timeSpinBt->GetValue())*1000);
392 }
393 
OnSliderScroll(wxScrollEvent & event)394 void ChaptersDlg::OnSliderScroll(wxScrollEvent& event) {
395 	SeekVideo(((long)m_slider->GetValue())*1000);
396 }
397 
OnEndChoice(wxCommandEvent & event)398 void ChaptersDlg::OnEndChoice(wxCommandEvent& event) {
399 	if (m_endChoice->GetSelection() == 0) {
400 		int idx = m_thumbnails->GetSelected();
401 		Cell* nextCell = idx >= 0 && idx + 1 < (int) m_cells.size() ? m_cells[idx + 1] : NULL;
402 		long endTime = nextCell ? nextCell->GetStart() : m_vob->GetResultDuration()*1000;
403 		m_endSpinBt->SetValue(endTime / 1000);
404 		m_endCtrl->ChangeValue(Time2String(endTime, true));
405 	}
406 	m_endCtrl->Enable(m_endChoice->GetSelection() > 0);
407 	m_endSpinBt->Enable(m_endChoice->GetSelection() > 0);
408 	m_endFrameSpinBt->Enable(m_endChoice->GetSelection() > 0);
409 }
410 
OnChangeEnd(wxCommandEvent & event)411 void ChaptersDlg::OnChangeEnd(wxCommandEvent& event) {
412 	if (s_timeRE.Matches(m_endCtrl->GetValue())) {
413 		m_endSpinBt->SetValue(String2Time(m_endCtrl->GetValue(), m_mediaCtrl->GetFps()) / 1000);
414 	}
415 }
416 
OnFrameSpin(wxSpinEvent & event)417 void ChaptersDlg::OnFrameSpin(wxSpinEvent& event) {
418 	SeekVideo(String2Time(m_timeCtrl->GetValue(), m_mediaCtrl->GetFps()) + 1000 / m_mediaCtrl->GetFps());
419 }
420 
OnFrameSpinDown(wxSpinEvent & event)421 void ChaptersDlg::OnFrameSpinDown(wxSpinEvent& event) {
422 	long pos = String2Time(m_timeCtrl->GetValue(), m_mediaCtrl->GetFps()) - 1000 / m_mediaCtrl->GetFps();
423 	SeekVideo(pos >= 0 ? pos : 0);
424 }
425 
OnEndSpin(wxSpinEvent & event)426 void ChaptersDlg::OnEndSpin(wxSpinEvent& event) {
427 	m_endCtrl->ChangeValue(Time2String(m_endSpinBt->GetValue() * 1000, true));
428 }
429 
OnEndFrameSpin(wxSpinEvent & event)430 void ChaptersDlg::OnEndFrameSpin(wxSpinEvent& event) {
431 	long pos = String2Time(m_endCtrl->GetValue(), m_mediaCtrl->GetFps()) + 1000 / m_mediaCtrl->GetFps();
432 	m_endCtrl->ChangeValue(Time2String(pos, true));
433 }
434 
OnEndFrameSpinDown(wxSpinEvent & event)435 void ChaptersDlg::OnEndFrameSpinDown(wxSpinEvent& event) {
436 	long pos = String2Time(m_endCtrl->GetValue(), m_mediaCtrl->GetFps()) - 1000 / m_mediaCtrl->GetFps();
437 	m_endCtrl->ChangeValue(Time2String(pos >= 0 ? pos : 0, true));
438 }
439 
CheckCellValues()440 bool ChaptersDlg::CheckCellValues() {
441 	if (m_selectedCell == NULL)
442 		return true;
443 
444 	if (!s_timeRE.Matches(m_timeCtrl->GetValue())) {
445 		wxLogError(_("'%s' is not valid time"), m_timeCtrl->GetValue().c_str());
446 		return false;
447 	}
448 	if (m_endChoice->GetSelection() > 0 && m_endCtrl->GetValue().length() > 0
449 			&& !s_timeRE.Matches(m_endCtrl->GetValue())) {
450 		wxLogError(_("'%s' is not valid time"), m_endCtrl->GetValue().c_str());
451 		return false;
452 	}
453 	if (m_endChoice->GetSelection() > 0 && m_endCtrl->GetValue().length() > 0
454 			&& String2Time(m_timeCtrl->GetValue()) > String2Time(m_endCtrl->GetValue())) {
455 		wxLogError(_("Start time cannot be after end time"));
456 		return false;
457 	}
458 	DVDAction action;
459 	action.SetCustom(m_commandsCtrl->GetValue());
460 	if (m_commandsCtrl->GetValue().length() && !action.IsValid(m_dvd, m_tsi, m_pgci, false, wxT(""), true, false)) {
461 		return false;
462 	}
463 
464 	// update
465 	m_selectedCell->SetStart(String2Time(m_timeCtrl->GetValue(), m_mediaCtrl->GetFps()));
466 	m_selectedCell->SetEnd(m_endChoice->GetSelection() > 0 && m_endCtrl->GetValue().length()
467 			? String2Time(m_endCtrl->GetValue(), m_mediaCtrl->GetFps()) : -1);
468 	m_selectedCell->SetPause(m_pauseSpin->GetValue());
469 	m_selectedCell->SetCommands(m_commandsCtrl->GetValue());
470 	m_selectedCell->SetChapter(m_chapterBt->GetValue());
471 	m_selectedCell->SetProgram(m_programBt->GetValue());
472 	return true;
473 }
474 
OnOkBt(wxCommandEvent & event)475 void ChaptersDlg::OnOkBt(wxCommandEvent& event) {
476 	if (!CheckCellValues())
477 		return;
478 	// update VOB
479 	if (m_cells.size() == 0 && m_vobi == 0)
480 		m_cells.push_back(new Cell(0));
481 	else if (m_cells.size() && m_cells.front()->GetStart() != 0 && m_addCellAtBegin->GetValue())
482 		m_cells.insert(m_cells.begin(), new Cell(0, m_vobi == 0));
483 	m_vob->GetCells().swap(m_cells);
484 	EndModal(wxID_OK);
485 }
486