source: trunk/client/modules/Elezioni/grafici/jpgraph_gantt.php@ 113

Last change on this file since 113 was 2, checked in by root, 15 years ago

importo il progetto

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