RangesetHotKeys

From Niki

Jump to: navigation, search

This is an update to the RangeSetMacroCollection. The update allows the user to access some of the RangesetMacroCollection features with two function keys.

F7 - Find All Selected (predetermined rangeset colors are rotated automatically)

Highlight text and press F7. All text in the file that matches the highlighted text is marked with the default rangeset color and the rangset default color is automatically changed. Place the cursor in an existing rangeset, press F7 and the rangeset is removed.

shift F7 - Remove all

F8 - Go to next range (rangeset color is determined by cursor position)

Place cursor in an existing rangeset and press F8. The rangeset under the cursor becomes the default rangeset and the cursor goes to the next occurrence of the rangeset.

shift F8 Go to prev. (rangeset color is determined by cursor position)

Place cursor in an existing rangeset and press shift F8. The rangeset under the curser becomes the default rangeset and the cursor goes to the previous occurrence of the rangeset.

Import this updated macro menu definitions into NEdit.

nedit.macroCommands: \
	Rangesets>Goto next range:::: {\n\
		RANGESET_goto_next()\n\
	}\n\
	Rangesets>Goto prev. range:::: {\n\
		RANGESET_goto_prev()\n\
	}\n\
	Rangesets>Add:::R: {\n\
		RANGESET_add()\n\
	}\n\
	Rangesets>Remove:::R: {\n\
		RANGESET_remove_selected()\n\
	}\n\
	Rangesets>Clear:::: {\n\
		RANGESET_remove_all()\n\
	}\n\
	Rangesets>Destroy:::: {\n\
		RANGESET_destroy()\n\
	}\n\
	Rangesets>Rename:::: {\n\
		RANGESET_rename()\n\
	}\n\
	Rangesets>Color:::: {\n\
		RANGESET_color()\n\
	}\n\
	Rangesets>Save:::: {\n\
		RANGESET_save()\n\
	}\n\
	Rangesets>Load:::: {\n\
		RANGESET_load()\n\
	}\n\
	Rangesets>Find All:::: {\n\
		RANGESET_find_all()\n\
	}\n\
	Rangesets>Show Changes:::: {\n\
		RANGESET_show_changes()\n\
	}\n\
	Rangesets>Go to next range:F8::: {\n\
		RANGESET_go_to_next()\n\
	}\n\
	Rangesets>Go to prev. range:Shift+F8::: {\n\
		RANGESET_goto_prev()\n\
	}\n\
	Rangesets>Find All Selected :F7::: {\n\
		RANGESET_find_all_selected("new_color")\n\
	}\n\
	Rangesets>Remove all:Shift+F7::: {\n\
		RANGESET_remove()\n\
	}\n


Substitute this rangeset.nm file in place of the file listed under RangeSetMacroCollection and be amazed at how quickly you can take advantage of the rangeset capability of NEdit.

# ======================================================================
# Adapt $COLOR_LIST: specify path to rangesetRgb.txt file !
#
# This file contains following interface procedures:
# - RANGESET_add
# - RANGESET_remove_selected
# - RANGESET_remove_all
# - RANGESET_color
# - RANGESET_rename
# - RANGESET_destroy
# - RANGESET_save
# - RANGESET_load
# - RANGESET_find_all
# - RANGESET_goto_next
# - RANGESET_goto_prev
# - RANGESET_show_changes
# ======================================================================
# This file contains following sub procedures:
# ======================================================================
# - RANGESET_ask_for_name
# - RANGESET_verify_name
# - RANGESET_get_id
# - RANGESET_rename_in_all_windows
# - RANGESET_rename_in_window
# - RANGESET_define_new
# - RANGESET_allocate_new
# - RANGESET_destroy_name
# - RANGESET_select
# - RANGESET_choose_color
# - RANGESET_get_color
# - RANGESET_set_color
# - RANGESET_add_selection
# - RANGESET_get_next_pos
# - RANGESET_get_prev_pos
# - RANGESET_exists
# - RANGESET_store_inline
# - RANGESET_store_in_file
# - RANGESET_write
# - RANGESET_read_inline
# - RANGESET_read_from_file
# - RANGESET_read
# - RANGESET_read_set
# - RANGESET_read_block
# - RANGESET_get_line_info
# ======================================================================
# Global variables used:
# READ ONLY:
# - $NEDIT_HOME = name of the NEDIT_HOME directory of the user
# - $COLOR_LIST = specifies a file holding predefined color values
# READ & WRITE:
# - $NBR_OF_RANGESETS = number of defined rangesets
# - $RANGESET         = array holding defined rangesets; each array
#                       element holds following "fields":
#                       * "name"  : name of rangeset
#                       * "color" : associated color of rangeset
# ======================================================================

# ======================================================================
# === Initialization ===================================================
# ======================================================================

$COLOR_LIST = getenv("HOME")"/.nedit/macros/rangesetRgb.txt"
$NBR_OF_RANGESETS = 0
RANGESET_allocate_new( "Yellow", "yellow" )
RANGESET_allocate_new( "Green", "lightgreen" )
RANGESET_allocate_new( "Blue", "lightblue" )
RANGESET_allocate_new( "Red", "red" )
$color_name = "Yellow"
$last_selected = RANGESET_get_id($color_name)
# ======================================================================
# === Interface procedures =============================================
# ======================================================================

# ----------------------------------------------------------------------
# RANGESET_add():
# add selected text to a rangeset, which is chosen by dialog
# ----------------------------------------------------------------------

define RANGESET_add {

  name = RANGESET_ask_for_name("Add selected text to rangeset:\n\nChoose Rangeset", "NEW")

  if(name != "")
    RANGESET_add_selection( RANGESET_get_id( name ) )
}

# ----------------------------------------------------------------------
# RANGESET_remove_selected():
# remove selected text from a rangeset, which is chosen by dialog
# ----------------------------------------------------------------------

