module RNDK

# RNDK String Markup

RNDK has special formatting commands which can be included in any String which add highlights, justification, or even colors to a basic String.

These attributes, once set, remain in effect until changed explicitly, or until the end of the string.

## Colors

RNDK has the capability to display colors in almost any text inside a Widget.

To turn on colors, the function initCDKColor has to be called. When this function is called 64 color pairs are created. Normally the color pairs are accessed via the COLOR_PAIR macro. You can still do this, but creating a string with multiple colors gets terribly difficult. That is why the color commands were created.

The color settings are stored directly in the string. When the widget is created or activated, the string is converted to take advan- tage of any color commands in the string. To turn on a color pair insert </XX> into the string; where XX is a numeric value from 0 to 64. Color pair 0 is the standard default color pair for the screen. To turn off a color pair use the format command <!XX> where XX is a numeric value from 0 to 64.

For example:

"</31>This line should have a yellow foreground and a cyan background.<!31>"
"</05>This line should have a white  foreground and a blue background.<!05>"
"</26>This line should have a yellow foreground and a red  background.<!26>"
"<C>This line should be set to whatever the screen default is."

## Attributes

RNDK also provides attribute commands which allow different character attributes to be displayed in a Widget.

To use a character attribute the format command is ‘</X>` where X is one of several command characters. To turn a attribute off use the command `<!X>`.

Here’s the command characters supported:

B

Bold

U

Underline

K

Blink

R

Reverse

S

Standout

D

Dim

N

Normal

For example:

"</B/31>Bold text        yellow foreground / blue background.<!31>"
"</U/05>Underlined text  white  foreground / blue background.<!05>"
"</K/26>Blinking text    yellow foreground / red  background.<!26>"
"<C>This line uses the screen default colors."

## Justification

Justification commands can **left justify**, **right justify**, or center a string of text.

A format command must be at the beginning of the string.

To use a justification format in a string the command ‘<X>` is used.

Format commands:

<L>

Left Justified. Default if not stated.

<C>

Centered text.

<R>

Right justified.

<I=X>

Indent the line X characters.

<B=X>

Bullet. X is the bullet string to use.

<F=X>

Links in a file where X is the filename. This works only with the viewer widget.

For example:

"<R></B/31>This line should have a yellow foreground and a blue background.<!31>"
"</U/05>This line should have a white  foreground and a blue background.<!05>"
"<B=+>This is a bullet."
"<I=10>This is indented 10 characters."
"<C>This line should be set to whatever the screen default is."

The bullet format command can take either a single character or a string. The bullet in the above example would look like

+ This is a bullet.

but if we were to use the following command instead

<B=***>This is a bullet.

it would look like

*** This is a bullet.

## Special Drawing Characters

RNDK has a set of special drawing characters which can be inserted into any ASCII file. In order to use a special character the format command ‘<#XXX>` is used.

Special character commands:

<#UL>

Upper Left Corner

<#UR>

Upper Right Corner

<#LL>

Lower Left Corner

<#LR>

Lower Right Corner

<#LT>

Left Tee

<#RT>

Right Tee

<#TT>

Top Tee

<#BT>

Bottom Tee

<#HL>

Horizontal Line

<#VL>

Vertical Line

<#PL>

Plus Sign

<#PM>

Plus or Minus Sign

<#DG>

Degree Sign

<#CB>

Checker Board

<#DI>

Diamond

<#BU>

Bullet

<#S1>

Scan line 1

<#S9>

Scan line 9

<#LA>

Left Arrow

<#RA>

Right Arrow

<#TA>

Top Arrow

<#BA>

Bottom Arrow

The character formats can be repeated using an optional numeric repeat value. To repeat a character add the repeat count within parentheses to the end of the character format.

The following example draws 10 horizontal-line characters:

<#HL(10)>

And the most complex example until now. Guess what it does:

"<C><#UL><#HL(26)><#UR>"
"<C><#VL></R>This text should be boxed.<!R><#VL>"
"<C><#LL><#HL(26)><#LR>"
"<C>While this is not."

