Revision 442:753f1380d6bc lib/redmine/helpers/.svn/text-base

View differences:

lib/redmine/helpers/.svn/text-base/calendar.rb.svn-base
68 68
        case Setting.start_of_week.to_i
69 69
        when 1
70 70
          @first_dow ||= (1 - 1)%7 + 1
71
        when 6
72
          @first_dow ||= (6 - 1)%7 + 1
71 73
        when 7
72 74
          @first_dow ||= (7 - 1)%7 + 1
73 75
        else
lib/redmine/helpers/.svn/text-base/gantt.rb.svn-base
1 1
# Redmine - project management software
2
# Copyright (C) 2006-2008  Jean-Philippe Lang
2
# Copyright (C) 2006-2011  Jean-Philippe Lang
3 3
#
4 4
# This program is free software; you can redistribute it and/or
5 5
# modify it under the terms of the GNU General Public License
6 6
# as published by the Free Software Foundation; either version 2
7 7
# of the License, or (at your option) any later version.
8
# 
8
#
9 9
# This program is distributed in the hope that it will be useful,
10 10
# but WITHOUT ANY WARRANTY; without even the implied warranty of
11 11
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
12 12
# GNU General Public License for more details.
13
# 
13
#
14 14
# You should have received a copy of the GNU General Public License
15 15
# along with this program; if not, write to the Free Software
16 16
# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301, USA.
......
38 38
      attr_accessor :query
39 39
      attr_accessor :project
40 40
      attr_accessor :view
41
      
41

  
42 42
      def initialize(options={})
43 43
        options = options.dup
44
        
44

  
45 45
        if options[:year] && options[:year].to_i >0
46 46
          @year_from = options[:year].to_i
47 47
          if options[:month] && options[:month].to_i >=1 && options[:month].to_i <= 12
......
53 53
          @month_from ||= Date.today.month
54 54
          @year_from ||= Date.today.year
55 55
        end
56
        
56

  
57 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 59
        months = (options[:months] || User.current.pref[:gantt_months]).to_i
60 60
        @months = (months > 0 && months < 25) ? months : 6
61
        
61

  
62 62
        # Save gantt parameters as user preference (zoom and months count)
63 63
        if (User.current.logged? && (@zoom != User.current.pref[:gantt_zoom] || @months != User.current.pref[:gantt_months]))
64 64
          User.current.pref[:gantt_zoom], User.current.pref[:gantt_months] = @zoom, @months
65 65
          User.current.preference.save
66 66
        end
67
        
67

  
68 68
        @date_from = Date.civil(@year_from, @month_from, 1)
69 69
        @date_to = (@date_from >> @months) - 1
70
        
70

  
71 71
        @subjects = ''
72 72
        @lines = ''
73 73
        @number_of_rows = nil
74
        
74

  
75 75
        @issue_ancestors = []
76
        
76

  
77 77
        @truncated = false
78 78
        if options.has_key?(:max_rows)
79 79
          @max_rows = options[:max_rows]
......
85 85
      def common_params
86 86
        { :controller => 'gantts', :action => 'show', :project_id => @project }
87 87
      end
88
      
88

  
89 89
      def params
90 90
        common_params.merge({  :zoom => zoom, :year => year_from, :month => month_from, :months => months })
91 91
      end
92
      
92

  
93 93
      def params_previous
94 94
        common_params.merge({:year => (date_from << months).year, :month => (date_from << months).month, :zoom => zoom, :months => months })
95 95
      end
96
      
96

  
97 97
      def params_next
98 98
        common_params.merge({:year => (date_from >> months).year, :month => (date_from >> months).month, :zoom => zoom, :months => months })
99 99
      end
100 100

  
101
            ### Extracted from the HTML view/helpers
102 101
      # Returns the number of rows that will be rendered on the Gantt chart
103 102
      def number_of_rows
104 103
        return @number_of_rows if @number_of_rows
105
        
106
        rows = if @project
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
        
104

  
105
        rows = projects.inject(0) {|total, p| total += number_of_rows_on_project(p)}
114 106
        rows > @max_rows ? @max_rows : rows
115 107
      end
116 108

  
117 109
      # Returns the number of rows that will be used to list a project on
118 110
      # the Gantt chart.  This will recurse for each subproject.
119 111
      def number_of_rows_on_project(project)
120
        # Remove the project requirement for Versions because it will
