pomera-ai-commander 0.1.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/LICENSE +21 -0
- package/README.md +680 -0
- package/bin/pomera-ai-commander.js +62 -0
- package/core/__init__.py +66 -0
- package/core/__pycache__/__init__.cpython-313.pyc +0 -0
- package/core/__pycache__/app_context.cpython-313.pyc +0 -0
- package/core/__pycache__/async_text_processor.cpython-313.pyc +0 -0
- package/core/__pycache__/backup_manager.cpython-313.pyc +0 -0
- package/core/__pycache__/backup_recovery_manager.cpython-313.pyc +0 -0
- package/core/__pycache__/content_hash_cache.cpython-313.pyc +0 -0
- package/core/__pycache__/context_menu.cpython-313.pyc +0 -0
- package/core/__pycache__/data_validator.cpython-313.pyc +0 -0
- package/core/__pycache__/database_connection_manager.cpython-313.pyc +0 -0
- package/core/__pycache__/database_curl_settings_manager.cpython-313.pyc +0 -0
- package/core/__pycache__/database_promera_ai_settings_manager.cpython-313.pyc +0 -0
- package/core/__pycache__/database_schema.cpython-313.pyc +0 -0
- package/core/__pycache__/database_schema_manager.cpython-313.pyc +0 -0
- package/core/__pycache__/database_settings_manager.cpython-313.pyc +0 -0
- package/core/__pycache__/database_settings_manager_interface.cpython-313.pyc +0 -0
- package/core/__pycache__/dialog_manager.cpython-313.pyc +0 -0
- package/core/__pycache__/efficient_line_numbers.cpython-313.pyc +0 -0
- package/core/__pycache__/error_handler.cpython-313.pyc +0 -0
- package/core/__pycache__/error_service.cpython-313.pyc +0 -0
- package/core/__pycache__/event_consolidator.cpython-313.pyc +0 -0
- package/core/__pycache__/memory_efficient_text_widget.cpython-313.pyc +0 -0
- package/core/__pycache__/migration_manager.cpython-313.pyc +0 -0
- package/core/__pycache__/migration_test_suite.cpython-313.pyc +0 -0
- package/core/__pycache__/migration_validator.cpython-313.pyc +0 -0
- package/core/__pycache__/optimized_find_replace.cpython-313.pyc +0 -0
- package/core/__pycache__/optimized_pattern_engine.cpython-313.pyc +0 -0
- package/core/__pycache__/optimized_search_highlighter.cpython-313.pyc +0 -0
- package/core/__pycache__/performance_monitor.cpython-313.pyc +0 -0
- package/core/__pycache__/persistence_manager.cpython-313.pyc +0 -0
- package/core/__pycache__/progressive_stats_calculator.cpython-313.pyc +0 -0
- package/core/__pycache__/regex_pattern_cache.cpython-313.pyc +0 -0
- package/core/__pycache__/regex_pattern_library.cpython-313.pyc +0 -0
- package/core/__pycache__/search_operation_manager.cpython-313.pyc +0 -0
- package/core/__pycache__/settings_defaults_registry.cpython-313.pyc +0 -0
- package/core/__pycache__/settings_integrity_validator.cpython-313.pyc +0 -0
- package/core/__pycache__/settings_serializer.cpython-313.pyc +0 -0
- package/core/__pycache__/settings_validator.cpython-313.pyc +0 -0
- package/core/__pycache__/smart_stats_calculator.cpython-313.pyc +0 -0
- package/core/__pycache__/statistics_update_manager.cpython-313.pyc +0 -0
- package/core/__pycache__/stats_config_manager.cpython-313.pyc +0 -0
- package/core/__pycache__/streaming_text_handler.cpython-313.pyc +0 -0
- package/core/__pycache__/task_scheduler.cpython-313.pyc +0 -0
- package/core/__pycache__/visibility_monitor.cpython-313.pyc +0 -0
- package/core/__pycache__/widget_cache.cpython-313.pyc +0 -0
- package/core/app_context.py +482 -0
- package/core/async_text_processor.py +422 -0
- package/core/backup_manager.py +656 -0
- package/core/backup_recovery_manager.py +1034 -0
- package/core/content_hash_cache.py +509 -0
- package/core/context_menu.py +313 -0
- package/core/data_validator.py +1067 -0
- package/core/database_connection_manager.py +745 -0
- package/core/database_curl_settings_manager.py +609 -0
- package/core/database_promera_ai_settings_manager.py +447 -0
- package/core/database_schema.py +412 -0
- package/core/database_schema_manager.py +396 -0
- package/core/database_settings_manager.py +1508 -0
- package/core/database_settings_manager_interface.py +457 -0
- package/core/dialog_manager.py +735 -0
- package/core/efficient_line_numbers.py +511 -0
- package/core/error_handler.py +747 -0
- package/core/error_service.py +431 -0
- package/core/event_consolidator.py +512 -0
- package/core/mcp/__init__.py +43 -0
- package/core/mcp/__pycache__/__init__.cpython-313.pyc +0 -0
- package/core/mcp/__pycache__/protocol.cpython-313.pyc +0 -0
- package/core/mcp/__pycache__/schema.cpython-313.pyc +0 -0
- package/core/mcp/__pycache__/server_stdio.cpython-313.pyc +0 -0
- package/core/mcp/__pycache__/tool_registry.cpython-313.pyc +0 -0
- package/core/mcp/protocol.py +288 -0
- package/core/mcp/schema.py +251 -0
- package/core/mcp/server_stdio.py +299 -0
- package/core/mcp/tool_registry.py +2345 -0
- package/core/memory_efficient_text_widget.py +712 -0
- package/core/migration_manager.py +915 -0
- package/core/migration_test_suite.py +1086 -0
- package/core/migration_validator.py +1144 -0
- package/core/optimized_find_replace.py +715 -0
- package/core/optimized_pattern_engine.py +424 -0
- package/core/optimized_search_highlighter.py +553 -0
- package/core/performance_monitor.py +675 -0
- package/core/persistence_manager.py +713 -0
- package/core/progressive_stats_calculator.py +632 -0
- package/core/regex_pattern_cache.py +530 -0
- package/core/regex_pattern_library.py +351 -0
- package/core/search_operation_manager.py +435 -0
- package/core/settings_defaults_registry.py +1087 -0
- package/core/settings_integrity_validator.py +1112 -0
- package/core/settings_serializer.py +558 -0
- package/core/settings_validator.py +1824 -0
- package/core/smart_stats_calculator.py +710 -0
- package/core/statistics_update_manager.py +619 -0
- package/core/stats_config_manager.py +858 -0
- package/core/streaming_text_handler.py +723 -0
- package/core/task_scheduler.py +596 -0
- package/core/update_pattern_library.py +169 -0
- package/core/visibility_monitor.py +596 -0
- package/core/widget_cache.py +498 -0
- package/mcp.json +61 -0
- package/package.json +57 -0
- package/pomera.py +7483 -0
- package/pomera_mcp_server.py +144 -0
- package/tools/__init__.py +5 -0
- package/tools/__pycache__/__init__.cpython-313.pyc +0 -0
- package/tools/__pycache__/ai_tools.cpython-313.pyc +0 -0
- package/tools/__pycache__/ascii_art_generator.cpython-313.pyc +0 -0
- package/tools/__pycache__/base64_tools.cpython-313.pyc +0 -0
- package/tools/__pycache__/base_tool.cpython-313.pyc +0 -0
- package/tools/__pycache__/case_tool.cpython-313.pyc +0 -0
- package/tools/__pycache__/column_tools.cpython-313.pyc +0 -0
- package/tools/__pycache__/cron_tool.cpython-313.pyc +0 -0
- package/tools/__pycache__/curl_history.cpython-313.pyc +0 -0
- package/tools/__pycache__/curl_processor.cpython-313.pyc +0 -0
- package/tools/__pycache__/curl_settings.cpython-313.pyc +0 -0
- package/tools/__pycache__/curl_tool.cpython-313.pyc +0 -0
- package/tools/__pycache__/diff_viewer.cpython-313.pyc +0 -0
- package/tools/__pycache__/email_extraction_tool.cpython-313.pyc +0 -0
- package/tools/__pycache__/email_header_analyzer.cpython-313.pyc +0 -0
- package/tools/__pycache__/extraction_tools.cpython-313.pyc +0 -0
- package/tools/__pycache__/find_replace.cpython-313.pyc +0 -0
- package/tools/__pycache__/folder_file_reporter.cpython-313.pyc +0 -0
- package/tools/__pycache__/folder_file_reporter_adapter.cpython-313.pyc +0 -0
- package/tools/__pycache__/generator_tools.cpython-313.pyc +0 -0
- package/tools/__pycache__/hash_generator.cpython-313.pyc +0 -0
- package/tools/__pycache__/html_tool.cpython-313.pyc +0 -0
- package/tools/__pycache__/huggingface_helper.cpython-313.pyc +0 -0
- package/tools/__pycache__/jsonxml_tool.cpython-313.pyc +0 -0
- package/tools/__pycache__/line_tools.cpython-313.pyc +0 -0
- package/tools/__pycache__/list_comparator.cpython-313.pyc +0 -0
- package/tools/__pycache__/markdown_tools.cpython-313.pyc +0 -0
- package/tools/__pycache__/mcp_widget.cpython-313.pyc +0 -0
- package/tools/__pycache__/notes_widget.cpython-313.pyc +0 -0
- package/tools/__pycache__/number_base_converter.cpython-313.pyc +0 -0
- package/tools/__pycache__/regex_extractor.cpython-313.pyc +0 -0
- package/tools/__pycache__/slug_generator.cpython-313.pyc +0 -0
- package/tools/__pycache__/sorter_tools.cpython-313.pyc +0 -0
- package/tools/__pycache__/string_escape_tool.cpython-313.pyc +0 -0
- package/tools/__pycache__/text_statistics_tool.cpython-313.pyc +0 -0
- package/tools/__pycache__/text_wrapper.cpython-313.pyc +0 -0
- package/tools/__pycache__/timestamp_converter.cpython-313.pyc +0 -0
- package/tools/__pycache__/tool_loader.cpython-313.pyc +0 -0
- package/tools/__pycache__/translator_tools.cpython-313.pyc +0 -0
- package/tools/__pycache__/url_link_extractor.cpython-313.pyc +0 -0
- package/tools/__pycache__/url_parser.cpython-313.pyc +0 -0
- package/tools/__pycache__/whitespace_tools.cpython-313.pyc +0 -0
- package/tools/__pycache__/word_frequency_counter.cpython-313.pyc +0 -0
- package/tools/ai_tools.py +2892 -0
- package/tools/ascii_art_generator.py +353 -0
- package/tools/base64_tools.py +184 -0
- package/tools/base_tool.py +511 -0
- package/tools/case_tool.py +309 -0
- package/tools/column_tools.py +396 -0
- package/tools/cron_tool.py +885 -0
- package/tools/curl_history.py +601 -0
- package/tools/curl_processor.py +1208 -0
- package/tools/curl_settings.py +503 -0
- package/tools/curl_tool.py +5467 -0
- package/tools/diff_viewer.py +1072 -0
- package/tools/email_extraction_tool.py +249 -0
- package/tools/email_header_analyzer.py +426 -0
- package/tools/extraction_tools.py +250 -0
- package/tools/find_replace.py +1751 -0
- package/tools/folder_file_reporter.py +1463 -0
- package/tools/folder_file_reporter_adapter.py +480 -0
- package/tools/generator_tools.py +1217 -0
- package/tools/hash_generator.py +256 -0
- package/tools/html_tool.py +657 -0
- package/tools/huggingface_helper.py +449 -0
- package/tools/jsonxml_tool.py +730 -0
- package/tools/line_tools.py +419 -0
- package/tools/list_comparator.py +720 -0
- package/tools/markdown_tools.py +562 -0
- package/tools/mcp_widget.py +1417 -0
- package/tools/notes_widget.py +973 -0
- package/tools/number_base_converter.py +373 -0
- package/tools/regex_extractor.py +572 -0
- package/tools/slug_generator.py +311 -0
- package/tools/sorter_tools.py +459 -0
- package/tools/string_escape_tool.py +393 -0
- package/tools/text_statistics_tool.py +366 -0
- package/tools/text_wrapper.py +431 -0
- package/tools/timestamp_converter.py +422 -0
- package/tools/tool_loader.py +710 -0
- package/tools/translator_tools.py +523 -0
- package/tools/url_link_extractor.py +262 -0
- package/tools/url_parser.py +205 -0
- package/tools/whitespace_tools.py +356 -0
- package/tools/word_frequency_counter.py +147 -0
|
@@ -0,0 +1,313 @@
|
|
|
1
|
+
"""
|
|
2
|
+
Context Menu Module for Text Widgets
|
|
3
|
+
|
|
4
|
+
This module provides a reusable right-click context menu for text widgets and entry fields
|
|
5
|
+
with standard operations: Cut, Copy, Paste, Select All, and Delete.
|
|
6
|
+
|
|
7
|
+
Features:
|
|
8
|
+
- Automatic detection of selected text
|
|
9
|
+
- Smart menu item enabling/disabling based on context
|
|
10
|
+
- Support for Text, Entry, and ScrolledText widgets
|
|
11
|
+
- Keyboard shortcuts displayed in menu
|
|
12
|
+
- Cross-platform compatibility
|
|
13
|
+
|
|
14
|
+
Author: Promera AI Commander
|
|
15
|
+
"""
|
|
16
|
+
|
|
17
|
+
import tkinter as tk
|
|
18
|
+
from tkinter import ttk, scrolledtext
|
|
19
|
+
from typing import Optional, Union
|
|
20
|
+
import platform
|
|
21
|
+
|
|
22
|
+
|
|
23
|
+
class TextContextMenu:
|
|
24
|
+
"""
|
|
25
|
+
Context menu manager for text widgets.
|
|
26
|
+
|
|
27
|
+
Provides right-click context menu with Cut, Copy, Paste, Select All, and Delete
|
|
28
|
+
operations. Automatically enables/disables menu items based on selection state
|
|
29
|
+
and clipboard content.
|
|
30
|
+
"""
|
|
31
|
+
|
|
32
|
+
def __init__(self, widget: Union[tk.Text, tk.Entry, scrolledtext.ScrolledText]):
|
|
33
|
+
"""
|
|
34
|
+
Initialize context menu for a text widget.
|
|
35
|
+
|
|
36
|
+
Args:
|
|
37
|
+
widget: The text widget to attach the context menu to
|
|
38
|
+
"""
|
|
39
|
+
self.widget = widget
|
|
40
|
+
self.menu = None
|
|
41
|
+
self._create_menu()
|
|
42
|
+
self._bind_events()
|
|
43
|
+
|
|
44
|
+
def _create_menu(self):
|
|
45
|
+
"""Create the context menu with standard operations."""
|
|
46
|
+
self.menu = tk.Menu(self.widget, tearoff=0)
|
|
47
|
+
|
|
48
|
+
# Determine keyboard shortcuts based on platform
|
|
49
|
+
if platform.system() == "Darwin": # macOS
|
|
50
|
+
cut_accel = "Cmd+X"
|
|
51
|
+
copy_accel = "Cmd+C"
|
|
52
|
+
paste_accel = "Cmd+V"
|
|
53
|
+
select_all_accel = "Cmd+A"
|
|
54
|
+
else: # Windows/Linux
|
|
55
|
+
cut_accel = "Ctrl+X"
|
|
56
|
+
copy_accel = "Ctrl+C"
|
|
57
|
+
paste_accel = "Ctrl+V"
|
|
58
|
+
select_all_accel = "Ctrl+A"
|
|
59
|
+
|
|
60
|
+
# Add menu items
|
|
61
|
+
self.menu.add_command(
|
|
62
|
+
label="Cut",
|
|
63
|
+
command=self._cut,
|
|
64
|
+
accelerator=cut_accel
|
|
65
|
+
)
|
|
66
|
+
self.menu.add_command(
|
|
67
|
+
label="Copy",
|
|
68
|
+
command=self._copy,
|
|
69
|
+
accelerator=copy_accel
|
|
70
|
+
)
|
|
71
|
+
self.menu.add_command(
|
|
72
|
+
label="Paste",
|
|
73
|
+
command=self._paste,
|
|
74
|
+
accelerator=paste_accel
|
|
75
|
+
)
|
|
76
|
+
self.menu.add_separator()
|
|
77
|
+
self.menu.add_command(
|
|
78
|
+
label="Select All",
|
|
79
|
+
command=self._select_all,
|
|
80
|
+
accelerator=select_all_accel
|
|
81
|
+
)
|
|
82
|
+
self.menu.add_command(
|
|
83
|
+
label="Delete",
|
|
84
|
+
command=self._delete
|
|
85
|
+
)
|
|
86
|
+
|
|
87
|
+
def _bind_events(self):
|
|
88
|
+
"""Bind right-click event to show menu."""
|
|
89
|
+
# Right-click on Windows/Linux
|
|
90
|
+
self.widget.bind("<Button-3>", self._show_menu)
|
|
91
|
+
|
|
92
|
+
# Right-click on macOS (also bind Button-2 for compatibility)
|
|
93
|
+
if platform.system() == "Darwin":
|
|
94
|
+
self.widget.bind("<Button-2>", self._show_menu)
|
|
95
|
+
self.widget.bind("<Control-Button-1>", self._show_menu)
|
|
96
|
+
|
|
97
|
+
def _show_menu(self, event):
|
|
98
|
+
"""
|
|
99
|
+
Show the context menu at the cursor position.
|
|
100
|
+
|
|
101
|
+
Args:
|
|
102
|
+
event: The mouse event
|
|
103
|
+
"""
|
|
104
|
+
# Update menu item states based on current context
|
|
105
|
+
self._update_menu_states()
|
|
106
|
+
|
|
107
|
+
# Show menu at cursor position
|
|
108
|
+
try:
|
|
109
|
+
self.menu.tk_popup(event.x_root, event.y_root)
|
|
110
|
+
finally:
|
|
111
|
+
self.menu.grab_release()
|
|
112
|
+
|
|
113
|
+
def _update_menu_states(self):
|
|
114
|
+
"""Update menu item states based on selection and clipboard."""
|
|
115
|
+
has_selection = self._has_selection()
|
|
116
|
+
is_readonly = self._is_readonly()
|
|
117
|
+
has_clipboard = self._has_clipboard_content()
|
|
118
|
+
|
|
119
|
+
# Cut: enabled if has selection and not readonly
|
|
120
|
+
if has_selection and not is_readonly:
|
|
121
|
+
self.menu.entryconfig("Cut", state="normal")
|
|
122
|
+
else:
|
|
123
|
+
self.menu.entryconfig("Cut", state="disabled")
|
|
124
|
+
|
|
125
|
+
# Copy: enabled if has selection
|
|
126
|
+
if has_selection:
|
|
127
|
+
self.menu.entryconfig("Copy", state="normal")
|
|
128
|
+
else:
|
|
129
|
+
self.menu.entryconfig("Copy", state="disabled")
|
|
130
|
+
|
|
131
|
+
# Paste: enabled if clipboard has content and not readonly
|
|
132
|
+
if has_clipboard and not is_readonly:
|
|
133
|
+
self.menu.entryconfig("Paste", state="normal")
|
|
134
|
+
else:
|
|
135
|
+
self.menu.entryconfig("Paste", state="disabled")
|
|
136
|
+
|
|
137
|
+
# Select All: always enabled if widget has content
|
|
138
|
+
if self._has_content():
|
|
139
|
+
self.menu.entryconfig("Select All", state="normal")
|
|
140
|
+
else:
|
|
141
|
+
self.menu.entryconfig("Select All", state="disabled")
|
|
142
|
+
|
|
143
|
+
# Delete: enabled if has selection and not readonly
|
|
144
|
+
if has_selection and not is_readonly:
|
|
145
|
+
self.menu.entryconfig("Delete", state="normal")
|
|
146
|
+
else:
|
|
147
|
+
self.menu.entryconfig("Delete", state="disabled")
|
|
148
|
+
|
|
149
|
+
def _has_selection(self) -> bool:
|
|
150
|
+
"""Check if widget has selected text."""
|
|
151
|
+
try:
|
|
152
|
+
if isinstance(self.widget, tk.Text):
|
|
153
|
+
return bool(self.widget.tag_ranges("sel"))
|
|
154
|
+
elif isinstance(self.widget, tk.Entry):
|
|
155
|
+
return self.widget.selection_present()
|
|
156
|
+
return False
|
|
157
|
+
except:
|
|
158
|
+
return False
|
|
159
|
+
|
|
160
|
+
def _is_readonly(self) -> bool:
|
|
161
|
+
"""Check if widget is read-only."""
|
|
162
|
+
try:
|
|
163
|
+
if isinstance(self.widget, tk.Text):
|
|
164
|
+
state = str(self.widget.cget("state"))
|
|
165
|
+
return state == "disabled"
|
|
166
|
+
elif isinstance(self.widget, tk.Entry):
|
|
167
|
+
state = str(self.widget.cget("state"))
|
|
168
|
+
return state == "disabled" or state == "readonly"
|
|
169
|
+
return False
|
|
170
|
+
except:
|
|
171
|
+
return False
|
|
172
|
+
|
|
173
|
+
def _has_clipboard_content(self) -> bool:
|
|
174
|
+
"""Check if clipboard has content."""
|
|
175
|
+
try:
|
|
176
|
+
self.widget.clipboard_get()
|
|
177
|
+
return True
|
|
178
|
+
except:
|
|
179
|
+
return False
|
|
180
|
+
|
|
181
|
+
def _has_content(self) -> bool:
|
|
182
|
+
"""Check if widget has any content."""
|
|
183
|
+
try:
|
|
184
|
+
if isinstance(self.widget, tk.Text):
|
|
185
|
+
content = self.widget.get("1.0", tk.END).strip()
|
|
186
|
+
return bool(content)
|
|
187
|
+
elif isinstance(self.widget, tk.Entry):
|
|
188
|
+
content = self.widget.get().strip()
|
|
189
|
+
return bool(content)
|
|
190
|
+
return False
|
|
191
|
+
except:
|
|
192
|
+
return False
|
|
193
|
+
|
|
194
|
+
def _cut(self):
|
|
195
|
+
"""Cut selected text to clipboard."""
|
|
196
|
+
try:
|
|
197
|
+
if isinstance(self.widget, tk.Text):
|
|
198
|
+
if self.widget.tag_ranges("sel"):
|
|
199
|
+
self.widget.event_generate("<<Cut>>")
|
|
200
|
+
elif isinstance(self.widget, tk.Entry):
|
|
201
|
+
if self.widget.selection_present():
|
|
202
|
+
self.widget.event_generate("<<Cut>>")
|
|
203
|
+
except Exception as e:
|
|
204
|
+
print(f"Error in cut operation: {e}")
|
|
205
|
+
|
|
206
|
+
def _copy(self):
|
|
207
|
+
"""Copy selected text to clipboard."""
|
|
208
|
+
try:
|
|
209
|
+
if isinstance(self.widget, tk.Text):
|
|
210
|
+
if self.widget.tag_ranges("sel"):
|
|
211
|
+
self.widget.event_generate("<<Copy>>")
|
|
212
|
+
elif isinstance(self.widget, tk.Entry):
|
|
213
|
+
if self.widget.selection_present():
|
|
214
|
+
self.widget.event_generate("<<Copy>>")
|
|
215
|
+
except Exception as e:
|
|
216
|
+
print(f"Error in copy operation: {e}")
|
|
217
|
+
|
|
218
|
+
def _paste(self):
|
|
219
|
+
"""Paste clipboard content at cursor position."""
|
|
220
|
+
try:
|
|
221
|
+
self.widget.event_generate("<<Paste>>")
|
|
222
|
+
except Exception as e:
|
|
223
|
+
print(f"Error in paste operation: {e}")
|
|
224
|
+
|
|
225
|
+
def _select_all(self):
|
|
226
|
+
"""Select all text in widget."""
|
|
227
|
+
try:
|
|
228
|
+
if isinstance(self.widget, tk.Text):
|
|
229
|
+
self.widget.tag_add("sel", "1.0", tk.END)
|
|
230
|
+
self.widget.mark_set("insert", "1.0")
|
|
231
|
+
self.widget.see("insert")
|
|
232
|
+
elif isinstance(self.widget, tk.Entry):
|
|
233
|
+
self.widget.select_range(0, tk.END)
|
|
234
|
+
self.widget.icursor(tk.END)
|
|
235
|
+
except Exception as e:
|
|
236
|
+
print(f"Error in select all operation: {e}")
|
|
237
|
+
|
|
238
|
+
def _delete(self):
|
|
239
|
+
"""Delete selected text."""
|
|
240
|
+
try:
|
|
241
|
+
if isinstance(self.widget, tk.Text):
|
|
242
|
+
if self.widget.tag_ranges("sel"):
|
|
243
|
+
self.widget.delete("sel.first", "sel.last")
|
|
244
|
+
elif isinstance(self.widget, tk.Entry):
|
|
245
|
+
if self.widget.selection_present():
|
|
246
|
+
self.widget.delete("sel.first", "sel.last")
|
|
247
|
+
except Exception as e:
|
|
248
|
+
print(f"Error in delete operation: {e}")
|
|
249
|
+
|
|
250
|
+
|
|
251
|
+
def add_context_menu(widget: Union[tk.Text, tk.Entry, scrolledtext.ScrolledText]) -> TextContextMenu:
|
|
252
|
+
"""
|
|
253
|
+
Add a context menu to a text widget.
|
|
254
|
+
|
|
255
|
+
This is a convenience function that creates and attaches a context menu
|
|
256
|
+
to the specified widget.
|
|
257
|
+
|
|
258
|
+
Args:
|
|
259
|
+
widget: The text widget to add context menu to
|
|
260
|
+
|
|
261
|
+
Returns:
|
|
262
|
+
TextContextMenu instance
|
|
263
|
+
|
|
264
|
+
Example:
|
|
265
|
+
>>> text_widget = tk.Text(parent)
|
|
266
|
+
>>> context_menu = add_context_menu(text_widget)
|
|
267
|
+
"""
|
|
268
|
+
return TextContextMenu(widget)
|
|
269
|
+
|
|
270
|
+
|
|
271
|
+
def add_context_menu_to_children(parent: tk.Widget, widget_types: Optional[tuple] = None):
|
|
272
|
+
"""
|
|
273
|
+
Recursively add context menus to all text widgets in a parent widget.
|
|
274
|
+
|
|
275
|
+
Args:
|
|
276
|
+
parent: Parent widget to search for text widgets
|
|
277
|
+
widget_types: Tuple of widget types to add context menu to.
|
|
278
|
+
Defaults to (tk.Text, tk.Entry, scrolledtext.ScrolledText)
|
|
279
|
+
|
|
280
|
+
Example:
|
|
281
|
+
>>> # Add context menus to all text widgets in a frame
|
|
282
|
+
>>> add_context_menu_to_children(my_frame)
|
|
283
|
+
"""
|
|
284
|
+
if widget_types is None:
|
|
285
|
+
widget_types = (tk.Text, tk.Entry)
|
|
286
|
+
|
|
287
|
+
try:
|
|
288
|
+
for child in parent.winfo_children():
|
|
289
|
+
# Add context menu if it's a text widget
|
|
290
|
+
if isinstance(child, widget_types):
|
|
291
|
+
# Check if context menu already exists
|
|
292
|
+
if not hasattr(child, '_context_menu'):
|
|
293
|
+
child._context_menu = add_context_menu(child)
|
|
294
|
+
|
|
295
|
+
# Recursively process children
|
|
296
|
+
if hasattr(child, 'winfo_children'):
|
|
297
|
+
add_context_menu_to_children(child, widget_types)
|
|
298
|
+
except Exception as e:
|
|
299
|
+
print(f"Error adding context menus to children: {e}")
|
|
300
|
+
|
|
301
|
+
|
|
302
|
+
# Convenience function for backward compatibility
|
|
303
|
+
def setup_text_context_menu(widget: Union[tk.Text, tk.Entry]) -> TextContextMenu:
|
|
304
|
+
"""
|
|
305
|
+
Setup context menu for a text widget (alias for add_context_menu).
|
|
306
|
+
|
|
307
|
+
Args:
|
|
308
|
+
widget: The text widget
|
|
309
|
+
|
|
310
|
+
Returns:
|
|
311
|
+
TextContextMenu instance
|
|
312
|
+
"""
|
|
313
|
+
return add_context_menu(widget)
|