Utility functions - misc things.

Constants

ALL_SCREENS

All the screens eventually created.

ALL_WIDGETS

All the widgets eventually created.

BACKCHAR
BEGOFLINE
BOTTOM
CENTER
COL
COPY
CUT
DELETE
ENDOFLINE
ERASE
FORCHAR
FULL
HORIZONTAL
KEY_ESC
KEY_RETURN
KEY_TAB
LEFT
L_MARKER
MAX_BINDINGS
MAX_ButtonS
MAX_ITEMS
NEXT
NONE
PASTE
PREV
REFRESH

Aliasing the default keybindings for Widget interaction.

Example: ‘RNDK.CTRL(’L’)‘ is `Ctrl+L` or `C-l`

RNDK_PATHMAX

Some nice global constants.

ROW
R_MARKER
TOP
TRANSPOSE
VERSION
VERSION_MAJOR
VERSION_MINOR
VERSION_PATCH
VERTICAL

Public Class Methods

CTRL(char) click to toggle source

Key value when pressing Ctrl+‘char`.

# File lib/rndk.rb, line 66
def RNDK.CTRL(char)
  char.ord & 0x1f
end
KEY_F(n) click to toggle source

Returns internal Ncurses’ function keys values (F1, F2 … F12 …)

# File lib/rndk.rb, line 127
def RNDK.KEY_F(n)
  264 + n
end
alignxy(window, xpos, ypos, box_width, box_height) click to toggle source

Aligns a box on the given ‘window` with the width and height given.

x and y position values are like ‘RNDK::CENTER`, `RNDK::LEFT`, `RNDK::RIGHT`.

‘xpos`, `ypos` is an Array with exactly one value, an integer.

‘box_width`, `box_height` is an integer.

# File lib/rndk.rb, line 204
def RNDK.alignxy (window, xpos, ypos, box_width, box_height)

  # Handling xpos
  first = Ncurses.getbegx window
  last  = Ncurses.getmaxx window

  gap = 0 if (gap = (last - box_width)) < 0

  last = first + gap

  case xpos[0]
  when LEFT   then xpos[0] = first
  when RIGHT  then xpos[0] = first +  gap
  when CENTER then xpos[0] = first + (gap / 2)
  else
    xpos[0] = last  if xpos[0] > last
    xpos[0] = first if xpos[0] < first
  end

  # Handling ypos
  first = Ncurses.getbegy window
  last  = Ncurses.getmaxy window

  gap = 0 if (gap = (last - box_height)) < 0

  last = first + gap

  case ypos[0]
  when TOP    then ypos[0] = first
  when BOTTOM then ypos[0] = first +  gap
  when CENTER then ypos[0] = first + (gap / 2)
  else
    ypos[0] = last  if ypos[0] > last
    ypos[0] = first if ypos[0] < first
  end
end
baseName(pathname) click to toggle source

Returns the filename portion of the given pathname, i.e. after the last slash.

For now this function is just a wrapper for ‘File.basename`, being kept for portability.

# File lib/rndk/core/utils.rb, line 80
def RNDK.baseName (pathname)
  File.basename(pathname)
end
beep() click to toggle source

Beeps then flushes the stdout stream.

Normally it emits an audible bell - if that’s not possible it flashes the screen.

# File lib/rndk.rb, line 102
def RNDK.beep
  Ncurses.beep
  $stdout.flush
end
char2Chtype(string, to, align) click to toggle source

Takes a String full of format markers and translates it into a chtype array.

This is better suited to curses because curses uses chtype almost exclusively