define RANGESET_remove_selected {
  name = RANGESET_ask_for_name("Remove selected text from rangeset:\n\nChoose Rangeset", "")

  if (name != "")
  {
    rangeset_subtract( RANGESET_get_id( name ) )
    deselect_all()
  }
}

# ----------------------------------------------------------------------
# RANGESET_remove_all():
# remove all text from a rangeset / all rangesets (chosen by dialog)
# within current window.
# (i.e. clear rangeset)
# ----------------------------------------------------------------------

define RANGESET_remove_all {
  name = RANGESET_ask_for_name("Clear marked text\n(i.e. remove all text from rangeset):\n\nChoose Rangeset", "ALL")

  if (name != "")
  {
    if (name == "ALL")
    {
      for(i=0; i < $NBR_OF_RANGESETS; i++)
      {
        rangeset_subtract(RANGESET_get_id($RANGESET[i, "name"]), 0, $text_length)
      }
    }
    else
    {
      rangeset_subtract(RANGESET_get_id(name), 0, $text_length)
    }
  }
}

# ----------------------------------------------------------------------
# RANGESET_color():
# change color of a rangeset, which is chosen by dialog
# ----------------------------------------------------------------------

define RANGESET_color {
  name = RANGESET_ask_for_name("Change color:\n\nChoose Rangeset", "")

  if (name != "")
  {
    oldColor = RANGESET_get_color(name)
    color = RANGESET_choose_color(name, oldColor, 0)
    RANGESET_set_color( name, color )
  }
}

# ----------------------------------------------------------------------
# RANGESET_rename():
# rename an existing rangeset, which is chosen by dialog
# ----------------------------------------------------------------------

define RANGESET_rename {
  oldName = RANGESET_ask_for_name("Rename:\n\nChoose Rangeset", "")

  if (oldName != "")
  {
    while(1)
    {
      newName = string_dialog( "New Name of Rangeset:", "OK", "Cancel" )

      if($string_dialog_button == 1)
      {
        if (RANGESET_verify_name(newName))
        {
          for(i=0; i < $NBR_OF_RANGESETS; i++)
          {
            if( $RANGESET[i, "name"] == oldName)
            {
              RANGESET_rename_in_all_windows(oldName, newName)

              $RANGESET[i, "name"] = newName
            }
          }
          return
        }
        else
        {
          if (oldName == newName)
            return
          if (newName != "")
            dialog("Name <" newName "> already defined.\nPlease choose another one.", "OK")
        }
      }
      else
        return
    }
  }
}

# ----------------------------------------------------------------------
# RANGESET_destroy():
# destroy existing rangeset(s) (chosen by dialog)
# ----------------------------------------------------------------------

define RANGESET_destroy {
  name = RANGESET_ask_for_name("Choose Rangeset to destroy", "ALL")

  if (name != "")
  {
    if (name == "ALL")
    {
      while ($NBR_OF_RANGESETS != 0)
      {
        RANGESET_destroy_name($RANGESET[$NBR_OF_RANGESETS - 1, "name"])
      }
    }
    else
    {
      RANGESET_destroy_name(name)
    }
  }
}

# ----------------------------------------------------------------------
# RANGESET_save():
# save information of all rangesets defined in current window
# ----------------------------------------------------------------------

define RANGESET_save {
  storageType = dialog("Save Rangset Info:\nPlease select storage location.", "Inline", "Extra File", "Cancel")

  if (storageType == 1)
  {
    RANGESET_store_inline()
  }
  else if (storageType == 2)
  {
    RANGESET_store_in_file()
  }
}

# ----------------------------------------------------------------------
# RANGESET_load():
# load rangeset information and apply it on current window
# ----------------------------------------------------------------------

define RANGESET_load {

  storageType = dialog("Load Rangset Info:\nPlease select source location.", "Inline", "Extra File", "Cancel")

  if (storageType == 1)
  {
    RANGESET_read_inline()
  }
  else if (storageType == 2)
  {
    RANGESET_read_from_file()
  }
}

# ----------------------------------------------------------------------
# RANGESET_find_all():
# find all occurrences of a string and add them to a rangeset,
# which is chosen by dialog. The text to be searched as well as the
# mode of search is chosen by dialog.
# ----------------------------------------------------------------------

define RANGESET_find_all {
  type       = "case"

  findString = string_dialog("Find string", "Case", "Literal", "Regex", "Dismiss")

  if (findString == "")
    return

  if ($string_dialog_button == 1)
    type = "case"
  else if ($string_dialog_button == 2)
    type = "literal"
  else if ($string_dialog_button == 3)
    type = "regex"
  else
    return

  name = RANGESET_ask_for_name("Choose rangeset to add finds to", "NEW")
  if (name == "")
    return

  id = RANGESET_get_id(name)

  pos = 0
  for (pos = search(findString, 0, type); pos != -1; pos = search(findString, pos, type))
  {
    rangeset_add(id, pos, $search_end)
    pos = $search_end
  }
}

# ----------------------------------------------------------------------
# RANGESET_goto_next():
# Goto start of a range which is part of a rangeset and which is
# located next to current cursor position in forward direction.
# The rangeset is selected by dialog, if current cursor position
# is not located inside a rangeset.
# ----------------------------------------------------------------------

define RANGESET_goto_next {
  startPos = $cursor
  idx = 0

  for (i=0; i < $NBR_OF_RANGESETS && idx == 0; i++)
  {
    rId = RANGESET_get_id($RANGESET[i, "name"])
    idx = rangeset_includes(rId, startPos)
    if (idx == 0)  # if cursor is at end of a range list then accept it
      idx = rangeset_includes(rId, startPos-1)
  }

  if (idx == 0)
  {
    # -- cursor not located within any rangeset:
    # -- ask user for rangeset to goto
    name = RANGESET_ask_for_name("Goto next range:\n\nChoose rangeset", "")
    if (name == "")
      return

    rId = RANGESET_get_id( name )

    nextPos = RANGESET_get_next_pos(rId, startPos, 1)
  }
  else
  {
    nextPos = RANGESET_get_next_pos(rId, rangeset_range( rId, idx )["end"] + 1, idx+1)
  }

  if (nextPos == -1 || startPos == nextPos)
    beep()
  else
  {
    if (startPos > nextPos)
      beep()

    set_cursor_pos (nextPos)
    RANGESET_centerline()
  }
}