121
        # restrict issues to only be on the current project.  This
122
        # ends up missing issues which are assigned to shared versions.
123
        @query.project = nil if @query.project
112
        return 0 unless projects.include?(project)
124 113

  
125
        # One Root project
126 114
        count = 1
127
        # Issues without a Version
128
        count += project.issues.for_gantt.without_version.with_query(@query).count
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

  
115
        count += project_issues(project).size
116
        count += project_versions(project).size
143 117
        count
144 118
      end
145 119

  
......
154 128
        render(options.merge(:only => :lines)) unless @lines_rendered
155 129
        @lines
156 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 174
      def render(options={})
159
        options = {:indent => 4, :render => :subject, :format => :html}.merge(options)
160
        
175
        options = {:top => 0, :top_increment => 20, :indent_increment => 20, :render => :subject, :format => :html}.merge(options)
176
        indent = options[:indent] || 4
177

  
161 178
        @subjects = '' unless options[:only] == :lines
162 179
        @lines = '' unless options[:only] == :subjects
163 180
        @number_of_rows = 0
164
        
165
        if @project
166
          render_project(@project, options)
167
        else
168
          Project.roots.visible.has_module('issue_tracking').each do |project|
169
            render_project(project, options)
170
            break if abort?
171
          end
181

  
182
        Project.project_tree(projects) do |project, level|
183
          options[:indent] = indent + level * options[:indent_increment]
184
          render_project(project, options)
185
          break if abort?
172 186
        end
173
        
187

  
174 188
        @subjects_rendered = true unless options[:only] == :lines
175 189
        @lines_rendered = true unless options[:only] == :subjects
176
        
190

  
177 191
        render_end(options)
178 192
      end
179 193

  
180 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 195
        subject_for_project(project, options) unless options[:only] == :lines
186 196
        line_for_project(project, options) unless options[:only] == :subjects
187
        
197

  
188 198
        options[:top] += options[:top_increment]
189 199
        options[:indent] += options[:indent_increment]
190 200
        @number_of_rows += 1
191 201
        return if abort?
192
        
193
        # Second, Issues without a version
194
        issues = project.issues.for_gantt.without_version.with_query(@query).all(:limit => current_limit)
202

  
203
        issues = project_issues(project).select {|i| i.fixed_version.nil?}
195 204
        sort_issues!(issues)
196 205
        if issues
197 206
          render_issues(issues, options)
198 207
          return if abort?
199 208
        end
200 209

  
201
        # Third, Versions
202
        project.versions.sort.each do |version|
203
          render_version(version, options)
204
          return if abort?
210
        versions = project_versions(project)
211
        versions.each do |version|
212
          render_version(project, version, options)
205 213
        end
206 214

  
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

  
213 215
        # Remove indent to hit the next sibling
214 216
        options[:indent] -= options[:indent_increment]
215 217
      end
216 218

  
217 219
      def render_issues(issues, options={})
218 220
        @issue_ancestors = []
219
        
221

  
220 222
        issues.each do |i|
221 223
          subject_for_issue(i, options) unless options[:only] == :lines
222 224
          line_for_issue(i, options) unless options[:only] == :subjects
223
          
225

  
224 226
          options[:top] += options[:top_increment]
225 227
          @number_of_rows += 1
226 228
          break if abort?
227 229
        end
228
        
230

  
229 231
        options[:indent] -= (options[:indent_increment] * @issue_ancestors.size)
230 232
      end
231 233

  
232
      def render_version(version, options={})
234
      def render_version(project, version, options={})
233 235
        # Version header
234 236
        subject_for_version(version, options) unless options[:only] == :lines
235 237
        line_for_version(version, options) unless options[:only] == :subjects
236
        
238

  
237 239
        options[:top] += options[:top_increment]
238 240
        @number_of_rows += 1
239 241
        return if abort?
240
        
241
        # Remove the project requirement for Versions because it will
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)
242

  
243
        issues = version_issues(project, version)
247 244
        if issues
248 245
          sort_issues!(issues)
249 246
          # Indent issues
......
252 249
          options[:indent] -= options[:indent_increment]
253 250
        end
254 251
      end
255
      
252

  
256 253
      def render_end(options={})
257 254
        case options[:format]
258
        when :pdf        
255
        when :pdf
259 256
          options[:pdf].Line(15, options[:top], PDF::TotalWidth, options[:top])
260 257
        end
261 258
      end