# File lib/rndk/core/markup.rb, line 162
def RNDK.char2Chtype(string, to, align)
  to << 0
  align << LEFT
  result = []

  if string.size > 0
    used = 0

    # The original code makes two passes since it has to pre-allocate space but
    # we should be able to make do with one since we can dynamically size it
    adjust = 0
    attrib = RNDK::Color[:normal]
    last_char = 0
    start = 0
    used = 0
    x = 3

    # Look for an alignment marker.
    if string[0] == L_MARKER
      if string[1] == 'C' && string[2] == R_MARKER
        align[0] = CENTER
        start = 3
      elsif string[1] == 'R' && string[2] == R_MARKER
        align[0] = RIGHT
        start = 3
      elsif string[1] == 'L' && string[2] == R_MARKER
        start = 3
      elsif string[1] == 'B' && string[2] == '='
        # Set the item index value in the string.
        result = [' '.ord, ' '.ord, ' '.ord]

        # Pull out the bullet marker.
        while x < string.size and string[x] != R_MARKER
          result << (string[x].ord | RNDK::Color[:bold])
          x += 1
        end
        adjust = 1

        # Set the alignment variables
        start = x
        used = x
      elsif string[1] == 'I' && string[2] == '='
        from = 3
        x = 0

        while from < string.size && string[from] != Ncurses.R_MARKER
          if RNDK.digit?(string[from])
            adjust = adjust * 10 + string[from].to_i
            x += 1
          end
          from += 1
        end

        start = x + 4
      end
    end

    while adjust > 0
      adjust -= 1
      result << ' '
      used += 1
    end

    # Set the format marker boolean to false
    inside_marker = false

    # Start parsing the character string.
    from = start
    while from < string.size
      # Are we inside a format marker?
      if !inside_marker
        if string[from] == L_MARKER &&
            ['/', '!', '#'].include?(string[from + 1])
          inside_marker = true
        elsif string[from] == "\\" && string[from + 1] == L_MARKER
          from += 1
          result << (string[from].ord | attrib)
          used += 1
          from += 1
        elsif string[from] == "\t"
          begin
            result << ' '
            used += 1
          end while (used & 7).nonzero?
        else
          result << (string[from].ord | attrib)
          used += 1
        end
      else
        case string[from]
        when R_MARKER
          inside_marker = false
        when '#'
          last_char = 0
          case string[from + 2]
          when 'L'
            case string[from + 1]
            when 'L'
              last_char = Ncurses::ACS_LLCORNER
            when 'U'
              last_char = Ncurses::ACS_ULCORNER
            when 'H'
              last_char = Ncurses::ACS_HLINE
            when 'V'
              last_char = Ncurses::ACS_VLINE
            when 'P'
              last_char = Ncurses::ACS_PLUS
            end
          when 'R'
            case string[from + 1]
            when 'L'
              last_char = Ncurses::ACS_LRCORNER
            when 'U'
              last_char = Ncurses::ACS_URCORNER
            end
          when 'T'
            case string[from + 1]
            when 'T'
              last_char = Ncurses::ACS_TTEE
            when 'R'
              last_char = Ncurses::ACS_RTEE
            when 'L'
              last_char = Ncurses::ACS_LTEE
            when 'B'
              last_char = Ncurses::ACS_BTEE
            end
          when 'A'
            case string[from + 1]
            when 'L'
              last_char = Ncurses::ACS_LARROW
            when 'R'
              last_char = Ncurses::ACS_RARROW
            when 'U'
              last_char = Ncurses::ACS_UARROW
            when 'D'
              last_char = Ncurses::ACS_DARROW
            end
          else
            case [string[from + 1], string[from + 2]]
            when ['D', 'I']
              last_char = Ncurses::ACS_DIAMOND
            when ['C', 'B']
              last_char = Ncurses::ACS_CKBOARD
            when ['D', 'G']
              last_char = Ncurses::ACS_DEGREE
            when ['P', 'M']
              last_char = Ncurses::ACS_PLMINUS
            when ['B', 'U']
              last_char = Ncurses::ACS_BULLET
            when ['S', '1']
              last_char = Ncurses::ACS_S1
            when ['S', '9']
              last_char = Ncurses::ACS_S9
            end
          end

          if last_char.nonzero?
            adjust = 1
            from += 2

            if string[from + 1] == '('
              # check for a possible numeric modifier
              from += 2
              adjust = 0

              while from < string.size && string[from] != ')'
                if RNDK.digit?(string[from])
                  adjust = (adjust * 10) + string[from].to_i
                end
                from += 1
              end
            end
          end
          (0...adjust).each do |x|
            result << (last_char | attrib)
            used += 1
          end
        when '/'
          mask = []
          from = RNDK.encodeAttribute(string, from, mask)
          attrib |= mask[0]
        when '!'
          mask = []
          from = RNDK.encodeAttribute(string, from, mask)
          attrib &= ~(mask[0])
        end
      end
      from += 1
    end

    if result.size == 0
      result << attrib
    end
    to[0] = used
  else
    result = []
  end
  return result
