Subversion-Projekte lars-tiefland.php_share

Revision

Details | Letzte Änderung | Log anzeigen | RSS feed

Revision Autor Zeilennr. Zeile
1 lars 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 857 2007-03-23 19:03:13Z 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='',$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='') {
1522
	if( is_string($aTarget) )
1523
	    $aTarget = array($aTarget);
1524
	$this->csimtarget=$aTarget;
1525
	if( is_string($aAltText) )
1526
	    $aAltText = array($aAltText);
1527
        $this->csimalt=$aAltText;
1528
    }
1529
 
1530
    function SetCSIMAlt($aAltText) {
1531
	if( is_string($aAltText) )
1532
	    $aAltText = array($aAltText);
1533
        $this->csimalt=$aAltText;
1534
    }
1535
 
1536
    // Set text color
1537
    function SetColor($aColor) {
1538
	$this->iColor = $aColor;
1539
    }
1540
 
1541
    function HasTabs() {
1542
	if( is_string($this->iText) ) {
1543
	    return substr_count($this->iText,"\t") > 0;
1544
	}
1545
	elseif( is_array($this->iText) ) {
1546
	    return false;
1547
	}
1548
    }
1549
 
1550
    // Get number of tabs in string
1551
    function GetNbrTabs() {
1552
	if( is_string($this->iText) ) {
1553
	    return substr_count($this->iText,"\t") ;
1554
	}
1555
	else{
1556
	    return 0;
1557
	}
1558
    }
1559
 
1560
    // Set alignment
1561
    function Align($aHAlign,$aVAlign="bottom") {
1562
	$this->iHAlign=$aHAlign;
1563
	$this->iVAlign=$aVAlign;
1564
    }
1565
 
1566
    // Synonym
1567
    function SetAlign($aHAlign,$aVAlign="bottom") {
1568
	$this->iHAlign=$aHAlign;
1569
	$this->iVAlign=$aVAlign;
1570
    }
1571
 
1572
    // Specify font
1573
    function SetFont($aFFamily,$aFStyle=FS_NORMAL,$aFSize=10) {
1574
	$this->iFFamily = $aFFamily;
1575
	$this->iFStyle	 = $aFStyle;
1576
	$this->iFSize	 = $aFSize;
1577
    }
1578
 
1579
    function IsColumns() {
1580
	return is_array($this->iText) ;
1581
    }
1582
 
1583
    // Get width of text. If text contains several columns separated by
1584
    // tabs then return both the total width as well as an array with a
1585
    // width for each column.
1586
    function GetWidth(&$aImg,$aUseTabs=false,$aTabExtraMargin=1.1) {
1587
	$extra_margin=4;
1588
	$aImg->SetFont($this->iFFamily,$this->iFStyle,$this->iFSize);
1589
	if( is_string($this->iText) ) {
1590
	    if( strlen($this->iText) == 0 ) return 0;
1591
	    $tmp = split("\t",$this->iText);
1592
	    if( count($tmp) <= 1 || !$aUseTabs ) {
1593
		$w = $aImg->GetTextWidth($this->iText);
1594
		return $w + 2*$extra_margin;
1595
	    }
1596
	    else {
1597
		$tot=0;
1598
		$n = count($tmp);
1599
		for($i=0; $i < $n; ++$i) {
1600
		    $res[$i] = $aImg->GetTextWidth($tmp[$i]);
1601
		    $tot += $res[$i]*$aTabExtraMargin;
1602
		}
1603
		return array(round($tot),$res);
1604
	    }
1605
	}
1606
	elseif( is_object($this->iText) ) {
1607
	    // A single icon
1608
	    return $this->iText->GetWidth()+2*$extra_margin;
1609
	}
1610
	elseif( is_array($this->iText) ) {
1611
	    // Must be an array of texts. In this case we return the sum of the
1612
	    // length + a fixed margin of 4 pixels on each text string
1613
	    $n = count($this->iText);
1614
	    for( $i=0, $w=0; $i < $n; ++$i ) {
1615
		$tmp = $this->iText[$i];
1616
		if( is_string($tmp) ) {
1617
		    $w += $aImg->GetTextWidth($tmp)+$extra_margin;
1618
		}
1619
		else {
1620
		    if( is_object($tmp) === false ) {
1621
			JpGraphError::RaiseL(6012);
1622
		    }
1623
		    $w += $tmp->GetWidth()+$extra_margin;
1624
		}
1625
	    }
1626
	    return $w;
1627
	}
1628
	else {
1629
	    JpGraphError::RaiseL(6012);
1630
	}
1631
    }
1632
 
1633
    // for the case where we have multiple columns this function returns the width of each
1634
    // column individually. If there is no columns just return the width of the single
1635
    // column as an array of one
1636
    function GetColWidth(&$aImg,$aMargin=0) {
1637
	$aImg->SetFont($this->iFFamily,$this->iFStyle,$this->iFSize);
1638
	if( is_array($this->iText) ) {
1639
	    $n = count($this->iText);
1640
	    for( $i=0, $w=array(); $i < $n; ++$i ) {
1641
		$tmp = $this->iText[$i];
1642
		if( is_string($tmp) ) {
1643
		    $w[$i] = $aImg->GetTextWidth($this->iText[$i])+$aMargin;
1644
		}
1645
		else {
1646
		    if( is_object($tmp) === false ) {
1647
			JpGraphError::RaiseL(6012);
1648
		    }
1649
		    $w[$i] = $tmp->GetWidth()+$aMargin;
1650
		}
1651
	    }
1652
	    return $w;
1653
	}
1654
	else {
1655
	    return array($this->GetWidth($aImg));
1656
	}
1657
    }
1658
 
1659
    // Get total height of text
1660
    function GetHeight(&$aImg) {
1661
	$aImg->SetFont($this->iFFamily,$this->iFStyle,$this->iFSize);
1662
	return $aImg->GetFontHeight();
1663
    }
1664
 
1665
    // Unhide/hide the text
1666
    function Show($aShow=true) {
1667
	$this->iShow=$aShow;
1668
    }
1669
 
1670
    // Stroke text at (x,y) coordinates. If the text contains tabs then the
1671
    // x parameter should be an array of positions to be used for each successive
1672
    // tab mark. If no array is supplied then the tabs will be ignored.
1673
    function Stroke(&$aImg,$aX,$aY) {
1674
	if( $this->iShow ) {
1675
	    $aImg->SetColor($this->iColor);
1676
	    $aImg->SetFont($this->iFFamily,$this->iFStyle,$this->iFSize);
1677
	    $aImg->SetTextAlign($this->iHAlign,$this->iVAlign);
1678
	    if( $this->GetNbrTabs() <= 1 ) {
1679
		if( is_string($this->iText) ) {
1680
		    // Get rid of any "\t" characters and stroke string
1681
		    if( is_array($aX) ) $aX=$aX[0];
1682
		    if( is_array($aY) ) $aY=$aY[0];
1683
		    $aImg->StrokeText($aX,$aY,str_replace("\t"," ",$this->iText));
1684
		}
1685
		else {
1686
		    $n = count($this->iText);
1687
		    $ax = is_array($aX) ;
1688
		    $ay = is_array($aY) ;
1689
		    if( $ax && $ay ) {
1690
			// Nothing; both are already arrays
1691
		    }
1692
		    elseif( $ax ) {
1693
			$aY = array_fill(0,$n,$aY);
1694
		    }
1695
		    elseif( $ay ) {
1696
			$aX = array_fill(0,$n,$aX);
1697
		    }
1698
		    else {
1699
			$aX = array_fill(0,$n,$aX);
1700
			$aY = array_fill(0,$n,$aY);
1701
		    }
1702
		    $n = min($n, count($aX) ) ;
1703
		    $n = min($n, count($aY) ) ;
1704
		    for($i=0; $i < $n; ++$i ) {
1705
			$tmp = $this->iText[$i];
1706
			if( is_object($tmp) ) {
1707
			    $tmp->Stroke($aImg,$aX[$i],$aY[$i]);
1708
			}
1709
			else
1710
			    $aImg->StrokeText($aX[$i],$aY[$i],str_replace("\t"," ",$tmp));
1711
		    }
1712
		}
1713
	    }
1714
	    else {
1715
		$tmp = split("\t",$this->iText);
1716
		$n = min(count($tmp),count($aX));
1717
		for($i=0; $i < $n; ++$i) {
1718
		    $aImg->StrokeText($aX[$i],$aY,$tmp[$i]);
1719
		}
1720
	    }
1721
	}
1722
    }
1723
}
1724
 
1725
//===================================================
1726
// CLASS HeaderProperty
1727
// Description: Data encapsulating class to hold property
1728
// for each type of the scale headers
1729
//===================================================
1730
class HeaderProperty {
1731
    var $iTitleVertMargin=3,$iFFamily=FF_FONT0,$iFStyle=FS_NORMAL,$iFSize=8;
1732
    var $iFrameColor="black",$iFrameWeight=1;
1733
    var $iShowLabels=true,$iShowGrid=true;
1734
    var $iBackgroundColor="white";
1735
    var $iWeekendBackgroundColor="lightgray",$iSundayTextColor="red"; // these are only used with day scale
1736
    var $iTextColor="black";
1737
    var $iLabelFormStr="%d";
1738
    var $grid,$iStyle=0;
1739
    var $iIntervall = 1;
1740
 
1741
//---------------
1742
// CONSTRUCTOR
1743
    function HeaderProperty() {
1744
	$this->grid = new LineProperty();
1745
    }
1746
 
1747
//---------------
1748
// PUBLIC METHODS
1749
    function Show($aShow=true) {
1750
	$this->iShowLabels = $aShow;
1751
    }
1752
 
1753
    function SetIntervall($aInt) {
1754
	$this->iIntervall = $aInt;
1755
    }
1756
 
1757
    function GetIntervall() {
1758
	return $this->iIntervall ;
1759
    }
1760
 
1761
    function SetFont($aFFamily,$aFStyle=FS_NORMAL,$aFSize=10) {
1762
	$this->iFFamily = $aFFamily;
1763
	$this->iFStyle	 = $aFStyle;
1764
	$this->iFSize	 = $aFSize;
1765
    }
1766
 
1767
    function SetFontColor($aColor) {
1768
	$this->iTextColor = $aColor;
1769
    }
1770
 
1771
    function GetFontHeight(&$aImg) {
1772
	$aImg->SetFont($this->iFFamily,$this->iFStyle,$this->iFSize);
1773
	return $aImg->GetFontHeight();
1774
    }
1775
 
1776
    function GetFontWidth(&$aImg) {
1777
	$aImg->SetFont($this->iFFamily,$this->iFStyle,$this->iFSize);
1778
	return $aImg->GetFontWidth();
1779
    }
1780
 
1781
    function GetStrWidth(&$aImg,$aStr) {
1782
	$aImg->SetFont($this->iFFamily,$this->iFStyle,$this->iFSize);
1783
	return $aImg->GetTextWidth($aStr);
1784
    }
1785
 
1786
    function SetStyle($aStyle) {
1787
	$this->iStyle = $aStyle;
1788
    }
1789
 
1790
    function SetBackgroundColor($aColor) {
1791
	$this->iBackgroundColor=$aColor;
1792
    }
1793
 
1794
    function SetFrameWeight($aWeight) {
1795
	$this->iFrameWeight=$aWeight;
1796
    }
1797
 
1798
    function SetFrameColor($aColor) {
1799
	$this->iFrameColor=$aColor;
1800
    }
1801
 
1802
    // Only used by day scale
1803
    function SetWeekendColor($aColor) {
1804
	$this->iWeekendBackgroundColor=$aColor;
1805
    }
1806
 
1807
    // Only used by day scale
1808
    function SetSundayFontColor($aColor) {
1809
	$this->iSundayTextColor=$aColor;
1810
    }
1811
 
1812
    function SetTitleVertMargin($aMargin) {
1813
	$this->iTitleVertMargin=$aMargin;
1814
    }
1815
 
1816
    function SetLabelFormatString($aStr) {
1817
	$this->iLabelFormStr=$aStr;
1818
    }
1819
 
1820
    function SetFormatString($aStr) {
1821
	$this->SetLabelFormatString($aStr);
1822
    }
1823
 
1824
 
1825
}
1826
 
