MainWindow.cc
上传用户:weiliju62
上传日期:2007-01-06
资源大小:619k
文件大小:25k
源码类别:

SCSI/ASPI

开发平台:

MultiPlatform

  1. /*  cdrdao - write audio CD-Rs in disc-at-once mode
  2.  *
  3.  *  Copyright (C) 1998  Andreas Mueller <mueller@daneb.ping.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
  16.  *  along with this program; if not, write to the Free Software
  17.  *  Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
  18.  */
  19. /*
  20.  * $Log: MainWindow.cc,v $
  21.  * Revision 1.6  1999/05/24 18:10:25  mueller
  22.  * Adapted to new reading interface of 'trackdb'.
  23.  *
  24.  * Revision 1.5  1999/03/06 13:55:18  mueller
  25.  * Adapted to Gtk-- version 0.99.1
  26.  *
  27.  * Revision 1.4  1999/02/28 10:59:07  mueller
  28.  * Adapted to changes in 'trackdb'.
  29.  *
  30.  * Revision 1.3  1999/01/30 19:45:43  mueller
  31.  * Fixes for compilation with Gtk-- 0.11.1.
  32.  *
  33.  * Revision 1.1  1998/11/20 18:54:34  mueller
  34.  * Initial revision
  35.  *
  36.  */
  37. static char rcsid[] = "$Id$";
  38. #include <stdio.h>
  39. #include <fstream.h>
  40. #include <string.h>
  41. #include <errno.h>
  42. #include <stdarg.h>
  43. #include <strstream.h>
  44. #include "xcdrdao.h"
  45. #include "MainWindow.h"
  46. #include "guiUpdate.h"
  47. #include "SampleDisplay.h"
  48. #include "AddSilenceDialog.h"
  49. #include "AddFileDialog.h"
  50. #include "TrackInfoDialog.h"
  51. #include "TocInfoDialog.h"
  52. #include "DeviceConfDialog.h"
  53. #include "RecordDialog.h"
  54. #include "SoundIF.h"
  55. #include "TocEdit.h"
  56. #include "MessageBox.h"
  57. #include "Toc.h"
  58. #include "TrackData.h"
  59. #include "util.h"
  60. MainWindow::MainWindow(TocEdit *tedit) : vbox_(false, 5), tocReader(NULL)
  61. {
  62.   tocEdit_ = tedit;
  63.   playing_ = 0;
  64.   playBurst_ = 588 * 5;
  65.   soundInterface_ = new SoundIF;
  66.   playBuffer_ = new Sample[playBurst_];
  67.   soundInterface_ = NULL;
  68.   fileSelector_ = new Gtk_FileSelection("");
  69.   fileSelector_->complete(string("*.toc"));
  70.   createMenuBar();
  71.   set_usize(600,400);
  72.   vbox_.pack_start(*menuBar_, FALSE, FALSE);
  73.   menuBar_->show();
  74.   sampleDisplay_ = new SampleDisplay;
  75.   sampleDisplay_->setTocEdit(tocEdit_);
  76.   vbox_.pack_start(*sampleDisplay_, TRUE, TRUE);
  77.   sampleDisplay_->show();
  78.   Gtk_HScrollbar *scrollBar =
  79.     new Gtk_HScrollbar(*(sampleDisplay_->getAdjustment()));
  80.   vbox_.pack_start(*scrollBar, FALSE, FALSE);
  81.   scrollBar->show();
  82.   
  83.   Gtk_Label *label;
  84.   Gtk_HBox *selectionInfoBox = new Gtk_HBox;
  85.   markerPos_ = new Gtk_Entry;
  86.   markerPos_->set_editable(true);
  87.   connect_to_method(markerPos_->activate, this, &MainWindow::markerSet);
  88.   cursorPos_ = new Gtk_Entry;
  89.   cursorPos_->set_editable(false);
  90.   selectionStartPos_ = new Gtk_Entry;
  91.   selectionStartPos_->set_editable(true);
  92.   connect_to_method(selectionStartPos_->activate, this,
  93.     &MainWindow::selectionSet);
  94.   selectionEndPos_ = new Gtk_Entry;
  95.   selectionEndPos_->set_editable(true);
  96.   connect_to_method(selectionEndPos_->activate, this,
  97.     &MainWindow::selectionSet);
  98.   label = new Gtk_Label(string("Cursor: "));
  99.   selectionInfoBox->pack_start(*label, FALSE, FALSE);
  100.   selectionInfoBox->pack_start(*cursorPos_);
  101.   label->show();
  102.   cursorPos_->show();
  103.   label = new Gtk_Label(string("Marker: "));
  104.   selectionInfoBox->pack_start(*label, FALSE, FALSE);
  105.   selectionInfoBox->pack_start(*markerPos_);
  106.   label->show();
  107.   markerPos_->show();
  108.   label = new Gtk_Label(string("Selection: "));
  109.   selectionInfoBox->pack_start(*label, FALSE, FALSE);
  110.   selectionInfoBox->pack_start(*selectionStartPos_);
  111.   label->show();
  112.   selectionStartPos_->show();
  113.   label = new Gtk_Label(string(" - "));
  114.   selectionInfoBox->pack_start(*label, FALSE, FALSE);
  115.   selectionInfoBox->pack_start(*selectionEndPos_);
  116.   label->show();
  117.   selectionEndPos_->show();
  118.   
  119.   vbox_.pack_start(*selectionInfoBox, FALSE, FALSE);
  120.   selectionInfoBox->show();
  121.   Gtk_HButtonBox *buttonBox = new Gtk_HButtonBox(GTK_BUTTONBOX_START, 5);
  122.   zoomButton_ = new Gtk_RadioButton(NULL, string("Zoom"));
  123.   selectButton_ = new Gtk_RadioButton(zoomButton_->group(), string("Select"));
  124.   playButton_ = new Gtk_Button(string("Play"));
  125.   buttonBox->pack_start(*zoomButton_);
  126.   zoomButton_->show();
  127.   buttonBox->pack_start(*selectButton_);
  128.   selectButton_->show();
  129.   zoomButton_->set_active(true);
  130.   setMode(ZOOM);
  131.   buttonBox->pack_start(*playButton_);
  132.   playButton_->show();
  133.   vbox_.pack_start(*buttonBox, FALSE, FALSE);
  134.   buttonBox->show();
  135.   statusBar_ = new Gtk_Statusbar;
  136.   vbox_.pack_start(*statusBar_, FALSE, FALSE);
  137.   lastMessageId_ = statusBar_->push(1, string(""));
  138.   statusBar_->show();
  139.   statusMessage("xcdrdao %s", VERSION);
  140.   add(&vbox_);
  141.   vbox_.show();
  142.   connect_to_method(sampleDisplay_->markerSet, this,
  143.                     &MainWindow::markerSetCallback);
  144.   connect_to_method(sampleDisplay_->selectionSet, this,
  145.     &MainWindow::selectionSetCallback);
  146.   connect_to_method(sampleDisplay_->cursorMoved, this,
  147.                     &MainWindow::cursorMovedCallback);
  148.   connect_to_method(sampleDisplay_->trackMarkSelected, this,
  149.     &MainWindow::trackMarkSelectedCallback);
  150.   connect_to_method(sampleDisplay_->trackMarkMoved, this,
  151.     &MainWindow::trackMarkMovedCallback);
  152.   connect_to_method(zoomButton_->toggled, this, &MainWindow::setMode, ZOOM);
  153.   connect_to_method(selectButton_->toggled, this, &MainWindow::setMode, SELECT);
  154.   connect_to_method(playButton_->clicked, this, &MainWindow::play);
  155. }
  156. void MainWindow::createMenuBar()
  157. {
  158.   Gtk_AccelGroup accelGroup;
  159.   itemFactory_ = new Gtk_ItemFactory_MenuBar("<Main>", accelGroup);
  160.   Gtk_ItemFactory &f = *itemFactory_;
  161.   f.create_item("/File", NULL, "<Branch>", 0);
  162.   f.create_item("/File/New", 0, "<Item>",
  163.    ItemFactoryConnector<MainWindow, ignored>(this, &MainWindow::newToc));
  164.   f.create_item("/File/Open...", "<control>O", "<Item>",
  165.    ItemFactoryConnector<MainWindow, ignored>(this, &MainWindow::readToc));
  166.   f.create_item("/File/Save", "<control>S", "<Item>",
  167.    ItemFactoryConnector<MainWindow, ignored>(this, &MainWindow::saveToc));
  168.   f.create_item("/File/Save As...", 0,"<Item>",
  169.    ItemFactoryConnector<MainWindow, ignored>(this, &MainWindow::saveAsToc));
  170.   f.create_item("/File/", 0, "<Separator>",0);
  171.   f.create_item("/File/Quit", "<alt>X", "<Item>",
  172.    ItemFactoryConnector<MainWindow, ignored>(this, &MainWindow::quit));
  173.   f.create_item("/View", NULL, "<Branch>", 0);
  174.   f.create_item("/View/Zoom to Selection", "<shift>z", "<Item>",
  175. ItemFactoryConnector<MainWindow, ignored>(this, &MainWindow::zoomIn));
  176.   f.create_item("/View/Zoom out", "Z", "<Item>",
  177.    ItemFactoryConnector<MainWindow, ignored>(this, &MainWindow::zoomOut));
  178.   f.create_item("/View/Fullview", "F", "<Item>",
  179.    ItemFactoryConnector<MainWindow, ignored>(this, &MainWindow::fullView));
  180.   f.create_item("/Edit", NULL, "<Branch>", 0);
  181.   f.create_item("/Edit/Cut", "<control>K", "<Item>",
  182.    ItemFactoryConnector<MainWindow, ignored>(this,
  183.      &MainWindow::cutTrackData));
  184.   f.create_item("/Edit/Paste", "<control>Y", "<Item>",
  185.    ItemFactoryConnector<MainWindow, ignored>(this,
  186.      &MainWindow::pasteTrackData));
  187.   f.create_item("/Edit/", 0, "<Separator>", 0);
  188.   f.create_item("/Edit/Add Track Mark", "T", "<Item>",
  189.    ItemFactoryConnector<MainWindow, ignored>(this, &MainWindow::addTrackMark));
  190.   f.create_item("/Edit/Add Index Mark", "I", "<Item>",
  191.    ItemFactoryConnector<MainWindow, ignored>(this, &MainWindow::addIndexMark));
  192.   f.create_item("/Edit/Add Pre-Gap", "P", "<Item>",
  193.    ItemFactoryConnector<MainWindow, ignored>(this, &MainWindow::addPregap));
  194.   f.create_item("/Edit/Remove Track Mark", "<control>D", "<Item>",
  195.    ItemFactoryConnector<MainWindow, ignored>(this,
  196.      &MainWindow::removeTrackMark));
  197.   f.create_item("/Tools", NULL, "<Branch>", 0);
  198.   f.create_item("/Tools/Disk Info...", 0, "<Item>",
  199. ItemFactoryConnector<MainWindow, ignored>(this,
  200.   &MainWindow::tocInfo));
  201.   f.create_item("/Tools/Track Info...", 0, "<Item>",
  202. ItemFactoryConnector<MainWindow, ignored>(this,
  203.   &MainWindow::trackInfo));
  204.   f.create_item("/Tools/", 0, "<Separator>", 0);
  205.   f.create_item("/Tools/Append Track...", "<control>T", "<Item>",
  206.     ItemFactoryConnector<MainWindow, ignored>(this, &MainWindow::appendTrack));
  207.   f.create_item("/Tools/Append File...", "<control>F", "<Item>",
  208.     ItemFactoryConnector<MainWindow, ignored>(this, &MainWindow::appendFile));
  209.   f.create_item("/Tools/Insert File...", "<control>I" , "<Item>",
  210.     ItemFactoryConnector<MainWindow, ignored>(this, &MainWindow::insertFile));
  211.   f.create_item("/Tools/", 0, "<Separator>", 0);
  212.   f.create_item("/Tools/Append Silence...", 0, "<Item>",
  213.     ItemFactoryConnector<MainWindow, ignored>(this,
  214.       &MainWindow::appendSilence));
  215.   f.create_item("/Tools/Insert Silence...", 0, "<Item>",
  216.     ItemFactoryConnector<MainWindow, ignored>(this,
  217.       &MainWindow::insertSilence));
  218.   f.create_item("/Settings", 0, "<Branch>", 0);
  219.   f.create_item("/Settings/Devices...", 0, "<Item>",
  220.     ItemFactoryConnector<MainWindow, ignored>(this,
  221.       &MainWindow::configureDevices));
  222.   //f.create_item("/Options/Settings...", 0, "<Item>", 0);
  223.   f.create_item("/Actions", 0, "<Branch>", 0);
  224.   f.create_item("/Actions/Record...", 0, "<Item>",
  225.     ItemFactoryConnector<MainWindow, ignored>(this,
  226.       &MainWindow::record));
  227.   
  228.   //f.create_item("/Help", 0, "<LastBranch>", 0);
  229.   add_accel_group(accelGroup);
  230.   menuBar_ = f.get_menubar_widget("");
  231. }
  232. gint MainWindow::delete_event_impl(GdkEventAny*)
  233. {
  234.   quit();
  235.   return 1;
  236. }
  237. void MainWindow::quit()
  238. {
  239.   if (tocEdit_->tocDirty()) {
  240.     Ask2Box msg(this, "Quit", 0, 2, "Current work not saved.", "",
  241. "Really Quit?", NULL);
  242.     if (msg.run() != 1)
  243.       return;
  244.   }
  245.   hide();
  246.   Gtk_Main::instance()->quit();
  247. }
  248. void MainWindow::update(unsigned long level)
  249. {
  250.   if (level & (UPD_TOC_DIRTY | UPD_TOC_DATA)) {
  251.     string s(tocEdit_->filename());
  252.     if (tocEdit_->tocDirty())
  253.       s += "(*)";
  254.     
  255.     set_title(s);
  256.     cursorPos_->set_text("");
  257.   }
  258.   if (level & UPD_TRACK_MARK_SEL) {
  259.     int trackNr, indexNr;
  260.     if (tocEdit_->trackSelection(&trackNr) && 
  261. tocEdit_->indexSelection(&indexNr)) {
  262.       sampleDisplay_->setSelectedTrackMarker(trackNr, indexNr);
  263.     }
  264.     else {
  265.       sampleDisplay_->setSelectedTrackMarker(0, 0);
  266.     }
  267.   }
  268.   if (level & UPD_SAMPLES) {
  269.     sampleDisplay_->updateToc();
  270.   }
  271.   else if (level & (UPD_TRACK_DATA | UPD_TRACK_MARK_SEL)) {
  272.     sampleDisplay_->updateTrackMarks();
  273.   }
  274.   if (level & UPD_SAMPLE_MARKER) {
  275.     unsigned long marker;
  276.     if (tocEdit_->sampleMarker(&marker)) {
  277.       markerPos_->set_text(string(sample2string(marker)));
  278.       sampleDisplay_->setMarker(marker);
  279.     }
  280.     else {
  281.       markerPos_->set_text(string(""));
  282.       sampleDisplay_->clearMarker();
  283.     }
  284.   }
  285.   if (level & UPD_SAMPLE_SEL) {
  286.     unsigned long start, end;
  287.     if (tocEdit_->sampleSelection(&start, &end)) {
  288.       selectionStartPos_->set_text(string(sample2string(start)));
  289.       selectionEndPos_->set_text(string(sample2string(end)));
  290.       sampleDisplay_->setRegion(start, end);
  291.     }
  292.     else {
  293.       selectionStartPos_->set_text(string(""));
  294.       selectionEndPos_->set_text(string(""));
  295.       sampleDisplay_->setRegion(1, 0);
  296.     }
  297.   }
  298. }
  299. void MainWindow::tocBlockedMsg(const char *op)
  300. {
  301.   MessageBox msg(this, op, 0,
  302.  "Cannot perform requested operation because", 
  303.  "project is in read-only state.", NULL);
  304.   msg.run();
  305. }
  306. void MainWindow::newToc()
  307. {
  308.   if (!tocEdit_->editable()) {
  309.     tocBlockedMsg("New Toc");
  310.     return;
  311.   }
  312.   if (tocEdit_->tocDirty()) {
  313.     Ask2Box msg(this, "New", 0, 2, "Current work not saved.", "",
  314. "Continue?", NULL);
  315.     if (msg.run() != 1)
  316.       return;
  317.   }
  318.   Toc *toc = new Toc;
  319.   
  320.   tocEdit_->toc(toc, "unnamed.toc");
  321.   guiUpdate();
  322. }
  323. void MainWindow::readToc()
  324. {
  325.   if (!tocEdit_->editable()) {
  326.     tocBlockedMsg("Read Toc");
  327.     return;
  328.   }
  329.   if (tocEdit_->tocDirty()) {
  330.     Ask2Box msg(this, "Read", 0, 2, "Current work not saved.", "",
  331. "Continue?", NULL);
  332.     if (msg.run() != 1)
  333.       return;
  334.   }
  335.   fileSelector_->set_title("Read Toc-File");
  336.   fileSelectorC1_ = connect_to_method(fileSelector_->get_ok_button()->clicked,
  337.       this, &MainWindow::readTocCallback, 1);
  338.   fileSelectorC2_ =
  339.     connect_to_method(fileSelector_->get_cancel_button()->clicked,
  340.       this, &MainWindow::readTocCallback, 0);
  341.   Gtk_Main::instance()->grab_add(*fileSelector_);
  342.   fileSelector_->show();
  343. }
  344. void MainWindow::saveToc()
  345. {
  346.   if (tocEdit_->saveToc() == 0) {
  347.     statusMessage("Toc saved to "%s".", tocEdit_->filename());
  348.     guiUpdate();
  349.   }
  350.   else {
  351.     statusMessage("Cannot save toc to "%s": %s", tocEdit_->filename(),
  352.   strerror(errno));
  353.   }
  354. }
  355. void MainWindow::saveAsToc()
  356. {
  357.   fileSelector_->set_title("Save Toc-File");
  358.   fileSelectorC1_ = connect_to_method(fileSelector_->get_ok_button()->clicked,
  359.       this, &MainWindow::saveAsTocCallback, 1);
  360.   fileSelectorC2_ =
  361.     connect_to_method(fileSelector_->get_cancel_button()->clicked,
  362.       this, &MainWindow::saveAsTocCallback, 0);
  363.   Gtk_Main::instance()->grab_add(*fileSelector_);
  364.   fileSelector_->show();
  365. }
  366. void MainWindow::readTocCallback(int action)
  367. {
  368.   if (action == 0) {
  369.     fileSelectorC1_.disconnect();
  370.     fileSelectorC2_.disconnect();
  371.     Gtk_Main::instance()->grab_remove(*fileSelector_);
  372.     fileSelector_->hide();
  373.     
  374.   }
  375.   else {
  376.     const char *s = strdupCC(fileSelector_->get_filename().c_str());
  377.     if (s != NULL && *s != 0 && s[strlen(s) - 1] != '/') {
  378.       fileSelectorC1_.disconnect();
  379.       fileSelectorC2_.disconnect();
  380.       Gtk_Main::instance()->grab_remove(*fileSelector_);
  381.       fileSelector_->hide();
  382.       if (tocEdit_->readToc(stripCwd(s)) == 0) {
  383. sampleDisplay_->setView(0, tocEdit_->lengthSample() - 1);
  384. guiUpdate();
  385.       }
  386.     }
  387.     delete[] s;
  388.   }
  389. }
  390. void MainWindow::saveAsTocCallback(int action)
  391. {
  392.   if (action == 0) {
  393.     fileSelectorC1_.disconnect();
  394.     fileSelectorC2_.disconnect();
  395.     Gtk_Main::instance()->grab_remove(*fileSelector_);
  396.     fileSelector_->hide();
  397.   }
  398.   else {
  399.     const char *s = strdupCC(fileSelector_->get_filename().c_str());
  400.     if (s != NULL && *s != 0 && s[strlen(s) - 1] != '/') {
  401.       fileSelectorC1_.disconnect();
  402.       fileSelectorC2_.disconnect();
  403.       Gtk_Main::instance()->grab_remove(*fileSelector_);
  404.       fileSelector_->hide();
  405.       if (tocEdit_->saveAsToc(stripCwd(s)) == 0) {
  406. statusMessage("Toc saved to "%s".", tocEdit_->filename());
  407. guiUpdate();
  408.       }
  409.       else {
  410. statusMessage("Cannot save toc to "%s": %s", s, strerror(errno));
  411.       }
  412.     }
  413.     delete[] s;
  414.   }
  415. }
  416. void MainWindow::zoomIn()
  417. {
  418.   unsigned long start, end;
  419.   if (tocEdit_->sampleSelection(&start, &end)) {
  420.     sampleDisplay_->setView(start, end);
  421.   }
  422. }
  423.  
  424. void MainWindow::zoomOut()
  425. {
  426.   unsigned long start, end, len, center;
  427.   sampleDisplay_->getView(&start, &end);
  428.   len = end - start + 1;
  429.   center = start + len / 2;
  430.   if (center > len)
  431.     start = center - len;
  432.   else 
  433.     start = 0;
  434.   end = center + len;
  435.   if (end >= tocEdit_->toc()->length().samples())
  436.     end = tocEdit_->toc()->length().samples() - 1;
  437.   
  438.   sampleDisplay_->setView(start, end);
  439. }
  440. void MainWindow::fullView()
  441. {
  442.   unsigned long len;
  443.   if ((len = tocEdit_->toc()->length().samples()) > 0) {
  444.     sampleDisplay_->setView(0, len - 1);
  445.   }
  446. }
  447. void MainWindow::markerSetCallback(unsigned long sample)
  448. {
  449.   tocEdit_->sampleMarker(sample);
  450.   guiUpdate();
  451. }
  452. void MainWindow::selectionSetCallback(unsigned long start,
  453.       unsigned long end)
  454. {
  455.   if (mode_ == ZOOM) {
  456.     sampleDisplay_->setView(start, end);
  457.   }
  458.   else {
  459.     tocEdit_->sampleSelection(start, end);
  460.     guiUpdate();
  461.   }
  462. }
  463. void MainWindow::cursorMovedCallback(unsigned long pos)
  464. {
  465.   cursorPos_->set_text(string(sample2string(pos)));
  466. }
  467. void MainWindow::setMode(Mode m)
  468. {
  469.   mode_ = m;
  470.   /*
  471.   if (mode_ == ZOOM && !zoomButton_->get_state()) {
  472.     zoomButton_->set_state(true);
  473.   }
  474.   else if (mode_ == SELECT && !selectButton_->get_state()) {
  475.     selectButton_->set_state(true);
  476.   }
  477.   */
  478. }
  479. const char *MainWindow::sample2string(unsigned long sample)
  480. {
  481.   static char buf[50];
  482.   unsigned long min = sample / (60 * 44100);
  483.   sample %= 60 * 44100;
  484.   unsigned long sec = sample / 44100;
  485.   sample %= 44100;
  486.   unsigned long frame = sample / 588;
  487.   sample %= 588;
  488.   sprintf(buf, "%2lu:%02lu:%02lu.%03lu", min, sec, frame, sample);
  489.   
  490.   return buf;
  491. }
  492. unsigned long MainWindow::string2sample(const char *str)
  493. {
  494.   int m = 0;
  495.   int s = 0;
  496.   int f = 0;
  497.   int n = 0;
  498.   sscanf(str, "%d:%d:%d.%d", &m, &s, &f, &n);
  499.   if (m < 0)
  500.     m = 0;
  501.   if (s < 0 || s > 59)
  502.     s = 0;
  503.   if (f < 0 || f > 74)
  504.     f = 0;
  505.   if (n < 0 || n > 587)
  506.     n = 0;
  507.   return Msf(m, s, f).samples() + n;
  508. }
  509. int MainWindow::snapSampleToBlock(unsigned long sample, long *block)
  510. {
  511.   unsigned long rest = sample % SAMPLES_PER_BLOCK;
  512.   *block = sample / SAMPLES_PER_BLOCK;
  513.   if (rest == 0) 
  514.     return 0;
  515.   if (rest > SAMPLES_PER_BLOCK / 2)
  516.     *block += 1;
  517.   return 1;
  518. }
  519. void MainWindow::statusMessage(const char *fmt, ...)
  520. {
  521.   va_list args;
  522.   va_start(args, fmt);
  523.   strstream str;
  524.   str.vform(fmt, args);
  525.   str << ends;
  526.   statusBar_->remove_message(1, lastMessageId_);
  527.   lastMessageId_ = statusBar_->push(1, string(str.str()));
  528.   str.freeze(0);
  529.   va_end(args);
  530. }
  531. void MainWindow::trackMarkSelectedCallback(const Track *, int trackNr,
  532.    int indexNr)
  533. {
  534.   tocEdit_->trackSelection(trackNr);
  535.   tocEdit_->indexSelection(indexNr);
  536.   guiUpdate();
  537. }
  538. void MainWindow::trackMarkMovedCallback(const Track *, int trackNr,
  539. int indexNr, unsigned long sample)
  540. {
  541.   if (!tocEdit_->editable()) {
  542.     tocBlockedMsg("Move Track Marker");
  543.     return;
  544.   }
  545.   long lba;
  546.   int snapped = snapSampleToBlock(sample, &lba);
  547.   switch (tocEdit_->moveTrackMarker(trackNr, indexNr, lba)) {
  548.   case 0:
  549.     statusMessage("Moved track marker to %s%s.", Msf(lba).str(),
  550.   snapped ? " (snapped to next block)" : "");
  551.     break;
  552.   case 6:
  553.     statusMessage("Cannot modify a data track.");
  554.     break;
  555.   default:
  556.     statusMessage("Illegal track marker position.");
  557.     break;
  558.   }
  559.   tocEdit_->trackSelection(trackNr);
  560.   tocEdit_->indexSelection(indexNr);
  561.   guiUpdate();
  562. }
  563. void MainWindow::play()
  564. {
  565.   unsigned long start, end;
  566.   if (playing_) {
  567.     playAbort_ = 1;
  568.     return;
  569.   }
  570.   if (tocEdit_->lengthSample() == 0)
  571.     return;
  572.   if (soundInterface_ == NULL) {
  573.     soundInterface_ = new SoundIF;
  574.     if (soundInterface_->init() != 0) {
  575.       delete soundInterface_;
  576.       soundInterface_ = NULL;
  577.       return;
  578.     }
  579.   }
  580.   if (soundInterface_->start() != 0)
  581.     return;
  582.   if (!sampleDisplay_->getRegion(&start, &end))
  583.     sampleDisplay_->getView(&start, &end);
  584.   tocReader.init(tocEdit_->toc());
  585.   if (tocReader.openData() != 0) {
  586.     tocReader.init(NULL);
  587.     soundInterface_->end();
  588.     return;
  589.     }
  590.   if (tocReader.seekSample(start) != 0) {
  591.     tocReader.init(NULL);
  592.     soundInterface_->end();
  593.     return;
  594.   }
  595.   playLength_ = end - start + 1;
  596.   playPosition_ = start;
  597.   playing_ = 1;
  598.   playAbort_ = 0;
  599.   tocEdit_->blockEdit();
  600.   guiUpdate();
  601.   connect_to_method(Gtk_Main::idle(), this, &MainWindow::playCallback);
  602. }
  603. int MainWindow::playCallback()
  604. {
  605.   long len = playLength_ > playBurst_ ? playBurst_ : playLength_;
  606.   if (tocReader.readSamples(playBuffer_, len) != len ||
  607.       soundInterface_->play(playBuffer_, len) != 0) {
  608.     soundInterface_->end();
  609.     tocReader.init(NULL);
  610.     playing_ = 0;
  611.     sampleDisplay_->setCursor(0, 0);
  612.     tocEdit_->unblockEdit();
  613.     guiUpdate();
  614.     return 0; // remove idle handler
  615.   }
  616.   playLength_ -= len;
  617.   playPosition_ += len;
  618.   unsigned long delay = soundInterface_->getDelay();
  619.   if (delay <= playPosition_) {
  620.     sampleDisplay_->setCursor(1, playPosition_ - delay);
  621.     cursorPos_->set_text(string(sample2string(playPosition_ - delay)));
  622.   }
  623.   if (len == 0 || playAbort_ != 0) {
  624.     soundInterface_->end();
  625.     tocReader.init(NULL);
  626.     playing_ = 0;
  627.     sampleDisplay_->setCursor(0, 0);
  628.     tocEdit_->unblockEdit();
  629.     guiUpdate();
  630.     return 0; // remove idle handler
  631.   }
  632.   else {
  633.     return 1; // keep idle handler
  634.   }
  635. }
  636. int MainWindow::getMarker(unsigned long *sample)
  637. {
  638.   if (tocEdit_->lengthSample() == 0)
  639.     return 0;
  640.   if (sampleDisplay_->getMarker(sample) == 0) {
  641.     statusMessage("Please set marker.");
  642.     return 0;
  643.   }
  644.   return 1;
  645. }
  646. void MainWindow::addTrackMark()
  647. {
  648.   unsigned long sample;
  649.   if (!tocEdit_->editable()) {
  650.     tocBlockedMsg("Add Track Mark");
  651.     return;
  652.   }
  653.   if (getMarker(&sample)) {
  654.     long lba;
  655.     int snapped = snapSampleToBlock(sample, &lba);
  656.     switch (tocEdit_->addTrackMarker(lba)) {
  657.     case 0:
  658.       statusMessage("Added track mark at %s%s.", Msf(lba).str(),
  659.     snapped ? " (snapped to next block)" : "");
  660.       guiUpdate();
  661.       break;
  662.     case 2:
  663.       statusMessage("Cannot add track at this point.");
  664.       break;
  665.     case 3:
  666.     case 4:
  667.       statusMessage("Resulting track would be shorter than 4 seconds.");
  668.       break;
  669.     case 5:
  670.       statusMessage("Cannot modify a data track.");
  671.       break;
  672.     default:
  673.       statusMessage("Internal error in addTrackMark(), please report.");
  674.       break;
  675.     }
  676.   }
  677. }
  678. void MainWindow::addIndexMark()
  679. {
  680.   unsigned long sample;
  681.   if (!tocEdit_->editable()) {
  682.     tocBlockedMsg("Add Index Mark");
  683.     return;
  684.   }
  685.   if (getMarker(&sample)) {
  686.     long lba;
  687.     int snapped = snapSampleToBlock(sample, &lba);
  688.     switch (tocEdit_->addIndexMarker(lba)) {
  689.     case 0:
  690.       statusMessage("Added index mark at %s%s.", Msf(lba).str(),
  691.     snapped ? " (snapped to next block)" : "");
  692.       guiUpdate();
  693.       break;
  694.     case 2:
  695.       statusMessage("Cannot add index at this point.");
  696.       break;
  697.     case 3:
  698.       statusMessage("Track has already 98 index marks.");
  699.       break;
  700.     default:
  701.       statusMessage("Internal error in addIndexMark(), please report.");
  702.       break;
  703.     }
  704.   }
  705. }
  706. void MainWindow::addPregap()
  707. {
  708.   unsigned long sample;
  709.   if (!tocEdit_->editable()) {
  710.     tocBlockedMsg("Add Pre-Gap");
  711.     return;
  712.   }
  713.   if (getMarker(&sample)) {
  714.     long lba;
  715.     int snapped = snapSampleToBlock(sample, &lba);
  716.     switch (tocEdit_->addPregap(lba)) {
  717.     case 0:
  718.       statusMessage("Added pre-gap mark at %s%s.", Msf(lba).str(),
  719.     snapped ? " (snapped to next block)" : "");
  720.       guiUpdate();
  721.       break;
  722.     case 2:
  723.       statusMessage("Cannot add pre-gap at this point.");
  724.       break;
  725.     case 3:
  726.       statusMessage("Track would be shorter than 4 seconds.");
  727.       break;
  728.     case 4:
  729.       statusMessage("Cannot modify a data track.");
  730.       break;
  731.     default:
  732.       statusMessage("Internal error in addPregap(), please report.");
  733.       break;
  734.     }
  735.   }
  736. }
  737. void MainWindow::removeTrackMark()
  738. {
  739.   int trackNr;
  740.   int indexNr;
  741.   if (!tocEdit_->editable()) {
  742.     tocBlockedMsg("Remove Track Mark");
  743.     return;
  744.   }
  745.   if (tocEdit_->trackSelection(&trackNr) &&
  746.       tocEdit_->indexSelection(&indexNr)) {
  747.     switch (tocEdit_->removeTrackMarker(trackNr, indexNr)) {
  748.     case 0:
  749.       statusMessage("Removed track/index marker.");
  750.       guiUpdate();
  751.       break;
  752.     case 1:
  753.       statusMessage("Cannot remove first track.");
  754.       break;
  755.     case 3:
  756.       statusMessage("Cannot modify a data track.");
  757.       break;
  758.     default:
  759.       statusMessage("Internal error in removeTrackMark(), please report.");
  760.       break; 
  761.     }
  762.   }
  763.   else {
  764.     statusMessage("Please select a track/index mark.");
  765.   }
  766. }
  767. void MainWindow::appendTrack()
  768. {
  769.   ADD_FILE_DIALOG->mode(AddFileDialog::M_APPEND_TRACK);
  770.   ADD_FILE_DIALOG->start(tocEdit_);
  771. }
  772. void MainWindow::appendFile()
  773. {
  774.   ADD_FILE_DIALOG->mode(AddFileDialog::M_APPEND_FILE);
  775.   ADD_FILE_DIALOG->start(tocEdit_);
  776. }
  777. void MainWindow::insertFile()
  778. {
  779.   ADD_FILE_DIALOG->mode(AddFileDialog::M_INSERT_FILE);
  780.   ADD_FILE_DIALOG->start(tocEdit_);
  781. }
  782. void MainWindow::appendSilence()
  783. {
  784.   ADD_SILENCE_DIALOG->mode(AddSilenceDialog::M_APPEND);
  785.   ADD_SILENCE_DIALOG->start(tocEdit_);
  786. }
  787. void MainWindow::insertSilence()
  788. {
  789.   ADD_SILENCE_DIALOG->mode(AddSilenceDialog::M_INSERT);
  790.   ADD_SILENCE_DIALOG->start(tocEdit_);
  791. }
  792. void MainWindow::trackInfo()
  793. {
  794.   TRACK_INFO_DIALOG->start(tocEdit_);
  795. }
  796. void MainWindow::tocInfo()
  797. {
  798.   TOC_INFO_DIALOG->start(tocEdit_);
  799. }
  800. void MainWindow::cutTrackData()
  801. {
  802.   if (!tocEdit_->editable()) {
  803.     tocBlockedMsg("Cut");
  804.     return;
  805.   }
  806.   switch (tocEdit_->removeTrackData()) {
  807.   case 0:
  808.     statusMessage("Removed selected samples.");
  809.     guiUpdate();
  810.     break;
  811.   case 1:
  812.     statusMessage("Please select samples.");
  813.     break;
  814.   case 2:
  815.     statusMessage("Selected sample range crosses track boundaries.");
  816.     break;
  817.   }
  818. }
  819. void MainWindow::pasteTrackData()
  820. {
  821.   if (!tocEdit_->editable()) {
  822.     tocBlockedMsg("Paste");
  823.     return;
  824.   }
  825.   switch (tocEdit_->insertTrackData()) {
  826.   case 0:
  827.     statusMessage("Pasted samples.");
  828.     guiUpdate();
  829.     break;
  830.   case 1:
  831.     statusMessage("No samples in scrap.");
  832.     break;
  833.   }
  834. }
  835. void MainWindow::markerSet()
  836. {
  837.   unsigned long s = string2sample(markerPos_->get_text().c_str());
  838.   tocEdit_->sampleMarker(s);
  839.   guiUpdate();
  840. }
  841. void MainWindow::selectionSet()
  842. {
  843.   unsigned long s1 = string2sample(selectionStartPos_->get_text().c_str());
  844.   unsigned long s2 = string2sample(selectionEndPos_->get_text().c_str());
  845.   tocEdit_->sampleSelection(s1, s2);
  846.   guiUpdate();
  847. }
  848. void MainWindow::configureDevices()
  849. {
  850.   DEVICE_CONF_DIALOG->start(tocEdit_);
  851. }
  852. void MainWindow::record()
  853. {
  854.   RECORD_DIALOG->start(tocEdit_);
  855. }