backlogops-gui 0.7__tar.gz → 0.9__tar.gz

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.
Files changed (56) hide show
  1. {backlogops_gui-0.7 → backlogops_gui-0.9}/PKG-INFO +20 -7
  2. {backlogops_gui-0.7 → backlogops_gui-0.9}/README_pypi.md +14 -3
  3. {backlogops_gui-0.7 → backlogops_gui-0.9}/backlogops_gui.egg-info/PKG-INFO +20 -7
  4. {backlogops_gui-0.7 → backlogops_gui-0.9}/backlogops_gui.egg-info/SOURCES.txt +2 -13
  5. backlogops_gui-0.9/backlogops_gui.egg-info/requires.txt +6 -0
  6. {backlogops_gui-0.7 → backlogops_gui-0.9}/setup.py +6 -4
  7. {backlogops_gui-0.7 → backlogops_gui-0.9}/src/backlogops_gui/application.py +50 -27
  8. {backlogops_gui-0.7 → backlogops_gui-0.9}/src/backlogops_gui/backlog_dialogs.py +1 -1
  9. {backlogops_gui-0.7 → backlogops_gui-0.9}/src/backlogops_gui/backlog_window.py +2 -2
  10. {backlogops_gui-0.7 → backlogops_gui-0.9}/src/backlogops_gui/choice_dialogs.py +27 -3
  11. backlogops_gui-0.9/src/backlogops_gui/config_edit.py +226 -0
  12. {backlogops_gui-0.7 → backlogops_gui-0.9}/src/backlogops_gui/file_choosers.py +13 -0
  13. {backlogops_gui-0.7 → backlogops_gui-0.9}/src/backlogops_gui/format_dialogs.py +1 -1
  14. {backlogops_gui-0.7 → backlogops_gui-0.9}/src/backlogops_gui/jira_dialogs.py +1 -1
  15. {backlogops_gui-0.7 → backlogops_gui-0.9}/src/backlogops_gui/key_list_box.py +1 -1
  16. {backlogops_gui-0.7 → backlogops_gui-0.9}/src/backlogops_gui/modal_dialog.py +2 -2
  17. {backlogops_gui-0.7 → backlogops_gui-0.9}/src/backlogops_gui/report_windows.py +1 -1
  18. {backlogops_gui-0.7 → backlogops_gui-0.9}/src/backlogops_gui/token_dialog.py +1 -1
  19. backlogops_gui-0.7/backlogops_gui.egg-info/requires.txt +0 -4
  20. backlogops_gui-0.7/src/backlogops_gui/auto_scroll.py +0 -37
  21. backlogops_gui-0.7/src/backlogops_gui/close_binding.py +0 -47
  22. backlogops_gui-0.7/src/backlogops_gui/gui_style.py +0 -70
  23. backlogops_gui-0.7/src/backlogops_gui/gui_wizard.py +0 -138
  24. backlogops_gui-0.7/src/backlogops_gui/wizard_calendar.py +0 -170
  25. backlogops_gui-0.7/src/backlogops_gui/wizard_form.py +0 -670
  26. backlogops_gui-0.7/src/backlogops_gui/wizard_path.py +0 -177
  27. backlogops_gui-0.7/src/backlogops_gui/wizard_pick_row.py +0 -154
  28. backlogops_gui-0.7/src/backlogops_gui/wizard_prefill.py +0 -138
  29. backlogops_gui-0.7/src/backlogops_gui/wizard_table.py +0 -259
  30. backlogops_gui-0.7/src/backlogops_gui/wizard_typed.py +0 -286
  31. backlogops_gui-0.7/src/backlogops_gui/wizard_window.py +0 -364
  32. {backlogops_gui-0.7 → backlogops_gui-0.9}/LICENSE.txt +0 -0
  33. {backlogops_gui-0.7 → backlogops_gui-0.9}/backlogops_gui.egg-info/dependency_links.txt +0 -0
  34. {backlogops_gui-0.7 → backlogops_gui-0.9}/backlogops_gui.egg-info/entry_points.txt +0 -0
  35. {backlogops_gui-0.7 → backlogops_gui-0.9}/backlogops_gui.egg-info/top_level.txt +0 -0
  36. {backlogops_gui-0.7 → backlogops_gui-0.9}/pyproject.toml +0 -0
  37. {backlogops_gui-0.7 → backlogops_gui-0.9}/setup.cfg +0 -0
  38. {backlogops_gui-0.7 → backlogops_gui-0.9}/src/backlogops_gui/__init__.py +0 -0
  39. {backlogops_gui-0.7 → backlogops_gui-0.9}/src/backlogops_gui/__main__.py +0 -0
  40. {backlogops_gui-0.7 → backlogops_gui-0.9}/src/backlogops_gui/_migrate_warn.py +0 -0
  41. {backlogops_gui-0.7 → backlogops_gui-0.9}/src/backlogops_gui/backlog_actions.py +0 -0
  42. {backlogops_gui-0.7 → backlogops_gui-0.9}/src/backlogops_gui/backlog_io.py +0 -0
  43. {backlogops_gui-0.7 → backlogops_gui-0.9}/src/backlogops_gui/blog_version_reporter.py +0 -0
  44. {backlogops_gui-0.7 → backlogops_gui-0.9}/src/backlogops_gui/jira_actions.py +0 -0
  45. {backlogops_gui-0.7 → backlogops_gui-0.9}/src/backlogops_gui/jira_base.py +0 -0
  46. {backlogops_gui-0.7 → backlogops_gui-0.9}/src/backlogops_gui/jira_order.py +0 -0
  47. {backlogops_gui-0.7 → backlogops_gui-0.9}/src/backlogops_gui/jira_rank.py +0 -0
  48. {backlogops_gui-0.7 → backlogops_gui-0.9}/src/backlogops_gui/jira_read.py +0 -0
  49. {backlogops_gui-0.7 → backlogops_gui-0.9}/src/backlogops_gui/jira_rename.py +0 -0
  50. {backlogops_gui-0.7 → backlogops_gui-0.9}/src/backlogops_gui/jira_update.py +0 -0
  51. {backlogops_gui-0.7 → backlogops_gui-0.9}/src/backlogops_gui/jira_write.py +0 -0
  52. {backlogops_gui-0.7 → backlogops_gui-0.9}/src/backlogops_gui/log_buffer.py +0 -0
  53. {backlogops_gui-0.7 → backlogops_gui-0.9}/src/backlogops_gui/py.typed +0 -0
  54. {backlogops_gui-0.7 → backlogops_gui-0.9}/src/backlogops_gui/python_version.py +0 -0
  55. {backlogops_gui-0.7 → backlogops_gui-0.9}/src/backlogops_gui/table_view.py +0 -0
  56. {backlogops_gui-0.7 → backlogops_gui-0.9}/src/backlogops_gui/tcltk_version.py +0 -0
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: backlogops-gui
3
- Version: 0.7
3
+ Version: 0.9
4
4
  Summary: Graphical user interface for backlog operations.
