// rdlibrary.cpp
//
// The Library Utility for Rivendell.
//
//   (C) Copyright 2002-2018 Fred Gleason <fredg@paravelsystems.com>
//
//   This program is free software; you can redistribute it and/or modify
//   it under the terms of the GNU General Public License version 2 as
//   published by the Free Software Foundation.
//
//   This program is distributed in the hope that it will be useful,
//   but WITHOUT ANY WARRANTY; without even the implied warranty of
//   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
//   GNU General Public License for more details.
//
//   You should have received a copy of the GNU General Public
//   License along with this program; if not, write to the Free Software
//   Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
//

#include <unistd.h>
#include <signal.h>
#include <sys/types.h>
#include <sys/wait.h>

#include <qapplication.h>
#include <qwindowsstyle.h>
#include <qeventloop.h>
#include <qwidget.h>
#include <qpainter.h>
#include <q3sqlpropertymap.h>
#include <qmessagebox.h>
#include <qlabel.h>
#include <qtextcodec.h>
#include <qtranslator.h>
#include <qlabel.h>
#include <q3listview.h>
#include <q3progressdialog.h>
#include <qtooltip.h>
#include <qstylefactory.h>
#include <qkeysequence.h>
#include <qshortcut.h>

#include <curl/curl.h>

#include <dbversion.h>
#include <rd.h>
#include <rdadd_cart.h>
#include <rdapplication.h>
#include <rdaudio_port.h>
#include <rdcart_search_text.h>
#include <rdconf.h>
#include <rdescape_string.h>
#include <rdmixer.h>
#include <rdprofile.h>
#include <rdtextvalidator.h>

#include "cdripper.h"
#include "disk_ripper.h"
#include "edit_cart.h"
#include "filter.h"
#include "globals.h"
#include "list_reports.h"
#include "rdlibrary.h"
#include "validate_cut.h"

//
// Global Resources
//
RDAudioPort *rdaudioport_conf;
DiskGauge *disk_gauge;
RDCut *cut_clipboard=NULL;
bool audio_changed;

//
// Prototypes
//
void SigHandler(int signo);

//
// Icons
//
#include "../icons/play.xpm"
#include "../icons/rml5.xpm"
#include "../icons/track_cart.xpm"
#include "../icons/rdlibrary-22x22.xpm"

