Subversion Repositories Applications.annuaire

Rev

Rev 66 | Details | Compare with Previous | Last modification | View Log | RSS feed

Rev Author Line No. Line
66 aurelien 1
<?php
2
/*=======================================================================
3
 // File:        JPGRAPH_GANTT.PHP
4
 // Description: JpGraph Gantt plot extension
5
 // Created:     2001-11-12
6
 // Ver:         $Id: jpgraph_gantt.php 1809 2009-09-09 13:07:33Z ljp $
7
 //
8
 // Copyright (c) Aditus Consulting. All rights reserved.
9
 //========================================================================
10
 */
11
 
12
require_once('jpgraph_plotband.php');
13
require_once('jpgraph_iconplot.php');
14
require_once('jpgraph_plotmark.inc.php');
15
 
16
// Maximum size for Automatic Gantt chart
17
define('MAX_GANTTIMG_SIZE_W',8000);
18
define('MAX_GANTTIMG_SIZE_H',5000);
19
 
20
// Scale Header types
21
define("GANTT_HDAY",1);
22
define("GANTT_HWEEK",2);
23
define("GANTT_HMONTH",4);
24
define("GANTT_HYEAR",8);
25
define("GANTT_HHOUR",16);
26
define("GANTT_HMIN",32);
27
 
28
// Bar patterns
29
define("GANTT_RDIAG",BAND_RDIAG); // Right diagonal lines
30
define("GANTT_LDIAG",BAND_LDIAG); // Left diagonal lines
31
define("GANTT_SOLID",BAND_SOLID); // Solid one color
32
define("GANTT_VLINE",BAND_VLINE); // Vertical lines
33
define("GANTT_HLINE",BAND_HLINE);  // Horizontal lines
34
define("GANTT_3DPLANE",BAND_3DPLANE);  // "3D" Plane
35
define("GANTT_HVCROSS",BAND_HVCROSS);  // Vertical/Hor crosses
36
define("GANTT_DIAGCROSS",BAND_DIAGCROSS); // Diagonal crosses
37
 
38
// Conversion constant
39
define("SECPERDAY",3600*24);
40
 
41
// Locales. ONLY KEPT FOR BACKWARDS COMPATIBILITY
42
// You should use the proper locale strings directly
43
// from now on.
44
define("LOCALE_EN","en_UK");
45
define("LOCALE_SV","sv_SE");
46
 
47
// Layout of bars
48
define("GANTT_EVEN",1);
49
define("GANTT_FROMTOP",2);
50
 
51
// Style for minute header
52
define("MINUTESTYLE_MM",0);  // 15
53
define("MINUTESTYLE_CUSTOM",2);  // Custom format
54
 
55
 
56
// Style for hour header
57
define("HOURSTYLE_HM24",0);  // 13:10
58
define("HOURSTYLE_HMAMPM",1);  // 1:10pm
59
define("HOURSTYLE_H24",2);  // 13
60
define("HOURSTYLE_HAMPM",3);  // 1pm
61
define("HOURSTYLE_CUSTOM",4);  // User defined
62
 
63
// Style for day header
64
define("DAYSTYLE_ONELETTER",0);  // "M"
65
define("DAYSTYLE_LONG",1);  // "Monday"
66
define("DAYSTYLE_LONGDAYDATE1",2); // "Monday 23 Jun"
67
define("DAYSTYLE_LONGDAYDATE2",3); // "Monday 23 Jun 2003"
68
define("DAYSTYLE_SHORT",4);  // "Mon"
69
define("DAYSTYLE_SHORTDAYDATE1",5); // "Mon 23/6"
70
define("DAYSTYLE_SHORTDAYDATE2",6); // "Mon 23 Jun"
71
define("DAYSTYLE_SHORTDAYDATE3",7); // "Mon 23"
72
define("DAYSTYLE_SHORTDATE1",8); // "23/6"
73
define("DAYSTYLE_SHORTDATE2",9); // "23 Jun"
74
define("DAYSTYLE_SHORTDATE3",10); // "Mon 23"
75
define("DAYSTYLE_SHORTDATE4",11); // "23"
76
define("DAYSTYLE_CUSTOM",12);  // "M"
77
 
78
// Styles for week header
79
define("WEEKSTYLE_WNBR",0);
80
define("WEEKSTYLE_FIRSTDAY",1);
81
define("WEEKSTYLE_FIRSTDAY2",2);
82
define("WEEKSTYLE_FIRSTDAYWNBR",3);
83
define("WEEKSTYLE_FIRSTDAY2WNBR",4);
84
 
85
// Styles for month header
86
define("MONTHSTYLE_SHORTNAME",0);
87
define("MONTHSTYLE_LONGNAME",1);
88
define("MONTHSTYLE_LONGNAMEYEAR2",2);
89
define("MONTHSTYLE_SHORTNAMEYEAR2",3);
90
define("MONTHSTYLE_LONGNAMEYEAR4",4);
91
define("MONTHSTYLE_SHORTNAMEYEAR4",5);
92
define("MONTHSTYLE_FIRSTLETTER",6);
93
 
94
 
95
// Types of constrain links
96
define('CONSTRAIN_STARTSTART',0);
97
define('CONSTRAIN_STARTEND',1);
98
define('CONSTRAIN_ENDSTART',2);
99
define('CONSTRAIN_ENDEND',3);
100
 
101
// Arrow direction for constrain links
102
define('ARROW_DOWN',0);
103
define('ARROW_UP',1);
104
define('ARROW_LEFT',2);
105
define('ARROW_RIGHT',3);
106
 
107
// Arrow type for constrain type
108
define('ARROWT_SOLID',0);
109
define('ARROWT_OPEN',1);
110
 
111
// Arrow size for constrain lines
112
define('ARROW_S1',0);
113
define('ARROW_S2',1);
114
define('ARROW_S3',2);
115
define('ARROW_S4',3);
116
define('ARROW_S5',4);
117
 
118
// Activity types for use with utility method CreateSimple()
119
define('ACTYPE_NORMAL',0);
120
define('ACTYPE_GROUP',1);
121
define('ACTYPE_MILESTONE',2);
122
 
123
define('ACTINFO_3D',1);
124
define('ACTINFO_2D',0);
125
 
126
 
127
// Check if array_fill() exists
128
if (!function_exists('array_fill')) {
129
    function array_fill($iStart, $iLen, $vValue) {
130
        $aResult = array();
131
        for ($iCount = $iStart; $iCount < $iLen + $iStart; $iCount++) {
132
            $aResult[$iCount] = $vValue;
133
        }
134
        return $aResult;
135
    }
136
}
137
 
138
//===================================================
139
// CLASS GanttActivityInfo
140
// Description:
141
//===================================================
142
class GanttActivityInfo {
143
    public $iShow=true;
144
    public $iLeftColMargin=4,$iRightColMargin=1,$iTopColMargin=1,$iBottomColMargin=3;
145
    public $vgrid = null;
146
    private $iColor='black';
147
    private $iBackgroundColor='lightgray';
148
    private $iFFamily=FF_FONT1,$iFStyle=FS_NORMAL,$iFSize=10,$iFontColor='black';
149
    private $iTitles=array();
150
    private $iWidth=array(),$iHeight=-1;
151
    private $iTopHeaderMargin = 4;
152
    private $iStyle=1;
153
    private $iHeaderAlign='center';
154
 
155
    function __construct() {
156
        $this->vgrid = new LineProperty();
157
    }
158
 
159
    function Hide($aF=true) {
160
        $this->iShow=!$aF;
161
    }
162
 
163
    function Show($aF=true) {
164
        $this->iShow=$aF;
165
    }
166
 
167
    // Specify font
168
    function SetFont($aFFamily,$aFStyle=FS_NORMAL,$aFSize=10) {
169
        $this->iFFamily = $aFFamily;
170
        $this->iFStyle  = $aFStyle;
171
        $this->iFSize  = $aFSize;
172
    }
173
 
174
    function SetStyle($aStyle) {
175
        $this->iStyle = $aStyle;
176
    }
177
 
178
    function SetColumnMargin($aLeft,$aRight) {
179
        $this->iLeftColMargin = $aLeft;
180
        $this->iRightColMargin = $aRight;
181
    }
182
 
183
    function SetFontColor($aFontColor) {
184
        $this->iFontColor = $aFontColor;
185
    }
186
 
187
    function SetColor($aColor) {
188
        $this->iColor = $aColor;
189
    }
190
 
191
    function SetBackgroundColor($aColor) {
192
        $this->iBackgroundColor = $aColor;
193
    }
194
 
195
    function SetColTitles($aTitles,$aWidth=null) {
196
        $this->iTitles = $aTitles;
197
        $this->iWidth = $aWidth;
198
    }
199
 
200
    function SetMinColWidth($aWidths) {
201
        $n = min(count($this->iTitles),count($aWidths));
202
        for($i=0; $i < $n; ++$i ) {
203
            if( !empty($aWidths[$i]) ) {
204
                if( empty($this->iWidth[$i]) ) {
205
                    $this->iWidth[$i] = $aWidths[$i];
206
                }
207
                else {
208
                    $this->iWidth[$i] = max($this->iWidth[$i],$aWidths[$i]);
209
                }
210
            }
211
        }
212
    }
213
 
214
    function GetWidth($aImg) {
215
        $txt = new TextProperty();
216
        $txt->SetFont($this->iFFamily,$this->iFStyle,$this->iFSize);
217
        $n = count($this->iTitles) ;
218
        $rm=$this->iRightColMargin;
219
        $w = 0;
220
        for($h=0, $i=0; $i < $n; ++$i ) {
221
            $w += $this->iLeftColMargin;
222
            $txt->Set($this->iTitles[$i]);
223
            if( !empty($this->iWidth[$i]) ) {
224
                $w1 = max($txt->GetWidth($aImg)+$rm,$this->iWidth[$i]);
225
            }
226
            else {
227
                $w1 = $txt->GetWidth($aImg)+$rm;
228
            }
229
            $this->iWidth[$i] = $w1;
230
            $w += $w1;
231
            $h = max($h,$txt->GetHeight($aImg));
232
        }
233
        $this->iHeight = $h+$this->iTopHeaderMargin;
234
        $txt='';
235
        return $w;
236
    }
237
 
238
    function GetColStart($aImg,&$aStart,$aAddLeftMargin=false) {
239
        $n = count($this->iTitles) ;
240
        $adj = $aAddLeftMargin ? $this->iLeftColMargin : 0;
241
        $aStart=array($aImg->left_margin+$adj);
242
        for( $i=1; $i < $n; ++$i ) {
243
            $aStart[$i] = $aStart[$i-1]+$this->iLeftColMargin+$this->iWidth[$i-1];
244
        }
245
    }
246
 
247
    // Adjust headers left, right or centered
248
    function SetHeaderAlign($aAlign) {
249
        $this->iHeaderAlign=$aAlign;
250
    }
251
 
252
    function Stroke($aImg,$aXLeft,$aYTop,$aXRight,$aYBottom,$aUseTextHeight=false) {
253
 
254
        if( !$this->iShow ) return;
255
 
256
        $txt = new TextProperty();
257
        $txt->SetFont($this->iFFamily,$this->iFStyle,$this->iFSize);
258
        $txt->SetColor($this->iFontColor);
259
        $txt->SetAlign($this->iHeaderAlign,'top');
260
        $n=count($this->iTitles);
261
 
262
        if( $n == 0 )
263
        return;
264
 
265
        $x = $aXLeft;
266
        $h = $this->iHeight;
267
        $yTop = $aUseTextHeight ? $aYBottom-$h-$this->iTopColMargin-$this->iBottomColMargin : $aYTop ;
268
 
269
        if( $h < 0 ) {
270
            JpGraphError::RaiseL(6001);
271
            //('Internal error. Height for ActivityTitles is < 0');
272
        }
273
 
274
        $aImg->SetLineWeight(1);
275
        // Set background color
276
        $aImg->SetColor($this->iBackgroundColor);
277
        $aImg->FilledRectangle($aXLeft,$yTop,$aXRight,$aYBottom-1);
278
 
279
        if( $this->iStyle == 1 ) {
280
            // Make a 3D effect
281
            $aImg->SetColor('white');
282
            $aImg->Line($aXLeft,$yTop+1,$aXRight,$yTop+1);
283
        }
284
 
285
        for($i=0; $i < $n; ++$i ) {
286
            if( $this->iStyle == 1 ) {
287
                // Make a 3D effect
288
                $aImg->SetColor('white');
289
                $aImg->Line($x+1,$yTop,$x+1,$aYBottom);
290
            }
291
            $x += $this->iLeftColMargin;
292
            $txt->Set($this->iTitles[$i]);
293
 
294
            // Adjust the text anchor position according to the choosen alignment
295
            $xp = $x;
296
            if( $this->iHeaderAlign == 'center' ) {
297
                $xp = (($x-$this->iLeftColMargin)+($x+$this->iWidth[$i]))/2;
298
            }
299
            elseif( $this->iHeaderAlign == 'right' ) {
300
                $xp = $x +$this->iWidth[$i]-$this->iRightColMargin;
301
            }
302
 
303
            $txt->Stroke($aImg,$xp,$yTop+$this->iTopHeaderMargin);
304
            $x += $this->iWidth[$i];
305
            if( $i < $n-1 ) {
306
                $aImg->SetColor($this->iColor);
307
                $aImg->Line($x,$yTop,$x,$aYBottom);
308
            }
309
        }
310
 
311
        $aImg->SetColor($this->iColor);
312
        $aImg->Line($aXLeft,$yTop, $aXRight,$yTop);
313
 
314
        // Stroke vertical column dividers
315
        $cols=array();
316
        $this->GetColStart($aImg,$cols);
317
        $n=count($cols);
318
        for( $i=1; $i < $n; ++$i ) {
319
            $this->vgrid->Stroke($aImg,$cols[$i],$aYBottom,$cols[$i],
320
            $aImg->height - $aImg->bottom_margin);
321
        }
322
    }
323
}
324
 
325
 
