Subversion Repositories Applications.papyrus

Rev

Details | Last modification | View Log | RSS feed

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