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 <CmdDenotate.h>
29 #include <iostream>
30 #include <Context.h>
31 #include <Filter.h>
32 #include <shared.h>
33 #include <format.h>
34 #include <util.h>
35 #include <main.h>
36
37 #define STRING_CMD_DENO_NO "Task not denotated."
38 #define STRING_CMD_DENO_1 "Denotated {1} task."
39 #define STRING_CMD_DENO_N "Denotated {1} tasks."
40
41 ////////////////////////////////////////////////////////////////////////////////
CmdDenotate()42 CmdDenotate::CmdDenotate ()
43 {
44 _keyword = "denotate";
45 _usage = "task <filter> denotate <pattern>";
46 _description = "Deletes an annotation";
47 _read_only = false;
48 _displays_id = false;
49 _needs_gc = false;
50 _uses_context = true;
51 _accepts_filter = true;
52 _accepts_modifications = false;
53 _accepts_miscellaneous = true;
54 _category = Command::Category::operation;
55 }
56
57 ////////////////////////////////////////////////////////////////////////////////
execute(std::string &)58 int CmdDenotate::execute (std::string&)
59 {
60 auto rc = 0;
61 auto count = 0;
62 auto sensitive = Context::getContext ().config.getBoolean ("search.case.sensitive");
63
64 // Apply filter.
65 Filter filter;
66 std::vector <Task> filtered;
67 filter.subset (filtered);
68 if (filtered.size () == 0)
69 {
70 Context::getContext ().footnote ("No tasks specified.");
71 return 1;
72 }
73
74 // Extract all the ORIGINAL MODIFICATION args as simple text patterns.
75 std::string pattern = "";
76 for (auto& a : Context::getContext ().cli2._args)
77 {
78 if (a.hasTag ("MISCELLANEOUS"))
79 {
80 if (pattern != "")
81 pattern += ' ';
82
83 pattern += a.attribute ("raw");
84 }
85 }
86
87 // Accumulated project change notifications.
88 std::map <std::string, std::string> projectChanges;
89
90 if(filtered.size() > 1) {
91 feedback_affected("This command will alter {1} tasks.", filtered.size());
92 }
93 for (auto& task : filtered)
94 {
95 Task before (task);
96
97 auto annotations = task.getAnnotations ();
98
99 if (annotations.size () == 0)
100 throw std::string ("The specified task has no annotations that can be deleted.");
101
102 std::string anno;
103 auto match = false;
104 for (auto i = annotations.begin (); i != annotations.end (); ++i)
105 {
106 if (i->second == pattern)
107 {
108 match = true;
109 anno = i->second;
110 annotations.erase (i);
111 task.setAnnotations (annotations);
112 break;
113 }
114 }
115
116 if (! match)
117 {
118 for (auto i = annotations.begin (); i != annotations.end (); ++i)
119 {
120 auto loc = find (i->second, pattern, sensitive);
121 if (loc != std::string::npos)
122 {
123 anno = i->second;
124 annotations.erase (i);
125 task.setAnnotations (annotations);
126 break;
127 }
128 }
129 }
130
131 if (before.data != task.data)
132 {
133 auto question = format ("Denotate task {1} '{2}'?",
134 task.identifier (true),
135 task.get ("description"));
136
137 if (permission (before.diff (task) + question, filtered.size ()))
138 {
139 ++count;
140 Context::getContext ().tdb2.modify (task);
141 feedback_affected (format ("Found annotation '{1}' and deleted it.", anno));
142 if (Context::getContext ().verbose ("project"))
143 projectChanges[task.get ("project")] = onProjectChange (task, false);
144 }
145 else
146 {
147 std::cout << STRING_CMD_DENO_NO << '\n';
148 rc = 1;
149 if (_permission_quit)
150 break;
151 }
152 }
153 else
154 {
155 std::cout << format ("Did not find any matching annotation to be deleted for '{1}'.\n", pattern);
156 rc = 1;
157 }
158 }
159
160 // Now list the project changes.
161 for (const auto& change : projectChanges)
162 if (change.first != "")
163 Context::getContext ().footnote (change.second);
164
165 feedback_affected (count == 1 ? STRING_CMD_DENO_1 : STRING_CMD_DENO_N, count);
166 return rc;
167 }
168
169 ////////////////////////////////////////////////////////////////////////////////
170