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.acacia.client.widgets.code; 029 030import org.opencms.acacia.client.css.I_CmsLayoutBundle; 031import org.opencms.acacia.client.widgets.I_CmsEditWidget; 032import org.opencms.gwt.client.CmsCoreProvider; 033import org.opencms.gwt.client.Messages; 034import org.opencms.gwt.client.ui.FontOpenCms; 035import org.opencms.gwt.client.ui.input.CmsSelectBox; 036import org.opencms.gwt.client.util.CmsDomUtil; 037import org.opencms.gwt.client.util.CmsJsUtil; 038import org.opencms.gwt.shared.I_CmsCodeMirrorClientConfiguration; 039 040import java.util.Arrays; 041import java.util.HashMap; 042import java.util.LinkedHashMap; 043import java.util.Map; 044 045import com.google.gwt.core.client.JavaScriptObject; 046import com.google.gwt.dom.client.Document; 047import com.google.gwt.dom.client.Element; 048import com.google.gwt.event.dom.client.FocusEvent; 049import com.google.gwt.event.dom.client.FocusHandler; 050import com.google.gwt.event.logical.shared.HasResizeHandlers; 051import com.google.gwt.event.logical.shared.ResizeEvent; 052import com.google.gwt.event.logical.shared.ResizeHandler; 053import com.google.gwt.event.logical.shared.ValueChangeEvent; 054import com.google.gwt.event.logical.shared.ValueChangeHandler; 055import com.google.gwt.event.shared.HandlerRegistration; 056import com.google.gwt.json.client.JSONObject; 057import com.google.gwt.user.client.ui.ComplexPanel; 058import com.google.gwt.user.client.ui.FlowPanel; 059import com.google.web.bindery.autobean.shared.AutoBeanCodex; 060 061/** 062 * Client-side content editor widget for editing source code using the CodeMirror library. 063 * */ 064public class CmsCodeMirrorWidget extends ComplexPanel implements I_CmsEditWidget, HasResizeHandlers { 065 066 /** Map from modes to labels. */ 067 private static LinkedHashMap<String, String> m_modes; 068 069 /** Map from simplified mode names to actual mode names (e.g. from 'javascript' to 'text/javascript'). */ 070 private static Map<String, String> m_simpleModeTranslations; 071 072 /** Helper for loading the necessary scripts. */ 073 private static final CmsCodeMirrorScriptLoader scriptLoader = new CmsCodeMirrorScriptLoader(); 074 075 static { 076 m_modes = new LinkedHashMap<>(); 077 m_simpleModeTranslations = new HashMap<>(); 078 addMode("text/css", "css", "CSS"); 079 addMode("text/html", "html", "HTML"); 080 addMode("text/plain", "text", "Text"); 081 addMode("text/x-java", "java", "Java"); 082 addMode("text/javascript", "javascript", "Javascript"); 083 addMode("application/x-jsp", "jsp", "JSP"); 084 addMode("application/xml", "xml", "XML"); 085 } 086 087 /** The token to control activation. */ 088 private boolean m_active = true; 089 090 /** The configuration from the server. */ 091 private I_CmsCodeMirrorClientConfiguration m_config; 092 093 /** The native CodeMirror instance. */ 094 private JavaScriptObject m_editor; 095 096 /** Boolean flag indicating whether syntax highlighting is currently turned on.*/ 097 private boolean m_highlighting = true; 098 099 /** The currently selected mode (may be different from the actual mode in the CodeMirror instance). */ 100 private String m_mode = "text/plain"; 101 102 /** The mode selector. */ 103 private CmsSelectBox m_modeSelect; 104 105 /** The initial content (we need to save this because CodeMirror is loaded asynchronously). */ 106 private String m_originalContent; 107 108 /** The previous value. */ 109 private String m_previousValue; 110 111 /** The 'redo' button. */ 112 private CmsCodeMirrorToolbarButton m_redo; 113 114 /** Toggle button for automatically closing brackets / tags. */ 115 private CmsCodeMirrorToggleButton m_toggleAutoClose; 116 117 /** The 'show tabs' toggle button. */ 118 private CmsCodeMirrorToggleButton m_toggleShowTabs; 119 120 /** The 'undo' button. */ 121 private CmsCodeMirrorToolbarButton m_undo; 122 123 private String m_id; 124 125 /** 126 * Creates a new display widget.<p> 127 * 128 * @param config the widget configuration string 129 */ 130 public CmsCodeMirrorWidget(String config) { 131 132 super(); 133 m_id = "" + Math.random(); 134 setElement(Document.get().createDivElement()); 135 m_config = AutoBeanCodex.decode( 136 CmsCoreProvider.AUTO_BEAN_FACTORY, 137 I_CmsCodeMirrorClientConfiguration.class, 138 config).as(); 139 addStyleName("oc-codemirror-editorwidget"); 140 Integer height = m_config.getHeight(); 141 if (height != null) { 142 nativeSetProperty(getElement(), "--codemirror-height", height + "px"); 143 } 144 add(createToolbar(), getElement().<Element> cast()); 145 } 146 147 /** 148 * Helper method used to make a mode available for selection. 149 * 150 * @param name the normal MIME type name of the mode 151 * @param simpleName simple mode name for use in the server-side configuration, can be null 152 * @param title the title to be used in the mode selector 153 */ 154 private static void addMode(String name, String simpleName, String title) { 155 156 m_modes.put(name, title); 157 if (simpleName != null) { 158 m_simpleModeTranslations.put(simpleName, name); 159 } 160 } 161 162 /** 163 * @see com.google.gwt.event.dom.client.HasFocusHandlers#addFocusHandler(com.google.gwt.event.dom.client.FocusHandler) 164 */ 165 public HandlerRegistration addFocusHandler(FocusHandler handler) { 166 167 return addDomHandler(handler, FocusEvent.getType()); 168 } 169 170 /** 171 * @see com.google.gwt.event.logical.shared.HasResizeHandlers#addResizeHandler(com.google.gwt.event.logical.shared.ResizeHandler) 172 */ 173 174 public HandlerRegistration addResizeHandler(ResizeHandler handler) { 175 176 return addHandler(handler, ResizeEvent.getType()); 177 } 178 179 /** 180 * @see com.google.gwt.event.logical.shared.HasValueChangeHandlers#addValueChangeHandler(com.google.gwt.event.logical.shared.ValueChangeHandler) 181 */ 182 public HandlerRegistration addValueChangeHandler(ValueChangeHandler<String> handler) { 183 184 return addHandler(handler, ValueChangeEvent.getType()); 185 } 186 187 /** 188 * Represents a value change event.<p> 189 * 190 */ 191 public void fireChangeEvent() { 192 193 updateUndoRedo(); 194 fireValueChange(false); 195 } 196 197 /** 198 * Represents a resize event.<p> 199 * @param event from text area panel 200 */ 201 public void fireResizeeEvent(ResizeEvent event) { 202 203 ResizeEvent.fire(this, event.getWidth(), event.getHeight()); 204 } 205 206 /** 207 * Gets the effective mode to use, which depends both on the selected mode and on whether the user has enabled syntax highlighting. 208 * 209 * @return the effective mode 210 */ 211 public String getEffectiveMode() { 212 213 return m_highlighting ? m_mode : "text/plain"; 214 } 215 216 /** 217 * @see com.google.gwt.user.client.ui.HasValue#getValue() 218 */ 219 public String getValue() { 220 221 return getContent(); 222 } 223 224 /** 225 * @see org.opencms.acacia.client.widgets.I_CmsEditWidget#isActive() 226 */ 227 public boolean isActive() { 228 229 return m_active; 230 } 231 232 /** 233 * @see org.opencms.acacia.client.widgets.I_CmsEditWidget#onAttachWidget() 234 */ 235 public void onAttachWidget() { 236 237 onAttach(); 238 } 239 240 /** 241 * @see org.opencms.acacia.client.widgets.I_CmsEditWidget#owns(com.google.gwt.dom.client.Element) 242 */ 243 public boolean owns(Element element) { 244 245 return getElement().isOrHasChild(element); 246 247 } 248 249 /** 250 * @see org.opencms.acacia.client.widgets.I_CmsEditWidget#setActive(boolean) 251 */ 252 public void setActive(boolean active) { 253 254 if (m_active == active) { 255 return; 256 } 257 m_active = active; 258 if (m_editor != null) { 259 if (m_active) { 260 getElement().removeClassName(I_CmsLayoutBundle.INSTANCE.form().inActive()); 261 fireValueChange(true); 262 } else { 263 getElement().addClassName(I_CmsLayoutBundle.INSTANCE.form().inActive()); 264 } 265 } 266 } 267 268 /** 269 * @see org.opencms.acacia.client.widgets.I_CmsEditWidget#setName(java.lang.String) 270 */ 271 public void setName(String name) { 272 273 // not used 274 } 275 276 /** 277 * @see com.google.gwt.user.client.ui.HasValue#setValue(java.lang.Object) 278 */ 279 public void setValue(String value) { 280 281 setValue(value, false); 282 } 283 284 /** 285 * @see com.google.gwt.user.client.ui.HasValue#setValue(java.lang.Object, boolean) 286 */ 287 public void setValue(String value, boolean fireEvents) { 288 289 if (value != null) { 290 value = value.trim(); 291 } 292 setPreviousValue(value); 293 if (m_editor == null) { 294 // editor has not been initialized yet 295 m_originalContent = value; 296 } else { 297 nativeSetContent(value); 298 } 299 if (fireEvents) { 300 fireValueChange(true); 301 } 302 303 } 304 305 /** 306 * Fires the value change event, if the value has changed.<p> 307 * 308 * @param force <code>true</code> to force firing the event, not regarding an actually changed value 309 */ 310 protected void fireValueChange(boolean force) { 311 312 String currentValue = getValue(); 313 if (force || !currentValue.equals(m_previousValue)) { 314 m_previousValue = currentValue; 315 ValueChangeEvent.fire(this, currentValue); 316 } 317 } 318 319 /** 320 * Returns the previous value.<p> 321 * 322 * @return the previous value 323 */ 324 protected String getPreviousValue() { 325 326 return m_previousValue; 327 } 328 329 /** 330 * @see com.google.gwt.user.client.ui.FocusWidget#onAttach() 331 */ 332 @Override 333 protected void onAttach() { 334 335 super.onAttach(); 336 scriptLoader.load(() -> { 337 int height = -1; 338 if (m_config.getHeight() != null) { 339 height = m_config.getHeight().intValue(); 340 } 341 initCodeMirror( 342 CmsCodeMirrorWidget.this, 343 getElement(), 344 CmsJsUtil.parseJSON(m_config.getPhrasesJSON()), 345 height); 346 initializeUserControlledSettings(); 347 if (m_originalContent != null) { 348 nativeSetContent(m_originalContent); 349 clearHistory(); 350 updateUndoRedo(); 351 } 352 }); 353 } 354 355 /** 356 * Sets the previous value.<p> 357 * 358 * @param previousValue the previous value to set 359 */ 360 protected void setPreviousValue(String previousValue) { 361 362 m_previousValue = previousValue; 363 } 364 365 /** 366 * Clears the undo / redo history. 367 */ 368 private native void clearHistory() /*-{ 369 var editor = this.@org.opencms.acacia.client.widgets.code.CmsCodeMirrorWidget::m_editor; 370 editor.clearHistory(); 371 }-*/; 372 373 /** 374 * Creates a simple toggle button for enabling / disabling a native CodeMirror option. 375 * 376 * @param icon the icon 377 * @param optionName the option name 378 * @param initialValue the initial vale of the option 379 * 380 * @return the newly created toggle button 381 */ 382 private CmsCodeMirrorToggleButton createOptionButton(FontOpenCms icon, String optionName, boolean initialValue) { 383 384 CmsCodeMirrorToggleButton result = new CmsCodeMirrorToggleButton(icon); 385 result.setValue(initialValue, false); 386 result.addValueChangeHandler(event -> nativeSetOption(optionName, event.getValue().booleanValue())); 387 return result; 388 389 } 390 391 /** 392 * Creates the editor tool bar. 393 * @return the editor tool bar 394 */ 395 private FlowPanel createToolbar() { 396 397 FlowPanel result = new FlowPanel(); 398 result.addStyleName("oc-codewidget-toolbar"); 399 400 { 401 CmsCodeMirrorToolbarButton undo = new CmsCodeMirrorToolbarButton(FontOpenCms.UNDO); 402 undo.setTitle(Messages.get().key(Messages.GUI_CODEMIRROR_BUTTON_UNDO_0)); 403 undo.addClickHandler(event -> executeCommand("undo")); 404 m_undo = undo; 405 result.add(undo); 406 } 407 408 { 409 CmsCodeMirrorToolbarButton redo = new CmsCodeMirrorToolbarButton(FontOpenCms.REDO); 410 redo.setTitle(Messages.get().key(Messages.GUI_CODEMIRROR_BUTTON_REDO_0)); 411 redo.addClickHandler(event -> executeCommand("redo")); 412 m_redo = redo; 413 result.add(redo); 414 } 415 416 { 417 CmsCodeMirrorToolbarButton search = new CmsCodeMirrorToolbarButton(FontOpenCms.SEARCH); 418 search.setTitle(Messages.get().key(Messages.GUI_CODEMIRROR_BUTTON_FIND_0)); 419 search.addClickHandler(event -> { 420 search(); 421 }); 422 result.add(search); 423 } 424 425 { 426 CmsCodeMirrorToolbarButton searchReplace = new CmsCodeMirrorToolbarButton(FontOpenCms.SEARCH_REPLACE); 427 searchReplace.setTitle(Messages.get().key(Messages.GUI_CODEMIRROR_BUTTON_REPLACE_0)); 428 searchReplace.addClickHandler(event -> searchReplace()); 429 result.add(searchReplace); 430 } 431 432 { 433 CmsCodeMirrorToolbarButton lineButton = new CmsCodeMirrorToolbarButton("L"); 434 lineButton.setTitle(Messages.get().key(Messages.GUI_CODEMIRROR_BUTTON_JUMP_TO_LINE_0)); 435 lineButton.addClickHandler(event -> { 436 executeCommand("jumpToLine"); 437 }); 438 result.add(lineButton); 439 } 440 { 441 CmsCodeMirrorToggleButton highlight = new CmsCodeMirrorToggleButton(FontOpenCms.HIGHLIGHT); 442 highlight.setValue(true, false); 443 highlight.setTitle(Messages.get().key(Messages.GUI_CODEMIRROR_TOGGLE_SYNTAX_HIGHLIGHTING_0)); 444 highlight.addValueChangeHandler(event -> setSyntaxHighlightingEnabled(event.getValue().booleanValue())); 445 result.add(highlight); 446 } 447 448 { 449 CmsCodeMirrorToggleButton wrap = createOptionButton(FontOpenCms.WRAP_LINES, "lineWrapping", false); 450 wrap.setTitle(Messages.get().key(Messages.GUI_CODEMIRROR_TOGGLE_LINE_WRAP_0)); 451 result.add(wrap); 452 } 453 454 { 455 CmsCodeMirrorToggleButton showTabs = new CmsCodeMirrorToggleButton(FontOpenCms.INVISIBLE_CHARS); 456 showTabs.setTitle(Messages.get().key(Messages.GUI_CODEMIRROR_TOGGLE_TABS_0)); 457 showTabs.addValueChangeHandler(event -> setShowTabs(event.getValue().booleanValue())); 458 m_toggleShowTabs = showTabs; 459 result.add(showTabs); 460 } 461 462 { 463 CmsCodeMirrorToggleButton brackets = new CmsCodeMirrorToggleButton(FontOpenCms.BRACKETS); 464 brackets.setTitle(Messages.get().key(Messages.GUI_CODEMIRROR_TOGGLE_AUTO_CLOSE_BRACKETS_0)); 465 brackets.addValueChangeHandler(event -> setAutoCloseBrackets(event.getValue().booleanValue())); 466 m_toggleAutoClose = brackets; 467 result.add(brackets); 468 } 469 470 m_modeSelect = new CmsSelectBox(m_modes); 471 m_modeSelect.addStyleName("oc-codewidget-mode-select"); 472 473 Map<String, String> items = new LinkedHashMap<>(); 474 for (String fontSize : Arrays.asList("8px", "10px", "12px", "14px", "16px", "18px", "20px")) { 475 items.put(fontSize, fontSize); 476 } 477 CmsSelectBox select = new CmsSelectBox(items); 478 select.addStyleName("oc-codewidget-fontsize-select"); 479 select.setFormValue("14px"); 480 result.add(select); 481 result.add(m_modeSelect); 482 483 select.addValueChangeHandler(event -> { 484 String fontSize = event.getValue(); 485 setFontSize(fontSize); 486 }); 487 488 m_modeSelect.addValueChangeHandler(event -> { 489 String mode = event.getValue(); 490 setMode(mode); 491 492 }); 493 return result; 494 495 } 496 497 /** 498 * Executes a named CodeMirror command. 499 * 500 * @param command the name of the command to execute 501 */ 502 private native void executeCommand(String command) /*-{ 503 var editor = this.@org.opencms.acacia.client.widgets.code.CmsCodeMirrorWidget::m_editor; 504 $wnd.CodeMirror.commands[command](editor); 505 }-*/; 506 507 /** 508 * Gets the editor content. 509 * 510 * @return the editor content 511 */ 512 private native String getContent() /*-{ 513 var editor = this.@org.opencms.acacia.client.widgets.code.CmsCodeMirrorWidget::m_editor; 514 return editor.getValue(); 515 }-*/; 516 517 /** 518 * Gets the history size. 519 * 520 * @return the history size 521 */ 522 private native JavaScriptObject getHistorySize() /*-{ 523 var editor = this.@org.opencms.acacia.client.widgets.code.CmsCodeMirrorWidget::m_editor; 524 return editor.historySize(); 525 }-*/; 526 527 /** 528 * Checks if a CodeMirror dialog is currently being displayed. 529 * 530 * @return true if a CodeMirror dialog is being displayed 531 */ 532 private boolean hasDialog() { 533 534 return CmsDomUtil.getElementsByClass("CodeMirror-dialog", getElement()).size() > 0; 535 } 536 537 /** 538 * Initializes the CodeMirror instance. 539 * 540 * @param elem the parent element 541 * @param phrases the localization phrases 542 * @param height the initial editor height, or -1 if no height should be set 543 */ 544 private native void initCodeMirror(CmsCodeMirrorWidget instance, Element elem, JavaScriptObject phrases, int height) /*-{ 545 var config = { 546 theme: "eclipse", 547 mode: "text/plain", 548 lineNumbers: true, 549 styleActiveLine: true, 550 fixedGutter: true, 551 indentUnit: 4, 552 indentWithTabs: true, 553 smartIndent: false 554 }; 555 556 config.phrases = phrases; 557 var result = $wnd.CodeMirror(elem, config); 558 instance.getEditor = function() { 559 return result; 560 } 561 this.@org.opencms.acacia.client.widgets.code.CmsCodeMirrorWidget::m_editor = result; 562 var that = this; 563 result.on("changes", function(editor, changes) { 564 that.@org.opencms.acacia.client.widgets.code.CmsCodeMirrorWidget::fireChangeEvent()(); 565 }); 566 if (height > 0) { 567 result.setSize(null, height); 568 } 569 var resizeHandler = new ResizeObserver(function(entries) { 570 result.refresh(); 571 }); 572 resizeHandler.observe(elem); 573 }-*/; 574 575 /** 576 * Initializes the settings controlled by buttons / select boxes after CodeMirror is loaded. 577 */ 578 private void initializeUserControlledSettings() { 579 580 String startMode = m_config.getStartMode(); 581 if (startMode != null) { 582 if (!m_modes.containsKey(startMode)) { 583 startMode = m_simpleModeTranslations.get(startMode.toLowerCase()); 584 } 585 if (startMode != null) { 586 m_modeSelect.setFormValue(startMode, true); 587 } 588 } 589 m_toggleAutoClose.setValue(true, /*fireEvents=*/true); 590 m_toggleShowTabs.setValue(false, /*fireEvents=*/true); 591 } 592 593 /** 594 * Sets the content in the native CodeMirror instance. 595 * 596 * @param content the new editor content 597 */ 598 private native void nativeSetContent(String content) /*-{ 599 var editor = this.@org.opencms.acacia.client.widgets.code.CmsCodeMirrorWidget::m_editor; 600 editor.setValue(content); 601 }-*/; 602 603 /** 604 * Sets the mode on the native CodeMirror instance. 605 * 606 * @param mode the mode to set 607 */ 608 private native void nativeSetMode(String mode) /*-{ 609 var editor = this.@org.opencms.acacia.client.widgets.code.CmsCodeMirrorWidget::m_editor; 610 editor.setOption("mode", mode); 611 }-*/; 612 613 /** 614 * Sets a CodeMirror option of type 'boolean'. 615 * 616 * @param name the name of the option 617 * @param booleanValue the option value 618 */ 619 private native void nativeSetOption(String name, boolean booleanValue) /*-{ 620 var editor = this.@org.opencms.acacia.client.widgets.code.CmsCodeMirrorWidget::m_editor; 621 if (editor) { // editor may not be fully loaded yet 622 editor.setOption(name, booleanValue); 623 } 624 }-*/; 625 626 /** 627 * Sets a custom CSS property on the given element. 628 * 629 * @param element the element on which to set the property 630 * @param name the name of the CSS property 631 * @param value the value of the CSS property 632 */ 633 private native void nativeSetProperty(com.google.gwt.dom.client.Element element, String name, String value) /*-{ 634 element.style.setProperty(name, value); 635 }-*/; 636 637 /** 638 * Executes the 'redo' command. 639 */ 640 private void redo() { 641 642 executeCommand("redo"); 643 } 644 645 /** 646 * Refreshes the editor layout. 647 */ 648 private native void refresh() /*-{ 649 var editor = this.@org.opencms.acacia.client.widgets.code.CmsCodeMirrorWidget::m_editor; 650 editor.refresh(); 651 }-*/; 652 653 /** 654 * Starts a search operation. 655 */ 656 private void search() { 657 658 if (!hasDialog()) { 659 // executing a dialog command while a dialog is active leads to weird/confusing UI state 660 executeCommand("find"); 661 } 662 } 663 664 /** 665 * Starts a search/replace operation. 666 */ 667 private void searchReplace() { 668 669 if (!hasDialog()) { 670 // executing a dialog command while a dialog is active leads to weird/confusing UI state 671 executeCommand("replace"); 672 } 673 } 674 675 /** 676 * Enables/disables automatic closing of brackets. 677 * 678 * @param value true if automatic bracket closing should be enabled 679 */ 680 private void setAutoCloseBrackets(boolean value) { 681 682 nativeSetOption("autoCloseBrackets", value); 683 nativeSetOption("autoCloseTags", value); 684 nativeSetOption("matchBrackets", value); 685 } 686 687 /** 688 * Updates the editor font size. 689 * 690 * @param value the font size, e.g. '12px' 691 */ 692 private void setFontSize(String value) { 693 694 nativeSetProperty(getElement().cast(), "--codemirror-font-size", value); 695 refresh(); 696 } 697 698 /** 699 * Sets the mode selected by the user (this may not result in the mode actually being changed in the editor if syntax highlighting is turned off). 700 * 701 * @param mode the new mode 702 */ 703 private void setMode(String mode) { 704 705 m_mode = mode; 706 if (m_editor != null) { 707 nativeSetMode(getEffectiveMode()); 708 } 709 710 } 711 712 /** 713 * Enables/disables tab visibility. 714 * 715 * @param value true if tabs should be shown 716 */ 717 private void setShowTabs(boolean value) { 718 719 nativeSetProperty(getElement(), "--codemirror-tab-state", value ? "visible" : "hidden"); 720 nativeSetOption("showTrailingSpace", value); 721 722 } 723 724 /** 725 * Enables / disables syntax highlighting. 726 * 727 * @param booleanValue true if syntax highlighting should be enabled 728 */ 729 private void setSyntaxHighlightingEnabled(boolean booleanValue) { 730 731 m_highlighting = booleanValue; 732 nativeSetMode(getEffectiveMode()); 733 } 734 735 /** 736 * Updates undo/redo button states based on history size. 737 */ 738 private void updateUndoRedo() { 739 740 JavaScriptObject jso = getHistorySize(); 741 JSONObject historySize = new JSONObject(jso); 742 int undo = (int)(historySize.get("undo").isNumber().doubleValue()); 743 int redo = (int)(historySize.get("redo").isNumber().doubleValue()); 744 m_undo.setEnabled(undo > 0); 745 m_redo.setEnabled(redo > 0); 746 } 747 748}