1 ////////////////////////////////////////////////////////////////////////////////
2 //
3 // Copyright 2006 - 2021, Tomas Babej, Paul Beckingham, Federico Hernandez.
4 //
5 // Permission is hereby granted, free of charge, to any person obtaining a copy
6 // of this software and associated documentation files (the "Software"), to deal
7 // in the Software without restriction, including without limitation the rights
8 // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9 // copies of the Software, and to permit persons to whom the Software is
10 // furnished to do so, subject to the following conditions:
11 //
12 // The above copyright notice and this permission notice shall be included
13 // in all copies or substantial portions of the Software.
14 //
15 // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS
16 // OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17 // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
18 // THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19 // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20 // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21 // SOFTWARE.
22 //
23 // https://www.opensource.org/licenses/mit-license.php
24 //
25 ////////////////////////////////////////////////////////////////////////////////
26 
27 #include <cmake.h>
28 #include <CmdPrepend.h>
29 #include <iostream>
30 #include <Context.h>
31 #include <Filter.h>
32 #include <shared.h>
33 #include <format.h>
34 #include <main.h>
35 
36 ////////////////////////////////////////////////////////////////////////////////
CmdPrepend()37 CmdPrepend::CmdPrepend ()
38 {
39   _keyword               = "prepend";
40   _usage                 = "task <filter> prepend <mods>";
41   _description           = "Prepends text to an existing task description";
42   _read_only             = false;
43   _displays_id           = false;
44   _needs_gc              = false;
45   _uses_context          = true;
46   _accepts_filter        = true;
47   _accepts_modifications = true;
48   _accepts_miscellaneous = false;
49   _category              = Command::Category::operation;
50 }
51 
52 ////////////////////////////////////////////////////////////////////////////////
execute(std::string &)53 int CmdPrepend::execute (std::string&)
54 {
55   int rc = 0;
56   int count = 0;
57 
58   // Apply filter.
59   Filter filter;
60   std::vector <Task> filtered;
61   filter.subset (filtered);
62   if (filtered.size () == 0)
63   {
64     Context::getContext ().footnote ("No tasks specified.");
65     return 1;
66   }
67 
68   // TODO Complain when no modifications are specified.
69 
70   // Accumulated project change notifications.
71   std::map <std::string, std::string> projectChanges;
72 
73   if(filtered.size() > 1) {
74     feedback_affected("This command will alter {1} tasks.", filtered.size());
75   }
76   for (auto& task : filtered)
77   {
78     Task before (task);
79 
80     // Prepend to the specified task.
81     std::string question = format ("Prepend to task {1} '{2}'?",
82                                    task.identifier (true),
83                                    task.get ("description"));
84 
85     task.modify (Task::modPrepend, true);
86 
87     if (permission (before.diff (task) + question, filtered.size ()))
88     {
89       Context::getContext ().tdb2.modify (task);
90       ++count;
91       feedback_affected ("Prepending to task {1} '{2}'.", task);
92       if (Context::getContext ().verbose ("project"))
93         projectChanges[task.get ("project")] = onProjectChange (task, false);
94 
95       // Prepend to siblings.
96       if (task.has ("parent"))
97       {
98         if ((Context::getContext ().config.get ("recurrence.confirmation") == "prompt"
99              && confirm ("This is a recurring task.  Do you want to prepend to all pending recurrences of this same task?")) ||
100             Context::getContext ().config.getBoolean ("recurrence.confirmation"))
101         {
102           std::vector <Task> siblings = Context::getContext ().tdb2.siblings (task);
103           for (auto& sibling : siblings)
104           {
105             sibling.modify (Task::modPrepend, true);
106             Context::getContext ().tdb2.modify (sibling);
107             ++count;
108             feedback_affected ("Prepending to recurring task {1} '{2}'.", sibling);
109           }
110 
111           // Prepend to the parent
112           Task parent;
113           Context::getContext ().tdb2.get (task.get ("parent"), parent);
114           parent.modify (Task::modPrepend, true);
115           Context::getContext ().tdb2.modify (parent);
116         }
117       }
118     }
119     else
120     {
121       std::cout << "Task not prepended.\n";
122       rc = 1;
123       if (_permission_quit)
124         break;
125     }
126   }
127 
128   // Now list the project changes.
129   for (auto& change : projectChanges)
130     if (change.first != "")
131       Context::getContext ().footnote (change.second);
132 
133   feedback_affected (count == 1 ? "Prepended {1} task." : "Prepended {1} tasks.", count);
134   return rc;
135 }
136 
137 ////////////////////////////////////////////////////////////////////////////////
138