1827
//===================================================
1828
// CLASS GanttScale
1829
// Description: Responsible for calculating and showing
1830
// the scale in a gantt chart. This includes providing methods for
1831
// converting dates to position in the chart as well as stroking the
1832
// date headers (days, week, etc).
1833
//===================================================
1834
class GanttScale {
1835
    var $minute,$hour,$day,$week,$month,$year;
1836
    var $divider,$dividerh,$tableTitle;
1837
    var $iStartDate=-1,$iEndDate=-1;
1838
    // Number of gantt bar position (n.b not necessariliy the same as the number of bars)
1839
    // we could have on bar in position 1, and one bar in position 5 then there are two
1840
    // bars but the number of bar positions is 5
1841
    var $iVertLines=-1;
1842
    // The width of the labels (defaults to the widest of all labels)
1843
    var $iLabelWidth;
1844
    // Out image to stroke the scale to
1845
    var $iImg;
1846
    var $iTableHeaderBackgroundColor="white",$iTableHeaderFrameColor="black";
1847
    var $iTableHeaderFrameWeight=1;
1848
    var $iAvailableHeight=-1,$iVertSpacing=-1,$iVertHeaderSize=-1;
1849
    var $iDateLocale;
1850
    var $iVertLayout=GANTT_EVEN;
1851
    var $iTopPlotMargin=10,$iBottomPlotMargin=15;
1852
    var $iUsePlotWeekendBackground=true;
1853
    var $iWeekStart = 1;	// Default to have weekends start on Monday
1854
    var $actinfo;
1855
 
1856
//---------------
1857
// CONSTRUCTOR
1858
    function GanttScale(&$aImg) {
1859
	$this->iImg = &$aImg;
1860
	$this->iDateLocale = new DateLocale();
1861
 
1862
	$this->minute = new HeaderProperty();
1863
	$this->minute->SetIntervall(15);
1864
	$this->minute->SetLabelFormatString('i');
1865
	$this->minute->SetFont(FF_FONT0);
1866
	$this->minute->grid->SetColor("gray");
1867
 
1868
	$this->hour = new HeaderProperty();
1869
	$this->hour->SetFont(FF_FONT0);
1870
	$this->hour->SetIntervall(6);
1871
	$this->hour->SetStyle(HOURSTYLE_HM24);
1872
	$this->hour->SetLabelFormatString('H:i');
1873
	$this->hour->grid->SetColor("gray");
1874
 
1875
	$this->day = new HeaderProperty();
1876
	$this->day->grid->SetColor("gray");
1877
	$this->day->SetLabelFormatString('l');
1878
 
1879
	$this->week = new HeaderProperty();
1880
	$this->week->SetLabelFormatString("w%d");
1881
	$this->week->SetFont(FF_FONT1);
1882
 
1883
	$this->month = new HeaderProperty();
1884
	$this->month->SetFont(FF_FONT1,FS_BOLD);
1885
 
1886
	$this->year = new HeaderProperty();
1887
	$this->year->SetFont(FF_FONT1,FS_BOLD);
1888
 
1889
	$this->divider=new LineProperty();
1890
	$this->dividerh=new LineProperty();
1891
	$this->dividerh->SetWeight(2);
1892
	$this->divider->SetWeight(6);
1893
	$this->divider->SetColor('gray');
1894
	$this->divider->SetStyle('fancy');
1895
 
1896
	$this->tableTitle=new TextProperty();
1897
	$this->tableTitle->Show(false);
1898
	$this->actinfo = new GanttActivityInfo();
1899
    }
1900
 
1901
//---------------
1902
// PUBLIC METHODS
1903
    // Specify what headers should be visible
1904
    function ShowHeaders($aFlg) {
1905
	$this->day->Show($aFlg & GANTT_HDAY);
1906
	$this->week->Show($aFlg & GANTT_HWEEK);
1907
	$this->month->Show($aFlg & GANTT_HMONTH);
1908
	$this->year->Show($aFlg & GANTT_HYEAR);
1909
	$this->hour->Show($aFlg & GANTT_HHOUR);
1910
	$this->minute->Show($aFlg & GANTT_HMIN);
1911
 
1912
	// Make some default settings of gridlines whihc makes sense
1913
	if( $aFlg & GANTT_HWEEK ) {
1914
	    $this->month->grid->Show(false);
1915
	    $this->year->grid->Show(false);
1916
	}
1917
	if( $aFlg & GANTT_HHOUR ) {
1918
	    $this->day->grid->SetColor("black");
1919
	}
1920
    }
1921
 
1922
    // Should the weekend background stretch all the way down in the plotarea
1923
    function UseWeekendBackground($aShow) {
1924
	$this->iUsePlotWeekendBackground = $aShow;
1925
    }
1926
 
1927
    // Have a range been specified?
1928
    function IsRangeSet() {
1929
	return $this->iStartDate!=-1 && $this->iEndDate!=-1;
1930
    }
1931
 
1932
    // Should the layout be from top or even?
1933
    function SetVertLayout($aLayout) {
1934
	$this->iVertLayout = $aLayout;
1935
    }
1936
 
1937
    // Which locale should be used?
1938
    function SetDateLocale($aLocale) {
1939
	$this->iDateLocale->Set($aLocale);
1940
    }
1941
 
1942
    // Number of days we are showing
1943
    function GetNumberOfDays() {
1944
	return round(($this->iEndDate-$this->iStartDate)/SECPERDAY);
1945
    }
1946
 
1947
    // The width of the actual plot area
1948
    function GetPlotWidth() {
1949
	$img=$this->iImg;
1950
	return $img->width - $img->left_margin - $img->right_margin;
1951
    }
1952
 
1953
    // Specify the width of the titles(labels) for the activities
1954
    // (This is by default set to the minimum width enought for the
1955
    // widest title)
1956
    function SetLabelWidth($aLabelWidth) {
1957
	$this->iLabelWidth=$aLabelWidth;
1958
    }
1959
 
1960
	// Which day should the week start?
1961
	// 0==Sun, 1==Monday, 2==Tuesday etc
1962
    function SetWeekStart($aStartDay) {
1963
	$this->iWeekStart = $aStartDay % 7;
1964
 
1965
	//Recalculate the startday since this will change the week start
1966
	$this->SetRange($this->iStartDate,$this->iEndDate);
1967
    }
1968
 
1969
    // Do we show min scale?
1970
    function IsDisplayMinute() {
1971
	return $this->minute->iShowLabels;
1972
    }
1973
 
1974
    // Do we show day scale?
1975
    function IsDisplayHour() {
1976
	return $this->hour->iShowLabels;
1977
    }
1978
 
1979
 
1980
    // Do we show day scale?
1981
    function IsDisplayDay() {
1982
	return $this->day->iShowLabels;
1983
    }
1984
 
1985
    // Do we show week scale?
1986
    function IsDisplayWeek() {
1987
	return $this->week->iShowLabels;
1988
    }
1989
 
1990
    // Do we show month scale?
1991
    function IsDisplayMonth() {
1992
	return $this->month->iShowLabels;
1993
    }
1994
 
1995
    // Do we show year scale?
1996
    function IsDisplayYear() {
1997
	return $this->year->iShowLabels;
1998
    }
1999
 
2000
    // Specify spacing (in percent of bar height) between activity bars
2001
    function SetVertSpacing($aSpacing) {
2002
	$this->iVertSpacing = $aSpacing;
2003
    }
2004
 
2005
    // Specify scale min and max date either as timestamp or as date strings
2006
    // Always round to the nearest week boundary
2007
    function SetRange($aMin,$aMax) {
2008
	$this->iStartDate = $this->NormalizeDate($aMin);
2009
	$this->iEndDate = $this->NormalizeDate($aMax);
2010
    }
2011
 
2012
 
2013
    // Adjust the start and end date so they fit to beginning/ending
2014
    // of the week taking the specified week start day into account.
2015
    function AdjustStartEndDay() {
2016
 
2017
	if( !($this->IsDisplayYear() ||$this->IsDisplayMonth() || $this->IsDisplayWeek()) ) {
2018
	    // Don't adjust
2019
	    return;
2020
	}
2021
 
2022
	// Get day in week for start and ending date (Sun==0)
2023
	$ds=strftime("%w",$this->iStartDate);
2024
	$de=strftime("%w",$this->iEndDate);
2025
 
2026
	// We want to start on iWeekStart day. But first we subtract a week
2027
	// if the startdate is "behind" the day the week start at.
2028
	// This way we ensure that the given start date is always included
2029
	// in the range. If we don't do this the nearest correct weekday in the week
2030
	// to start at might be later than the start date.
2031
	if( $ds < $this->iWeekStart )
2032
	    $d = strtotime('-7 day',$this->iStartDate);
2033
	else
2034
	    $d = $this->iStartDate;
2035
	$adjdate = strtotime(($this->iWeekStart-$ds).' day',$d /*$this->iStartDate*/ );
2036
	$this->iStartDate = $adjdate;
2037
 
2038
	// We want to end on the last day of the week
2039
	$preferredEndDay = ($this->iWeekStart+6)%7;
2040
	if( $preferredEndDay != $de ) {
2041
	    // Solve equivalence eq:    $de + x ~ $preferredDay (mod 7)
2042
	    $adj = (7+($preferredEndDay - $de)) % 7;
2043
	    $adjdate = strtotime("+$adj day",$this->iEndDate);
2044
	    $this->iEndDate = $adjdate;
2045
	}
2046
    }
2047
 
2048
    // Specify background for the table title area (upper left corner of the table)
2049
    function SetTableTitleBackground($aColor) {
2050
	$this->iTableHeaderBackgroundColor = $aColor;
2051
    }
2052
 
2053
///////////////////////////////////////
2054
// PRIVATE Methods
2055
 
2056
    // Determine the height of all the scale headers combined
2057
    function GetHeaderHeight() {
2058
	$img=$this->iImg;
2059
	$height=1;
2060
	if( $this->minute->iShowLabels ) {
2061
	    $height += $this->minute->GetFontHeight($img);
2062
	    $height += $this->minute->iTitleVertMargin;
2063
	}
2064
	if( $this->hour->iShowLabels ) {
2065
	    $height += $this->hour->GetFontHeight($img);
2066
	    $height += $this->hour->iTitleVertMargin;
2067
	}
2068
	if( $this->day->iShowLabels ) {
2069
	    $height += $this->day->GetFontHeight($img);
2070
	    $height += $this->day->iTitleVertMargin;
2071
	}
2072
	if( $this->week->iShowLabels ) {
2073
	    $height += $this->week->GetFontHeight($img);
2074
	    $height += $this->week->iTitleVertMargin;
2075
	}
2076
	if( $this->month->iShowLabels ) {
2077
	    $height += $this->month->GetFontHeight($img);
2078
	    $height += $this->month->iTitleVertMargin;
2079
	}
2080
	if( $this->year->iShowLabels ) {
2081
	    $height += $this->year->GetFontHeight($img);
2082
	    $height += $this->year->iTitleVertMargin;
2083
	}
2084
	return $height;
2085
    }
2086
 
2087
    // Get width (in pixels) for a single day
2088
    function GetDayWidth() {
2089
	return ($this->GetPlotWidth()-$this->iLabelWidth+1)/$this->GetNumberOfDays();
2090
    }
2091
 
2092
    // Get width (in pixels) for a single hour
2093
    function GetHourWidth() {
2094
	return $this->GetDayWidth() / 24 ;
2095
    }
2096
 
2097
    function GetMinuteWidth() {
2098
	return $this->GetHourWidth() / 60 ;
2099
    }
2100
 
2101
    // Nuber of days in a year
2102
    function GetNumDaysInYear($aYear) {
2103
	if( $this->IsLeap($aYear) )
2104
	    return 366;
2105
	else
2106
	    return 365;
2107
    }
2108
 
2109
    // Get week number
2110
    function GetWeekNbr($aDate,$aSunStart=true) {
2111
	// We can't use the internal strftime() since it gets the weeknumber
2112
	// wrong since it doesn't follow ISO on all systems since this is
2113
	// system linrary dependent.
2114
	// Even worse is that this works differently if we are on a Windows
2115
	// or UNIX box (it even differs between UNIX boxes how strftime()
2116
	// is natively implemented)
2117
	//
2118
	// Credit to Nicolas Hoizey <nhoizey@phpheaven.net> for this elegant
2119
	// version of Week Nbr calculation.
2120
 
2121
	$day = $this->NormalizeDate($aDate) ;
2122
	if( $aSunStart )
2123
	    $day += 60*60*24;
2124
 
2125
	/*-------------------------------------------------------------------------
2126
	  According to ISO-8601 :
2127
	  "Week 01 of a year is per definition the first week that has the Thursday in this year,
2128
	  which is equivalent to the week that contains the fourth day of January.
2129
	  In other words, the first week of a new year is the week that has the majority of its
2130
	  days in the new year."
2131
 
2132
	  Be carefull, with PHP, -3 % 7 = -3, instead of 4 !!!
2133
 
2134
	  day of year             = date("z", $day) + 1
2135
	  offset to thursday      = 3 - (date("w", $day) + 6) % 7
2136
	  first thursday of year  = 1 + (11 - date("w", mktime(0, 0, 0, 1, 1, date("Y", $day)))) % 7
2137
	  week number             = (thursday's day of year - first thursday's day of year) / 7 + 1
2138
	  ---------------------------------------------------------------------------*/
2139
 
2140
	$thursday = $day + 60 * 60 * 24 * (3 - (date("w", $day) + 6) % 7);              // take week's thursday
2141
	$week = 1 + (date("z", $thursday) - (11 - date("w", mktime(0, 0, 0, 1, 1, date("Y", $thursday)))) % 7) / 7;
2142
 
2143
	return $week;
2144
    }
2145
 
2146
    // Is year a leap year?
2147
    function IsLeap($aYear) {
2148
	// Is the year a leap year?
2149
	//$year = 0+date("Y",$aDate);
2150
	if( $aYear % 4 == 0)
2151
	    if( !($aYear % 100 == 0) || ($aYear % 400 == 0) )
2152
		return true;
2153
	return false;
2154
    }
2155
 
2156
    // Get current year
2157
    function GetYear($aDate) {
2158
	return 0+Date("Y",$aDate);
2159
    }
2160
 
2161
    // Return number of days in a year
2162
    function GetNumDaysInMonth($aMonth,$aYear) {
2163
	$days=array(31,28,31,30,31,30,31,31,30,31,30,31);
2164
	$daysl=array(31,29,31,30,31,30,31,31,30,31,30,31);
2165
	if( $this->IsLeap($aYear))
2166
	    return $daysl[$aMonth];
2167
	else
2168
	    return $days[$aMonth];
2169
    }
2170
 
2171
    // Get day in month
2172
    function GetMonthDayNbr($aDate) {
2173
	return 0+strftime("%d",$aDate);
2174
    }
2175
 
2176
    // Get day in year
2177
    function GetYearDayNbr($aDate) {
2178
	return 0+strftime("%j",$aDate);
2179
    }
2180
 
2181
    // Get month number
2182
    function GetMonthNbr($aDate) {
2183
	return 0+strftime("%m",$aDate);
2184
    }
2185
 
2186
    // Translate a date to screen coordinates	(horizontal scale)
2187
    function TranslateDate($aDate) {
2188
	//
2189
	// In order to handle the problem with Daylight savings time
2190
	// the scale written with equal number of seconds per day beginning
2191
	// with the start date. This means that we "cement" the state of
2192
	// DST as it is in the start date. If later the scale includes the
2193
	// switchover date (depends on the locale) we need to adjust back
2194
	// if the date we try to translate has a different DST status since
2195
	// we would otherwise be off by one hour.
2196
	$aDate = $this->NormalizeDate($aDate);
2197
	$tmp = localtime($aDate);
2198
	$cloc = $tmp[8];
2199
	$tmp = localtime($this->iStartDate);
2200
	$sloc = $tmp[8];
2201
	$offset = 0;
2202
	if( $sloc != $cloc) {
2203
	    if( $sloc )
2204
		$offset = 3600;
2205
	    else
2206
		$offset = -3600;
2207
	}
2208
	$img=$this->iImg;
2209
	return ($aDate-$this->iStartDate-$offset)/SECPERDAY*$this->GetDayWidth()+$img->left_margin+$this->iLabelWidth;;
2210
    }
2211
 
2212
    // Get screen coordinatesz for the vertical position for a bar
2213
    function TranslateVertPos($aPos) {
2214
	$img=$this->iImg;
2215
	$ph=$this->iAvailableHeight;
2216
	if( $aPos > $this->iVertLines )
2217
	    JpGraphError::RaiseL(6015,$aPos);
2218
// 'Illegal vertical position %d'
2219
	if( $this->iVertLayout == GANTT_EVEN ) {
2220
	    // Position the top bar at 1 vert spacing from the scale
2221
	    return round($img->top_margin + $this->iVertHeaderSize +  ($aPos+1)*$this->iVertSpacing);
2222
	}
2223
	else {
2224
	    // position the top bar at 1/2 a vert spacing from the scale
2225
	    return round($img->top_margin + $this->iVertHeaderSize  + $this->iTopPlotMargin + ($aPos+1)*$this->iVertSpacing);
2226
	}
2227
    }
2228
 
2229
    // What is the vertical spacing?
2230
    function GetVertSpacing() {
2231
	return $this->iVertSpacing;
2232
    }
2233
 
2234
    // Convert a date to timestamp
2235
    function NormalizeDate($aDate) {
2236
	if( $aDate === false ) return false;
2237
	if( is_string($aDate) ) {
2238
	    $t = strtotime($aDate);
2239
	    if( $t === FALSE || $t === -1 ) {
2240
		JpGraphError::RaiseL(6016,$aDate);
2241
//("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");
2242
	    }
2243
	    return $t;
2244
	}
2245
	elseif( is_int($aDate) || is_float($aDate) )
2246
	    return $aDate;
2247
	else
2248
	    JpGraphError::RaiseL(6017,$aDate);
2249
//Unknown date format in GanttScale ($aDate).");
2250
    }
2251
 
2252
 
2253
    // Convert a time string to minutes
2254
 
2255
    function TimeToMinutes($aTimeString) {
2256
	// Split in hours and minutes
2257
	$pos=strpos($aTimeString,':');
2258
	$minint=60;
2259
	if( $pos === false ) {
2260
	    $hourint = $aTimeString;
2261
	    $minint = 0;
2262
	}
2263
	else {
2264
	    $hourint = floor(substr($aTimeString,0,$pos));
2265
	    $minint = floor(substr($aTimeString,$pos+1));
2266
	}
2267
	$minint += 60 * $hourint;
2268
	return $minint;
2269
    }
2270
 
2271
    // Stroke the day scale (including gridlines)
2272
    function StrokeMinutes($aYCoord,$getHeight=false) {
2273
	$img=$this->iImg;
2274
	$xt=$img->left_margin+$this->iLabelWidth;
2275
	$yt=$aYCoord+$img->top_margin;
2276
	if( $this->minute->iShowLabels ) {
2277
	    $img->SetFont($this->minute->iFFamily,$this->minute->iFStyle,$this->minute->iFSize);
2278
	    $yb = $yt + $img->GetFontHeight() +
2279
		  $this->minute->iTitleVertMargin + $this->minute->iFrameWeight;
2280
	    if( $getHeight ) {
2281
		return $yb - $img->top_margin;
2282
	    }
2283
	    $xb = $img->width-$img->right_margin+1;
2284
	    $img->SetColor($this->minute->iBackgroundColor);
2285
	    $img->FilledRectangle($xt,$yt,$xb,$yb);
2286
 
2287
	    $x = $xt;
2288
	    $img->SetTextAlign("center");
2289
	    $day = date('w',$this->iStartDate);
2290
	    $minint = $this->minute->GetIntervall() ;
2291
 
2292
	    if( 60 % $minint !== 0 ) {
2293
                JpGraphError::RaiseL(6018,$minint);
2294
//'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.');
2295
            }
2296
 
2297
 
2298
	    $n = 60 / $minint;
2299
	    $datestamp = $this->iStartDate;
2300
	    $width = $this->GetHourWidth() / $n ;
2301
	    if( $width < 8 ) {
2302
		// TO small width to draw minute scale
2303
		JpGraphError::RaiseL(6019,$width);
2304
//('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.');
2305
	    }
2306
 
2307
	    $nh = ceil(24*60 / $this->TimeToMinutes($this->hour->GetIntervall()) );
2308
	    $nd = $this->GetNumberOfDays();
2309
	    // Convert to intervall to seconds
2310
	    $minint *= 60;
2311
	    for($j=0; $j < $nd; ++$j, $day += 1, $day %= 7) {
2312
		for( $k=0; $k < $nh; ++$k ) {
2313
		    for($i=0; $i < $n ;++$i, $x+=$width, $datestamp += $minint ) {
2314
			if( $day==6 || $day==0 ) {
2315
 
2316
			    $img->PushColor($this->day->iWeekendBackgroundColor);
2317
			    if( $this->iUsePlotWeekendBackground )
2318
				$img->FilledRectangle($x,$yt+$this->day->iFrameWeight,$x+$width,$img->height-$img->bottom_margin);
2319
			    else
2320
				$img->FilledRectangle($x,$yt+$this->day->iFrameWeight,$x+$width,$yb-$this->day->iFrameWeight);
2321
			    $img->PopColor();
2322
 
2323
			}
2324
 
2325
			if( $day==0 )
2326
			    $img->SetColor($this->day->iSundayTextColor);
2327
			else
2328
			    $img->SetColor($this->day->iTextColor);
2329
 
2330
			switch( $this->minute->iStyle ) {
2331
			    case MINUTESTYLE_CUSTOM:
2332
				$txt = date($this->minute->iLabelFormStr,$datestamp);
2333
				break;
2334
			    case MINUTESTYLE_MM:
2335
			    default:
2336
				// 15
2337
				$txt = date('i',$datestamp);
2338
				break;
2339
			}
2340
			$img->StrokeText(round($x+$width/2),round($yb-$this->minute->iTitleVertMargin),$txt);
2341
 
2342
			// FIXME: The rounding problem needs to be solved properly ...
2343
			//
2344
			// Fix a rounding problem the wrong way ..
2345
			// If we also have hour scale then don't draw the firsta or last
2346
			// gridline since that will be overwritten by the hour scale gridline if such exists.
2347
			// However, due to the propagation of rounding of the 'x+=width' term in the loop
2348
			// this might sometimes be one pixel of so we fix this by not drawing it.
2349
			// The proper way to fix it would be to re-calculate the scale for each step and
2350
			// not using the additive term.
2351
			if( !(($i == $n || $i==0) && $this->hour->iShowLabels && $this->hour->grid->iShow) ) {
2352
			    $img->SetColor($this->minute->grid->iColor);
2353
			    $img->SetLineWeight($this->minute->grid->iWeight);
2354
			    $img->Line($x,$yt,$x,$yb);
2355
			    $this->minute->grid->Stroke($img,$x,$yb,$x,$img->height-$img->bottom_margin);
2356
			}
2357
		    }
2358
		}
2359
	    }
2360
	    $img->SetColor($this->minute->iFrameColor);
2361
	    $img->SetLineWeight($this->minute->iFrameWeight);
2362
	    $img->Rectangle($xt,$yt,$xb,$yb);
2363
	    return $yb - $img->top_margin;
2364
	}
2365
	return $aYCoord;
2366
    }
2367
 
2368
    // Stroke the day scale (including gridlines)
2369
    function StrokeHours($aYCoord,$getHeight=false) {
2370
	$img=$this->iImg;
2371
	$xt=$img->left_margin+$this->iLabelWidth;
2372
	$yt=$aYCoord+$img->top_margin;
2373
	if( $this->hour->iShowLabels ) {
2374
	    $img->SetFont($this->hour->iFFamily,$this->hour->iFStyle,$this->hour->iFSize);
2375
	    $yb = $yt + $img->GetFontHeight() +
2376
		  $this->hour->iTitleVertMargin + $this->hour->iFrameWeight;
2377
	    if( $getHeight ) {
2378
		return $yb - $img->top_margin;
2379
	    }
2380
	    $xb = $img->width-$img->right_margin+1;
2381
	    $img->SetColor($this->hour->iBackgroundColor);
2382
	    $img->FilledRectangle($xt,$yt,$xb,$yb);
2383
 
2384
	    $x = $xt;
2385
	    $img->SetTextAlign("center");
2386
	    $tmp = $this->hour->GetIntervall() ;
2387
	    $minint = $this->TimeToMinutes($tmp);
2388
	    if( 1440 % $minint !== 0 ) {
2389
                JpGraphError::RaiseL(6020,$tmp);
2390
//('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);
2391
            }
2392
 
2393
	    $n = ceil(24*60 / $minint );
2394
	    $datestamp = $this->iStartDate;
2395
	    $day = date('w',$this->iStartDate);
2396
	    $doback = !$this->minute->iShowLabels;
2397
	    $width = $this->GetDayWidth() / $n ;
2398
	    for($j=0; $j < $this->GetNumberOfDays(); ++$j, $day += 1,$day %= 7) {
2399
		for($i=0; $i < $n ;++$i, $x+=$width) {
2400
		    if( $day==6 || $day==0 ) {
2401
 
2402
			$img->PushColor($this->day->iWeekendBackgroundColor);
2403
			if( $this->iUsePlotWeekendBackground && $doback )
2404
			    $img->FilledRectangle($x,$yt+$this->day->iFrameWeight,$x+$width,$img->height-$img->bottom_margin);
2405
			else
2406
			    $img->FilledRectangle($x,$yt+$this->day->iFrameWeight,$x+$width,$yb-$this->day->iFrameWeight);
2407
			$img->PopColor();
2408
 
2409
		    }
2410
 
2411
		    if( $day==0 )
2412
			$img->SetColor($this->day->iSundayTextColor);
2413
		    else
2414
			$img->SetColor($this->day->iTextColor);
2415
 
2416
		    switch( $this->hour->iStyle ) {
2417
			case HOURSTYLE_HMAMPM:
2418
			    // 1:35pm
2419
			    $txt = date('g:ia',$datestamp);
2420
			    break;
2421
			case HOURSTYLE_H24:
2422
			    // 13
2423
			    $txt = date('H',$datestamp);
2424
			    break;
2425
			case HOURSTYLE_HAMPM:
2426
			    $txt = date('ga',$datestamp);
2427
			    break;
2428
			case HOURSTYLE_CUSTOM:
2429
			    $txt = date($this->hour->iLabelFormStr,$datestamp);
2430
			    break;
2431
			case HOURSTYLE_HM24:
2432
			default:
2433
			    $txt = date('H:i',$datestamp);
2434
			    break;
2435
		    }
2436
		    $img->StrokeText(round($x+$width/2),round($yb-$this->hour->iTitleVertMargin),$txt);
2437
		    $img->SetColor($this->hour->grid->iColor);
2438
		    $img->SetLineWeight($this->hour->grid->iWeight);
2439
		    $img->Line($x,$yt,$x,$yb);
2440
		    $this->hour->grid->Stroke($img,$x,$yb,$x,$img->height-$img->bottom_margin);
2441
		    //$datestamp += $minint*60
2442
		    $datestamp = mktime(date('H',$datestamp),date('i',$datestamp)+$minint,0,
2443
					date("m",$datestamp),date("d",$datestamp)+1,date("Y",$datestamp));
2444
 
2445
		}
2446
	    }
2447
	    $img->SetColor($this->hour->iFrameColor);
2448
	    $img->SetLineWeight($this->hour->iFrameWeight);
2449
	    $img->Rectangle($xt,$yt,$xb,$yb);
2450
	    return $yb - $img->top_margin;
2451
	}
2452
	return $aYCoord;
2453
    }
2454
 
2455
 
2456
    // Stroke the day scale (including gridlines)
2457
    function StrokeDays($aYCoord,$getHeight=false) {
2458
	$img=$this->iImg;
2459
	$daywidth=$this->GetDayWidth();
2460
	$xt=$img->left_margin+$this->iLabelWidth;
2461
	$yt=$aYCoord+$img->top_margin;
2462
	if( $this->day->iShowLabels ) {
2463
	    $img->SetFont($this->day->iFFamily,$this->day->iFStyle,$this->day->iFSize);
2464
	    $yb=$yt + $img->GetFontHeight() + $this->day->iTitleVertMargin + $this->day->iFrameWeight;
2465
	    if( $getHeight ) {
2466
		return $yb - $img->top_margin;
2467
	    }
2468
	    $xb=$img->width-$img->right_margin+1;
2469
	    $img->SetColor($this->day->iBackgroundColor);
2470
	    $img->FilledRectangle($xt,$yt,$xb,$yb);
2471
 
2472
	    $x = $xt;
2473
	    $img->SetTextAlign("center");
2474
	    $day = date('w',$this->iStartDate);
2475
	    $datestamp = $this->iStartDate;
2476
 
2477
	    $doback = !($this->hour->iShowLabels || $this->minute->iShowLabels);
2478
 
2479
	    setlocale(LC_TIME,$this->iDateLocale->iLocale);
2480
 
2481
	    for($i=0; $i < $this->GetNumberOfDays(); ++$i, $x+=$daywidth, $day += 1,$day %= 7) {
2482
		if( $day==6 || $day==0 ) {
2483
		    $img->SetColor($this->day->iWeekendBackgroundColor);
2484
		    if( $this->iUsePlotWeekendBackground && $doback)
2485
			$img->FilledRectangle($x,$yt+$this->day->iFrameWeight,
2486
					      $x+$daywidth,$img->height-$img->bottom_margin);
2487
		    else
2488
			$img->FilledRectangle($x,$yt+$this->day->iFrameWeight,
2489
					      $x+$daywidth,$yb-$this->day->iFrameWeight);
2490
		}
2491
 
2492
		$mn = strftime('%m',$datestamp);
2493
		if( $mn[0]=='0' )
2494
		    $mn = $mn[1];
2495
 
2496
		switch( $this->day->iStyle ) {
2497
		    case DAYSTYLE_LONG:
2498
			// "Monday"
2499
			$txt = strftime('%A',$datestamp);
2500
			break;
2501
		    case DAYSTYLE_SHORT:
2502
			// "Mon"
2503
			$txt = strftime('%a',$datestamp);
2504
			break;
2505
		    case DAYSTYLE_SHORTDAYDATE1:
2506
			// "Mon 23/6"
2507
			$txt = strftime('%a %d/'.$mn,$datestamp);
2508
			break;
2509
		    case DAYSTYLE_SHORTDAYDATE2:
2510
			// "Mon 23 Jun"
2511
			$txt = strftime('%a %d %b',$datestamp);
2512
			break;
2513
		    case DAYSTYLE_SHORTDAYDATE3:
2514
			// "Mon 23 Jun 2003"
2515
			$txt = strftime('%a %d %b %Y',$datestamp);
2516
			break;
2517
		    case DAYSTYLE_LONGDAYDATE1:
2518
			// "Monday 23 Jun"
2519
			$txt = strftime('%A %d %b',$datestamp);
2520
			break;
2521
		    case DAYSTYLE_LONGDAYDATE2:
2522
			// "Monday 23 Jun 2003"
2523
			$txt = strftime('%A %d %b %Y',$datestamp);
2524
			break;
2525
		    case DAYSTYLE_SHORTDATE1:
2526
			// "23/6"
2527
			$txt = strftime('%d/'.$mn,$datestamp);
2528
			break;
2529
		    case DAYSTYLE_SHORTDATE2:
2530
			// "23 Jun"
2531
			$txt = strftime('%d %b',$datestamp);
2532
			break;
2533
		    case DAYSTYLE_SHORTDATE3:
2534
			// "Mon 23"
2535
			$txt = strftime('%a %d',$datestamp);
2536
			break;
2537
		    case DAYSTYLE_SHORTDATE4:
2538
			// "23"
2539
			$txt = strftime('%d',$datestamp);
2540
			break;
2541
		    case DAYSTYLE_CUSTOM:
2542
			// Custom format
2543
			$txt = strftime($this->day->iLabelFormStr,$datestamp);
2544
			break;
2545
		    case DAYSTYLE_ONELETTER:
2546
		    default:
2547
			// "M"
2548
			$txt = strftime('%A',$datestamp);
2549
			$txt = strtoupper($txt[0]);
2550
			break;
2551
		}
2552
 
2553
		if( $day==0 )
2554
		    $img->SetColor($this->day->iSundayTextColor);
2555
		else
2556
		    $img->SetColor($this->day->iTextColor);
2557
		$img->StrokeText(round($x+$daywidth/2+1),
2558
				 round($yb-$this->day->iTitleVertMargin),$txt);
2559
		$img->SetColor($this->day->grid->iColor);
2560
		$img->SetLineWeight($this->day->grid->iWeight);
2561
		$img->Line($x,$yt,$x,$yb);
2562
		$this->day->grid->Stroke($img,$x,$yb,$x,$img->height-$img->bottom_margin);
2563
		$datestamp = mktime(0,0,0,date("m",$datestamp),date("d",$datestamp)+1,date("Y",$datestamp));
2564
		//$datestamp += SECPERDAY;
2565
 
2566
	    }
2567
	    $img->SetColor($this->day->iFrameColor);
2568
	    $img->SetLineWeight($this->day->iFrameWeight);
2569
	    $img->Rectangle($xt,$yt,$xb,$yb);
2570
	    return $yb - $img->top_margin;
2571
	}
2572
	return $aYCoord;
2573
    }
2574
 
2575
    // Stroke week header and grid
2576
    function StrokeWeeks($aYCoord,$getHeight=false) {
2577
	if( $this->week->iShowLabels ) {
2578
	    $img=$this->iImg;
2579
	    $yt=$aYCoord+$img->top_margin;
2580
	    $img->SetFont($this->week->iFFamily,$this->week->iFStyle,$this->week->iFSize);
2581
	    $yb=$yt + $img->GetFontHeight() + $this->week->iTitleVertMargin + $this->week->iFrameWeight;
2582
 
2583
	    if( $getHeight ) {
2584
		return $yb - $img->top_margin;
2585
	    }
2586
 
2587
	    $xt=$img->left_margin+$this->iLabelWidth;
2588
	    $weekwidth=$this->GetDayWidth()*7;
2589
	    $wdays=$this->iDateLocale->GetDayAbb();
2590
	    $xb=$img->width-$img->right_margin+1;
2591
	    $week = $this->iStartDate;
2592
	    $weeknbr=$this->GetWeekNbr($week);
2593
	    $img->SetColor($this->week->iBackgroundColor);
2594
	    $img->FilledRectangle($xt,$yt,$xb,$yb);
2595
	    $img->SetColor($this->week->grid->iColor);
2596
	    $x = $xt;
2597
	    if( $this->week->iStyle==WEEKSTYLE_WNBR ) {
2598
		$img->SetTextAlign("center");
2599
		$txtOffset = $weekwidth/2+1;
2600
	    }
2601
	    elseif( $this->week->iStyle==WEEKSTYLE_FIRSTDAY  ||
2602
		    $this->week->iStyle==WEEKSTYLE_FIRSTDAY2 ||
2603
		    $this->week->iStyle==WEEKSTYLE_FIRSTDAYWNBR ||
2604
		    $this->week->iStyle==WEEKSTYLE_FIRSTDAY2WNBR ) {
2605
		$img->SetTextAlign("left");
2606
		$txtOffset = 3;
2607
	    }
2608
	    else
2609
		JpGraphError::RaiseL(6021);
2610
//("Unknown formatting style for week.");
2611
 
2612
	    for($i=0; $i<$this->GetNumberOfDays()/7; ++$i, $x+=$weekwidth) {
2613
		$img->PushColor($this->week->iTextColor);
2614
 
2615
		if( $this->week->iStyle==WEEKSTYLE_WNBR )
2616
		    $txt = sprintf($this->week->iLabelFormStr,$weeknbr);
2617
		elseif( $this->week->iStyle==WEEKSTYLE_FIRSTDAY ||
2618
			$this->week->iStyle==WEEKSTYLE_FIRSTDAYWNBR )
2619
		    $txt = date("j/n",$week);
2620
		elseif( $this->week->iStyle==WEEKSTYLE_FIRSTDAY2 ||
2621
			$this->week->iStyle==WEEKSTYLE_FIRSTDAY2WNBR ) {
2622
		    $monthnbr = date("n",$week)-1;
2623
		    $shortmonth = $this->iDateLocale->GetShortMonthName($monthnbr);
2624
		    $txt = Date("j",$week)." ".$shortmonth;
2625
		}
2626
 
2627
		if( $this->week->iStyle==WEEKSTYLE_FIRSTDAYWNBR ||
2628
		    $this->week->iStyle==WEEKSTYLE_FIRSTDAY2WNBR ) {
2629
		    $w = sprintf($this->week->iLabelFormStr,$weeknbr);
2630
		    $txt .= ' '.$w;
2631
		}
2632
 
2633
		$img->StrokeText(round($x+$txtOffset),
2634
				 round($yb-$this->week->iTitleVertMargin),$txt);
2635
 
2636
		$week = strtotime('+7 day',$week);
2637
		$weeknbr = $this->GetWeekNbr($week);
2638
		$img->PopColor();
2639
		$img->SetLineWeight($this->week->grid->iWeight);
2640
		$img->Line($x,$yt,$x,$yb);
2641
		$this->week->grid->Stroke($img,$x,$yb,$x,$img->height-$img->bottom_margin);
2642
	    }
2643
	    $img->SetColor($this->week->iFrameColor);
2644
	    $img->SetLineWeight($this->week->iFrameWeight);
2645
	    $img->Rectangle($xt,$yt,$xb,$yb);
2646
	    return $yb-$img->top_margin;
2647
	}
2648
	return $aYCoord;
2649
    }
2650
 
2651
    // Format the mont scale header string
2652
    function GetMonthLabel($aMonthNbr,$year) {
2653
	$sn = $this->iDateLocale->GetShortMonthName($aMonthNbr);
2654
	$ln = $this->iDateLocale->GetLongMonthName($aMonthNbr);
2655
	switch($this->month->iStyle) {
2656
	    case MONTHSTYLE_SHORTNAME:
2657
		$m=$sn;
2658
		break;
2659
	    case MONTHSTYLE_LONGNAME:
2660
		$m=$ln;
2661
		break;
2662
	    case MONTHSTYLE_SHORTNAMEYEAR2:
2663
		$m=$sn." '".substr("".$year,2);
2664
		break;
2665
	    case MONTHSTYLE_SHORTNAMEYEAR4:
2666
		$m=$sn." ".$year;
2667
		break;
2668
	    case MONTHSTYLE_LONGNAMEYEAR2:
2669
		$m=$ln." '".substr("".$year,2);
2670
		break;
2671
	    case MONTHSTYLE_LONGNAMEYEAR4:
2672
		$m=$ln." ".$year;
2673
		break;
2674
	    case MONTHSTYLE_FIRSTLETTER:
2675
		$m=$sn[0];
2676
		break;
2677
	}
2678
	return $m;
2679
    }
2680
 
2681
    // Stroke month scale and gridlines
2682
    function StrokeMonths($aYCoord,$getHeight=false) {
2683
	if( $this->month->iShowLabels ) {
2684
	    $img=$this->iImg;
2685
	    $img->SetFont($this->month->iFFamily,$this->month->iFStyle,$this->month->iFSize);
2686
	    $yt=$aYCoord+$img->top_margin;
2687
	    $yb=$yt + $img->GetFontHeight() + $this->month->iTitleVertMargin + $this->month->iFrameWeight;
2688
	    if( $getHeight ) {
2689
		return $yb - $img->top_margin;
2690
	    }
2691
	    $monthnbr = $this->GetMonthNbr($this->iStartDate)-1;
2692
	    $xt=$img->left_margin+$this->iLabelWidth;
2693
	    $xb=$img->width-$img->right_margin+1;
2694
 
2695
	    $img->SetColor($this->month->iBackgroundColor);
2696
	    $img->FilledRectangle($xt,$yt,$xb,$yb);
2697
 
2698
	    $img->SetLineWeight($this->month->grid->iWeight);
2699
	    $img->SetColor($this->month->iTextColor);
2700
	    $year = 0+strftime("%Y",$this->iStartDate);
2701
	    $img->SetTextAlign("center");
2702
	    if( $this->GetMonthNbr($this->iStartDate) == $this->GetMonthNbr($this->iEndDate)
2703
		&& $this->GetYear($this->iStartDate)==$this->GetYear($this->iEndDate) ) {
2704
	    	$monthwidth=$this->GetDayWidth()*($this->GetMonthDayNbr($this->iEndDate) - $this->GetMonthDayNbr($this->iStartDate) + 1);
2705
	    }
2706
	    else {
2707
	    	$monthwidth=$this->GetDayWidth()*($this->GetNumDaysInMonth($monthnbr,$year)-$this->GetMonthDayNbr($this->iStartDate)+1);
2708
	    }
2709
	    // Is it enough space to stroke the first month?
2710
	    $monthName = $this->GetMonthLabel($monthnbr,$year);
2711
	    if( $monthwidth >= 1.2*$img->GetTextWidth($monthName) ) {
2712
		$img->SetColor($this->month->iTextColor);
2713
		$img->StrokeText(round($xt+$monthwidth/2+1),
2714
				 round($yb-$this->month->iTitleVertMargin),
2715
				 $monthName);
2716
	    }
2717
	    $x = $xt + $monthwidth;
2718
	    while( $x < $xb ) {
2719
		$img->SetColor($this->month->grid->iColor);
2720
		$img->Line($x,$yt,$x,$yb);
2721
		$this->month->grid->Stroke($img,$x,$yb,$x,$img->height-$img->bottom_margin);
2722
		$monthnbr++;
2723
		if( $monthnbr==12 ) {
2724
		    $monthnbr=0;
2725
		    $year++;
2726
		}
2727
		$monthName = $this->GetMonthLabel($monthnbr,$year);
2728
		$monthwidth=$this->GetDayWidth()*$this->GetNumDaysInMonth($monthnbr,$year);
2729
		if( $x + $monthwidth < $xb )
2730
		    $w = $monthwidth;
2731
		else
2732
		    $w = $xb-$x;
2733
		if( $w >= 1.2*$img->GetTextWidth($monthName) ) {
2734
		    $img->SetColor($this->month->iTextColor);
2735
		    $img->StrokeText(round($x+$w/2+1),
2736
				     round($yb-$this->month->iTitleVertMargin),$monthName);
2737
		}
2738
		$x += $monthwidth;
2739
	    }
2740
	    $img->SetColor($this->month->iFrameColor);
2741
	    $img->SetLineWeight($this->month->iFrameWeight);
2742
	    $img->Rectangle($xt,$yt,$xb,$yb);
2743
	    return $yb-$img->top_margin;
2744
	}
2745
	return $aYCoord;
2746
    }
2747
 
2748
    // Stroke year scale and gridlines
2749
    function StrokeYears($aYCoord,$getHeight=false) {
2750
	if( $this->year->iShowLabels ) {
2751
	    $img=$this->iImg;
2752
	    $yt=$aYCoord+$img->top_margin;
2753
	    $img->SetFont($this->year->iFFamily,$this->year->iFStyle,$this->year->iFSize);
2754
	    $yb=$yt + $img->GetFontHeight() + $this->year->iTitleVertMargin + $this->year->iFrameWeight;
2755
 
2756
	    if( $getHeight ) {
2757
		return $yb - $img->top_margin;
2758
	    }
2759
 
2760
	    $xb=$img->width-$img->right_margin+1;
2761
	    $xt=$img->left_margin+$this->iLabelWidth;
2762
	    $year = $this->GetYear($this->iStartDate);
2763
	    $img->SetColor($this->year->iBackgroundColor);
2764
	    $img->FilledRectangle($xt,$yt,$xb,$yb);
2765
	    $img->SetLineWeight($this->year->grid->iWeight);
2766
	    $img->SetTextAlign("center");
2767
	    if( $year == $this->GetYear($this->iEndDate) )
2768
		$yearwidth=$this->GetDayWidth()*($this->GetYearDayNbr($this->iEndDate)-$this->GetYearDayNbr($this->iStartDate)+1);
2769
	    else
2770
		$yearwidth=$this->GetDayWidth()*($this->GetNumDaysInYear($year)-$this->GetYearDayNbr($this->iStartDate)+1);
2771
 
2772
	    // The space for a year must be at least 20% bigger than the actual text
2773
	    // so we allow 10% margin on each side
2774
	    if( $yearwidth >= 1.20*$img->GetTextWidth("".$year) ) {
2775
		$img->SetColor($this->year->iTextColor);
2776
		$img->StrokeText(round($xt+$yearwidth/2+1),
2777
				 round($yb-$this->year->iTitleVertMargin),
2778
				 $year);
2779
	    }
2780
	    $x = $xt + $yearwidth;
2781
	    while( $x < $xb ) {
2782
		$img->SetColor($this->year->grid->iColor);
2783
		$img->Line($x,$yt,$x,$yb);
2784
		$this->year->grid->Stroke($img,$x,$yb,$x,$img->height-$img->bottom_margin);
2785
		$year += 1;
2786
		$yearwidth=$this->GetDayWidth()*$this->GetNumDaysInYear($year);
2787
		if( $x + $yearwidth < $xb )
2788
		    $w = $yearwidth;
2789
		else
2790
		    $w = $xb-$x;
2791
		if( $w >= 1.2*$img->GetTextWidth("".$year) ) {
2792
		    $img->SetColor($this->year->iTextColor);
2793
		    $img->StrokeText(round($x+$w/2+1),
2794
				     round($yb-$this->year->iTitleVertMargin),
2795
				     $year);
2796
		}
2797
		$x += $yearwidth;
2798
	    }
2799
	    $img->SetColor($this->year->iFrameColor);
2800
	    $img->SetLineWeight($this->year->iFrameWeight);
2801
	    $img->Rectangle($xt,$yt,$xb,$yb);
2802
	    return $yb-$img->top_margin;
2803
	}
2804
	return $aYCoord;
2805
    }
2806
 
2807
    // Stroke table title (upper left corner)
2808
    function StrokeTableHeaders($aYBottom) {
2809
	$img=$this->iImg;
2810
	$xt=$img->left_margin;
2811
	$yt=$img->top_margin;
2812
	$xb=$xt+$this->iLabelWidth;
2813
	$yb=$aYBottom+$img->top_margin;
2814
 
2815
	if( $this->tableTitle->iShow ) {
2816
	    $img->SetColor($this->iTableHeaderBackgroundColor);
2817
	    $img->FilledRectangle($xt,$yt,$xb,$yb);
2818
	    $this->tableTitle->Align("center","top");
2819
	    $this->tableTitle->Stroke($img,$xt+($xb-$xt)/2+1,$yt+2);
2820
	    $img->SetColor($this->iTableHeaderFrameColor);
2821
	    $img->SetLineWeight($this->iTableHeaderFrameWeight);
2822
	    $img->Rectangle($xt,$yt,$xb,$yb);
2823
	}
2824
 
2825
	$this->actinfo->Stroke($img,$xt,$yt,$xb,$yb,$this->tableTitle->iShow);
2826
 
2827
 
2828
	// Draw the horizontal dividing line
2829
	$this->dividerh->Stroke($img,$xt,$yb,$img->width-$img->right_margin,$yb);
2830
 
2831
	// Draw the vertical dividing line
2832
	// We do the width "manually" since we want the line only to grow
2833
	// to the left
2834
	$fancy = $this->divider->iStyle == 'fancy' ;
2835
	if( $fancy ) {
2836
	    $this->divider->iStyle = 'solid';
2837
	}
2838
 
2839
	$tmp = $this->divider->iWeight;
2840
	$this->divider->iWeight=1;
2841
	$y = $img->height-$img->bottom_margin;
2842
	for($i=0; $i < $tmp; ++$i ) {
2843
	    $this->divider->Stroke($img,$xb-$i,$yt,$xb-$i,$y);
2844
	}
2845
 
2846
	// Should we draw "fancy" divider
2847
	if( $fancy ) {
2848
	    $img->SetLineWeight(1);
2849
	    $img->SetColor($this->iTableHeaderFrameColor);
2850
	    $img->Line($xb,$yt,$xb,$y);
2851
	    $img->Line($xb-$tmp+1,$yt,$xb-$tmp+1,$y);
2852
	    $img->SetColor('white');
2853
	    $img->Line($xb-$tmp+2,$yt,$xb-$tmp+2,$y);
2854
	}
2855
    }
2856
 
2857
    // Main entry point to stroke scale
2858
    function Stroke() {
2859
	if( !$this->IsRangeSet() )
2860
	    JpGraphError::RaiseL(6022);
2861
//("Gantt scale has not been specified.");
2862
	$img=$this->iImg;
2863
 
2864
	// If minutes are displayed then hour interval must be 1
2865
	if( $this->IsDisplayMinute() && $this->hour->GetIntervall() > 1 ) {
2866
	    JpGraphError::RaiseL(6023);
2867
//('If you display both hour and minutes the hour intervall must be 1 (Otherwise it doesn\' make sense to display minutes).');
2868
	}
2869
 
2870
	// Stroke all headers. As argument we supply the offset from the
2871
	// top which depends on any previous headers
2872
 
2873
	// First find out the height of each header
2874
	$offy=$this->StrokeYears(0,true);
2875
	$offm=$this->StrokeMonths($offy,true);
2876
	$offw=$this->StrokeWeeks($offm,true);
2877
	$offd=$this->StrokeDays($offw,true);
2878
	$offh=$this->StrokeHours($offd,true);
2879
	$offmin=$this->StrokeMinutes($offh,true);
2880
 
2881
 
2882
	// ... then we can stroke them in the "backwards order to ensure that
2883
	// the larger scale gridlines is stroked over the smaller scale gridline
2884
	$this->StrokeMinutes($offh);
2885
	$this->StrokeHours($offd);
2886
	$this->StrokeDays($offw);
2887
	$this->StrokeWeeks($offm);
2888
	$this->StrokeMonths($offy);
2889
	$this->StrokeYears(0);
2890
 
2891
	// Now when we now the oaverall size of the scale headers
2892
	// we can stroke the overall table headers
2893
	$this->StrokeTableHeaders($offmin);
2894
 
2895
	// Now we can calculate the correct scaling factor for each vertical position
2896
	$this->iAvailableHeight = $img->height - $img->top_margin - $img->bottom_margin - $offd;
2897
	$this->iVertHeaderSize = $offmin;
2898
	if( $this->iVertSpacing == -1 )
2899
	    $this->iVertSpacing = $this->iAvailableHeight / $this->iVertLines;
2900
    }
2901
}
2902
 