5
5
  Author: Tom Björkholm
6
6
  Author-email: Tom Björkholm <klausuler_linnet0q@icloud.com>
@@ -21,9 +21,11 @@ Classifier: Typing :: Typed
21
21
  Requires-Python: >=3.12
22
22
  Description-Content-Type: text/markdown
23
23
  License-File: LICENSE.txt
24
- Requires-Dist: backlogops>=0.7
25
- Requires-Dist: argcomplete>=3.7.1
26
- Requires-Dist: wizard-ui-bridge>=1.2
24
+ Requires-Dist: backlogops>=0.9
25
+ Requires-Dist: argcomplete>=3.7.2
26
+ Requires-Dist: wizard-ui-bridge>=1.3
27
+ Requires-Dist: wizard-tk-bridge>=1.3
28
+ Requires-Dist: edit-cfg-json-tk==0.0.2
27
29
  Requires-Dist: versionreporter>=0.4
28
30
  Dynamic: author
29
31
  Dynamic: license-file
@@ -284,9 +286,20 @@ is in the menus.
284
286
  closed for vacation, and preset configuration for the inputs or
285
287
  outputs you want to use.
286
288
 
289
+ - Edit configuration...: Show the whole configuration in a folding
290
+ editor, so you can change one value without stepping through the
291
+ wizard. It asks whether to edit the configuration the application is
292
+ using or one in a file you pick. Saving validates the configuration
293
+ first, keeps what it writes over as a `.bak` file, and makes what it
294
+ wrote the configuration in use.
295
+
287
296
  - Create IO preset file...: Create a stand-alone input or output preset