326
//===================================================
327
// CLASS GanttGraph
328
// Description: Main class to handle gantt graphs
329
//===================================================
330
class GanttGraph extends Graph {
331
    public $scale;  // Public accessible
332
    public $hgrid=null;
333
    private $iObj=array();    // Gantt objects
334
    private $iLabelHMarginFactor=0.2; // 10% margin on each side of the labels
335
    private $iLabelVMarginFactor=0.4; // 40% margin on top and bottom of label
336
    private $iLayout=GANTT_FROMTOP; // Could also be GANTT_EVEN
337
    private $iSimpleFont = FF_FONT1,$iSimpleFontSize=11;
338
    private $iSimpleStyle=GANTT_RDIAG,$iSimpleColor='yellow',$iSimpleBkgColor='red';
339
    private $iSimpleProgressBkgColor='gray',$iSimpleProgressColor='darkgreen';
340
    private $iSimpleProgressStyle=GANTT_SOLID;
341
    private $iZoomFactor = 1.0;
342
    //---------------
343
    // CONSTRUCTOR
344
    // Create a new gantt graph
345
    function __construct($aWidth=0,$aHeight=0,$aCachedName="",$aTimeOut=0,$aInline=true) {
346
 
347
        // Backward compatibility
348
        if( $aWidth == -1 ) $aWidth=0;
349
        if( $aHeight == -1 ) $aHeight=0;
350
 
351
        if( $aWidth<  0 || $aHeight < 0 ) {
352
            JpgraphError::RaiseL(6002);
353
            //("You can't specify negative sizes for Gantt graph dimensions. Use 0 to indicate that you want the library to automatically determine a dimension.");
354
        }
355
        parent::__construct($aWidth,$aHeight,$aCachedName,$aTimeOut,$aInline);
356
        $this->scale = new GanttScale($this->img);
357
 
358
        // Default margins
359
        $this->img->SetMargin(15,17,25,15);
360
 
361
        $this->hgrid = new HorizontalGridLine();
362
 
363
        $this->scale->ShowHeaders(GANTT_HWEEK|GANTT_HDAY);
364
        $this->SetBox();
365
    }
366
 
367
    //---------------
368
    // PUBLIC METHODS
369
 
370
    //
371
 
372
    function SetSimpleFont($aFont,$aSize) {
373
        $this->iSimpleFont = $aFont;
374
        $this->iSimpleFontSize = $aSize;
375
    }
376
 
377
    function SetSimpleStyle($aBand,$aColor,$aBkgColor) {
378
        $this->iSimpleStyle = $aBand;
379
        $this->iSimpleColor = $aColor;
380
        $this->iSimpleBkgColor = $aBkgColor;
381
    }
382
 
383
    // A utility function to help create basic Gantt charts
384
    function CreateSimple($data,$constrains=array(),$progress=array()) {
385
        $num = count($data);
386
        for( $i=0; $i < $num; ++$i) {
387
            switch( $data[$i][1] ) {
388
                case ACTYPE_GROUP:
389
                    // Create a slightly smaller height bar since the
390
                    // "wings" at the end will make it look taller
391
                    $a = new GanttBar($data[$i][0],$data[$i][2],$data[$i][3],$data[$i][4],'',8);
392
                    $a->title->SetFont($this->iSimpleFont,FS_BOLD,$this->iSimpleFontSize);
393
                    $a->rightMark->Show();
394
                    $a->rightMark->SetType(MARK_RIGHTTRIANGLE);
395
                    $a->rightMark->SetWidth(8);
396
                    $a->rightMark->SetColor('black');
397
                    $a->rightMark->SetFillColor('black');
398
 
399
                    $a->leftMark->Show();
400
                    $a->leftMark->SetType(MARK_LEFTTRIANGLE);
401
                    $a->leftMark->SetWidth(8);
402
                    $a->leftMark->SetColor('black');
403
                    $a->leftMark->SetFillColor('black');
404
 
405
                    $a->SetPattern(BAND_SOLID,'black');
406
                    $csimpos = 6;
407
                    break;
408
 
409
                case ACTYPE_NORMAL:
410
                    $a = new GanttBar($data[$i][0],$data[$i][2],$data[$i][3],$data[$i][4],'',10);
411
                    $a->title->SetFont($this->iSimpleFont,FS_NORMAL,$this->iSimpleFontSize);
412
                    $a->SetPattern($this->iSimpleStyle,$this->iSimpleColor);
413
                    $a->SetFillColor($this->iSimpleBkgColor);
414
                    // Check if this activity should have a constrain line
415
                    $n = count($constrains);
416
                    for( $j=0; $j < $n; ++$j ) {
417
                        if( empty($constrains[$j]) || (count($constrains[$j]) != 3) ) {
418
                            JpGraphError::RaiseL(6003,$j);
419
                            //("Invalid format for Constrain parameter at index=$j in CreateSimple(). Parameter must start with index 0 and contain arrays of (Row,Constrain-To,Constrain-Type)");
420
                        }
421
                        if( $constrains[$j][0]==$data[$i][0] ) {
422
                            $a->SetConstrain($constrains[$j][1],$constrains[$j][2],'black',ARROW_S2,ARROWT_SOLID);
423
                        }
424
                    }
425
 
426
                    // Check if this activity have a progress bar
427
                    $n = count($progress);
428
                    for( $j=0; $j < $n; ++$j ) {
429
 
430
                        if( empty($progress[$j]) || (count($progress[$j]) != 2) ) {
431
                            JpGraphError::RaiseL(6004,$j);
432
                            //("Invalid format for Progress parameter at index=$j in CreateSimple(). Parameter must start with index 0 and contain arrays of (Row,Progress)");
433
                        }
434
                        if( $progress[$j][0]==$data[$i][0] ) {
435
                            $a->progress->Set($progress[$j][1]);
436
                            $a->progress->SetPattern($this->iSimpleProgressStyle,
437
                            $this->iSimpleProgressColor);
438
                            $a->progress->SetFillColor($this->iSimpleProgressBkgColor);
439
                            //$a->progress->SetPattern($progress[$j][2],$progress[$j][3]);
440
                            break;
441
                        }
442
                    }
443
                    $csimpos = 6;
444
                    break;
445
 
446
                case ACTYPE_MILESTONE:
447
                    $a = new MileStone($data[$i][0],$data[$i][2],$data[$i][3]);
448
                    $a->title->SetFont($this->iSimpleFont,FS_NORMAL,$this->iSimpleFontSize);
449
                    $a->caption->SetFont($this->iSimpleFont,FS_NORMAL,$this->iSimpleFontSize);
450
                    $csimpos = 5;
451
                    break;
452
                default:
453
                    die('Unknown activity type');
454
                    break;
455
            }
456
 
457
            // Setup caption
458
            $a->caption->Set($data[$i][$csimpos-1]);
459
 
460
            // Check if this activity should have a CSIM target�?
461
            if( !empty($data[$i][$csimpos]) ) {
462
                $a->SetCSIMTarget($data[$i][$csimpos]);
463
                $a->SetCSIMAlt($data[$i][$csimpos+1]);
464
            }
465
            if( !empty($data[$i][$csimpos+2]) ) {
466
                $a->title->SetCSIMTarget($data[$i][$csimpos+2]);
467
                $a->title->SetCSIMAlt($data[$i][$csimpos+3]);
468
            }
469
 
470
            $this->Add($a);
471
        }
472
    }
473
 
474
    // Set user specified scale zoom factor when auto sizing is used
475
    function SetZoomFactor($aZoom) {
476
    	$this->iZoomFactor = $aZoom;
477
    }
478
 
479
 
480
    // Set what headers should be shown
481
    function ShowHeaders($aFlg) {
482
        $this->scale->ShowHeaders($aFlg);
483
    }
484
 
485
    // Specify the fraction of the font height that should be added
486
    // as vertical margin
487
    function SetLabelVMarginFactor($aVal) {
488
        $this->iLabelVMarginFactor = $aVal;
489
    }
490
 
491
    // Synonym to the method above
492
    function SetVMarginFactor($aVal) {
493
        $this->iLabelVMarginFactor = $aVal;
494
    }
495
 
496
 
497
    // Add a new Gantt object
498
    function Add($aObject) {
499
        if( is_array($aObject) && count($aObject) > 0 ) {
500
            $cl = $aObject[0];
501
            if( class_exists('IconPlot',false) && ($cl instanceof IconPlot) ) {
502
                $this->AddIcon($aObject);
503
            }
504
            elseif( class_exists('Text',false) && ($cl instanceof Text) ) {
505
            	$this->AddText($aObject);
506
            }
507
            else {
508
                $n = count($aObject);
509
                for($i=0; $i < $n; ++$i)
510
                $this->iObj[] = $aObject[$i];
511
            }
512
        }
513
        else {
514
            if( class_exists('IconPlot',false) && ($aObject instanceof IconPlot) ) {
515
                $this->AddIcon($aObject);
516
            }
517
            elseif( class_exists('Text',false) && ($aObject instanceof Text) ) {
518
            	$this->AddText($aObject);
519
            }
520
            else {
521
                $this->iObj[] = $aObject;
522
            }
523
        }
524
    }
525
 
526
	function StrokeTexts() {
527
        // Stroke any user added text objects
528
        if( $this->texts != null ) {
529
        	$n = count($this->texts);
530
            for($i=0; $i < $n; ++$i) {
531
            	if( $this->texts[$i]->iScalePosX !== null && $this->texts[$i]->iScalePosY !== null ) {
532
            		$x = $this->scale->TranslateDate($this->texts[$i]->iScalePosX);
533
            		$y = $this->scale->TranslateVertPos($this->texts[$i]->iScalePosY);
534
            		$y -= $this->scale->GetVertSpacing()/2;
535
            	}
536
            	else {
537
            		$x = $y = null;
538
            	}
539
                $this->texts[$i]->Stroke($this->img,$x,$y);
540
            }
541
        }
542
	}
543
 
544
    // Override inherit method from Graph and give a warning message
545
    function SetScale($aAxisType,$aYMin=1,$aYMax=1,$aXMin=1,$aXMax=1) {
546
        JpGraphError::RaiseL(6005);
547
        //("SetScale() is not meaningfull with Gantt charts.");
548
    }
549
 
550
    // Specify the date range for Gantt graphs (if this is not set it will be
551
    // automtically determined from the input data)
552
    function SetDateRange($aStart,$aEnd) {
553
        // Adjust the start and end so that the indicate the
554
        // begining and end of respective start and end days
555
        if( strpos($aStart,':') === false )
556
        $aStart = date('Y-m-d 00:00',strtotime($aStart));
557
        if( strpos($aEnd,':') === false )
558
        $aEnd = date('Y-m-d 23:59',strtotime($aEnd));
559
        $this->scale->SetRange($aStart,$aEnd);
560
    }
561
 
562
    // Get the maximum width of the activity titles columns for the bars
563
    // The name is lightly misleading since we from now on can have
564
    // multiple columns in the label section. When this was first written
565
    // it only supported a single label, hence the name.
566
    function GetMaxLabelWidth() {
567
        $m=10;
568
        if( $this->iObj != null ) {
569
            $marg = $this->scale->actinfo->iLeftColMargin+$this->scale->actinfo->iRightColMargin;
570
            $n = count($this->iObj);
571
            for($i=0; $i < $n; ++$i) {
572
                if( !empty($this->iObj[$i]->title) ) {
573
                    if( $this->iObj[$i]->title->HasTabs() ) {
574
                        list($tot,$w) = $this->iObj[$i]->title->GetWidth($this->img,true);
575
                        $m=max($m,$tot);
576
                    }
577
                    else
578
                    $m=max($m,$this->iObj[$i]->title->GetWidth($this->img));
579
                }
580
            }
581
        }
582
        return $m;
583
    }
584
 
585
    // Get the maximum height of the titles for the bars
586
    function GetMaxLabelHeight() {
587
        $m=10;
588
        if( $this->iObj != null ) {
589
            $n = count($this->iObj);
590
            // We can not include the title of GnttVLine since that title is stroked at the bottom
591
            // of the Gantt bar and not in the activity title columns
592
            for($i=0; $i < $n; ++$i) {
593
                if( !empty($this->iObj[$i]->title) && !($this->iObj[$i] instanceof GanttVLine) ) {
594
                    $m=max($m,$this->iObj[$i]->title->GetHeight($this->img));
595
                }
596
            }
597
        }
598
        return $m;
599
    }
600
 
601
    function GetMaxBarAbsHeight() {
602
        $m=0;
603
        if( $this->iObj != null ) {
604
            $m = $this->iObj[0]->GetAbsHeight($this->img);
605
            $n = count($this->iObj);
606
            for($i=1; $i < $n; ++$i) {
607
                $m=max($m,$this->iObj[$i]->GetAbsHeight($this->img));
608
            }
609
        }
610
        return $m;
611
    }
612
 
613
    // Get the maximum used line number (vertical position) for bars
614
    function GetBarMaxLineNumber() {
615
        $m=1;
616
        if( $this->iObj != null ) {
617
            $m = $this->iObj[0]->GetLineNbr();
618
            $n = count($this->iObj);
619
            for($i=1; $i < $n; ++$i) {
620
                $m=max($m,$this->iObj[$i]->GetLineNbr());
621
            }
622
        }
623
        return $m;
624
    }
625
 
626
    // Get the minumum and maximum used dates for all bars
627
    function GetBarMinMax() {
628
        $start = 0 ;
629
        $n = count($this->iObj);
630
        while( $start < $n && $this->iObj[$start]->GetMaxDate() === false )
631
        ++$start;
632
        if( $start >= $n ) {
633
            JpgraphError::RaiseL(6006);
634
            //('Cannot autoscale Gantt chart. No dated activities exist. [GetBarMinMax() start >= n]');
635
        }
636
 
637
        $max=$this->scale->NormalizeDate($this->iObj[$start]->GetMaxDate());
638
        $min=$this->scale->NormalizeDate($this->iObj[$start]->GetMinDate());
639
 
640
        for($i=$start+1; $i < $n; ++$i) {
641
            $rmax = $this->scale->NormalizeDate($this->iObj[$i]->GetMaxDate());
642
            if( $rmax != false )
643
            $max=Max($max,$rmax);
644
            $rmin = $this->scale->NormalizeDate($this->iObj[$i]->GetMinDate());
645
            if( $rmin != false )
646
            $min=Min($min,$rmin);
647
        }
648
        $minDate = date("Y-m-d",$min);
649
        $min = strtotime($minDate);
650
        $maxDate = date("Y-m-d 23:59",$max);
651
        $max = strtotime($maxDate);
652
        return array($min,$max);
653
    }
654
 
655
    // Create a new auto sized canvas if the user hasn't specified a size
656
    // The size is determined by what scale the user has choosen and hence
657
    // the minimum width needed to display the headers. Some margins are
658
    // also added to make it better looking.
659
    function AutoSize() {
660
 
661
        if( $this->img->img == null ) {
662
            // The predefined left, right, top, bottom margins.
663
            // Note that the top margin might incease depending on
664
            // the title.
665
           $hadj = $vadj = 0;
666
           if( $this->doshadow ) {
667
           		$hadj = $this->shadow_width;
668
                $vadj = $this->shadow_width+5;
669
            }
670
 
671
            $lm = $this->img->left_margin;
672
            $rm = $this->img->right_margin +$hadj;
673
            $rm += 2 ;
674
            $tm = $this->img->top_margin;
675
            $bm = $this->img->bottom_margin + $vadj;
676
            $bm += 2;
677
 
678
            // If there are any added GanttVLine we must make sure that the
679
            // bottom margin is wide enough to hold a title.
680
            $n = count($this->iObj);
681
        	for($i=0; $i < $n; ++$i) {
682
            	if( $this->iObj[$i] instanceof GanttVLine ) {
683
					$bm = max($bm,$this->iObj[$i]->title->GetHeight($this->img)+10);
684
            	}
685
        	}
686
 
687
            // First find out the height
688
            $n=$this->GetBarMaxLineNumber()+1;
689
            $m=max($this->GetMaxLabelHeight(),$this->GetMaxBarAbsHeight());
690
            $height=$n*((1+$this->iLabelVMarginFactor)*$m);
691
 
692
            // Add the height of the scale titles
693
            $h=$this->scale->GetHeaderHeight();
694
            $height += $h;
695
 
696
            // Calculate the top margin needed for title and subtitle
697
            if( $this->title->t != "" ) {
698
                $tm += $this->title->GetFontHeight($this->img);
699
            }
700
            if( $this->subtitle->t != "" ) {
701
                $tm += $this->subtitle->GetFontHeight($this->img);
702
            }
703
 
704
            // ...and then take the bottom and top plot margins into account
705
            $height += $tm + $bm + $this->scale->iTopPlotMargin + $this->scale->iBottomPlotMargin;
706
            // Now find the minimum width for the chart required
707
 
708
            // If day scale or smaller is shown then we use the day font width
709
            // as the base size unit.
710
            // If only weeks or above is displayed we use a modified unit to
711
            // get a smaller image.
712
            if( $this->scale->IsDisplayHour() || $this->scale->IsDisplayMinute() ) {
713
                // Add 2 pixel margin on each side
714
                $fw=$this->scale->day->GetFontWidth($this->img)+4;
715
            }
716
            elseif( $this->scale->IsDisplayWeek() ) {
717
                $fw = 8;
718
            }
719
            elseif( $this->scale->IsDisplayMonth() ) {
720
                $fw = 4;
721
            }
722
            else {
723
                $fw = 2;
724
            }
725
 
726
            $nd=$this->scale->GetNumberOfDays();
727
 
728
            if( $this->scale->IsDisplayDay() ) {
729
                // If the days are displayed we also need to figure out
730
                // how much space each day's title will require.
731
                switch( $this->scale->day->iStyle ) {
732
                    case DAYSTYLE_LONG :
733
                        $txt = "Monday";
734
                        break;
735
                    case DAYSTYLE_LONGDAYDATE1 :
736
                        $txt =  "Monday 23 Jun";
737
                        break;
738
                    case DAYSTYLE_LONGDAYDATE2 :
739
                        $txt =  "Monday 23 Jun 2003";
740
                        break;
741
                    case DAYSTYLE_SHORT :
742
                        $txt =  "Mon";
743
                        break;
744
                    case DAYSTYLE_SHORTDAYDATE1 :
745
                        $txt =  "Mon 23/6";
746
                        break;
747
                    case DAYSTYLE_SHORTDAYDATE2 :
748
                        $txt =  "Mon 23 Jun";
749
                        break;
750
                    case DAYSTYLE_SHORTDAYDATE3 :
751
                        $txt =  "Mon 23";
752
                        break;
753
                    case DAYSTYLE_SHORTDATE1 :
754
                        $txt =  "23/6";
755
                        break;
756
                    case DAYSTYLE_SHORTDATE2 :
757
                        $txt =  "23 Jun";
758
                        break;
759
                    case DAYSTYLE_SHORTDATE3 :
760
                        $txt =  "Mon 23";
761
                        break;
762
                    case DAYSTYLE_SHORTDATE4 :
763
                        $txt =  "88";
764
                        break;
765
                    case DAYSTYLE_CUSTOM :
766
                        $txt = date($this->scale->day->iLabelFormStr,strtotime('2003-12-20 18:00'));
767
                        break;
768
                    case DAYSTYLE_ONELETTER :
769
                    default:
770
                        $txt = "M";
771
                        break;
772
                }
773
                $fw = $this->scale->day->GetStrWidth($this->img,$txt)+6;
774
            }
775
 
776
            // If we have hours enabled we must make sure that each day has enough
777
            // space to fit the number of hours to be displayed.
778
            if( $this->scale->IsDisplayHour() ) {
779
                // Depending on what format the user has choose we need different amount
780
                // of space. We therefore create a typical string for the choosen format
781
                // and determine the length of that string.
782
                switch( $this->scale->hour->iStyle ) {
783
                    case HOURSTYLE_HMAMPM:
784
                        $txt = '12:00pm';
785
                        break;
786
                    case HOURSTYLE_H24:
787
                        // 13
788
                        $txt = '24';
789
                        break;
790
                    case HOURSTYLE_HAMPM:
791
                        $txt = '12pm';
792
                        break;
793
                    case HOURSTYLE_CUSTOM:
794
                        $txt = date($this->scale->hour->iLabelFormStr,strtotime('2003-12-20 18:00'));
795
                        break;
796
                    case HOURSTYLE_HM24:
797
                    default:
798
                        $txt = '24:00';
799
                        break;
800
                }
801
 
802
                $hfw = $this->scale->hour->GetStrWidth($this->img,$txt)+6;
803
                $mw = $hfw;
804
                if( $this->scale->IsDisplayMinute() ) {
805
                    // Depending on what format the user has choose we need different amount
806
                    // of space. We therefore create a typical string for the choosen format
807
                    // and determine the length of that string.
808
                    switch( $this->scale->minute->iStyle ) {
809
                        case HOURSTYLE_CUSTOM:
810
                            $txt2 = date($this->scale->minute->iLabelFormStr,strtotime('2005-05-15 18:55'));
811
                            break;
812
                        case MINUTESTYLE_MM:
813
                        default:
814
                            $txt2 = '15';
815
                            break;
816
                    }
817
 
818
                    $mfw = $this->scale->minute->GetStrWidth($this->img,$txt2)+6;
819
                    $n2 = ceil(60 / $this->scale->minute->GetIntervall() );
820
                    $mw = $n2 * $mfw;
821
                }
822
                $hfw = $hfw < $mw ? $mw : $hfw ;
823
                $n = ceil(24*60 / $this->scale->TimeToMinutes($this->scale->hour->GetIntervall()) );
824
                $hw = $n * $hfw;
825
                $fw = $fw < $hw ? $hw : $fw ;
826
            }
827
 
828
            // We need to repeat this code block here as well.
829
            // THIS iS NOT A MISTAKE !
830
            // We really need it since we need to adjust for minutes both in the case
831
            // where hour scale is shown and when it is not shown.
832
 
833
            if( $this->scale->IsDisplayMinute() ) {
834
                // Depending on what format the user has choose we need different amount
835
                // of space. We therefore create a typical string for the choosen format
836
                // and determine the length of that string.
837
                switch( $this->scale->minute->iStyle ) {
838
                    case HOURSTYLE_CUSTOM:
839
                        $txt = date($this->scale->minute->iLabelFormStr,strtotime('2005-05-15 18:55'));
840
                        break;
841
                    case MINUTESTYLE_MM:
842
                    default:
843
                        $txt = '15';
844
                        break;
845
                }
846
 
847
                $mfw = $this->scale->minute->GetStrWidth($this->img,$txt)+6;
848
                $n = ceil(60 / $this->scale->TimeToMinutes($this->scale->minute->GetIntervall()) );
849
                $mw = $n * $mfw;
850
                $fw = $fw < $mw ? $mw : $fw ;
851
            }
852
 
853
            // If we display week we must make sure that 7*$fw is enough
854
            // to fit up to 10 characters of the week font (if the week is enabled)
855
            if( $this->scale->IsDisplayWeek() ) {
856
                // Depending on what format the user has choose we need different amount
857
                // of space
858
                $fsw = strlen($this->scale->week->iLabelFormStr);
859
                if( $this->scale->week->iStyle==WEEKSTYLE_FIRSTDAY2WNBR ) {
860
                    $fsw += 8;
861
                }
862
                elseif( $this->scale->week->iStyle==WEEKSTYLE_FIRSTDAYWNBR ) {
863
                    $fsw += 7;
864
                }
865
                else {
866
                    $fsw += 4;
867
                }
868
 
869
                $ww = $fsw*$this->scale->week->GetFontWidth($this->img);
870
                if( 7*$fw < $ww ) {
871
                    $fw = ceil($ww/7);
872
                }
873
            }
874
 
875
            if( !$this->scale->IsDisplayDay() && !$this->scale->IsDisplayHour() &&
876
            	!( ($this->scale->week->iStyle==WEEKSTYLE_FIRSTDAYWNBR ||
877
            		$this->scale->week->iStyle==WEEKSTYLE_FIRSTDAY2WNBR) && $this->scale->IsDisplayWeek() ) ) {
878
                // If we don't display the individual days we can shrink the
879
                // scale a little bit. This is a little bit pragmatic at the
880
                // moment and should be re-written to take into account
881
                // a) What scales exactly are shown and
882
                // b) what format do they use so we know how wide we need to
883
                // make each scale text space at minimum.
884
                $fw /= 2;
885
                if( !$this->scale->IsDisplayWeek() ) {
886
                    $fw /= 1.8;
887
                }
888
            }
889
 
890
            $cw = $this->GetMaxActInfoColWidth() ;
891
            $this->scale->actinfo->SetMinColWidth($cw);
892
            if( $this->img->width <= 0 ) {
893
                // Now determine the width for the activity titles column
894
 
895
                // Firdst find out the maximum width of each object column
896
                $titlewidth = max(max($this->GetMaxLabelWidth(),
897
                $this->scale->tableTitle->GetWidth($this->img)),
898
                $this->scale->actinfo->GetWidth($this->img));
899
 
900
                // Add the width of the vertivcal divider line
901
                $titlewidth += $this->scale->divider->iWeight*2;
902
 
903
				// Adjust the width by the user specified zoom factor
904
				$fw *= $this->iZoomFactor;
905
 
906
                // Now get the total width taking
907
                // titlewidth, left and rigt margin, dayfont size
908
                // into account
909
                $width = $titlewidth + $nd*$fw + $lm+$rm;
910
            }
911
            else {
912
                $width = $this->img->width;
913
            }
914
 
915
            $width = round($width);
916
            $height = round($height);
917
            // Make a sanity check on image size
918
            if( $width > MAX_GANTTIMG_SIZE_W || $height > MAX_GANTTIMG_SIZE_H ) {
919
                JpgraphError::RaiseL(6007,$width,$height);
920
                //("Sanity check for automatic Gantt chart size failed. Either the width (=$width) or height (=$height) is larger than MAX_GANTTIMG_SIZE. This could potentially be caused by a wrong date in one of the activities.");
921
            }
922
            $this->img->CreateImgCanvas($width,$height);
923
            $this->img->SetMargin($lm,$rm,$tm,$bm);
924
        }
925
    }
926
 
927
    // Return an array width the maximum width for each activity
928
    // column. This is used when we autosize the columns where we need
929
    // to find out the maximum width of each column. In order to do that we
930
    // must walk through all the objects, sigh...
931
    function GetMaxActInfoColWidth() {
932
        $n = count($this->iObj);
933
        if( $n == 0 ) return;
934
        $w = array();
935
        $m = $this->scale->actinfo->iLeftColMargin + $this->scale->actinfo->iRightColMargin;
936
 
937
        for( $i=0; $i < $n; ++$i ) {
938
            $tmp = $this->iObj[$i]->title->GetColWidth($this->img,$m);
939
            $nn = count($tmp);
940
            for( $j=0; $j < $nn; ++$j ) {
941
                if( empty($w[$j]) )
942
                $w[$j] = $tmp[$j];
943
                else
944
                $w[$j] = max($w[$j],$tmp[$j]);
945
            }
946
        }
947
        return $w;
948
    }
949
 
950
    // Stroke the gantt chart
951
    function Stroke($aStrokeFileName="") {
952
 
953
        // If the filename is the predefined value = '_csim_special_'
954
        // we assume that the call to stroke only needs to do enough
955
        // to correctly generate the CSIM maps.
956
        // We use this variable to skip things we don't strictly need
957
        // to do to generate the image map to improve performance
958
        // a best we can. Therefor you will see a lot of tests !$_csim in the
959
        // code below.
960
        $_csim = ($aStrokeFileName===_CSIM_SPECIALFILE);
961
 
962
        // Should we autoscale dates?
963
 
964
        if( !$this->scale->IsRangeSet() ) {
965
            list($min,$max) = $this->GetBarMinMax();
966
            $this->scale->SetRange($min,$max);
967
        }
968
 
969
        $this->scale->AdjustStartEndDay();
970
 
971
        // Check if we should autoscale the image
972
        $this->AutoSize();
973
 
974
        // Should we start from the top or just spread the bars out even over the
975
        // available height
976
        $this->scale->SetVertLayout($this->iLayout);
977
        if( $this->iLayout == GANTT_FROMTOP ) {
978
            $maxheight=max($this->GetMaxLabelHeight(),$this->GetMaxBarAbsHeight());
979
            $this->scale->SetVertSpacing($maxheight*(1+$this->iLabelVMarginFactor));
980
        }
981
        // If it hasn't been set find out the maximum line number
982
        if( $this->scale->iVertLines == -1 )
983
        	$this->scale->iVertLines = $this->GetBarMaxLineNumber()+1;
984
 
985
        $maxwidth=max($this->scale->actinfo->GetWidth($this->img),
986
        max($this->GetMaxLabelWidth(),
987
        $this->scale->tableTitle->GetWidth($this->img)));
988
 
989
        $this->scale->SetLabelWidth($maxwidth+$this->scale->divider->iWeight);//*(1+$this->iLabelHMarginFactor));
990
 
991
        if( !$_csim ) {
992
            $this->StrokePlotArea();
993
            if( $this->iIconDepth == DEPTH_BACK ) {
994
                $this->StrokeIcons();
995
            }
996
        }
997
 
998
        $this->scale->Stroke();
999
 
1000
        if( !$_csim ) {
1001
            // Due to a minor off by 1 bug we need to temporarily adjust the margin
1002
            $this->img->right_margin--;
1003
            $this->StrokePlotBox();
1004
            $this->img->right_margin++;
1005
        }
1006
 
1007
        // Stroke Grid line
1008
        $this->hgrid->Stroke($this->img,$this->scale);
1009
 
1010
        $n = count($this->iObj);
1011
        for($i=0; $i < $n; ++$i) {
1012
            //$this->iObj[$i]->SetLabelLeftMargin(round($maxwidth*$this->iLabelHMarginFactor/2));
1013
            $this->iObj[$i]->Stroke($this->img,$this->scale);
1014
        }
1015
 
1016
        $this->StrokeTitles();
1017
 
1018
        if( !$_csim ) {
1019
            $this->StrokeConstrains();
1020
            $this->footer->Stroke($this->img);
1021
 
1022
 
1023
            if( $this->iIconDepth == DEPTH_FRONT) {
1024
                $this->StrokeIcons();
1025
            }
1026
 
1027
            // Stroke all added user texts
1028
            $this->StrokeTexts();
1029
 
1030
            // Should we do any final image transformation
1031
            if( $this->iImgTrans ) {
1032
                if( !class_exists('ImgTrans',false) ) {
1033
                    require_once('jpgraph_imgtrans.php');
1034
                }
1035
 
1036
                $tform = new ImgTrans($this->img->img);
1037
                $this->img->img = $tform->Skew3D($this->iImgTransHorizon,$this->iImgTransSkewDist,
1038
                $this->iImgTransDirection,$this->iImgTransHighQ,
1039
                $this->iImgTransMinSize,$this->iImgTransFillColor,
1040
                $this->iImgTransBorder);
1041
            }
1042
 
1043
 
1044
            // If the filename is given as the special "__handle"
1045
            // then the image handler is returned and the image is NOT
1046
            // streamed back
1047
            if( $aStrokeFileName == _IMG_HANDLER ) {
1048
                return $this->img->img;
1049
            }
1050
            else {
1051
                // Finally stream the generated picture
1052
                $this->cache->PutAndStream($this->img,$this->cache_name,$this->inline,
1053
                $aStrokeFileName);
1054
            }
1055
        }
1056
    }
1057
 
1058
    function StrokeConstrains() {
1059
        $n = count($this->iObj);
1060
 
1061
        // Stroke all constrains
1062
        for($i=0; $i < $n; ++$i) {
1063
 
1064
            // Some gantt objects may not have constraints associated with them
1065
            // for example we can add IconPlots which doesn't have this property.
1066
            if( empty($this->iObj[$i]->constraints) ) continue;
1067
 
1068
            $numConstrains = count($this->iObj[$i]->constraints);
1069
 
1070
            for( $k = 0; $k < $numConstrains; $k++ ) {
1071
                $vpos = $this->iObj[$i]->constraints[$k]->iConstrainRow;
1072
                if( $vpos >= 0 ) {
1073
                    $c1 = $this->iObj[$i]->iConstrainPos;
1074
 
1075
                    // Find out which object is on the target row
1076
                    $targetobj = -1;
1077
                    for( $j=0; $j < $n && $targetobj == -1; ++$j ) {
1078
                        if( $this->iObj[$j]->iVPos == $vpos ) {
1079
                            $targetobj = $j;
1080
                        }
1081
                    }
1082
                    if( $targetobj == -1 ) {
1083
                        JpGraphError::RaiseL(6008,$this->iObj[$i]->iVPos,$vpos);
1084
                        //('You have specifed a constrain from row='.$this->iObj[$i]->iVPos.' to row='.$vpos.' which does not have any activity.');
1085
                    }
1086
                    $c2 = $this->iObj[$targetobj]->iConstrainPos;
1087
                    if( count($c1) == 4 && count($c2 ) == 4) {
1088
                        switch( $this->iObj[$i]->constraints[$k]->iConstrainType ) {
1089
                            case CONSTRAIN_ENDSTART:
1090
                                if( $c1[1] < $c2[1] ) {
1091
                                    $link = new GanttLink($c1[2],$c1[3],$c2[0],$c2[1]);
1092
                                }
1093
                                else {
1094
                                    $link = new GanttLink($c1[2],$c1[1],$c2[0],$c2[3]);
1095
                                }
1096
                                $link->SetPath(3);
1097
                                break;
1098
                            case CONSTRAIN_STARTEND:
1099
                                if( $c1[1] < $c2[1] ) {
1100
                                    $link = new GanttLink($c1[0],$c1[3],$c2[2],$c2[1]);
1101
                                }
1102
                                else {
1103
                                    $link = new GanttLink($c1[0],$c1[1],$c2[2],$c2[3]);
1104
                                }
1105
                                $link->SetPath(0);
1106
                                break;
1107
                            case CONSTRAIN_ENDEND:
1108
                                if( $c1[1] < $c2[1] ) {
1109
                                    $link = new GanttLink($c1[2],$c1[3],$c2[2],$c2[1]);
1110
                                }
1111
                                else {
1112
                                    $link = new GanttLink($c1[2],$c1[1],$c2[2],$c2[3]);
1113
                                }
1114
                                $link->SetPath(1);
1115
                                break;
1116
                            case CONSTRAIN_STARTSTART:
1117
                                if( $c1[1] < $c2[1] ) {
1118
                                    $link = new GanttLink($c1[0],$c1[3],$c2[0],$c2[1]);
1119
                                }
1120
                                else {
1121
                                    $link = new GanttLink($c1[0],$c1[1],$c2[0],$c2[3]);
1122
                                }
1123
                                $link->SetPath(3);
1124
                                break;
1125
                            default:
1126
                                JpGraphError::RaiseL(6009,$this->iObj[$i]->iVPos,$vpos);
1127
                                //('Unknown constrain type specified from row='.$this->iObj[$i]->iVPos.' to row='.$vpos);
1128
                                break;
1129
                        }
1130
 
1131
                        $link->SetColor($this->iObj[$i]->constraints[$k]->iConstrainColor);
1132
                        $link->SetArrow($this->iObj[$i]->constraints[$k]->iConstrainArrowSize,
1133
                        $this->iObj[$i]->constraints[$k]->iConstrainArrowType);
1134
 
1135
                        $link->Stroke($this->img);
1136
                    }
1137
                }
1138
            }
1139
        }
1140
    }
1141
 
1142
    function GetCSIMAreas() {
1143
        if( !$this->iHasStroked )
1144
        $this->Stroke(_CSIM_SPECIALFILE);
1145
 
1146
        $csim = $this->title->GetCSIMAreas();
1147
        $csim .= $this->subtitle->GetCSIMAreas();
1148
        $csim .= $this->subsubtitle->GetCSIMAreas();
1149
 
1150
        $n = count($this->iObj);
1151
        for( $i=$n-1; $i >= 0; --$i )
1152
        $csim .= $this->iObj[$i]->GetCSIMArea();
1153
        return $csim;
1154
    }
1155
}
1156
 
1157
//===================================================
1158
// CLASS PredefIcons
1159
// Description: Predefined icons for use with Gantt charts
1160
//===================================================
1161
define('GICON_WARNINGRED',0);
1162
define('GICON_TEXT',1);
1163
define('GICON_ENDCONS',2);
1164
define('GICON_MAIL',3);
1165
define('GICON_STARTCONS',4);
1166
define('GICON_CALC',5);
1167
define('GICON_MAGNIFIER',6);
1168
define('GICON_LOCK',7);
1169
define('GICON_STOP',8);
1170
define('GICON_WARNINGYELLOW',9);
1171
define('GICON_FOLDEROPEN',10);
1172
define('GICON_FOLDER',11);
1173
define('GICON_TEXTIMPORTANT',12);
1174
 