......
280 277
        if project.is_a?(Project) && project.start_date && project.due_date
281 278
          options[:zoom] ||= 1
282 279
          options[:g_width] ||= (self.date_to - self.date_from + 1) * options[:zoom]
283
            
280

  
284 281
          coords = coordinates(project.start_date, project.due_date, nil, options[:zoom])
285 282
          label = h(project)
286
          
283

  
287 284
          case options[:format]
288 285
          when :html
289 286
            html_task(options, coords, :css => "project task", :label => label, :markers => true)
......
318 315
        if version.is_a?(Version) && version.start_date && version.due_date
319 316
          options[:zoom] ||= 1
320 317
          options[:g_width] ||= (self.date_to - self.date_from + 1) * options[:zoom]
321
          
318

  
322 319
          coords = coordinates(version.start_date, version.due_date, version.completed_pourcent, options[:zoom])
323 320
          label = "#{h version } #{h version.completed_pourcent.to_i.to_s}%"
324 321
          label = h("#{version.project} -") + label unless @project && @project == version.project
......
342 339
          @issue_ancestors.pop
343 340
          options[:indent] -= options[:indent_increment]
344 341
        end
345
          
342

  
346 343
        output = case options[:format]
347 344
        when :html
348 345
          css_classes = ''
349 346
          css_classes << ' issue-overdue' if issue.overdue?
350 347
          css_classes << ' issue-behind-schedule' if issue.behind_schedule?
351 348
          css_classes << ' icon icon-issue' unless Setting.gravatar_enabled? && issue.assigned_to
352
          
349

  
353 350
          subject = "<span class='#{css_classes}'>"
354 351
          if issue.assigned_to.present?
355 352
            assigned_string = l(:field_assigned_to) + ": " + issue.assigned_to.name
......
369 366
          @issue_ancestors << issue
370 367
          options[:indent] += options[:indent_increment]
371 368
        end
372
        
369

  
373 370
        output
374 371
      end
375 372

  
......
378 375
        if issue.is_a?(Issue) && issue.due_before
379 376
          coords = coordinates(issue.start_date, issue.due_before, issue.done_ratio, options[:zoom])
380 377
          label = "#{ issue.status.name } #{ issue.done_ratio }%"
381
          
378

  
382 379
          case options[:format]
383 380
          when :html
384 381
            html_task(options, coords, :css => "task " + (issue.leaf? ? 'leaf' : 'parent'), :label => label, :issue => issue, :markers => !issue.leaf?)
......
396 393
      # Generates a gantt image
397 394
      # Only defined if RMagick is avalaible
398 395
      def to_image(format='PNG')
399
        date_to = (@date_from >> @months)-1    
396
        date_to = (@date_from >> @months)-1
400 397
        show_weeks = @zoom > 1
401 398
        show_days = @zoom > 2
402
        
399

  
403 400
        subject_width = 400
404
        header_heigth = 18
401
        header_height = 18
405 402
        # width of one day in pixels
406 403
        zoom = @zoom*2
407 404
        g_width = (@date_to - @date_from + 1)*zoom
408 405
        g_height = 20 * number_of_rows + 30
409
        headers_heigth = (show_weeks ? 2*header_heigth : header_heigth)
410
        height = g_height + headers_heigth
411
            
406
        headers_height = (show_weeks ? 2*header_height : header_height)
407
        height = g_height + headers_height
408

  
412 409
        imgl = Magick::ImageList.new
413 410
        imgl.new_image(subject_width+g_width+1, height)
414 411
        gc = Magick::Draw.new
415
        
412

  
416 413
        # Subjects
417 414
        gc.stroke('transparent')
418
        subjects(:image => gc, :top => (headers_heigth + 20), :indent => 4, :format => :image)
419
    
415
        subjects(:image => gc, :top => (headers_height + 20), :indent => 4, :format => :image)
416

  
420 417
        # Months headers
421 418
        month_f = @date_from
422 419
        left = subject_width
423
        @months.times do 
420
        @months.times do
424 421
          width = ((month_f >> 1) - month_f) * zoom
425 422
          gc.fill('white')
426 423
          gc.stroke('grey')
......
433 430
          left = left + width
434 431
          month_f = month_f >> 1
435 432
        end
436
        
433

  
437 434
        # Weeks headers
438 435
        if show_weeks
439 436
        	left = subject_width
440
        	height = header_heigth
