require 'fox16'

include Fox

$bitmap_bits = [
   0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0x03, 0x00, 0x00, 0x00,
   0x00, 0x00, 0x00, 0xc0, 0x05, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xa0,
   0x09, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x90, 0x11, 0x00, 0x00, 0x00,
   0x00, 0x00, 0x00, 0x88, 0x21, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x84,
   0x41, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x82, 0x81, 0x00, 0x00, 0x00,
   0x00, 0x00, 0x00, 0x81, 0x01, 0x01, 0x00, 0x00, 0x00, 0x00, 0x80, 0x80,
   0x01, 0x02, 0x00, 0x00, 0x00, 0x00, 0x40, 0x80, 0x01, 0x04, 0x00, 0x00,
   0x00, 0x00, 0x20, 0x80, 0x01, 0x08, 0x00, 0x00, 0x00, 0x00, 0x10, 0x80,
   0x01, 0x10, 0x00, 0x00, 0x00, 0x00, 0x08, 0x80, 0x01, 0x20, 0x00, 0x00,
   0x00, 0x00, 0x04, 0x80, 0x01, 0x40, 0x00, 0x00, 0x00, 0x00, 0x02, 0x80,
   0x01, 0x80, 0x00, 0x00, 0x00, 0x00, 0x01, 0x80, 0x01, 0x00, 0x01, 0x00,
   0x00, 0x80, 0x00, 0x80, 0x01, 0x00, 0x02, 0x00, 0x00, 0x40, 0x00, 0x80,
   0x01, 0x00, 0x04, 0x00, 0x00, 0x20, 0x00, 0x80, 0x01, 0x00, 0x08, 0x00,
   0x00, 0x10, 0x00, 0x80, 0x01, 0x00, 0x10, 0x00, 0x00, 0x08, 0x00, 0x80,
   0x01, 0x00, 0x20, 0x00, 0x00, 0x04, 0x00, 0x80, 0x01, 0x00, 0x40, 0x00,
   0x00, 0x02, 0x00, 0x80, 0x01, 0x00, 0x80, 0x00, 0x00, 0x01, 0x00, 0x80,
   0x01, 0x00, 0x00, 0x01, 0x80, 0x00, 0x00, 0x80, 0x01, 0x00, 0x00, 0x02,
   0x40, 0x00, 0x00, 0x80, 0x01, 0x00, 0x00, 0x04, 0x20, 0x00, 0x00, 0x80,
   0x01, 0x00, 0x00, 0x08, 0x10, 0x00, 0x00, 0x80, 0x01, 0x00, 0x00, 0x10,
   0x08, 0x00, 0x00, 0x80, 0x01, 0x00, 0x00, 0x20, 0x04, 0x00, 0x00, 0x80,
   0x01, 0x00, 0x00, 0x40, 0x02, 0x00, 0x00, 0x80, 0x01, 0x00, 0x00, 0x80,
   0x01, 0x00, 0x00, 0x80, 0x01, 0x00, 0x00, 0x80, 0x01, 0x00, 0x00, 0x80,
   0x01, 0x00, 0x00, 0x40, 0x02, 0x00, 0x00, 0x80, 0x01, 0x00, 0x00, 0x20,
   0x04, 0x00, 0x00, 0x80, 0x01, 0x00, 0x00, 0x10, 0x08, 0x00, 0x00, 0x80,
   0x01, 0x00, 0x00, 0x08, 0x10, 0x00, 0x00, 0x80, 0x01, 0x00, 0x00, 0x04,
   0x20, 0x00, 0x00, 0x80, 0x01, 0x00, 0x00, 0x02, 0x40, 0x00, 0x00, 0x80,
   0x01, 0x00, 0x00, 0x01, 0x80, 0x00, 0x00, 0x80, 0x01, 0x00, 0x80, 0x00,
   0x00, 0x01, 0x00, 0x80, 0x01, 0x00, 0x40, 0x00, 0x00, 0x02, 0x00, 0x80,
   0x01, 0x00, 0x20, 0x00, 0x00, 0x04, 0x00, 0x80, 0x01, 0x00, 0x10, 0x00,
   0x00, 0x08, 0x00, 0x80, 0x01, 0x00, 0x08, 0x00, 0x00, 0x10, 0x00, 0x80,
   0x01, 0x00, 0x04, 0x00, 0x00, 0x20, 0x00, 0x80, 0x01, 0x00, 0x02, 0x00,
   0x00, 0x40, 0x00, 0x80, 0x01, 0x00, 0x01, 0x00, 0x00, 0x80, 0x00, 0x80,
   0xff, 0xff, 0x00, 0x00, 0x00, 0x00, 0x01, 0x80, 0xff, 0xff, 0x00, 0x00,
   0x00, 0x00, 0x02, 0x80, 0xff, 0xff, 0x00, 0x00, 0x00, 0x00, 0x04, 0x80,
   0xff, 0xff, 0x00, 0x00, 0x00, 0x00, 0x08, 0x80, 0xff, 0xff, 0x00, 0x00,
   0x00, 0x00, 0x10, 0x80, 0xff, 0xff, 0x00, 0x00, 0x00, 0x00, 0x20, 0x80,
   0xff, 0xff, 0x00, 0x00, 0x00, 0x00, 0x40, 0x80, 0xff, 0xff, 0x00, 0x00,
   0x00, 0x00, 0x80, 0x80, 0xff, 0xff, 0x00, 0x00, 0x00, 0x00, 0x00, 0x81,
   0xff, 0xff, 0x00, 0x00, 0x00, 0x00, 0x00, 0x82, 0xff, 0xff, 0x00, 0x00,
   0x00, 0x00, 0x00, 0x84, 0xff, 0xff, 0x00, 0x00, 0x00, 0x00, 0x00, 0x88,
   0xff, 0xff, 0x00, 0x00, 0x00, 0x00, 0x00, 0x90, 0xff, 0xff, 0x00, 0x00,
   0x00, 0x00, 0x00, 0xa0, 0xff, 0xff, 0x00, 0x00, 0x00, 0x00, 0x00, 0xc0,
   0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff]
   