1175
class PredefIcons {
1176
    private $iBuiltinIcon = null, $iLen = -1 ;
1177
 
1178
    function GetLen() {
1179
        return $this->iLen ;
1180
    }
1181
 
1182
    function GetImg($aIdx) {
1183
        if( $aIdx < 0 || $aIdx >= $this->iLen ) {
1184
            JpGraphError::RaiseL(6010,$aIdx);
1185
            //('Illegal icon index for Gantt builtin icon ['.$aIdx.']');
1186
        }
1187
        return Image::CreateFromString(base64_decode($this->iBuiltinIcon[$aIdx][1]));
1188
    }
1189
 
1190
    function __construct() {
1191
        //==========================================================
1192
        // warning.png
1193
        //==========================================================
1194
        $this->iBuiltinIcon[0][0]= 1043 ;
1195
        $this->iBuiltinIcon[0][1]=
1196
     'iVBORw0KGgoAAAANSUhEUgAAACAAAAAgCAYAAABzenr0AAAABmJLR0QA/wD/AP+gvaeTAAAACXBIWXMAAAsSAAALEgHS3X78AAAA'.
1197
     'B3RJTUUH0wgKFSgilWPhUQAAA6BJREFUeNrtl91rHFUYh5/3zMx+Z5JNUoOamCZNaqTZ6IWIkqRiQWmi1IDetHfeiCiltgXBP8AL'.
1198
     '0SIUxf/AvfRSBS9EKILFFqyIH9CEmFZtPqrBJLs7c+b1YneT3WTTbNsUFPLCcAbmzPt73o9zzgzs2Z793231UOdv3w9k9Z2uzOdA'.
1199
     '5+2+79yNeL7Hl7hw7oeixRMZ6PJM26W18DNAm/Vh7lR8fqh97NmMF11es1iFpMATqdirwMNA/J4DpIzkr5YsAF1PO6gIMYHRdPwl'.
1200
     'oO2elmB+qH3sm7XozbkgYvy8SzYnZPtcblyM6I+5z3jQ+0vJfgpEu56BfI9vUkbyi2HZd1QJoeWRiAjBd4SDCW8SSAOy6wBHMzF7'.
1201
     'YdV2A+ROuvRPLfHoiSU0EMY/cDAIhxJeGngKaN1VgHyPL7NBxI1K9P4QxBzw3K1zJ/zkG8B9uwaQ7/HNsRZv9kohBGD0o7JqMYS/'.
1202
     '/ynPidQw/LrBiPBcS/yFCT95DvB2BWAy4575PaQbQKW+tPd3GCItu2odKI++YxiKu0d26oWmAD7paZU/rLz37VqIijD2YbnzNBBE'.
1203
     'IBHf8K8qjL7vYhCGErEU8CTg3xXAeMp96GrJEqkyXkm9Bhui1xfsunjdGhcYLq+IzjsGmBt5YH/cmJkFq6gIqlon3u4LxdKGuCIo'.
1204
     'Qu41g0E41po+2R33Xt5uz9kRIB2UTle7PnfKrROP1HD4sRjZlq0lzhwoZ6rDNeTi3nEg1si/7FT7kYQbXS6E5E65tA5uRF9tutq0'.
1205
     'K/VwAF+/FbIYWt6+tjQM/AqUms7A4Wy6d7YSfSNxgMmzi0ycWWworio4QJvj4LpuL5BqugTnXzzqJsJwurrlNhJXFaavW67NRw3F'.
1206
     'q+aJcCQVe9fzvJGmAY7/dPH0gi0f64OveGxa+usCuQMeZ0+kt8BVrX+qPO9Bzx0MgqBvs+a2PfDdYIf+WAjXU1ub4tqNaPPzRs8A'.
1207
     'blrli+WVn79cXn0cWKl+tGx7HLc7pu3CSmnfitL+l1UihAhwjFkPQev4K/fSABjBM8JCaFuurJU+rgW41SroA8aNMVNAFtgHJCsn'.
1208
     'XGy/58QVxAC9MccJtZ5kIzNlW440WrJ2ea4YPA9cAooA7i0A/gS+iqLoOpB1HOegqrYB3UBmJrAtQAJwpwPr1Ry92wVlgZsiYlW1'.
1209
     'uX1gU36dymgqYxJIJJNJT1W9QqHgNwFQBGYqo94OwHZQUuPD7ACglSvc+5n5T9m/wfJJX4U9qzEAAAAASUVORK5CYII=' ;
1210
 
1211
        //==========================================================
1212
        // edit.png
1213
        //==========================================================
1214
        $this->iBuiltinIcon[1][0]= 959 ;
1215
        $this->iBuiltinIcon[1][1]=
1216
     'iVBORw0KGgoAAAANSUhEUgAAABYAAAAWCAYAAADEtGw7AAAABGdBTUEAALGPC/xhBQAAAAZiS0dEAFgAWABY9j+ZuwAAAAlwSFlz'.
1217
     'AAALEAAACxABrSO9dQAAAAd0SU1FB9AKDAwbIEXOA6AAAAM8SURBVHicpdRPaBxlHMbx76ZvsmOTmm1dsEqQSIIsEmGVBAQjivEQ'.
1218
     'PAUJngpWsAWlBw8egpQepKwplN4ULEG9CjkEyUFKlSJrWTG0IU51pCsdYW2ncUPjdtp9Z+f3vuNhu8nKbmhaf5cZeGc+PO8zf1Lc'.
1219
     'm0KhkACICCKCMeaBjiLC0tLSnjNvPmuOHRpH0TZTU1M8zBi9wakzn7OFTs5sw8YYACYmJrre7HkeuVyu69qPF77hlT1XmZ0eQ03O'.
1220
     'wOLJTvhBx1rLz18VmJ0eY+jVd2FxDkKXnvYLHgb97OgLzE4ON9Hzc1B1QaQzsed5O0Lta3Ec89OnR5h5McfQ+Mw2qgQUnfBOPbZ3'.
1221
     'bK3l+xOvMT0+3ERLp5FNF6UEjcL32+DdVmGt5WLhDYYPZrbRqreFumXwql0S3w9tnDvLWD5PZigPpdOwuYpSCo3C8wU3UHxQdHbf'.
1222
     'cZIkNM6dxcnlUM4k1eUFMlUPpUADbpkttFarHe6oYqeOr6yt4RzMQHYUcUsQVtGicHDwKprViuLDkkOtVnsHCHZVRVy/zcj1i5Af'.
1223
     'h8AjdIts+hUcGcYPK3iBtKM3gD/uAzf/AdY2mmmVgy6X8YNNKmGIvyloPcB8SUin07RQ4EZHFdsdG0wkJEnEaHAJxvKEpSLeaokV'.
1224
     'r4zWmhUZYLlY4b1D03y5eIEWCtS7vsciAgiIxkQRabWOrlQor66y4pUphoJb1jiO4uO5o0S3q6RSqVbiOmC7VCEgAhLSaDQ48dH7'.
1225
     'vD46REY0iysegSjKQciRt99ib7qXwX0O+pG4teM6YKHLB9JMq4mTmF9/+AKA4wvLZByH7OgYL7+UY2qvw/7Bfg5kHiXjJFyv3CGO'.
1226
     'Y1rof+BW4t/XLiPG0DCGr79d4XzRxRnIMn98huXSTYyJ6et1UNYQhRvcinpJq86H3wGPPPM0iBDd+QffD1g4eZjLvuG7S1Wef26E'.
1227
     'J7L7eSx7gAHVg7V3MSbi6m/r93baBd6qQjerAJg/9Ql/XrvG0ON1+vv7GH3qSfY5fahUnSTpwZgIEQesaVXRPbHRG/xyJSAxMYlp'.
1228
     'EOm71HUINiY7mGb95l/8jZCyQmJjMDGJjUmsdCROtZ0n/P/Z8v4Fs2MTUUf7vYoAAAAASUVORK5CYII=' ;
1229
 
1230
        //==========================================================
1231
        // endconstrain.png
1232
        //==========================================================
1233
        $this->iBuiltinIcon[2][0]= 666 ;
1234
        $this->iBuiltinIcon[2][1]=
1235
     'iVBORw0KGgoAAAANSUhEUgAAABYAAAAWCAYAAADEtGw7AAAABGdBTUEAALGPC/xhBQAAAAZiS0dEAP8A/wD/oL2nkwAAAAlwSFlz'.
1236
     'AAALDwAACw8BkvkDpQAAAAd0SU1FB9ALEREILkh0+eQAAAIXSURBVHictZU9aFNRFMd/N81HX77aptJUWmp1LHRpIcWhg5sIDlUQ'.
1237
     'LAXB4t7RRUpwEhy7iQ46CCIoSHcl0CFaoVARU2MFMYktadLXJNok7x2HtCExvuYFmnO4w/3gx+Gc/z1HKRTdMEdXqHbB/sgc/sic'.
1238
     'nDoYAI8XwDa8o1RMLT+2hAsigtTvbIGVqhX46szUifBGswUeCPgAGB7QeLk0X4Ork+HOxo1VgSqGASjMqkn8W4r4vVtEgI/RRQEL'.
1239
     'vaoGD85cl5V3nySR/S1mxWxab7f35PnntNyMJeRr9kCMqiHTy09EoeToLwggx6ymiMOD/VwcD7Oa/MHkcIiQx026WGYto5P/U+ZZ'.
1240
     '7gD0QwDuT5z9N3LrVPi0Xs543eQPKkRzaS54eviJIp4tMFQFMllAWN2qcRZHBnixNM8NYD162xq8u7ePSQ+GX2Pjwxc2dB2cLtB8'.
1241
     '7GgamCb0anBYBeChMtl8855CarclxU1gvViiUK4w2OMkNDnGeJ8bt9fH90yOnOkCwLFTwhzykhvtYzOWoBBbY//R3dbaNTYhf2RO'.
1242
     'QpeuUMzv188MlwuHy0H13HnE48UzMcL0WAtUHX8OxZHoG1URiFw7rnLLCswuSPD1ulze/iWjT2PSf+dBXRFtVVGIvzqph0pQL7VE'.
1243
     'avXYaXXxPwsnt0imdttCocMmZBdK7YU9D8wuNOW0nXc6QWzPsSa5naZ1beb9BbGB6dxGtMnXAAAAAElFTkSuQmCC' ;
1244
 
1245
        //==========================================================
1246
        // mail.png
1247
        //==========================================================
1248
        $this->iBuiltinIcon[3][0]= 1122 ;
1249
        $this->iBuiltinIcon[3][1]=
1250
     'iVBORw0KGgoAAAANSUhEUgAAABYAAAAWCAYAAADEtGw7AAAABGdBTUEAALGPC/xhBQAAAAZiS0dEAP8A/wD/oL2nkwAAAAlwSFlz'.
1251
     'AAALEAAACxABrSO9dQAAAAd0SU1FB9AJHAMfFvL9OU8AAAPfSURBVHictZRdaBRXFMd/987H7tbNx8aYtGCrEexDsOBDaKHFxirb'.
1252
     'h0qhsiY0ykppKq1osI99C4H2WSiFFMHWUhXBrjRi0uCmtSEUGgP1QWqhWjGkoW7M1kTX3WRn5p4+TJJNGolQ6IXDnDtz+N0z/3PP'.
1253
     'UWBIpdpYa23b9g09PZ2kUrOrvmUyGVKp1Ao/mUyi56YnVgWfO/P1CihAd/dJMpmaNROIRq8BkM1m0bH6TasC3j6QXgFdXI+DR6PR'.
1254
     'JX/Pno8B+KLnMKqlpUU8z8MYs2RBEDzWf9J+0RcRbMdxGBsbw/fmCXwPMUEYID4iAVp8wIRmDIHMo4yHSIBSASKC+CWE0C/PF9jU'.
1255
     '3B6Cp+4M07C5FUtKGNvGwQJctPgIsgD2wRhEIqAMGB+UQYkHJgYYZD7P1HwVlmWhHcfhyk83KeRGUW4t6CgoG5SNUS4KBWgQDUov'.
1256
     '7AGlwYASBVqH0Bk49dXpCviVV3dw/tI1Bvr7kMIIlh0NYUpjlF0BAYvcxSXmEVLKceHSCJm+PnbueBHbtkNwTXUNBzo6aGpq4sSZ'.
1257
     'GwT5H7BsF6Wdf1GWHQAoM0upeI9PT1yioS7B7tdaSdSuw7KsUGMAy7HYsmUztTW1nMwM0txssX1rlHjjS5jy/Uq2YkK/eJuLl6/z'.
1258
     'x+1xkslW6mrixGIODx8EFSlEBC0+tmXT0NhA2763iEUjnLv4C8XpUbSbAB1mKkGJ3J83Od77HW5EszvZSqK2iljMIeJaRGNuJePF'.
1259
     '6mspY7BJ1DXwQnCd2fxGRq5OUCz8xt72dyhMZcn++Cu3xu9SKhdp2b4ZHWnAtTSxmIWlhcIjlksR3lNBYzlxZsb7+f7ne+xtSzOd'.
1260
     'u83szH1OnThOPp/n+a0beeP1l4mvq+PU2Qyd+5PY1RuwlAqLYFaBfbTbyPSdfgaH77A//QF4f1O/vpr6RJyq+C5Kc/M8FbFxXItY'.
1261
     'xOHDrvfo/fxLDnbsJBp5BowBReVWYAzabeTh5ABDw7cWoNNL3YYYNtSv57lnn6Z+Qx01VeuIuBa2DV1HD3H63BAPZu4u1WGpeLHq'.
1262
     'Rh7+NcjA0O+0p4+CNwXigwnbWlQQdpuEpli+n+PIkcOc//YKuckJJFh2K2anrjFw+QZt6S6kPImIF/b+cqAJD1LihWAxC61twBTo'.
1263
     'fPcQF/oGsVW5ovHQlavs2/8+uYnRVSOUgHAmmAClBIOBwKC0gPjhIRgEIX2wg7NnwpZW3d3d4vs+vu8TBMGK51rvPM9b8hdteZxd'.
1264
     'LBbVR8feJDs0Rlv6GFKeXJ21rNRXESxMPR+CBUl0nN7PjtO+dye7Up/8v1I88bf/ixT/AO1/hZsqW+C6AAAAAElFTkSuQmCC' ;
1265
 
1266
        //==========================================================
1267
        // startconstrain.png
1268
        //==========================================================
1269
        $this->iBuiltinIcon[4][0]= 725 ;
1270
        $this->iBuiltinIcon[4][1]=
1271
     'iVBORw0KGgoAAAANSUhEUgAAABYAAAAWCAYAAADEtGw7AAAABGdBTUEAALGPC/xhBQAAAAZiS0dEAP8A/wD/oL2nkwAAAAlwSFlz'.
1272
     'AAALDgAACw4BQL7hQQAAAAd0SU1FB9ALEREICJp5fBkAAAJSSURBVHic3dS9a1NRGMfx77kxtS+xqS9FG6p1ER3qVJpBQUUc3CRU'.
1273
     'BwURVLB1EAuKIP0THJQiiNRJBK3iJl18AyeltRZa0bbaJMbUNmlNSm5e7s25j0NqpSSmyag/OMM9POdzDuflwn8djz8gClVRrVEV'.
1274
     'ur4Bl1FTNSzLrSS6vbml0jUUwSXj8Qfk3PkLtLW2AeBIybmrgz3+gFzpucjlE4f4btuFTuWuCF5XDr3a3UPf6cM8GQvxzbsRAJdh'.
1275
     'ScfxSywml5j7mVypN0eGEJ0tebIre+zxB6Tv7jPReS2hREpOvpmUXU+H5eC913JnNCSRVE60pUVbWoZjprR39Yq70bdqj4pW7PEH'.
1276
     '5FpvL9e79jOTTHM7ssDL6CJZ08LbvAGnrpZg2mI2Z/MlZfN8IkxuSwu4V9+WIrj7zFlOHfXzKrLIi2SGh5ECKjnNVNxkQEc55vOw'.
1277
     'rb6O8JLFdHyJ+ayFElUeHvjwkfteL/V7fKTSkFvIQE4DoLI2Mz/muTkTApcBKIwaN8pwIUrKw+ajWwDknAO0d/r4zFaMuRS63sWm'.
1278
     'RoOdm+vRIriUYjKexrQV+t1o0YEVwfZSVJmD/dIABJuO0LG3lRFx0GOfiAELE9OgCrfU0XnIp5FwGLEy5WEAOxlR5uN+ARhP7GN3'.
1279
     '5w7Gv4bQI2+xpt4jjv2nWBmIlcExE2vDAHYioszBZXw6CPE4ADoWVHmd/tuwlZR9eXYyoszBfpiNQqaAOU5+TXRN+DeeenADPT9b'.
1280
     'EVgKVsutKPl0TGWGhwofoquaoKK4apsq/tH/e/kFwBMXLgAEKK4AAAAASUVORK5CYII=' ;
1281
 
1282
        //==========================================================
1283
        // calc.png
1284
        //==========================================================
1285
        $this->iBuiltinIcon[5][0]= 589 ;
1286
        $this->iBuiltinIcon[5][1]=
1287
     'iVBORw0KGgoAAAANSUhEUgAAABYAAAAWCAYAAADEtGw7AAAABGdBTUEAALGPC/xhBQAAAAZiS0dEAA4AIwBbgMF12wAAAAlwSFlz'.
1288
     'AAALEQAACxEBf2RfkQAAAAd0SU1FB9AHBxQeFsqn0wQAAAHKSURBVHicnZWff+RAGIef3U/gcOEgUAgUCgcLhYXCwsHBQeGgUDgs'.
1289
     'FgMHB4VA/4Bg4XChWFgIFIqBwkJhsRAYeOGF+TQHmWSTTbKd9pU37/x45jvfTDITXEynAbdWKVQB0NazcVm0alcL4rJaRVzm+w/e'.
1290
     '3iwAkzbYRcnnYgI04GCvsxxSPabYaEdt2Ra6D0atcvvvDmyrMWBX1zPq2ircP/Tk98DiJtjV/fim6ziOCL6dDHZNhxQ3arIMsox4'.
1291
     'vejleL2Ay9+jaw6A+4OSICG2cacGKhsGxg+CxeqAQS0Y7BYJvowq7iGMOhXHEfzpvpQkA9bLKgOgWKt+4Lo1mM9hs9m17QNsJ70P'.
1292
     'Fjc/O52joogoX8MZKiBiAFxd9Z1vcj9wfSpUlDRNMcYQxzFpmnJ0FPH8nDe1MQaWSz9woQpWSZKEojDkeaWoKAyr1tlu+s48wfVx'.
1293
     'u7n5i7jthmGIiEGcT+36PP+gFeJrxWLhb0UA/lb4ggGs1T0rZs0zwM/ZjNfilcIY5tutPxgOW3F6dUX464LrKILLiw+A7WErrl+2'.
1294
     'rABG1EL/BilZP8DjU2uR4U+2E49P1Z8QJmNXUzl24A9GBT0IruCfi86d9x+D12RGzt+pNAAAAABJRU5ErkJggg==' ;
1295
 
1296
        //==========================================================
1297
        // mag.png
1298
        //==========================================================
1299
        $this->iBuiltinIcon[6][0]= 1415 ;
1300
        $this->iBuiltinIcon[6][1]=
1301
     'iVBORw0KGgoAAAANSUhEUgAAABYAAAAWCAYAAADEtGw7AAAABGdBTUEAALGPC/xhBQAAAAZiS0dEAP8A/wD/oL2nkwAAAAlwSFlz'.
1302
     'AAALDAAACwwBP0AiyAAAAAd0SU1FB9ALDxEWDY6Ul+UAAAUESURBVHicdZVrbFRFGIafsyyF0nalV1R6WiggaAptlzsr1OgEogmC'.
1303
     '0IgoBAsBgkIrBAPEhBj/AP6xRTCUFEwRI4jcgsitXMrFCJptJWvBNpXYbbXtbtttt6e7e86ec/yxadlCfZPJZDIz73zzzjfvR2VL'.
1304
     'F7U+hf0HD2JduIzTFy6SlJRkPtkcDgdCCE65OxFC8NPV6wghyM7OptankJ2dzbSC5QghEEIgCSHog9PpNAF27dlN6miZuPgElB4/'.
1305
     'nmY3O7ZtByA1NVUCkGWZweD1eklJScESTbqxuIjrd+/x6uIl5M19hSy7nfGOeUxf+g7VjU1sKi7C4/GYsiyz7tAJAD4/cRaA1tZW'.
1306
     'AHIPnECUVGD1+/3U19ebG4uLeHf1akamjsIwoVnVCOvQEdLoVILYYmMo3PIxSBJflpSaDX5FAmju1QAYv/8k/s8+wLVxOU0jR2LZ'.
1307
     '8sMFAApWrCApbRRDrRZirBYSLBKaoRPQw3SFernf2sav7T0Ubt4KwL4FMwF4Vu8FoHBCKgCzDhwHwLIhZ7y5a89u4m2JhA0wTdDC'.
1308
     'OrphEjJMNElCHxKDEjaobmvlfo/Krj27CQQCJsCGJW8C0KXqAMxMiosQA8hZWcTFx9OsaniDKh1qmG7VoFsL0x0K06kbeAMhWpRe'.
1309
     '/KpG+gwHAKUnz7Dz3BUMw6DK18nuw99wt0Nh6VdHI8RJicmETQgFg7SFwjSrGv+oKp6ghldV6dZ0ugJBlF6FmCESQ2w2AIqXLsan'.
1310
     'BrFYLJTnTCBrdBqveeopWZiPFaBHUegJhegMqGgxEkHDwB/UaQ9rdIV06v0+TD2EEQjQFtAY0dsNgNvt5sialQAIIXh7wQKuVf6J'.
1311
     'gTsSccPDWlQstClBGjr9eHpVWvUQncEwdYEedF8noQ4vmYmpZMTH0nTvDn25vLbrNmu7bvfnsYEbAMnhcPDgwQPzUo2LJusw/mhp'.
1312
     'QwlHNO0KBAnoIfxtrcQMT2De1Mm891wyUzNlUlJSpIyMDBobGzlzr5rFM/Koq6vrP8ASGxsLwPmKcvIShjPGZiPOakE3VFB8hHwd'.
1313
     'vJAxhrk5L7Ly+RQuH/sWgPdXrwFg/6HDFBUsIj09nehfbAWwPWOT9n5RYhqGwarNWxkRM5TRCfF4U1PQsDDJFk9uYhwXvzvKjm3b'.
1314
     'KSsro3DJInNW5RXp7u2bAKSlpeH1esnPz6eqqgqLpmmcr3Fht9ulfaV7mZk1Bs+lM6T1djM9fhg5egDPpTNMy5TZsW07kydPYdWM'.
1315
     'aXx96ixOp9O8cfUa80srmDpjOgAulytiQqZpMnvObLbt/JTtHxXj9/tRVdU0DGOAufRpevPDTeac0hJyc3NxOOawfv161lVWS6eX'.
1316
     'z+9/UOCxu1VWVvaTRGv16NFfjB2bNeAQp9NpTpmSM4DcbrdL0WsGDKLRR+52uwe1yP8jb2lpYfikyY9t80n03UCWZeaXVjw1f+zs'.
1317
     'Oen+/d+pqanhzp2fKSsrw+l0mi6XiyPl5ZGITdN8fAVJwjRNJEmi1qfw1kw7siyTnJxMe3s71dXV3GpoZO64DG41NPJylvxU5D/e'.
1318
     'qJKsfWQD9IkaZ2RmUvr9aV4aGYcQgjfO3aWoYBF5eXm4ewIsu/CbdPz1aWb0/p1bNoOrQxlUiuiaFo3c3FyEEOx9+C9CCD6paaTW'.
1319
     'p/TXyYkTJ0Xe59jf7QOyAKDWp/QXxcFQ61P4pT3ShBBcvnUHIQTjxmX19/8BCeVg+/GPpskAAAAASUVORK5CYII=' ;
1320
 
1321
        //==========================================================
1322
        // lock.png
1323
        //==========================================================
1324
        $this->iBuiltinIcon[7][0]= 963 ;
1325
        $this->iBuiltinIcon[7][1]=
1326
     'iVBORw0KGgoAAAANSUhEUgAAABYAAAAWCAYAAADEtGw7AAAABGdBTUEAALGPC/xhBQAAAAZiS0dEAAAAAAAA+UO7fwAAAAlwSFlz'.
1327
     'AAALCwAACwsBbQSEtwAAAAd0SU1FB9AKAw0XDmwMOwIAAANASURBVHic7ZXfS1t3GMY/3+PprI7aisvo2YU6h6ATA8JW4rrlsF4U'.
1328
     'qiAsF9mhl0N2cYTRy9G/wptAYWPD9iJtRy5asDe7cYFmyjaXOLaMImOrmkRrjL9yTmIS3120JybWQgfb3R74wuc8Lzw858vLOUpE'.
1329
     'OK6pqSm2trbY39+nu7tbPHYch7m5OcLhMIA67kWj0aMQEWk6tm17rNm2LSIie3t7ksvlJJ1OSyqVkls3Z8SyLMnlcqTTaVKpFLdu'.
1330
     'zmBZVj1HeY2VUti2TSQSQSml2bZdi0QirK2tMT09zerqKtlslqGhISYnJ4nHv2N+foFsNquOe9FotLlxOBwmk8lgWRbhcFgymYxY'.
1331
     'liUi0mqaJoAuIi2macrdO7fFsizx3to0Te7euV1vrXtXEgqFmJmZYWVlhXK5LB4/U9kwDL784kYV0A3DYHd3m4sXRymXywKoRi8U'.
1332
     'Ch01DgQCJBIJLMsiEAhIIpHw2uLz+eqtYrEYIqKZpimxWEyCwaCMjY01zYPBIJpXqVQqsby8TLVabWKA/v5+RkZGMAyDrq4ulFKH'.
1333
     'HsfjcWZnZ+ns7KTRqwcnk0mKxSKFQqGJlVKtruuSTCYB6O3trW9UI/v9/iZPB/j8s2HOnX0FgHfeXpeffnzK+fWf+fijvhLs0PtG'.
1334
     'D/n1OJ9+MsrlSwb3733DwMCAt1EyPj6uACYmJp56168NU6nUqFSE9nZdPE7+WqC/r4NKTagcCJVqDaUUB5VDAA4Pa9x7sMLlSwan'.
1335
     'WjRmv13D7/erpaWlo604qOp88OF7LC48rPNosMq5Th+Dgxd4/XyA1rbzADi7j8jnf2P++wdcvSr8MJ/i8eomAKlUqn41OsDAQDeD'.
1336
     'g++yuPCwzm/2vU8+n2a7sMFfj79mp7BBuVzioFSiXHJx3SKuW2Rzy0Up9dxnQVvODALQerqNRn4ZKe0Mvtc6TpzpmqbxalcY9Ato'.
1337
     '2v06t515C73YQftZB9GLnDrt4LoujuPgOA4Ui+C6yOpXJwZrJ7r/gv4P/u+D9W7fLxTz+1ScQxrZ3atRLaVxdjbY2d184R6/sLHe'.
1338
     'opHP7/Do90Ua+WWUyezzZHObP/7cfX54/dowE1d66s8TV3oE+Mfn+L/zb4XmHPjRG9YjAAAAAElFTkSuQmCC' ;
1339
 
1340
        //==========================================================
1341
        // stop.png
1342
        //==========================================================
1343
        $this->iBuiltinIcon[8][0]= 889 ;
1344
        $this->iBuiltinIcon[8][1]=
1345
     'iVBORw0KGgoAAAANSUhEUgAAABYAAAAWCAYAAADEtGw7AAAABGdBTUEAALGPC/xhBQAAAAZiS0dEAAAAAAAA+UO7fwAAAAlwSFlz'.
1346
     'AAALDwAACw8BkvkDpQAAAAd0SU1FB9AJDwEvNyD6M/0AAAL2SURBVHic1ZTLaxVnGIefb2bO5OScHJN4oWrFNqcUJYoUEgU3/Qf6'.
1347
     'F7gwCkIrvdBLUtqqiLhSg9bgBduFSHZdiG5ctkJ3xRDbUFwUmghNzBDanPGMkzOX79LFJGPMOSd204U/+Bbzvd/78F4H/ieJdoad'.
1348
     'pZKxRFszAI/DcP0HazXY22v+HB01kee1PA/v3zfnjx4xgGnHcNZe7OvuNj+cOEF1ZATv5nUA4jhBSgmADCVWo8Ge2Of9wb18P/G7'.
1349
     'oUXmYi30zqlTVEdGWLh1g2D6MYlKkXGE0Vl8aa2GEB149+4xXSzyoOIw/mimiZV/DPb25pFOj13A9gOMEChhUEqhVYqWKUk9QAUp'.
1350
     'sT/P4s8PmKlUmNhQaIJbkDVqBbpw6wZ2zUc4Nm+ePku5p4eOrgpueQOFUoVCVxcD4+N07dpF9+5tVJeWGPBjhvr7WF1zC8ASgtcP'.
1351
     'H8a7eZ1odh4sh50nzwCw9ZNh3M4Stutiu0X2nB/LyjZ6lcIbVTpdQU/jWVPzLADM8+ZGBRdtC7wrF/O7bR99iu26VL86iU4SAH4b'.
1352
     'Po5d6AQhstMSvGyI4wS5FJBKSRwnzF8byx/u+PjzzMF1mfryQ1K/jnCahqp1xEopjFLoNEFJSRJHzF799gWHqa+/QKcSUXBI609f'.
1353
     'Al5W4teQSiHDOipNUKnMI13RvnOXAIEKQixvGWya98SC560MFwPiqEG86JM8q79Q06lvhnOndy5/B6GPCUOMUu3BQgg8z0M3GmBZ'.
1354
     'iGJn3v2VmsqnfzNx7FDueODuj8ROCFpjtG5TCmOYv32bJ09msP0ISydMfnAUgF8/O45RAA6WTPjlvXcB+Gn7FuRf/zAnNX6x3ARe'.
1355
     'PSdmqL+P/YHkwMGDOGWDZTlQcNBRhPEComgB/YeHfq2InF1kLlXUOkpMbio1bd7aATRD/X0M1lPeSlM2vt2X1XBZjZnpLG2tmZO6'.
1356
     'LbQVOIcP+HG2UauH3xgwBqOz9Cc3l1tC24Fz+MvUDroeGNb5if9H/1dM/wLPCYMw9fryKgAAAABJRU5ErkJggg==' ;
1357
 
1358
        //==========================================================
1359
        // error.png
1360
        //==========================================================
1361
        $this->iBuiltinIcon[9][0]= 541 ;
1362
        $this->iBuiltinIcon[9][1]=
1363
     'iVBORw0KGgoAAAANSUhEUgAAACgAAAAoCAMAAAC7IEhfAAAAaVBMVEX//////2Xy8mLl5V/Z2VvMzFi/v1WyslKlpU+ZmUyMjEh/'.
1364
     'f0VyckJlZT9YWDxMTDjAwMDy8sLl5bnY2K/MzKW/v5yyspKlpYiYmH+MjHY/PzV/f2xycmJlZVlZWU9MTEXY2Ms/PzwyMjLFTjea'.
1365
     'AAAAAXRSTlMAQObYZgAAAAFiS0dEAIgFHUgAAAAJcEhZcwAACxIAAAsSAdLdfvwAAAAHdElNRQfTCAkUMSj9wWSOAAABLUlEQVR4'.
1366
     '2s2U3ZKCMAxGjfzJanFAXFkUle/9H9JUKA1gKTN7Yy6YMjl+kNPK5rlZVSuxf1ZRnlZxFYAm93NnIKvR+MEHUgqBXx93wZGIUrSe'.
1367
     'h+ctEgbpiMo3iQ4kioHCGxir/ZYUbr7AgPXs9bX0BCYM8vN/cPe8oQYzom3tVsSBMVHEoOJ5dm5F1RsIe9CtqGgRacCAkUvRtevT'.
1368
     'e2pd6vOWF+gCuc/brcuhyARakBU9FgK5bUBWdHEH8tHpDsZnRTZQGzdLVvQ3CzyYZiTAmSIODEwzFCAdJopuvbpeZDisJ4pKEcjD'.
1369
     'ijWPJhU1MjCo9dkYfiUVjQNTDKY6CVbR6A0niUSZjRwFanR0l9i/TyvGnFdqwStq5axMfDbyBksld/FUumvxS/Bd9VyJvQDWiiMx'.
1370
     'iOsCHgAAAABJRU5ErkJggg==' ;
1371
 
1372
        //==========================================================
1373
        // openfolder.png
1374
        //==========================================================
1375
        $this->iBuiltinIcon[10][0]= 2040 ;
1376
        $this->iBuiltinIcon[10][1]=
1377
     'iVBORw0KGgoAAAANSUhEUgAAACAAAAAgCAYAAABzenr0AAAABGdBTUEAALGPC/xhBQAAAAZiS0dEANAAtwClFht71AAAAAlwSFlz'.
1378
     'AAALEAAACxABrSO9dQAAAAd0SU1FB9AKDQ4RIXMeaLcAAAd1SURBVHicxZd7jBXVHcc/58zcvTNzH8vusqw8FsTsKiCUUh5WBZXG'.
1379
     'GkOptmqwNWsWLKXFGlEpzZI0AWNKSy0WhDS22gJKtWlTsSRqzYIuLGB2WVvDIwQMZQMsy2OFfdzde+/OnHP6x907vJaFpjb9JZM5'.
1380
     'c85Mfp/f9/s7Jxn4P4e41gtSyp78WGvtfdEAcqDFYUOH9HS0NhGk9tPb/ilSyp789UUB2AMuqhQy3Uzm7HGkE6W3dTNZMRI3EcWO'.
1381
     'jf9ClLmWBT3dzW8jUsevWHCG3UpWl+IkHSxnbDh/Mcz12NevBcuWXTmf6TjnXvJ88gDmVB3pw3+nt3UzHa1NqMzBS2zqPLGFjtMN'.
1382
     'ZNr3XdW+qyqwZcFk76HX/tHWfuQvyO4W7qhaHwL8efkMRlRUpPv7rqD0RrJ+FgAjLy1a20OIxZJEEuNCRfIApj+om4bGM3u2/sYU'.
1383
     '9J41d8973f3Dhg1pISTV1dXXBRNJxPGFCzhou+DCQrScZOkktNaeDZjamgeZ9MgiYmVDccvHhjAzJw0NTh8/alyZMaVJicp0iTHj'.
1384
     'JpgNv38tjWUhhGROdbUL9W5/MH5XCkjlcibi+KIop5LVHLKEu8A/f4r286doa9pGrGwYAAsfqbbH3b8MgO/Nqgy6WvdbbXHMkEFJ'.
1385
     '4xUOMVEvaTZu3BgmvF4Yk4hz9rO/Ulr5cE9owae/rcGxohSOuiWkC2IjcIqKyPZm+OmCH7GhoZEF077EEzVVweAbJ+riEeO0Ey8y'.
1386
     'UubqOHn0AOgMwvf59txnBrSp9dgxKmf/+kIP1NY8SFk0jh5ajmNHAWg5b2E5EexojGHjbiVRMoRMNs0LC+Yz46vTuH3enN7BI8fr'.
1387
     'qFdo0BoVZNC9aVSQ4fNjBzEmQJiARxb+/AqYPMAVB5FsPU5v37g9OxgLhe14ZM5/ju052E6MNZvf5pmHHuLmmWOkEysxUtpGAtme'.
1388
     'dtHTflJkezqQto3jFRnLssyf1jydxiiM7zNnye/c3ZsqLu2BN5fcMfzrv/hby1tPzmRUoihcTJ87CwQI2yLtDcIqsIjYUf51qBlf'.
1389
     'OnScOSrdQUOMURkiXsLUzJnvbGhoBGDHH5cGyZLhOpYoNl5hqYnYEXOu5fDl9eYAHntx98n8hFHZcPHUuTSxSASAeK/CGIOxJJ0f'.
1390
     'bOGNPU280dgkq6Y2yu8vfjCIlwwzr+/ZQ/PHO0gOLuO5qsftDQ2NbN+4OCgqG6WTxWVaq6zpF+DiSHWnicdylp3r6aZTWthIOrNp'.
1391
     'ktHcvBu0sHX1Sm6ozB3B42d90zZA9bQp7PvgPSzXZfnqX/HS4DKKK2+x69Y/HURs26iBAN5ccsfw7774UcumF37C6f07KSt2OHji'.
1392
     'DEUJD0tISjyPrrSPlAKvN0JP/U4O1NfjuhG2rvklN1SOpfXwftpbTqAyKRrff5fb7rs9V1R7m4wlz2ihA3HpmXflUWyOH2umpLiY'.
1393
     'ui3v8M+6bWzfsRNbSgqkxaCkiy0simMuEWEhpcRzIhQWOIAh6tiAwS4owInFiTou5dOnMnl2NR++ujBwXEc9terD6M43nrj6LgAB'.
1394
     'QnDPA9/irtkP8JRS7Hr/3T6YekDQ1pEiEXOwpUVJzCVlZZFS4mZtkpEo9ChAkDp/jtLMBACy6S4RiQghLyv5cgBRPnKUOX6smUGF'.
1395
     'hSil0MYw9d77mPy1e5mnFE3batm3czvb6nYgEJztSFGU9LCRlMRdUjIH0+lnEMIwPNXD3NumoVJnrMCJaiciMUZfvQnz4QcBSvV1'.
1396
     'vjE5GK358t0zmXDnDB79saLpo20c+aSRD+t25JTp7GZQwsEWFiVxl6hlUf/WO9z32CxmL1rOe6u/I2KuwGhzLQCB7/sYY9Bah3el'.
1397
     'FKbvrrVm4vS7GH/7ncx+chEHGz7myCeNbPtoO0JI2jq78WIRLGkzsqs7V5SfFV5EovXACoiqqsfNpk2vo5VCWtYFBfoU0VoTBAFa'.
1398
     'a7TRaK2p+MoURk+cxMzq+Rzbv49DDbuo27UTW9h0dedssPxuK+kIfN8XxhgDYPVXf2Fh4XKtFIl4AiklAlBKAYRKKK36wHIweTCt'.
1399
     'NfHiEkaOn8j0+7/BmDFjaT30GbHywSxcuZkpFfFg+m1jjZ/NmnVvNfRvwd69e8WBA/uNFAIh4JVXXmHsmDHE4vEQQgjQ2lxQIm9N'.
1400
     'nz35q3BEOZOHzaG2thaA4mRU+L29It+IV21CpbRQfeMFC35gRB/M2rVrubnyZmLxWJhECBEmz/eHyo/7lMlH3LFFujsthNFCCGOu'.
1401
     '+WNyeUgpjSVzMKtWraKyshLPdcPEeYWCIEBdpIxSivr6eta8vI7d6+cGnhdV06pe1QP+F/QXWmuRL+jZZ58LlVmxYgUVFRV4rhtu'.
1402
     '4TzMxXAA6XRaRAtsYUkx8I/JtSJQOlSwpmZpCLN8+fPcdNNoHMfB9/0QJgRoP295TlR7UVv8xxZcHMuWIZ9/Hn35vG3JEGZpzVJG'.
1403
     'jx5N1IlitKahsZE1L69j69qHgx+urFX/lQL9JYdLlfnZihUhzOLFi8N3Ml1dthOxVH/f/8/CtqSJ2JaJ2JZ59J7RPsC/AViJsQS/'.
1404
     'dBntAAAAAElFTkSuQmCC' ;
1405
 
1406
        //==========================================================
1407
        // folder.png
1408
        //==========================================================
1409
        $this->iBuiltinIcon[11][0]= 1824 ;
1410
        $this->iBuiltinIcon[11][1]=
1411
     'iVBORw0KGgoAAAANSUhEUgAAACIAAAAiCAYAAAA6RwvCAAAABGdBTUEAALGPC/xhBQAAAAZiS0dEAAAAAAAA+UO7fwAAAAlwSFlz'.
1412
     'AAALEAAACxABrSO9dQAAAAd0SU1FB9ECAQgFFyd9cRUAAAadSURBVHiczdhvbBP3Hcfx9/2xfefEOA5JoCNNnIT8AdtZmYBETJsI'.
1413
     '6+jQOlQihT1AYgytqzZpD1atfyYqlT1h0lRpT7aRJ4NQpRvZGELVuo5Ua9jEJDIETQsNQyPBsUJMWGPnj//e+e72wNg4xElMR6ed'.
1414
     'ZNln3933dZ/f93f6yfB/sgmrHdDV1WXlPg8NDZUDScD8LFFFEZZlWYZhWMFg0Orq6sq/gDJAfFy1iiZy9OjrVnj4JzQ1rMWqfxm/'.
1415
     '309jYyNtbW0kEgnu3bvH4cOH88c/jqSKQl4/XGkd+eVtAN46up1LH92ktqYS++ZX8Pv9NDQ0sGnTJlKpFOFwmO7u7vy5IyMjeVRd'.
1416
     'XV1+WEOh0IrY4pDnq6wXX/sTiCJaMkFZdRNqxefoe7VtCSqXVDqdZnZ2ltraWkzTpKqqijt3JpFlG7dvj7NzZ1f++qFQyA3EClHL'.
1417
     'Ql743nFkhxPDtJAd5eTaYSVUfX09lZWVlJWVIUnSg7sVQMBCUcu4ceMGe/bsIRQK1QAzOcyykIM9P0KyudAyCWyqG8nhwqa4SkLt'.
1418
     '3r0bVVVxu924XC40TUOWZUQxe97CwgIdHR2LMHIxSCaVInVvFElxE0vMY1Pd2NUKJMWNTXHlUfF//4vETJCelwbpFm3MjP2dt37x'.
1419
     'AlN+PzU1NViWRSwW4+7du3g8HjweD4qi5EFAJzAExIpCANbooxhplfB0FJvTg6xWIqsVRVF6MopkU3FXPcnkJxGU0VEAdF2noqKC'.
1420
     'W3/8DpnqLjzep2lubsblcjE8PExHR8fboVDID9xYFpLBDpJF0jDQIncQpWlkm31FlFLtp9PfyuW/vYQj1kPSuRW/38+lj27S2Q7v'.
1421
     '/aWXUBVUffVNtm3blivVCEwsC5Eyc5iiApEpDEAXMqQdldhSiWVQHjJagud+8Fuexck/zv+K82dfoSbSCsDe75/km+4GVPd6+l5t'.
1422
     '4zJHcqVUYN2yEEtZQDCSJCueRAYsPY49HsFIZVG6p25JUumFafT4DKJN4amtT7Nz38sk5+5A70HMtEYyMkFiZhxzjQ/poXrLQrRU'.
1423
     'DFGEeFpAlkQkm4pRiCpIKodKzk0T/2QMh+piPjxKZPwiSkUtu/b9mNnJEWS7E8nhAmvpM60oJDkXJxqNozxRRUxPIesispBBlsXV'.
1424
     'UaKEFo8gzoaJhz8s2lOmrpUG+WBhJ9/60g+Z+fDXTAXfxllRjl1VkO0OFATsYhYliiK21ZKKhhHnFveUqSdKgwAEOp7F2v51vvw8'.
1425
     'XH7/N1wd/BlTweuUV65BdtgfoLTSkipsdD3tRi0VYpommUwGwzDwdT5HYEc3giAwcvH3jLz3BlPB67jWeZBEKYsSBWwpHZtNKo4q'.
1426
     'aHTDsJeeiGEYWJaFZVmYpommaRiGQdPnv0bb1m8gSRL/vPIOV979aR4lmAJ2p4qCgCxksNuKJ6VNpx4NYhgGpmkuQhmGQTqdxjAM'.
1427
     'qr2d7HtxEEEQuH1tkKvvvkF44tqDnrIcKJKAPf1g+LAUElq8dIiu60sApmnm93Pfzc7OYhgGrie+wFe++ztcLhcT1wf54PzPCU9c'.
1428
     'w7XWjWS3IdsdOAUBWZAxrRJnTQ6SG5bce2FCpmkughmGQSqVYm5uDtnj44sH38TtdhP6+Dwf//V4ttHXrkGURZJaic8RgHQ6jWma'.
1429
     'SJKUL5RLKNfIOczDKF3XSSaTRCIRhLJWntp3nGfWrSMxc5OLf3iNP4+68T9Ub9nF76lTpxgfHycajZJKpdA0LZ9GbjYV7hcDWZaF'.
1430
     'pmnMz88Ti8UYunSLmu1HFi2aVkxkaGjINTY2ttDb24vX6+XQoUNs3ryZ8vJyIDu1BUFYkkxhgxeiWlpaOHPmDE1NTdTX1xe98eWG'.
1431
     'JnF/9dQZCoXUYDA4AOD1ejlw4ACtra2Ul5fniwmCkEcUJiUIAoFAgL6+Pnw+H21tbfT39z8SxCS7hHsfWH9/8dL4MKqnp4eWlhac'.
1432
     'TmcekEvMNE2am5s5ceIEgUCA9vZ2Tp48ic/nY3j4UsmQHCYOjJHtpeBKqL1799Lc3IzT6UTXdRobGxkYGKC9vZ3W1tZ8Ko86NJ8a'.
1433
     'tXHjRo4dO8bp06fZsmULGzZsoL+/n0AggNfr5ezZs/8VpGTU5OSkc//+/acBfD4f1dXV7Nq1i4aGBs6dO4fP5+Pq1SuPBbIiyjTN'.
1434
     'RUnV1dUNXLhwAa/Xy44dO4jFYgBEo9FFF1r134BPuYlk16LrAYXsAlmtq6sbKDwoFAp9m+ykuP5ZQVZF3f8tCdwCov8LyHIoAANI'.
1435
     'AXf/A1TI0XCDh7OWAAAAAElFTkSuQmCC' ;
1436
 
1437
        //==========================================================
1438
        // file_important.png
1439
        //==========================================================
1440
        $this->iBuiltinIcon[12][0]= 1785 ;
1441
        $this->iBuiltinIcon[12][1]=
1442
     'iVBORw0KGgoAAAANSUhEUgAAACIAAAAiCAYAAAA6RwvCAAAABGdBTUEAALGPC/xhBQAAAAZiS0dEAAAAAAAA+UO7fwAAAAlwSFlz'.
1443
     'AAALDwAACw8BkvkDpQAAAAd0SU1FB9ECDAcjDeD3lKsAAAZ2SURBVHicrZhPaFzHHcc/897s7lutJCsr2VHsOHWMk0MPbsBUrcnF'.
1444
     'OFRdSo6FNhdB6SGHlpDmYtJCDyoxyKe6EBxKQkt7KKL0T6ABo0NbciqigtC6PhWKI2NFqqxdSd7V2/dmftPDvPd212t55dCBYfbN'.
1445
     'zpvfZ77z+/1mdhUjytWrV93Hf/24eD5z9gwiMlDjOKbb7dLtdhER2u02u7u73Lp1CxEZBw4AeZwdNQqkMd9wbziFGINJUt6rRbz5'.
1446
     '1ptUq1XK5TJBEAAUMHt7e+zu7gKwvLzMysoKwAng/uNg9CgQgFKlgg1DUJ67Vqtx6tQpZmdniaIIpRTOOZRSdDoddnZ2aLfbLC8v'.
1447
     's7S0xJUrV7ZGwQSj1PhhfRodVdDlMrpc5vup5Z2fvMPdu3fZ29vDWjvwztjYGPV6nVqtRqVS4dKlSywtLQFsAdOH2XwsCEApg3jl'.
1448
     'w98Rak2gvYjNZpNms0mSJDjnHgkDMDc3dySYQ0Ea8w139YUX0OUKulzyg7UmCEO+l1huvHuDra0t9vf3h1TJYSqVypFhHquIrlQI'.
1449
     'S5qv/uIDAC7/4bcEQYAKvK+0Wq1DVQGIoog7d+4cCeaRII35hrt+8SsEOkRlUaEyR0UpFIrXHxyMVKVUKnHv3r0jwRwaNelBjBjL'.
1450
     'Sz/7KYuLiwAsLi7y4z/9kY9e+TpkCuSqjI+Po7XuAWeKXLt2DWNMUZMkwRjDhQsXWFtbK6JpCCT3jfQgxomPtPX19YHWicM5x3c2'.
1451
     '73Pj3Ru8/aO3mZqaolKpoHVvyuvXr/Ppnf/Q7uzz380NPtu4y/qnG+ztd1hfX2dtbQ3gIvDnRyqSxl1UoPjyz98D4PTp0wPtq39Z'.
1452
     '4fdzLxegrVaLVqvF5OQkYRgWqpRKJZ77wvNsbW1RG5tgfKLOTH2G7Z1twqBQrgrMDvhInjfSOCY5iIv+hYWFgRZArEWsZWF941Bf'.
1453
     'SdMUgMnJCWpjVU4cn+HUyePM1Gc4+fRUPkzBI5w1jbukcczLv/5l0XfmzJmBFuCba38r/CRXpT+CrDUoZ0jjB4RYonJAOYRobJKT'.
1454
     'z5zgqfqxAbsFSH6mpHFM2qdGXh4VnoViD6mSJF2cTQeqDqBaKVHWmonJCWpZjhkC6anR5WsffTgwaHV1FaUUq6urA/2v3f5k4LnV'.
1455
     'arG9tUn3oI2YBCcWHYAxMVYs1qZEZY2SFB2aYZDGfMN9d7uJiWPSeFiNo5Rclc3NTXZbO6RpF7EJVixYA9agwwDnUiqlEPdQ3imi'.
1456
     'Jo27BGHIt/7x9yEjc3Nzh27Na7c/4TdffKl4bja3ae5MUIu0T/HOEIaOpJt4gwoSsVTK4SBIY77hFtY3ABBjBiZ90rKwvsH77/+K'.
1457
     't37wOhO1iPpTk4SBw1mLsz6CnKQ4l3qV+kE+t9XHlNZOk+bUJLVIE1VCcIJWQmJ6qjj30NbcXLkZMt8YPig+Z3n1G5fZ39/j/vY2'.
1458
     '9ckqZT2Ochbn0p4qNkU/dDfUADdXbh4HXgRO4zNdEU0XL1784PLly5w9e7Z4SazFOfGrEotDcOKrcoJPmrYIXf/Zop3QNd1skuGt'.
1459
     'cUAb2MgAxvHZTgFUq1Wmp6eZnZ0F8JlTjDduDThBnDeECEoJtbGIp6enqEblzCcEZ1PECU4yVRiOGgd0gc+AB0CZvkv1sWPHOHfu'.
1460
     'HOfPn8da41cpkkltEBEPJhYnBkTQJcdYVKGkgRxCfBsq5xXNgAa2Bn+hjTOgHEKBP8pzRUxykIH4ifLJRTJAl+UMBJzPHQ6bfe/f'.
1461
     'cWIzPxlUpD+zugzIZtVk1d8znBAqRxgoQuVQgSJQ3h9C5QhDRYgjUILCAzlnEdsHYTKfMTEBcP7F54YUGVmc2GLlIn6ve6v0ahSt'.
1462
     '8X25TzjJ+rIx1grKpQPWR4LkGVVsMgghvS0qjPdvm5OeceOTWA5Evo2mFzkjQfL7hZPUy5yvvF/uPFQL3+nbDmsLCEmT3sTmCTNr'.
1463
     'rogT6yFsOix3ftw7OwQhkvSU6CuinhCk0+kAkFoBazEEICHaHHiPVmU0gnUp4EAc1mYrF0EBVpwPi34VrBkwPxKk3W5ju/e5/c+d'.
1464
     'bGUHIAIuydTIE5zfc5Wr4lJcahHnHTP3CVGm78DrgY38N+DEibp7dmYKdAQmBh1hjEFjis+9CTWYGK21H6PxPyOI0DobYwzZF/z7'.
1465
     '7jadTvJtYG0kCD7lfwl49ijgT1gc0AH+dZSJA/xB+Mz/GSIvFoj/B7H1mAd8CO/zAAAAAElFTkSuQmCC' ;
1466
 
1467
        $this->iLen = count($this->iBuiltinIcon);
1468
    }
1469
}
1470
 
