Subversion Repositories Sites.obs-saisons.fr

Rev

Go to most recent revision | Details | Last modification | View Log | RSS feed

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