288
297
  configuration file via a wizard.
289
298
 
299
+ - Edit IO preset file...: Show a stand-alone input or output preset file
300
+ in the same folding editor. Whether the file is an input or an output
301
+ preset is detected from the file itself.
302
+
290
303
  - Migrate IO preset file...: Migrate an older input or output preset
291
304
  file to the current file format.
292
305
 
@@ -349,10 +362,10 @@ one with the list of releases. You will want to use the menus.
349
362
 
350
363
  ## Test summary
351
364
 
352
- - Test result: 2453 passed, 9 deselected in 38s
365
+ - Test result: 2232 passed, 1 deselected in 44s
353
366
  - No flake8 warnings.
354
367
  - No mypy errors found.
355
368
  - No pylint warnings.
356
369
  - No python layout warnings.
357
- - Built version(s): 0.7
358
- - Build and test using Python 3.14.6
370
+ - Built version(s): 0.9
371
+ - Build and test using Python 3.14.7
@@ -252,9 +252,20 @@ is in the menus.
252
252
  closed for vacation, and preset configuration for the inputs or
253
253
  outputs you want to use.
254
254
 
255
+ - Edit configuration...: Show the whole configuration in a folding
256
+ editor, so you can change one value without stepping through the
257
+ wizard. It asks whether to edit the configuration the application is
258
+ using or one in a file you pick. Saving validates the configuration
259
+ first, keeps what it writes over as a `.bak` file, and makes what it
260
+ wrote the configuration in use.
261
+
255
262
  - Create IO preset file...: Create a stand-alone input or output preset
256
263
  configuration file via a wizard.
257
264
 
265
+ - Edit IO preset file...: Show a stand-alone input or output preset file
266
+ in the same folding editor. Whether the file is an input or an output
267
+ preset is detected from the file itself.
268
+
258
269
  - Migrate IO preset file...: Migrate an older input or output preset
259
270
  file to the current file format.
260
271
 
@@ -317,10 +328,10 @@ one with the list of releases. You will want to use the menus.
317
328
 
318
329
  ## Test summary
319
330
 
320
- - Test result: 2453 passed, 9 deselected in 38s
331
+ - Test result: 2232 passed, 1 deselected in 44s
321
332
  - No flake8 warnings.
322
333
  - No mypy errors found.
323
334
  - No pylint warnings.
324
335
  - No python layout warnings.
325
- - Built version(s): 0.7
326
- - Build and test using Python 3.14.6
336
+ - Built version(s): 0.9
337
+ - Build and test using Python 3.14.7
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: backlogops-gui
3
- Version: 0.7
3
+ Version: 0.9
4
4
  Summary: Graphical user interface for backlog operations.
5
5
  Author: Tom Björkholm
6
6
  Author-email: Tom Björkholm <klausuler_linnet0q@icloud.com>
@@ -21,9 +21,11 @@ Classifier: Typing :: Typed
21
21
  Requires-Python: >=3.12
22
22
  Description-Content-Type: text/markdown
23
23
  License-File: LICENSE.txt
24
- Requires-Dist: backlogops>=0.7
25
- Requires-Dist: argcomplete>=3.7.1
26
- Requires-Dist: wizard-ui-bridge>=1.2
24
+ Requires-Dist: backlogops>=0.9
25
+ Requires-Dist: argcomplete>=3.7.2
26
+ Requires-Dist: wizard-ui-bridge>=1.3
27
+ Requires-Dist: wizard-tk-bridge>=1.3
28
+ Requires-Dist: edit-cfg-json-tk==0.0.2
27
29
  Requires-Dist: versionreporter>=0.4
