qtbindings版Simple OLE Browser

# 最初に
ここで言う「Simple OLE Browser」というのは、RubyライブラリWin32OLEの作者でいらっしゃる助田さん作成のツールです。
オリジナルはこちらからダウンロードできます→ http://homepage1.nifty.com/markey/ruby/win32ole/

# 前回のSimple OLE Browserは！
ふつうに添付のsoleb.rbを動かすスキルを持たなかったdoju_mは無謀にもwxRubyで書き直そうと目論んだ！
奇跡的にそれは動作したが、新たな敵がdoju_mを待ち受ける！
「wxRubyはRuby2.0に対応していない……だと……」

# そんなわけで
新たなガワを探す旅に出た私でしたが紆余曲折あって「Qtでいこか」と決めたのでした。
もっとも使ってるのがmingw版ということでqtruby4がgemで入らず、
「どうすればいいんだ」
とひとしきり悩んだ後qtbindingsで大丈夫だ、問題ないということに気がつくのですが。

# そんなことより~~おうどんたべたい~~コードはどうした
例によってWindowsでしか動きませんのでご注意を。
起動時に分単位で待たされるのも相変わらずですorz

```ruby:qtsoleb.rbw
# -*- encoding: utf-8 -*-

require 'thread' # workaround for https://github.com/ryanmelt/qtbindings/issues/69
require 'Qt'
require 'win32ole'
WIN32OLE.codepage = WIN32OLE::CP_UTF8

class OLEINFO
  def initialize
    @members = []
    @tlibs = nil
  end
  def typelibs
    if @tlibs.nil?
      @tlibs = WIN32OLE_TYPELIB.typelibs.select{|t|
        t.name.size > 0
      }.sort {|a, b| a.name <=> b.name}
    end
    @tlibs
  end

  def ole_classes(tlib)
    WIN32OLE_TYPE.ole_classes(tlib).collect {|t|
      t.name
    }.sort
  end

  def ole_members(tlib, klass)
    @members = []
    k = WIN32OLE_TYPE.ole_classes(tlib).find {|t|
      t.name == klass
    }
    return [] if !k
    @members = (k.ole_methods + k.variables).sort{|m1, m2|
      m1.name <=> m2.name
    }
    (@members).collect {|m|
      m.name
    }
  end

  def ole_class_info(tlib, klass)
    k = WIN32OLE_TYPE.ole_classes(tlib).find {|t|
      t.name == klass
    }
    return "" if !k
    "#{k.ole_type}  #{k.name}\r\n" +
    "  GUID : #{k.guid}\r\n" +
    "  PROGID : #{k.progid}\r\n" +
    "  DESCRIPTION : #{k.helpstring}\r\n\r\n"
  end

  def method_info(m)
    info = ""
    info += "Event " if m.event?
    info += m.invoke_kind 
    info += " "
    info += m.return_type
    info += " "
    info += m.name
    info += "\r\n"
    info += "  Dispatch ID : #{m.dispid}\r\n"
    info += "  DESCRIPTION : #{m.helpstring}\r\n" 
    m.params.each_with_index do |param, i|
      pinfos = []
      pinfos.push "IN" if param.input?
      pinfos.push "OUT" if param.output?
      pinfos.push "OPTION" if param.optional?
      pinfo = "arg#{i+1} - #{param.ole_type} #{param.name} [#{pinfos.join(',')}]"
      pinfo += " = #{param.default}" if param.default
      info += "  #{pinfo}\r\n"
    end
    info += "\r\n"
    info += "  Event Interface : #{m.event_interface}\r\n" if m.event?
    info
  end

  def variable_info(m)
    info = "\r\n  #{m.variable_kind} #{m.ole_type} #{m.name}"
    info += " = #{m.value}" if m.value
    info
  end

  def ole_member_info(tlib, klass, member, i)
    k = WIN32OLE_TYPE.ole_classes(tlib).find {|t|
      t.name == klass
    }
    return "" if !k
    m = (k.ole_methods + k.variables).find {|mm|
       mm.name == member
    }
    m = @members[i]
    return "" if !m
    if m.kind_of?(WIN32OLE_METHOD)
      return method_info(m)
    else
      return variable_info(m)
    end
  end
end

module SOleB
  class App
    VERSION = '0.0.1a'
    
    def initialize(argv)
      @app = Qt::Application.new(argv)
      @frame = SOleB::MainWindow.new(self)
    end
    
    def version
      VERSION
    end
    
    def exec
      @frame.show
      @app.exec
    end
  end
  
  class MainWindow < Qt::MainWindow
    def initialize(app)
      super()
      @main_wid = SOleB::MainFrame.new
      self.window_title = "Simple OLE Browser #{app.version}"
      #resize(400, 300)
      setCentralWidget(@main_wid)
    end
  end
  
  class MainFrame < Qt::Widget
    slots 'tlib_selchanged(const QModelIndex&)'
    slots 'class_selchanged(const QModelIndex&)'
    slots 'member_selchanged(const QModelIndex&)'
  
    def initialize
      super
      ole_info = OLEINFO.new
      frame = self
      if_list = nil
      method_list = nil
      desc_area = nil
      setLayout(Qt::VBoxLayout.new {
        lib_list = Qt::ListView.new
        lib_model = Qt::StringListModel.new
        lib_model.stringList = ole_info.typelibs.collect {|e| e.name}
        lib_list.model = lib_model
        connect(lib_list, SIGNAL('clicked(const QModelIndex&)'),
          frame, SLOT('tlib_selchanged(const QModelIndex&)'))
        addWidget lib_list
        addLayout Qt::HBoxLayout.new {
          if_list = Qt::ListView.new
          connect(if_list, SIGNAL('clicked(const QModelIndex&)'),
            frame, SLOT('class_selchanged(const QModelIndex&)'))
          addWidget if_list
          method_list = Qt::ListView.new
          connect(method_list, SIGNAL('clicked(const QModelIndex&)'),
            frame, SLOT('member_selchanged(const QModelIndex&)'))
          addWidget method_list
        }
        desc_area = Qt::TextEdit.new
        desc_area.readOnly = true
        addWidget desc_area
      })
      @ole_info = ole_info
      @if_list = if_list
      @method_list = method_list
      @desc_area = desc_area
      @current_guid = nil
    end
    
    def tlib_selchanged(model_idx)
      if_model = Qt::StringListModel.new
      @current_guid = @ole_info.typelibs[model_idx.row].guid
      if_model.stringList = @ole_info.ole_classes(@current_guid)
      @if_list.model = if_model
    end
    
    def class_selchanged(model_idx)
      @current_class_name = @if_list.model.data(model_idx).toString()
      #puts @current_class_name
      method_model = Qt::StringListModel.new
      method_model.stringList = @ole_info.ole_members(
        @current_guid, @current_class_name)
      @method_list.model = method_model
      @desc_area.plainText = get_classinfo(@current_guid, @current_class_name)
    end
    
    def member_selchanged(model_idx)
      member_name = @method_list.model.data(model_idx).toString()
      #puts member_name
      @desc_area.plainText =
        get_classinfo(@current_guid, @current_class_name) +
        @ole_info.ole_member_info(
          @current_guid, @current_class_name, member_name, model_idx.row)
    end
    
    def get_classinfo(guid, class_name)
      @ole_info.ole_class_info(guid, class_name)
    end
    
  end
  
end

SOleB::App.new(ARGV).exec
```

基本ガワ固有部分以外はほぼ共通のはずです。
(WIN32OLE.codepageなんてあったんだ、ということでUTF-8に統一したくらいかと)

# 付け足し
前回同様、ライセンスはオリジナルにならい
> You can redistribute it under the terms specified in the COPYING file of the Ruby distribution.
THIS SOFTWARE IS PROVIDED "AS IS" AND WITHOUT ANY EXPRESS OR
IMPLIED WARRANTIES, INCLUDING, WITHOUT LIMITATION, THE IMPLIED
WARRANTIES OF MERCHANTIBILITY AND FITNESS FOR A PARTICULAR
PURPOSE.

と致します。改変もご自由に。