437
        	height = header_height
441 438
        	if @date_from.cwday == 1
442 439
        	    # date_from is monday
443 440
                week_f = date_from
......
448 445
                gc.fill('white')
449 446
                gc.stroke('grey')
450 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 449
        		left = left + width
453 450
        	end
454 451
        	while week_f <= date_to
......
456 453
                gc.fill('white')
457 454
                gc.stroke('grey')
458 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 457
                gc.fill('black')
461 458
                gc.stroke('transparent')
462 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 461
        		left = left + width
465 462
        		week_f = week_f+7
466 463
        	end
467 464
        end
468
        
465

  
469 466
        # Days details (week-end in grey)
470 467
        if show_days
471 468
        	left = subject_width
472
        	height = g_height + header_heigth - 1
469
        	height = g_height + header_height - 1
473 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 472
              width =  zoom
476 473
              gc.fill(wday == 6 || wday == 7 ? '#eee' : 'white')
477 474
              gc.stroke('#ddd')
478 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 477
              left = left + width
481 478
              wday = wday + 1
482 479
              wday = 1 if wday > 7
483 480
        	end
484 481
        end
485
    
482

  
486 483
        # border
487 484
        gc.fill('transparent')
488 485
        gc.stroke('grey')
489 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 488
        gc.stroke('black')
492
        gc.rectangle(0, 0, subject_width+g_width, g_height+ headers_heigth-1)
493
            
489
        gc.rectangle(0, 0, subject_width+g_width, g_height+ headers_height-1)
490

  
494 491
        # content
495
        top = headers_heigth + 20
492
        top = headers_height + 20
496 493

  
497 494
        gc.stroke('transparent')
498 495
        lines(:image => gc, :top => top, :zoom => zoom, :subject_width => subject_width, :format => :image)
499
        
496

  
500 497
        # today red line
501 498
        if Date.today >= @date_from and Date.today <= date_to
502 499
          gc.stroke('red')
503 500
          x = (Date.today-@date_from+1)*zoom + subject_width
504
          gc.line(x, headers_heigth, x, headers_heigth + g_height-1)      
505
        end    
506
        
501
          gc.line(x, headers_height, x, headers_height + g_height-1)
502
        end
503

  
507 504
        gc.draw(imgl)
508 505
        imgl.format = format
509 506
        imgl.to_blob
510 507
      end if Object.const_defined?(:Magick)
511 508

  
512 509
      def to_pdf
513
        pdf = ::Redmine::Export::PDF::IFPDF.new(current_language)
510
        pdf = ::Redmine::Export::PDF::ITCPDF.new(current_language)
514 511
        pdf.SetTitle("#{l(:label_gantt)} #{project}")
515
        pdf.AliasNbPages
512
        pdf.alias_nb_pages
516 513
        pdf.footer_date = format_date(Date.today)
517 514
        pdf.AddPage("L")
518 515
        pdf.SetFontStyle('B',12)
519 516
        pdf.SetX(15)
520
        pdf.Cell(PDF::LeftPaneWidth, 20, project.to_s)
517
        pdf.RDMCell(PDF::LeftPaneWidth, 20, project.to_s)
521 518
        pdf.Ln
522 519
        pdf.SetFontStyle('B',9)
523
        
520

  
524 521
        subject_width = PDF::LeftPaneWidth
525
        header_heigth = 5
526
        
527
        headers_heigth = header_heigth
522
        header_height = 5
523

  
524
        headers_height = header_height
528 525
        show_weeks = false
529 526
        show_days = false
530
        
527

  
531 528
        if self.months < 7
532 529
          show_weeks = true
533
          headers_heigth = 2*header_heigth
530
          headers_height = 2*header_height
534 531
          if self.months < 3
535 532
            show_days = true
536
            headers_heigth = 3*header_heigth
533
            headers_height = 3*header_height
537 534
          end
538 535
        end
539
        
536

  
540 537
        g_width = PDF.right_pane_width
541 538
        zoom = (g_width) / (self.date_to - self.date_from + 1)
542 539
        g_height = 120
543
        t_height = g_height + headers_heigth
544
        
540
        t_height = g_height + headers_height
541

  
545 542
        y_start = pdf.GetY
546
        
543

  
547 544
        # Months headers
548 545
        month_f = self.date_from
549 546
        left = subject_width
550
        height = header_heigth
551
        self.months.times do 