$bitmap_bits = $bitmap_bits.pack("c*")
   
$blit_modes = {
  BLT_CLR => "Clear\tBLT_CLR",
  BLT_SRC_AND_DST => "And\tBLT_SRC_AND_DST",
  BLT_SRC_AND_NOT_DST => "AndRev\tBLT_SRC_AND_NOT_DST",
  BLT_SRC => "Copy\tBLT_SRC",
  BLT_NOT_SRC_AND_DST => "AndInv\tBLT_NOT_SRC_AND_DST",
  BLT_DST=> "NoOp\tBLT_DST",
  BLT_SRC_XOR_DST => "Xor\tBLT_SRC_XOR_DST",
  BLT_SRC_OR_DST => "Or\tBLT_SRC_OR_DST",
  BLT_NOT_SRC_AND_NOT_DST => "Nor\tBLT_NOT_SRC_AND_NOT_DST",
  BLT_NOT_SRC_XOR_DST => "Equiv\tBLT_NOT_SRC_XOR_DST",
  BLT_NOT_DST => "Invert\tBLT_NOT_DST",
  BLT_SRC_OR_NOT_DST => "OrRev\tBLT_SRC_OR_NOT_DST",
  BLT_NOT_SRC => "CopyInv\tBLT_NOT_SRC",
  BLT_NOT_SRC_OR_DST => "OrInv\tBLT_NOT_SRC_OR_DST",
  BLT_NOT_SRC_OR_NOT_DST => "Nand\tBLT_NOT_SRC_OR_NOT_DST",
  BLT_SET => "Set\tBLT_SET"
}

$fill_styles = {
  FILL_SOLID => "FILL_SOLID",
  FILL_TILED => "FILL_TILED",
  FILL_STIPPLED => "FILL_STIPPLED",
  FILL_OPAQUESTIPPLED => "FILL_OPAQUESTIPPLED"
}

$stipples = {
  STIPPLE_NONE => "NONE\tSTIPPLE_NONE",
  STIPPLE_BLACK => "BLACK\tSTIPPLE_BLACK",
  STIPPLE_WHITE => "WHITE\tSTIPPLE_WHITE",
  STIPPLE_0 => "0\tSTIPPLE_0",
  STIPPLE_1 => "1\tSTIPPLE_1",
  STIPPLE_2 => "2\tSTIPPLE_2",
  STIPPLE_3 => "3\tSTIPPLE_3",
  STIPPLE_4 => "4\tSTIPPLE_4",
  STIPPLE_5 => "5\tSTIPPLE_5",
  STIPPLE_6 => "6\tSTIPPLE_6",
  STIPPLE_7 => "7\tSTIPPLE_7",
  STIPPLE_8 => "8\tSTIPPLE_8",
  STIPPLE_9 => "9\tSTIPPLE_9",
  STIPPLE_10 => "10\tSTIPPLE_10",
  STIPPLE_11 => "11\tSTIPPLE_11",
  STIPPLE_12 => "12\tSTIPPLE_12",
  STIPPLE_13 => "13\tSTIPPLE_13",
  STIPPLE_14 => "14\tSTIPPLE_14",
  STIPPLE_15 => "15\tSTIPPLE_15",
  STIPPLE_16 => "16\tSTIPPLE_16",
  STIPPLE_HORZ => "HORZ\tSTIPPLE_HORZ",
  STIPPLE_VERT => "VERT\tSTIPPLE_VERT",
  STIPPLE_CROSS => "CROSS\tSTIPPLE_CROSS",
  STIPPLE_DIAG => "DIAG\tSTIPPLE_DIAG",
  STIPPLE_REVDIAG => "REVDIAG\tSTIPPLE_REVDIAG",
  STIPPLE_CROSSDIAG => "CROSSDIAG\tSTIPPLE_CROSSDIAG",
}

