Changeset 306
- Timestamp:
- 04/03/05 23:32:57 (4 years ago)
- Files:
-
- todo2/trunk/configure.in (modified) (1 diff)
- todo2/trunk/README (modified) (3 diffs)
- todo2/trunk/src/backend/.makeclass.cc (modified) (2 diffs)
- todo2/trunk/src/backend/.makeclass.h (modified) (1 diff)
- todo2/trunk/src/backend/XML.cc (modified) (9 diffs)
- todo2/trunk/src/ContactMethod.cc (modified) (1 diff)
- todo2/trunk/src/contactmethod/.makeclass.cc (modified) (2 diffs)
- todo2/trunk/src/contactmethod/.makeclass.h (modified) (2 diffs)
- todo2/trunk/src/Database.cc (modified) (3 diffs)
- todo2/trunk/src/filter/Expression.cc (modified) (4 diffs)
- todo2/trunk/src/frontend/Console.cc (modified) (6 diffs)
- todo2/trunk/src/frontend/Console.h (modified) (3 diffs)
- todo2/trunk/src/frontend/.makeclass.cc (modified) (1 diff)
- todo2/trunk/src/frontend/.makeclass.h (modified) (1 diff)
- todo2/trunk/src/Item.cc (modified) (7 diffs)
- todo2/trunk/src/Item.h (modified) (8 diffs)
- todo2/trunk/src/Makefile.am (modified) (1 diff)
- todo2/trunk/src/Manager.cc (modified) (13 diffs)
- todo2/trunk/src/Manager.h (modified) (2 diffs)
- todo2/trunk/src/ModuleProxy.h (modified) (1 diff)
- todo2/trunk/src/PropertyMap.h (modified) (1 diff)
- todo2/trunk/src/Time.cc (added)
- todo2/trunk/src/Time.h (added)
- todo2/trunk/src/Uncontactable.cc (added)
- todo2/trunk/src/Uncontactable.h (added)
- todo2/trunk/.todo (modified) (2 diffs)
- todo2/trunk/.todo2 (modified) (3 diffs)
Legend:
- Unmodified
- Added
- Removed
- Modified
- Copied
- Moved
todo2/trunk/configure.in
r300 r306 103 103 104 104 LIBS="${LIBS} ${LIBLTDL}" 105 CPPFLAGS="${CPPFLAGS} ${LTDLINCL} -Wall - DSYSCONFDIR=\\\"${sysconfdir}\\\""105 CPPFLAGS="${CPPFLAGS} ${LTDLINCL} -Wall -Wold-style-cast -DSYSCONFDIR=\\\"${sysconfdir}\\\"" 106 106 107 107 AC_CONFIG_HEADERS([src/config.h]) todo2/trunk/README
r295 r306 1 1 vim: syntax=txt 2 !!! devtodo2 2 = Developer Todo 2 = 3 3 4 !! Modular Components 4 == Modular Components == 5 5 6 ! Overview 6 There are four modular components to DT2. 7 7 8 There are four modular components to devtodo2. 9 10 1. FrontEnd 8 === FrontEnd === 11 9 12 10 Front-ends are responsible for almost all user interaction, with the caveat … … 14 12 will be performed using the system console. 15 13 16 The de-facto FrontEnd is Console, which uses colour textual output to represent 17 the database. There are plans for GTK and QT FrontEnds. 14 The default frontend module is "console" which performs all user interaction via 15 command line arguments and console input. This is similar to DT1, however the 16 arguments have been improved and cleaned up. 18 17 19 2. BackEnd 18 There are plans for GTK and QT !FrontEnds. 20 19 21 Back-ends perform all loading, locking and saving of the devtodo2 databases. 20 === BackEnd === 22 21 23 The de-facto BackEnd is XML which stores the hierarchical database in an XML 24 format. I would like to have a MySQL BackEnd, and possibly an SSH-based BackEnd 25 for doing secure remote database changes, though I'm not exactly sure how 26 workable the latter is. 22 Back-ends perform all loading, locking and saving of the DT2 databases. 27 23 28 3. Filter 24 The default backend 25 26 I would like to have a MySQL !BackEnd, and possibly an SSH-based BackEnd for 27 doing secure remote database changes, though I'm not exactly sure how workable 28 the latter is. 29 30 === Filter === 29 31 30 32 A set of filters are provided which perform various transformations on the … … 33 35 and filtering based on user supplied expressions. 34 36 35 4. CommChannel 37 === ContactMethod === 36 38 37 The final modular component is co mmunication channels. When an item is38 modified, fellow collaborators can be informed; the CommChannel modules handle39 thiscommunication.39 The final modular component is contact methods. When an item is modified, 40 fellow collaborators can be informed; the !ContactMethod modules handle this 41 communication. 40 42 41 The default, and currently only, CommChannel is Mail which informs 42 interested parties via the local Unix system MDA. 43 The default !ContactMethod is "mail" which informs interested parties via the 44 local Unix system MDA. There is one other default !ContactMethod 45 "uncontactable" which is the default if users are not in the address book. 43 46 44 ! Command-Line Arguments 47 == Address Book == 45 48 46 Each module can register command-line arguments with devtodo2. These will show 49 DT2 has the concept of an address book, which maps user id's (eg. athomas) to 50 contact information. 51 52 If a user does not have an address book entry a default entry will be created 53 with "uncontactable" as their contact information. 54 55 == Command-Line Arguments == 56 57 Each module registers a set of command-line arguments with DT2. These will show 47 58 up in a --help and the module will be called upon to parse these arguments. 48 59 49 !! Format 60 Some useful debugging arguments are --dump-config (dump the configuration that 61 DT2 is using), --list-modules (list all successfully loaded modules) and -D99 62 (enable full debugging). 50 63 51 devtodo databases are collections of key/value pairs, with some specific 52 exclusions. category, address, assigned are all mult-key values. 64 == Database == 65 66 The DT2 database consists of a hierarchy of items, and per-database users and 67 categories. 68 69 Each item in a DT2 database is a collection of key/value pairs. Some default 70 keys are used by DT2 internally while others can be used by specific modules 71 to perform extra logic. For example, the colour key might be used by the 72 Console backend to override the default display colour for an item. 53 73 54 74 All date/time values are stored as UNIX timestamps in UTC to avoid issues with 55 sharing data between time zones.75 sharing data between time zones. todo2/trunk/src/backend/.makeclass.cc
r295 r306 3 3 using namespace std; 4 4 5 ${CLASSNAME}::${CLASSNAME}() {5 ${CLASSNAME}::${CLASSNAME}() : BackEnd("${LCLASSNAME}") { 6 6 } 7 7 … … 9 9 } 10 10 11 /* 12 bool ${CLASSNAME}::arg(crash::Args::iterator argument) { 13 switch (argument.option()) { 14 } 15 return false; 11 bool ${CLASSNAME}::loadable(std::string path) { 16 12 } 17 */ 13 14 Handle *${CLASSNAME}::prepare(std::string path) { 15 } 16 17 /////////////////////////// ${CLASSNAME}::Handle 18 19 ${CLASSNAME}::Handle::Handle(BackEnd *backend, std::string path) : BackEnd::Handle(backend, path) { 20 } 21 22 ${CLASSNAME}::Handle::~Handle() { 23 } 24 25 bool ${CLASSNAME}::Handle::lock(unsigned mode) { 26 } 27 28 void ${CLASSNAME}::Handle::unlock() { 29 } 30 31 Database *${CLASSNAME}::Handle::load() { 32 } 33 34 void ${CLASSNAME}::Handle::save(Database *database) { 35 } 36 todo2/trunk/src/backend/.makeclass.h
r295 r306 12 12 SUBCLASS_MODULE_EXCEPTION(BackEnd::Exception) 13 13 14 class Handle : public BackEnd::Handle { 15 public : 16 Handle(BackEnd *backend, std::string path); 17 ~Handle(); 18 19 bool lock(unsigned mode); 20 //bool lock(Item *item, unsigned mode); 21 void unlock(); 22 //void unlock(Item *item); 23 Database *load(); 24 void save(Database *database); 25 private : 26 }; 27 14 28 ${CLASSNAME}(); 15 29 ~${CLASSNAME}(); 16 30 17 //bool arg(crash::Args::iterator argument); 31 virtual bool loadable(std::string path); 32 virtual Handle *prepare(std::string path); 33 18 34 private : 19 35 }; todo2/trunk/src/backend/XML.cc
r305 r306 18 18 method to report errors. */ 19 19 static void loadableErrorHandler(void *arg, const char * msg, xmlParserSeverities severity, xmlTextReaderLocatorPtr locator) { 20 string &s = * reinterpret_cast<string*>(arg);20 string &s = *static_cast<string*>(arg); 21 21 22 22 s = "line " + to_string(xmlTextReaderLocatorLineNumber(locator)) + ": " + msg; … … 39 39 } 40 40 41 //////////////////// XML::Handle 41 42 42 //////////////////// XML::Handle43 43 XML::Handle::Handle(BackEnd *backend, std::string path) : BackEnd::Handle(backend, path) { 44 44 } … … 69 69 if (i->type == XML_ELEMENT_NODE) 70 70 if (!i->children) 71 props.set( (const char*)i->name, "");71 props.set(reinterpret_cast<const char*>(i->name), ""); 72 72 else 73 73 if (i->children->type != XML_TEXT_NODE) 74 throw ParseError(node, _("properties must be in the form <prop>content</prop>, '") + string( (const char*)i->name) + _("' is not"));74 throw ParseError(node, _("properties must be in the form <prop>content</prop>, '") + string(reinterpret_cast<const char*>(i->name)) + _("' is not")); 75 75 else 76 props.set( (const char*)i->name, (const char*)i->children->content);76 props.set(reinterpret_cast<const char*>(i->name), reinterpret_cast<const char*>(i->children->content)); 77 77 return props; 78 78 } … … 81 81 if (!node) throw Exception(backend(), _("can't parse NULL node (this should not occur and is to be considered a BUG)")); 82 82 83 if (node->type == XML_ELEMENT_NODE && !xmlStrcmp(node->name, (const xmlChar*)"item")) {83 if (node->type == XML_ELEMENT_NODE && !xmlStrcmp(node->name, reinterpret_cast<const xmlChar*>("item"))) { 84 84 Item *item = new Item(); 85 86 item->set<time_t>("created", time(0));87 item->set<time_t>("modified", time(0));88 85 89 86 // We have reached an item node, parse out meta-data … … 92 89 string name = reinterpret_cast<const char*>(attribute->name); 93 90 94 // Skip items on this parse91 // Skip child items on this parse 95 92 if (name == "item") continue; 96 93 94 // Comment 95 if (name == "comment") { 96 const char *author = reinterpret_cast<const char*>(xmlGetProp(attribute, reinterpret_cast<const xmlChar*>("author"))); 97 const char *time = reinterpret_cast<const char*>(xmlGetProp(attribute, reinterpret_cast<const xmlChar*>("time"))); 98 99 if (!author) throw ParseError(attribute, "attribute '" + name + "' does not have the required property 'author'"); 100 if (!time) throw ParseError(attribute, "attribute '" + name + "' does not have the required property 'time'"); 101 if (attribute->children && attribute->children->type == XML_TEXT_NODE) { 102 const User *user = MANAGER.getUser(author); 103 104 item->addComment(Item::Comment( 105 reinterpret_cast<const char*>(attribute->children->content), 106 user, 107 from_string<time_t>(time) 108 )); 109 } else 110 throw ParseError(attribute, "no comment body found"); 111 } else 97 112 // timestamps 98 113 if (name == "created" || name == "start" || name == "end" || 99 114 name == "modified" || name == "completed") { 100 const char *value = (const char*)xmlGetProp(attribute, (const xmlChar*)"time");115 const char *value = reinterpret_cast<const char*>(xmlGetProp(attribute, reinterpret_cast<const xmlChar*>("time"))); 101 116 102 117 if (!value) 103 118 throw ParseError(attribute, "attribute '" + name + "' does not have the required property 'time'"); 104 105 try { 106 item->set<time_t>(name, MANAGER.utc2local(from_string<time_t>(value))); 107 } catch (BadCast) { 108 throw ParseError(attribute, _("'time' attribute is in an invalid format '") + string(value) + _("'")); 109 } 119 item->set(name, value); 110 120 } else 111 121 if (name == "category") { 112 const char *value = (const char*)xmlGetProp(attribute, (const xmlChar*)"id");122 const char *value = reinterpret_cast<const char*>(xmlGetProp(attribute, reinterpret_cast<const xmlChar*>("id"))); 113 123 114 124 if (!value) … … 121 131 } else 122 132 if (name == "owner" || name == "assigned") { 123 const char *value = (const char*)xmlGetProp(attribute, (const xmlChar*)"id");133 const char *value = reinterpret_cast<const char*>(xmlGetProp(attribute, reinterpret_cast<const xmlChar*>("id"))); 124 134 125 135 if (!value) … … 137 147 if (attribute->children) 138 148 if (attribute->children->type == XML_TEXT_NODE) 139 content = (const char*)attribute->children->content;149 content = reinterpret_cast<const char*>(attribute->children->content); 140 150 else 141 151 throw ParseError(attribute, "'" + name + _("' should either be empty or have content consisting solely of text")); … … 183 193 // Do top-level meta-data parse 184 194 for (xmlNode *top = xroot; top != 0; top = top->next) { 185 if (xmlStrcmp(top->name, (const xmlChar*)"todo2"))186 throw Exception(backend(), _("unexpected top-level XML node '") + string( (const char*)top->name) + "'");195 if (xmlStrcmp(top->name, reinterpret_cast<const xmlChar*>("todo2"))) 196 throw Exception(backend(), _("unexpected top-level XML node '") + string(reinterpret_cast<const char*>(top->name)) + "'"); 187 197 xitems = top->children; 188 198 for (xmlNode *node = top->children; node != 0; node = node->next) { 189 199 if (node->type == XML_ELEMENT_NODE) { 190 if (!xmlStrcmp(node->name, (const xmlChar*)"user")) {200 if (!xmlStrcmp(node->name, reinterpret_cast<const xmlChar*>("user"))) { 191 201 MANAGER.addUser(loadProperties(node)); 192 202 } else 193 if (!xmlStrcmp(node->name, (const xmlChar*)"category")) {203 if (!xmlStrcmp(node->name, reinterpret_cast<const xmlChar*>("category"))) { 194 204 MANAGER.addCategory(loadProperties(node)); 195 205 } else 196 if (xmlStrcmp(node->name, (const xmlChar*)"item"))197 throw Exception(backend(), _("unexpected top-level XML node '") + string( (const char*)node->name) + "'");206 if (xmlStrcmp(node->name, reinterpret_cast<const xmlChar*>("item"))) 207 throw Exception(backend(), _("unexpected top-level XML node '") + string(reinterpret_cast<const char*>(node->name)) + "'"); 198 208 } 199 209 } … … 251 261 } 252 262 263 ///////////////////// XML 253 264 254 ///////////////////// XML255 265 XML::XML() : BackEnd("xml") { 256 266 } todo2/trunk/src/ContactMethod.cc
r305 r306 1 1 #include "ContactMethod.h" 2 #include "Uncontactable.h" 3 4 using namespace std; 5 using namespace crash; 2 6 3 7 ModuleProxy<ContactMethod>::List 4 8 ModuleProxy<ContactMethod>::mproxies; 9 10 // Built-in modules 11 REGISTER_CONTACTMETHOD(Uncontactable); 5 12 6 13 ContactMethod::ContactMethod(const std::string &name) : Module(name) { todo2/trunk/src/contactmethod/.makeclass.cc
r295 r306 5 5 using namespace crash; 6 6 7 REGISTER_CO MMCHANNEL(Mail)7 REGISTER_CONTACTMETHOD(${CLASSNAME}) 8 8 9 ${CLASSNAME}::${CLASSNAME}() {9 ${CLASSNAME}::${CLASSNAME}() : ContactMethod("${LCLASSNAME}") { 10 10 } 11 11 … … 16 16 throw Exception("${CLASSNAME}::send() not implemented yet"); 17 17 } 18 19 void ${CLASSNAME}::args(crash::Args &args) { 20 } 21 22 bool ${CLASSNAME}::arg(crash::Args::iterator arg) { 23 } todo2/trunk/src/contactmethod/.makeclass.h
r295 r306 2 2 #define ${UCLASSNAME}_H__ 3 3 4 #include "Co mmChannel.h"4 #include "ContactMethod.h" 5 5 6 6 /** … … 8 8 */ 9 9 10 class ${CLASSNAME} : public Co mmChannel{10 class ${CLASSNAME} : public ContactMethod { 11 11 public : 12 SUBCLASS_ EXCEPTION(CommChannel::Exception)12 SUBCLASS_MODULE_EXCEPTION(ContactMethod::Exception) 13 13 14 explicit${CLASSNAME}();14 ${CLASSNAME}(); 15 15 ~${CLASSNAME}(); 16 16 17 static const char *name() { return "${LCLASSNAME}"; }18 CommChannel *instantiate() { return new ${CLASSNAME}(); }17 void args(crash::Args &args); 18 bool arg(crash::Args::iterator arg); 19 19 20 20 void send(User *user, const std::string &subject, const std::string &message); todo2/trunk/src/Database.cc
r295 r306 22 22 23 23 Item *Database::checkout(Item::Index index) { 24 if (mflags & READONLY) throw Exception("attempted to modify item " + (string)index+ " in read-only database");24 if (mflags & READONLY) throw Exception("attempted to modify item " + string(index) + " in read-only database"); 25 25 26 26 Item *item = find(index); … … 28 28 29 29 if (i != mtransactions.end()) 30 throw Exception("item " + (string)index+ " is already checked out");30 throw Exception("item " + string(index) + " is already checked out"); 31 31 onCheckout(this, item); 32 32 mtransactions[item] = index; … … 38 38 39 39 if (i == mtransactions.end()) throw Exception("no open transaction for item, can't commit"); 40 if (mflags & READONLY) throw Exception("attempted to commit item " + (string)i->second+ " to read-only database");40 if (mflags & READONLY) throw Exception("attempted to commit item " + string(i->second) + " to read-only database"); 41 41 onCommit(this, item); 42 42 mtransactions.erase(i); todo2/trunk/src/filter/Expression.cc
r305 r306 13 13 14 14 Expression::Expression() : Filter("expression"), musedefaultvalue(false), mconstructingfilter(false) { 15 setUnlessDefined("default-value", musedefaultvalue); 16 setUnlessDefined("filter", "!completed"); 17 MANAGER.setUnlessDefined("default-argument-handler", "expression"); 15 setDefault("default-value", musedefaultvalue); 16 setDefault("filter", "!completed"); 18 17 19 18 // Load aliases … … 259 258 return execute(item, alias->second, depth + 1); 260 259 261 if (ev == "children") return (double)item->children();262 if (ev == "index") return (double)item->index();263 if (ev == "absindex") return (string)item->absIndex();264 if (ev == "depth") return (double)item->depth();260 if (ev == "children") return double(item->children()); 261 if (ev == "index") return double(item->index()); 262 if (ev == "absindex") return string(item->absIndex()); 263 if (ev == "depth") return double(item->depth()); 265 264 if (ev == "true") return 1.0; 266 265 if (ev == "false") return 0.0; 267 if (ev == "active") return (double)item->active();268 if (ev == "created") return (double)item->created();269 if (ev == "modified") return (double)item->modified();266 if (ev == "active") return double(item->active()); 267 if (ev == "created") return double(item->created()); 268 if (ev == "modified") return double(item->modified()); 270 269 if (ev == "owner") return item->owner()->id(); 271 if (ev == "type") return (double)item->type();272 if (ev == "now") return Manager::time();273 if (ev == "lifetime") return (double)item->lifetime();270 if (ev == "type") return double(item->type()); 271 if (ev == "now") return to_string(Time()); 272 if (ev == "lifetime") return double(item->lifetime()); 274 273 if (ev == "username") return MANAGER.get("username"); 275 274 if (ev == "hostname") return MANAGER.get("hostname"); 276 275 277 if (MANAGER.havePriority(ev)) return (double)MANAGER.priority(ev);276 if (MANAGER.havePriority(ev)) return double(MANAGER.priority(ev)); 278 277 279 278 // Search attributes of the item first … … 310 309 if (expr->parameters() != 1) 311 310 throw Exception(this, _("category(catid) requires category id as its only argument")); 312 return (bool)MANAGER.getCategory(execute(item, expr->parameter(0), depth + 1));311 return bool(MANAGER.getCategory(execute(item, expr->parameter(0), depth + 1))); 313 312 } 314 313 … … 316 315 if (expr->parameters() != 1) 317 316 throw Exception(this, _("user(userid) requires user id as its only argument")); 318 return (bool)MANAGER.getUser(execute(item, expr->parameter(0), depth + 1));317 return bool(MANAGER.getUser(execute(item, expr->parameter(0), depth + 1))); 319 318 } 320 319 todo2/trunk/src/frontend/Console.cc
r305 r306 1 #include <cstdlib> 1 2 #include <unistd.h> 3 #include <sys/types.h> 4 #include <sys/stat.h> 2 5 #include <crash/TermColour.h> 3 6 #include <crash/Input.h> 7 #include <crash/Crash.h> 4 8 #include "Manager.h" 5 9 #include "ModuleProxy.h" … … 11 15 REGISTER_FRONTEND(Console) 12 16 13 Console::Console() : FrontEnd("console"), mterm(), mtermios(mterm), mpriority(0) { 17 Console::Console() : FrontEnd("console"), mterm(), mtermios(mterm), mpriority(0), mmode(DEFAULT), mactions(0), mcomplete(0) { 18 setDefault("colour", "always"); 19 setDefault("editor", "internal"); 20 14 21 // Are we outputting to a TTY? 15 mcolour = mtty = isatty(1); 16 if (exists("colour")) { 17 string cc = get("colour"); 18 19 if (cc == "always") 20 mcolour = true; 21 else 22 if (cc != "auto") 23 mcolour = get<bool>("colour", true); 24 } 25 colour(mcolour); 22 mtty = isatty(1); 23 colour(get("colour")); 26 24 } 27 25 … … 29 27 } 30 28 31 void Console::colour(bool state) { 32 if (state) { 29 void Console::colour(std::string state) { 30 bool colour = true; 31 32 if (state == "auto") 33 colour = isatty(1); 34 else 35 if (state == "never") 36 colour = false; 37 else 38 if (state == "always") 39 colour = true; 40 else 41 throw Exception(this, "unknown colour mode '" + state + "'"); 42 43 mcolour = colour; 44 45 if (colour) { 33 46 BLACK = foreground(crash::BLACK); 34 47 RED = foreground(crash::RED); … … 57 70 args.add('m', "move", Args::OPTIONAL, _("Move item to underneath <parent>, or root if <parent> is not specified."), _("<parent>")); 58 71 args.add('c', "complete", Args::OPTIONAL, _("Mark an item as 100% complete or optionally specify the percentage."), _("<percent>")); 59 args.add('S', "start", Args:: REQUIRED, _("Mark start of a work period on an item."));72 args.add('S', "start", Args::NONE, _("Mark start of a work period on an item.")); 60 73 args.add('E', "end", Args::OPTIONAL, _("Mark end of a work period on an item."), _("<text>")); 61 74 ARG_SCHEDULED_START = args.add(Args::ALLOCATE, "scheduled-start", Args::REQUIRED, _("Specify the scheduled start time for an item."), _("<time>")); 62 75 ARG_SCHEDULED_END = args.add(Args::ALLOCATE, "scheduled-end", Args::REQUIRED, _("Specify the scheduled end time for an item."), _("<time>")); 63 ARG_DELETE = args.add(Args::ALLOCATE, "delete", Args::REQUIRED, _("Delete the specified index."), _("<index>"));64 76 } 65 77 66 78 bool Console::arg(crash::Args::iterator arg) { 79 // DEFAULT, ADD, REMOVE, EDIT, MOVE 67 80 switch (arg.option()) { 68 case 0 : if (!defaultArgumentHandler()) break; 81 case 0 : 82 if (mmode != DEFAULT) 83 try { 84 mindex = arg.parameter(); 85 } catch (BadCast) { 86 DEBUG(9, "rejected '" << arg.parameter() << "' as default item index"); 87 return false; 88 } 89 else 90 if (!defaultArgumentHandler()) 91 return false; 92 break; 69 93 case 'i' : 70 throw Exception(this, "-i not implemented");94 mindex = arg.parameter(); 71 95 break; 72 96 case 'e' : 73 throw Exception(this, "-e not implemented"); 74 break; 75 case 'd' : 76 throw Exception(this, "-d not implemented"); 97 mmode = EDIT; 77 98 break; 78 99 case 'r' : 79 throw Exception(this, "-r not implemented");100 mmode = REMOVE; 80 101 break; 81 102 case 'm' : 82 throw Exception(this, "-m not implemented"); 103 mmode = MOVE; 104 if (arg.hasParameter()) 105 mmoveindex = arg.parameter(); 83 106 break; 84 107 case 'c' : 85 throw Exception(this, "-c not implemented"); 108 if (arg.hasParameter()) 109 mcomplete = from_string<double>(arg.parameter()) / 100.0; 110 else 111 mcomplete = 100; 112 mactions |= COMPLETE; 86 113 break; 87 114 case 'a' : 88 throw Exception(this, "-a not implemented"); 115 mmode = ADD; 116 if (arg.hasParameter()) 117 maddtext = arg.parameter(); 89 118 break; 90 119 case 'p' : … … 92 121 break; 93 122 case 'S' : 94 throw Exception(this, "-S not implemented");123 mactions |= START; 95 124 break; 96 125 case 'E' : 97 throw Exception(this, "-E not implemented"); 126 mactions |= END; 127 if (arg.hasParameter()) 128 mendtext = arg.parameter(); 98 129 break; 99 130 default : 100 131 if (arg.option() == ARG_COLOUR || arg.option() == ARG_COLOR) { 101 if (arg.hasParameter()) { 102 const string &mode = arg.parameter(); 103 104 if (mode == "always") 105 colour(mcolour = true); 106 else 107 if (mode == "never" || mode == "none") 108 colour(mcolour = false); 109 else 110 if (mode == "auto") 111 colour(mcolour = isatty(1)); 112 else 113 throw Exception(this, "unknown colour mode '" + mode + "'"); 114 } else 115 colour(mcolour = true); 132 if (arg.hasParameter()) 133 colour(arg.parameter()); 134 else 135 colour("always"); 116 136 } else 117 if (arg.option() == ARG_SCHEDULED_START) 118 throw Exception(this, "--scheduled-start not implemented"); 119 else 120 if (arg.option() == ARG_SCHEDULED_END) 121 throw Exception(this, "--scheduled-end not implemented"); 122 else 123 if (arg.option() == ARG_DELETE) 124 throw Exception(this, "--delete not implemented"); 125 else 137 if (arg.option() == ARG_SCHEDULED_START) { 138 mscheduledstart = from_string<time_t>(arg.parameter()); 139 mactions |= SCHEDULED_START; 140 } else 141 if (arg.option() == ARG_SCHEDULED_END) { 142 mscheduledend = from_string<time_t>(arg.parameter()); 143 mactions |= SCHEDULED_END; 144 } else 126 145 return false; 127 146 break; 128 147 } 129 return false;148 return true; 130 149 } 131 150 … … 175 194 176 195 std::string Console::input(const std::string &prompt, const std::string &title, bool password) { 177 if (title != "") cout << title << endl; 178 Input input(mterm, mtermios); 179 if (prompt != "") input.prompt(prompt); 180 input.echo(!password); 181 return input.get(); 182 } 196 if (get("editor") == "internal") { 197 if (title != "") cout << title << endl; 198 Input input(mterm, mtermios); 199 if (prompt != "") input.prompt(prompt); 200 input.echo(!password); 201 return input.get(); 202 } else { 203 string editor = get("editor"); 204 char temp[PATH_MAX] = "/tmp/todo2.console.XXXXXX"; 205 206 if (editor == "environment") { 207 if (!getenv("EDITOR")) 208 throw Exception(this, "module.console.editor set to environment, but EDITOR envar not set"); 209 editor = getenv("EDITOR"); 210 } 211 212 mode_t mask = umask(0600); 213 int tempfd; 214 string input; 215 216 try { 217 if ((tempfd = mkstemp(temp)) == -1) 218 throw Exception(this, "failed to create temporar file '" + string(temp) + "'"); 219 system((editor + " " + temp).c_str()); 220 input = readfile(temp); 221 } catch (...) { 222 umask(mask); 223 throw; 224 } 225 umask(mask); 226 return input; 227 } 228 } todo2/trunk/src/frontend/Console.h
r299 r306 17 17 SUBCLASS_MODULE_EXCEPTION(FrontEnd::Exception) 18 18 19 explicitConsole();19 Console(); 20 20 ~Console(); 21 21 … … 36 36 37 37 private : 38 void colour(bool state); 38 void colour(std::string state); 39 40 enum Mode { 41 DEFAULT, ADD, REMOVE, EDIT, MOVE, 42 }; 43 44 enum Actions { 45 COMPLETE = 1, 46 START = 2, 47 END = 4, 48 SCHEDULED_START = 8, 49 SCHEDULED_END = 16, 50 }; 39 51 40 52 std::string BOLD, NORMAL, RESET, BLACK, RED, GREEN, BROWN, BLUE, MAGENTA, CYAN, WHITE; 41 53 42 int ARG_COLOR, ARG_COLOUR, ARG_ DELETE, ARG_SCHEDULED_START, ARG_SCHEDULED_END;54 int ARG_COLOR, ARG_COLOUR, ARG_SCHEDULED_START, ARG_SCHEDULED_END; 43 55 bool mtty, mcolour; 44 56 crash::Terminal mterm; … … 46 58 Database *mdatabase; 47 59 int mpriority;