28
30
  Dynamic: author
29
31
  Dynamic: license-file
@@ -284,9 +286,20 @@ is in the menus.
284
286
  closed for vacation, and preset configuration for the inputs or
285
287
  outputs you want to use.
286
288
 
289
+ - Edit configuration...: Show the whole configuration in a folding
290
+ editor, so you can change one value without stepping through the
291
+ wizard. It asks whether to edit the configuration the application is
292
+ using or one in a file you pick. Saving validates the configuration
293
+ first, keeps what it writes over as a `.bak` file, and makes what it
294
+ wrote the configuration in use.
295
+
287
296
  - Create IO preset file...: Create a stand-alone input or output preset
288
297
  configuration file via a wizard.
289
298
 
299
+ - Edit IO preset file...: Show a stand-alone input or output preset file
300
+ in the same folding editor. Whether the file is an input or an output
301
+ preset is detected from the file itself.
302
+
290
303
  - Migrate IO preset file...: Migrate an older input or output preset
291
304
  file to the current file format.
292
305
 
@@ -349,10 +362,10 @@ one with the list of releases. You will want to use the menus.
349
362
 
350
363
  ## Test summary
351
364
 
352
- - Test result: 2453 passed, 9 deselected in 38s
365
+ - Test result: 2232 passed, 1 deselected in 44s
353
366
  - No flake8 warnings.
354
367
  - No mypy errors found.
355
368
  - No pylint warnings.
356
369
  - No python layout warnings.
357
- - Built version(s): 0.7
358
- - Build and test using Python 3.14.6
370
+ - Built version(s): 0.9
371
+ - Build and test using Python 3.14.7
@@ -12,18 +12,15 @@ src/backlogops_gui/__init__.py
12
12
  src/backlogops_gui/__main__.py
13
13
  src/backlogops_gui/_migrate_warn.py
14
14
  src/backlogops_gui/application.py
15
- src/backlogops_gui/auto_scroll.py
16
15
  src/backlogops_gui/backlog_actions.py
17
16
  src/backlogops_gui/backlog_dialogs.py
18
17
  src/backlogops_gui/backlog_io.py
19
18
  src/backlogops_gui/backlog_window.py
20
19
  src/backlogops_gui/blog_version_reporter.py
21
20
  src/backlogops_gui/choice_dialogs.py
22
- src/backlogops_gui/close_binding.py
21
+ src/backlogops_gui/config_edit.py
23
22
  src/backlogops_gui/file_choosers.py
24
23
  src/backlogops_gui/format_dialogs.py
25
- src/backlogops_gui/gui_style.py
26
- src/backlogops_gui/gui_wizard.py
27
24
  src/backlogops_gui/jira_actions.py
28
25
  src/backlogops_gui/jira_base.py
29
26
  src/backlogops_gui/jira_dialogs.py
@@ -41,12 +38,4 @@ src/backlogops_gui/python_version.py
41
38
  src/backlogops_gui/report_windows.py
42
39
  src/backlogops_gui/table_view.py
43
40
  src/backlogops_gui/tcltk_version.py
44
- src/backlogops_gui/token_dialog.py
45
- src/backlogops_gui/wizard_calendar.py
46
- src/backlogops_gui/wizard_form.py
47
- src/backlogops_gui/wizard_path.py
48
- src/backlogops_gui/wizard_pick_row.py
49
- src/backlogops_gui/wizard_prefill.py
50
- src/backlogops_gui/wizard_table.py
51
- src/backlogops_gui/wizard_typed.py
52
- src/backlogops_gui/wizard_window.py
41
+ src/backlogops_gui/token_dialog.py
@@ -0,0 +1,6 @@
1
+ backlogops>=0.9
2
+ argcomplete>=3.7.2
3
+ wizard-ui-bridge>=1.3
4
+ wizard-tk-bridge>=1.3
5
+ edit-cfg-json-tk==0.0.2
6
+ versionreporter>=0.4
@@ -5,7 +5,7 @@ from setuptools import setup
5
5
 