# ----------------------------------------------------------------------
# RANGESET_goto_prev():
# Goto start of a range which is part of a rangeset and which is
# located next to current cursor position in backward direction.
# The rangeset is selected by dialog, if current cursor position
# is not located inside a rangeset.
# ----------------------------------------------------------------------

define RANGESET_goto_prev {
  startPos = $cursor
  idx = 0

  for (i=0; i < $NBR_OF_RANGESETS && idx == 0; i++)
  {
    rId = RANGESET_get_id($RANGESET[i, "name"])
    idx = rangeset_includes(rId, startPos)
    if (idx == 0)  # if cursor is at end of a range list then accept it
      idx = rangeset_includes(rId, startPos-1)
  }

  if(idx == 0)
  {
    # -- cursor not located within any rangeset:
    # -- ask user for rangeset to goto
    name = RANGESET_ask_for_name("Goto previous range:\n\nChoose rangeset", "")
    if (name == "")
      return

    rId = RANGESET_get_id(name)

    prevPos = RANGESET_get_prev_pos(rId, startPos, -1)
  }
  else
  {
    prevPos = RANGESET_get_prev_pos(rId, rangeset_range(rId, idx)["start"], idx - 1)
  }

  if (prevPos == -1 || startPos == prevPos)
    beep()
  else
  {
    if (startPos < prevPos)
      beep()

    set_cursor_pos(prevPos)
    RANGESET_centerline()
  }
}

# ----------------------------------------------------------------------
# RANGESET_show_changes(): opens a new window showing changes between
# the current editor window buffer and the file it represents.
# ----------------------------------------------------------------------

define RANGESET_show_changes {
  filespec = $file_path $file_name

  buffer = get_range(0, $text_length)

  # t_print( ">" buffer "<\n" )

  grep_cmd = "/tools/gnu/bin/grep -E -e '^[0-9]+[adc][0-9]+$'" \
                    " -e '^[0-9]+[adc][0-9]+,[0-9]+$'" \
                    " -e '^[0-9]+,[0-9]+[adc][0-9]+$'" \
                    " -e '^[0-9]+,[0-9]+[adc][0-9]+,[0-9]+$'"
  diff_cmd = "diff " filespec " -"

  # -- get the difference command's output
  diff_txt = shell_command(diff_cmd, buffer)
  if ($shell_cmd_status > 1)
  {
    dialog("The difference command returned an error status:\n" \
           "Command: " diff_cmd "\n" \
           "Status: " $shell_cmd_status "\n" \
           "Output: " diff_txt)
    return
  }

  if ($shell_cmd_status == 0)
  {
    dialog("No differences found with file " filespec)
    return
  }

  # -- t_print( "\n --- diff ---\n" diff_txt "\n ------\n")

  # -- remove all but the difference "instructions"
  diff_txt = shell_command(grep_cmd, diff_txt)
  if ($shell_cmd_status != 0)
  {
    dialog("Problem finding difference directives:\n" \
           "Command: " grep_cmd "\n" \
           "Status: " $shell_cmd_status "\n" \
           "Output: " diff_txt)
    return
  }

  # t_print( "\n --- grep ---\n" diff_txt "\n ------\n")

  # now we have a list of Add, Change and Delete line range instructions
  # right: now some comments
  # each directive is of the form #1,#2[adc]#3,#4, and represent operations
  # required to convert from the file to the current text in our editor window.
  #     a - add at #1,#2 lines between #3,#4
  #     d - delete lines #1,#2 to get to #3,#4
  #     c - change lines #1,#2 to lines #3,#4
  # orthogonalise them, remove commas, keep only the RHS, drop deletes

  if (search_string(diff_txt,"^[0-9,]+([adc])([0-9]+),([0-9]+)$",0,"regex")!=-1)
    diff_txt = replace_in_string(diff_txt, \
                                 "^[0-9,]+([adc])([0-9]+),([0-9]+)$", \
                                 "\\1 \\2 \\3 ", "regex")
  if (search_string(diff_txt, "^[0-9,]+([adc])([0-9]+)$", 0, "regex") != -1)
    diff_txt = replace_in_string(diff_txt, \
                                 "^[0-9,]+([adc])([0-9]+)$", \
                                 "\\1 \\2 \\2 ", "regex")

  RANGESET_destroy_name("Add")
  RANGESET_destroy_name("Change")
  RANGESET_destroy_name("Delete")

  RANGESET_allocate_new("Add","#80ff00" )
  RANGESET_allocate_new("Change", "orange")
  RANGESET_allocate_new("Delete", "gray")

  add = RANGESET_get_id("Add")
  chg = RANGESET_get_id("Change")
  del = RANGESET_get_id("Delete")

  # t_print( "\n --- repl ---\n" diff_txt "\n ------\n")

  # -- determine start / end pos of lines in buffer
  linePosInfo = RANGESET_get_line_info(buffer "\n")

  # -- parse diff_txt
  lineEndPos = 0
  idx        = 0
  pos        = 0

  while (lineEndPos != -1)
  {
    lineEndPos = search_string( diff_txt, "\n", pos )

    if( lineEndPos != -1)
    {
      line = substring( diff_txt, pos, lineEndPos)
      diffInfo[idx,"type" ] = substring( line, 0, 1 )
      blankPos = search_string( line, " ", 2 )
      diffInfo[idx,"start"] = substring( line, 2, blankPos )
      diffInfo[idx,"end"  ] = substring( line, blankPos+1, length( line ) )

      # t_print( "\n" idx ": " diffInfo[idx,"type" ] ";")
      # t_print( diffInfo[idx,"start" ] ";")
      # t_print( diffInfo[idx,"end" ] "\n")

      idx ++
      pos = lineEndPos + 1
    }
  }

  # -- t_print( "\n-------------\n")

  for( i=0; i < idx; i++ )
  {
    startLineNbr = diffInfo[i,"start"] + 0
    endLineNbr   = diffInfo[i,"end"] + 0

    if((diffInfo[i,"type"] == "d") && (startLineNbr == endLineNbr))
    {
      startLineNbr ++
      endLineNbr ++
    }

    startPos = linePosInfo[startLineNbr, "start"]
    endPos   = linePosInfo[endLineNbr, "end"]

    if( startPos == endPos)
    {
      # -- empty line -> include CR
      endPos ++
    }

    # -- t_print( startLineNbr "; " endLineNbr "\n")

    if(diffInfo[i,"type"] == "d")
      rangeset_add(del, startPos , endPos)
    else if (diffInfo[i,"type"] == "a")
      rangeset_add(add, startPos , endPos)
    else
      rangeset_add(chg, startPos , endPos)
  }
}

