Gantt progress lines (#12122).
Patch by Toshi MARUYAMA. git-svn-id: svn+ssh://rubyforge.org/var/svn/redmine/trunk@11210 e93f8b46-1217-0410-a6f0-8f06a7374b81
This commit is contained in:
parent
30801ca968
commit
237f297f03
|
@ -12,6 +12,39 @@
|
||||||
<%= render :partial => 'queries/filters', :locals => {:query => @query} %>
|
<%= render :partial => 'queries/filters', :locals => {:query => @query} %>
|
||||||
</div>
|
</div>
|
||||||
</fieldset>
|
</fieldset>
|
||||||
|
<fieldset id="filters" class="collapsible">
|
||||||
|
<legend onclick="toggleFieldset(this);"><%= l(:label_display) %></legend>
|
||||||
|
<div>
|
||||||
|
<table>
|
||||||
|
<tr>
|
||||||
|
<td>
|
||||||
|
<fieldset>
|
||||||
|
<legend><%= l(:label_related_issues) %></legend>
|
||||||
|
<label>
|
||||||
|
<%= check_box_tag "draw_rels", params["draw_rels"], true %>
|
||||||
|
<% rels = [IssueRelation::TYPE_BLOCKS, IssueRelation::TYPE_PRECEDES] %>
|
||||||
|
<% rels.each do |rel| %>
|
||||||
|
<% color = Redmine::Helpers::Gantt::DRAW_TYPES[rel][:color] %>
|
||||||
|
<%= content_tag(:span, ' '.html_safe,
|
||||||
|
:style => "background-color: #{color}") %>
|
||||||
|
<%= l(IssueRelation::TYPES[rel][:name]) %>
|
||||||
|
<% end %>
|
||||||
|
</label>
|
||||||
|
</fieldset>
|
||||||
|
</td>
|
||||||
|
<td>
|
||||||
|
<fieldset>
|
||||||
|
<legend><%= l(:label_gantt_progress_line) %></legend>
|
||||||
|
<label>
|
||||||
|
<%= check_box_tag "draw_progress_line", params[:draw_progress_line], false %>
|
||||||
|
<%= l(:label_display) %>
|
||||||
|
</label>
|
||||||
|
</fieldset>
|
||||||
|
</td>
|
||||||
|
</tr>
|
||||||
|
</table>
|
||||||
|
</div>
|
||||||
|
</fieldset>
|
||||||
|
|
||||||
<p class="contextual">
|
<p class="contextual">
|
||||||
<%= gantt_zoom_link(@gantt, :in) %>
|
<%= gantt_zoom_link(@gantt, :in) %>
|
||||||
|
@ -229,7 +262,7 @@
|
||||||
style += "width:10px;"
|
style += "width:10px;"
|
||||||
style += "border-left: 1px dashed red;"
|
style += "border-left: 1px dashed red;"
|
||||||
%>
|
%>
|
||||||
<%= content_tag(:div, ' '.html_safe, :style => style) %>
|
<%= content_tag(:div, ' '.html_safe, :style => style, :id => 'today_line') %>
|
||||||
<% end %>
|
<% end %>
|
||||||
<%
|
<%
|
||||||
style = ""
|
style = ""
|
||||||
|
@ -279,4 +312,8 @@
|
||||||
var issue_relation_type = <%= raw Redmine::Helpers::Gantt::DRAW_TYPES.to_json %>;
|
var issue_relation_type = <%= raw Redmine::Helpers::Gantt::DRAW_TYPES.to_json %>;
|
||||||
$(document).ready(drawGanttHandler);
|
$(document).ready(drawGanttHandler);
|
||||||
$(window).resize(drawGanttHandler);
|
$(window).resize(drawGanttHandler);
|
||||||
|
$(function() {
|
||||||
|
$("#draw_rels").change(drawGanttHandler);
|
||||||
|
$("#draw_progress_line").change(drawGanttHandler);
|
||||||
|
});
|
||||||
<% end %>
|
<% end %>
|
||||||
|
|
|
@ -886,6 +886,7 @@ en:
|
||||||
label_cross_project_tree: With project tree
|
label_cross_project_tree: With project tree
|
||||||
label_cross_project_hierarchy: With project hierarchy
|
label_cross_project_hierarchy: With project hierarchy
|
||||||
label_cross_project_system: With all projects
|
label_cross_project_system: With all projects
|
||||||
|
label_gantt_progress_line: Progress line
|
||||||
|
|
||||||
button_login: Login
|
button_login: Login
|
||||||
button_submit: Submit
|
button_submit: Submit
|
||||||
|
|
|
@ -833,6 +833,7 @@ ja:
|
||||||
label_git_report_last_commit: ファイルとディレクトリの最新コミットを表示する
|
label_git_report_last_commit: ファイルとディレクトリの最新コミットを表示する
|
||||||
label_parent_revision: 親
|
label_parent_revision: 親
|
||||||
label_child_revision: 子
|
label_child_revision: 子
|
||||||
|
label_gantt_progress_line: イナズマ線
|
||||||
|
|
||||||
button_login: ログイン
|
button_login: ログイン
|
||||||
button_submit: 送信
|
button_submit: 送信
|
||||||
|
|
|
@ -308,10 +308,18 @@ module Redmine
|
||||||
html_class << 'icon icon-package '
|
html_class << 'icon icon-package '
|
||||||
html_class << (version.behind_schedule? ? 'version-behind-schedule' : '') << " "
|
html_class << (version.behind_schedule? ? 'version-behind-schedule' : '') << " "
|
||||||
html_class << (version.overdue? ? 'version-overdue' : '')
|
html_class << (version.overdue? ? 'version-overdue' : '')
|
||||||
|
html_class << ' version-closed' unless version.open?
|
||||||
|
if version.start_date && version.due_date && version.completed_pourcent
|
||||||
|
progress_date = calc_progress_date(version.start_date,
|
||||||
|
version.due_date, version.completed_pourcent)
|
||||||
|
html_class << ' behind-start-date' if progress_date < self.date_from
|
||||||
|
html_class << ' over-end-date' if progress_date > self.date_to
|
||||||
|
end
|
||||||
s = view.link_to_version(version).html_safe
|
s = view.link_to_version(version).html_safe
|
||||||
subject = view.content_tag(:span, s,
|
subject = view.content_tag(:span, s,
|
||||||
:class => html_class).html_safe
|
:class => html_class).html_safe
|
||||||
html_subject(options, subject, :css => "version-name")
|
html_subject(options, subject, :css => "version-name",
|
||||||
|
:id => "version-#{version.id}")
|
||||||
when :image
|
when :image
|
||||||
image_subject(options, version.to_s_with_project)
|
image_subject(options, version.to_s_with_project)
|
||||||
when :pdf
|
when :pdf
|
||||||
|
@ -332,7 +340,8 @@ module Redmine
|
||||||
label = h("#{version.project} -") + label unless @project && @project == version.project
|
label = h("#{version.project} -") + label unless @project && @project == version.project
|
||||||
case options[:format]
|
case options[:format]
|
||||||
when :html
|
when :html
|
||||||
html_task(options, coords, :css => "version task", :label => label, :markers => true)
|
html_task(options, coords, :css => "version task",
|
||||||
|
:label => label, :markers => true, :version => version)
|
||||||
when :image
|
when :image
|
||||||
image_task(options, coords, :label => label, :markers => true, :height => 3)
|
image_task(options, coords, :label => label, :markers => true, :height => 3)
|
||||||
when :pdf
|
when :pdf
|
||||||
|
@ -354,6 +363,13 @@ module Redmine
|
||||||
css_classes << ' issue-overdue' if issue.overdue?
|
css_classes << ' issue-overdue' if issue.overdue?
|
||||||
css_classes << ' issue-behind-schedule' if issue.behind_schedule?
|
css_classes << ' issue-behind-schedule' if issue.behind_schedule?
|
||||||
css_classes << ' icon icon-issue' unless Setting.gravatar_enabled? && issue.assigned_to
|
css_classes << ' icon icon-issue' unless Setting.gravatar_enabled? && issue.assigned_to
|
||||||
|
css_classes << ' issue-closed' if issue.closed?
|
||||||
|
if issue.start_date && issue.due_before && issue.done_ratio
|
||||||
|
progress_date = calc_progress_date(issue.start_date,
|
||||||
|
issue.due_before, issue.done_ratio)
|
||||||
|
css_classes << ' behind-start-date' if progress_date < self.date_from
|
||||||
|
css_classes << ' over-end-date' if progress_date > self.date_to
|
||||||
|
end
|
||||||
s = "".html_safe
|
s = "".html_safe
|
||||||
if issue.assigned_to.present?
|
if issue.assigned_to.present?
|
||||||
assigned_string = l(:field_assigned_to) + ": " + issue.assigned_to.name
|
assigned_string = l(:field_assigned_to) + ": " + issue.assigned_to.name
|
||||||
|
@ -365,7 +381,7 @@ module Redmine
|
||||||
s << view.link_to_issue(issue).html_safe
|
s << view.link_to_issue(issue).html_safe
|
||||||
subject = view.content_tag(:span, s, :class => css_classes).html_safe
|
subject = view.content_tag(:span, s, :class => css_classes).html_safe
|
||||||
html_subject(options, subject, :css => "issue-subject",
|
html_subject(options, subject, :css => "issue-subject",
|
||||||
:title => issue.subject) + "\n"
|
:title => issue.subject, :id => "issue-#{issue.id}") + "\n"
|
||||||
when :image
|
when :image
|
||||||
image_subject(options, issue.subject)
|
image_subject(options, issue.subject)
|
||||||
when :pdf
|
when :pdf
|
||||||
|
@ -628,7 +644,7 @@ module Redmine
|
||||||
coords[:bar_end] = self.date_to - self.date_from + 1
|
coords[:bar_end] = self.date_to - self.date_from + 1
|
||||||
end
|
end
|
||||||
if progress
|
if progress
|
||||||
progress_date = start_date + (end_date - start_date + 1) * (progress / 100.0)
|
progress_date = calc_progress_date(start_date, end_date, progress)
|
||||||
if progress_date > self.date_from && progress_date > start_date
|
if progress_date > self.date_from && progress_date > start_date
|
||||||
if progress_date < self.date_to
|
if progress_date < self.date_to
|
||||||
coords[:bar_progress_end] = progress_date - self.date_from
|
coords[:bar_progress_end] = progress_date - self.date_from
|
||||||
|
@ -655,6 +671,10 @@ module Redmine
|
||||||
coords
|
coords
|
||||||
end
|
end
|
||||||
|
|
||||||
|
def calc_progress_date(start_date, end_date, progress)
|
||||||
|
start_date + (end_date - start_date + 1) * (progress / 100.0)
|
||||||
|
end
|
||||||
|
|
||||||
# Sorts a collection of issues by start_date, due_date, id for gantt rendering
|
# Sorts a collection of issues by start_date, due_date, id for gantt rendering
|
||||||
def sort_issues!(issues)
|
def sort_issues!(issues)
|
||||||
issues.sort! { |a, b| gantt_issue_compare(a, b) }
|
issues.sort! { |a, b| gantt_issue_compare(a, b) }
|
||||||
|
@ -695,9 +715,10 @@ module Redmine
|
||||||
def html_subject(params, subject, options={})
|
def html_subject(params, subject, options={})
|
||||||
style = "position: absolute;top:#{params[:top]}px;left:#{params[:indent]}px;"
|
style = "position: absolute;top:#{params[:top]}px;left:#{params[:indent]}px;"
|
||||||
style << "width:#{params[:subject_width] - params[:indent]}px;" if params[:subject_width]
|
style << "width:#{params[:subject_width] - params[:indent]}px;" if params[:subject_width]
|
||||||
output = view.content_tag('div', subject,
|
output = view.content_tag(:div, subject,
|
||||||
:class => options[:css], :style => style,
|
:class => options[:css], :style => style,
|
||||||
:title => options[:title])
|
:title => options[:title],
|
||||||
|
:id => options[:id])
|
||||||
@subjects << output
|
@subjects << output
|
||||||
output
|
output
|
||||||
end
|
end
|
||||||
|
@ -742,6 +763,7 @@ module Redmine
|
||||||
style << "left:#{coords[:bar_start]}px;"
|
style << "left:#{coords[:bar_start]}px;"
|
||||||
style << "width:#{width}px;"
|
style << "width:#{width}px;"
|
||||||
html_id = "task-todo-issue-#{options[:issue].id}" if options[:issue]
|
html_id = "task-todo-issue-#{options[:issue].id}" if options[:issue]
|
||||||
|
html_id = "task-todo-version-#{options[:version].id}" if options[:version]
|
||||||
content_opt = {:style => style,
|
content_opt = {:style => style,
|
||||||
:class => "#{options[:css]} task_todo",
|
:class => "#{options[:css]} task_todo",
|
||||||
:id => html_id}
|
:id => html_id}
|
||||||
|
@ -768,9 +790,12 @@ module Redmine
|
||||||
style << "top:#{params[:top]}px;"
|
style << "top:#{params[:top]}px;"
|
||||||
style << "left:#{coords[:bar_start]}px;"
|
style << "left:#{coords[:bar_start]}px;"
|
||||||
style << "width:#{width}px;"
|
style << "width:#{width}px;"
|
||||||
|
html_id = "task-done-issue-#{options[:issue].id}" if options[:issue]
|
||||||
|
html_id = "task-done-version-#{options[:version].id}" if options[:version]
|
||||||
output << view.content_tag(:div, ' '.html_safe,
|
output << view.content_tag(:div, ' '.html_safe,
|
||||||
:style => style,
|
:style => style,
|
||||||
:class => "#{options[:css]} task_done")
|
:class => "#{options[:css]} task_done",
|
||||||
|
:id => html_id)
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
# Renders the markers
|
# Renders the markers
|
||||||
|
|
|
@ -98,6 +98,66 @@ function drawRelations() {
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
function getProgressLinesArray() {
|
||||||
|
var arr = new Array();
|
||||||
|
var today_left = $('#today_line').position().left;
|
||||||
|
arr.push({left: today_left, top: 0});
|
||||||
|
$.each($('div.issue-subject, div.version-name'), function(index, element) {
|
||||||
|
var t = $(element).position().top - draw_top ;
|
||||||
|
var h = ($(element).height() / 9);
|
||||||
|
var element_top_upper = t - h;
|
||||||
|
var element_top_center = t + (h * 3);
|
||||||
|
var element_top_lower = t + (h * 8);
|
||||||
|
var issue_closed = $(element).children('span').hasClass('issue-closed');
|
||||||
|
var version_closed = $(element).children('span').hasClass('version-closed');
|
||||||
|
if (issue_closed || version_closed) {
|
||||||
|
arr.push({left: today_left, top: element_top_center});
|
||||||
|
} else {
|
||||||
|
var issue_done = $("#task-done-" + $(element).attr("id"));
|
||||||
|
var is_behind_start = $(element).children('span').hasClass('behind-start-date');
|
||||||
|
var is_over_end = $(element).children('span').hasClass('over-end-date');
|
||||||
|
if (is_over_end) {
|
||||||
|
arr.push({left: draw_right, top: element_top_upper, is_right_edge: true});
|
||||||
|
arr.push({left: draw_right, top: element_top_lower, is_right_edge: true, none_stroke: true});
|
||||||
|
} else if (issue_done.size() > 0) {
|
||||||
|
var done_left = issue_done.first().position().left +
|
||||||
|
issue_done.first().width();
|
||||||
|
arr.push({left: done_left, top: element_top_center});
|
||||||
|
} else if (is_behind_start) {
|
||||||
|
arr.push({left: 0 , top: element_top_upper, is_left_edge: true});
|
||||||
|
arr.push({left: 0 , top: element_top_lower, is_left_edge: true, none_stroke: true});
|
||||||
|
} else {
|
||||||
|
var todo_left = today_left;
|
||||||
|
var issue_todo = $("#task-todo-" + $(element).attr("id"));
|
||||||
|
if (issue_todo.size() > 0){
|
||||||
|
todo_left = issue_todo.first().position().left;
|
||||||
|
}
|
||||||
|
arr.push({left: Math.min(today_left, todo_left), top: element_top_center});
|
||||||
|
}
|
||||||
|
}
|
||||||
|
});
|
||||||
|
return arr;
|
||||||
|
}
|
||||||
|
|
||||||
|
function drawGanttProgressLines() {
|
||||||
|
var arr = getProgressLinesArray();
|
||||||
|
var color = $("#today_line")
|
||||||
|
.css("border-left-color");
|
||||||
|
var i;
|
||||||
|
for(i = 1 ; i < arr.length ; i++) {
|
||||||
|
if (!("none_stroke" in arr[i]) &&
|
||||||
|
(!("is_right_edge" in arr[i - 1] && "is_right_edge" in arr[i]) &&
|
||||||
|
!("is_left_edge" in arr[i - 1] && "is_left_edge" in arr[i]))
|
||||||
|
) {
|
||||||
|
var x1 = (arr[i - 1].left == 0) ? 0 : arr[i - 1].left + draw_left;
|
||||||
|
var x2 = (arr[i].left == 0) ? 0 : arr[i].left + draw_left;
|
||||||
|
draw_gantt.path(["M", x1, arr[i - 1].top,
|
||||||
|
"L", x2, arr[i].top])
|
||||||
|
.attr({stroke: color, "stroke-width": 2});
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
function drawGanttHandler() {
|
function drawGanttHandler() {
|
||||||
var folder = document.getElementById('gantt_draw_area');
|
var folder = document.getElementById('gantt_draw_area');
|
||||||
if(draw_gantt != null)
|
if(draw_gantt != null)
|
||||||
|
@ -105,5 +165,8 @@ function drawGanttHandler() {
|
||||||
else
|
else
|
||||||
draw_gantt = Raphael(folder);
|
draw_gantt = Raphael(folder);
|
||||||
setDrawArea();
|
setDrawArea();
|
||||||
drawRelations();
|
if ($("#draw_progress_line").attr('checked'))
|
||||||
|
drawGanttProgressLines();
|
||||||
|
if ($("#draw_rels").attr('checked'))
|
||||||
|
drawRelations();
|
||||||
}
|
}
|
||||||
|
|
Loading…
Reference in New Issue