6
6
  setup(
7
7
  name='backlogops-gui',
8
- version='0.7',
8
+ version='0.9',
9
9
  description='Graphical user interface for backlog operations.',
10
10
  author='Tom Björkholm',
11
11
  author_email='klausuler_linnet0q@icloud.com',
@@ -19,9 +19,11 @@ setup(
19
19
  ]
20
20
  },
21
21
  install_requires=[
22
- 'backlogops >= 0.7',
23
- 'argcomplete >= 3.7.1',
24
- 'wizard-ui-bridge >= 1.2',
22
+ 'backlogops >= 0.9',
23
+ 'argcomplete >= 3.7.2',
24
+ 'wizard-ui-bridge >= 1.3',
25
+ 'wizard-tk-bridge >= 1.3',
26
+ 'edit-cfg-json-tk == 0.0.2',
25
27
  'versionreporter >= 0.4'
26
28
  ]
27
29
  )
@@ -3,7 +3,9 @@
3
3
 
4
4
  The application opens a main window whose menu reads a backlog from a file
5
5
  or from Jira, loads or replaces the active configuration from a file, runs
6
- the teams configuration wizard, creates a stand-alone input or output
6
+ the teams configuration wizard, edits a configuration or a stand-alone
7
+ preset file in the folding editor of
8
+ :mod:`backlogops_gui.config_edit`, creates a stand-alone input or output
7
9
  preset file, migrates a stand-alone preset file to the current format,
8
10
  writes the running configuration to a file, and creates a demonstration
9
11
  backlog. The configuration wizard and the preset wizard first ask whether
@@ -39,20 +41,21 @@ from typing import Callable, Optional, TextIO, TypeVar
39
41
  import argcomplete
40
42
  from config_as_json import Config, migrate_cfg
41
43
  from config_as_json.file_extension import fix_file_extension
44
+ from wizard_tk_bridge import WizardUiBridgeTk
42
45
  from backlogops import (
43
- AvailableTeams, BacklogOpsConfig, BacklogReleases, GuiDisplayConfig,
44
- InputFormatConfig, Levels, OutputFormatConfig, Status, get_demo_backlog,
45
- get_backlog_ops_config, backlog_ops_wizard, preset_wizard,
46
- read_backlog_ops_config, read_io_preset, safe_write_config,
46
+ AvailableTeams, BacklogOpsConfig, BacklogReleases, CONFIG_EXTENSION,
47
+ GuiDisplayConfig, InputFormatConfig, Levels, OutputFormatConfig, Status,
48
+ get_demo_backlog, get_backlog_ops_config, backlog_ops_wizard,
49
+ preset_wizard, read_backlog_ops_config, read_io_preset, safe_write_config,
47
50
  encrypt_token_file, encrypt_token_to_file)
48
51
  from backlogops_gui.backlog_io import read_backlog
49
52
  from backlogops_gui.backlog_window import (
50
53
  BacklogWindow, BacklogSource, JiraHandlers, current_time)
51
54
  from backlogops_gui.blog_version_reporter import BloGuiVersionReporter
52
- from backlogops_gui.gui_wizard import TkWizardBridge
53
55
  from backlogops_gui.choice_dialogs import (
54
56
  ConfigChoice, PresetKind, SourceChoice, ask_no_config_choice,
55
57
  ask_preset_kind, ask_source_choice)
58
+ from backlogops_gui.config_edit import edit_config, edit_preset_file
56
59
  from backlogops_gui.file_choosers import (
57
60
  choose_config_file, choose_existing_config, choose_existing_preset,
58
61
  choose_input_file, choose_migrated_preset, choose_preset_to_migrate)
@@ -66,17 +69,17 @@ from backlogops_gui.python_version import check_python_version
66
69
  from backlogops_gui.tcltk_version import check_tcltk_version
