1use tracing::info;
2
3use super::Manipulate;
4use crate::{
5 annotations_accessor_mut,
6 events::Events,
7 file_util::PathPair,
8 history::{History, Record},
9 make_tool_transform,
10 parameters::{ParamMap, ParamVal},
11 result::trace_ok_err,
12 tools_data::{AttributesToolData, attributes_data::set_attrmap_val},
13 tools_data_accessors,
14 world::World,
15 world_annotations_accessor,
16};
17use std::mem;
18const MISSING_DATA_MSG: &str = "Missing data for Attributes";
19pub const ACTOR_NAME: &str = "Attributes";
20annotations_accessor_mut!(
21 ACTOR_NAME,
22 attributes_mut,
23 "Attribute didn't work",
24 ParamMap
25);
26world_annotations_accessor!(ACTOR_NAME, attributes, "Attribute didn't work", ParamMap);
27tools_data_accessors!(
28 ACTOR_NAME,
29 MISSING_DATA_MSG,
30 attributes_data,
31 AttributesToolData,
32 attributes,
33 attributes_mut
34);
35
36fn propagate_annos(
37 mut annos: ParamMap,
38 attr_names: &[String],
39 to_propagate: &[(usize, ParamVal)],
40) -> ParamMap {
41 for (attr_idx, val) in to_propagate {
42 if let Some(attr_val) = attr_names
43 .get(*attr_idx)
44 .and_then(|attr_name| annos.get_mut(attr_name))
45 {
46 *attr_val = val.clone();
47 }
48 }
49 annos
50}
51
52fn get_buffers(world: &World) -> Vec<String> {
53 let annos = get_annos(world);
54 let data = get_specific(world);
55 if let (Some(data), Some(annos)) = (data, annos) {
56 data.attr_names()
57 .iter()
58 .map(|attr_name| {
59 if let Some(attrval) = annos.get(attr_name) {
60 attrval.to_string()
61 } else {
62 "".to_string()
63 }
64 })
65 .collect()
66 } else {
67 vec![]
68 }
69}
70fn propagate_buffer(
71 mut attribute_buffer: Vec<String>,
72 to_propagate: &[(usize, ParamVal)],
73) -> Vec<String> {
74 for (attr_idx, val) in to_propagate {
75 if let Some(ab) = attribute_buffer.get_mut(*attr_idx) {
76 *ab = val.to_string();
77 }
78 }
79 attribute_buffer
80}
81fn apply_menu_update(world: &mut World) -> bool {
84 let is_update_triggered = get_specific(world).map(|d| d.options.is_update_triggered);
85 if is_update_triggered == Some(true) {
86 info!("update attr");
87 let current_from_menu_clone = get_specific(world).and_then(|d| d.current_attr_map.clone());
88 if let (Some(mut cfm), Some(anno)) = (current_from_menu_clone, get_annos_mut(world)) {
89 *anno = mem::take(&mut cfm);
90 }
91 if let Some(update_current_attr_map) =
92 get_specific_mut(world).map(|d| &mut d.options.is_update_triggered)
93 {
94 *update_current_attr_map = false;
95 }
96 true
97 } else {
98 false
99 }
100}
101
102fn file_change(mut world: World) -> World {
103 use_currentimageshape_for_annos(&mut world);
104 let attr_buffers = get_buffers(&world);
105 let annos = get_annos_mut(&mut world).map(mem::take);
106 let data = get_specific_mut(&mut world);
107
108 if let (Some(data), Some(mut annos)) = (data, annos) {
109 for (attr_name, attr_val) in data.attr_names().iter().zip(data.attr_vals().iter()) {
111 if !annos.contains(attr_name) {
112 set_attrmap_val(&mut annos, attr_name, attr_val.clone().reset());
113 }
114 }
115
116 for (attr_name, attr_val) in annos.iter() {
119 if !data.attr_names().contains(attr_name) {
120 tracing::warn!(
121 "Attribute {attr_name} exists in the data but not in the tool data, adding it"
122 );
123 data.push(attr_name.clone(), attr_val.clone().reset());
124 }
125 }
126
127 let attr_buffers = propagate_buffer(attr_buffers, &data.to_propagate_attr_val);
129 for (i, buffer) in attr_buffers.into_iter().enumerate() {
130 if let Some(attr_buffer) = data.attr_value_buffer_mut(i) {
131 *attr_buffer = buffer;
132 }
133 }
134
135 annos = propagate_annos(annos, data.attr_names(), &data.to_propagate_attr_val);
136
137 if let Some(annos_) = get_annos_mut(&mut world) {
138 *annos_ = annos;
139 }
140 }
141 let current = get_annos(&world).cloned();
142 if let Some(data) = get_specific_mut(&mut world) {
143 data.current_attr_map = current;
144 }
145 world
146}
147fn add_attribute(
148 mut world: World,
149 mut history: History,
150 suppress_exists_err: bool,
151) -> (World, History) {
152 let attr_map_tmp = get_annos_mut(&mut world).map(mem::take);
153 let data = get_specific_mut(&mut world);
154
155 if let (Some(mut attr_map_tmp), Some(data)) = (attr_map_tmp, data) {
156 let new_attr_name = data.new_attr_name.clone();
157 if data.attr_names().contains(&new_attr_name) && !suppress_exists_err {
158 tracing::error!("New attribute {new_attr_name} could not be created, already exists");
159 } else {
160 let new_attr_val = data.new_attr_val.clone();
161 for (_, (val_map, _)) in data.anno_iter_mut() {
162 set_attrmap_val(val_map, &new_attr_name, new_attr_val.clone());
163 }
164 set_attrmap_val(&mut attr_map_tmp, &new_attr_name, new_attr_val.clone());
165 if let Some(a) = get_annos_mut(&mut world) {
166 a.clone_from(&attr_map_tmp);
167 }
168 if let Some(data) = get_specific_mut(&mut world) {
169 data.current_attr_map = Some(attr_map_tmp);
170 data.push(new_attr_name, new_attr_val);
171 history.push(Record::new(world.clone(), ACTOR_NAME));
172 }
173 }
174 }
175 if let Some(data) = get_specific_mut(&mut world) {
176 data.options.is_addition_triggered = false;
177 data.new_attr_name = String::new();
178 data.new_attr_val = ParamVal::default();
179 }
180 (world, history)
181}
182
183fn check_remove(mut world: World, mut history: History) -> (World, History) {
184 if let Some(removal_idx) = get_specific(&world).map(|d| d.options.removal_idx) {
185 let data = get_specific_mut(&mut world);
186 if let (Some(data), Some(removal_idx)) = (data, removal_idx) {
187 data.remove_attr(removal_idx);
188 history.push(Record::new(world.clone(), ACTOR_NAME));
189 }
190 if let Some(removal_idx) = get_specific_mut(&mut world).map(|d| &mut d.options.removal_idx)
191 {
192 *removal_idx = None;
193 }
194 }
195 (world, history)
196}
197
198#[derive(Clone, Copy, Debug)]
199pub struct Attributes;
200
201impl Manipulate for Attributes {
202 fn new() -> Self
203 where
204 Self: Sized,
205 {
206 Self
207 }
208
209 fn on_activate(&mut self, mut world: World) -> World {
210 let data = get_data_mut(&mut world);
211 if let Some(data) = trace_ok_err(data) {
212 data.menu_active = true;
213 }
214 file_change(world)
215 }
216 fn on_deactivate(&mut self, mut world: World) -> World {
217 let data = get_data_mut(&mut world);
218 if let Some(data) = trace_ok_err(data) {
219 data.menu_active = false;
220 }
221 world
222 }
223 fn on_filechange(&mut self, world: World, history: History) -> (World, History) {
224 (file_change(world), history)
225 }
226 fn before_file_change(&mut self, mut world: World) -> World {
227 apply_menu_update(&mut world);
231 world
232 }
233 fn events_tf(
234 &mut self,
235 mut world: World,
236 mut history: History,
237 _event: &Events,
238 ) -> (World, History) {
239 let is_addition_triggered = get_specific(&world).map(|d| d.options.is_addition_triggered);
240 if is_addition_triggered == Some(true) {
241 (world, history) = add_attribute(world, history, false);
243 }
244 let attr_data = get_specific_mut(&mut world);
245 if let Some(attr_data) = attr_data
246 && let Some(rename_src_idx) = attr_data.options.rename_src_idx
247 {
248 let from_name = attr_data.attr_names().get(rename_src_idx).cloned();
249 let to_name = &attr_data.new_attr_name.clone();
250 if let Some(from_name) = from_name {
251 tracing::info!("Rename attribute {from_name} to {to_name}");
252 attr_data.rename(&from_name, to_name);
253 attr_data.options.rename_src_idx = None;
254 } else {
255 tracing::error!("could not rename attribute {from_name:?} to {to_name}");
256 }
257 }
258 (world, history) = check_remove(world, history);
259
260 let is_export_triggered =
261 get_specific(&world).map(|d| d.options.import_export_trigger.export_triggered());
262 if is_export_triggered == Some(true) {
263 let ssh_cfg = world.data.meta_data.ssh_cfg.clone();
264 let attr_data = get_specific(&world);
265 let export_only_opened_folder =
266 attr_data.map(|d| d.options.export_only_opened_folder) == Some(true);
267 let key_filter = if export_only_opened_folder {
268 world
269 .data
270 .meta_data
271 .opened_folder
272 .as_ref()
273 .map(PathPair::path_relative)
274 } else {
275 None
276 };
277 let annos_str = get_specific(&world)
278 .and_then(|d| trace_ok_err(d.serialize_annotations(key_filter)));
279 if let (Some(annos_str), Some(data)) = (annos_str, get_specific(&world))
280 && trace_ok_err(data.export_path.conn.write(
281 &annos_str,
282 &data.export_path.path,
283 ssh_cfg.as_ref(),
284 ))
285 .is_some()
286 {
287 info!("exported annotations to {:?}", data.export_path.path);
288 }
289
290 if let Some(export_triggered) =
291 get_specific_mut(&mut world).map(|d| &mut d.options.import_export_trigger)
292 {
293 export_triggered.untrigger_export();
294 }
295 }
296 let is_import_triggered =
297 get_specific(&world).map(|d| d.options.import_export_trigger.import_triggered());
298 if is_import_triggered == Some(true) {
299 tracing::info!("import attr tiggered");
300 let ssh_cfg = world.data.meta_data.ssh_cfg.clone();
301 let cur_prj = world.data.meta_data.prj_path().map(|p| p.to_path_buf());
302 let attr_data = get_specific_mut(&mut world);
303 let imported_map = attr_data.and_then(|data| {
304 let in_path = &data.export_path.path;
305 tracing::info!("importing attributes from {in_path:?}");
306 let json_str = trace_ok_err(data.export_path.conn.read(in_path, ssh_cfg.as_ref()));
307 if let Some(s) = json_str {
308 trace_ok_err(AttributesToolData::deserialize_annotations(
309 &s,
310 cur_prj.as_deref(),
311 ))
312 } else {
313 None
314 }
315 });
316 if let Some(imported_map) = &imported_map {
317 for (_, (attr_map, _)) in imported_map.iter() {
319 for (attr_name, attr_val) in attr_map.iter() {
320 let data = get_specific_mut(&mut world);
321 if let Some(d) = data {
322 d.new_attr_name = attr_name.clone();
323 d.new_attr_val = attr_val.clone().reset();
324 }
325 tracing::debug!("inserting attr {attr_name} with value {attr_val}");
326 (world, history) = add_attribute(world, history, true);
327 }
328 }
329 }
330 if let Some(imported_map) = imported_map {
331 let data = get_specific_mut(&mut world);
332 if let Some(d) = data {
333 d.merge_map(imported_map);
334 }
335 }
336 let annos = get_annos(&world).cloned();
337 let attr_buffer = get_buffers(&world);
338 if let (Some(data), Some(annos)) = (get_specific_mut(&mut world), annos) {
339 data.current_attr_map = Some(annos);
340 data.set_new_attr_value_buffer(attr_buffer);
341 }
342 }
343 if let Some(import_trigger) =
344 get_specific_mut(&mut world).map(|d| &mut d.options.import_export_trigger)
345 {
346 import_trigger.untrigger_import();
347 }
348 make_tool_transform!(self, world, history, event, [])
349 }
350}
351#[cfg(test)]
352use {
353 crate::tracing_setup::init_tracing_for_tests,
354 crate::types::{ThumbIms, ViewImage},
355 image::DynamicImage,
356 std::collections::HashMap,
357 std::fs,
358 std::path::Path,
359};
360#[cfg(test)]
361pub(super) fn test_data() -> (World, History) {
362 use std::path::Path;
363
364 use crate::ToolsDataMap;
365
366 let im_test = DynamicImage::ImageRgb8(ViewImage::new(64, 64));
367 let mut world = World::from_real_im(
368 im_test,
369 ThumbIms::default(),
370 ToolsDataMap::new(),
371 None,
372 Some("superimage.png".to_string()),
373 Path::new("superimage.png"),
374 Some(0),
375 );
376 world.data.meta_data.flags.is_loading_screen_active = Some(false);
377
378 let history = History::default();
379 (world, history)
380}
381#[test]
382fn test_import_export() {
383 init_tracing_for_tests();
384 fn test(testpath: &Path) {
385 let (mut world, history) = test_data();
386 let data = get_specific_mut(&mut world).unwrap();
387 let json_str = fs::read_to_string(testpath).unwrap();
388 let reference_data = AttributesToolData::deserialize_annotations(&json_str, None).unwrap();
389 tracing::debug!("reference_data: {:?}", reference_data);
390 data.export_path.path = testpath.to_path_buf();
391 data.options.import_export_trigger.trigger_import();
392 let events = Events::default();
393 let (world, _) = Attributes {}.events_tf(world, history, &events);
394 let annos = world.data.tools_data_map[ACTOR_NAME]
395 .specifics
396 .attributes()
397 .unwrap()
398 .anno_iter()
399 .collect::<HashMap<_, _>>();
400 tracing::debug!("annos: {:?}", annos);
401 for k in reference_data.keys() {
402 tracing::debug!("k: {:?}", k);
403 let (annos, _) = annos.get(k).unwrap();
404 let (ref_annos, _) = &reference_data[k];
405 assert_eq!(annos, ref_annos);
406 }
407 let current = get_annos(&world).unwrap();
408 for v in current.values() {
409 assert!(v.is_default());
410 }
411 }
412 let testpath = Path::new("resources/test_data/attr_import.json");
413 test(testpath);
414 let testpath = Path::new("resources/test_data/attr_import_untagged.json");
415 test(testpath);
416}
417
418#[test]
419fn test_add() {
420 init_tracing_for_tests();
421 let mut attr_tool = Attributes::new();
422 let events = Events::default();
423 let (mut world, history) = test_data();
424 let attr_data = get_specific_mut(&mut world).unwrap();
425 attr_data.options.is_addition_triggered = true;
426 attr_data.new_attr_name = "a attr".to_string();
427 attr_data.new_attr_val = ParamVal::Int(Some(1));
428 let (mut world, history) = attr_tool.events_tf(world, history, &events);
429 let attr_data = get_specific_mut(&mut world).unwrap();
430 attr_data.options.is_addition_triggered = true;
431 attr_data.new_attr_name = "c attr".to_string();
432 attr_data.new_attr_val = ParamVal::Int(Some(2));
433 let (mut world, history) = attr_tool.events_tf(world, history, &events);
434 let attr_data = get_specific_mut(&mut world).unwrap();
435 attr_data.options.is_addition_triggered = true;
436 attr_data.new_attr_name = "b attr".to_string();
437 attr_data.new_attr_val = ParamVal::Int(Some(3));
438 let (world, _) = attr_tool.events_tf(world, history, &events);
439 let data = get_specific(&world).unwrap();
440 let cam = data.current_attr_map.as_ref().unwrap();
441 let c_attr_val = cam.get("c attr").unwrap();
442 assert_eq!(c_attr_val, &ParamVal::Int(Some(2)));
443 let b_attr_val = cam.get("b attr").unwrap();
444 assert_eq!(b_attr_val, &ParamVal::Int(Some(3)));
445 let a_attr_val = cam.get("a attr").unwrap();
446 assert_eq!(a_attr_val, &ParamVal::Int(Some(1)));
447
448 assert_eq!(data.attr_names(), &["a attr", "b attr", "c attr"]);
450 assert_eq!(
451 data.attr_vals(),
452 &[
453 ParamVal::Int(Some(1)),
454 ParamVal::Int(Some(3)),
455 ParamVal::Int(Some(2)),
456 ]
457 );
458}
459#[test]
460fn test_rm_add() {
461 init_tracing_for_tests();
462 let (mut world, history) = test_data();
463 let attr_data = get_specific_mut(&mut world).unwrap();
464 attr_data.options.is_addition_triggered = true;
465 attr_data.new_attr_name = "test_attr".to_string();
466 attr_data.new_attr_val = ParamVal::Str("123".into());
467 let (mut world, history) = add_attribute(world, history, false);
468 let attr_data = get_specific_mut(&mut world).unwrap();
469 attr_data.options.removal_idx = Some(0);
470 let (mut world, _) = check_remove(world, history);
471 let attr_data = get_specific_mut(&mut world).unwrap();
472 assert!(!attr_data.options.is_addition_triggered);
473 assert!(attr_data.options.removal_idx.is_none());
474 assert_eq!(
475 attr_data.current_attr_map.as_ref().map(|cam| cam.len()),
476 Some(0)
477 );
478}