gantt_edit_v1-3-3.patch

Jun NAITOH, 2012-06-16 02:34

Download (37.9 KB)

View differences:

app/helpers/issues_helper.rb (working copy)
51 51
    (link_to_issue(issue) + "<br /><br />" +
52 52
      "<strong>#{@cached_label_project}</strong>: #{link_to_project(issue.project)}<br />" +
53 53
      "<strong>#{@cached_label_status}</strong>: #{h(issue.status.name)}<br />" +
54
      "<strong>#{@cached_label_start_date}</strong>: #{format_date(issue.start_date)}<br />" +
55
      "<strong>#{@cached_label_due_date}</strong>: #{format_date(issue.due_date)}<br />" +
54
      #pstart
55
      "<strong>#{@cached_label_start_date}</strong>: <span id='tooltip_start_date_i#{issue.id}'>#{format_date(issue.start_date)}</span><br />" +
56
      "<strong>#{@cached_label_due_date}</strong>: <span id='tooltip_due_date_i#{issue.id}'>#{format_date(issue.due_date)}</span><br />" +
57
      #pend
56 58
      "<strong>#{@cached_label_assigned_to}</strong>: #{h(issue.assigned_to)}<br />" +
57 59
      "<strong>#{@cached_label_priority}</strong>: #{h(issue.priority.name)}").html_safe
58 60
  end
app/helpers/application_helper.rb (working copy)
959 959
    javascript_tag("Calendar.setup({inputField : '#{field_id}', ifFormat : '%Y-%m-%d', button : '#{field_id}_trigger' });")
960 960
  end
961 961

  
962
  #pstart
963
  def g_calendar_for(field_id)
964
    include_calendar_headers_tags
965
    image_tag("calendar.png", {:id => "#{field_id}_trigger",:class => "calendar-trigger"}) +
966
    javascript_tag("Calendar.setup({inputField : '#{field_id}', electric : false, ifFormat : '%Y-%m-%d', button : '#{field_id}_trigger' });")
967
  end
968
  #pend
969

  
962 970
  def include_calendar_headers_tags
963 971
    unless @calendar_headers_tags_included
964 972
      @calendar_headers_tags_included = true
app/models/issue.rb (working copy)
539 539
    dependencies
540 540
  end
541 541

  
542
  #pstart
543
  def all_precedes_issues
544
    dependencies = []
545
    relations_from.each do |relation|
546
      next unless relation.relation_type == IssueRelation::TYPE_PRECEDES
547
      dependencies << relation.issue_to
548
      dependencies += relation.issue_to.all_dependent_issues
549
    end
550
    dependencies
551
  end
552
  #pend
553

  
542 554
  # Returns an array of issues that duplicate this one
543 555
  def duplicates
544 556
    relations_to.select {|r| r.relation_type == IssueRelation::TYPE_DUPLICATES}.collect {|r| r.issue_from}
app/controllers/gantts_controller.rb (working copy)
45 45
      format.pdf  { send_data(@gantt.to_pdf, :type => 'application/pdf', :filename => "#{basename}.pdf") }
46 46
    end
47 47
  end
48

  
49
  #pstart
50
  def edit_gantt
51
    date_from = Date.parse(params[:date_from])
52
    date_to = Date.parse(params[:date_to])
53
    months = date_to.month - date_from.month + 1
54
    params[:year] = date_from.year
55
    params[:month] = date_from.month
56
    params[:months] = months
57
    @gantt = Redmine::Helpers::Gantt.new(params)
58
    @gantt.project = @project
59
    text, status = @gantt.edit(params)
60
    render :text=>text, :status=>status
61
  end
62

  
63
  def find_optional_project
64
    begin
65
      if params[:action] && params[:action].to_s == "edit_gantt"
66
        @project = Project.find(params[:project_id]) unless params[:project_id].blank?
67
        allowed = User.current.allowed_to?(:edit_issues, @project, :global => true)
68
        if allowed
69
          return true
70
        else
71
          render :text=>"lack of permission to edit issues", :status=>403
72
        end
73
      else
74
        super
75
      end
76
    rescue => e
77
      return e.to_s + "\n===\n" + [$!,$@.join("\n")].join("\n")
78
    end
79
  end
80
  #pend
81

  
48 82
end
app/views/gantts/show.html.erb (working copy)
1
<% include_calendar_headers_tags %>
1 2
<% @gantt.view = self %>
2 3
<h2><%= @query.new_record? ? l(:label_gantt) : h(@query.name) %></h2>
3 4

  
......
32 33
<% zoom = 1
33 34
@gantt.zoom.times { zoom = zoom * 2 }
34 35

  
35
subject_width = 330
36
subject_width = 280
36 37
header_heigth = 18
37 38

  
38 39
headers_height = header_heigth
......
59 60

  
60 61
%>
61 62

  
63
<input type="hidden" name="_date_from" id="i_date_from" value="<%=h(@gantt.date_from)%>" />
64
<input type="hidden" name="_date_to" id="i_date_to" value="<%=h(@gantt.date_to)%>" />
65
<input type="hidden" name="zm" id="i_zm" value="<%=zoom%>" />
66
<input type="hidden" name="pzm" id="i_pzm" value="<%=@gantt.zoom%>" />
67
<script type='text/javascript'>
68
  function issue_moved(elem) {
69
    var id_str = elem.id.substring(3, elem.id.length);
70
    var v_date_from = document.getElementById('i_date_from').getAttribute("value");
71
    var v_date_to = document.getElementById('i_date_to').getAttribute("value");
72
    var v_zm = document.getElementById('i_zm').getAttribute("value");
73
    var v_pzm = document.getElementById('i_pzm').getAttribute("value");
74
    var url_str = '<%=  url_for(:controller=>:gantts, :action => :edit_gantt) %>';
75
    url_str = url_str + "/" + id_str;
76
    var day = parseInt(elem.style.left)/parseInt(v_zm);
77
    new Ajax.Request(url_str, {asynchronous:true, evalScripts:true,
78
      parameters: 'day='+day+'&date_from='+v_date_from+'&date_to='+v_date_to+'&zoom='+v_pzm+"&project_id=<%= @project.to_param %>",
79
      onSuccess:function(obj) {
80
        change_dates(obj.responseText);
81
      },
82
      onFailure:function(obj) {
83
        handle_failure(obj.responseText);
84
      }
85
    });
86

  
87
  }
