FreeDebks  1.0.3
 All Classes Files Functions Variables Friends Pages
FdModelResults.cpp
Go to the documentation of this file.
1 // --------------------------------------------------------------------
2 // Copyright © 2011-2013 Mathieu Schopfer
3 //
4 // This file is part of FreeDebks.
5 //
6 // FreeDebks is free software: you can redistribute it and/or modify
7 // it under the terms of the GNU General Public License as published by
8 // the Free Software Foundation, either version 3 of the License, or
9 // (at your option) any later version.
10 //
11 // FreeDebks is distributed in the hope that it will be useful,
12 // but WITHOUT ANY WARRANTY; without even the implied warranty of
13 // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
14 // GNU General Public License for more details.
15 //
16 // You should have received a copy of the GNU General Public License
17 // along with FreeDebks. If not, see <http://www.gnu.org/licenses/>.
18 // --------------------------------------------------------------------
19 
20 #include "FdModelResults.hpp"
21 #include "FdItemResults.hpp"
22 #include "FdCommandsResults.hpp"
23 #include "FdModelCoa.hpp"
24 #include "FdItemCoa.hpp"
25 #include "../gui/FdSubWindow.hpp"
26 
27 
28 FdModelResults::FdModelResults(FdSubWindow* parent) : QAbstractTableModel(0), mSubwindow(parent)
29 {
30  init();
31 }
32 
36 FdModelResults::FdModelResults(FdSubWindow *parent, QDomElement &results) : QAbstractTableModel(0), mSubwindow(parent)
37 {
38  mResults = QList<FdItemResults_p>();
39  QDomElement calculation = results.firstChildElement();
40  while(!calculation.isNull())
41  {
42  mResults << FdItemResults::pointer(calculation);
43  calculation = calculation.nextSiblingElement();
44  }
45 
46  init();
47 }
48 
54 FdModelResults::FdModelResults(FdSubWindow *parent, const FdModelResults *model) : QAbstractTableModel(0), mSubwindow(parent)
55 {
56  mResults = QList<FdItemResults_p>();
57  for(int i = 0; i < model->items().size(); ++i)
58  mResults << FdItemResults::pointer(model->items()[i]);
59 
60  init();
61 }
62 
64 {
65  setSupportedDragActions(Qt::MoveAction);
66  mStack = new QUndoStack();
67  connect(mStack, SIGNAL(cleanChanged(bool)), mSubwindow, SLOT(cleanStateChanged(bool)));
68 }
69 
70 QVariant FdModelResults::data(FdItemResults_p calculation, ResultsColumn column) const
71 {
72  switch(column)
73  {
74  case ResultsId: return calculation->id();
75  case ResultsLabel: return calculation->label();
76  case ResultsCalculation: return calculation->calculation();
77  case ResultsResult:
78  if(!calculation->isEmpty())
79  return calculation->result();
80  else
81  return "";
82  }
83 
84  return QVariant();
85 }
86 
87 QVariant FdModelResults::data(const QModelIndex &index, int role) const
88 {
89  if(!index.isValid())
90  return QVariant();
91  if(role == Qt::TextAlignmentRole && index.column() == ResultsResult)
92  return Qt::AlignRight;
93  if(role == ValidityRole)
94  return isValid(mResults[index.row()]->calculation());
95  if(role != Qt::DisplayRole && role != Qt::EditRole)
96  return QVariant();
97 
98  return data(mResults[index.row()], ResultsColumn(index.column()));
99 }
100 
101 void FdModelResults::setData(FdItemResults_p calculation, ResultsColumn column, const QVariant &value)
102 {
103  int row = mResults.indexOf(calculation);
104  switch(column)
105  {
106  case ResultsId:
107  calculation->setId(value.toString());
108  break;
109  case ResultsLabel:
110  calculation->setLabel(value.toString());
111  break;
112  case ResultsCalculation:
113  calculation->setCalculation(value.toString());
114  break;
115  }
116 
117  emit(dataChanged(index(row, column), index(row, column)));
118 }
119 
130 bool FdModelResults::setData(const QModelIndex &index, const QVariant &value, int role)
131 {
132  if(!index.isValid() || role != Qt::EditRole)
133  return false;
134 
135  if(value == data(index, Qt::DisplayRole))
136  return true;
137 
138  int column = index.column();
139  switch(column)
140  {
141  case ResultsId:
142  if(idExists(value.toString()))
143  return false;
144  break;
145  case ResultsCalculation:
146  break;
147  }
148 
149  FdItemResults_p calculation = mResults.at(index.row());
150  mStack->push(new FdResultsSetData(this, calculation, ResultsColumn(column), value));
151  return true;
152 }
153 
154 QVariant FdModelResults::headerData(int section, Qt::Orientation orientation, int role) const
155 {
156  if(role == Qt::DisplayRole)
157  {
158  if (orientation == Qt::Horizontal)
159  {
160  switch(section)
161  {
162  case ResultsId: return tr("Id", "Results page column header.");
163  case ResultsLabel: return tr("Label", "Results page column header.");
164  case ResultsCalculation: return tr("Calculation", "Results page column header.");
165  case ResultsResult: return tr("Result", "Results page column header.");
166  }
167  }
168  else
169  return section+1;
170  }
171 
172  return QVariant();
173 }
174 
175 Qt::ItemFlags FdModelResults::flags(const QModelIndex &index) const
176 {
177  Qt::ItemFlags flags;
178  if(index.isValid())
179  {
180  flags = Qt::ItemIsSelectable | Qt::ItemIsEnabled | Qt::ItemIsDragEnabled | Qt::ItemIsDropEnabled;
181  if(index.column() != ResultsResult)
182  flags |= Qt::ItemIsEditable;
183  }
184 
185  return flags;
186 }
187 
188 Qt::DropActions FdModelResults::supportedDropActions() const
189 {
190  return Qt::MoveAction;
191 }
192 
193 QStringList FdModelResults::mimeTypes() const
194 {
195  return QStringList() << "application/freedebks";
196 }
197 
203 QMimeData* FdModelResults::mimeData(const QModelIndexList &indexes) const
204 {
205  QMimeData* mimeData = new QMimeData();
206  QByteArray encodedData;
207  QDataStream stream(&encodedData, QIODevice::WriteOnly);
208 
209  for(int i = 0; i < indexes.size(); ++i)
210  {
211  if(indexes[i].column() == 0)
212  stream << indexes[i].row();
213  }
214 
215  mimeData->setData("application/freedebks", encodedData);
216  return mimeData;
217 }
218 
222 bool FdModelResults::dropMimeData(const QMimeData *data, Qt::DropAction action, int row, int column, const QModelIndex &parent)
223 {
224  if(action != Qt::MoveAction)
225  return false;
226 
227  QByteArray encodedData = data->data("application/freedebks");
228  QDataStream stream(&encodedData, QIODevice::ReadOnly);
229  QList<int> rows;
230  row = parent.row();
231  int j;
232 
233  while(!stream.atEnd())
234  {
235  j = 0;
236  stream >> row;
237  for(int i = 0; i < rows.size(); i++)
238  if(row > rows[i])
239  ++j;
240 
241  rows.insert(j, row);
242  }
243 
244  mStack->push(new FdResultsDragDrop(this, rows, parent.row()));
245  return true;
246 }
247 
248 bool FdModelResults::removeRows(int row, int count, const QModelIndex &parent)
249 {
250  beginRemoveRows(parent, row, row+count-1);
251  for(int i = 0; i < count; ++i)
252  {
253  mResults.removeAt(row);
254  }
255  endRemoveRows();
256  return true;
257 }
258 
259 int FdModelResults::rowCount(const QModelIndex &parent) const
260 {
261  return mResults.size();
262 }
263 
264 int FdModelResults::columnCount(const QModelIndex &parent) const
265 {
266  return ResultsColumnCount;
267 }
268 
269 void FdModelResults::insertCalculation(FdItemResults_p calculation, int row)
270 {
271  insertCalculations(QList<FdItemResults_p>() << calculation, row);
272 }
273 
274 void FdModelResults::insertCalculations(QList<FdItemResults_p> calculations, int row)
275 {
276  int count = calculations.size();
277  beginInsertRows(QModelIndex(), row, row+count-1);
278  for(int i = 0; i < count; ++i)
279  {
280  mResults.insert(row+i, calculations[i]);
281  }
282  endInsertRows();
283 }
284 
285 void FdModelResults::removeCalculation(FdItemResults_p calculation)
286 {
287  removeCalculations(QList<FdItemResults_p>() << calculation);
288 }
289 
290 void FdModelResults::removeCalculations(QList<FdItemResults_p> calculations)
291 {
292  for(int i = 0; i < calculations.size(); ++i)
293  removeRow(mResults.indexOf(calculations[i]));
294 }
295 
299 QAction* FdModelResults::redo()
300 {
301  return mStack->createRedoAction(this);
302 }
303 
307 QAction* FdModelResults::undo()
308 {
309  return mStack->createUndoAction(this);
310 }
311 
315 bool FdModelResults::idExists(QString id) const
316 {
317  if(mSubwindow->coa()->idExists(id) || !item(id).isNull())
318  return true;
319 
320  return false;
321 }
322 
326 FdItemResults_p FdModelResults::item(QString id) const
327 {
328  for(int i = 0; i < mResults.size(); ++i)
329  {
330  if(mResults[i]->id() == id)
331  return mResults[i];
332  }
333 
334  return FdItemResults_p(0);
335 }
336 
337 FdItemResults_p FdModelResults::item(int row) const
338 {
339  return mResults[row];
340 }
341 
342 QList<FdItemResults_p> FdModelResults::items() const
343 {
344  return mResults;
345 }
346 
350 QModelIndexList FdModelResults::entryByPartialText(QString text) const
351 {
352  QModelIndexList results;
353  for(int i = 0; i < rowCount(); ++i)
354  {
355  if(mResults[i]->label().contains(text, Qt::CaseInsensitive))
356  results << index(i, 0);
357  }
358  return results;
359 }
360 
364 bool FdModelResults::isValid(QString calculation) const
365 {
366  QRegExp symbols("[-+*/()\"]");
367  int p = 0;
368  QString term;
369  QString symbol;
370 
371  while(calculation.size())
372  {
373  p = calculation.indexOf(symbols);
374 
375  if(p != -1)
376  {
377  symbol = calculation[p];
378 
379  if(p == 0 && symbol == "\"")
380  {
381  calculation = calculation.mid(1);
382  p = calculation.indexOf("\"");
383  if(p == -1)
384  return false;
385  else
386  {
387  term = calculation.left(p);
388  FdItemCoa_p itemCoa = mSubwindow->coa()->itemById(term);
389  FdItemResults_p itemResults = this->item(term);
390  if(itemCoa.isNull() && itemResults.isNull())
391  return false;
392  }
393  }
394  else if(p && !calculation.left(p).toDouble())
395  return false;
396 
397  calculation = calculation.mid(p+1);
398  }
399  else if(!calculation.toDouble())
400  return false;
401  else
402  calculation = "";
403  }
404 
405  return true;
406 }
407 
415 double FdModelResults::parse(QString calculation) const
416 {
417  QStack<QString> postfix = FdModelResults::infixToPostfix(calculation);
418  QStack<double> stack;
419  QString e;
420  double opL = 0;
421  double opR = 0;
422  QRegExp operators("[-+*/]");
423 
424  while(!postfix.isEmpty())
425  {
426  e = postfix.front();
427  postfix.pop_front();
428  if(e.size() == 1 && e.contains(operators))
429  {
430  opR = stack.pop();
431  opL = stack.pop();
432 
433  if(e == "+")
434  stack.push(opL+opR);
435  else if(e == "-")
436  stack.push(opL-opR);
437  else if(e == "*")
438  stack.push(opL*opR);
439  else if(e == "/")
440  stack.push(opL/opR);
441  }
442  else
443  stack.push(parseItem(e));
444  }
445 
446  return stack.pop();
447 
448 }
449 
454 double FdModelResults::parseItem(QString element) const
455 {
456  int i = 0;
457  QString e = element.at(0);
458 
459  if(e == "\"")
460  {
461  i = element.indexOf("\"", 1);
462  if(i == -1)
463  return 0;
464  else
465  {
466  element = element.mid(1, i-1);
467  FdItemCoa_p itemCoa = mSubwindow->coa()->itemById(element);
468  FdItemResults_p itemResults = this->item(element);
469  if(!itemCoa.isNull())
470  return itemCoa->balance();
471  else if(!itemResults.isNull())
472  return itemResults->result();
473  else
474  return 0;
475  }
476  }
477  else
478  return element.toDouble();
479 }
480 
481 QDomElement FdModelResults::toXml(QDomDocument &document) const
482 {
483  QDomElement results = document.createElement("Results");
484  for(int i = 0; i < mResults.size(); ++i)
485  {
486  if(!mResults[i]->isEmpty())
487  {
488  QDomElement calculation = mResults[i]->toXml(document);
489  results.appendChild(calculation);
490  }
491  }
492 
494  mStack->setClean();
495  return results;
496 }
497 
501 QModelIndex FdModelResults::addCalculation(const QModelIndex &selected)
502 {
503  int row;
504  if(selected.isValid())
505  row = selected.row()+1;
506  else
507  row = rowCount();
508 
509  // Choses a new id
510  QString base = "R";
511  QString id;
512  int i = 0;
513  do
514  {
515  id = base+QString::number(i);
516  ++i;
517  }
518  while(idExists(id));
519 
520  struct locationResults location;
521  location.item = FdItemResults_p(new FdItemResults(id));
522  location.row = row;
523 
524  mStack->push(new FdResultsAdd(this, location));
525  return index(row, ResultsId);
526 }
527 
531 void FdModelResults::removeSelected(const QList<int> &rows)
532 {
533  if(rows.size())
534  mStack->push(new FdResultsRemove(this, rows));
535 }
536 
543 {
544  for(int i = 0; i < mResults.size(); ++i)
545  {
546  if(!mResults[i]->isEmpty())
547  mResults[i]->setResult(parse(mResults[i]->calculation()));
548  }
549  emit(dataChanged(index(0,0), index(rowCount()-1, columnCount()-1)));
550 }
551 
556 QStack<QString> FdModelResults::infixToPostfix(QString expr)
557 {
558  QRegExp operators("[-+*/]");
559  QRegExp symbols("[-+*/()]");
560  QStack<QString> stack;
561  QStack<QString> postfix; // This will be a stack containing the postfix expression upside down.
562  QString e;
563  bool prevOp = true; // Is true if the previous element was an operator (or a "("). Helps treating negative numbers.
564  int i = 0;
565 
566  while(expr.size())
567  {
568  // indexOf() returns -1 if no symbols were found.
569  if(prevOp && QString(expr.at(0)) == "-")
570  {
571  if(QString(expr.at(1)) == "(")
572  expr.insert(1, "1");
573  i = expr.indexOf(symbols,1);
574  }
575  else
576  i = expr.indexOf(symbols);
577 
578  if(i)
579  {
580  postfix.push(expr.left(i));
581  expr = expr.mid(i);
582  if(i == -1)
583  break;
584  if(QString(expr.at(0)) == "(")
585  expr.prepend("*");
586  prevOp=false;
587  }
588 
589  e = expr.at(0);
590 
591  if(e == "-" && prevOp)
592  {
593  i = expr.indexOf(symbols,1);
594  postfix.push(expr.left(i));
595  expr = expr.mid(i);
596  prevOp=false;
597  e = expr.at(0);
598  }
599 
600  if(e == "(")
601  {
602  stack.push("(");
603  prevOp=true;
604  }
605 
606  if(e == ")")
607  {
608  while(stack.top() != "(")
609  postfix.push(stack.pop());
610 
611  stack.pop();
612  }
613 
614  if(e.contains(operators))
615  {
616  if(stack.isEmpty())
617  stack.push(e);
618  else
619  {
620  while(!stack.isEmpty() && FdModelResults::opPriority(stack.top()) >= FdModelResults::opPriority(e))
621  postfix.push(stack.pop());
622 
623  stack.push(e);
624  }
625  prevOp=true;
626  }
627 
628  expr.remove(0,1);
629  }
630 
631  while(!stack.isEmpty())
632  postfix.push(stack.pop());
633 
634  return postfix;
635 }
636 
637 
638 int FdModelResults::opPriority(QString op)
639 {
640  if(op == "+" || op == "-")
641  return 1;
642  else if(op == "*" || op == "/")
643  return 2;
644  else
645  return 0;
646 
647 }