sourcepetanque::Editor.fan

//
// Copyright (c) 2012, Brian Frank
// Licensed under the Academic Free License version 3.0
//
// History:
//   22 Apr 12  Brian Frank  Creation
//

using gfx
using fwt
using syntax

**
** Editor is the canvas used to display and edit syntax
** color coded text.
**
class Editor : Panel
{

//////////////////////////////////////////////////////////////////////////
// Construction
//////////////////////////////////////////////////////////////////////////

  ** It-block constructor
  new make(|This|? f := null)
  {
    if (f != null) f(this)
    this.doubleBuffered = true
    this.viewport = Viewport(this)
    this.controller = Controller(this)
    this.doc = Doc(this)
    this.cursor = Cursor.text
    this.lineh = options.font.height
    this.colw  = options.font.width("w")
  }

//////////////////////////////////////////////////////////////////////////
// Fields
//////////////////////////////////////////////////////////////////////////

  ** Options defines alll the colors, styling, key bindings
  const EditorOptions options := EditorOptions()

  ** Syntax rules to use for color coding
  const SyntaxRules rules := SyntaxRules {}

  ** Is editor read only
  const Bool ro := false

  ** Callback when the text is modified.
  once EventListeners onModify() { EventListeners() }

  ** Callback when the caret position is modified.
  once EventListeners onCaret() { EventListeners() }

//////////////////////////////////////////////////////////////////////////
// I/O
//////////////////////////////////////////////////////////////////////////

  ** Load from lines already parsed into memory
  Void loadLines(Str[] lines) { doc.load(lines) }

  ** Load the document from the given input stream
  Void load(InStream in) { loadLines(in.readAllLines) }

  ** Save the document to the given output stream
  Void save(OutStream out) { doc.save(out) }

  ** Remove text between span and/or insert new given text
  ** at that position.  Return new position of end of inserted text.
  Void modify(Span span, Str newText) { controller.modify(span, newText) }

  ** Get text for the given span
  Str textForSpan(Span span) { doc.textRange(span) }

//////////////////////////////////////////////////////////////////////////
// Document
//////////////////////////////////////////////////////////////////////////

  ** Number of columns
  override Int colCount() { doc.colCount }

  ** Number of lines
  override Int lineCount() { doc.lineCount }

  ** Height of each line
  override const Int lineh

  ** Width of each column
  override const Int colw

  ** Get line string for given zero based line number
  Str line(Int lineNum) { doc.line(lineNum) }

  ** List of spans to highligh in the document
  Span[] highlights := Span[,]
  {
    set { &highlights = it; repaint }
  }

  ** Position of document end
  Pos docEndPos() { doc.endPos }

//////////////////////////////////////////////////////////////////////////
// Navigation
//////////////////////////////////////////////////////////////////////////

  ** Get current caret position
  Pos caret() { viewport.caret }

  ** Move caret and scroll to the given position
  ** and ensure editor is focused
  Void goto(Pos pos)
  {
    viewport.goto(pos)
    focus
  }

  ** Current selection or null for no selection
  Span? selection
  {
    get { viewport.getSelection }
    set { viewport.setSelection(it) }
  }

  ** Find the specified string in the document starting the
  ** search at the document offset and looking forward.
  ** Return null is not found.  Note we don't currently
  ** support searching across multiple lines.
  Pos? findNext(Str s, Pos? last, Bool matchCase) { doc.findNext(s, last, matchCase) }

//////////////////////////////////////////////////////////////////////////
// Eventing
//////////////////////////////////////////////////////////////////////////

  ** Trap handling of an input event, call
  ** consume if handled by a subclass
  @NoDoc virtual Void trapEvent(Event event) {}

  override Void onPaintBackground(Graphics g) { viewport.onPaintBackground(g) }

  override Void onPaintLines(Graphics g, Range lines) { viewport.onPaintLines(g, lines) }

//////////////////////////////////////////////////////////////////////////
// Fields
//////////////////////////////////////////////////////////////////////////

  internal Doc doc
  @NoDoc Viewport viewport
  @NoDoc Controller controller

}