1: <?php
  2:   3:   4:   5:   6: 
  7: 
  8:   9:  10:  11:  12:  13:  14:  15:  16:  17:  18:  19:  20:  21:  22:  23:  24:  25:  26:  27:  28:  29:  30:  31:  32:  33:  34: 
 35:     class QImageBrowserNav extends QPanel {
 36:         protected $btnFirst;
 37:         protected $btnPrev;
 38:         protected $btnNext;
 39:         protected $btnLast;
 40:         
 41:         public function __construct($objParentObject, $strControlId = null) {
 42:             
 43:             try {
 44:                 parent::__construct($objParentObject, $strControlId);
 45:             } catch (QCallerException $objExc) {
 46:                 $objExc->IncrementOffset();
 47:                 throw $objExc;
 48:             }
 49:             $this->AutoRenderChildren = true;
 50:             
 51:             $this->btnFirst = new QButton($this);
 52:             $this->btnFirst->Text = QApplication::Translate('First');
 53:             $this->btnFirst->Enabled = false;
 54:             $this->btnFirst->CssClass = 'button ib_nav_button ib_nav_button_first';
 55: 
 56:             $this->btnPrev = new QButton($this);
 57:             $this->btnPrev->Text = QApplication::Translate('Previous');
 58:             $this->btnPrev->Enabled = false;
 59:             $this->btnPrev->CssClass = 'button ib_nav_button ib_nav_button_prev';
 60: 
 61:             $this->btnNext = new QButton($this);
 62:             $this->btnNext->Text = QApplication::Translate('Next');
 63:             $this->btnNext->Enabled = false;
 64:             $this->btnNext->CssClass = 'button ib_nav_button ib_nav_button_next';
 65: 
 66:             $this->btnLast = new QButton($this);
 67:             $this->btnLast->Text = QApplication::Translate('Last');
 68:             $this->btnLast->Enabled = false;
 69:             $this->btnLast->CssClass = 'button ib_nav_button ib_nav_button_last';
 70: 
 71:             $this->setButtonActions();
 72:         }
 73:         
 74:         protected function setButtonActions(array $arrButtons = null) {
 75:             
 76:             $objImageBrowser = $this->ParentControl;
 77:             while ( !($objImageBrowser instanceof QImageBrowserBase) ) {
 78:                 $objImageBrowser = $objImageBrowser->ParentControl;
 79:                 if (is_null($objImageBrowser) || $objImageBrowser instanceof QForm) {
 80:                     throw new QCallerException("QImageBrowserNav must be inside a QImageBrowser");
 81:                 }
 82:             }
 83:             if (!$arrButtons) {
 84:                 $arrButtons = array(
 85:                     "btnFirst_Click"    => $this->btnFirst, 
 86:                     "btnPrev_Click"     => $this->btnPrev, 
 87:                     "btnNext_Click"     => $this->btnNext, 
 88:                     "btnLast_Click"     => $this->btnLast);
 89:             }
 90:             
 91:             foreach ($arrButtons as $strActionCalback => $objButton) {
 92:                 $objButton->RemoveAllActions(QClickEvent::EventName);
 93:                 $objButton->AddAction(new QClickEvent(), new QAjaxControlAction($objImageBrowser, $strActionCalback));
 94:             }
 95:         }
 96:         
 97:         public function BackButtonsEnabled($blnEnable) {
 98:             $this->btnFirst->Enabled = $blnEnable;
 99:             $this->btnPrev->Enabled = $blnEnable;
100:         }
101:         
102:         public function ForwardButtonsEnabled($blnEnable) {
103:             $this->btnNext->Enabled = $blnEnable;
104:             $this->btnLast->Enabled = $blnEnable;
105:         }
106: 
107:         public function __get($strName) {
108:             switch ($strName) {
109:                 case "FirstButton": return $this->btnFirst;
110:                 case "PrevButton":  return $this->btnPrev;
111:                 case "NextButton":  return $this->btnNext;
112:                 case "LastButton":  return $this->btnLast;
113:                 default:
114:                     try {
115:                         return parent::__get($strName);
116:                     } catch (QCallerException $objExc) {
117:                         $objExc->IncrementOffset();
118:                         throw $objExc;
119:                     }
120:             }
121:         }
122: 
123:         public function __set($strName, $mixValue) {
124:             $this->blnModified = true;
125: 
126:             switch ($strName) {
127:                 case "FirstButton":
128:                     try {
129:                         $this->RemoveChildControl($this->btnFirst->ControlId, true);
130:                         $this->btnFirst = QType::Cast($mixValue, 'QControl');
131:                         $this->setButtonActions(array("btnFirst_Click" => $this->btnFirst));
132:                         break;
133:                     } catch (QInvalidCastException $objExc) {
134:                         $objExc->IncrementOffset();
135:                         throw $objExc;
136:                     }
137: 
138:                 case "PrevButton":
139:                     try {
140:                         $this->RemoveChildControl($this->btnPrev->ControlId, true);
141:                         $this->btnPrev = QType::Cast($mixValue, 'QControl');
142:                         $this->setButtonActions(array("btnPrev_Click" => $this->btnPrev));
143:                         break;
144:                     } catch (QInvalidCastException $objExc) {
145:                         $objExc->IncrementOffset();
146:                         throw $objExc;
147:                     }
148: 
149:                 case "NextButton":
150:                     try {
151:                         $this->RemoveChildControl($this->btnNext->ControlId, true);
152:                         $this->btnNext = QType::Cast($mixValue, 'QControl');
153:                         $this->setButtonActions(array("btnNext_Click" => $this->btnNext));
154:                         break;
155:                     } catch (QInvalidCastException $objExc) {
156:                         $objExc->IncrementOffset();
157:                         throw $objExc;
158:                     }
159: 
160:                 case "LastButton":
161:                     try {
162:                         $this->RemoveChildControl($this->btnLast->ControlId, true);
163:                         $this->btnLast = QType::Cast($mixValue, 'QControl');
164:                         $this->setButtonActions(array("btnLast_Click" => $this->btnLast));
165:                         break;
166:                     } catch (QInvalidCastException $objExc) {
167:                         $objExc->IncrementOffset();
168:                         throw $objExc;
169:                     }
170: 
171:                 default:
172:                     try {
173:                         parent::__set($strName, $mixValue);
174:                     } catch (QCallerException $objExc) {
175:                         $objExc->IncrementOffset();
176:                         throw $objExc;
177:                     }
178:                     break;
179:             }
180:         }
181:     }
182:     
183:     184: 185: 
186:     class QImageBrowserThumbnails extends QPanel {
187:         public function __construct($objParentObject, $strControlId = null) {
188:             
189:             try {
190:                 parent::__construct($objParentObject, $strControlId);
191:             } catch (QCallerException $objExc) {
192:                 $objExc->IncrementOffset();
193:                 throw $objExc;
194:             }
195:             $this->AutoRenderChildren = true;
196:         }
197: 
198:         public function reload() {
199:             $this->RemoveChildControls(true);
200:             $img = null;
201:             
202:             $objImageBrowser = $this->ParentControl;
203:             while ( !($objImageBrowser instanceof QImageBrowserBase) ) {
204:                 $objImageBrowser = $objImageBrowser->ParentControl;
205:                 if (is_null($objImageBrowser) || $objImageBrowser instanceof QForm) {
206:                     throw new QCallerException("QImageBrowserThumbnails must be inside a QImageBrowser");
207:                 }
208:             }
209:             $iEnd = $objImageBrowser->ImageCount();
210:             for ($i = 0; $i < $iEnd; ++$i) {
211:                 $strImagePath = $objImageBrowser->ThumbnailImagePath($i);
212:                 $img = new QImageControl($this);
213:                 $img->CssClass = 'ib_thm_image';
214:                 $img->ImagePath = $strImagePath;
215:                 $img->AlternateText = $strImagePath;
216:                 $img->ActionParameter = $i;
217:             
218:                 
219:                 
220:                 $img->CacheFolder = __IMAGE_CACHE_ASSETS__;
221:                 
222:                 $img->AddAction(new QClickEvent(), new QAjaxControlAction($objImageBrowser, "imgThm_Click"));
223:             }
224:             if ($img) {
225:                 $img->CssClass = 'ib_thm_image ib_thm_image_last';
226:                 $this->Text = '';
227:             } else {
228:                 $this->Text = QApplication::Translate('No thumbnails');
229:             }
230:         }
231:     }
232: 
233: 234: 235: 236: 237: 238: 239: 240: 241: 
242:     abstract class QImageBrowserBase extends QPanel {
243:         protected $intCurrentImage;
244:         protected $imgMainImage;
245:         protected $txtCaption;
246:         protected $btnSave;
247:         protected $ibnNavigation1;
248:         protected $ibnNavigation2;
249:         protected $ibtThumbnails;
250:         
251:         252: 253: 254: 255: 256: 257: 
258:         public function __construct($objParentObject, $blnReadOnlyCaption = true, $blnTwoNavBars = false, $blnThumbnails = true, $strControlId = null) {
259:             
260:             try {
261:                 parent::__construct($objParentObject, $strControlId);
262:             } catch (QCallerException $objExc) {
263:                 $objExc->IncrementOffset();
264:                 throw $objExc;
265:             }
266:             $this->intCurrentImage = null;
267:             
268:             
269:             $this->imgMainImage = new QImageControl($this);
270:             $this->imgMainImage->CssClass = 'ib_main_image';
271:             $this->imgMainImage->ImagePath = $this->invalidImagePath();
272:             
273:             
274:             
275:             $this->imgMainImage->CacheFolder = __IMAGE_CACHE_ASSETS__;
276:             
277:             
278:             $this->txtCaption = new QTextBox($this);
279:             $this->txtCaption->Name = 'Caption';
280:             $this->txtCaption->TextMode = QTextMode::MultiLine;
281:             $this->txtCaption->Rows = 2;
282:             $this->txtCaption->Enabled = false;
283:             if ($blnReadOnlyCaption) {
284:                 $this->txtCaption->CssClass = 'textbox ib_caption ib_caption_readonly';
285:                 $this->txtCaption->ReadOnly = true;
286:             } else {
287:                 $this->txtCaption->CssClass = 'textbox ib_caption';
288:                 $this->txtCaption->AddAction(new QChangeEvent(), new QAjaxControlAction($this, "txtCaption_Change"));
289: 
290:                 $this->btnSave = new QButton($this);
291:                 $this->btnSave->Text = QApplication::Translate('Save');
292:                 $this->btnSave->Enabled = false;
293:                 $this->btnSave->AddAction(new QClickEvent(), new QAjaxControlAction($this, "btnSave_Click"));
294:             }
295:             
296:             
297:             $this->ibnNavigation1 = new QImageBrowserNav($this);
298:             $this->ibnNavigation1->CssClass = 'ib_nav ib_nav1';
299:             if ($blnTwoNavBars) {
300:                 $this->ibnNavigation2 = new QImageBrowserNav($this);
301:                 $this->ibnNavigation2->CssClass = 'ib_nav ib_nav2';
302:             }
303:             
304:             
305:             if ($blnThumbnails) {
306:                 $this->ibtThumbnails = new QImageBrowserThumbnails($this);
307:                 $this->ibtThumbnails->CssClass = 'ib_thm';
308:             }
309:         }
310:         
311:         protected function reload() {
312:             $this->ibtThumbnails->reload();
313:             $this->intCurrentImage = 0;
314:             $this->setMainImage($this->intCurrentImage);
315:         }
316: 
317:         protected function setMainImage($intIdx) {
318:             $intCount = $this->ImageCount();
319:             $blnBackButtonsEnabled = $intCount > 1 && $intIdx > 0;
320:             $blnForwardButtonsEnabled = $intCount > 1 && $intIdx+1 < $intCount;
321:             $this->ibnNavigation1->BackButtonsEnabled($blnBackButtonsEnabled);
322:             if ($this->ibnNavigation2) {
323:                 $this->ibnNavigation2->BackButtonsEnabled($blnBackButtonsEnabled);
324:             }
325:             $this->ibnNavigation1->ForwardButtonsEnabled($blnForwardButtonsEnabled);
326:             if ($this->ibnNavigation2) {
327:                 $this->ibnNavigation2->ForwardButtonsEnabled($blnForwardButtonsEnabled);
328:             }
329:             if ($this->btnSave) {
330:                 $this->btnSave->Enabled = false;
331:             }
332:             if ($intIdx < 0 || $intIdx >= $intCount) {
333:                 $this->intCurrentImage = null;
334:                 $this->imgMainImage->ImagePath = $this->invalidImagePath();
335:                 $this->txtCaption->Enabled = false;
336:                 $this->txtCaption->Text = '';
337:                 return;
338:             }
339:             $this->txtCaption->Enabled = true;
340:             $strImagePath = $this->ImagePath($intIdx);
341:             $this->imgMainImage->ImagePath = $strImagePath;
342:             if ($this->ibtThumbnails) {
343:                 foreach ($this->ibtThumbnails->GetChildControls() as $ctrl) {
344:                     if ($ctrl instanceof QImageControl) {
345:                         if ($ctrl->ImagePath == $strImagePath) {
346:                             $ctrl->AddCssClass($this->selectThumbnailCssClass());
347:                         } else {
348:                             $ctrl->RemoveCssClass($this->selectThumbnailCssClass());
349:                         }
350:                     }
351:                 }
352:             }
353:             $this->intCurrentImage = $intIdx;
354:             $this->txtCaption->Text = $this->loadCaption($intIdx);
355:         }
356:         
357:         public function btnFirst_Click($strFormId, $strControlId, $strParameter) {
358:             $this->setMainImage(0);
359:         }
360:         
361:         public function btnNext_Click($strFormId, $strControlId, $strParameter) {
362:             if (!is_null($this->intCurrentImage)) {
363:                 $this->setMainImage($this->intCurrentImage + 1);
364:             }
365:         }
366:         
367:         public function btnPrev_Click($strFormId, $strControlId, $strParameter) {
368:             if (!is_null($this->intCurrentImage)) {
369:                 $this->setMainImage($this->intCurrentImage - 1);
370:             }
371:         }
372:         
373:         public function btnLast_Click($strFormId, $strControlId, $strParameter) {
374:             $this->setMainImage($this->ImageCount()-1);
375:         }
376:         
377:         public function imgThm_Click($strFormId, $strControlId, $strParameter) {
378:             $this->setMainImage($strParameter);
379:         }
380:         
381:         public function btnSave_Click($strFormId, $strControlId, $strParameter) {
382:             $this->saveCaption($this->intCurrentImage, $this->txtCaption->Text);
383:             if ($this->btnSave) {
384:                 $this->btnSave->Enabled = false;
385:             }
386:         }
387:         
388:         public function txtCaption_Change($strFormId, $strControlId, $strParameter) {
389:             if ($this->btnSave) {
390:                 $this->btnSave->Enabled = !is_null($this->intCurrentImage);
391:             }
392:         }
393:         
394:         
395:         
396:         
397: 
398:         399: 400: 401: 402: 
403:         abstract public function ImageCount();
404: 
405:         406: 407: 408: 409: 410: 
411:         abstract public function ImagePath($intIdx);    
412: 
413:         414: 415: 416: 417: 418: 419: 
420:         abstract public function ThumbnailImagePath($intIdx);
421: 
422:         423: 424: 425: 426: 427: 
428:         abstract protected function loadCaption($intIdx);
429:         
430:         
431:         432: 433: 434: 435: 
436:         abstract protected function saveCaption($intIdx, $strCaption);
437:         
438: 
439:         440: 441: 442: 443: 444: 
445:         protected function selectThumbnailCssClass() {
446:             return 'ib_thm_selected';
447:         }
448: 
449:         450: 451: 452: 453: 454: 
455:         protected function invalidImagePath() {
456:             return __DOCROOT__ . __IMAGE_ASSETS__ . '/file_asset_blank.png';
457:         }
458:         
459:         public function __get($strName) {
460:             switch ($strName) {
461:                 case "MainImage": return $this->imgMainImage;
462:                 case "Caption": return $this->txtCaption;
463:                 case "SaveButton": return $this->btnSave;
464:                 case "Navigation1": return $this->ibnNavigation1;
465:                 case "Navigation2": return $this->ibnNavigation2;
466:                 case "Thumbnails": return $this->ibtThumbnails;
467:                 default:
468:                     try {
469:                         return parent::__get($strName);
470:                     } catch (QCallerException $objExc) {
471:                         $objExc->IncrementOffset();
472:                         throw $objExc;
473:                     }
474:             }
475:         }
476: 
477:         public function __set($strName, $mixValue) {
478:             $this->blnModified = true;
479: 
480:             switch ($strName) {
481:                 case "Navigation1":
482:                     try {
483:                         if ($this->ibnNavigation1)
484:                             $this->RemoveChildControl($this->ibnNavigation1->ControlId, true);
485:                         $this->ibnNavigation1 = QType::Cast($mixValue, 'QImageBrowserNav');
486:                         break;
487:                     } catch (QInvalidCastException $objExc) {
488:                         $objExc->IncrementOffset();
489:                         throw $objExc;
490:                     }
491: 
492:                 case "Navigation2":
493:                     try {
494:                         if ($this->ibnNavigation2)
495:                             $this->RemoveChildControl($this->ibnNavigation2->ControlId, true);
496:                         $this->ibnNavigation2 = QType::Cast($mixValue, 'QImageBrowserNav');
497:                         break;
498:                     } catch (QInvalidCastException $objExc) {
499:                         $objExc->IncrementOffset();
500:                         throw $objExc;
501:                     }
502: 
503:                 case "Thumbnails":
504:                     try {
505:                         if ($this->ibtThumbnails)
506:                             $this->RemoveChildControl($this->ibtThumbnails->ControlId, true);
507:                         $this->ibtThumbnails = QType::Cast($mixValue, 'QImageBrowserThumbnails');
508:                         break;
509:                     } catch (QInvalidCastException $objExc) {
510:                         $objExc->IncrementOffset();
511:                         throw $objExc;
512:                     }
513: 
514:                 case "Caption":
515:                     try {
516:                         if ($this->txtCaption)
517:                             $this->RemoveChildControl($this->txtCaption->ControlId, true);
518:                         $this->txtCaption = QType::Cast($mixValue, 'QControl');
519:                         break;
520:                     } catch (QInvalidCastException $objExc) {
521:                         $objExc->IncrementOffset();
522:                         throw $objExc;
523:                     }
524: 
525:                 case "SaveButton":
526:                     try {
527:                         if ($this->btnSave) {
528:                             $this->btnSave->RemoveAllActions(QClickEvent::EventName);
529:                             $this->RemoveChildControl($this->btnSave->ControlId, true);
530:                         }
531:                         $this->btnSave = QType::Cast($mixValue, 'QControl');
532:                         $this->btnSave->AddAction(new QClickEvent(), new QAjaxControlAction($this, "btnSave_Click"));
533:                         break;
534:                     } catch (QInvalidCastException $objExc) {
535:                         $objExc->IncrementOffset();
536:                         throw $objExc;
537:                     }
538: 
539:                 default:
540:                     try {
541:                         parent::__set($strName, $mixValue);
542:                     } catch (QCallerException $objExc) {
543:                         $objExc->IncrementOffset();
544:                         throw $objExc;
545:                     }
546:                     break;
547:             }
548:         }
549:     }
550:     
551:     552: 553: 554: 555: 556: 557: 558: 559: 560: 
561:     class QImageBrowser extends QImageBrowserBase {
562:         protected $arrImagePaths;
563: 
564:         public function LoadImagesFromDirectory($strDir, $strPattern) {
565:             if (!is_dir($strDir)) {
566:                 throw new QCallerException("$strDir is not a directory"); 
567:             }
568: 
569:             $dh = opendir($strDir);
570:             if ($dh === false) {
571:                 throw new QCallerException("Could not open directory $strDir");
572:             }
573:             $this->arrImagePaths = array();
574:             while ($strFile = readdir($dh)) {
575:                 if ("." == $strFile || ".." == $strFile) {
576:                     continue;
577:                 }
578:                 if (preg_match($strPattern, $strFile) > 0) {
579:                     $this->arrImagePaths[] = $strDir.'/'.$strFile;
580:                 }
581:             }
582:             closedir($dh);
583:             $this->reload();
584:         }
585:         
586:         public function ImageCount() {
587:             if (!$this->arrImagePaths) return 0;
588:             return count($this->arrImagePaths);
589:         }
590: 
591:         public function ImagePath($intIdx) {
592:             return $this->arrImagePaths[$intIdx];
593:         }
594: 
595:         public function ThumbnailImagePath($intIdx) {
596:             return $this->ImagePath($intIdx);
597:         }
598: 
599:         protected function captionFileName($intIdx) {
600:             $strImagePath = $this->ImagePath($intIdx);
601:             return $strImagePath.'.txt';
602:         }
603:         
604:         protected function loadCaption($intIdx) {
605:             $strCaptionFile = $this->captionFileName($intIdx);
606:             if (!file_exists($strCaptionFile)) {
607:                 
608:                 return '';
609:             }
610:             if (false === ($strCaption = file_get_contents($strCaptionFile))) {
611:                 
612:                 return '';
613:             }
614:             return $strCaption;
615:         }
616:         
617:         protected function saveCaption($intIdx, $strCaption) {
618:             $strCaptionFile = $this->captionFileName($intIdx);
619:             file_put_contents($strCaptionFile, $strCaption, LOCK_EX);
620:         }
621:         
622:         public function __get($strName) {
623:             switch ($strName) {
624:                 case "ImagePaths": return $this->arrImagePaths;
625:                 default:
626:                     try {
627:                         return parent::__get($strName);
628:                     } catch (QCallerException $objExc) {
629:                         $objExc->IncrementOffset();
630:                         throw $objExc;
631:                     }
632:             }
633:         }
634:         public function __set($strName, $mixValue) {
635:             $this->blnModified = true;
636: 
637:             switch ($strName) {
638:                 case "ImagePaths":
639:                     try {
640:                         $this->arrImagePaths = QType::Cast($mixValue, QType::ArrayType);
641:                         $this->reload();
642:                         break;
643:                     } catch (QInvalidCastException $objExc) {
644:                         $objExc->IncrementOffset();
645:                         throw $objExc;
646:                     }
647: 
648:                 default:
649:                     try {
650:                         parent::__set($strName, $mixValue);
651:                     } catch (QCallerException $objExc) {
652:                         $objExc->IncrementOffset();
653:                         throw $objExc;
654:                     }
655:                     break;
656:             }
657:         }
658:     }