001/* 002 * This library is part of OpenCms - 003 * the Open Source Content Management System 004 * 005 * Copyright (c) Alkacon Software GmbH & Co. KG (http://www.alkacon.com) 006 * 007 * This library is free software; you can redistribute it and/or 008 * modify it under the terms of the GNU Lesser General Public 009 * License as published by the Free Software Foundation; either 010 * version 2.1 of the License, or (at your option) any later version. 011 * 012 * This library is distributed in the hope that it will be useful, 013 * but WITHOUT ANY WARRANTY; without even the implied warranty of 014 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU 015 * Lesser General Public License for more details. 016 * 017 * For further information about Alkacon Software, please see the 018 * company website: http://www.alkacon.com 019 * 020 * For further information about OpenCms, please see the 021 * project website: http://www.opencms.org 022 * 023 * You should have received a copy of the GNU Lesser General Public 024 * License along with this library; if not, write to the Free Software 025 * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA 026 */ 027 028package org.opencms.ade.galleries.client.preview; 029 030import org.opencms.ade.galleries.client.Messages; 031import org.opencms.ade.galleries.client.preview.ui.CmsCroppingDialog; 032import org.opencms.ade.galleries.client.preview.ui.CmsImageFormatsForm; 033import org.opencms.ade.galleries.client.ui.CmsGalleryDialog; 034import org.opencms.ade.galleries.shared.I_CmsGalleryProviderConstants.GalleryMode; 035import org.opencms.gwt.client.util.CmsClientStringUtil; 036import org.opencms.util.CmsStringUtil; 037 038import java.util.Collections; 039import java.util.LinkedHashMap; 040import java.util.Map; 041import java.util.Map.Entry; 042 043import com.google.gwt.event.logical.shared.HasValueChangeHandlers; 044import com.google.gwt.event.logical.shared.ValueChangeEvent; 045import com.google.gwt.event.logical.shared.ValueChangeHandler; 046import com.google.gwt.event.shared.EventHandler; 047import com.google.gwt.event.shared.GwtEvent; 048import com.google.gwt.event.shared.HandlerRegistration; 049import com.google.gwt.event.shared.SimpleEventBus; 050 051/** 052 * Image format form handler.<p> 053 * 054 * @since 8.0.0 055 */ 056public class CmsImageFormatHandler implements HasValueChangeHandlers<CmsCroppingParamBean> { 057 058 /** Default image formats. */ 059 private enum DefaultRestriction { 060 /** Big image format. */ 061 big, /** Free image format. */ 062 free, /** Original format. */ 063 original, /** Small image format. */ 064 small, /** User defined image format. */ 065 user 066 } 067 068 /** Default format configuration. */ 069 private static final String[] DEFAULT_FORMAT_NAMES = { 070 DefaultRestriction.original.name() + ":" + Messages.get().key(Messages.GUI_IMAGE_ORIGINAL_FORMAT_LABEL_0), 071 DefaultRestriction.user.name() + ":" + Messages.get().key(Messages.GUI_IMAGE_USER_FORMAT_LABEL_0), 072 DefaultRestriction.free.name() + ":" + Messages.get().key(Messages.GUI_IMAGE_FREE_FORMAT_LABEL_0), 073 DefaultRestriction.small.name() + ":" + Messages.get().key(Messages.GUI_IMAGE_SMALL_FORMAT_LABEL_0), 074 DefaultRestriction.big.name() + ":" + Messages.get().key(Messages.GUI_IMAGE_BIG_FORMAT_LABEL_0)}; 075 076 /** Default format configuration. */ 077 private static final String[] DEFAULT_FORMAT_VALUES = { 078 DefaultRestriction.original.name(), 079 DefaultRestriction.user.name(), 080 DefaultRestriction.free.name(), 081 DefaultRestriction.small.name(), 082 DefaultRestriction.big.name()}; 083 084 /** The cropping dialog instance. */ 085 private CmsCroppingDialog m_croppingDialog; 086 087 /** The current cropping parameter. */ 088 private CmsCroppingParamBean m_croppingParam; 089 090 /** The current image format restriction. */ 091 private I_CmsFormatRestriction m_currentFormat; 092 093 /** The event bus. */ 094 private SimpleEventBus m_eventBus; 095 096 /** The format form. */ 097 private CmsImageFormatsForm m_formatForm; 098 099 /** The format names and labels configuration. */ 100 private String[] m_formatNames; 101 102 /** The map of available format restrictions. */ 103 private Map<String, I_CmsFormatRestriction> m_formats = Collections.emptyMap(); 104 105 /** The Format configuration. */ 106 private String[] m_formatValues; 107 108 /** Flag to indicate the handler has been initialized. */ 109 private boolean m_initialized; 110 111 /** The image height. */ 112 private int m_originalHeight = -1; 113 114 /** The image width. */ 115 private int m_originalWidth = -1; 116 117 /** Flag indicating if the height / width ratio is locked. */ 118 private boolean m_ratioLocked; 119 120 /** Flag to indicate if image format may be changed. */ 121 private boolean m_useFormats; 122 123 /** The user format key, if available. */ 124 private String m_userFormatKey; 125 126 /** 127 * Constructor.<p> 128 * 129 * @param galleryMode the gallery mode 130 * @param dialog the gallery dialog 131 * @param selectedPath the selected gallery path 132 * @param imageHeight the image height 133 * @param imageWidth the image width 134 */ 135 public CmsImageFormatHandler( 136 GalleryMode galleryMode, 137 CmsGalleryDialog dialog, 138 String selectedPath, 139 int imageHeight, 140 int imageWidth) { 141 142 m_originalHeight = imageHeight; 143 m_originalWidth = imageWidth; 144 m_croppingParam = CmsCroppingParamBean.parseImagePath(selectedPath); 145 m_croppingParam.setOrgHeight(imageHeight); 146 m_croppingParam.setOrgWidth(imageWidth); 147 m_ratioLocked = true; 148 m_useFormats = dialog.isUseFormats(); 149 if (m_useFormats) { 150 if (CmsStringUtil.isNotEmptyOrWhitespaceOnly(dialog.getImageFormats())) { 151 m_formatValues = dialog.getImageFormats().split(","); 152 } 153 if (CmsStringUtil.isNotEmptyOrWhitespaceOnly(dialog.getImageFormatNames())) { 154 m_formatNames = dialog.getImageFormatNames().split(","); 155 } 156 } 157 readFormatsConfig(galleryMode, dialog.isNativeWidget(), dialog.isOverrideFormats()); 158 if (m_useFormats) { 159 generateFormats(); 160 } 161 } 162 163 /** 164 * @see com.google.gwt.event.logical.shared.HasValueChangeHandlers#addValueChangeHandler(com.google.gwt.event.logical.shared.ValueChangeHandler) 165 */ 166 public HandlerRegistration addValueChangeHandler(ValueChangeHandler<CmsCroppingParamBean> handler) { 167 168 return addHandler(handler, ValueChangeEvent.getType()); 169 } 170 171 /** 172 * @see com.google.gwt.event.shared.HasHandlers#fireEvent(com.google.gwt.event.shared.GwtEvent) 173 */ 174 public void fireEvent(GwtEvent<?> event) { 175 176 ensureHandlers().fireEventFromSource(event, this); 177 } 178 179 /** 180 * Returns the current cropping parameter.<p> 181 * 182 * @return the current cropping parameter 183 */ 184 public CmsCroppingParamBean getCroppingParam() { 185 186 return m_croppingParam; 187 } 188 189 /** 190 * Returns the current format.<p> 191 * 192 * @return the current format 193 */ 194 public I_CmsFormatRestriction getCurrentFormat() { 195 196 return m_currentFormat; 197 } 198 199 /** 200 * Returns the formats.<p> 201 * 202 * @return the formats 203 */ 204 public Map<String, I_CmsFormatRestriction> getFormats() { 205 206 return m_formats; 207 } 208 209 /** 210 * Adds necessary attributes to the map.<p> 211 * 212 * @param attributes the attribute map 213 * @return the attribute map 214 */ 215 public Map<String, String> getImageAttributes(Map<String, String> attributes) { 216 217 attributes.put("height", String.valueOf(m_croppingParam.getResultingHeight())); 218 attributes.put("width", String.valueOf(m_croppingParam.getResultingWidth())); 219 return attributes; 220 } 221 222 /** 223 * Returns the original height.<p> 224 * 225 * @return the original height 226 */ 227 public int getOriginalHeight() { 228 229 return m_originalHeight; 230 } 231 232 /** 233 * Returns the original width.<p> 234 * 235 * @return the original width 236 */ 237 public int getOriginalWidth() { 238 239 return m_originalWidth; 240 } 241 242 /** 243 * Initializes the format form handler.<p> 244 * 245 * @param formatForm the format form 246 * @param croppingDialog the cropping dialog 247 */ 248 public void init(CmsImageFormatsForm formatForm, CmsCroppingDialog croppingDialog) { 249 250 m_croppingDialog = croppingDialog; 251 m_formatForm = formatForm; 252 if (m_useFormats) { 253 for (Entry<String, I_CmsFormatRestriction> entry : m_formats.entrySet()) { 254 m_formatForm.addFormatSelectOption(entry.getKey(), entry.getValue().getLabel()); 255 } 256 I_CmsFormatRestriction match = getMatchingFormat(m_croppingParam, true); 257 if (match != null) { 258 m_currentFormat = match; 259 m_formatForm.setFormatSelectValue(match.getName()); 260 adjustToCurrentFormat(); 261 } else { 262 onResetSize(); 263 } 264 } else { 265 m_formatForm.addFormatSelectOption("--", "--"); 266 m_formatForm.setFormEnabled(m_useFormats); 267 } 268 if (m_croppingParam.isCropped()) { 269 setCropping(m_croppingParam); 270 } 271 m_croppingDialog.addValueChangeHandler(new ValueChangeHandler<CmsCroppingParamBean>() { 272 273 /** 274 * Executed on value change. Sets the returned cropping parameters.<p> 275 * 276 * @param event the value change event 277 */ 278 public void onValueChange(ValueChangeEvent<CmsCroppingParamBean> event) { 279 280 setCropping(event.getValue()); 281 } 282 }); 283 m_initialized = true; 284 } 285 286 /** 287 * Returns if scaling formats may be selected for the image.<p> 288 * 289 * @return <code>true</code> if scaling formats may be selected for the image 290 */ 291 public boolean isUseFormats() { 292 293 return m_useFormats; 294 } 295 296 /** 297 * Execute on format change.<p> 298 * 299 * @param formatKey the new format value 300 */ 301 public void onFormatChange(String formatKey) { 302 303 // setting the selected format restriction 304 m_currentFormat = m_formats.get(formatKey); 305 m_currentFormat.adjustCroppingParam(m_croppingParam); 306 adjustToCurrentFormat(); 307 if (m_initialized) { 308 // fire change only if initialized 309 fireValueChangedEvent(); 310 } 311 } 312 313 /** 314 * Execute on height change.<p> 315 * 316 * @param height the new height 317 */ 318 public void onHeightChange(String height) { 319 320 int value = CmsClientStringUtil.parseInt(height); 321 if ((m_croppingParam.getTargetHeight() == value) || (value == 0)) { 322 // the value has not changed, ignore'0' 323 return; 324 } 325 m_croppingParam.setTargetHeight(value); 326 if (m_ratioLocked) { 327 m_croppingParam.setTargetWidth((value * m_originalWidth) / m_originalHeight); 328 m_formatForm.setWidthInput(m_croppingParam.getTargetWidth()); 329 } 330 // in case the width and height parameter don't match the current format any longer, switch to user defined format 331 if ((!m_currentFormat.isHeightEditable() || (m_ratioLocked && !m_currentFormat.isWidthEditable())) 332 && hasUserFormatRestriction()) { 333 m_formatForm.setFormatSelectValue(m_userFormatKey); 334 } else { 335 fireValueChangedEvent(); 336 } 337 } 338 339 /** 340 * Execute when the lock image ratio is clicked.<p> 341 * 342 * @param locked <code>true</code> if ratio is locked 343 */ 344 public void onLockRatio(boolean locked) { 345 346 m_ratioLocked = locked; 347 } 348 349 /** 350 * Execute when cropping is removed.<p> 351 */ 352 public void onRemoveCropping() { 353 354 m_formatForm.setCropped(false); 355 onResetSize(); 356 } 357 358 /** 359 * Execute to reset image format and size input.<p> 360 */ 361 public void onResetSize() { 362 363 String restrictionKey; 364 if (m_formats.containsKey(DefaultRestriction.original.name())) { 365 restrictionKey = DefaultRestriction.original.name(); 366 } else { 367 restrictionKey = m_formats.keySet().iterator().next(); 368 } 369 m_formatForm.setFormatSelectValue(restrictionKey); 370 m_croppingParam.reset(); 371 onFormatChange(restrictionKey); 372 } 373 374 /** 375 * Execute on width change.<p> 376 * 377 * @param width the new width 378 */ 379 public void onWidthChange(String width) { 380 381 int value = CmsClientStringUtil.parseInt(width); 382 if ((m_croppingParam.getTargetWidth() == value) || (value == 0)) { 383 // the value has not changed, ignore'0' 384 return; 385 } 386 m_croppingParam.setTargetWidth(value); 387 if (m_ratioLocked) { 388 m_croppingParam.setTargetHeight((value * m_originalHeight) / m_originalWidth); 389 m_formatForm.setHeightInput(m_croppingParam.getTargetHeight()); 390 } 391 // in case the width and height parameter don't match the current format any longer, switch to user defined format 392 if ((!m_currentFormat.isWidthEditable() || (m_ratioLocked && !m_currentFormat.isHeightEditable())) 393 && hasUserFormatRestriction()) { 394 m_formatForm.setFormatSelectValue(m_userFormatKey); 395 } else { 396 fireValueChangedEvent(); 397 } 398 } 399 400 /** 401 * Shows the image cropping dialog.<p> 402 */ 403 public void openCropping() { 404 405 CmsCroppingParamBean param = new CmsCroppingParamBean(m_croppingParam); 406 m_currentFormat.adjustCroppingParam(param); 407 m_croppingDialog.show(param); 408 } 409 410 /** 411 * Sets the given cropping parameter.<p> 412 * 413 * @param croppingParam the cropping parameter 414 */ 415 public void setCropping(CmsCroppingParamBean croppingParam) { 416 417 m_croppingParam = croppingParam; 418 m_formatForm.setHeightInput(m_croppingParam.getTargetHeight()); 419 m_formatForm.setWidthInput(m_croppingParam.getTargetWidth()); 420 421 // only in case of the original-format-restriction, the cropping dialog may be opened to override the selected format 422 if (m_currentFormat instanceof CmsOriginalFormatRestriction) { 423 I_CmsFormatRestriction format = getMatchingFormat(m_croppingParam, false); 424 if (format != null) { 425 m_currentFormat = format; 426 m_formatForm.setFormatSelectValue(format.getName()); 427 } 428 } 429 m_formatForm.setCropped(true); 430 fireValueChangedEvent(); 431 } 432 433 /** 434 * Sets the original width.<p> 435 * 436 * @param originalWidth the original width to set 437 */ 438 public void setOriginalWidth(int originalWidth) { 439 440 m_originalWidth = originalWidth; 441 } 442 443 /** 444 * Adds this handler to the widget. 445 * 446 * @param <H> the type of handler to add 447 * @param type the event type 448 * @param handler the handler 449 * @return {@link HandlerRegistration} used to remove the handler 450 */ 451 protected final <H extends EventHandler> HandlerRegistration addHandler(final H handler, GwtEvent.Type<H> type) { 452 453 return ensureHandlers().addHandlerToSource(type, this, handler); 454 } 455 456 /** 457 * Helper method for firing a 'value changed' event.<p> 458 */ 459 protected void fireValueChangedEvent() { 460 461 ValueChangeEvent.fire(this, m_croppingParam); 462 } 463 464 /** 465 * Adjusts the current format to the cropping parameter.<p> 466 */ 467 private void adjustToCurrentFormat() { 468 469 // in case of a locked or fixed image ratio height and width need to be reset 470 int height = m_croppingParam.getOrgHeight(); 471 int width = m_croppingParam.getOrgWidth(); 472 if (m_croppingParam.isScaled()) { 473 if (m_croppingParam.getTargetHeight() == -1) { 474 height = (int)Math.floor( 475 ((1.00 * m_croppingParam.getOrgHeight()) / m_croppingParam.getOrgWidth()) 476 * m_croppingParam.getTargetWidth()); 477 } else { 478 height = m_croppingParam.getTargetHeight(); 479 } 480 if (m_croppingParam.getTargetWidth() == -1) { 481 width = (int)Math.floor( 482 ((1.00 * m_croppingParam.getOrgWidth()) / m_croppingParam.getOrgHeight()) 483 * m_croppingParam.getTargetHeight()); 484 } else { 485 width = m_croppingParam.getTargetWidth(); 486 } 487 488 } else { 489 m_croppingParam.setTargetHeight(height); 490 m_croppingParam.setTargetWidth(width); 491 } 492 m_formatForm.setHeightInput(height); 493 m_formatForm.setWidthInput(width); 494 // enabling/disabling ratio lock button 495 if (m_currentFormat.isFixedRatio()) { 496 m_formatForm.setRatioButton(false, false, Messages.get().key(Messages.GUI_PRIVIEW_BUTTON_RATIO_FIXED_0)); 497 m_ratioLocked = true; 498 } else { 499 if (!m_currentFormat.isHeightEditable() && !m_currentFormat.isWidthEditable()) { 500 // neither height nor width are editable, disable ratio lock button 501 m_formatForm.setRatioButton( 502 false, 503 false, 504 Messages.get().key(Messages.GUI_PRIVIEW_BUTTON_NOT_EDITABLE_0)); 505 } else { 506 m_formatForm.setRatioButton(false, true, null); 507 } 508 m_ratioLocked = true; 509 } 510 // enabling/disabling height and width input 511 m_formatForm.setHeightInputEnabled(m_currentFormat.isHeightEditable() || hasUserFormatRestriction()); 512 m_formatForm.setWidthInputEnabled(m_currentFormat.isWidthEditable() || hasUserFormatRestriction()); 513 } 514 515 /** 516 * Lazy initializing the handler manager.<p> 517 * 518 * @return the handler manager 519 */ 520 private SimpleEventBus ensureHandlers() { 521 522 if (m_eventBus == null) { 523 m_eventBus = new SimpleEventBus(); 524 } 525 return m_eventBus; 526 } 527 528 /** 529 * Generates the format restriction objects.<p> 530 */ 531 private void generateFormats() { 532 533 m_formats = new LinkedHashMap<String, I_CmsFormatRestriction>(); 534 for (int i = 0; i < m_formatValues.length; i++) { 535 String value = m_formatValues[i].trim(); 536 537 if (CmsStringUtil.isNotEmptyOrWhitespaceOnly(value)) { 538 String label = value; 539 String key = value; 540 if ((m_formatNames != null) 541 && (m_formatNames.length > i) 542 && CmsStringUtil.isNotEmptyOrWhitespaceOnly(m_formatNames[i])) { 543 int pos = m_formatNames[i].indexOf(":"); 544 if (pos > 0) { 545 label = m_formatNames[i].substring(pos + 1, m_formatNames[i].length()); 546 key = m_formatNames[i].substring(0, pos); 547 } else { 548 label = m_formatNames[i]; 549 key = m_formatNames[i]; 550 } 551 } 552 553 DefaultRestriction restrictionType = null; 554 try { 555 restrictionType = DefaultRestriction.valueOf(value); 556 } catch (Exception e) { 557 // happens with user defined restriction settings 558 } 559 if (restrictionType != null) { 560 switch (restrictionType) { 561 case original: 562 m_formats.put(key, new CmsOriginalFormatRestriction(key, label)); 563 break; 564 case user: 565 m_userFormatKey = key; 566 m_formats.put(key, new CmsUserFormatRestriction(key, label)); 567 break; 568 case free: 569 m_formats.put(key, new CmsFreeFormatRestriction(key, label)); 570 break; 571 case small: 572 m_formats.put(key, new CmsImageFormatRestriction(key, label, "200x?")); 573 break; 574 case big: 575 m_formats.put(key, new CmsImageFormatRestriction(key, label, "500x?")); 576 break; 577 default: 578 } 579 } else { 580 if (CmsImageFormatRestriction.isValidConfig(value)) { 581 m_formats.put(key, new CmsImageFormatRestriction(key, label, value)); 582 } 583 } 584 } 585 } 586 } 587 588 /** 589 * Checks the format restrictions if the match the giving cropping parameter.<p> 590 * 591 * @param croppingParam the cropping parameter 592 * @param forceByName force format match by name within cropping parameter 593 * 594 * @return the matching format restriction 595 */ 596 private I_CmsFormatRestriction getMatchingFormat(CmsCroppingParamBean croppingParam, boolean forceByName) { 597 598 I_CmsFormatRestriction result = null; 599 if (forceByName && m_formats.containsKey(croppingParam.getFormatName())) { 600 result = m_formats.get(croppingParam.getFormatName()); 601 if (!result.matchesCroppingParam(croppingParam)) { 602 result.adjustCroppingParam(croppingParam); 603 } 604 return result; 605 } 606 for (I_CmsFormatRestriction format : m_formats.values()) { 607 608 if (format.matchesCroppingParam(croppingParam)) { 609 result = format; 610 if (format.getName().equals(croppingParam.getFormatName())) { 611 break; 612 } 613 } 614 } 615 return result; 616 } 617 618 /** 619 * Returns if the user defined format restriction is available.<p> 620 * 621 * @return <code>true</code> if the user defined format restriction is available 622 */ 623 private boolean hasUserFormatRestriction() { 624 625 return m_userFormatKey != null; 626 } 627 628 /** 629 * Reads the format configuration for the given gallery mode.<p> 630 * 631 * @param mode the gallery mode 632 * @param isNativeWidget if the dialog is used as a native widget 633 * @param overrideFormats true if the formats from the gallery dialog should take priority in 'editor' mode 634 */ 635 private void readFormatsConfig(GalleryMode mode, boolean isNativeWidget, boolean overrideFormats) { 636 637 switch (mode) { 638 case editor: 639 if (!overrideFormats) { 640 m_useFormats = true; 641 m_formatNames = DEFAULT_FORMAT_NAMES; 642 m_formatValues = DEFAULT_FORMAT_VALUES; 643 } else { 644 if ((m_formatNames == null) && m_useFormats) { 645 m_formatNames = DEFAULT_FORMAT_NAMES; 646 } 647 if ((m_formatValues == null) && m_useFormats) { 648 m_formatValues = DEFAULT_FORMAT_VALUES; 649 } 650 } 651 break; 652 case widget: 653 if (!isNativeWidget) { 654 m_useFormats = CmsPreviewUtil.isShowFormats(); 655 if (m_useFormats) { 656 m_formatValues = CmsPreviewUtil.getFormats(); 657 if (m_formatValues == null) { 658 m_formatNames = DEFAULT_FORMAT_NAMES; 659 m_formatValues = DEFAULT_FORMAT_VALUES; 660 } else { 661 m_formatNames = CmsPreviewUtil.getFormatNames(); 662 } 663 } 664 } else if (m_useFormats && (m_formatValues == null)) { 665 m_formatNames = DEFAULT_FORMAT_NAMES; 666 m_formatValues = DEFAULT_FORMAT_VALUES; 667 } 668 break; 669 case ade: 670 case view: 671 case adeView: 672 m_useFormats = false; 673 break; 674 default: 675 } 676 } 677}