1471
//===================================================
1472
// Global cache for builtin images
1473
//===================================================
1474
$_gPredefIcons = new PredefIcons();
1475
 
1476
//===================================================
1477
// CLASS IconImage
1478
// Description: Holds properties for an icon image
1479
//===================================================
1480
class IconImage {
1481
    private $iGDImage=null;
1482
    private $iWidth,$iHeight;
1483
    private $ixalign='left',$iyalign='center';
1484
    private $iScale=1.0;
1485
 
1486
    function __construct($aIcon,$aScale=1) {
1487
        GLOBAL $_gPredefIcons ;
1488
        if( is_string($aIcon) ) {
1489
            $this->iGDImage = Graph::LoadBkgImage('',$aIcon);
1490
        }
1491
        elseif( is_integer($aIcon) ) {
1492
            // Builtin image
1493
            $this->iGDImage = $_gPredefIcons->GetImg($aIcon);
1494
        }
1495
        else {
1496
            JpGraphError::RaiseL(6011);
1497
            //('Argument to IconImage must be string or integer');
1498
        }
1499
        $this->iScale = $aScale;
1500
        $this->iWidth = Image::GetWidth($this->iGDImage);
1501
        $this->iHeight = Image::GetHeight($this->iGDImage);
1502
    }
1503
 
1504
    function GetWidth() {
1505
        return round($this->iScale*$this->iWidth);
1506
    }
1507
 
1508
    function GetHeight() {
1509
        return round($this->iScale*$this->iHeight);
1510
    }
1511
 
1512
    function SetAlign($aX='left',$aY='center') {
1513
        $this->ixalign = $aX;
1514
        $this->iyalign = $aY;
1515
    }
1516
 
1517
    function Stroke($aImg,$x,$y) {
1518
 
1519
        if( $this->ixalign == 'right' ) {
1520
            $x -= $this->iWidth;
1521
        }
1522
        elseif( $this->ixalign == 'center' ) {
1523
            $x -= round($this->iWidth/2*$this->iScale);
1524
        }
1525
 
1526
        if( $this->iyalign == 'bottom' ) {
1527
            $y -= $this->iHeight;
1528
        }
1529
        elseif( $this->iyalign == 'center' ) {
1530
            $y -= round($this->iHeight/2*$this->iScale);
1531
        }
1532
 
1533
        $aImg->Copy($this->iGDImage,
1534
        			$x,$y,0,0,
1535
        			round($this->iWidth*$this->iScale),round($this->iHeight*$this->iScale),
1536
        			$this->iWidth,$this->iHeight);
1537
    }
1538
}
1539
 
1540
 
