in ec2rlcore/menu.py [0:0]
def setup(self, stdscr):
"""
Draw the menu and handle keyboard input.
Parameters:
stdscr (WindowObject): the screen; handled by curses.wrapper
Returns:
the string representing the currently selected row
"""
# Initialize the WindowObject
stdscr.clear()
stdscr = curses.initscr()
curses.noecho()
curses.cbreak()
# Start color support if supported
if curses.has_colors():
curses.start_color()
# Initialize color pairs
# Main window
curses.init_pair(1, curses.COLOR_WHITE, curses.COLOR_BLUE)
# Sub-windows
curses.init_pair(2, curses.COLOR_BLACK, curses.COLOR_WHITE)
# Verify this is a terminal that is at least both 80 columns and 24 lines
if curses.LINES < 24 or curses.COLS < 80:
raise curses.error(
"Required minimum terminal dimensions of 80x24 > {}x{}.".format(curses.COLS, curses.LINES))
main_window, screen, footer = self.draw_menu(stdscr)
# Menu loop until item is chosen with the <enter> key or the user quits with "q"
while True:
# Get a character from the keyboard
key = stdscr.getch()
# Enter selects the highlighted item
if key == ord("\n"):
footer_selection = self.footer_items[self.current_column]
if footer_selection == "Select":
if isinstance(self._items[self.current_row - 1], (ec2rlcore.menu_item.ExitItem,
ec2rlcore.menu_item.RunItem)):
self._items[self.current_row - 1]()
self.current_row = 1
self.current_column = 0
self.current_page = 1
self.done = True
return
else:
self.current_column = 0
return self._items[self.current_row - 1]()
elif footer_selection == "Exit":
self.current_row = 1
self.current_column = 0
self.current_page = 1
self.done = True
return
elif footer_selection == "Help":
self.current_column = 0
return self.show_item_help(self._items[self.current_row - 1])
elif footer_selection == "Clear":
if isinstance(self._items[self.current_row - 1], ec2rlcore.menu_item.TextEntryItem):
self._items[self.current_row - 1].row_right = ""
else:
raise MenuUnsupportedFooterOptionError(footer_selection)
elif key == ord(" "):
if isinstance(self._items[self.current_row - 1], ec2rlcore.menu_item.ToggleItem):
self._items[self.current_row - 1]()
elif key in (78, "N"):
# Select/deselect all ToggleItems in current menu
for item in self._items:
if isinstance(item, ec2rlcore.menu_item.ToggleItem) \
and ((self.toggle_state and item.toggled) or (not self.toggle_state and not item.toggled)):
item()
self.toggle_state = not self.toggle_state
elif key in (260, curses.KEY_LEFT):
if self.current_column > 0:
self.current_column -= 1
# The right arrow key selects the footer option to the right of the currently selected item
elif key in (261, curses.KEY_RIGHT):
if self.current_column < self.num_columns - 1:
self.current_column += 1
# The down arrow key selects the next option in the menu window and increments the page as needed
elif key in (65, 258, curses.KEY_DOWN):
if self.current_page == 1:
if self.current_row < self.max_displayed_rows and \
self.current_row < self.num_rows:
self.current_row += 1
# Else + if num_pages > 1
elif self.num_pages > 1:
self.current_page += 1
self.current_row = 1 + (self.max_displayed_rows * (self.current_page - 1))
elif self.current_page == self.num_pages:
if self.current_row < self.num_rows:
self.current_row += 1
else:
if self.current_row < self.max_displayed_rows + \
(self.max_displayed_rows * (self.current_page - 1)):
self.current_row += 1
else:
self.current_page += 1
self.current_row = 1 + (self.max_displayed_rows * (self.current_page - 1))
# The up arrow key selects the previous option in the menu window and decrements the page as needed
elif key in (66, 259, curses.KEY_UP):
if self.current_page == 1:
if self.current_row > 1:
self.current_row -= 1
else:
if self.current_row > (1 + (self.max_displayed_rows * (self.current_page - 1))):
self.current_row -= 1
else:
self.current_page -= 1
self.current_row = \
self.max_displayed_rows + (self.max_displayed_rows * (self.current_page - 1))
# The page down key increments the page and selects the row in the same position in the menu on that page
# or the first row if the next page doesn't have an equivalent row
# Ignore page down key presses on the last page
elif key in (338, curses.KEY_NPAGE) and self.current_page < self.num_pages:
self.current_page += 1
# Bounds check to ensure there are enough items on the next page to select an item in the same position
# This will be true for all pages but the last page which will likely be a partial page of items
if self.current_row + self.max_displayed_rows < self.num_rows:
self.current_row += self.max_displayed_rows
# If the bounds check fails then select the first item on the next (last) page
else:
self.current_row = ((self.current_page - 1) * self.max_displayed_rows) + 1
# The page up key decrements the page and selects the row in the same position in the menu on that page
# Ignore page up key presses when on the first page
elif key in (339, curses.KEY_PPAGE) and self.current_page > 1:
self.current_page -= 1
# Shift the currently selected row to the same place on the new page
self.current_row -= self.max_displayed_rows
elif key in (410, curses.KEY_RESIZE):
# Get the new terminal dimensions and resize the terminal
curses.resizeterm(*stdscr.getmaxyx())
# Verify the terminal is still at least 80x24
if curses.LINES < 24 or curses.COLS < 80:
raise curses.error(
"Required minimum terminal dimensions of 80x24 > {}x{}.".format(curses.COLS, curses.LINES))
# Initialize a new WindowObject
stdscr = curses.initscr()
curses.noecho()
curses.cbreak()
main_window, screen, footer = self.draw_menu(stdscr)
# Recalculate the current page
# current_selected_row - 1 is needed because the row in the window is offset by 1
self.current_page = int(math.ceil(self.current_row / self.max_displayed_rows))
# For some reason, another getch() call is required to pull the KEY_RESIZE out of the buffer
stdscr.getch()
# Erase the screen so it can be cleanly redrawn
screen.erase()
screen.border(0)
self._draw_menu(screen)
# Draw the navigation items in the footer
self._draw_footer(footer)
# Draw the pieces of the overall screen (order matters)
stdscr.noutrefresh()
main_window.noutrefresh()
screen.noutrefresh()
footer.noutrefresh()
curses.doupdate()