#!/usr/bin/env ruby

require 'fox'
require 'fox/responder'
require 'fox/colors'

include Fox

class ScribbleWindow < FXMainWindow

  include Responder

  def initialize(app)
    # Call base class initializer first
    super(app, "Scribble Application", nil, nil, DECOR_ALL,
      0, 0, 800, 600)

    # Message identifiers for this class
    identifier :ID_CANVAS, :ID_CLEAR

    # And here's the message map for this class
    FXMAPFUNC(SEL_PAINT,             ID_CANVAS, :onPaint)
    FXMAPFUNC(SEL_LEFTBUTTONPRESS,   ID_CANVAS, :onMouseDown)
    FXMAPFUNC(SEL_LEFTBUTTONRELEASE, ID_CANVAS, :onMouseUp)
    FXMAPFUNC(SEL_MOTION,            ID_CANVAS, :onMouseMove)
    FXMAPFUNC(SEL_COMMAND,           ID_CLEAR,  :onCmdClear)
    FXMAPFUNC(SEL_UPDATE,            ID_CLEAR,  :onUpdClear)

    # Construct a horizontal frame to hold the main window's contents
    @contents = FXHorizontalFrame.new(self,
      LAYOUT_SIDE_TOP|LAYOUT_FILL_X|LAYOUT_FILL_Y, 0, 0, 0, 0, 0, 0, 0, 0)

    # Left pane contains the canvas
    @canvasFrame = FXVerticalFrame.new(@contents,
      FRAME_SUNKEN|LAYOUT_FILL_X|LAYOUT_FILL_Y|LAYOUT_TOP|LAYOUT_LEFT,
      0, 0, 0, 0, 10, 10, 10, 10)

    # Place a label above the canvas
    FXLabel.new(@canvasFrame, "Canvas Frame", nil,
      JUSTIFY_CENTER_X|LAYOUT_FILL_X)

    # Horizontal divider line
    FXHorizontalSeparator.new(@canvasFrame, SEPARATOR_GROOVE|LAYOUT_FILL_X)

    # Drawing canvas
    @canvas = FXCanvas.new(@canvasFrame, self, ID_CANVAS,
      LAYOUT_FILL_X|LAYOUT_FILL_Y|LAYOUT_TOP|LAYOUT_LEFT)

    # Right pane for the buttons
    @buttonFrame = FXVerticalFrame.new(@contents,
      FRAME_SUNKEN|LAYOUT_FILL_Y|LAYOUT_TOP|LAYOUT_LEFT,
      0, 0, 0, 0, 10, 10, 10, 10)

    # Label above the buttons
    FXLabel.new(@buttonFrame, "Button Frame", nil,
      JUSTIFY_CENTER_X|LAYOUT_FILL_X)

    # Horizontal divider line
    FXHorizontalSeparator.new(@buttonFrame,
      SEPARATOR_RIDGE|LAYOUT_FILL_X)

    # Button to clear the canvas
    FXButton.new(@buttonFrame, "&Clear", nil, self, ID_CLEAR,
      FRAME_THICK|FRAME_RAISED|LAYOUT_FILL_X|LAYOUT_TOP|LAYOUT_LEFT,
      0, 0, 0, 0, 10, 10, 5, 5)

    # Exit button
    FXButton.new(@buttonFrame, "&Exit", nil, app, FXApp::ID_QUIT,
      FRAME_THICK|FRAME_RAISED|LAYOUT_FILL_X|LAYOUT_TOP|LAYOUT_LEFT,
      0, 0, 0, 0, 10, 10, 5, 5)

    # Initialize other member variables
    @drawColor = FXColor::Red
    @mouseDown = false
    @dirty = false
  end

  # Create and show the main window
  def create
    super                  # Create the windows
    show(PLACEMENT_SCREEN) # Make the main window appear
  end

  # Mouse button was pressed somewhere
  def onMouseDown(sender, sel, ptr)
    @canvas.grab
    @mouseDown = true
    return 1
  end

  # Mouse has moved, so draw a line
  def onMouseMove(sender, sel, event)
    if @mouseDown
      # Get device context for the canvas
      dc = FXDCWindow.new(@canvas)

      # Set the foreground color for drawing
      dc.setForeground(@drawColor)

      # Draw a line from the previous mouse coordinates to the current ones
      dc.drawLine(event.last_x, event.last_y, event.win_x, event.win_y)

      # We have drawn something, so now the canvas is dirty
      @dirty = true

      # Release the DC immediately
      dc.end
    end
    return 1
  end

  # Mouse button released
  def onMouseUp(sender, sel, event)
    @canvas.ungrab
    if @mouseDown
      # Get device context for the canvas
      dc = FXDCWindow.new(@canvas)

      # Set the foreground color for drawing
      dc.setForeground(@drawColor)

      # Draw a line from the previous mouse coordinates to the current ones
      dc.drawLine(event.last_x, event.last_y, event.win_x, event.win_y)

      # We have drawn something, so now the canvas is dirty
      @dirty = true

      # Mouse no longer down
      @mouseDown = false

      # Release the DC immediately
      dc.end
    end
    return 1
  end

  # Paint the canvas
  def onPaint(sender, sel, event)
    dc = FXDCWindow.new(@canvas, event)
    dc.setForeground(@canvas.backColor)
    dc.fillRectangle(event.rect.x, event.rect.y, event.rect.w, event.rect.h)
    dc.end
  end

  # Handle the clear message
  def onCmdClear(sender, sel, ptr)
    dc = FXDCWindow.new(@canvas)
    dc.setForeground(@canvas.backColor)
    dc.fillRectangle(0, 0, @canvas.width, @canvas.height)
    @dirty = false
    dc.end
    return 1
  end

  # This function handles the update message sent by the Clear button
  # to its target. Every widget in FOX receives a message (SEL_UPDATE)
  # during idle processing, asking it to update itself. For example,
  # buttons could be enabled or disabled as the state of the application
  # changes.
  #
  # In this case, we'll disable the sender (the Clear button) when the
  # canvas has already been cleared (i.e. it's "clean"), and enable it when
  # it has been painted (i.e. it's "dirty").
  #
  def onUpdClear(sender, sel, ptr)
    message = @dirty ? FXWindow::ID_ENABLE : FXWindow::ID_DISABLE
    sender.handle(self, MKUINT(message, SEL_COMMAND), nil)
    return 1
  end
end

def run
  # Construct the application object
  application = FXApp.new('Scribble', 'FoxTest')
  
  # Construct the main window
  scribble = ScribbleWindow.new(application)
  
  # Create the application
  application.create
  
  # Run the application
  application.run
end

run