end
char_of(chtype) click to toggle source
# File lib/rndk/core/markup.rb, line 153
def RNDK.char_of chtype
  (chtype.ord & 255).chr
end
chtype2Char(string) click to toggle source

This returns a string from a chtype array Formatting codes are omitted.

# File lib/rndk/core/markup.rb, line 395
def RNDK.chtype2Char(string)
  newstring = ''

  unless string.nil?
    string.each do |char|
      newstring << RNDK.char_of(char)
    end
  end

  return newstring
end
chtype2String(string) click to toggle source

This returns a string from a chtype array Formatting codes are embedded

# File lib/rndk/core/markup.rb, line 409
def RNDK.chtype2String(string)
  newstring = ''
  unless string.nil?
    need = 0
    (0...string.size).each do |x|
      need = RNDK.decodeAttribute(newstring, need,
                                 x > 0 ? string[x - 1] : 0, string[x])
      newstring << string[x]
    end
  end

  return newstring
end
cleanChar(s, len, character) click to toggle source

This sets a blank string to be len of the given character.

# File lib/rndk.rb, line 132
def RNDK.cleanChar(s, len, character)
  s << character * len
end
cleanChtype(s, len, character) click to toggle source
# File lib/rndk.rb, line 136
def RNDK.cleanChtype(s, len, character)
  s.concat(character * len)
end
cmpStrChstr(str, chstr) click to toggle source

Compare a regular string to a chtype string

# File lib/rndk/core/markup.rb, line 363
def RNDK.cmpStrChstr(str, chstr)
  i = 0
  r = 0

  if str.nil? && chstr.nil?
    return 0
  elsif str.nil?
    return 1
  elsif chstr.nil?
    return -1
  end

  while i < str.size && i < chstr.size
    if str[r].ord < chstr[r]
      return -1
    elsif str[r].ord > chstr[r]
      return 1
    end
    i += 1
  end

  if str.size < chstr.size
    return -1
  elsif str.size > chstr.size
    return 1
  else
    return 0
  end
end
decodeAttribute(string, from, oldattr, newattr) click to toggle source

The reverse of encodeAttribute Well, almost. If attributes such as bold and underline are combined in the same string, we do not necessarily reconstruct them in the same order. Also, alignment markers and tabs are lost.

# File lib/rndk/core/markup.rb, line 495
def RNDK.decodeAttribute (string, from, oldattr, newattr)
  table = {
    'B' => RNDK::Color[:bold],
    'D' => RNDK::Color[:dim],
    'K' => RNDK::Color[:blink],
    'R' => RNDK::Color[:reverse],
    'S' => RNDK::Color[:standout],
    'U' => RNDK::Color[:underline]
  }

  result = if string.nil? then '' else string end
  base_len = result.size
  tmpattr = oldattr & RNDK::Color[:extract]

  newattr &= RNDK::Color[:extract]
  if tmpattr != newattr
    while tmpattr != newattr
      found = false
      table.keys.each do |key|
        if (table[key] & tmpattr) != (table[key] & newattr)
          found = true
          result << RNDK::L_MARKER
          if (table[key] & tmpattr).nonzero?
            result << '!'
            tmpattr &= ~(table[key])
          else
            result << '/'
            tmpattr |= table[key]
          end
          result << key
          break
        end
      end
      # XXX: Only checks if terminal has colours not if colours are started
      if Ncurses.has_colors
        if (tmpattr & Ncurses::A_COLOR) != (newattr & Ncurses::A_COLOR)
          oldpair = Ncurses.PAIR_NUMBER(tmpattr)
          newpair = Ncurses.PAIR_NUMBER(newattr)
          if !found
            found = true
            result << RNDK::L_MARKER
          end
          if newpair.zero?
            result << '!'
            result << oldpair.to_s
          else
            result << '/'
            result << newpair.to_s
          end
          tmpattr &= ~(Ncurses::A_COLOR)
          newattr &= ~(Ncurses::A_COLOR)
        end
      end

      if found
        result << RNDK::R_MARKER
      else
        break
      end
    end
  end

  return from + result.size - base_len