2903
 
2904
//===================================================
2905
// CLASS GanttConstraint
2906
// Just a structure to store all the values for a constraint
2907
//===================================================
2908
class GanttConstraint {
2909
    var $iConstrainType;
2910
    var $iConstrainRow;
2911
    var $iConstrainColor;
2912
    var $iConstrainArrowSize;
2913
    var $iConstrainArrowType;
2914
 
2915
//---------------
2916
// CONSTRUCTOR
2917
    function GanttConstraint($aRow,$aType,$aColor,$aArrowSize,$aArrowType){
2918
	$this->iConstrainType = $aType;
2919
	$this->iConstrainRow = $aRow;
2920
	$this->iConstrainColor=$aColor;
2921
	$this->iConstrainArrowSize=$aArrowSize;
2922
	$this->iConstrainArrowType=$aArrowType;
2923
    }
2924
}
2925
 
2926
 
2927
//===================================================
2928
// CLASS GanttPlotObject
2929
// The common signature for a Gantt object
2930
//===================================================
2931
class GanttPlotObject {
2932
    var $iVPos=0;					// Vertical position
2933
    var $iLabelLeftMargin=2;	// Title margin
2934
    var $iStart="";				// Start date
2935
    var $title,$caption;
2936
    var $iCaptionMargin=5;
2937
    var $csimarea='',$csimtarget='',$csimalt='';
2938
 
2939
    var $constraints = array();
2940
    var $iConstrainPos=array();
2941
 
2942
    function GanttPlotObject() {
2943
 	$this->title = new TextProperty();
2944
	$this->title->Align("left","center");
2945
	$this->caption = new TextProperty();
2946
    }
2947
 
2948
    function GetCSIMArea() {
2949
	return $this->csimarea;
2950
    }
2951
 
2952
    function SetCSIMTarget($aTarget,$aAlt='') {
2953
	if( !is_string($aTarget) ) {
2954
	    $tv = substr(var_export($aTarget,true),0,40);
2955
	    JpGraphError::RaiseL(6024,$tv);
2956
//('CSIM Target must be specified as a string.'."\nStart of target is:\n$tv");
2957
	}
2958
	if( !is_string($aAlt) ) {
2959
	    $tv = substr(var_export($aAlt,true),0,40);
2960
	    JpGraphError::RaiseL(6025,$tv);
2961
//('CSIM Alt text must be specified as a string.'."\nStart of alt text is:\n$tv");
2962
	}
2963
 
2964
        $this->csimtarget=$aTarget;
2965
        $this->csimalt=$aAlt;
2966
    }
2967
 
2968
    function SetCSIMAlt($aAlt) {
2969
	if( !is_string($aAlt) ) {
2970
	    $tv = substr(var_export($aAlt,true),0,40);
2971
	    JpGraphError::RaiseL(6025,$tv);
2972
//('CSIM Alt text must be specified as a string.'."\nStart of alt text is:\n$tv");
2973
	}
2974
        $this->csimalt=$aAlt;
2975
    }
2976
 
2977
    function SetConstrain($aRow,$aType,$aColor='black',$aArrowSize=ARROW_S2,$aArrowType=ARROWT_SOLID) {
2978
	$this->constraints[] = new GanttConstraint($aRow, $aType, $aColor, $aArrowSize, $aArrowType);
2979
    }
2980
 
2981
    function SetConstrainPos($xt,$yt,$xb,$yb) {
2982
	$this->iConstrainPos = array($xt,$yt,$xb,$yb);
2983
    }
2984
 
2985
    /*
2986
    function GetConstrain() {
2987
	return array($this->iConstrainRow,$this->iConstrainType);
2988
    }
2989
    */
2990
 
2991
    function GetMinDate() {
2992
	return $this->iStart;
2993
    }
2994
 
2995
    function GetMaxDate() {
2996
	return $this->iStart;
2997
    }
2998
 
2999
    function SetCaptionMargin($aMarg) {
3000
	$this->iCaptionMargin=$aMarg;
3001
    }
3002
 
3003
    function GetAbsHeight(&$aImg) {
3004
	return 0;
3005
    }
3006
 
3007
    function GetLineNbr() {
3008
	return $this->iVPos;
3009
    }
3010
 
3011
    function SetLabelLeftMargin($aOff) {
3012
	$this->iLabelLeftMargin=$aOff;
3013
    }
3014
 
3015
    function StrokeActInfo(&$aImg,$aScale,$aYPos) {
3016
	$cols=array();
3017
	$aScale->actinfo->GetColStart($aImg,$cols,true);
3018
	$this->title->Stroke($aImg,$cols,$aYPos);
3019
    }
3020
}
3021
 
