1 /***************************************************************************
2   hopmodel.cpp
3   -------------------
4   Hop model
5   -------------------
6   Copyright 2006-2008, David Johnson
7   Please see the header file for copyright and license information
8  ***************************************************************************/
9 
10 #include <QApplication>
11 #include <QMessageBox>
12 #include <QPair>
13 
14 #include "data.h"
15 #include "hopmodel.h"
16 
17 using namespace Resource;
18 
19 //////////////////////////////////////////////////////////////////////////////
20 // HopModel()
21 // ------------
22 // Constructor
23 
HopModel(QObject * parent,HopList * list)24 HopModel::HopModel(QObject *parent, HopList *list)
25     : QAbstractTableModel(parent), list_(list)
26 {}
27 
~HopModel()28 HopModel::~HopModel() {}
29 
30 //////////////////////////////////////////////////////////////////////////////
31 // flush()
32 // -------
33 // Reset the model
34 
flush()35 void HopModel::flush()
36 {
37     beginResetModel();
38     endResetModel();
39 }
40 
41 //////////////////////////////////////////////////////////////////////////////
42 // data()
43 // ------
44 // Return data at index
45 
data(const QModelIndex & index,int role) const46 QVariant HopModel::data(const QModelIndex &index, int role) const
47 {
48     if (!index.isValid()) return QVariant();
49     if (index.row() >= list_->count()) return QVariant();
50 
51     // row is the entry in the QList
52     const Hop &hop = list_->at(index.row());
53 
54     // column is the hop "field"
55     if (role == Qt::DisplayRole) {
56         switch (index.column()) {
57           case NAME:
58               return hop.name();
59           case WEIGHT:
60               return hop.weight().toString(3);
61           case ALPHA:
62               return QString::number(hop.alpha(), 'f', 1) + '%';
63           case TIME:
64               return QString::number(hop.time()) + tr(" min", "minutes");
65           case TYPE:
66               return hop.type();
67           default:
68               return QVariant();
69         }
70     } else if (role == Qt::EditRole) {
71         switch (index.column()) {
72           case NAME:
73               return hop.name();
74           case WEIGHT: {
75               // return converted quantity
76               Weight weight = hop.weight();
77               weight.convert(Data::instance()->defaultHopUnit());
78               return weight.amount();
79           }
80           case ALPHA:
81               return hop.alpha();
82           case TIME:
83               return hop.time();
84           case TYPE:
85               return hop.type();
86           default:
87               return QVariant();
88         }
89     } else if (role == Qt::TextAlignmentRole) {
90         switch (index.column()) {
91           case NAME:
92           case TYPE:
93               return Qt::AlignLeft;
94           case WEIGHT:
95           case ALPHA:
96           case TIME:
97           default:
98               return Qt::AlignRight;
99         }
100     } else {
101         return QVariant();
102     }
103 }
104 
105 //////////////////////////////////////////////////////////////////////////////
106 // setData()
107 // ---------
108 // Set data at index
109 
setData(const QModelIndex & index,const QVariant & value,int role)110 bool HopModel::setData(const QModelIndex &index,
111                        const QVariant &value, int role)
112 {
113     static bool deleting = false;
114 
115     Hop hop;
116     QString name;
117     int row = index.row();
118     int column = index.column();
119 
120     if (!index.isValid()) return false;
121     if (role != Qt::EditRole) return false;
122     if (row >= list_->count()) return false;
123 
124     // grab existing hop
125     if (row < list_->count()) hop = list_->value(row);
126 
127     switch (column) {
128       case NAME:
129           // editing name as several special cases
130           name = value.toString();
131 
132           // deleting name deletes hop
133           if (name.isEmpty()) {
134               if (row >= list_->count()) return false; // already blank
135 
136               // TODO: for some reason this gets entered recursively...
137               if (deleting) return false;
138               deleting = true;
139 
140               int status = QMessageBox::question(QApplication::activeWindow(),
141                                TITLE + tr(" - Delete?"),
142                                tr("Do you wish to remove this entry?"),
143                                QMessageBox::Yes | QMessageBox::Cancel);
144               if (status == QMessageBox::Yes) {
145                   // remove hop
146                   beginRemoveRows(index.parent(), row, row);
147 
148                   list_->removeAt(row);
149                   emit modified();
150                   endRemoveRows();
151 
152                   deleting = false;
153                   return true;
154               } else {
155                   // ignore
156                   deleting = false;
157                   return false;
158               }
159           }
160 
161           // no change, nothing to do
162           if (name == list_->at(row).name()) {
163               return false;
164           }
165 
166           // changed name
167           hop.setName(name);
168           if (Data::instance()->hasHop(name)) {
169               Hop newhop = Data::instance()->hop(name);
170               // we don't override weight or time
171               hop.setType(newhop.type());
172               hop.setAlpha(newhop.alpha());
173           }
174           break;
175 
176       case WEIGHT:
177           hop.setWeight(Weight(value.toDouble(),
178                                Data::instance()->defaultHopUnit()));
179           break;
180 
181       case ALPHA:
182           hop.setAlpha(value.toDouble());
183           break;
184 
185       case TIME:
186           hop.setTime(value.toUInt());
187           break;
188 
189       case TYPE:
190           hop.setType(value.toString());
191           break;
192 
193       default:
194           return false;
195     }
196 
197     list_->replace(row, hop);
198     emit modified();
199 
200     // whole row may have changed
201     emit dataChanged(index.sibling(row, NAME),
202                      index.sibling(row, TIME));
203 
204     return true;
205 }
206 
207 //////////////////////////////////////////////////////////////////////////////
208 // insertRows()
209 // ------------
210 // Insert rows into table
211 
insertRows(int row,int count,const QModelIndex &)212 bool HopModel::insertRows(int row, int count, const QModelIndex&)
213 {
214     if (count != 1) return false; // only insert one row at a time
215     if ((row < 0) || (row >= list_->count())) row = list_->count();
216 
217     Hop hop = Data::instance()->hop(tr("Generic"));
218 
219     beginInsertRows(QModelIndex(), row, row);
220     list_->insert(row, hop);
221     emit modified();
222     endInsertRows();
223     return true;
224 }
225 
226 //////////////////////////////////////////////////////////////////////////////
227 // removeRows()
228 // ------------
229 // Remove rows from table
230 
removeRows(int row,int count,const QModelIndex &)231 bool HopModel::removeRows(int row, int count, const QModelIndex&)
232 {
233     if (count != 1) return false; // only remove one row at a time
234     if ((row < 0) || (row >= list_->count())) return false;
235 
236     int status = QMessageBox::question(QApplication::activeWindow(),
237                                TITLE + tr(" - Delete?"),
238                                tr("Do you wish to remove this entry?"),
239                                QMessageBox::Yes | QMessageBox::Cancel);
240     if (status == QMessageBox::Cancel) {
241         return false;
242     }
243 
244     beginRemoveRows(QModelIndex(), row, row);
245     list_->removeAt(row);
246     emit modified();
247     endRemoveRows();
248     return true;
249 }
250 
251 //////////////////////////////////////////////////////////////////////////////
252 // headerData()
253 // ------------
254 // Return header information
255 
headerData(int section,Qt::Orientation orientation,int role) const256 QVariant HopModel::headerData(int section, Qt::Orientation orientation,
257                                 int role) const
258 {
259     if (orientation == Qt::Horizontal && role == Qt::DisplayRole) {
260         switch (section) {
261           case NAME:
262               return tr("Hop");
263           case WEIGHT:
264               return tr("Weight");
265           case ALPHA:
266               return tr("Alpha");
267           case TIME:
268               return tr("Time");
269           case TYPE:
270               return tr("Type");
271           default:
272               return QVariant();
273         }
274     }
275 
276     return QVariant();
277 }
278 
279 //////////////////////////////////////////////////////////////////////////////
280 // flags()
281 // ------
282 // Return flags at index
283 
flags(const QModelIndex & index) const284 Qt::ItemFlags HopModel::flags(const QModelIndex &index) const
285 {
286     if (!index.isValid()) return Qt::ItemIsEnabled;
287 
288     return QAbstractTableModel::flags(index) | Qt::ItemIsEditable;
289 }
290 
291 //////////////////////////////////////////////////////////////////////////////
292 // rowCount()
293 // ----------
294 // Return number of rows of data
295 
rowCount(const QModelIndex &) const296 int HopModel::rowCount(const QModelIndex &) const
297 {
298     return list_->count();
299 }
300 
301 //////////////////////////////////////////////////////////////////////////////
302 // columnCount()
303 // -------------
304 // Return number of columns of data
305 
columnCount(const QModelIndex &) const306 int HopModel::columnCount(const QModelIndex &) const
307 {
308     return COUNT;
309 }
310 
311 //////////////////////////////////////////////////////////////////////////////
312 // sort()
313 // ------
314 // Sort by column
315 
sort(int column,Qt::SortOrder order)316 void HopModel::sort(int column, Qt::SortOrder order)
317 {
318     QList<QPair<QString,Hop> > sortlist;
319 
320     foreach(Hop hop, *list_) {
321         QString field;
322         switch (column) {
323           case NAME:
324               field = hop.name();
325               break;
326           case WEIGHT:
327               field = QString::number(hop.weight().amount()).rightJustified(8,'0');
328               break;
329           case ALPHA:
330               field = QString::number(hop.alpha(),'f',1).rightJustified(8,'0');
331               break;
332           case TYPE:
333               field = hop.type();
334               break;
335           case TIME:
336           default:
337               field = QString::number(hop.time()).rightJustified(8,'0');
338               break;
339         }
340         sortlist.append(QPair<QString,Hop>(field, hop));
341     }
342 
343     // sort list
344     qSort(sortlist.begin(), sortlist.end());
345 
346     emit layoutAboutToBeChanged();
347 
348     // create new list
349     list_->clear();
350     QPair<QString,Hop> pair;
351     foreach(pair, sortlist) {
352         if (order == Qt::AscendingOrder)
353             list_->append(pair.second);
354         else
355             list_->prepend(pair.second);
356     }
357 
358     emit layoutChanged();
359 }
360