# ======================================================================
# === Sub procedures ===================================================
# ======================================================================

# ----------------------------------------------------------------------
# RANGESET_ask_for_name(infoText, mode):
# select a name of a rangeset.
# Parameters:
# $1 = list dialog info text
# $2 = mode:
#        "NEW" -> allow creation of new rangesets
#        "ALL" -> allow selection of all rangesets
# returns: name of rangeset (empty string, if none was chosen)
# ----------------------------------------------------------------------

define RANGESET_ask_for_name {

  if ($2 != "")
    nameList = $2"\n"
  else
    nameList = ""

  for(i=0; i < $NBR_OF_RANGESETS; i++)
  {
    nameList = nameList $RANGESET[i, "name"] "\n"
  }

  selName = list_dialog( $1, nameList, "OK", "Cancel")

  if($list_dialog_button == 2)
  {
    return ""
  }

  if(selName == "NEW")
  {
    selName = RANGESET_define_new()
  }

  return selName
}

# ----------------------------------------------------------------------
# RANGESET_verify_name(name):
# verify if given name is valid for rangeset (e.g. check for duplicates).
# Parameters:
# $1 = name to be verified
# returns: 1, if verification is successful, else 0.
# ----------------------------------------------------------------------

define RANGESET_verify_name {

  if ($1 == "")
    return 0

  return !RANGESET_exists($1)
}

# ----------------------------------------------------------------------
# RANGESET_get_id(name)
# get ID of rangeset of current window whose name matches "name"
# Parameters:
# $1 = name of rangeset
# returns: ID of rangeset
# ----------------------------------------------------------------------

define RANGESET_get_id {
  for (i=0; i < $NBR_OF_RANGESETS; i++)
  {
    if ($RANGESET[i, "name"] == $1)
    {
      rIdArray = rangeset_get_by_name($1)

      if (rIdArray[] == 0)
      {
        # allocate new rangeset id for window ..
        rId = rangeset_create()
        rangeset_set_color(rId, $RANGESET[i, "color"])
        rangeset_set_name(rId, $1)
      }
      else
        rId = rIdArray[0]

      return rId
    }
  }

  # no entry in $RANGESET for given name found: (should never happen)
  return "data inconsistency"
}

# ----------------------------------------------------------------------
# RANGESET_rename_in_all_windows(oldName, newName)
# rename rangeset named "oldName" to "newName" within all windows.
# Parameters:
# $1 = old name
# $2 = new name
# none
# returns: ---
# ----------------------------------------------------------------------

define RANGESET_rename_in_all_windows {

  startWindowId = $file_path $file_name
  currentWindowId = startWindowId
  endOfWindow = 0

  while (!endOfWindow)
  {
    RANGESET_rename_in_window($1, $2)
    currentWindowId = focus_window("next")
    if (currentWindowId == "")
      currentWindowId = focus_window("last")

    if (currentWindowId == startWindowId)
      endOfWindow = 1
  }
}

# ----------------------------------------------------------------------
# RANGESET_rename_in_window(oldName, newName)
# rename rangeset named "oldName" to "newName" within current window.
# Parameters:
# $1 = old name
# $2 = new name
# returns: ---
# ----------------------------------------------------------------------

define RANGESET_rename_in_window {

  rIdArray = rangeset_get_by_name($1)

  if (rIdArray[] != 0)
  {
    rId = rIdArray[0]
    rangeset_set_name(rId, $2)
  }
}

# ----------------------------------------------------------------------
# RANGESET_define_new()
# define a new rangeset by selecting name and color via dialog.
# Parameters:
# ---
# returns: name of new rangeset
# ----------------------------------------------------------------------

define RANGESET_define_new {

  while(1)
  {
    name = string_dialog( "Name of new Rangeset:", "Create", "Cancel" )

    if($string_dialog_button == 1)
    {
      if (RANGESET_verify_name(name))
      {
        color = RANGESET_choose_color(name, "-", 1)
        if (color == "-")
          return ""

        if(!RANGESET_allocate_new(name, color))
        {
          return ""
        }

        return name
      }
      else
      {
        if (name != "")
          dialog("Name <" name "> already defined.\nPlease choose another one.", "OK")
      }
    }
    else
    {
      return ""
    }
  }
}

# ----------------------------------------------------------------------
# RANGESET_allocate_new(name, color)
# allocate a new rangeset of name "name"; assign given color to new
# rangeset.
# Parameters:
# $1 = name of range set
# $2 = color of range set
# returns: 1, if rangeset could be allocated, else 0.
# ----------------------------------------------------------------------

