#!/usr/bin/env ruby

require 'fox'
require 'fox/colors'

include Fox

BLURB = <<END
FXDataTarget can be used to connect a widget to an application variable without any of the
tradional "glue" programming code.

The widgets below are connected (via FXDataTarget) to an integer, real, string, option, and
color variable, respectively.

Changing one of them will cause all widgets connected to the same FXDataTarget to
update so as to reflect the value of the application variable.

The progress bar below shows a time-varying variable, demonstrating that widgets
can be updated via FXDataTarget's regardless how the variables are changed.

Note that the "Option" pulldown menu is also connected to the option variable!
END

class DataTargetWindow < FXMainWindow

  def initialize(app)
    # Initialize base class
    super(app, "Data Targets Test", nil, nil, DECOR_ALL, 20, 20, 700, 460)

    # Create a data target with an integer value
    @intTarget = FXDataTarget.new(10)

    # Create a data target with a floating point value
    @floatTarget = FXDataTarget.new(3.1415927)

    # Create a data target with a string value
    @stringTarget = FXDataTarget.new("FOX")

    # Create a data target with a color value
    @colorTarget = FXDataTarget.new(FXColor::Red)

    # Create an integer data target to track the selected option
    @optionTarget = FXDataTarget.new(1)

    # Create another integer data target to track the "progress"
    @progressTarget = FXDataTarget.new(0)

    # Menubar
    menubar = FXMenubar.new(self, LAYOUT_SIDE_TOP|LAYOUT_FILL_X)
    
    # File menu
    filemenu = FXMenuPane.new(self)
      FXMenuCommand.new(filemenu, "&Quit\tCtl-Q", nil,
        getApp(), FXApp::ID_QUIT)
    FXMenuTitle.new(menubar, "&File", nil, filemenu)
    
    # Option menu
    optionmenu = FXMenuPane.new(self)
      FXMenuCommand.new(optionmenu, "Option 1", nil,
        @optionTarget, FXDataTarget::ID_OPTION+1);
      FXMenuCommand.new(optionmenu, "Option 2", nil,
        @optionTarget, FXDataTarget::ID_OPTION+2);
      FXMenuCommand.new(optionmenu, "Option 3", nil,
        @optionTarget, FXDataTarget::ID_OPTION+3);
      FXMenuCommand.new(optionmenu, "Option 4", nil,
        @optionTarget, FXDataTarget::ID_OPTION+4);
    FXMenuTitle.new(menubar, "&Option", nil, optionmenu)
    
    # Lone progress bar at the bottom
    FXProgressBar.new(self, @progressTarget, FXDataTarget::ID_VALUE,
      LAYOUT_SIDE_BOTTOM|LAYOUT_FILL_X|FRAME_SUNKEN|FRAME_THICK)
    
    FXHorizontalSeparator.new(self,
      LAYOUT_SIDE_TOP|SEPARATOR_GROOVE|LAYOUT_FILL_X)
    
    FXLabel.new(self, BLURB, nil,
      LAYOUT_SIDE_TOP|LAYOUT_FILL_X|JUSTIFY_LEFT)
      
    FXHorizontalSeparator.new(self,
      LAYOUT_SIDE_TOP|SEPARATOR_GROOVE|LAYOUT_FILL_X)
    FXSlider.new(self, @intTarget, FXDataTarget::ID_VALUE, (SLIDER_VERTICAL|
      SLIDER_INSIDE_BAR|LAYOUT_SIDE_RIGHT|LAYOUT_FILL_Y|LAYOUT_FIX_WIDTH),
      0, 0, 20, 0)
    
    # Arange nicely
    matrix = FXMatrix.new(self, 7,
      MATRIX_BY_COLUMNS|LAYOUT_SIDE_TOP|LAYOUT_FILL_X|LAYOUT_FILL_Y)
    
    # First row
    FXLabel.new(matrix, "&Integer", nil,
      LAYOUT_CENTER_Y|LAYOUT_CENTER_X|JUSTIFY_RIGHT|LAYOUT_FILL_ROW)
    FXTextField.new(matrix, 10, @intTarget, FXDataTarget::ID_VALUE,
      LAYOUT_CENTER_Y|LAYOUT_CENTER_X|FRAME_SUNKEN|FRAME_THICK|LAYOUT_FILL_ROW)
    FXTextField.new(matrix, 10, @intTarget, FXDataTarget::ID_VALUE,
      LAYOUT_CENTER_Y|LAYOUT_CENTER_X|FRAME_SUNKEN|FRAME_THICK|LAYOUT_FILL_ROW)
    FXSlider.new(matrix, @intTarget, FXDataTarget::ID_VALUE,
      LAYOUT_CENTER_Y|LAYOUT_FILL_ROW|LAYOUT_FIX_WIDTH, 0, 0, 100)
    FXDial.new(matrix, @intTarget, FXDataTarget::ID_VALUE, (LAYOUT_CENTER_Y|
      LAYOUT_FILL_ROW|LAYOUT_FIX_WIDTH|DIAL_HORIZONTAL|DIAL_HAS_NOTCH),
      0, 0, 100)
    FXSpinner.new(matrix, 5, @intTarget, FXDataTarget::ID_VALUE,
      SPIN_CYCLIC|FRAME_SUNKEN|FRAME_THICK|LAYOUT_CENTER_Y|LAYOUT_FILL_ROW)
    FXProgressBar.new(matrix, @intTarget, FXDataTarget::ID_VALUE,
      (LAYOUT_CENTER_Y|LAYOUT_FILL_X|FRAME_SUNKEN|FRAME_THICK|
       PROGRESSBAR_PERCENTAGE|LAYOUT_FILL_COLUMN|LAYOUT_FILL_ROW))
    
    # Second row
    FXLabel.new(matrix, "&Real", nil,
      LAYOUT_CENTER_Y|LAYOUT_CENTER_X|JUSTIFY_RIGHT|LAYOUT_FILL_ROW)
    FXTextField.new(matrix, 10, @floatTarget, FXDataTarget::ID_VALUE,
      (TEXTFIELD_REAL|LAYOUT_CENTER_Y|LAYOUT_CENTER_X|
       FRAME_SUNKEN|FRAME_THICK|LAYOUT_FILL_ROW))
    FXTextField.new(matrix, 10, @floatTarget, FXDataTarget::ID_VALUE,
      (TEXTFIELD_REAL|LAYOUT_CENTER_Y|LAYOUT_CENTER_X|
       FRAME_SUNKEN|FRAME_THICK|LAYOUT_FILL_ROW))
    FXSlider.new(matrix, @floatTarget, FXDataTarget::ID_VALUE,
      LAYOUT_CENTER_Y|LAYOUT_FILL_X|LAYOUT_FILL_ROW|LAYOUT_FIX_WIDTH, 0, 0, 100)
    FXDial.new(matrix, @floatTarget, FXDataTarget::ID_VALUE,
      (LAYOUT_CENTER_Y|LAYOUT_FILL_X|LAYOUT_FILL_ROW|LAYOUT_FIX_WIDTH|
       DIAL_HORIZONTAL|DIAL_HAS_NOTCH), 0, 0, 100)
  
    FXFrame.new(matrix, LAYOUT_FILL_COLUMN|LAYOUT_FILL_ROW)
    FXFrame.new(matrix, LAYOUT_FILL_COLUMN|LAYOUT_FILL_ROW)
    
    # Third row
    FXLabel.new(matrix, "&String", nil,
      LAYOUT_CENTER_Y|LAYOUT_CENTER_X|JUSTIFY_RIGHT|LAYOUT_FILL_ROW)
    FXTextField.new(matrix, 10, @stringTarget, FXDataTarget::ID_VALUE,
      LAYOUT_CENTER_Y|LAYOUT_CENTER_X|FRAME_SUNKEN|FRAME_THICK|LAYOUT_FILL_ROW)
    FXTextField.new(matrix, 10, @stringTarget, FXDataTarget::ID_VALUE,
      LAYOUT_CENTER_Y|LAYOUT_CENTER_X|FRAME_SUNKEN|FRAME_THICK|LAYOUT_FILL_ROW)
    FXFrame.new(matrix, LAYOUT_FILL_COLUMN|LAYOUT_FILL_ROW)
    FXFrame.new(matrix, LAYOUT_FILL_COLUMN|LAYOUT_FILL_ROW)
    FXFrame.new(matrix, LAYOUT_FILL_COLUMN|LAYOUT_FILL_ROW)
    FXFrame.new(matrix, LAYOUT_FILL_COLUMN|LAYOUT_FILL_ROW)
    
    # Fourth row
    FXLabel.new(matrix, "&Option", nil,
      LAYOUT_CENTER_Y|LAYOUT_CENTER_X|JUSTIFY_RIGHT|LAYOUT_FILL_ROW)
    FXTextField.new(matrix, 10, @optionTarget, FXDataTarget::ID_VALUE,
      (TEXTFIELD_INTEGER|LAYOUT_CENTER_Y|LAYOUT_CENTER_X|
       FRAME_SUNKEN|FRAME_THICK|LAYOUT_FILL_ROW))
    FXRadioButton.new(matrix, "Option &1",
      @optionTarget, FXDataTarget::ID_OPTION + 1,
      LAYOUT_CENTER_Y|LAYOUT_FILL_COLUMN|LAYOUT_FILL_ROW|ICON_BEFORE_TEXT)
    FXRadioButton.new(matrix, "Option &2",
      @optionTarget, FXDataTarget::ID_OPTION + 2,
      LAYOUT_CENTER_Y|LAYOUT_FILL_COLUMN|LAYOUT_FILL_ROW|ICON_BEFORE_TEXT)
    FXRadioButton.new(matrix, "Option &3",
      @optionTarget, FXDataTarget::ID_OPTION + 3,
      LAYOUT_CENTER_Y|LAYOUT_FILL_COLUMN|LAYOUT_FILL_ROW|ICON_BEFORE_TEXT)
    FXRadioButton.new(matrix, "Option &4",
      @optionTarget, FXDataTarget::ID_OPTION + 4,
      LAYOUT_CENTER_Y|LAYOUT_FILL_COLUMN|LAYOUT_FILL_ROW|ICON_BEFORE_TEXT)
    FXFrame.new(matrix, LAYOUT_FILL_COLUMN|LAYOUT_FILL_ROW)
    
    # Fifth
    FXLabel.new(matrix, "&Color", nil,
      LAYOUT_CENTER_Y|LAYOUT_CENTER_X|JUSTIFY_RIGHT|LAYOUT_FILL_ROW)
    FXColorWell.new(matrix, 0, @colorTarget, FXDataTarget::ID_VALUE,
      LAYOUT_CENTER_Y|LAYOUT_FILL_X|LAYOUT_FILL_ROW, 0, 0, 0, 0, 0, 0, 0, 0)
    FXColorWell.new(matrix, 0, @colorTarget, FXDataTarget::ID_VALUE,
      LAYOUT_CENTER_Y|LAYOUT_FILL_X|LAYOUT_FILL_ROW, 0, 0, 0, 0, 0, 0, 0, 0)
    FXFrame.new(matrix, LAYOUT_FILL_COLUMN|LAYOUT_FILL_ROW)
    FXFrame.new(matrix, LAYOUT_FILL_COLUMN|LAYOUT_FILL_ROW)
    FXFrame.new(matrix, LAYOUT_FILL_COLUMN|LAYOUT_FILL_ROW)
    FXFrame.new(matrix, LAYOUT_FILL_COLUMN|LAYOUT_FILL_ROW)
    
    # Install an accelerator
    getAccelTable().addAccel(fxparseaccel("Ctl-Q"), getApp(),
      MKUINT(FXApp::ID_QUIT, SEL_COMMAND))
  end

  # Timer expired; update the progress
  def onTimeout(sender, sel, ptr)
    # Increment the progress modulo 100
    @progressTarget.value = (@progressTarget.value + 1) % 100

    # Reset the timer for next time
    getApp().addTimeout(80, method(:onTimeout))
  end

  # Quit
  def onCmdQuit(sender, sel, ptr)
    getApp.exit(0)
  end

  # Start
  def create
    # Create window
    super

    # Kick off the timer
    getApp().addTimeout(80, method(:onTimeout))

    # Show the main window
    show(PLACEMENT_SCREEN)
  end
end

if __FILE__ == $0
  # Make an application
  application = FXApp.new("DataTarget", "FoxTest")

  # Current threads implementation causes problems for this example, so disable
  application.disableThreads

  # Open display
  application.init(ARGV)

  # Create main window
  window = DataTargetWindow.new(application)

  # Handle interrupts to quit application gracefully
  application.addSignal("SIGINT", window.method(:onCmdQuit))

  # Create the application
  application.create

  # Run
  application.run
end