88

  
89
  function handle_failure(res_text) {
90
    var text = res_text.split('|');
91
    alert(text[0]);
92
    if (text.length == 1) {
93
      return;
94
    }
95
    change_dates(text[1]);//revert
96
  }
97

  
98
  function change_dates(issue_infos) {
99
    if (!issue_infos) {
100
      return;
101
    }
102
    var issue_list = issue_infos.split("|");
103
    for (i = 0; i < issue_list.length; i++) {
104
      change_date(issue_list[i]);
105
    }
106
  }
107

  
108
  function change_date(text) {
109
    if (!text) {
110
      return;
111
    }
112
    var issue_info = text.split("=");
113
    var elem_id = issue_info[0];
114
    var kind = elem_id.substring(0,1);
115
    var preClassName = "";
116
    if (kind == 'v') {
117
      preClassName = "version ";
118
    } else if (kind == 'p') {
119
      preClassName = "project ";
120
    }
121
    var vals = issue_info[1].split(',');
122
    var start_date_elem = document.getElementById(elem_id + '_start_date_str');
123
    if (!start_date_elem) {
124
      //target not exists
125
      return;
126
    }
127
    start_date_elem.innerHTML = vals[0];
128
    var tooltip_start_date_elem = document.getElementById('tooltip_start_date_' + elem_id);
129
    if (tooltip_start_date_elem) {
130
      tooltip_start_date_elem.innerHTML = vals[0];
131
    }
132
    var due_date_elem = document.getElementById(elem_id + '_due_date_str');
133
    if (due_date_elem) {
134
      due_date_elem.innerHTML = vals[2];
135
    }
136
    
137
    var tooltip_due_date_elem = document.getElementById('tooltip_due_date_' + elem_id);
138
    if (tooltip_due_date_elem) {
139
      tooltip_due_date_elem.innerHTML = vals[2];
140
    }
141
    
142
    var ev_elem = document.getElementById('ev_' + elem_id);
143
    if (ev_elem) {
144
      ev_elem.style.left = vals[4] + 'px';
145
      ev_elem.style.width = (parseInt(vals[5])+100)+'px';
146
    }
147
    var todo_elem = document.getElementById('task_todo_' + elem_id);
148
    if (todo_elem) {
149
      todo_elem.style.width = vals[5] + 'px';
150
    }
151
    
152
    var late_elem = document.getElementById('task_late_' + elem_id);
153
    if (late_elem) {
154
      var parentStr = "";
155
      if (late_elem.className.indexOf("parent") > 0) parentStr = "parent ";
156
      late_elem.style.width = vals[6] + 'px';
157
      if (vals[6] == '0') {
158
        late_elem.className = preClassName + 'task ' + parentStr + 'task_none';
159
      } else {
160
        late_elem.className = preClassName + 'task ' + parentStr + 'task_late';
161
      }
162
    }
163
    var done_elem = document.getElementById('task_done_' + elem_id);
164
    if (done_elem) {
165
      var parentStr = "";
166
      if (done_elem.className.indexOf("parent") > 0) parentStr = "parent ";
167
      done_elem.style.width = vals[7] + 'px';
168
      if (vals[7] == '0') {
169
        done_elem.className = preClassName + 'task ' + parentStr + 'task_none';
170
      } else {
171
        done_elem.className = preClassName + 'task ' + parentStr + 'task_done';
172
      }
173
    }
174
    //var task_elem = document.getElementById('task_task_' + elem_id);
175
    //if (task_elem) {
176
    //  task_elem.style.left = (parseInt(vals[5])+5) + 'px';
177
    //}
178
    var tooltip = document.getElementById("tt_" + elem_id);
179
    if (tooltip) {
180
      tooltip.style.left = ev_elem.style.left;
181
    }
182

  
183
    var label = document.getElementById("label_" + elem_id);
184
    if (label) {
185
      label.style.left = (parseInt(vals[4]) + parseInt(vals[5]) + 8) + 'px';
186
    }
187
    var marker_start = document.getElementById("marker_start_" + elem_id);
188
    if (marker_start && vals[8]) {
189
      marker_start.style.left = vals[8] + 'px';
190
    }
191
    var marker_end = document.getElementById("marker_end_" + elem_id);
192
    if (marker_end && vals[9]) {
193
      marker_end.style.left = vals[9] + 'px';
194
    }
195

  
196
    //change calendar date
197
    var elm1 = document.getElementById(elem_id+"_hidden_start_date");
198
    if (elm1) elm1.value = vals[1];
199
    var elm2 = document.getElementById(elem_id+"_start_date");
200
    if (elm2) elm2.value = vals[1];
201
    var elm3 = document.getElementById(elem_id+"_hidden_due_date");
202
    if (elm3) elm3.value = vals[3];
203
    var elm4 = document.getElementById(elem_id+"_due_date");
204
    if (elm4) elm4.value = vals[3];
205
  }