MainWidget::MainWidget(QWidget *parent)
  :QWidget(parent)
{
  QString err_msg;

  lib_resize=false;
  profile_ripping=false;
  lib_edit_pending=false;
  lib_user_changed=false;

  //
  // Fix the Window Size
  //
  setMinimumWidth(sizeHint().width());
  setMinimumHeight(sizeHint().height());

  //
  // Initialize LibCurl
  //
  curl_global_init(CURL_GLOBAL_ALL);

  //
  // Generate Fonts
  //
  QFont default_font("Helvetica",12,QFont::Normal);
  default_font.setPixelSize(12);
  qApp->setFont(default_font);
  QFont button_font=QFont("Helvetica",12,QFont::Bold);
  button_font.setPixelSize(12);
  QFont filter_font=QFont("Helvetica",16,QFont::Bold);
  filter_font.setPixelSize(16);

  //
  // Create Icons
  //
  lib_playout_map=new QPixmap(play_xpm);
  lib_macro_map=new QPixmap(rml5_xpm);
  lib_track_cart_map=new QPixmap(track_cart_xpm);
  lib_rivendell_map=new QPixmap(rdlibrary_22x22_xpm);
  setWindowIcon(*lib_rivendell_map);

  //
  // Progress Dialog
  //
  lib_progress_dialog=
    new Q3ProgressDialog(tr("Please Wait..."),"Cancel",10,this,
			"lib_progress_dialog",false,
			Qt::WStyle_Customize|Qt::WStyle_NormalBorder);
  lib_progress_dialog->setCaption(" ");
  QLabel *label=new QLabel(tr("Please Wait..."),lib_progress_dialog);
  label->setAlignment(Qt::AlignCenter);
  label->setFont(filter_font);
  lib_progress_dialog->setLabel(label);
  lib_progress_dialog->setCancelButton(NULL);
  lib_progress_dialog->setMinimumDuration(2000);

  //
  // Open the Database
  //
  rda=new RDApplication("RDLibrary","rdlibrary",RDLIBRARY_USAGE,this);
  if(!rda->open(&err_msg)) {
    QMessageBox::critical(this,"RDLibrary - "+tr("Error"),err_msg);
    exit(1);
  }

  //
  // Read Command Options
  //
  for(unsigned i=0;i<rda->cmdSwitch()->keys();i++) {
    if(rda->cmdSwitch()->key(i)=="--profile-ripping") {
      profile_ripping=true;
      rda->cmdSwitch()->setProcessed(i,true);
    }
    if(!rda->cmdSwitch()->processed(i)) {
      QMessageBox::critical(this,"RDLibrary - "+tr("Error"),
			    tr("Unknown command option")+": "+
			    rda->cmdSwitch()->key(i));
      exit(2);
    }
  }

  SetCaption("");
  lib_import_path=RDGetHomeDir();

  //
  // Allocate Global Resources
  //
  lib_filter_mode=rda->station()->filterMode();
  rdaudioport_conf=new RDAudioPort(rda->config()->stationName(),
				   rda->libraryConf()->inputCard());
  connect(rda,SIGNAL(userChanged()),this,SLOT(userData()));
  connect(rda->ripc(),SIGNAL(notificationReceived(RDNotification *)),
	  this,SLOT(notificationReceivedData(RDNotification *)));
  rda->ripc()->
    connectHost("localhost",RIPCD_TCP_PORT,rda->config()->password());
  cut_clipboard=NULL;
  lib_user_timer=new QTimer(this);
  connect(lib_user_timer,SIGNAL(timeout()),this,SLOT(userData()));

  //
  // CAE Connection
  //
  connect(rda->cae(),SIGNAL(isConnected(bool)),
	  this,SLOT(caeConnectedData(bool)));
  rda->cae()->connectHost();

  //
  // Load Audio Assignments
  //
  RDSetMixerPorts(rda->config()->stationName(),rda->cae());

  //
  // Filter
  //
  lib_filter_edit=new QLineEdit(this);
  lib_filter_edit->setFont(default_font);
  lib_filter_label=new QLabel(lib_filter_edit,tr("Filter:"),this);
  lib_filter_label->setFont(button_font);
  lib_filter_label->setAlignment(Qt::AlignVCenter|Qt::AlignRight);
  connect(lib_filter_edit,SIGNAL(textChanged(const QString &)),
	  this,SLOT(filterChangedData(const QString &)));
  connect(lib_filter_edit,SIGNAL(returnPressed()),
	  this,SLOT(searchClickedData()));

  //
  // Filter Search Button
  //
  lib_search_button=new QPushButton(tr("&Search"),this);
  lib_search_button->setFont(button_font);
  connect(lib_search_button,SIGNAL(clicked()),this,SLOT(searchClickedData()));
  switch(lib_filter_mode) {
  case RDStation::FilterSynchronous:
    lib_search_button->hide();
    break;

  case RDStation::FilterAsynchronous:
    break;
  }

  //
  // Filter Clear Button
  //
  lib_clear_button=new QPushButton(tr("&Clear"),this);
  lib_clear_button->setFont(button_font);
  lib_clear_button->setDisabled(true);
  connect(lib_clear_button,SIGNAL(clicked()),this,SLOT(clearClickedData()));

  //
  // Group Filter
  //
  lib_group_box=new QComboBox(this);
  lib_group_box->setFont(default_font);
  lib_group_label=new QLabel(lib_group_box,tr("Group:"),this);
  lib_group_label->setFont(button_font);
  lib_group_label->setAlignment(Qt::AlignVCenter|Qt::AlignRight);
  connect(lib_group_box,SIGNAL(activated(const QString &)),
	  this,SLOT(groupActivatedData(const QString &)));

  //
  // Scheduler Codes Filter
  //
  lib_codes_box=new QComboBox(this);
  lib_codes_box->setFont(default_font);
  lib_codes_label=new QLabel(lib_codes_box,tr("Scheduler Code:"),this);
  lib_codes_label->setFont(button_font);
  lib_codes_label->setAlignment(Qt::AlignVCenter|Qt::AlignRight);
  connect(lib_codes_box,SIGNAL(activated(const QString &)),
	  this,SLOT(groupActivatedData(const QString &)));

  //
  // Show Allow Cart Drags Checkbox
  //
  lib_allowdrag_box=new QCheckBox(this);
  lib_allowdrag_box->setChecked(false);
  lib_allowdrag_label=
    new QLabel(lib_allowdrag_box,tr("Allow Cart Dragging"),this);
  lib_allowdrag_label->setFont(button_font);
  lib_allowdrag_label->setAlignment(Qt::AlignVCenter|Qt::AlignLeft);
  connect(lib_allowdrag_box,SIGNAL(stateChanged(int)),
	  this,SLOT(dragsChangedData(int)));
  if(!rda->station()->enableDragdrop()) {
    lib_allowdrag_box->hide();
    lib_allowdrag_label->hide();
  }

  //
  // Show Audio Carts Checkbox
  //
  lib_showaudio_box=new QCheckBox(this);
  lib_showaudio_box->setChecked(true);
  lib_showaudio_label=new QLabel(lib_showaudio_box,tr("Show Audio Carts"),this);
  lib_showaudio_label->setFont(button_font);
  lib_showaudio_label->setAlignment(Qt::AlignVCenter|Qt::AlignLeft);
  connect(lib_showaudio_box,SIGNAL(stateChanged(int)),
	  this,SLOT(audioChangedData(int)));

  //
  // Show Macro Carts Checkbox
  //
  lib_showmacro_box=new QCheckBox(this);
  lib_showmacro_box->setChecked(true);
  lib_showmacro_label=new QLabel(lib_showmacro_box,tr("Show Macro Carts"),this);
  lib_showmacro_label->setFont(button_font);
  lib_showmacro_label->setAlignment(Qt::AlignVCenter|Qt::AlignLeft);
  connect(lib_showmacro_box,SIGNAL(stateChanged(int)),
	  this,SLOT(macroChangedData(int)));

  //
  // Show Cart Notes Checkbox
  //
  lib_shownotes_box=new QCheckBox(this);
  lib_shownotes_box->setChecked(true);
  lib_shownotes_label=
    new QLabel(lib_shownotes_box,tr("Show Note Bubbles"),this);
  lib_shownotes_label->setFont(button_font);
  lib_shownotes_label->setAlignment(Qt::AlignVCenter|Qt::AlignLeft);

  //
  // Show Matches Checkbox
  //
  lib_showmatches_box=new QCheckBox(this);
  lib_showmatches_label=
    new QLabel(lib_showmatches_box,tr("Show Only First ")+
	       QString().sprintf("%d",RD_LIMITED_CART_SEARCH_QUANTITY)+
	       tr(" Matches"),this);
  lib_showmatches_label->setFont(button_font);
  lib_showmatches_label->setAlignment(Qt::AlignVCenter|Qt::AlignLeft);
  connect(lib_showmatches_box,SIGNAL(stateChanged(int)),
	  this,SLOT(searchLimitChangedData(int)));

  //
  // Cart List
  //
  lib_cart_list=new LibListView(this);
  lib_cart_list->setFont(default_font);
  lib_cart_list->setAllColumnsShowFocus(true);
  lib_cart_list->setItemMargin(5);
  lib_cart_list->setSelectionMode(Q3ListView::Extended);
  lib_cart_list->setRootIsDecorated(true);
  lib_cart_list->enableNoteBubbles(true);
  connect(lib_cart_list,
	  SIGNAL(doubleClicked(Q3ListViewItem *,const QPoint &,int)),
	  this,
	  SLOT(cartDoubleclickedData(Q3ListViewItem *,const QPoint &,int)));
  connect(lib_cart_list,SIGNAL(selectionChanged()),
	  this,SLOT(cartClickedData()));
  connect(lib_cart_list,SIGNAL(onItem(Q3ListViewItem *)),
	  this,SLOT(cartOnItemData(Q3ListViewItem *)));
  connect(lib_shownotes_box,SIGNAL(toggled(bool)),
  	  lib_cart_list,SLOT(enableNoteBubbles(bool)));
  lib_cart_list->addColumn("");
  lib_cart_list->setColumnAlignment(Icon,Qt::AlignHCenter);
  lib_cart_list->addColumn(tr("Cart"));
  lib_cart_list->setColumnAlignment(Cart,Qt::AlignLeft);

  lib_cart_list->addColumn(tr("Group"));
  lib_cart_list->setColumnAlignment(Group,Qt::AlignHCenter);

  lib_cart_list->addColumn(tr("Length"));
  lib_cart_list->setColumnAlignment(Length,Qt::AlignRight);
  lib_cart_list->setColumnSortType(Length,RDListView::TimeSort);

  lib_cart_list->addColumn(tr("Talk"));
  lib_cart_list->setColumnAlignment(Talk,Qt::AlignRight);
  lib_cart_list->setColumnSortType(Length,RDListView::TimeSort);

  lib_cart_list->addColumn(tr("Title"));
  lib_cart_list->setColumnAlignment(Title,Qt::AlignLeft);

  lib_cart_list->addColumn(tr("Artist"));
  lib_cart_list->setColumnAlignment(Artist,Qt::AlignLeft);

  lib_cart_list->addColumn(tr("Start"));
  lib_cart_list->setColumnAlignment(Start,Qt::AlignHCenter);

  lib_cart_list->addColumn(tr("End"));
  lib_cart_list->setColumnAlignment(End,Qt::AlignHCenter);

  lib_cart_list->addColumn(tr("Album"));
  lib_cart_list->setColumnAlignment(Album,Qt::AlignLeft);

  lib_cart_list->addColumn(tr("Label"));
  lib_cart_list->setColumnAlignment(Label,Qt::AlignLeft);

  lib_cart_list->addColumn(tr("Composer"));
  lib_cart_list->setColumnAlignment(Composer,Qt::AlignLeft);

  lib_cart_list->addColumn(tr("Conductor"));
  lib_cart_list->setColumnAlignment(Conductor,Qt::AlignLeft);

  lib_cart_list->addColumn(tr("Publisher"));
  lib_cart_list->setColumnAlignment(Publisher,Qt::AlignLeft);

  lib_cart_list->addColumn(tr("Client"));
  lib_cart_list->setColumnAlignment(Client,Qt::AlignLeft);

  lib_cart_list->addColumn(tr("Agency"));
  lib_cart_list->setColumnAlignment(Agency,Qt::AlignLeft);

  lib_cart_list->addColumn(tr("User Defined"));
  lib_cart_list->setColumnAlignment(UserDefined,Qt::AlignLeft);

  lib_cart_list->addColumn(tr("Cuts"));
  lib_cart_list->setColumnAlignment(Cuts,Qt::AlignRight);

  lib_cart_list->addColumn(tr("Last Cut Played"));
  lib_cart_list->setColumnAlignment(LastCutPlayed,Qt::AlignHCenter);

  lib_cart_list->addColumn(tr("Enforce Length"));
  lib_cart_list->setColumnAlignment(EnforceLength,Qt::AlignHCenter);

  lib_cart_list->addColumn(tr("Preserve Pitch"));
  lib_cart_list->setColumnAlignment(PreservePitch,Qt::AlignHCenter);

  lib_cart_list->addColumn(tr("Length Deviation"));
  lib_cart_list->setColumnAlignment(LengthDeviation,Qt::AlignHCenter);

  lib_cart_list->addColumn(tr("Owned By"));
  lib_cart_list->setColumnAlignment(OwnedBy,Qt::AlignHCenter);

  //
  // Add Button
  //
  lib_add_button=new QPushButton(this);
  lib_add_button->setFont(button_font);
  lib_add_button->setText(tr("&Add"));
  connect(lib_add_button,SIGNAL(clicked()),this,SLOT(addData()));

  //
  // Edit Button
  //
  lib_edit_button=new QPushButton(this);
  lib_edit_button->setFont(button_font);
  lib_edit_button->setText(tr("&Edit"));
  lib_edit_button->setEnabled(false);
  connect(lib_edit_button,SIGNAL(clicked()),this,SLOT(editData()));

  //
  // Delete Button
  //
  lib_delete_button=new QPushButton(this);
  lib_delete_button->setFont(button_font);
  lib_delete_button->setText(tr("&Delete"));
  lib_delete_button->setEnabled(false);
  connect(lib_delete_button,SIGNAL(clicked()),this,SLOT(deleteData()));

  //
  // Run Macro Button
  //
  lib_macro_button=new QPushButton(this);
  lib_macro_button->setFont(button_font);
  lib_macro_button->setText(tr("Run\n&Macro"));
  lib_macro_button->setEnabled(false);
  lib_macro_button->setVisible(false);
  connect(lib_macro_button,SIGNAL(clicked()),this,SLOT(macroData()));

  //
  // Disk Gauge
  //
  disk_gauge=new DiskGauge(rda->system()->sampleRate(),
			   rda->libraryConf()->defaultChannels(),this);

  //
  // Rip Button
  //
  lib_rip_button=new QPushButton(this);
  lib_rip_button->setFont(button_font);
  lib_rip_button->setText(tr("&Rip\nCD"));
  connect(lib_rip_button,SIGNAL(clicked()),this,SLOT(ripData()));

  //
  // Reports Button
  //
  lib_reports_button=new QPushButton(this);
  lib_reports_button->setFont(button_font);
  lib_reports_button->setText(tr("Re&ports"));
  connect(lib_reports_button,SIGNAL(clicked()),this,SLOT(reportsData()));

  //
  // Close Button
  //
  lib_close_button=new QPushButton(this);
  lib_close_button->setFont(button_font);
  lib_close_button->setText(tr("&Close"));
  connect(lib_close_button,SIGNAL(clicked()),this,SLOT(quitMainWidget()));


  //
  // Load Output Assignment
  //
  lib_output_card=rda->libraryConf()->outputCard();
  lib_output_port=rda->libraryConf()->outputPort();


  //
  // Cart Player
  //
  lib_player=
    new RDSimplePlayer(rda->cae(),rda->ripc(),lib_output_card,lib_output_port,
                       0,0,this);
  lib_player->playButton()->setEnabled(false);
  lib_player->stopButton()->setEnabled(false);
  lib_player->stopButton()->setOnColor(Qt::red);
  lib_player->playButton()->setFocusPolicy(Qt::NoFocus);
  lib_player->stopButton()->setFocusPolicy(Qt::NoFocus);

  QShortcut *lib_player_shortcut=new QShortcut(Qt::Key_Space,this);
  connect(lib_player_shortcut,SIGNAL(activated()),this,SLOT(playerShortcutData()));

  // 
  // Setup Signal Handling 
  //
  ::signal(SIGCHLD,SigHandler);

  //
  // Load Data
  //
  switch(rda->libraryConf()->limitSearch()) {
  case RDLibraryConf::LimitNo:
    lib_showmatches_box->setChecked(false);
    break;

  case RDLibraryConf::LimitYes:
    lib_showmatches_box->setChecked(true);
    break;

  case RDLibraryConf::LimitPrevious:
    lib_showmatches_box->setChecked(rda->libraryConf()->searchLimited());
    break;
  }

  lib_resize=true;

  //
  // Create RDMacroEvent for running macros
  //
  lib_macro_events=new RDMacroEvent(rda->station()->address(),rda->ripc(),this);

  LoadGeometry();
}