class DCTestWindow < FXMainWindow
  def initialize(app)
    # Initialize base class first
    super(app, "Device Context Test", :opts => DECOR_ALL, :width => 850, :height => 740)
        
    opts = FRAME_SUNKEN|FRAME_THICK|LAYOUT_FILL_X|LAYOUT_FILL_Y
        
    # Preferred line attributes
    @lineStyle = LINE_SOLID
    @capStyle = CAP_BUTT
    @joinStyle = JOIN_MITER
        
    # Create a tooltip
    tooltip = FXToolTip.new(getApp())

    # Menu bar
    menubar = FXMenuBar.new(self, LAYOUT_SIDE_TOP|LAYOUT_FILL_X)

    # Separator
    FXHorizontalSeparator.new(self, LAYOUT_SIDE_TOP|LAYOUT_FILL_X|SEPARATOR_GROOVE)

    # Contents
    contents = FXHorizontalFrame.new(self, LAYOUT_SIDE_TOP|FRAME_NONE|LAYOUT_FILL_X|LAYOUT_FILL_Y|PACK_UNIFORM_WIDTH)

    # Controls on right
    controls = FXVerticalFrame.new(contents, LAYOUT_RIGHT|LAYOUT_FILL_Y)

    # Block for BLIT modes
    FXLabel.new(controls, "BLIT Function:", nil, LAYOUT_LEFT)
    blitgrid = FXMatrix.new(controls, 4, FRAME_RIDGE|MATRIX_BY_COLUMNS|LAYOUT_FILL_X|PACK_UNIFORM_WIDTH,
      :padLeft => 2, :padRight => 2, :padTop => 2, :padBottom => 2)
	
    # One button for each mode
    $blit_modes.each do |blit_mode, desc|
      btn = FXButton.new(blitgrid, desc, :opts => BUTTON_TOOLBAR|FRAME_RAISED|LAYOUT_FILL_X|LAYOUT_FILL_Y|LAYOUT_FILL_COLUMN)
      btn.userData = blit_mode
      btn.connect(SEL_COMMAND) do |sender, sel, ptr|
        @function = sender.userData
        @linesCanvas.update(0, 0, @linesCanvas.width, @linesCanvas.height)
      end
      btn.connect(SEL_UPDATE) do |sender, sel, ptr|
        if sender.userData == @function
          sender.handle(self, MKUINT(ID_CHECK, SEL_COMMAND), nil)
        else
          sender.handle(self, MKUINT(ID_UNCHECK, SEL_COMMAND), nil)
        end
      end
    end
	
    # Line dash style
    FXLabel.new(controls, "Line Style:", nil, LAYOUT_LEFT)
    linestyle = FXMatrix.new(controls, 3, FRAME_RIDGE|MATRIX_BY_COLUMNS|LAYOUT_FILL_X,
      :padLeft => 2, :padRight => 2, :padTop => 2, :padBottom => 2)
    lineSolidBtn = FXButton.new(linestyle, "\tLINE_SOLID", loadIcon("solid_line.png", 0, IMAGE_OPAQUE), :opts => BUTTON_TOOLBAR|JUSTIFY_CENTER_X|FRAME_RAISED|LAYOUT_FILL_X|LAYOUT_FILL_Y|LAYOUT_FILL_COLUMN)
    lineSolidBtn.connect(SEL_COMMAND) do |sender, sel, ptr|
      @lineStyle = LINE_SOLID
      @linesCanvas.update(0, 0, @linesCanvas.width, @linesCanvas.height)
    end
    lineSolidBtn.connect(SEL_UPDATE) do |sender, sel, ptr|
      if @lineStyle == LINE_SOLID
        sender.handle(self, MKUINT(ID_CHECK, SEL_COMMAND), nil)
      else
        sender.handle(self, MKUINT(ID_UNCHECK, SEL_COMMAND), nil)
      end
    end
    lineOnOffDashBtn = FXButton.new(linestyle, "\tLINE_ONOFF_DASH", loadIcon("onoff_dash.png"), nil, 0, BUTTON_TOOLBAR|JUSTIFY_CENTER_X|FRAME_RAISED|LAYOUT_FILL_X|LAYOUT_FILL_Y|LAYOUT_FILL_COLUMN)
    lineOnOffDashBtn.connect(SEL_COMMAND) do |sender, sel, ptr|
      @lineStyle = LINE_ONOFF_DASH
      @linesCanvas.update(0, 0, @linesCanvas.width, @linesCanvas.height)
    end
    lineOnOffDashBtn.connect(SEL_UPDATE) do |sender, sel, ptr|
      if @lineStyle == LINE_ONOFF_DASH
        sender.handle(self, MKUINT(ID_CHECK, SEL_COMMAND), nil)
      else
        sender.handle(self, MKUINT(ID_UNCHECK, SEL_COMMAND), nil)
      end
    end
    lineDoubleDashBtn = FXButton.new(linestyle, "\tLINE_DOUBLE_DASH", loadIcon("double_dash.png", 0,  IMAGE_OPAQUE), nil, 0, BUTTON_TOOLBAR|JUSTIFY_CENTER_X|FRAME_RAISED|LAYOUT_FILL_X|LAYOUT_FILL_Y|LAYOUT_FILL_COLUMN)
    lineDoubleDashBtn.connect(SEL_COMMAND) do |sender, sel, ptr|
      @lineStyle = LINE_DOUBLE_DASH
      @linesCanvas.update(0, 0, @linesCanvas.width, @linesCanvas.height)
    end
    lineDoubleDashBtn.connect(SEL_UPDATE) do |sender, sel, ptr|
      if @lineStyle == LINE_DOUBLE_DASH
        sender.handle(self, MKUINT(ID_CHECK, SEL_COMMAND), nil)
      else
        sender.handle(self, MKUINT(ID_UNCHECK, SEL_COMMAND), nil)
      end
    end
    
    # Line cap style
    FXLabel.new(controls, "Cap Style:", nil, LAYOUT_LEFT)
    capstyle = FXMatrix.new(controls, 4, FRAME_RIDGE|MATRIX_BY_COLUMNS|LAYOUT_FILL_X)
    capstyle.padLeft = 2
    capstyle.padRight = 2
    capstyle.padTop = 2
    capstyle.padBottom = 2
    btn = FXButton.new(capstyle, "\tCAP_NOT_LAST", loadIcon("capnotlast.png"), nil, 0, BUTTON_TOOLBAR|JUSTIFY_CENTER_X|FRAME_RAISED|LAYOUT_FILL_X|LAYOUT_FILL_Y|LAYOUT_FILL_COLUMN)
    btn.connect(SEL_COMMAND) do |sender, sel, ptr|
      @capStyle = CAP_NOT_LAST
      @linesCanvas.update(0, 0, @linesCanvas.width, @linesCanvas.height)
    end
    btn.connect(SEL_UPDATE) do |sender, sel, ptr|
      if @capStyle == CAP_NOT_LAST
        sender.handle(self, MKUINT(ID_CHECK, SEL_COMMAND), nil)
      else
        sender.handle(self, MKUINT(ID_UNCHECK, SEL_COMMAND), nil)
      end
    end
    btn = FXButton.new(capstyle, "\tCAP_BUTT", loadIcon("capbutt.png"), nil, 0, BUTTON_TOOLBAR|JUSTIFY_CENTER_X|FRAME_RAISED|LAYOUT_FILL_X|LAYOUT_FILL_Y|LAYOUT_FILL_COLUMN)
    btn.connect(SEL_COMMAND) do |sender, sel, ptr|
      @capStyle = CAP_BUTT
      @linesCanvas.update(0, 0, @linesCanvas.width, @linesCanvas.height)
    end
    btn.connect(SEL_UPDATE) do |sender, sel, ptr|
      if @capStyle == CAP_BUTT
        sender.handle(self, MKUINT(ID_CHECK, SEL_COMMAND), nil)
      else
        sender.handle(self, MKUINT(ID_UNCHECK, SEL_COMMAND), nil)
      end
    end
    btn = FXButton.new(capstyle, "\tCAP_ROUND", loadIcon("capround.png"), nil, 0, BUTTON_TOOLBAR|JUSTIFY_CENTER_X|FRAME_RAISED|LAYOUT_FILL_X|LAYOUT_FILL_Y|LAYOUT_FILL_COLUMN)
    btn.connect(SEL_COMMAND) do |sender, sel, ptr|
      @capStyle = CAP_ROUND
      @linesCanvas.update(0, 0, @linesCanvas.width, @linesCanvas.height)
    end
    btn.connect(SEL_UPDATE) do |sender, sel, ptr|
      if @capStyle == CAP_ROUND
        sender.handle(self, MKUINT(ID_CHECK, SEL_COMMAND), nil)
      else
        sender.handle(self, MKUINT(ID_UNCHECK, SEL_COMMAND), nil)
      end
    end
    btn = FXButton.new(capstyle, "\tCAP_PROJECTING", loadIcon("capproj.png"), nil, 0, BUTTON_TOOLBAR|JUSTIFY_CENTER_X|FRAME_RAISED|LAYOUT_FILL_X|LAYOUT_FILL_Y|LAYOUT_FILL_COLUMN)
    btn.connect(SEL_COMMAND) do |sender, sel, ptr|
      @capStyle = CAP_PROJECTING
      @linesCanvas.update(0, 0, @linesCanvas.width, @linesCanvas.height)
    end
    btn.connect(SEL_UPDATE) do |sender, sel, ptr|
      if @capStyle == CAP_PROJECTING
        sender.handle(self, MKUINT(ID_CHECK, SEL_COMMAND), nil)
      else
        sender.handle(self, MKUINT(ID_UNCHECK, SEL_COMMAND), nil)
      end
    end
  	
    # Line join style
    FXLabel.new(controls, "Join Style:", nil, LAYOUT_LEFT)
    joinstyle = FXMatrix.new(controls, 3, FRAME_RIDGE|MATRIX_BY_COLUMNS|LAYOUT_FILL_X)
    joinstyle.padLeft = 2
    joinstyle.padRight = 2
    joinstyle.padTop = 2
    joinstyle.padBottom = 2
    btn = FXButton.new(joinstyle, "\tJOIN_MITER", loadIcon("jmiter.png"), nil, 0, BUTTON_TOOLBAR|JUSTIFY_CENTER_X|FRAME_RAISED|LAYOUT_FILL_X|LAYOUT_FILL_Y|LAYOUT_FILL_COLUMN)
    btn.connect(SEL_COMMAND) do |sender, sel, ptr|
      @joinStyle = JOIN_MITER
      @linesCanvas.update(0, 0, @linesCanvas.width, @linesCanvas.height)
    end
    btn.connect(SEL_UPDATE) do |sender, sel, ptr|
      if @joinStyle == JOIN_MITER
        sender.handle(self, MKUINT(ID_CHECK, SEL_COMMAND), nil)
      else
        sender.handle(self, MKUINT(ID_UNCHECK, SEL_COMMAND), nil)
      end
    end
    btn = FXButton.new(joinstyle, "\tJOIN_ROUND", loadIcon("jround.png"), nil, 0, BUTTON_TOOLBAR|JUSTIFY_CENTER_X|FRAME_RAISED|LAYOUT_FILL_X|LAYOUT_FILL_Y|LAYOUT_FILL_COLUMN)
    btn.connect(SEL_COMMAND) do |sender, sel, ptr|
      @joinStyle = JOIN_ROUND
      @linesCanvas.update(0, 0, @linesCanvas.width, @linesCanvas.height)
    end
    btn.connect(SEL_UPDATE) do |sender, sel, ptr|
      if @joinStyle == JOIN_ROUND
        sender.handle(self, MKUINT(ID_CHECK, SEL_COMMAND), nil)
      else
        sender.handle(self, MKUINT(ID_UNCHECK, SEL_COMMAND), nil)
      end
    end
    btn = FXButton.new(joinstyle, "\tJOIN_BEVEL", loadIcon("jbevel.png"), nil, 0, BUTTON_TOOLBAR|JUSTIFY_CENTER_X|FRAME_RAISED|LAYOUT_FILL_X|LAYOUT_FILL_Y|LAYOUT_FILL_COLUMN)
    btn.connect(SEL_COMMAND) do |sender, sel, ptr|
      @joinStyle = JOIN_BEVEL
      @linesCanvas.update(0, 0, @linesCanvas.width, @linesCanvas.height)
    end
    btn.connect(SEL_UPDATE) do |sender, sel, ptr|
      if @joinStyle == JOIN_BEVEL
        sender.handle(self, MKUINT(ID_CHECK, SEL_COMMAND), nil)
      else
        sender.handle(self, MKUINT(ID_UNCHECK, SEL_COMMAND), nil)
      end
    end
  
    # Colors
    FXLabel.new(controls, "Colors:", nil, LAYOUT_LEFT)
    pairs = FXMatrix.new(controls, 2, FRAME_RIDGE|MATRIX_BY_COLUMNS|LAYOUT_FILL_X)
    pairs.padLeft = 2
    pairs.padRight = 2
    pairs.padTop = 2
    pairs.padBottom = 2
    pairs.hSpacing = 5
    pairs.vSpacing = 5
    
    # Back Color
    FXLabel.new(pairs, "Back Color:")
    @backWell = FXColorWell.new(pairs, FXRGB(0, 0, 255), nil, 0, FRAME_SUNKEN|FRAME_THICK|ICON_AFTER_TEXT|LAYOUT_FILL_X|LAYOUT_FILL_COLUMN)
    @backWell.connect(SEL_COMMAND) do |sender, sel, clr|
      @backcolor = clr
      @linesCanvas.update
      @shapesCanvas.update
      @imagesCanvas.update
    end
    @backWell.connect(SEL_CHANGED) do |sender, sel, clr|
      @backcolor = clr
      @linesCanvas.update
      @shapesCanvas.update
      @imagesCanvas.update
    end
    @backWell.connect(SEL_UPDATE) do |sender, sel, ptr|
      sender.handle(self, MKUINT(ID_SETVALUE, SEL_COMMAND), @backcolor)
    end
  
    # Fore Color
    FXLabel.new(pairs, "Fore Color:")
    @foreWell = FXColorWell.new(pairs, FXRGB(255, 0, 0), nil, 0, FRAME_SUNKEN|FRAME_THICK|ICON_AFTER_TEXT|LAYOUT_FILL_X|LAYOUT_FILL_COLUMN)
    @foreWell.connect(SEL_COMMAND) do |sender, sel, clr|
      @forecolor = clr
      @linesCanvas.update
      @shapesCanvas.update
      @imagesCanvas.update
    end
    @foreWell.connect(SEL_CHANGED) do |sender, sel, clr|
      @forecolor = clr
      @linesCanvas.update
      @shapesCanvas.update
      @imagesCanvas.update
    end
    @foreWell.connect(SEL_UPDATE) do |sender, sel, ptr|
      sender.handle(self, MKUINT(ID_SETVALUE, SEL_COMMAND), @forecolor)
    end
    
    # Erase Color
    FXLabel.new(pairs, "Erase Color:")
    @eraseWell = FXColorWell.new(pairs, FXRGB(255, 255, 255), nil, 0, FRAME_SUNKEN|FRAME_THICK|ICON_AFTER_TEXT|LAYOUT_FILL_X|LAYOUT_FILL_COLUMN)
    @eraseWell.connect(SEL_COMMAND) do |sender, sel, clr|
      @erasecolor = clr
      @linesCanvas.update
      @shapesCanvas.update
      @imagesCanvas.update
    end
    @eraseWell.connect(SEL_CHANGED) do |sender, sel, clr|
      @erasecolor = clr
      @linesCanvas.update
      @shapesCanvas.update
      @imagesCanvas.update
    end
    @eraseWell.connect(SEL_UPDATE) do |sender, sel, ptr|
      sender.handle(self, MKUINT(ID_SETVALUE, SEL_COMMAND), @erasecolor)
    end
    
    # Line width
    linew = FXMatrix.new(controls, 2, FRAME_RIDGE|MATRIX_BY_COLUMNS|LAYOUT_FILL_X)
    linew.padLeft = 2
    linew.padRight = 2
    linew.padTop = 2
    linew.padBottom = 2    
    FXLabel.new(linew, "Line Width:")
    @lineWidthSpinner = FXSpinner.new(linew, 4, nil, 0, SPIN_NORMAL|FRAME_SUNKEN|FRAME_THICK|LAYOUT_FILL_X|LAYOUT_FILL_COLUMN)
    @lineWidthSpinner.connect(SEL_COMMAND) { @linesCanvas.update(0, 0, @linesCanvas.width, @linesCanvas.height) }
    @lineWidthSpinner.range = 1..255
    @lineWidthSpinner.value = 1
  
    # Stipple
    stip = FXMatrix.new(controls, 2, FRAME_RIDGE|MATRIX_BY_COLUMNS|LAYOUT_FILL_X)
    stip.padLeft = 2
    stip.padRight = 2
    stip.padTop = 2
    stip.padBottom = 2
    FXLabel.new(stip, "Stipples:")
    pop = FXPopup.new(self)
    
    $stipples.each do |pat, desc|
      opt = FXOption.new(pop, desc, nil, nil, 0, JUSTIFY_HZ_APART|ICON_AFTER_TEXT)
      opt.userData = pat
      opt.connect(SEL_COMMAND) do |sender, sel, ptr|
        @stipple = sender.userData
        @linesCanvas.update(0, 0, @linesCanvas.width, @linesCanvas.height)
      end
    end
    FXOptionMenu.new(stip, pop, LAYOUT_TOP|FRAME_RAISED|FRAME_THICK|JUSTIFY_HZ_APART|ICON_AFTER_TEXT)
  
    # Fill Style
    FXLabel.new(controls, "Fill Style:", nil, LAYOUT_LEFT)
    fillstyle = FXMatrix.new(controls, 2, FRAME_RIDGE|MATRIX_BY_COLUMNS|LAYOUT_FILL_X)
    fillstyle.padLeft = 2
    fillstyle.padRight = 2
    fillstyle.padTop = 2
    fillstyle.padBottom = 2
    $fill_styles.each do |fs, desc|
      btn = FXButton.new(fillstyle, desc, nil, nil, 0, BUTTON_TOOLBAR|JUSTIFY_CENTER_X|FRAME_RAISED|LAYOUT_FILL_X|LAYOUT_FILL_Y|LAYOUT_FILL_COLUMN)
      btn.userData = fs
      btn.connect(SEL_COMMAND) do |sender, sel, ptr|
        @fillStyle = sender.userData
        @linesCanvas.update(0, 0, @linesCanvas.width, @linesCanvas.height)
      end
      btn.connect(SEL_UPDATE) do |sender, sel, ptr|
        if sender.userData == @fillStyle
          sender.handle(self, MKUINT(ID_CHECK, SEL_COMMAND), nil)
        else
          sender.handle(self, MKUINT(ID_UNCHECK, SEL_COMMAND), nil)
        end
      end
    end
  
    # Angles for arcs
    FXLabel.new(controls, "Arc angles:", nil, LAYOUT_LEFT)
    arcangles = FXMatrix.new(controls, 3, FRAME_RIDGE|MATRIX_BY_COLUMNS|LAYOUT_FILL_X)
    arcangles.padLeft = 2
    arcangles.padRight = 2
    arcangles.padTop = 2
    arcangles.padBottom = 2
    arcangles.hSpacing = 4
    arcangles.vSpacing = 4

    @ang1 = FXDataTarget.new(0)
    FXLabel.new(arcangles, "Ang1:", nil, LAYOUT_LEFT)
    FXTextField.new(arcangles, 4, @ang1, FXDataTarget::ID_VALUE, TEXTFIELD_INTEGER|JUSTIFY_RIGHT|FRAME_SUNKEN|FRAME_THICK)
    sang1 = FXSlider.new(arcangles, @ang1, FXDataTarget::ID_VALUE, LAYOUT_CENTER_Y|LAYOUT_FILL_X|SLIDER_INSIDE_BAR|LAYOUT_FILL_COLUMN)
    sang1.range = -360..360
    
    @ang2 = FXDataTarget.new(90)
    FXLabel.new(arcangles, "Ang2:", nil, LAYOUT_LEFT)
    FXTextField.new(arcangles, 4, @ang2, FXDataTarget::ID_VALUE, TEXTFIELD_INTEGER|JUSTIFY_RIGHT|FRAME_SUNKEN|FRAME_THICK)
    sang2 = FXSlider.new(arcangles, @ang2, FXDataTarget::ID_VALUE, LAYOUT_CENTER_Y|LAYOUT_FILL_X|SLIDER_INSIDE_BAR|LAYOUT_FILL_COLUMN)
    sang2.range = -360..360
  
    # Font
    fonts = FXHorizontalFrame.new(controls, FRAME_RIDGE|LAYOUT_FILL_X|PACK_UNIFORM_WIDTH)
    fonts.padLeft = 2
    fonts.padRight = 2
    fonts.padTop = 2
    fonts.padBottom = 2
    btn = FXButton.new(fonts, "Font Dialog...\tChange the text font", nil, nil, 0, BUTTON_TOOLBAR|JUSTIFY_CENTER_X|FRAME_RAISED|LAYOUT_FILL_X|LAYOUT_FILL_Y)
    btn.connect(SEL_COMMAND, method(:onCmdFont))
  
    # Printing
    printer = FXHorizontalFrame.new(controls, FRAME_RIDGE|LAYOUT_FILL_X|PACK_UNIFORM_WIDTH)
    printer.padLeft = 2
    printer.padRight = 2
    printer.padTop = 2
    printer.padBottom = 2
    btn = FXButton.new(printer, "Print Dialog...\tPrint it out", nil, nil, 0, BUTTON_TOOLBAR|JUSTIFY_CENTER_X|FRAME_RAISED|LAYOUT_FILL_X|LAYOUT_FILL_Y)
    btn.connect(SEL_COMMAND, method(:onCmdPrint))
  
    # Quit
    quitter = FXHorizontalFrame.new(controls, FRAME_RIDGE|LAYOUT_FILL_X|PACK_UNIFORM_WIDTH)
    quitter.padLeft = 2
    quitter.padRight = 2
    quitter.padTop = 2
    quitter.padBottom = 2
    FXButton.new(quitter, "Bye Bye!\tHasta la vista, baby!", nil, getApp(), FXApp::ID_QUIT, BUTTON_TOOLBAR|JUSTIFY_CENTER_X|FRAME_RAISED|LAYOUT_FILL_X|LAYOUT_FILL_Y)
  
    # Switcher
    tabbook = FXTabBook.new(contents, nil, 0, LAYOUT_FILL_X|LAYOUT_FILL_Y|LAYOUT_RIGHT)
    
    # First page shows various line styles
    linesTab = FXTabItem.new(tabbook, "&Lines", nil)
    linesPage = FXPacker.new(tabbook, FRAME_THICK|FRAME_RAISED)
    frame = FXHorizontalFrame.new(linesPage, FRAME_SUNKEN|FRAME_THICK|LAYOUT_FILL_X|LAYOUT_FILL_Y)
    frame.padLeft = 0
    frame.padRight = 0
    frame.padTop = 0
    frame.padBottom = 0
    @linesCanvas = FXCanvas.new(frame, nil, 0, LAYOUT_FILL_X|LAYOUT_FILL_Y)
    @linesCanvas.connect(SEL_PAINT) do |canvas, sel, ev|
      dc = FXDCWindow.new(canvas, ev)
      drawPage(dc, canvas.width, canvas.height)
    end
  
    # Second page shows various shapes
    shapesTab = FXTabItem.new(tabbook, "&Shapes", nil)
    shapesPage = FXPacker.new(tabbook, FRAME_THICK|FRAME_RAISED)
    frame = FXHorizontalFrame.new(shapesPage, FRAME_SUNKEN|FRAME_THICK|LAYOUT_FILL_X|LAYOUT_FILL_Y)
    frame.padLeft = 0
    frame.padRight = 0
    frame.padTop = 0
    frame.padBottom = 0
    @shapesCanvas = FXCanvas.new(frame, nil, 0, LAYOUT_FILL_X|LAYOUT_FILL_Y)
    @shapesCanvas.connect(SEL_PAINT) do |canvas, sel, ev|
      dc = FXDCWindow.new(canvas, ev)
      dc.foreground = @eraseWell.rgba
      dc.fillRectangle(0, 0, canvas.width, canvas.height)
      
      dc.foreground = @foreWell.rgba
      dc.background = @backWell.rgba
      dc.drawRectangle(5, 5, 50, 50)
      dc.fillRectangle(60, 5, 50, 50)
      
      dc.foreground = @foreWell.rgba
      dc.background = @backWell.rgba
      dc.drawArc(5, 60, 50, 50, 0, 64*90)
      dc.fillArc(60, 60, 50, 50, 64*90, 64*180)
      
      dc.foreground = @foreWell.rgba
      dc.background = @backWell.rgba
      dc.drawBitmap(@bitmap, 115, 5)
    end
    
    # Third page shows images
    imagesTab = FXTabItem.new(tabbook, "&Images", nil)
    imagesPage = FXPacker.new(tabbook, FRAME_THICK|FRAME_RAISED)
    frame = FXHorizontalFrame.new(imagesPage, FRAME_SUNKEN|FRAME_THICK|LAYOUT_FILL_X|LAYOUT_FILL_Y)
    frame.padLeft = 0
    frame.padRight = 0
    frame.padTop = 0
    frame.padBottom = 0
    @imagesCanvas = FXCanvas.new(frame, nil, 0, LAYOUT_FILL_X|LAYOUT_FILL_Y)
    @imagesCanvas.connect(SEL_PAINT) do |canvas, sel, ev|
      dc = FXDCWindow.new(canvas, ev)
      dc.foreground = @eraseWell.rgba
      dc.fillRectangle(0, 0, canvas.width, canvas.height)
      dc.drawImage(@birdImage, 0, 0)
    end
  
    # File menu
    @filemenu = FXMenuPane.new(self)
    FXMenuCommand.new(@filemenu, "&Print...\tCtl-P").connect(SEL_COMMAND, method(:onCmdPrint))
    FXMenuCommand.new(@filemenu, "&Font...\tCtl-F").connect(SEL_COMMAND, method(:onCmdFont))
    FXMenuCommand.new(@filemenu, "&Quit\tCtl-Q", nil, getApp(), FXApp::ID_QUIT)
    FXMenuTitle.new(menubar, "&File", nil, @filemenu)
    
    @birdImage = FXPNGImage.new(getApp(), File.open("icons/dippy.png", "rb").read)
    @bitmap = FXBitmap.new(getApp(), $bitmap_bits, 0, 64, 64)
    
    @function = BLT_SRC
    @lineStyle = LINE_SOLID
    @capStyle = CAP_BUTT
    @joinStyle = JOIN_MITER
    @fillStyle = FILL_SOLID
    @fillRule = RULE_EVEN_ODD
    @stipple = STIPPLE_NONE
    @forecolor = FXRGB(255, 0, 0) # red
    @backcolor = FXRGB(0, 0, 255) # blue
    @erasecolor = FXRGB(255, 255, 255) # white
    @testFont = FXFont.new(getApp(), "helvetica", 20)
  end
  
  def create
    super
    @birdImage.create
    @testFont.create
    @bitmap.create
    show(PLACEMENT_SCREEN)
  end
  
  def detach
    super
    @birdImage.detach
    @testFont.detach
    @bitmap.detach
  end
  
  def drawPage(dc, w, h)
    dc.foreground = @erasecolor
    dc.fillRectangle(0, 0, w, h)
    
    dc.foreground = @forecolor
    dc.background = @backcolor
    
    dc.lineStyle = @lineStyle
    dc.lineCap = @capStyle
    dc.lineJoin = @joinStyle
    dc.function = @function
    
    dc.stipple = @stipple
    dc.fillStyle = @fillStyle
    dc.lineWidth = @lineWidthSpinner.value
    
    # Here's a single line
    dc.drawLine(20, 200, w - 20, 200)
    
    # Here are some connected lines (to show join styles)
    points = []
    points << FXPoint.new(10, 3*h/4)
    points << FXPoint.new(points[0].x+w/6, h/2)
    points << FXPoint.new(points[1].x+w/6, points[0].y)
    points << FXPoint.new(points[2].x+w/6, points[1].y)
    points << FXPoint.new(points[3].x+w/6, points[0].y)
    points << FXPoint.new(points[4].x+w/6, points[1].y)
    dc.drawLines(points)
    
    dc.font = @testFont
    dc.foreground = @forecolor
    dc.background = @backcolor
    s = "Font: #{@testFont.name}  Size: #{@testFont.size/10}"
    dc.drawText(30, h-70, s)
    dc.drawImageText(30, h-30, s)
    
    dc.foreground = @forecolor
    dc.background = @backcolor
    dc.drawRectangle(20, 20, 200, 100)
    dc.fillRectangle(300, 20, 200, 100)
    
    dc.drawArc(20, 120, 100, 100, 64*@ang1.value, 64*@ang2.value)
    dc.fillArc(300, 120, 100, 100, 64*@ang1.value, 64*@ang2.value)
    
    poly = []
    poly << FXPoint.new(200, 230)
    poly << FXPoint.new(poly[0].x+40, poly[0].y+20)
    poly << FXPoint.new(poly[0].x+30, poly[0].y+60)
    poly << FXPoint.new(poly[0].x-30, poly[0].y+60)
    poly << FXPoint.new(poly[0].x-40, poly[0].y+20)
    dc.fillPolygon(poly)
    
    poly = []
    poly << FXPoint.new(300, 230)
    poly << FXPoint.new(poly[0].x+30, poly[0].y+60)
    poly << FXPoint.new(poly[0].x-40, poly[0].y+20)
    poly << FXPoint.new(poly[0].x+40, poly[0].y+20)
    poly << FXPoint.new(poly[0].x-30, poly[0].y+60)
    dc.fillComplexPolygon(poly)

    poly = []
    poly << FXPoint.new(400, 230)
    poly << FXPoint.new(poly[0].x+30, poly[0].y+60)
    poly << FXPoint.new(poly[0].x-40, poly[0].y+20)
    poly << FXPoint.new(poly[0].x+40, poly[0].y+20)
    poly << FXPoint.new(poly[0].x-30, poly[0].y+60)
    dc.fillRule = RULE_WINDING
    dc.fillComplexPolygon(poly)
    
    concave = []
    concave << FXPoint.new(w-100, h-100)
    concave << FXPoint.new(concave[0].x+40, concave[0].y-20)
    concave << FXPoint.new(concave[0].x   , concave[0].y+40)
    concave << FXPoint.new(concave[0].x-40, concave[0].y-20)
    dc.fillConcavePolygon(concave)
    
    # Draw a pale blue dot ;)
    dc.foreground = FXRGB(128, 128, 255)
    dc.drawPoint(w-20, h-20)
  end
  
  def onCmdFont(sender, sel, ptr)
    fontdlg = FXFontDialog.new(self, "Change Font", DECOR_BORDER|DECOR_TITLE)
    fontdlg.fontSelection = @testFont.fontDesc
    if fontdlg.execute != 0
      @testFont = FXFont.new(getApp(), fontdlg.fontSelection)
      @testFont.create
      @linesCanvas.update(0, 0, @linesCanvas.width, @linesCanvas.height)
    end
    return 1
  end
  
  def onCmdPrint(sender, sel, ptr)
    dlg = FXPrintDialog.new(self, "Print Graphics")
    if dlg.execute != 0
      p = dlg.printer
      pdc = FXDCPrint.new(getApp())
      unless pdc.beginPrint(p)
        FXMessageBox.error(self, MBOX_OK, "Printer Error", "Unable to print")
        return 1
      end
      pdc.beginPage(1)
      drawPage(pdc, 500, 500)
      pdc.endPage
      pdc.endPrint
    end
    return 1
  end
  
  # Load the named icon from a file
  def loadIcon(filename, clr = FXRGB(192, 192, 192), opts = 0)
    begin
      filename = File.join("icons", filename)
      icon = nil
      File.open(filename, "rb") { |f|
        icon = FXPNGIcon.new(getApp(), f.read, clr, opts)
      }
      icon
    rescue
      raise RuntimeError, "Couldn't load icon: #{filename}"
    end
  end
end

if __FILE__ == $0
  application = FXApp.new("DCTest", "FoxTest")
  DCTestWindow.new(application)
  application.create
  application.run
end