Send patches - preferably formatted by git format-patch - to patches at archlinux32 dot org.
summaryrefslogtreecommitdiff
path: root/archinstall/lib/menu/list_manager.py
diff options
context:
space:
mode:
Diffstat (limited to 'archinstall/lib/menu/list_manager.py')
-rw-r--r--archinstall/lib/menu/list_manager.py262
1 files changed, 37 insertions, 225 deletions
diff --git a/archinstall/lib/menu/list_manager.py b/archinstall/lib/menu/list_manager.py
index fe491caa..e7a9c2ac 100644
--- a/archinstall/lib/menu/list_manager.py
+++ b/archinstall/lib/menu/list_manager.py
@@ -1,94 +1,7 @@
-#!/usr/bin/python
-"""
-# Purpose
-ListManager is a widget based on `menu` which allows the handling of repetitive operations in a list.
-Imagine you have a list and want to add/copy/edit/delete their elements. With this widget you will be shown the list
-```
-Vamos alla
-
-Use ESC to skip
-
-
-> uno : 1
-dos : 2
-tres : 3
-cuatro : 4
-==>
-Confirm and exit
-Cancel
-(Press "/" to search)
-```
-Once you select one of the elements of the list, you will be promted with the action to be done to the selected element
-```
-
-uno : 1
-dos : 2
-> tres : 3
-cuatro : 4
-==>
-Confirm and exit
-Cancel
-(Press "/" to search)
-
-Select an action for < {'tres': 3} >
-
-
-Add
-Copy
-Edit
-Delete
-> Cancel
-```
-You execute the action for this element (which might or not involve user interaction) and return to the list main page
-till you call one of the options `confirm and exit` which returns the modified list or `cancel` which returns the original list unchanged.
-If the list is empty one action can be defined as default (usually Add). We call it **null_action**
-YOu can also define a **default_action** which will appear below the separator, not tied to any element of the list. Barring explicit definition, default_action will be the null_action
-```
-==>
-Add
-Confirm and exit
-Cancel
-(Press "/" to search)
-```
-The default implementation can handle simple lists and a key:value dictionary. The default actions are the shown above.
-A sample of basic usage is included at the end of the source.
-
-More sophisticaded uses can be achieved by
-* changing the action list and the null_action during initialization
-```
- opciones = ListManager('Vamos alla',opciones,[str(_('Add')),str(_('Delete'))],_('Add')).run()
-```
-* And using following methods to overwrite/define user actions and other details:
-* * `reformat`. To change the appearance of the list elements
-* * `action_list`. To modify the content of the action list once an element is defined. F.i. to avoid Delete to appear for certain elements, or to add/modify action based in the value of the element.
-* * `exec_action` which contains the actual code to be executed when an action is selected
-
-The contents in the base class of this methods serve for a very basic usage, and are to be taken as samples. Thus the best use of this class would be to subclass in your code
-
-```
- class ObjectList(archinstall.ListManager):
- def __init__(prompt,list):
- self.ObjectAction = [... list of actions ...]
- self.ObjectNullAction = one ObjectAction
- super().__init__(prompt,list,ObjectActions,ObjectNullAction)
- def reformat(self):
- ... beautfy the output of the list
- def action_list(self):
- ... if you need some changes to the action list based on self.target
- def exec_action(self):
- if self.action == self.ObjectAction[0]:
- performFirstAction(self.target, ...)
-
- ...
- resultList = ObjectList(prompt,originallist).run()
-```
-
-"""
import copy
from os import system
-from typing import Union, Any, TYPE_CHECKING, Dict, Optional, Tuple, List
+from typing import Any, TYPE_CHECKING, Dict, Optional, Tuple, List
-from .text_input import TextInput
from .menu import Menu
if TYPE_CHECKING:
@@ -98,58 +11,38 @@ if TYPE_CHECKING:
class ListManager:
def __init__(
self,
- prompt :str,
- base_list :Union[list,dict] ,
- base_actions :list = None,
- null_action :str = None,
- default_action :Union[str,list] = None,
- header :Union[str,list] = None):
+ prompt: str,
+ entries: List[Any],
+ base_actions: List[str],
+ sub_menu_actions: List[str]
+ ):
"""
- param :prompt Text which will appear at the header
+ :param prompt: Text which will appear at the header
type param: string | DeferredTranslation
- param :base:_list list/dict of option to be shown / mainpulated
- type param: list | dict
-
- param base_actions an alternate list of actions to the items of the object
+ :param entries: list/dict of option to be shown / manipulated
type param: list
- param: null_action action which will be taken (if any) when base_list is empty
- type param: string
-
- param: default_action action which will be presented at the bottom of the list. Shouldn't need a target. If not present, null_action is set there.
- Both Null and Default actions can be defined outside the base_actions list, as long as they are launched in exec_action
- type param: string or list
+ :param base_actions: list of actions that is displayed in the main list manager,
+ usually global actions such as 'Add...'
+ type param: list
- param: header one or more header lines for the list
- type param: string or list
+ :param sub_menu_actions: list of actions available for a chosen entry
+ type param: list
"""
+ self._original_data = copy.deepcopy(entries)
+ self._data = copy.deepcopy(entries)
explainer = str(_('\n Choose an object from the list, and select one of the available actions for it to execute'))
self._prompt = prompt + explainer if prompt else explainer
- self._null_action = str(null_action) if null_action else None
-
- if not default_action:
- self._default_action = [self._null_action]
- elif isinstance(default_action,(list,tuple)):
- self._default_action = default_action
- else:
- self._default_action = [str(default_action)]
-
- self._header = header if header else ''
- self._cancel_action = str(_('Cancel'))
- self._confirm_action = str(_('Confirm and exit'))
self._separator = ''
- self._bottom_list = [self._confirm_action, self._cancel_action]
- self._bottom_item = [self._cancel_action]
- self._base_actions = base_actions if base_actions else [str(_('Add')), str(_('Copy')), str(_('Edit')), str(_('Delete'))]
- self._original_data = copy.deepcopy(base_list)
- self._data = copy.deepcopy(base_list) # as refs, changes are immediate
+ self._confirm_action = str(_('Confirm and exit'))
+ self._cancel_action = str(_('Cancel'))
- # default values for the null case
- self.target: Optional[Any] = None
- self.action = self._null_action
+ self._terminate_actions = [self._confirm_action, self._cancel_action]
+ self._base_actions = base_actions
+ self._sub_menu_actions = sub_menu_actions
def run(self):
while True:
@@ -158,11 +51,6 @@ class ListManager:
data_formatted = self.reformat(self._data)
options, header = self._prepare_selection(data_formatted)
- menu_header = self._header
-
- if header:
- menu_header += header
-
system('clear')
choice = Menu(
@@ -177,27 +65,13 @@ class ListManager:
show_search_hint=False
).run()
- if not choice.value or choice.value in self._bottom_list:
- self.action = choice
+ if choice.value in self._base_actions:
+ self._data = self.handle_action(choice.value, None, self._data)
+ elif choice.value in self._terminate_actions:
break
-
- if choice.value and choice.value in self._default_action:
- self.action = choice.value
- self.target = None
- self._data = self.exec_action(self._data)
- continue
-
- if isinstance(self._data, dict):
- data_key = data_formatted[choice.value]
- key = self._data[data_key]
- self.target = {data_key: key}
- elif isinstance(self._data, list):
- self.target = [d for d in self._data if d == data_formatted[choice.value]][0]
- else:
- self.target = self._data[data_formatted[choice.value]]
-
- # Possible enhancement. If run_actions returns false a message line indicating the failure
- self.run_actions(choice.value)
+ else: # an entry of the existing selection was choosen
+ selected_entry = data_formatted[choice.value]
+ self._run_actions_on_entry(selected_entry)
if choice.value == self._cancel_action:
return self._original_data # return the original list
@@ -217,16 +91,14 @@ class ListManager:
if len(options) > 0:
options.append(self._separator)
- if self._default_action:
- # done only for mypy -> todo fix the self._default_action declaration
- options += [action for action in self._default_action if action]
+ options += self._base_actions
+ options += self._terminate_actions
- options += self._bottom_list
return options, header
- def run_actions(self,prompt_data=''):
- options = self.action_list() + self._bottom_item
- display_value = self.selected_action_display(self.target) if self.target else prompt_data
+ def _run_actions_on_entry(self, entry: Any):
+ options = self._sub_menu_actions + [self._cancel_action]
+ display_value = self.selected_action_display(entry)
prompt = _("Select an action for '{}'").format(display_value)
@@ -236,14 +108,11 @@ class ListManager:
sort=False,
clear_screen=False,
clear_menu_on_exit=False,
- preset_values=self._bottom_item,
show_search_hint=False
).run()
- self.action = choice.value
-
- if self.action and self.action != self._cancel_action:
- self._data = self.exec_action(self._data)
+ if choice.value and choice.value != self._cancel_action:
+ self._data = self.handle_action(choice.value, entry, self._data)
def selected_action_display(self, selection: Any) -> str:
# this will return the value to be displayed in the
@@ -256,64 +125,7 @@ class ListManager:
# in the header value (useful when displaying tables)
raise NotImplementedError('Please implement me in the child class')
- def action_list(self):
- """
- can define alternate action list or customize the list for each item.
- Executed after any item is selected, contained in self.target
- """
- active_entry = self.target if self.target else None
-
- if active_entry is None:
- return [self._base_actions[0]]
- else:
- return self._base_actions[1:]
-
- def exec_action(self, data: Any):
- """
- what's executed one an item (self.target) and one action (self.action) is selected.
- Should be overwritten by the user
- The result is expected to update self._data in this routine, else it is ignored
- The basic code is useful for simple lists and dictionaries (key:value pairs, both strings)
- """
- # TODO guarantee unicity
- if isinstance(self._data,list):
- if self.action == str(_('Add')):
- self.target = TextInput(_('Add: '),None).run()
- self._data.append(self.target)
- if self.action == str(_('Copy')):
- while True:
- target = TextInput(_('Copy to: '),self.target).run()
- if target != self.target:
- self._data.append(self.target)
- break
- elif self.action == str(_('Edit')):
- tgt = self.target
- idx = self._data.index(self.target)
- result = TextInput(_('Edit: '),tgt).run()
- self._data[idx] = result
- elif self.action == str(_('Delete')):
- del self._data[self._data.index(self.target)]
- elif isinstance(self._data,dict):
- # allows overwrites
- if self.target:
- origkey,origval = list(self.target.items())[0]
- else:
- origkey = None
- origval = None
- if self.action == str(_('Add')):
- key = TextInput(_('Key: '),None).run()
- value = TextInput(_('Value: '),None).run()
- self._data[key] = value
- if self.action == str(_('Copy')):
- while True:
- key = TextInput(_('Copy to new key:'),origkey).run()
- if key != origkey:
- self._data[key] = origval
- break
- elif self.action == str(_('Edit')):
- value = TextInput(_('Edit {}: ').format(origkey), origval).run()
- self._data[origkey] = value
- elif self.action == str(_('Delete')):
- del self._data[origkey]
-
- return self._data
+ def handle_action(self, action: Any, entry: Optional[Any], data: List[Any]) -> List[Any]:
+ # this function is called when a base action or
+ # a specific action for an entry is triggered
+ raise NotImplementedError('Please implement me in the child class')