67
70
 
68
71
  APP_TITLE = 'Backlog operations GUI'
69
- CONFIG_EXTENSION = '.cfg'
70
72
  WRAP_LENGTH = 520
71
73
  LOG_REFRESH_MS = 800
72
74
  HEADING_FONT = ('TkDefaultFont', 14, 'bold')
73
75
  INSTRUCTIONS = (
74
76
  'Use the menus to read a backlog from a file or from Jira, load a '
75
- 'configuration file, run the teams wizard, create a stand-alone input '
76
- 'or output preset file, migrate a preset file to the current format, '
77
- 'write the current configuration to a file, or create a demonstration '
78
- 'backlog. Each backlog opens in its own window. On macOS the menu bar '
79
- 'is at the top of the display.')
77
+ 'configuration file, run the teams wizard, edit a configuration or a '
78
+ 'preset file, create a stand-alone input or output preset file, '
79
+ 'migrate a preset file to the current format, write the current '
80
+ 'configuration to a file, or create a demonstration backlog. Each '
81
+ 'backlog opens in its own window. On macOS the menu bar is at the top '
82
+ 'of the display.')
80
83
  DESCRIPTION = 'Graphical user interface for backlog operations'
81
84
  CONFIG_ERRORS = (FileNotFoundError, NotADirectoryError, RuntimeError,
82
85
  ValueError, TypeError, KeyError, OSError)
@@ -155,7 +158,7 @@ class BacklogApp:
155
158
 
156
159
  def __init__(self, root: tk.Tk,
157
160
  config: Optional[BacklogOpsConfig] = None) -> None:
158
- """Store the window, config, log and Jira collaborators."""
161
+ """Store the window, config, log and the action collaborators."""
159
162
  self.root = root
160
163
  self.config = config
161
164
  self.log = LogBuffer()
@@ -164,6 +167,21 @@ class BacklogApp:
164
167
  self._status: Optional[tk.StringVar] = None
165
168
  self.jira = JiraActions(self)
166
169
 
170
+ def adopt_config(self, config: BacklogOpsConfig, source: str) -> None:
171
+ """Make one configuration the active one and say where it came from.
172
+
173
+ Every way a configuration becomes the active one goes through here —
174
+ loading a file, the wizard, and the editor — so the status line
175
+ cannot end up saying one thing while another is in use.
176
+
177
+ Args:
178
+ config: The configuration to use from now on.
179
+ source: Where it came from, as a file name or a short phrase.
180
+ """
181
+ self.config = config
182
+ self.config_source = source
183
+ self._update_status()
184
+
167
185
  def in_presets(self) -> Optional[dict[str, InputFormatConfig]]:
168
186
  """Return the input presets of the current configuration."""
169
187
  return self.config.input_configs if self.config else None
@@ -214,9 +232,8 @@ class BacklogApp:
214
232
  """
215
233
  config, error = initial_config(config_arg, self.log)
216
234
  if config is not None:
217
- self.config = config
218
- self.config_source = (config_arg if config_arg is not None
219
- else 'the default location')
235
+ self.adopt_config(config, config_arg if config_arg is not None
236
+ else 'the default location')
220
237
  return True
221
238
  if config_arg is not None and error is not None:
222
239
  self.show_error('Configuration error', error)
@@ -244,8 +261,7 @@ class BacklogApp:
244
261
  config = self.run_wizard()
245
262
  if config is None:
246
263
  return False
247
- self.config = config
248
- self.config_source = 'the wizard'
264
+ self.adopt_config(config, 'the wizard')
249
265
  return True
250
266
 
251
267
  def _adopt_loaded_config(self) -> bool:
@@ -258,8 +274,7 @@ class BacklogApp:
258
274
  self.show_error('Configuration error',
259
275
  error or 'Could not load the configuration.')
260
276
  return False
261
- self.config = config
262
- self.config_source = path
277
+ self.adopt_config(config, path)
263
278
  return True
264
279
 
265
280
  def _run_bridge_wizard(self, wizard: Callable[..., _WizardConfig],
@@ -273,7 +288,7 @@ class BacklogApp:
273
288
  wizard failure is reported under ``error_title`` and also yields
274
289
  None. The bridge window is always closed afterwards.
275
290
  """
