source: contrib/ProjectManager/inc/jpgraph-1.5.2/src/jpgraph_gantt.php @ 3594

Revision 3594, 45.3 KB checked in by afernandes, 13 years ago (diff)

Ticket #1416 - Disponibilizado o módulo ProjectManager? para a comunidade

  • Property svn:executable set to *
Line 
1<?php
2/*=======================================================================
3// File:        JPGRAPH_GANTT.PHP
4// Description: JpGraph Gantt plot extension
5// Created:     2001-11-12
6// Author:      Johan Persson (johanp@aditus.nu)
7// Ver:         $Id: jpgraph_gantt.php 18250 2005-05-07 14:13:43Z ralfbecker $
8//
9// License:     This code is released under GPL 2.0
10//
11//========================================================================
12*/
13
14// Scale Header types
15DEFINE("GANTT_HDAY",1);
16DEFINE("GANTT_HWEEK",2);
17DEFINE("GANTT_HMONTH",4);
18DEFINE("GANTT_HYEAR",8);
19
20// Bar patterns
21DEFINE("GANTT_RDIAG",BAND_RDIAG);       // Right diagonal lines
22DEFINE("GANTT_LDIAG",BAND_LDIAG); // Left diagonal lines
23DEFINE("GANTT_SOLID",BAND_SOLID); // Solid one color
24DEFINE("GANTT_VLINE",BAND_VLINE); // Vertical lines
25DEFINE("GANTT_HLINE",BAND_HLINE);  // Horizontal lines
26DEFINE("GANTT_3DPLANE",BAND_3DPLANE);  // "3D" Plane
27DEFINE("GANTT_HVCROSS",BAND_HVCROSS);  // Vertical/Hor crosses
28DEFINE("GANTT_DIAGCROSS",BAND_DIAGCROSS); // Diagonal crosses
29
30// Conversion constant
31DEFINE("SECPERDAY",3600*24);
32
33// Locales
34DEFINE("LOCALE_EN",0);
35DEFINE("LOCALE_SE",1);
36DEFINE("LOCALE_DE",2);
37
38// Layout of bars
39DEFINE("GANTT_EVEN",1);
40DEFINE("GANTT_FROMTOP",2);
41
42// Styles for week header
43DEFINE("WEEKSTYLE_WNBR",0);
44DEFINE("WEEKSTYLE_FIRSTDAY",1);
45DEFINE("WEEKSTYLE_FIRSTDAY2",1);
46
47// Styles for month header
48DEFINE("MONTHSTYLE_SHORTNAME",0);
49DEFINE("MONTHSTYLE_LONGNAME",1);
50DEFINE("MONTHSTYLE_LONGNAMEYEAR2",2);
51DEFINE("MONTHSTYLE_SHORTNAMEYEAR2",3);
52DEFINE("MONTHSTYLE_LONGNAMEYEAR4",4);
53DEFINE("MONTHSTYLE_SHORTNAMEYEAR4",5);
54
55//===================================================
56// CLASS DateLocale
57// Description: Hold localized text used in dates
58// ToDOo: Rewrite this to use the real local locale
59// instead.
60//===================================================
61class DateLocale {
62    var $iLocale=0;     // Default to english
63    var $iDayAbb = array(
64        array("M","T","W","T","F","S","S"),                     // English locale
65        array("M","T","O","T","F","L","S"),                     // Swedish locale
66        array("M","D","M","D","F","S","S"));                    // German locale
67    var $iShortDay = array(
68        array("Mon","Tue","Wed","Thu","Fri","Sat","Sun"),
69        array("Mån","Tis","Ons","Tor","Fre","Lör","Sön"),
70        array("Mon","Die","Mit","Don","Fre","Sam","Son"));
71    var $iShortMonth = array(
72        array("Jan","Feb","Mar","Apr","May","Jun","Jul","Aug","Sep","Oct","Nov","Dec"),
73        array("Jan","Feb","Mar","Apr","Maj","Jun","Jul","Aug","Sep","Okt","Nov","Dec"),
74        array("Jan","Feb","Mär","Apr","Mai","Jun","Jul","Aug","Sep","Okt","Nov","Dez"));
75    var $iMonthName = array(
76        array("January","February","Mars","April","May","June","July","August","September","October","November","December"),
77        array("Januari","Februari","Mars","April","Maj","Juni","Juli","Augusti","September","Oktober","November","December"),
78        array("Januar","Februar","März","April","Mai","Juni","July","August","September","Oktober","November","Dezember"));
79       
80//---------------
81// CONSTRUCTOR 
82    function DateLocale() {
83        // Empty
84    }
85
86//---------------
87// PUBLIC METHODS       
88    function Set($aLocale) {
89        if( $aLocale < LOCALE_EN || $aLocale > LOCALE_DE )
90            JpGraphError::Raise("<b>JpGraph Error:</b> Unsupported locale ($aLocale)");
91        $this->iLocale = $aLocale;
92    }
93       
94    function GetDayAbb() {
95        return $this->iDayAbb[$this->iLocale];
96    }
97       
98    function GetShortDay() {
99        return $this->iShortDay[$this->iLocale];
100    }
101
102    function GetShortMonth($aMonth=null) {
103        return $this->iShortMonth[$this->iLocale];
104    }
105       
106    function GetShortMonthName($aNbr) {
107        return $this->iShortMonth[$this->iLocale][$aNbr];
108    }
109
110    function GetLongMonthName($aNbr) {
111        return $this->iMonthName[$this->iLocale][$aNbr];
112    }
113
114    function GetMonth() {
115        return $this->iMonthName[$this->iLocale];
116    }
117}
118
119//===================================================
120// CLASS GanttGraph
121// Description: Main class to handle gantt graphs
122//===================================================
123class GanttGraph extends Graph {
124    var $scale;                                                 // Public accessible
125    var $iObj=array();                          // Gantt objects
126    var $iLabelHMarginFactor=0.2;       // 10% margin on each side of the labels
127    var $iLabelVMarginFactor=0.4;       // 40% margin on top and bottom of label
128    var $iLayout=GANTT_FROMTOP; // Could also be GANTT_EVEN
129
130//---------------
131// CONSTRUCTOR 
132    // Create a new gantt graph
133    function GanttGraph($aWidth=-1,$aHeight=-1,$aCachedName="",$aTimeOut=0,$aInline=true) {
134        Graph::Graph($aWidth,$aHeight,$aCachedName,$aTimeOut,$aInline);         
135        $this->scale = new GanttScale($this->img);
136        $this->img->SetMargin($aWidth/17,$aWidth/17,$aHeight/7,$aHeight/10);
137               
138        $this->scale->ShowHeaders(GANTT_HWEEK|GANTT_HDAY);
139        $this->SetBox();
140    }
141       
142//---------------
143// PUBLIC METHODS       
144    // Set what headers should be shown
145    function ShowHeaders($aFlg) {
146        $this->scale->ShowHeaders($aFlg);
147    }
148       
149    // Specify the fraction of the font height that should be added
150    // as vertical margin
151    function SetLabelVMarginFactor($aVal) {
152        $this->iLabelVMarginFactor = $aVal;
153    }
154       
155    // Add a new Gantt object
156    function Add(&$aObject) {
157        if( is_array($aObject) ) {
158            for($i=0; $i<count($aObject); ++$i)
159                $this->iObj[] = $aObject[$i];
160        }
161        else
162            $this->iObj[] = $aObject;
163    }
164
165    // Override inherit method from Graph and give a warning message
166    function SetScale() {
167        JpGraphError::Raise("<b>JpGraph Error:</b> SetScale() is not meaningfull with Gantt charts.");
168        // Empty
169    }
170
171    // Specify the date range for Gantt graphs (if this is not set it will be
172    // automtically determined from the input data)
173    function SetDateRange($aStart,$aEnd) {
174        $this->scale->SetRange($aStart,$aEnd);
175    }
176       
177    // Get the maximum width of the titles for the bars
178    function GetMaxLabelWidth() {
179        $m=0;
180        if( $this->iObj != null ) {
181            $m = $this->iObj[0]->title->GetWidth($this->img);
182            for($i=1; $i<count($this->iObj); ++$i) {
183                if( $this->iObj[$i]->title->HasTabs() ) {
184                    list($tot,$w) = $this->iObj[$i]->title->GetWidth($this->img,true);
185                    $m=max($m,$tot);
186                }
187                else
188                    $m=max($m,$this->iObj[$i]->title->GetWidth($this->img));
189            }
190        }
191        return $m;
192    }
193       
194    // Get the maximum height of the titles for the bars
195    function GetMaxLabelHeight() {
196        $m=0;
197        if( $this->iObj != null ) {
198            $m = $this->iObj[0]->title->GetHeight($this->img);
199            for($i=1; $i<count($this->iObj); ++$i) {
200                $m=max($m,$this->iObj[$i]->title->GetHeight($this->img));
201            }
202        }
203        return $m;
204    }
205
206    function GetMaxBarAbsHeight() {
207        $m=0;
208        if( $this->iObj != null ) {
209            $m = $this->iObj[0]->GetAbsHeight($this->img);
210            for($i=1; $i<count($this->iObj); ++$i) {
211                $m=max($m,$this->iObj[$i]->GetAbsHeight($this->img));
212            }
213        }
214        return $m;             
215    }
216       
217    // Get the maximum used line number (vertical position) for bars
218    function GetBarMaxLineNumber() {
219        $m=0;
220        if( $this->iObj != null ) {
221            $m = $this->iObj[0]->GetLineNbr();
222            for($i=1; $i<count($this->iObj); ++$i) {
223                $m=max($m,$this->iObj[$i]->GetLineNbr());
224            }
225        }
226        return $m;
227    }
228       
229    // Get the minumum and maximum used dates for all bars
230    function GetBarMinMax() {
231        $max=$this->scale->NormalizeDate($this->iObj[0]->GetMaxDate());
232        $min=$this->scale->NormalizeDate($this->iObj[0]->GetMinDate());
233        for($i=1; $i<count($this->iObj); ++$i) {
234            $max=Max($max,$this->scale->NormalizeDate($this->iObj[$i]->GetMaxDate()));
235            $min=Min($min,$this->scale->NormalizeDate($this->iObj[$i]->GetMinDate()));
236        }
237        $minDate = date("Y-m-d",$min);
238        $min = strtotime($minDate);
239        $maxDate = date("Y-m-d",$max);
240        $max = strtotime($maxDate);     
241        return array($min,$max);
242    }
243
244    // Stroke the gantt chart
245    function Stroke($aStrokeFileName="") {     
246
247        // Should we autoscale dates?
248        if( !$this->scale->IsRangeSet() ) {
249            list($min,$max) = $this->GetBarMinMax();
250            $this->scale->SetRange($min,$max);
251        }
252               
253        if( $this->img->img == null ) {
254            // The predefined left, right, top, bottom margins.
255            // Note that the top margin might incease depending on
256            // the title.
257            $lm=30;$rm=30;$tm=20;$bm=30;                       
258            if( BRAND_TIMING ) $bm += 10;
259                       
260            // First find out the height                       
261            $n=$this->GetBarMaxLineNumber()+1;
262            $m=max($this->GetMaxLabelHeight(),$this->GetMaxBarAbsHeight());
263            $height=$n*((1+$this->iLabelVMarginFactor)*$m);                     
264                       
265            // Add the height of the scale titles                       
266            $h=$this->scale->GetHeaderHeight();
267            $height += $h;
268
269            // Calculate the top margin needed for title and subtitle
270            if( $this->title->t != "" ) {
271                $tm += $this->title->GetFontHeight($this->img);
272            }
273            if( $this->subtitle->t != "" ) {
274                $tm += $this->subtitle->GetFontHeight($this->img);
275            }
276
277            // ...and then take the bottom and top plot margins into account
278            $height += $tm + $bm + $this->scale->iTopPlotMargin + $this->scale->iBottomPlotMargin;
279                       
280            // Now find the minimum width for the chart required
281            $fw=$this->scale->day->GetFontWidth($this->img)+4;
282            $nd=$this->scale->GetNumberOfDays();
283            if( !$this->scale->IsDisplayDay() ) {
284                                // If we don't display the individual days we can shrink the
285                                // scale a little bit. This is a little bit pragmatic at the
286                                // moment and should be re-written to take into account
287                                // a) What scales exactly are shown and
288                                // b) what format do they use so we know how wide we need to
289                                // make each scale text space at minimum.
290                $fw /= 2;
291                if( !$this->scale->IsDisplayWeek() ) {
292                    $fw /= 1.8;
293                }
294            }
295                       
296            // Now determine the width for the activity titles column
297            // This is complicated by the fact that the titles may have
298            // tabs. In that case we also need to calculate the individual
299            // tab positions based on the width of the individual columns
300                       
301            $titlewidth = $this->GetMaxLabelWidth();
302                                               
303            // Now get the total width taking
304            // titlewidth, left and rigt margin, dayfont size
305            // into account
306            $width = $titlewidth + $nd*$fw + $lm+$rm;
307                                               
308            $this->img->CreateImgCanvas($width,$height);                       
309            $this->img->SetMargin($lm,$rm,$tm,$bm);
310        }
311               
312        // Should we start from the top or just spread the bars out even over the
313        // available height
314        $this->scale->SetVertLayout($this->iLayout);                   
315        if( $this->iLayout == GANTT_FROMTOP ) {
316            $maxheight=max($this->GetMaxLabelHeight(),$this->GetMaxBarAbsHeight());
317            $this->scale->SetVertSpacing($maxheight*(1+$this->iLabelVMarginFactor));
318        }
319        // If it hasn't been set find out the maximum line number
320        if( $this->scale->iVertLines == -1 )
321            $this->scale->iVertLines = $this->GetBarMaxLineNumber()+1; 
322               
323        $maxwidth=max($this->GetMaxLabelWidth(),$this->scale->tableTitle->GetWidth($this->img));
324        $this->scale->SetLabelWidth($maxwidth*(1+$this->iLabelHMarginFactor));
325        $this->StrokePlotArea();
326        $this->scale->Stroke();
327        $this->StrokePlotBox();
328               
329        for($i=0; $i<count($this->iObj); ++$i) {
330            $this->iObj[$i]->SetLabelLeftMargin(round($maxwidth*$this->iLabelHMarginFactor/2));
331            $this->iObj[$i]->Stroke($this->img,$this->scale);
332        }
333               
334        $this->StrokeTitles();
335        $this->cache->PutAndStream($this->img,$this->cache_name,$this->inline,$aStrokeFileName);                               
336    }
337}
338
339//===================================================
340// CLASS TextProperty
341// Description: Holds properties for a text
342//===================================================
343class TextProperty {
344    var $iFFamily=FF_FONT1,$iFStyle=FS_NORMAL,$iFSize=10;
345    var $iColor="black";
346    var $iShow=true;
347    var $iText="";
348    var $iHAlign="left",$iVAlign="bottom";
349       
350//---------------
351// CONSTRUCTOR 
352    function TextProperty($aTxt="") {
353        $this->iText = $aTxt;
354    }           
355       
356//---------------
357// PUBLIC METHODS       
358    function Set($aTxt) {
359        $this->iText = $aTxt;
360    }
361       
362    // Set text color
363    function SetColor($aColor) {
364        $this->iColor = $aColor;
365    }
366       
367    function HasTabs() {
368        return substr_count($this->iText,"\t") > 0;
369    }
370       
371    // Get number of tabs in string
372    function GetNbrTabs() {
373        substr_count($this->iText,"\t");
374    }
375       
376    // Set alignment
377    function Align($aHAlign,$aVAlign="bottom") {
378        $this->iHAlign=$aHAlign;
379        $this->iVAlign=$aVAlign;
380    }
381       
382    // Specify font
383    function SetFont($aFFamily,$aFStyle=FS_NORMAL,$aFSize=10) {
384        $this->iFFamily = $aFFamily;
385        $this->iFStyle   = $aFStyle;
386        $this->iFSize    = $aFSize;
387    }
388       
389    // Get width of text. If text contains several columns separated by
390    // tabs then return both the total width as well as an array with a
391    // width for each column.
392    function GetWidth($aImg,$aUseTabs=false,$aTabExtraMargin=1.1) {
393        if( strlen($this->iText)== 0 ) return;
394        $tmp = split("\t",$this->iText);
395        $aImg->SetFont($this->iFFamily,$this->iFStyle,$this->iFSize);
396        if( count($tmp) <= 1 || !$aUseTabs ) {
397            return $aImg->GetTextWidth($this->iText);
398        }
399        else {
400            $tot=0;
401            for($i=0; $i<count($tmp); ++$i) {
402                $res[$i] = $aImg->GetTextWidth($tmp[$i]);
403                $tot += $res[$i]*$aTabExtraMargin;
404            }
405            return array($tot,$res);
406        }
407    }
408       
409    // Get total height of text
410    function GetHeight($aImg) {
411        $aImg->SetFont($this->iFFamily,$this->iFStyle,$this->iFSize);
412        return $aImg->GetFontHeight();
413    }
414       
415    // Unhide/hide the text     
416    function Show($aShow) {
417        $this->iShow=$aShow;
418    }
419       
420    // Stroke text at (x,y) coordinates. If the text contains tabs then the
421    // x parameter should be an array of positions to be used for each successive
422    // tab mark. If no array is supplied then the tabs will be ignored.
423    function Stroke($aImg,$aX,$aY) {
424        if( $this->iShow ) {
425            $aImg->SetColor($this->iColor);
426            $aImg->SetFont($this->iFFamily,$this->iFStyle,$this->iFSize);
427            $aImg->SetTextAlign($this->iHAlign,$this->iVAlign);                 
428            if( $this->GetNbrTabs() <= 1 || !is_array($aX) ) {
429                                // Get rid of any "\t" characters and stroke string
430                $aImg->StrokeText($aX,$aY,str_replace("\t"," ",$this->iText));
431            }
432            else {
433                $tmp = split("\t",$this->iText);
434                $n = min(count($tmp),count($aX));
435                for($i=0; $i<$n; ++$i) {
436                    $aImg->StrokeText($aX[$i],$aY,$tmp[$i]);
437                }       
438            }
439        }
440    }
441}
442
443//===================================================
444// CLASS HeaderProperty
445// Description: Data encapsulating class to hold property
446// for each type of the scale headers
447//===================================================
448class HeaderProperty {
449    var $iTitleVertMargin=3,$iFFamily=FF_FONT0,$iFStyle=FS_NORMAL,$iFSize=8;
450    var $iFrameColor="black",$iFrameWeight=1;
451    var $iShowLabels=true,$iShowGrid=true;
452    var $iBackgroundColor="white";
453    var $iWeekendBackgroundColor="lightgray",$iSundayTextColor="red"; // these are only used with day scale
454    var $iTextColor="black";
455    var $iLabelFormStr="%d";
456    var $grid,$iStyle=0;
457
458//---------------
459// CONSTRUCTOR 
460    function HeaderProperty() {
461        $this->grid = new LineProperty();
462    }
463
464//---------------
465// PUBLIC METHODS               
466    function Show($aShow) {
467        $this->iShowLabels = $aShow;
468    }
469       
470    function SetFont($aFFamily,$aFStyle=FS_NORMAL,$aFSize=10) {
471        $this->iFFamily = $aFFamily;
472        $this->iFStyle   = $aFStyle;
473        $this->iFSize    = $aFSize;
474    }
475
476    function SetFontColor($aColor) {
477        $this->iTextColor = $aColor;
478    }
479       
480    function GetFontHeight($aImg) {
481        $aImg->SetFont($this->iFFamily,$this->iFStyle,$this->iFSize);
482        return $aImg->GetFontHeight();
483    }
484
485    function GetFontWidth($aImg) {
486        $aImg->SetFont($this->iFFamily,$this->iFStyle,$this->iFSize);
487        return $aImg->GetFontWidth();
488    }
489       
490    function SetStyle($aStyle) {
491        $this->iStyle = $aStyle;
492    }
493       
494    function SetBackgroundColor($aColor) {
495        $this->iBackgroundColor=$aColor;
496    }
497
498    function SetFrameWeight($aWeight) {
499        $this->iFrameWeight=$aWeight;
500    }
501
502    function SetFrameColor($aColor) {
503        $this->iFrameColor=$aColor;
504    }
505       
506    // Only used by day scale
507    function SetWeekendColor($aColor) {
508        $this->iWeekendBackgroundColor=$aColor;
509    }
510       
511    // Only used by day scale
512    function SetSundayFontColor($aColor) {
513        $this->iSundayTextColor=$aColor;
514    }
515       
516    function SetTitleVertMargin($aMargin) {
517        $this->iTitleVertMargin=$aMargin;
518    }
519       
520    function SetLabelFormatString($aStr) {
521        $this->iLabelFormStr=$aStr;
522    }
523}
524
525//===================================================
526// CLASS GanttScale
527// Description: Responsible for calculating and showing
528// the scale in a gantt chart. This includes providing methods for
529// converting dates to position in the chart as well as stroking the
530// date headers (days, week, etc).
531//===================================================
532class GanttScale {
533    var $day,$week,$month,$year;
534    var $divider,$dividerh,$tableTitle;
535    var $iStartDate=-1,$iEndDate=-1;   
536    // Number of gantt bar position (n.b not necessariliy the same as the number of bars)
537    // we could have on bar in position 1, and one bar in position 5 then there are two
538    // bars but the number of bar positions is 5
539    var $iVertLines=-1;
540    // The width of the labels (defaults to the widest of all labels)
541    var $iLabelWidth;   
542    // Out image to stroke the scale to
543    var $iImg; 
544    var $iTableHeaderBackgroundColor="white",$iTableHeaderFrameColor="black";
545    var $iTableHeaderFrameWeight=1;
546    var $iAvailableHeight=-1,$iVertSpacing=-1,$iVertHeaderSize=-1;
547    var $iDateLocale;
548    var $iVertLayout=GANTT_EVEN;
549    var $iTopPlotMargin=10,$iBottomPlotMargin=15;
550    var $iUsePlotWeekendBackground=true;
551       
552//---------------
553// CONSTRUCTOR 
554    function GanttScale(&$aImg) {
555        $this->iImg = &$aImg;           
556        $this->iDateLocale = new DateLocale();
557        $this->day = new HeaderProperty();
558        $this->day->grid->SetColor("gray");
559
560        $this->week = new HeaderProperty();
561        $this->week->SetLabelFormatString("w%d");
562        $this->week->SetFont(FF_FONT1);
563
564        $this->month = new HeaderProperty();
565        $this->month->SetFont(FF_FONT1,FS_BOLD);
566
567        $this->year = new HeaderProperty();
568        $this->year->SetFont(FF_FONT1,FS_BOLD);         
569               
570        $this->divider=new LineProperty();
571        $this->dividerh=new LineProperty();             
572        $this->tableTitle=new TextProperty();
573    }
574       
575//---------------
576// PUBLIC METHODS       
577    // Specify what headers should be visible
578    function ShowHeaders($aFlg) {
579        $this->day->Show($aFlg & GANTT_HDAY);
580        $this->week->Show($aFlg & GANTT_HWEEK);
581        $this->month->Show($aFlg & GANTT_HMONTH);
582        $this->year->Show($aFlg & GANTT_HYEAR);
583
584        // Make some default settings of gridlines whihc makes sense
585        if( $aFlg & GANTT_HWEEK ) {
586            $this->month->grid->Show(false);
587            $this->year->grid->Show(false);
588        }
589    }
590       
591    // Should the weekend background stretch all the way down in the plotarea
592    function UseWeekendBackground($aShow) {
593        $this->iUsePlotWeekendBackground = $aShow;
594    }
595       
596    // Have a range been specified?
597    function IsRangeSet() {
598        return $this->iStartDate!=-1 && $this->iEndDate!=-1;
599    }
600       
601    // Should the layout be from top or even?
602    function SetVertLayout($aLayout) {
603        $this->iVertLayout = $aLayout;
604    }
605       
606    // Which locale should be used?
607    function SetDateLocale($aLocale) {
608        $this->iDateLocale->Set($aLocale);
609    }
610       
611    // Number of days we are showing
612    function GetNumberOfDays() {
613        return round(($this->iEndDate-$this->iStartDate)/SECPERDAY)+1;
614    }
615       
616    // The widthj of the actual plot area
617    function GetPlotWidth() {
618        $img=$this->iImg;
619        return $img->width - $img->left_margin - $img->right_margin;
620    }
621
622    // Specify the width of the titles(labels) for the activities
623    // (This is by default set to the minimum width enought for the
624    // widest title)
625    function SetLabelWidth($aLabelWidth) {
626        $this->iLabelWidth=$aLabelWidth;
627    }
628       
629    // Do we show day scale?
630    function IsDisplayDay() {
631        return $this->day->iShowLabels;
632    }
633       
634    // Do we show week scale?
635    function IsDisplayWeek() {
636        return $this->week->iShowLabels;
637    }
638       
639    // Do we show month scale?
640    function IsDisplayMonth() {
641        return $this->month->iShowLabels;
642    }
643       
644    // Do we show year scale?
645    function IsDisplayYear() {
646        return $this->year->iShowLabels;
647    }
648
649    // Specify spacing (in percent of bar height) between activity bars
650    function SetVertSpacing($aSpacing) {
651        $this->iVertSpacing = $aSpacing;
652    }
653
654    // Specify scale min and max date either as timestamp or as date strings
655    // Always round to the nearest week boundary
656    function SetRange($aMin,$aMax) {
657        $this->iStartDate = $this->NormalizeDate($aMin);
658        $this->iEndDate = $this->NormalizeDate($aMax);
659               
660        // Get day in week Sun=0
661        $ds=strftime("%w",$this->iStartDate);
662        $de=strftime("%w",$this->iEndDate);
663               
664        if( $ds==0 ) $ds=7;
665        if( $de==0 ) $de=7;
666       
667        // We want to start on Monday
668        $this->iStartDate -= SECPERDAY*($ds-1);
669               
670        // We want to end on a Sunday
671        $this->iEndDate += SECPERDAY*(7-$de);
672    }
673
674    // Specify background for the table title area (upper left corner of the table)     
675    function SetTableTitleBackground($aColor) {
676        $this->iTableHeaderBackgroundColor = $aColor;
677    }
678
679///////////////////////////////////////
680// PRIVATE Methods
681       
682    // Determine the height of all the scale headers combined
683    function GetHeaderHeight() {
684        $img=$this->iImg;
685        $height=1;
686        if( $this->day->iShowLabels ) {
687            $height += $this->day->GetFontHeight($img);
688            $height += $this->day->iTitleVertMargin;
689        }
690        if( $this->week->iShowLabels ) {
691            $height += $this->week->GetFontHeight($img);
692            $height += $this->week->iTitleVertMargin;
693        }
694        if( $this->month->iShowLabels ) {
695            $height += $this->month->GetFontHeight($img);
696            $height += $this->month->iTitleVertMargin;
697        }
698        if( $this->year->iShowLabels ) {
699            $height += $this->year->GetFontHeight($img);
700            $height += $this->year->iTitleVertMargin;
701        }
702        return $height;
703    }
704       
705    // Get width (in pisels) for a single day
706    function GetDayWidth() {
707        return ($this->GetPlotWidth()-$this->iLabelWidth+1)/$this->GetNumberOfDays();   
708    }
709
710    // Nuber of days in a year
711    function GetNumDaysInYear($aYear) {
712        if( $this->IsLeap($aYear) )
713            return 366;
714        else
715            return 365;
716    }
717       
718    // Get day number in year   
719    function GetDayNbrInYear($aDate) {
720        return 0+strftime("%j",$aDate);
721    }
722       
723    // Get week number
724    function GetWeekNbr($aDate) {
725        // We can't use the internal strftime() since it gets the weeknumber
726        // wrong since it doesn't follow ISO.
727        // Even worse is that this works differently if we are on a Windows
728        // or UNIX box (it even differs between UNIX boxes how strftime()
729        // is natively implemented)
730        //
731        // Credit to Nicolas Hoizey <nhoizey@phpheaven.net> for this elegant
732        // version of Week Nbr calculation.
733               
734        $day = $this->NormalizeDate($aDate);
735               
736        /*-------------------------------------------------------------------------
737          According to ISO-8601 :
738          "Week 01 of a year is per definition the first week that has the Thursday in this year,
739          which is equivalent to the week that contains the fourth day of January.
740          In other words, the first week of a new year is the week that has the majority of its
741          days in the new year."
742                 
743          Be carefull, with PHP, -3 % 7 = -3, instead of 4 !!!
744                 
745          day of year             = date("z", $day) + 1
746          offset to thursday      = 3 - (date("w", $day) + 6) % 7
747          first thursday of year  = 1 + (11 - date("w", mktime(0, 0, 0, 1, 1, date("Y", $day)))) % 7
748          week number             = (thursday's day of year - first thursday's day of year) / 7 + 1
749          ---------------------------------------------------------------------------*/
750                 
751        $thursday = $day + 60 * 60 * 24 * (3 - (date("w", $day) + 6) % 7);              // take week's thursday
752        $week = 1 + (date("z", $thursday) - (11 - date("w", mktime(0, 0, 0, 1, 1, date("Y", $thursday)))) % 7) / 7;
753                 
754        return $week;
755    }
756       
757    // Is year a leap year?
758    function IsLeap($aYear) {
759        // Is the year a leap year?
760        //$year = 0+date("Y",$aDate);
761        if( $aYear % 4 == 0)
762            if( !($aYear % 100 == 0) || ($aYear % 400 == 0) )
763                return true;
764        return false;
765    }
766
767    // Get current year
768    function GetYear($aDate) {
769        return 0+Date("Y",$aDate);
770    }
771       
772    // Return number of days in a year
773    function GetNumDaysInMonth($aMonth,$aYear) {
774        $days=array(31,28,31,30,31,30,31,31,30,31,30,31);
775        $daysl=array(31,29,31,30,31,30,31,31,30,31,30,31);
776        if( $this->IsLeap($aYear))
777            return $daysl[$aMonth];
778        else
779            return $days[$aMonth];
780    }
781       
782    // Get day in month
783    function GetMonthDayNbr($aDate) {
784        return 0+strftime("%d",$aDate);
785    }
786
787    // Get day in year
788    function GetYearDayNbr($aDate) {
789        return 0+strftime("%j",$aDate);
790    }
791       
792    // Get month number
793    function GetMonthNbr($aDate) {
794        return 0+strftime("%m",$aDate);
795    }
796       
797    // Translate a date to screen coordinates   (horizontal scale)
798    function TranslateDate($aDate) {
799        $aDate = $this->NormalizeDate($aDate);
800        $img=$this->iImg;               
801        //print date('d-m-Y H:i:s',$aDate).'--'.date('d-m-Y H:i:s',$this->iEndDate)."<br>";
802        if( $aDate < $this->iStartDate || $aDate > $this->iEndDate )
803            JpGraphError::Raise("<b>JpGraph Error:</b> Date is outside specified scale range.");
804        return ($aDate-$this->iStartDate)/SECPERDAY*$this->GetDayWidth()+$img->left_margin+$this->iLabelWidth;;
805    }
806
807    // Get screen coordinatesz for the vertical position for a bar             
808    function TranslateVertPos($aPos) {
809        $img=$this->iImg;
810        $ph=$this->iAvailableHeight;
811        if( $aPos > $this->iVertLines )
812            JpGraphError::Raise("<b>JpGraph Error:</b> Illegal vertical position $aPos");
813        if( $this->iVertLayout == GANTT_EVEN ) {
814            // Position the top bar at 1 vert spacing from the scale
815            return round($img->top_margin + $this->iVertHeaderSize +  ($aPos+1)*$this->iVertSpacing);
816        }
817        else {
818            // position the top bar at 1/2 a vert spacing from the scale
819            return round($img->top_margin + $this->iVertHeaderSize  + $this->iTopPlotMargin + ($aPos+1)*$this->iVertSpacing);           
820        }
821    }
822       
823    // What is the vertical spacing?
824    function GetVertSpacing() {
825        return $this->iVertSpacing;
826    }
827                                       
828    // Convert a date to timestamp
829    function NormalizeDate($aDate) {
830        if( is_string($aDate) )
831            return strtotime($aDate);
832        elseif( is_int($aDate) || is_float($aDate) )
833            return $aDate;
834        else
835            JpGraphError::Raise("<b>JpGraph Error:</b> Unknown date format in GanttScale ($aDate).");
836    }
837
838    // Stroke the day scale (including gridlines)                       
839    function StrokeDays($aYCoord) {
840        $wdays=$this->iDateLocale->GetDayAbb();
841        $img=$this->iImg;       
842        $daywidth=$this->GetDayWidth();
843        $xt=$img->left_margin+$this->iLabelWidth;
844        $yt=$aYCoord+$img->top_margin;         
845        if( $this->day->iShowLabels ) {
846            $img->SetFont($this->day->iFFamily,$this->day->iFStyle,$this->day->iFSize);
847            $xb=$img->width-$img->right_margin;
848            $yb=$yt + $img->GetFontHeight() + $this->day->iTitleVertMargin + $this->day->iFrameWeight;
849            $img->SetColor($this->day->iBackgroundColor);
850            $img->FilledRectangle($xt,$yt,$xb,$yb);
851
852            $img->SetColor($this->day->grid->iColor);
853            $x = $xt;
854            $img->SetTextAlign("center");
855            for($i=0; $i<$this->GetNumberOfDays(); ++$i, $x+=$daywidth) {
856                $day=$i%7;
857                if( $day==5 ) {
858                    $img->PushColor($this->day->iWeekendBackgroundColor);
859                    if( $this->iUsePlotWeekendBackground )
860                        $img->FilledRectangle($x,$yt+$this->day->iFrameWeight,$x+2*$daywidth,$img->height-$img->bottom_margin);                                         
861                    else
862                        $img->FilledRectangle($x,$yt+$this->day->iFrameWeight,$x+2*$daywidth,$yb-$this->day->iFrameWeight);
863                    $img->PopColor();
864                }
865                if( $day==6 )
866                    $img->PushColor($this->day->iSundayTextColor);
867                else
868                    $img->PushColor($this->day->iTextColor);
869                $img->StrokeText(round($x+$daywidth/2+1),
870                                 round($yb-$this->day->iTitleVertMargin),
871                                 $wdays[$i%7]);
872                $img->PopColor();                                               
873                $img->Line($x,$yt,$x,$yb);
874                $this->day->grid->Stroke($img,$x,$yb,$x,$img->height-$img->bottom_margin);
875            }                   
876            $img->SetColor($this->day->iFrameColor);
877            $img->SetLineWeight($this->day->iFrameWeight);
878            $img->Rectangle($xt,$yt,$xb,$yb);
879            return $yb - $img->top_margin;
880        }
881        return $aYCoord;
882    }
883       
884    // Stroke week header and grid
885    function StrokeWeeks($aYCoord) {
886        $wdays=$this->iDateLocale->GetDayAbb();
887        $img=$this->iImg;       
888        $weekwidth=$this->GetDayWidth()*7;
889        $xt=$img->left_margin+$this->iLabelWidth;
890        $yt=$aYCoord+$img->top_margin;         
891        $img->SetFont($this->week->iFFamily,$this->week->iFStyle,$this->week->iFSize);
892        $xb=$img->width-$img->right_margin;
893        $yb=$yt + $img->GetFontHeight() + $this->week->iTitleVertMargin + $this->week->iFrameWeight;
894               
895        $week = $this->iStartDate;
896        $weeknbr=$this->GetWeekNbr($week);
897        if( $this->week->iShowLabels ) {
898            $img->SetColor($this->week->iBackgroundColor);
899            $img->FilledRectangle($xt,$yt,$xb,$yb);
900            $img->SetColor($this->week->grid->iColor);
901            $x = $xt;
902            if( $this->week->iStyle==WEEKSTYLE_WNBR ) {
903                $img->SetTextAlign("center");
904                $txtOffset = $weekwidth/2+1;
905            }
906            elseif( $this->week->iStyle==WEEKSTYLE_FIRSTDAY || $this->week->iStyle==WEEKSTYLE_FIRSTDAY2 ) {
907                $img->SetTextAlign("left");
908                $txtOffset = 2;
909            }
910            else
911                JpGraphError::Raise("<b>JpGraph Error:</b>Unknown formatting style for week.");
912                               
913            for($i=0; $i<$this->GetNumberOfDays()/7; ++$i, $x+=$weekwidth) {
914                $img->PushColor($this->week->iTextColor);
915                               
916                if( $this->week->iStyle==WEEKSTYLE_WNBR )
917                    $txt = sprintf($this->week->iLabelFormStr,$weeknbr);
918                elseif( $this->week->iStyle==WEEKSTYLE_FIRSTDAY )
919                    $txt = date("j/n",$week);
920                elseif( $this->week->iStyle==WEEKSTYLE_FIRSTDAY2 ) {
921                    $monthnbr = date("n",$week)-1;
922                    $shortmonth = $this->iDateLocale->GetShortMonthName($monthnbr);
923                    $txt = Date("j",$week)." ".$shortmonth;
924                }
925                               
926                $img->StrokeText(round($x+$txtOffset),round($yb-$this->week->iTitleVertMargin),$txt);
927                               
928                $week += 7*SECPERDAY;
929                $weeknbr = $this->GetWeekNbr($week);
930                $img->PopColor();                                               
931                $img->Line($x,$yt,$x,$yb);
932                $this->week->grid->Stroke($img,$x,$yb,$x,$img->height-$img->bottom_margin);
933            }                   
934            $img->SetColor($this->week->iFrameColor);
935            $img->SetLineWeight($this->week->iFrameWeight);
936            $img->Rectangle($xt,$yt,$xb,$yb);
937            return $yb-$img->top_margin;
938        }
939        return $aYCoord;
940    }   
941       
942    // Format the mont scale header string
943    function GetMonthLabel($aMonthNbr,$year) {
944        $sn = $this->iDateLocale->GetShortMonthName($aMonthNbr);
945        $ln = $this->iDateLocale->GetLongMonthName($aMonthNbr);
946        switch($this->month->iStyle) {
947            case MONTHSTYLE_SHORTNAME:
948                $m=$sn;
949            break;
950            case MONTHSTYLE_LONGNAME:
951                $m=$ln;
952            break;
953            case MONTHSTYLE_SHORTNAMEYEAR2:
954                $m=$sn." '".substr("".$year,2);
955            break;
956            case MONTHSTYLE_SHORTNAMEYEAR4:
957                $m=$sn." ".$year;
958            break;
959            case MONTHSTYLE_LONGNAMEYEAR2:
960                $m=$ln." '".substr("".$year,2);
961            break;
962            case MONTHSTYLE_LONGNAMEYEAR4:
963                $m=$ln." ".$year;
964            break;
965        }
966        return $m;
967    }
968       
969    // Stroke month scale and gridlines
970    function StrokeMonths($aYCoord) {
971        if( $this->month->iShowLabels ) {
972            $monthnbr = $this->GetMonthNbr($this->iStartDate)-1;
973            $img=$this->iImg;   
974               
975            $xt=$img->left_margin+$this->iLabelWidth;
976            $yt=$aYCoord+$img->top_margin;             
977            $img->SetFont($this->month->iFFamily,$this->month->iFStyle,$this->month->iFSize);
978            $xb=$img->width-$img->right_margin;
979            $yb=$yt + $img->GetFontHeight() + $this->month->iTitleVertMargin + $this->month->iFrameWeight;
980                       
981            $img->SetColor($this->month->iBackgroundColor);
982            $img->FilledRectangle($xt,$yt,$xb,$yb);
983
984            $img->SetLineWeight($this->month->grid->iWeight);
985            $img->SetColor($this->month->iTextColor);
986            $year = 0+strftime("%Y",$this->iStartDate);
987            $img->SetTextAlign("center");
988            $monthwidth=$this->GetDayWidth()*($this->GetNumDaysInMonth($monthnbr,$year)-$this->GetMonthDayNbr($this->iStartDate)+1);
989            // Is it enough space to stroke the first month?
990            $monthName = $this->GetMonthLabel($monthnbr,$year);
991            if( $monthwidth >= 1.2*$img->GetTextWidth($monthName) ) {
992                $img->SetColor($this->month->iTextColor);                               
993                $img->StrokeText(round($xt+$monthwidth/2+1),
994                                 round($yb-$this->month->iTitleVertMargin),
995                                 $monthName);
996            }
997            $x = $xt + $monthwidth;
998            while( $x < $xb ) {
999                $img->SetColor($this->month->grid->iColor);                             
1000                $img->Line($x,$yt,$x,$yb);
1001                $this->month->grid->Stroke($img,$x,$yb,$x,$img->height-$img->bottom_margin);
1002                $monthnbr++;
1003                if( $monthnbr==12 ) {
1004                    $monthnbr=0;
1005                    $year++;
1006                }
1007                $monthName = $this->GetMonthLabel($monthnbr,$year);
1008                $monthwidth=$this->GetDayWidth()*$this->GetNumDaysInMonth($monthnbr,$year);                             
1009                if( $x + $monthwidth < $xb )
1010                    $w = $monthwidth;
1011                else
1012                    $w = $xb-$x;
1013                if( $w >= 1.2*$img->GetTextWidth($monthName) ) {
1014                    $img->SetColor($this->month->iTextColor);                           
1015                    $img->StrokeText(round($x+$w/2+1),
1016                                     round($yb-$this->month->iTitleVertMargin),$monthName);
1017                }
1018                $x += $monthwidth;
1019            }   
1020            $img->SetColor($this->month->iFrameColor);
1021            $img->SetLineWeight($this->month->iFrameWeight);
1022            $img->Rectangle($xt,$yt,$xb,$yb);                   
1023            return $yb-$img->top_margin;
1024        }
1025        return $aYCoord;
1026    }
1027
1028    // Stroke year scale and gridlines
1029    function StrokeYears($aYCoord) {
1030        if( $this->year->iShowLabels ) {
1031            $year = $this->GetYear($this->iStartDate);
1032            $img=$this->iImg;   
1033               
1034            $xt=$img->left_margin+$this->iLabelWidth;
1035            $yt=$aYCoord+$img->top_margin;             
1036            $img->SetFont($this->year->iFFamily,$this->year->iFStyle,$this->year->iFSize);
1037            $xb=$img->width-$img->right_margin;
1038            $yb=$yt + $img->GetFontHeight() + $this->year->iTitleVertMargin + $this->year->iFrameWeight;
1039                       
1040            $img->SetColor($this->year->iBackgroundColor);
1041            $img->FilledRectangle($xt,$yt,$xb,$yb);
1042            $img->SetLineWeight($this->year->grid->iWeight);
1043            $img->SetTextAlign("center");
1044            if( $year == $this->GetYear($this->iEndDate) )
1045                $yearwidth=$this->GetDayWidth()*($this->GetYearDayNbr($this->iEndDate)-$this->GetYearDayNbr($this->iStartDate)+1);
1046            else
1047                $yearwidth=$this->GetDayWidth()*($this->GetNumDaysInYear($year)-$this->GetYearDayNbr($this->iStartDate)+1);
1048                       
1049            // The space for a year must be at least 20% bigger than the actual text
1050            // so we allow 10% margin on each side
1051            if( $yearwidth >= 1.20*$img->GetTextWidth("".$year) ) {
1052                $img->SetColor($this->year->iTextColor);                               
1053                $img->StrokeText(round($xt+$yearwidth/2+1),
1054                                 round($yb-$this->year->iTitleVertMargin),
1055                                 $year);
1056            }
1057            $x = $xt + $yearwidth;
1058            while( $x < $xb ) {
1059                $img->SetColor($this->year->grid->iColor);                             
1060                $img->Line($x,$yt,$x,$yb);
1061                $this->year->grid->Stroke($img,$x,$yb,$x,$img->height-$img->bottom_margin);
1062                $year += 1;
1063                $yearwidth=$this->GetDayWidth()*$this->GetNumDaysInYear($year);                         
1064                if( $x + $yearwidth < $xb )
1065                    $w = $yearwidth;
1066                else
1067                    $w = $xb-$x;
1068                if( $w >= 1.2*$img->GetTextWidth("".$year) ) {
1069                    $img->SetColor($this->year->iTextColor);
1070                    $img->StrokeText(round($x+$w/2+1),
1071                                     round($yb-$this->year->iTitleVertMargin),
1072                                     $year);
1073                }
1074                $x += $yearwidth;
1075            }
1076            $img->SetColor($this->year->iFrameColor);
1077            $img->SetLineWeight($this->year->iFrameWeight);
1078            $img->Rectangle($xt,$yt,$xb,$yb);                   
1079            return $yb-$img->top_margin;
1080        }
1081        return $aYCoord;
1082    }
1083       
1084    // Stroke table title (upper left corner)
1085    function StrokeTableHeaders($aYBottom) {
1086        $img=$this->iImg;
1087        $xt=$img->left_margin;
1088        $yt=$img->top_margin;
1089        $xb=$xt+$this->iLabelWidth;
1090        $yb=$aYBottom+$img->top_margin;
1091               
1092        $img->SetColor($this->iTableHeaderBackgroundColor);
1093        $img->FilledRectangle($xt,$yt,$xb,$yb);
1094        $this->tableTitle->Align("center","center");
1095        $this->tableTitle->Stroke($img,$xt+($xb-$xt)/2+1,$yt+($yb-$yt)/2);             
1096        $img->SetColor($this->iTableHeaderFrameColor);
1097        $img->SetLineWeight($this->iTableHeaderFrameWeight);
1098        $img->Rectangle($xt,$yt,$xb,$yb);
1099               
1100        // Draw the vertical dividing line
1101        $this->divider->Stroke($img,$xb,$yt,$xb,$img->height-$img->bottom_margin);
1102               
1103        // Draw the horizontal dividing line           
1104        $this->dividerh->Stroke($img,$xt,$yb,$img->width-$img->right_margin,$yb);               
1105    }
1106
1107    // Main entry point to stroke scale
1108    function Stroke() {
1109        if( !$this->IsRangeSet() )
1110            JpGraphError::Raise("<b>JpGraph Error:</b> Gantt scale has not been specified.");
1111        $img=$this->iImg;
1112               
1113        // Stroke all headers. Aa argument we supply the offset from the
1114        // top which depends on any previous headers
1115        $offy=$this->StrokeYears(0);
1116        $offm=$this->StrokeMonths($offy);
1117        $offw=$this->StrokeWeeks($offm);
1118        $offd=$this->StrokeDays($offw);
1119
1120        // We stroke again in case days also have gridlines that may have
1121        // overwritten the weeks gridline (or month/year). It may seem that we should have logic
1122        // in the days routine instead but this is much easier and wont make to much
1123        // of an performance impact.
1124        $this->StrokeWeeks($offm);             
1125        $this->StrokeMonths($offy);             
1126        $this->StrokeYears(0);
1127        $this->StrokeTableHeaders($offd);
1128               
1129        // Now we can calculate the correct scaling factor for each vertical position
1130        $this->iAvailableHeight = $img->height - $img->top_margin - $img->bottom_margin - $offd;               
1131        $this->iVertHeaderSize = $offd;
1132        if( $this->iVertSpacing == -1 )
1133            $this->iVertSpacing = $this->iAvailableHeight / $this->iVertLines;
1134    }   
1135}
1136
1137//===================================================
1138// CLASS GanttPlotObject
1139// The common signature for a Gantt object
1140//===================================================
1141class GanttPlotObject {
1142    var $iVPos=0;                                       // Vertical position
1143    var $iLabelLeftMargin=2;    // Title margin
1144    var $iStart="";                             // Start date
1145    var $title,$caption;
1146    var $iCaptionMargin=5;
1147               
1148    function GanttPlotObject() {
1149        $this->title = new TextProperty();
1150        $this->title->Align("left","center");
1151        $this->caption = new TextProperty();
1152    }
1153       
1154    function GetMinDate() {
1155        return $this->iStart;
1156    }
1157
1158    function GetMaxDate() {
1159        return $this->iStart;
1160    }
1161       
1162    function SetCaptionMargin($aMarg) {
1163        $this->iCaptionMargin=$aMarg;
1164    }
1165
1166#    function GetLineNbr() {
1167#       return 0;
1168#    }
1169
1170    function GetAbsHeight($aImg) {
1171        return 0;
1172    }
1173       
1174    function GetLineNbr() {
1175        return $this->iVPos;
1176    }
1177
1178    function SetLabelLeftMargin($aOff) {
1179        $this->iLabelLeftMargin=$aOff;
1180    }           
1181}
1182
1183//===================================================
1184// CLASS Progress
1185// Holds parameters for the progress indicator
1186// displyed within a bar
1187//===================================================
1188class Progress {
1189    var $iProgress=-1, $iColor="black", $iPattern=GANTT_SOLID;
1190    var $iDensity=98, $iHeight=0.65;
1191       
1192    function Set($aProg) {
1193        if( $aProg < 0.0 || $aProg > 1.0 )
1194            JpGraphError::Raise("<b>JpGraph Error:</b> Progress value must in range [0, 1]");
1195        $this->iProgress = $aProg;
1196    }
1197
1198    function SetPattern($aPattern,$aColor="blue",$aDensity=98) {               
1199        $this->iPattern = $aPattern;
1200        $this->iColor = $aColor;
1201        $this->iDensity = $aDensity;
1202    }
1203       
1204    function SetHeight($aHeight) {
1205        $this->iHeight = $aHeight;
1206    }
1207}
1208
1209//===================================================
1210// CLASS GanttBar
1211// Responsible for formatting individual gantt bars
1212//===================================================
1213class GanttBar extends GanttPlotObject {
1214    var $iEnd;
1215    var $iHeightFactor=0.5;
1216    var $iFillColor="white",$iFrameColor="blue";
1217    var $iShadow=false,$iShadowColor="darkgray",$iShadowWidth=1,$iShadowFrame="black";
1218    var $iPattern=GANTT_RDIAG,$iPatternColor="blue",$iPatternDensity=95;
1219    var $leftMark,$rightMark;
1220    var $progress;
1221       
1222//---------------
1223// CONSTRUCTOR 
1224    function GanttBar($aPos,$aLabel,$aStart,$aEnd,$aCaption="",$aHeightFactor=0.6) {
1225        parent::GanttPlotObject();     
1226        $this->iStart = $aStart;       
1227        // Is the end date given as a date or as number of days added to start date?
1228        if( is_string($aEnd) )
1229            $this->iEnd = strtotime($aEnd)+SECPERDAY;
1230        // check for unix timestamp
1231        elseif($aEnd > 1000000)
1232            $this->iEnd = $aEnd;
1233        elseif(is_int($aEnd) || is_float($aEnd) )
1234            $this->iEnd = strtotime($aStart)+round($aEnd*SECPERDAY);
1235        $this->iVPos = $aPos;
1236        $this->iHeightFactor = $aHeightFactor;
1237        $this->title->Set($aLabel);
1238        $this->caption = new TextProperty($aCaption);
1239        $this->caption->Align("left","center");
1240        $this->leftMark =new PlotMark();
1241        $this->leftMark->Hide();
1242        $this->rightMark=new PlotMark();
1243        $this->rightMark->Hide();
1244        $this->progress = new Progress();
1245    }
1246       
1247//---------------
1248// PUBLIC METHODS       
1249    function SetShadow($aShadow=true,$aColor="gray") {
1250        $this->iShadow=$aShadow;
1251        $this->iShadowColor=$aColor;
1252    }
1253               
1254    function GetMaxDate() {
1255        return $this->iEnd;
1256    }
1257       
1258    function SetHeight($aHeight) {
1259        $this->iHeightFactor = $aHeight;
1260    }
1261
1262    function SetColor($aColor) {
1263        $this->iFrameColor = $aColor;
1264    }
1265
1266    function SetFillColor($aColor) {
1267        $this->iFillColor = $aColor;
1268    }
1269
1270    function GetAbsHeight($aImg) {
1271        if( is_int($this->iHeightFactor) || $this->leftMark->show || $this->rightMark->show ) {
1272            $m=-1;
1273            if( is_int($this->iHeightFactor) )
1274                $m = $this->iHeightFactor;
1275            if( $this->leftMark->show )
1276                $m = max($m,$this->leftMark->width*2);
1277            if( $this->rightMark->show )
1278                $m = max($m,$this->rightMark->width*2);
1279            return $m;
1280        }
1281        else
1282            return -1;
1283    }
1284       
1285    function SetPattern($aPattern,$aColor="blue",$aDensity=95) {               
1286        $this->iPattern = $aPattern;
1287        $this->iPatternColor = $aColor;
1288        $this->iPatternDensity = $aDensity;
1289    }
1290
1291    function Stroke($aImg,$aScale) {
1292        $factory = new RectPatternFactory();
1293        $prect = $factory->Create($this->iPattern,$this->iPatternColor);
1294        $prect->SetDensity($this->iPatternDensity);
1295               
1296        // If height factor is specified as a float between 0,1 then we take it as meaning
1297        // percetage of the scale width between horizontal line.
1298        // If it is an integer > 1 we take it to mean the absolute height in pixels
1299        if( $this->iHeightFactor > -0.0 && $this->iHeightFactor <= 1.1)
1300            $vs = $aScale->GetVertSpacing()*$this->iHeightFactor;
1301        elseif(is_int($this->iHeightFactor) && $this->iHeightFactor>2 && $this->iHeightFactor<200)
1302            $vs = $this->iHeightFactor;
1303        else
1304            JpGraphError::Raise("<b>JpGraph Error:</b>Specified height (".$this->iHeightFactor.") for gantt bar is out of range.");
1305
1306        $xt = $aScale->TranslateDate($aScale->NormalizeDate($this->iStart));
1307        $xb = $aScale->TranslateDate($aScale->NormalizeDate($this->iEnd));
1308        $yt = $aScale->TranslateVertPos($this->iVPos)-$vs-($aScale->GetVertSpacing()/2-$vs/2);
1309        $yb = $aScale->TranslateVertPos($this->iVPos)-($aScale->GetVertSpacing()/2-$vs/2);
1310
1311        $prect->ShowFrame(false);
1312        $prect->SetBackground($this->iFillColor);
1313        if( $this->iShadow ) {
1314            $aImg->SetColor($this->iFrameColor);
1315            $aImg->ShadowRectangle($xt,$yt,$xb,$yb,$this->iFillColor,$this->iShadowWidth,$this->iShadowColor);                         
1316            $prect->SetPos(new Rectangle($xt+1,$yt+1,$xb-$xt-$this->iShadowWidth-2,$yb-$yt-$this->iShadowWidth-2));                             
1317            $prect->Stroke($aImg);
1318        }
1319        else { 
1320            $prect->SetPos(new Rectangle($xt,$yt,$xb-$xt+1,$yb-$yt+1));                         
1321            $prect->Stroke($aImg);
1322            $aImg->SetColor($this->iFrameColor);
1323            $aImg->Rectangle($xt,$yt,$xb,$yb);
1324        }
1325        if( $this->progress->iProgress > 0 ) {
1326            $prog = $factory->Create($this->progress->iPattern,$this->progress->iColor);
1327            $prog->SetDensity($this->progress->iDensity);
1328            $barheight = ($yb-$yt+1);
1329            if( $this->iShadow )
1330                $barheight -= $this->iShadowWidth;
1331            $progressheight = floor($barheight*$this->progress->iHeight);
1332            $marg = ceil(($barheight-$progressheight)/2);
1333            $pos = new Rectangle($xt,
1334            $yt + $marg,
1335            ($xb-$xt+1)*$this->progress->iProgress,
1336            $barheight-2*$marg);
1337            $prog->SetPos($pos);
1338            $prog->Stroke($aImg);
1339        }
1340               
1341        $middle = round($yt+($yb-$yt)/2);
1342        $this->title->Stroke($aImg,$aImg->left_margin+$this->iLabelLeftMargin,$middle);
1343        $this->leftMark->Stroke($aImg,$xt,$middle);
1344        $this->rightMark->Stroke($aImg,$xb,$middle);
1345        $margin = $this->iCaptionMargin;
1346        if( $this->rightMark->show )
1347            $margin += $this->rightMark->GetWidth();
1348
1349        $this->caption->Stroke($aImg,$xb+$margin,$middle);
1350    }
1351}
1352
1353//===================================================
1354// CLASS MileStone
1355// Responsible for formatting individual milestones
1356//===================================================
1357class MileStone extends GanttPlotObject {
1358    var $mark;
1359       
1360//---------------
1361// CONSTRUCTOR 
1362    function MileStone($aVPos,$aLabel,$aDate,$aCaption="") {
1363        GanttPlotObject::GanttPlotObject();
1364        $this->caption->Set($aCaption);
1365        $this->caption->Align("left","center");
1366        $this->caption->SetFont(FF_FONT1,FS_BOLD);
1367        $this->title->Set($aLabel);
1368        $this->title->SetColor("darkred");
1369        $this->mark = new PlotMark();
1370        $this->mark->SetWidth(10);
1371        $this->mark->SetType(MARK_DIAMOND);
1372        $this->mark->SetColor("darkred");
1373        $this->mark->SetFillColor("darkred");
1374        $this->iVPos = $aVPos;
1375        $this->iStart = $aDate;
1376    }
1377       
1378//---------------
1379// PUBLIC METHODS       
1380       
1381    function GetAbsHeight($aImg) {
1382        return max($this->title->GetHeight($aImg),$this->mark->GetWidth());
1383    }
1384               
1385    function Stroke($aImg,$aScale) {
1386        // Put the mark in the middle at the middle of the day
1387        $x = $aScale->TranslateDate($aScale->NormalizeDate($this->iStart)+SECPERDAY/2);
1388        $y = $aScale->TranslateVertPos($this->iVPos)-($aScale->GetVertSpacing()/2);
1389               
1390        $this->mark->Stroke($aImg,$x,$y);               
1391        $this->caption->Stroke($aImg,$x+$this->mark->width/2+$this->iCaptionMargin,$y);
1392        $x=$aImg->left_margin+$this->iLabelLeftMargin;
1393        $this->title->Stroke($aImg,$x,$y);
1394    }
1395}
1396
1397
1398//===================================================
1399// CLASS GanttVLine
1400// Responsible for formatting individual milestones
1401//===================================================
1402
1403class GanttVLine extends GanttPlotObject {
1404
1405    var $iLine,$title_margin=3;
1406    var $iDayOffset=0;  // Defult to left edge of day
1407       
1408//---------------
1409// CONSTRUCTOR 
1410    function GanttVLine($aDate,$aTitle="",$aColor="black",$aWeight=3,$aStyle="dashed") {
1411        GanttPlotObject::GanttPlotObject();
1412        $this->iLine = new LineProperty();
1413        $this->iLine->SetColor($aColor);
1414        $this->iLine->SetWeight($aWeight);
1415        $this->iLine->SetStyle($aStyle);
1416        $this->iStart = $aDate;
1417        $this->title->Set($aTitle);
1418    }
1419
1420//---------------
1421// PUBLIC METHODS       
1422
1423    function SetDayOffset($aOff=0.5) {
1424        if( $aOff < 0.0 || $aOff > 1.0 )
1425            JpGraphError::Raise("<b>JpGraph Error:</b> Offset for vertical line must be in range [0,1]");
1426        $this->iDayOffset = $aOff;
1427    }
1428       
1429    function SetTitleMargin($aMarg) {
1430        $this->title_margin = $aMarg;
1431    }
1432       
1433    function Stroke($aImg,$aScale) {
1434        $x = $aScale->TranslateDate($aScale->NormalizeDate($this->iStart)+$this->iDayOffset*SECPERDAY);
1435        $y1 = $aScale->iVertHeaderSize+$aImg->top_margin;
1436        $y2 = $aImg->height - $aImg->bottom_margin;
1437        $this->iLine->Stroke($aImg,$x,$y1,$x,$y2);
1438        $this->title->Align("center","top");
1439        $this->title->Stroke($aImg,$x,$y2+$this->title_margin);
1440    }   
1441}
1442
1443// <EOF>
1444?>
Note: See TracBrowser for help on using the repository browser.