define RANGESET_allocate_new {

    rId = rangeset_create()

    if(rId == 0)
    {
      dialog( "Sorry:\n\nNo more rangesets available", "OK" )
      return 0
    }

    rangeset_set_color(rId, $2)
    rangeset_set_name(rId, $1)

    $RANGESET[$NBR_OF_RANGESETS, "name" ] = $1
    $RANGESET[$NBR_OF_RANGESETS, "color"] = $2

    $NBR_OF_RANGESETS ++

    return 1
}

# ----------------------------------------------------------------------
# RANGESET_destroy_name(name)
# Destroy rangeset identified by "name" within all windows.
# Parameters:
# $1 = name of rangeset
# returns: ---
# ----------------------------------------------------------------------

define RANGESET_destroy_name {

  currentWindowId = $file_path $file_name

  delIdx = -1

  for (i=0; i < $NBR_OF_RANGESETS; i++)
  {
    if (delIdx != -1)
    {
      $RANGESET[i-1, "name" ] = $RANGESET[i, "name" ]
      $RANGESET[i-1, "color"] = $RANGESET[i, "color"]
    }
    else if($RANGESET[i, "name"] == $1)
    {
      startWindowId = currentWindowId
      endOfWindow = 0

      while (!endOfWindow)
      {
        rIdArray = rangeset_get_by_name($1)

        if (rIdArray[] != 0)
          rangeset_destroy(rIdArray[0])

        currentWindowId = focus_window("next")
        if (currentWindowId == "")
          currentWindowId = focus_window("last")

        if (currentWindowId == startWindowId)
          endOfWindow = 1
      }

      delIdx = i
    }
  }

  if (delIdx != -1)
  {
    $NBR_OF_RANGESETS --
    focus_window( currentWindowId )
  }
}

# ----------------------------------------------------------------------
# RANGESET_select(selectionParamArray)
# select some text specified by "selectionParamArray".
# Parameters:
# $1 = array with selection parameters ("start", "end", "left", "right")
# returns: ---
# ----------------------------------------------------------------------

define RANGESET_select {
  sel = $1
  if (sel["left"] != -1)
    select_rectangle(sel["start"], sel["end"], sel["left"], sel["right"])
  else if (sel["start"] != -1)
    select(sel["start"], sel["end"])
}

# ----------------------------------------------------------------------
# RANGESET_choose_color(name, oldColor, newFlag)
# select (via dialog) and assign a color to rangeset identified
# by given "name".
# Parameters:
# $1 = name of rangeset
# $2 = old color
# $3 = new rangeset flag (1 = new rangeset)
# returns: name of selected color
# ----------------------------------------------------------------------

define RANGESET_choose_color {
  appliedColor = $2
  newRS = $3
  chosen = 0
  byListDialog = 1
  rsAvailable = 0
  triedColor = ""
  sel["start"] = $selection_start
  sel["end"]   = $selection_end
  sel["left"]  = $selection_left
  sel["right"] = $selection_right

  preDefColorList = read_file($COLOR_LIST)
  if (preDefColorList == "")
    dialog("Warning: no predefined colors available.\nFile <"\
           $COLOR_LIST "> not existing ?", "OK")

  if (sel["start"] != -1)
  {
    rId = rangeset_create()
    RANGESET_add_selection(rId)
    RANGESET_select(sel)
    rsAvailable = 1
  }
  else if (!newRS)
  {
    rId = RANGESET_get_id($1)
    rsAvailable = 1
  }

  while (!chosen)
  {
      if (appliedColor == "")
        dialogColor = "NONE"
      else
        dialogColor = appliedColor

      dialogText = "Choose color for rangeset '" $1 "'\n(current chosen color = " dialogColor "):"

      if (preDefColorList == "")
      {
        color = string_dialog(dialogText, "OK", "Try", "Cancel")
        buttonSelected = $string_dialog_button
        if (buttonSelected > 1)
          buttonSelected ++
      }
      else if (byListDialog)
      {
        color = list_dialog(dialogText, preDefColorList, "OK", "Other", "Try", "Cancel")
        buttonSelected = $list_dialog_button
      }
      else
      {
        color = string_dialog(dialogText, "OK", "Predefined", "Try", "Cancel")
        buttonSelected = $string_dialog_button
      }

      if (buttonSelected == 1)
      {
        if (appliedColor != "" && color == "")
          color = appliedColor
        chosen = 1
      }
      else if (buttonSelected == 2)
      {
        byListDialog = !byListDialog
      }
      else if (buttonSelected == 3)
      {
        deselect_all()
        if (rsAvailable)
        {
          rangeset_set_color(rId, color)
        }
        appliedColor = color
      }
      else
      {
        color = $2
        chosen = 1
      }
  }

  if (sel["start"] != -1)
  {
    rangeset_destroy(rId)
  }

  RANGESET_select(sel)

  return color
}

# ----------------------------------------------------------------------
# RANGESET_get_color(name):
# get color of rangeset identified by "name".
# Parameters:
# $1 = name of rangeset
# returns: color of rangeset
# ----------------------------------------------------------------------

define RANGESET_get_color {

  for (i=0; i < $NBR_OF_RANGESETS; i++)
  {
    if ($RANGESET[i, "name"] == $1)
    {
      return $RANGESET[i, "color"]
    }
  }
  return ""
}

# ----------------------------------------------------------------------
# RANGESET_set_color(name, color):
# assign given "color" to rangeset identified by "name"
# Parameters:
# $1 = name of rangeset
# $2 = color
# returns: ---
# ----------------------------------------------------------------------

