class Gantt

this file is part of manqod manqod is distributed under the CDDL licence the author of manqod is Dobai-Pataky Balint(dpblnt@gmail.com)

Constants

Connecting
Moving
None
Resolution

seconds/pixel,scale point distance, time_format, day0_format, name

Scrolling

Attributes

cursor_id[R]
day0[R]
dur_column[R]
gantt_holder[R]
group_color_column[R]
group_column[R]
max_coo[R]
max_time[R]
min_coo[R]
min_time[R]
partial_column[R]
percentage_column[R]
points[R]
rectangles[R]
res_idx[R]
scaler_format[R]
start_column[R]
start_editable[R]
successors_column[R]
successors_editable[R]
time_format[R]
widget[R]

Public Class Methods

new(gantt_holder) click to toggle source
Calls superclass method
# File lib/ListHolder/GanttHolder/Gantt.rb, line 25
        def initialize(gantt_holder)
                @gantt_holder=gantt_holder
                #hold start and end points here for which Scaler will need to draw header
                @points=Array.new
                @rectangles=Hash.new
                #time interval/pixel
                @min_time=nil
                @res_idx=(list.gtk_attribute("gantt_res_idx") || '2').to_i
                @day0=(list.gtk_attribute("gantt_day0") || 'false') == 'true'
                @scaler_format=if @day0 then list.gtk_attribute("gantt_day0_header_format") || "%d %b\n%H:%M"
                        else list.gtk_attribute("gantt_header_format") || "%d %b\n%H:%M" end
                @time_format=list.gtk_attribute("gantt_time_format") || "%H:%M"

                super(Gtk::Adjustment.new(0,0,0,0,0,0),list.holder.list_scroller.vadjustment)
                set_resize_mode(Gtk::RESIZE_PARENT)
                add(Gtk::EventBox.new.add(@widget=Gtk::Fixed.new))

                child.signal_connect("scroll-event"){|me,ev|
                        zoom_in if ev.direction == Gdk::EventScroll::UP && ev.state.control_mask?
                        zoom_out if ev.direction == Gdk::EventScroll::DOWN && ev.state.control_mask?
                }
                child.signal_connect("button-press-event"){|me,ev|
                        case ev.button
                                #right button dragging: editing successors/predecessors
                                when 3 then
                                        unless (@successors_column && successors_editable)
                                                ewarn("no editable successors_column specified?")
                                                else
                                                if @start=rectangle?(ev.x,ev.y)
                                                        #double
#                                                       if ev.event_type == Gdk::Event::BUTTON2_PRESS
#                                                               @start.set_new_successors(nil)
#                                                       else
                                                                @state = Connecting
                                                                window.set_cursor(Gdk::Cursor.new(Gdk::Cursor::CROSS))
#                                                       end
                                                end
                                        end
                                #left click: moving
                                when 1 then 
                                        @start=rectangle?(ev.x,ev.y)
                                        if start_editable && !@start.nil? && !@start.has_child?
                                                @state = Moving
                                                @move_shift=ev.x-@start.x1
                                                window.set_cursor(Gdk::Cursor.new(Gdk::Cursor::HAND2))
                                        end
                                #middle click: panning
                                when 2 then 
                                        @state = Scrolling
                                        @scroll_x=ev.x
                                        @scroll_y=ev.y
                                        window.set_cursor(Gdk::Cursor.new(Gdk::Cursor::HAND1))
                        end
                        clear
#                       gantt_holder.scaler.clear
                        false
                }
                child.signal_connect("button-release-event"){|me,ev|
                        case @state
                                when Connecting
                                        @motion=nil
                                        @end=rectangle?(ev.x,ev.y)
                                        @start.toggle_successor(@end)
                                when Moving
                                        #moving finished
                                        @start.move_to(hadjustment.value+self.pointer[0]-@move_shift,true)
                                        @motion=nil
                                when Scrolling
                        end
                        window.set_cursor(nil)
                        @state = None
                        clear
                        false
                }
                child.signal_connect("motion-notify-event"){|me,ev|
                        case @state
                                when Connecting then 
                                        @motion=[ev.x,ev.y]
                                        clear
                                when Moving then
                                        @start.move_to(hadjustment.value+self.pointer[0]-@move_shift)
                                        @motion=[ev.x,ev.y]
                                        clear
                                when Scrolling then
                                        px=@scroll_x - self.pointer[0]
                                        py=@scroll_y - self.pointer[1]
                                        px=hadjustment.lower if px < hadjustment.lower
                                        py=vadjustment.lower if py < hadjustment.lower
                                        px=hadjustment.upper - hadjustment.page_size if px > hadjustment.upper - hadjustment.page_size
                                        py=vadjustment.upper - vadjustment.page_size if py > vadjustment.upper - vadjustment.page_size
                                        hadjustment.value=px
                                        vadjustment.value=py
                                        clear
                        end
                }
                #set footer height
                list.holder.signal_connect("size-allocate"){|me,alloc|
                        h=list.holder.buttonholder.holder.allocation.height
                        h+=list.holder.list_panel.allocation.height if list.holder.list_panel.visibility
                        gantt_holder.footer.set_height_request(h) unless gantt_holder.footer.destroyed?
                        false
                }
                #set gantt height
                list.holder.list_scroller.vadjustment.signal_connect("changed"){|vad|
                        @widget.set_height_request(vad.upper) unless @widget.destroyed?
                        false
                }
                @widget.signal_connect("expose-event"){|me,ev|
                        cr=me.window.create_cairo_context.set_line_cap(Cairo::LineCap::ROUND)
                        @rectangles.each{|rid,r|
                                #draw rectangles, hilight the hovered on connecting
                                r.draw(cr,(@start == r) || (@end == r) || (@motion && r.in_area?(@motion[0],@motion[1])))
                        }
                        #draw arrows,verticals
                        @rectangles.each{|rid,r| 
                                r.draw_arrow(cr).draw_verticals(cr)
                                r.draw_hilight(cr) if @cursor_id == rid
                        }
                        #printing percentages
                        @rectangles.each{|rid,r| 
                                r.print_percentage(cr)
                        } if percentage_column
                        #draw connecting line
                        if @start && @state == Connecting && @motion
                                cr.set_line_width(3).set_source_rgba(148.0/255,88.0/255,116.0/255,0.5).set_dash(100000)
                                cr.move_to(@start.x,@start.y)
                                cr.line_to(@motion[0],@motion[1])
                                cr.stroke
                        end
                }
                signal_connect("destroy"){|me|
                        list.delete_observer(self)
                }
        end