552
          width = ((month_f >> 1) - month_f) * zoom 
547
        height = header_height
548
        self.months.times do
549
          width = ((month_f >> 1) - month_f) * zoom
553 550
          pdf.SetY(y_start)
554 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 553
          left = left + width
557 554
          month_f = month_f >> 1
558
        end  
559
        
555
        end
556

  
560 557
        # Weeks headers
561 558
        if show_weeks
562 559
          left = subject_width
563
          height = header_heigth
560
          height = header_height
564 561
          if self.date_from.cwday == 1
565 562
            # self.date_from is monday
566 563
            week_f = self.date_from
......
568 565
            # find next monday after self.date_from
569 566
            week_f = self.date_from + (7 - self.date_from.cwday + 1)
570 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 569
            pdf.SetX(left)
573
            pdf.Cell(width + 1, height, "", "LTR")
570
            pdf.RDMCell(width + 1, height, "", "LTR")
574 571
            left = left + width+1
575 572
          end
576 573
          while week_f <= self.date_to
577 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 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 578
            left = left + width
582 579
            week_f = week_f+7
583 580
          end
584 581
        end
585
        
582

  
586 583
        # Days headers
587 584
        if show_days
588 585
          left = subject_width
589
          height = header_heigth
586
          height = header_height
590 587
          wday = self.date_from.cwday
591 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 590
            width = zoom
594
            pdf.SetY(y_start + 2 * header_heigth)
591
            pdf.SetY(y_start + 2 * header_height)
595 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 594
            left = left + width
598 595
            wday = wday + 1
599 596
            wday = 1 if wday > 7
600 597
          end
601 598
        end
602
        
599

  
603 600
        pdf.SetY(y_start)
604 601
        pdf.SetX(15)
605
        pdf.Cell(subject_width+g_width-15, headers_heigth, "", 1)
606
        
602
        pdf.RDMCell(subject_width+g_width-15, headers_height, "", 1)
603

  
607 604
        # Tasks
608
        top = headers_heigth + y_start
605
        top = headers_height + y_start
