PageRenderTime 74ms CodeModel.GetById 18ms RepoModel.GetById 0ms app.codeStats 0ms

/libraries/plugin/jpgraph/jpgraph_gantt.php

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