define RANGESET_set_color {

  currentWindowId = $file_path $file_name

  for(i=0; i < $NBR_OF_RANGESETS; i++)
  {
    if( $RANGESET[i, "name"] == $1)
    {
      startWindowId = currentWindowId
      endOfWindow = 0

      while (!endOfWindow)
      {
        rIdArray = rangeset_get_by_name($1)

        if (rIdArray[] != 0)
          rangeset_set_color(rIdArray[0], $2)

        currentWindowId = focus_window("next")
        if (currentWindowId == "")
          currentWindowId = focus_window("last")

        if (currentWindowId == startWindowId)
          endOfWindow = 1
      }

      $RANGESET[i, "color"] = $2
    }
  }
  focus_window(currentWindowId)
}

# ----------------------------------------------------------------------
# RANGESET_add_selection(rangesetId):
# add selected text to rangeset specified by "rangesetId".
# Parameters:
# $1 = ID of rangeset
# returns: ---
# ----------------------------------------------------------------------

define RANGESET_add_selection {
  if ($selection_left == -1)
    rangeset_add($1)
  else
  {
    # Rectangular selection: add line by line

    # to determine start of line:
    lineEndPos = search("\n", $selection_start, "backward")

    while (lineEndPos < $selection_end)
    {
      # determine start of line
      lineStartPos = lineEndPos + 1

      # determine end of line
      lineEndPos = search("\n", lineStartPos)

      # determine subrange start pos.
      pos = lineStartPos + $selection_left

      if (pos < lineEndPos)
      {
        if (lineEndPos == -1)
          lineEndPos = $text_length
# returns: ---

        # determine endPos of subrange:
        # is $selection_right behind line end ?
        # set subrange end to lineEnd in this case
        posEnd = lineStartPos + $selection_right

        if (posEnd > lineEndPos)
          posEnd = lineEndPos

        #add the subrange
        rangeset_add($1, pos, posEnd)
      }
    }
  }
  deselect_all()
}

# ----------------------------------------------------------------------
# RANGESET_centerline():
# attempts to vertical center the cursor position in the current window.
# returns: ---
# ----------------------------------------------------------------------

define RANGESET_centerline {
  cursor = $cursor

  scroll_to_line($line - $n_display_lines/2)

  set_cursor_pos(cursor)
}

# ----------------------------------------------------------------------
# RANGESET_get_next_pos(rangesetId, cursorPos, rangeIndex):
# determine start position of range, which is located next to given
# cursor position "cursorPos" in forward direction.
# Parameters:
# $1 = ID of rangeset
# $2 = cursor pos. to start with
# $3 = range index to start with
# returns: start position of next range
# ----------------------------------------------------------------------

define RANGESET_get_next_pos {

  rId = $1
  pos = $2

  info = rangeset_info( rId )

  nbrOfRanges = info["count"]

  for (i = $3; i <= nbrOfRanges; i++)
  {
    range = rangeset_range( rId, i )

    if (range["end"] >= pos)
      return range["start"]
  }

  # -- no range after "pos" ->
  # -- select 1st range, if any exists
  if (nbrOfRanges == 0)
    return -1
  else
    return rangeset_range(rId, 1)["start"]
}

# ----------------------------------------------------------------------
# RANGESET_get_prev_pos(rangesetId, cursorPos, rangeIndex):
# determine start position of range, which is located next to given
# cursor position "cursorPos" in backward direction.
# Parameters:
# $1 = ID of rangeset
# $2 = cursor pos. to start with
# $3 = range index to start with (-1 -> start with last one)
# returns: start position of previous range
# ----------------------------------------------------------------------

define RANGESET_get_prev_pos {

  rId = $1
  pos = $2

  info = rangeset_info(rId)

  nbrOfRanges = info["count"]

  if ($3 == -1)
    startIdx = nbrOfRanges
  else
    startIdx = $3

  for (i = startIdx; i >= 1; i--)
  {
    range = rangeset_range(rId, i)

    if (range["end"] <= pos)
      return range["start"]
  }

  # -- no range after "pos" ->
  # -- select last range, if any exists
  if (nbrOfRanges == 0)
    return -1
  else
    return rangeset_range(rId, nbrOfRanges)["start"]
}

# ----------------------------------------------------------------------
# RANGESET_exists(name):
# check, if rangeset specified by name is defined.
# Parameters:
# $1 = name of rangeset
# returns: 1, if rangeset exists, else 0
# ----------------------------------------------------------------------

define RANGESET_exists {

  for (i=0; i < $NBR_OF_RANGESETS; i++)
  {
    if ($RANGESET[i, "name"] == $1)
    {
      return 1
    }
  }
  return 0
}

# ----------------------------------------------------------------------
# RANGESET_store_inline():
# store rangeset info inside current window (XML style)
# returns: ---
# ----------------------------------------------------------------------

define RANGESET_store_inline {

  # -- look for an already existing rangeset-info block
  # -- in current buffer

  buffer = get_range(0, $text_length)

  rsiBlock = RANGESET_read_block(buffer, "rangeset-info", 0)

  if (rsiBlock["eobPos"] != -1)
  {
    out = RANGESET_write(0)

    startReplace = rsiBlock["startContPos"]
    endReplace   = startReplace + length(rsiBlock["content"])

    replace_range(startReplace, endReplace, out)
  }
  else
  {
    out = RANGESET_write(1)

    if ($language_mode == "CHILL" || \
        $language_mode == "CHILL Sourceman" || \
        $language_mode == "C++" || \
        $language_mode == "C")
    {
      out = "/**************\n" out "*************/\n"
    }

    currentPos = $cursor
    end_of_file()
    insert_string("\n" out)
    set_cursor_pos(currentPos)
  }
}

# ----------------------------------------------------------------------
# RANGESET_store_in_file():
# store rangeset info within an extra file (XML style)
# returns: ---
# ----------------------------------------------------------------------

define RANGESET_store_in_file {
  out = RANGESET_write(1)

  defName = $file_name ".rsi"

  opt["title"] = "Save Rangeset Info - Select File"
  opt["path"] = $file_path
  opt["default_filename"] = defName
  opt["filter"] = "*.rsi"
  opt["new"] = 1
  fileName = filename_dialog(opt)

  if (fileName != "")
  {
    write_file(out, fileName)
  }
}

