text: #@+leo-ver=5-thin
  #@+node:ekr.20170428084208.68: * @file ../external/npyscreen/wgmultiline.py
  #!/usr/bin/python
  # pylint: disable=no-member
  import leo.core.leoGlobals as g
  assert g
  import copy
  from . import wgwidget       as widget
  from . import wgtextbox      as textbox
  import textwrap
  import curses
  from . import wgtitlefield   as titlefield
  from . import fmPopup        as Popup
  import weakref
  import collections
  MORE_LABEL = "- more -" # string to tell user there are more options
  #@+others
  #@+node:ekr.20170428084208.69: ** Declarations
  #@+node:ekr.20170428084208.70: ** class FilterPopupHelper
  class FilterPopupHelper(Popup.Popup):
      #@+others
      #@+node:ekr.20170428084208.71: *3* create
      def create(self):
          super(FilterPopupHelper, self).create()
          self.filterbox = self.add(titlefield.TitleText, name='Find:', )
          self.nextrely += 1
          self.statusline = self.add(textbox.Textfield, color = 'LABEL', editable = False)
  
      #@+node:ekr.20170428084208.72: *3* updatestatusline
      def updatestatusline(self):
          self.owner_widget._filter   = self.filterbox.value
          filtered_lines = self.owner_widget.get_filtered_indexes()
          len_f = len(filtered_lines)
          if self.filterbox.value == None or self.filterbox.value == '':
              self.statusline.value = ''
          elif len_f == 0:
              self.statusline.value = '(No Matches)'
          elif len_f == 1:
              self.statusline.value = '(1 Match)'
          else:
              self.statusline.value = '(%s Matches)' % len_f
  
      #@+node:ekr.20170428084208.73: *3* adjust_widgets
      def adjust_widgets(self):
          self.updatestatusline()
          self.statusline.display()
  
  
  
  
      #@-others
  #@+node:ekr.20170428084208.74: ** class MultiLine (Widget)
  class MultiLine(widget.Widget):
      _safe_to_display_cache = True
      """
      Display a list of items to the user. By overloading the display_value
      method, this widget can be made to display different kinds of objects.
      Given the standard definition, the same effect can be achieved by
      altering the __str__() method of displayed objects
      """
      _MINIMUM_HEIGHT = 2 # Raise an error if not given this.
      _contained_widgets = textbox.Textfield
      _contained_widget_height = 1
      #@+others
      #@+node:ekr.20170428084208.75: *3* MultiLine.__init__
      def __init__(self, screen,
          values=None,
          value=None,
          slow_scroll=False,
          scroll_exit=False,
          return_exit=False,
          select_exit=False,
          exit_left=False,
          exit_right=False,
          widgets_inherit_color=False,
          always_show_cursor=False,
          allow_filtering=True,
          ** keywords
      ):
          self.never_cache = False
          self.exit_left = exit_left
          self.exit_right = exit_right
          self.allow_filtering = allow_filtering
          self.widgets_inherit_color = widgets_inherit_color
          super(MultiLine, self).__init__(screen, **keywords)
              # Call the base class.
          if self.height < self.__class__._MINIMUM_HEIGHT:
              raise widget.NotEnoughSpaceForWidget(
                  "Height of %s allocated. Not enough space allowed for %s" % (
                      self.height, str(self)))
          self.make_contained_widgets()
          self.return_exit = return_exit
              # does pushing return select and then leave the widget?
          self.select_exit = select_exit
              # does any selection leave the widget?
          self.always_show_cursor = always_show_cursor
              # Show cursor even when not editing?
          self.slow_scroll = slow_scroll
          self.scroll_exit = scroll_exit
          # EKR: Cursor and value ivars.
          self.start_display_at = 0
          self.cursor_line = 0
          self.values = values or []
          self.value = value
          self._filter = None
          # For optimisation...
          self._last_start_display_at = None
          self._last_cursor_line = None
          self._last_values = copy.copy(values)
          self._last_value = copy.copy(value)
          self._last_filter = None
          self._filtered_values_cache = []
          # override - it looks nicer.
          if self.scroll_exit:
              self.slow_scroll = True
      #@+node:ekr.20170428084208.76: *3* MultiLine.resize
      def resize(self):
          super(MultiLine, self).resize()
          self.make_contained_widgets()
          self.reset_display_cache()
          self.display()
  
      #@+node:ekr.20170428084208.77: *3* MultiLine.make_contained_widgets
      def make_contained_widgets(self):
          # The *only* make_contained_widgets (plural) in npyscreen.
          trace = False
          trace_widgets = True
          self._my_widgets = []
          height = self.height // self.__class__._contained_widget_height
          if trace: g.trace(self.__class__.__name__, height) #, g.callers(2))
              # Called from BoxTitle.make_contained_widget.
          for h in range(height):
              # EKR: it's LeoMLTree._contained_widgets that we have to emulate.
              self._my_widgets.append(
                  self._contained_widgets(
                      self.parent,
                      rely=(h*self._contained_widget_height)+self.rely,
                      relx = self.relx,
                      max_width=self.width,
                      max_height=self.__class__._contained_widget_height
              ))
          if trace and trace_widgets:
              g.printList(self._my_widgets)
              g.printList(['value: %r' % (z.value) for z in self._my_widgets])
      #@+node:ekr.20170428084208.78: *3* MultiLine.display_value
      def display_value(self, vl):
          """Overload this function to change how values are displayed.
          Should accept one argument (the object to be represented), and return a string or the
          object to be passed to the contained widget."""
          try:
              return self.safe_string(str(vl))
          except ReferenceError:
              return "**REFERENCE ERROR**"
          try:
              return "Error displaying " + self.safe_string(repr(vl))
          except Exception:
              return "**** Error ****"
      #@+node:ekr.20170428084208.79: *3* MultiLine.calculate_area_needed
      def calculate_area_needed(self):
          return 0,0
      #@+node:ekr.20170428084208.80: *3* MultiLine.reset_cursor
      def reset_cursor(self):
          self.start_display_at = 0
          self.cursor_line      = 0
      #@+node:ekr.20170428084208.81: *3* MultiLine.reset_display_cache
      def reset_display_cache(self):
          self._last_values = False
          self._last_value  = False
      #@+node:ekr.20170428084208.82: *3* MultiLine.update (LeoMLTree overrides this)
      def update(self, clear=True):
          trace = False # LeoMLTree.update overrides this.
          if trace and self.hidden:
              g.trace('hidden')
          if self.hidden and clear:
              self.clear()
              return False
          elif self.hidden:
              return False
          if self.values == None:
              self.values = []
          # clear = None is a good value for this widget
          display_length = len(self._my_widgets)
          #self._remake_filter_cache()
          self._filtered_values_cache = self.get_filtered_indexes()
          if self.editing or self.always_show_cursor:
              # EKR: Put cursor_line in range.
              if self.cursor_line < 0:
                  self.cursor_line = 0
              if self.cursor_line > len(self.values) - 1:
                  self.cursor_line = len(self.values) - 1
              if self.slow_scroll:
                  # Scroll by lines.
                  if self.cursor_line > self.start_display_at + display_length - 1:
                      self.start_display_at = self.cursor_line - (display_length - 1)
                  if self.cursor_line < self.start_display_at:
                      self.start_display_at = self.cursor_line
              else:
                  # Scroll by pages.
                  if self.cursor_line > self.start_display_at + (display_length - 2):
                      self.start_display_at = self.cursor_line
                  if self.cursor_line < self.start_display_at:
                      self.start_display_at = self.cursor_line - (display_length - 2)
                      if self.start_display_at < 0: self.start_display_at = 0
          # Don't update the screen if nothing has changed.
          # no_change = False
          try:
              no_change = (
                  self._safe_to_display_cache and
                  self._last_value is self.value and
                  self.values == self._last_values and
                  self.start_display_at == self._last_start_display_at and
                  clear != True and
                  self._last_cursor_line == self.cursor_line and
                  self._last_filter == self._filter and
                  self.editing
              )
          except Exception:
              no_change = False
          if clear:
              no_change = False
          if trace:
              from . import npysTree as npysTree
              val = self.values[self.cursor_line]
              # name = val.__class__.__name__
              if isinstance(val, npysTree.TreeData):
                  val = val.get_content()
              g.trace('changed: %5s, cursor_line: %s %s' % (
                  not no_change, self.cursor_line, val))
                  # self.start_display_at,
          if not no_change or clear or self.never_cache:
              if clear is True:
                  self.clear()
              if self._last_start_display_at != self.start_display_at and clear is None:
                  self.clear()
              else:
                  pass
              self._last_start_display_at = self.start_display_at
              self._before_print_lines()
              indexer = 0 + self.start_display_at
              for line in self._my_widgets[: -1]:
                  self._print_line(line, indexer)
                  line.task = "PRINTLINE"
                  line.update(clear=True)
                  indexer += 1
              # Now do the final line
              line = self._my_widgets[-1]
              if (len(self.values) <= indexer + 1):
                  # or (len(self._my_widgets)*self._contained_widget_height)<self.height:
                  self._print_line(line, indexer)
                  line.task = "PRINTLINE"
                  line.update(clear=False)
              elif len((self._my_widgets) * self._contained_widget_height) < self.height:
                  self._print_line(line, indexer)
                  line.task = "PRINTLINELASTOFSCREEN"
                  line.update(clear=False)
                  if self.do_colors():
                      self.parent.curses_pad.addstr(self.rely + self.height - 1, self.relx, MORE_LABEL, self.parent.theme_manager.findPair(self, 'CONTROL'))
                  else:
                      self.parent.curses_pad.addstr(self.rely + self.height - 1, self.relx, MORE_LABEL)
              else:
                  #line.value = MORE_LABEL
                  line.name = MORE_LABEL
                  line.task = MORE_LABEL
                  #line.highlight = False
                  #line.show_bold = False
                  line.clear()
                  if self.do_colors():
                      self.parent.curses_pad.addstr(
                          self.rely + self.height - 1,
                          self.relx, MORE_LABEL,
                          self.parent.theme_manager.findPair(self, 'CONTROL'),
                      )
                  else:
                      self.parent.curses_pad.addstr(
                          self.rely + self.height - 1,
                          self.relx,
                          MORE_LABEL,
                      )
              if self.editing or self.always_show_cursor:
                  self.set_is_line_cursor(self._my_widgets[(self.cursor_line - self.start_display_at)], True)
                  self._my_widgets[(self.cursor_line - self.start_display_at)].update(clear=True)
              else:
                  # There is a bug somewhere that affects the first line.  This cures it.
                  # Without this line, the first line inherits the color of the form when not editing. Not clear why.
                  self._my_widgets[0].update()
          # EKR: remember the previous values.
          self._last_start_display_at = self.start_display_at
          self._last_cursor_line = self.cursor_line
          self._last_values = copy.copy(self.values)
          self._last_value = copy.copy(self.value)
          # Prevent the program crashing if the user has changed values and
          # the cursor is now on the bottom line.
          if (self._my_widgets[self.cursor_line - self.start_display_at].task in
              (MORE_LABEL, "PRINTLINELASTOFSCREEN")
          ):
              if self.slow_scroll:
                  self.start_display_at += 1
              else:
                  self.start_display_at = self.cursor_line
              self.update(clear=clear)
      #@+node:ekr.20170428084208.83: *3* MultiLine._before_print_lines
      def _before_print_lines(self):
          # Provide a function for the Tree classes to override.
          pass
      #@+node:ekr.20170428084208.84: *3* MultiLine._print_line
      def _print_line(self, line, value_indexer):
  
          trace = False # LeoMLTree.update overrides this.
          if self.widgets_inherit_color and self.do_colors():
              line.color = self.color
          self._set_line_values(line, value_indexer)
          # Sets line.value
          if trace: g.trace(value_indexer, line.value.get_content())
              # line.value is a weakref to a LeoTreeData.
          self._set_line_highlighting(line, value_indexer)
      #@+node:ekr.20170504211313.1: *3* MultiLine.setters
      #@+node:ekr.20170428084208.85: *4* MultiLine._set_line_values
      def _set_line_values(self, line, value_indexer):
          try:
              _vl = self.values[value_indexer]
          except IndexError:
              self._set_line_blank(line)
              return False
          except TypeError:
              self._set_line_blank(line)
              return False
          line.value = self.display_value(_vl)
          line.hidden = False
      #@+node:ekr.20170428084208.86: *4* MultiLine._set_line_blank
      def _set_line_blank(self, line):
          line.value    = None
          line.show_bold= False
          line.name     = None
          line.hidden   = True
      #@+node:ekr.20170428084208.87: *4* MultiLine._set_line_highlighting
      def _set_line_highlighting(self, line, value_indexer):
          if value_indexer in self._filtered_values_cache:
              self.set_is_line_important(line, True)
          else:
              self.set_is_line_important(line, False)
          if (value_indexer == self.value) and \
              (self.value is not None):
              self.set_is_line_bold(line, True)
          else:
              self.set_is_line_bold(line, False)
          self.set_is_line_cursor(line, False)
      #@+node:ekr.20170428084208.88: *4* MultiLine.set_is_line_important
      def set_is_line_important(self, line, value):
          line.important = value
      #@+node:ekr.20170428084208.89: *4* MultiLine.set_is_line_bold
      def set_is_line_bold(self, line, value):
          line.show_bold = value
      #@+node:ekr.20170428084208.90: *4* MultiLine.set_is_line_cursor
      def set_is_line_cursor(self, line, value):
  
          # g.trace('Multiline')
          line.highlight = value
      #@+node:ekr.20170504211232.1: *3* MultiLine.filters
      #@+node:ekr.20170428084208.91: *4* MultiLine.get_filtered_indexes
      def get_filtered_indexes(self, force_remake_cache=False):
          if not force_remake_cache:
              try:
                  if self._last_filter == self._filter and self._last_values == self.values:
                      return self._filtered_values_cache
              except ReferenceError:
                  # Can happen if self.values was a list of weak references
                  pass
          self._last_filter = self._filter
          self._last_values = copy.copy(self.values)
          if self._filter == None or self._filter == '':
              return []
          list_of_indexes = []
          for indexer in range(len(self.values)):
              if self.filter_value(indexer):
                  list_of_indexes.append(indexer)
          return list_of_indexes
      #@+node:ekr.20170428084208.92: *4* MultiLine.get_filtered_values
      def get_filtered_values(self):
          fvls = []
          for vli in self.get_filtered_indexes():
              fvls.append(self.values[vli])
          return fvls
  
      #@+node:ekr.20170428084208.93: *4* MultiLine._remake_filter_cache
      def _remake_filter_cache(self):
          self._filtered_values_cache = self.get_filtered_indexes(force_remake_cache=True)
      #@+node:ekr.20170428084208.94: *4* MultiLine.filter_value
      def filter_value(self, index):
          if self._filter in self.display_value(self.values[index]):
              return True
          else:
              return False
      #@+node:ekr.20170428084208.95: *4* MultiLine.jump_to_first_filtered
      def jump_to_first_filtered(self, ):
          self.h_cursor_beginning(None)
          self.move_next_filtered(include_this_line=True)
      #@+node:ekr.20170428084208.96: *4* MultiLine.clear_filter
      def clear_filter(self):
          self._filter = None
          self.cursor_line = 0
          self.start_display_at = 0
      #@+node:ekr.20170428084208.97: *4* MultiLine.move_next_filtered
      def move_next_filtered(self, include_this_line=False, *args):
          if self._filter == None:
              return False
          for possible in self._filtered_values_cache:
              if (possible==self.cursor_line and include_this_line==True):
                  self.update()
                  break
              elif possible > self.cursor_line:
                  self.cursor_line = possible
                  self.update()
                  break
          try:
              if self.cursor_line-self.start_display_at > len(self._my_widgets) or \
              self._my_widgets[self.cursor_line-self.start_display_at].task == MORE_LABEL:
                  if self.slow_scroll:
                      self.start_display_at += 1
                  else:
                      self.start_display_at = self.cursor_line
          except IndexError:
              self.cursor_line = 0
              self.start_display_at = 0
      #@+node:ekr.20170428084208.98: *4* MultiLine.move_previous_filtered
      def move_previous_filtered(self, *args):
          if self._filter == None:
              return False
          # nextline = self.cursor_line
          _filtered_values_cache_reversed = copy.copy(self._filtered_values_cache)
          _filtered_values_cache_reversed.reverse()
          for possible in _filtered_values_cache_reversed:
              if possible < self.cursor_line:
                  self.cursor_line = possible
                  return True
      #@+node:ekr.20170428084208.99: *3* MultiLine.get_selected_objects
      def get_selected_objects(self):
          if self.value == None:
              return None
          else:
              return [self.values[x] for x in self.value]
      #@+node:ekr.20170504210158.1: *3* MultiLine.Handlers
      #@+node:ekr.20170428084208.100: *4* MultiLine.handle_mouse_event
      def handle_mouse_event(self, mouse_event):
          # unfinished
          #mouse_id, x, y, z, bstate = mouse_event
          #self.cursor_line = y - self.rely - self.parent.show_aty + self.start_display_at
          mouse_id, rel_x, rel_y, z, bstate = self.interpret_mouse_event(mouse_event)
          self.cursor_line = rel_y // self._contained_widget_height + self.start_display_at
          ##if self.cursor_line > len(self.values):
          ##    self.cursor_line = len(self.values)
          self.display()
      #@+node:ekr.20170428084208.101: *4* MultiLine.set_up_handlers
      def set_up_handlers(self):
          '''MultiLine.set_up_handlers.'''
          super(MultiLine, self).set_up_handlers()
          self.handlers.update ({
              curses.KEY_UP:      self.h_cursor_line_up,
              ord('k'):           self.h_cursor_line_up,
              curses.KEY_LEFT:    self.h_cursor_line_up,
              curses.KEY_DOWN:    self.h_cursor_line_down,
              ord('j'):           self.h_cursor_line_down,
              curses.KEY_RIGHT:   self.h_cursor_line_down,
              curses.KEY_NPAGE:   self.h_cursor_page_down,
              curses.KEY_PPAGE:   self.h_cursor_page_up,
              curses.ascii.TAB:   self.h_exit_down,
              curses.ascii.NL:    self.h_select_exit,
              curses.KEY_HOME:    self.h_cursor_beginning,
              curses.KEY_END:     self.h_cursor_end,
              ord('g'):           self.h_cursor_beginning,
              ord('G'):           self.h_cursor_end,
              ord('x'):           self.h_select,
              # "^L":        self.h_set_filtered_to_selected,
              curses.ascii.SP:    self.h_select,
              curses.ascii.ESC:   self.h_exit_escape,
              curses.ascii.CR:    self.h_select_exit,
          })
          if self.allow_filtering:
              self.handlers.update ( {
                  ord('l'):       self.h_set_filter,
                  ord('L'):       self.h_clear_filter,
                  ord('n'):       self.move_next_filtered,
                  ord('N'):       self.move_previous_filtered,
                  ord('p'):       self.move_previous_filtered,
                  # "^L":        self.h_set_filtered_to_selected,
              } )
          if self.exit_left:
              self.handlers.update({
                  curses.KEY_LEFT:    self.h_exit_left
              })
          if self.exit_right:
              self.handlers.update({
                  curses.KEY_RIGHT:   self.h_exit_right
              })
          self.complex_handlers = [
              # (self.t_input_isprint, self.h_find_char)
          ]
      #@+node:ekr.20170428084208.102: *4* MultiLine.h_find_char
      def h_find_char(self, input):
          # The following ought to work, but there is a curses keyname bug
          # searchingfor = curses.keyname(input).upper()
          # do this instead:
          searchingfor = chr(input).upper()
          for counter in range(len(self.values)):
              try:
                  if self.values[counter].find(searchingfor) is not -1:
                      self.cursor_line = counter
                      break
              except AttributeError:
                  break
      #@+node:ekr.20170428084208.103: *4* MultiLine.t_input_isprint
      def t_input_isprint(self, input):
          if curses.ascii.isprint(input): return True
          else: return False
      #@+node:ekr.20170428084208.104: *4* MultiLine.h_set_filter
      def h_set_filter(self, ch):
          if not self.allow_filtering:
              return None
          P = FilterPopupHelper()
          P.owner_widget = weakref.proxy(self)
          P.display()
          P.filterbox.edit()
          self._remake_filter_cache()
          self.jump_to_first_filtered()
      #@+node:ekr.20170428084208.105: *4* MultiLine.h_clear_filter
      def h_clear_filter(self, ch):
          self.clear_filter()
          self.update()
  
      #@+node:ekr.20170428084208.106: *4* MultiLine.h_cursor_beginning
      def h_cursor_beginning(self, ch):
  
          self.cursor_line = 0
      #@+node:ekr.20170428084208.107: *4* MultiLine.h_cursor_end
      def h_cursor_end(self, ch):
          self.cursor_line= len(self.values)-1
          if self.cursor_line < 0:
              self.cursor_line = 0
      #@+node:ekr.20170428084208.108: *4* MultiLine.h_cursor_page_down
      def h_cursor_page_down(self, ch):
          self.cursor_line += (len(self._my_widgets)-1) # -1 because of the -more-
          if self.cursor_line >= len(self.values)-1:
              self.cursor_line = len(self.values) -1
          if not (self.start_display_at + len(self._my_widgets) -1 ) > len(self.values):
              self.start_display_at += (len(self._my_widgets)-1)
              if self.start_display_at > len(self.values) - (len(self._my_widgets)-1):
                  self.start_display_at = len(self.values) - (len(self._my_widgets)-1)
  
      #@+node:ekr.20170428084208.109: *4* MultiLine.h_cursor_page_up
      def h_cursor_page_up(self, ch):
          self.cursor_line -= (len(self._my_widgets)-1)
          if self.cursor_line < 0:
              self.cursor_line = 0
          self.start_display_at -= (len(self._my_widgets)-1)
          if self.start_display_at < 0: self.start_display_at = 0
      #@+node:ekr.20170428084208.110: *4* MultiLine.h_cursor_line_up
      def h_cursor_line_up(self, ch):
          self.cursor_line -= 1
          if self.cursor_line < 0:
              if self.scroll_exit:
                  self.cursor_line = 0
                  self.h_exit_up(ch)
              else:
                  self.cursor_line = 0
  
      #@+node:ekr.20170428084208.111: *4* MultiLine.h_cursor_line_down
      def h_cursor_line_down(self, ch):
          self.cursor_line += 1
          if self.cursor_line >= len(self.values):
              if self.scroll_exit:
                  self.cursor_line = len(self.values)-1
                  self.h_exit_down(ch)
                  return True
              else:
                  self.cursor_line -=1
                  return True
          if self._my_widgets[self.cursor_line-self.start_display_at].task == MORE_LABEL:
              if self.slow_scroll:
                  self.start_display_at += 1
              else:
                  self.start_display_at = self.cursor_line
      #@+node:ekr.20170428084208.112: *4* MultiLine.h_exit
      def h_exit(self, ch):
  
          # g.trace('MultiLine')
          self.editing = False
          self.how_exited = True
  
      #@+node:ekr.20170428084208.113: *4* MultiLine.h_set_filtered_to_selected
      def h_set_filtered_to_selected(self, ch):
          # This is broken on multiline
          if len(self._filtered_values_cache) < 2:
              self.value = self._filtered_values_cache
          else:
              # There is an error - trying to select too many things.
              curses.beep()
      #@+node:ekr.20170428084208.114: *4* MultiLine.h_select
      def h_select(self, ch):
  
          # g.trace('MultiLine')
          self.value = self.cursor_line
          if self.select_exit:
              self.editing = False
              self.how_exited = True
      #@+node:ekr.20170428084208.115: *4* MultiLine.h_select_exit
      def h_select_exit(self, ch):
  
          # g.trace('MultiLine')
          self.h_select(ch)
          if self.return_exit or self.select_exit:
              self.editing = False
              self.how_exited=True
      #@+node:ekr.20170428084208.116: *4* MultiLine.edit
      def edit(self):
          trace = False and not g.unitTesting
          if trace:
              g.trace('===== (MultiLine:%s)' % self.__class__.__name__)
          self.editing = True
          self.how_exited = None
          #if self.value: self.cursor_line = self.value
          self.display()
          while self.editing:
              if trace: g.trace('(MultiLine:%s) LOOP' % self.__class__.__name__)
              self.get_and_use_key_press()
              self.update(clear=None)
              ##  self.clear()
              ##  self.update(clear=False)
              self.parent.refresh()
              ##  curses.napms(10)
              ##  curses.flushinp()
          if trace: g.trace('(MultiLine:%s) DONE' % self.__class__.__name__)
      #@-others
  #@+node:ekr.20170428084208.117: ** class MultiLineAction
  class MultiLineAction(MultiLine):
      RAISE_ERROR_IF_EMPTY_ACTION = False
      #@+others
      #@+node:ekr.20170428084208.118: *3* MultiLineAction.__init__
      def __init__(self, *args, **keywords):
          self.allow_multi_action = False
          super(MultiLineAction, self).__init__(*args, **keywords)
  
      #@+node:ekr.20170428084208.119: *3* MultiLineAction.actionHighlighted
      def actionHighlighted(self, act_on_this, key_press):
          "Override this Method"
          pass
  
      #@+node:ekr.20170428084208.120: *3* MultiLineAction.h_act_on_highlighted
      def h_act_on_highlighted(self, ch):
          try:
              return self.actionHighlighted(self.values[self.cursor_line], ch)
          except IndexError:
              if self.RAISE_ERROR_IF_EMPTY_ACTION:
                  raise
              else:
                  pass
  
      #@+node:ekr.20170428084208.121: *3* MultiLineAction.set_up_handlers
      def set_up_handlers(self):
          '''MultiLineAction.set_up_handlers.'''
          super(MultiLineAction, self).set_up_handlers()
          self.handlers.update ( {
              curses.ascii.NL:    self.h_act_on_highlighted,
              curses.ascii.CR:    self.h_act_on_highlighted,
              ord('x'):           self.h_act_on_highlighted,
              curses.ascii.SP:    self.h_act_on_highlighted,
          })
  
  
      #@-others
  #@+node:ekr.20170428084208.122: ** class MultiLineActionWithShortcuts
  class MultiLineActionWithShortcuts(MultiLineAction):
      shortcut_attribute_name = 'shortcut'
      #@+others
      #@+node:ekr.20170428084208.123: *3* MultiLineActionWithShortcuts.set_up_handlers
      def set_up_handlers(self):
          '''MultiLineActionWithShortcuts.set_up_handlers.'''
          super(MultiLineActionWithShortcuts, self).set_up_handlers()
          self.add_complex_handlers( ((self.h_find_shortcut_action, self.h_execute_shortcut_action),) )
  
  
      #@+node:ekr.20170428084208.124: *3* MultiLineActionWithShortcuts.h_find_shortcut_action
      def h_find_shortcut_action(self, _input):
          _input_decoded = curses.ascii.unctrl(_input)
          for r in range(len(self.values)):
              if hasattr(self.values[r], self.shortcut_attribute_name):
                  # from . import utilNotify
                  if getattr(self.values[r], self.shortcut_attribute_name) == _input \
                  or getattr(self.values[r], self.shortcut_attribute_name) == _input_decoded:
                      return r
          return False
  
      #@+node:ekr.20170428084208.125: *3* MultiLineActionWithShortcuts.h_execute_shortcut_action
      def h_execute_shortcut_action(self, _input):
          l = self.h_find_shortcut_action(_input)
          if l is False:
              return None
          self.cursor_line = l
          self.display()
          self.h_act_on_highlighted(_input)
  
  
  
  
      #@-others
  #@+node:ekr.20170428084208.126: ** class Pager
  class Pager(MultiLine):
      #@+others
      #@+node:ekr.20170428084208.127: *3* Pager.__init__
      def __init__(self, screen, autowrap=False,  center=False, **keywords):
          super(Pager, self).__init__(screen, **keywords)
          self.autowrap = autowrap
          self.center = center
          self._values_cache_for_wrapping = []
  
      #@+node:ekr.20170428084208.128: *3* Pager.reset_display_cache
      def reset_display_cache(self):
          super(Pager, self).reset_display_cache()
          self._values_cache_for_wrapping = False
  
      #@+node:ekr.20170428084208.129: *3* Pager._wrap_message_lines
      def _wrap_message_lines(self, message_lines, line_length):
          lines = []
          for line in message_lines:
              if line.rstrip() == '':
                  lines.append('')
              else:
                  this_line_set = textwrap.wrap(line.rstrip(), line_length)
                  if this_line_set:
                      lines.extend(this_line_set)
                  else:
                      lines.append('')
          return lines
  
      #@+node:ekr.20170428084208.130: *3* Pager.resize
      def resize(self):
          super(Pager, self).resize()
          if self.autowrap:
              self.setValuesWrap(list(self.values))
          if self.center:
              self.centerValues()
  
      #@+node:ekr.20170428084208.131: *3* Pager.setValuesWrap
      def setValuesWrap(self, lines):
          if self.autowrap and (lines == self._values_cache_for_wrapping):
              return False
          try:
              lines = lines.split('\n')
          except AttributeError:
              pass
          self.values = self._wrap_message_lines(lines, self.width-1)
          self._values_cache_for_wrapping = self.values
  
      #@+node:ekr.20170428084208.132: *3* Pager.centerValues
      def centerValues(self):
          self.values  = [ l.strip().center(self.width-1) for l in self.values ]
  
      #@+node:ekr.20170428084208.133: *3* Pager.update
      def update(self, clear=True):
          #we look this up a lot. Let's have it here.
          if self.autowrap:
              self.setValuesWrap(list(self.values))
  
          if self.center:
              self.centerValues()
  
          display_length = len(self._my_widgets)
          values_len = len(self.values)
  
          if self.start_display_at > values_len - display_length:
              self.start_display_at = values_len - display_length
          if self.start_display_at < 0: self.start_display_at = 0
  
          indexer = 0 + self.start_display_at
          for line in self._my_widgets[:-1]:
              self._print_line(line, indexer)
              indexer += 1
  
          # Now do the final line
          line = self._my_widgets[-1]
  
          if values_len <= indexer+1:
              self._print_line(line, indexer)
          else:
              line.value = MORE_LABEL
              line.highlight = False
              line.show_bold = False
  
          for w in self._my_widgets:
              # call update to avoid needless refreshes
              w.update(clear=True)
          # There is a bug somewhere that affects the first line.  This cures it.
          # Without this line, the first line inherits the color of the form when not editing. Not clear why.
          self._my_widgets[0].update()
  
  
  
      #@+node:ekr.20170428084208.134: *3* Pager.edit
      def edit(self):
          # Make sure a value never gets set.
          value = self.value
          super(Pager, self).edit()
          self.value = value
  
      #@+node:ekr.20170428084208.135: *3* Pager.h_scroll_line_up
      def h_scroll_line_up(self, input):
          self.start_display_at -= 1
          if self.scroll_exit and self.start_display_at < 0:
              self.editing = False
              self.how_exited = widget.EXITED_UP
  
      #@+node:ekr.20170428084208.136: *3* Pager.h_scroll_line_down
      def h_scroll_line_down(self, input):
          self.start_display_at += 1
          if self.scroll_exit and self.start_display_at >= len(self.values)-self.start_display_at+1:
              self.editing = False
              self.how_exited = widget.EXITED_DOWN
  
      #@+node:ekr.20170428084208.137: *3* Pager.h_scroll_page_down
      def h_scroll_page_down(self, input):
          self.start_display_at += len(self._my_widgets)
  
      #@+node:ekr.20170428084208.138: *3* Pager.h_scroll_page_up
      def h_scroll_page_up(self, input):
          self.start_display_at -= len(self._my_widgets)
  
      #@+node:ekr.20170428084208.139: *3* Pager.h_show_beginning
      def h_show_beginning(self, input):
          self.start_display_at = 0
  
      #@+node:ekr.20170428084208.140: *3* Pager.h_show_end
      def h_show_end(self, input):
          self.start_display_at = len(self.values) - len(self._my_widgets)
  
      #@+node:ekr.20170428084208.141: *3* Pager.h_select_exit
      def h_select_exit(self, _input):
          self.exit(self, _input)
  
      #@+node:ekr.20170428084208.142: *3* Pager.set_up_handlers
      def set_up_handlers(self):
          '''Pager.set_up_handlers.'''
          super(Pager, self).set_up_handlers()
          self.handlers = {
              curses.KEY_UP:      self.h_scroll_line_up,
              curses.KEY_LEFT:    self.h_scroll_line_up,
              curses.KEY_DOWN:    self.h_scroll_line_down,
              curses.KEY_RIGHT:   self.h_scroll_line_down,
              curses.KEY_NPAGE:   self.h_scroll_page_down,
              curses.KEY_PPAGE:   self.h_scroll_page_up,
              curses.KEY_HOME:    self.h_show_beginning,
              curses.KEY_END:     self.h_show_end,
              curses.ascii.NL:    self.h_exit,
              curses.ascii.CR:    self.h_exit,
              curses.ascii.SP:    self.h_scroll_page_down,
              curses.ascii.TAB:   self.h_exit,
              ord('j'):           self.h_scroll_line_down,
              ord('k'):           self.h_scroll_line_up,
              ord('x'):           self.h_exit,
              ord('q'):           self.h_exit,
              ord('g'):           self.h_show_beginning,
              ord('G'):           self.h_show_end,
              curses.ascii.ESC:   self.h_exit_escape,
          }
          self.complex_handlers = []
  
      #@-others
  #@+node:ekr.20170428084208.143: ** class TitleMultiLine
  class TitleMultiLine(titlefield.TitleText):
      _entry_type = MultiLine
  
      #@+others
      #@+node:ekr.20170428084208.144: *3* get_selected_objects
      def get_selected_objects(self):
          return self.entry_widget.get_selected_objects()
  
      #@+node:ekr.20170428084208.145: *3* get_values
      def get_values(self):
          if hasattr(self, 'entry_widget'):
              return self.entry_widget.values
          elif hasattr(self, '__tmp_value'):
              return self.__tmp_values
          else:
              return None
      #@+node:ekr.20170428084208.146: *3* set_values
      def set_values(self, value):
          if hasattr(self, 'entry_widget'):
              self.entry_widget.values = value
          elif hasattr(self, '__tmp_value'):
              # probably trying to set the value before the textarea is initialised
              self.__tmp_values = value
      #@+node:ekr.20170428084208.147: *3* del_values
      def del_values(self):
          del self.entry_widget.value
      values = property(get_values, set_values, del_values)
  
  
      #@-others
  #@+node:ekr.20170428084208.148: ** class TitlePager
  class TitlePager(TitleMultiLine):
      _entry_type = Pager
  
  #@+node:ekr.20170428084208.149: ** class BufferPager
  class BufferPager(Pager):
      DEFAULT_MAXLEN = None
  
      #@+others
      #@+node:ekr.20170428084208.150: *3* BufferPager.__init__
      def __init__(self, screen, maxlen=False, *args, **keywords):
          super(BufferPager, self).__init__(screen, *args, **keywords)
          if maxlen is False:
              maxlen = self.DEFAULT_MAXLEN
          self.values = collections.deque(maxlen=maxlen)
  
      #@+node:ekr.20170428084208.151: *3* BufferPager.clearBuffer
      def clearBuffer(self):
          self.values.clear()
  
      #@+node:ekr.20170428084208.152: *3* BufferPager.setValuesWrap
      def setValuesWrap(self, lines):
          if self.autowrap and (lines == self._values_cache_for_wrapping):
              return False
          try:
              lines = lines.split('\n')
          except AttributeError:
              pass
  
          self.clearBuffer()
          self.buffer(self._wrap_message_lines(lines, self.width-1))
          self._values_cache_for_wrapping = copy.deepcopy(self.values)
  
      #@+node:ekr.20170428084208.153: *3* BufferPager.buffer
      def buffer(self, lines, scroll_end=True, scroll_if_editing=False):
          "Add data to be displayed in the buffer."
          self.values.extend(lines)
          if scroll_end:
              if not self.editing:
                  self.start_display_at = len(self.values) - len(self._my_widgets)
              elif scroll_if_editing:
                  self.start_display_at = len(self.values) - len(self._my_widgets)
  
      #@-others
  #@+node:ekr.20170428084208.154: ** class TitleBufferPager
  class TitleBufferPager(TitleMultiLine):
      _entry_type = BufferPager
  
      #@+others
      #@+node:ekr.20170428084208.155: *3* clearBuffer
      def clearBuffer(self):
          return self.entry_widget.clearBuffer()
  
      #@+node:ekr.20170428084208.156: *3* buffer
      def buffer(self, *args, **values):
          return self.entry_widget.buffer(*args, **values)
  
  
  
  
  
      #@-others
  #@-others
  #@@language python
  #@@tabwidth -4
  #@-leo
  