276
- bridge = TkWizardBridge(self.root, self.log)
291
+ bridge = WizardUiBridgeTk(self.root, log=self.log)
277
292
  try:
278
293
  return wizard(bridge, default=default)
279
294
  except EOFError:
@@ -352,9 +367,7 @@ class BacklogApp:
352
367
  self.show_error('Configuration error',
353
368
  error or 'Could not load the configuration.')
354
369
  return
355
- self.config = config
356
- self.config_source = path
357
- self._update_status()
370
+ self.adopt_config(config, path)
358
371
  self.show_info('Configuration loaded',
359
372
  f'Loaded configuration from {path}.')
360
373
 
@@ -374,11 +387,17 @@ class BacklogApp:
374
387
  return
375
388
  config = self.run_wizard(default)
376
389
  if config is not None:
377
- self.config = config
378
- self.config_source = 'the wizard'
379
- self._update_status()
390
+ self.adopt_config(config, 'the wizard')
380
391
  self.show_info('Wizard', 'The new configuration is now active.')
381
392
 
393
+ def run_config_editor(self) -> None:
394
+ """Edit the configuration in use, or one in a file, in the editor."""
395
+ edit_config(self)
396
+
397
+ def run_preset_editor(self) -> None:
398
+ """Edit a stand-alone input or output preset file in the editor."""
399
+ edit_preset_file(self)
400
+
382
401
  def create_preset_file(self) -> None:
383
402
  """Ask the source, run the IO preset wizard, and write the preset.
384
403
 
@@ -651,8 +670,12 @@ class BacklogApp:
651
670
  menu.add_separator()
652
671
  menu.add_command(label='Run configuration wizard…',
653
672
  command=self.run_config_wizard)
673
+ menu.add_command(label='Edit configuration…',
674
+ command=self.run_config_editor)
654
675
  menu.add_command(label='Create IO preset file…',
655
676
  command=self.create_preset_file)
677
+ menu.add_command(label='Edit IO preset file…',
678
+ command=self.run_preset_editor)
656
679
  menu.add_command(label='Migrate IO preset file…',
657
680
  command=self.migrate_preset_file)
658
681
  menu.add_command(label='Write configuration…',
@@ -17,8 +17,8 @@ from tkinter import messagebox, ttk
17
17
  from dataclasses import dataclass
18
18
  from datetime import date
19
19
  from typing import Optional, TextIO
20
+ from wizard_tk_bridge.gui_style import style_input
20
21
  from backlogops import DependencyMode, DEFAULT_LEVELS
21
- from backlogops_gui.gui_style import style_input
22
22
  from backlogops_gui.key_list_box import build_key_box, load_keys_into
23
23
  from backlogops_gui.modal_dialog import ModalDialog
24
24
 
@@ -22,18 +22,18 @@ from pathlib import Path
22
22
  from tkinter import messagebox, ttk
23
23
  from typing import Callable, Literal, Optional, TextIO
24
24
  from tableio import ValueFmt
25
+ from wizard_tk_bridge.auto_scroll import auto_hide
26
+ from wizard_tk_bridge.close_binding import CLOSE_ACCELERATOR, bind_close
25
27
  from backlogops import (
26
28
  AddedReleasesToJira, AddedToJira, AvailableTeams, BacklogReleases,
27
29
  GuiDisplayConfig, Levels, OrderedReleasesInJira, OutputFormatConfig,
28
30
  RankedInJira, RenamedReleasesInJira, UpdatedBacklogInJira,
29
31
  UpdatedReleasesInJira, format_order_result, format_rank_result,
30
32
  format_release_result, format_release_updates, format_rename_result)
31
- from backlogops_gui.auto_scroll import auto_hide
32
33
  from backlogops_gui.backlog_actions import (
33
34
  adjust_content, apply_add_result, apply_update_result, estimate_date,
34
35
  extract_keys, order_by_deps, order_by_keys, order_by_release, order_dates,
35
36
  plan_dates, save_backlog, set_plan)
36
- from backlogops_gui.close_binding import CLOSE_ACCELERATOR, bind_close
37
37
  from backlogops_gui.report_windows import show_text_report
38
38
  from backlogops_gui.table_view import (
39
39
  backlog_table, make_table, release_table)
@@ -6,8 +6,10 @@ selecting one enumerated value, with no OK or Cancel. The no-configuration
6
6
  dialog offers to run the wizard, load a file, or exit at startup. The
7
7
  preset-kind dialog asks whether a stand-alone preset file is an input or
8
8
  an output preset before it is migrated. The source dialog asks whether to
9
- start a wizard from scratch, base it on an existing file, or cancel. All
10
- three are built from the same :class:`ButtonChoiceDialog`.
9
+ start a wizard from scratch, base it on an existing file, or cancel. The
10
+ edit-target dialog asks whether the configuration editor opens the
11
+ configuration the application is using or one in a file. All four are built
12
+ from the same :class:`ButtonChoiceDialog`.
11
13
  """
