class Wx::Window # Convenience function to replace one Window with another. Useful # because there are elements which are not available or convenient in # XRC, but a placeholder can be used with a real child created in # ruby. # # Expects a symbol (eg :@child by which the old can be got) and a new # child to replace it. The instance variable's value is overwritten. def replace_xrc(old_sym, new_win) old_win = instance_variable_get("@#{old_sym}") old_win.containing_sizer.replace( old_win, new_win ) old_win.destroy instance_variable_set("@#{old_sym}", new_win) new_win.containing_sizer.layout end end module QDA::GUI # Class for loading and saving application settings between runs. The # preferences are saved in a text file in the user's home directory. # # Keys are stored in a hierarchy with path-like names, and values may # be any normal ruby object. # # Two ways to use; firstly, read and write values one at a # time. Imagine wishing to save a frame's width between uses: # # width = Config.read('/MyFrame/width') # width = Config.write('/MyFrame/width', frame.size.width) # # Alternately, can be used with a block class Config require 'yaml' class << self attr_accessor :name # Passes the config object into a block, optionally opening it at # the specified +path+. Saves any changes at the end of the block def use(path = nil) config = load config.path = path if path yield config save(config) end # Reads a single value at +path+ def read(key) load[key] end # Write the single value +val+ to the path +key+ def write(key, val) use { | cfg | cfg[key] = val } val end private # Finds the config file def config_file name = Wx::THE_APP.app_name if name.empty? or name == 'w' Kernel.raise "No app name set for Config file" end config_file = File.join(Wx::get_home_dir, ".#{name}") end # Loads data from the config file def load if File.exists?(config_file) return new( YAML.load_file(config_file) ) else return new end end # Saves data to the config file def save(config) File.open(config_file, 'w') do | f | YAML.dump(config.instance_eval { @cfg }, f) end end end # Initializes a Config object with the data +cfg+ def initialize(data = {}) @cfg = data @path = @cfg end # Returns the data at +key+, or nil if there is no such key def [](key) keypath(key).inject(@path) { | res, key | res[key] or break nil } end # Sets the data at path +key+, creating an intermediate paths on the # way def []=(key, val) path = keypath(key) last = path.pop final = path.inject(@path) { | res, key | res[key] or res[key] = {} } final[last] = val end # Specifies that all paths in subsequent operations should be # considered relative to +path+ def path=(path) if path == "/" @path = @cfg else pieces = path.split("/") pieces.pop if pieces.last.empty? # Whether starting at /root or current path if pieces.first.empty? start = @cfg pieces.shift else start = @path end pieces.map! { | k | k.intern } @path = pieces.inject(start) { | res, key | res[key] or res[key] = {} } end end private # Returns a list of symbols to be used to navigate to a value def keypath(key) case key when String pieces = key.split("/").reject { | k | k.empty? }.map { | k | k.intern } when Symbol pieces = [ key ] else Kernel.raise ArgumentError, "Bad key #{key}, should be String path or Symbol" end end end end