3022
//===================================================
3023
// CLASS Progress
3024
// Holds parameters for the progress indicator
3025
// displyed within a bar
3026
//===================================================
3027
class Progress {
3028
    var $iProgress=-1, $iColor="black", $iFillColor='black';
3029
    var $iPattern=GANTT_SOLID;
3030
    var $iDensity=98, $iHeight=0.65;
3031
 
3032
    function Set($aProg) {
3033
	if( $aProg < 0.0 || $aProg > 1.0 )
3034
	    JpGraphError::RaiseL(6027);
3035
//("Progress value must in range [0, 1]");
3036
	$this->iProgress = $aProg;
3037
    }
3038
 
3039
    function SetPattern($aPattern,$aColor="blue",$aDensity=98) {
3040
	$this->iPattern = $aPattern;
3041
	$this->iColor = $aColor;
3042
	$this->iDensity = $aDensity;
3043
    }
3044
 
3045
    function SetFillColor($aColor) {
3046
	$this->iFillColor = $aColor;
3047
    }
3048
 
3049
    function SetHeight($aHeight) {
3050
	$this->iHeight = $aHeight;
3051
    }
3052
}
3053
 
3054
DEFINE('GANTT_HGRID1',0);
3055
DEFINE('GANTT_HGRID2',1);
3056
 
