1 /***************************************************************************
2 SaveBlocksPlugin.cpp - Plugin for saving blocks between labels
3 -------------------
4 begin : Thu Mar 01 2007
5 copyright : (C) 2007 by Thomas Eschenbacher
6 email : Thomas.Eschenbacher@gmx.de
7 ***************************************************************************/
8
9 /***************************************************************************
10 * *
11 * This program is free software; you can redistribute it and/or modify *
12 * it under the terms of the GNU General Public License as published by *
13 * the Free Software Foundation; either version 2 of the License, or *
14 * (at your option) any later version. *
15 * *
16 ***************************************************************************/
17
18 #include "config.h"
19
20 #include <errno.h>
21
22 #include <QDir>
23 #include <QFile>
24 #include <QFileInfo>
25 #include <QPointer>
26 #include <QRegExp>
27 #include <QStringList>
28
29 #include <KLocalizedString>
30
31 #include "libkwave/CodecManager.h"
32 #include "libkwave/Encoder.h"
33 #include "libkwave/FileInfo.h"
34 #include "libkwave/Label.h"
35 #include "libkwave/LabelList.h"
36 #include "libkwave/MessageBox.h"
37 #include "libkwave/MetaDataList.h"
38 #include "libkwave/Parser.h"
39 #include "libkwave/SignalManager.h"
40 #include "libkwave/String.h"
41 #include "libkwave/Utils.h"
42
43 #include "SaveBlocksDialog.h"
44 #include "SaveBlocksPlugin.h"
45
KWAVE_PLUGIN(saveblocks,SaveBlocksPlugin)46 KWAVE_PLUGIN(saveblocks, SaveBlocksPlugin)
47
48 //***************************************************************************
49 Kwave::SaveBlocksPlugin::SaveBlocksPlugin(QObject *parent,
50 const QVariantList &args)
51 :Kwave::Plugin(parent, args),
52 m_url(), m_pattern(), m_numbering_mode(CONTINUE),
53 m_selection_only(true), m_block_info()
54 {
55 }
56
57 //***************************************************************************
~SaveBlocksPlugin()58 Kwave::SaveBlocksPlugin::~SaveBlocksPlugin()
59 {
60 }
61
62 //***************************************************************************
setup(QStringList & previous_params)63 QStringList *Kwave::SaveBlocksPlugin::setup(QStringList &previous_params)
64 {
65 // try to interpret the previous parameters
66 interpreteParameters(previous_params);
67
68 // create the setup dialog
69 sample_index_t selection_left = 0;
70 sample_index_t selection_right = 0;
71 selection(Q_NULLPTR, &selection_left, &selection_right, false);
72
73 // enable the "selection only" checkbox only if there is something
74 // selected but not everything
75 bool selected_something = (selection_left != selection_right);
76 bool selected_all = ((selection_left == 0) &&
77 (selection_right + 1 >= signalLength()));
78 bool enable_selection_only = selected_something && !selected_all;
79
80 QString filename = m_url.path();
81 QString base = findBase(filename, m_pattern);
82 scanBlocksToSave(base, m_selection_only && enable_selection_only);
83
84 QPointer<Kwave::SaveBlocksDialog> dialog =
85 new(std::nothrow) Kwave::SaveBlocksDialog(
86 _("kfiledialog:///kwave_save_blocks"),
87 Kwave::CodecManager::encodingFilter(),
88 parentWidget(),
89 Kwave::URLfromUserInput(signalName()),
90 _("*.wav"),
91 m_pattern,
92 m_numbering_mode,
93 m_selection_only,
94 enable_selection_only
95 );
96 if (!dialog) return Q_NULLPTR;
97
98 // connect the signals/slots from the plugin and the dialog
99 connect(dialog, SIGNAL(sigSelectionChanged(QString,
100 QString,Kwave::SaveBlocksPlugin::numbering_mode_t,bool)),
101 this, SLOT(updateExample(QString,QString,
102 Kwave::SaveBlocksPlugin::numbering_mode_t,bool)));
103 connect(this, SIGNAL(sigNewExample(QString)),
104 dialog, SLOT(setNewExample(QString)));
105
106 dialog->setWindowTitle(description());
107 dialog->emitUpdate();
108 if ((dialog->exec() != QDialog::Accepted) || !dialog) {
109 delete dialog;
110 return Q_NULLPTR;
111 }
112
113 QStringList *list = new(std::nothrow) QStringList();
114 Q_ASSERT(list);
115 if (list) {
116 // user has pressed "OK"
117 QString pattern;
118
119 QUrl url = dialog->selectedUrl();
120 if (url.isEmpty()) {
121 delete dialog;
122 delete list;
123 return Q_NULLPTR;
124 }
125 QString name = url.path();
126 QFileInfo path(name);
127
128 // add the correct extension if necessary
129 if (!path.suffix().length()) {
130 QString ext = dialog->selectedExtension();
131 QStringList extensions = ext.split(_(" "));
132 ext = extensions.first();
133 name += ext.midRef(1);
134 path = name;
135 url.setPath(name);
136 }
137
138 name = Kwave::Parser::escape(name);
139 pattern = Kwave::Parser::escape(dialog->pattern());
140 int mode = static_cast<int>(dialog->numberingMode());
141 bool selection_only = (enable_selection_only) ?
142 dialog->selectionOnly() : m_selection_only;
143
144 *list << name;
145 *list << pattern;
146 *list << QString::number(mode);
147 *list << QString::number(selection_only);
148
149 emitCommand(_("plugin:execute(saveblocks,") +
150 name + _(",") + pattern + _(",") +
151 QString::number(mode) + _(",") +
152 QString::number(selection_only) + _(")")
153 );
154 }
155
156 if (dialog) delete dialog;
157 return list;
158 }
159
160 //***************************************************************************
createDisplayList(const QStringList & list,unsigned int max_entries) const161 QString Kwave::SaveBlocksPlugin::createDisplayList(
162 const QStringList &list,
163 unsigned int max_entries) const
164 {
165 if (!max_entries || list.isEmpty()) return QString();
166
167 QString retval;
168 unsigned int count = 0;
169
170 foreach (const QString &entry, list) {
171 if (count == 0) // first entry
172 retval = _("<br><br>");
173 if (count < max_entries)
174 retval += entry + _("<br>");
175 else if (count == max_entries)
176 retval += i18n("...") + _("<br>");
177
178 if (++count > max_entries)
179 break;
180 }
181
182 return retval;
183 }
184
185 //***************************************************************************
start(QStringList & params)186 int Kwave::SaveBlocksPlugin::start(QStringList ¶ms)
187 {
188 qDebug("SaveBlocksPlugin::start()");
189
190 // interpret the parameters
191 int result = interpreteParameters(params);
192 if (result) return result;
193
194 QString filename = m_url.path();
195 QFileInfo file(filename);
196 QString path = file.absolutePath();
197 QString ext = file.suffix();
198 QString base = findBase(filename, m_pattern);
199 QByteArray sep("/");
200
201 // determine the selection settings
202 sample_index_t selection_left = 0;
203 sample_index_t selection_right = 0;
204 selection(Q_NULLPTR, &selection_left, &selection_right, false);
205
206 bool selected_something = (selection_left != selection_right);
207 bool selected_all = ( (selection_left == 0) &&
208 ((selection_right + 1) >= signalLength()) );
209 bool enable_selection_only = selected_something && !selected_all;
210 bool selection_only = enable_selection_only && m_selection_only;
211 if (!selection_only) {
212 selection_left = 0;
213 selection_right = signalLength() - 1;
214 }
215
216 // get the index range
217 scanBlocksToSave(base, selection_only);
218 unsigned int count = m_block_info.count();
219 unsigned int first = firstIndex(path, base, ext, m_pattern,
220 m_numbering_mode, count);
221
222 // qDebug("m_url = '%s'", m_url.prettyURL().local8Bit().data());
223 // qDebug("m_pattern = '%s'", m_pattern.local8Bit().data());
224 // qDebug("m_numbering_mode = %d", (int)m_numbering_mode);
225 // qDebug("selection_only = %d", selection_only);
226 // qDebug("indices = %u...%u (count=%u)", first, first+count-1,count);
227
228 // remember the original file info and determine the list of unsupported
229 // properties, we need that later to avoid that the signal manager
230 // complains on saving each and every block, again and again...
231 const Kwave::FileInfo orig_file_info(signalManager().metaData());
232 Kwave::FileInfo file_info(orig_file_info);
233 QList<Kwave::FileProperty> unsupported_properties;
234 {
235 QString mimetype = Kwave::CodecManager::mimeTypeOf(m_url);
236 Kwave::Encoder *encoder = Kwave::CodecManager::encoder(mimetype);
237 if (encoder) {
238 unsupported_properties = encoder->unsupportedProperties(
239 file_info.properties().keys());
240 delete encoder;
241 }
242 }
243
244 // iterate over all blocks to check for overwritten files and missing dirs
245 QStringList overwritten_files;
246 QStringList missing_dirs;
247 for (unsigned int i = first; i < (first + count); i++) {
248 QString name = createFileName(base, ext, m_pattern, i, count,
249 first + count - 1);
250 QString display_name = Kwave::Parser::unescape(name);
251
252 // split the name into directory and file name
253 name = QString::fromLatin1(QUrl::toPercentEncoding(display_name, sep));
254 QUrl url = m_url.adjusted(QUrl::RemoveFilename);
255 url.setPath(url.path(QUrl::FullyEncoded) + name, QUrl::StrictMode);
256
257 QString p = url.path();
258 QFileInfo fi(p);
259
260 // check for potentially overwritten file
261 if (fi.exists())
262 overwritten_files += Kwave::Parser::unescape(display_name);
263
264 // check for missing subdirectory
265 if (!fi.dir().exists()) {
266 QString missing_dir = fi.absolutePath();
267 if (!missing_dirs.contains(missing_dir))
268 missing_dirs += missing_dir;
269 }
270 }
271
272 // inform about overwritten files
273 if (!overwritten_files.isEmpty()) {
274 // ask the user for confirmation if he really wants to overwrite...
275 if (Kwave::MessageBox::warningYesNo(parentWidget(),
276 _("<html>") +
277 i18n("This would overwrite the following file(s): %1" \
278 "Do you really want to continue?",
279 createDisplayList(overwritten_files, 5)) +
280 _("</html>") ) != KMessageBox::Yes)
281 {
282 return -1;
283 }
284 }
285
286 // handle missing directories
287 if (!missing_dirs.isEmpty()) {
288 // ask the user if he wants to continue and create the directory
289 if (Kwave::MessageBox::warningContinueCancel(parentWidget(),
290 i18n("The following directories do not exist: %1"
291 "Do you want to create them and continue?",
292 createDisplayList(missing_dirs, 5)),
293 QString(),
294 QString(),
295 QString(),
296 _("saveblocks_create_missing_dirs")
297 ) != KMessageBox::Continue)
298 {
299 return -1;
300 }
301
302 // create all missing directories
303 QUrl base_url = m_url.adjusted(QUrl::RemoveFilename);
304 foreach (const QString &missing, missing_dirs) {
305 QUrl url(base_url);
306 url.setPath(
307 base_url.path(QUrl::FullyEncoded) +
308 QString::fromLatin1(QUrl::toPercentEncoding(missing)),
309 QUrl::StrictMode
310 );
311 QString p = url.path();
312 QDir dir;
313 if (!dir.mkpath(p))
314 qWarning("creating path '%s' failed", DBG(p));
315 }
316 }
317
318 // save the current selection, we have to restore it afterwards!
319 sample_index_t saved_selection_left = 0;
320 sample_index_t saved_selection_right = 0;
321 selection(Q_NULLPTR, &saved_selection_left, &saved_selection_right, false);
322
323 // now we can loop over all blocks and save them
324 sample_index_t block_start;
325 sample_index_t block_end = 0;
326 Kwave::LabelList labels(signalManager().metaData());
327 Kwave::LabelListIterator it(labels);
328 Kwave::Label label = it.hasNext() ? it.next() : Kwave::Label();
329
330 for (unsigned int index = first;;) {
331 block_start = block_end;
332 block_end = (label.isNull()) ? signalLength() : label.pos();
333
334 if ((selection_left < block_end) && (selection_right > block_start)) {
335 // found a block to save...
336 Q_ASSERT(index < first + count);
337
338 sample_index_t left = block_start;
339 sample_index_t right = block_end - 1;
340 if (left < selection_left) left = selection_left;
341 if (right > selection_right) right = selection_right;
342 Q_ASSERT(right > left);
343 if (right <= left) break; // zero-length ?
344
345 // select the range of samples
346 selectRange(left, right - left + 1);
347
348 // determine the filename
349 QString name = createFileName(base, ext, m_pattern, index, count,
350 first + count - 1);
351 name = Kwave::Parser::unescape(name);
352 // use URL encoding for the filename
353 name = QString::fromLatin1(QUrl::toPercentEncoding(name, sep));
354 QUrl url = m_url.adjusted(QUrl::RemoveFilename);
355 url.setPath(url.path(QUrl::FullyEncoded) + name, QUrl::StrictMode);
356
357 // enter the title of the block into the meta data if supported
358 if (!unsupported_properties.contains(INF_NAME)) {
359 QString title = orig_file_info.get(INF_NAME).toString();
360 int idx = index - first;
361 if ((idx >= 0) && (idx < m_block_info.count())) {
362 QString block_title = m_block_info[idx].m_title;
363 if (block_title.length())
364 title = title + _(", ") + block_title;
365 }
366 file_info.set(INF_NAME, QVariant(title));
367 signalManager().metaData().replace(
368 Kwave::MetaDataList(file_info));
369 }
370
371 qDebug("saving %9lu...%9lu -> '%s'",
372 static_cast<unsigned long int>(left),
373 static_cast<unsigned long int>(right),
374 DBG(url.toDisplayString()));
375 if (signalManager().save(url, true) < 0)
376 break;
377
378 // if there were unsupported properties, the user might have been
379 // asked whether it is ok to continue or not. If he answered with
380 // "Cancel", we do not reach this point, otherwise we can continue
381 // and prevent any further annoying questions by removing all
382 // unsupported file info before the next run...
383 if ((index == first) && !unsupported_properties.isEmpty()) {
384 foreach (const Kwave::FileProperty &p, unsupported_properties) {
385 file_info.set(p, QVariant());
386 }
387 signalManager().metaData().replace(
388 Kwave::MetaDataList(file_info));
389 }
390
391 // increment the index for the next filename
392 index++;
393 }
394 if (label.isNull()) break;
395 label = (it.hasNext()) ? it.next() : Kwave::Label();
396 }
397
398 // restore the original file info
399 signalManager().metaData().replace(
400 Kwave::MetaDataList(orig_file_info));
401
402 // restore the previous selection
403 selectRange(saved_selection_left,
404 (saved_selection_left != saved_selection_right) ?
405 (saved_selection_right - saved_selection_left + 1) : 0);
406
407 return result;
408 }
409
410 //***************************************************************************
interpreteParameters(QStringList & params)411 int Kwave::SaveBlocksPlugin::interpreteParameters(QStringList ¶ms)
412 {
413 bool ok;
414 QString param;
415
416 // evaluate the parameter list
417 if (params.count() != 4) {
418 return -EINVAL;
419 }
420
421 // the selected URL
422 m_url = Kwave::URLfromUserInput(Kwave::Parser::unescape(params[0]));
423 if (!m_url.isValid()) return -EINVAL;
424
425 // filename pattern
426 m_pattern = Kwave::Parser::unescape(params[1]);
427 if (!m_pattern.length()) return -EINVAL;
428
429 // numbering mode
430 param = params[2];
431 int mode = param.toInt(&ok);
432 Q_ASSERT(ok);
433 if (!ok) return -EINVAL;
434 if ((mode != CONTINUE) &&
435 (mode != START_AT_ONE)) return -EINVAL;
436 m_numbering_mode = static_cast<numbering_mode_t>(mode);
437
438 // flag: save only the selection
439 param = params[3];
440 m_selection_only = (param.toUInt(&ok) != 0);
441 Q_ASSERT(ok);
442 if (!ok) return -EINVAL;
443
444 return 0;
445 }
446
447 //***************************************************************************
scanBlocksToSave(const QString & base,bool selection_only)448 void Kwave::SaveBlocksPlugin::scanBlocksToSave(const QString &base,
449 bool selection_only)
450 {
451 sample_index_t selection_left, selection_right;
452
453 sample_index_t block_start;
454 sample_index_t block_end = 0;
455 QString block_title;
456 Kwave::LabelList labels(signalManager().metaData());
457 Kwave::LabelListIterator it(labels);
458 Kwave::Label label = (it.hasNext()) ? it.next() : Kwave::Label();
459
460 if (selection_only) {
461 selection(Q_NULLPTR, &selection_left, &selection_right, true);
462 } else {
463 selection_left = 0;
464 selection_right = signalLength() - 1;
465 }
466
467 // get the title of the whole file, in case that a block does not have
468 // an own title
469 FileInfo info(signalManager().metaData());
470 QString file_title = info.get(INF_NAME).toString();
471
472 // fallback: if there is no INF_NAME either, fall back to the file
473 // name as last resort
474 if (!file_title.length()) file_title = base;
475
476 m_block_info.clear();
477 QString prev_title;
478 for (;;) {
479 block_start = block_end;
480 block_end = (label.isNull()) ? signalLength() : label.pos();
481 block_title = prev_title;
482 prev_title = (label.isNull()) ? file_title : label.name();
483
484 if ((block_end > selection_left) && (block_start <= selection_right)) {
485 BlockInfo block;
486 block.m_start = block_start;
487 block.m_length = block_end - block_start;
488 block.m_title = block_title;
489 if (!block.m_title.length()) block.m_title = file_title;
490 m_block_info.append(block);
491 }
492
493 if (label.isNull()) break;
494 label = (it.hasNext()) ? it.next() : Kwave::Label();
495 }
496 }
497
498 //***************************************************************************
createFileName(const QString & base,const QString & ext,const QString & pattern,unsigned int index,int count,int total)499 QString Kwave::SaveBlocksPlugin::createFileName(const QString &base,
500 const QString &ext, const QString &pattern,
501 unsigned int index, int count, int total)
502 {
503 QString p = QRegExp::escape(pattern);
504 QString nr;
505
506 // format the "index" parameter
507 QRegExp rx_nr(_("(\\\\\\[%(\\d*)nr\\\\\\])"), Qt::CaseInsensitive);
508 while (rx_nr.indexIn(p) >= 0) {
509 QString format = rx_nr.cap(1);
510 format = format.mid(2, format.length() - 6);
511 QString ex = _("(\\\\\\[") + format + _("nr\\\\\\])");
512 QRegExp rx(ex, Qt::CaseInsensitive);
513 format += _("u");
514 p.replace(rx, nr.asprintf(format.toLatin1(), index));
515 }
516
517 // format the "count" parameter
518 QRegExp rx_count(_("(\\\\\\[%\\d*count\\\\\\])"), Qt::CaseInsensitive);
519 while (rx_count.indexIn(p) >= 0) {
520 if (count >= 0) {
521 QString format = rx_count.cap(1);
522 format = format.mid(2, format.length() - 9);
523 QString ex = _("(\\\\\\[") + format + _("count\\\\\\])");
524 QRegExp rx(ex, Qt::CaseInsensitive);
525 format += _("u");
526 p.replace(rx, nr.asprintf(format.toLatin1(), count));
527 } else {
528 p.replace(rx_count, _("(\\d+)"));
529 }
530 }
531
532 // format the "total" parameter
533 QRegExp rx_total(_("(\\\\\\[%\\d*total\\\\\\])"), Qt::CaseInsensitive);
534 while (rx_total.indexIn(p) >= 0) {
535 if (total >= 0) {
536 QString format = rx_total.cap(1);
537 format = format.mid(2, format.length() - 9);
538 QString ex = _("(\\\\\\[") + format + _("total\\\\\\])");
539 QRegExp rx(ex, Qt::CaseInsensitive);
540 format += _("u");
541 p.replace(rx, nr.asprintf(format.toLatin1(), total));
542 } else {
543 p.replace(rx_total, _("(\\d+)"));
544 }
545 }
546
547 // format the "filename" parameter
548 QRegExp rx_filename(_("\\\\\\[%filename\\\\\\]"), Qt::CaseInsensitive);
549 if (rx_filename.indexIn(p) >= 0) {
550 p.replace(rx_filename, QRegExp::escape(base));
551 }
552
553 // support for file info
554 QRegExp rx_fileinfo(
555 _("\\\\\\[%(\\d*)fileinfo\\\\\\{([\\w\\s]+)\\\\\\}\\\\\\]"),
556 Qt::CaseInsensitive
557 );
558 Kwave::FileInfo info(signalManager().metaData());
559 while (rx_fileinfo.indexIn(p) >= 0) {
560 const QString format = rx_fileinfo.cap(1);
561 const QString id = rx_fileinfo.cap(2);
562 QString value;
563 FileProperty property = info.fromName(id);
564 if (property != Kwave::INF_UNKNOWN) {
565 QVariant val = info.get(property);
566 if (!val.isNull()) {
567 // we have a property value
568 value = val.toString();
569
570 // check for format (desired minimum string length)
571 bool ok = false;
572 int len = format.toUInt(&ok);
573 if ((len > 0) && ok) {
574 Kwave::FileInfo::Flags flags = info.flags(property);
575 if (flags & Kwave::FileInfo::FP_FORMAT_NUMERIC) {
576 // numeric format, pad with leading zeros or spaces
577 QString pad = (format.startsWith(QLatin1Char('0'))) ?
578 _("0") : _(" ");
579 while (value.length() < len)
580 value = pad + value;
581 } else {
582 // string format, pad with trailing spaces
583 while (value.length() < len)
584 value = value + _(" ");
585 }
586 }
587 value = Kwave::Parser::escapeForFileName(value);
588 }
589 }
590
591 QString ex(_("(\\\\\\[%") + format + _("fileinfo\\\\\\{") + id +
592 _("\\\\\\}\\\\\\])"));
593 QRegExp rx(ex, Qt::CaseInsensitive);
594 p.replace(rx, value);
595 }
596
597 // format the "title" parameter
598 QRegExp rx_title(_("\\\\\\[%title\\\\\\]"), Qt::CaseInsensitive);
599 if (rx_title.indexIn(p) >= 0) {
600 QString title;
601 int idx = (index - 1) - (total - count);
602 if ((idx >= 0) && (idx < m_block_info.count()))
603 title = m_block_info[idx].m_title;
604 if (title.length()) {
605 title = Kwave::Parser::escapeForFileName(title);
606 p.replace(rx_title, title);
607 }
608 }
609
610 if (ext.length()) p += _(".") + ext;
611
612 // sanitize the filename/path, make sure that there are no spaces
613 // before and after all path separators
614 QString sep = _("/");
615 QRegExp rx_sep(_("\\s*") + sep + _("\\s*"));
616 p.replace(rx_sep, sep);
617
618 return p;
619 }
620
621 //***************************************************************************
firstIndex(const QString & path,const QString & base,const QString & ext,const QString & pattern,Kwave::SaveBlocksPlugin::numbering_mode_t mode,unsigned int count)622 unsigned int Kwave::SaveBlocksPlugin::firstIndex(const QString &path,
623 const QString &base, const QString &ext, const QString &pattern,
624 Kwave::SaveBlocksPlugin::numbering_mode_t mode, unsigned int count)
625 {
626 unsigned int first = 1;
627 switch (mode) {
628 case START_AT_ONE:
629 first = 1;
630 break;
631 case CONTINUE: {
632 QDir dir(path, _("*"));
633 QStringList files;
634 files = dir.entryList();
635 for (unsigned int i = first; i < (first + count); i++) {
636 QString name = createFileName(base, ext, pattern, i, -1, -1);
637 QRegExp rx(_("^(") + name + _(")$"),
638 Qt::CaseInsensitive);
639 QStringList matches = files.filter(rx);
640 if (matches.count() > 0) first = i + 1;
641 }
642 break;
643 }
644 }
645
646 return first;
647 }
648
649 //***************************************************************************
findBase(const QString & filename,const QString & pattern)650 QString Kwave::SaveBlocksPlugin::findBase(const QString &filename,
651 const QString &pattern)
652 {
653 QFileInfo file(filename);
654 QString name = file.fileName();
655 QString base = file.completeBaseName();
656 QString ext = file.suffix();
657
658 // convert the pattern into a regular expression in order to check if
659 // the current name already is produced by the current pattern
660 // \[%[0-9]?nr\] -> \d+
661 // \[%[0-9]?count\] -> \d+
662 // \[%[0-9]?total\] -> \d+
663 // \[%filename\] -> base
664 // \[%fileinfo\] -> .
665 // \[%title\] -> .
666 QRegExp rx_nr(_("\\\\\\[%\\d*nr\\\\\\]"), Qt::CaseInsensitive);
667 QRegExp rx_count(_("\\\\\\[%\\d*count\\\\\\]"), Qt::CaseInsensitive);
668 QRegExp rx_total(_("\\\\\\[%\\d*total\\\\\\]"), Qt::CaseInsensitive);
669 QRegExp rx_filename(_("\\\\\\[%filename\\\\\\]"), Qt::CaseInsensitive);
670 QRegExp rx_fileinfo(_("\\\\\\[%fileinfo\\\\\\]"), Qt::CaseInsensitive);
671 QRegExp rx_title(_("\\\\\\[%title\\\\\\]"), Qt::CaseInsensitive);
672
673 QString p = QRegExp::escape(pattern);
674 int idx_nr = rx_nr.indexIn(p);
675 int idx_count = rx_count.indexIn(p);
676 int idx_total = rx_total.indexIn(p);
677 int idx_filename = rx_filename.indexIn(p);
678 int idx_fileinfo = rx_fileinfo.indexIn(p);
679 int idx_title = rx_fileinfo.indexIn(p);
680 p.replace(rx_nr, _("(\\d+)"));
681 p.replace(rx_count, _("(\\d+)"));
682 p.replace(rx_total, _("(\\d+)"));
683 p.replace(rx_filename, _("(.+)"));
684 p.replace(rx_fileinfo, _("(.+)"));
685 p.replace(rx_title, _("(.+)"));
686
687 int max = 0;
688 for (int i = 0; i < pattern.length(); i++) {
689 if (idx_nr == max) max++;
690 if (idx_count == max) max++;
691 if (idx_total == max) max++;
692 if (idx_filename == max) max++;
693 if (idx_fileinfo == max) max++;
694 if (idx_title == max) max++;
695
696 if (idx_nr > max) idx_nr--;
697 if (idx_count > max) idx_count--;
698 if (idx_total > max) idx_total--;
699 if (idx_filename > max) idx_filename--;
700 if (idx_fileinfo > max) idx_fileinfo--;
701 if (idx_title > max) idx_title--;
702 }
703
704 if (ext.length()) p += _(".") + ext;
705 QRegExp rx_current(p, Qt::CaseInsensitive);
706 if (rx_current.indexIn(name) >= 0) {
707 // filename already produced by this pattern
708 base = rx_current.cap(idx_filename + 1);
709 }
710
711 return base;
712 }
713
714 //***************************************************************************
firstFileName(const QString & filename,const QString & pattern,Kwave::SaveBlocksPlugin::numbering_mode_t mode,bool selection_only)715 QString Kwave::SaveBlocksPlugin::firstFileName(const QString &filename,
716 const QString &pattern, Kwave::SaveBlocksPlugin::numbering_mode_t mode,
717 bool selection_only)
718 {
719 QFileInfo file(filename);
720 QString path = file.absolutePath();
721 QString ext = file.suffix();
722 QString base = findBase(filename, pattern);
723
724 // now we have a new name, base and extension
725 // -> find out the numbering, min/max etc...
726 scanBlocksToSave(base, selection_only);
727 unsigned int count = m_block_info.count();
728 unsigned int first = firstIndex(path, base, ext, pattern, mode, count);
729 unsigned int total = first + count - 1;
730
731 // create the complete filename, including extension but without path
732 return createFileName(base, ext, pattern, first, count, total);
733 }
734
735 //***************************************************************************
updateExample(const QString & filename,const QString & pattern,Kwave::SaveBlocksPlugin::numbering_mode_t mode,bool selection_only)736 void Kwave::SaveBlocksPlugin::updateExample(const QString &filename,
737 const QString &pattern, Kwave::SaveBlocksPlugin::numbering_mode_t mode,
738 bool selection_only)
739 {
740 QString example = firstFileName(filename, pattern, mode, selection_only);
741 emit sigNewExample(Kwave::Parser::unescape(example));
742 }
743
744 //***************************************************************************
745 #include "SaveBlocksPlugin.moc"
746 //***************************************************************************
747 //***************************************************************************
748