1541
//===================================================
1542
// CLASS TextProperty
1543
// Description: Holds properties for a text
1544
//===================================================
1545
class TextProperty {
1546
    public $iShow=true;
1547
    public $csimtarget='',$csimwintarget='',$csimalt='';
1548
    private $iFFamily=FF_FONT1,$iFStyle=FS_NORMAL,$iFSize=10;
1549
    private $iFontArray=array();
1550
    private $iColor="black";
1551
    private $iText="";
1552
    private $iHAlign="left",$iVAlign="bottom";
1553
 
1554
    //---------------
1555
    // CONSTRUCTOR
1556
    function __construct($aTxt='') {
1557
        $this->iText = $aTxt;
1558
    }
1559
 
1560
    //---------------
1561
    // PUBLIC METHODS
1562
    function Set($aTxt) {
1563
        $this->iText = $aTxt;
1564
    }
1565
 
1566
    function SetCSIMTarget($aTarget,$aAltText='',$aWinTarget='') {
1567
        if( is_string($aTarget) )
1568
        $aTarget = array($aTarget);
1569
        $this->csimtarget=$aTarget;
1570
 
1571
        if( is_string($aWinTarget) )
1572
        $aWinTarget = array($aWinTarget);
1573
        $this->csimwintarget=$aWinTarget;
1574
 
1575
        if( is_string($aAltText) )
1576
        $aAltText = array($aAltText);
1577
        $this->csimalt=$aAltText;
1578
 
1579
    }
1580
 
1581
    function SetCSIMAlt($aAltText) {
1582
        if( is_string($aAltText) )
1583
        $aAltText = array($aAltText);
1584
        $this->csimalt=$aAltText;
1585
    }
1586
 
1587
    // Set text color
1588
    function SetColor($aColor) {
1589
        $this->iColor = $aColor;
1590
    }
1591
 
1592
    function HasTabs() {
1593
        if( is_string($this->iText) ) {
1594
            return substr_count($this->iText,"\t") > 0;
1595
        }
1596
        elseif( is_array($this->iText) ) {
1597
            return false;
1598
        }
1599
    }
1600
 
1601
    // Get number of tabs in string
1602
    function GetNbrTabs() {
1603
        if( is_string($this->iText) ) {
1604
            return substr_count($this->iText,"\t") ;
1605
        }
1606
        else{
1607
            return 0;
1608
        }
1609
    }
1610
 
1611
    // Set alignment
1612
    function Align($aHAlign,$aVAlign="bottom") {
1613
        $this->iHAlign=$aHAlign;
1614
        $this->iVAlign=$aVAlign;
1615
    }
1616
 
1617
    // Synonym
1618
    function SetAlign($aHAlign,$aVAlign="bottom") {
1619
        $this->iHAlign=$aHAlign;
1620
        $this->iVAlign=$aVAlign;
1621
    }
1622
 
1623
    // Specify font
1624
    function SetFont($aFFamily,$aFStyle=FS_NORMAL,$aFSize=10) {
1625
        $this->iFFamily = $aFFamily;
1626
        $this->iFStyle  = $aFStyle;
1627
        $this->iFSize  = $aFSize;
1628
    }
1629
 
1630
    function SetColumnFonts($aFontArray) {
1631
        if( !is_array($aFontArray) || count($aFontArray[0]) != 3 ) {
1632
            JpGraphError::RaiseL(6033);
1633
            // 'Array of fonts must contain arrays with 3 elements, i.e. (Family, Style, Size)'
1634
        }
1635
        $this->iFontArray = $aFontArray;
1636
    }
1637
 
1638
 
1639
    function IsColumns() {
1640
        return is_array($this->iText) ;
1641
    }
1642
 
1643
    // Get width of text. If text contains several columns separated by
1644
    // tabs then return both the total width as well as an array with a
1645
    // width for each column.
1646
    function GetWidth($aImg,$aUseTabs=false,$aTabExtraMargin=1.1) {
1647
        $extra_margin=4;
1648
        $aImg->SetFont($this->iFFamily,$this->iFStyle,$this->iFSize);
1649
        if( is_string($this->iText) ) {
1650
            if( strlen($this->iText) == 0 ) return 0;
1651
            $tmp = preg_split('/\t/',$this->iText);
1652
            if( count($tmp) <= 1 || !$aUseTabs ) {
1653
                $w = $aImg->GetTextWidth($this->iText);
1654
                return $w + 2*$extra_margin;
1655
            }
1656
            else {
1657
                $tot=0;
1658
                $n = count($tmp);
1659
                for($i=0; $i < $n; ++$i) {
1660
                    $res[$i] = $aImg->GetTextWidth($tmp[$i]);
1661
                    $tot += $res[$i]*$aTabExtraMargin;
1662
                }
1663
                return array(round($tot),$res);
1664
            }
1665
        }
1666
        elseif( is_object($this->iText) ) {
1667
            // A single icon
1668
            return $this->iText->GetWidth()+2*$extra_margin;
1669
        }
1670
        elseif( is_array($this->iText) ) {
1671
            // Must be an array of texts. In this case we return the sum of the
1672
            // length + a fixed margin of 4 pixels on each text string
1673
            $n = count($this->iText);
1674
            $nf = count($this->iFontArray);
1675
            for( $i=0, $w=0; $i < $n; ++$i ) {
1676
                if( $i < $nf ) {
1677
                    $aImg->SetFont($this->iFontArray[$i][0],$this->iFontArray[$i][1],$this->iFontArray[$i][2]);
1678
                }
1679
                else {
1680
                    $aImg->SetFont($this->iFFamily,$this->iFStyle,$this->iFSize);
1681
                }
1682
                $tmp = $this->iText[$i];
1683
                if( is_string($tmp) ) {
1684
                    $w += $aImg->GetTextWidth($tmp)+$extra_margin;
1685
                }
1686
                else {
1687
                    if( is_object($tmp) === false ) {
1688
                        JpGraphError::RaiseL(6012);
1689
                    }
1690
                    $w += $tmp->GetWidth()+$extra_margin;
1691
                }
1692
            }
1693
            return $w;
1694
        }
1695
        else {
1696
            JpGraphError::RaiseL(6012);
1697
        }
1698
    }
1699
 
1700
    // for the case where we have multiple columns this function returns the width of each
1701
    // column individually. If there is no columns just return the width of the single
1702
    // column as an array of one
1703
    function GetColWidth($aImg,$aMargin=0) {
1704
        $aImg->SetFont($this->iFFamily,$this->iFStyle,$this->iFSize);
1705
        if( is_array($this->iText) ) {
1706
            $n = count($this->iText);
1707
            $nf = count($this->iFontArray);
1708
            for( $i=0, $w=array(); $i < $n; ++$i ) {
1709
                $tmp = $this->iText[$i];
1710
                if( is_string($tmp) ) {
1711
                    if( $i < $nf ) {
1712
                        $aImg->SetFont($this->iFontArray[$i][0],$this->iFontArray[$i][1],$this->iFontArray[$i][2]);
1713
                    }
1714
                    else {
1715
                        $aImg->SetFont($this->iFFamily,$this->iFStyle,$this->iFSize);
1716
                    }
1717
                    $w[$i] = $aImg->GetTextWidth($tmp)+$aMargin;
1718
                }
1719
                else {
1720
                    if( is_object($tmp) === false ) {
1721
                        JpGraphError::RaiseL(6012);
1722
                    }
1723
                    $w[$i] = $tmp->GetWidth()+$aMargin;
1724
                }
1725
            }
1726
            return $w;
1727
        }
1728
        else {
1729
            return array($this->GetWidth($aImg));
1730
        }
1731
    }
1732
 
1733
    // Get total height of text
1734
    function GetHeight($aImg) {
1735
        $nf = count($this->iFontArray);
1736
        $maxheight = -1;
1737
 
1738
        if( $nf > 0 ) {
1739
            // We have to find out the largest font and take that one as the
1740
            // height of the row
1741
            for($i=0; $i < $nf; ++$i ) {
1742
                $aImg->SetFont($this->iFontArray[$i][0],$this->iFontArray[$i][1],$this->iFontArray[$i][2]);
1743
                $height = $aImg->GetFontHeight();
1744
                $maxheight = max($height,$maxheight);
1745
            }
1746
        }
1747
 
1748
        $aImg->SetFont($this->iFFamily,$this->iFStyle,$this->iFSize);
1749
        $height = $aImg->GetFontHeight();
1750
        $maxheight = max($height,$maxheight);
1751
        return $maxheight;
1752
    }
1753
 
1754
    // Unhide/hide the text
1755
    function Show($aShow=true) {
1756
        $this->iShow=$aShow;
1757
    }
1758
 
1759
    // Stroke text at (x,y) coordinates. If the text contains tabs then the
1760
    // x parameter should be an array of positions to be used for each successive
1761
    // tab mark. If no array is supplied then the tabs will be ignored.
1762
    function Stroke($aImg,$aX,$aY) {
1763
        if( $this->iShow ) {
1764
            $aImg->SetColor($this->iColor);
1765
            $aImg->SetFont($this->iFFamily,$this->iFStyle,$this->iFSize);
1766
            $aImg->SetTextAlign($this->iHAlign,$this->iVAlign);
1767
            if( $this->GetNbrTabs() < 1 ) {
1768
                if( is_string($this->iText) ) {
1769
                    if( is_array($aX) ) $aX=$aX[0];
1770
                    if( is_array($aY) ) $aY=$aY[0];
1771
                    $aImg->StrokeText($aX,$aY,$this->iText);
1772
                }
1773
                elseif( is_array($this->iText) && ($n = count($this->iText)) > 0 ) {
1774
                    $ax = is_array($aX) ;
1775
                    $ay = is_array($aY) ;
1776
                    if( $ax && $ay ) {
1777
                        // Nothing; both are already arrays
1778
                    }
1779
                    elseif( $ax ) {
1780
                        $aY = array_fill(0,$n,$aY);
1781
                    }
1782
                    elseif( $ay ) {
1783
                        $aX = array_fill(0,$n,$aX);
1784
                    }
1785
                    else {
1786
                        $aX = array_fill(0,$n,$aX);
1787
                        $aY = array_fill(0,$n,$aY);
1788
                    }
1789
                    $n = min($n, count($aX) ) ;
1790
                    $n = min($n, count($aY) ) ;
1791
                    for($i=0; $i < $n; ++$i ) {
1792
                        $tmp = $this->iText[$i];
1793
                        if( is_object($tmp) ) {
1794
                            $tmp->Stroke($aImg,$aX[$i],$aY[$i]);
1795
                        }
1796
                        else {
1797
                            if( $i < count($this->iFontArray) ) {
1798
                                $font = $this->iFontArray[$i];
1799
                                $aImg->SetFont($font[0],$font[1],$font[2]);
1800
                            }
1801
                            else {
1802
                                $aImg->SetFont($this->iFFamily,$this->iFStyle,$this->iFSize);
1803
                            }
1804
                        	$aImg->StrokeText($aX[$i],$aY[$i],str_replace("\t"," ",$tmp));
1805
                        }
1806
                    }
1807
                }
1808
            }
1809
            else {
1810
                $tmp = preg_split('/\t/',$this->iText);
1811
                $n = min(count($tmp),count($aX));
1812
                for($i=0; $i < $n; ++$i) {
1813
                    if( $i < count($this->iFontArray) ) {
1814
                        $font = $this->iFontArray[$i];
1815
                        $aImg->SetFont($font[0],$font[1],$font[2]);
1816
                    }
1817
                    else {
1818
                        $aImg->SetFont($this->iFFamily,$this->iFStyle,$this->iFSize);
1819
                    }
1820
                    $aImg->StrokeText($aX[$i],$aY,$tmp[$i]);
1821
                }
1822
            }
1823
        }
1824
    }
1825
}
1826
 
1827
//===================================================
1828
// CLASS HeaderProperty
1829
// Description: Data encapsulating class to hold property
1830
// for each type of the scale headers
1831
//===================================================
1832
class HeaderProperty {
1833
    public $grid;
1834
    public $iShowLabels=true,$iShowGrid=true;
1835
    public $iTitleVertMargin=3,$iFFamily=FF_FONT0,$iFStyle=FS_NORMAL,$iFSize=8;
1836
    public $iStyle=0;
1837
    public $iFrameColor="black",$iFrameWeight=1;
1838
    public $iBackgroundColor="white";
1839
    public $iWeekendBackgroundColor="lightgray",$iSundayTextColor="red"; // these are only used with day scale
1840
    public $iTextColor="black";
1841
    public $iLabelFormStr="%d";
1842
    public $iIntervall = 1;
1843
 
1844
    //---------------
1845
    // CONSTRUCTOR
1846
    function __construct() {
1847
        $this->grid = new LineProperty();
1848
    }
1849
 
1850
    //---------------
1851
    // PUBLIC METHODS
1852
    function Show($aShow=true) {
1853
        $this->iShowLabels = $aShow;
1854
    }
1855
 
1856
    function SetIntervall($aInt) {
1857
    	$this->iIntervall = $aInt;
1858
    }
1859
 
1860
    function SetInterval($aInt) {
1861
        $this->iIntervall = $aInt;
1862
    }
1863
 
1864
    function GetIntervall() {
1865
        return $this->iIntervall ;
1866
    }
1867
 
1868
    function SetFont($aFFamily,$aFStyle=FS_NORMAL,$aFSize=10) {
1869
        $this->iFFamily = $aFFamily;
1870
        $this->iFStyle  = $aFStyle;
1871
        $this->iFSize  = $aFSize;
1872
    }
1873
 
1874
    function SetFontColor($aColor) {
1875
        $this->iTextColor = $aColor;
1876
    }
1877
 
1878
    function GetFontHeight($aImg) {
1879
        $aImg->SetFont($this->iFFamily,$this->iFStyle,$this->iFSize);
1880
        return $aImg->GetFontHeight();
1881
    }
1882
 
1883
    function GetFontWidth($aImg) {
1884
        $aImg->SetFont($this->iFFamily,$this->iFStyle,$this->iFSize);
1885
        return $aImg->GetFontWidth();
1886
    }
1887
 
1888
    function GetStrWidth($aImg,$aStr) {
1889
        $aImg->SetFont($this->iFFamily,$this->iFStyle,$this->iFSize);
1890
        return $aImg->GetTextWidth($aStr);
1891
    }
1892
 
1893
    function SetStyle($aStyle) {
1894
        $this->iStyle = $aStyle;
1895
    }
1896
 
1897
    function SetBackgroundColor($aColor) {
1898
        $this->iBackgroundColor=$aColor;
1899
    }
1900
 
1901
    function SetFrameWeight($aWeight) {
1902
        $this->iFrameWeight=$aWeight;
1903
    }
1904
 
1905
    function SetFrameColor($aColor) {
1906
        $this->iFrameColor=$aColor;
1907
    }
1908
 
1909
    // Only used by day scale
1910
    function SetWeekendColor($aColor) {
1911
        $this->iWeekendBackgroundColor=$aColor;
1912
    }
1913
 
1914
    // Only used by day scale
1915
    function SetSundayFontColor($aColor) {
1916
        $this->iSundayTextColor=$aColor;
1917
    }
1918
 
1919
    function SetTitleVertMargin($aMargin) {
1920
        $this->iTitleVertMargin=$aMargin;
1921
    }
1922
 
1923
    function SetLabelFormatString($aStr) {
1924
        $this->iLabelFormStr=$aStr;
1925
    }
1926
 
1927
    function SetFormatString($aStr) {
1928
        $this->SetLabelFormatString($aStr);
1929
    }
1930
 
1931
 
1932
}
1933
 