QSize MainWidget::sizeHint() const
{
  return QSize(850,600);
}


QSizePolicy MainWidget::sizePolicy() const
{
  return QSizePolicy(QSizePolicy::Fixed,QSizePolicy::Fixed);
}


void MainWidget::caeConnectedData(bool state)
{
  if(state) {
    std::vector<int> cards;
    cards.push_back(rda->libraryConf()->inputCard());
    cards.push_back(rda->libraryConf()->outputCard());
    rda->cae()->enableMetering(&cards);
  }
}


void MainWidget::userData()
{
  QString sql;
  RDSqlQuery *q;

  if(lib_edit_pending) {
    lib_user_changed=true;
    return;
  }

  SetCaption(rda->ripc()->user());

  lib_group_box->clear();
  lib_group_box->insertItem(tr("ALL"));
  sql=QString("select GROUP_NAME from USER_PERMS where ")+
    "USER_NAME=\""+RDEscapeString(rda->user()->name())+"\" order by GROUP_NAME";
  q=new RDSqlQuery(sql);
  while(q->next()) {
    lib_group_box->insertItem(q->value(0).toString());
  }
  delete q;

  if(lib_group_box->count()==1) {
    lib_add_button->setDisabled(true);
    lib_edit_button->setDisabled(true);
    lib_delete_button->setDisabled(true);
    lib_rip_button->setDisabled(true);
  }
  else {
    lib_add_button->setEnabled(rda->user()->createCarts());
    lib_rip_button->setEnabled(rda->user()->editAudio());
  }

  lib_codes_box->clear();
  lib_codes_box->insertItem(tr("ALL"));
  sql=QString().sprintf("select CODE from SCHED_CODES");
  q=new RDSqlQuery(sql);
  while(q->next()) {
    lib_codes_box->insertItem(q->value(0).toString());
  }
  delete q;
  lib_search_button->setDisabled(true);
  groupActivatedData(lib_group_box->currentText());
}


void MainWidget::filterChangedData(const QString &str)
{
  lib_search_button->setEnabled(true);
  if(lib_filter_mode!=RDStation::FilterSynchronous) {
    return;
  }
  searchClickedData();
}


void MainWidget::searchClickedData()
{
  lib_search_button->setDisabled(true);
  if(lib_filter_edit->text().isEmpty()) {
    lib_clear_button->setDisabled(true);
  }
  else {
    lib_clear_button->setEnabled(true);
  }
  RefreshList();
}


void MainWidget::clearClickedData()
{
  lib_filter_edit->clear();
  filterChangedData("");
}


void MainWidget::groupActivatedData(const QString &str)
{
  if(str!=tr("ALL")) {
    lib_default_group=str;
  }
  filterChangedData("");
}


void MainWidget::playerShortcutData()
{
  if(lib_player->isPlaying()) {
    lib_player->stop();
  }
  else if(lib_player->playButton()->isEnabled()) {
    lib_player->play();
  }
  else if(lib_macro_button->isEnabled()) {
    macroData();
  }
}

