Mercurial > hg > soundsoftware-site
comparison lib/redmine/helpers/gantt.rb @ 441:cbce1fd3b1b7 redmine-1.2
Update to Redmine 1.2-stable branch (Redmine SVN rev 6000)
author | Chris Cannam |
---|---|
date | Mon, 06 Jun 2011 14:24:13 +0100 |
parents | 051f544170fe |
children | cbb26bc654de |
comparison
equal
deleted
inserted
replaced
245:051f544170fe | 441:cbce1fd3b1b7 |
---|---|
1 # Redmine - project management software | 1 # Redmine - project management software |
2 # Copyright (C) 2006-2008 Jean-Philippe Lang | 2 # Copyright (C) 2006-2011 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. |
8 # | 8 # |
9 # This program is distributed in the hope that it will be useful, | 9 # This program is distributed in the hope that it will be useful, |
10 # but WITHOUT ANY WARRANTY; without even the implied warranty of | 10 # but WITHOUT ANY WARRANTY; without even the implied warranty of |
11 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the | 11 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the |
12 # GNU General Public License for more details. | 12 # GNU General Public License for more details. |
13 # | 13 # |
14 # You should have received a copy of the GNU General Public License | 14 # You should have received a copy of the GNU General Public License |
15 # along with this program; if not, write to the Free Software | 15 # along with this program; if not, write to the Free Software |
16 # Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. | 16 # Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. |
17 | 17 |
18 module Redmine | 18 module Redmine |
36 | 36 |
37 attr_reader :year_from, :month_from, :date_from, :date_to, :zoom, :months, :truncated, :max_rows | 37 attr_reader :year_from, :month_from, :date_from, :date_to, :zoom, :months, :truncated, :max_rows |
38 attr_accessor :query | 38 attr_accessor :query |
39 attr_accessor :project | 39 attr_accessor :project |
40 attr_accessor :view | 40 attr_accessor :view |
41 | 41 |
42 def initialize(options={}) | 42 def initialize(options={}) |
43 options = options.dup | 43 options = options.dup |
44 | 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 | 56 |
57 zoom = (options[:zoom] || User.current.pref[:gantt_zoom]).to_i | 57 zoom = (options[:zoom] || User.current.pref[:gantt_zoom]).to_i |
58 @zoom = (zoom > 0 && zoom < 5) ? zoom : 2 | 58 @zoom = (zoom > 0 && zoom < 5) ? zoom : 2 |
59 months = (options[:months] || User.current.pref[:gantt_months]).to_i | 59 months = (options[:months] || User.current.pref[:gantt_months]).to_i |
60 @months = (months > 0 && months < 25) ? months : 6 | 60 @months = (months > 0 && months < 25) ? months : 6 |
61 | 61 |
62 # Save gantt parameters as user preference (zoom and months count) | 62 # 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])) | 63 if (User.current.logged? && (@zoom != User.current.pref[:gantt_zoom] || @months != User.current.pref[:gantt_months])) |
64 User.current.pref[:gantt_zoom], User.current.pref[:gantt_months] = @zoom, @months | 64 User.current.pref[:gantt_zoom], User.current.pref[:gantt_months] = @zoom, @months |
65 User.current.preference.save | 65 User.current.preference.save |
66 end | 66 end |
67 | 67 |
68 @date_from = Date.civil(@year_from, @month_from, 1) | 68 @date_from = Date.civil(@year_from, @month_from, 1) |
69 @date_to = (@date_from >> @months) - 1 | 69 @date_to = (@date_from >> @months) - 1 |
70 | 70 |
71 @subjects = '' | 71 @subjects = '' |
72 @lines = '' | 72 @lines = '' |
73 @number_of_rows = nil | 73 @number_of_rows = nil |
74 | 74 |
75 @issue_ancestors = [] | 75 @issue_ancestors = [] |
76 | 76 |
77 @truncated = false | 77 @truncated = false |
78 if options.has_key?(:max_rows) | 78 if options.has_key?(:max_rows) |
79 @max_rows = options[:max_rows] | 79 @max_rows = options[:max_rows] |
80 else | 80 else |
81 @max_rows = Setting.gantt_items_limit.blank? ? nil : Setting.gantt_items_limit.to_i | 81 @max_rows = Setting.gantt_items_limit.blank? ? nil : Setting.gantt_items_limit.to_i |
83 end | 83 end |
84 | 84 |
85 def common_params | 85 def common_params |
86 { :controller => 'gantts', :action => 'show', :project_id => @project } | 86 { :controller => 'gantts', :action => 'show', :project_id => @project } |
87 end | 87 end |
88 | 88 |
89 def params | 89 def params |
90 common_params.merge({ :zoom => zoom, :year => year_from, :month => month_from, :months => months }) | 90 common_params.merge({ :zoom => zoom, :year => year_from, :month => month_from, :months => months }) |
91 end | 91 end |
92 | 92 |
93 def params_previous | 93 def params_previous |
94 common_params.merge({:year => (date_from << months).year, :month => (date_from << months).month, :zoom => zoom, :months => months }) | 94 common_params.merge({:year => (date_from << months).year, :month => (date_from << months).month, :zoom => zoom, :months => months }) |
95 end | 95 end |
96 | 96 |
97 def params_next | 97 def params_next |
98 common_params.merge({:year => (date_from >> months).year, :month => (date_from >> months).month, :zoom => zoom, :months => months }) | 98 common_params.merge({:year => (date_from >> months).year, :month => (date_from >> months).month, :zoom => zoom, :months => months }) |
99 end | 99 end |
100 | 100 |
101 ### Extracted from the HTML view/helpers | |
102 # 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 |
103 def number_of_rows | 102 def number_of_rows |
104 return @number_of_rows if @number_of_rows | 103 return @number_of_rows if @number_of_rows |
105 | 104 |
106 rows = if @project | 105 rows = projects.inject(0) {|total, p| total += number_of_rows_on_project(p)} |
107 number_of_rows_on_project(@project) | |
108 else | |
109 Project.roots.visible.has_module('issue_tracking').inject(0) do |total, project| | |
110 total += number_of_rows_on_project(project) | |
111 end | |
112 end | |
113 | |
114 rows > @max_rows ? @max_rows : rows | 106 rows > @max_rows ? @max_rows : rows |
115 end | 107 end |
116 | 108 |
117 # Returns the number of rows that will be used to list a project on | 109 # Returns the number of rows that will be used to list a project on |
118 # the Gantt chart. This will recurse for each subproject. | 110 # the Gantt chart. This will recurse for each subproject. |
119 def number_of_rows_on_project(project) | 111 def number_of_rows_on_project(project) |
120 # Remove the project requirement for Versions because it will | 112 return 0 unless projects.include?(project) |
121 # restrict issues to only be on the current project. This | 113 |
122 # ends up missing issues which are assigned to shared versions. | |
123 @query.project = nil if @query.project | |
124 | |
125 # One Root project | |
126 count = 1 | 114 count = 1 |
127 # Issues without a Version | 115 count += project_issues(project).size |
128 count += project.issues.for_gantt.without_version.with_query(@query).count | 116 count += project_versions(project).size |
129 | |
130 # Versions | |
131 count += project.versions.count | |
132 | |
133 # Issues on the Versions | |
134 project.versions.each do |version| | |
135 count += version.fixed_issues.for_gantt.with_query(@query).count | |
136 end | |
137 | |
138 # Subprojects | |
139 project.children.visible.has_module('issue_tracking').each do |subproject| | |
140 count += number_of_rows_on_project(subproject) | |
141 end | |
142 | |
143 count | 117 count |
144 end | 118 end |
145 | 119 |
146 # Renders the subjects of the Gantt chart, the left side. | 120 # Renders the subjects of the Gantt chart, the left side. |
147 def subjects(options={}) | 121 def subjects(options={}) |
152 # Renders the lines of the Gantt chart, the right side | 126 # Renders the lines of the Gantt chart, the right side |
153 def lines(options={}) | 127 def lines(options={}) |
154 render(options.merge(:only => :lines)) unless @lines_rendered | 128 render(options.merge(:only => :lines)) unless @lines_rendered |
155 @lines | 129 @lines |
156 end | 130 end |
157 | 131 |
132 # Returns issues that will be rendered | |
133 def issues | |
134 @issues ||= @query.issues( | |
135 :include => [:assigned_to, :tracker, :priority, :category, :fixed_version], | |
136 :order => "#{Project.table_name}.lft ASC, #{Issue.table_name}.id ASC", | |
137 :limit => @max_rows | |
138 ) | |
139 end | |
140 | |
141 # Return all the project nodes that will be displayed | |
142 def projects | |
143 return @projects if @projects | |
144 | |
145 ids = issues.collect(&:project).uniq.collect(&:id) | |
146 if ids.any? | |
147 # All issues projects and their visible ancestors | |
148 @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", | |
150 :conditions => ["child.id IN (?)", ids], | |
151 :order => "#{Project.table_name}.lft ASC" | |
152 ).uniq | |
153 else | |
154 @projects = [] | |
155 end | |
156 end | |
157 | |
158 # Returns the issues that belong to +project+ | |
159 def project_issues(project) | |
160 @issues_by_project ||= issues.group_by(&:project) | |
161 @issues_by_project[project] || [] | |
162 end | |
163 | |
164 # Returns the distinct versions of the issues that belong to +project+ | |
165 def project_versions(project) | |
166 project_issues(project).collect(&:fixed_version).compact.uniq | |
167 end | |
168 | |
169 # Returns the issues that belong to +project+ and are assigned to +version+ | |
170 def version_issues(project, version) | |
171 project_issues(project).select {|issue| issue.fixed_version == version} | |
172 end | |
173 | |
158 def render(options={}) | 174 def render(options={}) |
159 options = {:indent => 4, :render => :subject, :format => :html}.merge(options) | 175 options = {:top => 0, :top_increment => 20, :indent_increment => 20, :render => :subject, :format => :html}.merge(options) |
160 | 176 indent = options[:indent] || 4 |
177 | |
161 @subjects = '' unless options[:only] == :lines | 178 @subjects = '' unless options[:only] == :lines |
162 @lines = '' unless options[:only] == :subjects | 179 @lines = '' unless options[:only] == :subjects |
163 @number_of_rows = 0 | 180 @number_of_rows = 0 |
164 | 181 |
165 if @project | 182 Project.project_tree(projects) do |project, level| |
166 render_project(@project, options) | 183 options[:indent] = indent + level * options[:indent_increment] |
167 else | 184 render_project(project, options) |
168 Project.roots.visible.has_module('issue_tracking').each do |project| | 185 break if abort? |
169 render_project(project, options) | 186 end |
170 break if abort? | 187 |
171 end | |
172 end | |
173 | |
174 @subjects_rendered = true unless options[:only] == :lines | 188 @subjects_rendered = true unless options[:only] == :lines |
175 @lines_rendered = true unless options[:only] == :subjects | 189 @lines_rendered = true unless options[:only] == :subjects |
176 | 190 |
177 render_end(options) | 191 render_end(options) |
178 end | 192 end |
179 | 193 |
180 def render_project(project, options={}) | 194 def render_project(project, options={}) |
181 options[:top] = 0 unless options.key? :top | |
182 options[:indent_increment] = 20 unless options.key? :indent_increment | |
183 options[:top_increment] = 20 unless options.key? :top_increment | |
184 | |
185 subject_for_project(project, options) unless options[:only] == :lines | 195 subject_for_project(project, options) unless options[:only] == :lines |
186 line_for_project(project, options) unless options[:only] == :subjects | 196 line_for_project(project, options) unless options[:only] == :subjects |
187 | 197 |
188 options[:top] += options[:top_increment] | 198 options[:top] += options[:top_increment] |
189 options[:indent] += options[:indent_increment] | 199 options[:indent] += options[:indent_increment] |
190 @number_of_rows += 1 | 200 @number_of_rows += 1 |
191 return if abort? | 201 return if abort? |
192 | 202 |
193 # Second, Issues without a version | 203 issues = project_issues(project).select {|i| i.fixed_version.nil?} |
194 issues = project.issues.for_gantt.without_version.with_query(@query).all(:limit => current_limit) | |
195 sort_issues!(issues) | 204 sort_issues!(issues) |
196 if issues | 205 if issues |
197 render_issues(issues, options) | 206 render_issues(issues, options) |
198 return if abort? | 207 return if abort? |
199 end | 208 end |
200 | 209 |
201 # Third, Versions | 210 versions = project_versions(project) |
202 project.versions.sort.each do |version| | 211 versions.each do |version| |
203 render_version(version, options) | 212 render_version(project, version, options) |
204 return if abort? | 213 end |
205 end | |
206 | |
207 # Fourth, subprojects | |
208 project.children.visible.has_module('issue_tracking').each do |project| | |
209 render_project(project, options) | |
210 return if abort? | |
211 end unless project.leaf? | |
212 | 214 |
213 # Remove indent to hit the next sibling | 215 # Remove indent to hit the next sibling |
214 options[:indent] -= options[:indent_increment] | 216 options[:indent] -= options[:indent_increment] |
215 end | 217 end |
216 | 218 |
217 def render_issues(issues, options={}) | 219 def render_issues(issues, options={}) |
218 @issue_ancestors = [] | 220 @issue_ancestors = [] |
219 | 221 |
220 issues.each do |i| | 222 issues.each do |i| |
221 subject_for_issue(i, options) unless options[:only] == :lines | 223 subject_for_issue(i, options) unless options[:only] == :lines |
222 line_for_issue(i, options) unless options[:only] == :subjects | 224 line_for_issue(i, options) unless options[:only] == :subjects |
223 | 225 |
224 options[:top] += options[:top_increment] | 226 options[:top] += options[:top_increment] |
225 @number_of_rows += 1 | 227 @number_of_rows += 1 |
226 break if abort? | 228 break if abort? |
227 end | 229 end |
228 | 230 |
229 options[:indent] -= (options[:indent_increment] * @issue_ancestors.size) | 231 options[:indent] -= (options[:indent_increment] * @issue_ancestors.size) |
230 end | 232 end |
231 | 233 |
232 def render_version(version, options={}) | 234 def render_version(project, version, options={}) |
233 # Version header | 235 # Version header |
234 subject_for_version(version, options) unless options[:only] == :lines | 236 subject_for_version(version, options) unless options[:only] == :lines |
235 line_for_version(version, options) unless options[:only] == :subjects | 237 line_for_version(version, options) unless options[:only] == :subjects |
236 | 238 |
237 options[:top] += options[:top_increment] | 239 options[:top] += options[:top_increment] |
238 @number_of_rows += 1 | 240 @number_of_rows += 1 |
239 return if abort? | 241 return if abort? |
240 | 242 |
241 # Remove the project requirement for Versions because it will | 243 issues = version_issues(project, version) |
242 # restrict issues to only be on the current project. This | |
243 # ends up missing issues which are assigned to shared versions. | |
244 @query.project = nil if @query.project | |
245 | |
246 issues = version.fixed_issues.for_gantt.with_query(@query).all(:limit => current_limit) | |
247 if issues | 244 if issues |
248 sort_issues!(issues) | 245 sort_issues!(issues) |
249 # Indent issues | 246 # Indent issues |
250 options[:indent] += options[:indent_increment] | 247 options[:indent] += options[:indent_increment] |
251 render_issues(issues, options) | 248 render_issues(issues, options) |
252 options[:indent] -= options[:indent_increment] | 249 options[:indent] -= options[:indent_increment] |
253 end | 250 end |
254 end | 251 end |
255 | 252 |
256 def render_end(options={}) | 253 def render_end(options={}) |
257 case options[:format] | 254 case options[:format] |
258 when :pdf | 255 when :pdf |
259 options[:pdf].Line(15, options[:top], PDF::TotalWidth, options[:top]) | 256 options[:pdf].Line(15, options[:top], PDF::TotalWidth, options[:top]) |
260 end | 257 end |
261 end | 258 end |
262 | 259 |
263 def subject_for_project(project, options) | 260 def subject_for_project(project, options) |
278 def line_for_project(project, options) | 275 def line_for_project(project, options) |
279 # Skip versions that don't have a start_date or due date | 276 # Skip versions that don't have a start_date or due date |
280 if project.is_a?(Project) && project.start_date && project.due_date | 277 if project.is_a?(Project) && project.start_date && project.due_date |
281 options[:zoom] ||= 1 | 278 options[:zoom] ||= 1 |
282 options[:g_width] ||= (self.date_to - self.date_from + 1) * options[:zoom] | 279 options[:g_width] ||= (self.date_to - self.date_from + 1) * options[:zoom] |
283 | 280 |
284 coords = coordinates(project.start_date, project.due_date, nil, options[:zoom]) | 281 coords = coordinates(project.start_date, project.due_date, nil, options[:zoom]) |
285 label = h(project) | 282 label = h(project) |
286 | 283 |
287 case options[:format] | 284 case options[:format] |
288 when :html | 285 when :html |
289 html_task(options, coords, :css => "project task", :label => label, :markers => true) | 286 html_task(options, coords, :css => "project task", :label => label, :markers => true) |
290 when :image | 287 when :image |
291 image_task(options, coords, :label => label, :markers => true, :height => 3) | 288 image_task(options, coords, :label => label, :markers => true, :height => 3) |
316 def line_for_version(version, options) | 313 def line_for_version(version, options) |
317 # Skip versions that don't have a start_date | 314 # Skip versions that don't have a start_date |
318 if version.is_a?(Version) && version.start_date && version.due_date | 315 if version.is_a?(Version) && version.start_date && version.due_date |
319 options[:zoom] ||= 1 | 316 options[:zoom] ||= 1 |
320 options[:g_width] ||= (self.date_to - self.date_from + 1) * options[:zoom] | 317 options[:g_width] ||= (self.date_to - self.date_from + 1) * options[:zoom] |
321 | 318 |
322 coords = coordinates(version.start_date, version.due_date, version.completed_pourcent, options[:zoom]) | 319 coords = coordinates(version.start_date, version.due_date, version.completed_pourcent, options[:zoom]) |
323 label = "#{h version } #{h version.completed_pourcent.to_i.to_s}%" | 320 label = "#{h version } #{h version.completed_pourcent.to_i.to_s}%" |
324 label = h("#{version.project} -") + label unless @project && @project == version.project | 321 label = h("#{version.project} -") + label unless @project && @project == version.project |
325 | 322 |
326 case options[:format] | 323 case options[:format] |
340 def subject_for_issue(issue, options) | 337 def subject_for_issue(issue, options) |
341 while @issue_ancestors.any? && !issue.is_descendant_of?(@issue_ancestors.last) | 338 while @issue_ancestors.any? && !issue.is_descendant_of?(@issue_ancestors.last) |
342 @issue_ancestors.pop | 339 @issue_ancestors.pop |
343 options[:indent] -= options[:indent_increment] | 340 options[:indent] -= options[:indent_increment] |
344 end | 341 end |
345 | 342 |
346 output = case options[:format] | 343 output = case options[:format] |
347 when :html | 344 when :html |
348 css_classes = '' | 345 css_classes = '' |
349 css_classes << ' issue-overdue' if issue.overdue? | 346 css_classes << ' issue-overdue' if issue.overdue? |
350 css_classes << ' issue-behind-schedule' if issue.behind_schedule? | 347 css_classes << ' issue-behind-schedule' if issue.behind_schedule? |
351 css_classes << ' icon icon-issue' unless Setting.gravatar_enabled? && issue.assigned_to | 348 css_classes << ' icon icon-issue' unless Setting.gravatar_enabled? && issue.assigned_to |
352 | 349 |
353 subject = "<span class='#{css_classes}'>" | 350 subject = "<span class='#{css_classes}'>" |
354 if issue.assigned_to.present? | 351 if issue.assigned_to.present? |
355 assigned_string = l(:field_assigned_to) + ": " + issue.assigned_to.name | 352 assigned_string = l(:field_assigned_to) + ": " + issue.assigned_to.name |
356 subject << view.avatar(issue.assigned_to, :class => 'gravatar icon-gravatar', :size => 10, :title => assigned_string).to_s | 353 subject << view.avatar(issue.assigned_to, :class => 'gravatar icon-gravatar', :size => 10, :title => assigned_string).to_s |
357 end | 354 end |
367 | 364 |
368 unless issue.leaf? | 365 unless issue.leaf? |
369 @issue_ancestors << issue | 366 @issue_ancestors << issue |
370 options[:indent] += options[:indent_increment] | 367 options[:indent] += options[:indent_increment] |
371 end | 368 end |
372 | 369 |
373 output | 370 output |
374 end | 371 end |
375 | 372 |
376 def line_for_issue(issue, options) | 373 def line_for_issue(issue, options) |
377 # Skip issues that don't have a due_before (due_date or version's due_date) | 374 # Skip issues that don't have a due_before (due_date or version's due_date) |
378 if issue.is_a?(Issue) && issue.due_before | 375 if issue.is_a?(Issue) && issue.due_before |
379 coords = coordinates(issue.start_date, issue.due_before, issue.done_ratio, options[:zoom]) | 376 coords = coordinates(issue.start_date, issue.due_before, issue.done_ratio, options[:zoom]) |
380 label = "#{ issue.status.name } #{ issue.done_ratio }%" | 377 label = "#{ issue.status.name } #{ issue.done_ratio }%" |
381 | 378 |
382 case options[:format] | 379 case options[:format] |
383 when :html | 380 when :html |
384 html_task(options, coords, :css => "task " + (issue.leaf? ? 'leaf' : 'parent'), :label => label, :issue => issue, :markers => !issue.leaf?) | 381 html_task(options, coords, :css => "task " + (issue.leaf? ? 'leaf' : 'parent'), :label => label, :issue => issue, :markers => !issue.leaf?) |
385 when :image | 382 when :image |
386 image_task(options, coords, :label => label) | 383 image_task(options, coords, :label => label) |
394 end | 391 end |
395 | 392 |
396 # Generates a gantt image | 393 # Generates a gantt image |
397 # Only defined if RMagick is avalaible | 394 # Only defined if RMagick is avalaible |
398 def to_image(format='PNG') | 395 def to_image(format='PNG') |
399 date_to = (@date_from >> @months)-1 | 396 date_to = (@date_from >> @months)-1 |
400 show_weeks = @zoom > 1 | 397 show_weeks = @zoom > 1 |
401 show_days = @zoom > 2 | 398 show_days = @zoom > 2 |
402 | 399 |
403 subject_width = 400 | 400 subject_width = 400 |
404 header_heigth = 18 | 401 header_height = 18 |
405 # width of one day in pixels | 402 # width of one day in pixels |
406 zoom = @zoom*2 | 403 zoom = @zoom*2 |
407 g_width = (@date_to - @date_from + 1)*zoom | 404 g_width = (@date_to - @date_from + 1)*zoom |
408 g_height = 20 * number_of_rows + 30 | 405 g_height = 20 * number_of_rows + 30 |
409 headers_heigth = (show_weeks ? 2*header_heigth : header_heigth) | 406 headers_height = (show_weeks ? 2*header_height : header_height) |
410 height = g_height + headers_heigth | 407 height = g_height + headers_height |
411 | 408 |
412 imgl = Magick::ImageList.new | 409 imgl = Magick::ImageList.new |
413 imgl.new_image(subject_width+g_width+1, height) | 410 imgl.new_image(subject_width+g_width+1, height) |
414 gc = Magick::Draw.new | 411 gc = Magick::Draw.new |
415 | 412 |
416 # Subjects | 413 # Subjects |
417 gc.stroke('transparent') | 414 gc.stroke('transparent') |
418 subjects(:image => gc, :top => (headers_heigth + 20), :indent => 4, :format => :image) | 415 subjects(:image => gc, :top => (headers_height + 20), :indent => 4, :format => :image) |
419 | 416 |
420 # Months headers | 417 # Months headers |
421 month_f = @date_from | 418 month_f = @date_from |
422 left = subject_width | 419 left = subject_width |
423 @months.times do | 420 @months.times do |
424 width = ((month_f >> 1) - month_f) * zoom | 421 width = ((month_f >> 1) - month_f) * zoom |
425 gc.fill('white') | 422 gc.fill('white') |
426 gc.stroke('grey') | 423 gc.stroke('grey') |
427 gc.stroke_width(1) | 424 gc.stroke_width(1) |
428 gc.rectangle(left, 0, left + width, height) | 425 gc.rectangle(left, 0, left + width, height) |
431 gc.stroke_width(1) | 428 gc.stroke_width(1) |
432 gc.text(left.round + 8, 14, "#{month_f.year}-#{month_f.month}") | 429 gc.text(left.round + 8, 14, "#{month_f.year}-#{month_f.month}") |
433 left = left + width | 430 left = left + width |
434 month_f = month_f >> 1 | 431 month_f = month_f >> 1 |
435 end | 432 end |
436 | 433 |
437 # Weeks headers | 434 # Weeks headers |
438 if show_weeks | 435 if show_weeks |
439 left = subject_width | 436 left = subject_width |
440 height = header_heigth | 437 height = header_height |
441 if @date_from.cwday == 1 | 438 if @date_from.cwday == 1 |
442 # date_from is monday | 439 # date_from is monday |
443 week_f = date_from | 440 week_f = date_from |
444 else | 441 else |
445 # find next monday after date_from | 442 # find next monday after date_from |
446 week_f = @date_from + (7 - @date_from.cwday + 1) | 443 week_f = @date_from + (7 - @date_from.cwday + 1) |
447 width = (7 - @date_from.cwday + 1) * zoom | 444 width = (7 - @date_from.cwday + 1) * zoom |
448 gc.fill('white') | 445 gc.fill('white') |
449 gc.stroke('grey') | 446 gc.stroke('grey') |
450 gc.stroke_width(1) | 447 gc.stroke_width(1) |
451 gc.rectangle(left, header_heigth, left + width, 2*header_heigth + g_height-1) | 448 gc.rectangle(left, header_height, left + width, 2*header_height + g_height-1) |
452 left = left + width | 449 left = left + width |
453 end | 450 end |
454 while week_f <= date_to | 451 while week_f <= date_to |
455 width = (week_f + 6 <= date_to) ? 7 * zoom : (date_to - week_f + 1) * zoom | 452 width = (week_f + 6 <= date_to) ? 7 * zoom : (date_to - week_f + 1) * zoom |
456 gc.fill('white') | 453 gc.fill('white') |
457 gc.stroke('grey') | 454 gc.stroke('grey') |
458 gc.stroke_width(1) | 455 gc.stroke_width(1) |
459 gc.rectangle(left.round, header_heigth, left.round + width, 2*header_heigth + g_height-1) | 456 gc.rectangle(left.round, header_height, left.round + width, 2*header_height + g_height-1) |
460 gc.fill('black') | 457 gc.fill('black') |
461 gc.stroke('transparent') | 458 gc.stroke('transparent') |
462 gc.stroke_width(1) | 459 gc.stroke_width(1) |
463 gc.text(left.round + 2, header_heigth + 14, week_f.cweek.to_s) | 460 gc.text(left.round + 2, header_height + 14, week_f.cweek.to_s) |
464 left = left + width | 461 left = left + width |
465 week_f = week_f+7 | 462 week_f = week_f+7 |
466 end | 463 end |
467 end | 464 end |
468 | 465 |
469 # Days details (week-end in grey) | 466 # Days details (week-end in grey) |
470 if show_days | 467 if show_days |
471 left = subject_width | 468 left = subject_width |
472 height = g_height + header_heigth - 1 | 469 height = g_height + header_height - 1 |
473 wday = @date_from.cwday | 470 wday = @date_from.cwday |
474 (date_to - @date_from + 1).to_i.times do | 471 (date_to - @date_from + 1).to_i.times do |
475 width = zoom | 472 width = zoom |
476 gc.fill(wday == 6 || wday == 7 ? '#eee' : 'white') | 473 gc.fill(wday == 6 || wday == 7 ? '#eee' : 'white') |
477 gc.stroke('#ddd') | 474 gc.stroke('#ddd') |
478 gc.stroke_width(1) | 475 gc.stroke_width(1) |
479 gc.rectangle(left, 2*header_heigth, left + width, 2*header_heigth + g_height-1) | 476 gc.rectangle(left, 2*header_height, left + width, 2*header_height + g_height-1) |
480 left = left + width | 477 left = left + width |
481 wday = wday + 1 | 478 wday = wday + 1 |
482 wday = 1 if wday > 7 | 479 wday = 1 if wday > 7 |
483 end | 480 end |
484 end | 481 end |
485 | 482 |
486 # border | 483 # border |
487 gc.fill('transparent') | 484 gc.fill('transparent') |
488 gc.stroke('grey') | 485 gc.stroke('grey') |
489 gc.stroke_width(1) | 486 gc.stroke_width(1) |
490 gc.rectangle(0, 0, subject_width+g_width, headers_heigth) | 487 gc.rectangle(0, 0, subject_width+g_width, headers_height) |
491 gc.stroke('black') | 488 gc.stroke('black') |
492 gc.rectangle(0, 0, subject_width+g_width, g_height+ headers_heigth-1) | 489 gc.rectangle(0, 0, subject_width+g_width, g_height+ headers_height-1) |
493 | 490 |
494 # content | 491 # content |
495 top = headers_heigth + 20 | 492 top = headers_height + 20 |
496 | 493 |
497 gc.stroke('transparent') | 494 gc.stroke('transparent') |
498 lines(:image => gc, :top => top, :zoom => zoom, :subject_width => subject_width, :format => :image) | 495 lines(:image => gc, :top => top, :zoom => zoom, :subject_width => subject_width, :format => :image) |
499 | 496 |
500 # today red line | 497 # today red line |
501 if Date.today >= @date_from and Date.today <= date_to | 498 if Date.today >= @date_from and Date.today <= date_to |
502 gc.stroke('red') | 499 gc.stroke('red') |
503 x = (Date.today-@date_from+1)*zoom + subject_width | 500 x = (Date.today-@date_from+1)*zoom + subject_width |
504 gc.line(x, headers_heigth, x, headers_heigth + g_height-1) | 501 gc.line(x, headers_height, x, headers_height + g_height-1) |
505 end | 502 end |
506 | 503 |
507 gc.draw(imgl) | 504 gc.draw(imgl) |
508 imgl.format = format | 505 imgl.format = format |
509 imgl.to_blob | 506 imgl.to_blob |
510 end if Object.const_defined?(:Magick) | 507 end if Object.const_defined?(:Magick) |
511 | 508 |
512 def to_pdf | 509 def to_pdf |
513 pdf = ::Redmine::Export::PDF::IFPDF.new(current_language) | 510 pdf = ::Redmine::Export::PDF::ITCPDF.new(current_language) |
514 pdf.SetTitle("#{l(:label_gantt)} #{project}") | 511 pdf.SetTitle("#{l(:label_gantt)} #{project}") |
515 pdf.AliasNbPages | 512 pdf.alias_nb_pages |
516 pdf.footer_date = format_date(Date.today) | 513 pdf.footer_date = format_date(Date.today) |
517 pdf.AddPage("L") | 514 pdf.AddPage("L") |
518 pdf.SetFontStyle('B',12) | 515 pdf.SetFontStyle('B',12) |
519 pdf.SetX(15) | 516 pdf.SetX(15) |
520 pdf.Cell(PDF::LeftPaneWidth, 20, project.to_s) | 517 pdf.RDMCell(PDF::LeftPaneWidth, 20, project.to_s) |
521 pdf.Ln | 518 pdf.Ln |
522 pdf.SetFontStyle('B',9) | 519 pdf.SetFontStyle('B',9) |
523 | 520 |
524 subject_width = PDF::LeftPaneWidth | 521 subject_width = PDF::LeftPaneWidth |
525 header_heigth = 5 | 522 header_height = 5 |
526 | 523 |
527 headers_heigth = header_heigth | 524 headers_height = header_height |
528 show_weeks = false | 525 show_weeks = false |
529 show_days = false | 526 show_days = false |
530 | 527 |
531 if self.months < 7 | 528 if self.months < 7 |
532 show_weeks = true | 529 show_weeks = true |
533 headers_heigth = 2*header_heigth | 530 headers_height = 2*header_height |
534 if self.months < 3 | 531 if self.months < 3 |
535 show_days = true | 532 show_days = true |
536 headers_heigth = 3*header_heigth | 533 headers_height = 3*header_height |
537 end | 534 end |
538 end | 535 end |
539 | 536 |
540 g_width = PDF.right_pane_width | 537 g_width = PDF.right_pane_width |
541 zoom = (g_width) / (self.date_to - self.date_from + 1) | 538 zoom = (g_width) / (self.date_to - self.date_from + 1) |
542 g_height = 120 | 539 g_height = 120 |
543 t_height = g_height + headers_heigth | 540 t_height = g_height + headers_height |
544 | 541 |
545 y_start = pdf.GetY | 542 y_start = pdf.GetY |
546 | 543 |
547 # Months headers | 544 # Months headers |
548 month_f = self.date_from | 545 month_f = self.date_from |
549 left = subject_width | 546 left = subject_width |
550 height = header_heigth | 547 height = header_height |
551 self.months.times do | 548 self.months.times do |
552 width = ((month_f >> 1) - month_f) * zoom | 549 width = ((month_f >> 1) - month_f) * zoom |
553 pdf.SetY(y_start) | 550 pdf.SetY(y_start) |
554 pdf.SetX(left) | 551 pdf.SetX(left) |
555 pdf.Cell(width, height, "#{month_f.year}-#{month_f.month}", "LTR", 0, "C") | 552 pdf.RDMCell(width, height, "#{month_f.year}-#{month_f.month}", "LTR", 0, "C") |
556 left = left + width | 553 left = left + width |
557 month_f = month_f >> 1 | 554 month_f = month_f >> 1 |
558 end | 555 end |
559 | 556 |
560 # Weeks headers | 557 # Weeks headers |
561 if show_weeks | 558 if show_weeks |
562 left = subject_width | 559 left = subject_width |
563 height = header_heigth | 560 height = header_height |
564 if self.date_from.cwday == 1 | 561 if self.date_from.cwday == 1 |
565 # self.date_from is monday | 562 # self.date_from is monday |
566 week_f = self.date_from | 563 week_f = self.date_from |
567 else | 564 else |
568 # find next monday after self.date_from | 565 # find next monday after self.date_from |
569 week_f = self.date_from + (7 - self.date_from.cwday + 1) | 566 week_f = self.date_from + (7 - self.date_from.cwday + 1) |
570 width = (7 - self.date_from.cwday + 1) * zoom-1 | 567 width = (7 - self.date_from.cwday + 1) * zoom-1 |
571 pdf.SetY(y_start + header_heigth) | 568 pdf.SetY(y_start + header_height) |
572 pdf.SetX(left) | 569 pdf.SetX(left) |
573 pdf.Cell(width + 1, height, "", "LTR") | 570 pdf.RDMCell(width + 1, height, "", "LTR") |
574 left = left + width+1 | 571 left = left + width+1 |
575 end | 572 end |
576 while week_f <= self.date_to | 573 while week_f <= self.date_to |
577 width = (week_f + 6 <= self.date_to) ? 7 * zoom : (self.date_to - week_f + 1) * zoom | 574 width = (week_f + 6 <= self.date_to) ? 7 * zoom : (self.date_to - week_f + 1) * zoom |
578 pdf.SetY(y_start + header_heigth) | 575 pdf.SetY(y_start + header_height) |
579 pdf.SetX(left) | 576 pdf.SetX(left) |
580 pdf.Cell(width, height, (width >= 5 ? week_f.cweek.to_s : ""), "LTR", 0, "C") | 577 pdf.RDMCell(width, height, (width >= 5 ? week_f.cweek.to_s : ""), "LTR", 0, "C") |
581 left = left + width | 578 left = left + width |
582 week_f = week_f+7 | 579 week_f = week_f+7 |
583 end | 580 end |
584 end | 581 end |
585 | 582 |
586 # Days headers | 583 # Days headers |
587 if show_days | 584 if show_days |
588 left = subject_width | 585 left = subject_width |
589 height = header_heigth | 586 height = header_height |
590 wday = self.date_from.cwday | 587 wday = self.date_from.cwday |
591 pdf.SetFontStyle('B',7) | 588 pdf.SetFontStyle('B',7) |
592 (self.date_to - self.date_from + 1).to_i.times do | 589 (self.date_to - self.date_from + 1).to_i.times do |
593 width = zoom | 590 width = zoom |
594 pdf.SetY(y_start + 2 * header_heigth) | 591 pdf.SetY(y_start + 2 * header_height) |
595 pdf.SetX(left) | 592 pdf.SetX(left) |
596 pdf.Cell(width, height, day_name(wday).first, "LTR", 0, "C") | 593 pdf.RDMCell(width, height, day_name(wday).first, "LTR", 0, "C") |
597 left = left + width | 594 left = left + width |
598 wday = wday + 1 | 595 wday = wday + 1 |
599 wday = 1 if wday > 7 | 596 wday = 1 if wday > 7 |
600 end | 597 end |
601 end | 598 end |
602 | 599 |
603 pdf.SetY(y_start) | 600 pdf.SetY(y_start) |
604 pdf.SetX(15) | 601 pdf.SetX(15) |
605 pdf.Cell(subject_width+g_width-15, headers_heigth, "", 1) | 602 pdf.RDMCell(subject_width+g_width-15, headers_height, "", 1) |
606 | 603 |
607 # Tasks | 604 # Tasks |
608 top = headers_heigth + y_start | 605 top = headers_height + y_start |
609 options = { | 606 options = { |
610 :top => top, | 607 :top => top, |
611 :zoom => zoom, | 608 :zoom => zoom, |
612 :subject_width => subject_width, | 609 :subject_width => subject_width, |
613 :g_width => g_width, | 610 :g_width => g_width, |
618 :pdf => pdf | 615 :pdf => pdf |
619 } | 616 } |
620 render(options) | 617 render(options) |
621 pdf.Output | 618 pdf.Output |
622 end | 619 end |
623 | 620 |
624 private | 621 private |
625 | 622 |
626 def coordinates(start_date, end_date, progress, zoom=nil) | 623 def coordinates(start_date, end_date, progress, zoom=nil) |
627 zoom ||= @zoom | 624 zoom ||= @zoom |
628 | 625 |
629 coords = {} | 626 coords = {} |
630 if start_date && end_date && start_date < self.date_to && end_date > self.date_from | 627 if start_date && end_date && start_date < self.date_to && end_date > self.date_from |
631 if start_date > self.date_from | 628 if start_date > self.date_from |
632 coords[:start] = start_date - self.date_from | 629 coords[:start] = start_date - self.date_from |
633 coords[:bar_start] = start_date - self.date_from | 630 coords[:bar_start] = start_date - self.date_from |
638 coords[:end] = end_date - self.date_from | 635 coords[:end] = end_date - self.date_from |
639 coords[:bar_end] = end_date - self.date_from + 1 | 636 coords[:bar_end] = end_date - self.date_from + 1 |
640 else | 637 else |
641 coords[:bar_end] = self.date_to - self.date_from + 1 | 638 coords[:bar_end] = self.date_to - self.date_from + 1 |
642 end | 639 end |
643 | 640 |
644 if progress | 641 if progress |
645 progress_date = start_date + (end_date - start_date) * (progress / 100.0) | 642 progress_date = start_date + (end_date - start_date + 1) * (progress / 100.0) |
646 if progress_date > self.date_from && progress_date > start_date | 643 if progress_date > self.date_from && progress_date > start_date |
647 if progress_date < self.date_to | 644 if progress_date < self.date_to |
648 coords[:bar_progress_end] = progress_date - self.date_from + 1 | 645 coords[:bar_progress_end] = progress_date - self.date_from |
649 else | 646 else |
650 coords[:bar_progress_end] = self.date_to - self.date_from + 1 | 647 coords[:bar_progress_end] = self.date_to - self.date_from + 1 |
651 end | 648 end |
652 end | 649 end |
653 | 650 |
654 if progress_date < Date.today | 651 if progress_date < Date.today |
655 late_date = [Date.today, end_date].min | 652 late_date = [Date.today, end_date].min |
656 if late_date > self.date_from && late_date > start_date | 653 if late_date > self.date_from && late_date > start_date |
657 if late_date < self.date_to | 654 if late_date < self.date_to |
658 coords[:bar_late_end] = late_date - self.date_from + 1 | 655 coords[:bar_late_end] = late_date - self.date_from + 1 |
661 end | 658 end |
662 end | 659 end |
663 end | 660 end |
664 end | 661 end |
665 end | 662 end |
666 | 663 |
667 # Transforms dates into pixels witdh | 664 # Transforms dates into pixels witdh |
668 coords.keys.each do |key| | 665 coords.keys.each do |key| |
669 coords[key] = (coords[key] * zoom).floor | 666 coords[key] = (coords[key] * zoom).floor |
670 end | 667 end |
671 coords | 668 coords |
673 | 670 |
674 # Sorts a collection of issues by start_date, due_date, id for gantt rendering | 671 # Sorts a collection of issues by start_date, due_date, id for gantt rendering |
675 def sort_issues!(issues) | 672 def sort_issues!(issues) |
676 issues.sort! { |a, b| gantt_issue_compare(a, b, issues) } | 673 issues.sort! { |a, b| gantt_issue_compare(a, b, issues) } |
677 end | 674 end |
678 | 675 |
679 # TODO: top level issues should be sorted by start date | 676 # TODO: top level issues should be sorted by start date |
680 def gantt_issue_compare(x, y, issues) | 677 def gantt_issue_compare(x, y, issues) |
681 if x.root_id == y.root_id | 678 if x.root_id == y.root_id |
682 x.lft <=> y.lft | 679 x.lft <=> y.lft |
683 else | 680 else |
684 x.root_id <=> y.root_id | 681 x.root_id <=> y.root_id |
685 end | 682 end |
686 end | 683 end |
687 | 684 |
688 def current_limit | 685 def current_limit |
689 if @max_rows | 686 if @max_rows |
690 @max_rows - @number_of_rows | 687 @max_rows - @number_of_rows |
691 else | 688 else |
692 nil | 689 nil |
693 end | 690 end |
694 end | 691 end |
695 | 692 |
696 def abort? | 693 def abort? |
697 if @max_rows && @number_of_rows >= @max_rows | 694 if @max_rows && @number_of_rows >= @max_rows |
698 @truncated = true | 695 @truncated = true |
699 end | 696 end |
700 end | 697 end |
701 | 698 |
702 def pdf_new_page?(options) | 699 def pdf_new_page?(options) |
703 if options[:top] > 180 | 700 if options[:top] > 180 |
704 options[:pdf].Line(15, options[:top], PDF::TotalWidth, options[:top]) | 701 options[:pdf].Line(15, options[:top], PDF::TotalWidth, options[:top]) |
705 options[:pdf].AddPage("L") | 702 options[:pdf].AddPage("L") |
706 options[:top] = 15 | 703 options[:top] = 15 |
707 options[:pdf].Line(15, options[:top] - 0.1, PDF::TotalWidth, options[:top] - 0.1) | 704 options[:pdf].Line(15, options[:top] - 0.1, PDF::TotalWidth, options[:top] - 0.1) |
708 end | 705 end |
709 end | 706 end |
710 | 707 |
711 def html_subject(params, subject, options={}) | 708 def html_subject(params, subject, options={}) |
712 style = "position: absolute;top:#{params[:top]}px;left:#{params[:indent]}px;" | 709 style = "position: absolute;top:#{params[:top]}px;left:#{params[:indent]}px;" |
713 style << "width:#{params[:subject_width] - params[:indent]}px;" if params[:subject_width] | 710 style << "width:#{params[:subject_width] - params[:indent]}px;" if params[:subject_width] |
714 | 711 |
715 output = view.content_tag 'div', subject, :class => options[:css], :style => style, :title => options[:title] | 712 output = view.content_tag 'div', subject, :class => options[:css], :style => style, :title => options[:title] |
716 @subjects << output | 713 @subjects << output |
717 output | 714 output |
718 end | 715 end |
719 | 716 |
720 def pdf_subject(params, subject, options={}) | 717 def pdf_subject(params, subject, options={}) |
721 params[:pdf].SetY(params[:top]) | 718 params[:pdf].SetY(params[:top]) |
722 params[:pdf].SetX(15) | 719 params[:pdf].SetX(15) |
723 | 720 |
724 char_limit = PDF::MaxCharactorsForSubject - params[:indent] | 721 char_limit = PDF::MaxCharactorsForSubject - params[:indent] |
725 params[:pdf].Cell(params[:subject_width]-15, 5, (" " * params[:indent]) + subject.to_s.sub(/^(.{#{char_limit}}[^\s]*\s).*$/, '\1 (...)'), "LR") | 722 params[:pdf].RDMCell(params[:subject_width]-15, 5, (" " * params[:indent]) + subject.to_s.sub(/^(.{#{char_limit}}[^\s]*\s).*$/, '\1 (...)'), "LR") |
726 | 723 |
727 params[:pdf].SetY(params[:top]) | 724 params[:pdf].SetY(params[:top]) |
728 params[:pdf].SetX(params[:subject_width]) | 725 params[:pdf].SetX(params[:subject_width]) |
729 params[:pdf].Cell(params[:g_width], 5, "", "LR") | 726 params[:pdf].RDMCell(params[:g_width], 5, "", "LR") |
730 end | 727 end |
731 | 728 |
732 def image_subject(params, subject, options={}) | 729 def image_subject(params, subject, options={}) |
733 params[:image].fill('black') | 730 params[:image].fill('black') |
734 params[:image].stroke('transparent') | 731 params[:image].stroke('transparent') |
735 params[:image].stroke_width(1) | 732 params[:image].stroke_width(1) |
736 params[:image].text(params[:indent], params[:top] + 2, subject) | 733 params[:image].text(params[:indent], params[:top] + 2, subject) |
737 end | 734 end |
738 | 735 |
739 def html_task(params, coords, options={}) | 736 def html_task(params, coords, options={}) |
740 output = '' | 737 output = '' |
741 # Renders the task bar, with progress and late | 738 # Renders the task bar, with progress and late |
742 if coords[:bar_start] && coords[:bar_end] | 739 if coords[:bar_start] && coords[:bar_end] |
743 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>" | 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>" |
744 | 741 |
745 if coords[:bar_late_end] | 742 if coords[:bar_late_end] |
746 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>" | 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>" |
747 end | 744 end |
748 if coords[:bar_progress_end] | 745 if coords[:bar_progress_end] |
749 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>" | 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>" |
772 output << "</span></div>" | 769 output << "</span></div>" |
773 end | 770 end |
774 @lines << output | 771 @lines << output |
775 output | 772 output |
776 end | 773 end |
777 | 774 |
778 def pdf_task(params, coords, options={}) | 775 def pdf_task(params, coords, options={}) |
779 height = options[:height] || 2 | 776 height = options[:height] || 2 |
780 | 777 |
781 # Renders the task bar, with progress and late | 778 # Renders the task bar, with progress and late |
782 if coords[:bar_start] && coords[:bar_end] | 779 if coords[:bar_start] && coords[:bar_end] |
783 params[:pdf].SetY(params[:top]+1.5) | 780 params[:pdf].SetY(params[:top]+1.5) |
784 params[:pdf].SetX(params[:subject_width] + coords[:bar_start]) | 781 params[:pdf].SetX(params[:subject_width] + coords[:bar_start]) |
785 params[:pdf].SetFillColor(200,200,200) | 782 params[:pdf].SetFillColor(200,200,200) |
786 params[:pdf].Cell(coords[:bar_end] - coords[:bar_start], height, "", 0, 0, "", 1) | 783 params[:pdf].RDMCell(coords[:bar_end] - coords[:bar_start], height, "", 0, 0, "", 1) |
787 | 784 |
788 if coords[:bar_late_end] | 785 if coords[:bar_late_end] |
789 params[:pdf].SetY(params[:top]+1.5) | 786 params[:pdf].SetY(params[:top]+1.5) |
790 params[:pdf].SetX(params[:subject_width] + coords[:bar_start]) | 787 params[:pdf].SetX(params[:subject_width] + coords[:bar_start]) |
791 params[:pdf].SetFillColor(255,100,100) | 788 params[:pdf].SetFillColor(255,100,100) |
792 params[:pdf].Cell(coords[:bar_late_end] - coords[:bar_start], height, "", 0, 0, "", 1) | 789 params[:pdf].RDMCell(coords[:bar_late_end] - coords[:bar_start], height, "", 0, 0, "", 1) |
793 end | 790 end |
794 if coords[:bar_progress_end] | 791 if coords[:bar_progress_end] |
795 params[:pdf].SetY(params[:top]+1.5) | 792 params[:pdf].SetY(params[:top]+1.5) |
796 params[:pdf].SetX(params[:subject_width] + coords[:bar_start]) | 793 params[:pdf].SetX(params[:subject_width] + coords[:bar_start]) |
797 params[:pdf].SetFillColor(90,200,90) | 794 params[:pdf].SetFillColor(90,200,90) |
798 params[:pdf].Cell(coords[:bar_progress_end] - coords[:bar_start], height, "", 0, 0, "", 1) | 795 params[:pdf].RDMCell(coords[:bar_progress_end] - coords[:bar_start], height, "", 0, 0, "", 1) |
799 end | 796 end |
800 end | 797 end |
801 # Renders the markers | 798 # Renders the markers |
802 if options[:markers] | 799 if options[:markers] |
803 if coords[:start] | 800 if coords[:start] |
804 params[:pdf].SetY(params[:top] + 1) | 801 params[:pdf].SetY(params[:top] + 1) |
805 params[:pdf].SetX(params[:subject_width] + coords[:start] - 1) | 802 params[:pdf].SetX(params[:subject_width] + coords[:start] - 1) |
806 params[:pdf].SetFillColor(50,50,200) | 803 params[:pdf].SetFillColor(50,50,200) |
807 params[:pdf].Cell(2, 2, "", 0, 0, "", 1) | 804 params[:pdf].RDMCell(2, 2, "", 0, 0, "", 1) |
808 end | 805 end |
809 if coords[:end] | 806 if coords[:end] |
810 params[:pdf].SetY(params[:top] + 1) | 807 params[:pdf].SetY(params[:top] + 1) |
811 params[:pdf].SetX(params[:subject_width] + coords[:end] - 1) | 808 params[:pdf].SetX(params[:subject_width] + coords[:end] - 1) |
812 params[:pdf].SetFillColor(50,50,200) | 809 params[:pdf].SetFillColor(50,50,200) |
813 params[:pdf].Cell(2, 2, "", 0, 0, "", 1) | 810 params[:pdf].RDMCell(2, 2, "", 0, 0, "", 1) |
814 end | 811 end |
815 end | 812 end |
816 # Renders the label on the right | 813 # Renders the label on the right |
817 if options[:label] | 814 if options[:label] |
818 params[:pdf].SetX(params[:subject_width] + (coords[:bar_end] || 0) + 5) | 815 params[:pdf].SetX(params[:subject_width] + (coords[:bar_end] || 0) + 5) |
819 params[:pdf].Cell(30, 2, options[:label]) | 816 params[:pdf].RDMCell(30, 2, options[:label]) |
820 end | 817 end |
821 end | 818 end |
822 | 819 |
823 def image_task(params, coords, options={}) | 820 def image_task(params, coords, options={}) |
824 height = options[:height] || 6 | 821 height = options[:height] || 6 |
825 | 822 |
826 # Renders the task bar, with progress and late | 823 # Renders the task bar, with progress and late |
827 if coords[:bar_start] && coords[:bar_end] | 824 if coords[:bar_start] && coords[:bar_end] |
828 params[:image].fill('#aaa') | 825 params[:image].fill('#aaa') |
829 params[:image].rectangle(params[:subject_width] + coords[:bar_start], params[:top], params[:subject_width] + coords[:bar_end], params[:top] - height) | 826 params[:image].rectangle(params[:subject_width] + coords[:bar_start], params[:top], params[:subject_width] + coords[:bar_end], params[:top] - height) |
830 | 827 |
831 if coords[:bar_late_end] | 828 if coords[:bar_late_end] |
832 params[:image].fill('#f66') | 829 params[:image].fill('#f66') |
833 params[:image].rectangle(params[:subject_width] + coords[:bar_start], params[:top], params[:subject_width] + coords[:bar_late_end], params[:top] - height) | 830 params[:image].rectangle(params[:subject_width] + coords[:bar_start], params[:top], params[:subject_width] + coords[:bar_late_end], params[:top] - height) |
834 end | 831 end |
835 if coords[:bar_progress_end] | 832 if coords[:bar_progress_end] |