3057
//===================================================
3058
// CLASS HorizontalGridLine
3059
// Responsible for drawinf horizontal gridlines and filled alternatibg rows
3060
//===================================================
3061
class HorizontalGridLine {
3062
    var $iGraph=NULL;
3063
    var $iRowColor1 = '', $iRowColor2 = '';
3064
    var $iShow=false;
3065
    var $line=null;
3066
    var $iStart=0; // 0=from left margin, 1=just along header
3067
 
3068
    function HorizontalGridLine() {
3069
	$this->line = new LineProperty();
3070
	$this->line->SetColor('gray@0.4');
3071
	$this->line->SetStyle('dashed');
3072
    }
3073
 
3074
    function Show($aShow=true) {
3075
	$this->iShow = $aShow;
3076
    }
3077
 
3078
    function SetRowFillColor($aColor1,$aColor2='') {
3079
	$this->iRowColor1 = $aColor1;
3080
	$this->iRowColor2 = $aColor2;
3081
    }
3082
 
3083
    function SetStart($aStart) {
3084
	$this->iStart = $aStart;
3085
    }
3086
 
3087
    function Stroke(&$aImg,$aScale) {
3088
 
3089
	if( ! $this->iShow ) return;
3090
 
3091
	// Get horizontal width of line
3092
	/*
3093
	$limst = $aScale->iStartDate;
3094
	$limen = $aScale->iEndDate;
3095
	$xt = round($aScale->TranslateDate($aScale->iStartDate));
3096
	$xb = round($aScale->TranslateDate($limen));
3097
	*/
3098
 
3099
	if( $this->iStart === 0 ) {
3100
	    $xt = $aImg->left_margin-1;
3101
	}
3102
	else {
3103
	    $xt = round($aScale->TranslateDate($aScale->iStartDate))+1;
3104
	}
3105
 
3106
	$xb = $aImg->width-$aImg->right_margin;
3107
 
3108
	$yt = round($aScale->TranslateVertPos(0));
3109
	$yb = round($aScale->TranslateVertPos(1));
3110
	$height = $yb - $yt;
3111
 
3112
	// Loop around for all lines in the chart
3113
	for($i=0; $i < $aScale->iVertLines; ++$i ) {
3114
	    $yb = $yt - $height;
3115
	    $this->line->Stroke($aImg,$xt,$yb,$xb,$yb);
3116
	    if( $this->iRowColor1 !== '' ) {
3117
		if( $i % 2 == 0 ) {
3118
		    $aImg->PushColor($this->iRowColor1);
3119
		    $aImg->FilledRectangle($xt,$yt,$xb,$yb);
3120
		    $aImg->PopColor();
3121
		}
3122
		elseif( $this->iRowColor2 !== '' ) {
3123
		    $aImg->PushColor($this->iRowColor2);
3124
		    $aImg->FilledRectangle($xt,$yt,$xb,$yb);
3125
		    $aImg->PopColor();
3126
		}
3127
	    }
3128
	    $yt = round($aScale->TranslateVertPos($i+1));
3129
	}
3130
	$yb = $yt - $height;
3131
	$this->line->Stroke($aImg,$xt,$yb,$xb,$yb);
3132
    }
3133
}
3134
 