void MainWidget::addData()
{
  QString sql;
  RDSqlQuery *q;
  int cart_num;
  RDCart::Type cart_type=RDCart::All;
  QString cart_title;

  lib_player->stop();
  LockUser();

  RDAddCart *add_cart=new RDAddCart(&lib_default_group,&cart_type,&cart_title,
				    rda->user()->name(),rda->system(),this);
  if((cart_num=add_cart->exec())<0) {
    delete add_cart;
    UnlockUser();
    return;
  }
  delete add_cart;
  sql=QString("insert into CART set ")+
    QString().sprintf("NUMBER=%u,TYPE=%d,",cart_num,cart_type)+
    "GROUP_NAME=\""+RDEscapeString(lib_default_group)+"\","+
    "TITLE=\""+RDEscapeString(cart_title)+"\"";
  q=new RDSqlQuery(sql);
  delete q;
  
  EditCart *cart=
    new EditCart(cart_num,&lib_import_path,true,profile_ripping,this);
  if(cart->exec() <0) {
    RDCart *rdcart=new RDCart(cart_num);
    rdcart->remove(rda->station(),rda->user(),rda->config());
    delete rdcart;
  } 
  else {
    RDListViewItem *item=new RDListViewItem(lib_cart_list);
    item->setText(Cart,QString().sprintf("%06u",cart_num));
    RefreshLine(item);
    SendNotification(RDNotification::AddAction,cart_num);
    Q3ListViewItemIterator it(lib_cart_list);
    while(it.current()) {
      lib_cart_list->setSelected(it.current(),false);
      ++it;
    }
    lib_cart_list->setSelected(item,true);
    lib_cart_list->ensureItemVisible(item);
  }
  delete cart;

  UnlockUser();
}



void MainWidget::editData()
{
  int sel_count=0;
  Q3ListViewItemIterator *it;

  lib_player->stop();
  LockUser();

  it=new Q3ListViewItemIterator(lib_cart_list);
  while(it->current()) {
    if (it->current()->isSelected() && !it->current()->parent()) {
      sel_count++;
    }
    ++(*it);
  }
  delete it;

  if(sel_count==0) {
    UnlockUser();
    return;
  }
  if(sel_count==1) { //single edit
    it=new Q3ListViewItemIterator(lib_cart_list);
    while(!it->current()->isSelected() || it->current()->parent()) {
      ++(*it);
    }
    RDListViewItem *item=(RDListViewItem *)it->current();

    EditCart *edit_cart=new EditCart(item->text(Cart).toUInt(),&lib_import_path,
				     false,profile_ripping,this);
    edit_cart->exec();
    RefreshLine(item);
    cartOnItemData(item);
    SendNotification(RDNotification::ModifyAction,item->text(Cart).toUInt());
    delete edit_cart;
    delete it;
  }
  else { //multi edit
    if(rda->user()->modifyCarts()) {
      EditCart *edit_cart=
	new EditCart(0,&lib_import_path,false,profile_ripping,this,"",
				       lib_cart_list);
    
      edit_cart->exec();
      delete edit_cart;
    
      it=new Q3ListViewItemIterator(lib_cart_list);
      while(it->current()) {
        if (it->current()->isSelected() && !it->current()->parent()) {
          RefreshLine((RDListViewItem *)it->current());
	  SendNotification(RDNotification::ModifyAction,
			   it->current()->text(1).toUInt());
        }
        ++(*it);
      }
      delete it;
    }
  }
  UnlockUser();
}


void MainWidget::macroData()
{
  Q3ListViewItemIterator *it;
  Q3ListViewItem *item;

  it=new Q3ListViewItemIterator(lib_cart_list);
  while(it->current()) {
    if (it->current()->isSelected()) {
      item=it->current();
      RDCart *rdcart=new RDCart(item->text(Cart).toUInt());
      lib_macro_events->clear();
      lib_macro_events->load(rdcart->macros());
      lib_macro_events->exec();
      delete rdcart;
      break;
    }
    ++(*it);
  }
  delete it;
}

void MainWidget::deleteData()
{
  QString filename;
  QString sql;
  RDSqlQuery *q;
  QString str;
  int sel_count=0;
  Q3ListViewItemIterator *it;
  bool del_flag;

  lib_player->stop();
  LockUser();

  it=new Q3ListViewItemIterator(lib_cart_list);
  while(it->current()) {
    if (it->current()->isSelected() && !it->current()->parent()) {
      sel_count++;
    }
    ++(*it);
  }
  delete it;

  if(sel_count==0) {
    UnlockUser();
    return;
  }

  str=QString(tr("Are you sure you want to delete cart(s)"));
  if(QMessageBox::question(this,tr("Delete Cart(s)"),str,QMessageBox::Yes,QMessageBox::No)!=
     QMessageBox::Yes) {
    UnlockUser();
    return;
  }
  it=new Q3ListViewItemIterator(lib_cart_list);
  while(it->current()) {
    if (it->current()->isSelected() && !it->current()->parent()) {
    del_flag=true;
    RDListViewItem *item=(RDListViewItem *)it->current();
  sql=QString().sprintf("select CUT_NAME from RECORDINGS where \
                         (CUT_NAME like \"%06u_%%\")||(MACRO_CART=%u)",
			item->text(Cart).toUInt(),item->text(Cart).toUInt());
  q=new RDSqlQuery(sql);
  if(q->first()) {
      QString str=QString().sprintf(tr("Cart %06u is used in one or more RDCatch events!\n\
Do you still want to delete it?"),item->text(Cart).toUInt());
      if(QMessageBox::warning(this,tr("RDCatch Event Exists"),str,
			        QMessageBox::Yes,QMessageBox::No)==QMessageBox::No) {
        del_flag=false;
    }
  }
  delete q;
  if(cut_clipboard!=NULL) {
    if(item->text(Cart).toUInt()==cut_clipboard->cartNumber()) {
      	QString str=QString().sprintf(tr("Deleting cart %06u will also empty the clipboard.\n\
      	Do you still want to proceed?"),item->text(Cart).toUInt());
        switch(QMessageBox::question(this,tr("Empty Clipboard"),str,
				  QMessageBox::Yes,
				  QMessageBox::No)) {
	case QMessageBox::No:
	case QMessageBox::NoButton:
	  del_flag=false;

	default:
	  break;
      }
      delete cut_clipboard;
      cut_clipboard=NULL;
    }
  }
  if(del_flag && item->text(OwnedBy).isEmpty()) {
    RDCart *rdcart=new RDCart(item->text(Cart).toUInt());
    if(!rdcart->remove(rda->station(),rda->user(),rda->config())) {
      QMessageBox::warning(this,tr("RDLibrary"),tr("Unable to delete audio!"));
      return;
    }
    SendNotification(RDNotification::DeleteAction,rdcart->number());
    delete rdcart;
    delete item;
  } 
  else {
    ++(*it);
  } 
    }
    else {
      ++(*it);
    } 
  }
  delete it;

  UnlockUser();
}


void MainWidget::ripData()
{
  lib_player->stop();
  LockUser();
  QString group=lib_group_box->currentText();
  QString schedcode=lib_codes_box->currentText();
  DiskRipper *dialog=new DiskRipper(&lib_filter_text,&group,&schedcode,
				    profile_ripping,this);
  if(dialog->exec()==0) {
    for(int i=0;i<lib_group_box->count();i++) {
      if(lib_group_box->text(i)==group) {
	lib_filter_edit->setText(lib_filter_text);
	lib_group_box->setCurrentItem(i);
	groupActivatedData(lib_group_box->currentText());
      }
    }
  }
  delete dialog;
  if(!UnlockUser()) {
    RefreshList();
  }
}


void MainWidget::reportsData()
{
  lib_player->stop();
  LockUser();
  ListReports *lr=
    new ListReports(lib_filter_edit->text(),GetTypeFilter(),
		    lib_group_box->currentText(),lib_codes_box->currentText(),
		    this);
  lr->exec();
  delete lr;
  UnlockUser();
}


