# HG changeset patch # User Toshi MARUYAMA # Date 1354592875 -32400 # Node ID 287b115293ceb1e41388c34e5af97fb23a0cafa5 # Parent 4a143f7a4e6a9f589891f5acdbb13cb7222fc8a4 gantt: (#3436, #12122) diff --git a/app/views/gantts/show.html.erb b/app/views/gantts/show.html.erb --- a/app/views/gantts/show.html.erb +++ b/app/views/gantts/show.html.erb @@ -1,3 +1,11 @@ +<% content_for :header_tags do %> + <%= javascript_include_tag 'raphael' %> + <%= javascript_include_tag 'gantt' %> +<% end %> +<%= javascript_tag do %> + $(document).ready(drawGanttHandler); + $(window).resize(drawGanttHandler); +<% end %> <% @gantt.view = self %>
<%= gantt_zoom_link(@gantt, :in) %> diff --git a/lib/redmine/helpers/gantt.rb b/lib/redmine/helpers/gantt.rb --- a/lib/redmine/helpers/gantt.rb +++ b/lib/redmine/helpers/gantt.rb @@ -705,6 +705,15 @@ module Redmine params[:image].text(params[:indent], params[:top] + 2, subject) end + def issue_relations(issue) + relations = {} + issue.relations_to.each do |relation| + relation_type = relation.relation_type_for(relation.issue_to) + (relations[relation_type] ||= []) << relation.issue_from_id + end + relations + end + def html_task(params, coords, options={}) output = '' # Renders the task bar, with progress and late @@ -714,9 +723,17 @@ module Redmine style << "top:#{params[:top]}px;" style << "left:#{coords[:bar_start]}px;" style << "width:#{width}px;" - output << view.content_tag(:div, ' '.html_safe, - :style => style, - :class => "#{options[:css]} task_todo") + html_id = "task-todo-issue-#{options[:issue].id}" if options[:issue] + content_opt = {:style => style, + :class => "#{options[:css]} task_todo", + :id => html_id} + if options[:issue] + relations = issue_relations(options[:issue]) + relations.each do |k, v| + content_opt[:"data-rel-#{k}"] = v.join(',') + end + end + output << view.content_tag(:div, ' '.html_safe, content_opt) if coords[:bar_late_end] width = coords[:bar_late_end] - coords[:bar_start] - 2 style = "" diff --git a/public/javascripts/gantt.js b/public/javascripts/gantt.js --- a/public/javascripts/gantt.js +++ b/public/javascripts/gantt.js @@ -3,12 +3,125 @@ var draw_top; var draw_right; var draw_left; +var draw_relations = { + "relates": { + "landscape_mergin": 8, + "no_arrow": true, + }, + "duplicates": { + "landscape_mergin": 12, + }, + "blocks": { + "landscape_mergin": 16, + }, + "precedes": { + "landscape_mergin": 20, + }, + "copied_to": { + "landscape_mergin": 24, + }, +}; + function setDrawArea() { draw_top = $("#gantt_draw_area").position().top; draw_right = $("#gantt_draw_area").width(); draw_left = $("#gantt_area").scrollLeft(); } +function getRelationsArray() { + var arr = new Array(); + $.each($('div.task_todo'), function(index_div, element) { + var element_id = $(element).attr("id"); + if (element_id != null) { + var issue_id = element_id.replace("task-todo-issue-", ""); + for(rel_type_key in issue_relation_type) { + var data_rel_attr = $(element).attr("data-rel-" + rel_type_key); + if (data_rel_attr != null) { + var issue_arr = data_rel_attr.split(","); + if ("reverse" in issue_relation_type[rel_type_key]) { + $.each(issue_arr, function(index_issue, element_issue) { + arr.push({issue_from: element_issue, issue_to: issue_id, + rel_type: issue_relation_type[rel_type_key]["reverse"]}); + }); + } else { + $.each(issue_arr, function(index_issue, element_issue) { + arr.push({issue_from: issue_id, issue_to: element_issue, + rel_type: rel_type_key}); + }); + } + } + } + } + }); + return arr; +} + +function drawRelations() { + var arr = getRelationsArray(); + $.each(arr, function(index_issue, element_issue) { + if (!$("#draw_" + element_issue["rel_type"]).attr('checked')) { + return; + } + var issue_from = $("#task-todo-issue-" + element_issue["issue_from"]); + var issue_to = $("#task-todo-issue-" + element_issue["issue_to"]); + var issue_height = issue_from.height(); + var issue_from_top = issue_from.position().top + (issue_height / 2) - draw_top; + var issue_from_right = issue_from.position().left + issue_from.width(); + var issue_to_top = issue_to.position().top + (issue_height / 2) - draw_top; + var issue_to_left = issue_to.position().left; + var color = $("#gantt_draw_rel_color_" + element_issue["rel_type"]) + .css("background-color"); + var landscape_mergin = draw_relations[element_issue["rel_type"]]["landscape_mergin"]; + var issue_from_right_rel = issue_from_right + landscape_mergin; + var issue_to_left_rel = issue_to_left - landscape_mergin; + var x1, y1, x2, y2; + draw_gantt.path(["M", issue_from_right + draw_left, issue_from_top, + "L", issue_from_right_rel + draw_left, issue_from_top]) + .attr({stroke: color, "stroke-width": 2}); + if (issue_from_right_rel < issue_to_left_rel) { + draw_gantt.path(["M", issue_from_right_rel + draw_left, issue_from_top, + "L", issue_from_right_rel + draw_left, issue_to_top]) + .attr({stroke: color, "stroke-width": 2}); + draw_gantt.path(["M", issue_from_right_rel + draw_left, issue_to_top, + "L", issue_to_left + draw_left, issue_to_top]) + .attr({stroke: color, + "stroke-width": 2, + "stroke-linecap": "butt", + "stroke-linejoin": "miter", + }); + } else { + var issue_middle_top = issue_to_top + + (issue_height * + ((issue_from_top > issue_to_top) ? 1 : -1 )); + draw_gantt.path(["M", issue_from_right_rel + draw_left, issue_from_top, + "L", issue_from_right_rel + draw_left, issue_middle_top]) + .attr({stroke: color, "stroke-width": 2}); + draw_gantt.path(["M", issue_from_right_rel + draw_left, issue_middle_top, + "L", issue_to_left_rel + draw_left, issue_middle_top]) + .attr({stroke:color, "stroke-width": 2}); + draw_gantt.path(["M", issue_to_left_rel + draw_left, issue_middle_top, + "L", issue_to_left_rel + draw_left, issue_to_top]) + .attr({stroke: color, "stroke-width": 2}); + draw_gantt.path(["M", issue_to_left_rel + draw_left, issue_to_top, + "L", issue_to_left + draw_left, issue_to_top]) + .attr({stroke: color, + "stroke-width": 2, + "stroke-linecap": "butt", + "stroke-linejoin": "miter", + }); + } + if (!("no_arrow" in draw_relations[element_issue["rel_type"]])) { + draw_gantt.path(["M", issue_to_left + draw_left, issue_to_top, + "l", -8, -4, "l", 0, 8, "z"]) + .attr({stroke: "none", + fill: color, + "stroke-linecap": "butt", + "stroke-linejoin": "miter", + }); + } + }); +} + function drawGanttHandler() { var folder = document.getElementById('gantt_draw_area'); if(draw_gantt != null) @@ -16,4 +129,5 @@ function drawGanttHandler() { else draw_gantt = Raphael(folder); setDrawArea(); + drawRelations(); } diff --git a/public/stylesheets/application.css b/public/stylesheets/application.css --- a/public/stylesheets/application.css +++ b/public/stylesheets/application.css @@ -868,6 +868,12 @@ a.close-icon { a.close-icon:hover {background-image:url('../images/close_hl.png');} /***** Gantt chart *****/ +#gantt_draw_rel_color_relates {background-color:#00c551;} +#gantt_draw_rel_color_duplicates {background-color:#4f52c3;} +#gantt_draw_rel_color_blocks {background-color:#fb7d2f;} +#gantt_draw_rel_color_precedes {background-color:#df347c;} +#gantt_draw_rel_color_copied_to {background-color:#00c5c2;} + .gantt_hdr { position:absolute; top:0;