Mercurial > hg > soundsoftware-site
comparison lib/redmine/helpers/gantt.rb @ 1115:433d4f72a19b redmine-2.2
Update to Redmine SVN revision 11137 on 2.2-stable branch
author | Chris Cannam |
---|---|
date | Mon, 07 Jan 2013 12:01:42 +0000 |
parents | cbb26bc654de |
children | 622f24f53b42 261b3d9a4903 |
comparison
equal
deleted
inserted
replaced
929:5f33065ddc4b | 1115:433d4f72a19b |
---|---|
1 # Redmine - project management software | 1 # Redmine - project management software |
2 # Copyright (C) 2006-2011 Jean-Philippe Lang | 2 # Copyright (C) 2006-2012 Jean-Philippe Lang |
3 # | 3 # |
4 # This program is free software; you can redistribute it and/or | 4 # This program is free software; you can redistribute it and/or |
5 # modify it under the terms of the GNU General Public License | 5 # modify it under the terms of the GNU General Public License |
6 # as published by the Free Software Foundation; either version 2 | 6 # as published by the Free Software Foundation; either version 2 |
7 # of the License, or (at your option) any later version. | 7 # of the License, or (at your option) any later version. |
19 module Helpers | 19 module Helpers |
20 # Simple class to handle gantt chart data | 20 # Simple class to handle gantt chart data |
21 class Gantt | 21 class Gantt |
22 include ERB::Util | 22 include ERB::Util |
23 include Redmine::I18n | 23 include Redmine::I18n |
24 include Redmine::Utils::DateCalculation | |
24 | 25 |
25 # :nodoc: | 26 # :nodoc: |
26 # Some utility methods for the PDF export | 27 # Some utility methods for the PDF export |
27 class PDF | 28 class PDF |
28 MaxCharactorsForSubject = 45 | 29 MaxCharactorsForSubject = 45 |
39 attr_accessor :project | 40 attr_accessor :project |
40 attr_accessor :view | 41 attr_accessor :view |
41 | 42 |
42 def initialize(options={}) | 43 def initialize(options={}) |
43 options = options.dup | 44 options = options.dup |
44 | |
45 if options[:year] && options[:year].to_i >0 | 45 if options[:year] && options[:year].to_i >0 |
46 @year_from = options[:year].to_i | 46 @year_from = options[:year].to_i |
47 if options[:month] && options[:month].to_i >=1 && options[:month].to_i <= 12 | 47 if options[:month] && options[:month].to_i >=1 && options[:month].to_i <= 12 |
48 @month_from = options[:month].to_i | 48 @month_from = options[:month].to_i |
49 else | 49 else |
51 end | 51 end |
52 else | 52 else |
53 @month_from ||= Date.today.month | 53 @month_from ||= Date.today.month |
54 @year_from ||= Date.today.year | 54 @year_from ||= Date.today.year |
55 end | 55 end |
56 | |
57 zoom = (options[:zoom] || User.current.pref[:gantt_zoom]).to_i | 56 zoom = (options[:zoom] || User.current.pref[:gantt_zoom]).to_i |
58 @zoom = (zoom > 0 && zoom < 5) ? zoom : 2 | 57 @zoom = (zoom > 0 && zoom < 5) ? zoom : 2 |
59 months = (options[:months] || User.current.pref[:gantt_months]).to_i | 58 months = (options[:months] || User.current.pref[:gantt_months]).to_i |
60 @months = (months > 0 && months < 25) ? months : 6 | 59 @months = (months > 0 && months < 25) ? months : 6 |
61 | |
62 # Save gantt parameters as user preference (zoom and months count) | 60 # Save gantt parameters as user preference (zoom and months count) |
63 if (User.current.logged? && (@zoom != User.current.pref[:gantt_zoom] || @months != User.current.pref[:gantt_months])) | 61 if (User.current.logged? && (@zoom != User.current.pref[:gantt_zoom] || |
62 @months != User.current.pref[:gantt_months])) | |
64 User.current.pref[:gantt_zoom], User.current.pref[:gantt_months] = @zoom, @months | 63 User.current.pref[:gantt_zoom], User.current.pref[:gantt_months] = @zoom, @months |
65 User.current.preference.save | 64 User.current.preference.save |
66 end | 65 end |
67 | |
68 @date_from = Date.civil(@year_from, @month_from, 1) | 66 @date_from = Date.civil(@year_from, @month_from, 1) |
69 @date_to = (@date_from >> @months) - 1 | 67 @date_to = (@date_from >> @months) - 1 |
70 | |
71 @subjects = '' | 68 @subjects = '' |
72 @lines = '' | 69 @lines = '' |
73 @number_of_rows = nil | 70 @number_of_rows = nil |
74 | |
75 @issue_ancestors = [] | 71 @issue_ancestors = [] |
76 | |
77 @truncated = false | 72 @truncated = false |
78 if options.has_key?(:max_rows) | 73 if options.has_key?(:max_rows) |
79 @max_rows = options[:max_rows] | 74 @max_rows = options[:max_rows] |
80 else | 75 else |
81 @max_rows = Setting.gantt_items_limit.blank? ? nil : Setting.gantt_items_limit.to_i | 76 @max_rows = Setting.gantt_items_limit.blank? ? nil : Setting.gantt_items_limit.to_i |
85 def common_params | 80 def common_params |
86 { :controller => 'gantts', :action => 'show', :project_id => @project } | 81 { :controller => 'gantts', :action => 'show', :project_id => @project } |
87 end | 82 end |
88 | 83 |
89 def params | 84 def params |
90 common_params.merge({ :zoom => zoom, :year => year_from, :month => month_from, :months => months }) | 85 common_params.merge({:zoom => zoom, :year => year_from, |
86 :month => month_from, :months => months}) | |
91 end | 87 end |
92 | 88 |
93 def params_previous | 89 def params_previous |
94 common_params.merge({:year => (date_from << months).year, :month => (date_from << months).month, :zoom => zoom, :months => months }) | 90 common_params.merge({:year => (date_from << months).year, |
91 :month => (date_from << months).month, | |
92 :zoom => zoom, :months => months}) | |
95 end | 93 end |
96 | 94 |
97 def params_next | 95 def params_next |
98 common_params.merge({:year => (date_from >> months).year, :month => (date_from >> months).month, :zoom => zoom, :months => months }) | 96 common_params.merge({:year => (date_from >> months).year, |
97 :month => (date_from >> months).month, | |
98 :zoom => zoom, :months => months}) | |
99 end | 99 end |
100 | 100 |
101 # Returns the number of rows that will be rendered on the Gantt chart | 101 # Returns the number of rows that will be rendered on the Gantt chart |
102 def number_of_rows | 102 def number_of_rows |
103 return @number_of_rows if @number_of_rows | 103 return @number_of_rows if @number_of_rows |
104 | |
105 rows = projects.inject(0) {|total, p| total += number_of_rows_on_project(p)} | 104 rows = projects.inject(0) {|total, p| total += number_of_rows_on_project(p)} |
106 rows > @max_rows ? @max_rows : rows | 105 rows > @max_rows ? @max_rows : rows |
107 end | 106 end |
108 | 107 |
109 # Returns the number of rows that will be used to list a project on | 108 # Returns the number of rows that will be used to list a project on |
110 # the Gantt chart. This will recurse for each subproject. | 109 # the Gantt chart. This will recurse for each subproject. |
111 def number_of_rows_on_project(project) | 110 def number_of_rows_on_project(project) |
112 return 0 unless projects.include?(project) | 111 return 0 unless projects.include?(project) |
113 | |
114 count = 1 | 112 count = 1 |
115 count += project_issues(project).size | 113 count += project_issues(project).size |
116 count += project_versions(project).size | 114 count += project_versions(project).size |
117 count | 115 count |
118 end | 116 end |
139 end | 137 end |
140 | 138 |
141 # Return all the project nodes that will be displayed | 139 # Return all the project nodes that will be displayed |
142 def projects | 140 def projects |
143 return @projects if @projects | 141 return @projects if @projects |
144 | |
145 ids = issues.collect(&:project).uniq.collect(&:id) | 142 ids = issues.collect(&:project).uniq.collect(&:id) |
146 if ids.any? | 143 if ids.any? |
147 # All issues projects and their visible ancestors | 144 # All issues projects and their visible ancestors |
148 @projects = Project.visible.all( | 145 @projects = Project.visible.all( |
149 :joins => "LEFT JOIN #{Project.table_name} child ON #{Project.table_name}.lft <= child.lft AND #{Project.table_name}.rgt >= child.rgt", | 146 :joins => "LEFT JOIN #{Project.table_name} child ON #{Project.table_name}.lft <= child.lft AND #{Project.table_name}.rgt >= child.rgt", |
170 def version_issues(project, version) | 167 def version_issues(project, version) |
171 project_issues(project).select {|issue| issue.fixed_version == version} | 168 project_issues(project).select {|issue| issue.fixed_version == version} |
172 end | 169 end |
173 | 170 |
174 def render(options={}) | 171 def render(options={}) |
175 options = {:top => 0, :top_increment => 20, :indent_increment => 20, :render => :subject, :format => :html}.merge(options) | 172 options = {:top => 0, :top_increment => 20, |
173 :indent_increment => 20, :render => :subject, | |
174 :format => :html}.merge(options) | |
176 indent = options[:indent] || 4 | 175 indent = options[:indent] || 4 |
177 | |
178 @subjects = '' unless options[:only] == :lines | 176 @subjects = '' unless options[:only] == :lines |
179 @lines = '' unless options[:only] == :subjects | 177 @lines = '' unless options[:only] == :subjects |
180 @number_of_rows = 0 | 178 @number_of_rows = 0 |
181 | |
182 Project.project_tree(projects) do |project, level| | 179 Project.project_tree(projects) do |project, level| |
183 options[:indent] = indent + level * options[:indent_increment] | 180 options[:indent] = indent + level * options[:indent_increment] |
184 render_project(project, options) | 181 render_project(project, options) |
185 break if abort? | 182 break if abort? |
186 end | 183 end |
187 | |
188 @subjects_rendered = true unless options[:only] == :lines | 184 @subjects_rendered = true unless options[:only] == :lines |
189 @lines_rendered = true unless options[:only] == :subjects | 185 @lines_rendered = true unless options[:only] == :subjects |
190 | |
191 render_end(options) | 186 render_end(options) |
192 end | 187 end |
193 | 188 |
194 def render_project(project, options={}) | 189 def render_project(project, options={}) |
195 subject_for_project(project, options) unless options[:only] == :lines | 190 subject_for_project(project, options) unless options[:only] == :lines |
196 line_for_project(project, options) unless options[:only] == :subjects | 191 line_for_project(project, options) unless options[:only] == :subjects |
197 | |
198 options[:top] += options[:top_increment] | 192 options[:top] += options[:top_increment] |
199 options[:indent] += options[:indent_increment] | 193 options[:indent] += options[:indent_increment] |
200 @number_of_rows += 1 | 194 @number_of_rows += 1 |
201 return if abort? | 195 return if abort? |
202 | |
203 issues = project_issues(project).select {|i| i.fixed_version.nil?} | 196 issues = project_issues(project).select {|i| i.fixed_version.nil?} |
204 sort_issues!(issues) | 197 sort_issues!(issues) |
205 if issues | 198 if issues |
206 render_issues(issues, options) | 199 render_issues(issues, options) |
207 return if abort? | 200 return if abort? |
208 end | 201 end |
209 | |
210 versions = project_versions(project) | 202 versions = project_versions(project) |
211 versions.each do |version| | 203 versions.each do |version| |
212 render_version(project, version, options) | 204 render_version(project, version, options) |
213 end | 205 end |
214 | |
215 # Remove indent to hit the next sibling | 206 # Remove indent to hit the next sibling |
216 options[:indent] -= options[:indent_increment] | 207 options[:indent] -= options[:indent_increment] |
217 end | 208 end |
218 | 209 |
219 def render_issues(issues, options={}) | 210 def render_issues(issues, options={}) |
220 @issue_ancestors = [] | 211 @issue_ancestors = [] |
221 | |
222 issues.each do |i| | 212 issues.each do |i| |
223 subject_for_issue(i, options) unless options[:only] == :lines | 213 subject_for_issue(i, options) unless options[:only] == :lines |
224 line_for_issue(i, options) unless options[:only] == :subjects | 214 line_for_issue(i, options) unless options[:only] == :subjects |
225 | |
226 options[:top] += options[:top_increment] | 215 options[:top] += options[:top_increment] |
227 @number_of_rows += 1 | 216 @number_of_rows += 1 |
228 break if abort? | 217 break if abort? |
229 end | 218 end |
230 | |
231 options[:indent] -= (options[:indent_increment] * @issue_ancestors.size) | 219 options[:indent] -= (options[:indent_increment] * @issue_ancestors.size) |
232 end | 220 end |
233 | 221 |
234 def render_version(project, version, options={}) | 222 def render_version(project, version, options={}) |
235 # Version header | 223 # Version header |
236 subject_for_version(version, options) unless options[:only] == :lines | 224 subject_for_version(version, options) unless options[:only] == :lines |
237 line_for_version(version, options) unless options[:only] == :subjects | 225 line_for_version(version, options) unless options[:only] == :subjects |
238 | |
239 options[:top] += options[:top_increment] | 226 options[:top] += options[:top_increment] |
240 @number_of_rows += 1 | 227 @number_of_rows += 1 |
241 return if abort? | 228 return if abort? |
242 | |
243 issues = version_issues(project, version) | 229 issues = version_issues(project, version) |
244 if issues | 230 if issues |
245 sort_issues!(issues) | 231 sort_issues!(issues) |
246 # Indent issues | 232 # Indent issues |
247 options[:indent] += options[:indent_increment] | 233 options[:indent] += options[:indent_increment] |
258 end | 244 end |
259 | 245 |
260 def subject_for_project(project, options) | 246 def subject_for_project(project, options) |
261 case options[:format] | 247 case options[:format] |
262 when :html | 248 when :html |
263 subject = "<span class='icon icon-projects #{project.overdue? ? 'project-overdue' : ''}'>".html_safe | 249 html_class = "" |
264 subject << view.link_to_project(project).html_safe | 250 html_class << 'icon icon-projects ' |
265 subject << '</span>'.html_safe | 251 html_class << (project.overdue? ? 'project-overdue' : '') |
252 s = view.link_to_project(project).html_safe | |
253 subject = view.content_tag(:span, s, | |
254 :class => html_class).html_safe | |
266 html_subject(options, subject, :css => "project-name") | 255 html_subject(options, subject, :css => "project-name") |
267 when :image | 256 when :image |
268 image_subject(options, project.name) | 257 image_subject(options, project.name) |
269 when :pdf | 258 when :pdf |
270 pdf_new_page?(options) | 259 pdf_new_page?(options) |
275 def line_for_project(project, options) | 264 def line_for_project(project, options) |
276 # Skip versions that don't have a start_date or due date | 265 # Skip versions that don't have a start_date or due date |
277 if project.is_a?(Project) && project.start_date && project.due_date | 266 if project.is_a?(Project) && project.start_date && project.due_date |
278 options[:zoom] ||= 1 | 267 options[:zoom] ||= 1 |
279 options[:g_width] ||= (self.date_to - self.date_from + 1) * options[:zoom] | 268 options[:g_width] ||= (self.date_to - self.date_from + 1) * options[:zoom] |
280 | |
281 coords = coordinates(project.start_date, project.due_date, nil, options[:zoom]) | 269 coords = coordinates(project.start_date, project.due_date, nil, options[:zoom]) |
282 label = h(project) | 270 label = h(project) |
283 | |
284 case options[:format] | 271 case options[:format] |
285 when :html | 272 when :html |
286 html_task(options, coords, :css => "project task", :label => label, :markers => true) | 273 html_task(options, coords, :css => "project task", :label => label, :markers => true) |
287 when :image | 274 when :image |
288 image_task(options, coords, :label => label, :markers => true, :height => 3) | 275 image_task(options, coords, :label => label, :markers => true, :height => 3) |
296 end | 283 end |
297 | 284 |
298 def subject_for_version(version, options) | 285 def subject_for_version(version, options) |
299 case options[:format] | 286 case options[:format] |
300 when :html | 287 when :html |
301 subject = "<span class='icon icon-package #{version.behind_schedule? ? 'version-behind-schedule' : ''} #{version.overdue? ? 'version-overdue' : ''}'>".html_safe | 288 html_class = "" |
302 subject << view.link_to_version(version).html_safe | 289 html_class << 'icon icon-package ' |
303 subject << '</span>'.html_safe | 290 html_class << (version.behind_schedule? ? 'version-behind-schedule' : '') << " " |
291 html_class << (version.overdue? ? 'version-overdue' : '') | |
292 s = view.link_to_version(version).html_safe | |
293 subject = view.content_tag(:span, s, | |
294 :class => html_class).html_safe | |
304 html_subject(options, subject, :css => "version-name") | 295 html_subject(options, subject, :css => "version-name") |
305 when :image | 296 when :image |
306 image_subject(options, version.to_s_with_project) | 297 image_subject(options, version.to_s_with_project) |
307 when :pdf | 298 when :pdf |
308 pdf_new_page?(options) | 299 pdf_new_page?(options) |
313 def line_for_version(version, options) | 304 def line_for_version(version, options) |
314 # Skip versions that don't have a start_date | 305 # Skip versions that don't have a start_date |
315 if version.is_a?(Version) && version.start_date && version.due_date | 306 if version.is_a?(Version) && version.start_date && version.due_date |
316 options[:zoom] ||= 1 | 307 options[:zoom] ||= 1 |
317 options[:g_width] ||= (self.date_to - self.date_from + 1) * options[:zoom] | 308 options[:g_width] ||= (self.date_to - self.date_from + 1) * options[:zoom] |
318 | 309 coords = coordinates(version.start_date, |
319 coords = coordinates(version.start_date, version.due_date, version.completed_pourcent, options[:zoom]) | 310 version.due_date, version.completed_pourcent, |
320 label = "#{h version } #{h version.completed_pourcent.to_i.to_s}%" | 311 options[:zoom]) |
312 label = "#{h version} #{h version.completed_pourcent.to_i.to_s}%" | |
321 label = h("#{version.project} -") + label unless @project && @project == version.project | 313 label = h("#{version.project} -") + label unless @project && @project == version.project |
322 | |
323 case options[:format] | 314 case options[:format] |
324 when :html | 315 when :html |
325 html_task(options, coords, :css => "version task", :label => label, :markers => true) | 316 html_task(options, coords, :css => "version task", :label => label, :markers => true) |
326 when :image | 317 when :image |
327 image_task(options, coords, :label => label, :markers => true, :height => 3) | 318 image_task(options, coords, :label => label, :markers => true, :height => 3) |
337 def subject_for_issue(issue, options) | 328 def subject_for_issue(issue, options) |
338 while @issue_ancestors.any? && !issue.is_descendant_of?(@issue_ancestors.last) | 329 while @issue_ancestors.any? && !issue.is_descendant_of?(@issue_ancestors.last) |
339 @issue_ancestors.pop | 330 @issue_ancestors.pop |
340 options[:indent] -= options[:indent_increment] | 331 options[:indent] -= options[:indent_increment] |
341 end | 332 end |
342 | |
343 output = case options[:format] | 333 output = case options[:format] |
344 when :html | 334 when :html |
345 css_classes = '' | 335 css_classes = '' |
346 css_classes << ' issue-overdue' if issue.overdue? | 336 css_classes << ' issue-overdue' if issue.overdue? |
347 css_classes << ' issue-behind-schedule' if issue.behind_schedule? | 337 css_classes << ' issue-behind-schedule' if issue.behind_schedule? |
348 css_classes << ' icon icon-issue' unless Setting.gravatar_enabled? && issue.assigned_to | 338 css_classes << ' icon icon-issue' unless Setting.gravatar_enabled? && issue.assigned_to |
349 | 339 s = "".html_safe |
350 subject = "<span class='#{css_classes}'>".html_safe | |
351 if issue.assigned_to.present? | 340 if issue.assigned_to.present? |
352 assigned_string = l(:field_assigned_to) + ": " + issue.assigned_to.name | 341 assigned_string = l(:field_assigned_to) + ": " + issue.assigned_to.name |
353 subject << view.avatar(issue.assigned_to, :class => 'gravatar icon-gravatar', :size => 10, :title => assigned_string).to_s.html_safe | 342 s << view.avatar(issue.assigned_to, |
354 end | 343 :class => 'gravatar icon-gravatar', |
355 subject << view.link_to_issue(issue).html_safe | 344 :size => 10, |
356 subject << '</span>'.html_safe | 345 :title => assigned_string).to_s.html_safe |
357 html_subject(options, subject, :css => "issue-subject", :title => issue.subject) + "\n" | 346 end |
347 s << view.link_to_issue(issue).html_safe | |
348 subject = view.content_tag(:span, s, :class => css_classes).html_safe | |
349 html_subject(options, subject, :css => "issue-subject", | |
350 :title => issue.subject) + "\n" | |
358 when :image | 351 when :image |
359 image_subject(options, issue.subject) | 352 image_subject(options, issue.subject) |
360 when :pdf | 353 when :pdf |
361 pdf_new_page?(options) | 354 pdf_new_page?(options) |
362 pdf_subject(options, issue.subject) | 355 pdf_subject(options, issue.subject) |
363 end | 356 end |
364 | |
365 unless issue.leaf? | 357 unless issue.leaf? |
366 @issue_ancestors << issue | 358 @issue_ancestors << issue |
367 options[:indent] += options[:indent_increment] | 359 options[:indent] += options[:indent_increment] |
368 end | 360 end |
369 | |
370 output | 361 output |
371 end | 362 end |
372 | 363 |
373 def line_for_issue(issue, options) | 364 def line_for_issue(issue, options) |
374 # Skip issues that don't have a due_before (due_date or version's due_date) | 365 # Skip issues that don't have a due_before (due_date or version's due_date) |
375 if issue.is_a?(Issue) && issue.due_before | 366 if issue.is_a?(Issue) && issue.due_before |
376 coords = coordinates(issue.start_date, issue.due_before, issue.done_ratio, options[:zoom]) | 367 coords = coordinates(issue.start_date, issue.due_before, issue.done_ratio, options[:zoom]) |
377 label = "#{ issue.status.name } #{ issue.done_ratio }%" | 368 label = "#{issue.status.name} #{issue.done_ratio}%" |
378 | |
379 case options[:format] | 369 case options[:format] |
380 when :html | 370 when :html |
381 html_task(options, coords, :css => "task " + (issue.leaf? ? 'leaf' : 'parent'), :label => label, :issue => issue, :markers => !issue.leaf?) | 371 html_task(options, coords, |
372 :css => "task " + (issue.leaf? ? 'leaf' : 'parent'), | |
373 :label => label, :issue => issue, | |
374 :markers => !issue.leaf?) | |
382 when :image | 375 when :image |
383 image_task(options, coords, :label => label) | 376 image_task(options, coords, :label => label) |
384 when :pdf | 377 when :pdf |
385 pdf_task(options, coords, :label => label) | 378 pdf_task(options, coords, :label => label) |
386 end | 379 end |
391 end | 384 end |
392 | 385 |
393 # Generates a gantt image | 386 # Generates a gantt image |
394 # Only defined if RMagick is avalaible | 387 # Only defined if RMagick is avalaible |
395 def to_image(format='PNG') | 388 def to_image(format='PNG') |
396 date_to = (@date_from >> @months)-1 | 389 date_to = (@date_from >> @months) - 1 |
397 show_weeks = @zoom > 1 | 390 show_weeks = @zoom > 1 |
398 show_days = @zoom > 2 | 391 show_days = @zoom > 2 |
399 | |
400 subject_width = 400 | 392 subject_width = 400 |
401 header_height = 18 | 393 header_height = 18 |
402 # width of one day in pixels | 394 # width of one day in pixels |
403 zoom = @zoom*2 | 395 zoom = @zoom * 2 |
404 g_width = (@date_to - @date_from + 1)*zoom | 396 g_width = (@date_to - @date_from + 1) * zoom |
405 g_height = 20 * number_of_rows + 30 | 397 g_height = 20 * number_of_rows + 30 |
406 headers_height = (show_weeks ? 2*header_height : header_height) | 398 headers_height = (show_weeks ? 2 * header_height : header_height) |
407 height = g_height + headers_height | 399 height = g_height + headers_height |
408 | |
409 imgl = Magick::ImageList.new | 400 imgl = Magick::ImageList.new |
410 imgl.new_image(subject_width+g_width+1, height) | 401 imgl.new_image(subject_width + g_width + 1, height) |
411 gc = Magick::Draw.new | 402 gc = Magick::Draw.new |
412 | 403 gc.font = Redmine::Configuration['rmagick_font_path'] || "" |
413 # Subjects | 404 # Subjects |
414 gc.stroke('transparent') | 405 gc.stroke('transparent') |
415 subjects(:image => gc, :top => (headers_height + 20), :indent => 4, :format => :image) | 406 subjects(:image => gc, :top => (headers_height + 20), :indent => 4, :format => :image) |
416 | |
417 # Months headers | 407 # Months headers |
418 month_f = @date_from | 408 month_f = @date_from |
419 left = subject_width | 409 left = subject_width |
420 @months.times do | 410 @months.times do |
421 width = ((month_f >> 1) - month_f) * zoom | 411 width = ((month_f >> 1) - month_f) * zoom |
428 gc.stroke_width(1) | 418 gc.stroke_width(1) |
429 gc.text(left.round + 8, 14, "#{month_f.year}-#{month_f.month}") | 419 gc.text(left.round + 8, 14, "#{month_f.year}-#{month_f.month}") |
430 left = left + width | 420 left = left + width |
431 month_f = month_f >> 1 | 421 month_f = month_f >> 1 |
432 end | 422 end |
433 | |
434 # Weeks headers | 423 # Weeks headers |
435 if show_weeks | 424 if show_weeks |
436 left = subject_width | 425 left = subject_width |
437 height = header_height | 426 height = header_height |
438 if @date_from.cwday == 1 | 427 if @date_from.cwday == 1 |
439 # date_from is monday | 428 # date_from is monday |
440 week_f = date_from | 429 week_f = date_from |
441 else | 430 else |
442 # find next monday after date_from | 431 # find next monday after date_from |
443 week_f = @date_from + (7 - @date_from.cwday + 1) | 432 week_f = @date_from + (7 - @date_from.cwday + 1) |
444 width = (7 - @date_from.cwday + 1) * zoom | 433 width = (7 - @date_from.cwday + 1) * zoom |
445 gc.fill('white') | 434 gc.fill('white') |
446 gc.stroke('grey') | 435 gc.stroke('grey') |
447 gc.stroke_width(1) | 436 gc.stroke_width(1) |
448 gc.rectangle(left, header_height, left + width, 2*header_height + g_height-1) | 437 gc.rectangle(left, header_height, left + width, 2 * header_height + g_height - 1) |
449 left = left + width | 438 left = left + width |
450 end | 439 end |
451 while week_f <= date_to | 440 while week_f <= date_to |
452 width = (week_f + 6 <= date_to) ? 7 * zoom : (date_to - week_f + 1) * zoom | 441 width = (week_f + 6 <= date_to) ? 7 * zoom : (date_to - week_f + 1) * zoom |
453 gc.fill('white') | 442 gc.fill('white') |
454 gc.stroke('grey') | 443 gc.stroke('grey') |
455 gc.stroke_width(1) | 444 gc.stroke_width(1) |
456 gc.rectangle(left.round, header_height, left.round + width, 2*header_height + g_height-1) | 445 gc.rectangle(left.round, header_height, left.round + width, 2 * header_height + g_height - 1) |
457 gc.fill('black') | 446 gc.fill('black') |
458 gc.stroke('transparent') | 447 gc.stroke('transparent') |
459 gc.stroke_width(1) | 448 gc.stroke_width(1) |
460 gc.text(left.round + 2, header_height + 14, week_f.cweek.to_s) | 449 gc.text(left.round + 2, header_height + 14, week_f.cweek.to_s) |
461 left = left + width | 450 left = left + width |
462 week_f = week_f+7 | 451 week_f = week_f + 7 |
463 end | 452 end |
464 end | 453 end |
465 | |
466 # Days details (week-end in grey) | 454 # Days details (week-end in grey) |
467 if show_days | 455 if show_days |
468 left = subject_width | 456 left = subject_width |
469 height = g_height + header_height - 1 | 457 height = g_height + header_height - 1 |
470 wday = @date_from.cwday | 458 wday = @date_from.cwday |
471 (date_to - @date_from + 1).to_i.times do | 459 (date_to - @date_from + 1).to_i.times do |
472 width = zoom | 460 width = zoom |
473 gc.fill(wday == 6 || wday == 7 ? '#eee' : 'white') | 461 gc.fill(non_working_week_days.include?(wday) ? '#eee' : 'white') |
474 gc.stroke('#ddd') | 462 gc.stroke('#ddd') |
475 gc.stroke_width(1) | 463 gc.stroke_width(1) |
476 gc.rectangle(left, 2*header_height, left + width, 2*header_height + g_height-1) | 464 gc.rectangle(left, 2 * header_height, left + width, 2 * header_height + g_height - 1) |
477 left = left + width | 465 left = left + width |
478 wday = wday + 1 | 466 wday = wday + 1 |
479 wday = 1 if wday > 7 | 467 wday = 1 if wday > 7 |
480 end | 468 end |
481 end | 469 end |
482 | |
483 # border | 470 # border |
484 gc.fill('transparent') | 471 gc.fill('transparent') |
485 gc.stroke('grey') | 472 gc.stroke('grey') |
486 gc.stroke_width(1) | 473 gc.stroke_width(1) |
487 gc.rectangle(0, 0, subject_width+g_width, headers_height) | 474 gc.rectangle(0, 0, subject_width + g_width, headers_height) |
488 gc.stroke('black') | 475 gc.stroke('black') |
489 gc.rectangle(0, 0, subject_width+g_width, g_height+ headers_height-1) | 476 gc.rectangle(0, 0, subject_width + g_width, g_height + headers_height - 1) |
490 | |
491 # content | 477 # content |
492 top = headers_height + 20 | 478 top = headers_height + 20 |
493 | |
494 gc.stroke('transparent') | 479 gc.stroke('transparent') |
495 lines(:image => gc, :top => top, :zoom => zoom, :subject_width => subject_width, :format => :image) | 480 lines(:image => gc, :top => top, :zoom => zoom, |
496 | 481 :subject_width => subject_width, :format => :image) |
497 # today red line | 482 # today red line |
498 if Date.today >= @date_from and Date.today <= date_to | 483 if Date.today >= @date_from and Date.today <= date_to |
499 gc.stroke('red') | 484 gc.stroke('red') |
500 x = (Date.today-@date_from+1)*zoom + subject_width | 485 x = (Date.today - @date_from + 1) * zoom + subject_width |
501 gc.line(x, headers_height, x, headers_height + g_height-1) | 486 gc.line(x, headers_height, x, headers_height + g_height - 1) |
502 end | 487 end |
503 | |
504 gc.draw(imgl) | 488 gc.draw(imgl) |
505 imgl.format = format | 489 imgl.format = format |
506 imgl.to_blob | 490 imgl.to_blob |
507 end if Object.const_defined?(:Magick) | 491 end if Object.const_defined?(:Magick) |
508 | 492 |
510 pdf = ::Redmine::Export::PDF::ITCPDF.new(current_language) | 494 pdf = ::Redmine::Export::PDF::ITCPDF.new(current_language) |
511 pdf.SetTitle("#{l(:label_gantt)} #{project}") | 495 pdf.SetTitle("#{l(:label_gantt)} #{project}") |
512 pdf.alias_nb_pages | 496 pdf.alias_nb_pages |
513 pdf.footer_date = format_date(Date.today) | 497 pdf.footer_date = format_date(Date.today) |
514 pdf.AddPage("L") | 498 pdf.AddPage("L") |
515 pdf.SetFontStyle('B',12) | 499 pdf.SetFontStyle('B', 12) |
516 pdf.SetX(15) | 500 pdf.SetX(15) |
517 pdf.RDMCell(PDF::LeftPaneWidth, 20, project.to_s) | 501 pdf.RDMCell(PDF::LeftPaneWidth, 20, project.to_s) |
518 pdf.Ln | 502 pdf.Ln |
519 pdf.SetFontStyle('B',9) | 503 pdf.SetFontStyle('B', 9) |
520 | |
521 subject_width = PDF::LeftPaneWidth | 504 subject_width = PDF::LeftPaneWidth |
522 header_height = 5 | 505 header_height = 5 |
523 | |
524 headers_height = header_height | 506 headers_height = header_height |
525 show_weeks = false | 507 show_weeks = false |
526 show_days = false | 508 show_days = false |
527 | |
528 if self.months < 7 | 509 if self.months < 7 |
529 show_weeks = true | 510 show_weeks = true |
530 headers_height = 2*header_height | 511 headers_height = 2 * header_height |
531 if self.months < 3 | 512 if self.months < 3 |
532 show_days = true | 513 show_days = true |
533 headers_height = 3*header_height | 514 headers_height = 3 * header_height |
534 end | 515 end |
535 end | 516 end |
536 | |
537 g_width = PDF.right_pane_width | 517 g_width = PDF.right_pane_width |
538 zoom = (g_width) / (self.date_to - self.date_from + 1) | 518 zoom = (g_width) / (self.date_to - self.date_from + 1) |
539 g_height = 120 | 519 g_height = 120 |
540 t_height = g_height + headers_height | 520 t_height = g_height + headers_height |
541 | |
542 y_start = pdf.GetY | 521 y_start = pdf.GetY |
543 | |
544 # Months headers | 522 # Months headers |
545 month_f = self.date_from | 523 month_f = self.date_from |
546 left = subject_width | 524 left = subject_width |
547 height = header_height | 525 height = header_height |
548 self.months.times do | 526 self.months.times do |
551 pdf.SetX(left) | 529 pdf.SetX(left) |
552 pdf.RDMCell(width, height, "#{month_f.year}-#{month_f.month}", "LTR", 0, "C") | 530 pdf.RDMCell(width, height, "#{month_f.year}-#{month_f.month}", "LTR", 0, "C") |
553 left = left + width | 531 left = left + width |
554 month_f = month_f >> 1 | 532 month_f = month_f >> 1 |
555 end | 533 end |
556 | |
557 # Weeks headers | 534 # Weeks headers |
558 if show_weeks | 535 if show_weeks |
559 left = subject_width | 536 left = subject_width |
560 height = header_height | 537 height = header_height |
561 if self.date_from.cwday == 1 | 538 if self.date_from.cwday == 1 |
566 week_f = self.date_from + (7 - self.date_from.cwday + 1) | 543 week_f = self.date_from + (7 - self.date_from.cwday + 1) |
567 width = (7 - self.date_from.cwday + 1) * zoom-1 | 544 width = (7 - self.date_from.cwday + 1) * zoom-1 |
568 pdf.SetY(y_start + header_height) | 545 pdf.SetY(y_start + header_height) |
569 pdf.SetX(left) | 546 pdf.SetX(left) |
570 pdf.RDMCell(width + 1, height, "", "LTR") | 547 pdf.RDMCell(width + 1, height, "", "LTR") |
571 left = left + width+1 | 548 left = left + width + 1 |
572 end | 549 end |
573 while week_f <= self.date_to | 550 while week_f <= self.date_to |
574 width = (week_f + 6 <= self.date_to) ? 7 * zoom : (self.date_to - week_f + 1) * zoom | 551 width = (week_f + 6 <= self.date_to) ? 7 * zoom : (self.date_to - week_f + 1) * zoom |
575 pdf.SetY(y_start + header_height) | 552 pdf.SetY(y_start + header_height) |
576 pdf.SetX(left) | 553 pdf.SetX(left) |
577 pdf.RDMCell(width, height, (width >= 5 ? week_f.cweek.to_s : ""), "LTR", 0, "C") | 554 pdf.RDMCell(width, height, (width >= 5 ? week_f.cweek.to_s : ""), "LTR", 0, "C") |
578 left = left + width | 555 left = left + width |
579 week_f = week_f+7 | 556 week_f = week_f + 7 |
580 end | 557 end |
581 end | 558 end |
582 | |
583 # Days headers | 559 # Days headers |
584 if show_days | 560 if show_days |
585 left = subject_width | 561 left = subject_width |
586 height = header_height | 562 height = header_height |
587 wday = self.date_from.cwday | 563 wday = self.date_from.cwday |
588 pdf.SetFontStyle('B',7) | 564 pdf.SetFontStyle('B', 7) |
589 (self.date_to - self.date_from + 1).to_i.times do | 565 (self.date_to - self.date_from + 1).to_i.times do |
590 width = zoom | 566 width = zoom |
591 pdf.SetY(y_start + 2 * header_height) | 567 pdf.SetY(y_start + 2 * header_height) |
592 pdf.SetX(left) | 568 pdf.SetX(left) |
593 pdf.RDMCell(width, height, day_name(wday).first, "LTR", 0, "C") | 569 pdf.RDMCell(width, height, day_name(wday).first, "LTR", 0, "C") |
594 left = left + width | 570 left = left + width |
595 wday = wday + 1 | 571 wday = wday + 1 |
596 wday = 1 if wday > 7 | 572 wday = 1 if wday > 7 |
597 end | 573 end |
598 end | 574 end |
599 | |
600 pdf.SetY(y_start) | 575 pdf.SetY(y_start) |
601 pdf.SetX(15) | 576 pdf.SetX(15) |
602 pdf.RDMCell(subject_width+g_width-15, headers_height, "", 1) | 577 pdf.RDMCell(subject_width + g_width - 15, headers_height, "", 1) |
603 | |
604 # Tasks | 578 # Tasks |
605 top = headers_height + y_start | 579 top = headers_height + y_start |
606 options = { | 580 options = { |
607 :top => top, | 581 :top => top, |
608 :zoom => zoom, | 582 :zoom => zoom, |
620 | 594 |
621 private | 595 private |
622 | 596 |
623 def coordinates(start_date, end_date, progress, zoom=nil) | 597 def coordinates(start_date, end_date, progress, zoom=nil) |
624 zoom ||= @zoom | 598 zoom ||= @zoom |
625 | |
626 coords = {} | 599 coords = {} |
627 if start_date && end_date && start_date < self.date_to && end_date > self.date_from | 600 if start_date && end_date && start_date < self.date_to && end_date > self.date_from |
628 if start_date > self.date_from | 601 if start_date > self.date_from |
629 coords[:start] = start_date - self.date_from | 602 coords[:start] = start_date - self.date_from |
630 coords[:bar_start] = start_date - self.date_from | 603 coords[:bar_start] = start_date - self.date_from |
635 coords[:end] = end_date - self.date_from | 608 coords[:end] = end_date - self.date_from |
636 coords[:bar_end] = end_date - self.date_from + 1 | 609 coords[:bar_end] = end_date - self.date_from + 1 |
637 else | 610 else |
638 coords[:bar_end] = self.date_to - self.date_from + 1 | 611 coords[:bar_end] = self.date_to - self.date_from + 1 |
639 end | 612 end |
640 | |
641 if progress | 613 if progress |
642 progress_date = start_date + (end_date - start_date + 1) * (progress / 100.0) | 614 progress_date = start_date + (end_date - start_date + 1) * (progress / 100.0) |
643 if progress_date > self.date_from && progress_date > start_date | 615 if progress_date > self.date_from && progress_date > start_date |
644 if progress_date < self.date_to | 616 if progress_date < self.date_to |
645 coords[:bar_progress_end] = progress_date - self.date_from | 617 coords[:bar_progress_end] = progress_date - self.date_from |
646 else | 618 else |
647 coords[:bar_progress_end] = self.date_to - self.date_from + 1 | 619 coords[:bar_progress_end] = self.date_to - self.date_from + 1 |
648 end | 620 end |
649 end | 621 end |
650 | |
651 if progress_date < Date.today | 622 if progress_date < Date.today |
652 late_date = [Date.today, end_date].min | 623 late_date = [Date.today, end_date].min |
653 if late_date > self.date_from && late_date > start_date | 624 if late_date > self.date_from && late_date > start_date |
654 if late_date < self.date_to | 625 if late_date < self.date_to |
655 coords[:bar_late_end] = late_date - self.date_from + 1 | 626 coords[:bar_late_end] = late_date - self.date_from + 1 |
658 end | 629 end |
659 end | 630 end |
660 end | 631 end |
661 end | 632 end |
662 end | 633 end |
663 | |
664 # Transforms dates into pixels witdh | 634 # Transforms dates into pixels witdh |
665 coords.keys.each do |key| | 635 coords.keys.each do |key| |
666 coords[key] = (coords[key] * zoom).floor | 636 coords[key] = (coords[key] * zoom).floor |
667 end | 637 end |
668 coords | 638 coords |
669 end | 639 end |
670 | 640 |
671 # Sorts a collection of issues by start_date, due_date, id for gantt rendering | 641 # Sorts a collection of issues by start_date, due_date, id for gantt rendering |
672 def sort_issues!(issues) | 642 def sort_issues!(issues) |
673 issues.sort! { |a, b| gantt_issue_compare(a, b, issues) } | 643 issues.sort! { |a, b| gantt_issue_compare(a, b) } |
674 end | 644 end |
675 | 645 |
676 # TODO: top level issues should be sorted by start date | 646 # TODO: top level issues should be sorted by start date |
677 def gantt_issue_compare(x, y, issues) | 647 def gantt_issue_compare(x, y) |
678 if x.root_id == y.root_id | 648 if x.root_id == y.root_id |
679 x.lft <=> y.lft | 649 x.lft <=> y.lft |
680 else | 650 else |
681 x.root_id <=> y.root_id | 651 x.root_id <=> y.root_id |
682 end | 652 end |
706 end | 676 end |
707 | 677 |
708 def html_subject(params, subject, options={}) | 678 def html_subject(params, subject, options={}) |
709 style = "position: absolute;top:#{params[:top]}px;left:#{params[:indent]}px;" | 679 style = "position: absolute;top:#{params[:top]}px;left:#{params[:indent]}px;" |
710 style << "width:#{params[:subject_width] - params[:indent]}px;" if params[:subject_width] | 680 style << "width:#{params[:subject_width] - params[:indent]}px;" if params[:subject_width] |
711 | 681 output = view.content_tag('div', subject, |
712 output = view.content_tag 'div', subject, :class => options[:css], :style => style, :title => options[:title] | 682 :class => options[:css], :style => style, |
683 :title => options[:title]) | |
713 @subjects << output | 684 @subjects << output |
714 output | 685 output |
715 end | 686 end |
716 | 687 |
717 def pdf_subject(params, subject, options={}) | 688 def pdf_subject(params, subject, options={}) |
718 params[:pdf].SetY(params[:top]) | 689 params[:pdf].SetY(params[:top]) |
719 params[:pdf].SetX(15) | 690 params[:pdf].SetX(15) |
720 | |
721 char_limit = PDF::MaxCharactorsForSubject - params[:indent] | 691 char_limit = PDF::MaxCharactorsForSubject - params[:indent] |
722 params[:pdf].RDMCell(params[:subject_width]-15, 5, (" " * params[:indent]) + subject.to_s.sub(/^(.{#{char_limit}}[^\s]*\s).*$/, '\1 (...)'), "LR") | 692 params[:pdf].RDMCell(params[:subject_width] - 15, 5, |
723 | 693 (" " * params[:indent]) + |
694 subject.to_s.sub(/^(.{#{char_limit}}[^\s]*\s).*$/, '\1 (...)'), | |
695 "LR") | |
724 params[:pdf].SetY(params[:top]) | 696 params[:pdf].SetY(params[:top]) |
725 params[:pdf].SetX(params[:subject_width]) | 697 params[:pdf].SetX(params[:subject_width]) |
726 params[:pdf].RDMCell(params[:g_width], 5, "", "LR") | 698 params[:pdf].RDMCell(params[:g_width], 5, "", "LR") |
727 end | 699 end |
728 | 700 |
735 | 707 |
736 def html_task(params, coords, options={}) | 708 def html_task(params, coords, options={}) |
737 output = '' | 709 output = '' |
738 # Renders the task bar, with progress and late | 710 # Renders the task bar, with progress and late |
739 if coords[:bar_start] && coords[:bar_end] | 711 if coords[:bar_start] && coords[:bar_end] |
740 output << "<div style='top:#{ params[:top] }px;left:#{ coords[:bar_start] }px;width:#{ coords[:bar_end] - coords[:bar_start] - 2}px;' class='#{options[:css]} task_todo'> </div>".html_safe | 712 width = coords[:bar_end] - coords[:bar_start] - 2 |
741 | 713 style = "" |
714 style << "top:#{params[:top]}px;" | |
715 style << "left:#{coords[:bar_start]}px;" | |
716 style << "width:#{width}px;" | |
717 output << view.content_tag(:div, ' '.html_safe, | |
718 :style => style, | |
719 :class => "#{options[:css]} task_todo") | |
742 if coords[:bar_late_end] | 720 if coords[:bar_late_end] |
743 output << "<div style='top:#{ params[:top] }px;left:#{ coords[:bar_start] }px;width:#{ coords[:bar_late_end] - coords[:bar_start] - 2}px;' class='#{options[:css]} task_late'> </div>".html_safe | 721 width = coords[:bar_late_end] - coords[:bar_start] - 2 |
722 style = "" | |
723 style << "top:#{params[:top]}px;" | |
724 style << "left:#{coords[:bar_start]}px;" | |
725 style << "width:#{width}px;" | |
726 output << view.content_tag(:div, ' '.html_safe, | |
727 :style => style, | |
728 :class => "#{options[:css]} task_late") | |
744 end | 729 end |
745 if coords[:bar_progress_end] | 730 if coords[:bar_progress_end] |
746 output << "<div style='top:#{ params[:top] }px;left:#{ coords[:bar_start] }px;width:#{ coords[:bar_progress_end] - coords[:bar_start] - 2}px;' class='#{options[:css]} task_done'> </div>".html_safe | 731 width = coords[:bar_progress_end] - coords[:bar_start] - 2 |
732 style = "" | |
733 style << "top:#{params[:top]}px;" | |
734 style << "left:#{coords[:bar_start]}px;" | |
735 style << "width:#{width}px;" | |
736 output << view.content_tag(:div, ' '.html_safe, | |
737 :style => style, | |
738 :class => "#{options[:css]} task_done") | |
747 end | 739 end |
748 end | 740 end |
749 # Renders the markers | 741 # Renders the markers |
750 if options[:markers] | 742 if options[:markers] |
751 if coords[:start] | 743 if coords[:start] |
752 output << "<div style='top:#{ params[:top] }px;left:#{ coords[:start] }px;width:15px;' class='#{options[:css]} marker starting'> </div>".html_safe | 744 style = "" |
745 style << "top:#{params[:top]}px;" | |
746 style << "left:#{coords[:start]}px;" | |
747 style << "width:15px;" | |
748 output << view.content_tag(:div, ' '.html_safe, | |
749 :style => style, | |
750 :class => "#{options[:css]} marker starting") | |
753 end | 751 end |
754 if coords[:end] | 752 if coords[:end] |
755 output << "<div style='top:#{ params[:top] }px;left:#{ coords[:end] + params[:zoom] }px;width:15px;' class='#{options[:css]} marker ending'> </div>".html_safe | 753 style = "" |
754 style << "top:#{params[:top]}px;" | |
755 style << "left:#{coords[:end] + params[:zoom]}px;" | |
756 style << "width:15px;" | |
757 output << view.content_tag(:div, ' '.html_safe, | |
758 :style => style, | |
759 :class => "#{options[:css]} marker ending") | |
756 end | 760 end |
757 end | 761 end |
758 # Renders the label on the right | 762 # Renders the label on the right |
759 if options[:label] | 763 if options[:label] |
760 output << "<div style='top:#{ params[:top] }px;left:#{ (coords[:bar_end] || 0) + 8 }px;' class='#{options[:css]} label'>".html_safe | 764 style = "" |
761 output << options[:label] | 765 style << "top:#{params[:top]}px;" |
762 output << "</div>".html_safe | 766 style << "left:#{(coords[:bar_end] || 0) + 8}px;" |
767 style << "width:15px;" | |
768 output << view.content_tag(:div, options[:label], | |
769 :style => style, | |
770 :class => "#{options[:css]} label") | |
763 end | 771 end |
764 # Renders the tooltip | 772 # Renders the tooltip |
765 if options[:issue] && coords[:bar_start] && coords[:bar_end] | 773 if options[:issue] && coords[:bar_start] && coords[:bar_end] |
766 output << "<div class='tooltip' style='position: absolute;top:#{ params[:top] }px;left:#{ coords[:bar_start] }px;width:#{ coords[:bar_end] - coords[:bar_start] }px;height:12px;'>".html_safe | 774 s = view.content_tag(:span, |
767 output << '<span class="tip">'.html_safe | 775 view.render_issue_tooltip(options[:issue]).html_safe, |
768 output << view.render_issue_tooltip(options[:issue]).html_safe | 776 :class => "tip") |
769 output << "</span></div>".html_safe | 777 style = "" |
778 style << "position: absolute;" | |
779 style << "top:#{params[:top]}px;" | |
780 style << "left:#{coords[:bar_start]}px;" | |
781 style << "width:#{coords[:bar_end] - coords[:bar_start]}px;" | |
782 style << "height:12px;" | |
783 output << view.content_tag(:div, s.html_safe, | |
784 :style => style, | |
785 :class => "tooltip") | |
770 end | 786 end |
771 @lines << output | 787 @lines << output |
772 output | 788 output |
773 end | 789 end |
774 | 790 |
775 def pdf_task(params, coords, options={}) | 791 def pdf_task(params, coords, options={}) |
776 height = options[:height] || 2 | 792 height = options[:height] || 2 |
777 | |
778 # Renders the task bar, with progress and late | 793 # Renders the task bar, with progress and late |
779 if coords[:bar_start] && coords[:bar_end] | 794 if coords[:bar_start] && coords[:bar_end] |
780 params[:pdf].SetY(params[:top]+1.5) | 795 params[:pdf].SetY(params[:top] + 1.5) |
781 params[:pdf].SetX(params[:subject_width] + coords[:bar_start]) | 796 params[:pdf].SetX(params[:subject_width] + coords[:bar_start]) |
782 params[:pdf].SetFillColor(200,200,200) | 797 params[:pdf].SetFillColor(200, 200, 200) |
783 params[:pdf].RDMCell(coords[:bar_end] - coords[:bar_start], height, "", 0, 0, "", 1) | 798 params[:pdf].RDMCell(coords[:bar_end] - coords[:bar_start], height, "", 0, 0, "", 1) |
784 | |
785 if coords[:bar_late_end] | 799 if coords[:bar_late_end] |
786 params[:pdf].SetY(params[:top]+1.5) | 800 params[:pdf].SetY(params[:top] + 1.5) |
787 params[:pdf].SetX(params[:subject_width] + coords[:bar_start]) | 801 params[:pdf].SetX(params[:subject_width] + coords[:bar_start]) |
788 params[:pdf].SetFillColor(255,100,100) | 802 params[:pdf].SetFillColor(255, 100, 100) |
789 params[:pdf].RDMCell(coords[:bar_late_end] - coords[:bar_start], height, "", 0, 0, "", 1) | 803 params[:pdf].RDMCell(coords[:bar_late_end] - coords[:bar_start], height, "", 0, 0, "", 1) |
790 end | 804 end |
791 if coords[:bar_progress_end] | 805 if coords[:bar_progress_end] |
792 params[:pdf].SetY(params[:top]+1.5) | 806 params[:pdf].SetY(params[:top] + 1.5) |
793 params[:pdf].SetX(params[:subject_width] + coords[:bar_start]) | 807 params[:pdf].SetX(params[:subject_width] + coords[:bar_start]) |
794 params[:pdf].SetFillColor(90,200,90) | 808 params[:pdf].SetFillColor(90, 200, 90) |
795 params[:pdf].RDMCell(coords[:bar_progress_end] - coords[:bar_start], height, "", 0, 0, "", 1) | 809 params[:pdf].RDMCell(coords[:bar_progress_end] - coords[:bar_start], height, "", 0, 0, "", 1) |
796 end | 810 end |
797 end | 811 end |
798 # Renders the markers | 812 # Renders the markers |
799 if options[:markers] | 813 if options[:markers] |
800 if coords[:start] | 814 if coords[:start] |
801 params[:pdf].SetY(params[:top] + 1) | 815 params[:pdf].SetY(params[:top] + 1) |
802 params[:pdf].SetX(params[:subject_width] + coords[:start] - 1) | 816 params[:pdf].SetX(params[:subject_width] + coords[:start] - 1) |
803 params[:pdf].SetFillColor(50,50,200) | 817 params[:pdf].SetFillColor(50, 50, 200) |
804 params[:pdf].RDMCell(2, 2, "", 0, 0, "", 1) | 818 params[:pdf].RDMCell(2, 2, "", 0, 0, "", 1) |
805 end | 819 end |
806 if coords[:end] | 820 if coords[:end] |
807 params[:pdf].SetY(params[:top] + 1) | 821 params[:pdf].SetY(params[:top] + 1) |
808 params[:pdf].SetX(params[:subject_width] + coords[:end] - 1) | 822 params[:pdf].SetX(params[:subject_width] + coords[:end] - 1) |
809 params[:pdf].SetFillColor(50,50,200) | 823 params[:pdf].SetFillColor(50, 50, 200) |
810 params[:pdf].RDMCell(2, 2, "", 0, 0, "", 1) | 824 params[:pdf].RDMCell(2, 2, "", 0, 0, "", 1) |
811 end | 825 end |
812 end | 826 end |
813 # Renders the label on the right | 827 # Renders the label on the right |
814 if options[:label] | 828 if options[:label] |
817 end | 831 end |
818 end | 832 end |
819 | 833 |
820 def image_task(params, coords, options={}) | 834 def image_task(params, coords, options={}) |
821 height = options[:height] || 6 | 835 height = options[:height] || 6 |
822 | |
823 # Renders the task bar, with progress and late | 836 # Renders the task bar, with progress and late |
824 if coords[:bar_start] && coords[:bar_end] | 837 if coords[:bar_start] && coords[:bar_end] |
825 params[:image].fill('#aaa') | 838 params[:image].fill('#aaa') |
826 params[:image].rectangle(params[:subject_width] + coords[:bar_start], params[:top], params[:subject_width] + coords[:bar_end], params[:top] - height) | 839 params[:image].rectangle(params[:subject_width] + coords[:bar_start], |
827 | 840 params[:top], |
841 params[:subject_width] + coords[:bar_end], | |
842 params[:top] - height) | |
828 if coords[:bar_late_end] | 843 if coords[:bar_late_end] |
829 params[:image].fill('#f66') | 844 params[:image].fill('#f66') |
830 params[:image].rectangle(params[:subject_width] + coords[:bar_start], params[:top], params[:subject_width] + coords[:bar_late_end], params[:top] - height) | 845 params[:image].rectangle(params[:subject_width] + coords[:bar_start], |
846 params[:top], | |
847 params[:subject_width] + coords[:bar_late_end], | |
848 params[:top] - height) | |
831 end | 849 end |
832 if coords[:bar_progress_end] | 850 if coords[:bar_progress_end] |
833 params[:image].fill('#00c600') | 851 params[:image].fill('#00c600') |
834 params[:image].rectangle(params[:subject_width] + coords[:bar_start], params[:top], params[:subject_width] + coords[:bar_progress_end], params[:top] - height) | 852 params[:image].rectangle(params[:subject_width] + coords[:bar_start], |
853 params[:top], | |
854 params[:subject_width] + coords[:bar_progress_end], | |
855 params[:top] - height) | |
835 end | 856 end |
836 end | 857 end |
837 # Renders the markers | 858 # Renders the markers |
838 if options[:markers] | 859 if options[:markers] |
839 if coords[:start] | 860 if coords[:start] |
840 x = params[:subject_width] + coords[:start] | 861 x = params[:subject_width] + coords[:start] |
841 y = params[:top] - height / 2 | 862 y = params[:top] - height / 2 |
842 params[:image].fill('blue') | 863 params[:image].fill('blue') |
843 params[:image].polygon(x-4, y, x, y-4, x+4, y, x, y+4) | 864 params[:image].polygon(x - 4, y, x, y - 4, x + 4, y, x, y + 4) |
844 end | 865 end |
845 if coords[:end] | 866 if coords[:end] |
846 x = params[:subject_width] + coords[:end] + params[:zoom] | 867 x = params[:subject_width] + coords[:end] + params[:zoom] |
847 y = params[:top] - height / 2 | 868 y = params[:top] - height / 2 |
848 params[:image].fill('blue') | 869 params[:image].fill('blue') |
849 params[:image].polygon(x-4, y, x, y-4, x+4, y, x, y+4) | 870 params[:image].polygon(x - 4, y, x, y - 4, x + 4, y, x, y + 4) |
850 end | 871 end |
851 end | 872 end |
852 # Renders the label on the right | 873 # Renders the label on the right |
853 if options[:label] | 874 if options[:label] |
854 params[:image].fill('black') | 875 params[:image].fill('black') |
855 params[:image].text(params[:subject_width] + (coords[:bar_end] || 0) + 5,params[:top] + 1, options[:label]) | 876 params[:image].text(params[:subject_width] + (coords[:bar_end] || 0) + 5, |
877 params[:top] + 1, | |
878 options[:label]) | |
856 end | 879 end |
857 end | 880 end |
858 end | 881 end |
859 end | 882 end |
860 end | 883 end |