3 # A.k.a `Best web-album Of the world, Or your money back, Humerus'.
5 # The acronyn sucks, however this is a tribute to Dragon Ball by
6 # Akira Toriyama, where the last enemy beaten by heroes of Dragon
7 # Ball is named "Boo". But there was already a free software project
8 # called Boo, so this one will be it "Booh". Or whatever.
11 # Copyright (c) 2004 Guillaume Cottenceau <gc3 at bluewin.ch>
13 # This software may be freely redistributed under the terms of the GNU
14 # public license version 2.
16 # You should have received a copy of the GNU General Public License
17 # along with this program; if not, write to the Free Software
18 # Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
22 require 'booh/booh-lib'
27 "width:#{width} heigth:#{height}"
31 class Gtk::AutoTable < Gtk::VBox
33 attr_accessor :queue_draws
35 def initialize(row_spacings)
41 @row_spacings = row_spacings
45 signal_connect('size-allocate') { |w, allocation|
46 msg 3, "got self allocation: #{allocation}"
47 if @width != allocation.width
48 if !@old_widths.include?(allocation.width)
49 @width = allocation.width
50 @old_widths.unshift(@width)
51 @old_widths = @old_widths[0..2]
54 msg 3, "\tDISABLING: #{allocation.width} - #{@old_widths.join(',')}"
58 @timeout = Gtk.timeout_add(100) {
59 if @queue_draws.size > 0
60 @queue_draws.each { |elem| elem.call }
69 Gtk.timeout_remove(@timeout)
73 #- add (append) a widget to the list of automatically handled widgets
74 def append(widget, name)
75 #- create my child hash
76 child = { :widget => widget, :name => name }
77 #- put it in the table if last container's widget has been allocated
78 last_container = @containers[-1]
79 if !last_container || last_container[:contained_element] && last_container[:contained_element][:allocation]
80 put(child, (last_container ? last_container[:x] : -1) + 1, last_container ? last_container[:y] : 0)
82 #- add it to the internal children array
85 #- connect 'size-allocate' signal to be sure to update allocation when received
86 widget.signal_connect('size-allocate') { |w, allocation|
87 msg 3, "got allocation for #{w.hash}: #{allocation} (#{allocation.hash})"
88 chld = @children.find { |e| e[:widget] == w }
90 old = chld[:allocation]
91 #- need to copy values because the object will be magically exploded when widget is removed
92 chld[:allocation] = { :width => allocation.width, :height => allocation.height }
93 if !old #|| old[:width] != allocation.width # || old[:height] != allocation.height
94 msg 3, "redistribute!"
95 chld == @children[0] && old and msg 3, "************ old was #{old[:width]} #{old[:height]}"
99 warn "Critical: child not found!"
104 #- remove a widget from the list of automatically handled widgets
106 @children.each_with_index { |chld, index|
107 if chld[:widget] == widget
108 @children.delete_at(index)
116 #- re-insert a widget at a given pos
117 def reinsert(pos, widget, name)
118 child = { :widget => widget, :name => name }
119 @children[pos, 0] = child
123 #- remove all widgets
129 #- get current order of widget
131 return @children.collect { |chld| chld[:name] }
134 #- get current [x, y] position of widget within automatically handled table
135 def get_current_pos(widget)
136 chld = @children.find { |e| e[:widget] == widget }
138 return [ chld[:x], chld[:y] ]
144 #- get current number (rank in current ordering) of widget within automatically handled table
145 def get_current_number(widget)
146 @children.each_with_index { |chld, index|
147 if chld[:widget] == widget
154 #- move widgets by numbers
157 chld = @children.delete_at(src)
158 @children[dst > src ? dst - 1 : dst, 0] = chld
163 #- get widget at [x, y] position of automatically handled table
164 def get_widget_at_pos(x, y)
165 @children.each { |chld|
166 if chld[:x] == x && chld[:y] == y
173 #- get maximum `y' position within the automatically handled table
175 return @children[-1][:y]
178 #- get the current `previous' widget (table-wise); important since widgets can be reordered with mouse drags
179 def get_previous_widget(widget)
180 @children.each_with_index { |chld, index|
181 if chld[:widget] == widget
185 return @children[index - 1][:widget]
192 #- get the current `next' widget (table-wise); important since widgets can be reordered with mouse drags
193 def get_next_widget(widget)
194 @children.each_with_index { |chld, index|
195 if chld[:widget] == widget
196 if index == @children.size - 1
199 return @children[index + 1][:widget]
206 #- move specified widget `up' in the table
208 @children.each_with_index { |chld, index|
209 if chld[:widget] == widget && chld[:y] > 0
210 @children.each_with_index { |chld2, index2|
211 if chld2[:x] == chld[:x] && chld[:y] == chld2[:y] + 1
212 @children[index], @children[index2] = chld2, chld
222 #- move specified widget `down' in the table
223 def move_down(widget)
224 @children.each_with_index { |chld, index|
225 if chld[:widget] == widget && chld[:y] < get_max_y
226 @children.each_with_index { |chld2, index2|
227 if chld2[:x] == chld[:x] && chld[:y] == chld2[:y] - 1
228 @children[index], @children[index2] = chld2, chld
238 #- move specified widget `left' in the table
239 def move_left(widget)
240 @children.each_with_index { |chld, index|
241 if chld[:widget] == widget && chld[:x] > 0
242 @children[index], @children[index - 1] = @children[index - 1], @children[index]
250 #- move specified widget `right' in the table
251 def move_right(widget)
252 @children.each_with_index { |chld, index|
253 if chld[:widget] == widget && @children[index + 1] && chld[:x] < @children[index + 1][:x]
254 @children[index], @children[index + 1] = @children[index + 1], @children[index]
265 def put(element, x, y)
266 msg 3, "putting #{element[:widget].hash} at #{x},#{y}"
269 container = @containers.find { |e| e[:x] == x && e[:y] == y }
271 container = { :x => x, :y => y, :widget => Gtk::VBox.new, :fake => Gtk::Label.new('fake') }
272 msg 3, "attaching at #{x},#{y}"
273 @table.attach(container[:widget], x, x + 1, y, y + 1, Gtk::FILL, Gtk::FILL, 5, 0)
274 @containers << container
276 if container[:contained_element]
277 container[:widget].remove(container[:contained_element][:widget])
279 container[:contained_element] = element
280 container[:widget].add(element[:widget])
285 @containers.each { |e|
286 if e[:contained_element]
287 e[:widget].remove(e[:contained_element][:widget])
295 add(@table = Gtk::Table.new(0, 0, true))
296 @table.set_row_spacings(@row_spacings)
299 def redistribute(force)
300 msg 3, "redistribute: "
301 @children.each { |e| msg_ 3, e[:allocation] ? 'O' : '.' }; msg 3, ''
302 if unallocated = @children.find { |e| !e[:allocation] }
303 #- waiting for allocations. replace last displayed widget with first unallocated.
304 last_container = @containers[-1]
305 put(unallocated, last_container ? last_container[:x] : 0, last_container ? last_container[:y]: 0)
308 if @children.size == 0
313 totalwidth = allocation.width
314 maxwidth = @children.collect { |chld| chld[:allocation][:width] }.max
315 xpix = 5 + maxwidth + 5
318 @children[1..-1].each { |e|
319 if xpix + 5 + maxwidth + 5 > totalwidth - 1
327 xpix += 5 + maxwidth + 5
329 if @children[1..-1].find { |e| e[:xnew] != e[:x] || e[:ynew] != e[:y] } || force
330 msg 3, "I can proceed with #{allocation}"
332 put(@children[0], 0, 0)
333 @children[1..-1].each { |e|
334 put(e, e[:xnew], e[:ynew])
337 @queue_draws << proc { queue_draw }