Made Category headings dynamic
[shopper.git] / src / shopperList.cc
blob9ce7c8a5f8dea95dd19d1960f189fe937ec7a47e
1 /* Shopper
2 * Copyright (C) 2008 David Greaves
4 * This software is free software; you can redistribute it and/or
5 * modify it under the terms of the GNU Lesser General Public License
6 * as published by the Free Software Foundation; either version 2.1 of
7 * the License, or (at your option) any later version.
9 * This software is distributed in the hope that it will be useful, but
10 * WITHOUT ANY WARRANTY; without even the implied warranty of
11 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
12 * Lesser General Public License for more details.
14 * You should have received a copy of the GNU Lesser General Public
15 * License along with this software; if not, write to the Free Software
16 * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA
17 * 02110-1301 USA
20 #define DEBUG_SHOPPER 1
22 #include "shopper.h"
24 #include <iostream>
25 #include <sstream>
26 #include <string>
27 #include <fstream>
28 #include <list>
29 #include <set>
30 #include <algorithm>
31 #include <signal.h>
32 #include "shopperList.h"
34 using namespace std;
36 std::basic_ostream<char>& operator<<(std::basic_ostream<char>& os, const
37 QString& str) {
38 return os << qPrintable(str);
41 namespace Shopper
43 ////////////////////////////////////////////////////////////////
44 // Category
45 int Category::id_master = 0;
46 Category::Category(sparseCategory dummy) : // SPARSE constructor for ListParser
47 _wanted(0),
48 _bought(0)
50 Category::Category() :
51 id (Category::id_master++),
52 _wanted(0),
53 _bought(0)
56 Category::Category(QString name) :
57 id (Category::id_master++),
58 name(name),
59 _wanted(0),
60 _bought(0)
62 Category::~Category()
64 emit deleted();
67 void Category::dbg() {
68 DEBUG("\nCat name : " << name <<
69 "\n id : " << id <<
70 "\n wanted : " << _wanted <<
71 "\n bought : " << _bought <<
72 "\n");
75 // Operators
76 bool operator< (const Category &a, const Category &b){
77 return (a.id < b.id);
79 bool cat_cmp (const Category *a, const Category *b) {
80 return (a->id < b->id);
82 // cmp (user defined)
83 // bool user_cmp (const Category &a, const Category &b)
84 // {
85 // return (a.pos < b.pos);
86 // }
88 // accessors
89 int Category::get_id() { return id; }
90 void Category::set_id(int i) {
91 id = i;
92 emit changed();
94 void Category::set_name(QString n) {
95 name = n;
96 emit changed();
98 QString Category::get_name() { return name; }
99 int Category::get_size() { return items.size(); }
101 void Category::setWanted(bool b) { b?_wanted++:_wanted--; emit changed(); }
102 void Category::setBought(bool b) { b?_bought++:_bought--; emit changed(); }
103 int Category::wanted() { return _wanted; }
104 int Category::bought() { return _bought; }
107 void Category::add(Item &it)
109 DEBUG("Category add item " << it.get_desc() << "\n");
110 items.push_back(&it);
112 void Category::rm(Item &it)
114 DEBUG("Category remove item " << it.get_desc() << "\n");
115 items.remove(&it);
118 Category::pItemIter Category::itemsI() {return items.begin();}
119 Category::pItemIter Category::itemsEnd(){return items.end();}
120 int Category::size(){return items.size();}
122 ////////////////////////////////////////////////////////////////
123 // Item
124 int Item::id_master = 0;
126 Item::Item(sparseItem dummy) : // SPARSE constructor for ListParser
127 id (Item::id_master++),
128 category (NULL),
129 wanted (false),
130 bought (false)
132 Item::Item() :
133 id (Item::id_master++),
134 category (NULL),
135 wanted (false),
136 bought (false)
138 Item::Item(Category &c, QString d, QString n, bool w, bool b) :
139 id (Item::id_master++),
140 category (&c),
141 desc (d),
142 note (n),
143 wanted (false),
144 bought (false)
146 set_wanted (w);
147 set_bought (b);
149 DEBUG("Created an item from parts\n");
152 Item::~Item()
154 emit deleted();
157 void Item::dbg() {
158 DEBUG(
159 "\nItem desc : " << desc <<
160 "\n id : " << id <<
161 "\n note : " << note <<
162 "\n bought : " << (bought ? "true":"false") <<
163 "\n wanted : " << (wanted ? "true":"false") <<
164 "\n category : " << category->get_name() <<
165 "\n");
167 // accessors
168 int Item::get_id(){ return id; }
169 void Item::set_id(int i){ id = i; }
170 void Item::set_desc(QString d){
171 desc = d;
172 emit changed();
174 QString Item::get_desc(){ return desc; }
176 void Item::set_note(QString n) {
177 note = n;
178 emit changed();
180 QString Item::get_note(){ return note; }
181 void Item::set_wanted(bool w) {
182 if (wanted !=w) {
183 DEBUG("Wanted\n");
184 wanted =w;
185 category->setWanted(wanted);
186 emit changed();
189 bool Item::get_wanted(){ return wanted; }
190 void Item::set_bought(bool b)
192 if (bought != b) {
193 DEBUG("Bought\n");
194 bought = b;
195 category->setBought(bought);
196 emit changed();
199 bool Item::get_bought(){ return bought; }
200 void Item::set_category(Category *c){
201 category = c;
202 emit changed();
204 Category* Item::get_category(){ return category; }
206 ////////////////////////////////////////////////////////////////
207 // List
209 List::List() :
210 name("Sainsburys"),
211 state(MAKING_LIST),
212 active_category(NULL)
214 // No items, no categories
215 DEBUG("\n\n\nMAKING NEW LIST\n\n\n");
217 List::List(QString dump)
219 DEBUG("\n\n\nMAKING NEW LIST FROM STRING\n\n\n");
220 ListParser parse(this);
221 parse.from_string(dump);
222 } // initiate from a dump
223 List::~List()
225 for(list<Category*>::iterator catI = categories.begin();
226 catI != categories.end(); ++catI) {
227 delete *catI;
229 for(list<Item*>::iterator itI = items.begin();
230 itI != items.end(); ++itI) {
231 delete *itI;
236 List* List::from_file(QString filename)
238 // open file
239 std::ifstream file(filename.toAscii());
240 if (!file) {
241 return NULL;
243 // Read xml as a stream
244 std::stringstream xml;
245 xml << file.rdbuf();
246 std::string sxml(xml.str());
247 QString qxml(sxml.c_str());
248 DEBUG("read from : " <<filename <<"\n"<<qxml);
249 // Create new list and return it
250 return new Shopper::List(qxml); // FIXME: may not succeed.
253 void List::to_file(QString filename)
255 std::ofstream outf(filename.toAscii());
256 if (!outf) {
257 DEBUG("Couldn't open " << filename << " for writing\n");
258 exit(1);
260 outf << *this ;
263 List::operator QString() {return QString("XML here");} // a ustring representation of a List (XML)
265 // an ostream representation of a List (XML)
266 std::ostream& operator <<(std::ostream &out, const List &l)
268 DEBUG("in << operator\n");
269 XMLWriter w(&l);
270 DEBUG("written XML\n");
271 return w.write(out);
274 void List::add(Item &it)
276 if (categories.empty()) {
277 cerr << "Adding an item but there are no categories\n";
278 exit(1); // FIXME - is this a no-op or an exception or...
280 DEBUG("Add item " << it.get_desc() << "\n");
281 items.push_back(&it);
282 it.get_category()->add(it);
283 emit item_list_changed();
285 void List::rm(Item &it)
287 DEBUG("Remove item " << it.get_desc() << "\n");
288 items.remove(&it);
289 it.get_category()->rm(it);
290 emit item_list_changed();
291 delete &it;
294 void List::add(Category &cat)
296 DEBUG("Add category " << cat.get_name());
297 if (categories.empty())
299 DEBUG(" made active\n");
300 categories.push_back(&cat);
301 make_category_active(cat);
302 } else {
303 list<Category*>::iterator low = lower_bound(categories.begin(),categories.end(), &cat, &cat_cmp);
304 categories.insert(low, &cat);
305 resequence();
307 emit category_list_changed();
309 void List::rm(Category &cat)
311 if (categories.size() == 1) return;
312 DEBUG("Removing category\n");
313 if (is_category_active(cat)) cycle_active_category(true);
314 for(list<Item*>::iterator itemI = cat.items.begin();
315 itemI != cat.items.end(); itemI=cat.items.erase(itemI)) {
316 (**itemI).dbg();
317 DEBUG("Removing an item from list");
318 rm(**itemI);
320 categories.remove(&cat);
321 resequence();
322 emit category_list_changed();
323 delete &cat;
326 // accessors
327 QString List::get_name(){return name;}
328 void List::set_name(QString)
330 emit changed();
332 sState List::get_state(){
333 return state;
335 void List::set_state(sState s)
337 if (state != s){
338 state = s;
339 emit state_changed();
343 // Category filtering
344 Category* List::get_active_category()
346 return(active_category);
348 bool List::is_category_active(Category &c) // empty set = all active
350 return (active_category == NULL || active_category == &c);
351 // If using a set for multiple active categories
352 // return (active_categories.empty() || ( active_categories.find(c) != active_categories.end() ));;
354 void List::cycle_active_category(bool f)
356 List::pCategoryIter begin = categories.begin();
357 List::pCategoryIter end = categories.end();
358 if (active_category == NULL) { // use first or last element as 'next'
359 active_category = f ? *(begin) : *(categories.rbegin());
360 } else {
361 List::pCategoryIter c =
362 find(begin, end, active_category);
363 if (c == end) {
364 DEBUG("Not found - shouldn't happen\n");
365 exit(1);
367 if (f) {
368 active_category = (++c == end)?NULL:*c;
369 } else {
370 active_category = (c == begin)?NULL:*--c;
375 void List::make_next_category_active()
377 cycle_active_category(true);
378 emit active_category_changed();
380 void List::make_prev_category_active()
382 cycle_active_category(false);
383 emit active_category_changed();
385 void List::make_category_active(Category &c)
387 if (active_category != &c) {
388 active_category = &c;
389 // If using a set for multiple active categories
390 // active_categories.insert(c);
391 emit active_category_changed();
394 void List::make_category_inactive(Category &c)
396 if (active_category != NULL) {
397 active_category = NULL;
398 // If using a set for multiple active categories
399 // active_categories.erase(c);
400 emit active_category_changed();
403 void List::no_category_active()
405 if (active_category != NULL) {
406 active_category = NULL;
407 emit active_category_changed();
411 void List::resequence()
413 int id = 0;
414 for(list<Category*>::iterator catI = categories.begin();
415 catI != categories.end(); ++catI) {
416 (*catI)->set_id(id++);
419 void List::reorder()
421 categories.sort(&cat_cmp);
424 void List::swap_categories(Category *a, Category *b)
426 int a_id = a->get_id();
427 DEBUG("a id is : " << a->get_id()
428 << " b id is : " << b->get_id()
429 << "\n");
430 a->set_id(b->get_id());
431 DEBUG("Now set b\n");
432 b->set_id(a_id);
433 DEBUG("a id is : " << a->get_id()
434 << " b id is : " << b->get_id()
435 << "\n");
436 reorder();
437 DEBUG("Reorder done\n");
439 void List::swap_categories(int a, int b)
441 /*Category *c1 = cat_by_id[a];
442 Category *c2 = cat_by_id[b];
443 c1->set_id(b);
444 c2->set_id(a);
445 cat_by_id[a] = c2;
446 cat_by_id[b] = c1;
447 reorder();*/
450 QString List::modeText()
452 return modeText(state);
454 QString List::modeText(sState s)
456 switch (s) {
457 case WHATS_LEFT :
458 return(QString("What's Left"));
459 case OUT_SHOPPING:
460 return(QString("Full List"));
461 case MAKING_LIST:
462 return(QString("Making List"));
463 default:
464 return(QString("BAD State"));
467 List::pItemIter List::itemsI() { return items.begin(); }
468 List::pCategoryIter List::categoriesI(){ return categories.begin(); }
469 List::pItemIter List::itemsEnd() { return items.end(); }
470 List::pCategoryIter List::categoriesEnd(){ return categories.end(); }
472 void List::boughtNothing(){} // marks all items as not bought
473 void List::newList(){} // marks all items as not wanted