1934
//===================================================
1935
// CLASS GanttScale
1936
// Description: Responsible for calculating and showing
1937
// the scale in a gantt chart. This includes providing methods for
1938
// converting dates to position in the chart as well as stroking the
1939
// date headers (days, week, etc).
1940
//===================================================
1941
class GanttScale {
1942
    public $minute,$hour,$day,$week,$month,$year;
1943
    public $divider,$dividerh,$tableTitle;
1944
    public $iStartDate=-1,$iEndDate=-1;
1945
    // Number of gantt bar position (n.b not necessariliy the same as the number of bars)
1946
    // we could have on bar in position 1, and one bar in position 5 then there are two
1947
    // bars but the number of bar positions is 5
1948
    public $actinfo;
1949
    public $iTopPlotMargin=10,$iBottomPlotMargin=15;
1950
    public $iVertLines=-1;
1951
    public $iVertHeaderSize=-1;
1952
    // The width of the labels (defaults to the widest of all labels)
1953
    private $iLabelWidth;
1954
    // Out image to stroke the scale to
1955
    private $iImg;
1956
    private $iTableHeaderBackgroundColor="white",$iTableHeaderFrameColor="black";
1957
    private $iTableHeaderFrameWeight=1;
1958
    private $iAvailableHeight=-1,$iVertSpacing=-1;
1959
    private $iDateLocale;
1960
    private $iVertLayout=GANTT_EVEN;
1961
    private $iUsePlotWeekendBackground=true;
1962
    private $iWeekStart = 1; // Default to have weekends start on Monday
1963
 
1964
    //---------------
1965
    // CONSTRUCTOR
1966
    function __construct($aImg) {
1967
        $this->iImg = $aImg;
1968
        $this->iDateLocale = new DateLocale();
1969
 
1970
        $this->minute = new HeaderProperty();
1971
        $this->minute->SetIntervall(15);
1972
        $this->minute->SetLabelFormatString('i');
1973
        $this->minute->SetFont(FF_FONT0);
1974
        $this->minute->grid->SetColor("gray");
1975
 
1976
        $this->hour = new HeaderProperty();
1977
        $this->hour->SetFont(FF_FONT0);
1978
        $this->hour->SetIntervall(6);
1979
        $this->hour->SetStyle(HOURSTYLE_HM24);
1980
        $this->hour->SetLabelFormatString('H:i');
1981
        $this->hour->grid->SetColor("gray");
1982
 
1983
        $this->day = new HeaderProperty();
1984
        $this->day->grid->SetColor("gray");
1985
        $this->day->SetLabelFormatString('l');
1986
 
1987
        $this->week = new HeaderProperty();
1988
        $this->week->SetLabelFormatString("w%d");
1989
        $this->week->SetFont(FF_FONT1);
1990
 
1991
        $this->month = new HeaderProperty();
1992
        $this->month->SetFont(FF_FONT1,FS_BOLD);
1993
 
1994
        $this->year = new HeaderProperty();
1995
        $this->year->SetFont(FF_FONT1,FS_BOLD);
1996
 
1997
        $this->divider=new LineProperty();
1998
        $this->dividerh=new LineProperty();
1999
        $this->dividerh->SetWeight(2);
2000
        $this->divider->SetWeight(6);
2001
        $this->divider->SetColor('gray');
2002
        $this->divider->SetStyle('fancy');
2003
 
2004
        $this->tableTitle=new TextProperty();
2005
        $this->tableTitle->Show(false);
2006
        $this->actinfo = new GanttActivityInfo();
2007
    }
2008
 
2009
    //---------------
2010
    // PUBLIC METHODS
2011
    // Specify what headers should be visible
2012
    function ShowHeaders($aFlg) {
2013
        $this->day->Show($aFlg & GANTT_HDAY);
2014
        $this->week->Show($aFlg & GANTT_HWEEK);
2015
        $this->month->Show($aFlg & GANTT_HMONTH);
2016
        $this->year->Show($aFlg & GANTT_HYEAR);
2017
        $this->hour->Show($aFlg & GANTT_HHOUR);
2018
        $this->minute->Show($aFlg & GANTT_HMIN);
2019
 
2020
        // Make some default settings of gridlines whihc makes sense
2021
        if( $aFlg & GANTT_HWEEK ) {
2022
            $this->month->grid->Show(false);
2023
            $this->year->grid->Show(false);
2024
        }
2025
        if( $aFlg & GANTT_HHOUR ) {
2026
            $this->day->grid->SetColor("black");
2027
        }
2028
    }
2029
 
2030
    // Should the weekend background stretch all the way down in the plotarea
2031
    function UseWeekendBackground($aShow) {
2032
        $this->iUsePlotWeekendBackground = $aShow;
2033
    }
2034
 
2035
    // Have a range been specified?
2036
    function IsRangeSet() {
2037
        return $this->iStartDate!=-1 && $this->iEndDate!=-1;
2038
    }
2039
 
2040
    // Should the layout be from top or even?
2041
    function SetVertLayout($aLayout) {
2042
        $this->iVertLayout = $aLayout;
2043
    }
2044
 
2045
    // Which locale should be used?
2046
    function SetDateLocale($aLocale) {
2047
        $this->iDateLocale->Set($aLocale);
2048
    }
2049
 
2050
    // Number of days we are showing
2051
    function GetNumberOfDays() {
2052
        return round(($this->iEndDate-$this->iStartDate)/SECPERDAY);
2053
    }
2054
 
2055
    // The width of the actual plot area
2056
    function GetPlotWidth() {
2057
        $img=$this->iImg;
2058
        return $img->width - $img->left_margin - $img->right_margin;
2059
    }
2060
 
2061
    // Specify the width of the titles(labels) for the activities
2062
    // (This is by default set to the minimum width enought for the
2063
    // widest title)
2064
    function SetLabelWidth($aLabelWidth) {
2065
        $this->iLabelWidth=$aLabelWidth;
2066
    }
2067
 
2068
    // Which day should the week start?
2069
    // 0==Sun, 1==Monday, 2==Tuesday etc
2070
    function SetWeekStart($aStartDay) {
2071
        $this->iWeekStart = $aStartDay % 7;
2072
 
2073
        //Recalculate the startday since this will change the week start
2074
        $this->SetRange($this->iStartDate,$this->iEndDate);
2075
    }
2076
 
2077
    // Do we show min scale?
2078
    function IsDisplayMinute() {
2079
        return $this->minute->iShowLabels;
2080
    }
2081
 
2082
    // Do we show day scale?
2083
    function IsDisplayHour() {
2084
        return $this->hour->iShowLabels;
2085
    }
2086
 
2087
 
2088
    // Do we show day scale?
2089
    function IsDisplayDay() {
2090
        return $this->day->iShowLabels;
2091
    }
2092
 
2093
    // Do we show week scale?
2094
    function IsDisplayWeek() {
2095
        return $this->week->iShowLabels;
2096
    }
2097
 
2098
    // Do we show month scale?
2099
    function IsDisplayMonth() {
2100
        return $this->month->iShowLabels;
2101
    }
2102
 
2103
    // Do we show year scale?
2104
    function IsDisplayYear() {
2105
        return $this->year->iShowLabels;
2106
    }
2107
 
2108
    // Specify spacing (in percent of bar height) between activity bars
2109
    function SetVertSpacing($aSpacing) {
2110
        $this->iVertSpacing = $aSpacing;
2111
    }
2112
 
2113
    // Specify scale min and max date either as timestamp or as date strings
2114
    // Always round to the nearest week boundary
2115
    function SetRange($aMin,$aMax) {
2116
        $this->iStartDate = $this->NormalizeDate($aMin);
2117
        $this->iEndDate = $this->NormalizeDate($aMax);
2118
    }
2119
 
2120
 
2121
    // Adjust the start and end date so they fit to beginning/ending
2122
    // of the week taking the specified week start day into account.
2123
    function AdjustStartEndDay() {
2124
 
2125
        if( !($this->IsDisplayYear() ||$this->IsDisplayMonth() || $this->IsDisplayWeek()) ) {
2126
            // Don't adjust
2127
            return;
2128
        }
2129
 
2130
        // Get day in week for start and ending date (Sun==0)
2131
        $ds=strftime("%w",$this->iStartDate);
2132
        $de=strftime("%w",$this->iEndDate);
2133
 
2134
        // We want to start on iWeekStart day. But first we subtract a week
2135
        // if the startdate is "behind" the day the week start at.
2136
        // This way we ensure that the given start date is always included
2137
        // in the range. If we don't do this the nearest correct weekday in the week
2138
        // to start at might be later than the start date.
2139
        if( $ds < $this->iWeekStart )
2140
        $d = strtotime('-7 day',$this->iStartDate);
2141
        else
2142
        $d = $this->iStartDate;
2143
        $adjdate = strtotime(($this->iWeekStart-$ds).' day',$d /*$this->iStartDate*/ );
2144
        $this->iStartDate = $adjdate;
2145
 
2146
        // We want to end on the last day of the week
2147
        $preferredEndDay = ($this->iWeekStart+6)%7;
2148
        if( $preferredEndDay != $de ) {
2149
            // Solve equivalence eq:    $de + x ~ $preferredDay (mod 7)
2150
            $adj = (7+($preferredEndDay - $de)) % 7;
2151
            $adjdate = strtotime("+$adj day",$this->iEndDate);
2152
            $this->iEndDate = $adjdate;
2153
        }
2154
    }
2155
 
2156
    // Specify background for the table title area (upper left corner of the table)
2157
    function SetTableTitleBackground($aColor) {
2158
        $this->iTableHeaderBackgroundColor = $aColor;
2159
    }
2160
 
2161
    ///////////////////////////////////////
2162
    // PRIVATE Methods
2163
 
2164
    // Determine the height of all the scale headers combined
2165
    function GetHeaderHeight() {
2166
        $img=$this->iImg;
2167
        $height=1;
2168
        if( $this->minute->iShowLabels ) {
2169
            $height += $this->minute->GetFontHeight($img);
2170
            $height += $this->minute->iTitleVertMargin;
2171
        }
2172
        if( $this->hour->iShowLabels ) {
2173
            $height += $this->hour->GetFontHeight($img);
2174
            $height += $this->hour->iTitleVertMargin;
2175
        }
2176
        if( $this->day->iShowLabels ) {
2177
            $height += $this->day->GetFontHeight($img);
2178
            $height += $this->day->iTitleVertMargin;
2179
        }
2180
        if( $this->week->iShowLabels ) {
2181
            $height += $this->week->GetFontHeight($img);
2182
            $height += $this->week->iTitleVertMargin;
2183
        }
2184
        if( $this->month->iShowLabels ) {
2185
            $height += $this->month->GetFontHeight($img);
2186
            $height += $this->month->iTitleVertMargin;
2187
        }
2188
        if( $this->year->iShowLabels ) {
2189
            $height += $this->year->GetFontHeight($img);
2190
            $height += $this->year->iTitleVertMargin;
2191
        }
2192
        return $height;
2193
    }
2194
 
2195
    // Get width (in pixels) for a single day
2196
    function GetDayWidth() {
2197
        return ($this->GetPlotWidth()-$this->iLabelWidth+1)/$this->GetNumberOfDays();
2198
    }
2199
 
2200
    // Get width (in pixels) for a single hour
2201
    function GetHourWidth() {
2202
        return $this->GetDayWidth() / 24 ;
2203
    }
2204
 
2205
    function GetMinuteWidth() {
2206
        return $this->GetHourWidth() / 60 ;
2207
    }
2208
 
2209
    // Nuber of days in a year
2210
    function GetNumDaysInYear($aYear) {
2211
        if( $this->IsLeap($aYear) )
2212
        return 366;
2213
        else
2214
        return 365;
2215
    }
2216
 
2217
    // Get week number
2218
    function GetWeekNbr($aDate,$aSunStart=true) {
2219
        // We can't use the internal strftime() since it gets the weeknumber
2220
        // wrong since it doesn't follow ISO on all systems since this is
2221
        // system linrary dependent.
2222
        // Even worse is that this works differently if we are on a Windows
2223
        // or UNIX box (it even differs between UNIX boxes how strftime()
2224
        // is natively implemented)
2225
        //
2226
        // Credit to Nicolas Hoizey <nhoizey@phpheaven.net> for this elegant
2227
        // version of Week Nbr calculation.
2228
 
2229
        $day = $this->NormalizeDate($aDate);
2230
        if( $aSunStart )
2231
        $day += 60*60*24;
2232
 
2233
        /*-------------------------------------------------------------------------
2234
         According to ISO-8601 :
2235
         "Week 01 of a year is per definition the first week that has the Thursday in this year,
2236
         which is equivalent to the week that contains the fourth day of January.
2237
         In other words, the first week of a new year is the week that has the majority of its
2238
         days in the new year."
2239
 
2240
         Be carefull, with PHP, -3 % 7 = -3, instead of 4 !!!
2241
 
2242
         day of year             = date("z", $day) + 1
2243
         offset to thursday      = 3 - (date("w", $day) + 6) % 7
2244
         first thursday of year  = 1 + (11 - date("w", mktime(0, 0, 0, 1, 1, date("Y", $day)))) % 7
2245
         week number             = (thursday's day of year - first thursday's day of year) / 7 + 1
2246
         ---------------------------------------------------------------------------*/
2247
 
2248
        $thursday = $day + 60 * 60 * 24 * (3 - (date("w", $day) + 6) % 7);              // take week's thursday
2249
        $week = 1 + (date("z", $thursday) - (11 - date("w", mktime(0, 0, 0, 1, 1, date("Y", $thursday)))) % 7) / 7;
2250
 
2251
        return $week;
2252
    }
2253
 
2254
    // Is year a leap year?
2255
    function IsLeap($aYear) {
2256
        // Is the year a leap year?
2257
        //$year = 0+date("Y",$aDate);
2258
        if( $aYear % 4 == 0)
2259
        if( !($aYear % 100 == 0) || ($aYear % 400 == 0) )
2260
        return true;
2261
        return false;
2262
    }
2263
 
2264
    // Get current year
2265
    function GetYear($aDate) {
2266
        return 0+Date("Y",$aDate);
2267
    }
2268
 
2269
    // Return number of days in a year
2270
    function GetNumDaysInMonth($aMonth,$aYear) {
2271
        $days=array(31,28,31,30,31,30,31,31,30,31,30,31);
2272
        $daysl=array(31,29,31,30,31,30,31,31,30,31,30,31);
2273
        if( $this->IsLeap($aYear))
2274
        return $daysl[$aMonth];
2275
        else
2276
        return $days[$aMonth];
2277
    }
2278
 
2279
    // Get day in month
2280
    function GetMonthDayNbr($aDate) {
2281
        return 0+strftime("%d",$aDate);
2282
    }
2283
 
2284
    // Get day in year
2285
    function GetYearDayNbr($aDate) {
2286
        return 0+strftime("%j",$aDate);
2287
    }
2288
 
2289
    // Get month number
2290
    function GetMonthNbr($aDate) {
2291
        return 0+strftime("%m",$aDate);
2292
    }
2293
 
2294
    // Translate a date to screen coordinates (horizontal scale)
2295
    function TranslateDate($aDate) {
2296
        //
2297
        // In order to handle the problem with Daylight savings time
2298
        // the scale written with equal number of seconds per day beginning
2299
        // with the start date. This means that we "cement" the state of
2300
        // DST as it is in the start date. If later the scale includes the
2301
        // switchover date (depends on the locale) we need to adjust back
2302
        // if the date we try to translate has a different DST status since
2303
        // we would otherwise be off by one hour.
2304
        $aDate = $this->NormalizeDate($aDate);
2305
        $tmp = localtime($aDate);
2306
        $cloc = $tmp[8];
2307
        $tmp = localtime($this->iStartDate);
2308
        $sloc = $tmp[8];
2309
        $offset = 0;
2310
        if( $sloc != $cloc) {
2311
            if( $sloc )
2312
            $offset = 3600;
2313
            else
2314
            $offset = -3600;
2315
        }
2316
        $img=$this->iImg;
2317
        return ($aDate-$this->iStartDate-$offset)/SECPERDAY*$this->GetDayWidth()+$img->left_margin+$this->iLabelWidth;;
2318
    }
2319
 
2320
    // Get screen coordinatesz for the vertical position for a bar
2321
    function TranslateVertPos($aPos,$atTop=false) {
2322
        $img=$this->iImg;
2323
        if( $aPos > $this->iVertLines )
2324
        	JpGraphError::RaiseL(6015,$aPos);
2325
        // 'Illegal vertical position %d'
2326
        if( $this->iVertLayout == GANTT_EVEN ) {
2327
            // Position the top bar at 1 vert spacing from the scale
2328
            $pos =  round($img->top_margin + $this->iVertHeaderSize +  ($aPos+1)*$this->iVertSpacing);
2329
        }
2330
        else {
2331
            // position the top bar at 1/2 a vert spacing from the scale
2332
            $pos = round($img->top_margin + $this->iVertHeaderSize  + $this->iTopPlotMargin + ($aPos+1)*$this->iVertSpacing);
2333
        }
2334
 
2335
        if( $atTop )
2336
            $pos -= $this->iVertSpacing;
2337
 
2338
        return $pos;
2339
    }
2340
 
2341
    // What is the vertical spacing?
2342
    function GetVertSpacing() {
2343
        return $this->iVertSpacing;
2344
    }
2345
 
2346
    // Convert a date to timestamp
2347
    function NormalizeDate($aDate) {
2348
        if( $aDate === false ) return false;
2349
        if( is_string($aDate) ) {
2350
            $t = strtotime($aDate);
2351
            if( $t === FALSE || $t === -1 ) {
2352
                JpGraphError::RaiseL(6016,$aDate);
2353
                //("Date string ($aDate) specified for Gantt activity can not be interpretated. Please make sure it is a valid time string, e.g. 2005-04-23 13:30");
2354
            }
2355
            return $t;
2356
        }
2357
        elseif( is_int($aDate) || is_float($aDate) )
2358
            return $aDate;
2359
        else
2360
            JpGraphError::RaiseL(6017,$aDate);
2361
        //Unknown date format in GanttScale ($aDate).");
2362
    }
2363
 
2364
 
2365
    // Convert a time string to minutes
2366
 
2367
    function TimeToMinutes($aTimeString) {
2368
        // Split in hours and minutes
2369
        $pos=strpos($aTimeString,':');
2370
        $minint=60;
2371
        if( $pos === false ) {
2372
            $hourint = $aTimeString;
2373
            $minint = 0;
2374
        }
2375
        else {
2376
            $hourint = floor(substr($aTimeString,0,$pos));
2377
            $minint = floor(substr($aTimeString,$pos+1));
2378
        }
2379
        $minint += 60 * $hourint;
2380
        return $minint;
2381
    }
2382
 
2383
    // Stroke the day scale (including gridlines)
2384
    function StrokeMinutes($aYCoord,$getHeight=false) {
2385
        $img=$this->iImg;
2386
        $xt=$img->left_margin+$this->iLabelWidth;
2387
        $yt=$aYCoord+$img->top_margin;
2388
        if( $this->minute->iShowLabels ) {
2389
            $img->SetFont($this->minute->iFFamily,$this->minute->iFStyle,$this->minute->iFSize);
2390
            $yb = $yt + $img->GetFontHeight() +
2391
            $this->minute->iTitleVertMargin + $this->minute->iFrameWeight;
2392
            if( $getHeight ) {
2393
                return $yb - $img->top_margin;
2394
            }
2395
            $xb = $img->width-$img->right_margin+1;
2396
            $img->SetColor($this->minute->iBackgroundColor);
2397
            $img->FilledRectangle($xt,$yt,$xb,$yb);
2398
 
2399
            $x = $xt;
2400
            $img->SetTextAlign("center");
2401
            $day = date('w',$this->iStartDate);
2402
            $minint = $this->minute->GetIntervall() ;
2403
 
2404
            if( 60 % $minint !== 0 ) {
2405
                JpGraphError::RaiseL(6018,$minint);
2406
                //'Intervall for minutes must divide the hour evenly, e.g. 1,5,10,12,15,20,30 etc You have specified an intervall of '.$minint.' minutes.');
2407
            }
2408
 
2409
 
2410
            $n = 60 / $minint;
2411
            $datestamp = $this->iStartDate;
2412
            $width = $this->GetHourWidth() / $n ;
2413
            if( $width < 8 ) {
2414
                // TO small width to draw minute scale
2415
                JpGraphError::RaiseL(6019,$width);
2416
                //('The available width ('.$width.') for minutes are to small for this scale to be displayed. Please use auto-sizing or increase the width of the graph.');
2417
            }
2418
 
2419
            $nh = ceil(24*60 / $this->TimeToMinutes($this->hour->GetIntervall()) );
2420
            $nd = $this->GetNumberOfDays();
2421
            // Convert to intervall to seconds
2422
            $minint *= 60;
2423
            for($j=0; $j < $nd; ++$j, $day += 1, $day %= 7) {
2424
                for( $k=0; $k < $nh; ++$k ) {
2425
                    for($i=0; $i < $n ;++$i, $x+=$width, $datestamp += $minint ) {
2426
                        if( $day==6 || $day==0 ) {
2427
 
2428
                            $img->PushColor($this->day->iWeekendBackgroundColor);
2429
                            if( $this->iUsePlotWeekendBackground )
2430
                            $img->FilledRectangle($x,$yt+$this->day->iFrameWeight,$x+$width,$img->height-$img->bottom_margin);
2431
                            else
2432
                            $img->FilledRectangle($x,$yt+$this->day->iFrameWeight,$x+$width,$yb-$this->day->iFrameWeight);
2433
                            $img->PopColor();
2434
 
2435
                        }
2436
 
2437
                        if( $day==0 )
2438
                        $img->SetColor($this->day->iSundayTextColor);
2439
                        else
2440
                        $img->SetColor($this->day->iTextColor);
2441
 
2442
                        switch( $this->minute->iStyle ) {
2443
                            case MINUTESTYLE_CUSTOM:
2444
                                $txt = date($this->minute->iLabelFormStr,$datestamp);
2445
                                break;
2446
                            case MINUTESTYLE_MM:
2447
                            default:
2448
                                // 15
2449
                                $txt = date('i',$datestamp);
2450
                                break;
2451
                        }
2452
                        $img->StrokeText(round($x+$width/2),round($yb-$this->minute->iTitleVertMargin),$txt);
2453
 
2454
                        // Fix a rounding problem the wrong way ..
2455
                        // If we also have hour scale then don't draw the firsta or last
2456
                        // gridline since that will be overwritten by the hour scale gridline if such exists.
2457
                        // However, due to the propagation of rounding of the 'x+=width' term in the loop
2458
                        // this might sometimes be one pixel of so we fix this by not drawing it.
2459
                        // The proper way to fix it would be to re-calculate the scale for each step and
2460
                        // not using the additive term.
2461
                        if( !(($i == $n || $i==0) && $this->hour->iShowLabels && $this->hour->grid->iShow) ) {
2462
                            $img->SetColor($this->minute->grid->iColor);
2463
                            $img->SetLineWeight($this->minute->grid->iWeight);
2464
                            $img->Line($x,$yt,$x,$yb);
2465
                            $this->minute->grid->Stroke($img,$x,$yb,$x,$img->height-$img->bottom_margin);
2466
                        }
2467
                    }
2468
                }
2469
            }
2470
            $img->SetColor($this->minute->iFrameColor);
2471
            $img->SetLineWeight($this->minute->iFrameWeight);
2472
            $img->Rectangle($xt,$yt,$xb,$yb);
2473
            return $yb - $img->top_margin;
2474
        }
2475
        return $aYCoord;
2476
    }
2477
 
2478
    // Stroke the day scale (including gridlines)
2479
    function StrokeHours($aYCoord,$getHeight=false) {
2480
        $img=$this->iImg;
2481
        $xt=$img->left_margin+$this->iLabelWidth;
2482
        $yt=$aYCoord+$img->top_margin;
2483
        if( $this->hour->iShowLabels ) {
2484
            $img->SetFont($this->hour->iFFamily,$this->hour->iFStyle,$this->hour->iFSize);
2485
            $yb = $yt + $img->GetFontHeight() +
2486
            $this->hour->iTitleVertMargin + $this->hour->iFrameWeight;
2487
            if( $getHeight ) {
2488
                return $yb - $img->top_margin;
2489
            }
2490
            $xb = $img->width-$img->right_margin+1;
2491
            $img->SetColor($this->hour->iBackgroundColor);
2492
            $img->FilledRectangle($xt,$yt,$xb,$yb);
2493
 
2494
            $x = $xt;
2495
            $img->SetTextAlign("center");
2496
            $tmp = $this->hour->GetIntervall() ;
2497
            $minint = $this->TimeToMinutes($tmp);
2498
            if( 1440 % $minint !== 0 ) {
2499
                JpGraphError::RaiseL(6020,$tmp);
2500
                //('Intervall for hours must divide the day evenly, e.g. 0:30, 1:00, 1:30, 4:00 etc. You have specified an intervall of '.$tmp);
2501
            }
2502
 
2503
            $n = ceil(24*60 / $minint );
2504
            $datestamp = $this->iStartDate;
2505
            $day = date('w',$this->iStartDate);
2506
            $doback = !$this->minute->iShowLabels;
2507
            $width = $this->GetDayWidth() / $n ;
2508
            for($j=0; $j < $this->GetNumberOfDays(); ++$j, $day += 1,$day %= 7) {
2509
                for($i=0; $i < $n ;++$i, $x+=$width) {
2510
                    if( $day==6 || $day==0 ) {
2511
 
2512
                        $img->PushColor($this->day->iWeekendBackgroundColor);
2513
                        if( $this->iUsePlotWeekendBackground && $doback )
2514
                        $img->FilledRectangle($x,$yt+$this->day->iFrameWeight,$x+$width,$img->height-$img->bottom_margin);
2515
                        else
2516
                        $img->FilledRectangle($x,$yt+$this->day->iFrameWeight,$x+$width,$yb-$this->day->iFrameWeight);
2517
                        $img->PopColor();
2518
 
2519
                    }
2520
 
2521
                    if( $day==0 )
2522
                    $img->SetColor($this->day->iSundayTextColor);
2523
                    else
2524
                    $img->SetColor($this->day->iTextColor);
2525
 
2526
                    switch( $this->hour->iStyle ) {
2527
                        case HOURSTYLE_HMAMPM:
2528
                            // 1:35pm
2529
                            $txt = date('g:ia',$datestamp);
2530
                            break;
2531
                        case HOURSTYLE_H24:
2532
                            // 13
2533
                            $txt = date('H',$datestamp);
2534
                            break;
2535
                        case HOURSTYLE_HAMPM:
2536
                            $txt = date('ga',$datestamp);
2537
                            break;
2538
                        case HOURSTYLE_CUSTOM:
2539
                            $txt = date($this->hour->iLabelFormStr,$datestamp);
2540
                            break;
2541
                        case HOURSTYLE_HM24:
2542
                        default:
2543
                            $txt = date('H:i',$datestamp);
2544
                            break;
2545
                    }
2546
                    $img->StrokeText(round($x+$width/2),round($yb-$this->hour->iTitleVertMargin),$txt);
2547
                    $img->SetColor($this->hour->grid->iColor);
2548
                    $img->SetLineWeight($this->hour->grid->iWeight);
2549
                    $img->Line($x,$yt,$x,$yb);
2550
                    $this->hour->grid->Stroke($img,$x,$yb,$x,$img->height-$img->bottom_margin);
2551
                    //$datestamp += $minint*60
2552
                    $datestamp = mktime(date('H',$datestamp),date('i',$datestamp)+$minint,0,
2553
                    date("m",$datestamp),date("d",$datestamp)+1,date("Y",$datestamp));
2554
 
2555
                }
2556
            }
2557
            $img->SetColor($this->hour->iFrameColor);
2558
            $img->SetLineWeight($this->hour->iFrameWeight);
2559
            $img->Rectangle($xt,$yt,$xb,$yb);
2560
            return $yb - $img->top_margin;
2561
        }
2562
        return $aYCoord;
2563
    }
2564
 
2565
 
2566
    // Stroke the day scale (including gridlines)
2567
    function StrokeDays($aYCoord,$getHeight=false) {
2568
        $img=$this->iImg;
2569
        $daywidth=$this->GetDayWidth();
2570
        $xt=$img->left_margin+$this->iLabelWidth;
2571
        $yt=$aYCoord+$img->top_margin;
2572
        if( $this->day->iShowLabels ) {
2573
            $img->SetFont($this->day->iFFamily,$this->day->iFStyle,$this->day->iFSize);
2574
            $yb=$yt + $img->GetFontHeight() + $this->day->iTitleVertMargin + $this->day->iFrameWeight;
2575
            if( $getHeight ) {
2576
                return $yb - $img->top_margin;
2577
            }
2578
            $xb=$img->width-$img->right_margin+1;
2579
            $img->SetColor($this->day->iBackgroundColor);
2580
            $img->FilledRectangle($xt,$yt,$xb,$yb);
2581
 
2582
            $x = $xt;
2583
            $img->SetTextAlign("center");
2584
            $day = date('w',$this->iStartDate);
2585
            $datestamp = $this->iStartDate;
2586
 
2587
            $doback = !($this->hour->iShowLabels || $this->minute->iShowLabels);
2588
 
2589
            setlocale(LC_TIME,$this->iDateLocale->iLocale);
2590
 
2591
            for($i=0; $i < $this->GetNumberOfDays(); ++$i, $x+=$daywidth, $day += 1,$day %= 7) {
2592
                if( $day==6 || $day==0 ) {
2593
                    $img->SetColor($this->day->iWeekendBackgroundColor);
2594
                    if( $this->iUsePlotWeekendBackground && $doback)
2595
                        $img->FilledRectangle($x,$yt+$this->day->iFrameWeight,
2596
                                              $x+$daywidth,$img->height-$img->bottom_margin);
2597
                    else
2598
                        $img->FilledRectangle($x,$yt+$this->day->iFrameWeight,
2599
                    $x+$daywidth,$yb-$this->day->iFrameWeight);
2600
                }
2601
 
2602
                $mn = strftime('%m',$datestamp);
2603
                if( $mn[0]=='0' )
2604
                    $mn = $mn[1];
2605
 
2606
                switch( $this->day->iStyle ) {
2607
                    case DAYSTYLE_LONG:
2608
                        // "Monday"
2609
                        $txt = strftime('%A',$datestamp);
2610
                        break;
2611
                    case DAYSTYLE_SHORT:
2612
                        // "Mon"
2613
                        $txt = strftime('%a',$datestamp);
2614
                        break;
2615
                    case DAYSTYLE_SHORTDAYDATE1:
2616
                        // "Mon 23/6"
2617
                        $txt = strftime('%a %d/'.$mn,$datestamp);
2618
                        break;
2619
                    case DAYSTYLE_SHORTDAYDATE2:
2620
                        // "Mon 23 Jun"
2621
                        $txt = strftime('%a %d %b',$datestamp);
2622
                        break;
2623
                    case DAYSTYLE_SHORTDAYDATE3:
2624
                        // "Mon 23 Jun 2003"
2625
                        $txt = strftime('%a %d %b %Y',$datestamp);
2626
                        break;
2627
                    case DAYSTYLE_LONGDAYDATE1:
2628
                        // "Monday 23 Jun"
2629
                        $txt = strftime('%A %d %b',$datestamp);
2630
                        break;
2631
                    case DAYSTYLE_LONGDAYDATE2:
2632
                        // "Monday 23 Jun 2003"
2633
                        $txt = strftime('%A %d %b %Y',$datestamp);
2634
                        break;
2635
                    case DAYSTYLE_SHORTDATE1:
2636
                        // "23/6"
2637
                        $txt = strftime('%d/'.$mn,$datestamp);
2638
                        break;
2639
                    case DAYSTYLE_SHORTDATE2:
2640
                        // "23 Jun"
2641
                        $txt = strftime('%d %b',$datestamp);
2642
                        break;
2643
                    case DAYSTYLE_SHORTDATE3:
2644
                        // "Mon 23"
2645
                        $txt = strftime('%a %d',$datestamp);
2646
                        break;
2647
                    case DAYSTYLE_SHORTDATE4:
2648
                        // "23"
2649
                        $txt = strftime('%d',$datestamp);
2650
                        break;
2651
                    case DAYSTYLE_CUSTOM:
2652
                        // Custom format
2653
                        $txt = strftime($this->day->iLabelFormStr,$datestamp);
2654
                        break;
2655
                    case DAYSTYLE_ONELETTER:
2656
                    default:
2657
                        // "M"
2658
                        $txt = strftime('%A',$datestamp);
2659
                        $txt = strtoupper($txt[0]);
2660
                        break;
2661
                }
2662
 
2663
                if( $day==0 )
2664
                    $img->SetColor($this->day->iSundayTextColor);
2665
                else
2666
                    $img->SetColor($this->day->iTextColor);
2667
                $img->StrokeText(round($x+$daywidth/2+1),
2668
                round($yb-$this->day->iTitleVertMargin),$txt);
2669
                $img->SetColor($this->day->grid->iColor);
2670
                $img->SetLineWeight($this->day->grid->iWeight);
2671
                $img->Line($x,$yt,$x,$yb);
2672
                $this->day->grid->Stroke($img,$x,$yb,$x,$img->height-$img->bottom_margin);
2673
                $datestamp = mktime(0,0,0,date("m",$datestamp),date("d",$datestamp)+1,date("Y",$datestamp));
2674
                //$datestamp += SECPERDAY;
2675
 
2676
            }
2677
            $img->SetColor($this->day->iFrameColor);
2678
            $img->SetLineWeight($this->day->iFrameWeight);
2679
            $img->Rectangle($xt,$yt,$xb,$yb);
2680
            return $yb - $img->top_margin;
2681
        }
2682
        return $aYCoord;
2683
    }
2684
 
2685
    // Stroke week header and grid
2686
    function StrokeWeeks($aYCoord,$getHeight=false) {
2687
        if( $this->week->iShowLabels ) {
2688
            $img=$this->iImg;
2689
            $yt=$aYCoord+$img->top_margin;
2690
            $img->SetFont($this->week->iFFamily,$this->week->iFStyle,$this->week->iFSize);
2691
            $yb=$yt + $img->GetFontHeight() + $this->week->iTitleVertMargin + $this->week->iFrameWeight;
2692
 
2693
            if( $getHeight ) {
2694
                return $yb - $img->top_margin;
2695
            }
2696
 
2697
            $xt=$img->left_margin+$this->iLabelWidth;
2698
            $weekwidth=$this->GetDayWidth()*7;
2699
            $wdays=$this->iDateLocale->GetDayAbb();
2700
            $xb=$img->width-$img->right_margin+1;
2701
            $week = $this->iStartDate;
2702
            $weeknbr=$this->GetWeekNbr($week);
2703
            $img->SetColor($this->week->iBackgroundColor);
2704
            $img->FilledRectangle($xt,$yt,$xb,$yb);
2705
            $img->SetColor($this->week->grid->iColor);
2706
            $x = $xt;
2707
            if( $this->week->iStyle==WEEKSTYLE_WNBR ) {
2708
                $img->SetTextAlign("center");
2709
                $txtOffset = $weekwidth/2+1;
2710
            }
2711
            elseif( $this->week->iStyle==WEEKSTYLE_FIRSTDAY  ||
2712
                    $this->week->iStyle==WEEKSTYLE_FIRSTDAY2 ||
2713
                    $this->week->iStyle==WEEKSTYLE_FIRSTDAYWNBR ||
2714
                    $this->week->iStyle==WEEKSTYLE_FIRSTDAY2WNBR ) {
2715
                $img->SetTextAlign("left");
2716
                $txtOffset = 3;
2717
            }
2718
            else {
2719
                JpGraphError::RaiseL(6021);
2720
                //("Unknown formatting style for week.");
2721
            }
2722
 
2723
            for($i=0; $i<$this->GetNumberOfDays()/7; ++$i, $x+=$weekwidth) {
2724
                $img->PushColor($this->week->iTextColor);
2725
 
2726
                if( $this->week->iStyle==WEEKSTYLE_WNBR )
2727
                    $txt = sprintf($this->week->iLabelFormStr,$weeknbr);
2728
                elseif( $this->week->iStyle==WEEKSTYLE_FIRSTDAY ||
2729
                        $this->week->iStyle==WEEKSTYLE_FIRSTDAYWNBR )
2730
                    $txt = date("j/n",$week);
2731
                elseif( $this->week->iStyle==WEEKSTYLE_FIRSTDAY2 ||
2732
                        $this->week->iStyle==WEEKSTYLE_FIRSTDAY2WNBR ) {
2733
                    $monthnbr = date("n",$week)-1;
2734
                    $shortmonth = $this->iDateLocale->GetShortMonthName($monthnbr);
2735
                    $txt = Date("j",$week)." ".$shortmonth;
2736
                }
2737
 
2738
                if( $this->week->iStyle==WEEKSTYLE_FIRSTDAYWNBR ||
2739
                $this->week->iStyle==WEEKSTYLE_FIRSTDAY2WNBR ) {
2740
                    $w = sprintf($this->week->iLabelFormStr,$weeknbr);
2741
                    $txt .= ' '.$w;
2742
                }
2743
 
2744
                $img->StrokeText(round($x+$txtOffset),
2745
                round($yb-$this->week->iTitleVertMargin),$txt);
2746
 
2747
                $week = strtotime('+7 day',$week);
2748
                $weeknbr = $this->GetWeekNbr($week);
2749
                $img->PopColor();
2750
                $img->SetLineWeight($this->week->grid->iWeight);
2751
                $img->Line($x,$yt,$x,$yb);
2752
                $this->week->grid->Stroke($img,$x,$yb,$x,$img->height-$img->bottom_margin);
2753
            }
2754
            $img->SetColor($this->week->iFrameColor);
2755
            $img->SetLineWeight($this->week->iFrameWeight);
2756
            $img->Rectangle($xt,$yt,$xb,$yb);
2757
            return $yb-$img->top_margin;
2758
        }
2759
        return $aYCoord;
2760
    }
2761
 
2762
    // Format the mont scale header string
2763
    function GetMonthLabel($aMonthNbr,$year) {
2764
        $sn = $this->iDateLocale->GetShortMonthName($aMonthNbr);
2765
        $ln = $this->iDateLocale->GetLongMonthName($aMonthNbr);
2766
        switch($this->month->iStyle) {
2767
            case MONTHSTYLE_SHORTNAME:
2768
                $m=$sn;
2769
                break;
2770
            case MONTHSTYLE_LONGNAME:
2771
                $m=$ln;
2772
                break;
2773
            case MONTHSTYLE_SHORTNAMEYEAR2:
2774
                $m=$sn." '".substr("".$year,2);
2775
                break;
2776
            case MONTHSTYLE_SHORTNAMEYEAR4:
2777
                $m=$sn." ".$year;
2778
                break;
2779
            case MONTHSTYLE_LONGNAMEYEAR2:
2780
                $m=$ln." '".substr("".$year,2);
2781
                break;
2782
            case MONTHSTYLE_LONGNAMEYEAR4:
2783
                $m=$ln." ".$year;
2784
                break;
2785
            case MONTHSTYLE_FIRSTLETTER:
2786
                $m=$sn[0];
2787
                break;
2788
        }
2789
        return $m;
2790
    }
2791
 
2792
    // Stroke month scale and gridlines
2793
    function StrokeMonths($aYCoord,$getHeight=false) {
2794
        if( $this->month->iShowLabels ) {
2795
            $img=$this->iImg;
2796
            $img->SetFont($this->month->iFFamily,$this->month->iFStyle,$this->month->iFSize);
2797
            $yt=$aYCoord+$img->top_margin;
2798
            $yb=$yt + $img->GetFontHeight() + $this->month->iTitleVertMargin + $this->month->iFrameWeight;
2799
            if( $getHeight ) {
2800
                return $yb - $img->top_margin;
2801
            }
2802
            $monthnbr = $this->GetMonthNbr($this->iStartDate)-1;
2803
            $xt=$img->left_margin+$this->iLabelWidth;
2804
            $xb=$img->width-$img->right_margin+1;
2805
 
2806
            $img->SetColor($this->month->iBackgroundColor);
2807
            $img->FilledRectangle($xt,$yt,$xb,$yb);
2808
 
2809
            $img->SetLineWeight($this->month->grid->iWeight);
2810
            $img->SetColor($this->month->iTextColor);
2811
            $year = 0+strftime("%Y",$this->iStartDate);
2812
            $img->SetTextAlign("center");
2813
            if( $this->GetMonthNbr($this->iStartDate) == $this->GetMonthNbr($this->iEndDate)
2814
                && $this->GetYear($this->iStartDate)==$this->GetYear($this->iEndDate) ) {
2815
                $monthwidth=$this->GetDayWidth()*($this->GetMonthDayNbr($this->iEndDate) - $this->GetMonthDayNbr($this->iStartDate) + 1);
2816
            }
2817
            else {
2818
                $monthwidth=$this->GetDayWidth()*($this->GetNumDaysInMonth($monthnbr,$year)-$this->GetMonthDayNbr($this->iStartDate)+1);
2819
            }
2820
            // Is it enough space to stroke the first month?
2821
            $monthName = $this->GetMonthLabel($monthnbr,$year);
2822
            if( $monthwidth >= 1.2*$img->GetTextWidth($monthName) ) {
2823
                $img->SetColor($this->month->iTextColor);
2824
                $img->StrokeText(round($xt+$monthwidth/2+1),
2825
                round($yb-$this->month->iTitleVertMargin),
2826
                $monthName);
2827
            }
2828
            $x = $xt + $monthwidth;
2829
            while( $x < $xb ) {
2830
                $img->SetColor($this->month->grid->iColor);
2831
                $img->Line($x,$yt,$x,$yb);
2832
                $this->month->grid->Stroke($img,$x,$yb,$x,$img->height-$img->bottom_margin);
2833
                $monthnbr++;
2834
                if( $monthnbr==12 ) {
2835
                    $monthnbr=0;
2836
                    $year++;
2837
                }
2838
                $monthName = $this->GetMonthLabel($monthnbr,$year);
2839
                $monthwidth=$this->GetDayWidth()*$this->GetNumDaysInMonth($monthnbr,$year);
2840
                if( $x + $monthwidth < $xb )
2841
                    $w = $monthwidth;
2842
                else
2843
                    $w = $xb-$x;
2844
                if( $w >= 1.2*$img->GetTextWidth($monthName) ) {
2845
                    $img->SetColor($this->month->iTextColor);
2846
                    $img->StrokeText(round($x+$w/2+1),
2847
                    round($yb-$this->month->iTitleVertMargin),$monthName);
2848
                }
2849
                $x += $monthwidth;
2850
            }
2851
            $img->SetColor($this->month->iFrameColor);
2852
            $img->SetLineWeight($this->month->iFrameWeight);
2853
            $img->Rectangle($xt,$yt,$xb,$yb);
2854
            return $yb-$img->top_margin;
2855
        }
2856
        return $aYCoord;
2857
    }
2858
 
2859
    // Stroke year scale and gridlines
2860
    function StrokeYears($aYCoord,$getHeight=false) {
2861
        if( $this->year->iShowLabels ) {
2862
            $img=$this->iImg;
2863
            $yt=$aYCoord+$img->top_margin;
2864
            $img->SetFont($this->year->iFFamily,$this->year->iFStyle,$this->year->iFSize);
2865
            $yb=$yt + $img->GetFontHeight() + $this->year->iTitleVertMargin + $this->year->iFrameWeight;
2866
 
2867
            if( $getHeight ) {
2868
                return $yb - $img->top_margin;
2869
            }
2870
 
2871
            $xb=$img->width-$img->right_margin+1;
2872
            $xt=$img->left_margin+$this->iLabelWidth;
2873
            $year = $this->GetYear($this->iStartDate);
2874
            $img->SetColor($this->year->iBackgroundColor);
2875
            $img->FilledRectangle($xt,$yt,$xb,$yb);
2876
            $img->SetLineWeight($this->year->grid->iWeight);
2877
            $img->SetTextAlign("center");
2878
            if( $year == $this->GetYear($this->iEndDate) )
2879
                $yearwidth=$this->GetDayWidth()*($this->GetYearDayNbr($this->iEndDate)-$this->GetYearDayNbr($this->iStartDate)+1);
2880
            else
2881
                $yearwidth=$this->GetDayWidth()*($this->GetNumDaysInYear($year)-$this->GetYearDayNbr($this->iStartDate)+1);
2882
 
2883
            // The space for a year must be at least 20% bigger than the actual text
2884
            // so we allow 10% margin on each side
2885
            if( $yearwidth >= 1.20*$img->GetTextWidth("".$year) ) {
2886
                $img->SetColor($this->year->iTextColor);
2887
                $img->StrokeText(round($xt+$yearwidth/2+1),
2888
                                 round($yb-$this->year->iTitleVertMargin),
2889
                                 $year);
2890
            }
2891
            $x = $xt + $yearwidth;
2892
            while( $x < $xb ) {
2893
                $img->SetColor($this->year->grid->iColor);
2894
                $img->Line($x,$yt,$x,$yb);
2895
                $this->year->grid->Stroke($img,$x,$yb,$x,$img->height-$img->bottom_margin);
2896
                $year += 1;
2897
                $yearwidth=$this->GetDayWidth()*$this->GetNumDaysInYear($year);
2898
                if( $x + $yearwidth < $xb )
2899
                    $w = $yearwidth;
2900
                else
2901
                    $w = $xb-$x;
2902
                if( $w >= 1.2*$img->GetTextWidth("".$year) ) {
2903
                    $img->SetColor($this->year->iTextColor);
2904
                    $img->StrokeText(round($x+$w/2+1),
2905
                                     round($yb-$this->year->iTitleVertMargin),
2906
                                     $year);
2907
                }
2908
                $x += $yearwidth;
2909
            }
2910
            $img->SetColor($this->year->iFrameColor);
2911
            $img->SetLineWeight($this->year->iFrameWeight);
2912
            $img->Rectangle($xt,$yt,$xb,$yb);
2913
            return $yb-$img->top_margin;
2914
        }
2915
        return $aYCoord;
2916
    }
2917
 
2918
    // Stroke table title (upper left corner)
2919
    function StrokeTableHeaders($aYBottom) {
2920
        $img=$this->iImg;
2921
        $xt=$img->left_margin;
2922
        $yt=$img->top_margin;
2923
        $xb=$xt+$this->iLabelWidth;
2924
        $yb=$aYBottom+$img->top_margin;
2925
 
2926
        if( $this->tableTitle->iShow ) {
2927
            $img->SetColor($this->iTableHeaderBackgroundColor);
2928
            $img->FilledRectangle($xt,$yt,$xb,$yb);
2929
            $this->tableTitle->Align("center","top");
2930
            $this->tableTitle->Stroke($img,$xt+($xb-$xt)/2+1,$yt+2);
2931
            $img->SetColor($this->iTableHeaderFrameColor);
2932
            $img->SetLineWeight($this->iTableHeaderFrameWeight);
2933
            $img->Rectangle($xt,$yt,$xb,$yb);
2934
        }
2935
 
2936
        $this->actinfo->Stroke($img,$xt,$yt,$xb,$yb,$this->tableTitle->iShow);
2937
 
2938
 
2939
        // Draw the horizontal dividing line
2940
        $this->dividerh->Stroke($img,$xt,$yb,$img->width-$img->right_margin,$yb);
2941
 
2942
        // Draw the vertical dividing line
2943
        // We do the width "manually" since we want the line only to grow
2944
        // to the left
2945
        $fancy = $this->divider->iStyle == 'fancy' ;
2946
        if( $fancy ) {
2947
            $this->divider->iStyle = 'solid';
2948
        }
2949
 
2950
        $tmp = $this->divider->iWeight;
2951
        $this->divider->iWeight=1;
2952
        $y = $img->height-$img->bottom_margin;
2953
        for($i=0; $i < $tmp; ++$i ) {
2954
            $this->divider->Stroke($img,$xb-$i,$yt,$xb-$i,$y);
2955
        }
2956
 
2957
        // Should we draw "fancy" divider
2958
        if( $fancy ) {
2959
            $img->SetLineWeight(1);
2960
            $img->SetColor($this->iTableHeaderFrameColor);
2961
            $img->Line($xb,$yt,$xb,$y);
2962
            $img->Line($xb-$tmp+1,$yt,$xb-$tmp+1,$y);
2963
            $img->SetColor('white');
2964
            $img->Line($xb-$tmp+2,$yt,$xb-$tmp+2,$y);
2965
        }
2966
    }
2967
 
2968
    // Main entry point to stroke scale
2969
    function Stroke() {
2970
        if( !$this->IsRangeSet() ) {
2971
        	JpGraphError::RaiseL(6022);
2972
        	//("Gantt scale has not been specified.");
2973
        }
2974
        $img=$this->iImg;
2975
 
2976
        // If minutes are displayed then hour interval must be 1
2977
        if( $this->IsDisplayMinute() && $this->hour->GetIntervall() > 1 ) {
2978
            JpGraphError::RaiseL(6023);
2979
            //('If you display both hour and minutes the hour intervall must be 1 (Otherwise it doesn\' make sense to display minutes).');
2980
        }
2981
 
2982
        // Stroke all headers. As argument we supply the offset from the
2983
        // top which depends on any previous headers
2984
 
2985
        // First find out the height of each header
2986
        $offy=$this->StrokeYears(0,true);
2987
        $offm=$this->StrokeMonths($offy,true);
2988
        $offw=$this->StrokeWeeks($offm,true);
2989
        $offd=$this->StrokeDays($offw,true);
2990
        $offh=$this->StrokeHours($offd,true);
2991
        $offmin=$this->StrokeMinutes($offh,true);
2992
 
2993
 
2994
        // ... then we can stroke them in the "backwards order to ensure that
2995
        // the larger scale gridlines is stroked over the smaller scale gridline
2996
        $this->StrokeMinutes($offh);
2997
        $this->StrokeHours($offd);
2998
        $this->StrokeDays($offw);
2999
        $this->StrokeWeeks($offm);
3000
        $this->StrokeMonths($offy);
3001
        $this->StrokeYears(0);
3002
 
3003
        // Now when we now the oaverall size of the scale headers
3004
        // we can stroke the overall table headers
3005
        $this->StrokeTableHeaders($offmin);
3006
 
3007
        // Now we can calculate the correct scaling factor for each vertical position
3008
        $this->iAvailableHeight = $img->height - $img->top_margin - $img->bottom_margin - $offd;
3009
 
3010
        $this->iVertHeaderSize = $offmin;
3011
        if( $this->iVertSpacing == -1 )
3012
        	$this->iVertSpacing = $this->iAvailableHeight / $this->iVertLines;
3013
    }
3014
}
3015
 