end
digit?(character) click to toggle source
# File lib/rndk.rb, line 107
def RNDK.digit? character
  return false if character.nil?

  not character.match(/^[[:digit:]]$/).nil?
end
dirName(pathname) click to toggle source

Returns the directory for the given pathname, i.e. the part before the last slash For now this function is just a wrapper for File.dirname kept for ease of porting and will be completely replaced in the future

# File lib/rndk/core/utils.rb, line 88
def RNDK.dirName (pathname)
  File.dirname(pathname)
end
encodeAttribute(string, from, mask) click to toggle source
# File lib/rndk/core/markup.rb, line 447
def RNDK.encodeAttribute (string, from, mask)
  mask << 0
  case string[from + 1]
  when 'B'
    mask[0] = RNDK::Color[:bold]
  when 'D'
    mask[0] = RNDK::Color[:dim]
  when 'K'
    mask[0] = RNDK::Color[:blink]
  when 'R'
    mask[0] = RNDK::Color[:reverse]
  when 'S'
    mask[0] = RNDK::Color[:standout]
  when 'U'
    mask[0] = RNDK::Color[:underline]
  end

  if mask[0] != 0
    from += 1
  elsif RNDK.digit?(string[from+1]) and RNDK.digit?(string[from + 2])
    if Ncurses.has_colors
      # XXX: Only checks if terminal has colours not if colours are started
      pair = string[from + 1..from + 2].to_i
      mask[0] = Ncurses.COLOR_PAIR(pair)
    else
      mask[0] = Ncurses.A_BOLD
    end

    from += 2
  elsif RNDK.digit?(string[from + 1])
    if Ncurses.has_colors
      # XXX: Only checks if terminal has colours not if colours are started
      pair = string[from + 1].to_i
      mask[0] = Ncurses.COLOR_PAIR(pair)
    else
      mask[0] = Ncurses.A_BOLD
    end

    from += 1
  end

  return from
end
get_directory_contents(directory) click to toggle source

Returns all files from ‘directory` as an Array of Strings.

# File lib/rndk/core/utils.rb, line 12
def RNDK.get_directory_contents directory
  list = []

  Dir.foreach(directory) do |filename|
    next if filename == '.' or filename == '..'
    list << filename
  end

  list.sort
end
intlen(value) click to toggle source

Number of digits ‘value` has when represented as a String.

# File lib/rndk/core/utils.rb, line 6
def RNDK.intlen value
  value.to_str.size
end
is_alpha?(character) click to toggle source
# File lib/rndk.rb, line 113
def RNDK.is_alpha? character
  return false if character.nil?

  not character.match(/^[[:alpha:]]$/).nil?
end
is_char?(character) click to toggle source
# File lib/rndk.rb, line 119
def RNDK.is_char? character
  return false if character.nil?

  (character >= 0) and (character < Ncurses::KEY_MIN)
end
justifyString(box_width, mesg_length, justify) click to toggle source

This takes a string, a field width, and a justification type and returns the adjustment to make, to fill the justification requirement