3135
 
3136
//===================================================
3137
// CLASS GanttBar
3138
// Responsible for formatting individual gantt bars
3139
//===================================================
3140
class GanttBar extends GanttPlotObject {
3141
    var $iEnd;
3142
    var $iHeightFactor=0.5;
3143
    var $iFillColor="white",$iFrameColor="black";
3144
    var $iShadow=false,$iShadowColor="darkgray",$iShadowWidth=1,$iShadowFrame="black";
3145
    var $iPattern=GANTT_RDIAG,$iPatternColor="blue",$iPatternDensity=95;
3146
    var $leftMark,$rightMark;
3147
    var $progress;
3148
//---------------
3149
// CONSTRUCTOR
3150
    function GanttBar($aPos,$aLabel,$aStart,$aEnd,$aCaption="",$aHeightFactor=0.6) {
3151
	parent::GanttPlotObject();
3152
	$this->iStart = $aStart;
3153
	// Is the end date given as a date or as number of days added to start date?
3154
	if( is_string($aEnd) ) {
3155
	    // If end date has been specified without a time we will asssume
3156
	    // end date is at the end of that date
3157
	    if( strpos($aEnd,':') === false )
3158
		$this->iEnd = strtotime($aEnd)+SECPERDAY-1;
3159
	    else
3160
		$this->iEnd = $aEnd;
3161
	}
3162
	elseif(is_int($aEnd) || is_float($aEnd) )
3163
	    $this->iEnd = strtotime($aStart)+round($aEnd*SECPERDAY);
3164
	$this->iVPos = $aPos;
3165
	$this->iHeightFactor = $aHeightFactor;
3166
	$this->title->Set($aLabel);
3167
	$this->caption = new TextProperty($aCaption);
3168
	$this->caption->Align("left","center");
3169
	$this->leftMark =new PlotMark();
3170
	$this->leftMark->Hide();
3171
	$this->rightMark=new PlotMark();
3172
	$this->rightMark->Hide();
3173
	$this->progress = new Progress();
3174
    }
3175
 
3176
//---------------
3177
// PUBLIC METHODS
3178
    function SetShadow($aShadow=true,$aColor="gray") {
3179
	$this->iShadow=$aShadow;
3180
	$this->iShadowColor=$aColor;
3181
    }
3182
 
3183
    function GetMaxDate() {
3184
	return $this->iEnd;
3185
    }
3186
 
3187
    function SetHeight($aHeight) {
3188
	$this->iHeightFactor = $aHeight;
3189
    }
3190
 
3191
    function SetColor($aColor) {
3192
	$this->iFrameColor = $aColor;
3193
    }
3194
 
3195
    function SetFillColor($aColor) {
3196
	$this->iFillColor = $aColor;
3197
    }
3198
 
3199
    function GetAbsHeight(&$aImg) {
3200
	if( is_int($this->iHeightFactor) || $this->leftMark->show || $this->rightMark->show ) {
3201
	    $m=-1;
3202
	    if( is_int($this->iHeightFactor) )
3203
		$m = $this->iHeightFactor;
3204
	    if( $this->leftMark->show )
3205
		$m = max($m,$this->leftMark->width*2);
3206
	    if( $this->rightMark->show )
3207
		$m = max($m,$this->rightMark->width*2);
3208
	    return $m;
3209
	}
3210
	else
3211
	    return -1;
3212
    }
3213
 
3214
    function SetPattern($aPattern,$aColor="blue",$aDensity=95) {
3215
	$this->iPattern = $aPattern;
3216
	$this->iPatternColor = $aColor;
3217
	$this->iPatternDensity = $aDensity;
3218
    }
3219
 
3220
    function Stroke(&$aImg,$aScale) {
3221
	$factory = new RectPatternFactory();
3222
	$prect = $factory->Create($this->iPattern,$this->iPatternColor);
3223
	$prect->SetDensity($this->iPatternDensity);
3224
 
3225
	// If height factor is specified as a float between 0,1 then we take it as meaning
3226
	// percetage of the scale width between horizontal line.
3227
	// If it is an integer > 1 we take it to mean the absolute height in pixels
3228
	if( $this->iHeightFactor > -0.0 && $this->iHeightFactor <= 1.1)
3229
	    $vs = $aScale->GetVertSpacing()*$this->iHeightFactor;
3230
	elseif(is_int($this->iHeightFactor) && $this->iHeightFactor>2 && $this->iHeightFactor < 200 )
3231
	    $vs = $this->iHeightFactor;
3232
	else
3233
	    JpGraphError::RaiseL(6028,$this->iHeightFactor);
3234
//("Specified height (".$this->iHeightFactor.") for gantt bar is out of range.");
3235
 
3236
	// Clip date to min max dates to show
3237
	$st = $aScale->NormalizeDate($this->iStart);
3238
	$en = $aScale->NormalizeDate($this->iEnd);
3239
 
3240
 
3241
	$limst = max($st,$aScale->iStartDate);
3242
	$limen = min($en,$aScale->iEndDate);
3243
 
3244
	$xt = round($aScale->TranslateDate($limst));
3245
	$xb = round($aScale->TranslateDate($limen));
3246
	$yt = round($aScale->TranslateVertPos($this->iVPos)-$vs-($aScale->GetVertSpacing()/2-$vs/2));
3247
	$yb = round($aScale->TranslateVertPos($this->iVPos)-($aScale->GetVertSpacing()/2-$vs/2));
3248
	$middle = round($yt+($yb-$yt)/2);
3249
	$this->StrokeActInfo($aImg,$aScale,$middle);
3250
 
3251
	// CSIM for title
3252
	if( ! empty($this->title->csimtarget) ) {
3253
	    $colwidth = $this->title->GetColWidth($aImg);
3254
	    $colstarts=array();
3255
	    $aScale->actinfo->GetColStart($aImg,$colstarts,true);
3256
	    $n = min(count($colwidth),count($this->title->csimtarget));
3257
	    for( $i=0; $i < $n; ++$i ) {
3258
		$title_xt = $colstarts[$i];
3259
		$title_xb = $title_xt + $colwidth[$i];
3260
		$coords = "$title_xt,$yt,$title_xb,$yt,$title_xb,$yb,$title_xt,$yb";
3261
		$this->csimarea .= "<area shape=\"poly\" coords=\"$coords\" href=\"".$this->title->csimtarget[$i]."\"";
3262
		if( ! empty($this->title->csimalt[$i]) ) {
3263
		    $tmp = $this->title->csimalt[$i];
3264
		    $this->csimarea .= " title=\"$tmp\"";
3265
		}
3266
		$this->csimarea .= " alt=\"$tmp\" />\n";
3267
	    }
3268
	}
3269
 
3270
	// Check if the bar is totally outside the current scale range
3271
	if( $en <  $aScale->iStartDate || $st > $aScale->iEndDate )
3272
		return;
3273
 
3274
 
3275
	// Remember the positions for the bar
3276
	$this->SetConstrainPos($xt,$yt,$xb,$yb);
3277
 
3278
	$prect->ShowFrame(false);
3279
	$prect->SetBackground($this->iFillColor);
3280
	if( $this->iShadow ) {
3281
	    $aImg->SetColor($this->iFrameColor);
3282
	    $aImg->ShadowRectangle($xt,$yt,$xb,$yb,$this->iFillColor,$this->iShadowWidth,$this->iShadowColor);
3283
	    $prect->SetPos(new Rectangle($xt+1,$yt+1,$xb-$xt-$this->iShadowWidth-2,$yb-$yt-$this->iShadowWidth-2));
3284
	    $prect->Stroke($aImg);
3285
	}
3286
	else {
3287
	    $prect->SetPos(new Rectangle($xt,$yt,$xb-$xt+1,$yb-$yt+1));
3288
	    $prect->Stroke($aImg);
3289
	    $aImg->SetColor($this->iFrameColor);
3290
	    $aImg->Rectangle($xt,$yt,$xb,$yb);
3291
	}
3292
 
3293
	// CSIM for bar
3294
	if( $this->csimtarget != '' ) {
3295
 
3296
	    $coords = "$xt,$yt,$xb,$yt,$xb,$yb,$xt,$yb";
3297
	    $this->csimarea .= "<area shape=\"poly\" coords=\"$coords\" href=\"".
3298
		              $this->csimtarget."\"";
3299
	    if( $this->csimalt != '' ) {
3300
		$tmp = $this->csimalt;
3301
		$this->csimarea .= " title=\"$tmp\"";
3302
	    }
3303
	    $this->csimarea .= " alt=\"$tmp\" />\n";
3304
	}
3305
 
3306
	// Draw progress bar inside activity bar
3307
	if( $this->progress->iProgress > 0 ) {
3308
 
3309
	    $xtp = $aScale->TranslateDate($st);
3310
	    $xbp = $aScale->TranslateDate($en);
3311
	    $len = ($xbp-$xtp)*$this->progress->iProgress;
3312
 
3313
	    $endpos = $xtp+$len;
3314
 
3315
	    // Is the the progress bar visible after the start date?
3316
	    if( $endpos > $xt ) {
3317
 
3318
		// Take away the length of the progress that is not visible (before the start date)
3319
		$len -= ($xt-$xtp);
3320
 
3321
		// Is the the progress bar visible after the start date?
3322
		if( $xtp < $xt )
3323
		    $xtp = $xt;
3324
 
3325
		// Make sure that the progess bar doesn't extend over the end date
3326
		if( $xtp+$len-1 > $xb )
3327
		    $len = $xb - $xtp  ;
3328
 
3329
		$prog = $factory->Create($this->progress->iPattern,$this->progress->iColor);
3330
		$prog->SetDensity($this->progress->iDensity);
3331
		$prog->SetBackground($this->progress->iFillColor);
3332
	    	$barheight = ($yb-$yt+1);
3333
		if( $this->iShadow )
3334
		    $barheight -= $this->iShadowWidth;
3335
		$progressheight = floor($barheight*$this->progress->iHeight);
3336
		$marg = ceil(($barheight-$progressheight)/2);
3337
	    	$pos = new Rectangle($xtp,$yt + $marg, $len,$barheight-2*$marg);
3338
		$prog->SetPos($pos);
3339
		$prog->Stroke($aImg);
3340
	    }
3341
	}
3342
 
3343
	// We don't plot the end mark if the bar has been capped
3344
	if( $limst == $st ) {
3345
	    $y = $middle;
3346
	    // We treat the RIGHT and LEFT triangle mark a little bi
3347
	    // special so that these marks are placed right under the
3348
	    // bar.
3349
	    if( $this->leftMark->GetType() == MARK_LEFTTRIANGLE ) {
3350
		$y = $yb ;
3351
	    }
3352
	    $this->leftMark->Stroke($aImg,$xt,$y);
3353
	}
3354
	if( $limen == $en ) {
3355
	    $y = $middle;
3356
	    // We treat the RIGHT and LEFT triangle mark a little bi
3357
	    // special so that these marks are placed right under the
3358
	    // bar.
3359
	    if( $this->rightMark->GetType() == MARK_RIGHTTRIANGLE ) {
3360
		$y = $yb ;
3361
	    }
3362
	    $this->rightMark->Stroke($aImg,$xb,$y);
3363
 
3364
	    $margin = $this->iCaptionMargin;
3365
	    if( $this->rightMark->show )
3366
	    	$margin += $this->rightMark->GetWidth();
3367
	    $this->caption->Stroke($aImg,$xb+$margin,$middle);
3368
	}
3369
    }
3370
}
3371
 
