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.gwt.client.property; 029 030import org.opencms.gwt.client.I_CmsDescendantResizeHandler; 031import org.opencms.gwt.client.Messages; 032import org.opencms.gwt.client.ui.CmsFieldSet; 033import org.opencms.gwt.client.ui.CmsListItemWidget; 034import org.opencms.gwt.client.ui.CmsScrollPanel; 035import org.opencms.gwt.client.ui.CmsTabbedPanel; 036import org.opencms.gwt.client.ui.css.I_CmsInputLayoutBundle; 037import org.opencms.gwt.client.ui.input.CmsTextArea; 038import org.opencms.gwt.client.ui.input.CmsTextBox; 039import org.opencms.gwt.client.ui.input.I_CmsFormField; 040import org.opencms.gwt.client.ui.input.I_CmsFormWidget; 041import org.opencms.gwt.client.ui.input.form.A_CmsFormFieldPanel; 042import org.opencms.gwt.client.ui.input.form.CmsFormDialog; 043import org.opencms.gwt.client.ui.input.form.CmsInfoBoxFormFieldPanel; 044import org.opencms.gwt.client.util.CmsDomUtil; 045 046import org.opencms.gwt.shared.CmsListInfoBean; 047import org.opencms.util.CmsStringUtil; 048 049import java.util.ArrayList; 050import java.util.Collection; 051import java.util.Iterator; 052import java.util.LinkedHashMap; 053import java.util.List; 054import java.util.Map; 055import java.util.Set; 056 057import com.google.common.collect.ArrayListMultimap; 058import com.google.common.collect.Multimap; 059import com.google.common.collect.Sets; 060import com.google.gwt.core.client.GWT; 061import com.google.gwt.dom.client.Style.Unit; 062import com.google.gwt.event.logical.shared.BeforeSelectionHandler; 063import com.google.gwt.event.logical.shared.SelectionEvent; 064import com.google.gwt.event.logical.shared.SelectionHandler; 065import com.google.gwt.user.client.Timer; 066import com.google.gwt.user.client.ui.FlowPanel; 067import com.google.gwt.user.client.ui.Panel; 068import com.google.gwt.user.client.ui.Widget; 069 070/** 071 * A tabbed form field container widget.<p> 072 * 073 * @since 8.0.0 074 */ 075public class CmsPropertyPanel extends A_CmsFormFieldPanel { 076 077 /** Layout data key. */ 078 public static final String LD_DISPLAY_VALUE = "displayValue"; 079 080 /** Layout data key. */ 081 public static final String LD_GROUP = "group"; 082 083 /** Layout data key. */ 084 public static final String LD_PROPERTY = "property"; 085 086 /** Tab id for the "individual" tab. */ 087 public static final String TAB_INDIVIDUAL = "individual"; 088 089 /** Tab id for the "shared" tab. */ 090 public static final String TAB_SHARED = "shared"; 091 092 /** Tab id for the "simple" tab. */ 093 public static final String TAB_SIMPLE = "simple"; 094 095 /** The tab panel. */ 096 protected CmsTabbedPanel<CmsScrollPanel> m_tabPanel = new CmsTabbedPanel<CmsScrollPanel>(); 097 098 /** Multimap of fields by field group. */ 099 private Multimap<String, I_CmsFormField> m_fieldsByGroup = ArrayListMultimap.create(); 100 101 /** Map from group/tab names to corresponding panels. */ 102 private Map<String, Panel> m_groups = new LinkedHashMap<String, Panel>(); 103 104 /** Set of fields which should be displayed at the top of the "individual" tab. */ 105 private Set<String> m_individualDisplay = Sets.newHashSet(); 106 107 /** The "individual" tab. */ 108 private FlowPanel m_individualTab = new FlowPanel(); 109 110 /** The tab wrapper for the individual tab. */ 111 private FlowPanel m_individualTabWrapper = new FlowPanel(); 112 113 /** Name of property which should be focused, used while rendering the extended tabs. */ 114 private String m_markedProperty; 115 116 /** Set of fields which should be displayed at the top of the "shared" tab. */ 117 private Set<String> m_sharedDisplay = Sets.newHashSet(); 118 119 /** The "shared" tab. */ 120 private FlowPanel m_sharedTab = new FlowPanel(); 121 122 /** The tab wrapper for the shared tab. */ 123 private FlowPanel m_sharedTabWrapper = new FlowPanel(); 124 125 /** True if the "shared" tab should be shown. */ 126 private boolean m_showShared; 127 128 /** The "simple" tab. */ 129 private FlowPanel m_simpleTab = new FlowPanel(); 130 131 /** The tab wrapper for the simple tab. */ 132 private FlowPanel m_simpleTabWrapper = new FlowPanel(); 133 134 /** 135 * Creates a new instance.<p> 136 * 137 * @param showShared true if the "shared" tab should be shown 138 * @param info the bean to use for displaying the info item 139 */ 140 public CmsPropertyPanel(boolean showShared, CmsListInfoBean info) { 141 142 m_infoWidget = createListItemWidget(info); 143 m_simpleTabWrapper.add(m_infoWidget); 144 m_simpleTabWrapper.add(m_simpleTab); 145 m_simpleTab.addStyleName(org.opencms.gwt.client.ui.css.I_CmsLayoutBundle.INSTANCE.generalCss().cornerAll()); 146 m_simpleTab.addStyleName( 147 org.opencms.gwt.client.ui.css.I_CmsLayoutBundle.INSTANCE.propertiesCss().vfsModeSimplePropertiesBox()); 148 m_simpleTab.addStyleName(I_CmsInputLayoutBundle.INSTANCE.inputCss().formGradientBackground()); 149 m_sharedTabWrapper.add(createListItemWidget(info)); 150 m_sharedTabWrapper.add(m_sharedTab); 151 152 m_individualTabWrapper.add(createListItemWidget(info)); 153 m_individualTabWrapper.add(m_individualTab); 154 m_groups.put(TAB_SIMPLE, m_simpleTab); 155 m_groups.put(TAB_SHARED, m_sharedTab); 156 m_groups.put(TAB_INDIVIDUAL, m_individualTab); 157 CmsScrollPanel scrollPanel = GWT.create(CmsScrollPanel.class); 158 scrollPanel.setWidget(m_simpleTabWrapper); 159 m_tabPanel.add(scrollPanel, Messages.get().key(Messages.GUI_PROPERTY_TAB_SIMPLE_0)); 160 m_showShared = showShared; 161 if (m_showShared) { 162 scrollPanel = GWT.create(CmsScrollPanel.class); 163 scrollPanel.setWidget(m_individualTabWrapper); 164 m_tabPanel.add(scrollPanel, Messages.get().key(Messages.GUI_PROPERTY_TAB_STRUCTURE_0)); 165 scrollPanel = GWT.create(CmsScrollPanel.class); 166 scrollPanel.setWidget(m_sharedTabWrapper); 167 m_tabPanel.add(scrollPanel, Messages.get().key(Messages.GUI_PROPERTY_TAB_RESOURCE_0)); 168 } else { 169 scrollPanel = GWT.create(CmsScrollPanel.class); 170 scrollPanel.setWidget(m_individualTabWrapper); 171 m_tabPanel.add(scrollPanel, Messages.get().key(Messages.GUI_PROPERTY_TAB_COMPLETE_0)); 172 } 173 initWidget(m_tabPanel); 174 addStyleName(org.opencms.gwt.client.ui.css.I_CmsLayoutBundle.INSTANCE.propertiesCss().propertyPanel()); 175 m_tabPanel.addSelectionHandler(new SelectionHandler<Integer>() { 176 177 public void onSelection(SelectionEvent<Integer> event) { 178 179 Widget selectedTab = m_tabPanel.getWidget(event.getSelectedItem().intValue()); 180 if (selectedTab instanceof I_CmsDescendantResizeHandler) { 181 ((I_CmsDescendantResizeHandler)selectedTab).onResizeDescendant(); 182 } 183 } 184 }); 185 } 186 187 /** 188 * Adds the {@link BeforeSelectionHandler} for the tab panel.<p> 189 * 190 * @param handler the pre-selection handler 191 */ 192 public void addBeforeSelectionHandler(BeforeSelectionHandler<Integer> handler) { 193 194 m_tabPanel.addBeforeSelectionHandler(handler); 195 } 196 197 /** 198 * Clears the tab with the given id.<p> 199 * 200 * @param tabId the id of the tab to clear 201 */ 202 public void clearTab(String tabId) { 203 204 m_groups.get(tabId).clear(); 205 } 206 207 /** 208 * @see org.opencms.gwt.client.ui.input.form.A_CmsFormFieldPanel#getDefaultGroup() 209 */ 210 @Override 211 public String getDefaultGroup() { 212 213 return TAB_SIMPLE; 214 } 215 216 /** 217 * Renders a extended tab.<p> 218 * 219 * @param fields the fields to add 220 * @param tab the tab 221 */ 222 public void renderExtendedTab(Collection<I_CmsFormField> fields, FlowPanel tab) { 223 224 List<CmsFieldSet> result = new ArrayList<CmsFieldSet>(); 225 226 tab.clear(); 227 228 String used = Messages.get().key(Messages.GUI_PROPERTY_BLOCK_USED_0); 229 CmsFieldSet usedFieldSet = new CmsFieldSet(); 230 usedFieldSet.addStyleName(I_CmsInputLayoutBundle.INSTANCE.inputCss().formGradientBackground()); 231 usedFieldSet.setLegend(used); 232 usedFieldSet.setAnimationDuration(50); 233 234 String unused = Messages.get().key(Messages.GUI_PROPERTY_BLOCK_UNUSED_0); 235 CmsFieldSet unusedFieldSet = new CmsFieldSet(); 236 unusedFieldSet.addStyleName(I_CmsInputLayoutBundle.INSTANCE.inputCss().formGradientBackground()); 237 unusedFieldSet.setOpen(false); 238 unusedFieldSet.setLegend(unused); 239 unusedFieldSet.setAnimationDuration(50); 240 boolean reopen = false; 241 final String currentMarkedProperty = m_markedProperty; 242 m_markedProperty = null; 243 for (I_CmsFormField field : fields) { 244 if (isTop(field)) { 245 usedFieldSet.addContent(createRow(field)); 246 } else { 247 if ((currentMarkedProperty != null) 248 && field.getLayoutData().get(LD_PROPERTY).equals(currentMarkedProperty)) { 249 reopen = true; 250 } 251 unusedFieldSet.addContent(createRow(field)); 252 } 253 } 254 if (reopen) { 255 unusedFieldSet.setOpen(true); 256 } 257 258 if (usedFieldSet.getWidgetCount() > 0) { 259 result.add(usedFieldSet); 260 } 261 if (unusedFieldSet.getWidgetCount() > 0) { 262 result.add(unusedFieldSet); 263 } 264 265 Iterator<CmsFieldSet> iter = result.iterator(); 266 while (iter.hasNext()) { 267 CmsFieldSet fieldSet = iter.next(); 268 if (iter.hasNext()) { 269 fieldSet.getElement().getStyle().setMarginTop(9, Unit.PX); 270 } else { 271 fieldSet.getElement().getStyle().setMarginTop(15, Unit.PX); 272 } 273 tab.add(fieldSet); 274 } 275 CmsDomUtil.resizeAncestor(tab.getParent()); 276 } 277 278 /** 279 * @see org.opencms.gwt.client.ui.input.form.A_CmsFormFieldPanel#renderFields(java.util.Collection) 280 */ 281 @Override 282 public void renderFields(Collection<I_CmsFormField> fields) { 283 284 m_fieldsByGroup = getFieldsByGroup(fields); 285 Collection<I_CmsFormField> simpleTabFields = m_fieldsByGroup.get(TAB_SIMPLE); 286 Collection<I_CmsFormField> individualTabFields = m_fieldsByGroup.get(TAB_INDIVIDUAL); 287 Collection<I_CmsFormField> sharedTabfields = m_fieldsByGroup.get(TAB_SHARED); 288 289 // process simple tab 290 renderSimpleTab(simpleTabFields); 291 292 // process individual tab 293 m_individualDisplay = preprocessFields(individualTabFields); 294 // process shared tab 295 if (m_showShared) { 296 m_sharedDisplay = preprocessFields(sharedTabfields); 297 } 298 } 299 300 /** 301 * @see org.opencms.gwt.client.ui.input.form.A_CmsFormFieldPanel#rerenderFields(java.lang.String, java.util.Collection) 302 */ 303 @Override 304 public void rerenderFields(String tab, Collection<I_CmsFormField> fields) { 305 306 m_fieldsByGroup.removeAll(tab); 307 m_fieldsByGroup.putAll(tab, fields); 308 309 m_individualDisplay = preprocessFields(m_fieldsByGroup.get(TAB_INDIVIDUAL)); 310 m_sharedDisplay = preprocessFields(m_fieldsByGroup.get(TAB_SHARED)); 311 312 if (tab.equals(TAB_SIMPLE)) { 313 m_simpleTab.clear(); 314 renderSimpleTab(fields); 315 } else { 316 if (tab.equals(TAB_INDIVIDUAL)) { 317 renderExtendedTab(fields, m_individualTab); 318 } else if (tab.equals(TAB_SHARED)) { 319 renderExtendedTab(fields, m_sharedTab); 320 } 321 } 322 } 323 324 /** 325 * Tries to restore the active field data.<p> 326 * 327 * @param fieldDataToBeRestored the field data which should be restored (may be null, in which case this method does nothing) 328 */ 329 public void tryToRestoreFieldData(CmsActiveFieldData fieldDataToBeRestored) { 330 331 if (fieldDataToBeRestored == null) { 332 return; 333 } 334 m_markedProperty = null; 335 final String propName = fieldDataToBeRestored.getProperty(); 336 final String tabName = fieldDataToBeRestored.getTab(); 337 m_markedProperty = propName; 338 final int tabIndex; 339 if (TAB_INDIVIDUAL.equals(tabName)) { 340 tabIndex = 1; 341 } else if (TAB_SHARED.equals(tabName)) { 342 tabIndex = 2; 343 } else { 344 tabIndex = 0; 345 } 346 347 Timer timer = new Timer() { 348 349 @Override 350 public void run() { 351 352 if ((tabIndex >= 0) && (tabIndex < m_tabPanel.getTabCount())) { 353 I_CmsFormField markedField = null; 354 m_tabPanel.selectTab(tabIndex); 355 @SuppressWarnings("synthetic-access") 356 Collection<I_CmsFormField> fieldsForTab = m_fieldsByGroup.get(tabName); 357 if ((fieldsForTab != null) && !fieldsForTab.isEmpty()) { 358 for (I_CmsFormField currentField : fieldsForTab) { 359 if (currentField.getLayoutData().get(LD_PROPERTY).equals(propName)) { 360 markedField = currentField; 361 break; 362 } 363 } 364 } 365 if (markedField != null) { 366 final I_CmsFormField constMarkedField = markedField; 367 Timer timer2 = new Timer() { 368 369 @Override 370 @SuppressWarnings("synthetic-access") 371 public void run() { 372 373 focusField(constMarkedField); 374 } 375 376 }; 377 timer2.schedule(1); 378 } 379 } 380 381 } 382 383 private void focusField(final I_CmsFormField constMarkedField) { 384 385 I_CmsFormWidget widget = constMarkedField.getWidget(); 386 if (widget instanceof CmsTextBox) { 387 CmsTextBox box = (CmsTextBox)widget; 388 box.selectAll(); 389 box.setFocus(true); 390 } else if (widget instanceof CmsTextArea) { 391 CmsTextArea textarea = ((CmsTextArea)widget); 392 textarea.selectAll(); 393 textarea.setFocus(true); 394 } 395 } 396 }; 397 timer.schedule(1); 398 399 } 400 401 /** 402 * Creates a list item widget from a list info bean.<p> 403 * 404 * @param info the list info bean 405 * 406 * @return the list item widget 407 */ 408 protected CmsListItemWidget createListItemWidget(CmsListInfoBean info) { 409 410 CmsListItemWidget result = new CmsListItemWidget(info); 411 412 result.truncate(CmsInfoBoxFormFieldPanel.TM_INFOBOX, CmsFormDialog.STANDARD_DIALOG_WIDTH - 50); 413 return result; 414 } 415 416 /** 417 * Returns the tabbed panel.<p> 418 * 419 * @return the tabbed panel 420 */ 421 protected CmsTabbedPanel<CmsScrollPanel> getTabPanel() { 422 423 return m_tabPanel; 424 } 425 426 /** 427 * Partitions a collection of fields by group.<p> 428 * 429 * @param fields the collection of fields 430 * 431 * @return a multimap from groups to form fields 432 */ 433 private Multimap<String, I_CmsFormField> getFieldsByGroup(Collection<I_CmsFormField> fields) { 434 435 Multimap<String, I_CmsFormField> result = ArrayListMultimap.create(); 436 for (I_CmsFormField field : fields) { 437 String group = field.getLayoutData().get(LD_GROUP); 438 result.put(group, field); 439 } 440 return result; 441 } 442 443 /** 444 * Returns true if the field should be displayed at the top of both the "individual" and "shared" tabs.<p> 445 * 446 * @param field the field to test 447 * 448 * @return true if the field should be displayed at the top 449 */ 450 private boolean isTop(I_CmsFormField field) { 451 452 String propName = field.getLayoutData().get(LD_PROPERTY); 453 return m_individualDisplay.contains(propName) || m_sharedDisplay.contains(propName); 454 } 455 456 /** 457 * Preprocesses the fields to find out which fields need to displayed at the top/bottom later.<p> 458 * 459 * @param fields the fields 460 * 461 * @return the set of property names of the preprocessed fields 462 */ 463 private Set<String> preprocessFields(Collection<I_CmsFormField> fields) { 464 465 Set<String> displaySet = Sets.newHashSet(); 466 for (I_CmsFormField field : fields) { 467 boolean hasValue = !CmsStringUtil.isEmpty(field.getWidget().getApparentValue()); 468 if (hasValue || Boolean.TRUE.toString().equals(field.getLayoutData().get(LD_DISPLAY_VALUE))) { 469 String propName = field.getLayoutData().get(LD_PROPERTY); 470 displaySet.add(propName); 471 } 472 } 473 return displaySet; 474 } 475 476 /** 477 * Renders the simple tab.<p> 478 * 479 * @param fields the fields to render 480 */ 481 private void renderSimpleTab(Collection<I_CmsFormField> fields) { 482 483 for (I_CmsFormField field : fields) { 484 m_simpleTab.add(createRow(field)); 485 } 486 CmsDomUtil.resizeAncestor(m_simpleTab.getParent()); 487 } 488}