Updated bsconf to the latest from uSTL
[ttodo.git] / tddoc.cc
blobaa433c51a6eb00c36ee3ac23c7355e9cfd34b60a
1 // This file is part of a terminal todo application.
2 //
3 // Copyright (C) 2006 by Mike Sharov <msharov@users.sourceforge.net>
4 // This file is free software, distributed under the MIT License.
5 //
6 // tddoc.cc
7 //
9 #include "tddoc.h"
10 #include <unistd.h>
11 #if HAVE_IFF_H
12 #include <iff.h>
13 #else
14 #error "libiff-0.1.0 or later is required. http://iff.sourceforge.net"
15 #endif
16 #if !defined(IFF_VERSION) || IFF_VERSION < 0x010
17 #error "libiff-0.1.0 or later is required. http://iff.sourceforge.net"
18 #endif
20 //----------------------------------------------------------------------
22 /// Default constructor.
23 CTodoDocument::CTodoDocument (void)
24 : CDocument (),
25 m_Todos (),
26 m_Deps ()
28 m_Todos.insert (CTodoItem());
31 //--{ Internal data accessors }-----------------------------------------
33 /// Looks up item \p id in m_Todos, returns NULL if not found.
34 CTodoDocument::icitem_t CTodoDocument::FindItem (itemid_t id) const
36 icitem_t i = m_Todos.find (id);
37 return (i == m_Todos.end() ? NULL : i);
40 /// Links \p m to the dependency list for \p id.
41 void CTodoDocument::ItemDeps (itemid_t id, tddepmap_t& m) const
43 pair<icdep_t, icdep_t> dr = equal_range (m_Deps, CTodoDep(id));
44 m.link (dr.first, dr.second);
47 /// Returns the next available item id.
48 inline CTodoDocument::itemid_t CTodoDocument::GetNextItemId (void) const
50 return (m_Todos.back().Id() + 1);
53 //--{ Serialization }---------------------------------------------------
55 enum {
56 fmt_TodoList = IFF_FMT('T','O','D','O'),
57 fmt_Options = IFF_FMT('O','P','T','S'),
58 fmt_Item = IFF_FMT('T','I','T','M'),
59 fmt_Dep = IFF_FMT('T','D','E','P')
62 static const uint32_t c_CurrentVersion = 2;
64 //----------------------------------------------------------------------
66 class CTodoHeader {
67 public:
68 inline CTodoHeader (void) : m_Version (c_CurrentVersion), m_Flags (0), m_Reserved (0) { }
69 inline void read (istream& is) { is >> m_Version >> m_Flags >> m_Reserved; }
70 inline void write (ostream& os) const { os << m_Version << m_Flags << m_Reserved; }
71 inline size_t stream_size (void) const { return (stream_size_of (m_Version) + stream_size_of (m_Flags) + stream_size_of(m_Reserved)); }
72 public:
73 uint32_t m_Version; ///< File format's version number.
74 bitset<32> m_Flags; ///< Various flags. None for now.
75 uint32_t m_Reserved; ///< Future functionality.
78 STD_STREAMABLE (CTodoHeader)
80 /// Reads the object from stream \p is.
81 void CTodoDocument::read (istream& is)
83 iff::SkipChunk (is, fmt_Options);
84 iff::ReadVector (is, m_Todos, fmt_Item);
85 iff::ReadVector (is, m_Deps, fmt_Dep);
86 VerifyData();
89 /// Writes the object to stream \p os.
90 void CTodoDocument::write (ostream& os) const
92 iff::WriteChunk (os, CTodoHeader(), fmt_Options);
93 iff::WriteVector (os, m_Todos, fmt_Item);
94 iff::WriteVector (os, m_Deps, fmt_Dep);
97 /// Returns the size of the written object.
98 size_t CTodoDocument::stream_size (void) const
100 return (iff::chunk_size_of (CTodoHeader()) +
101 iff::vector_size_of (m_Todos) +
102 iff::vector_size_of (m_Deps));
105 /// Opens \p filename and reads todo entries from it.
106 void CTodoDocument::Open (const string& filename)
108 CDocument::Open (filename);
109 if (access (filename, F_OK)) // Check if it's a new document.
110 return;
111 if (access (filename, W_OK)) // Check if it is read-only.
112 SetFlag (f_ReadOnly);
113 memblock buf;
114 buf.read_file (filename);
115 istream is (buf);
116 iff::ReadFORM (is, *this, fmt_TodoList);
117 SetFlag (f_Changed, false);
118 UpdateAllViews();
121 /// Saves the data to the currently open file.
122 void CTodoDocument::Save (void)
124 if (Flag (f_ReadOnly) && access (Filename(), W_OK)) {
125 MessageBox ("Can't save: this file is marked as read-only");
126 return;
128 memblock buf (iff::form_size_of (*this));
129 ostream os (buf);
130 iff::WriteFORM (os, *this, fmt_TodoList);
131 buf.write_file (Filename());
132 SetFlag (f_Changed, false);
133 UpdateAllViews();
136 /// Verifies and corrects any defects in the data.
137 void CTodoDocument::VerifyData (void)
139 foreach (tddepmap_t::iterator, i, m_Deps) {
140 if (!FindItem (i->m_ItemId) || !FindItem (i->m_DepId)) {
141 assert (!"Found a dependency pointing to a nonexistent item!");
142 --(i = m_Deps.erase (i));
147 /// Returns the true if \p i1 ought to appear before \p i2 in the visible list.
148 bool CTodoDocument::VisibleOrderLess (const CTodoDep& d1, const CTodoDep& d2) const
150 const icitem_t i1 (FindItem (d1.m_DepId)), i2 (FindItem (d2.m_DepId));
151 // Sort to put completed items at the end, then sort by priority, and then by creation date.
152 return (i1->Complete() < i2->Complete() ||
153 (!i1->Complete() && !i2->Complete() && (i1->Priority() < i2->Priority() ||
154 (i1->Priority() == i2->Priority() && *i1 < *i2))) ||
155 (i1->Complete() && i2->Complete() && (i1->Done() > i2->Done() ||
156 (i1->Done() == i2->Done() && *i1 < *i2))));
159 /// Reorders the items in the list by canonical sort order.
160 void CTodoDocument::ResortItemDeps (idep_t first, idep_t last) const
162 for (idep_t j, i = first; ++i < last;) {
163 for (j = i; j-- > first && !VisibleOrderLess (*j, *i););
164 rotate (++j, i, i + 1);
168 //--{ Item progress recursive updating }--------------------------------
170 /// Creates a new item and returns its id.
171 CTodoDocument::itemid_t CTodoDocument::CreateItem (void)
173 CTodoItem e (GetNextItemId());
174 assert (!FindItem (e.Id()));
175 // To the complete list
176 m_Todos.insert (e);
177 SetFlag (f_Changed);
178 return (e.Id());
181 /// Makes item \p id a dependency of \p parent item.
182 void CTodoDocument::LinkItem (itemid_t id, itemid_t parent)
184 iitem_t iItem = FindItem (id), iParent = FindItem (parent);
185 if (!iParent | !iItem)
186 return;
187 // m_Deps is sorted by parent, but not by < (because < requires accessing m_Todos)
188 idep_t ip = upper_bound (m_Deps, CTodoDep (parent));
189 m_Deps.insert (ip, CTodoDep (parent, id));
190 // Update the new item's status.
191 iItem->AddRef();
192 iParent->SetHasSublist (true);
193 UpdateCompleteStatus (id);
194 // Tell the world
195 SetFlag (f_Changed);
196 UpdateAllViews();
199 /// Removes one reference the given item.
200 void CTodoDocument::UnlinkItem (icdep_t id)
202 assert (id >= m_Deps.begin() && id < m_Deps.end() && "UnlinkItem takes an iterator from the vector set by ItemDeps");
203 iitem_t ii = FindItem (id->m_DepId); // Cache master list iterator while we still have the item to look for.
204 // Remove from dependency list.
205 m_Deps.erase (const_cast<idep_t>(id));
206 pair<idep_t, idep_t> r = equal_range (m_Deps, CTodoDep(ii->Id()));
207 if (r.first == r.second) // Can't be cleanly done in UpdateItemProgress.
208 FindItem (id->m_ItemId)->SetHasSublist (false);
209 // Update the item in the master list.
210 if (!ii->DelRef()) // If no more refs, then delete.
211 m_Todos.erase (ii);
212 UpdateItemProgress (r.first, r.second);
213 // Update visible list.
214 SetFlag (f_Changed);
215 UpdateAllViews();
218 /// Updates item \p v in the master list.
219 void CTodoDocument::UpdateItem (rcitem_t v)
221 iitem_t i = FindItem (v.Id());
222 assert (i && "Update item must only be called with already existent items. Call AppendItem to create new ones.");
223 *i = v;
224 UpdateCompleteStatus (v.Id());
225 // Tell the world
226 SetFlag (f_Changed);
227 UpdateAllViews();
230 /// Updates the progress of the item given by dependency range [first, last)
231 void CTodoDocument::UpdateItemProgress (idep_t first, idep_t last)
233 const uint32_t nItems = distance (first, last);
234 if (!nItems)
235 return;
236 ResortItemDeps (first, last);
237 const itemid_t rangeId (first->m_ItemId);
238 uint32_t progress = 0;
239 for (; first < last; ++first)
240 progress += FindItem(first->m_DepId)->Progress();
241 // +1 treats the parent item as part of the list.
242 progress = min (progress / (nItems + 1), 100U);
243 iitem_t ii = FindItem (rangeId);
244 if (!ii || ii->Progress() == progress)
245 return;
246 ii->MarkComplete (progress);
247 UpdateCompleteStatus (rangeId); // Recurse to propagate the change up.
250 /// Updates progress values of item dependent on \p dep
251 void CTodoDocument::UpdateCompleteStatus (itemid_t dep)
253 // Each item has a range of dependencies; ranges are sequential in m_Deps.
254 idep_t rangeStart = m_Deps.begin(); // The start of the current range.
255 const idep_t iEnd = m_Deps.end();
256 bool hasDep = false; // Does current range have dep?
257 for (idep_t i = rangeStart; i < iEnd; ++i) {
258 if (i->m_ItemId != rangeStart->m_ItemId) {
259 if (hasDep)
260 UpdateItemProgress (rangeStart, i);
261 rangeStart = i;
262 hasDep = false;
264 hasDep = hasDep || i->m_DepId == dep;
266 if (hasDep)
267 UpdateItemProgress (rangeStart, iEnd);