Task 2.0.0 Code Salvage

This commit is contained in:
Paul Beckingham 2010-09-09 20:49:17 -04:00
parent 7f54b89f24
commit 73ff6ea973
29 changed files with 2713 additions and 4 deletions

View file

@ -1,7 +1,7 @@
////////////////////////////////////////////////////////////////////////////////
// taskwarrior - a command line task list manager.
//
// Copyright 2006 - 2009, Paul Beckingham.
// Copyright 2006 - 2010, Paul Beckingham.
// All rights reserved.
//
// This program is free software; you can redistribute it and/or modify it under
@ -125,6 +125,16 @@ size_t File::size () const
return 0;
}
////////////////////////////////////////////////////////////////////////////////
time_t File::mtime () const
{
struct stat s;
if (!stat (data.c_str (), &s))
return s.st_mtime;
return 0;
}
////////////////////////////////////////////////////////////////////////////////
bool File::create (const std::string& name)
{

View file

@ -1,7 +1,7 @@
////////////////////////////////////////////////////////////////////////////////
// taskwarrior - a command line task list manager.
//
// Copyright 2006 - 2009, Paul Beckingham.
// Copyright 2006 - 2010, Paul Beckingham.
// All rights reserved.
//
// This program is free software; you can redistribute it and/or modify it under
@ -62,6 +62,7 @@ public:
virtual mode_t mode ();
virtual size_t size () const;
virtual time_t mtime () const;
static bool create (const std::string&);
static std::string read (const std::string&);

View file

@ -50,6 +50,22 @@ int main (int argc, char** argv)
{
context.initialize (argc, argv);
/* From 2.0.0
std::string::size_type task = context.program.find ("/task");
std::string::size_type t = context.program.find ("/t");
std::string::size_type cal = context.program.find ("/cal");
if (context.program != "task" &&
context.program != "t" &&
context.program != "cal" &&
(task == std::string::npos || context.program.length () != task + 5) &&
(t == std::string::npos || context.program.length () != t + 2) &&
(cal == std::string::npos || context.program.length () != cal + 4))
status = context.handleInteractive ();
else
status = context.run ();
*/
std::string::size_type itask = context.program.find ("/itask");
if (context.program == "itask" ||
(itask != std::string::npos && context.program.length () == itask + 5))

138
src/ui/Clock.cpp Normal file
View file

@ -0,0 +1,138 @@
////////////////////////////////////////////////////////////////////////////////
// task - a command line task list manager.
//
// Copyright 2006 - 2010, Paul Beckingham.
// All rights reserved.
//
// This program is free software; you can redistribute it and/or modify it under
// the terms of the GNU General Public License as published by the Free Software
// Foundation; either version 2 of the License, or (at your option) any later
// version.
//
// This program is distributed in the hope that it will be useful, but WITHOUT
// ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
// FOR A PARTICULAR PURPOSE. See the GNU General Public License for more
// details.
//
// You should have received a copy of the GNU General Public License along with
// this program; if not, write to the
//
// Free Software Foundation, Inc.,
// 51 Franklin Street, Fifth Floor,
// Boston, MA
// 02110-1301
// USA
//
////////////////////////////////////////////////////////////////////////////////
#include <string.h>
#include <unistd.h>
#include <signal.h>
#include <time.h>
#include "log.h"
#include "Clock.h"
#include "Context.h"
// Constriction point for ncurses calls.
extern pthread_mutex_t conch;
extern Context context;
static int refreshDelay; // Override with ui.clock.refresh
////////////////////////////////////////////////////////////////////////////////
// This thread sleeps and updates the time every second.
static void tick (int* arg)
{
Clock* c = (Clock*) arg;
while (c)
{
sleep (refreshDelay);
// If the element is deinitialized the window goes away, but the thread does
// not. Protect against an attempted redraw of a dead window.
if (c->window != NULL)
c->redraw ();
}
}
////////////////////////////////////////////////////////////////////////////////
// Create thread to track time.
Clock::Clock ()
{
refreshDelay = context.config.getInteger ("ui.clock.refresh");
logWrite ("Clock::Clock refreshDelay=%d", refreshDelay);
pthread_create (&ticker, NULL, (void*(*)(void*)) tick, (void*) this);
}
////////////////////////////////////////////////////////////////////////////////
Clock::~Clock ()
{
pthread_kill (ticker, SIGQUIT);
}
////////////////////////////////////////////////////////////////////////////////
void Clock::redraw ()
{
logWrite ("Clock::redraw");
time_t now;
time (&now);
struct tm* t = localtime (&now);
// TODO Load these from config.
const char* formats[] =
{
// These should remain sorted from longest
// to shortest, in their expanded form,
// shown below.
" %A %e %B %Y, wk%U, %H:%M:%S %z ", // " Sunday 18 October 2009, wk42, 22:42:39 -0400 "
" %A %e %B %Y, wk%U, %H:%M:%S %Z ", // " Sunday 18 October 2009, wk42, 22:42:39 EDT "
" %A %e %B %Y wk%U, %H:%M:%S %Z ", // " Sunday 18 October 2009 wk42, 22:42:39 EDT "
" %A %e %B %Y wk%U %H:%M:%S %Z ", // " Sunday 18 October 2009 wk42 22:42:39 EDT "
" %A %e %B %Y, wk%U, %H:%M:%S ", // " Sunday 18 October 2009, wk42, 22:42:39 "
" %A %e %B %Y, wk%U, %H:%M:%S ", // " Sunday 18 October 2009, wk42, 22:42:39 "
" %A %e %B %Y wk%U, %H:%M:%S ", // " Sunday 18 October 2009 wk42, 22:42:39 "
" %A %e %B %Y wk%U %H:%M:%S ", // " Sunday 18 October 2009 wk42 22:42:39 "
" %A %e %b %Y,wk%U, %H:%M:%S ", // " Sunday 18 Oct 2009, wk42, 22:42:39 "
" %A %e %b %Y wk%U, %H:%M:%S ", // " Sunday 18 Oct 2009 wk42, 22:42:39 "
" %A %e %b %Y wk%U %H:%M:%S ", // " Sunday 18 Oct 2009 wk42 22:42:39 "
" %a %e %b %Y, wk%U, %H:%M:%S ", // " Sun 18 Oct 2009, wk42, 22:42:39 "
" %a %e %b %Y wk%U, %H:%M:%S ", // " Sun 18 Oct 2009 wk42, 22:42:39 "
" %a %e %b %Y, %H:%M:%S ", // " Sun 18 Oct 2009, 22:42:39 "
" %a %e %b %Y %H:%M:%S ", // " Sun 18 Oct 2009 22:42:39 "
" %a %e %b, %H:%M:%S ", // " Sun 18 Oct, 22:42:39 "
" %a %e %b %H:%M:%S ", // " Sun 18 Oct 22:42:39 "
" %e %b, %H:%M:%S ", // " 18 Oct, 22:42:39 "
" %e %b %H:%M:%S ", // " 18 Oct 22:42:39 "
" %H:%M:%S ", // " 22:42:39 "
"%H:%M:%S", // "22:42:39"
" %H:%M ", // " 22:42 "
"%H:%M", // "22:42"
"",
};
// TODO %U (Sun), $W (Mon)
// TODO i18n "wk"
// Render them all, but only keep the longest one that fits.
std::string keeper = "";
char buffer[128];
for (int i = 0; formats[i][0]; ++i)
{
strftime (buffer, 128, formats[i], t);
size_t len = strlen (buffer);
if (len <= (size_t) width && len > keeper.length ())
keeper = buffer;
}
pthread_mutex_lock (&conch);
wbkgd (window, COLOR_PAIR (5));
wbkgdset (window, COLOR_PAIR(5) | A_DIM);
mvwaddstr (window, 0, width - keeper.length (), keeper.c_str ());
wrefresh (window);
pthread_mutex_unlock (&conch);
}
////////////////////////////////////////////////////////////////////////////////

49
src/ui/Clock.h Normal file
View file

@ -0,0 +1,49 @@
////////////////////////////////////////////////////////////////////////////////
// task - a command line task list manager.
//
// Copyright 2006 - 2010, Paul Beckingham.
// All rights reserved.
//
// This program is free software; you can redistribute it and/or modify it under
// the terms of the GNU General Public License as published by the Free Software
// Foundation; either version 2 of the License, or (at your option) any later
// version.
//
// This program is distributed in the hope that it will be useful, but WITHOUT
// ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
// FOR A PARTICULAR PURPOSE. See the GNU General Public License for more
// details.
//
// You should have received a copy of the GNU General Public License along with
// this program; if not, write to the
//
// Free Software Foundation, Inc.,
// 51 Franklin Street, Fifth Floor,
// Boston, MA
// 02110-1301
// USA
//
////////////////////////////////////////////////////////////////////////////////
#ifndef INCLUDED_CLOCK
#define INCLUDED_CLOCK
#include <pthread.h>
#include "Element.h"
class Clock : public Element
{
public:
Clock ();
Clock (const Clock&);
Clock& operator= (const Clock&);
~Clock ();
void redraw ();
private:
pthread_t ticker;
};
#endif
////////////////////////////////////////////////////////////////////////////////

152
src/ui/Element.cpp Normal file
View file

@ -0,0 +1,152 @@
////////////////////////////////////////////////////////////////////////////////
// task - a command line task list manager.
//
// Copyright 2006 - 2010, Paul Beckingham.
// All rights reserved.
//
// This program is free software; you can redistribute it and/or modify it under
// the terms of the GNU General Public License as published by the Free Software
// Foundation; either version 2 of the License, or (at your option) any later
// version.
//
// This program is distributed in the hope that it will be useful, but WITHOUT
// ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
// FOR A PARTICULAR PURPOSE. See the GNU General Public License for more
// details.
//
// You should have received a copy of the GNU General Public License along with
// this program; if not, write to the
//
// Free Software Foundation, Inc.,
// 51 Franklin Street, Fifth Floor,
// Boston, MA
// 02110-1301
// USA
//
////////////////////////////////////////////////////////////////////////////////
#include <stdlib.h>
#include <pthread.h>
#include "log.h"
#include "Element.h"
#include "Report.h"
#include "ReportStats.h"
#include "Title.h"
#include "Clock.h"
#include "Keys.h"
#include "Message.h"
#include "Stats.h"
// Constriction point for ncurses calls.
extern pthread_mutex_t conch;
////////////////////////////////////////////////////////////////////////////////
// Factory for Element construction, based on spec.
Element* Element::factory (const std::string& name)
{
// logWrite ("Element::factory %s", name.c_str ());
Element* e;
if (name == "report") e = new Report (); // TODO Remove.
else if (name == "report.stats") e = new ReportStats ();
else if (name == "title") e = new Title ();
else if (name == "clock") e = new Clock ();
else if (name == "keys") e = new Keys ();
else if (name == "message") e = new Message ();
else if (name == "stats") e = new Stats ();
else
throw std::string ("Unrecognized element type: '") + name + "'";
e->type = name;
return e;
}
////////////////////////////////////////////////////////////////////////////////
Element::Element ()
: left (0)
, top (0)
, width (0)
, height (0)
, type ("")
, window (NULL)
{
// logWrite ("Element::Element");
}
////////////////////////////////////////////////////////////////////////////////
Element::~Element ()
{
deinitialize ();
}
////////////////////////////////////////////////////////////////////////////////
// When a layout is selected, all the elements are initialized, which means the
// window is created.
void Element::initialize ()
{
// logWrite ("Element::initialize %s", type.c_str ());
window = newwin (height, width, top, left);
}
////////////////////////////////////////////////////////////////////////////////
// When a layout is switched out, the elements are deinitialized, which means
// the window is deleted. The original specs are preserved, and ready for a
// possible later initialize if the layout is switched in.
void Element::deinitialize ()
{
// logWrite ("Element::deinitialize %s", type.c_str ());
if (window)
{
pthread_mutex_lock (&conch);
delwin (window);
pthread_mutex_unlock (&conch);
window = NULL;
}
}
////////////////////////////////////////////////////////////////////////////////
// Notification of new size for stdscr, triggering a size recalc.
void Element::recalc (int l, int t, int w, int h)
{
left = l;
top = t;
width = w;
height = h;
logWrite ("Element::recalc %-7s [l%d,t%d,w%d,h%d]", type.c_str (), left, top, width, height);
}
////////////////////////////////////////////////////////////////////////////////
void Element::relocate ()
{
// logWrite ("Element::relocate %s", type.c_str ());
if (window)
{
pthread_mutex_lock (&conch);
mvwin (window, top, left);
wresize (window, height, width);
pthread_mutex_unlock (&conch);
logWrite ("Element::relocate %-7s [l%d,t%d,w%d,h%d]", type.c_str (), left, top, height, width);
}
}
////////////////////////////////////////////////////////////////////////////////
// Default event handler for Element.
bool Element::event (int e)
{
// logWrite ("Element::event %s %d", type.c_str (), e);
return false;
}
////////////////////////////////////////////////////////////////////////////////
// Default repaint handler for Element.
void Element::redraw ()
{
logWrite ("Element::redraw %s", type.c_str ());
}
////////////////////////////////////////////////////////////////////////////////

64
src/ui/Element.h Normal file
View file

@ -0,0 +1,64 @@
////////////////////////////////////////////////////////////////////////////////
// task - a command line task list manager.
//
// Copyright 2006 - 2010, Paul Beckingham.
// All rights reserved.
//
// This program is free software; you can redistribute it and/or modify it under
// the terms of the GNU General Public License as published by the Free Software
// Foundation; either version 2 of the License, or (at your option) any later
// version.
//
// This program is distributed in the hope that it will be useful, but WITHOUT
// ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
// FOR A PARTICULAR PURPOSE. See the GNU General Public License for more
// details.
//
// You should have received a copy of the GNU General Public License along with
// this program; if not, write to the
//
// Free Software Foundation, Inc.,
// 51 Franklin Street, Fifth Floor,
// Boston, MA
// 02110-1301
// USA
//
////////////////////////////////////////////////////////////////////////////////
#ifndef INCLUDED_ELEMENT
#define INCLUDED_ELEMENT
#include <string>
#include <ncurses.h>
class Layout;
class Element
{
public:
static Element* factory (const std::string&);
Element ();
Element (const Element&); // Unimplemented
Element& operator= (const Element&); // Unimplemented
virtual ~Element ();
virtual void initialize ();
virtual void deinitialize ();
virtual void recalc (int, int, int, int);
virtual void relocate ();
virtual bool event (int);
virtual void redraw ();
public:
// The actual dimensions of the window. These change when stdscr resizes.
int left;
int top;
int width;
int height;
std::string type;
WINDOW* window;
};
#endif
////////////////////////////////////////////////////////////////////////////////

85
src/ui/Keys.cpp Normal file
View file

@ -0,0 +1,85 @@
////////////////////////////////////////////////////////////////////////////////
// task - a command line task list manager.
//
// Copyright 2006 - 2010, Paul Beckingham.
// All rights reserved.
//
// This program is free software; you can redistribute it and/or modify it under
// the terms of the GNU General Public License as published by the Free Software
// Foundation; either version 2 of the License, or (at your option) any later
// version.
//
// This program is distributed in the hope that it will be useful, but WITHOUT
// ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
// FOR A PARTICULAR PURPOSE. See the GNU General Public License for more
// details.
//
// You should have received a copy of the GNU General Public License along with
// this program; if not, write to the
//
// Free Software Foundation, Inc.,
// 51 Franklin Street, Fifth Floor,
// Boston, MA
// 02110-1301
// USA
//
////////////////////////////////////////////////////////////////////////////////
#include <pthread.h>
#include "log.h"
#include "Keys.h"
// Constriction point for ncurses calls.
extern pthread_mutex_t conch;
////////////////////////////////////////////////////////////////////////////////
Keys::Keys ()
{
// logWrite ("Keys::Keys");
}
////////////////////////////////////////////////////////////////////////////////
Keys::~Keys ()
{
}
////////////////////////////////////////////////////////////////////////////////
void Keys::redraw ()
{
logWrite ("Keys::redraw");
pthread_mutex_lock (&conch);
wbkgd (window, COLOR_PAIR (1));
if (width >= 5)
{
wbkgdset (window, COLOR_PAIR(1) | A_BOLD);
mvwaddstr (window, 0, 0, " q");
wbkgdset (window, COLOR_PAIR(1));
mvwaddstr (window, 0, 2, "uit");
if (width >= 13)
{
wbkgdset (window, COLOR_PAIR(1) | A_BOLD);
mvwaddstr (window, 0, 5, " d");
wbkgdset (window, COLOR_PAIR(1));
mvwaddstr (window, 0, 7, "efault");
if (width >= 20)
{
wbkgdset (window, COLOR_PAIR(1) | A_BOLD);
mvwaddstr (window, 0, 13, " s");
wbkgdset (window, COLOR_PAIR(1));
mvwaddstr (window, 0, 15, "tats");
}
}
}
wrefresh (window);
pthread_mutex_unlock (&conch);
}
////////////////////////////////////////////////////////////////////////////////

48
src/ui/Keys.h Normal file
View file

@ -0,0 +1,48 @@
////////////////////////////////////////////////////////////////////////////////
// task - a command line task list manager.
//
// Copyright 2006 - 2010, Paul Beckingham.
// All rights reserved.
//
// This program is free software; you can redistribute it and/or modify it under
// the terms of the GNU General Public License as published by the Free Software
// Foundation; either version 2 of the License, or (at your option) any later
// version.
//
// This program is distributed in the hope that it will be useful, but WITHOUT
// ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
// FOR A PARTICULAR PURPOSE. See the GNU General Public License for more
// details.
//
// You should have received a copy of the GNU General Public License along with
// this program; if not, write to the
//
// Free Software Foundation, Inc.,
// 51 Franklin Street, Fifth Floor,
// Boston, MA
// 02110-1301
// USA
//
////////////////////////////////////////////////////////////////////////////////
#ifndef INCLUDED_KEYS
#define INCLUDED_KEYS
#include <string>
#include "Element.h"
class Keys : public Element
{
public:
Keys ();
Keys (const Keys&);
Keys& operator= (const Keys&);
~Keys ();
void redraw ();
private:
};
#endif
////////////////////////////////////////////////////////////////////////////////

172
src/ui/Layout.cpp Normal file
View file

@ -0,0 +1,172 @@
////////////////////////////////////////////////////////////////////////////////
// task - a command line task list manager.
//
// Copyright 2006 - 2010, Paul Beckingham.
// All rights reserved.
//
// This program is free software; you can redistribute it and/or modify it under
// the terms of the GNU General Public License as published by the Free Software
// Foundation; either version 2 of the License, or (at your option) any later
// version.
//
// This program is distributed in the hope that it will be useful, but WITHOUT
// ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
// FOR A PARTICULAR PURPOSE. See the GNU General Public License for more
// details.
//
// You should have received a copy of the GNU General Public License along with
// this program; if not, write to the
//
// Free Software Foundation, Inc.,
// 51 Franklin Street, Fifth Floor,
// Boston, MA
// 02110-1301
// USA
//
////////////////////////////////////////////////////////////////////////////////
#include <ncurses.h>
#include <stdlib.h>
#include <ctype.h>
#include <pthread.h>
#include "log.h"
#include "Rectangle.h"
#include "Layout.h"
// Constriction point for ncurses calls.
extern pthread_mutex_t conch;
////////////////////////////////////////////////////////////////////////////////
Layout::Layout (const std::string& spec)
: definition (spec)
{
logWrite ("Layout::Layout %s", spec.c_str ());
// Invoke the resize just to extract the element names.
void resize (const std::string&, const int, const int, std::string& name, std::map <std::string, Rectangle>&);
std::map <std::string, Rectangle> defs;
resize (definition, 80, 24, name, defs);
logWrite ("Layout::Layout name=%s", name.c_str ());
// Create an element for each panel in the definition.
std::map <std::string, Rectangle>::iterator it;
for (it = defs.begin (); it != defs.end (); ++it)
{
Element* e = Element::factory (it->first);
elements[e->type] = e;
}
}
////////////////////////////////////////////////////////////////////////////////
Layout::~Layout ()
{
std::map <std::string, Element*>::iterator it;
for (it = elements.begin (); it != elements.end (); ++it)
delete it->second;
elements.clear ();
}
////////////////////////////////////////////////////////////////////////////////
void Layout::initialize ()
{
logWrite ("Layout::initialize");
// Iterate over elements[] (specs) and create windows.
std::map <std::string, Element*>::iterator it;
for (it = elements.begin (); it != elements.end (); ++it)
it->second->initialize ();
}
////////////////////////////////////////////////////////////////////////////////
void Layout::deinitialize ()
{
logWrite ("Layout::deinitialize");
std::map <std::string, Element*>::iterator it;
for (it = elements.begin (); it != elements.end (); ++it)
it->second->deinitialize ();
// At this point the display should be stale but not cleared.
}
////////////////////////////////////////////////////////////////////////////////
bool Layout::event (int e)
{
switch (e)
{
case 12:
logWrite ("Layout::event Ctrl-L");
this->redraw (true);
return true; // handled.
break;
default:
logWrite ("Layout::event %d delegated", e);
{
std::map <std::string, Element*>::iterator it;
for (it = elements.begin (); it != elements.end (); ++it)
it->second->event (e);
}
return true; // handled.
break;
}
return false; // not handled.
}
////////////////////////////////////////////////////////////////////////////////
void Layout::recalc (int w, int h)
{
logWrite ("Layout::recalc %d,%d", w, h);
// Apply layout definition to [w,h], yielding rectangles for each element.
void resize (const std::string&, const int, const int, std::string& name, std::map <std::string, Rectangle>&);
std::string name;
std::map <std::string, Rectangle> defs;
resize (definition, w, h, name, defs);
// Relocate each element.
std::map <std::string, Rectangle>::iterator it;
for (it = defs.begin (); it != defs.end (); ++it)
{
Element* e = elements[it->first];
if (e)
{
e->recalc (
it->second.left,
it->second.top,
it->second.width,
it->second.height);
e->relocate ();
}
}
}
////////////////////////////////////////////////////////////////////////////////
// Redrawing the layout consists of touching and refreshing all the elements.
void Layout::redraw (bool force /* = false */)
{
logWrite ("Layout::redraw");
// This is the magic line that makes resize work. I wish I knew why.
refresh ();
std::map <std::string, Element*>::iterator it;
for (it = elements.begin (); it != elements.end (); ++it)
{
it->second->redraw ();
if (force)
{
pthread_mutex_lock (&conch);
touchwin (it->second->window);
pthread_mutex_unlock (&conch);
}
pthread_mutex_lock (&conch);
wrefresh (it->second->window);
pthread_mutex_unlock (&conch);
}
}
////////////////////////////////////////////////////////////////////////////////

57
src/ui/Layout.h Normal file
View file

@ -0,0 +1,57 @@
////////////////////////////////////////////////////////////////////////////////
// task - a command line task list manager.
//
// Copyright 2006 - 2010, Paul Beckingham.
// All rights reserved.
//
// This program is free software; you can redistribute it and/or modify it under
// the terms of the GNU General Public License as published by the Free Software
// Foundation; either version 2 of the License, or (at your option) any later
// version.
//
// This program is distributed in the hope that it will be useful, but WITHOUT
// ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
// FOR A PARTICULAR PURPOSE. See the GNU General Public License for more
// details.
//
// You should have received a copy of the GNU General Public License along with
// this program; if not, write to the
//
// Free Software Foundation, Inc.,
// 51 Franklin Street, Fifth Floor,
// Boston, MA
// 02110-1301
// USA
//
////////////////////////////////////////////////////////////////////////////////
#ifndef INCLUDED_LAYOUT
#define INCLUDED_LAYOUT
#include <map>
#include <vector>
#include <string>
#include "Element.h"
class Layout
{
public:
Layout (const std::string&);
Layout (const Layout&);
Layout& operator= (const Layout&);
~Layout ();
void initialize ();
void deinitialize ();
bool event (int);
void recalc (int, int);
void redraw (bool force = false);
private:
std::string definition;
std::string name;
std::map <std::string, Element*> elements;
};
#endif
////////////////////////////////////////////////////////////////////////////////

8
src/ui/Makefile.am Normal file
View file

@ -0,0 +1,8 @@
libtaskui_SHORTNAME = ui
noinst_LIBRARIES = libtaskui.a
libtaskui_a_SOURCES = UI.cpp Layout.cpp Element.cpp Clock.cpp Keys.cpp \
Message.cpp Report.cpp Stats.cpp Title.cpp resize.cpp \
ReportStats.cpp log.cpp \
Clock.h Element.h Keys.h Layout.h Message.h Report.h \
ReportStats.h Stats.h Title.h UI.h log.h
libtaskui_a_CPPFLAGS = -I../util -I..

73
src/ui/Message.cpp Normal file
View file

@ -0,0 +1,73 @@
////////////////////////////////////////////////////////////////////////////////
// task - a command line task list manager.
//
// Copyright 2006 - 2010, Paul Beckingham.
// All rights reserved.
//
// This program is free software; you can redistribute it and/or modify it under
// the terms of the GNU General Public License as published by the Free Software
// Foundation; either version 2 of the License, or (at your option) any later
// version.
//
// This program is distributed in the hope that it will be useful, but WITHOUT
// ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
// FOR A PARTICULAR PURPOSE. See the GNU General Public License for more
// details.
//
// You should have received a copy of the GNU General Public License along with
// this program; if not, write to the
//
// Free Software Foundation, Inc.,
// 51 Franklin Street, Fifth Floor,
// Boston, MA
// 02110-1301
// USA
//
////////////////////////////////////////////////////////////////////////////////
#include <pthread.h>
#include "log.h"
#include "Message.h"
// Constriction point for ncurses calls.
extern pthread_mutex_t conch;
////////////////////////////////////////////////////////////////////////////////
Message::Message ()
{
// logWrite ("Message::Message");
}
////////////////////////////////////////////////////////////////////////////////
Message::~Message ()
{
}
////////////////////////////////////////////////////////////////////////////////
void Message::redraw ()
{
logWrite ("Message::redraw");
// Truncate the message, if necessary.
std::string s = "Using database ~/.task";
if (width - 1 < (int) s.length ())
s = s.substr (0, width - 4) + "...";
// Need at least space for blank + 1 char + ellipsis.
if (width <= 5)
s = "";
pthread_mutex_lock (&conch);
wbkgd (window, COLOR_PAIR (5));
wbkgdset (window, COLOR_PAIR(5) | A_DIM);
if (width <= 5)
touchwin (window);
mvwaddstr (window, 0, 1, s.c_str ());
wrefresh (window);
pthread_mutex_unlock (&conch);
}
////////////////////////////////////////////////////////////////////////////////

48
src/ui/Message.h Normal file
View file

@ -0,0 +1,48 @@
////////////////////////////////////////////////////////////////////////////////
// task - a command line task list manager.
//
// Copyright 2006 - 2010, Paul Beckingham.
// All rights reserved.
//
// This program is free software; you can redistribute it and/or modify it under
// the terms of the GNU General Public License as published by the Free Software
// Foundation; either version 2 of the License, or (at your option) any later
// version.
//
// This program is distributed in the hope that it will be useful, but WITHOUT
// ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
// FOR A PARTICULAR PURPOSE. See the GNU General Public License for more
// details.
//
// You should have received a copy of the GNU General Public License along with
// this program; if not, write to the
//
// Free Software Foundation, Inc.,
// 51 Franklin Street, Fifth Floor,
// Boston, MA
// 02110-1301
// USA
//
////////////////////////////////////////////////////////////////////////////////
#ifndef INCLUDED_MESSAGE
#define INCLUDED_MESSAGE
#include <string>
#include "Element.h"
class Message : public Element
{
public:
Message ();
Message (const Message&);
Message& operator= (const Message&);
~Message ();
void redraw ();
private:
};
#endif
////////////////////////////////////////////////////////////////////////////////

80
src/ui/Report.cpp Normal file
View file

@ -0,0 +1,80 @@
////////////////////////////////////////////////////////////////////////////////
// task - a command line task list manager.
//
// Copyright 2006 - 2010, Paul Beckingham.
// All rights reserved.
//
// This program is free software; you can redistribute it and/or modify it under
// the terms of the GNU General Public License as published by the Free Software
// Foundation; either version 2 of the License, or (at your option) any later
// version.
//
// This program is distributed in the hope that it will be useful, but WITHOUT
// ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
// FOR A PARTICULAR PURPOSE. See the GNU General Public License for more
// details.
//
// You should have received a copy of the GNU General Public License along with
// this program; if not, write to the
//
// Free Software Foundation, Inc.,
// 51 Franklin Street, Fifth Floor,
// Boston, MA
// 02110-1301
// USA
//
////////////////////////////////////////////////////////////////////////////////
#include <pthread.h>
#include "log.h"
#include "Report.h"
// Constriction point for ncurses calls.
extern pthread_mutex_t conch;
////////////////////////////////////////////////////////////////////////////////
Report::Report ()
{
// logWrite ("Report::Report");
}
////////////////////////////////////////////////////////////////////////////////
Report::~Report ()
{
}
////////////////////////////////////////////////////////////////////////////////
bool Report::event (int e)
{
switch (e)
{
case KEY_MOUSE:
{
MEVENT m;
getmouse (&m);
logWrite ("Report::event KEY_MOUSE [%d,%d] %x (%s)",
m.x,
m.y,
m.bstate,
((m.x >= left && m.x < left + width && m.y >= top && m.y < top + height) ? "hit" : "miss"));
}
return true;
break;
}
return false;
}
////////////////////////////////////////////////////////////////////////////////
void Report::redraw ()
{
logWrite ("Report::redraw");
pthread_mutex_lock (&conch);
wbkgd (window, COLOR_PAIR (20));
mvwaddstr (window, 0, 0, "default report");
wrefresh (window);
pthread_mutex_unlock (&conch);
}
////////////////////////////////////////////////////////////////////////////////

50
src/ui/Report.h Normal file
View file

@ -0,0 +1,50 @@
////////////////////////////////////////////////////////////////////////////////
// task - a command line task list manager.
//
// Copyright 2006 - 2010, Paul Beckingham.
// All rights reserved.
//
// This program is free software; you can redistribute it and/or modify it under
// the terms of the GNU General Public License as published by the Free Software
// Foundation; either version 2 of the License, or (at your option) any later
// version.
//
// This program is distributed in the hope that it will be useful, but WITHOUT
// ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
// FOR A PARTICULAR PURPOSE. See the GNU General Public License for more
// details.
//
// You should have received a copy of the GNU General Public License along with
// this program; if not, write to the
//
// Free Software Foundation, Inc.,
// 51 Franklin Street, Fifth Floor,
// Boston, MA
// 02110-1301
// USA
//
////////////////////////////////////////////////////////////////////////////////
#ifndef INCLUDED_REPORT
#define INCLUDED_REPORT
#include <string>
#include "Element.h"
class Report : public Element
{
public:
Report ();
Report (const Report&);
Report& operator= (const Report&);
~Report ();
bool event (int);
void redraw ();
private:
bool value;
};
#endif
////////////////////////////////////////////////////////////////////////////////

385
src/ui/ReportStats.cpp Normal file
View file

@ -0,0 +1,385 @@
////////////////////////////////////////////////////////////////////////////////
// task - a command line task list manager.
//
// Copyright 2006 - 2010, Paul Beckingham.
// All rights reserved.
//
// This program is free software; you can redistribute it and/or modify it under
// the terms of the GNU General Public License as published by the Free Software
// Foundation; either version 2 of the License, or (at your option) any later
// version.
//
// This program is distributed in the hope that it will be useful, but WITHOUT
// ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
// FOR A PARTICULAR PURPOSE. See the GNU General Public License for more
// details.
//
// You should have received a copy of the GNU General Public License along with
// this program; if not, write to the
//
// Free Software Foundation, Inc.,
// 51 Franklin Street, Fifth Floor,
// Boston, MA
// 02110-1301
// USA
//
////////////////////////////////////////////////////////////////////////////////
#include <sstream>
#include <iomanip>
#include <pthread.h>
#include <signal.h>
#include <stdlib.h>
#include "log.h"
#include "ReportStats.h"
#include "Context.h"
#include "File.h"
#include "Path.h"
#include "util.h"
#include "text.h"
#include "main.h"
// Constriction point for ncurses calls.
extern pthread_mutex_t conch;
extern Context context;
static int refreshDelay; // Override with ui.stats.refresh
////////////////////////////////////////////////////////////////////////////////
// This thread sleeps and updates the time every second.
static void tick (int* arg)
{
ReportStats* r = (ReportStats *) arg;
do
{
// If the element is deinitialized the window goes away, but the thread does
// not. Protect against an attempted redraw of a dead window.
if (r->window != NULL &&
r->dataChanged ())
{
r->gatherStats ();
r->redraw ();
}
sleep (refreshDelay);
}
while (arg);
}
////////////////////////////////////////////////////////////////////////////////
ReportStats::ReportStats ()
: highlight_shown (false)
{
// logWrite ("ReportStats::ReportStats");
refreshDelay = context.config.getInteger ("ui.stats.refresh");
logWrite ("ReportStats::ReportStats refreshDelay=%d", refreshDelay);
pthread_create (&ticker, NULL, (void*(*)(void*)) tick, (void*) this);
}
////////////////////////////////////////////////////////////////////////////////
ReportStats::~ReportStats ()
{
pthread_kill (ticker, SIGQUIT);
}
////////////////////////////////////////////////////////////////////////////////
void ReportStats::initialize ()
{
Element::initialize ();
scrollok (window, TRUE);
// idlok (window, TRUE);
}
////////////////////////////////////////////////////////////////////////////////
// TODO Replace with Sensor object.
bool ReportStats::dataChanged ()
{
bool status = false;
struct stat s;
std::string location = Path::expand (context.config.get ("data.location"));
std::string file = location + "/pending.data";
stat (file.c_str (), &s);
if (s.st_mtime != stat_pending.st_mtime)
{
stat_pending = s;
status = true;
}
file = location + "/completed.data";
stat (file.c_str (), &s);
if (s.st_mtime != stat_completed.st_mtime)
{
stat_completed = s;
status = true;
}
file = location + "/undo.data";
stat (file.c_str (), &s);
if (s.st_mtime != stat_undo.st_mtime)
{
stat_undo = s;
status = true;
}
if (highlight_shown)
status = true;
return status;
}
////////////////////////////////////////////////////////////////////////////////
// Maintains two sets of data, previous and current. All newly gathered data
// goes into current. Next iteration, the current is moved to previous.
//
// This two-stage tracking allows detection of changes from one refresh to the
// next, and therefore the highlighting.
void ReportStats::gatherStats ()
{
// The new becomes the old.
previous = current;
// Data size
size_t dataSize = stat_pending.st_size +
stat_completed.st_size +
stat_undo.st_size;
// Undo transactions
std::string location = Path::expand (context.config.get ("data.location"));
std::string file = location + "/undo.data";
std::vector <std::string> undo;
File::read (file, undo);
int undoCount = 0;
foreach (tx, undo)
if (tx->substr (0, 3) == "---")
++undoCount;
// Get all the tasks.
std::vector <Task> tasks;
context.tdb.lock (context.config.getBoolean ("locking"));
handleRecurrence ();
context.tdb.load (tasks, context.filter);
context.tdb.commit ();
context.tdb.unlock ();
Date now;
time_t earliest = time (NULL);
time_t latest = 1;
int totalT = 0;
int deletedT = 0;
int pendingT = 0;
int completedT = 0;
int waitingT = 0;
int taggedT = 0;
int annotationsT = 0;
int recurringT = 0;
float daysPending = 0.0;
int descLength = 0;
std::map <std::string, int> allTags;
std::map <std::string, int> allProjects;
std::vector <Task>::iterator it;
for (it = tasks.begin (); it != tasks.end (); ++it)
{
++totalT;
if (it->getStatus () == Task::deleted) ++deletedT;
if (it->getStatus () == Task::pending) ++pendingT;
if (it->getStatus () == Task::completed) ++completedT;
if (it->getStatus () == Task::recurring) ++recurringT;
if (it->getStatus () == Task::waiting) ++waitingT;
time_t entry = atoi (it->get ("entry").c_str ());
if (entry < earliest) earliest = entry;
if (entry > latest) latest = entry;
if (it->getStatus () == Task::completed)
{
time_t end = atoi (it->get ("end").c_str ());
daysPending += (end - entry) / 86400.0;
}
if (it->getStatus () == Task::pending)
daysPending += (now - entry) / 86400.0;
descLength += it->get ("description").length ();
std::vector <Att> annotations;
it->getAnnotations (annotations);
annotationsT += annotations.size ();
std::vector <std::string> tags;
it->getTags (tags);
if (tags.size ()) ++taggedT;
foreach (t, tags)
allTags[*t] = 0;
std::string project = it->get ("project");
if (project != "")
allProjects[project] = 0;
}
current["pending"] = commify (pendingT);
current["waiting"] = commify (waitingT);
current["recurring"] = commify (recurringT);
current["completed"] = commify (completedT);
current["deleted"] = commify (deletedT);
current["total"] = commify (totalT);
current["annotations"] = commify (annotationsT);
current["tags"] = commify ((int)allTags.size ());
current["projects"] = commify ((int)allProjects.size ());
current["size"] = formatBytes (dataSize);
current["undo"] = commify (undoCount);
std::stringstream s;
if (totalT)
s << std::setprecision (3) << (100.0 * taggedT / totalT);
else
s << 0;
s << "%";
current["tagged"] = s.str ();
if (tasks.size ())
{
Date e (earliest);
Date l (latest);
current["oldest"] = e.toString (context.config.get ("dateformat"));
current["newest"] = l.toString (context.config.get ("dateformat"));
current["since"] = formatSeconds (latest - earliest);
}
else
{
current["oldest"] = "-";
current["newest"] = "-";
current["since"] = "-";
}
if (totalT)
current["add_every"] = formatSeconds ((latest - earliest) / totalT);
else
current["add_every"] = "-";
if (completedT)
current["complete_every"] = formatSeconds ((latest - earliest) / completedT);
else
current["complete_every"] = "-";
if (deletedT)
current["delete_every"] = formatSeconds ((latest - earliest) / deletedT);
else
current["delete_every"] = "-";
if (pendingT || completedT)
current["time"] = formatSeconds ((int) ((daysPending / (pendingT + completedT)) * 86400));
else
current["time"] = "-";
if (totalT)
{
std::stringstream value;
value << (int) (descLength / totalT) << " characters";
current["desc"] = value.str ();
}
else
current["desc"] = "-";
}
////////////////////////////////////////////////////////////////////////////////
bool ReportStats::event (int e)
{
switch (e)
{
case KEY_UP:
logWrite ("ReportStats::event KEY_UP");
wscrl (window, -1);
wrefresh (window);
break;
case KEY_DOWN:
logWrite ("ReportStats::event KEY_DOWN");
wscrl (window, 1);
wrefresh (window);
break;
case KEY_MOUSE:
{
MEVENT m;
getmouse (&m);
logWrite ("ReportStats::event KEY_MOUSE [%d,%d] %x (%s)",
m.x,
m.y,
m.bstate,
((m.x >= left && m.x < left + width && m.y >= top && m.y < top + height) ? "hit" : "miss"));
}
return true;
break;
}
return false;
}
////////////////////////////////////////////////////////////////////////////////
void ReportStats::redraw ()
{
logWrite ("ReportStats::redraw");
pthread_mutex_lock (&conch);
wbkgd (window, COLOR_PAIR (20) | A_DIM);
wbkgdset (window, COLOR_PAIR(20) | A_DIM);
std::stringstream title;
title << "Live Statistics (updates every "
<< refreshDelay
<< " seconds)";
mvwaddstr (window, 0, 0, title.str ().c_str ());
highlight_shown = false;
renderItem (2, 24, "Pending tasks", "pending");
renderItem (3, 24, "Waiting tasks", "waiting");
renderItem (4, 24, "Recurring tasks", "recurring");
renderItem (5, 24, "Completed tasks", "completed");
renderItem (6, 24, "Deleted tasks", "deleted");
renderItem (7, 24, "Total tasks", "total");
renderItem (8, 24, "Total annotations", "annotations");
renderItem (9, 24, "Unique tags", "tags");
renderItem (10, 24, "Projects", "projects");
renderItem (11, 24, "Data size", "size");
renderItem (12, 24, "Undo transactions", "undo");
renderItem (13, 24, "Tasks tagged", "tagged");
renderItem (14, 24, "Oldest task", "oldest");
renderItem (15, 24, "Newest task", "newest");
renderItem (16, 24, "Task used for", "since");
renderItem (17, 24, "Task added every", "add_every");
renderItem (18, 24, "Task completed every", "complete_every");
renderItem (19, 24, "Task deleted every", "delete_every");
renderItem (20, 24, "Average time pending", "time");
renderItem (21, 24, "Average desc length", "desc");
wrefresh (window);
pthread_mutex_unlock (&conch);
}
////////////////////////////////////////////////////////////////////////////////
void ReportStats::renderItem (
int row,
int col,
const std::string& description,
const std::string& key)
{
wbkgdset (window, COLOR_PAIR(20) | A_DIM);
mvwaddstr (window, row, 0, description.c_str ());
if (current[key] != previous[key])
{
wbkgdset (window, COLOR_PAIR(3) | A_BOLD);
highlight_shown = true;
}
mvwaddstr (window, row, col, current[key].c_str ());
}
////////////////////////////////////////////////////////////////////////////////

66
src/ui/ReportStats.h Normal file
View file

@ -0,0 +1,66 @@
////////////////////////////////////////////////////////////////////////////////
// task - a command line task list manager.
//
// Copyright 2006 - 2010, Paul Beckingham.
// All rights reserved.
//
// This program is free software; you can redistribute it and/or modify it under
// the terms of the GNU General Public License as published by the Free Software
// Foundation; either version 2 of the License, or (at your option) any later
// version.
//
// This program is distributed in the hope that it will be useful, but WITHOUT
// ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
// FOR A PARTICULAR PURPOSE. See the GNU General Public License for more
// details.
//
// You should have received a copy of the GNU General Public License along with
// this program; if not, write to the
//
// Free Software Foundation, Inc.,
// 51 Franklin Street, Fifth Floor,
// Boston, MA
// 02110-1301
// USA
//
////////////////////////////////////////////////////////////////////////////////
#ifndef INCLUDED_REPORTSTATS
#define INCLUDED_REPORTSTATS
#include <map>
#include <string>
#include <pthread.h>
#include <sys/types.h>
#include <sys/stat.h>
#include "Element.h"
class ReportStats : public Element
{
public:
ReportStats ();
ReportStats (const ReportStats&);
ReportStats& operator= (const ReportStats&);
~ReportStats ();
void initialize ();
bool dataChanged ();
void gatherStats ();
bool event (int);
void redraw ();
void renderItem (int, int, const std::string&, const std::string&);
public:
struct stat stat_pending;
struct stat stat_completed;
struct stat stat_undo;
private:
pthread_t ticker;
std::map <std::string, std::string> previous;
std::map <std::string, std::string> current;
bool highlight_shown;
};
#endif
////////////////////////////////////////////////////////////////////////////////

88
src/ui/Stats.cpp Normal file
View file

@ -0,0 +1,88 @@
////////////////////////////////////////////////////////////////////////////////
// task - a command line task list manager.
//
// Copyright 2006 - 2010, Paul Beckingham.
// All rights reserved.
//
// This program is free software; you can redistribute it and/or modify it under
// the terms of the GNU General Public License as published by the Free Software
// Foundation; either version 2 of the License, or (at your option) any later
// version.
//
// This program is distributed in the hope that it will be useful, but WITHOUT
// ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
// FOR A PARTICULAR PURPOSE. See the GNU General Public License for more
// details.
//
// You should have received a copy of the GNU General Public License along with
// this program; if not, write to the
//
// Free Software Foundation, Inc.,
// 51 Franklin Street, Fifth Floor,
// Boston, MA
// 02110-1301
// USA
//
////////////////////////////////////////////////////////////////////////////////
#include <pthread.h>
#include "log.h"
#include "Stats.h"
// Constriction point for ncurses calls.
extern pthread_mutex_t conch;
////////////////////////////////////////////////////////////////////////////////
Stats::Stats ()
{
// logWrite ("Stats::Stats");
}
////////////////////////////////////////////////////////////////////////////////
Stats::~Stats ()
{
}
////////////////////////////////////////////////////////////////////////////////
void Stats::redraw ()
{
logWrite ("Stats::redraw");
pthread_mutex_lock (&conch);
wbkgd (window, COLOR_PAIR(1) | A_DIM);
// Allowing 5-digits for tasks.
if (height >= 3)
{
if (width >= 19) mvwaddstr (window, 2, 6, "Waiting tasks");
else if (width >= 13) mvwaddstr (window, 2, 6, "Waiting");
else if (width >= 10) mvwaddstr (window, 2, 6, "Wait");
else if (width >= 7) mvwaddstr (window, 2, 6, "W");
}
if (height >= 2)
{
if (width >= 21) mvwaddstr (window, 1, 6, "Completed tasks");
else if (width >= 15) mvwaddstr (window, 1, 6, "Completed");
else if (width >= 10) mvwaddstr (window, 1, 6, "Comp");
else if (width >= 7) mvwaddstr (window, 1, 6, "C");
}
if (width >= 19) mvwaddstr (window, 0, 6, "Pending tasks");
else if (width >= 13) mvwaddstr (window, 0, 6, "Pending");
else if (width >= 10) mvwaddstr (window, 0, 6, "Pend");
else if (width >= 7) mvwaddstr (window, 0, 6, "P");
if (width >= 7)
{
wbkgdset (window, COLOR_PAIR(1) | A_BOLD);
mvwaddstr (window, 0, 0, "_____");
if (height >= 2) mvwaddstr (window, 1, 0, "_____");
if (height >= 3) mvwaddstr (window, 2, 0, "_____");
}
wrefresh (window);
pthread_mutex_unlock (&conch);
}
////////////////////////////////////////////////////////////////////////////////

48
src/ui/Stats.h Normal file
View file

@ -0,0 +1,48 @@
////////////////////////////////////////////////////////////////////////////////
// task - a command line task list manager.
//
// Copyright 2006 - 2010, Paul Beckingham.
// All rights reserved.
//
// This program is free software; you can redistribute it and/or modify it under
// the terms of the GNU General Public License as published by the Free Software
// Foundation; either version 2 of the License, or (at your option) any later
// version.
//
// This program is distributed in the hope that it will be useful, but WITHOUT
// ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
// FOR A PARTICULAR PURPOSE. See the GNU General Public License for more
// details.
//
// You should have received a copy of the GNU General Public License along with
// this program; if not, write to the
//
// Free Software Foundation, Inc.,
// 51 Franklin Street, Fifth Floor,
// Boston, MA
// 02110-1301
// USA
//
////////////////////////////////////////////////////////////////////////////////
#ifndef INCLUDED_STATS
#define INCLUDED_STATS
#include <string>
#include "Element.h"
class Stats : public Element
{
public:
Stats ();
Stats (const Stats&);
Stats& operator= (const Stats&);
~Stats ();
void redraw ();
private:
};
#endif
////////////////////////////////////////////////////////////////////////////////

69
src/ui/Title.cpp Normal file
View file

@ -0,0 +1,69 @@
////////////////////////////////////////////////////////////////////////////////
// task - a command line task list manager.
//
// Copyright 2006 - 2010, Paul Beckingham.
// All rights reserved.
//
// This program is free software; you can redistribute it and/or modify it under
// the terms of the GNU General Public License as published by the Free Software
// Foundation; either version 2 of the License, or (at your option) any later
// version.
//
// This program is distributed in the hope that it will be useful, but WITHOUT
// ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
// FOR A PARTICULAR PURPOSE. See the GNU General Public License for more
// details.
//
// You should have received a copy of the GNU General Public License along with
// this program; if not, write to the
//
// Free Software Foundation, Inc.,
// 51 Franklin Street, Fifth Floor,
// Boston, MA
// 02110-1301
// USA
//
////////////////////////////////////////////////////////////////////////////////
#include <pthread.h>
#include "log.h"
#include "Title.h"
// Constriction point for ncurses calls.
extern pthread_mutex_t conch;
////////////////////////////////////////////////////////////////////////////////
Title::Title ()
{
// logWrite ("Title::Title");
}
////////////////////////////////////////////////////////////////////////////////
Title::~Title ()
{
}
////////////////////////////////////////////////////////////////////////////////
void Title::redraw ()
{
logWrite ("Title::redraw [%d,%d]", width, height);
pthread_mutex_lock (&conch);
wbkgd (window, COLOR_PAIR (1) | ' ');
if (width >= 21) mvwaddstr (window, 0, 1, "task 2.0.0 pre-alpha");
else if (width >= 11) mvwaddstr (window, 0, 1, "task 2.0.0");
else if (width >= 9) mvwaddstr (window, 0, 1, "task 2.0");
else if (width >= 5) mvwaddstr (window, 0, 1, "task");
if (height >= 2)
{
wbkgdset (window, COLOR_PAIR(8));
if (width >= 23) mvwaddstr (window, 1, 1, "http://taskwarrior.org");
else if (width >= 16) mvwaddstr (window, 1, 1, "taskwarrior.org");
}
wrefresh (window);
pthread_mutex_unlock (&conch);
}
////////////////////////////////////////////////////////////////////////////////

48
src/ui/Title.h Normal file
View file

@ -0,0 +1,48 @@
////////////////////////////////////////////////////////////////////////////////
// task - a command line task list manager.
//
// Copyright 2006 - 2010, Paul Beckingham.
// All rights reserved.
//
// This program is free software; you can redistribute it and/or modify it under
// the terms of the GNU General Public License as published by the Free Software
// Foundation; either version 2 of the License, or (at your option) any later
// version.
//
// This program is distributed in the hope that it will be useful, but WITHOUT
// ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
// FOR A PARTICULAR PURPOSE. See the GNU General Public License for more
// details.
//
// You should have received a copy of the GNU General Public License along with
// this program; if not, write to the
//
// Free Software Foundation, Inc.,
// 51 Franklin Street, Fifth Floor,
// Boston, MA
// 02110-1301
// USA
//
////////////////////////////////////////////////////////////////////////////////
#ifndef INCLUDED_TITLE
#define INCLUDED_TITLE
#include <string>
#include "Element.h"
class Title : public Element
{
public:
Title ();
Title (const Title&);
Title& operator= (const Title&);
~Title ();
void redraw ();
private:
};
#endif
////////////////////////////////////////////////////////////////////////////////

221
src/ui/UI.cpp Normal file
View file

@ -0,0 +1,221 @@
////////////////////////////////////////////////////////////////////////////////
// task - a command line task list manager.
//
// Copyright 2006 - 2010, Paul Beckingham.
// All rights reserved.
//
// This program is free software; you can redistribute it and/or modify it under
// the terms of the GNU General Public License as published by the Free Software
// Foundation; either version 2 of the License, or (at your option) any later
// version.
//
// This program is distributed in the hope that it will be useful, but WITHOUT
// ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
// FOR A PARTICULAR PURPOSE. See the GNU General Public License for more
// details.
//
// You should have received a copy of the GNU General Public License along with
// this program; if not, write to the
//
// Free Software Foundation, Inc.,
// 51 Franklin Street, Fifth Floor,
// Boston, MA
// 02110-1301
// USA
//
////////////////////////////////////////////////////////////////////////////////
#include <ncurses.h>
#include <ctype.h>
#include <pthread.h>
#include "log.h"
#include "UI.h"
// Constriction point for ncurses calls.
pthread_mutex_t conch = PTHREAD_MUTEX_INITIALIZER;
////////////////////////////////////////////////////////////////////////////////
UI::UI ()
: current (NULL)
{
}
////////////////////////////////////////////////////////////////////////////////
UI::~UI ()
{
std::map <std::string, Layout*>::iterator it;
for (it = layouts.begin (); it != layouts.end (); ++it)
delete it->second;
layouts.clear ();
}
////////////////////////////////////////////////////////////////////////////////
void UI::add (const std::string& name, Layout* layout)
{
logWrite ("UI::add %s", name.c_str ());
layouts[name] = layout;
// First layout added automatically becomes the current layout. Subsequent
// layouts accumulate.
if (current == NULL)
current = layout;
}
////////////////////////////////////////////////////////////////////////////////
void UI::switchLayout (const std::string& name)
{
logWrite ("UI::switchLayout %s", name.c_str ());
if (layouts.find (name) == layouts.end ())
throw std::string ("Cannot switch to non-existent layout '") + name + "'";
// Only switch if the proposed layout is not the current layout.
if (layouts[name] != current)
{
// Close the old windows.
current->deinitialize ();
// Set the new current layout.
current = layouts[name];
// Create the new windows.
current->initialize ();
// Need a size recalc, because the new current layout may have been used
// before at a different size.
this->recalc (COLS, LINES);
}
}
////////////////////////////////////////////////////////////////////////////////
void UI::initialize ()
{
logWrite ("UI::initialize");
// Rainbow.
init_pair (1, COLOR_WHITE, COLOR_BLUE);
init_pair (2, COLOR_WHITE, COLOR_RED);
init_pair (3, COLOR_BLACK, COLOR_GREEN);
init_pair (4, COLOR_WHITE, COLOR_MAGENTA);
init_pair (5, COLOR_BLACK, COLOR_CYAN);
init_pair (6, COLOR_BLACK, COLOR_YELLOW);
init_pair (7, COLOR_BLACK, COLOR_WHITE);
init_pair (8, COLOR_CYAN, COLOR_BLUE);
init_pair (9, COLOR_BLUE, COLOR_CYAN);
init_pair (10, COLOR_YELLOW, COLOR_BLUE);
// init_pair (11, COLOR_GREEN, COLOR_BLACK);
// Plain.
init_pair (20, COLOR_WHITE, COLOR_BLACK);
// Propagate to current layout.
current->initialize ();
}
////////////////////////////////////////////////////////////////////////////////
void UI::deinitialize ()
{
logWrite ("UI::deinitialize");
current->deinitialize ();
}
////////////////////////////////////////////////////////////////////////////////
void UI::interactive ()
{
logWrite ("UI::interactive");
if (!current)
throw std::string ("Cannot start interactive mode without an initial layout.");
initscr ();
logWrite ("UI::interactive ncurses started");
refresh (); // Blank screen.
curs_set (0);
if (has_colors ())
start_color ();
this->recalc (COLS, LINES);
this->initialize ();
current->redraw ();
keypad (stdscr, TRUE);
noecho ();
nl ();
raw ();
cbreak ();
mousemask (ALL_MOUSE_EVENTS, NULL);
while (this->event (getch ()))
;
this->deinitialize ();
endwin ();
logWrite ("UI::interactive ncurses stopped");
}
////////////////////////////////////////////////////////////////////////////////
bool UI::event (int e)
{
switch (e)
{
case 'd': // Default layout
switchLayout ("default");
this->recalc (COLS, LINES);
current->redraw (true);
break;
case 's': // report.stats layout
switchLayout ("report.stats");
this->recalc (COLS, LINES);
current->redraw (true);
break;
case 'Q': // Quit.
case 'q': // quit.
logWrite ("UI::event %c", (char)e);
return false;
break;
case ERR: // No need to propagate this.
logWrite ("UI::event ERR ignored");
break;
case KEY_RESIZE: // This gets propagated through UI::recalc.
logWrite ("UI::event KEY_RESIZE");
this->recalc (COLS, LINES);
current->redraw (true); // TODO has no apparent effect.
break;
default: // Unhandled events are propagated.
// Ctrl-L is handled by the layout.
logWrite ("UI::event %d delegated", e);
current->event (e);
break;
}
return true; // Continue;
}
////////////////////////////////////////////////////////////////////////////////
// This is called at the beginning, to calculate element sizes, and on every
// window resize.
void UI::recalc (int w, int h)
{
logWrite ("UI::recalc %d,%d", w, h);
// The current layout needs to know.
current->recalc (w, h);
// Park the cursor.
// TODO Evaluate whether this is needed.
pthread_mutex_lock (&conch);
move (h - 1, w - 1);
pthread_mutex_unlock (&conch);
}
////////////////////////////////////////////////////////////////////////////////

57
src/ui/UI.h Normal file
View file

@ -0,0 +1,57 @@
////////////////////////////////////////////////////////////////////////////////
// task - a command line task list manager.
//
// Copyright 2006 - 2010, Paul Beckingham.
// All rights reserved.
//
// This program is free software; you can redistribute it and/or modify it under
// the terms of the GNU General Public License as published by the Free Software
// Foundation; either version 2 of the License, or (at your option) any later
// version.
//
// This program is distributed in the hope that it will be useful, but WITHOUT
// ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
// FOR A PARTICULAR PURPOSE. See the GNU General Public License for more
// details.
//
// You should have received a copy of the GNU General Public License along with
// this program; if not, write to the
//
// Free Software Foundation, Inc.,
// 51 Franklin Street, Fifth Floor,
// Boston, MA
// 02110-1301
// USA
//
////////////////////////////////////////////////////////////////////////////////
#ifndef INCLUDED_UI
#define INCLUDED_UI
#include <map>
#include <string>
#include "Layout.h"
class UI
{
public:
UI ();
UI (const UI&);
UI& operator= (const UI&);
~UI ();
void add (const std::string&, Layout*);
void switchLayout (const std::string&);
void initialize ();
void deinitialize ();
void interactive ();
void recalc (int, int);
bool event (int);
private:
std::map <std::string, Layout*> layouts;
Layout* current; // Points to one of the layouts.
};
#endif
////////////////////////////////////////////////////////////////////////////////

133
src/ui/interactive.cpp Normal file
View file

@ -0,0 +1,133 @@
////////////////////////////////////////////////////////////////////////////////
// task - a command line task list manager.
//
// Copyright 2006 - 2010, Paul Beckingham.
// All rights reserved.
//
// This program is free software; you can redistribute it and/or modify it under
// the terms of the GNU General Public License as published by the Free Software
// Foundation; either version 2 of the License, or (at your option) any later
// version.
//
// This program is distributed in the hope that it will be useful, but WITHOUT
// ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
// FOR A PARTICULAR PURPOSE. See the GNU General Public License for more
// details.
//
// You should have received a copy of the GNU General Public License along with
// this program; if not, write to the
//
// Free Software Foundation, Inc.,
// 51 Franklin Street, Fifth Floor,
// Boston, MA
// 02110-1301
// USA
//
////////////////////////////////////////////////////////////////////////////////
#include <iostream>
#include <sstream>
#include <string>
#include "Context.h"
#include "main.h"
#include "log.h"
#include "Layout.h"
#include "i18n.h"
#include "../auto.h"
#ifdef HAVE_LIBNCURSES
#include <ncurses.h>
#endif
////////////////////////////////////////////////////////////////////////////////
int Context::handleInteractive ()
{
#ifdef HAVE_LIBNCURSES
// TODO Need to wrap all UI code with HAVE_LIBNCURSES.
// TODO Load this, and all others from .taskrc to override the defaults.
logSetDirectory (".");
logSetName ("task");
logDuplicates (false);
logEnable (false);
try
{
// TODO Ick. Clean this up.
std::string prefix = " (vertical (horizontal 2 (panel title *) (panel stats 22)) (horizontal 1 (panel message 60%) (panel clock *)) ";
std::string suffix = " (panel keys 2)))";
std::string layout_def = "(layout default" + prefix + "(panel report *)" + suffix;
std::string layout_report_stats = "(layout report.stats" + prefix + "(panel report.stats *)" + suffix;
UI ui; // Construct a UI coordinator.
createLayout (ui, "default", layout_def);
createLayout (ui, "report.stats", layout_report_stats);
ui.interactive (); // Start ncurses, event loop.
}
// TODO Integrate regular task error handling, using Context::debug.
catch (int e) { std::cout << e << std::endl; }
catch (const char* e) { std::cout << e << std::endl; }
catch (std::string& e) { std::cout << e << std::endl; }
catch (...) { std::cout << "Unknown error." << std::endl; }
logWrite ("---");
#else
throw stringtable.get (INTERACTIVE_NO_NCURSES,
"Interactive task is only available when built with ncurses "
"support.");
#endif
return 0;
}
////////////////////////////////////////////////////////////////////////////////
void Context::createLayout (
UI& ui,
const std::string& name,
const std::string& def)
{
// A layout named 'foo' can be overridden with a .taskrc config variable that
// is named 'layout.foo'.
std::string definition = config.get ("layout." + name);
if (definition == "")
definition = def;
Layout* l = new Layout (definition);
ui.add (name, l);
}
////////////////////////////////////////////////////////////////////////////////
int Context::getWidth ()
{
// Determine window size, and set table accordingly.
int width = config.getInteger ("defaultwidth");
#ifdef HAVE_LIBNCURSES
if (config.getBoolean ("curses"))
{
initscr ();
width = COLS;
endwin ();
std::stringstream out;
out << "Context::getWidth: ncurses determined width of " << width << " characters";
debug (out.str ());
}
else
debug ("Context::getWidth: ncurses available but disabled.");
#else
std::stringstream out;
out << "Context::getWidth: no ncurses, using width of " << width << " characters";
debug (out.str ());
#endif
return width;
}
////////////////////////////////////////////////////////////////////////////////

217
src/ui/log.cpp Normal file
View file

@ -0,0 +1,217 @@
////////////////////////////////////////////////////////////////////////////////
// task - a command line task list manager.
//
// Copyright 2006 - 2010, Paul Beckingham.
// All rights reserved.
//
// This program is free software; you can redistribute it and/or modify it under
// the terms of the GNU General Public License as published by the Free Software
// Foundation; either version 2 of the License, or (at your option) any later
// version.
//
// This program is distributed in the hope that it will be useful, but WITHOUT
// ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
// FOR A PARTICULAR PURPOSE. See the GNU General Public License for more
// details.
//
// You should have received a copy of the GNU General Public License along with
// this program; if not, write to the
//
// Free Software Foundation, Inc.,
// 51 Franklin Street, Fifth Floor,
// Boston, MA
// 02110-1301
// USA
//
////////////////////////////////////////////////////////////////////////////////
#include <sys/file.h>
#include <sys/stat.h>
#include <pwd.h>
#include <stdarg.h>
#include <stdio.h>
#include <string.h>
#include <limits.h>
#include <string>
////////////////////////////////////////////////////////////////////////////////
// Global data.
static std::string gLogDir = ".";
static std::string gLogName = "default";
static bool gEnabled = true;
static const unsigned int maxMessageLength = 128;
static bool bDuplicates = false;
static std::string gPrior = "none";
static int gRepetitionCount = 0;
////////////////////////////////////////////////////////////////////////////////
void logSetDirectory (const std::string& dir)
{
gLogDir = dir;
}
////////////////////////////////////////////////////////////////////////////////
void logSetName (const std::string& name)
{
gLogName = name;
}
////////////////////////////////////////////////////////////////////////////////
void logEnable (const bool value)
{
gEnabled = value;
}
////////////////////////////////////////////////////////////////////////////////
void logDuplicates (const bool value)
{
bDuplicates = value;
}
////////////////////////////////////////////////////////////////////////////////
void getTimeStamp (std::string& ts)
{
// Get time info.
time_t now;
time (&now);
struct tm* t = localtime (&now);
// Generate timestamp.
char timestamp[20];
sprintf (timestamp, "%04d-%02d-%02d %02d:%02d:%02d",
t->tm_year + 1900,
t->tm_mon + 1,
t->tm_mday,
t->tm_hour,
t->tm_min,
t->tm_sec);
ts = timestamp;
}
////////////////////////////////////////////////////////////////////////////////
// #Version: 1.0
// #Date: 17-Oct-2004
// #Fields: date time x-pid x-message
// 2004-10-18 00:12:00 12345 "Message"
void logWrite (const std::string& message)
{
if (!gEnabled) return;
if (!bDuplicates)
{
if (message == gPrior)
{
++gRepetitionCount;
return;
}
else
gPrior = message;
}
// Get time info.
time_t now;
time (&now);
struct tm* t = localtime (&now);
// Sanitize 'message'.
std::string sanitized = message;
std::string::size_type bad;
while ((bad = sanitized.find ("\"")) != std::string::npos)
sanitized.replace (bad, 1, "'");
if (sanitized.length () > maxMessageLength)
sanitized = sanitized.substr (0, maxMessageLength) + "...";
// Generate timestamp.
char timestamp[20];
sprintf (timestamp, "%04d-%02d-%02d %02d:%02d:%02d",
t->tm_year + 1900,
t->tm_mon + 1,
t->tm_mday,
t->tm_hour,
t->tm_min,
t->tm_sec);
// Determine file name.
char file[_POSIX_PATH_MAX];
sprintf (file, "%s/%s.%04d%02d%02d.log",
gLogDir.c_str (),
gLogName.c_str (),
t->tm_year + 1900,
t->tm_mon + 1,
t->tm_mday);
// Determine whether file exists.
bool exists = access (file, F_OK) != -1 ? true : false;
// Create file if necessary, with header.
FILE* log = fopen (file, "a");
if (log)
{
// Lock file.
#if defined (__SVR4) && defined (__sun)
#else
int fn = fileno (log);
if (! flock (fn, LOCK_EX))
{
#endif
// Create the header, if the file did not exist.
if (! exists)
{
fprintf (log, "# File: %s.%04d%02d%02d.log\n",
gLogName.c_str (),
t->tm_year + 1900,
t->tm_mon + 1,
t->tm_mday);
fprintf (log, "# Fields: date time x-pid x-message\n");
}
// Optionally write a repetition message.
if (gRepetitionCount)
{
fprintf (log, "%s %d \"(Repeated %d times)\"\n",
timestamp,
(int) getpid (),
gRepetitionCount);
gRepetitionCount = 0;
}
// Write entry.
fprintf (log, "%s %d \"%s\"\n",
timestamp,
(int) getpid (),
sanitized.c_str ());
#if defined (__SVR4) && defined (__sun)
#else
}
#endif
// close file.
fclose (log);
if (! exists)
chmod (file, S_IRUSR | S_IWUSR | S_IRGRP | S_IWGRP | S_IROTH | S_IWOTH);
}
}
////////////////////////////////////////////////////////////////////////////////
void logWrite (const char* message, ...)
{
if (!gEnabled) return;
// Crude and mostly ineffective check for buffer overrun.
if (::strlen (message) >= 65536)
throw std::string ("Data exceeds 65,536 bytes. Break data into smaller chunks.");
char buffer[65536];
va_list args;
va_start (args, message);
vsnprintf (buffer, 65536, message, args);
va_end (args);
logWrite (std::string (buffer));
}
////////////////////////////////////////////////////////////////////////////////

42
src/ui/log.h Normal file
View file

@ -0,0 +1,42 @@
////////////////////////////////////////////////////////////////////////////////
// task - a command line task list manager.
//
// Copyright 2006 - 2010, Paul Beckingham.
// All rights reserved.
//
// This program is free software; you can redistribute it and/or modify it under
// the terms of the GNU General Public License as published by the Free Software
// Foundation; either version 2 of the License, or (at your option) any later
// version.
//
// This program is distributed in the hope that it will be useful, but WITHOUT
// ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
// FOR A PARTICULAR PURPOSE. See the GNU General Public License for more
// details.
//
// You should have received a copy of the GNU General Public License along with
// this program; if not, write to the
//
// Free Software Foundation, Inc.,
// 51 Franklin Street, Fifth Floor,
// Boston, MA
// 02110-1301
// USA
//
////////////////////////////////////////////////////////////////////////////////
#ifndef INCLUDED_LOG
#define INCLUDED_LOG
#include <string>
void logSetDirectory (const std::string&);
void logSetName (const std::string&);
void logEnable (const bool);
void logDuplicates (const bool);
void getTimeStamp (std::string&);
void logWrite (const std::string&);
void logWrite (const char*, ...);
#endif
////////////////////////////////////////////////////////////////////////////////

284
src/ui/resize.cpp Normal file
View file

@ -0,0 +1,284 @@
////////////////////////////////////////////////////////////////////////////////
// task - a command line task list manager.
//
// Copyright 2006 - 2010, Paul Beckingham.
// All rights reserved.
//
// This program is free software; you can redistribute it and/or modify it under
// the terms of the GNU General Public License as published by the Free Software
// Foundation; either version 2 of the License, or (at your option) any later
// version.
//
// This program is distributed in the hope that it will be useful, but WITHOUT
// ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
// FOR A PARTICULAR PURPOSE. See the GNU General Public License for more
// details.
//
// You should have received a copy of the GNU General Public License along with
// this program; if not, write to the
//
// Free Software Foundation, Inc.,
// 51 Franklin Street, Fifth Floor,
// Boston, MA
// 02110-1301
// USA
//
////////////////////////////////////////////////////////////////////////////////
#include <map>
#include <vector>
#include <string>
#include <stdlib.h>
#include "Rectangle.h"
#include "Tree.h"
#include "Lisp.h"
static void recalcNode (Tree*, int, int, int, int);
static void distribute (const int, const std::vector <std::string>&, std::vector <int>&);
static void getRequests (Tree*, std::vector <std::string>&);
////////////////////////////////////////////////////////////////////////////////
void resize (
const std::string& input,
const int width,
const int height,
std::string& name,
std::map <std::string, Rectangle>& panels)
{
// Parse the input into a tree.
Lisp l;
Tree* root = l.parse (input);
// Navigate the tree and calculate sizes. This requires a breadth-first
// traversal, so that calculations at one level are then propagated down.
// After all, the window dimensions dictate the layout size, and so on down.
Tree* layout = (*root)[0];
layout->attribute ("left", 0);
layout->attribute ("top", 0);
layout->attribute ("width", width);
layout->attribute ("height", height);
recalcNode (layout, 0, 0, width, height);
// Extract the name of the layout.
std::vector <std::string> tags = layout->allTags ();
if (tags.size () >= 2)
name = tags[1];
else
name = "anonymous";
// Extract panels.
panels.clear ();
std::vector <Tree*> nodes;
layout->enumerate (nodes);
std::vector <Tree*>::iterator it;
for (it = nodes.begin (); it != nodes.end (); ++it)
{
tags = (*it)->allTags ();
if (tags[0] == "panel")
panels[tags[1]] =
Rectangle (
atoi ((*it)->attribute ("left").c_str ()),
atoi ((*it)->attribute ("top").c_str ()),
atoi ((*it)->attribute ("width").c_str ()),
atoi ((*it)->attribute ("height").c_str ()));
}
delete root;
}
////////////////////////////////////////////////////////////////////////////////
// Specified node has associated width and height. Subdivide either width or
// height across all tree branches depending on which container the parent is.
static void recalcNode (Tree* parent, int left, int top, int width, int height)
{
// What kind of parent is this? layout, horizontal or vertical?
std::string parent_type = "?";
std::vector <std::string> parent_tags = parent->allTags ();
if (parent_tags.size ())
parent_type = parent_tags[0];
else
throw std::string ("Error: node has no specified type");
if (parent_type == "horizontal")
{
std::vector <std::string> requested;
getRequests (parent, requested);
std::vector <int> allocated;
distribute (width, requested, allocated);
int current_left = left;
for (int i = 0; i < parent->branches (); ++i)
{
(*parent)[i]->attribute ("left", current_left);
(*parent)[i]->attribute ("top", top);
(*parent)[i]->attribute ("width", allocated[i]);
(*parent)[i]->attribute ("height", height);
current_left += allocated[i];
}
}
else if (parent_type == "vertical")
{
std::vector <std::string> requested;
getRequests (parent, requested);
std::vector <int> allocated;
distribute (height, requested, allocated);
int current_top = top;
for (int i = 0; i < parent->branches (); ++i)
{
(*parent)[i]->attribute ("left", left);
(*parent)[i]->attribute ("top", current_top);
(*parent)[i]->attribute ("width", width);
(*parent)[i]->attribute ("height", allocated[i]);
current_top += allocated[i];
}
}
else if (parent_type == "layout")
{
if (! (*parent)[0])
throw std::string ("Error: layout has no contents.");
(*parent)[0]->attribute ("left", left);
(*parent)[0]->attribute ("top", top);
(*parent)[0]->attribute ("width", width);
(*parent)[0]->attribute ("height", height);
}
// Now recurse to each branch of parent that is a container.
for (int i = 0; i < parent->branches (); ++i)
{
Tree* child = (*parent)[i];
if (child->hasTag ("horizontal") ||
child->hasTag ("vertical"))
{
recalcNode (child,
atoi (child->attribute ("left").c_str ()),
atoi (child->attribute ("top").c_str ()),
atoi (child->attribute ("width").c_str ()),
atoi (child->attribute ("height").c_str ()));
}
}
}
////////////////////////////////////////////////////////////////////////////////
// If the call looks like this:
// distribute (60, [10, *, *, *, *, *, 50%], [])
//
// Then the result is:
// distribute (60, [10, *, 50%], [10, 5, 5, 5, 5, 5, 25])
//
// A literal number is a request. It will be granted, provided there is space.
// A percentage is a request for a portion of the unallocated amount.
// A wildcard is a request for an equal share, among all wildcards, of the
// unallocated amount.
//
static void distribute (
const int total,
const std::vector <std::string>& input,
std::vector <int>& output)
{
int allocated = 0;
int unallocated = total;
output.clear ();
// First count up the requested.
for (unsigned int i = 0; i < input.size (); ++i)
if (input[i] == "*" || input[i].find ("%") != std::string::npos)
output.push_back (0);
else
{
int value = atoi (input[i].c_str ());
output.push_back (value);
allocated += value;
unallocated -= value;
}
if (allocated > total)
throw std::string ("Error: over allocation by request.");
// Now include the proportional.
int before_allocated = allocated;
for (unsigned int i = 0; i < input.size (); ++i)
{
if (input[i].find ("%") != std::string::npos)
{
int value = atoi (input[i].c_str ());
value = (value * unallocated) / 100;
output[i] = value;
allocated += value;
}
}
unallocated -= (allocated - before_allocated);
if (allocated > total)
throw std::string ("Error: over allocation by request.");
// Count the wildcards.
int wildcards = 0;
for (unsigned int i = 0; i < input.size (); ++i)
if (input[i] == "*")
++wildcards;
// Evenly distribute unallocated among the wildcards.
for (unsigned int i = 0; i < input.size (); ++i)
{
if (input[i] == "*")
{
if (wildcards > 1)
{
int portion = unallocated / wildcards;
--wildcards;
output[i] = portion;
allocated += portion;
unallocated -= portion;
}
else
{
output[i] = unallocated;
allocated += unallocated;
unallocated = 0;
}
}
}
}
////////////////////////////////////////////////////////////////////////////////
static void getRequests (Tree* node, std::vector <std::string>& requested)
{
requested.clear ();
for (int i = 0; i < node->branches (); ++i)
{
Tree* child = (*node)[i];
std::vector <std::string> child_tags = child->allTags ();
switch (child_tags.size ())
{
case 1: // (xcontainer (...))
requested.push_back ("*");
break;
case 2: // (xcontainer size (...))
requested.push_back (child_tags[1]);
break;
case 3: // (panel name size)
requested.push_back (child_tags[2]);
break;
default:
throw std::string ("Error: unexpected number of tags in a node");
break;
}
}
}
////////////////////////////////////////////////////////////////////////////////