206
</script>
207

  
62 208
<% if @gantt.truncated %>
63 209
  <p class="warning"><%= l(:notice_gantt_chart_truncated, :max => @gantt.max_rows) %></p>
64 210
<% end %>
......
77 223

  
78 224
</div>
79 225
</td>
226

  
227
<td style="width:225px; padding:0px;">
228
<div style="position:relative;height:<%= t_height + 24 %>px;width:225px;">
229
<div style="width:225px;height:<%= headers_height %>px;background: #eee;" class="gantt_hdr"></div>
230
<div style="width:225px;height:<%= t_height %>px;border-left: 1px solid #c0c0c0;overflow:hidden;" class="gantt_hdr"></div>
231

  
232
<div class="gantt_subjects">
233
<%= @gantt.calendars %>
234
</div>
235

  
236
</div>
237
</td>
238

  
80 239
<td>
81 240

  
82
<div style="position:relative;height:<%= t_height + 24 %>px;overflow:auto;">
241
<div style="position:relative;height:<%= t_height + 24 %>px;overflow:auto;" id="gantt-container">
83 242
<div style="width:<%= g_width-1 %>px;height:<%= headers_height %>px;background: #eee;" class="gantt_hdr">&nbsp;</div>
84 243
<%
85 244
#
......
139 298
  left = 0
140 299
  height = g_height + header_heigth - 1
141 300
  wday = @gantt.date_from.cwday
301
  dt = @gantt.date_from
142 302
  (@gantt.date_to - @gantt.date_from + 1).to_i.times do
143 303
  width =  zoom - 1
144 304
  %>
145 305
  <div style="left:<%= left %>px;top:37px;width:<%= width %>px;height:<%= height %>px;font-size:0.7em;<%= "background:#f1f1f1;" if wday > 5 %>" class="gantt_hdr">
146 306
  <%= day_name(wday).first %>
307
  <%=  "#{dt.day}<br>"if @gantt.zoom == 4 %><%= day_name(wday).first %>
147 308
  </div>
148 309
  <%
149 310
  left = left + width+1
150 311
  wday = wday + 1
312
  dt = dt + 1
151 313
  wday = 1 if wday > 7
152 314
  end
153 315
end %>
lib/redmine/helpers/gantt.rb (working copy)
70 70

  
71 71
        @subjects = ''
72 72
        @lines = ''
73
        #pstart
74
        @calendars = ''
75
        #pend
73 76
        @number_of_rows = nil
74 77

  
75 78
        @issue_ancestors = []
......
129 132
        @lines
130 133
      end
131 134

  
135
      #pstart
136
      # Renders the calendars of the Gantt chart, the right side
137
      def calendars(options={})
138
        render(options.merge(:only => :calendars)) unless @calendars_rendered
139
        @calendars
140
      end
141
      #pend
142

  
132 143
      # Returns issues that will be rendered
133 144
      def issues
