1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208
|
# -*- coding: utf-8 -*-
require 'gtk2'
require_relative '../utils'
miquire :mui, 'extension'
miquire :mui, 'contextmenu'
# CRUDなリストビューを簡単に実現するためのクラス
class Gtk::CRUD < Gtk::TreeView
extend Memoist
attr_accessor :creatable, :updatable, :deletable, :dialog_title
type_register
def initialize
super()
initialize_model
@creatable = @updatable = @deletable = true
set_columns
# self.set_enable_search(true).set_search_column(1).set_search_equal_func{ |model, column, key, iter|
# not iter[column].include?(key) }
handle_release_event
handle_row_activated
end
def buttons(box_klass)
box_klass.new(false, 4).closeup(create_button).closeup(update_button).closeup(delete_button) end
def create_button
if not defined? @create_button
@create_button = Gtk::Button.new(Gtk::Stock::ADD)
@create_button.ssc(:clicked) {
record_create(nil, nil) } end
@create_button end
def update_button
if not defined? @update_button
@update_button = Gtk::Button.new(Gtk::Stock::EDIT)
@update_button.ssc(:clicked) {
record_update(nil, nil) } end
@update_button end
def delete_button
if not defined? @delete_button
@delete_button = Gtk::Button.new(Gtk::Stock::DELETE)
@delete_button.ssc(:clicked) {
record_delete(nil, nil) } end
@delete_button end
protected
def handle_row_activated
self.signal_connect("row-activated"){|view, path, column|
if @updatable and iter = view.model.get_iter(path)
if record = popup_input_window((0...model.n_columns).map{|i| iter[i] })
force_record_update(iter, record) end end }
end
def handle_release_event
self.signal_connect('button_release_event'){ |widget, event|
if (event.button == 3)
menu_pop(self, event)
true end }
end
def on_created(iter)
end
def on_updated(iter)
end
def on_deleted(iter)
end
private
def initialize_model
set_model(Gtk::ListStore.new(*column_schemer.flatten.map{|x| x[:type]}))
end
def set_columns
column_schemer.inject(0){ |index, scheme|
if scheme.is_a? Array
col = Gtk::TreeViewColumn.new(scheme.first[:label])
col.resizable = scheme.first[:resizable]
scheme.each{ |cell|
if cell[:kind]
cell_renderer = get_render_by(cell, index)
col.pack_start(cell_renderer, cell[:expand])
col.add_attribute(cell_renderer, cell[:kind], index) end
index += 1 }
append_column(col)
else
if(scheme[:label] and scheme[:kind])
col = Gtk::TreeViewColumn.new(scheme[:label], get_render_by(scheme, index), scheme[:kind] => index)
col.resizable = scheme[:resizable]
append_column(col) end
index += 1 end
index }
end
def get_render_by(scheme, index)
kind = scheme[:kind]
renderer = scheme[:renderer]
case
when renderer
if renderer.is_a?(Proc)
renderer.call(scheme, index)
else
renderer.new end
when kind == :text
Gtk::CellRendererText.new
when kind == :pixbuf
Gtk::CellRendererPixbuf.new
when kind == :active
toggled = Gtk::CellRendererToggle.new
toggled.signal_connect('toggled'){ |toggled, path|
iter = model.get_iter(path)
iter[index] = !iter[index]
on_updated(iter) }
toggled
end
end
def column_schemer
[{:kind => :active, :widget => :boolean, :type => TrueClass, :label => '表示'},
{:kind => :text, :widget => :input, :type => String, :label => '名前'},
{:type => Object, :widget => :message_picker},
].freeze
end
memoize :column_schemer
def force_record_create(record)
iter = model.model.append
record.each_with_index{ |item, index|
iter[index] = item }
on_created(iter) end
def force_record_update(iter, record)
if defined? model.convert_iter_to_child_iter(iter)
iter = model.convert_iter_to_child_iter iter end
record.each_with_index{ |item, index|
iter[index] = item }
on_updated(iter) end
def force_record_delete(iter)
if defined? model.convert_iter_to_child_iter(iter)
iter = model.convert_iter_to_child_iter iter end
on_deleted(iter)
model.model.remove(iter)
end
def record_create(optional, widget)
if @creatable
record = popup_input_window()
if record
force_record_create(record) end end end
def record_update(optional, widget)
if @updatable
self.selection.selected_each {|model, path, iter|
record = popup_input_window((0...model.n_columns).map{|i| iter[i] })
if record and not model.destroyed?
force_record_update(iter, record) end } end end
def record_delete(optional, widget)
if @deletable
self.selection.selected_each {|model, path, iter|
if Gtk::Dialog.confirm("本当に削除しますか?\n" +
"一度削除するともうもどってこないよ。")
force_record_delete(iter) end } end end
def menu_pop(widget, event)
if(@creatable or @updatable or @deletable)
contextmenu = Gtk::ContextMenu.new
contextmenu.register("新規作成", &method(:record_create)) if @creatable
contextmenu.register("編集", &method(:record_update)) if @updatable
contextmenu.register("削除", &method(:record_delete)) if @deletable
contextmenu.popup(widget, widget) end end
# 入力ウィンドウを表示する
def popup_input_window(defaults = [])
input = gen_popup_window_widget(defaults)
Mtk.scrolled_dialog(dialog_title || "", input[:widget], self.toplevel || self, &input[:result]) end
def gen_popup_window_widget(results = [])
widget = Gtk::VBox.new
column_schemer.flatten.each_with_index{ |scheme, index|
case scheme[:widget]
when :message_picker
widget.closeup(Mtk.message_picker(lambda{ |new|
if(new.nil?)
results[index].freeze_ifn
else
results[index] = new.freeze_ifn end }))
when nil
;
else
widget.closeup(Mtk.__send__((scheme[:widget] or :input), lambda{ |new|
if(new.nil?)
results[index].freeze_ifn
else
results[index] = new.freeze_ifn end },
scheme[:label], *(scheme[:args].to_a or []))) end }
{ :widget => widget,
:result => lambda{
results } } end
end
|