stationlistmodel.cpp

Go to the documentation of this file.
00001 /*
00002     Copyright (C) 2008  Tim Fechtner < urwald at users dot sourceforge dot net >
00003 
00004     This program is free software; you can redistribute it and/or
00005     modify it under the terms of the GNU General Public License as
00006     published by the Free Software Foundation; either version 2 of
00007     the License or (at your option) version 3 or any later version
00008     accepted by the membership of KDE e.V. (or its successor approved
00009     by the membership of KDE e.V.), which shall act as a proxy
00010     defined in Section 14 of version 3 of the license.
00011 
00012     This program is distributed in the hope that it will be useful,
00013     but WITHOUT ANY WARRANTY; without even the implied warranty of
00014     MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
00015     GNU General Public License for more details.
00016 
00017     You should have received a copy of the GNU General Public License
00018     along with this program.  If not, see <http://www.gnu.org/licenses/>.
00019 */
00020 
00021 #include "stationlistmodel.h"
00022 #include "stationlistwidget.h"
00023 #include "settings_general.h"
00024 #include <QAbstractItemModel>
00025 #include <QApplication>
00026 #include <QClipboard>
00027 #include <QFile>
00028 #include <QItemSelection>
00029 #include <QMimeData>
00030 #include <KProgressDialog>
00031 #include <KLocale>
00032 #define AND  &&
00033 #define OR  ||
00034 #define NOT  !
00035 #define EQUAL  ==
00036 
00037 stationlistModel::stationlistModel(stationlistWidget *parent,
00038                                    QWidget *mainWidget) : QAbstractTableModel(parent)
00039 {
00040   // variables
00041   QStringList m_stringList;
00042   int i;  // using int because this is the return type of QStringList::size()
00043 
00044   // general setup
00045   m_mainWidget = mainWidget;
00046   view = parent;
00047 
00048   // setup data model
00049   m_stringList = settings_general::streamConfigFiles();
00050   for (i = 0; i < m_stringList.size(); ++i) {
00051     m_stationlist.append(new radioStation(this, m_mainWidget, m_stringList.at(i), i));
00052     helper_connectSignalsAndSlots(m_stationlist.last());
00053   };
00054   helper_writeStationListToGeneralSettings();  /* This is because when radioStation can't
00055   open the file, it creates a new one. Probaby this won't happen, but we have to make sure
00056   that the list in the general config file is actual. */
00057 
00058   // initialize attributes
00059   recalculate_numberOfActiveStreams_and_bandwidth(); // now possible (after making stationlist)
00060   connect(this, SIGNAL(rowsInserted(const QModelIndex &, int, int)),
00061           this, SLOT(recalculate_numberOfActiveStreams_and_bandwidth()));
00062   connect(this, SIGNAL(rowsRemoved(const QModelIndex &, int, int)),
00063           this, SLOT(recalculate_numberOfActiveStreams_and_bandwidth()));
00064   m_listOfStreamsOfWhichTheUserWantsThatTheyRip = 0;
00065 }
00066 
00067 stationlistModel::~stationlistModel()
00068 {
00069   delete m_listOfStreamsOfWhichTheUserWantsThatTheyRip;
00070 }
00071 
00072 void stationlistModel::helper_connectSignalsAndSlots(radioStation *m_stream)
00073 {
00074     connect(m_stream, SIGNAL(streamNameChanged(qlonglong, PropertyValue)),
00075             this, SLOT(reloadStreamName(qlonglong)));
00076     connect(m_stream, SIGNAL(uriChanged(qlonglong, PropertyValue)),
00077             this, SLOT(reloadUri(qlonglong)));
00078     connect(m_stream, SIGNAL(statusChanged(qlonglong, PropertyValue)),
00079             this, SLOT(reloadStatus(qlonglong)));
00080     connect(m_stream, SIGNAL(errorChanged(qlonglong, PropertyValue)),
00081             this, SLOT(reloadStatus(qlonglong)));
00082     connect(m_stream, SIGNAL(songChanged(qlonglong, PropertyValue)),
00083             this, SLOT(reloadSong(qlonglong)));
00084     connect(m_stream, SIGNAL(dataSizeChanged(qlonglong, PropertyValue)),
00085             this, SLOT(reloadDataSize(qlonglong)));
00086     connect(m_stream, SIGNAL(bitrateChanged(qlonglong, PropertyValue)),
00087             this, SLOT(reloadBitrate(qlonglong)));
00088     connect(m_stream, SIGNAL(bitrateChanged(qlonglong, PropertyValue)),
00089             this, SLOT(recalculate_numberOfActiveStreams_and_bandwidth()));
00090     connect(m_stream, SIGNAL(metaIntervalChanged(qlonglong, PropertyValue)),
00091             this, SLOT(reloadMetaInterval(qlonglong)));
00092     connect(m_stream, SIGNAL(metaInterval_milliSecondsChanged(qlonglong, PropertyValue)),
00093             this, SLOT(reloadMetaInterval_milliSeconds(qlonglong)));
00094     connect(m_stream, SIGNAL(serverNameChanged(qlonglong, PropertyValue)),
00095             this, SLOT(reloadServerName(qlonglong)));
00096     connect(m_stream, SIGNAL(relayPortChanged(qlonglong, PropertyValue)),
00097             this, SLOT(reloadRelayPort(qlonglong)));
00098     connect(m_stream, SIGNAL(running()),
00099             this, SLOT(recalculate_numberOfActiveStreams_and_bandwidth()));
00100     connect(m_stream, SIGNAL(not_running()),
00101             this, SLOT(recalculate_numberOfActiveStreams_and_bandwidth()));
00102 }
00103 
00104 int stationlistModel::rowCount (const QModelIndex & parent) const
00105 {
00106   if (parent.isValid()) {  // no tree structure in our table-based model! Return a neutral value...
00107     return 0;
00108   } else {
00109     return m_stationlist.size();
00110   };
00111 }
00112 
00113 int stationlistModel::columnCount (const QModelIndex & parent) const
00114 {
00115   if (parent.isValid()) {  // no tree structure in our table-based model! Return a neutral value...
00116     return 0;
00117   } else {
00118     return 10;
00119   };
00120 }
00121 
00122 QVariant stationlistModel::columnInfo(const columnInfoType type,
00123                                       const int column,
00124                                       const qint64 row,
00125                                       const quint64 value,
00126                                       QList<radioStation *> listOfStations) const
00127 {
00128   QVariant temp;
00129 
00130   if (listOfStations.isEmpty()) {
00131     listOfStations = m_stationlist;
00132   };
00133 
00134   switch (type) {  // make sure that row is valid to prevent a memory access failure
00135     case columnHeaderTitle:
00136     case columnHeaderToolTip:
00137     case columnHeaderWhatsThis:
00138     case columnWidth:
00139     case setColumnWidth:
00140     case columnVisibility:
00141     case setColumnVisibility:
00142       break;  // These functions are known to not make use of the row.
00143     default:  // Else we break as a precaution we break up here.
00144       if ((row >= listOfStations.size()) OR (row < 0)) {
00145         return QVariant();
00146       };
00147   }
00148   /* IF data is requested that is NOT depending on the "row"
00149   *  THEN no check is needed.
00150   *  ELSE (=when data is depending on the "row") we check if it is a valid row.
00151   *  If not, we return a QVariant(). This is important, because the rest of this
00152   *  function body assumes that "row" is valid and uses it for QList::at(row) - and
00153   *  when "row" is invalid, this function leeds to a crash.
00154   *
00155   *  I use the "switch" systax because this looks clearer than an "if" construct.
00156   *  And I test for the cases where the row is NOT important - this way (when one
00157   *  day the enum gets more items that depend on the row - and we forget to change
00158   *  it here) at least we don't get a crash.
00159   *
00160   *  We don't test if "column" is valid, because the rest of the function is a
00161   *  switch-case, and when there nothing matches, an invalid QVariant is returned. */
00162 
00163   switch (column) {
00164 
00165     case 0:
00166     {
00167       switch (type) {
00168         case columnHeaderTitle:
00169           return i18nc("@title:column", "stream name");
00170         case columnHeaderToolTip:
00171         case columnHeaderWhatsThis:
00172           return QVariant();
00173         case columnWidth:
00174           return settings_general::columnWidth_streamName();
00175         case setColumnWidth:
00176           settings_general::setColumnWidth_streamName(value);
00177           settings_general::self()->writeConfig();
00178           return QVariant();
00179         case columnVisibility:
00180           return settings_general::columnVisibility_streamName();
00181         case setColumnVisibility:
00182           settings_general::setColumnVisibility_streamName(value);
00183           settings_general::self()->writeConfig();
00184           return QVariant();
00185         case columnData:
00186           temp.setValue<PropertyValue>(listOfStations.at(row)->streamName());
00187           return temp;
00188       };
00189       break;
00190     }
00191 
00192     case 1:
00193     {
00194       switch (type) {
00195         case columnHeaderTitle:
00196           return i18nc("@title:column", "URL");
00197         case columnHeaderToolTip:
00198         case columnHeaderWhatsThis:
00199           return QVariant();
00200         case columnWidth:
00201           return settings_general::columnWidth_uri();
00202         case setColumnWidth:
00203           settings_general::setColumnWidth_uri(value);
00204           settings_general::self()->writeConfig();
00205           return QVariant();
00206         case columnVisibility:
00207           return settings_general::columnVisibility_uri();
00208         case setColumnVisibility:
00209           settings_general::setColumnVisibility_uri(value);
00210           settings_general::self()->writeConfig();
00211           return QVariant();
00212         case columnData:
00213           temp.setValue<PropertyValue>(listOfStations.at(row)->uri());
00214           return temp;
00215       };
00216       break;
00217     }
00218 
00219     case 2:
00220     {
00221       switch (type) {
00222         case columnHeaderTitle:
00223           return i18nc("@title:column", "status");
00224         case columnHeaderToolTip:
00225         case columnHeaderWhatsThis:
00226           return QVariant();
00227         case columnWidth:
00228           return settings_general::columnWidth_statusAndError();
00229         case setColumnWidth:
00230           settings_general::setColumnWidth_statusAndError(value);
00231           settings_general::self()->writeConfig();
00232           return QVariant();
00233         case columnVisibility:
00234           return settings_general::columnVisibility_statusAndError();
00235         case setColumnVisibility:
00236           settings_general::setColumnVisibility_statusAndError(value);
00237           settings_general::self()->writeConfig();
00238           return QVariant();
00239         case columnData:
00240           PropertyValue my_statusAndError;
00241           if (listOfStations.at(row)->error().formatedValue.isEmpty()) {
00242             my_statusAndError.formatedValue = listOfStations.at(row)->status().formatedValue;
00243             my_statusAndError.toolTip = listOfStations.at(row)->status().toolTip;
00244             my_statusAndError.whatsThis = listOfStations.at(row)->status().whatsThis;
00245           } else {
00246             my_statusAndError.toolTip = listOfStations.at(row)->error().toolTip;
00247             my_statusAndError.whatsThis = listOfStations.at(row)->error().whatsThis;
00248             if (listOfStations.at(row)->status().formatedValue.isEmpty()) {
00249               my_statusAndError.formatedValue = listOfStations.at(row)->error().formatedValue;
00250             } else {
00251               my_statusAndError.formatedValue = QString(i18nc(
00252                 "@item:intable This produces the status message from the actual status (%1) and "
00253                   "the error message (%3). Both of them are guaranteed to be not empty. %2 is "
00254                   "replaced by an en-dash (Unicode U+2013). Use it or replace it by something "
00255                   "what looks nicer in your language.",
00256                 "%1 %2 %3",
00257                 listOfStations.at(row)->status().formatedValue,
00258                 QString(QChar(0x2013)),
00259                 listOfStations.at(row)->error().formatedValue));
00260             };
00261           };
00262           my_statusAndError.internalValue = listOfStations.at(row)->status().internalValue;
00263           if ((!(listOfStations.at(row)->error().type == PropertyValue::unset)) ||
00264               (listOfStations.at(row)->status().type == PropertyValue::error)) {
00265             my_statusAndError.type = PropertyValue::error;
00266           } else if (my_statusAndError.formatedValue.isEmpty()) {
00267             my_statusAndError.type = PropertyValue::unset;
00268           } else {
00269             my_statusAndError.type = PropertyValue::value;
00270           };
00271           temp.setValue<PropertyValue>(my_statusAndError);
00272           return temp;
00273       };
00274       break;
00275     }
00276 
00277     case 3:
00278     {
00279       switch (type) {
00280         case columnHeaderTitle:
00281           return  i18nc("@title:column header of the column with the name of the actual track",
00282                         "track");
00283         case columnHeaderToolTip:
00284         case columnHeaderWhatsThis:
00285           return  QVariant();
00286         case columnWidth:
00287           return settings_general::columnWidth_song();
00288         case setColumnWidth:
00289           settings_general::setColumnWidth_song(value);
00290           settings_general::self()->writeConfig();
00291           return QVariant();
00292         case columnVisibility:
00293           return settings_general::columnVisibility_song();
00294         case setColumnVisibility:
00295           settings_general::setColumnVisibility_song(value);
00296           settings_general::self()->writeConfig();
00297           return QVariant();
00298         case columnData:
00299           temp.setValue<PropertyValue>(listOfStations.at(row)->song());
00300           return temp;
00301       };
00302       break;
00303     }
00304 
00305     case 4:
00306     {
00307       switch (type) {
00308         case columnHeaderTitle:
00309           return  i18nc("@title:column", "track size");
00310         case columnHeaderToolTip:
00311         case columnHeaderWhatsThis:
00312           return  QVariant();
00313         case columnWidth:
00314           return settings_general::columnWidth_dataSize();
00315         case setColumnWidth:
00316           settings_general::setColumnWidth_dataSize(value);
00317           settings_general::self()->writeConfig();
00318           return QVariant();
00319         case columnVisibility:
00320           return settings_general::columnVisibility_dataSize();
00321         case setColumnVisibility:
00322           settings_general::setColumnVisibility_dataSize(value);
00323           settings_general::self()->writeConfig();
00324           return QVariant();
00325         case columnData:
00326           temp.setValue<PropertyValue>(listOfStations.at(row)->dataSize());
00327           return temp;
00328       };
00329       break;
00330     }
00331 
00332     case 5:
00333     {
00334       switch (type) {
00335         case columnHeaderTitle:
00336           return  i18nc("@title:column", "bit rate");
00337         case columnHeaderToolTip:
00338         case columnHeaderWhatsThis:
00339           return  QVariant();
00340         case columnWidth:
00341           return settings_general::columnWidth_bitrate();
00342         case setColumnWidth:
00343           settings_general::setColumnWidth_bitrate(value);
00344           settings_general::self()->writeConfig();
00345           return QVariant();
00346         case columnVisibility:
00347           return settings_general::columnVisibility_bitrate();
00348         case setColumnVisibility:
00349           settings_general::setColumnVisibility_bitrate(value);
00350           settings_general::self()->writeConfig();
00351           return QVariant();
00352         case columnData:
00353           temp.setValue<PropertyValue>(listOfStations.at(row)->bitrate());
00354           return temp;
00355       };
00356       break;
00357     }
00358 
00359     case 6:
00360     {
00361       switch (type) {
00362         case columnHeaderTitle:
00363           return  i18nc("@title:column The unit is KiB instead of kB. See "
00364                           "http://en.wikipedia.org/wiki/Binary_prefix for details.",
00365                         "meta data interval (KiB)");
00366         case columnHeaderToolTip:
00367           return  i18nc("@info:tooltip", "interval of meta data in bytes");
00368         case columnHeaderWhatsThis:
00369           return  QVariant();
00370         case columnWidth:
00371           return settings_general::columnWidth_metaInterval();
00372         case setColumnWidth:
00373           settings_general::setColumnWidth_metaInterval(value);
00374           settings_general::self()->writeConfig();
00375           return QVariant();
00376         case columnVisibility:
00377           return settings_general::columnVisibility_metaInterval();
00378         case setColumnVisibility:
00379           settings_general::setColumnVisibility_metaInterval(value);
00380           settings_general::self()->writeConfig();
00381           return QVariant();
00382         case columnData:
00383           temp.setValue<PropertyValue>(listOfStations.at(row)->metaInterval());
00384           return temp;
00385       };
00386       break;
00387     }
00388 
00389     case 7:
00390     {
00391       switch (type) {
00392         case columnHeaderTitle:
00393           return  i18nc("@title:column unit is millisecond", "meta data interval (ms)");
00394         case columnHeaderToolTip:
00395           return  i18nc("@info:tooltip", "interval of meta data in milliseconds");
00396         case columnHeaderWhatsThis:
00397           return  QVariant();
00398         case columnWidth:
00399           return settings_general::columnWidth_metaInterval_milliSeconds();
00400         case setColumnWidth:
00401           settings_general::setColumnWidth_metaInterval_milliSeconds(value);
00402           settings_general::self()->writeConfig();
00403           return QVariant();
00404         case columnVisibility:
00405           return settings_general::columnVisibility_metaInterval_milliSeconds();
00406         case setColumnVisibility:
00407           settings_general::setColumnVisibility_metaInterval_milliSeconds(value);
00408           settings_general::self()->writeConfig();
00409           return QVariant();
00410         case columnData:
00411           temp.setValue<PropertyValue>(listOfStations.at(row)->metaInterval_milliSeconds());
00412           return temp;
00413       };
00414       break;
00415     }
00416 
00417     case 8:
00418     {
00419       switch (type) {
00420         case columnHeaderTitle:
00421           return  i18nc("@title:column", "server name");
00422         case columnHeaderToolTip:
00423         case columnHeaderWhatsThis:
00424           return  QVariant();
00425         case columnWidth:
00426           return settings_general::columnWidth_serverName();
00427         case setColumnWidth:
00428           settings_general::setColumnWidth_serverName(value);
00429           settings_general::self()->writeConfig();
00430           return QVariant();
00431         case columnVisibility:
00432           return settings_general::columnVisibility_serverName();
00433         case setColumnVisibility:
00434           settings_general::setColumnVisibility_serverName(value);
00435           settings_general::self()->writeConfig();
00436           return QVariant();
00437         case columnData:
00438           temp.setValue<PropertyValue>(listOfStations.at(row)->serverName());
00439           return temp;
00440       };
00441       break;
00442     }
00443 
00444     case 9:
00445     {
00446       switch (type) {
00447         case columnHeaderTitle:
00448           return  i18nc("@title:column 'relay port' means 'the port that the relay server uses'",
00449                         "relay port");
00450         case columnHeaderToolTip:
00451           return  i18nc("@info:tooltip", "port of the relay server");
00452         case columnHeaderWhatsThis:
00453           return  QVariant();
00454         case columnWidth:
00455           return settings_general::columnWidth_relayPort();
00456         case setColumnWidth:
00457           settings_general::setColumnWidth_relayPort(value);
00458           settings_general::self()->writeConfig();
00459           return QVariant();
00460         case columnVisibility:
00461           return settings_general::columnVisibility_relayPort();
00462         case setColumnVisibility:
00463           settings_general::setColumnVisibility_relayPort(value);
00464           settings_general::self()->writeConfig();
00465           return QVariant();
00466         case columnData:
00467           temp.setValue<PropertyValue>(listOfStations.at(row)->relayPort());
00468           return temp;
00469       };
00470       break;
00471     }
00472 
00473   /* No "default:"! This way we get a compiler warning when one day
00474   *  the enum type has more items and we forget to implement this here. */
00475 
00476   };
00477   return QVariant();  // Return an invalid QVariant if the given "column" was invalid.
00478 }
00479 
00480 void stationlistModel::recalculate_numberOfActiveStreams_and_bandwidth()
00481 {
00482   // variables
00483   int i;
00484   int temp_numberOfActiveStreams = 0;
00485   quint64 temp_bandwidth = 0;
00486 
00487   // code
00488 
00489   // calculate new values
00490   for (i=0; i < m_stationlist.size(); ++i) {
00491     if (m_stationlist.at(i)->status().internalValue.value<ripping::statusType>() != ripping::idle) {
00492       temp_numberOfActiveStreams++;
00493       if (m_stationlist.at(i)->bitrate().type == PropertyValue::value) {
00494         temp_bandwidth = temp_bandwidth +
00495                              m_stationlist.at(i)->bitrate().internalValue.toULongLong();
00496       };
00497     };
00498   };
00499 
00500   // actualize properties if necessary
00501   if (internal_numberOfActiveStreams != temp_numberOfActiveStreams) {
00502     internal_numberOfActiveStreams = temp_numberOfActiveStreams;
00503     emit numberOfActiveStreamsChanged();
00504     if (internal_numberOfActiveStreams == 0) {
00505       emit numberOfActiveStreamsIsZero();
00506     };
00507   };
00508   if (internal_bandwidth != temp_bandwidth) {
00509     internal_bandwidth = temp_bandwidth;
00510     emit bandwidthChanged();
00511   };
00512 }
00513 
00514 void stationlistModel::reloadStreamName(const qlonglong stationIndex)
00515 {
00516   emit dataChanged(index(stationIndex, 0), index(stationIndex, 0));
00517 }
00518 
00519 void stationlistModel::reloadUri(const qlonglong stationIndex)
00520 {
00521   emit dataChanged(index(stationIndex, 1), index(stationIndex, 0));
00522 }
00523 
00524 void stationlistModel::reloadStatus(const qlonglong stationIndex)
00525 {
00526   emit dataChanged(index(stationIndex, 2), index(stationIndex, 2));
00527 }
00528 
00529 void stationlistModel::reloadSong(const qlonglong stationIndex)
00530 {
00531   emit dataChanged(index(stationIndex, 3), index(stationIndex, 3));
00532 }
00533 
00534 void stationlistModel::reloadDataSize(const qlonglong stationIndex)
00535 {
00536   emit dataChanged(index(stationIndex, 4), index(stationIndex, 4));
00537 }
00538 
00539 void stationlistModel::reloadBitrate(const qlonglong stationIndex)
00540 {
00541   emit dataChanged(index(stationIndex, 5), index(stationIndex, 5));
00542 }
00543 
00544 void stationlistModel::reloadMetaInterval(const qlonglong stationIndex)
00545 {
00546   emit dataChanged(index(stationIndex, 6), index(stationIndex, 6));
00547 }
00548 
00549 void stationlistModel::reloadMetaInterval_milliSeconds(const qlonglong stationIndex)
00550 {
00551   emit dataChanged(index(stationIndex, 7), index(stationIndex, 7));
00552 }
00553 
00554 void stationlistModel::reloadServerName(const qlonglong stationIndex)
00555 {
00556   emit dataChanged(index(stationIndex, 8), index(stationIndex, 8));
00557 }
00558 
00559 void stationlistModel::reloadRelayPort(const qlonglong stationIndex)
00560 {
00561   emit dataChanged(index(stationIndex, 9), index(stationIndex, 9));
00562 }
00563 
00564 QVariant stationlistModel::headerData(int section, Qt::Orientation orientation, int role) const
00565 {
00566      if (orientation EQUAL Qt::Vertical) {
00567        if (role == Qt::DisplayRole) {
00568          return (int(section+1));
00569        } else {
00570          return QVariant();
00571        };
00572      } else {
00573        switch (role) {
00574          case Qt::DisplayRole:
00575            return columnInfo(columnHeaderTitle, section);
00576          case Qt::ToolTipRole:
00577            return columnInfo(columnHeaderToolTip, section);
00578          case Qt::WhatsThisRole:
00579            return columnInfo(columnHeaderWhatsThis, section);
00580          default:
00581            return QVariant();  // other "display roles" aren't supported.
00582        };
00583      };
00584      return QVariant();  // This point should never be reached. It just exists
00585                          // to be sure to deliver a return value, also when
00586                          // I introduce new bugs in the code above.
00587 }
00588 
00589 QVariant stationlistModel::data(const QModelIndex &index, int role) const
00590 {
00591   //variables
00592   QVariant returnValue;
00593 
00594   //code
00595   if ((index.model() EQUAL this) AND (!index.parent().isValid()) AND (index.isValid())) {
00596     switch (role) {
00597       case Qt::DisplayRole:
00598         returnValue =
00599           columnInfo(columnData, index.column(), index.row()).value<PropertyValue>().formatedValue;
00600         break;
00601       case Qt::ToolTipRole:
00602         returnValue =
00603           columnInfo(columnData, index.column(), index.row()).value<PropertyValue>().toolTip;
00604         break;
00605       case Qt::WhatsThisRole:
00606         returnValue =
00607           columnInfo(columnData, index.column(), index.row()).value<PropertyValue>().whatsThis;
00608         break;
00609       case Qt::TextAlignmentRole:
00610         returnValue = static_cast<int>(columnInfo(columnData, index.column(), index.row())
00611                                          .value<PropertyValue>().formatedValueAlignment);
00612         break;
00613       default: // other "display roles" aren't supported.
00614         returnValue = QVariant();
00615         break;
00616     };
00617   }
00618 
00619   //return
00620   return returnValue;
00621 }
00622 
00623 void stationlistModel::sort(int column, Qt::SortOrder order)
00624 {
00625   if (m_stationlist.isEmpty() || column < 0 || column >= columnCount()) {  // nothing to do
00626     return;
00627   };
00628 
00629   // variables
00630   int i;
00631   int j;
00632   bool abort;
00633   PropertyValue temp;
00634   QList<radioStation *> sorted_list;
00635   QModelIndexList temp_indexList;
00636   QStringList selection;
00637 
00638   // code
00639 
00640   // save the selection
00641   temp_indexList = view->selectionModel()->selectedRows();
00642   foreach (const QModelIndex & tempModelIndex, temp_indexList) {
00643     selection.append(m_stationlist.at(tempModelIndex.row())->configFileName());
00644   };
00645 
00646   // generate a sorted list
00647   for (i = 0; i < m_stationlist.size(); ++i) {
00648     temp = columnInfo(columnData, column, i).value<PropertyValue>();
00649     j = 0;
00650     abort = (j >= sorted_list.size());
00651     while (!abort) {
00652       if (helper_firstIsAfterSecond(
00653             temp,
00654             columnInfo(columnData, column, j, -1, sorted_list).value<PropertyValue>(),
00655             order)) {
00656         ++j;
00657         abort = (j >= sorted_list.size());
00658       } else {
00659         abort = true;
00660       };
00661     };
00662     sorted_list.insert(j, m_stationlist.at(i));
00663   };
00664 
00665   // use the sorted list instead of the old one
00666   m_stationlist = sorted_list;
00667 
00668   // adapt index values and restore selection
00669   view->selectionModel()->clear();
00670   for (i = 0; i < m_stationlist.size(); ++i) {
00671     m_stationlist.at(i)->setIndex(i);
00672     if (selection.contains(m_stationlist.at(i)->configFileName())) {
00673       view->selectionModel()->select(
00674         index(i, 0),
00675         QItemSelectionModel::Select | QItemSelectionModel::Rows);
00676     };
00677   };
00678 
00679   // save the new sort order
00680   settings_general::setSortingAscendingly(order == Qt::AscendingOrder);
00681   settings_general::setSortByColumn(column);
00682   settings_general::self()->writeConfig();
00683 
00684   // emit the necessary signals
00685   emit dataChanged(index(0, 0),
00686                    index(m_stationlist.size() - 1, columnCount() - 1));
00687 
00688 }
00689 
00690 bool stationlistModel::helper_isNumericValue(const QVariant & value)
00691 {
00692   // variables
00693   QMetaType::Type type;
00694 
00695   // code
00696   type = static_cast<QMetaType::Type>(value.type());
00697   return (type == QMetaType::Int ||
00698           type == QMetaType::UInt ||
00699           type == QMetaType::Double ||
00700           type == QMetaType::Long ||
00701           type == QMetaType::LongLong ||
00702           type == QMetaType::Short ||
00703           type == QMetaType::Char ||
00704           type == QMetaType::ULong ||
00705           type == QMetaType::ULongLong ||
00706           type == QMetaType::UShort ||
00707           type == QMetaType::UChar ||
00708           type == QMetaType::Float ||
00709           // I don't understand why QString() is necessary, but without it doesn't work.
00710           value.typeName() == QString("ripping::statusType"));
00711 }
00712 
00713 qlonglong stationlistModel::helper_toLongLong(const PropertyValue & value)
00714 {
00715   // I don't understand why QString() is necessary, but without it doesn't work
00716   if (value.internalValue.typeName() == QString("ripping::statusType")) {
00717     return value.internalValue.value<ripping::statusType>();
00718   } else {
00719     return value.internalValue.toLongLong();
00720   };
00721 }
00722 
00723 bool stationlistModel::helper_firstIsAfterSecond(const PropertyValue & firstValue,
00724                                                  const PropertyValue & secondValue,
00725                                                  Qt::SortOrder order)
00726 {
00727   // code
00728   if (order == Qt::AscendingOrder) {
00729       switch (firstValue.type) {
00730           case PropertyValue::unset:
00731               return (secondValue.type == PropertyValue::unset);
00732           case PropertyValue::value:
00733               if (secondValue.type == PropertyValue::unset) {
00734                   return true;
00735               } else if (secondValue.type == PropertyValue::error) {
00736                   return false;
00737               } else {
00738                   if (helper_isNumericValue(firstValue.internalValue) &&
00739                       helper_isNumericValue(secondValue.internalValue)) {
00740                       return helper_toLongLong(firstValue) >= helper_toLongLong(secondValue);
00741                   } else {
00742                       return QString::localeAwareCompare(firstValue.formatedValue,
00743                                                          secondValue.formatedValue) >= 0;
00744                   };
00745               };
00746           case PropertyValue::error:
00747               if (secondValue.type == PropertyValue::error) {
00748                   return QString::localeAwareCompare(firstValue.formatedValue,
00749                                                      secondValue.formatedValue) >= 0;
00750               } else {
00751                   return true;
00752               };
00753       };
00754   } else {
00755       switch (firstValue.type) {
00756           case PropertyValue::unset:
00757               return true;
00758           case PropertyValue::value:
00759               if (secondValue.type == PropertyValue::unset) {
00760                   return false;
00761               } else if (secondValue.type == PropertyValue::error) {
00762                   return true;
00763               } else {
00764                   if (helper_isNumericValue(firstValue.internalValue) &&
00765                       helper_isNumericValue(secondValue.internalValue)) {
00766                       return helper_toLongLong(firstValue) <= helper_toLongLong(secondValue);
00767                   } else {
00768                       return QString::localeAwareCompare(firstValue.formatedValue,
00769                                                          secondValue.formatedValue) <= 0;
00770                   };
00771               };
00772           case PropertyValue::error:
00773               if (secondValue.type == PropertyValue::error) {
00774                   return QString::localeAwareCompare(firstValue.formatedValue,
00775                                                      secondValue.formatedValue) <= 0;
00776               } else {
00777                   return false;
00778               };
00779       };
00780   };
00781   kDebug() << "Function hasn't calculated a return value. TRUE is returned to prevent a crash.";
00782   return true;
00783 }
00784 
00785 QModelIndex stationlistModel::addNewStation()
00786 {
00787   // consts
00788   const int size_of_list = m_stationlist.size();
00789 
00790   // variables
00791   QPointer<radioStation> m_newStation;
00792   int exitCode;
00793   QString m_fileName;
00794   QModelIndex returnValue;
00795 
00796   // code
00797   m_newStation = new radioStation(this, m_mainWidget, QString(), size_of_list);
00798   exitCode = m_newStation->execSettingsDialog();
00799   if (exitCode == QDialog::Accepted) {  // insert the new station in our model
00800     beginInsertRows(QModelIndex(), size_of_list, size_of_list);
00801     m_stationlist.append(m_newStation);
00802     helper_connectSignalsAndSlots(m_stationlist.last());
00803     helper_writeStationListToGeneralSettings();
00804     endInsertRows();
00805     returnValue = index(size_of_list, 0);
00806   } else {  // delete the station object and the config file
00807     m_fileName = m_newStation->configFileName();
00808     delete m_newStation;
00809     // We must wait with constructing QFile until the station
00810     // object has been deleted (we shouldn't open a file that's yet/still open!).
00811     QFile theFile(m_fileName);
00812     theFile.remove();
00813   };
00814   return returnValue;
00815 }
00816 
00817 bool stationlistModel::removeRows(int row, int count, const QModelIndex & parent)
00818 {
00819   // variables
00820   int i;
00821   QString filename;
00822   QFile the_file;
00823 
00824   // code
00825   if (count == 0) {
00826     return true;
00827   };
00828   // test if the index is valid (if not, QList.at() would crash!):
00829   if ((row >= 0) &&
00830       (count > 0) &&
00831       ((row + count - 1) < m_stationlist.size()) &&
00832       (!parent.isValid())) {
00833     beginRemoveRows(QModelIndex(), row, row + count - 1);
00834     for (i = row; i <= row+count-1; ++i) {
00835       // save the filename of the config file, we'll need it later to delete the file:
00836       filename = m_stationlist.at(i)->configFileName();
00837       delete m_stationlist.at(i);  // delete the radioStation object
00838       // remove the pointer to the (now deleted) radioStation object from our internal list:
00839       m_stationlist.removeAt(i);
00840       // delete the config file of the stream
00841       the_file.setFileName(filename);
00842       the_file.remove();
00843     };
00844     // synchronize the general config file with our internal list:
00845     helper_writeStationListToGeneralSettings();
00846     // correct the index for all following rows (which is now changed):
00847     for (i = row; i < m_stationlist.size(); ++i) {
00848       m_stationlist.at(i)->setIndex(i);
00849     };
00850     endRemoveRows();
00851     return true;
00852   } else {
00853     return false;
00854   };
00855 }
00856 
00857 inline void stationlistModel::helper_writeStationListToGeneralSettings()
00858 {
00859   // variables
00860   int i;
00861   QStringList helper_stringlist;
00862 
00863   // code
00864   for (i = 0; i < m_stationlist.size(); ++i) {
00865     helper_stringlist.append(m_stationlist.at(i)->configFileName());
00866   };
00867   settings_general::setStreamConfigFiles(helper_stringlist);
00868   settings_general::self()->writeConfig();
00869 }
00870 
00871 void stationlistModel::record(const int index)
00872 {
00873   // test if the index is valid (when it is not valid, we may not use QList.at()!):
00874   if ((index >= 0) AND (index < m_stationlist.size())) {
00875     m_stationlist.at(index)->startStreamripper();
00876   };
00877 }
00878 
00879 void stationlistModel::stopRecording(const int index)
00880 {
00881   // test if the index is valid (when it is not valid, we may not use QList.at()!):
00882   if ((index >= 0) AND (index < m_stationlist.size())) {
00883     m_stationlist.at(index)->shutDown();
00884   };
00885 }
00886 
00887 void stationlistModel::showConfigDialog(const int index)
00888 {
00889   // test if the index is valid (when it is not valid, we may not use QList.at()!):
00890   if ((index >= 0) AND (index < m_stationlist.size())) {
00891     m_stationlist.at(index)->showSettingsDialog();
00892   };
00893 }
00894 
00895 quint64 stationlistModel::bandwidth() const
00896 {
00897   return internal_bandwidth;
00898 }
00899 
00900 int stationlistModel::numberOfActiveStreams() const
00901 {
00902   return internal_numberOfActiveStreams;
00903 }
00904 
00905 void stationlistModel::rememberListOfStreamsWhichTheUserWantsToRip_ifNotYetDone()
00906 {
00907   // variables
00908   int i;
00909   QStringList temp_stringList;
00910 
00911   // code
00912   if (m_listOfStreamsOfWhichTheUserWantsThatTheyRip == 0) {
00913     for (i=0; i < rowCount(); i++) {
00914       if (m_stationlist.at(i)->doesTheUserWantsThatTheStreamIsRipping()) {
00915         temp_stringList.append(m_stationlist.at(i)->configFileName());
00916       };
00917     };
00918     m_listOfStreamsOfWhichTheUserWantsThatTheyRip = new QStringList(temp_stringList);
00919   };
00920 }
00921 
00922 bool stationlistModel::queryClose()
00923 {
00924   // variables
00925   int i;
00926 
00927   // code
00928 
00929   rememberListOfStreamsWhichTheUserWantsToRip_ifNotYetDone();
00930 
00931   // start shutdown for all processes
00932   if (numberOfActiveStreams() > 0) {
00933     for (i = 0; i < m_stationlist.size(); ++i) {
00934       m_stationlist.at(i)->shutDown();
00935     };
00936   };
00937 
00938   /* Now we test a second time if there are still running streams. (This could
00939   have changed because we have shutted down before.) If there remain running
00940   streams, we display a "busy" dialog until the shutdown is finished. */
00941   if (numberOfActiveStreams() > 0) {
00942     QPointer<KProgressDialog> m_dialog = new KProgressDialog(m_mainWidget,
00943       i18nc("@title:window", "Saving files..."),
00944       i18nc("@label", "Please wait while last files are saved..."));
00945     connect(this, SIGNAL(numberOfActiveStreamsIsZero()), m_dialog, SLOT(reject()));
00946     // min AND max = 0 means: show "busy" indicator instead of progress:
00947     m_dialog->progressBar()->setMinimum(0);
00948     m_dialog->progressBar()->setMaximum(0);
00949     m_dialog->setModal(true);
00950     m_dialog->setAutoClose(false);
00951     m_dialog->setMinimumDuration(0);
00952     // The following line hasn't any effect because of a bug in KProgressDialog.
00953     // Is fixed in KDE 4.2 (and maybe yet in 4.1.4).
00954     m_dialog->setButtonText(i18nc("@action:button", "Quit without saving"));
00955     m_dialog->exec();
00956     delete m_dialog;
00957   };
00958 
00959   return true;
00960 }
00961 
00962 void stationlistModel::saveProperties(KConfigGroup & m_configGroup)
00963 {
00964   rememberListOfStreamsWhichTheUserWantsToRip_ifNotYetDone();
00965   m_configGroup.writePathEntry("streamsToResume",
00966                                 * m_listOfStreamsOfWhichTheUserWantsThatTheyRip);
00967   // No need to save the changes with sync() - KMainWindow and friends take care of this.
00968 }
00969 
00970 void stationlistModel::readProperties(const KConfigGroup & m_configGroup)
00971 {
00972   // variables
00973   QStringList temp_stringList;
00974   int i;
00975 
00976   // code
00977   temp_stringList = m_configGroup.readPathEntry("streamsToResume", temp_stringList);
00978   for (i = 0; i < rowCount(); i++) {
00979     if (temp_stringList.contains(m_stationlist.at(i)->configFileName())) {
00980       m_stationlist.at(i)->startStreamripper();
00981     };
00982   };
00983 }
00984 
00985 QList<QUrl> stationlistModel::helper_convertToUrl(const QMimeData *data)
00986 {
00987   // variables
00988   QList<QUrl> returnValue;
00989 
00990   // code
00991   if (data->hasUrls()) {
00992     returnValue = data->urls();
00993   } else if (data->hasText()) {
00994     QStringList temp = data->text().split(QRegExp("\\s"), QString::SkipEmptyParts);
00995     foreach (const QString & singleString, temp) {
00996       returnValue.append(singleString);
00997     };
00998   };
00999   int i = 0;
01000   while (i < returnValue.size()) {
01001     if (returnValue.at(i).isValid() && (!returnValue.at(i).isEmpty())) {
01002       ++i;
01003     } else {
01004       returnValue.removeAt(i);
01005     };
01006   };
01007 
01008   return returnValue;
01009 }
01010 
01011 void stationlistModel::paste()
01012 {
01013   dropMimeData(QApplication::clipboard()->mimeData(),
01014                Qt::CopyAction,
01015                0,
01016                0,
01017                QModelIndex());
01018 }
01019 
01020 void stationlistModel::pasteSelection()
01021 {
01022   dropMimeData(QApplication::clipboard()->mimeData(QClipboard::Selection),
01023                Qt::CopyAction,
01024                0,
01025                0,
01026                QModelIndex());
01027 }
01028 
01029 bool stationlistModel::dropMimeData(const QMimeData *data,
01030                                     Qt::DropAction action,
01031                                     int,
01032                                     int,
01033                                     const QModelIndex &)
01034 {
01035   if (action == Qt::IgnoreAction) {
01036     return true;
01037   };
01038 
01039   if (!(action == Qt::CopyAction)) {
01040     return false;
01041   };
01042 
01043   QList<QUrl> urlList = helper_convertToUrl(data);
01044 
01045   if (urlList.isEmpty()) {
01046     return false;
01047   } else {
01048     // consts
01049     const int size_of_list = m_stationlist.size();
01050     // variables
01051     QPointer<radioStation> m_newStation;
01052     // code
01053     beginInsertRows(QModelIndex(), size_of_list, size_of_list + urlList.size() - 1);
01054     for (int i = 0; i < urlList.size(); ++i) {
01055       m_newStation = new radioStation(this, m_mainWidget, QString(), size_of_list + i);
01056       m_newStation->setServerUri(urlList.at(i));
01057       m_stationlist.append(m_newStation);
01058       helper_connectSignalsAndSlots(m_stationlist.last());
01059     };
01060     helper_writeStationListToGeneralSettings();
01061     endInsertRows();
01062     if (!view.isNull()) {
01063       view->selectionModel()->clear();
01064       view->selectionModel()->select(
01065         QItemSelection(index(size_of_list, 0),
01066                        index(size_of_list + urlList.size() - 1, columnCount() - 1)),
01067         QItemSelectionModel::SelectCurrent);
01068     };
01069     return true;
01070   };
01071 
01072   return false;  // safety fallback
01073 }
01074 
01075 Qt::ItemFlags stationlistModel::flags(const QModelIndex & index) const
01076 {
01077   return (Qt::ItemIsDropEnabled | QAbstractItemModel::flags(index));
01078 }
01079 
01080 QStringList stationlistModel::mimeTypes () const
01081 {
01082   QStringList value;
01083   value << "text/plain" << "text/uri-list";
01084   return value;
01085 }
01086 
01087 void stationlistModel::enableListeningIn()
01088 {
01089   QModelIndexList actuallySelectedRows = view->selectionModel()->selectedRows();
01090   if (actuallySelectedRows.size() == 1) {
01091     listen.setStation(m_stationlist.at(actuallySelectedRows.at(0).row()));
01092   } else {
01093     listen.setStation(0);
01094   };
01095 }
01096 
01097 void stationlistModel::disableListeningIn()
01098 {
01099   listen.setStation(0);
01100 }

doxygen