# ----------------------------------------------------------------------
# RANGESET_write(includeBlockFlag)
# create a rangeset-info string holding info of all rangesets of
# current window.
# Parameters:
# $1 = "include <rangeset-info> block" flag (1=include; 0=exclude)
# returns: return "rangeset-info" string
# ----------------------------------------------------------------------

define RANGESET_write {

  if ($1 == 1)
    out = "<rangeset-info>\n"
  else
    out = "\n"

  for (i=0; i < $NBR_OF_RANGESETS; i++)
  {
    out = out "  <rangeset>\n"

    name = $RANGESET[i, "name"]
    out = out "    <name>" name "</name>\n"
    out = out "    <color>" $RANGESET[i, "color"] "</color>\n"

    rId = RANGESET_get_id(name)

    rInfo = rangeset_info(rId)
    nbrRanges = rInfo["count"]

    for (j=1; j <= nbrRanges; j++)
    {
      range = rangeset_range(rId, j)
      out = out "    <range>\n"
      out = out "      <start>" range["start"] "</start>\n"
      out = out "      <end>"   range["end"]   "</end>\n"
      out = out "    </range>\n"
    }
    out = out "  </rangeset>\n"
  }

 if ($1 == 1)
   out = out "</rangeset-info>\n"

  return out
}

# ----------------------------------------------------------------------
# RANGESET_read_inline():
# read rangeset info out of rangeset-info block, which is stored
# within current window. Apply read rangeset info to current window.
# returns: ---
# ----------------------------------------------------------------------

define RANGESET_read_inline {
  RANGESET_read(get_range(0, $text_length))
}

# ----------------------------------------------------------------------
# RANGESET_read_from_file():
# read rangeset info from a file (file is selected by dialog).
# Apply read rangeset info to current window.
# returns: ---
# ----------------------------------------------------------------------

define RANGESET_read_from_file {
  defName = $file_name ".rsi"

  opt["title"] = "Load Rangeset Info - Select File"
  opt["path"] = $file_path
  opt["default_filename"] = defName
  opt["filter"] = "*.rsi"
  fileName = filename_dialog(opt)

  if (fileName != "")
  {
    input = read_file(fileName)

    RANGESET_read(input)
  }
}

# ----------------------------------------------------------------------
# RANGESET_read(stringBuffer):
# read rangeset info from given string buffer and apply it to
# current window.
# Parameters:
# $1 = string buffer to be scanned for rangeset info
# returns: --
# ----------------------------------------------------------------------

define RANGESET_read {
  block = RANGESET_read_block($1, "rangeset-info", 0)

  if (block["eobPos"] == -1)
    return

  pos = 0
  rangesetInfo = block["content"]

  while (pos != -1)
  {
    pos = RANGESET_read_set(rangesetInfo, pos)
  }

}

# ----------------------------------------------------------------------
# RANGESET_read_set(stringBuffer, startPos):
# read one rangeset from given stringBuffer starting at given start pos.
# Apply read rangeset to current window.
# Parameters:
# $1 = string buffer to be scanned for next rangeset
# $2 = pos. to start scan
# returns: next pos. to continue scan with
# ----------------------------------------------------------------------

define RANGESET_read_set {

  # -- scan for <rangeset> block
  block = RANGESET_read_block($1, "rangeset", $2)

  if (block["eobPos"] == -1)
    return -1

  rangeset   = block["content"]
  endOfBlock = block["eobPos"]

  # -- scan for name of rangeset
  block = RANGESET_read_block(rangeset, "name", 0)
  if (block["eobPos"] == -1)
    return -1

  name = block["content"]

  # -- scan for color of rangeset
  block = RANGESET_read_block(rangeset, "color", block["eobPos"])
  if (block["eobPos"] == -1)
    return -1

  color = block["content"]

  # -- check, if new rangeset needs to be allocated
  if (RANGESET_exists(name) == 0)
  {
    RANGESET_allocate_new(name, color)
  }
  else
  {
    RANGESET_set_color(name, color)
  }

  # -- scan for range blocks
  pos = block["eobPos"]
  rId = RANGESET_get_id(name)

  while (pos != -1)
  {
    block = RANGESET_read_block(rangeset, "range", pos)
    if (block["eobPos"] == -1)
      return endOfBlock

    range = block["content"]
    pos   = block["eobPos"]

    # -- read start pos of range
    block = RANGESET_read_block(range, "start", 0)
    if (block["eobPos"] == -1)
      return endOfBlock

    startPos = block["content"]

    # -- read end pos of range
    block = RANGESET_read_block(range, "end", block["eobPos"])
    if (block["eobPos"] == -1)
      return endOfBlock

    endPos = block["content"]

    # -- add range
    rangeset_add(rId, startPos, endPos)
  }

  return endOfBlock
}


# ----------------------------------------------------------------------
# RANGESET_read_block(stringBuffer, blockTag, startPos):
# read a XML style block identified by blockTag from given stringBuffer
# starting at given start pos.
# Parameters:
# $1 = string buffer to be scanned for next block
# $2 = block tag
# $3 = pos. to start scan
# returns: array holding following elements:
# - "eobPos"       = end of block pos. (= -1 in case of failure)
# - "startContPos" = start pos. of block (= -1 in case of failure)
# - "content"      = content of block
# ----------------------------------------------------------------------

define RANGESET_read_block {
  blockStartTag = "<" $2 ">"
  blockEndTag   = "</" $2 ">"

  block["eobPos"] = -1

  # -- scan for start of block
  pos = search_string($1, blockStartTag, $3)

  if (pos == -1)
    return block

  pos = pos + length(blockStartTag)

  endPos = search_string($1, blockEndTag, pos)
  if (endPos == -1)
    return block

  block["content"]      = substring($1, pos, endPos)
  block["startContPos"] = pos
  block["eobPos"]       = endPos + length(blockEndTag)

  return block
}