3016
 
3017
//===================================================
3018
// CLASS GanttConstraint
3019
// Just a structure to store all the values for a constraint
3020
//===================================================
3021
class GanttConstraint {
3022
    public $iConstrainRow;
3023
    public $iConstrainType;
3024
    public $iConstrainColor;
3025
    public $iConstrainArrowSize;
3026
    public $iConstrainArrowType;
3027
 
3028
    //---------------
3029
    // CONSTRUCTOR
3030
    function __construct($aRow,$aType,$aColor,$aArrowSize,$aArrowType){
3031
        $this->iConstrainType = $aType;
3032
        $this->iConstrainRow = $aRow;
3033
        $this->iConstrainColor=$aColor;
3034
        $this->iConstrainArrowSize=$aArrowSize;
3035
        $this->iConstrainArrowType=$aArrowType;
3036
    }
3037
}
3038
 
3039
 
3040
//===================================================
3041
// CLASS GanttPlotObject
3042
// The common signature for a Gantt object
3043
//===================================================
3044
class GanttPlotObject {
3045
    public $title,$caption;
3046
    public $csimarea='',$csimtarget='',$csimwintarget='',$csimalt='';
3047
    public $constraints = array();
3048
    public $iCaptionMargin=5;
3049
    public $iConstrainPos=array();
3050
    protected $iStart="";    // Start date
3051
    public $iVPos=0;     // Vertical position
3052
    protected $iLabelLeftMargin=2; // Title margin
3053
 
3054
    function __construct() {
3055
        $this->title = new TextProperty();
3056
        $this->title->Align('left','center');
3057
        $this->caption = new TextProperty();
3058
    }
3059
 
3060
    function GetCSIMArea() {
3061
        return $this->csimarea;
3062
    }
3063
 
3064
    function SetCSIMTarget($aTarget,$aAlt='',$aWinTarget='') {
3065
        if( !is_string($aTarget) ) {
3066
            $tv = substr(var_export($aTarget,true),0,40);
3067
            JpGraphError::RaiseL(6024,$tv);
3068
            //('CSIM Target must be specified as a string.'."\nStart of target is:\n$tv");
3069
        }
3070
        if( !is_string($aAlt) ) {
3071
            $tv = substr(var_export($aAlt,true),0,40);
3072
            JpGraphError::RaiseL(6025,$tv);
3073
            //('CSIM Alt text must be specified as a string.'."\nStart of alt text is:\n$tv");
3074
        }
3075
 
3076
        $this->csimtarget=$aTarget;
3077
        $this->csimwintarget=$aWinTarget;
3078
        $this->csimalt=$aAlt;
3079
    }
3080
 
3081
    function SetCSIMAlt($aAlt) {
3082
        if( !is_string($aAlt) ) {
3083
            $tv = substr(var_export($aAlt,true),0,40);
3084
            JpGraphError::RaiseL(6025,$tv);
3085
            //('CSIM Alt text must be specified as a string.'."\nStart of alt text is:\n$tv");
3086
        }
3087
        $this->csimalt=$aAlt;
3088
    }
3089
 
3090
    function SetConstrain($aRow,$aType,$aColor='black',$aArrowSize=ARROW_S2,$aArrowType=ARROWT_SOLID) {
3091
        $this->constraints[] = new GanttConstraint($aRow, $aType, $aColor, $aArrowSize, $aArrowType);
3092
    }
3093
 
3094
    function SetConstrainPos($xt,$yt,$xb,$yb) {
3095
        $this->iConstrainPos = array($xt,$yt,$xb,$yb);
3096
    }
3097
 
3098
    function GetMinDate() {
3099
        return $this->iStart;
3100
    }
3101
 
3102
    function GetMaxDate() {
3103
        return $this->iStart;
3104
    }
3105
 
3106
    function SetCaptionMargin($aMarg) {
3107
        $this->iCaptionMargin=$aMarg;
3108
    }
3109
 
3110
    function GetAbsHeight($aImg) {
3111
        return 0;
3112
    }
3113
 
3114
    function GetLineNbr() {
3115
        return $this->iVPos;
3116
    }
3117
 
3118
    function SetLabelLeftMargin($aOff) {
3119
        $this->iLabelLeftMargin=$aOff;
3120
    }
3121
 
3122
    function StrokeActInfo($aImg,$aScale,$aYPos) {
3123
        $cols=array();
3124
        $aScale->actinfo->GetColStart($aImg,$cols,true);
3125
        $this->title->Stroke($aImg,$cols,$aYPos);
3126
    }
3127
}
3128
 
3129
//===================================================
3130
// CLASS Progress
3131
// Holds parameters for the progress indicator
3132
// displyed within a bar
3133
//===================================================
3134
class Progress {
3135
    public $iProgress=-1;
3136
    public $iPattern=GANTT_SOLID;
3137
    public $iColor="black", $iFillColor='black';
3138
    public $iDensity=98, $iHeight=0.65;
3139
 
3140
    function Set($aProg) {
3141
        if( $aProg < 0.0 || $aProg > 1.0 ) {
3142
        	JpGraphError::RaiseL(6027);
3143
        	//("Progress value must in range [0, 1]");
3144
        }
3145
        $this->iProgress = $aProg;
3146
    }
3147
 
3148
    function SetPattern($aPattern,$aColor="blue",$aDensity=98) {
3149
        $this->iPattern = $aPattern;
3150
        $this->iColor = $aColor;
3151
        $this->iDensity = $aDensity;
3152
    }
3153
 
3154
    function SetFillColor($aColor) {
3155
        $this->iFillColor = $aColor;
3156
    }
3157
 
3158
    function SetHeight($aHeight) {
3159
        $this->iHeight = $aHeight;
3160
    }
3161
}
3162
 
3163
define('GANTT_HGRID1',0);
3164
define('GANTT_HGRID2',1);
3165
 
3166
//===================================================
3167
// CLASS HorizontalGridLine
3168
// Responsible for drawinf horizontal gridlines and filled alternatibg rows
3169
//===================================================
3170
class HorizontalGridLine {
3171
    private $iGraph=NULL;
3172
    private $iRowColor1 = '', $iRowColor2 = '';
3173
    private $iShow=false;
3174
    private $line=null;
3175
    private $iStart=0; // 0=from left margin, 1=just along header
3176
 
3177
    function __construct() {
3178
        $this->line = new LineProperty();
3179
        $this->line->SetColor('gray@0.4');
3180
        $this->line->SetStyle('dashed');
3181
    }
3182
 
3183
    function Show($aShow=true) {
3184
        $this->iShow = $aShow;
3185
    }
3186
 
3187
    function SetRowFillColor($aColor1,$aColor2='') {
3188
        $this->iRowColor1 = $aColor1;
3189
        $this->iRowColor2 = $aColor2;
3190
    }
3191
 
3192
    function SetStart($aStart) {
3193
        $this->iStart = $aStart;
3194
    }
3195
 
3196
    function Stroke($aImg,$aScale) {
3197
 
3198
        if( ! $this->iShow ) return;
3199
 
3200
        // Get horizontal width of line
3201
        /*
3202
        $limst = $aScale->iStartDate;
3203
        $limen = $aScale->iEndDate;
3204
        $xt = round($aScale->TranslateDate($aScale->iStartDate));
3205
        $xb = round($aScale->TranslateDate($limen));
3206
        */
3207
 
3208
        if( $this->iStart === 0 ) {
3209
            $xt = $aImg->left_margin-1;
3210
        }
3211
        else {
3212
            $xt = round($aScale->TranslateDate($aScale->iStartDate))+1;
3213
        }
3214
 
3215
        $xb = $aImg->width-$aImg->right_margin;
3216
 
3217
        $yt = round($aScale->TranslateVertPos(0));
3218
        $yb = round($aScale->TranslateVertPos(1));
3219
        $height = $yb - $yt;
3220
 
3221
        // Loop around for all lines in the chart
3222
        for($i=0; $i < $aScale->iVertLines; ++$i ) {
3223
            $yb = $yt - $height;
3224
            $this->line->Stroke($aImg,$xt,$yb,$xb,$yb);
3225
            if( $this->iRowColor1 !== '' ) {
3226
                if( $i % 2 == 0 ) {
3227
                    $aImg->PushColor($this->iRowColor1);
3228
                    $aImg->FilledRectangle($xt,$yt,$xb,$yb);
3229
                    $aImg->PopColor();
3230
                }
3231
                elseif( $this->iRowColor2 !== '' ) {
3232
                    $aImg->PushColor($this->iRowColor2);
3233
                    $aImg->FilledRectangle($xt,$yt,$xb,$yb);
3234
                    $aImg->PopColor();
3235
                }
3236
            }
3237
            $yt = round($aScale->TranslateVertPos($i+1));
3238
        }
3239
        $yb = $yt - $height;
3240
        $this->line->Stroke($aImg,$xt,$yb,$xb,$yb);
3241
    }
3242
}
3243
 
3244
 