Public Instance Methods

clear() click to toggle source
# File lib/ListHolder/GanttHolder/Gantt.rb, line 280
def clear
        @widget.queue_draw_area(0, 0, @widget.allocation.width, @widget.allocation.height)
end
collisions(pr) click to toggle source
# File lib/ListHolder/GanttHolder/Gantt.rb, line 265
def collisions(pr)
        cs=Array.new
        @rectangles.each_value{|r| 
                #by goup items' maximum
                if group_column && r != pr && r.group == pr.group
                        min=[pr.start,r.start].max
                        max=[pr.start+pr.dur,r.start+r.dur].min
                        cs.push([min,max]) if min<max
                end
        }
        cs
end
create_rectangle(iter,level=0) click to toggle source
# File lib/ListHolder/GanttHolder/Gantt.rb, line 214
def create_rectangle(iter,level=0)
        #crawl the tree recursively and create rectangles
        vi=!iter.nil?
        start=nil
        finish=nil
        height=nil
        percent=0
        dursum=0
        while vi
                r=Rectangle.new(self,iter)
                r.level=level
                start=r.start if start.nil? || start>r.start
                finish=r.finish if finish.nil? || finish<r.finish
                height=r.y1+r.height if height.nil? || height<r.height+r.y1
                dursum+=r.dur
                percent+=r.dur.to_f * r.percentage/100.0 #calculate the current complete percentage of r
                @rectangles[r.iter_id]=r
                if iter.has_child?
                        starts,finishs,percents,heights=create_rectangle(iter.first_child,level+1)
                        r.set_height(heights-r.y1) unless heights-r.y1 == r.height
                        height=r.y1+r.height if height.nil? || height<r.height+r.y1 #recalc height, since it changes by depth
                        r.set_start(starts,false) unless starts == r.start
                        r.set_duration(finishs-starts,false) unless finishs-starts == r.dur
                        r.set_percentage(percents,true) unless percents == r.percentage
                end
                vi=iter.next!
        end
        [start,finish,if dursum==0 then 0 else (100.0*percent/dursum).to_i end,height]
end
display_time(t) click to toggle source
# File lib/ListHolder/GanttHolder/Gantt.rb, line 306
def display_time(t)
        s=Time.at(t).strftime(@time_format)
end
list() click to toggle source
# File lib/ListHolder/GanttHolder/Gantt.rb, line 283
def list
        gantt_holder.list
end
min_for_rectangle(pr) click to toggle source
# File lib/ListHolder/GanttHolder/Gantt.rb, line 254
def min_for_rectangle(pr)
        min=0
        chmin=0
        @rectangles.each_value{|r|
                #by goup items' maximum
                min=r.finish if group_column && r.group == pr.group && r.start<pr.start && pr.start<r.finish
                #children's maxmimum
                r.successors.each{|sid,s| chmin=r.partial_finish if s == pr && r.partial_finish>chmin }
        }
        [min,chmin].max
end
model() click to toggle source
# File lib/ListHolder/GanttHolder/Gantt.rb, line 286
def model
        list.list_model.data
end
rectangle?(x,y) click to toggle source

check if this rectangle contains the point @ x,y