# File lib/rndk/core/markup.rb, line 426
def RNDK.justifyString (box_width, mesg_length, justify)

  # make sure the message isn't longer than the width
  # if it is, return 0
  if mesg_length >= box_width
    return 0
  end

  # try to justify the message
  case justify
  when LEFT
    0
  when RIGHT
    box_width - mesg_length
  when CENTER
    (box_width - mesg_length) / 2
  else
    justify
  end
end
read_file(filename) click to toggle source

Reads a file and concatenate it’s lines into ‘array`.

@note The lines don’t end with ‘n’.

# File lib/rndk/core/utils.rb, line 122
def RNDK.read_file filename
  begin
    fd = File.new(filename, "r")
  rescue
    return nil
  end

  lines = fd.readlines.map do |line|
    if line.size > 0 && line[-1] == "\n"
      line[0...-1]
    else
      line
    end
  end
  fd.close

  lines
end
search_list(list, list_size, pattern) click to toggle source

This looks for a subset of a word in the given list

# File lib/rndk/core/utils.rb, line 24
def RNDK.search_list(list, list_size, pattern)
  index = -1

  if pattern.size > 0
    (0...list_size).each do |x|
      len = [list[x].size, pattern.size].min
      ret = (list[x][0...len] <=> pattern)

      # If 'ret' is less than 0 then the current word is alphabetically
      # less than the provided word.  At this point we will set the index
      # to the current position.  If 'ret' is greater than 0, then the
      # current word is alphabetically greater than the given word. We
      # should return with index, which might contain the last best match.
      # If they are equal then we've found it.
      if ret < 0
        index = ret
      else
        if ret == 0
          index = x
        end
        break
      end
    end
  end
  return index
end
set_widget_dimension(parent_dim, proposed_dim, adjustment) click to toggle source

If the dimension is a negative value, the dimension will be the full height/width of the parent window - the value of the dimension. Otherwise, the dimension will be the given value.

# File lib/rndk/core/utils.rb, line 95
def RNDK.set_widget_dimension (parent_dim, proposed_dim, adjustment)
  # If the user passed in FULL, return the parents size
  if proposed_dim == FULL or proposed_dim == 0
    parent_dim
  elsif proposed_dim >= 0
    # if they gave a positive value, return it

    if proposed_dim >= parent_dim
      parent_dim
    else
      proposed_dim + adjustment
    end
  else
    # if they gave a negative value then return the dimension
    # of the parent plus the value given
    #
    if parent_dim + proposed_dim < 0
      parent_dim
    else
      parent_dim + proposed_dim
    end
  end
end
window_delete(window) click to toggle source

Safely deletes a raw Ncurses window.

# File lib/rndk.rb, line 153
def RNDK.window_delete window
  return if window.nil?

  RNDK.window_erase window
  Ncurses.delwin window
  window = nil
end
window_erase(window) click to toggle source

Safely erases a raw Ncurses window.

# File lib/rndk.rb, line 145
def RNDK.window_erase window
  return if window.nil?

  Ncurses.werase window
  Ncurses.wrefresh window
end
window_move(window, xdiff, ydiff) click to toggle source

Safely moves a raw Ncurses window.

## Developer Note:

Moves a given window if we’re able to set the window’s beginning. We do not use mvwin(), because it does not (usually) move subwindows.

# File lib/rndk.rb, line 167
def RNDK.window_move(window, xdiff, ydiff)
  return if window.nil?

  xpos = []
  ypos = []
  Ncurses.getbegyx(window, ypos, xpos)
  if Ncurses.mvwin(window, ypos[0], xpos[0]) != Ncurses::ERR
    xpos[0] += xdiff
    ypos[0] += ydiff
    Ncurses.werase window
    Ncurses.mvwin(window, ypos[0], xpos[0])
  else
    RNDK.beep
  end
end
window_refresh(win) click to toggle source

Refreshes a raw Ncurses window.

## Developer Notes

FIXME(original): this should be rewritten to use the panel library, so it would not be necessary to touch the window to ensure that it covers other windows.

# File lib/rndk.rb, line 190
def RNDK.window_refresh win
  Ncurses.touchwin win
  Ncurses.wrefresh win
end