| 36 | 36 |       end | 
  | 37 | 37 |  | 
  | 38 | 38 |       attr_reader :year_from, :month_from, :date_from, :date_to, :zoom, :months, :truncated, :max_rows | 
  |  | 39 |       attr_reader :detailed_groups, :version_groups | 
  | 39 | 40 |       attr_accessor :query | 
  | 40 | 41 |       attr_accessor :project | 
  | 41 | 42 |       attr_accessor :view | 
  | ... | ... |  | 
  | 64 | 65 |           @month_from = 1 | 
  | 65 | 66 |           @year_from += 1 | 
  | 66 | 67 |         end | 
  | 67 |  |          | 
  |  | 68 |  | 
  | 68 | 69 |         months = (options[:months] || User.current.pref[:gantt_months]).to_i | 
  | 69 | 70 |         @months = (months > 0 && months < 48) ? months : 48 | 
  | 70 |  |          | 
  |  | 71 |  | 
  | 71 | 72 |         zoom = (options[:zoom] || User.current.pref[:gantt_zoom]).to_i | 
  | 72 | 73 |         @zoom = (zoom > 0 && zoom < 5) ? zoom : 2 | 
  | 73 | 74 |  | 
  | 74 |  |         # Save gantt parameters as user preference (zoom and months count) | 
  | 75 |  |         if (User.current.logged? && (@zoom != User.current.pref[:gantt_zoom] || | 
  | 76 |  |               @months != User.current.pref[:gantt_months])) | 
  | 77 |  |           User.current.pref[:gantt_zoom], User.current.pref[:gantt_months] = @zoom, @months | 
  |  | 75 |         detailed_groups = User.current.pref[:gantt_detailed_groups] | 
  |  | 76 |         if options[:set_filter] == '1' | 
  |  | 77 |           detailed_groups = (options[:not_detailed_groups].to_s == 'true') ? false : true | 
  |  | 78 |         end | 
  |  | 79 |         @detailed_groups = (detailed_groups.to_s == 'true') ? true : false | 
  |  | 80 |  | 
  |  | 81 |         version_groups = User.current.pref[:gantt_version_groups].to_s | 
  |  | 82 |         if options[:set_filter] == '1' | 
  |  | 83 |           version_groups = options[:version_groups].to_s | 
  |  | 84 |         end | 
  |  | 85 |         @version_groups = (version_groups == 'true') ? true : false | 
  |  | 86 |  | 
  |  | 87 |         # Save gantt parameters as user preference (zoom, months count and detailed grouping) | 
  |  | 88 |         if (User.current.logged? && | 
  |  | 89 |              (@zoom != User.current.pref[:gantt_zoom] || | 
  |  | 90 |               @months != User.current.pref[:gantt_months] || | 
  |  | 91 |               @detailed_groups != User.current.pref[:gantt_detailed_groups] || | 
  |  | 92 |               @version_groups != User.current.pref[:gantt_version_groups])) | 
  |  | 93 |           User.current.pref[:gantt_zoom] = @zoom | 
  |  | 94 |           User.current.pref[:gantt_months] = @months | 
  |  | 95 |           User.current.pref[:gantt_detailed_groups] = @detailed_groups | 
  |  | 96 |           User.current.pref[:gantt_version_groups] = @version_groups | 
  | 78 | 97 |           User.current.preference.save | 
  | 79 | 98 |         end | 
  | 80 | 99 |  | 
  | ... | ... |  | 
  | 120 | 139 |  | 
  | 121 | 140 |       def params | 
  | 122 | 141 |         common_params.merge({:zoom => zoom, :year => year_from, | 
  | 123 |  |                              :month => month_from, :months => months}) | 
  |  | 142 |                              :month => month_from, :months => months, | 
  |  | 143 |                              :not_detailed_groups => !detailed_groups, | 
  |  | 144 |                              :version_groups => version_groups}) | 
  | 124 | 145 |       end | 
  | 125 | 146 |  | 
  | 126 | 147 |       def params_previous | 
  | 127 | 148 |         common_params.merge({:year => (date_from << months).year, | 
  | 128 | 149 |                              :month => (date_from << months).month, | 
  | 129 |  |                              :zoom => zoom, :months => months}) | 
  |  | 150 |                              :zoom => zoom, :months => months, | 
  |  | 151 |                              :not_detailed_groups => !detailed_groups, | 
  |  | 152 |                              :version_groups => version_groups}) | 
  | 130 | 153 |       end | 
  | 131 | 154 |  | 
  | 132 | 155 |       def params_next | 
  | 133 | 156 |         common_params.merge({:year => (date_from >> months).year, | 
  | 134 | 157 |                              :month => (date_from >> months).month, | 
  | 135 |  |                              :zoom => zoom, :months => months}) | 
  |  | 158 |                              :zoom => zoom, :months => months, | 
  |  | 159 |                              :not_detailed_groups => !detailed_groups, | 
  |  | 160 |                              :version_groups => version_groups}) | 
  | 136 | 161 |       end | 
  | 137 | 162 |  | 
  | 138 | 163 |       # Returns the number of rows that will be rendered on the Gantt chart | 
  | ... | ... |  | 
  | 205 | 230 |         project_issues(project).select {|issue| issue.fixed_version == version} | 
  | 206 | 231 |       end | 
  | 207 | 232 |  | 
  |  | 233 |       # Returns the issues that belong to +project+ and are grouped by +group+ | 
  |  | 234 |       def group_issues!(group, issues) | 
  |  | 235 |         result = issues.take_while {|issue| group_name(issue) == group} | 
  |  | 236 |         issues.reject! {|issue| group_name(issue) == group} | 
  |  | 237 |         result | 
  |  | 238 |       end | 
  |  | 239 |  | 
  | 208 | 240 |       def render(options={}) | 
  | 209 | 241 |         options = {:top => 0, :top_increment => 20, | 
  | 210 | 242 |                    :indent_increment => 20, :render => :subject, | 
  | ... | ... |  | 
  | 230 | 262 |         options[:indent] += options[:indent_increment] | 
  | 231 | 263 |         @number_of_rows += 1 | 
  | 232 | 264 |         return if abort? | 
  | 233 |  |         issues = project_issues(project).select {|i| i.fixed_version.nil?} | 
  | 234 |  |         sort_issues!(issues) | 
  | 235 |  |         if issues | 
  |  | 265 |         issues = project_issues(project).select {|i| i.fixed_version.nil? || (grouped? && !@version_groups) } | 
  |  | 266 |         if issues && grouped? | 
  |  | 267 |           render_groups(issues, options) | 
  |  | 268 |           return if abort? | 
  |  | 269 |         else | 
  |  | 270 |           sort_issues!(issues) | 
  | 236 | 271 |           render_issues(issues, options) | 
  | 237 | 272 |           return if abort? | 
  | 238 | 273 |         end | 
  | ... | ... |  | 
  | 245 | 280 |         options[:indent] -= options[:indent_increment] | 
  | 246 | 281 |       end | 
  | 247 | 282 |  | 
  | 248 |  |       def render_issues(issues, options={}) | 
  | 249 |  |         @issue_ancestors = [] | 
  | 250 |  |         issues.each do |i| | 
  | 251 |  |           subject_for_issue(i, options) unless options[:only] == :lines | 
  | 252 |  |           line_for_issue(i, options) unless options[:only] == :subjects | 
  | 253 |  |           options[:top] += options[:top_increment] | 
  | 254 |  |           @number_of_rows += 1 | 
  | 255 |  |           break if abort? | 
  | 256 |  |         end | 
  | 257 |  |         options[:indent] -= (options[:indent_increment] * @issue_ancestors.size) | 
  | 258 |  |       end | 
  | 259 |  |  | 
  | 260 | 283 |       def render_version(project, version, options={}) | 
  | 261 | 284 |         # Version header | 
  | 262 | 285 |         subject_for_version(version, options) unless options[:only] == :lines | 
  | 263 | 286 |         line_for_version(version, options) unless options[:only] == :subjects | 
  | 264 | 287 |         options[:top] += options[:top_increment] | 
  |  | 288 |         options[:indent] += options[:indent_increment] | 
  | 265 | 289 |         @number_of_rows += 1 | 
  | 266 |  |         return if abort? | 
  |  | 290 |         return if abort? || (grouped? && !@version_groups) | 
  | 267 | 291 |         issues = version_issues(project, version) | 
  | 268 |  |         if issues | 
  |  | 292 |         if issues && grouped? | 
  |  | 293 |           render_groups(issues, options) | 
  |  | 294 |           return if abort? | 
  |  | 295 |         else | 
  | 269 | 296 |           sort_issues!(issues) | 
  | 270 |  |           # Indent issues | 
  | 271 |  |           options[:indent] += options[:indent_increment] | 
  | 272 | 297 |           render_issues(issues, options) | 
  | 273 |  |           options[:indent] -= options[:indent_increment] | 
  |  | 298 |           return if abort? | 
  | 274 | 299 |         end | 
  |  | 300 |         # Remove indent to hit the next sibling | 
  |  | 301 |         options[:indent] -= options[:indent_increment] | 
  |  | 302 |       end | 
  |  | 303 |  | 
  |  | 304 |       def render_groups(issues, options={}) | 
  |  | 305 |         while !issues.empty? | 
  |  | 306 |           # Group header | 
  |  | 307 |           group = group_name(issues[0]) | 
  |  | 308 |           subject_for_group(group, options) unless options[:only] == :lines | 
  |  | 309 |           options[:top] += options[:top_increment] | 
  |  | 310 |           @number_of_rows += 1 | 
  |  | 311 |           break if abort? | 
  |  | 312 |           gr_issues = group_issues!(group, issues) | 
  |  | 313 |           if gr_issues | 
  |  | 314 |             sort_issues!(gr_issues) | 
  |  | 315 |             # Indent issues | 
  |  | 316 |             options[:indent] += options[:indent_increment] | 
  |  | 317 |             render_issues(gr_issues, options) | 
  |  | 318 |             options[:indent] -= options[:indent_increment] | 
  |  | 319 |           end | 
  |  | 320 |           break if abort? | 
  |  | 321 |         end | 
  |  | 322 |       end | 
  |  | 323 |  | 
  |  | 324 |       def render_issues(issues, options={}) | 
  |  | 325 |         @issue_ancestors = [] | 
  |  | 326 |         if !grouped? || @detailed_groups | 
  |  | 327 |           issues.each do |i| | 
  |  | 328 |             if i.due_before == nil | 
  |  | 329 |               i.due_date = i.start_date | 
  |  | 330 |             end | 
  |  | 331 |             subject_for_issue(i, options) unless options[:only] == :lines | 
  |  | 332 |             line_for_issue(i, options) unless options[:only] == :subjects | 
  |  | 333 |             options[:top] += options[:top_increment] | 
  |  | 334 |             @number_of_rows += 1 | 
  |  | 335 |             break if abort? | 
  |  | 336 |           end | 
  |  | 337 |         else | 
  |  | 338 |           group_start = options[:top] - options[:top_increment] | 
  |  | 339 |           group_max_line = 0 | 
  |  | 340 |           group_write_line = group_max_line | 
  |  | 341 |           dates_in_line = [[]] | 
  |  | 342 |           @number_of_rows += 1 | 
  |  | 343 |           issues.each do |i| | 
  |  | 344 |             if i.leaf? | 
  |  | 345 |               if i.due_before == nil | 
  |  | 346 |                 i.due_date = i.start_date | 
  |  | 347 |               end | 
  |  | 348 |               group_write_line = -1 | 
  |  | 349 |               dates_in_line.each_with_index do |dates, line| | 
  |  | 350 |                 if dates.find {|e| (i.start_date >= e[0] && i.start_date <= e[1]) || (i.start_date <= e[0] && i.due_before >= e[1]) } == nil | 
  |  | 351 |                   group_write_line = line | 
  |  | 352 |                   break | 
  |  | 353 |                 end | 
  |  | 354 |               end | 
  |  | 355 |               if group_write_line == -1 | 
  |  | 356 |                 group_max_line += 1 | 
  |  | 357 |                 group_write_line = group_max_line | 
  |  | 358 |                 dates_in_line.push([]) | 
  |  | 359 |                 @number_of_rows += 1 | 
  |  | 360 |               end | 
  |  | 361 |               options[:top] = group_start + (group_write_line * options[:top_increment]) | 
  |  | 362 |               subject_for_issue(i, options) unless options[:only] == :lines | 
  |  | 363 |               line_for_issue(i, options) unless options[:only] == :subjects | 
  |  | 364 |               dates_in_line[group_write_line].push([i.start_date, i.due_before]) | 
  |  | 365 |             end | 
  |  | 366 |             break if abort? | 
  |  | 367 |           end | 
  |  | 368 |           options[:top] = group_start + ((group_max_line+1) * options[:top_increment]) | 
  |  | 369 |         end | 
  |  | 370 |         options[:indent] -= (options[:indent_increment] * @issue_ancestors.size) | 
  | 275 | 371 |       end | 
  | 276 | 372 |  | 
  | 277 | 373 |       def render_end(options={}) | 
  | ... | ... |  | 
  | 300 | 396 |       end | 
  | 301 | 397 |  | 
  | 302 | 398 |       def line_for_project(project, options) | 
  | 303 |  |         # Skip versions that don't have a start_date or due date | 
  |  | 399 |         # Skip projects that don't have a start_date or due date | 
  | 304 | 400 |         if project.is_a?(Project) && project.start_date && project.due_date | 
  | 305 | 401 |           options[:zoom] ||= 1 | 
  | 306 | 402 |           options[:g_width] ||= (self.date_to - self.date_from + 1) * options[:zoom] | 
  | ... | ... |  | 
  | 363 | 459 |         end | 
  | 364 | 460 |       end | 
  | 365 | 461 |  | 
  |  | 462 |       def subject_for_group(group, options) | 
  |  | 463 |         case options[:format] | 
  |  | 464 |         when :html | 
  |  | 465 |           html_class = "" | 
  |  | 466 |           html_class << 'icon icon-package ' | 
  |  | 467 |           s = group.html_safe | 
  |  | 468 |           subject = view.content_tag(:span, s, | 
  |  | 469 |                                      :class => html_class).html_safe | 
  |  | 470 |           html_subject(options, subject, :css => "version-name") | 
  |  | 471 |         when :image | 
  |  | 472 |           image_subject(options, group) | 
  |  | 473 |         when :pdf | 
  |  | 474 |           pdf_new_page?(options) | 
  |  | 475 |           pdf_subject(options, group) | 
  |  | 476 |         end | 
  |  | 477 |       end | 
  |  | 478 |  | 
  | 366 | 479 |       def subject_for_issue(issue, options) | 
  | 367 | 480 |         while @issue_ancestors.any? && !issue.is_descendant_of?(@issue_ancestors.last) | 
  | 368 | 481 |           @issue_ancestors.pop | 
  | ... | ... |  | 
  | 370 | 483 |         end | 
  | 371 | 484 |         output = case options[:format] | 
  | 372 | 485 |         when :html | 
  | 373 |  |           css_classes = '' | 
  | 374 |  |           css_classes << ' issue-overdue' if issue.overdue? | 
  | 375 |  |           css_classes << ' issue-behind-schedule' if issue.behind_schedule? | 
  | 376 |  |           css_classes << ' icon icon-issue' unless Setting.gravatar_enabled? && issue.assigned_to | 
  | 377 |  |           s = "".html_safe | 
  | 378 |  |           if issue.assigned_to.present? | 
  | 379 |  |             assigned_string = l(:field_assigned_to) + ": " + issue.assigned_to.name | 
  | 380 |  |             s << view.avatar(issue.assigned_to, | 
  | 381 |  |                              :class => 'gravatar icon-gravatar', | 
  | 382 |  |                              :size => 10, | 
  | 383 |  |                              :title => assigned_string).to_s.html_safe | 
  | 384 |  |           end | 
  | 385 |  |           s << view.link_to_issue(issue).html_safe | 
  | 386 |  |           subject = view.content_tag(:span, s, :class => css_classes).html_safe | 
  | 387 |  |           html_subject(options, subject, :css => "issue-subject", | 
  | 388 |  |                        :title => issue.subject) + "\n" | 
  |  | 486 |           if !grouped? || @detailed_groups | 
  |  | 487 |             css_classes = '' | 
  |  | 488 |             css_classes << ' issue-overdue' if issue.overdue? | 
  |  | 489 |             css_classes << ' issue-behind-schedule' if issue.behind_schedule? | 
  |  | 490 |             css_classes << ' icon icon-issue' unless Setting.gravatar_enabled? && issue.assigned_to | 
  |  | 491 |             s = "".html_safe | 
  |  | 492 |             if issue.assigned_to.present? | 
  |  | 493 |               assigned_string = l(:field_assigned_to) + ": " + issue.assigned_to.name | 
  |  | 494 |               s << view.avatar(issue.assigned_to, | 
  |  | 495 |                                :class => 'gravatar icon-gravatar', | 
  |  | 496 |                                :size => 10, | 
  |  | 497 |                                :title => assigned_string).to_s.html_safe | 
  |  | 498 |             end | 
  |  | 499 |             s << view.link_to_issue(issue).html_safe | 
  |  | 500 |             subject = view.content_tag(:span, s, :class => css_classes).html_safe | 
  |  | 501 |             html_subject(options, subject, :css => "issue-subject", | 
  |  | 502 |                          :title => issue.subject) + "\n" | 
  |  | 503 |           end | 
  | 389 | 504 |         when :image | 
  | 390 |  |           image_subject(options, issue.subject) | 
  |  | 505 |           if !grouped? || @detailed_groups | 
  |  | 506 |             image_subject(options, issue.subject) | 
  |  | 507 |           end | 
  | 391 | 508 |         when :pdf | 
  | 392 | 509 |           pdf_new_page?(options) | 
  | 393 |  |           pdf_subject(options, issue.subject) | 
  |  | 510 |           pdf_subject(options, (grouped? || @detailed_groups) ? ' ' : issue.subject) | 
  | 394 | 511 |         end | 
  | 395 | 512 |         unless issue.leaf? | 
  | 396 | 513 |           @issue_ancestors << issue | 
  | ... | ... |  | 
  | 403 | 520 |         # Skip issues that don't have a due_before (due_date or version's due_date) | 
  | 404 | 521 |         if issue.is_a?(Issue) && issue.due_before | 
  | 405 | 522 |           coords = coordinates(issue.start_date, issue.due_before, issue.done_ratio, options[:zoom]) | 
  | 406 |  |           label = "#{issue.status.name} #{issue.done_ratio}%" | 
  |  | 523 |           label = " " | 
  |  | 524 |           if !grouped? || @detailed_groups | 
  |  | 525 |             label = "#{issue.status.name} #{issue.done_ratio}%" | 
  |  | 526 |           end | 
  | 407 | 527 |           case options[:format] | 
  | 408 | 528 |           when :html | 
  | 409 | 529 |             html_task(options, coords, | 
  | ... | ... |  | 
  | 742 | 862 |         [min, max] | 
  | 743 | 863 |       end | 
  | 744 | 864 |  | 
  |  | 865 |       def grouped? | 
  |  | 866 |         @query.grouped? | 
  |  | 867 |       end | 
  |  | 868 |  | 
  |  | 869 |       def new_group?(x, y) | 
  |  | 870 |         if grouped? | 
  |  | 871 |           value_x = @query.group_by_column.value(x) | 
  |  | 872 |           value_y = @query.group_by_column.value(y) | 
  |  | 873 |  | 
  |  | 874 |           value_x != value_y | 
  |  | 875 |         else | 
  |  | 876 |           true | 
  |  | 877 |         end | 
  |  | 878 |       end | 
  |  | 879 |  | 
  |  | 880 |       def group_name(issue) | 
  |  | 881 |         if grouped? | 
  |  | 882 |           result = @query.group_by_column.value(issue) | 
  |  | 883 |           if result == nil || result == '' | 
  |  | 884 |             'None' | 
  |  | 885 |           else | 
  |  | 886 |             result.to_s | 
  |  | 887 |           end | 
  |  | 888 |         else | 
  |  | 889 |           issue.subject | 
  |  | 890 |         end | 
  |  | 891 |       end | 
  |  | 892 |  | 
  | 745 | 893 |       def pdf_new_page?(options) | 
  | 746 | 894 |         if options[:top] > 180 | 
  | 747 | 895 |           options[:pdf].Line(15, options[:top], PDF::TotalWidth, options[:top]) |