3372
//===================================================
3373
// CLASS MileStone
3374
// Responsible for formatting individual milestones
3375
//===================================================
3376
class MileStone extends GanttPlotObject {
3377
    var $mark;
3378
 
3379
//---------------
3380
// CONSTRUCTOR
3381
    function MileStone($aVPos,$aLabel,$aDate,$aCaption="") {
3382
	GanttPlotObject::GanttPlotObject();
3383
	$this->caption->Set($aCaption);
3384
	$this->caption->Align("left","center");
3385
	$this->caption->SetFont(FF_FONT1,FS_BOLD);
3386
	$this->title->Set($aLabel);
3387
	$this->title->SetColor("darkred");
3388
	$this->mark = new PlotMark();
3389
	$this->mark->SetWidth(10);
3390
	$this->mark->SetType(MARK_DIAMOND);
3391
	$this->mark->SetColor("darkred");
3392
	$this->mark->SetFillColor("darkred");
3393
	$this->iVPos = $aVPos;
3394
	$this->iStart = $aDate;
3395
    }
3396
 
3397
//---------------
3398
// PUBLIC METHODS
3399
 
3400
    function GetAbsHeight(&$aImg) {
3401
	return max($this->title->GetHeight($aImg),$this->mark->GetWidth());
3402
    }
3403
 
3404
    function Stroke(&$aImg,$aScale) {
3405
	// Put the mark in the middle at the middle of the day
3406
	$d = $aScale->NormalizeDate($this->iStart)+SECPERDAY/2;
3407
	$x = $aScale->TranslateDate($d);
3408
	$y = $aScale->TranslateVertPos($this->iVPos)-($aScale->GetVertSpacing()/2);
3409
 
3410
	$this->StrokeActInfo($aImg,$aScale,$y);
3411
 
3412
	// CSIM for title
3413
	if( ! empty($this->title->csimtarget) ) {
3414
 
3415
	    $yt = round($y - $this->title->GetHeight($aImg)/2);
3416
	    $yb = round($y + $this->title->GetHeight($aImg)/2);
3417
 
3418
	    $colwidth = $this->title->GetColWidth($aImg);
3419
	    $colstarts=array();
3420
	    $aScale->actinfo->GetColStart($aImg,$colstarts,true);
3421
	    $n = min(count($colwidth),count($this->title->csimtarget));
3422
	    for( $i=0; $i < $n; ++$i ) {
3423
		$title_xt = $colstarts[$i];
3424
		$title_xb = $title_xt + $colwidth[$i];
3425
		$coords = "$title_xt,$yt,$title_xb,$yt,$title_xb,$yb,$title_xt,$yb";
3426
		$this->csimarea .= "<area shape=\"poly\" coords=\"$coords\" href=\"".$this->title->csimtarget[$i]."\"";
3427
		if( ! empty($this->title->csimalt[$i]) ) {
3428
		    $tmp = $this->title->csimalt[$i];
3429
		    $this->csimarea .= " title=\"$tmp\"";
3430
		}
3431
		$this->csimarea .= " alt=\"$tmp\" />\n";
3432
	    }
3433
	}
3434
 
3435
	if( $d <  $aScale->iStartDate || $d > $aScale->iEndDate )
3436
		return;
3437
 
3438
	// Remember the coordinates for any constrains linking to
3439
	// this milestone
3440
	$w = $this->mark->GetWidth()/2;
3441
	$this->SetConstrainPos($x,round($y-$w),$x,round($y+$w));
3442
 
3443
	// Setup CSIM
3444
	if( $this->csimtarget != '' ) {
3445
	    $this->mark->SetCSIMTarget( $this->csimtarget );
3446
	    $this->mark->SetCSIMAlt( $this->csimalt );
3447
	}
3448
 
3449
	$this->mark->Stroke($aImg,$x,$y);
3450
	$this->caption->Stroke($aImg,$x+$this->mark->width/2+$this->iCaptionMargin,$y);
3451
 
3452
	$this->csimarea .= $this->mark->GetCSIMAreas();
3453
    }
3454
}
3455
 
3456
 
3457
//===================================================
3458
// CLASS GanttVLine
3459
// Responsible for formatting individual milestones
3460
//===================================================
3461
 
