3 require 'booh/booh-lib'
6 class Gtk::AutoTable < Gtk::VBox
8 def initialize(row_spacings)
13 @row_spacings = row_spacings
17 signal_connect('size-allocate') { |w, allocation|
18 msg 3, "got self allocation: #{allocation}"
19 if @width != allocation.width
20 if !@old_widths.include?(allocation.width)
21 @width = allocation.width
22 @old_widths.unshift(@width)
23 @old_widths = @old_widths[0..2]
26 msg 3, "\tDISABLING: #{allocation.width} - #{@old_widths.join(',')}"
30 @timeout = Gtk.timeout_add(100) {
41 Gtk.timeout_remove(@timeout)
45 #- add (append) a widget to the list of automatically handled widgets
46 def append(widget, name)
47 #- create my child hash
48 child = { :widget => widget, :name => name }
49 #- put it in the table if last container's widget has been allocated
50 last_container = @containers[-1]
51 if !last_container || last_container[:contained_element] && last_container[:contained_element][:allocation]
52 put(child, (last_container ? last_container[:x] : -1) + 1, last_container ? last_container[:y] : 0)
54 #- add it to the internal children array
57 #- connect 'size-allocate' signal to be sure to update allocation when received
58 widget.signal_connect('size-allocate') { |w, allocation|
59 msg 3, "got allocation for #{w.hash}: #{allocation} (#{allocation.hash})"
60 chld = @children.find { |e| e[:widget] == w }
62 old = chld[:allocation]
63 #- need to copy values because the object will be magically exploded when widget is removed
64 chld[:allocation] = { :width => allocation.width, :height => allocation.height }
65 if !old #|| old[:width] != allocation.width # || old[:height] != allocation.height
66 msg 3, "redistribute!"
67 chld == @children[0] && old and msg 3, "************ old was #{old[:width]} #{old[:height]}"
71 warn "Critical: child not found!"
75 #- handle reordering with drag and drop
76 Gtk::Drag.source_set(widget, Gdk::Window::BUTTON1_MASK, [['reorder-elements', Gtk::Drag::TARGET_SAME_APP, 1]], Gdk::DragContext::ACTION_MOVE)
77 Gtk::Drag.dest_set(widget, Gtk::Drag::DEST_DEFAULT_ALL, [['reorder-elements', Gtk::Drag::TARGET_SAME_APP, 1]], Gdk::DragContext::ACTION_MOVE)
78 widget.signal_connect('drag-data-get') { |w, ctxt, selection_data, info, time|
79 selection_data.set(Gdk::Selection::TYPE_STRING, get_current_number(widget).to_s)
81 widget.signal_connect('drag-data-received') { |w, ctxt, x, y, selection_data, info, time|
82 ctxt.targets.each { |target|
83 if target.name == 'reorder-elements'
84 insert(selection_data.data.to_i, get_current_number(widget))
90 #- remove a widget from the list of automatically handled widgets
92 @children.each_with_index { |chld, index|
93 if chld[:widget] == widget
94 @children.delete_at(index)
102 #- remove all widgets
108 #- get current order of widget
110 return @children.collect { |chld| chld[:name] }
113 #- get current [x, y] position of widget within automatically handled table
114 def get_current_pos(widget)
115 chld = @children.find { |e| e[:widget] == widget }
117 return [ chld[:x], chld[:y] ]
123 #- get current number (rank in current ordering) of widget within automatically handled table
124 def get_current_number(widget)
125 @children.each_with_index { |chld, index|
126 if chld[:widget] == widget
133 #- insert a widget before another by numbers
135 chld = @children.delete_at(src)
136 @children[dst > src ? dst - 1 : dst, 0] = chld
140 #- get widget at [x, y] position of automatically handled table
141 def get_widget_at_pos(x, y)
142 @children.each { |chld|
143 if chld[:x] == x && chld[:y] == y
150 #- get maximum `y' position within the automatically handled table
152 return @children[-1][:y]
155 #- get the current `previous' widget (table-wise); important since widgets can be reordered with mouse drags
156 def get_previous_widget(widget)
157 @children.each_with_index { |chld, index|
158 if chld[:widget] == widget
162 return @children[index - 1][:widget]
169 #- get the current `next' widget (table-wise); important since widgets can be reordered with mouse drags
170 def get_next_widget(widget)
171 @children.each_with_index { |chld, index|
172 if chld[:widget] == widget
173 if index == @children.size - 1
176 return @children[index + 1][:widget]
183 #- move specified widget `up' in the table
185 @children.each_with_index { |chld, index|
186 if chld[:widget] == widget && chld[:y] > 0
187 @children.each_with_index { |chld2, index2|
188 if chld2[:x] == chld[:x] && chld[:y] == chld2[:y] + 1
189 @children[index], @children[index2] = chld2, chld
199 #- move specified widget `down' in the table
200 def move_down(widget)
201 @children.each_with_index { |chld, index|
202 if chld[:widget] == widget && chld[:y] < get_max_y
203 @children.each_with_index { |chld2, index2|
204 if chld2[:x] == chld[:x] && chld[:y] == chld2[:y] - 1
205 @children[index], @children[index2] = chld2, chld
215 #- move specified widget `left' in the table
216 def move_left(widget)
217 @children.each_with_index { |chld, index|
218 if chld[:widget] == widget && chld[:x] > 0
219 @children[index], @children[index - 1] = @children[index - 1], @children[index]
227 #- move specified widget `right' in the table
228 def move_right(widget)
229 @children.each_with_index { |chld, index|
230 if chld[:widget] == widget && @children[index + 1] && chld[:x] < @children[index + 1][:x]
231 @children[index], @children[index + 1] = @children[index + 1], @children[index]
242 def put(element, x, y)
243 msg 3, "putting #{element[:widget].hash} at #{x},#{y}"
246 container = @containers.find { |e| e[:x] == x && e[:y] == y }
248 container = { :x => x, :y => y, :widget => Gtk::VBox.new, :fake => Gtk::Label.new('fake') }
249 msg 3, "attaching at #{x},#{y}"
250 @table.attach(container[:widget], x, x + 1, y, y + 1, Gtk::FILL, Gtk::FILL, 5, 0)
251 @containers << container
253 if container[:contained_element]
254 container[:widget].remove(container[:contained_element][:widget])
256 container[:contained_element] = element
257 container[:widget].add(element[:widget])
262 @containers.each { |e|
263 if e[:contained_element]
264 e[:widget].remove(e[:contained_element][:widget])
272 add(@table = Gtk::Table.new(0, 0, true))
273 @table.set_row_spacings(@row_spacings)
276 def redistribute(force)
277 msg 3, "redistribute: "
278 @children.each { |e| msg_ 3, e[:allocation] ? 'O' : '.' }; msg 3, ''
279 if unallocated = @children.find { |e| !e[:allocation] }
280 #- waiting for allocations. replace last displayed widget with first unallocated.
281 last_container = @containers[-1]
282 put(unallocated, last_container[:x], last_container[:y])
285 if @children.size == 0
290 maxwidth = allocation.width
291 xpix = 5 + @children[0][:allocation][:width] + 5
294 @children[1..-1].each { |e|
295 if xpix + 5 + e[:allocation][:width] + 5 > maxwidth - 1
303 xpix += 5 + e[:allocation][:width] + 5
305 if @children[1..-1].find { |e| e[:xnew] != e[:x] || e[:ynew] != e[:y] } || force
306 msg 3, "I can proceed with #{allocation}"
308 put(@children[0], 0, 0)
309 @children[1..-1].each { |e|
310 put(e, e[:xnew], e[:ynew])