3245
//===================================================
3246
// CLASS GanttBar
3247
// Responsible for formatting individual gantt bars
3248
//===================================================
3249
class GanttBar extends GanttPlotObject {
3250
    public $progress;
3251
    public $leftMark,$rightMark;
3252
    private $iEnd;
3253
    private $iHeightFactor=0.5;
3254
    private $iFillColor="white",$iFrameColor="black";
3255
    private $iShadow=false,$iShadowColor="darkgray",$iShadowWidth=1,$iShadowFrame="black";
3256
    private $iPattern=GANTT_RDIAG,$iPatternColor="blue",$iPatternDensity=95;
3257
    private $iBreakStyle=false, $iBreakLineStyle='dotted',$iBreakLineWeight=1;
3258
    //---------------
3259
    // CONSTRUCTOR
3260
    function __construct($aPos,$aLabel,$aStart,$aEnd,$aCaption="",$aHeightFactor=0.6) {
3261
        parent::__construct();
3262
        $this->iStart = $aStart;
3263
        // Is the end date given as a date or as number of days added to start date?
3264
        if( is_string($aEnd) ) {
3265
            // If end date has been specified without a time we will asssume
3266
            // end date is at the end of that date
3267
            if( strpos($aEnd,':') === false ) {
3268
            	$this->iEnd = strtotime($aEnd)+SECPERDAY-1;
3269
            }
3270
            else {
3271
            	$this->iEnd = $aEnd;
3272
            }
3273
        }
3274
        elseif(is_int($aEnd) || is_float($aEnd) ) {
3275
        	$this->iEnd = strtotime($aStart)+round($aEnd*SECPERDAY);
3276
        }
3277
        $this->iVPos = $aPos;
3278
        $this->iHeightFactor = $aHeightFactor;
3279
        $this->title->Set($aLabel);
3280
        $this->caption = new TextProperty($aCaption);
3281
        $this->caption->Align("left","center");
3282
        $this->leftMark =new PlotMark();
3283
        $this->leftMark->Hide();
3284
        $this->rightMark=new PlotMark();
3285
        $this->rightMark->Hide();
3286
        $this->progress = new Progress();
3287
    }
3288
 
3289
    //---------------
3290
    // PUBLIC METHODS
3291
    function SetShadow($aShadow=true,$aColor="gray") {
3292
        $this->iShadow=$aShadow;
3293
        $this->iShadowColor=$aColor;
3294
    }
3295
 
3296
    function SetBreakStyle($aFlg=true,$aLineStyle='dotted',$aLineWeight=1) {
3297
    	$this->iBreakStyle = $aFlg;
3298
    	$this->iBreakLineStyle = $aLineStyle;
3299
    	$this->iBreakLineWeight = $aLineWeight;
3300
    }
3301
 
3302
    function GetMaxDate() {
3303
        return $this->iEnd;
3304
    }
3305
 
3306
    function SetHeight($aHeight) {
3307
        $this->iHeightFactor = $aHeight;
3308
    }
3309
 
3310
    function SetColor($aColor) {
3311
        $this->iFrameColor = $aColor;
3312
    }
3313
 
3314
    function SetFillColor($aColor) {
3315
        $this->iFillColor = $aColor;
3316
    }
3317
 
3318
    function GetAbsHeight($aImg) {
3319
        if( is_int($this->iHeightFactor) || $this->leftMark->show || $this->rightMark->show ) {
3320
            $m=-1;
3321
            if( is_int($this->iHeightFactor) )
3322
            	$m = $this->iHeightFactor;
3323
            if( $this->leftMark->show )
3324
            	$m = max($m,$this->leftMark->width*2);
3325
            if( $this->rightMark->show )
3326
            	$m = max($m,$this->rightMark->width*2);
3327
            return $m;
3328
        }
3329
        else
3330
        	return -1;
3331
    }
3332
 
3333
    function SetPattern($aPattern,$aColor="blue",$aDensity=95) {
3334
        $this->iPattern = $aPattern;
3335
        $this->iPatternColor = $aColor;
3336
        $this->iPatternDensity = $aDensity;
3337
    }
3338
 
3339
    function Stroke($aImg,$aScale) {
3340
        $factory = new RectPatternFactory();
3341
        $prect = $factory->Create($this->iPattern,$this->iPatternColor);
3342
        $prect->SetDensity($this->iPatternDensity);
3343
 
3344
        // If height factor is specified as a float between 0,1 then we take it as meaning
3345
        // percetage of the scale width between horizontal line.
3346
        // If it is an integer > 1 we take it to mean the absolute height in pixels
3347
        if( $this->iHeightFactor > -0.0 && $this->iHeightFactor <= 1.1)
3348
        	$vs = $aScale->GetVertSpacing()*$this->iHeightFactor;
3349
        elseif(is_int($this->iHeightFactor) && $this->iHeightFactor>2 && $this->iHeightFactor < 200 )
3350
        	$vs = $this->iHeightFactor;
3351
        else {
3352
        	JpGraphError::RaiseL(6028,$this->iHeightFactor);
3353
        	//	("Specified height (".$this->iHeightFactor.") for gantt bar is out of range.");
3354
        }
3355
 
3356
        // Clip date to min max dates to show
3357
        $st = $aScale->NormalizeDate($this->iStart);
3358
        $en = $aScale->NormalizeDate($this->iEnd);
3359
 
3360
        $limst = max($st,$aScale->iStartDate);
3361
        $limen = min($en,$aScale->iEndDate);
3362
 
3363
        $xt = round($aScale->TranslateDate($limst));
3364
        $xb = round($aScale->TranslateDate($limen));
3365
        $yt = round($aScale->TranslateVertPos($this->iVPos)-$vs-($aScale->GetVertSpacing()/2-$vs/2));
3366
        $yb = round($aScale->TranslateVertPos($this->iVPos)-($aScale->GetVertSpacing()/2-$vs/2));
3367
        $middle = round($yt+($yb-$yt)/2);
3368
        $this->StrokeActInfo($aImg,$aScale,$middle);
3369
 
3370
        // CSIM for title
3371
        if( ! empty($this->title->csimtarget) ) {
3372
            $colwidth = $this->title->GetColWidth($aImg);
3373
            $colstarts=array();
3374
            $aScale->actinfo->GetColStart($aImg,$colstarts,true);
3375
            $n = min(count($colwidth),count($this->title->csimtarget));
3376
            for( $i=0; $i < $n; ++$i ) {
3377
                $title_xt = $colstarts[$i];
3378
                $title_xb = $title_xt + $colwidth[$i];
3379
                $coords = "$title_xt,$yt,$title_xb,$yt,$title_xb,$yb,$title_xt,$yb";
3380
 
3381
                if( ! empty($this->title->csimtarget[$i]) ) {
3382
                    $this->csimarea .= "<area shape=\"poly\" coords=\"$coords\" href=\"".$this->title->csimtarget[$i]."\"";
3383
 
3384
                    if( ! empty($this->title->csimwintarget[$i]) ) {
3385
                        $this->csimarea .= "target=\"".$this->title->csimwintarget[$i]."\" ";
3386
                    }
3387
 
3388
                    if( ! empty($this->title->csimalt[$i]) ) {
3389
                        $tmp = $this->title->csimalt[$i];
3390
                        $this->csimarea .= " title=\"$tmp\" alt=\"$tmp\" ";
3391
                    }
3392
                    $this->csimarea .= " />\n";
3393
                }
3394
            }
3395
        }
3396
 
3397
        // Check if the bar is totally outside the current scale range
3398
        if( $en <  $aScale->iStartDate || $st > $aScale->iEndDate )
3399
        	return;
3400
 
3401
 
3402
        // Remember the positions for the bar
3403
        $this->SetConstrainPos($xt,$yt,$xb,$yb);
3404
 
3405
 
3406
 
3407
        $prect->ShowFrame(false);
3408
        $prect->SetBackground($this->iFillColor);
3409
        if( $this->iBreakStyle ) {
3410
        	$aImg->SetColor($this->iFrameColor);
3411
        	$olds = $aImg->SetLineStyle($this->iBreakLineStyle);
3412
        	$oldw = $aImg->SetLineWeight($this->iBreakLineWeight);
3413
        	$aImg->StyleLine($xt,$yt,$xb,$yt);
3414
        	$aImg->StyleLine($xt,$yb,$xb,$yb);
3415
        	$aImg->SetLineStyle($olds);
3416
        	$aImg->SetLineWeight($oldw);
3417
        }
3418
        else {
3419
	        if( $this->iShadow ) {
3420
	            $aImg->SetColor($this->iFrameColor);
3421
	            $aImg->ShadowRectangle($xt,$yt,$xb,$yb,$this->iFillColor,$this->iShadowWidth,$this->iShadowColor);
3422
	            $prect->SetPos(new Rectangle($xt+1,$yt+1,$xb-$xt-$this->iShadowWidth-2,$yb-$yt-$this->iShadowWidth-2));
3423
	            $prect->Stroke($aImg);
3424
	        }
3425
	        else {
3426
	            $prect->SetPos(new Rectangle($xt,$yt,$xb-$xt+1,$yb-$yt+1));
3427
	            $prect->Stroke($aImg);
3428
	            $aImg->SetColor($this->iFrameColor);
3429
	            $aImg->Rectangle($xt,$yt,$xb,$yb);
3430
	        }
3431
        }
3432
        // CSIM for bar
3433
        if( ! empty($this->csimtarget) ) {
3434
 
3435
            $coords = "$xt,$yt,$xb,$yt,$xb,$yb,$xt,$yb";
3436
            $this->csimarea .= "<area shape=\"poly\" coords=\"$coords\" href=\"".$this->csimtarget."\"";
3437
 
3438
            if( !empty($this->csimwintarget) ) {
3439
                $this->csimarea .= " target=\"".$this->csimwintarget."\" ";
3440
            }
3441
 
3442
            if( $this->csimalt != '' ) {
3443
                $tmp = $this->csimalt;
3444
                $this->csimarea .= " title=\"$tmp\" alt=\"$tmp\" ";
3445
            }
3446
            $this->csimarea .= " />\n";
3447
        }
3448
 
3449
        // Draw progress bar inside activity bar
3450
        if( $this->progress->iProgress > 0 ) {
3451
 
3452
            $xtp = $aScale->TranslateDate($st);
3453
            $xbp = $aScale->TranslateDate($en);
3454
            $len = ($xbp-$xtp)*$this->progress->iProgress;
3455
 
3456
            $endpos = $xtp+$len;
3457
            if( $endpos > $xt ) {
3458
 
3459
                // Take away the length of the progress that is not visible (before the start date)
3460
                $len -= ($xt-$xtp);
3461
 
3462
                // Is the the progress bar visible after the start date?
3463
                if( $xtp < $xt )
3464
                $xtp = $xt;
3465
 
3466
                // Make sure that the progess bar doesn't extend over the end date
3467
                if( $xtp+$len-1 > $xb )
3468
                $len = $xb - $xtp ;
3469
 
3470
                $prog = $factory->Create($this->progress->iPattern,$this->progress->iColor);
3471
                $prog->SetDensity($this->progress->iDensity);
3472
                $prog->SetBackground($this->progress->iFillColor);
3473
                $barheight = ($yb-$yt+1);
3474
                if( $this->iShadow )
3475
                	$barheight -= $this->iShadowWidth;
3476
                $progressheight = floor($barheight*$this->progress->iHeight);
3477
                $marg = ceil(($barheight-$progressheight)/2);
3478
                $pos = new Rectangle($xtp,$yt + $marg, $len,$barheight-2*$marg);
3479
                $prog->SetPos($pos);
3480
                $prog->Stroke($aImg);
3481
            }
3482
        }
3483
 
3484
        // We don't plot the end mark if the bar has been capped
3485
        if( $limst == $st ) {
3486
            $y = $middle;
3487
            // We treat the RIGHT and LEFT triangle mark a little bi
3488
            // special so that these marks are placed right under the
3489
            // bar.
3490
            if( $this->leftMark->GetType() == MARK_LEFTTRIANGLE ) {
3491
                $y = $yb ;
3492
            }
3493
            $this->leftMark->Stroke($aImg,$xt,$y);
3494
        }
3495
        if( $limen == $en ) {
3496
            $y = $middle;
3497
            // We treat the RIGHT and LEFT triangle mark a little bi
3498
            // special so that these marks are placed right under the
3499
            // bar.
3500
            if( $this->rightMark->GetType() == MARK_RIGHTTRIANGLE ) {
3501
                $y = $yb ;
3502
            }
3503
            $this->rightMark->Stroke($aImg,$xb,$y);
3504
 
3505
            $margin = $this->iCaptionMargin;
3506
            if( $this->rightMark->show )
3507
            	$margin += $this->rightMark->GetWidth();
3508
            $this->caption->Stroke($aImg,$xb+$margin,$middle);
3509
        }
3510
    }
3511
}
3512
 
3513
//===================================================
3514
// CLASS MileStone
3515
// Responsible for formatting individual milestones
3516
//===================================================
3517
class MileStone extends GanttPlotObject {
3518
    public $mark;
3519
 
3520
    //---------------
3521
    // CONSTRUCTOR
3522
    function __construct($aVPos,$aLabel,$aDate,$aCaption="") {
3523
        GanttPlotObject::__construct();
3524
        $this->caption->Set($aCaption);
3525
        $this->caption->Align("left","center");
3526
        $this->caption->SetFont(FF_FONT1,FS_BOLD);
3527
        $this->title->Set($aLabel);
3528
        $this->title->SetColor("darkred");
3529
        $this->mark = new PlotMark();
3530
        $this->mark->SetWidth(10);
3531
        $this->mark->SetType(MARK_DIAMOND);
3532
        $this->mark->SetColor("darkred");
3533
        $this->mark->SetFillColor("darkred");
3534
        $this->iVPos = $aVPos;
3535
        $this->iStart = $aDate;
3536
    }
3537
 
3538
    //---------------
3539
    // PUBLIC METHODS
3540
 
3541
    function GetAbsHeight($aImg) {
3542
        return max($this->title->GetHeight($aImg),$this->mark->GetWidth());
3543
    }
3544
 
3545
    function Stroke($aImg,$aScale) {
3546
        // Put the mark in the middle at the middle of the day
3547
        $d = $aScale->NormalizeDate($this->iStart)+SECPERDAY/2;
3548
        $x = $aScale->TranslateDate($d);
3549
        $y = $aScale->TranslateVertPos($this->iVPos)-($aScale->GetVertSpacing()/2);
3550
 
3551
        $this->StrokeActInfo($aImg,$aScale,$y);
3552
 
3553
        // CSIM for title
3554
        if( ! empty($this->title->csimtarget) ) {
3555
 
3556
            $yt = round($y - $this->title->GetHeight($aImg)/2);
3557
            $yb = round($y + $this->title->GetHeight($aImg)/2);
3558
 
3559
            $colwidth = $this->title->GetColWidth($aImg);
3560
            $colstarts=array();
3561
            $aScale->actinfo->GetColStart($aImg,$colstarts,true);
3562
            $n = min(count($colwidth),count($this->title->csimtarget));
3563
            for( $i=0; $i < $n; ++$i ) {
3564
                $title_xt = $colstarts[$i];
3565
                $title_xb = $title_xt + $colwidth[$i];
3566
                $coords = "$title_xt,$yt,$title_xb,$yt,$title_xb,$yb,$title_xt,$yb";
3567
 
3568
                if( !empty($this->title->csimtarget[$i]) ) {
3569
 
3570
                    $this->csimarea .= "<area shape=\"poly\" coords=\"$coords\" href=\"".$this->title->csimtarget[$i]."\"";
3571
 
3572
                    if( !empty($this->title->csimwintarget[$i]) ) {
3573
                        $this->csimarea .= "target=\"".$this->title->csimwintarget[$i]."\"";
3574
                    }
3575
 
3576
                    if( ! empty($this->title->csimalt[$i]) ) {
3577
                        $tmp = $this->title->csimalt[$i];
3578
                        $this->csimarea .= " title=\"$tmp\" alt=\"$tmp\" ";
3579
                    }
3580
                    $this->csimarea .= " />\n";
3581
                }
3582
            }
3583
        }
3584
 
3585
        if( $d <  $aScale->iStartDate || $d > $aScale->iEndDate )
3586
        	return;
3587
 
3588
        // Remember the coordinates for any constrains linking to
3589
        // this milestone
3590
        $w = $this->mark->GetWidth()/2;
3591
        $this->SetConstrainPos($x,round($y-$w),$x,round($y+$w));
3592
 
3593
        // Setup CSIM
3594
        if( $this->csimtarget != '' ) {
3595
            $this->mark->SetCSIMTarget( $this->csimtarget );
3596
            $this->mark->SetCSIMAlt( $this->csimalt );
3597
        }
3598
 
3599
        $this->mark->Stroke($aImg,$x,$y);
3600
        $this->caption->Stroke($aImg,$x+$this->mark->width/2+$this->iCaptionMargin,$y);
3601
 
3602
        $this->csimarea .= $this->mark->GetCSIMAreas();
3603
    }
3604
}
3605
 
3606
 
3607
//===================================================
3608
// CLASS GanttVLine
3609
// Responsible for formatting individual milestones
3610
//===================================================
3611
 
3612
class TextPropertyBelow extends TextProperty {
3613
    function __construct($aTxt='') {
3614
        parent::__construct($aTxt);
3615
    }
3616
 
3617
    function GetColWidth($aImg,$aMargin=0) {
3618
        // Since we are not stroking the title in the columns
3619
        // but rather under the graph we want this to return 0.
3620
        return array(0);
3621
    }
3622
}
3623
 
3624
class GanttVLine extends GanttPlotObject {
3625
 
3626
    private $iLine,$title_margin=3, $iDayOffset=0.5;
3627
    private $iStartRow = -1, $iEndRow = -1;
3628
 
3629
    //---------------
3630
    // CONSTRUCTOR
3631
    function __construct($aDate,$aTitle="",$aColor="darkred",$aWeight=2,$aStyle="solid") {
3632
        GanttPlotObject::__construct();
3633
        $this->iLine = new LineProperty();
3634
        $this->iLine->SetColor($aColor);
3635
        $this->iLine->SetWeight($aWeight);
3636
        $this->iLine->SetStyle($aStyle);
3637
        $this->iStart = $aDate;
3638
        $this->title = new TextPropertyBelow();
3639
        $this->title->Set($aTitle);
3640
    }
3641
 
3642
    //---------------
3643
    // PUBLIC METHODS
3644
 
3645
    // Set start and end rows for the VLine. By default the entire heigh of the
3646
    // Gantt chart is used
3647
    function SetRowSpan($aStart, $aEnd=-1) {
3648
        $this->iStartRow = $aStart;
3649
        $this->iEndRow = $aEnd;
3650
    }
3651
 
3652
    function SetDayOffset($aOff=0.5) {
3653
        if( $aOff < 0.0 || $aOff > 1.0 ) {
3654
        	JpGraphError::RaiseL(6029);
3655
        	//("Offset for vertical line must be in range [0,1]");
3656
        }
3657
        $this->iDayOffset = $aOff;
3658
    }
3659
 
3660
    function SetTitleMargin($aMarg) {
3661
        $this->title_margin = $aMarg;
3662
    }
3663
 
3664
    function SetWeight($aWeight) {
3665
        $this->iLine->SetWeight($aWeight);
3666
    }
3667
 
3668
    function Stroke($aImg,$aScale) {
3669
        $d = $aScale->NormalizeDate($this->iStart);
3670
        if( $d <  $aScale->iStartDate || $d > $aScale->iEndDate )
3671
            return;
3672
        if($this->iDayOffset != 0.0)
3673
            $d += 24*60*60*$this->iDayOffset;
3674
        $x = $aScale->TranslateDate($d);//d=1006858800,
3675
 
3676
        if( $this->iStartRow > -1 ) {
3677
            $y1 = $aScale->TranslateVertPos($this->iStartRow,true) ;
3678
        }
3679
        else {
3680
            $y1 = $aScale->iVertHeaderSize+$aImg->top_margin;
3681
        }
3682
 
3683
        if( $this->iEndRow > -1 ) {
3684
            $y2 = $aScale->TranslateVertPos($this->iEndRow);
3685
        }
3686
        else {
3687
            $y2 = $aImg->height - $aImg->bottom_margin;
3688
        }
3689
 
3690
        $this->iLine->Stroke($aImg,$x,$y1,$x,$y2);
3691
        $this->title->Align("center","top");
3692
        $this->title->Stroke($aImg,$x,$y2+$this->title_margin);
3693
    }
3694
}
3695
 
3696
//===================================================
3697
// CLASS LinkArrow
3698
// Handles the drawing of a an arrow
3699
//===================================================
3700
class LinkArrow {
3701
    private $ix,$iy;
3702
    private $isizespec = array(
3703
    array(2,3),array(3,5),array(3,8),array(6,15),array(8,22));
3704
    private $iDirection=ARROW_DOWN,$iType=ARROWT_SOLID,$iSize=ARROW_S2;
3705
    private $iColor='black';
3706
 
3707
    function __construct($x,$y,$aDirection,$aType=ARROWT_SOLID,$aSize=ARROW_S2) {
3708
        $this->iDirection = $aDirection;
3709
        $this->iType = $aType;
3710
        $this->iSize = $aSize;
3711
        $this->ix = $x;
3712
        $this->iy = $y;
3713
    }
3714
 
3715
    function SetColor($aColor) {
3716
        $this->iColor = $aColor;
3717
    }
3718
 
3719
    function SetSize($aSize) {
3720
        $this->iSize = $aSize;
3721
    }
3722
 
3723
    function SetType($aType) {
3724
        $this->iType = $aType;
3725
    }
3726
 
3727
    function Stroke($aImg) {
3728
        list($dx,$dy) = $this->isizespec[$this->iSize];
3729
        $x = $this->ix;
3730
        $y = $this->iy;
3731
        switch ( $this->iDirection ) {
3732
            case ARROW_DOWN:
3733
                $c = array($x,$y,$x-$dx,$y-$dy,$x+$dx,$y-$dy,$x,$y);
3734
                break;
3735
            case ARROW_UP:
3736
                $c = array($x,$y,$x-$dx,$y+$dy,$x+$dx,$y+$dy,$x,$y);
3737
                break;
3738
            case ARROW_LEFT:
3739
                $c = array($x,$y,$x+$dy,$y-$dx,$x+$dy,$y+$dx,$x,$y);
3740
                break;
3741
            case ARROW_RIGHT:
3742
                $c = array($x,$y,$x-$dy,$y-$dx,$x-$dy,$y+$dx,$x,$y);
3743
                break;
3744
            default:
3745
                JpGraphError::RaiseL(6030);
3746
                //('Unknown arrow direction for link.');
3747
                die();
3748
                break;
3749
        }
3750
        $aImg->SetColor($this->iColor);
3751
        switch( $this->iType ) {
3752
            case ARROWT_SOLID:
3753
                $aImg->FilledPolygon($c);
3754
                break;
3755
            case ARROWT_OPEN:
3756
                $aImg->Polygon($c);
3757
                break;
3758
            default:
3759
                JpGraphError::RaiseL(6031);
3760
                //('Unknown arrow type for link.');
3761
                die();
3762
                break;
3763
        }
3764
    }
3765
}
3766
 
3767
//===================================================
3768
// CLASS GanttLink
3769
// Handles the drawing of a link line between 2 points
3770
//===================================================
3771
 
3772
class GanttLink {
3773
    private $ix1,$ix2,$iy1,$iy2;
3774
    private $iPathType=2,$iPathExtend=15;
3775
    private $iColor='black',$iWeight=1;
3776
    private $iArrowSize=ARROW_S2,$iArrowType=ARROWT_SOLID;
3777
 
3778
    function __construct($x1=0,$y1=0,$x2=0,$y2=0) {
3779
        $this->ix1 = $x1;
3780
        $this->ix2 = $x2;
3781
        $this->iy1 = $y1;
3782
        $this->iy2 = $y2;
3783
    }
3784
 
3785
    function SetPos($x1,$y1,$x2,$y2) {
3786
        $this->ix1 = $x1;
3787
        $this->ix2 = $x2;
3788
        $this->iy1 = $y1;
3789
        $this->iy2 = $y2;
3790
    }
3791
 
3792
    function SetPath($aPath) {
3793
        $this->iPathType = $aPath;
3794
    }
3795
 
3796
    function SetColor($aColor) {
3797
        $this->iColor = $aColor;
3798
    }
3799
 
3800
    function SetArrow($aSize,$aType=ARROWT_SOLID) {
3801
        $this->iArrowSize = $aSize;
3802
        $this->iArrowType = $aType;
3803
    }
3804
 
3805
    function SetWeight($aWeight) {
3806
        $this->iWeight = $aWeight;
3807
    }
3808
 
3809
    function Stroke($aImg) {
3810
        // The way the path for the arrow is constructed is partly based
3811
        // on some heuristics. This is not an exact science but draws the
3812
        // path in a way that, for me, makes esthetic sence. For example
3813
        // if the start and end activities are very close we make a small
3814
        // detour to endter the target horixontally. If there are more
3815
        // space between axctivities then no suh detour is made and the
3816
        // target is "hit" directly vertical. I have tried to keep this
3817
        // simple. no doubt this could become almost infinitive complex
3818
        // and have some real AI. Feel free to modify this.
3819
        // This will no-doubt be tweaked as times go by. One design aim
3820
        // is to avoid having the user choose what types of arrow
3821
        // he wants.
3822
 
3823
        // The arrow is drawn between (x1,y1) to (x2,y2)
3824
        $x1 = $this->ix1 ;
3825
        $x2 = $this->ix2 ;
3826
        $y1 = $this->iy1 ;
3827
        $y2 = $this->iy2 ;
3828
 
3829
        // Depending on if the target is below or above we have to
3830
        // handle thi different.
3831
        if( $y2 > $y1 ) {
3832
            $arrowtype = ARROW_DOWN;
3833
            $midy = round(($y2-$y1)/2+$y1);
3834
            if( $x2 > $x1 ) {
3835
                switch ( $this->iPathType  ) {
3836
                    case 0:
3837
                        $c = array($x1,$y1,$x1,$midy,$x2,$midy,$x2,$y2);
3838
                        break;
3839
                    case 1:
3840
                    case 2:
3841
                    case 3:
3842
                        $c = array($x1,$y1,$x2,$y1,$x2,$y2);
3843
                        break;
3844
                    default:
3845
                        JpGraphError::RaiseL(6032,$this->iPathType);
3846
                        //('Internal error: Unknown path type (='.$this->iPathType .') specified for link.');
3847
                        exit(1);
3848
                        break;
3849
                }
3850
            }
3851
            else {
3852
                switch ( $this->iPathType  ) {
3853
                    case 0:
3854
                    case 1:
3855
                        $c = array($x1,$y1,$x1,$midy,$x2,$midy,$x2,$y2);
3856
                        break;
3857
                    case 2:
3858
                        // Always extend out horizontally a bit from the first point
3859
                        // If we draw a link back in time (end to start) and the bars
3860
                        // are very close we also change the path so it comes in from
3861
                        // the left on the activity
3862
                        $c = array($x1,$y1,$x1+$this->iPathExtend,$y1,
3863
                        $x1+$this->iPathExtend,$midy,
3864
                        $x2,$midy,$x2,$y2);
3865
                        break;
3866
                    case 3:
3867
                        if( $y2-$midy < 6 ) {
3868
                            $c = array($x1,$y1,$x1,$midy,
3869
                            $x2-$this->iPathExtend,$midy,
3870
                            $x2-$this->iPathExtend,$y2,
3871
                            $x2,$y2);
3872
                            $arrowtype = ARROW_RIGHT;
3873
                        }
3874
                        else {
3875
                            $c = array($x1,$y1,$x1,$midy,$x2,$midy,$x2,$y2);
3876
                        }
3877
                        break;
3878
                    default:
3879
                        JpGraphError::RaiseL(6032,$this->iPathType);
3880
                        //('Internal error: Unknown path type specified for link.');
3881
                        exit(1);
3882
                        break;
3883
                }
3884
            }
3885
            $arrow = new LinkArrow($x2,$y2,$arrowtype);
3886
        }
3887
        else {
3888
            // Y2 < Y1
3889
            $arrowtype = ARROW_UP;
3890
            $midy = round(($y1-$y2)/2+$y2);
3891
            if( $x2 > $x1 ) {
3892
                switch ( $this->iPathType  ) {
3893
                    case 0:
3894
                    case 1:
3895
                        $c = array($x1,$y1,$x1,$midy,$x2,$midy,$x2,$y2);
3896
                        break;
3897
                    case 3:
3898
                        if( $midy-$y2 < 8 ) {
3899
                            $arrowtype = ARROW_RIGHT;
3900
                            $c = array($x1,$y1,$x1,$y2,$x2,$y2);
3901
                        }
3902
                        else {
3903
                            $c = array($x1,$y1,$x1,$midy,$x2,$midy,$x2,$y2);
3904
                        }
3905
                        break;
3906
                    default:
3907
                        JpGraphError::RaiseL(6032,$this->iPathType);
3908
                        //('Internal error: Unknown path type specified for link.');
3909
                        break;
3910
                }
3911
            }
3912
            else {
3913
                switch ( $this->iPathType  ) {
3914
                    case 0:
3915
                    case 1:
3916
                        $c = array($x1,$y1,$x1,$midy,$x2,$midy,$x2,$y2);
3917
                        break;
3918
                    case 2:
3919
                        // Always extend out horizontally a bit from the first point
3920
                        $c = array($x1,$y1,$x1+$this->iPathExtend,$y1,
3921
                        $x1+$this->iPathExtend,$midy,
3922
                        $x2,$midy,$x2,$y2);
3923
                        break;
3924
                    case 3:
3925
                        if( $midy-$y2 < 16 ) {
3926
                            $arrowtype = ARROW_RIGHT;
3927
                            $c = array($x1,$y1,$x1,$midy,$x2-$this->iPathExtend,$midy,
3928
                            $x2-$this->iPathExtend,$y2,
3929
                            $x2,$y2);
3930
                        }
3931
                        else {
3932
                            $c = array($x1,$y1,$x1,$midy,$x2,$midy,$x2,$y2);
3933
                        }
3934
                        break;
3935
                    default:
3936
                        JpGraphError::RaiseL(6032,$this->iPathType);
3937
                        //('Internal error: Unknown path type specified for link.');
3938
                        break;
3939
                }
3940
            }
3941
            $arrow = new LinkArrow($x2,$y2,$arrowtype);
3942
        }
3943
        $aImg->SetColor($this->iColor);
3944
        $aImg->SetLineWeight($this->iWeight);
3945
        $aImg->Polygon($c);
3946
        $aImg->SetLineWeight(1);
3947
        $arrow->SetColor($this->iColor);
3948
        $arrow->SetSize($this->iArrowSize);
3949
        $arrow->SetType($this->iArrowType);
3950
        $arrow->Stroke($aImg);
3951
    }
3952
}
3953
 
3954
// <EOF>
3955
?>