# File lib/ListHolder/GanttHolder/Gantt.rb, line 245
def rectangle?(x,y)
        found=nil
        @rectangles.each_value{|r| 
                if r.in_area?(x,y) && (found.nil? || found.level<r.level)
                        found=r
                end
        }
        found
end
rectangle_by_id?(rid) click to toggle source
# File lib/ListHolder/GanttHolder/Gantt.rb, line 277
def rectangle_by_id?(rid)
        @rectangles[rid]
end
res() click to toggle source
# File lib/ListHolder/GanttHolder/Gantt.rb, line 289
def res
        Resolution[res_idx][0]
end
res_name() click to toggle source
# File lib/ListHolder/GanttHolder/Gantt.rb, line 309
def res_name
        Resolution[res_idx][4]
end
scaler_step() click to toggle source
# File lib/ListHolder/GanttHolder/Gantt.rb, line 292
def scaler_step
        if @scaler_format.include?("%H") then 60*24
                elsif @scaler_format.include?("%d") then 60*60*24
                      elsif @scaler_format.include?("%W") then 60*60*24*7
                              else eerror("set gantt_header_format to a value which includes %W,%d or %H"); 60*24
                      end
end
time_round_res(t) click to toggle source
# File lib/ListHolder/GanttHolder/Gantt.rb, line 299
def time_round_res(t)
        Time.at(t.to_i).round(if @scaler_format.include?("%H") then "H"
                elsif @scaler_format.include?("%d") then "d"
                      elsif @scaler_format.include?("%W") then "W"
                              else "d"
                      end)
end
to_s() click to toggle source
# File lib/ListHolder/GanttHolder/Gantt.rb, line 326
def to_s
        "Gantt of #{list}"
end
update(notifier) click to toggle source
# File lib/ListHolder/GanttHolder/Gantt.rb, line 165
def update(notifier)
        if list.model && model && list.list_model.column_of_gantt_start && list.list_model.column_of_gantt_duration
                @start_column=list.list_model.column_of_gantt_start["model_col"].to_i if list.list_model.column_of_gantt_start
                @start_editable=list.list_model.column_of_gantt_start["editable"] if list.list_model.column_of_gantt_start
                @dur_column=list.list_model.column_of_gantt_duration["model_col"].to_i if list.list_model.column_of_gantt_duration
                @successors_column=list.list_model.column_of_gantt_successors["model_col"].to_i if list.list_model.column_of_gantt_successors
                @successors_editable=list.list_model.column_of_gantt_successors["editable"] if list.list_model.column_of_gantt_successors
                @group_column=list.list_model.column_of_gantt_group["model_col"].to_i if list.list_model.column_of_gantt_group
                @partial_column=list.list_model.column_of_gantt_partial["model_col"].to_i if list.list_model.column_of_gantt_partial
                @group_color_column=list.list_model.column_of_gantt_group_color["model_col"].to_i if list.list_model.column_of_gantt_group_color
                @percentage_column=list.list_model.column_of_gantt_percentage["model_col"].to_i if list.list_model.column_of_gantt_percentage
                #set up rectangles
                @rectangles.clear
                create_rectangle(model.iter_first)
                #visible iters
                @min_time=@max_time=nil
                @rectangles.each_value{|r|
                        @min_time=r.start if @min_time.nil? || @min_time>r.start
                        @max_time=r.finish if @max_time.nil? || @max_time<r.finish
                }

                @min_time=0 if @min_time.nil?
                @max_time=0 if @max_time.nil?
                #extend min_time and max_time to fit resolution
                @min_time=time_round_res(@min_time).to_i
                @max_time=time_round_res(@max_time + scaler_step).to_i
                @points.clear
                @min_coo=if day0 then 0 else @min_time/res||0 end
                @max_coo=@max_time/res||0
                #set our maximum width
                @widget.set_width_request(@max_coo-@min_coo)
                @cursor_id=list.get_cursor_id
                @rectangles.each_pair{|rid,r|
                        r.set_gantt_min_x(@min_coo)
                        #center on selected
                        hadjustment.clamp_page(r.x1-hadjustment.page_size/3,r.x2+hadjustment.page_size/3) if @cursor_id == rid
                        #set up successors
                        r.init_successors if successors_column
                        #set up scaler points
                        time_round_res(r.start).to_i.step(time_round_res(r.finish+scaler_step).to_i,scaler_step){|i| @points.push(time_round_res(i).to_i)}}
                @points=@points.uniq.sort!

                #redraw
                gantt_holder.scaler.clear
                show_all
                clear
        end
end
zoom_in() click to toggle source
# File lib/ListHolder/GanttHolder/Gantt.rb, line 312
def zoom_in
        unless @res_idx==1
                @res_idx-=1 
                gantt_holder.footer.set_res_label
                update(self)
        end
end
zoom_out() click to toggle source
# File lib/ListHolder/GanttHolder/Gantt.rb, line 319
def zoom_out
        unless @res_idx==Resolution.size
                @res_idx+=1 
                gantt_holder.footer.set_res_label
                update(self)
        end
end