609 606
        options = {
610 607
          :top => top,
611 608
          :zoom => zoom,
......
620 617
        render(options)
621 618
        pdf.Output
622 619
      end
623
      
620

  
624 621
      private
625
      
622

  
626 623
      def coordinates(start_date, end_date, progress, zoom=nil)
627 624
        zoom ||= @zoom
628
        
625

  
629 626
        coords = {}
630 627
        if start_date && end_date && start_date < self.date_to && end_date > self.date_from
631 628
          if start_date > self.date_from
......
640 637
          else
641 638
            coords[:bar_end] = self.date_to - self.date_from + 1
642 639
          end
643
        
640

  
644 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 643
            if progress_date > self.date_from && progress_date > start_date
647 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 646
              else
650 647
                coords[:bar_progress_end] = self.date_to - self.date_from + 1
651 648
              end
652 649
            end
653
            
650

  
654 651
            if progress_date < Date.today
655 652
              late_date = [Date.today, end_date].min
656 653
              if late_date > self.date_from && late_date > start_date
......
663 660
            end
664 661
          end
665 662
        end
666
        
663

  
667 664
        # Transforms dates into pixels witdh
668 665
        coords.keys.each do |key|
669 666
          coords[key] = (coords[key] * zoom).floor
......
675 672
      def sort_issues!(issues)
676 673
        issues.sort! { |a, b| gantt_issue_compare(a, b, issues) }
677 674
      end
678
  
675

  
679 676
      # TODO: top level issues should be sorted by start date
680 677
      def gantt_issue_compare(x, y, issues)
681 678
        if x.root_id == y.root_id
......
684 681
          x.root_id <=> y.root_id
685 682
        end
686 683
      end
687
      
684

  
688 685
      def current_limit
689 686
        if @max_rows
690 687
          @max_rows - @number_of_rows
......
692 689
          nil
693 690
        end
694 691
      end
695
      
692

  
696 693
      def abort?
697 694
        if @max_rows && @number_of_rows >= @max_rows
698 695
          @truncated = true
699 696
        end
700 697
      end
701
      
698

  
702 699
      def pdf_new_page?(options)
703 700
        if options[:top] > 180
704 701
          options[:pdf].Line(15, options[:top], PDF::TotalWidth, options[:top])
......
707 704
          options[:pdf].Line(15, options[:top] - 0.1, PDF::TotalWidth, options[:top] - 0.1)
708 705
        end
709 706
      end
710
      
707

  
711 708
      def html_subject(params, subject, options={})
712 709
        style = "position: absolute;top:#{params[:top]}px;left:#{params[:indent]}px;"
713 710
        style << "width:#{params[:subject_width] - params[:indent]}px;" if params[:subject_width]
714
        
711

  
715 712
        output = view.content_tag 'div', subject, :class => options[:css], :style => style, :title => options[:title]
716 713
        @subjects << output
717 714
        output
718 715
      end
719
      
716

  
720 717
      def pdf_subject(params, subject, options={})
721 718
        params[:pdf].SetY(params[:top])
722 719
        params[:pdf].SetX(15)
723
        
720

  
724 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")
726
      
722
        params[:pdf].RDMCell(params[:subject_width]-15, 5, (" " * params[:indent]) +  subject.to_s.sub(/^(.{#{char_limit}}[^\s]*\s).*$/, '\1 (...)'), "LR")
723

  
727 724
        params[:pdf].SetY(params[:top])
728 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 727
      end
731
      
728

  
732 729
      def image_subject(params, subject, options={})
733 730
        params[:image].fill('black')
734 731
        params[:image].stroke('transparent')
735 732
        params[:image].stroke_width(1)
736 733
        params[:image].text(params[:indent], params[:top] + 2, subject)
737 734
      end
738
      
735

  
739 736
      def html_task(params, coords, options={})
740 737
        output = ''
741 738
        # Renders the task bar, with progress and late
742 739
        if coords[:bar_start] && coords[:bar_end]
743 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'>&nbsp;</div>"
744
          
741

  
745 742
          if coords[:bar_late_end]
746 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'>&nbsp;</div>"
747 744
          end
......
774 771
        @lines << output
775 772
        output
776 773
      end
777
      
774

  
778 775
      def pdf_task(params, coords, options={})
779 776
        height = options[:height] || 2
780
        
777

  
781 778
        # Renders the task bar, with progress and late
782 779
        if coords[:bar_start] && coords[:bar_end]
783 780
          params[:pdf].SetY(params[:top]+1.5)
784 781
          params[:pdf].SetX(params[:subject_width] + coords[:bar_start])
785 782
          params[:pdf].SetFillColor(200,200,200)
786
          params[:pdf].Cell(coords[:bar_end] - coords[:bar_start], height, "", 0, 0, "", 1)
787
            
783
          params[:pdf].RDMCell(coords[:bar_end] - coords[:bar_start], height, "", 0, 0, "", 1)
784

  
788 785
          if coords[:bar_late_end]
789 786
            params[:pdf].SetY(params[:top]+1.5)
790 787
            params[:pdf].SetX(params[:subject_width] + coords[:bar_start])
791 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 790
          end
794 791
          if coords[:bar_progress_end]
795 792
            params[:pdf].SetY(params[:top]+1.5)
796 793
            params[:pdf].SetX(params[:subject_width] + coords[:bar_start])
797 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 796
          end
800 797
        end
801 798
        # Renders the markers
......
804 801
            params[:pdf].SetY(params[:top] + 1)
805 802
            params[:pdf].SetX(params[:subject_width] + coords[:start] - 1)
806 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 805
          end
809 806
          if coords[:end]
810 807
            params[:pdf].SetY(params[:top] + 1)
811 808
            params[:pdf].SetX(params[:subject_width] + coords[:end] - 1)
812 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 811
          end
815 812
        end
816 813
        # Renders the label on the right
817 814
        if options[:label]
818 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 817
        end
821 818
      end
822 819

  
823 820
      def image_task(params, coords, options={})
824 821
        height = options[:height] || 6
825
        
822

  
826 823
        # Renders the task bar, with progress and late
827 824
        if coords[:bar_start] && coords[:bar_end]
828 825
          params[:image].fill('#aaa')
829 826
          params[:image].rectangle(params[:subject_width] + coords[:bar_start], params[:top], params[:subject_width] + coords[:bar_end], params[:top] - height)
830
 
827

  
831 828
          if coords[:bar_late_end]
832 829
            params[:image].fill('#f66')
833 830
            params[:image].rectangle(params[:subject_width] + coords[:bar_start], params[:top], params[:subject_width] + coords[:bar_late_end], params[:top] - height)

Also available in: Unified diff