void MainWidget::cartOnItemData(Q3ListViewItem *item)
{
  if((!lib_shownotes_box->isChecked())||(item==NULL)) {
    return;
  }
  //  lib_cart_tip->
  //    setCartNumber(lib_cart_list->itemRect(item),item->text(Cart).toUInt());
}


void MainWidget::cartClickedData()
{
  int del_count=0;
  int sel_count=0;
  int play_count=0;
  Q3ListViewItemIterator *it;
  Q3ListViewItem *item;

  it=new Q3ListViewItemIterator(lib_cart_list);
  while(it->current()) {
    if (it->current()->isSelected()) {
      play_count++;
      item=it->current();
      if(!it->current()->parent()) {
        sel_count++;
      }
      if(it->current()->text(OwnedBy).isEmpty() && !it->current()->parent()) {
        del_count++;
      }
    }
    ++(*it);
  }
  delete it;
  
  if(del_count>0) {
    lib_delete_button->setEnabled(rda->user()->deleteCarts());
  } 
  else {
    lib_delete_button->setEnabled(false);
  }

  if(play_count==1) {
    RDCart *rdcart=new RDCart(item->text(Cart).toUInt());
    if(rdcart->type()==RDCart::Macro) {
      lib_macro_button->setEnabled(true);
      lib_macro_button->setVisible(true);
      lib_player->playButton()->setEnabled(false);
      lib_player->stopButton()->setEnabled(false);
      lib_player->playButton()->setVisible(false);
      lib_player->stopButton()->setVisible(false);
      lib_player->stop();
    }
    else {
      lib_player->setCart(item->text(Cart));
      lib_player->playButton()->setEnabled(true);
      lib_player->stopButton()->setEnabled(true);
      lib_player->playButton()->setVisible(true);
      lib_player->stopButton()->setVisible(true);
      lib_macro_button->setEnabled(false);
      lib_macro_button->setVisible(false);
      if(lib_player->isPlaying()) {
        lib_player->play();
      }
    }
    delete rdcart;
  }
  else {
    lib_player->stop();
    lib_player->playButton()->setEnabled(false);
    lib_player->stopButton()->setEnabled(false);
    lib_player->playButton()->setVisible(true);
    lib_player->stopButton()->setVisible(true);
    lib_macro_button->setEnabled(false);
    lib_macro_button->setVisible(false);
  }

  if(sel_count) {
    if(del_count==0) {
      lib_edit_button->setEnabled(false);
    }
    else {
      lib_edit_button->setEnabled(rda->user()->modifyCarts());
    }
  } 
  else {
    lib_edit_button->setEnabled(false);
  }
}


void MainWidget::cartDoubleclickedData(Q3ListViewItem *,const QPoint &,int)
{
  editData();
}


void MainWidget::audioChangedData(int state)
{
  filterChangedData("");
}


void MainWidget::macroChangedData(int state)
{
  filterChangedData("");
}


void MainWidget::searchLimitChangedData(int state)
{
  rda->libraryConf()->setSearchLimited(state);
  filterChangedData("");
}


void MainWidget::dragsChangedData(int state)
{
  if(state) {
    lib_cart_list->setSelectionMode(Q3ListView::Single);
  }
  else {
    lib_cart_list->setSelectionMode(Q3ListView::Extended);
  }
}


void MainWidget::notificationReceivedData(RDNotification *notify)
{
  RDListViewItem *item=NULL;
  QString sql;
  RDSqlQuery *q;

  if(notify->type()==RDNotification::CartType) {
    unsigned cartnum=notify->id().toUInt();
    switch(notify->action()) {
    case RDNotification::AddAction:
      sql=QString("select CART.NUMBER from CART ")+
	"left join CUTS on CART.NUMBER=CUTS.CART_NUMBER "+
	WhereClause()+
	QString().sprintf(" && CART.NUMBER=%u ",cartnum);
      q=new RDSqlQuery(sql);
      if(q->first()) {
	item=new RDListViewItem(lib_cart_list);
	item->setText(Cart,QString().sprintf("%06u",cartnum));
	RefreshLine(item);
      }
      delete q;
      break;

    case RDNotification::ModifyAction:
      if((item=(RDListViewItem *)lib_cart_list->
	  findItem(QString().sprintf("%06u",cartnum),Cart))!=NULL) {
	RefreshLine(item);
      }
      break;

    case RDNotification::DeleteAction:
      if(lib_edit_pending) {
	lib_deleted_carts.push_back(cartnum);
      }
      else {
	if((item=(RDListViewItem *)lib_cart_list->findItem(QString().sprintf("%06u",cartnum),Cart))!=NULL) {
	  delete item;
	}
      }
      break;

    case RDNotification::NoAction:
    case RDNotification::LastAction:
      break;
    }
  }
}


void MainWidget::quitMainWidget()
{
  SaveGeometry();
  exit(0);
}


void MainWidget::closeEvent(QCloseEvent *e)
{
  quitMainWidget();
}


void MainWidget::resizeEvent(QResizeEvent *e)
{
  if(lib_resize) {
    switch(lib_filter_mode) {
    case RDStation::FilterSynchronous:
      lib_filter_edit->setGeometry(70,10,e->size().width()-170,20);
      break;

    case RDStation::FilterAsynchronous:
      lib_search_button->setGeometry(e->size().width()-180,10,80,50);
      lib_filter_edit->setGeometry(70,10,e->size().width()-260,20);
      break;
    }
    lib_clear_button->setGeometry(e->size().width()-90,10,80,50);
    lib_filter_label->setGeometry(10,10,55,20);
    lib_group_box->setGeometry(70,40,120,20);
    lib_group_label->setGeometry(10,40,55,20);
    lib_codes_box->setGeometry(330,40,120,20);
    lib_codes_label->setGeometry(195,40,130,20);
    lib_allowdrag_box->setGeometry(470,42,15,15);
    lib_allowdrag_label->setGeometry(490,40,130,20);
    lib_showaudio_box->setGeometry(70,67,15,15);
    lib_showaudio_label->setGeometry(90,65,130,20);
    lib_showmacro_box->setGeometry(230,67,15,15);
    lib_showmacro_label->setGeometry(250,65,130,20);
    lib_shownotes_box->setGeometry(390,67,15,15);
    lib_shownotes_label->setGeometry(410,65,130,20);
    lib_showmatches_box->setGeometry(550,67,15,15);
    lib_showmatches_label->setGeometry(570,65,200,20);
    lib_cart_list->
      setGeometry(10,90,e->size().width()-20,e->size().height()-155);
    lib_add_button->setGeometry(10,e->size().height()-60,80,50);
    lib_edit_button->setGeometry(100,e->size().height()-60,80,50);
    lib_delete_button->setGeometry(190,e->size().height()-60,80,50);
    lib_macro_button->setGeometry(290,e->size().height()-60,80,50);
    lib_player->playButton()->setGeometry(290,e->size().height()-60,80,50);
    lib_player->stopButton()->setGeometry(380,e->size().height()-60,80,50);
    disk_gauge->setGeometry(475,e->size().height()-55,
			    e->size().width()-765,
			    disk_gauge->sizeHint().height());
    lib_rip_button->
      setGeometry(e->size().width()-280,e->size().height()-60,80,50);
    lib_reports_button->
      setGeometry(e->size().width()-190,e->size().height()-60,80,50);
    lib_close_button->setGeometry(e->size().width()-90,e->size().height()-60,
				  80,50);
  }
}