3462
class TextPropertyBelow extends TextProperty {
3463
    function TextPropertyBelow($aTxt='') {
3464
	parent::TextProperty($aTxt);
3465
    }
3466
 
3467
    function GetColWidth(&$aImg,$margin) {
3468
	// Since we are not stroking the title in the columns
3469
	// but rather under the graph we want this to return 0.
3470
	return array(0);
3471
    }
3472
}
3473
 
3474
class GanttVLine extends GanttPlotObject {
3475
 
3476
    var $iLine,$title_margin=3;
3477
    var $iDayOffset=1;	// Defult to right edge of day
3478
 
3479
//---------------
3480
// CONSTRUCTOR
3481
    function GanttVLine($aDate,$aTitle="",$aColor="black",$aWeight=3,$aStyle="dashed") {
3482
	GanttPlotObject::GanttPlotObject();
3483
	$this->iLine = new LineProperty();
3484
	$this->iLine->SetColor($aColor);
3485
	$this->iLine->SetWeight($aWeight);
3486
	$this->iLine->SetStyle($aStyle);
3487
	$this->iStart = $aDate;
3488
	$this->title = new TextPropertyBelow();
3489
	$this->title->Set($aTitle);
3490
    }
3491
 
3492
//---------------
3493
// PUBLIC METHODS
3494
 
3495
    function SetDayOffset($aOff=0.5) {
3496
	if( $aOff < 0.0 || $aOff > 1.0 )
3497
	    JpGraphError::RaiseL(6029);
3498
//("Offset for vertical line must be in range [0,1]");
3499
	$this->iDayOffset = $aOff;
3500
    }
3501
 
3502
    function SetTitleMargin($aMarg) {
3503
	$this->title_margin = $aMarg;
3504
    }
3505
 
3506
    function Stroke(&$aImg,$aScale) {
3507
	$d = $aScale->NormalizeDate($this->iStart);
3508
	if( $d <  $aScale->iStartDate || $d > $aScale->iEndDate )
3509
	    return;
3510
	if($this->iDayOffset != 0.0)
3511
	    $d += 24*60*60*$this->iDayOffset;
3512
	$x = $aScale->TranslateDate($d);
3513
	$y1 = $aScale->iVertHeaderSize+$aImg->top_margin;
3514
	$y2 = $aImg->height - $aImg->bottom_margin;
3515
	$this->iLine->Stroke($aImg,$x,$y1,$x,$y2);
3516
	$this->title->Align("center","top");
3517
	$this->title->Stroke($aImg,$x,$y2+$this->title_margin);
3518
    }
3519
}
3520
 
3521
//===================================================
3522
// CLASS LinkArrow
3523
// Handles the drawing of a an arrow
3524
//===================================================
3525
class LinkArrow {
3526
    var $ix,$iy;
3527
    var $isizespec = array(
3528
	array(2,3),array(3,5),array(3,8),array(6,15),array(8,22));
3529
    var $iDirection=ARROW_DOWN,$iType=ARROWT_SOLID,$iSize=ARROW_S2;
3530
    var $iColor='black';
3531
 
3532
    function LinkArrow($x,$y,$aDirection,$aType=ARROWT_SOLID,$aSize=ARROW_S2) {
3533
	$this->iDirection = $aDirection;
3534
	$this->iType = $aType;
3535
	$this->iSize = $aSize;
3536
	$this->ix = $x;
3537
	$this->iy = $y;
3538
    }
3539
 
3540
    function SetColor($aColor) {
3541
	$this->iColor = $aColor;
3542
    }
3543
 
3544
    function SetSize($aSize) {
3545
	$this->iSize = $aSize;
3546
    }
3547
 
3548
    function SetType($aType) {
3549
	$this->iType = $aType;
3550
    }
3551
 
3552
    function Stroke(&$aImg) {
3553
	list($dx,$dy) = $this->isizespec[$this->iSize];
3554
	$x = $this->ix;
3555
	$y = $this->iy;
3556
	switch ( $this->iDirection ) {
3557
	    case ARROW_DOWN:
3558
		$c = array($x,$y,$x-$dx,$y-$dy,$x+$dx,$y-$dy,$x,$y);
3559
		break;
3560
	    case ARROW_UP:
3561
		$c = array($x,$y,$x-$dx,$y+$dy,$x+$dx,$y+$dy,$x,$y);
3562
		break;
3563
	    case ARROW_LEFT:
3564
		$c = array($x,$y,$x+$dy,$y-$dx,$x+$dy,$y+$dx,$x,$y);
3565
		break;
3566
	    case ARROW_RIGHT:
3567
		$c = array($x,$y,$x-$dy,$y-$dx,$x-$dy,$y+$dx,$x,$y);
3568
		break;
3569
	    default:
3570
		JpGraphError::RaiseL(6030);
3571
//('Unknown arrow direction for link.');
3572
		die();
3573
		break;
3574
	}
3575
	$aImg->SetColor($this->iColor);
3576
	switch( $this->iType ) {
3577
	    case ARROWT_SOLID:
3578
		$aImg->FilledPolygon($c);
3579
		break;
3580
	    case ARROWT_OPEN:
3581
		$aImg->Polygon($c);
3582
		break;
3583
	    default:
3584
		JpGraphError::RaiseL(6031);
3585
//('Unknown arrow type for link.');
3586
		die();
3587
		break;
3588
	}
3589
    }
3590
}
3591
 
3592
//===================================================
3593
// CLASS GanttLink
3594
// Handles the drawing of a link line between 2 points
3595
//===================================================
3596
 
3597
class GanttLink {
3598
    var $ix1,$ix2,$iy1,$iy2;
3599
    var $iPathType=2,$iPathExtend=15;
3600
    var $iColor='black',$iWeight=1;
3601
    var $iArrowSize=ARROW_S2,$iArrowType=ARROWT_SOLID;
3602
 
3603
    function GanttLink($x1=0,$y1=0,$x2=0,$y2=0) {
3604
	$this->ix1 = $x1;
3605
	$this->ix2 = $x2;
3606
	$this->iy1 = $y1;
3607
	$this->iy2 = $y2;
3608
    }
3609
 
3610
    function SetPos($x1,$y1,$x2,$y2) {
3611
	$this->ix1 = $x1;
3612
	$this->ix2 = $x2;
3613
	$this->iy1 = $y1;
3614
	$this->iy2 = $y2;
3615
    }
3616
 
3617
    function SetPath($aPath) {
3618
	$this->iPathType = $aPath;
3619
    }
3620
 
3621
    function SetColor($aColor) {
3622
	$this->iColor = $aColor;
3623
    }
3624
 
3625
    function SetArrow($aSize,$aType=ARROWT_SOLID) {
3626
	$this->iArrowSize = $aSize;
3627
	$this->iArrowType = $aType;
3628
    }
3629
 
3630
    function SetWeight($aWeight) {
3631
	$this->iWeight = $aWeight;
3632
    }
3633
 
3634
    function Stroke(&$aImg) {
3635
	// The way the path for the arrow is constructed is partly based
3636
	// on some heuristics. This is not an exact science but draws the
3637
	// path in a way that, for me, makes esthetic sence. For example
3638
	// if the start and end activities are very close we make a small
3639
	// detour to endter the target horixontally. If there are more
3640
	// space between axctivities then no suh detour is made and the
3641
	// target is "hit" directly vertical. I have tried to keep this
3642
	// simple. no doubt this could become almost infinitive complex
3643
	// and have some real AI. Feel free to modify this.
3644
	// This will no-doubt be tweaked as times go by. One design aim
3645
	// is to avoid having the user choose what types of arrow
3646
	// he wants.
3647
 
3648
	// The arrow is drawn between (x1,y1) to (x2,y2)
3649
	$x1 = $this->ix1 ;
3650
	$x2 = $this->ix2 ;
3651
	$y1 = $this->iy1 ;
3652
	$y2 = $this->iy2 ;
3653
 
3654
	// Depending on if the target is below or above we have to
3655
	// handle thi different.
3656
	if( $y2 > $y1 ) {
3657
	    $arrowtype = ARROW_DOWN;
3658
	    $midy = round(($y2-$y1)/2+$y1);
3659
	    if( $x2 > $x1 ) {
3660
		switch ( $this->iPathType  ) {
3661
		    case 0:
3662
			$c = array($x1,$y1,$x1,$midy,$x2,$midy,$x2,$y2);
3663
			break;
3664
		    case 1:
3665
		    case 2:
3666
		    case 3:
3667
			$c = array($x1,$y1,$x2,$y1,$x2,$y2);
3668
			break;
3669
		    default:
3670
			JpGraphError::RaiseL(6032,$this->iPathType);
3671
//('Internal error: Unknown path type (='.$this->iPathType .') specified for link.');
3672
			exit(1);
3673
			break;
3674
		}
3675
	    }
3676
	    else {
3677
		switch ( $this->iPathType  ) {
3678
		    case 0:
3679
		    case 1:
3680
			$c = array($x1,$y1,$x1,$midy,$x2,$midy,$x2,$y2);
3681
			break;
3682
		    case 2:
3683
			// Always extend out horizontally a bit from the first point
3684
			// If we draw a link back in time (end to start) and the bars
3685
			// are very close we also change the path so it comes in from
3686
			// the left on the activity
3687
			$c = array($x1,$y1,$x1+$this->iPathExtend,$y1,
3688
				   $x1+$this->iPathExtend,$midy,
3689
				   $x2,$midy,$x2,$y2);
3690
			break;
3691
		    case 3:
3692
			if( $y2-$midy < 6 ) {
3693
			    $c = array($x1,$y1,$x1,$midy,
3694
				       $x2-$this->iPathExtend,$midy,
3695
				       $x2-$this->iPathExtend,$y2,
3696
				       $x2,$y2);
3697
			    $arrowtype = ARROW_RIGHT;
3698
			}
3699
			else {
3700
			    $c = array($x1,$y1,$x1,$midy,$x2,$midy,$x2,$y2);
3701
			}
3702
			break;
3703
		    default:
3704
			JpGraphError::RaiseL(6032,$this->iPathType);
3705
//('Internal error: Unknown path type specified for link.');
3706
			exit(1);
3707
			break;
3708
		}
3709
	    }
3710
	    $arrow = new LinkArrow($x2,$y2,$arrowtype);
3711
	}
3712
	else {
3713
	    // Y2 < Y1
3714
	    $arrowtype = ARROW_UP;
3715
	    $midy = round(($y1-$y2)/2+$y2);
3716
	    if( $x2 > $x1 ) {
3717
		switch ( $this->iPathType  ) {
3718
		    case 0:
3719
		    case 1:
3720
			$c = array($x1,$y1,$x1,$midy,$x2,$midy,$x2,$y2);
3721
			break;
3722
		    case 3:
3723
			if( $midy-$y2 < 8 ) {
3724
			    $arrowtype = ARROW_RIGHT;
3725
			    $c = array($x1,$y1,$x1,$y2,$x2,$y2);
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
			break;
3735
		}
3736
	    }
3737
	    else {
3738
		switch ( $this->iPathType  ) {
3739
		    case 0:
3740
		    case 1:
3741
			$c = array($x1,$y1,$x1,$midy,$x2,$midy,$x2,$y2);
3742
			break;
3743
		    case 2:
3744
			// Always extend out horizontally a bit from the first point
3745
			$c = array($x1,$y1,$x1+$this->iPathExtend,$y1,
3746
				   $x1+$this->iPathExtend,$midy,
3747
				   $x2,$midy,$x2,$y2);
3748
			break;
3749
		    case 3:
3750
			if( $midy-$y2 < 16 ) {
3751
			    $arrowtype = ARROW_RIGHT;
3752
			    $c = array($x1,$y1,$x1,$midy,$x2-$this->iPathExtend,$midy,
3753
				       $x2-$this->iPathExtend,$y2,
3754
				       $x2,$y2);
3755
			}
3756
			else {
3757
			    $c = array($x1,$y1,$x1,$midy,$x2,$midy,$x2,$y2);
3758
			}
3759
			break;
3760
		    default:
3761
			JpGraphError::RaiseL(6032,$this->iPathType);
3762
//('Internal error: Unknown path type specified for link.');
3763
			break;
3764
		}
3765
	    }
3766
	    $arrow = new LinkArrow($x2,$y2,$arrowtype);
3767
	}
3768
	$aImg->SetColor($this->iColor);
3769
	$aImg->SetLineWeight($this->iWeight);
3770
	$aImg->Polygon($c);
3771
	$aImg->SetLineWeight(1);
3772
	$arrow->SetColor($this->iColor);
3773
	$arrow->SetSize($this->iArrowSize);
3774
	$arrow->SetType($this->iArrowType);
3775
	$arrow->Stroke($aImg);
3776
    }
3777
}
3778
 
3779
// <EOF>
3780
?>