# ----------------------------------------------------------------------
# RANGESET_get_line_info(stringBuffer):
# determines start and end positions of lines within given
# "stringBuffer".
# Parameters:
# $1 = string buffer
# returns: array holding per line "start" / "end" (inclusive) positions

define RANGESET_get_line_info {

  # -- determine start / end pos of lines in a string buffer
  lineEndPos = 0
  n          = 1
  pos        = 0
  while (lineEndPos != -1)
  {
    lineEndPos = search_string($1, "\n", pos)

    if (lineEndPos == -1)
    {
      if (pos < length($1))
      {
        linePosInfo[n,"start"] = pos+1
        linePosInfo[n,"end"  ] = length( $1 ) - 1
        # t_print( n ": start = " linePosInfo[n,"start"] "; end = " linePosInfo[n,"end"] "\n")
      }
    }
    else
    {
      linePosInfo[n,"start"] = pos
      linePosInfo[n,"end"  ] = lineEndPos

      # t_print( n ": start = " linePosInfo[n,"start"] "; end = " linePosInfo[n,"end"] "\n")
      n ++
      pos = lineEndPos + 1
    }
  }

  return linePosInfo
}




# ----------------------------------------------------------------------
# RANGESET_find_all_selected():
# find all occurrences of a string and add them to a rangeset.  Do not
# use dialog and use default color ($color_name).  If the "new_color"
# option is used change the default color. Default colors rotate
# (yelow,green,blue)
# ----------------------------------------------------------------------

define RANGESET_find_all_selected {

  findString = get_selection()


# type = "case"
  type = "literal"

  if (findString == "")
  {
    RANGESET_remove_current("set_color")
    return
  }
  else
    RANGESET_remove_current("")

  rangeset_subtract(RANGESET_get_id($color_name), 0, $text_length)

  id = RANGESET_get_id($color_name)
  $last_selected = id
  #t_print( "id = " id "; $color_name = " $color_name "\n")



  pos = 0
  for (pos = search(get_selection(), 0, type); pos != -1; pos = search(get_selection(), pos, type))
  {
    rangeset_add(id, pos, $search_end)
    pos = $search_end
  }
  if ($1=="next")
    RANGESET_goto_next()
  if ($1=="prev")
    RANGESET_goto_prev()

  if ($1=="new_color")
  {
    RANGESET_change_color()
  }
}

# ----------------------------------------------------------------------
# RANGESET_go_to_next():
# Goto start of a range which is part of a rangeset and which is
# located next to current cursor position in forward direction.
# The color of the last range that was selected is used, if current 
# cursor position is not located inside a rangeset.
# ----------------------------------------------------------------------

define RANGESET_go_to_next {
  startPos = $cursor
  idx = 0

  for (i=0; i < $NBR_OF_RANGESETS && idx == 0; i++)
  {
    rId = RANGESET_get_id($RANGESET[i, "name"])
    idx = rangeset_includes(rId, startPos)
    if (idx == 0)  # if cursor is at end of a range list then accept it
      idx = rangeset_includes(rId, startPos-1)
    #t_print( "idx = " idx "; rId = " rId "\n")
  }

  if (idx == 0)
  {
    # -- cursor not located within any rangeset:
    # -- ask user for rangeset to goto
    #name = RANGESET_ask_for_name("Goto next range:\n\nChoose rangeset", "")
    #if (name == "")
    #  return

    #rId = RANGESET_get_id( name )
    #rId = 58

    rId = $last_selected
    #t_print( "rId = " rId  "\n")


    nextPos = RANGESET_get_next_pos(rId, startPos, 1)
  }
  else
  {
    $last_selected = rId
    nextPos = RANGESET_get_next_pos(rId, rangeset_range( rId, idx )["end"] + 1, idx+1)
  }

  if (nextPos == -1 || startPos == nextPos)
    beep()
  else
  {
    if (startPos > nextPos)
      beep()

    set_cursor_pos (nextPos)
    RANGESET_centerline()
  }
}

# ----------------------------------------------------------------------
# RANGESET_remove_current():
# Remove the rangeset that is selected by the cursor
# ----------------------------------------------------------------------
define RANGESET_remove_current {
  startPos = $cursor
  idx = 0

  for (i=0; i < $NBR_OF_RANGESETS && idx == 0; i++)
  {
    rId = RANGESET_get_id($RANGESET[i, "name"])
    color = $RANGESET[i, "name"]
    idx = rangeset_includes(rId, startPos)
    if (idx == 0)  # if cursor is at end of a range list then accept it
      idx = rangeset_includes(rId, startPos-1)
    # t_print( "i = " i ";idx = " idx "; rId = " rId  "; color = " color "\n")
  }

  if (idx != 0)
  {
    #t_print( "removing color " color "\n")
    rangeset_subtract(RANGESET_get_id(color), 0, $text_length)
    if ($1=="set_color")
      $color_name = color
  }
}

# ----------------------------------------------------------------------
# RANGESET_remove():
# Remove the all rangesets
# ----------------------------------------------------------------------
define RANGESET_remove {

  for (i=0; i < $NBR_OF_RANGESETS; i++)
  {
    rId = RANGESET_get_id($RANGESET[i, "name"])

    color = $color_name
    rangeset_subtract(RANGESET_get_id(color), 0, $text_length)
    RANGESET_change_color()
  }
}

# ----------------------------------------------------------------------
# RANGESET_change_color():
# 
# ----------------------------------------------------------------------
define RANGESET_change_color {

     if ($color_name == "Yellow") 
       {
        $color_name = "Green"
       }
     else if ($color_name == "Green")
       {
        $color_name = "Blue"
       }
     else if ($color_name == "Blue")
       {
        $color_name = "Red"
       }
     else if ($color_name == "Red")
       {
        $color_name = "Yellow"
       }

}