1 /////////////////////////////////////////////////////////////////////////////
2 // Name:        ProgressDlg.cpp
3 // Author:      Alex Thuering
4 // Created:     14.08.2004
5 // RCS-ID:      $Id: ProgressDlg.cpp,v 1.182 2016/12/11 10:06:31 ntalex Exp $
6 // Copyright:   (c) Alex Thuering
7 // Licence:     GPL
8 /////////////////////////////////////////////////////////////////////////////
9 
10 #include "ProgressDlg.h"
11 #include "MainWin.h"
12 #include "BurnDlg.h"
13 #include "DVD.h"
14 #include "Cache.h"
15 #include "ProcessCleanTemp.h"
16 #include "ProcessProjectInfo.h"
17 #include "ProcessMenu.h"
18 #include "ProcessMenuTransitions.h"
19 #include "ProcessEncode.h"
20 #include "ProcessSlideshow.h"
21 #include "ProcessSubtitles.h"
22 #include "ProcessDvdFilesystem.h"
23 #include "ProcessPreview.h"
24 #include "ProcessIsoImage.h"
25 #include "ProcessEccData.h"
26 #include "ProcessFormatDvd.h"
27 #include "ProcessBurn.h"
28 #include "mediaenc_ffmpeg.h"
29 #include "Config.h"
30 #include "SysUtils.h"
31 #include "Version.h"
32 #include <wxVillaLib/utils.h>
33 #include <wx/filename.h>
34 
35 //(*InternalHeaders(ProgressDlg)
36 #include <wx/intl.h>
37 #include <wx/string.h>
38 //*)
39 
40 class ProcessLog: public wxLog {
41 public:
42 	/** Constructor */
ProcessLog(ProgressDlg * progressDlg)43 	ProcessLog(ProgressDlg* progressDlg) {
44 		this->m_progressDlg = progressDlg;
45 	}
46 protected:
47 	/** Print the message into progress dialog details window. */
DoLog(wxLogLevel level,const wxChar * szString,time_t t)48 	void DoLog(wxLogLevel level, const wxChar* szString, time_t t) {
49 		m_progressDlg->AddDetailMsg(szString, level <= wxLOG_Error ? *wxRED : wxColour(64,64,64));
50 	}
51 private:
52 	ProgressDlg* m_progressDlg;
53 };
54 
55 //(*IdInit(ProgressDlg)
56 const long ProgressDlg::ID_SUMMURY_CTRL = wxNewId();
57 const long ProgressDlg::ID_GAUGE1 = wxNewId();
58 const long ProgressDlg::ID_DETAILS_CTRL = wxNewId();
59 const long ProgressDlg::ID_DETAILS_BT = wxNewId();
60 const long ProgressDlg::ID_CHECKBOX1 = wxNewId();
61 const long ProgressDlg::ID_MINIMIZE_BT = wxNewId();
62 //*)
63 
BEGIN_EVENT_TABLE(ProgressDlg,wxDialog)64 BEGIN_EVENT_TABLE(ProgressDlg,wxDialog)
65 	//(*EventTable(ProgressDlg)
66 	//*)
67 END_EVENT_TABLE()
68 
69 ProgressDlg::ProgressDlg(MainWin* parent, Cache* cache, bool autoStart) {
70 	//(*Initialize(ProgressDlg)
71 	wxStaticText* summaryLabel;
72 	wxBoxSizer* btSizer;
73 	wxBoxSizer* mainSizer;
74 
75 	Create(parent, wxID_ANY, _("Generate DVD"), wxDefaultPosition, wxDefaultSize, wxDEFAULT_DIALOG_STYLE|wxRESIZE_BORDER, _T("wxID_ANY"));
76 	SetClientSize(wxSize(600,600));
77 	mainSizer = new wxBoxSizer(wxVERTICAL);
78 	m_panelSizer = new wxBoxSizer(wxVERTICAL);
79 	summaryLabel = new wxStaticText(this, wxID_ANY, _("Summary:"), wxDefaultPosition, wxDefaultSize, 0, _T("wxID_ANY"));
80 	m_panelSizer->Add(summaryLabel, 0, wxBOTTOM|wxALIGN_LEFT, 2);
81 	m_summaryText = new wxTextCtrl(this, ID_SUMMURY_CTRL, wxEmptyString, wxDefaultPosition, wxDefaultSize, wxTE_MULTILINE|wxTE_READONLY|wxTE_RICH, wxDefaultValidator, _T("ID_SUMMURY_CTRL"));
82 	m_panelSizer->Add(m_summaryText, 1, wxBOTTOM|wxEXPAND, 8);
83 	m_gauge = new wxGauge(this, ID_GAUGE1, 100, wxDefaultPosition, wxDefaultSize, wxGA_HORIZONTAL|wxGA_SMOOTH, wxDefaultValidator, _T("ID_GAUGE1"));
84 	m_panelSizer->Add(m_gauge, 0, wxBOTTOM|wxEXPAND, 4);
85 	m_detailsLabel = new wxStaticText(this, wxID_ANY, _("Details:"), wxDefaultPosition, wxDefaultSize, 0, _T("wxID_ANY"));
86 	m_panelSizer->Add(m_detailsLabel, 0, wxBOTTOM|wxALIGN_LEFT, 2);
87 	m_detailsText = new wxTextCtrl(this, ID_DETAILS_CTRL, wxEmptyString, wxDefaultPosition, wxDefaultSize, wxTE_MULTILINE|wxTE_READONLY|wxTE_RICH, wxDefaultValidator, _T("ID_DETAILS_CTRL"));
88 	m_panelSizer->Add(m_detailsText, 1, wxEXPAND, 0);
89 	mainSizer->Add(m_panelSizer, 1, wxTOP|wxLEFT|wxRIGHT|wxEXPAND, 8);
90 	btSizer = new wxBoxSizer(wxHORIZONTAL);
91 	m_detailsBt = new wxButton(this, ID_DETAILS_BT, _("Hide details"), wxDefaultPosition, wxDefaultSize, 0, wxDefaultValidator, _T("ID_DETAILS_BT"));
92 	btSizer->Add(m_detailsBt, 0, wxALIGN_CENTER_HORIZONTAL|wxALIGN_CENTER_VERTICAL, 5);
93 	m_shutdown = new wxCheckBox(this, ID_CHECKBOX1, _("Turn off PC when finished"), wxDefaultPosition, wxDefaultSize, 0, wxDefaultValidator, _T("ID_CHECKBOX1"));
94 	m_shutdown->SetValue(false);
95 	btSizer->Add(m_shutdown, 0, wxALL|wxALIGN_CENTER_HORIZONTAL|wxALIGN_CENTER_VERTICAL, 5);
96 	btSizer->Add(-1,-1,1, wxALIGN_CENTER_HORIZONTAL|wxALIGN_CENTER_VERTICAL, 5);
97 	m_minimizeBt = new wxButton(this, ID_MINIMIZE_BT, _("Minimize"), wxDefaultPosition, wxDefaultSize, 0, wxDefaultValidator, _T("ID_MINIMIZE_BT"));
98 	btSizer->Add(m_minimizeBt, 0, wxRIGHT|wxALIGN_CENTER_HORIZONTAL|wxALIGN_CENTER_VERTICAL, 8);
99 	m_cancelBt = new wxButton(this, wxID_CANCEL, _("Cancel"), wxDefaultPosition, wxDefaultSize, 0, wxDefaultValidator, _T("wxID_CANCEL"));
100 	btSizer->Add(m_cancelBt, 0, wxALIGN_CENTER_HORIZONTAL|wxALIGN_CENTER_VERTICAL, 5);
101 	mainSizer->Add(btSizer, 0, wxALL|wxEXPAND, 8);
102 	SetSizer(mainSizer);
103 	SetSizer(mainSizer);
104 	Layout();
105 	Center();
106 
107 	Connect(ID_DETAILS_BT,wxEVT_COMMAND_BUTTON_CLICKED,(wxObjectEventFunction)&ProgressDlg::OnHideDetails);
108 	Connect(ID_CHECKBOX1,wxEVT_COMMAND_CHECKBOX_CLICKED,(wxObjectEventFunction)&ProgressDlg::OnShutdownClick);
109 	Connect(ID_MINIMIZE_BT,wxEVT_COMMAND_BUTTON_CLICKED,(wxObjectEventFunction)&ProgressDlg::OnMinimize);
110 	Connect(wxID_CANCEL,wxEVT_COMMAND_BUTTON_CLICKED,(wxObjectEventFunction)&ProgressDlg::OnCancel);
111 	//*)
112 
113 	m_detailsBtLabel = m_detailsBt->GetLabel();
114 	m_detailsBt->SetLabel(_T("<< ") + m_detailsBtLabel);
115     m_detailsText->SetFont(wxFont(8, wxFONTFAMILY_TELETYPE, wxFONTSTYLE_NORMAL, wxFONTWEIGHT_NORMAL));
116 	m_winDisabler = NULL;
117 
118     m_end = false;
119     m_close = false;
120     m_autoStart = autoStart;
121     m_cancel = false;
122     m_step = 0;
123     m_stepCount = 0;
124     m_subStep = 0;
125     m_subStepCount = 0;
126     m_cache = cache;
127 
128 #if !defined(__WXMSW__) && !defined(HAVE_LIBDBUS)
129     m_shutdown->Enable(false);
130 #endif
131 }
132 
~ProgressDlg()133 ProgressDlg::~ProgressDlg() {
134 	//(*Destroy(ProgressDlg)
135 	//*)
136 }
137 
OnHideDetails(wxCommandEvent & event)138 void ProgressDlg::OnHideDetails(wxCommandEvent& event) {
139 	if (m_detailsText->IsShown()) {
140 		m_detailsLabel->Hide();
141 		m_detailsText->Hide();
142 		m_detailsText->Freeze();
143 		m_panelSizer->Detach(m_detailsLabel);
144 		m_panelSizer->Detach(m_detailsText);
145 		int height = m_detailsLabel->GetSize().GetY() + m_detailsText->GetSize().GetY() + 2;
146 		SetSize(GetSize().GetX(), GetSize().GetY() - height);
147 		m_detailsBt->SetLabel(_("Show details") + wxString(_T(" >>")));
148 	} else {
149 		m_detailsLabel->Show();
150 		m_detailsText->Show();
151 		m_detailsText->Thaw();
152 		m_panelSizer->Insert(3, m_detailsLabel, 0, wxBOTTOM, 2);
153 		m_panelSizer->Insert(4, m_detailsText, 1, wxEXPAND, 0);
154 		int height = m_detailsLabel->GetSize().GetY() + m_detailsText->GetSize().GetY() + 2;
155 		SetSize(GetSize().GetX(), GetSize().GetY() + height);
156 		m_detailsBt->SetLabel(_T("<< ") + m_detailsBtLabel);
157 	}
158 }
159 
OnMinimize(wxCommandEvent & event)160 void ProgressDlg::OnMinimize(wxCommandEvent& event) {
161 	wxPlatformInfo info;
162 	if ((info.GetOperatingSystemId() & wxOS_WINDOWS) && !info.CheckOSVersion(6,0))
163 		wxDELETE(m_winDisabler);
164 	((wxFrame*) GetParent())->Iconize();
165 }
166 
OnCancel(wxCommandEvent & event)167 void ProgressDlg::OnCancel(wxCommandEvent& event) {
168 	if (!WasCanceled() && !m_end) {
169 		Cancel();
170 	} else {
171 		m_close = true;
172 		((MainWin*) this->GetParent())->SetProgressBarValue(0, 100);
173 		((MainWin*) this->GetParent())->SetProgressBarState();
174 	}
175 }
176 
177 /** Adds the given message to summary textbox */
AddSummaryMsg(const wxString & message,const wxString & details,const wxColour & colour)178 void ProgressDlg::AddSummaryMsg(const wxString& message, const wxString& details, const wxColour& colour) {
179 	m_summaryText->SetDefaultStyle(wxTextAttr(colour.Ok() ? colour : *wxBLACK));
180 	m_summaryText->AppendText(message + _T("\n"));
181 	m_summaryText->ShowPosition(m_summaryText->GetLastPosition());
182 	AddDetailMsg(details.length() ? details : message, colour.Ok() ? colour : *wxBLACK);
183 	m_lastSummaryMsg = message;
184 }
185 
186 /** Replaces last line with the given text in details textbox */
ReplaceSummaryMsg(const wxString & message)187 void ProgressDlg::ReplaceSummaryMsg(const wxString& message) {
188 	long lastPos = m_summaryText->GetLastPosition();
189 	m_summaryText->Replace(lastPos - m_lastSummaryMsg.length() - 1, lastPos + 1, message + wxT("\n"));
190 	m_summaryText->ShowPosition(m_summaryText->GetLastPosition());
191 	wxYieldIfNeeded();
192 	m_lastSummaryMsg = message;
193 }
194 
195 /** Add the given message to details textbox */
AddDetailMsg(const wxString & message,const wxColour & colour)196 void ProgressDlg::AddDetailMsg(const wxString& message, const wxColour& colour) {
197 	if (m_cancel)
198 		return;
199 	if (colour.Ok())
200 		m_detailsText->SetDefaultStyle(wxTextAttr(colour));
201 	AddDetailText(message + _T("\n"));
202 	m_detailsText->SetDefaultStyle(wxTextAttr(wxColour(64, 64, 64)));
203 }
204 
205 /** Replaces last message with the given message in details textbox */
ReplaceLastDetailMsg(const wxString & message)206 void ProgressDlg::ReplaceLastDetailMsg(const wxString& message) {
207 	ReplaceLastDetailText(message + _T("\n"));
208 }
209 
210 /** Add the given text to details textbox */
AddDetailText(const wxString & text)211 void ProgressDlg::AddDetailText(const wxString& text) {
212 	if (wxLog::GetActiveTarget()->GetVerbose())
213 		fprintf(stderr, "%s", (const char*) text.mb_str());
214 	m_logFile.Write(text);
215 	m_logFile.Flush();
216 	m_detailsText->AppendText(text);
217 	m_detailsText->ShowPosition(m_detailsText->GetLastPosition());
218 	wxYieldIfNeeded();
219 	if (text.StartsWith(wxT("Encoding Mode:"))) {
220 		wxString mode = text.Mid(15);
221 		int len = mode.Find(wxT("HQ"));
222 		if (len > 0)
223 			mode = mode.Mid(0, len).Strip();
224 		else
225 			mode = mode.BeforeFirst(wxT(' '));
226 		if (mode.length() == 0)
227 			mode = _("Copy");
228 		int pos = m_lastSummaryMsg.Find(_("Encoding Mode"));
229 		if (pos > 0) {
230 			ReplaceSummaryMsg(m_lastSummaryMsg.Mid(0, pos + 15) + mode + wxT(")"));
231 		} else {
232 			ReplaceSummaryMsg(m_lastSummaryMsg + wxT(" (") + _("Encoding Mode") + wxT(": ") + mode + wxT(")"));
233 		}
234 	}
235 	m_lastDetailText = text;
236 }
237 
238 /** Replaces last line with the given text in details textbox */
ReplaceLastDetailText(const wxString & text)239 void ProgressDlg::ReplaceLastDetailText(const wxString& text) {
240 	if (wxLog::GetActiveTarget()->GetVerbose())
241 		fprintf(stderr, "%s", (const char*) text.mb_str());
242 	long lastPos = m_detailsText->GetLastPosition();
243 	m_detailsText->Replace(lastPos - m_lastDetailText.length(), lastPos + 1, text);
244 	m_detailsText->ShowPosition(m_detailsText->GetLastPosition());
245 	wxYieldIfNeeded();
246 	m_lastDetailText = text;
247 }
248 
249 /** Sets the step count of generating process */
SetSteps(int stepCount)250 void ProgressDlg::SetSteps(int stepCount) {
251 	m_stepCount = stepCount;
252 	m_step = 0;
253 	InitGauge(stepCount*100);
254 }
255 
UpdateGauge()256 void ProgressDlg::UpdateGauge() {
257 	int subStep = 0;
258 	if (m_subStepCount > 0 && m_subStep > m_subStepCount)
259 		m_subStep = m_subStepCount;
260 	if (m_subStepCount > 0)
261 		subStep = m_subStep * 100 / m_subStepCount;
262 	int step = m_step * 100 + subStep;
263 	m_gauge->SetValue(step);
264 	((MainWin*) this->GetParent())->SetProgressBarValue(step, m_stepCount * 100);
265 }
266 
267 /** Initializes gauge */
InitGauge(int range)268 void ProgressDlg::InitGauge(int range) {
269 	m_gauge->SetRange(range);
270 	((MainWin*) this->GetParent())->SetProgressBarState();
271 }
272 
Failed(const wxString & message)273 void ProgressDlg::Failed(const wxString& message) {
274 	AddSummaryMsg(_("Failed"), message, *wxRED);
275 	((MainWin*) this->GetParent())->SetProgressBarState(true);
276 }
277 
End()278 void ProgressDlg::End() {
279 	if (!WasCanceled())
280 		wxBell();
281 	m_cancelBt->SetLabel(_("Close"));
282 }
283 
WasCanceled()284 bool ProgressDlg::WasCanceled() {
285 	wxYieldIfNeeded();
286 	return m_cancel;
287 }
288 
289 /** Cancel the process */
Cancel()290 void ProgressDlg::Cancel() {
291 	AddSummaryMsg(_("Aborted"), wxEmptyString, *wxRED);
292 	m_cancel = true;
293 }
294 
Start(BurnDlg * burnDlg,DVD * dvd)295 bool ProgressDlg::Start(BurnDlg* burnDlg, DVD* dvd) {
296 	// disable parent window
297 	m_winDisabler = new wxWindowDisabler(this);
298 	// show dialog
299 	Show();
300 	// start
301 	wxLog* previousLog = wxLog::SetActiveTarget(new ProcessLog(this));
302 	// start
303 	Run(burnDlg, dvd);
304 	// end
305 	End();
306 	m_logFile.Close();
307 	// restore log
308 	delete wxLog::SetActiveTarget(previousLog);
309 	m_end = true;
310 	// close the Window or release the controls
311 	if (m_autoStart) {
312 		Close(true);
313 	} else {
314     	while (!m_close) {
315     		wxMilliSleep(100);
316     		wxYield();
317     	}
318     }
319 	wxDELETE(m_winDisabler);
320 	return !m_cancel;
321 }
322 
Run(BurnDlg * burnDlg,DVD * dvd)323 void ProgressDlg::Run(BurnDlg* burnDlg, DVD* dvd) {
324 	if (WasCanceled())
325 		return;
326 
327 	// check if libav or ffmpeg is present
328 #ifdef __WXMSW__
329 	if ((!wxFileExists(wxGetAppPath() + s_config.GetAVConvCmd() + wxT(".exe"))
330 			&& !wxFileExists(wxGetAppPath() + s_config.GetAVConvCmd()))
331 			|| s_config.GetAVConvCmd() == wxT("ffmpeg-vbr")) {
332 		if (wxFileExists(wxGetAppPath() + wxT("avconv.exe")))
333 			s_config.SetAVConvCmd(wxT("avconv"));
334 		else if (wxFileExists(wxGetAppPath() + wxT("ffmpeg.exe")))
335 			s_config.SetAVConvCmd(wxT("ffmpeg"));
336 	}
337 #endif
338 
339 	wxString tmpDir = burnDlg->GetTempDir();
340 	if (tmpDir.Last() != wxFILE_SEP_PATH)
341 		tmpDir += wxFILE_SEP_PATH;
342 	wxString dvdTmpDir = tmpDir + wxString(wxT("dvd-tmp")) + wxFILE_SEP_PATH;
343 	wxString dvdOutDir = tmpDir + wxString(wxT("dvd-out")) + wxFILE_SEP_PATH;
344 	if (burnDlg->DoGenerate()) {
345 		dvdOutDir = burnDlg->GetOutputDir();
346 		if (dvdOutDir.Last() != wxFILE_SEP_PATH)
347 			dvdOutDir += wxFILE_SEP_PATH;
348 	}
349 
350 	// create log file
351 	m_logFile.Open(tmpDir + wxT("dvdstyler.log"), wxT("w"));
352 
353 	// print version
354 	AddDetailMsg(wxT("DVDStyler v") + APP_VERSION);
355 	AddDetailMsg(wxGetOsDescription());
356 	AddDetailMsg((s_config.GetAVConvCmd() == wxT("ffmpeg") ? wxT("FFmpeg: ") : wxT("Libav: "))
357 			+ wxFfmpegMediaEncoder::GetBackendVersion());
358 
359 	// prepare
360 	AddSummaryMsg(_("Prepare"));
361 
362 	// clean temp dir
363 	ProcessCleanTemp cleanTemp(this, tmpDir, dvdTmpDir, dvdOutDir);
364 	if (!cleanTemp.Execute())
365 		return;
366 
367 	// check cache and calculate steps
368 	AddDetailMsg(_("Search for transcoded files in cache"));
369 	m_cache->BeginClean();
370 
371 	// processes
372 	vector<Process*> processes;
373 	processes.push_back(new ProcessProjectInfo(this, dvd)); // project info
374 	processes.push_back(new ProcessMenu(this, dvd, dvdTmpDir)); // menus
375 	processes.push_back(new ProcessEncode(this, dvd, m_cache, dvdTmpDir)); // titles
376 	processes.push_back(new ProcessSlideshow(this, dvd, dvdTmpDir)); // slideshow
377 	processes.push_back(new ProcessSubtitles(this, dvd, m_cache, dvdTmpDir)); // subtitle
378 	processes.push_back(new ProcessMenuTransitions(this, dvd, dvdTmpDir)); // menu transitions
379 	processes.push_back(new ProcessDvdFilesystem(this, dvd, dvdTmpDir, dvdOutDir));
380 	processes.push_back(new ProcessPreview(this, burnDlg, dvdOutDir));
381 	processes.push_back(new ProcessIsoImage(this, burnDlg, dvd, m_cache, dvdOutDir, tmpDir));
382 	processes.push_back(new ProcessEccData(this, burnDlg, tmpDir));
383 	processes.push_back(new ProcessFormatDvd(this, burnDlg));
384 	processes.push_back(new ProcessBurn(this, burnDlg, dvd, dvdOutDir, tmpDir));
385 
386 	// remove unused files from cache
387 	m_cache->EndClean();
388 
389 	// calculate step count
390 	int stepCount = 0;
391 	for (vector<Process*>::iterator it = processes.begin(); it != processes.end(); it++) {
392 		Process* process = *it;
393 		if (process->IsNeedExecute() && process->IsUpdateGauge())
394 			stepCount++;
395 	}
396 	SetSteps(stepCount);
397 
398 	// Start generation
399 	for (vector<Process*>::iterator it = processes.begin(); it != processes.end(); it++) {
400 		Process* process = *it;
401 		if (process->IsNeedExecute() && !process->Execute())
402 			return;
403 	}
404 
405 	if (WasCanceled())
406 		return;
407 
408 	// clear temp directory
409 	if (s_config.GetRemoveTempFiles())
410 		cleanTemp.DeleteTempFiles(burnDlg->DoCreateIso() || burnDlg->DoBurn());
411 
412 	if (burnDlg->DoBurn())
413 		AddSummaryMsg(_("Burning was successful."), wxEmptyString, wxColour(0, 128, 0));
414 	else
415 		AddSummaryMsg(_("Generating was successful."), wxEmptyString, wxColour(0, 128, 0));
416 	wxLog::FlushActive();
417 
418 	if (DoShutdown()) {
419 		SystemManager::Stop();
420 	}
421 }
422 
423 
OnShutdownClick(wxCommandEvent & event)424 void ProgressDlg::OnShutdownClick(wxCommandEvent& event) {
425 	if (event.IsChecked() && !SystemManager::CanStop()) {
426 		wxMessageBox(wxT("You don't have the privileges to turn off the computer."),
427 				wxTheApp->GetAppName(), wxICON_ERROR);
428 		m_shutdown->SetValue(false);
429 	}
430 }
431