Mixxx
|
00001 // wtracktableviewheader.cpp 00002 // Created 1/2/2010 by RJ Ryan (rryan@mit.edu) 00003 00004 #include <QtDebug> 00005 00006 #include "widget/wtracktableviewheader.h" 00007 #include "library/trackmodel.h" 00008 00009 #define WTTVH_MINIMUM_SECTION_SIZE 20 00010 00011 WTrackTableViewHeader::WTrackTableViewHeader(Qt::Orientation orientation, 00012 QWidget* parent) 00013 : QHeaderView(orientation, parent), 00014 m_menu(tr("Show or hide columns."), this), 00015 m_signalMapper(this) { 00016 connect(&m_signalMapper, SIGNAL(mapped(int)), 00017 this, SLOT(showOrHideColumn(int))); 00018 } 00019 00020 WTrackTableViewHeader::~WTrackTableViewHeader() { 00021 } 00022 00023 void WTrackTableViewHeader::contextMenuEvent(QContextMenuEvent* event) { 00024 m_menu.popup(event->globalPos()); 00025 } 00026 00027 void WTrackTableViewHeader::setModel(QAbstractItemModel* model) { 00028 TrackModel* oldTrackModel = getTrackModel(); 00029 00030 if (dynamic_cast<QAbstractItemModel*>(oldTrackModel) == model) { 00031 // If the models are the same, do nothing but the redundant call. 00032 QHeaderView::setModel(model); 00033 return; 00034 } 00035 00036 // Won't happen in practice since the WTrackTableView new's a new 00037 // WTrackTableViewHeader each time a new TrackModel is loaded. 00038 // if (oldTrackModel) { 00039 // saveHeaderState(); 00040 // } 00041 00042 // First clear all the context menu actions for the old model. 00043 clearActions(); 00044 00045 // Now set the header view to show the new model 00046 QHeaderView::setModel(model); 00047 00048 // Now build actions for the new TrackModel 00049 TrackModel* trackModel = dynamic_cast<TrackModel*>(model); 00050 00051 if (!trackModel) { 00052 return; 00053 } 00054 00055 // Restore saved header state to get sizes, column positioning, etc. back. 00056 restoreHeaderState(); 00057 00058 // Here we can override values to prevent restoring corrupt values from database 00059 setMovable(true); 00060 setCascadingSectionResizes(true); 00061 setMinimumSectionSize(WTTVH_MINIMUM_SECTION_SIZE); 00062 00063 int columns = model->columnCount(); 00064 for (int i = 0; i < columns; ++i) { 00065 if (trackModel->isColumnInternal(i)) { 00066 continue; 00067 } 00068 00069 QString title = model->headerData(i, orientation()).toString(); 00070 QAction* action = new QAction(title, &m_menu); 00071 action->setCheckable(true); 00072 00073 /* If Mixxx starts the first time or the header states have been cleared 00074 * due to database schema evolution we gonna hide all columns that may 00075 * contain a potential large number of NULL values. Here we uncheck 00076 * item in the context menu that are hidden by defualt (e.g., key 00077 * column) 00078 */ 00079 if (!hasPersistedHeaderState() && 00080 trackModel->isColumnHiddenByDefault(i)) { 00081 action->setChecked(false); 00082 } else { 00083 action->setChecked(!isSectionHidden(i)); 00084 } 00085 00086 // Map this action's signals via our QSignalMapper 00087 m_signalMapper.setMapping(action, i); 00088 m_columnActions.insert(i, action); 00089 connect(action, SIGNAL(triggered()), 00090 &m_signalMapper, SLOT(map())); 00091 m_menu.addAction(action); 00092 00093 // force the section size to be a least WTTVH_MINIMUM_SECTION_SIZE 00094 if (sectionSize(i) < WTTVH_MINIMUM_SECTION_SIZE) { 00095 // This might happen if WTTVH_MINIMUM_SECTION_SIZ has changed or 00096 // the header state from database was corrupt 00097 resizeSection(i,WTTVH_MINIMUM_SECTION_SIZE); 00098 } 00099 } 00100 00101 // Safety check against someone getting stuck with all columns hidden 00102 // (produces an empty library table). Just re-show them all. 00103 if (hiddenCount() == columns) { 00104 for (int i = 0; i < columns; ++i) { 00105 showSection(i); 00106 } 00107 } 00108 } 00109 00110 void WTrackTableViewHeader::saveHeaderState() { 00111 TrackModel* track_model = getTrackModel(); 00112 if (!track_model) { 00113 return; 00114 } 00115 // Convert the QByteArray to a Base64 string and save it. 00116 QString headerState = QString(saveState().toBase64()); 00117 bool result = track_model->setModelSetting("header_state", headerState); 00118 //qDebug() << "Saving old header state:" << result << headerState; 00119 } 00120 00121 void WTrackTableViewHeader::restoreHeaderState() { 00122 TrackModel* track_model = getTrackModel(); 00123 00124 if (!track_model) { 00125 return; 00126 } 00127 00128 QString headerStateString = track_model->getModelSetting("header_state"); 00129 if (!headerStateString.isNull()) { 00130 // Load the previous header state (stored as a Base 64 string). Decode 00131 // it and restore it. 00132 //qDebug() << "Restoring header state" << headerStateString; 00133 QByteArray headerState = headerStateString.toAscii(); 00134 headerState = QByteArray::fromBase64(headerState); 00135 restoreState(headerState); 00136 } 00137 } 00138 00139 bool WTrackTableViewHeader::hasPersistedHeaderState() { 00140 TrackModel* track_model = getTrackModel(); 00141 if (!track_model) { 00142 return false; 00143 } 00144 QString headerStateString = track_model->getModelSetting("header_state"); 00145 00146 if (!headerStateString.isNull()) return true; 00147 return false; 00148 00149 } 00150 void WTrackTableViewHeader::clearActions() { 00151 // The QActions are parented to the menu, so clearing deletes them. Since 00152 // they are deleted we don't have to disconnect their signals from the 00153 // mapper. 00154 m_columnActions.clear(); 00155 m_menu.clear(); 00156 } 00157 00158 void WTrackTableViewHeader::showOrHideColumn(int column) { 00159 if (!m_columnActions.contains(column)) { 00160 qDebug() << "WTrackTableViewHeader got invalid column" << column; 00161 return; 00162 } 00163 00164 QAction* action = m_columnActions[column]; 00165 if (action->isChecked()) { 00166 showSection(column); 00167 } else { 00168 // If the user hides every column then the table will disappear. This 00169 // guards against that. NB: hiddenCount reflects checked QAction's so 00170 // size-hiddenCount will be zero the moment they uncheck the last 00171 // section. 00172 if (m_columnActions.size() - hiddenCount() > 0) { 00173 hideSection(column); 00174 } else { 00175 // Otherwise, ignore the request and re-check this QAction. 00176 action->setChecked(true); 00177 } 00178 } 00179 } 00180 00181 int WTrackTableViewHeader::hiddenCount() { 00182 int count = 0; 00183 for (QMap<int, QAction*>::iterator it = m_columnActions.begin(); 00184 it != m_columnActions.end(); it++) { 00185 QAction* pAction = *it; 00186 if (!pAction->isChecked()) 00187 count += 1; 00188 } 00189 return count; 00190 } 00191 00192 TrackModel* WTrackTableViewHeader::getTrackModel() { 00193 TrackModel* trackModel = dynamic_cast<TrackModel*>(model()); 00194 return trackModel; 00195 }