mindm.mindmanager_mac_as module¶
MacOS-specific implementation of the Mindmanager interface. This class implementation uses the native AppleScript approach.
This module provides MacOS platform-specific implementation for interacting with MindManager application, including functionality for manipulating topics, properties, relationships, and document structure.
- class mindm.mindmanager_mac_as.Mindmanager(charttype)¶
Bases:
object- MACOS_LIBRARY_FOLDER = 'C:\\Users\\runneradmin\\Library\\Application Support\\Mindjet\\MindManager\\XX\\English\\Library'¶
- MACOS_MERGE_ALL_WINDOWS = False¶
- __init__(charttype)¶
- add_document(max_topic_level)¶
Opens the correct template based on charttype and subtopic counts.
- add_relationship(guid1, guid2, label='')¶
- add_subtopic_to_topic(topic_id, topic_text)¶
Create a new subtopic under topic_id with topic_text. Return the new subtopic’s ID or None on failure.
- add_tag_to_topic(topic_id, tag_text, topic_guid)¶
- add_topic_link(guid1, guid2, label='')¶
- create_map_icons(map_icons)¶
- create_tags(tags: list[str], DUPLICATED_TAG: str)¶
- document_exists()¶
Returns True if there’s at least one open document in MindManager.
- finalize(max_topic_level)¶
Balance the map, activate MindManager, optionally merge windows, and clean up.
- get_active_document_object()¶
Return ‘document 1’ if a document is open, else None.
- get_central_topic() MindmapTopic¶
Return the central topic’s ID or None if not found.
- get_guid_from_topic(topic_id) str¶
- get_icons_from_topic(topic_id) list[MindmapIcon]¶
- get_image_from_topic(topic_id) MindmapImage¶
- get_level_from_topic(topic)¶
- get_library_folder()¶
- get_links_from_topic(topic_id) list[MindmapLink]¶
- get_mindmanager_object()¶
- get_mindmaptopic_from_topic(topic) MindmapTopic¶
Returns a MindmapTopic with guid, text, rtf and level, all retrieved via a single AppleScript call.
- get_mindmaptopic_from_topic_content(topic_id) MindmapTopic¶
Returns a MindmapTopic with guid, text, rtf, level, and notes, all retrieved via a single AppleScript call.
- get_mindmaptopic_from_topic_full(topic_id) MindmapTopic¶
Returns a MindmapTopic with guid, text, rtf, level, notes, and references, all via one AppleScript call. (links/icons/tags/image remain unimplemented.)
- get_notes_from_topic(topic_id) MindmapNotes¶
Return MindmapNotes or None.
- get_parent_from_topic(topic_id)¶
Return the parent’s ID or None if there is no parent or the topic doesn’t exist.
- get_references_from_topic(topic_id) list[MindmapReference]¶
Return a list of MindmapReference objects for the given topic.
- get_selection()¶
Return a list of topic IDs in the current selection.
- get_subtopics_from_topic(topic_id)¶
Return a list of subtopic IDs.
- get_tags_from_topic(topic_id) list[MindmapTag]¶
- get_text_from_topic(topic_id)¶
- get_title_from_topic(topic_id)¶
- get_topic_by_id(topic_id)¶
- get_version()¶
- set_document_background_image(path)¶
- set_text_to_topic(topic_id, topic_text)¶
Set the topic’s text (equivalent to topic.name.set).
- set_title_to_topic(topic_id, topic_rtf)¶
Set the topic’s title (equivalent to topic.title.set).
- set_topic_from_mindmap_topic(topic_id, mindmap_topic, map_icons)¶
Updates the topic’s text, RTF title, and notes from mindmap_topic via a single AppleScript call. Returns (refreshed_topic_id, original_topic_id).
1"""
2MacOS-specific implementation of the Mindmanager interface.
3This class implementation uses the native AppleScript approach.
4
5This module provides MacOS platform-specific implementation for interacting
6with MindManager application, including functionality for manipulating topics,
7properties, relationships, and document structure.
8"""
9
10import os
11import sys
12import json
13import subprocess
14
15from mindmap.mindmap import (
16 MindmapLink,
17 MindmapImage,
18 MindmapNotes,
19 MindmapIcon,
20 MindmapTag,
21 MindmapReference,
22 MindmapTopic,
23)
24import mindmap.serialization as mms
25
26APPLESCRIPT_READ = os.path.join(os.path.dirname(__file__), "as", "read.scpt")
27APPLESCRIPT_WRITE = os.path.join(os.path.dirname(__file__), "as", "write.scpt")
28
29def _run_applescript(script: str, args: list = None) -> str:
30 if args is None:
31 args = []
32
33 command = ["osascript", "-e", script] + args
34 try:
35 result = subprocess.run(
36 command,
37 capture_output=True,
38 text=True,
39 check=True
40 )
41 return result.stdout.strip()
42 except subprocess.CalledProcessError as e:
43 print(f"AppleScript error: {e.stderr}")
44 return ""
45
46def _run_compiled_applescript(script_path: str, args: list = None) -> str:
47 if args is None:
48 args = []
49
50 command = ["osascript", script_path] + (args or [])
51
52 try:
53 result = subprocess.run(
54 command,
55 capture_output=True,
56 text=True,
57 check=True
58 )
59 return result.stdout.strip()
60 except subprocess.CalledProcessError as e:
61 print("osascript returned", e.returncode)
62 print("--- stdout ---\n", e.stdout)
63 print("--- stderr ---\n", e.stderr)
64 return ""
65
66class Mindmanager:
67
68 MACOS_MERGE_ALL_WINDOWS = False
69 MACOS_LIBRARY_FOLDER = os.path.join(
70 os.path.expanduser("~"),
71 "Library",
72 "Application Support",
73 "Mindjet",
74 "MindManager",
75 "XX",
76 "English",
77 "Library"
78 )
79
80 def __init__(self, charttype):
81 self._charttype = charttype
82
83 # Get version from MindManager
84 script = 'tell application "MindManager" to return version'
85 version_str = _run_applescript(script)
86 if version_str:
87 self._version = version_str.split('.')[0]
88 else:
89 self._version = "0"
90
91 self._library_folder = self.MACOS_LIBRARY_FOLDER.replace("XX", self._version)
92 if self._version == '24':
93 orgchart_template_path = os.path.join(self._library_folder, "Templates", "Blank Templates", "Organization Chart.mmat")
94 else:
95 orgchart_template_path = os.path.join(self._library_folder, "Templates", "Blank Templates", "Org-Chart Map.mmat")
96 self._orgchart_template = orgchart_template_path
97 self._radial_template = os.path.join(self._library_folder, "Templates", "Blank Templates", "Radial Map.mmat")
98
99 def _read(self, modus, arguments = None) -> list['MindmapTopic']:
100 if arguments:
101 script_args = [modus] + arguments
102 else:
103 script_args = [modus]
104
105 json_string = _run_compiled_applescript(APPLESCRIPT_READ, script_args)
106
107 if not json_string:
108 return None # no data returned
109 try:
110 jsonObject = json.loads(json_string)
111 if isinstance(jsonObject, dict):
112 # It's a single tree structure
113 topic = self._dict_to_mindmap_topic(jsonObject)
114 return [topic] if topic else None
115 elif isinstance(jsonObject, list):
116 # It's a list of topic structures
117 topics = []
118 for item in jsonObject:
119 if isinstance(item, dict):
120 topic = self._dict_to_mindmap_topic(item)
121 if topic:
122 topics.append(topic)
123 return topics
124 else:
125 # Unexpected format
126 print(f"Unexpected JSON format received: {type(jsonObject)}")
127 return None
128 except json.JSONDecodeError as e:
129 print(f"Error parsing JSON from AppleScript: {e}")
130 return None
131
132 def _write(self, modus, mindmap_topic):
133 json_string = json.dumps(mms.serialize_object_simple(mindmap_topic))
134 script_args = [modus] + [json_string]
135 result = _run_compiled_applescript(APPLESCRIPT_WRITE, script_args)
136 return result
137
138 def _dict_to_mindmap_topic(self, node_dict: dict) -> 'MindmapTopic':
139 """
140 Helper to recursively convert a dict of the form:
141 { "guid": ..., "text": ..., "level": ..., "notes": ..., "subtopics": [ ... ] }
142 into a MindmapTopic object with subtopics.
143 """
144 notes_obj = None
145 if node_dict.get("notes"):
146 notes_obj = MindmapNotes(text=node_dict["notes"])
147 references = []
148 for ref in node_dict.get("references", []) or []:
149 if not isinstance(ref, dict):
150 continue
151 direction = 0
152 try:
153 direction = int(ref.get("direction", 0) or 0)
154 except (TypeError, ValueError):
155 direction = 0
156 references.append(
157 MindmapReference(
158 direction=direction,
159 guid_1=ref.get("guid_1", ""),
160 guid_2=ref.get("guid_2", ""),
161 )
162 )
163 tags = []
164 for tag in node_dict.get("tags", []) or []:
165 if isinstance(tag, dict):
166 text_val = tag.get("text", "")
167 else:
168 text_val = tag
169 if text_val:
170 tags.append(MindmapTag(text=text_val))
171 topic = MindmapTopic(
172 guid=node_dict.get("guid", ""),
173 text=node_dict.get("text", ""),
174 level=int(node_dict.get("level", 0)),
175 notes=notes_obj,
176 tags=tags,
177 references=references,
178 )
179 subtopics = node_dict.get("subtopics", [])
180 for child_dict in subtopics:
181 child_topic = self._dict_to_mindmap_topic(child_dict)
182 if child_topic:
183 topic.subtopics.append(child_topic)
184 child_topic.parent = topic
185
186 return topic
187
188 def get_mindmanager_object(self):
189 return None
190
191 def get_active_document_object(self):
192 """
193 Return 'document 1' if a document is open, else None.
194 """
195 return "document 1" if self.document_exists() else None
196
197 def get_library_folder(self):
198 return self._library_folder
199
200 def get_version(self):
201 return self._version
202
203 def set_document_background_image(self, path):
204 pass
205
206 def document_exists(self):
207 """
208 Returns True if there's at least one open document in MindManager.
209 """
210 script = '''
211 tell application "MindManager"
212 if (count of documents) > 0 then
213 return "true"
214 else
215 return "false"
216 end if
217 end tell
218 '''
219 result = _run_applescript(script)
220 return (result == "true")
221
222 def get_central_topic(self) -> 'MindmapTopic':
223 """
224 Return the central topic's ID or None if not found.
225 """
226 result = self._read("getTree")
227 return result[0] if result else None
228
229 def get_mindmaptopic_from_topic(self, topic) -> 'MindmapTopic':
230 """
231 Returns a MindmapTopic with guid, text, rtf and level,
232 all retrieved via a single AppleScript call.
233 """
234 if not topic:
235 return None
236 topic_id = topic.guid
237 if not topic_id:
238 return None
239
240 # Single AppleScript to grab all basic properties at once:
241 script = f'''
242 tell application "MindManager"
243 try
244 set theTopic to first topic of document 1 whose id is "{topic_id}"
245 set theGUID to id of theTopic
246 set theName to name of theTopic
247 set theTitle to title of theTopic
248 set theLevel to level of theTopic
249 return theGUID & "%%" & theName & "%%" & theTitle & "%%" & (theLevel as text)
250 on error
251 return ""
252 end try
253 end tell
254 '''
255 result = _run_applescript(script)
256 if not result:
257 return None # topic not found or error
258
259 parts = result.split("%%", 3) # we expect exactly 4 parts
260 if len(parts) < 4:
261 return None
262
263 theGUID, theName, theTitle, theLevelStr = parts
264
265 # Convert level to integer if possible
266 try:
267 theLevel = int(theLevelStr)
268 except ValueError:
269 theLevel = None
270
271 # Clean up the text property so it mimics your old replacements
272 theName = theName.replace('"', '`').replace("'", "`").replace("\r", "").replace("\n", "")
273
274 # Construct and return the MindmapTopic
275 return MindmapTopic(
276 guid=theGUID,
277 text=theName,
278 rtf=theTitle,
279 level=theLevel,
280 )
281
282 def get_mindmaptopic_from_topic_content(self, topic_id) -> 'MindmapTopic':
283 """
284 Returns a MindmapTopic with guid, text, rtf, level, and notes,
285 all retrieved via a single AppleScript call.
286 """
287 if not topic_id:
288 return None
289
290 # Single AppleScript to grab all basic properties at once:
291 script = f'''
292 tell application "MindManager"
293 try
294 set theTopic to first topic of document 1 whose id is "{topic_id}"
295 set theGUID to id of theTopic
296 set theName to name of theTopic
297 set theTitle to title of theTopic
298 set theLevel to level of theTopic
299 set theNotes to notes of theTopic
300 return theGUID & "%%" & theName & "%%" & theTitle & "%%" & (theLevel as text) & "%%" & theNotes
301 on error
302 return ""
303 end try
304 end tell
305 '''
306 result = _run_applescript(script)
307 if not result:
308 return None # topic not found or error
309
310 parts = result.split("%%", 4) # we expect exactly 5 parts
311 if len(parts) < 5:
312 return None
313
314 theGUID, theName, theTitle, theLevelStr, theNotes = parts
315
316 # Convert level to integer if possible
317 try:
318 theLevel = int(theLevelStr)
319 except ValueError:
320 theLevel = None
321
322 # Clean up the text property so it mimics your old replacements
323 theName = theName.replace('"', '`').replace("'", "`").replace("\r", "").replace("\n", "")
324
325 # Build the MindmapNotes object if notes are non-empty
326 notes_obj = MindmapNotes(text=theNotes) if theNotes else None
327
328 # Construct and return the MindmapTopic
329 return MindmapTopic(
330 guid=theGUID,
331 text=theName,
332 rtf=theTitle,
333 level=theLevel,
334 notes=notes_obj,
335 )
336
337 def get_mindmaptopic_from_topic_full(self, topic_id) -> 'MindmapTopic':
338 """
339 Returns a MindmapTopic with guid, text, rtf, level, notes, and references,
340 all via one AppleScript call. (links/icons/tags/image remain unimplemented.)
341 """
342 if not topic_id:
343 return None
344
345 # Single AppleScript to grab all properties + references
346 script = f'''
347 tell application "MindManager"
348 try
349 set theTopic to first topic of document 1 whose id is "{topic_id}"
350 set theGUID to id of theTopic
351 set theName to name of theTopic
352 set theTitle to title of theTopic
353 set theLevel to level of theTopic
354 set theNotes to notes of theTopic
355 set rels to relationships of theTopic
356 set referencesString to ""
357 repeat with r in rels
358 set sLoc to id of (starting location of r)
359 set eLoc to id of (ending location of r)
360 set referencesString to referencesString & sLoc & "||" & eLoc & "||--||"
361 end repeat
362 return theGUID & "%%" & theName & "%%" & theTitle & "%%" & (theLevel as text) & "%%" & theNotes & "%%" & referencesString
363 on error
364 return ""
365 end try
366 end tell
367 '''
368 result = _run_applescript(script)
369 if not result:
370 return None
371
372 # We expect 6 parts: guid, name, title, level, notes, referencesString
373 parts = result.split("%%", 5)
374 if len(parts) < 6:
375 return None
376
377 theGUID, theName, theTitle, theLevelStr, theNotes, referencesRaw = parts
378
379 # Convert level to integer if possible
380 try:
381 theLevel = int(theLevelStr)
382 except ValueError:
383 theLevel = None
384
385 # Clean up the text property
386 theName = theName.replace('"', '`').replace("'", "`").replace("\r", "").replace("\n", "")
387
388 # Build the MindmapNotes object if notes are non-empty
389 notes_obj = MindmapNotes(text=theNotes) if theNotes else None
390
391 # Parse references:
392 # referencesRaw might look like "GUID1||GUID2||--||GUID3||GUID4||--||"
393 references = []
394 if referencesRaw:
395 rel_chunks = referencesRaw.split("||--||")
396 for chunk in rel_chunks:
397 chunk = chunk.strip()
398 if not chunk:
399 continue
400 pair = chunk.split("||")
401 if len(pair) == 2:
402 sLoc, eLoc = pair
403 if sLoc == theGUID: # If it matches the old pattern
404 references.append(
405 MindmapReference(direction=1, guid_1=sLoc, guid_2=eLoc)
406 )
407 else:
408 # Or handle direction=2 or other logic if needed
409 pass
410
411 # For now, links, icons, tags, image remain unimplemented => empty
412 links = []
413 icons = []
414 tags = []
415 image = None
416
417 return MindmapTopic(
418 guid=theGUID,
419 text=theName,
420 rtf=theTitle,
421 level=theLevel,
422 notes=notes_obj,
423 links=links,
424 image=image,
425 icons=icons,
426 tags=tags,
427 references=references,
428 )
429
430 def get_topic_by_id(self, topic_id):
431 return topic_id
432
433 def get_selection(self):
434 """
435 Return a list of topic IDs in the current selection.
436 """
437 result = self._read("getSelection")
438 return result
439
440 def get_level_from_topic(self, topic):
441 if not topic:
442 return None
443 topic_id = topic.guid
444 if not topic_id:
445 return None
446 script = f'''
447 tell application "MindManager"
448 try
449 set theTopic to first topic of document 1 whose id is "{topic_id}"
450 return level of theTopic
451 on error
452 return ""
453 end try
454 end tell
455 '''
456 level_str = _run_applescript(script)
457 return int(level_str) if level_str.isdigit() else None
458
459 def get_text_from_topic(self, topic_id):
460 if not topic_id:
461 return ""
462 script = f'''
463 tell application "MindManager"
464 try
465 set theTopic to first topic of document 1 whose id is "{topic_id}"
466 return name of theTopic
467 on error
468 return ""
469 end try
470 end tell
471 '''
472 text = _run_applescript(script)
473 # Replace certain characters (as in original code)
474 text = text.replace('"', '`').replace("'", "`").replace("\r", "").replace("\n", "")
475 return text
476
477 def get_title_from_topic(self, topic_id):
478 if not topic_id:
479 return ""
480 script = f'''
481 tell application "MindManager"
482 try
483 set theTopic to first topic of document 1 whose id is "{topic_id}"
484 return title of theTopic
485 on error
486 return ""
487 end try
488 end tell
489 '''
490 return _run_applescript(script)
491
492 def get_subtopics_from_topic(self, topic_id):
493 """
494 Return a list of subtopic IDs.
495 """
496 if not topic_id:
497 return []
498 script = f'''
499 tell application "MindManager"
500 try
501 set theTopic to first topic of document 1 whose id is "{topic_id}"
502 set subTs to subtopics of theTopic
503 set output to ""
504 repeat with t in subTs
505 set output to output & (id of t) & linefeed
506 end repeat
507 return output
508 on error
509 return ""
510 end try
511 end tell
512 '''
513 raw = _run_applescript(script)
514 return [x.strip() for x in raw.splitlines() if x.strip()]
515
516 def get_links_from_topic(self, topic_id) -> list[MindmapLink]:
517 return []
518
519 def get_image_from_topic(self, topic_id) -> MindmapImage:
520 return None
521
522 def get_icons_from_topic(self, topic_id) -> list[MindmapIcon]:
523 return []
524
525 def get_notes_from_topic(self, topic_id) -> MindmapNotes:
526 """
527 Return MindmapNotes or None.
528 """
529 if not topic_id:
530 return None
531 script = f'''
532 tell application "MindManager"
533 try
534 set theTopic to first topic of document 1 whose id is "{topic_id}"
535 return notes of theTopic
536 on error
537 return ""
538 end try
539 end tell
540 '''
541 notes_text = _run_applescript(script)
542 if notes_text:
543 return MindmapNotes(text=notes_text)
544 return None
545
546 def get_tags_from_topic(self, topic_id) -> list[MindmapTag]:
547 return []
548
549 def get_references_from_topic(self, topic_id) -> list[MindmapReference]:
550 """
551 Return a list of MindmapReference objects for the given topic.
552 """
553 references = []
554 if not topic_id:
555 return references
556
557 script = f'''
558 tell application "MindManager"
559 try
560 set theTopic to first topic of document 1 whose id is "{topic_id}"
561 set rels to relationships of theTopic
562 if (count of rels) = 0 then
563 return ""
564 end if
565 set outList to ""
566 repeat with r in rels
567 set sLoc to id of (starting location of r)
568 set eLoc to id of (ending location of r)
569 set outList to outList & sLoc & "||" & eLoc & linefeed
570 end repeat
571 return outList
572 on error
573 return ""
574 end try
575 end tell
576 '''
577 raw = _run_applescript(script)
578 for line in raw.splitlines():
579 parts = line.split("||")
580 if len(parts) == 2:
581 sLoc, eLoc = parts
582 if sLoc == topic_id:
583 references.append(
584 MindmapReference(
585 direction=1,
586 guid_1=sLoc,
587 guid_2=eLoc
588 )
589 )
590 return references
591
592 def get_guid_from_topic(self, topic_id) -> str:
593 return topic_id if topic_id else ""
594
595 def add_subtopic_to_topic(self, topic_id, topic_text):
596 """
597 Create a new subtopic under `topic_id` with `topic_text`.
598 Return the new subtopic's ID or None on failure.
599 """
600 if not topic_id:
601 return None
602 safe_text = topic_text.replace('"', '\\"')
603 script = f'''
604 tell application "MindManager"
605 try
606 set parentTopic to first topic of document 1 whose id is "{topic_id}"
607 set newT to make new topic at end of subtopics of parentTopic with properties {{name:"{safe_text}"}}
608 return id of newT
609 on error
610 return ""
611 end try
612 end tell
613 '''
614 new_id = _run_applescript(script)
615 return new_id if new_id else None
616
617 def get_parent_from_topic(self, topic_id):
618 """
619 Return the parent's ID or None if there is no parent or the topic doesn't exist.
620 """
621 if not topic_id:
622 return None
623 script = f'''
624 tell application "MindManager"
625 try
626 set theTopic to first topic of document 1 whose id is "{topic_id}"
627 set p to parent of theTopic
628 if p is not missing value then
629 return id of p
630 else
631 return ""
632 end if
633 on error
634 return ""
635 end try
636 end tell
637 '''
638 result = _run_applescript(script)
639 return result if result else None
640
641 def set_text_to_topic(self, topic_id, topic_text):
642 """
643 Set the topic's text (equivalent to topic.name.set).
644 """
645 if not topic_id:
646 return
647 safe_text = topic_text.replace('"', '\\"')
648 script = f'''
649 tell application "MindManager"
650 try
651 set theTopic to first topic of document 1 whose id is "{topic_id}"
652 set name of theTopic to "{safe_text}"
653 end try
654 end tell
655 '''
656 _run_applescript(script)
657
658 def set_title_to_topic(self, topic_id, topic_rtf):
659 """
660 Set the topic's title (equivalent to topic.title.set).
661 """
662 if not topic_id:
663 return
664 safe_text = topic_rtf.replace('"', '\\"')
665 script = f'''
666 tell application "MindManager"
667 try
668 set theTopic to first topic of document 1 whose id is "{topic_id}"
669 set title of theTopic to "{safe_text}"
670 end try
671 end tell
672 '''
673 _run_applescript(script)
674
675 def add_tag_to_topic(self, topic_id, tag_text, topic_guid):
676 pass
677
678 def set_topic_from_mindmap_topic(self, topic_id, mindmap_topic, map_icons):
679 """
680 Updates the topic's text, RTF title, and notes from `mindmap_topic`
681 via a single AppleScript call.
682 Returns (refreshed_topic_id, original_topic_id).
683 """
684 if not topic_id and not mindmap_topic:
685 return None, None
686
687 if not topic_id:
688 try:
689 self._write("writeTree", mindmap_topic)
690 return None, None
691
692 except Exception as e:
693 print(f"Error in set_topic_from_mindmap_topic: {e}")
694 return None, None
695 else:
696 try:
697 script_lines = []
698 script_lines.append('tell application "MindManager"')
699 script_lines.append(' try')
700 script_lines.append(f' set theTopic to first topic of document 1 whose id is "{topic_id}"')
701 safe_text = (mindmap_topic.text or "").replace('"', '\\"')
702 script_lines.append(f' set name of theTopic to "{safe_text}"')
703 if mindmap_topic.rtf:
704 safe_rtf = mindmap_topic.rtf.replace('"', '\\"')
705 script_lines.append(f' set title of theTopic to "{safe_rtf}"')
706 if mindmap_topic.notes:
707 safe_notes = (mindmap_topic.notes.text or "").replace('"', '\\"')
708 script_lines.append(f' set notes of theTopic to "{safe_notes}"')
709 script_lines.append(' return id of theTopic')
710 script_lines.append(' on error errMsg')
711 script_lines.append(' return ""')
712 script_lines.append(' end try')
713 script_lines.append('end tell')
714 full_script = "\n".join(script_lines)
715 refreshed_id = _run_applescript(full_script)
716 if not refreshed_id:
717 return None, None
718 return refreshed_id, topic_id
719
720 except Exception as e:
721 print(f"Error in set_topic_from_mindmap_topic: {e}")
722 return None, None
723
724 def create_map_icons(self, map_icons):
725 pass
726
727 def create_tags(self, tags: list[str], DUPLICATED_TAG: str):
728 pass
729
730 def add_relationship(self, guid1, guid2, label=''):
731 if not guid1 or not guid2:
732 print("Error in add_relationship: One or both topic IDs missing.")
733 return
734 script = f'''
735 tell application "MindManager"
736 try
737 set t1 to first topic of document 1 whose id is "{guid1}"
738 set t2 to first topic of document 1 whose id is "{guid2}"
739 if t1 is not missing value and t2 is not missing value then
740 make new relationship with properties {{starting location:t1, ending location:t2}}
741 end if
742 on error errMsg
743 return ""
744 end try
745 end tell
746 '''
747 _run_applescript(script)
748
749 def add_topic_link(self, guid1, guid2, label=''):
750 pass
751
752 def add_document(self, max_topic_level):
753 """
754 Opens the correct template based on charttype and subtopic counts.
755 """
756 if not self.document_exists():
757 cnt_subtopics = 0
758 else:
759 script_count = '''
760 tell application "MindManager"
761 set c to count of subtopics of central topic of document 1
762 return c
763 end tell
764 '''
765 res = _run_applescript(script_count)
766 try:
767 cnt_subtopics = int(res)
768 except:
769 cnt_subtopics = 0
770
771 if self._charttype == "orgchart":
772 template_alias = self._orgchart_template
773 elif self._charttype == "radial":
774 template_alias = self._radial_template
775 else:
776 # "auto"
777 if max_topic_level > 2 and cnt_subtopics > 4:
778 template_alias = self._orgchart_template
779 else:
780 template_alias = self._radial_template
781
782 safe_path = template_alias.replace('"', '\\"')
783 script_open = f'''
784 tell application "MindManager"
785 open POSIX file "{safe_path}"
786 end tell
787 '''
788 _run_applescript(script_open)
789
790 def finalize(self, max_topic_level):
791 """
792 Balance the map, activate MindManager, optionally merge windows, and clean up.
793 """
794 if not self.document_exists():
795 return
796
797 # Balance map
798 script_balance = '''
799 tell application "MindManager"
800 try
801 balance map of document 1
802 end try
803 end tell
804 '''
805 _run_applescript(script_balance)
806
807 # Activate MindManager
808 script_activate = '''
809 tell application "MindManager"
810 activate
811 end tell
812 '''
813 _run_applescript(script_activate)
814
815 # Optionally merge all windows
816 if self.MACOS_MERGE_ALL_WINDOWS:
817 self.merge_windows()
818
819 # No persistent object references to clear
820 pass