1 /******************************************************************************* 2 * Copyright (c) 2002, 2020 IBM Corporation and others. 3 * 4 * This program and the accompanying materials 5 * are made available under the terms of the Eclipse Public License 2.0 6 * which accompanies this distribution, and is available at 7 * https://www.eclipse.org/legal/epl-2.0/ 8 * 9 * SPDX-License-Identifier: EPL-2.0 10 * 11 * Contributors: 12 * IBM Corporation - initial API and implementation 13 * Asma Smaoui - CEA LIST - https://bugs.eclipse.org/bugs/show_bug.cgi?id=517379 14 * George Suaridze <suag@1c.ru> (1C-Soft LLC) - Bug 559885 15 *******************************************************************************/ 16 package org.eclipse.ui.internal.cheatsheets.views; 17 18 import java.util.ArrayList; 19 import java.util.Iterator; 20 21 import org.eclipse.help.HelpSystem; 22 import org.eclipse.help.IContext; 23 import org.eclipse.jface.action.Action; 24 import org.eclipse.swt.SWT; 25 import org.eclipse.swt.events.FocusEvent; 26 import org.eclipse.swt.events.FocusListener; 27 import org.eclipse.swt.events.SelectionAdapter; 28 import org.eclipse.swt.events.SelectionEvent; 29 import org.eclipse.swt.graphics.Color; 30 import org.eclipse.swt.graphics.Font; 31 import org.eclipse.swt.graphics.FontData; 32 import org.eclipse.swt.graphics.Image; 33 import org.eclipse.swt.graphics.Point; 34 import org.eclipse.swt.layout.GridData; 35 import org.eclipse.swt.layout.GridLayout; 36 import org.eclipse.swt.widgets.Composite; 37 import org.eclipse.swt.widgets.Control; 38 import org.eclipse.swt.widgets.Label; 39 import org.eclipse.swt.widgets.Widget; 40 import org.eclipse.ui.PlatformUI; 41 import org.eclipse.ui.cheatsheets.AbstractItemExtensionElement; 42 import org.eclipse.ui.forms.events.ExpansionAdapter; 43 import org.eclipse.ui.forms.events.ExpansionEvent; 44 import org.eclipse.ui.forms.events.HyperlinkAdapter; 45 import org.eclipse.ui.forms.events.HyperlinkEvent; 46 import org.eclipse.ui.forms.widgets.ExpandableComposite; 47 import org.eclipse.ui.forms.widgets.FormText; 48 import org.eclipse.ui.forms.widgets.FormToolkit; 49 import org.eclipse.ui.forms.widgets.ImageHyperlink; 50 import org.eclipse.ui.forms.widgets.TableWrapData; 51 import org.eclipse.ui.forms.widgets.TableWrapLayout; 52 import org.eclipse.ui.internal.cheatsheets.CheatSheetPlugin; 53 import org.eclipse.ui.internal.cheatsheets.CheatSheetStopWatch; 54 import org.eclipse.ui.internal.cheatsheets.ICheatSheetResource; 55 import org.eclipse.ui.internal.cheatsheets.Messages; 56 import org.eclipse.ui.internal.cheatsheets.data.IParserTags; 57 import org.eclipse.ui.internal.cheatsheets.data.Item; 58 import org.eclipse.ui.internal.cheatsheets.views.CheatSheetHyperlinkActionFactory.CheatSheetHyperlinkAction; 59 60 public abstract class ViewItem { 61 62 public final static byte VIEWITEM_ADVANCE = 0; 63 public final static byte VIEWITEM_DONOT_ADVANCE = 1; 64 private Composite bodyComp; 65 66 protected FormText bodyText; 67 protected FormText completionText; 68 // protected Label bodyText; 69 protected Composite bodyWrapperComposite; 70 protected Composite buttonComposite; 71 protected Composite completionComposite; 72 73 private boolean buttonExpanded = true; 74 private boolean completionMessageExpanded = false; 75 private Label checkDoneLabel; 76 private boolean completed = false; 77 78 protected Item item; 79 80 // Colors 81 protected Color itemColor; 82 83 private boolean isSkipped = false; 84 private ExpandableComposite mainItemComposite; 85 86 private Composite parent; 87 protected CheatSheetViewer viewer; 88 protected CheatSheetPage page; 89 private Composite titleComposite; 90 private boolean bold = true; 91 private Font boldFont; 92 private Font regularFont; 93 private boolean initialized = false; 94 protected ArrayList<SubItemCompositeHolder> listOfSubItemCompositeHolders; 95 96 /** 97 * Constructor for ViewItem. 98 */ ViewItem(CheatSheetPage page, Item item, Color itemColor, CheatSheetViewer viewer)99 public ViewItem(CheatSheetPage page, Item item, Color itemColor, CheatSheetViewer viewer) { 100 super(); 101 this.page = page; 102 this.parent = page.getForm().getBody(); 103 this.item = item; 104 this.itemColor = itemColor; 105 this.viewer = viewer; 106 addItem(); 107 } 108 109 //Adds the item to the main composite. addItem()110 private void addItem() { 111 CheatSheetStopWatch.startStopWatch("ViewItem.addItem()"); //$NON-NLS-1$ 112 CheatSheetStopWatch.printLapTime("ViewItem.addItem()", "Time in addItem() after getBannerBackground: "); //$NON-NLS-1$ //$NON-NLS-2$ 113 114 // Set up the main composite for the item.****************************************** 115 checkDoneLabel = page.getToolkit().createLabel(parent, " "); //$NON-NLS-1$ 116 CheatSheetStopWatch.printLapTime("ViewItem.addItem()", "Time in addItem() after create checkDoneLabel: "); //$NON-NLS-1$ //$NON-NLS-2$ 117 118 mainItemComposite = page.getToolkit().createSection(parent, ExpandableComposite.TWISTIE|ExpandableComposite.COMPACT); 119 mainItemComposite.setBackground(itemColor); 120 mainItemComposite.setLayoutData(new TableWrapData(TableWrapData.FILL_GRAB)); 121 String title = item.getTitle(); 122 if (title != null) { 123 mainItemComposite.setText(ViewUtilities.escapeForLabel(title)); 124 } 125 CheatSheetStopWatch.printLapTime("ViewItem.addItem()", "Time in addItem() after create mainItemComposite: "); //$NON-NLS-1$ //$NON-NLS-2$ 126 127 128 mainItemComposite.addExpansionListener(new ExpansionAdapter() { 129 @Override 130 public void expansionStateChanged(ExpansionEvent e) { 131 page.getForm().reflow(true); 132 } 133 }); 134 CheatSheetStopWatch.printLapTime("ViewItem.addItem()", "Time in addItem() after addExpansionListener: "); //$NON-NLS-1$ //$NON-NLS-2$ 135 136 // handle item extensions here 137 // check number of extensions for this item and adjust layout accordingly 138 int number = 0; 139 ArrayList<AbstractItemExtensionElement[]> itemExts = item.getItemExtensions(); 140 141 if((itemExts != null && itemExts.size() > 0) || item.getContextId() != null || item.getHref() != null) { 142 // Set up the title composite for the item. 143 titleComposite = page.getToolkit().createComposite(mainItemComposite); 144 titleComposite.setBackground(itemColor); 145 } 146 147 if(itemExts != null) { 148 for (int g = 0; g < itemExts.size(); g++) { 149 AbstractItemExtensionElement[] eea = itemExts.get(g); 150 number += eea.length; 151 for (int x = 0; x < eea.length; x++) { 152 eea[x].createControl(titleComposite); 153 } 154 } 155 } 156 CheatSheetStopWatch.printLapTime("ViewItem.addItem()", "Time in addItem() after create item extensions: "); //$NON-NLS-1$ //$NON-NLS-2$ 157 158 // don't add the help icon unless there is a context id or help link 159 if(item.getContextId() != null || item.getHref() != null) { 160 // adjust the layout count 161 number++; 162 ImageHyperlink helpButton = createButton(titleComposite, CheatSheetPlugin.getPlugin().getImage(ICheatSheetResource.CHEATSHEET_ITEM_HELP), this, itemColor, Messages.HELP_BUTTON_TOOLTIP); 163 helpButton.addHyperlinkListener(new HyperlinkAdapter() { 164 @Override 165 public void linkActivated(HyperlinkEvent e) { 166 // If we have a context id, handle this first and ignore an hrefs 167 if(item.getContextId() != null) { 168 openInfopop(e.widget); 169 } else { 170 // We only have an href, so let's open it in the help system 171 openHelpTopic(); 172 } 173 } 174 }); 175 } 176 CheatSheetStopWatch.printLapTime("ViewItem.addItem()", "Time in addItem() after create help button: "); //$NON-NLS-1$ //$NON-NLS-2$ 177 178 if(number > 0) { 179 mainItemComposite.setTextClient(titleComposite); 180 GridLayout layout = new GridLayout(number, false); 181 GridData data = new GridData(GridData.FILL_BOTH); 182 183 titleComposite.setLayout(layout); 184 titleComposite.setLayoutData(data); 185 layout.marginWidth = 0; 186 layout.marginHeight = 0; 187 layout.verticalSpacing = 0; 188 } 189 CheatSheetStopWatch.printLapTime("ViewItem.addItem()", "Time in addItem() after setTextClient: "); //$NON-NLS-1$ //$NON-NLS-2$ 190 191 //Body wrapper here. this composite will be hidden and shown as appropriate. 192 bodyWrapperComposite = page.getToolkit().createComposite(mainItemComposite); 193 mainItemComposite.setClient(bodyWrapperComposite); 194 TableWrapLayout wrapperLayout = new TableWrapLayout(); 195 bodyWrapperComposite.setLayout(wrapperLayout); 196 bodyWrapperComposite.setBackground(itemColor); 197 CheatSheetStopWatch.printLapTime("ViewItem.addItem()", "Time in addItem() after create bodyWrapperComposite: "); //$NON-NLS-1$ //$NON-NLS-2$ 198 199 bodyText = page.getToolkit().createFormText(bodyWrapperComposite, false); 200 bodyText.addSelectionListener(new SelectionAdapter() { 201 @Override 202 public void widgetSelected(SelectionEvent e) { 203 Action copyAction = viewer.getCopyAction(); 204 if (copyAction != null) { 205 copyAction.setEnabled(bodyText.canCopy()); 206 } 207 } 208 }); 209 bodyText.addFocusListener(new FocusListener() { 210 @Override 211 public void focusGained(FocusEvent e) { 212 Action copyAction = viewer.getCopyAction(); 213 if (copyAction != null) { 214 copyAction.setEnabled(bodyText.canCopy()); 215 } 216 } 217 @Override 218 public void focusLost(FocusEvent e) { 219 Action copyAction = viewer.getCopyAction(); 220 if (copyAction != null) { 221 copyAction.setEnabled(false); 222 } 223 } 224 }); 225 226 bodyText.addHyperlinkListener(new HyperlinkAdapter() { 227 @Override 228 public void linkActivated(HyperlinkEvent event) { 229 String url = (String) event.getHref(); 230 CheatSheetHyperlinkActionFactory actionFactory = new CheatSheetHyperlinkActionFactory(); 231 CheatSheetHyperlinkAction cheatSheetHyperlink = actionFactory.create(url); 232 cheatSheetHyperlink.execute(); 233 } 234 }); 235 236 // bodyText = toolkit.createLabel(bodyWrapperComposite, item.getDescription(), SWT.WRAP); 237 bodyText.setText(item.getDescription(), item.getDescription().startsWith(IParserTags.FORM_START_TAG), false); 238 239 //Set up the body text portion here. 240 bodyText.setBackground(itemColor); 241 bodyText.setLayoutData(new TableWrapData(TableWrapData.FILL_GRAB)); 242 CheatSheetStopWatch.printLapTime("ViewItem.addItem()", "Time in addItem() after create FormText: "); //$NON-NLS-1$ //$NON-NLS-2$ 243 244 //Handle the sub-steps and regular buttons here. 245 //First Check to see if there is sub steps. If there is, don't create the button comp, 246 //As it will be handled by the CoreItemWithSubs. 247 //If there is no sub steps, create a button composite and Pass it to CoreItem using the handleButtons. 248 249 if(!item.isDynamic()) { 250 handleButtons(); 251 } 252 CheatSheetStopWatch.printLapTime("ViewItem.addItem()", "Time in addItem() after handleButtons(): "); //$NON-NLS-1$ //$NON-NLS-2$ 253 254 setButtonsVisible(false); 255 setCollapsed(); 256 CheatSheetStopWatch.printLapTime("ViewItem.addItem()", "Time in addItem() after setting buttons and item collapsed: "); //$NON-NLS-1$ //$NON-NLS-2$ 257 258 boldFont = mainItemComposite.getFont(); 259 FontData[] fontDatas = boldFont.getFontData(); 260 for (int i = 0; i < fontDatas.length; i++) { 261 fontDatas[i].setStyle(fontDatas[i].getStyle() ^ SWT.BOLD); 262 } 263 regularFont = new Font(mainItemComposite.getDisplay(), fontDatas); 264 CheatSheetStopWatch.printLapTime("ViewItem.addItem()", "Time in addItem() after font initlization: "); //$NON-NLS-1$ //$NON-NLS-2$ 265 266 setBold(false); 267 CheatSheetStopWatch.printLapTime("ViewItem.addItem()", "Time in addItem() after setBold: "); //$NON-NLS-1$ //$NON-NLS-2$ 268 } 269 createButtonWithText(Composite parent, Image image, ViewItem item, Color color, String linkText)270 protected ImageHyperlink createButtonWithText(Composite parent, Image image, ViewItem item, Color color, String linkText) { 271 ImageHyperlink button = page.getToolkit().createImageHyperlink(parent, SWT.NULL); 272 button.setImage(image); 273 button.setData(item); 274 button.setBackground(color); 275 button.setText(linkText); 276 button.setToolTipText(linkText); 277 return button; 278 } 279 createButton(Composite parent, Image image, ViewItem item, Color color, String toolTipText)280 protected ImageHyperlink createButton(Composite parent, Image image, ViewItem item, Color color, String toolTipText) { 281 ImageHyperlink button = new ImageHyperlink(parent, SWT.NULL); 282 page.getToolkit().adapt(button, true, true); 283 button.setImage(image); 284 button.setData(item); 285 button.setBackground(color); 286 button.setToolTipText(toolTipText); 287 // button.setFAccessibleDescription(bodyText.getText()); 288 // button.setFAccessibleName(button.getToolTipText()); 289 290 return button; 291 } 292 dispose()293 public void dispose() { 294 if (checkDoneLabel != null) 295 checkDoneLabel.dispose(); 296 if (bodyText != null) 297 bodyText.dispose(); 298 if (buttonComposite != null) 299 buttonComposite.dispose(); 300 if (completionComposite != null) 301 completionComposite.dispose(); 302 if (bodyComp != null) 303 bodyComp.dispose(); 304 if (bodyWrapperComposite != null) 305 bodyWrapperComposite.dispose(); 306 if (mainItemComposite != null) 307 mainItemComposite.dispose(); 308 if (titleComposite != null) 309 titleComposite.dispose(); 310 if (regularFont != null) 311 regularFont.dispose(); 312 313 ArrayList<AbstractItemExtensionElement[]> itemExts = item.getItemExtensions(); 314 if (itemExts != null) { 315 for (int g = 0; g < itemExts.size(); g++) { 316 AbstractItemExtensionElement[] eea = itemExts.get(g); 317 for (int x = 0; x < eea.length; x++) { 318 eea[x].dispose(); 319 } 320 } 321 } 322 } 323 324 /** 325 * @return 326 */ 327 /*package*/ getCompleteImage()328 Image getCompleteImage() { 329 return CheatSheetPlugin.getPlugin().getImage(ICheatSheetResource.CHEATSHEET_ITEM_COMPLETE); 330 } 331 332 /** 333 * @return 334 */ getItem()335 public Item getItem() { 336 return item; 337 } 338 339 /** 340 * Returns the mainItemComposite. 341 * @return Composite 342 */ 343 /*package*/ getMainItemComposite()344 Composite getMainItemComposite() { 345 return mainItemComposite; 346 } 347 348 /** 349 * @return 350 */ 351 /*package*/ getSkipImage()352 Image getSkipImage() { 353 return CheatSheetPlugin.getPlugin().getImage(ICheatSheetResource.CHEATSHEET_ITEM_SKIP); 354 } 355 356 //Adds the buttons to the buttonComposite. 357 /*package*/ handleButtons()358 abstract void handleButtons(); 359 360 /*package*/ isBold()361 boolean isBold() { 362 return bold; 363 } 364 365 /** 366 * Returns the completed. 367 * @return boolean 368 */ isCompleted()369 public boolean isCompleted() { 370 return completed; 371 } 372 isExpanded()373 public boolean isExpanded() { 374 return mainItemComposite.isExpanded(); 375 } 376 isCompletionMessageExpanded()377 public boolean isCompletionMessageExpanded() { 378 return completionMessageExpanded; 379 } 380 381 /** 382 * Returns whether or not cheat sheet viewer containing this item is in 383 * a modal dialog. 384 * 385 * @return whether the cheat sheet viewer is in a modal dialog 386 */ isInDialogMode()387 public boolean isInDialogMode() { 388 return viewer.isInDialogMode(); 389 } 390 391 /*package*/ isSkipped()392 boolean isSkipped() { 393 return isSkipped; 394 } 395 396 /** 397 * Open a help topic 398 */ openHelpTopic()399 private void openHelpTopic() { 400 if (item == null || item.getHref() == null) { 401 return; 402 } 403 404 PlatformUI.getWorkbench().getHelpSystem().displayHelpResource(item.getHref()); 405 } 406 407 /** 408 * Open an infopop 409 */ openInfopop(Widget widget)410 private void openInfopop(Widget widget) { 411 if (item == null || item.getContextId() == null) { 412 return; 413 } 414 415 IContext context = HelpSystem.getContext(item.getContextId()); 416 417 if (context != null) { 418 // determine a location in the upper right corner of the widget 419 Point point = widget.getDisplay().getCursorLocation(); 420 point = new Point(point.x + 15, point.y); 421 // display the help 422 PlatformUI.getWorkbench().getHelpSystem().displayContext(context, point.x, point.y); 423 } 424 } 425 setAsCurrentActiveItem()426 public void setAsCurrentActiveItem() { 427 setColorAsCurrent(true); 428 setButtonsVisible(true); 429 setBold(true); 430 setExpanded(); 431 setFocus(); 432 } 433 setFocus()434 protected void setFocus() { 435 mainItemComposite.setFocus(); 436 FormToolkit.ensureVisible(getMainItemComposite()); 437 } 438 439 /*package*/ setAsNormalCollapsed()440 void setAsNormalCollapsed() { 441 setBold(false); 442 setColorAsCurrent(false); 443 if (mainItemComposite.isExpanded()) 444 setCollapsed(); 445 } 446 447 /*package*/ setAsNormalNonCollapsed()448 void setAsNormalNonCollapsed() { 449 setColorAsCurrent(false); 450 setBold(false); 451 } 452 setBodyColor(Color color)453 private void setBodyColor(Color color) { 454 mainItemComposite.setBackground(color); 455 setBackgroundColor(bodyWrapperComposite, color); 456 setBackgroundColor(buttonComposite, color); 457 setBackgroundColor(completionComposite, color); 458 } 459 460 /* 461 * Set the background color of this composite and its children. 462 * If the composite is null do nothing. 463 */ setBackgroundColor(Composite composite, Color color)464 protected void setBackgroundColor(Composite composite, Color color) { 465 if (composite != null) { 466 composite.setBackground(color); 467 Control[] children = composite.getChildren(); 468 for (Control element : children) { 469 element.setBackground(color); 470 } 471 } 472 } 473 474 /*package*/ setBold(boolean value)475 void setBold(boolean value) { 476 if(value) { 477 mainItemComposite.setFont(boldFont); 478 if(initialized) 479 mainItemComposite.layout(); 480 } else { 481 mainItemComposite.setFont(regularFont); 482 if(initialized) 483 mainItemComposite.layout(); 484 } 485 bold = value; 486 } 487 488 //collapse or expand the item 489 /*package*/ setButtonsVisible(boolean isVisible)490 void setButtonsVisible(boolean isVisible) { 491 if (buttonExpanded != isVisible) { 492 if (listOfSubItemCompositeHolders != null) { 493 for (Iterator<SubItemCompositeHolder> iter = listOfSubItemCompositeHolders.iterator(); iter 494 .hasNext();) { 495 iter.next().setButtonsVisible(isVisible); 496 } 497 } else if (buttonComposite != null) { 498 buttonComposite.setVisible(isVisible); 499 } 500 } 501 502 if(isVisible && initialized) { 503 FormToolkit.ensureVisible(getMainItemComposite()); 504 } 505 buttonExpanded = isVisible; 506 } 507 setCompletionMessageExpanded(boolean isFinalItem)508 protected void setCompletionMessageExpanded(boolean isFinalItem) { 509 if (hasCompletionMessage()) { 510 if (completionComposite == null) { 511 createCompletionComposite(isFinalItem); 512 } 513 if (!completionMessageExpanded) { 514 completionComposite.setVisible(true); 515 completionMessageExpanded = true; 516 } 517 } 518 } 519 createCompletionComposite(boolean isFinalItem)520 abstract void createCompletionComposite(boolean isFinalItem); 521 setCompletionMessageCollapsed()522 protected void setCompletionMessageCollapsed() { 523 if (completionComposite != null) { 524 if (completionMessageExpanded) { 525 completionComposite.dispose(); 526 completionComposite = null; 527 page.getForm().reflow(true); 528 } 529 } 530 completionMessageExpanded = false; 531 } 532 533 //collapses the item 534 /*package*/ setCollapsed()535 void setCollapsed() { 536 if (mainItemComposite.isExpanded()) { 537 mainItemComposite.setExpanded(false); 538 if(initialized) { 539 page.getForm().reflow(true); 540 FormToolkit.ensureVisible(getMainItemComposite()); 541 } 542 } 543 } 544 setColorAsCurrent(boolean active)545 private void setColorAsCurrent(boolean active) { 546 if (active) { 547 setTitleColor(page.getActiveColor()); 548 setBodyColor(page.getActiveColor()); 549 } else { 550 setTitleColor(itemColor); 551 setBodyColor(itemColor); 552 } 553 } 554 555 //marks the item as complete. setComplete()556 public void setComplete() { 557 completed = true; 558 checkDoneLabel.setImage(getCompleteImage()); 559 560 if(initialized) { 561 checkDoneLabel.getParent().layout(); 562 } 563 } 564 565 //expands the item setExpanded()566 public void setExpanded() { 567 if (!mainItemComposite.isExpanded()) { 568 mainItemComposite.setExpanded(true); 569 if(initialized) { 570 page.getForm().reflow(true); 571 FormToolkit.ensureVisible(getMainItemComposite()); 572 } 573 } 574 } 575 576 /*package*/ setIncomplete()577 void setIncomplete() { 578 checkDoneLabel.setImage(null); 579 completed = false; 580 setStartImage(); 581 } 582 583 /** 584 * Sets the itemColor. 585 * @param itemColor The itemColor to set 586 */ 587 /*package*/ setItemColor(Color itemColor)588 void setItemColor(Color itemColor) { 589 this.itemColor = itemColor; 590 } 591 592 /*package*/ setOriginalColor()593 void setOriginalColor() { 594 setTitleColor(itemColor); 595 setBodyColor(itemColor); 596 setBold(false); 597 } 598 599 /*package*/ setRestartImage()600 abstract void setRestartImage(); 601 602 /*package*/ setSkipped()603 void setSkipped() { 604 isSkipped = true; 605 checkDoneLabel.setImage(getSkipImage()); 606 607 if(initialized) { 608 checkDoneLabel.getParent().layout(); 609 } 610 } 611 /*package*/ setStartImage()612 abstract void setStartImage(); 613 setTitleColor(Color bg)614 private void setTitleColor(Color bg) { 615 if(titleComposite != null) { 616 titleComposite.setBackground(bg); 617 618 Control[] titlechildren = titleComposite.getChildren(); 619 for (Control element : titlechildren) { 620 element.setBackground(bg); 621 } 622 } 623 } 624 initialized()625 public void initialized() { 626 initialized = true; 627 } 628 canCopy()629 public boolean canCopy() { 630 return (bodyText!=null && !bodyText.isDisposed())?bodyText.canCopy():false; 631 } copy()632 public void copy() { 633 if (bodyText!=null && !bodyText.isDisposed()) 634 bodyText.copy(); 635 } 636 hasCompletionMessage()637 abstract boolean hasCompletionMessage(); 638 639 } 640