134 145
        @issues ||= @query.issues(
......
175 186
        options = {:top => 0, :top_increment => 20, :indent_increment => 20, :render => :subject, :format => :html}.merge(options)
176 187
        indent = options[:indent] || 4
177 188

  
178
        @subjects = '' unless options[:only] == :lines
179
        @lines = '' unless options[:only] == :subjects
189
         #pstart
190
        if options[:format] == :html
191
          @subjects = '' unless options[:only] == :lines && options[:only] == :calendars
192
          @lines = '' unless options[:only] == :subjects && options[:only] == :calendars
193
          @calendars = '' unless options[:only] == :lines && options[:only] == :subjects
194
        else
195
          @subjects = '' unless options[:only] == :lines
196
          @lines = '' unless options[:only] == :subjects
197
        end
198
        #pend
199

  
180 200
        @number_of_rows = 0
181 201

  
182 202
        Project.project_tree(projects) do |project, level|
......
185 205
          break if abort?
186 206
        end
187 207

  
188
        @subjects_rendered = true unless options[:only] == :lines
189
        @lines_rendered = true unless options[:only] == :subjects
208
        #pstart
209
        if options[:format] == :html
210
          @subjects_rendered = true unless options[:only] == :lines && options[:only] == :calendars
211
          @lines_rendered = true unless options[:only] == :subjects && options[:only] == :calendars
212
          @calendars_rendered = true unless options[:only] == :lines && options[:only] == :subjects
213
        else
214
          @subjects_rendered = true unless options[:only] == :lines
215
          @lines_rendered = true unless options[:only] == :subjects
216
        end
217
        #pend
190 218

  
191 219
        render_end(options)
192 220
      end
193 221

  
194 222
      def render_project(project, options={})
195
        subject_for_project(project, options) unless options[:only] == :lines
196
        line_for_project(project, options) unless options[:only] == :subjects
197 223

  
224
        #pstart
225
        if options[:format] == :html
226
          subject_for_project(project, options) unless options[:only] == :lines && options[:only] == :calendars
227
          line_for_project(project, options) unless options[:only] == :subjects && options[:only] == :calendars
228
          calendar_for_project(project, options) unless options[:only] == :lines && options[:only] == :subjects
229
        else
230
          subject_for_project(project, options) unless options[:only] == :lines
231
          line_for_project(project, options) unless options[:only] == :subjects
232
        end
233
        #pend
234

  
198 235
        options[:top] += options[:top_increment]
199 236
        options[:indent] += options[:indent_increment]
200 237
        @number_of_rows += 1
......
220 257
        @issue_ancestors = []
221 258

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

  
261
          #pstart
262
          if options[:format] == :html
263
            subject_for_issue(i, options) unless options[:only] == :lines && options[:only] == :calendars
264
            line_for_issue(i, options) unless options[:only] == :subjects && options[:only] == :calendars
265
            calendar_for_issue(i, options) unless options[:only] == :lines && options[:only] == :subjects
266
          else
267
            subject_for_issue(i, options) unless options[:only] == :lines
268
            line_for_issue(i, options) unless options[:only] == :subjects
269
          end
270
          #pend
271

  
226 272
          options[:top] += options[:top_increment]
227 273
          @number_of_rows += 1
228 274
          break if abort?
......
233 279

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

  
283
          #pstart
284
          if options[:format] == :html
285
            subject_for_version(version, options) unless options[:only] == :lines && options[:only] == :calendars
286
            line_for_version(version, options) unless options[:only] == :subjects && options[:only] == :calendars
287
            calendar_for_version(version, options) unless options[:only] == :lines && options[:only] == :subjects
288
          else
289
            subject_for_version(version, options) unless options[:only] == :lines
290
            line_for_version(version, options) unless options[:only] == :subjects
291
          end
292
          #pend
293

  
239 294
        options[:top] += options[:top_increment]
240 295
        @number_of_rows += 1
241 296
        return if abort?
......
283 338

  
284 339
          case options[:format]
285 340
          when :html
286
            html_task(options, coords, :css => "project task", :label => label, :markers => true)
341

  
342
            #pstart
343
            html_task(options, coords, :css => "project task", :label => label, :markers => true, :id => project.id, :kind => "p")
344
            #pend
345

  
287 346
          when :image
288 347
            image_task(options, coords, :label => label, :markers => true, :height => 3)
289 348
          when :pdf
......
322 381

  
323 382
          case options[:format]
324 383
          when :html
325
            html_task(options, coords, :css => "version task", :label => label, :markers => true)
384

  
385
            #pstart
386
            html_task(options, coords, :css => "version task", :label => label, :markers => true, :id => version.id, :kind => "v")
387
            #pend
388

  
326 389
          when :image
327 390
            image_task(options, coords, :label => label, :markers => true, :height => 3)
328 391
          when :pdf
......
376 439
          coords = coordinates(issue.start_date, issue.due_before, issue.done_ratio, options[:zoom])
377 440
          label = "#{ issue.status.name } #{ issue.done_ratio }%"
378 441

  
442
          #pstart
443
          if !issue.due_date && issue.fixed_version
444
            if options[:format] == :html
445
              label += "-&nbsp;<strong>#{h(issue.fixed_version.name)}</strong>"
446
            else
447
              label += "-#{h(issue.fixed_version.name)}"
448
            end
449
          end
450
          #pend
451

  
379 452
          case options[:format]
380 453
          when :html
381
            html_task(options, coords, :css => "task " + (issue.leaf? ? 'leaf' : 'parent'), :label => label, :issue => issue, :markers => !issue.leaf?)
454

  
455
            #pstart
456
            html_task(options, coords, :css => "task " + (issue.leaf? ? 'leaf' : 'parent'), :label => label, :issue => issue, :markers => !issue.leaf?, :id => issue.id, :kind => "i")
457
            #pend
458

  
382 459
          when :image
383 460
            image_task(options, coords, :label => label)
384 461
          when :pdf
......
618 695
        pdf.Output
619 696
      end
620 697

  
698
      #pstart
699
      def edit(pms)
700
        id = pms[:id]
701
        kind = id.slice!(0).chr
702
        begin
703
          case kind
704
          when 'i'
705
            @issue = Issue.find(pms[:id], :include => [:project, :tracker, :status, :author, :priority, :category])
706
          when 'p'
707
            @issue = Project.find(pms[:id])
708
          when 'v'
709
            @issue = Version.find(pms[:id], :include => [:project])
710
          end
711
        rescue ActiveRecord::RecordNotFound
712
          return "issue not found : #{pms[:id]}", 400
713
        end
714

  
715
        if !@issue.start_date || !@issue.due_before
716
          #render :text=>l(:notice_locking_conflict), :status=>400
717
          return l(:notice_locking_conflict), 400
718
        end
719
        @issue.init_journal(User.current)
720
        date_from = Date.parse(pms[:date_from])
721
        old_start_date = @issue.start_date
722
        o = get_issue_position(@issue, pms[:zoom])
723
        text_for_revert = "#{kind}#{id}=#{format_date(@issue.start_date)},#{@issue.start_date},#{format_date(@issue.due_before)},#{@issue.due_before},#{o[0]},#{o[1]},#{o[2]},#{o[3]}"
724

  
725
        if pms[:day]
726
          #bar moved
727
          day = pms[:day].to_i
728
          duration = @issue.due_before - @issue.start_date
729
          @issue.start_date = date_from + day
730
          @issue.due_date = @issue.start_date + duration.to_i if @issue.due_date
731
        elsif pms[:start_date]
732
          #start date changed
733
          start_date = Date.parse(pms[:start_date])
734
          if @issue.start_date == start_date
735
            #render :text=>""
736
            return "", 200 #nothing has changed
737
          end
738
          @issue.start_date = start_date
739
          @issue.due_date = start_date if @issue.due_date && start_date > @issue.due_date
740
        elsif pms[:due_date]
741
          #due date changed
742
          due_date = Date.parse(pms[:due_date])
743
          if @issue.due_date == due_date
744
            #render :text=>""
745
            return "", 200 #nothing has changed
746
          end
747
          @issue.due_date = due_date
748
          @issue.start_date = due_date if due_date < @issue.start_date
749
        end
750
        fv = @issue.fixed_version
751
        if fv && fv.effective_date && !@issue.due_date && fv.effective_date < @issue.start_date
752
          @issue.start_date = old_start_date
753
        end
754

  
755
        begin
756
          @issue.save!
757
          o = get_issue_position(@issue, pms[:zoom])
758
          text = "#{kind}#{id}=#{format_date(@issue.start_date)},#{@issue.start_date},#{format_date(@issue.due_before)},#{@issue.due_before},#{o[0]},#{o[1]},#{o[2]},#{o[3]}"
759

  
760
          prj_map = {}
761
          text = set_project_data(@issue.project, pms[:zoom], text, prj_map)
762
          version_map = {}
763
          text = set_version_data(@issue.fixed_version, pms[:zoom], text, version_map)
764

  
765
          #check dependencies
766
          issues = @issue.all_precedes_issues
767
          issues.each do |i|
768
            o = get_issue_position(i, pms[:zoom])
769
            text += "|i#{i.id}=#{format_date(i.start_date)},#{i.start_date},#{format_date(i.due_before)},#{i.due_before},#{o[0]},#{o[1]},#{o[2]},#{o[3]}"
770
            text = set_project_data(i.project, pms[:zoom], text, prj_map)
771
            text = set_version_data(i.fixed_version, pms[:zoom], text, version_map)
772
          end
773

  
774
          #check parent
775
          is = @issue
776
          while
777
            pid = is.parent_issue_id
778
            break if !pid
779
            i = Issue.find(pid)
780
            o = get_issue_position(i, pms[:zoom])
781
            text += "|i#{i.id}=#{format_date(i.start_date)},#{i.start_date},#{format_date(i.due_before)},#{i.due_before},#{o[0]},#{o[1]},#{o[2]},#{o[3]},#{o[4]},#{o[5]}"
782
            text = set_project_data(i.project, pms[:zoom], text, prj_map)
783
            text = set_version_data(i.fixed_version, pms[:zoom], text, version_map)
784
            is = i
785
          end
786
          #render :text=>text
787
          return text, 200
788
        rescue => e
789
          #render :text=>@issue.errors.full_messages.join("\n") + "|" + text_for_revert  , :status=>400
790
          if @issue.errors.full_messages.to_s == ""
791
            return e.to_s + "\n" + [$!,$@.join("\n")].join("\n") + "\n" + @issue.errors.full_messages.join("\n") + "|" + text_for_revert, 400
792
          else
793
            return @issue.errors.full_messages.join("\n") + "|" + text_for_revert, 400
794
          end
795
        end
796
      end
797
      #pend
798

  
621 799
      private
622 800

  
623 801
      def coordinates(start_date, end_date, progress, zoom=nil)
......
737 915
        output = ''
738 916
        # Renders the task bar, with progress and late
739 917
        if coords[:bar_start] && coords[:bar_end]
740
          output << "<div style='top:#{ params[:top] }px;left:#{ coords[:bar_start] }px;width:#{ coords[:bar_end] - coords[:bar_start] - 2}px;' class='#{options[:css]} task_todo'>&nbsp;</div>".html_safe
918
          #pstart
919
          i_width = coords[:bar_end] - coords[:bar_start] - 2
920
          output << "<div id='ev_#{options[:kind]}#{options[:id]}' style='position:absolute;left:#{coords[:bar_start]}px;top:#{params[:top]}px;padding-top:3px;height:18px;width:#{ i_width + 100}px;' #{options[:kind] == 'i' ? "class='handle'" : ""}>".html_safe
921
          output << "<div id='task_todo_#{options[:kind]}#{options[:id]}' style='float:left:0px; width:#{ i_width}px;' class='#{options[:css]} task_todo'>&nbsp;</div>".html_safe
922
          #pend
741 923

  
742 924
          if coords[:bar_late_end]
743
            output << "<div style='top:#{ params[:top] }px;left:#{ coords[:bar_start] }px;width:#{ coords[:bar_late_end] - coords[:bar_start] - 2}px;' class='#{options[:css]} task_late'>&nbsp;</div>".html_safe
925
            #pstart
926
            l_width = coords[:bar_late_end] - coords[:bar_start] - 2
927
            output << "<div id='task_late_#{options[:kind]}#{options[:id]}' style='float:left:0px; width:#{ l_width}px;' class='#{ l_width == 0 ? options[:css] + " task_none" : options[:css] + " task_late"}'>&nbsp;</div>".html_safe
928
          else
929
            output << "<div id='task_late_#{options[:kind]}#{options[:id]}' style='float:left:0px; width:0px;' class='#{ options[:css] + " task_none"}'>&nbsp;</div>".html_safe
930
            #pend
744 931
          end
745 932
          if coords[:bar_progress_end]
746
            output << "<div style='top:#{ params[:top] }px;left:#{ coords[:bar_start] }px;width:#{ coords[:bar_progress_end] - coords[:bar_start] - 2}px;' class='#{options[:css]} task_done'>&nbsp;</div>".html_safe
933
            #pstart
934
            d_width = coords[:bar_progress_end] - coords[:bar_start] - 2
935
            output << "<div id='task_done_#{options[:kind]}#{options[:id]}' style='float:left:0px; width:#{ d_width}px;' class='#{ d_width == 0 ? options[:css] + " task_none" : options[:css] + " task_done"}'>&nbsp;</div>".html_safe
936
          else
937
            output << "<div id='task_done_#{options[:kind]}#{options[:id]}' style='float:left:0px; width:0px;' class='#{ options[:css] + " task_none"}'>&nbsp;</div>".html_safe
938
            #pend
747 939
          end
940
          #pstart
941
          output << "</div>".html_safe
942
        else
943
          output << "<div id='ev_#{options[:kind]}#{options[:id]}' style='position:absolute;left:0px;top:#{params[:top]}px;padding-top:3px;height:18px;width:0px;' #{options[:kind] == 'i' ? "class='handle'" : ""}>".html_safe
944
          output << "<div id='task_todo_#{options[:kind]}#{options[:id]}' style='float:left:0px; width:0px;' class='#{ options[:css]} task_todo'>&nbsp;</div>".html_safe
945
          output << "<div id='task_late_#{options[:kind]}#{options[:id]}' style='float:left:0px; width:0px;' class='#{ options[:css] + " task_none"}'>&nbsp;</div>".html_safe
946
          output << "<div id='task_done_#{options[:kind]}#{options[:id]}' style='float:left:0px; width:0px;' class='#{ options[:css] + " task_none"}'>&nbsp;</div>".html_safe
947
          output << "</div>".html_safe
948
        #pend
748 949
        end
749 950
        # Renders the markers
750 951
        if options[:markers]
751 952
          if coords[:start]
752
            output << "<div style='top:#{ params[:top] }px;left:#{ coords[:start] }px;width:15px;' class='#{options[:css]} marker starting'>&nbsp;</div>".html_safe
953
            #pstart
954
            output << "<div id='marker_start_#{options[:kind]}#{options[:id]}' style='top:#{ params[:top] }px;left:#{ coords[:start] }px;width:15px;' class='#{options[:css]} marker starting'>&nbsp;</div>".html_safe
955
            #pend
753 956
          end
754 957
          if coords[:end]
755
            output << "<div style='top:#{ params[:top] }px;left:#{ coords[:end] + params[:zoom] }px;width:15px;' class='#{options[:css]} marker ending'>&nbsp;</div>".html_safe
958
            #pstart
959
            output << "<div id='marker_end_#{options[:kind]}#{options[:id]}' style='top:#{ params[:top] }px;left:#{ coords[:end] + params[:zoom] }px;width:15px;' class='#{options[:css]} marker ending'>&nbsp;</div>".html_safe
960
            #pend
756 961
          end
962
        #pstart
963
        else
964
          output << view.draggable_element("ev_#{options[:kind]}#{options[:id]}", :revert =>false, :scroll=>"'gantt-container'", :constraint => "'horizontal'", :snap=>params[:zoom],:onEnd=>'function( draggable, event )  {issue_moved(draggable.element);}')         #pend
757 965
        end
758 966
        # Renders the label on the right
759 967
        if options[:label]
760
          output << "<div style='top:#{ params[:top] }px;left:#{ (coords[:bar_end] || 0) + 8 }px;' class='#{options[:css]} label'>".html_safe
968
          #psrat
969
          output << "<div id='label_#{options[:kind]}#{options[:id]}' style='top:#{ params[:top] }px;left:#{ (coords[:bar_end] || 0) + 8 }px;' class='#{options[:css]} label'>".html_safe
970
          #pend
761 971
          output << options[:label]
762 972
          output << "</div>".html_safe
763 973
        end
764 974
        # Renders the tooltip
765 975
        if options[:issue] && coords[:bar_start] && coords[:bar_end]
766
          output << "<div class='tooltip' style='position: absolute;top:#{ params[:top] }px;left:#{ coords[:bar_start] }px;width:#{ coords[:bar_end] - coords[:bar_start] }px;height:12px;'>".html_safe
976
          #pstart
977
          output << "<div id='tt_#{options[:kind]}#{options[:id]}' class='tooltip' style='position: absolute;top:#{ params[:top] }px;left:#{ coords[:bar_start] }px;width:#{ coords[:bar_end] - coords[:bar_start] }px;height:12px;'>".html_safe
978
          #pend
767 979
          output << '<span class="tip">'.html_safe
768 980
          output << view.render_issue_tooltip(options[:issue]).html_safe
769 981
          output << "</span></div>".html_safe
......
855 1067
          params[:image].text(params[:subject_width] + (coords[:bar_end] || 0) + 5,params[:top] + 1, options[:label])
856 1068
        end
857 1069
      end
1070

  
1071
      #pstart
1072
      ##
1073
      ##  for edit gantt
1074
      ##
1075
      def set_project_data(prj, zoom, text, prj_map = {})
1076
        if !prj
1077
          return text
1078
        end
1079
        if !prj_map[prj.id]
1080
          o = get_project_position(prj, zoom)
1081
          text += "|p#{prj.id}=#{format_date(prj.start_date)},#{prj.start_date},#{format_date(prj.due_date)},#{prj.due_date},#{o[0]},#{o[1]},#{o[2]},#{o[3]},#{o[4]},#{o[5]}"
1082
          prj_map[prj.id] = prj
1083
        end
1084
        text = set_project_data(prj.parent, zoom, text, prj_map)
1085
      end
1086

  
1087
      def set_version_data(version, zoom, text, version_map = {})
1088
        if !version
1089
          return text
1090
        end
1091
        if !version_map[version.id]
1092
          o = get_version_position(version, zoom)
1093
          text += "|v#{version.id}=#{format_date(version.start_date)},#{version.start_date},#{format_date(version.due_date)},#{version.due_date},#{o[0]},#{o[1]},#{o[2]},#{o[3]},#{o[4]},#{o[5]}"
1094
          version_map[version.id] = version
1095
        end
1096
        return text
1097
      end
1098

  
1099
      def get_pos(coords)
1100
        i_left = 0
1101
        i_width = 0
1102
        l_width = 0
1103
        d_width = 0
1104
        if coords[:bar_start]
1105
          i_left = coords[:bar_start]
1106
          if coords[:bar_end]
1107
            i_width = coords[:bar_end] - coords[:bar_start] - 2
1108
            i_width = 0 if i_width < 0
1109
          end
1110
          if coords[:bar_late_end]
1111
            l_width = coords[:bar_late_end] - coords[:bar_start] - 2
1112
          end
1113
          if coords[:bar_progress_end]
1114
            d_width = coords[:bar_progress_end] - coords[:bar_start] - 2
1115
          end
1116
        end
1117
        return i_left, i_width, l_width, d_width
1118
      end
1119

  
1120
      def get_issue_position(issue, zoom_str)
1121
        z = zoom_str.to_i
1122
        zoom = 1
1123
        z.times { zoom = zoom * 2}
1124
        id = issue.due_before
1125
        if id && @date_to < id
1126
          id = @date_to
1127
        end
1128
        coords = coordinates(issue.start_date, id, issue.done_ratio, zoom)
1129

  
1130
        i_left, i_width, l_width, d_width = get_pos(coords)
1131
        if coords[:end]
1132
          return i_left, i_width, l_width, d_width, coords[:start], coords[:end] + zoom
1133
        else
1134
          return i_left, i_width, l_width, d_width, coords[:start], nil
1135
        end
1136
      end
1137

  
1138
      def get_project_position(project, zoom_str)
1139
        z = zoom_str.to_i
1140
        zoom = 1
1141
        z.times { zoom = zoom * 2}
1142
        pd = project.due_date
1143
        if pd && @date_to < pd
1144
          pd = @date_to
1145
        end
1146
        coords = coordinates(project.start_date, pd, nil, zoom)
1147
        i_left, i_width, l_width, d_width = get_pos(coords)
1148
        if coords[:end]
1149
          return i_left, i_width, l_width, d_width, coords[:start], coords[:end] + zoom
1150
        else
1151
          return i_left, i_width, l_width, d_width, coords[:start], nil
1152
        end
1153
      end
1154

  
1155
      def get_version_position(version, zoom_str)
1156
        z = zoom_str.to_i
1157
        zoom = 1
1158
        z.times { zoom = zoom * 2}
1159
        vd = version.due_date
1160
        if vd &&  @date_to < vd
1161
          vd = @date_to
1162
        end
1163
        coords = coordinates(version.start_date, vd, version.completed_pourcent, zoom)
1164
        i_left, i_width, l_width, d_width = get_pos(coords)
1165
        if coords[:end]
1166
          return i_left, i_width, l_width, d_width, coords[:start], coords[:end] + zoom
1167
        else
1168
          return i_left, i_width, l_width, d_width, coords[:start], nil
1169
        end
1170
      end
1171

  
1172
      def calendar_for_issue(issue, options)
1173
        # Skip issues that don't have a due_before (due_date or version's due_date)
1174
        if issue.is_a?(Issue) && issue.due_before
1175

  
1176
          case options[:format]
1177
          when :html
1178
            @calendars << "<div style='position: absolute;line-height:1.2em;height:16px;top:#{options[:top]}px;left:4px;overflow:hidden;'>"
1179
            start_date = issue.start_date
1180
            if start_date
1181
              @calendars << "<span id='i#{issue.id}_start_date_str'>"
1182
              @calendars << format_date(start_date)
1183
              @calendars << "</span>"
1184
              @calendars << "<input type='hidden' size='12' id='i#{issue.id}_hidden_start_date' value='#{start_date}' />"
1185
              if issue.leaf?
1186
                @calendars << "<input type='hidden' size='12' id='i#{issue.id}_start_date' value='#{start_date}' />#{view.g_calendar_for('i' + issue.id.to_s + '_start_date')}"
1187
              else
1188
                @calendars << "<input type='hidden' size='12' id='i#{issue.id}_start_date' value='#{start_date}' />&nbsp;&nbsp;&nbsp;"
1189
              end
1190
              @calendars << observe_date_field("i#{issue.id}", 'start')
1191
            end
1192
            due_date = issue.due_date
1193
            if due_date
1194
              @calendars << "<span id='i#{issue.id}_due_date_str'>"
1195
              @calendars << format_date(due_date)
1196
              @calendars << "</span>"
1197
              @calendars << "<input type='hidden' size='12' id='i#{issue.id}_hidden_due_date' value='#{due_date}' />"
1198
              if issue.leaf?
1199
                @calendars << "<input type='hidden' size='12' id='i#{issue.id}_due_date' value='#{due_date}' />#{view.g_calendar_for('i' + issue.id.to_s + '_due_date')}"
1200
              else
1201
                @calendars << "<input type='hidden' size='12' id='i#{issue.id}_due_date' value='#{due_date}' />"
1202
              end
1203
              @calendars << observe_date_field("i#{issue.id}", 'due')
1204
            end
1205
            @calendars << "</div>"
1206
          when :image
1207
            #nop
1208
          when :pdf
1209
            #nop
1210
          end
1211
        else
1212
          ActiveRecord::Base.logger.debug "GanttHelper#line_for_issue was not given an issue with a due_before"
1213
          ''
1214
        end
1215
      end
1216

  
1217
      def calendar_for_version(version, options)
1218
        # Skip version that don't have a due_before (due_date or version's due_date)
1219
        if version.is_a?(Version) && version.start_date && version.due_date
1220

  
1221
          case options[:format]
1222
          when :html
1223
            @calendars << "<div style='position: absolute;line-height:1.2em;height:16px;top:#{options[:top]}px;left:4px;overflow:hidden;'>"
1224
            @calendars << "<span id='v#{version.id}_start_date_str'>"
1225
            @calendars << format_date(version.effective_date)
1226
            @calendars << "</span>"
1227
            @calendars << "</div>"
1228
          when :image
1229
            #nop
1230
          when :pdf
1231
            #nop
1232
          end
1233
        else
1234
          ActiveRecord::Base.logger.debug "GanttHelper#line_for_issue was not given an issue with a due_before"
1235
          ''
1236
        end
1237
      end
1238

  
1239
      def calendar_for_project(project, options)
1240
        case options[:format]
1241
        when :html
1242
          @calendars << "<div style='position: absolute;line-height:1.2em;height:16px;top:#{options[:top]}px;left:4px;overflow:hidden;'>"
1243
          @calendars << "<span id='p#{project.id}_start_date_str'>"
1244
          @calendars << format_date(project.start_date) if project.start_date
1245
          @calendars << "</span>"
1246
          @calendars << "&nbsp;&nbsp;&nbsp;"
1247
          @calendars << "<span id='p#{project.id}_due_date_str'>"
1248
          @calendars << format_date(project.due_date) if project.due_date
1249
          @calendars << "</span>"
1250
          @calendars << "</div>"
1251
        when :image
1252
          # nop
1253
        when :pdf
1254
          # nop
1255
        end
1256
      end
1257

  
1258
      def observe_date_field(id, type)
1259
        output = ''
1260
        prj_id = ''
1261
        prj_id = @project.to_param if @project
1262
        output << "<script type='text/javascript'>\n"
1263
        output << "//<![CDATA[\n"
1264
        output << "new Form.Element.Observer('#{id}_#{type}_date', 0.25,\n"
1265
        output << "  function(element, value) {\n"
1266
        output << "    if (value == document.getElementById('#{id}_hidden_#{type}_date').value) {\n"
1267
        output << "      return ;\n"
1268
        output << "    }\n"
1269
        output << "    new Ajax.Request('#{view.url_for(:controller=>:gantts, :action => :edit_gantt, :id=>id, :date_from=>self.date_from.strftime("%Y-%m-%d"), :date_to=>self.date_to.strftime("%Y-%m-%d"), :zoom=>self.zoom, :escape => false, :project_id=>prj_id)}', {asynchronous:true, evalScripts:true, onFailure:function(request){handle_failure(request.responseText)}, onSuccess:function(request){change_dates(request.responseText)}, parameters:'#{type}_date=' + encodeURIComponent(value)});"
1270
        output << "  })\n"
1271
        output << "//]]>\n"
1272
        output << "</script>"
1273
      end
1274
    #pend
1275

  
858 1276
    end
859 1277
  end
860 1278
end
public/stylesheets/application.css (working copy)
857 857
.task_late { background:#f66 url(../images/task_late.png); border: 1px solid #f66; }
858 858
.task_done { background:#00c600 url(../images/task_done.png); border: 1px solid #00c600; }
859 859
.task_todo { background:#aaa url(../images/task_todo.png); border: 1px solid #aaa; }
860
.task_none { background:transparent; border-style: none; }
860 861

  
861 862
.task_todo.parent { background: #888; border: 1px solid #888; height: 3px;}
862 863
.task_late.parent, .task_done.parent { height: 3px;}