void MainWidget::RefreshCuts(RDListViewItem *p,unsigned cartnum)
{
  RDListViewItem *l=NULL;
  Q3ListViewItem *i=NULL;
  RDSqlQuery *q;
  QString sql;
  QDateTime current_datetime(QDate::currentDate(),QTime::currentTime());
  QDateTime end_datetime;
  RDCart::Validity validity=RDCart::NeverValid;

  while ((i=p->firstChild())) {
    delete i;
  }

  sql=QString("select ")+
    "CUTS.CART_NUMBER,"+        // 00
    "CUTS.CUT_NAME,"+           // 01
    "CUTS.DESCRIPTION,"+        // 02
    "CUTS.TALK_START_POINT,"+   // 03
    "CUTS.TALK_END_POINT,"+     // 04
    "CUTS.LENGTH,"+             // 05  offsets begin here
    "CUTS.EVERGREEN,"+          // 06
    "CUTS.START_DATETIME,"+     // 07
    "CUTS.END_DATETIME,"+       // 08
    "CUTS.START_DAYPART,"+      // 09
    "CUTS.END_DAYPART,"+        // 10
    "CUTS.MON,"+                // 11
    "CUTS.TUE,"+                // 12
    "CUTS.WED,"+                // 13
    "CUTS.THU,"+                // 14
    "CUTS.FRI,"+                // 15
    "CUTS.SAT,"+                // 16
    "CUTS.SUN "+                // 17
    "from CUTS ";
  sql+=QString().sprintf("where CUTS.CART_NUMBER=%u ",cartnum);
  sql+="order by CUTS.CUT_NAME";
  q=new RDSqlQuery(sql);
  if (q->size()>1) {
    while(q->next()) {
      l=new RDListViewItem(p);
      l->setDragEnabled(false);
      l->setText(Cart,q->value(1).toString());
      l->setText(Length,RDGetTimeLength(q->value(5).toUInt()));
      l->setText(Talk,RDGetTimeLength(q->value(4).toUInt()-q->value(3).toUInt()));
      l->setText(Title,q->value(2).toString());
      if(!q->value(7).toDateTime().isNull()) {
        l->setText(Start,q->value(7).toDateTime().
		   toString("MM/dd/yyyy hh:mm:ss"));
      }
      if(!q->value(8).toDateTime().isNull()) {
        l->setText(End,q->value(8).toDateTime().
		   toString("MM/dd/yyyy - hh:mm:ss"));
      }
      else {
        l->setText(End,"TFN");
      }
      validity=ValidateCut(q,5,validity,current_datetime);
      end_datetime=q->value(8).toDateTime();
      UpdateItemColor(l,validity,end_datetime,current_datetime);
    }
  }
  delete q;
}

void MainWidget::RefreshList()
{
  RDSqlQuery *q;
  QString sql;
  RDListViewItem *l=NULL;
  QString type_filter;
  QDateTime current_datetime(QDate::currentDate(),QTime::currentTime());
  unsigned cartnum=0;
  RDCart::Validity validity=RDCart::NeverValid;
  QDateTime end_datetime;

  lib_cart_list->clear();

  lib_edit_button->setEnabled(false);
  lib_delete_button->setEnabled(false);

  type_filter=GetTypeFilter();
  if(type_filter.isEmpty()) {
    return;
  }
  sql=QString("select ")+
    "CART.NUMBER,"+             // 00
    "CART.FORCED_LENGTH,"+      // 01
    "CART.TITLE,"+              // 02
    "CART.ARTIST,"+             // 03
    "CART.ALBUM,"+              // 04
    "CART.LABEL,"+              // 05
    "CART.CLIENT,"+             // 06
    "CART.AGENCY,"+             // 07
    "CART.USER_DEFINED,"+       // 08
    "CART.COMPOSER,"+           // 09
    "CART.PUBLISHER,"+          // 10
    "CART.CONDUCTOR,"+          // 11
    "CART.GROUP_NAME,"+         // 12
    "CART.START_DATETIME,"+     // 13
    "CART.END_DATETIME,"+       // 14
    "CART.TYPE,"+               // 15
    "CART.CUT_QUANTITY,"+       // 16
    "CART.LAST_CUT_PLAYED,"+    // 17
    "CART.ENFORCE_LENGTH,"+     // 18
    "CART.PRESERVE_PITCH,"+     // 19
    "CART.LENGTH_DEVIATION,"+   // 20
    "CART.OWNER,"+              // 21
    "CART.VALIDITY,"+           // 22
    "GROUPS.COLOR,"+            // 23
    "CUTS.TALK_START_POINT,"+   // 24
    "CUTS.TALK_END_POINT,"+     // 25
    "CUTS.LENGTH,"+             // 26  offsets begin here
    "CUTS.EVERGREEN,"+          // 27
    "CUTS.START_DATETIME,"+     // 28
    "CUTS.END_DATETIME,"+       // 29
    "CUTS.START_DAYPART,"+      // 30
    "CUTS.END_DAYPART,"+        // 31
    "CUTS.MON,"+                // 32
    "CUTS.TUE,"+                // 33
    "CUTS.WED,"+                // 34
    "CUTS.THU,"+                // 35
    "CUTS.FRI,"+                // 36
    "CUTS.SAT,"+                // 37
    "CUTS.SUN "+                // 38
    "from CART left join GROUPS on CART.GROUP_NAME=GROUPS.NAME "+
    "left join CUTS on CART.NUMBER=CUTS.CART_NUMBER";
  sql+=WhereClause();
  sql+=" order by CART.NUMBER";
  if(lib_showmatches_box->isChecked()) {
    sql+=QString().sprintf(" limit %d",RD_LIMITED_CART_SEARCH_QUANTITY);
  }
  q=new RDSqlQuery(sql);
  int step=0;
  int count=0;
  lib_progress_dialog->setTotalSteps(q->size()/RDLIBRARY_STEP_SIZE);
  lib_progress_dialog->setProgress(0);
  while(q->next()) {
    end_datetime=q->value(14).toDateTime();
    if(q->value(0).toUInt()==cartnum) {
      if((RDCart::Type)q->value(15).toUInt()==RDCart::Macro) {
	validity=RDCart::AlwaysValid;
      }
      else {
	validity=ValidateCut(q,26,validity,current_datetime);
      }
    }
    else {
      //
      // Write availability color
      //
      UpdateItemColor(l,validity,q->value(14).toDateTime(),current_datetime);

      //
      // Start a new entry
      //
      if((RDCart::Type)q->value(15).toUInt()==RDCart::Macro) {
	validity=RDCart::AlwaysValid;
      }
      else {
	validity=ValidateCut(q,26,RDCart::NeverValid,current_datetime);
      }
      l=new RDListViewItem(lib_cart_list);
      l->setExpandable(false);
      switch((RDCart::Type)q->value(15).toUInt()) {
      case RDCart::Audio:
	if(q->value(21).isNull()) {
	  l->setPixmap(Icon,*lib_playout_map);
	}
	else {
	  l->setPixmap(Icon,*lib_track_cart_map);
	}
	break;
	
      case RDCart::Macro:
	l->setPixmap(Icon,*lib_macro_map);
	l->setBackgroundColor(backgroundColor());
	break;
	
      case RDCart::All:
	break;
      }
      l->setText(Cart,QString().sprintf("%06d",q->value(0).toUInt()));
      l->setText(Group,q->value(12).toString());
      l->setTextColor(Group,q->value(23).toString(),QFont::Bold);
      if(q->value(16).toUInt()==1) {
	      l->setText(Length,RDGetTimeLength(q->value(1).toUInt()));
              l->setText(Talk,RDGetTimeLength(q->value(25).toUInt()-q->value(24).toUInt()));
      }
      l->setText(Title,q->value(2).toString());
      l->setText(Artist,q->value(3).toString());
      if(!q->value(13).toDateTime().isNull()) {
	l->setText(Start,q->value(13).toDateTime().
		   toString("MM/dd/yyyy - hh:mm:ss"));
      }
      if(!q->value(14).toDateTime().isNull()) {
	l->setText(End,q->value(14).toDateTime().
		   toString("MM/dd/yyyy - hh:mm:ss"));
      }
      else {
	l->setText(End,"TFN");
      }
      l->setText(Album,q->value(4).toString());
      l->setText(Label,q->value(5).toString());
      l->setText(Composer,q->value(9).toString());
      l->setText(Conductor,q->value(11).toString());
      l->setText(Publisher,q->value(10).toString());
      l->setText(Client,q->value(6).toString());
      l->setText(Agency,q->value(7).toString());
      l->setText(UserDefined,q->value(8).toString());
      l->setText(Cuts,q->value(16).toString());
      l->setText(LastCutPlayed,q->value(17).toString());
      l->setText(EnforceLength,q->value(18).toString());
      l->setText(PreservePitch,q->value(19).toString());
      l->setText(LengthDeviation,q->value(20).toString());
      l->setText(OwnedBy,q->value(21).toString());
      if(q->value(18).toString()=="Y") {
	l->setTextColor(Length,QColor(RDLIBRARY_ENFORCE_LENGTH_COLOR),QFont::Bold);
      }
      else {
	if((q->value(20).toUInt()>RDLIBRARY_MID_LENGTH_LIMIT)&&
	   (q->value(18).toString()=="N")) {
	  if(q->value(20).toUInt()>RDLIBRARY_MAX_LENGTH_LIMIT) {
	    l->setTextColor(Length,QColor(RDLIBRARY_MAX_LENGTH_COLOR),QFont::Bold);
	  }
	  else {
	    l->setTextColor(Length,QColor(RDLIBRARY_MID_LENGTH_COLOR),QFont::Bold);
	  }
	}
	else {
	  l->setTextColor(Length,QColor(Qt::black),QFont::Normal);
	}
      }
      if(q->value(16).toUInt() > 1) {
        RefreshCuts(l,q->value(0).toUInt());
      }
    }
    cartnum=q->value(0).toUInt();
    if(count++>RDLIBRARY_STEP_SIZE) {
      lib_progress_dialog->setProgress(++step);
      count=0;
      qApp->processEvents(QEventLoop::ExcludeUserInputEvents);
    }
  }
  UpdateItemColor(l,validity,end_datetime,current_datetime);
  lib_progress_dialog->reset();
  delete q;
}