12
14
 
13
15
  # Copyright (c) 2026, Tom Björkholm
@@ -16,7 +18,7 @@ three are built from the same :class:`ButtonChoiceDialog`.
16
18
  import tkinter as tk
17
19
  from enum import Enum
18
20
  from typing import Generic, Optional, Sequence, TypeVar
19
- from backlogops_gui.close_binding import bind_close
21
+ from wizard_tk_bridge.close_binding import bind_close
20
22
 
21
23
  CHOICE_WRAP = 360
22
24
  NO_CONFIG_TEXT = (
@@ -26,6 +28,11 @@ NO_CONFIG_TEXT = (
26
28
  PRESET_KIND_TEXT = (
27
29
  'Is the preset file an input format preset or an output format '
28
30
  'preset? The kind decides how the file is migrated.')
31
+ EDIT_TARGET_TITLE = 'Edit configuration'
32
+ EDIT_TARGET_TEXT = (
33
+ 'Edit the configuration the application is using, or one in a file '
34
+ 'you pick? Saving the configuration in use writes the file it was '
35
+ 'loaded from, and Save as… in the editor writes any other file.')
29
36
  _Choice = TypeVar('_Choice')
30
37
 
31
38
 
@@ -52,6 +59,14 @@ class SourceChoice(Enum):
52
59
  CANCEL = 'cancel'
53
60
 
54
61
 
62
+ class EditTargetChoice(Enum):
63
+ """Whether the editor opens the configuration in use or one in a file."""
64
+
65
+ IN_USE = 'in_use'
66
+ FROM_FILE = 'from_file'
67
+ CANCEL = 'cancel'
68
+
69
+
55
70
  # pylint: disable-next=too-few-public-methods
56
71
  class ButtonChoiceDialog(Generic[_Choice]):
57
72
  """Modal dialog presenting a column of single-choice buttons.
@@ -130,3 +145,12 @@ def ask_source_choice(parent: tk.Misc, title: str, text: str) -> SourceChoice:
130
145
  ('Cancel', SourceChoice.CANCEL)]
131
146
  return ButtonChoiceDialog(parent, title, text, options,
132
147
  SourceChoice.CANCEL).choice
148
+
149
+
150
+ def ask_edit_target(parent: tk.Misc) -> EditTargetChoice:
151
+ """Ask whether to edit the configuration in use or one in a file."""
152
+ options = [('The configuration in use', EditTargetChoice.IN_USE),
153
+ ('A configuration file…', EditTargetChoice.FROM_FILE),
154
+ ('Cancel', EditTargetChoice.CANCEL)]
155
+ return ButtonChoiceDialog(parent, EDIT_TARGET_TITLE, EDIT_TARGET_TEXT,
156
+ options, EditTargetChoice.CANCEL).choice