Moved List state handling into the data space to allow consistent behaviour.
[shopper.git] / src / List.cc
blob5d10db92b061cc5b2bcda2ffe49006c67dbc0f5d
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
21 #define DEBUG_SHOPPER 1
23 #include "shopper.h"
25 #include <iostream>
26 #include <sstream>
27 #include <string>
28 #include <fstream>
29 #include <list>
30 #include <set>
31 #include <algorithm>
32 #include <signal.h>
33 #include "shopperList.h"
35 using namespace std;
37 namespace Shopper
39 List::List() :
40 name("Sainsburys"),
41 state(MAKING_LIST),
42 active_category(NULL)
44 // No items, no categories
45 DEBUG("\n\n\nMAKING NEW LIST\n\n\n");
47 List::List(QString dump)
49 DEBUG("\n\n\nMAKING NEW LIST FROM STRING\n\n\n");
50 ListParser parse(this);
51 parse.from_string(dump);
52 } // initiate from a dump
54 List::~List()
56 for(list<Category*>::iterator catI = categories.begin();
57 catI != categories.end(); ++catI) {
58 delete *catI;
62 List* List::from_file(QString filename)
64 // open file
65 std::ifstream file(filename.toAscii());
66 if (!file) {
67 return NULL;
69 // Read xml as a stream
70 std::stringstream xml;
71 xml << file.rdbuf();
72 std::string sxml(xml.str());
73 QString qxml(sxml.c_str());
74 DEBUG("read from : " <<filename <<"\n"<<qxml);
75 // Create new list and return it
76 return new Shopper::List(qxml); // FIXME: may not succeed.
79 void List::to_file(QString filename)
81 std::ofstream outf(filename.toAscii());
82 if (!outf) {
83 DEBUG("Couldn't open " << filename << " for writing\n");
84 exit(1);
86 outf << *this ;
89 List::operator QString() {return QString("XML here");} // a ustring representation of a List (XML)
91 // an ostream representation of a List (XML)
92 std::ostream& operator <<(std::ostream &out, const List &l)
94 DEBUG("in << operator\n");
95 XMLWriter w(&l);
96 DEBUG("written XML\n");
97 return w.write(out);
100 // These are now just helper functions
101 void List::add(Item &it)
103 it.get_category()->add(it);
105 void List::rm(Item &it)
107 it.get_category()->rm(it);
110 void List::add(Category &cat)
112 DEBUG("Add category " << cat.get_name());
113 if (categories.empty())
115 DEBUG(" made active\n");
116 categories.push_back(&cat);
117 make_category_active(cat);
118 } else {
119 list<Category*>::iterator low = lower_bound(categories.begin(),categories.end(), &cat, &cat_cmp);
120 categories.insert(low, &cat);
121 resequence();
123 // Allows the list to emit signals when Items are added/removed
124 connect (&cat, SIGNAL(item_added(Item*)),
125 this, SIGNAL(item_added(Item*)));
126 connect (&cat, SIGNAL(item_removed(Item*)),
127 this, SIGNAL(item_removed(Item*)));
128 emit category_list_changed();
129 emit category_added(&cat);
131 void List::rm(Category &cat)
133 if (categories.size() == 1) return;
134 DEBUG("Removing category\n");
135 if (is_category_active(cat)) cycle_active_category(true);
136 categories.remove(&cat);
137 resequence();
138 emit category_removed(&cat);
139 emit category_list_changed();
140 delete &cat;
143 // accessors
144 QString List::get_name() const {return name;}
145 void List::set_name(QString)
147 emit changed();
149 sState List::get_state() const {
150 return state;
152 void List::set_state(sState s)
154 if (state == s) return;
156 sState old = state;
157 state = s; // Set the state
158 emit state_changed();
160 if (s == Shopper::MAKING_LIST) { // And go to category '1'
161 if (active_category == NULL) { // show category 1 rather than 'Everything'
162 make_category_active(*(categories.front()));
165 // And go to category 'Everything' if switching to an 'out shopping' state.
166 if (s == Shopper::WHATS_LEFT || s == Shopper::OUT_SHOPPING) {
167 if (old == Shopper::MAKING_LIST) no_category_active();
171 // Category filtering
172 Category* List::get_active_category()
174 return(active_category);
176 bool List::is_category_active(Category &c) const // empty set = all active
178 return (active_category == NULL || active_category == &c);
179 // If using a set for multiple active categories
180 // return (active_categories.empty() || ( active_categories.find(c) != active_categories.end() ));;
182 void List::cycle_active_category(bool f)
184 List::pCategoryIter begin = categories.begin();
185 List::pCategoryIter end = categories.end();
186 if (active_category == NULL) { // use first or last element as 'next'
187 active_category = f ? *(begin) : *(categories.rbegin());
188 } else {
189 List::pCategoryIter c =
190 find(begin, end, active_category);
191 if (c == end) {
192 DEBUG("Not found - shouldn't happen\n");
193 exit(1);
195 if (f) {
196 active_category = (++c == end)?NULL:*c;
197 } else {
198 active_category = (c == begin)?NULL:*--c;
203 void List::make_next_category_active()
205 cycle_active_category(true);
206 emit active_category_changed();
208 void List::make_prev_category_active()
210 cycle_active_category(false);
211 emit active_category_changed();
213 void List::make_category_active(Category &c)
215 if (active_category != &c) {
216 active_category = &c;
217 // If using a set for multiple active categories
218 // active_categories.insert(c);
219 emit active_category_changed();
222 void List::make_category_inactive(Category &c)
224 if (active_category != NULL) {
225 active_category = NULL;
226 // If using a set for multiple active categories
227 // active_categories.erase(c);
228 emit active_category_changed();
231 void List::no_category_active()
233 if (active_category != NULL) {
234 active_category = NULL;
235 emit active_category_changed();
239 void List::resequence()
241 int id = 0;
242 for(list<Category*>::iterator catI = categories.begin();
243 catI != categories.end(); ++catI) {
244 (*catI)->set_id(id++);
247 void List::reorder()
249 categories.sort(&cat_cmp);
252 void List::swap_categories(Category *a, Category *b)
254 int a_id = a->get_id();
255 DEBUG("a id is : " << a->get_id()
256 << " b id is : " << b->get_id()
257 << "\n");
258 a->set_id(b->get_id());
259 DEBUG("Now set b\n");
260 b->set_id(a_id);
261 DEBUG("a id is : " << a->get_id()
262 << " b id is : " << b->get_id()
263 << "\n");
264 reorder();
265 DEBUG("Reorder done\n");
267 void List::swap_categories(int a, int b)
269 /*Category *c1 = cat_by_id[a];
270 Category *c2 = cat_by_id[b];
271 c1->set_id(b);
272 c2->set_id(a);
273 cat_by_id[a] = c2;
274 cat_by_id[b] = c1;
275 reorder();*/
278 QString List::modeText()
280 return modeText(state);
282 QString List::modeText(sState s)
284 switch (s) {
285 case WHATS_LEFT :
286 return(QString("What's Left"));
287 case OUT_SHOPPING:
288 return(QString("Full List"));
289 case MAKING_LIST:
290 return(QString("Making List"));
291 default:
292 return(QString("BAD State"));
295 // List::pItemIter List::itemsI() { return items.begin(); }
296 List::pCategoryIter List::categoriesI(){ return categories.begin(); }
297 // List::pItemIter List::itemsEnd() { return items.end(); }
298 List::pCategoryIter List::categoriesEnd(){ return categories.end(); }
300 void List::boughtNothing(){} // marks all items as not bought
301 void List::newList(){} // marks all items as not wanted
303 void List::clear(clear_list_mode m)
305 for (List::pCategoryIter c = categories.begin(); c != categories.end(); c++) {
306 DEBUG("Clearing : "<<(*c)->get_name());
307 for (Category::pItemIter it = (*c)->items.begin(); it != (*c)->items.end(); it++) {
308 (*it)->set_bought(false); // Always clear bought
309 if (m == CLEAR_WANTED) {
310 (*it)->set_wanted(false);
312 DEBUG("Cleared : "<<(*it)->get_desc());
315 if (m == CLEAR_WANTED) {
316 set_state(Shopper::MAKING_LIST);