QString MainWidget::WhereClause() const
{
  QString sql="";
  QString type_filter=GetTypeFilter();

  QString schedcode="";
  if(lib_codes_box->currentText()!=tr("ALL")) {
    schedcode=lib_codes_box->currentText();
  }
  if(lib_group_box->currentText()==QString(tr("ALL"))) {
    sql+=RDAllCartSearchText(lib_filter_edit->text(),schedcode,
			  rda->user()->name(),true)+" && "+type_filter;

  }
  else {
    sql+=RDCartSearchText(lib_filter_edit->text(),lib_group_box->currentText(),
		       schedcode,true)+" && "+type_filter;      
  }

  return sql;
}


void SigHandler(int signo)
{
  pid_t pLocalPid;

  switch(signo) {
  case SIGCHLD:
    pLocalPid=waitpid(-1,NULL,WNOHANG);
    while(pLocalPid>0) {
      pLocalPid=waitpid(-1,NULL,WNOHANG);
    }
    ripper_running=false;
    import_active=false;
    signal(SIGCHLD,SigHandler);
    break;
  }
}


void MainWidget::RefreshLine(RDListViewItem *item)
{
  RDCart::Validity validity=RDCart::NeverValid;
  QDateTime current_datetime(QDate::currentDate(),QTime::currentTime());
  QString sql=QString("select ")+
    "CART.FORCED_LENGTH,"+     // 00
    "CART.TITLE,"+             // 01
    "CART.ARTIST,"+            // 02
    "CART.ALBUM,"+             // 03
    "CART.LABEL,"+             // 04
    "CART.CLIENT,"+            // 05
    "CART.AGENCY,"+            // 06
    "CART.USER_DEFINED,"+      // 07
    "CART.COMPOSER,"+          // 08
    "CART.CONDUCTOR,"+         // 09
    "CART.PUBLISHER,"+         // 10
    "CART.GROUP_NAME,"+        // 11
    "CART.START_DATETIME,"+    // 12
    "CART.END_DATETIME,"+      // 13
    "CART.TYPE,"+              // 14
    "CART.CUT_QUANTITY,"+      // 15
    "CART.LAST_CUT_PLAYED,"+   // 16
    "CART.ENFORCE_LENGTH,"+    // 17
    "CART.PRESERVE_PITCH,"+    // 18
    "CART.LENGTH_DEVIATION,"+  // 19
    "CART.OWNER,"+             // 20
    "CART.VALIDITY,"+          // 21
    "GROUPS.COLOR,"+           // 22
    "CUTS.TALK_START_POINT,"+  // 23
    "CUTS.TALK_END_POINT,"+    // 24
    "CUTS.LENGTH,"+            // 25  offsets begin here
    "CUTS.EVERGREEN,"+         // 26
    "CUTS.START_DATETIME,"+    // 27
    "CUTS.END_DATETIME,"+      // 28
    "CUTS.START_DAYPART,"+     // 29
    "CUTS.END_DAYPART,"+       // 30
    "CUTS.MON,"+               // 31
    "CUTS.TUE,"+               // 32
    "CUTS.WED,"+               // 33
    "CUTS.THU,"+               // 34
    "CUTS.FRI,"+               // 35
    "CUTS.SAT,"+               // 35
    "CUTS.SUN "+               // 37
    "from CART left join GROUPS "+
    "on CART.GROUP_NAME=GROUPS.NAME left join CUTS on "+
    "CART.NUMBER=CUTS.CART_NUMBER where "+
    QString().sprintf("CART.NUMBER=%u",item->text(Cart).toUInt());
  RDSqlQuery *q=new RDSqlQuery(sql);
  while(q->next()) {
    if((RDCart::Type)q->value(14).toUInt()==RDCart::Macro) {
      validity=RDCart::AlwaysValid;
    }
    else {
      validity=ValidateCut(q,25,validity,current_datetime);
    }
    switch((RDCart::Type)q->value(14).toUInt()) {
    case RDCart::Audio:
      if(q->value(20).isNull()) {
	item->setPixmap(Icon,*lib_playout_map);
      }
      else {
	item->setPixmap(Icon,*lib_track_cart_map);
      }
      if(q->value(Icon).toUInt()==0) {
	item->setBackgroundColor(RD_CART_ERROR_COLOR);
      }
      else {
	UpdateItemColor(item,validity,
			q->value(13).toDateTime(),current_datetime);
      }
      break;

    case RDCart::Macro:
      item->setPixmap(Icon,*lib_macro_map);
      break;

    case RDCart::All:
      break;
    }
    item->setText(Group,q->value(11).toString());
    item->setTextColor(Group,q->value(22).toString(),QFont::Bold);
    if(q->value(15).toUInt()==1) {
	    item->setText(Length,RDGetTimeLength(q->value(0).toUInt()));
            item->setText(Talk,RDGetTimeLength(q->value(24).toUInt()-q->value(23).toUInt()));
    }
    item->setText(Title,q->value(1).toString());
    item->setText(Artist,q->value(2).toString());
    item->setText(Album,q->value(3).toString());
    item->setText(Label,q->value(4).toString());
    item->setText(Composer,q->value(8).toString());
    item->setText(Conductor,q->value(9).toString());

    item->setText(Publisher,q->value(10).toString());
    item->setText(Client,q->value(5).toString());
    item->setText(Agency,q->value(6).toString());
    if(!q->value(12).toDateTime().isNull()) {
      item->setText(Start,q->value(12).toDateTime().
		    toString("MM/dd/yyyy - hh:mm:ss"));
    }
    else {
      item->setText(Start,"");
    }
    if(!q->value(13).toDateTime().isNull()) {
      item->setText(End,q->value(13).toDateTime().
		    toString("MM/dd/yyyy - hh:mm:ss"));
    }
    else {
      item->setText(End,tr("TFN"));
    }
    item->setText(Cuts,q->value(15).toString());
    item->setText(LastCutPlayed,q->value(16).toString());
    item->setText(EnforceLength,q->value(17).toString());
    item->setText(PreservePitch,q->value(18).toString());
    item->setText(LengthDeviation,q->value(19).toString());
    item->setText(OwnedBy,q->value(20).toString());
    if(q->value(17).toString()=="Y") {
      item->setTextColor(Length,QColor(RDLIBRARY_ENFORCE_LENGTH_COLOR),QFont::Bold);
    }
    else {
      if((q->value(19).toUInt()>RDLIBRARY_MID_LENGTH_LIMIT)&&
	 (q->value(17).toString()=="N")) {
	if(q->value(19).toUInt()>RDLIBRARY_MAX_LENGTH_LIMIT) {
	  item->setTextColor(Length,QColor(RDLIBRARY_MAX_LENGTH_COLOR),QFont::Bold);
	}
	else {
	  item->setTextColor(Length,QColor(RDLIBRARY_MID_LENGTH_COLOR),QFont::Bold);
	}
      }
      else {
	item->setTextColor(Length,QColor(Qt::black),QFont::Normal);
      }
    }

    RefreshCuts(item,item->text(Cart).toUInt());
  }
  delete q;
}


