Opengl Tk widget for Ruby?

Discussion in 'Ruby' started by mpthompson at gmail.com, Sep 12, 2006.

  1. I'm would like to create a unified Ruby on Windows XP that will combine
    user interface elements of Ruby/Tk and OpenGL. Using Google I've seen
    several year old references to a Togl Tk widget that seemed to wrap
    OpenGL as a Tk widget, but all the links to it appear to be dead and I
    can't find any references that it ever worked on Windows.

    I'm have looked into FxRuby as it has OpenGL support built in, but
    unfortunately aspects of my user interface are tied to the Tk Canvas
    object which provides needed 2D editing functionality for my
    application. Is it possible to wrap the Tk canvase within FxRuby? I
    assume not.

    I would appreciate it if someone provide insight into if it is indeed
    possible to wrap an OpenGL view as a Tk widget in Ruby.

    I guess my fallback is to create a single application that manages two
    windows, one being the OpenGL and the other being Ruby/Tk.

    --Mike
    mpthompson at gmail.com, Sep 12, 2006
    #1
    1. Advertising

  2. From: "mpthompson at gmail.com" <>
    Subject: Opengl Tk widget for Ruby?
    Date: Tue, 12 Sep 2006 09:45:34 +0900
    Message-ID: <>
    > I'm would like to create a unified Ruby on Windows XP that will combine
    > user interface elements of Ruby/Tk and OpenGL. Using Google I've seen
    > several year old references to a Togl Tk widget that seemed to wrap
    > OpenGL as a Tk widget, but all the links to it appear to be dead and I
    > can't find any references that it ever worked on Windows.


    There is a section "Windows 95/NT/2000/XP usage" on
    <http://togl.sourceforge.net/>.
    However, Togl seems to be integrated into Tcl3D <http://www.tcl3d.org/>.

    BTW, Ruby/Tk can handle almost all of Tcl/Tk extensions.
    If there is no Ruby/Tk's wrapper for a Tcl/Tk extension,
    you can send Tcl commands to handle the extension with
    Tk.tk_call(...tokens_of_Tcl_command_line...), and so on.

    I think that it is not difficult to write your own wrapper.
    For example, simple Togl wrapper is
    -----------------------------------------------------------
    #
    # IMPORTANT: NOT TESTED. So, this may not work properly.
    #
    TkPackage.require('Togl')

    class Tk::Togl < TkWindow
    # By default, the constracter of a widget class (subclass of TkWindow)
    # uses TkCommandNames[0] as a command to create a widget object.
    TkCommandNames = ['togl'.freeze].freeze

    # The following definitions are used to create a widget object
    # from a widget path string.
    WidgetClassName = 'Togl'.freeze
    WidgetClassNames[WidgetClassName] = self

    def render
    tk_send_without_enc('render')
    self
    end

    def swapbuffers
    tk_send_without_enc('swapbuffers')
    self
    end

    def make_current
    tk_send_without_enc('makecurrent')
    self
    end
    end

    # f = TkFrame.new
    # togl_obj1 = Tk::Togl.new(f, :width=>200, :height=>200, :ident=>'Single',
    # :rgba=>true, :double=>false, :depth=>true).pack
    # togl_obj2 = Tk::Togl.new(f, :width=>200, :height=>200, :ident=>'Double',
    # :sharelist=>'Single',
    # :rgba=>true, :double=>true, :depth=>true).pack
    # togl_obj1.bind('B1-Motion', proc{....})
    -----------------------------------------------------------
    --
    Hidetoshi NAGAI ()
    Hidetoshi NAGAI, Sep 12, 2006
    #2
    1. Advertising

  3. Hidetoshi NAGAI wrote:
    > There is a section "Windows 95/NT/2000/XP usage" on
    > <http://togl.sourceforge.net/>.
    > However, Togl seems to be integrated into Tcl3D <http://www.tcl3d.org/>.
    >
    > BTW, Ruby/Tk can handle almost all of Tcl/Tk extensions.
    > If there is no Ruby/Tk's wrapper for a Tcl/Tk extension,
    > you can send Tcl commands to handle the extension with
    > Tk.tk_call(...tokens_of_Tcl_command_line...), and so on.


    Thank you for the information. I'm actually pretty new to Ruby and
    know very little about the how it interacts with OpenGL or Tk under the
    hood. Writing my own wrappers looks to be a bit daunting, but I guess
    I may have to go in that direction.

    I did try and use Ruby SDL to accomplish my goal as I saw in a very old
    example in this news group from 2001 that showed affixing SDL to a
    TkFrame and the SDL displaying OpenGL graphics. However, I ran into an
    issue with SDL_WINDOWID not working correctly because of underlying
    issues with putenv/getenv with Windows when SDL is run from a DLL.

    --Mike
    mpthompson at gmail.com, Sep 12, 2006
    #3
  4. mpthompson at gmail.com wrote:
    > Hidetoshi NAGAI wrote:
    > > There is a section "Windows 95/NT/2000/XP usage" on
    > > <http://togl.sourceforge.net/>.
    > > However, Togl seems to be integrated into Tcl3D <http://www.tcl3d.org/>.


    I think I'm getting close to getting OpenGL and Tk playing nice in
    Ruby, but I'm not quite there yet. Any help would be appreciated.

    Based on the suggestion above I was able to get the Tcl3D installed
    with ActiveTcl 8.4 and get the Tcl based OpenGL demos distributed with
    Tcl3D working fine on my system. Next, I used Hidetoshi's code as a
    guide to create a simple wrapper, after a bit of fussing to determine
    the right way to get the tcl3d dll loaded, I created wrapper for togl
    within Tcl3D as shown below which includes an addition for
    postredisplay:

    ---------------------------------------------------------
    #
    # Togl wrapper for tcl3d Tk extension.
    #
    TkPackage.require('tcl3d')

    class Tk::Togl < TkWindow
    # By default, the constracter of a widget class (subclass of
    TkWindow)
    # uses TkCommandNames[0] as a command to create a widget object.
    TkCommandNames = ['togl'.freeze].freeze

    # The following definitions are used to create a widget object
    # from a widget path string.
    WidgetClassName = 'Togl'.freeze
    WidgetClassNames[WidgetClassName] = self

    def postredisplay
    tk_send_without_enc('postredisplay')
    self
    end

    def render
    tk_send_without_enc('render')
    self
    end

    def swapbuffers
    tk_send_without_enc('swapbuffers')
    self
    end

    def make_current
    tk_send_without_enc('makecurrent')
    self
    end
    end


    # f = TkFrame.new
    # togl_obj1 = Tk::Togl.new(f, :width=>200, :height=>200,
    :ident=>'Single',
    # :rgba=>true, :double=>false,
    :depth=>true).pack
    # togl_obj2 = Tk::Togl.new(f, :width=>200, :height=>200,
    :ident=>'Double',
    # :sharelist=>'Single',
    # :rgba=>true, :double=>true,
    :depth=>true).pack
    # togl_obj1.bind('B1-Motion', proc{....})
    ---------------------------------------------------------

    Now I'm trying to get the standard gears.rb OpenGL application running
    within the tk managed window. This is where I'm really getting lost. I
    posted below the application as it looks now, but it isn't working and
    the window just comes up blank. I assume I'm missing something
    fundamental here. I'm hoping someone could either provide a working
    example of a Tk/OpenGL application using togl or help steer me in where
    my code is failing below.

    Thanks,

    Mike Thompson

    ---------------------------------------------------------
    # 2005-05-01 Ruby version by Arto Bendiken based on gears.c rev 1.8.
    # 2005-01-09 Original C version (gears.c) by Brian Paul et al.
    # http://cvs.freedesktop.org/mesa/Mesa/progs/demos/gears.c?rev=1.8

    require 'opengl_c'
    require 'glut_c'
    require 'tk'
    require 'tkextlib/tkHTML'
    require 'togl'

    include OpenGL
    include Glut

    class Gears

    POS = [5.0, 5.0, 10.0, 0.0]
    RED = [0.8, 0.1, 0.0, 1.0]
    GREEN = [0.0, 0.8, 0.2, 1.0]
    BLUE = [0.2, 0.2, 1.0, 1.0]

    include Math

    # Draw a gear wheel. You'll probably want to call this function when
    # building a display list since we do a lot of trig here.
    #
    # Input: inner_radius - radius of hole at center
    # outer_radius - radius at center of teeth
    # width - width of gear
    # teeth - number of teeth
    # tooth_depth - depth of tooth
    def gear(inner_radius, outer_radius, width, teeth, tooth_depth)
    r0 = inner_radius
    r1 = outer_radius - tooth_depth / 2.0
    r2 = outer_radius + tooth_depth / 2.0

    da = 2.0 * PI / teeth / 4.0

    glShadeModel(GL_FLAT)

    glNormal(0.0, 0.0, 1.0)

    # Draw front face
    glBegin(GL_QUAD_STRIP)
    for i in 0..teeth
    angle = i * 2.0 * PI / teeth
    glVertex3f(r0 * cos(angle), r0 * sin(angle), width * 0.5)
    glVertex3f(r1 * cos(angle), r1 * sin(angle), width * 0.5)
    if i < teeth
    glVertex3f(r0 * cos(angle), r0 * sin(angle), width * 0.5)
    glVertex3f(r1 * cos(angle + 3 * da),
    r1 * sin(angle + 3 * da), width * 0.5)
    end
    end
    glEnd()

    # Draw front sides of teeth
    glBegin(GL_QUADS)
    for i in 0...teeth
    angle = i * 2.0 * PI / teeth
    glVertex3f(r1 * cos(angle), r1 * sin(angle), width * 0.5)
    glVertex3f(r2 * cos(angle + da), r2 * sin(angle + da), width *
    0.5)
    glVertex3f(r2 * cos(angle + 2 * da),
    r2 * sin(angle + 2 * da), width * 0.5)
    glVertex3f(r1 * cos(angle + 3 * da),
    r1 * sin(angle + 3 * da), width * 0.5)
    end
    glEnd()

    glNormal(0.0, 0.0, -1.0)

    # Draw back face
    glBegin(GL_QUAD_STRIP)
    for i in 0..teeth
    angle = i * 2.0 * PI / teeth
    glVertex3f(r1 * cos(angle), r1 * sin(angle), -width * 0.5)
    glVertex3f(r0 * cos(angle), r0 * sin(angle), -width * 0.5)
    if i < teeth
    glVertex3f(r1 * cos(angle + 3 * da),
    r1 * sin(angle + 3 * da), -width * 0.5)
    glVertex3f(r0 * cos(angle), r0 * sin(angle), -width * 0.5)
    end
    end
    glEnd()

    # Draw back sides of teeth
    glBegin(GL_QUADS)
    for i in 0...teeth
    angle = i * 2.0 * PI / teeth
    glVertex3f(r1 * cos(angle + 3 * da),
    r1 * sin(angle + 3 * da), -width * 0.5)
    glVertex3f(r2 * cos(angle + 2 * da),
    r2 * sin(angle + 2 * da), -width * 0.5)
    glVertex3f(r2 * cos(angle + da), r2 * sin(angle + da), -width *
    0.5)
    glVertex3f(r1 * cos(angle), r1 * sin(angle), -width * 0.5)
    end
    glEnd()

    # Draw outward faces of teeth
    glBegin(GL_QUAD_STRIP)
    for i in 0...teeth
    angle = i * 2.0 * PI / teeth
    glVertex3f(r1 * cos(angle), r1 * sin(angle), width * 0.5)
    glVertex3f(r1 * cos(angle), r1 * sin(angle), -width * 0.5)
    u = r2 * cos(angle + da) - r1 * cos(angle)
    v = r2 * sin(angle + da) - r1 * sin(angle)
    len = sqrt(u * u + v * v)
    u /= len
    v /= len
    glNormal(v, -u, 0.0)
    glVertex3f(r2 * cos(angle + da), r2 * sin(angle + da), width *
    0.5)
    glVertex3f(r2 * cos(angle + da), r2 * sin(angle + da), -width *
    0.5)
    glNormal(cos(angle), sin(angle), 0.0)
    glVertex3f(r2 * cos(angle + 2 * da),
    r2 * sin(angle + 2 * da), width * 0.5)
    glVertex3f(r2 * cos(angle + 2 * da),
    r2 * sin(angle + 2 * da), -width * 0.5)
    u = r1 * cos(angle + 3 * da) - r2 * cos(angle + 2 * da)
    v = r1 * sin(angle + 3 * da) - r2 * sin(angle + 2 * da)
    glNormal(v, -u, 0.0)
    glVertex3f(r1 * cos(angle + 3 * da),
    r1 * sin(angle + 3 * da), width * 0.5)
    glVertex3f(r1 * cos(angle + 3 * da),
    r1 * sin(angle + 3 * da), -width * 0.5)
    glNormal(cos(angle), sin(angle), 0.0)
    end
    glVertex3f(r1 * cos(0), r1 * sin(0), width * 0.5)
    glVertex3f(r1 * cos(0), r1 * sin(0), -width * 0.5)
    glEnd()

    glShadeModel(GL_SMOOTH)

    # Draw inside radius cylinder
    glBegin(GL_QUAD_STRIP)
    for i in 0..teeth
    angle = i * 2.0 * PI / teeth
    glNormal(-cos(angle), -sin(angle), 0.0)
    glVertex3f(r0 * cos(angle), r0 * sin(angle), -width * 0.5)
    glVertex3f(r0 * cos(angle), r0 * sin(angle), width * 0.5)
    end
    glEnd()
    end

    def draw
    glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);

    glPushMatrix()
    glRotate(@view_rotx, 1.0, 0.0, 0.0)
    glRotate(@view_roty, 0.0, 1.0, 0.0)
    glRotate(@view_rotz, 0.0, 0.0, 1.0)

    glPushMatrix()
    glTranslate(-3.0, -2.0, 0.0)
    glRotate(@angle, 0.0, 0.0, 1.0)
    glCallList(@gear1)
    glPopMatrix()

    glPushMatrix()
    glTranslate(3.1, -2.0, 0.0)
    glRotate(-2.0 * @angle - 9.0, 0.0, 0.0, 1.0)
    glCallList(@gear2)
    glPopMatrix()

    glPushMatrix()
    glTranslate(-3.1, 4.2, 0.0)
    glRotate(-2.0 * @angle - 25.0, 0.0, 0.0, 1.0)
    glCallList(@gear3)
    glPopMatrix()

    glPopMatrix()

    @glwin.swapbuffers
    end

    def idle
    #t = glutGet(GLUT_ELAPSED_TIME) / 1000.0
    #@t0_idle = t if !defined? @t0_idle
    # 90 degrees per second
    #@angle += 70.0 * (t - @t0_idle)
    #@t0_idle = t
    #@glwin.postredisplay
    end

    # Change view angle, exit upon ESC
    def key(k, x, y)
    case k
    when ?z
    @view_rotz += 5.0
    when ?Z
    @view_rotz -= 5.0
    when 27 # Escape
    exit
    end
    @glwin.postredisplay
    end

    # Change view angle
    def special(k, x, y)
    case k
    when GLUT_KEY_UP
    @view_rotx += 5.0
    when GLUT_KEY_DOWN
    @view_rotx -= 5.0
    when GLUT_KEY_LEFT
    @view_roty += 5.0
    when GLUT_KEY_RIGHT
    @view_roty -= 5.0
    end
    @glwin.postredisplay
    end

    # New window size or exposure
    def reshape(width, height)
    h = height.to_f / width.to_f
    glViewport(0, 0, width, height)
    glMatrixMode(GL_PROJECTION)
    glLoadIdentity()
    glFrustum(-1.0, 1.0, -h, h, 5.0, 60.0)
    glMatrixMode(GL_MODELVIEW)
    glLoadIdentity()
    glTranslate(0.0, 0.0, -40.0)
    end

    def init
    @angle = 0.0
    @view_rotx, @view_roty, @view_rotz = 20.0, 30.0, 0.0

    glLightfv(GL_LIGHT0, GL_POSITION, POS)
    glEnable(GL_CULL_FACE)
    glEnable(GL_LIGHTING)
    glEnable(GL_LIGHT0)
    glEnable(GL_DEPTH_TEST)

    # Make the gears
    @gear1 = glGenLists(1)
    glNewList(@gear1, GL_COMPILE)
    glMaterial(GL_FRONT, GL_AMBIENT_AND_DIFFUSE, RED)
    gear(1.0, 4.0, 1.0, 20, 0.7)
    glEndList()

    @gear2 = glGenLists(1)
    glNewList(@gear2, GL_COMPILE)
    glMaterial(GL_FRONT, GL_AMBIENT_AND_DIFFUSE, GREEN)
    gear(0.5, 2.0, 2.0, 10, 0.7)
    glEndList()

    @gear3 = glGenLists(1)
    glNewList(@gear3, GL_COMPILE)
    glMaterial(GL_FRONT, GL_AMBIENT_AND_DIFFUSE, BLUE)
    gear(1.3, 2.0, 0.5, 10, 0.7)
    glEndList()

    glEnable(GL_NORMALIZE)

    ARGV.each do |arg|
    case arg
    when '-info'
    printf("GL_RENDERER = %s\n", glGetString(GL_RENDERER))
    printf("GL_VERSION = %s\n", glGetString(GL_VERSION))
    printf("GL_VENDOR = %s\n", glGetString(GL_VENDOR))
    printf("GL_EXTENSIONS = %s\n", glGetString(GL_EXTENSIONS))
    when '-exit'
    @autoexit = 30
    printf("Auto Exit after %i seconds.\n", @autoexit);
    end
    end
    end

    def visible(vis)
    #glutIdleFunc((vis == GLUT_VISIBLE ? method:)idle).to_proc : nil))
    end

    def mouse(button, state, x, y)
    @mouse = state
    @x0, @y0 = x, y
    end

    def motion(x, y)
    if @mouse == GLUT_DOWN then
    @view_roty += @x0 - x
    @view_rotx += @y0 - y
    end
    @x0, @y0 = x, y
    end

    def initialize
    @root = TkRoot.new
    @frame = TkFrame.new(@root)
    @glwin = Tk::Togl.new(@frame, :width=>300, :height=>300,
    :ident=>'Single',
    :rgba=>true, :depth=>true,
    :double=>false).pack('fill'=>'both')
    init()
    end

    def start
    Tk.mainloop
    end

    end

    Gears.new.start
    ---------------------------------------------------------
    mpthompson at gmail.com, Sep 20, 2006
    #4
  5. OK, I think I got getting Ruby Tk and OpenGL figured out -- at least
    for the most part. For posterity and the hope it will be useful to
    someone else I'll go through the steps of how I got it working below.

    Step 1. I installed ActiveTcl 8.4 <http://www.activestate.com/> and
    Tcl3D <http://www.tcl3d.org/> installed on my system under the
    directory 'C:\Ruby\Tcl'. I then made sure the Tcl3D demos ran fine to
    confirm OpenGL was properly working.

    Step 2. I added the following lines to my setup.rb file in
    'C:\Ruby\lib\ruby\1.8\tkextlib'

    ---------------------------
    Tk::AUTO_PATH.list <<= 'C:/Ruby/Tcl/lib/tcl3d0.3.1/tcl3dCg'
    Tk::AUTO_PATH.list <<= 'C:/Ruby/Tcl/lib/tcl3d0.3.1/tcl3dFTGL'
    Tk::AUTO_PATH.list <<= 'C:/Ruby/Tcl/lib/tcl3d0.3.1/tcl3dGauges'
    Tk::AUTO_PATH.list <<= 'C:/Ruby/Tcl/lib/tcl3d0.3.1/tcl3dGl2ps'
    Tk::AUTO_PATH.list <<= 'C:/Ruby/Tcl/lib/tcl3d0.3.1/tcl3dOde'
    Tk::AUTO_PATH.list <<= 'C:/Ruby/Tcl/lib/tcl3d0.3.1/tcl3dOgl'
    Tk::AUTO_PATH.list <<= 'C:/Ruby/Tcl/lib/tcl3d0.3.1/tcl3dSDL'
    Tk::AUTO_PATH.list <<= 'C:/Ruby/Tcl/lib/tcl3d0.3.1/tcl3dTogl'
    Tk::AUTO_PATH.list <<= 'C:/Ruby/Tcl/lib/tcl3d0.3.1/tcl3dUtil'
    ---------------------------

    Step 3. I created a togl wrapper class in 'togl.rb' as suggested by
    Hidetoshi NAGAI as shown below:

    ---------------------------
    #
    # Togl wrapper for tcl3d Tk extension.
    #
    TkPackage.require('tcl3d')

    class Tk::Togl < TkWindow
    TkCommandNames = ['togl'.freeze].freeze
    WidgetClassName = 'Togl'.freeze
    WidgetClassNames[WidgetClassName] = self

    def render
    tk_send_without_enc('render')
    self
    end

    def swapbuffers
    tk_send_without_enc('swapbuffers')
    self
    end

    def make_current
    tk_send_without_enc('makecurrent')
    self
    end

    def post_redisplay
    tk_send_without_enc('postredisplay')
    self
    end
    end
    ---------------------------

    Step 4. I create a simple 'togltest.rb' application to test OpenGL
    working within Tk togl widget:

    ---------------------------
    require 'tk'
    require 'togl'
    require 'opengl'

    def create(toglwin)
    light_diffuse = [1.0, 0.0, 0.0, 1.0]
    light_position = [1.0, 1.0, 1.0, 0.0]

    GL::Light(GL::LIGHT0, GL::DIFFUSE, light_diffuse);
    GL::Light(GL::LIGHT0, GL::pOSITION, light_position);
    GL::Enable(GL::LIGHT0);
    GL::Enable(GL::LIGHTING);
    GL::Enable(GL::DEPTH_TEST);

    GL::MatrixMode(GL::pROJECTION);
    GLU::perspective(40.0, 1.0, 1.0, 10.0);
    GL::MatrixMode(GL::MODELVIEW);
    GLU::LookAt(0.0, 0.0, 5.0, 0.0, 0.0, 0.0, 0.0, 1.0, 0.0)

    GL::Translate(0.0, 0.0, -1.0);
    GL::Rotate(60, 1.0, 0.0, 0.0);
    GL::Rotate(-20, 0.0, 0.0, 1.0);
    end

    def reshape(toglwin, width, height)
    width = width.to_i
    height = height.to_i
    if $glwin
    $glwin.post_redisplay
    end
    end

    def display(toglwin)
    n = [
    [-1.0, 0.0, 0.0], [0.0, 1.0, 0.0],
    [1.0, 0.0, 0.0], [0.0, -1.0, 0.0],
    [0.0, 0.0, 1.0], [0.0, 0.0, -1.0]
    ]
    faces = [
    [0, 1, 2, 3], [3, 2, 6, 7], [7, 6, 5, 4],
    [4, 5, 1, 0], [5, 6, 2, 1], [7, 4, 0, 3]
    ]
    v = [
    [-1, -1,1], [-1, -1,-1],
    [-1,1,-1], [-1,1,1],
    [1, -1,1], [1, -1,-1],
    [1, 1,-1], [1,1,1]
    ]

    GL::Clear(GL::COLOR_BUFFER_BIT | GL::DEPTH_BUFFER_BIT);
    for i in (0..5)
    GL::Begin(GL::QUADS);
    GL::Normal(*(n));
    GL::Vertex(v[faces[0]]);
    GL::Vertex(v[faces[1]]);
    GL::Vertex(v[faces[2]]);
    GL::Vertex(v[faces[3]]);
    GL::End()
    end

    $glwin.swapbuffers
    end

    $root = TkRoot.new
    $frame = TkFrame.new($root).pack('fill'=>'both', 'padx'=>10,
    'pady'=>10)
    $glwin = Tk::Togl.new($frame, 'width'=>300, 'height'=>300,
    'ident'=>'Single',
    'rgba'=>true, 'depth'=>true, 'double'=>true,
    'createproc'=>proc {|toglwin| create(toglwin)},
    'displayproc'=>proc {|toglwin| display(toglwin)},
    'reshapeproc'=>proc {|toglwin, width, height|
    reshape(toglwin, width, height)}
    ).pack('fill'=>'both')

    Tk.mainloop
    ---------------------------

    That's it. When you run the Ruby Tk application a window with an
    OpenGL 3D red block should appear.

    I hope someone else finds this information useful.

    Mike Thompson
    mpthompson at gmail.com, Sep 21, 2006
    #5
    1. Advertising

Want to reply to this thread or ask your own question?

It takes just 2 minutes to sign up (and it's free!). Just click the sign up button to choose a username and then you can ask your own questions on the forum.
Similar Threads
  1. Alwin Blok

    ruby-opengl, ruby-glut

    Alwin Blok, Dec 30, 2003, in forum: Ruby
    Replies:
    5
    Views:
    184
    Alwin Blok
    Jan 21, 2004
  2. Replies:
    0
    Views:
    331
  3. Frederic Rentsch
    Replies:
    0
    Views:
    238
    Frederic Rentsch
    Jul 8, 2012
  4. Terry Reedy
    Replies:
    15
    Views:
    633
    Frederic Rentsch
    Jul 16, 2012
  5. Frederic Rentsch
    Replies:
    0
    Views:
    426
    Frederic Rentsch
    Jul 9, 2012
Loading...

Share This Page