OSDN Git Service

Refactored SysinfoModel and PreferencesModel. Also made those classes thread-safe!
[x264-launcher/x264-launcher.git] / src / model_jobList.cpp
1 ///////////////////////////////////////////////////////////////////////////////
2 // Simple x264 Launcher
3 // Copyright (C) 2004-2014 LoRd_MuldeR <MuldeR2@GMX.de>
4 //
5 // This program is free software; you can redistribute it and/or modify
6 // it under the terms of the GNU General Public License as published by
7 // the Free Software Foundation; either version 2 of the License, or
8 // (at your option) any later version.
9 //
10 // This program is distributed in the hope that it will be useful,
11 // but WITHOUT ANY WARRANTY; without even the implied warranty of
12 // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
13 // GNU General Public License for more details.
14 //
15 // You should have received a copy of the GNU General Public License along
16 // with this program; if not, write to the Free Software Foundation, Inc.,
17 // 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
18 //
19 // http://www.gnu.org/licenses/gpl-2.0.txt
20 ///////////////////////////////////////////////////////////////////////////////
21
22 #include "global.h"
23 #include "model_jobList.h"
24 #include "thread_encode.h"
25 #include "model_options.h"
26 #include "model_preferences.h"
27 #include "resource.h"
28
29 #include <QIcon>
30 #include <QFileInfo>
31
32 JobListModel::JobListModel(PreferencesModel *preferences)
33 {
34         m_preferences = preferences;
35 }
36
37 JobListModel::~JobListModel(void)
38 {
39         while(!m_jobs.isEmpty())
40         {
41                 QUuid id = m_jobs.takeFirst();
42                 EncodeThread *thread = m_threads.value(id, NULL);
43                 LogFileModel *logFile = m_logFile.value(id, NULL);
44                 X264_DELETE(thread);
45                 X264_DELETE(logFile);
46         }
47 }
48
49 ///////////////////////////////////////////////////////////////////////////////
50 // Model interface
51 ///////////////////////////////////////////////////////////////////////////////
52
53 int JobListModel::columnCount(const QModelIndex &parent) const
54 {
55         return 4;
56 }
57
58 int JobListModel::rowCount(const QModelIndex &parent) const
59 {
60         return m_jobs.count();
61 }
62
63 QVariant JobListModel::headerData(int section, Qt::Orientation orientation, int role) const 
64 {
65         if((orientation == Qt::Horizontal) && (role == Qt::DisplayRole))
66         {
67                 switch(section)
68                 {
69                 case 0:
70                         return QVariant::fromValue<QString>(tr("Job"));
71                         break;
72                 case 1:
73                         return QVariant::fromValue<QString>(tr("Status"));
74                         break;
75                 case 2:
76                         return QVariant::fromValue<QString>(tr("Progress"));
77                         break;
78                 case 3:
79                         return QVariant::fromValue<QString>(tr("Details"));
80                         break;
81                 default:
82                         return QVariant();
83                         break;
84                 }
85         }
86
87         return QVariant();
88 }
89
90 QModelIndex JobListModel::index(int row, int column, const QModelIndex &parent) const
91 {
92         return createIndex(row, column, NULL);
93 }
94
95 QModelIndex JobListModel::parent(const QModelIndex &index) const
96 {
97         return QModelIndex();
98 }
99
100 QVariant JobListModel::data(const QModelIndex &index, int role) const
101 {
102         if(role == Qt::DisplayRole)
103         {
104                 if(index.row() >= 0 && index.row() < m_jobs.count())
105                 {
106                         switch(index.column())
107                         {
108                         case 0:
109                                 return m_name.value(m_jobs.at(index.row()));
110                                 break;
111                         case 1:
112                                 switch(m_status.value(m_jobs.at(index.row())))
113                                 {
114                                 case JobStatus_Enqueued:
115                                         return QVariant::fromValue<QString>(tr("Enqueued."));
116                                         break;
117                                 case JobStatus_Starting:
118                                         return QVariant::fromValue<QString>(tr("Starting..."));
119                                         break;
120                                 case JobStatus_Indexing:
121                                         return QVariant::fromValue<QString>(tr("Indexing..."));
122                                         break;
123                                 case JobStatus_Running:
124                                         return QVariant::fromValue<QString>(tr("Running..."));
125                                         break;
126                                 case JobStatus_Running_Pass1:
127                                         return QVariant::fromValue<QString>(tr("Running... (Pass 1)"));
128                                         break;
129                                 case JobStatus_Running_Pass2:
130                                         return QVariant::fromValue<QString>(tr("Running... (Pass 2)"));
131                                         break;
132                                 case JobStatus_Completed:
133                                         return QVariant::fromValue<QString>(tr("Completed."));
134                                         break;
135                                 case JobStatus_Failed:
136                                         return QVariant::fromValue<QString>(tr("Failed!"));
137                                         break;
138                                 case JobStatus_Pausing:
139                                         return QVariant::fromValue<QString>(tr("Pausing..."));
140                                         break;
141                                 case JobStatus_Paused:
142                                         return QVariant::fromValue<QString>(tr("Paused."));
143                                         break;
144                                 case JobStatus_Resuming:
145                                         return QVariant::fromValue<QString>(tr("Resuming..."));
146                                         break;
147                                 case JobStatus_Aborting:
148                                         return QVariant::fromValue<QString>(tr("Aborting..."));
149                                         break;
150                                 case JobStatus_Aborted:
151                                         return QVariant::fromValue<QString>(tr("Aborted!"));
152                                         break;
153                                 default:
154                                         return QVariant::fromValue<QString>(tr("(Unknown)"));
155                                         break;
156                                 }
157                                 break;
158                         case 2:
159                                 return QString().sprintf("%d%%", m_progress.value(m_jobs.at(index.row())));
160                                 break;
161                         case 3:
162                                 return m_details.value(m_jobs.at(index.row()));
163                                 break;
164                         default:
165                                 return QVariant();
166                                 break;
167                         }
168                 }
169         }
170         else if(role == Qt::DecorationRole)
171         {
172                 if(index.row() >= 0 && index.row() < m_jobs.count() && index.column() == 0)
173                 {
174                         switch(m_status.value(m_jobs.at(index.row())))
175                         {
176                         case JobStatus_Enqueued:
177                                 return QIcon(":/buttons/hourglass.png");
178                                 break;
179                         case JobStatus_Starting:
180                                 return QIcon(":/buttons/lightning.png");
181                                 break;
182                         case JobStatus_Indexing:
183                                 return QIcon(":/buttons/find.png");
184                                 break;
185                         case JobStatus_Running:
186                         case JobStatus_Running_Pass1:
187                         case JobStatus_Running_Pass2:
188                                 return QIcon(":/buttons/play.png");
189                                 break;
190                         case JobStatus_Completed:
191                                 return QIcon(":/buttons/accept.png");
192                                 break;
193                         case JobStatus_Failed:
194                                 return QIcon(":/buttons/exclamation.png");
195                                 break;
196                         case JobStatus_Pausing:
197                                 return QIcon(":/buttons/clock_pause.png");
198                                 break;
199                         case JobStatus_Paused:
200                                 return QIcon(":/buttons/suspended.png");
201                                 break;
202                         case JobStatus_Resuming:
203                                 return QIcon(":/buttons/clock_play.png");
204                                 break;
205                         case JobStatus_Aborting:
206                                 return QIcon(":/buttons/clock_stop.png");
207                                 break;
208                         case JobStatus_Aborted:
209                                 return QIcon(":/buttons/error.png");
210                                 break;
211                         default:
212                                 return QVariant();
213                                 break;
214                         }
215                 }
216         }
217
218         return QVariant();
219 }
220
221 ///////////////////////////////////////////////////////////////////////////////
222 // Public interface
223 ///////////////////////////////////////////////////////////////////////////////
224
225 QModelIndex JobListModel::insertJob(EncodeThread *thread)
226 {
227         QUuid id = thread->getId();
228
229         if(m_jobs.contains(id))
230         {
231                 return QModelIndex();
232         }
233         
234         QString config = "N/A";
235
236         switch(thread->options()->rcMode())
237         {
238         case OptionsModel::RCMode_CRF:
239                 config = QString("CRF@%1").arg(QString::number(thread->options()->quantizer()));
240                 break;
241         case OptionsModel::RCMode_CQ:
242                 config = QString("CQ@%1").arg(QString::number(qRound(thread->options()->quantizer())));
243                 break;
244         case OptionsModel::RCMode_2Pass:
245                 config = QString("2Pass@%1").arg(QString::number(thread->options()->bitrate()));
246                 break;
247         case OptionsModel::RCMode_ABR:
248                 config = QString("ABR@%1").arg(QString::number(thread->options()->bitrate()));
249                 break;
250         }
251
252         int n = 2;
253         QString jobName = QString("%1 (%2)").arg(QFileInfo(thread->sourceFileName()).completeBaseName().simplified(), config);
254
255         forever
256         {
257                 bool unique = true;
258                 for(int i = 0; i < m_jobs.count(); i++)
259                 {
260                         if(m_name.value(m_jobs.at(i)).compare(jobName, Qt::CaseInsensitive) == 0)
261                         {
262                                 unique = false;
263                                 break;
264                         }
265                 }
266                 if(!unique)
267                 {
268                         jobName = QString("%1 %2 (%3)").arg(QFileInfo(thread->sourceFileName()).completeBaseName().simplified(), QString::number(n++), config);
269                         continue;
270                 }
271                 break;
272         }
273         
274         LogFileModel *logFile = new LogFileModel(thread->sourceFileName(), thread->outputFileName(), config);
275         
276         beginInsertRows(QModelIndex(), m_jobs.count(), m_jobs.count());
277         m_jobs.append(id);
278         m_name.insert(id, jobName);
279         m_status.insert(id, JobStatus_Enqueued);
280         m_progress.insert(id, 0);
281         m_threads.insert(id, thread);
282         m_logFile.insert(id, logFile);
283         m_details.insert(id, tr("Not started yet."));
284         endInsertRows();
285
286         connect(thread, SIGNAL(statusChanged(QUuid, JobStatus)), this, SLOT(updateStatus(QUuid, JobStatus)), Qt::QueuedConnection);
287         connect(thread, SIGNAL(progressChanged(QUuid, unsigned int)), this, SLOT(updateProgress(QUuid, unsigned int)), Qt::QueuedConnection);
288         connect(thread, SIGNAL(messageLogged(QUuid, QString)), logFile, SLOT(addLogMessage(QUuid, QString)), Qt::QueuedConnection);
289         connect(thread, SIGNAL(detailsChanged(QUuid, QString)), this, SLOT(updateDetails(QUuid, QString)), Qt::QueuedConnection);
290         
291         return createIndex(m_jobs.count() - 1, 0, NULL);
292 }
293
294 bool JobListModel::startJob(const QModelIndex &index)
295 {
296         if(index.isValid() && index.row() >= 0 && index.row() < m_jobs.count())
297         {
298                 QUuid id = m_jobs.at(index.row());
299                 if(m_status.value(id) == JobStatus_Enqueued)
300                 {
301                         updateStatus(id, JobStatus_Starting);
302                         updateDetails(id, tr("Starting up, please wait..."));
303                         m_threads.value(id)->start();
304                         return true;
305                 }
306         }
307
308         return false;
309 }
310
311 bool JobListModel::pauseJob(const QModelIndex &index)
312 {
313         if(index.isValid() && index.row() >= 0 && index.row() < m_jobs.count())
314         {
315                 QUuid id = m_jobs.at(index.row());
316                 JobStatus status = m_status.value(id);
317                 if((status == JobStatus_Indexing) || (status == JobStatus_Running) ||
318                         (status == JobStatus_Running_Pass1) || (status == JobStatus_Running_Pass2))
319                 {
320                         updateStatus(id, JobStatus_Pausing);
321                         m_threads.value(id)->pauseJob();
322                         return true;
323                 }
324         }
325
326         return false;
327 }
328
329 bool JobListModel::resumeJob(const QModelIndex &index)
330 {
331         if(index.isValid() && index.row() >= 0 && index.row() < m_jobs.count())
332         {
333                 QUuid id = m_jobs.at(index.row());
334                 JobStatus status = m_status.value(id);
335                 if(status == JobStatus_Paused)
336                 {
337                         updateStatus(id, JobStatus_Resuming);
338                         m_threads.value(id)->resumeJob();
339                         return true;
340                 }
341         }
342
343         return false;
344 }
345
346 bool JobListModel::abortJob(const QModelIndex &index)
347 {
348         if(index.isValid() && index.row() >= 0 && index.row() < m_jobs.count())
349         {
350                 QUuid id = m_jobs.at(index.row());
351                 if(m_status.value(id) == JobStatus_Indexing || m_status.value(id) == JobStatus_Running ||
352                         m_status.value(id) == JobStatus_Running_Pass1 || JobStatus_Running_Pass2)
353                 {
354                         updateStatus(id, JobStatus_Aborting);
355                         m_threads.value(id)->abortJob();
356                         return true;
357                 }
358         }
359
360         return false;
361 }
362
363 bool JobListModel::deleteJob(const QModelIndex &index)
364 {
365         if(index.isValid() && index.row() >= 0 && index.row() < m_jobs.count())
366         {
367                 QUuid id = m_jobs.at(index.row());
368                 if(m_status.value(id) == JobStatus_Completed || m_status.value(id) == JobStatus_Failed ||
369                         m_status.value(id) == JobStatus_Aborted || m_status.value(id) == JobStatus_Enqueued)
370                 {
371                         int idx = index.row();
372                         QUuid id = m_jobs.at(idx);
373                         EncodeThread *thread = m_threads.value(id, NULL);
374                         LogFileModel *logFile = m_logFile.value(id, NULL);
375                         if((thread == NULL) || (!thread->isRunning()))
376                         {
377                                 
378                                 beginRemoveRows(QModelIndex(), idx, idx);
379                                 m_jobs.removeAt(index.row());
380                                 m_name.remove(id);
381                                 m_threads.remove(id);
382                                 m_status.remove(id);
383                                 m_progress.remove(id);
384                                 m_logFile.remove(id);
385                                 m_details.remove(id);
386                                 endRemoveRows();
387                                 X264_DELETE(thread);
388                                 X264_DELETE(logFile);
389                                 return true;
390                         }
391                 }
392         }
393
394         return false;
395 }
396
397 LogFileModel *JobListModel::getLogFile(const QModelIndex &index)
398 {
399         if(index.isValid() && index.row() >= 0 && index.row() < m_jobs.count())
400         {
401                 return m_logFile.value(m_jobs.at(index.row()));
402         }
403
404         return NULL;
405 }
406
407 const QString &JobListModel::getJobSourceFile(const QModelIndex &index)
408 {
409         static QString nullStr;
410         
411         if(index.isValid() && index.row() >= 0 && index.row() < m_jobs.count())
412         {
413                 EncodeThread *thread = m_threads.value(m_jobs.at(index.row()));
414                 return (thread != NULL) ? thread->sourceFileName() : nullStr;
415         }
416
417         return nullStr;
418 }
419
420 const QString &JobListModel::getJobOutputFile(const QModelIndex &index)
421 {
422         static QString nullStr;
423         
424         if(index.isValid() && index.row() >= 0 && index.row() < m_jobs.count())
425         {
426                 EncodeThread *thread = m_threads.value(m_jobs.at(index.row()));
427                 return (thread != NULL) ? thread->outputFileName() : nullStr;
428         }
429
430         return nullStr;
431 }
432
433 JobStatus JobListModel::getJobStatus(const QModelIndex &index)
434 {
435         if(index.isValid() && index.row() >= 0 && index.row() < m_jobs.count())
436         {
437                 return m_status.value(m_jobs.at(index.row()));
438         }
439
440         return static_cast<JobStatus>(-1);
441 }
442
443 unsigned int JobListModel::getJobProgress(const QModelIndex &index)
444 {
445         if(index.isValid() && index.row() >= 0 && index.row() < m_jobs.count())
446         {
447                 return m_progress.value(m_jobs.at(index.row()));
448         }
449
450         return 0;
451 }
452
453 const OptionsModel *JobListModel::getJobOptions(const QModelIndex &index)
454 {
455         static QString nullStr;
456         
457         if(index.isValid() && index.row() >= 0 && index.row() < m_jobs.count())
458         {
459                 EncodeThread *thread = m_threads.value(m_jobs.at(index.row()));
460                 return (thread != NULL) ? thread->options() : NULL;
461         }
462
463         return NULL;
464 }
465
466 QModelIndex JobListModel::getJobIndexById(const QUuid &id)
467 {
468         if(m_jobs.contains(id))
469         {
470                 return createIndex(m_jobs.indexOf(id), 0);
471         }
472
473         return QModelIndex();
474 }
475
476 ///////////////////////////////////////////////////////////////////////////////
477 // Slots
478 ///////////////////////////////////////////////////////////////////////////////
479
480 void JobListModel::updateStatus(const QUuid &jobId, JobStatus newStatus)
481 {
482         int index = -1;
483         
484         if((index = m_jobs.indexOf(jobId)) >= 0)
485         {
486                 m_status.insert(jobId, newStatus);
487                 emit dataChanged(createIndex(index, 0), createIndex(index, 1));
488
489                 if(m_preferences->getEnableSounds())
490                 {
491                         switch(newStatus)
492                         {
493                         case JobStatus_Completed:
494                                 x264_play_sound(IDR_WAVE4, true);
495                                 break;
496                         case JobStatus_Aborted:
497                                 x264_play_sound(IDR_WAVE5, true);
498                                 break;
499                         case JobStatus_Failed:
500                                 x264_play_sound(IDR_WAVE6, true);
501                                 break;
502                         }
503                 }
504         }
505 }
506
507 void JobListModel::updateProgress(const QUuid &jobId, unsigned int newProgress)
508 {
509         int index = -1;
510
511         if((index = m_jobs.indexOf(jobId)) >= 0)
512         {
513                 m_progress.insert(jobId, qBound(0U, newProgress, 100U));
514                 emit dataChanged(createIndex(index, 2), createIndex(index, 2));
515         }
516 }
517
518 void JobListModel::updateDetails(const QUuid &jobId, const QString &details)
519 {
520         int index = -1;
521
522         if((index = m_jobs.indexOf(jobId)) >= 0)
523         {
524                 m_details.insert(jobId, details);
525                 emit dataChanged(createIndex(index, 3), createIndex(index, 3));
526         }
527 }