void MainWidget::UpdateItemColor(RDListViewItem *item,
				 RDCart::Validity validity,
				 const QDateTime &end_datetime,
				 const QDateTime &current_datetime)
{
  if(item!=NULL) {
    switch(validity) {
    case RDCart::NeverValid:
      item->setBackgroundColor(RD_CART_ERROR_COLOR);
      break;
	
    case RDCart::ConditionallyValid:
      if(end_datetime.isValid()&&
	 (end_datetime<current_datetime)) {
	item->setBackgroundColor(RD_CART_ERROR_COLOR);
      }
      else {
	item->setBackgroundColor(RD_CART_CONDITIONAL_COLOR);
      }
      break;
	
    case RDCart::FutureValid:
      item->setBackgroundColor(RD_CART_FUTURE_COLOR);
      break;
	
    case RDCart::AlwaysValid:
      item->setBackgroundColor(palette().color(QPalette::Active,
					       QColorGroup::Base));
      break;

    case RDCart::EvergreenValid:
      item->setBackgroundColor(RD_CART_EVERGREEN_COLOR);
      break;
    }
  }
}


void MainWidget::SetCaption(QString user)
{
  QString str1;
  QString str2;

  str1=QString("RDLibrary")+" v"+VERSION+" - "+tr("Host")+":";
  str2=tr("User")+":";
  setWindowTitle(str1+" "+rda->config()->stationName()+", "+str2+" "+user);
}


QString MainWidget::GetTypeFilter() const
{
  QString type_filter;

  if(lib_showaudio_box->isChecked()) {
    if(lib_showmacro_box->isChecked()) {
      type_filter="((TYPE=1)||(TYPE=2)||(TYPE=3))";
    }
    else {
      type_filter="((TYPE=1)||(TYPE=3))";
    }
  }
  else {
    if(lib_showmacro_box->isChecked()) {
      type_filter="(TYPE=2)";
    }
  }
  return type_filter;
}

QString MainWidget::GeometryFile() {
  bool home_found = false;
  QString home = RDGetHomeDir(&home_found);
  if (home_found) {
    return home + "/" + RDLIBRARY_GEOMETRY_FILE;
  } else {
    return NULL;
  }
}

void MainWidget::LoadGeometry()
{
  QString geometry_file=GeometryFile();
  if(geometry_file.isEmpty()) {
    return;
  }
  RDProfile *profile=new RDProfile();
  profile->setSource(geometry_file);
  resize(profile->intValue("RDLibrary","Width",sizeHint().width()),
	 profile->intValue("RDLibrary","Height",sizeHint().height()));
  lib_shownotes_box->
    setChecked(profile->boolValue("RDLibrary","ShowNoteBubbles",true));
  lib_allowdrag_box->
    setChecked(profile->boolValue("RDLibrary","AllowCartDragging",false));

  delete profile;
}


void MainWidget::SaveGeometry()
{
  QString geometry_file=GeometryFile();
  FILE *file=fopen(geometry_file,"w");
  if(file==NULL) {
    return;
  }
  fprintf(file,"[RDLibrary]\n");
  fprintf(file,"Width=%d\n",geometry().width());
  fprintf(file,"Height=%d\n",geometry().height());
  fprintf(file,"ShowNoteBubbles=");
  if(lib_shownotes_box->isChecked()) {
    fprintf(file,"Yes\n");
  }
  else {
    fprintf(file,"No\n");
  }
  fprintf(file,"AllowCartDragging=");
  if(lib_allowdrag_box->isChecked()) {
    fprintf(file,"Yes\n");
  }
  else {
    fprintf(file,"No\n");
  }

  fclose(file);
}


void MainWidget::LockUser()
{
  lib_edit_pending=true;
}


bool MainWidget::UnlockUser()
{

  RDListViewItem *item=NULL;

  //
  // Process Deleted Carts
  //
  for(unsigned i=0;i<lib_deleted_carts.size();i++) {
    if((item=(RDListViewItem *)lib_cart_list->findItem(QString().sprintf("%06u",lib_deleted_carts.at(i)),Cart))!=NULL) {
      delete item;
    }
  }

  //
  // Process User Change
  //
  bool ret=lib_user_changed;
  lib_edit_pending=false;
  if(lib_user_changed) {
    lib_user_timer->start(0,true);
    lib_user_changed=false;
  }

  return ret;
}


void MainWidget::SendNotification(RDNotification::Action action,
				  unsigned cartnum)
{
  RDNotification *notify=
    new RDNotification(RDNotification::CartType,action,QVariant(cartnum));
  rda->ripc()->sendNotification(*notify);
  delete notify;
}


int main(int argc,char *argv[])
{
  QApplication::setStyle(RD_GUI_STYLE);
  QApplication a(argc,argv);

  //
  // Load Translations
  //
  QTranslator qt(0);
  qt.load(QString("/usr/share/qt4/translations/qt_")+QTextCodec::locale(),
	  ".");
  a.installTranslator(&qt);

  QTranslator rd(0);
  rd.load(QString(PREFIX)+QString("/share/rivendell/librd_")+
	     QTextCodec::locale(),".");
  a.installTranslator(&rd);

  QTranslator rdhpi(0);
  rdhpi.load(QString(PREFIX)+QString("/share/rivendell/librdhpi_")+
	     QTextCodec::locale(),".");
  a.installTranslator(&rdhpi);

  QTranslator tr(0);
  tr.load(QString(PREFIX)+QString("/share/rivendell/rdlibrary_")+
	     QTextCodec::locale(),".");
  a.installTranslator(&tr);

  //
  // Start Event Loop
  //
  MainWidget *w=new MainWidget();
  a.setMainWidget(w);
  w->show();
  return a.exec();
}