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.ui.editors;
029
030import org.opencms.file.CmsResource;
031import org.opencms.file.types.CmsResourceTypeBinary;
032import org.opencms.file.types.CmsResourceTypeImage;
033import org.opencms.file.types.CmsResourceTypeJsp;
034import org.opencms.file.types.CmsResourceTypeXmlContent;
035import org.opencms.file.types.CmsResourceTypeXmlPage;
036import org.opencms.file.types.I_CmsResourceType;
037import org.opencms.i18n.CmsMessages;
038import org.opencms.json.JSONException;
039import org.opencms.json.JSONObject;
040import org.opencms.lock.CmsLockUtil.LockedFile;
041import org.opencms.main.OpenCms;
042import org.opencms.ui.A_CmsUI;
043import org.opencms.ui.CmsVaadinUtils;
044import org.opencms.ui.FontOpenCms;
045import org.opencms.ui.apps.CmsAppWorkplaceUi;
046import org.opencms.ui.apps.CmsEditor;
047import org.opencms.ui.apps.I_CmsAppSettings;
048import org.opencms.ui.apps.I_CmsAppUIContext;
049import org.opencms.ui.apps.I_CmsHasShortcutActions;
050import org.opencms.ui.components.CmsConfirmationDialog;
051import org.opencms.ui.components.CmsErrorDialog;
052import org.opencms.ui.components.CmsToolBar;
053import org.opencms.ui.components.I_CmsWindowCloseListener;
054import org.opencms.ui.components.OpenCmsTheme;
055import org.opencms.ui.components.codemirror.CmsCodeMirror;
056import org.opencms.ui.components.codemirror.CmsCodeMirror.CodeMirrorLanguage;
057
058import java.util.HashMap;
059import java.util.Map;
060
061import com.vaadin.event.Action;
062import com.vaadin.event.ShortcutAction;
063import com.vaadin.navigator.ViewChangeListener;
064import com.vaadin.ui.Button;
065import com.vaadin.ui.Button.ClickEvent;
066import com.vaadin.ui.Button.ClickListener;
067import com.vaadin.ui.UI;
068import com.vaadin.v7.data.Property.ValueChangeEvent;
069import com.vaadin.v7.data.Property.ValueChangeListener;
070import com.vaadin.v7.ui.ComboBox;
071
072/**
073 * The plain text editor.<p>
074 */
075@SuppressWarnings("deprecation")
076public class CmsSourceEditor
077implements I_CmsEditor, I_CmsWindowCloseListener, ViewChangeListener, I_CmsHasShortcutActions {
078
079    /**
080     * Stores the editor settings.<p>
081     */
082    public static class EditorSettings implements I_CmsAppSettings {
083
084        /** JSON key constant. */
085        private static final String BRACKETS = "brackets";
086        /** JSON key constant. */
087        private static final String FONTSIZE = "fontsize";
088        /** JSON key constant. */
089        private static final String HIGHLIGHTING = "highlighting";
090        /** JSON key constant. */
091        private static final String TABS = "tabs";
092        /** JSON key constant. */
093        private static final String WRAPPING = "wrapping";
094
095        /** The auto close brackets flag. */
096        boolean m_closeBrackets = true;
097
098        /** The font size. */
099        String m_fontSize = "16px";
100
101        /** The highlighting flag. */
102        boolean m_highlighting = true;
103
104        /** The line wrapping flag. */
105        boolean m_lineWrapping;
106
107        /** The tab visibility flag. */
108        boolean m_tabsVisible = true;
109
110        /**
111         * @see org.opencms.ui.apps.I_CmsAppSettings#getSettingsString()
112         */
113        public String getSettingsString() {
114
115            JSONObject json = new JSONObject();
116            try {
117                json.put(BRACKETS, m_closeBrackets);
118                json.put(HIGHLIGHTING, m_highlighting);
119                json.put(WRAPPING, m_lineWrapping);
120                json.put(FONTSIZE, m_fontSize);
121                json.put(TABS, m_tabsVisible);
122            } catch (JSONException e) {
123                // TODO Auto-generated catch block
124                e.printStackTrace();
125            }
126
127            return json.toString();
128        }
129
130        /**
131         * @see org.opencms.ui.apps.I_CmsAppSettings#restoreSettings(java.lang.String)
132         */
133        public void restoreSettings(String storedSettings) {
134
135            try {
136                JSONObject json = new JSONObject(storedSettings);
137                if (json.has(BRACKETS)) {
138                    m_closeBrackets = json.getBoolean(BRACKETS);
139                }
140                if (json.has(HIGHLIGHTING)) {
141                    m_highlighting = json.getBoolean(HIGHLIGHTING);
142                }
143                if (json.has(WRAPPING)) {
144                    m_lineWrapping = json.getBoolean(WRAPPING);
145                }
146                if (json.has(TABS)) {
147                    m_tabsVisible = json.getBoolean(TABS);
148                }
149                if (json.has(FONTSIZE)) {
150                    m_fontSize = json.getString(FONTSIZE);
151                }
152
153            } catch (JSONException e) {
154                //       LOG.error("Failed to restore file explorer settings from '" + storedSettings + "'", e);
155            }
156        }
157    }
158
159    /** Exit shortcut. */
160    private static final Action ACTION_EXIT = new ShortcutAction(
161        "Ctrl+Shift+X",
162        ShortcutAction.KeyCode.X,
163        new int[] {ShortcutAction.ModifierKey.CTRL, ShortcutAction.ModifierKey.SHIFT});
164
165    /** Exit shortcut, (using Apple CMD as modifier). */
166    private static final Action ACTION_EXIT_CMD = new ShortcutAction(
167        "CMD+Shift+X",
168        ShortcutAction.KeyCode.X,
169        new int[] {ShortcutAction.ModifierKey.META, ShortcutAction.ModifierKey.SHIFT});
170
171    /** Save shortcut. */
172    private static final Action ACTION_SAVE = new ShortcutAction(
173        "Ctrl+S",
174        ShortcutAction.KeyCode.S,
175        new int[] {ShortcutAction.ModifierKey.CTRL});
176
177    /** Save shortcut, (using Apple CMD as modifier). */
178    private static final Action ACTION_SAVE_CMD = new ShortcutAction(
179        "CMD+S",
180        ShortcutAction.KeyCode.S,
181        new int[] {ShortcutAction.ModifierKey.META});
182
183    /** Save & Exit shortcut. */
184    private static final Action ACTION_SAVE_AND_EXIT = new ShortcutAction(
185        "Ctrl+Shift+S",
186        ShortcutAction.KeyCode.S,
187        new int[] {ShortcutAction.ModifierKey.CTRL, ShortcutAction.ModifierKey.SHIFT});
188
189    /** Save & Exit shortcut, (using Apple CMD as modifier). */
190    private static final Action ACTION_SAVE_AND_EXIT_CMD = new ShortcutAction(
191        "CMD+Shift+S",
192        ShortcutAction.KeyCode.S,
193        new int[] {ShortcutAction.ModifierKey.META, ShortcutAction.ModifierKey.SHIFT});
194
195    /** The available font sizes. */
196    private static final String[] FONT_SIZES = new String[] {"8px", "10px", "12px", "14px", "16px", "18px", "20px"};
197
198    /** The serial version id. */
199    private static final long serialVersionUID = 726920483145397926L;
200
201    /** The editor back link. */
202    String m_backLink;
203
204    /** The code mirror instance. */
205    CmsCodeMirror m_codeMirror;
206
207    /** The bundle editor shortcuts. */
208    Map<Action, Runnable> m_shortcutActions;
209
210    /** The content changed flag. */
211    private boolean m_changed;
212
213    /** The cleared flag. */
214    private boolean m_cleared;
215
216    /** The exit button. */
217    private Button m_exit;
218
219    /** The current file. */
220    private LockedFile m_file;
221
222    /** The save button. */
223    private Button m_save;
224
225    /** The save and exit button. */
226    private Button m_saveAndExit;
227
228    /**
229     * Constructor.<p>
230     */
231    public CmsSourceEditor() {
232
233        m_shortcutActions = new HashMap<Action, Runnable>();
234        Runnable save = new Runnable() {
235
236            public void run() {
237
238                save();
239            }
240        };
241        m_shortcutActions.put(ACTION_SAVE, save);
242        m_shortcutActions.put(ACTION_SAVE_CMD, save);
243        Runnable saveExit = new Runnable() {
244
245            public void run() {
246
247                saveAndExit();
248            }
249        };
250        m_shortcutActions.put(ACTION_SAVE_AND_EXIT, saveExit);
251        m_shortcutActions.put(ACTION_SAVE_AND_EXIT_CMD, saveExit);
252        Runnable exit = new Runnable() {
253
254            public void run() {
255
256                exit();
257            }
258        };
259        m_shortcutActions.put(ACTION_EXIT, exit);
260        m_shortcutActions.put(ACTION_EXIT_CMD, exit);
261    }
262
263    /**
264     * @see com.vaadin.navigator.ViewChangeListener#afterViewChange(com.vaadin.navigator.ViewChangeListener.ViewChangeEvent)
265     */
266    public void afterViewChange(ViewChangeEvent event) {
267
268        // nothing to do
269    }
270
271    /**
272     * @see com.vaadin.navigator.ViewChangeListener#beforeViewChange(com.vaadin.navigator.ViewChangeListener.ViewChangeEvent)
273     */
274    public boolean beforeViewChange(final ViewChangeEvent event) {
275
276        if (m_changed) {
277            CmsConfirmationDialog.show(
278                CmsVaadinUtils.getMessageText(org.opencms.ui.apps.Messages.GUI_EDITOR_CLOSE_CAPTION_0),
279                CmsVaadinUtils.getMessageText(org.opencms.ui.apps.Messages.GUI_EDITOR_CLOSE_TEXT_0),
280                new Runnable() {
281
282                    public void run() {
283
284                        clear();
285                        event.getNavigator().navigateTo(event.getViewName());
286                    }
287                });
288            return false;
289        }
290        if (!m_cleared) {
291            clear();
292        }
293        return true;
294    }
295
296    /**
297     * Returns the syntax highlighting type for the currently edited resource.<p>
298     *
299     * @param resource the resource to edit
300     *
301     * @return the syntax highlighting type
302     */
303    public CodeMirrorLanguage getHighlightMode(CmsResource resource) {
304
305        if (resource != null) {
306            // determine resource type
307            int type = resource.getTypeId();
308            if (CmsResourceTypeJsp.isJspTypeId(type)) {
309                // JSP file
310                return CodeMirrorLanguage.JSP;
311            }
312            if (CmsResourceTypeXmlContent.isXmlContent(resource) || CmsResourceTypeXmlPage.isXmlPage(resource)) {
313                // XML content file or XML page file
314                return CodeMirrorLanguage.XML;
315            }
316            // all other files will be matched according to their suffix
317            int dotIndex = resource.getName().lastIndexOf('.');
318            if (dotIndex != -1) {
319                String suffix = resource.getName().substring(dotIndex + 1).toLowerCase();
320                for (CodeMirrorLanguage lang : CodeMirrorLanguage.values()) {
321                    if (lang.isSupportedFileType(suffix)) {
322                        return lang;
323                    }
324                }
325            }
326        }
327        // return HTML type as default
328        return CodeMirrorLanguage.HTML;
329    }
330
331    /**
332     * @see org.opencms.ui.editors.I_CmsEditor#getPriority()
333     */
334    public int getPriority() {
335
336        return 10;
337    }
338
339    /**
340     * @see org.opencms.ui.apps.I_CmsHasShortcutActions#getShortcutActions()
341     */
342    public Map<Action, Runnable> getShortcutActions() {
343
344        return m_shortcutActions;
345    }
346
347    /**
348     * @see org.opencms.ui.editors.I_CmsEditor#initUI(org.opencms.ui.apps.I_CmsAppUIContext, org.opencms.file.CmsResource, java.lang.String, java.util.Map)
349     */
350    public void initUI(I_CmsAppUIContext context, CmsResource resource, String backLink, Map<String, String> params) {
351
352        CmsMessages messages = Messages.get().getBundle(UI.getCurrent().getLocale());
353        context.showInfoArea(false);
354        context.setAppTitle(messages.key(Messages.GUI_SOURCE_EDITOR_TITLE_0));
355        CmsAppWorkplaceUi.setWindowTitle(
356            CmsVaadinUtils.getMessageText(
357                org.opencms.ui.apps.Messages.GUI_CONTENT_EDITOR_TITLE_2,
358                resource.getName(),
359                CmsResource.getParentFolder(A_CmsUI.getCmsObject().getSitePath(resource))));
360        m_backLink = backLink;
361        m_codeMirror = new CmsCodeMirror();
362        m_codeMirror.setSizeFull();
363        context.setAppContent(m_codeMirror);
364        context.enableDefaultToolbarButtons(false);
365        m_saveAndExit = CmsToolBar.createButton(
366            FontOpenCms.SAVE_EXIT,
367            messages.key(Messages.GUI_BUTTON_SAVE_AND_EXIT_0),
368            true);
369        m_saveAndExit.addClickListener(new ClickListener() {
370
371            private static final long serialVersionUID = 1L;
372
373            public void buttonClick(ClickEvent event) {
374
375                saveAndExit();
376            }
377
378        });
379        m_saveAndExit.setEnabled(false);
380        context.addToolbarButton(m_saveAndExit);
381
382        m_save = CmsToolBar.createButton(FontOpenCms.SAVE, messages.key(Messages.GUI_BUTTON_SAVE_0), true);
383        m_save.addClickListener(new ClickListener() {
384
385            private static final long serialVersionUID = 1L;
386
387            public void buttonClick(ClickEvent event) {
388
389                save();
390            }
391
392        });
393        m_save.setEnabled(false);
394        context.addToolbarButton(m_save);
395
396        Button undo = CmsToolBar.createButton(FontOpenCms.UNDO, messages.key(Messages.GUI_BUTTON_UNDO_0), true);
397        context.addToolbarButton(undo);
398
399        Button redo = CmsToolBar.createButton(FontOpenCms.REDO, messages.key(Messages.GUI_BUTTON_REDO_0), true);
400        context.addToolbarButton(redo);
401
402        m_codeMirror.registerUndoRedo(undo, redo);
403
404        Button search = CmsToolBar.createButton(FontOpenCms.SEARCH, messages.key(Messages.GUI_BUTTON_SEARCH_0), true);
405        context.addToolbarButton(search);
406
407        Button replace = CmsToolBar.createButton(
408            FontOpenCms.SEARCH_REPLACE,
409            messages.key(Messages.GUI_BUTTON_REPLACE_0),
410            true);
411        context.addToolbarButton(replace);
412
413        m_codeMirror.registerSearchReplace(search, replace);
414
415        EditorSettings settings;
416        try {
417            settings = OpenCms.getWorkplaceAppManager().getAppSettings(A_CmsUI.getCmsObject(), EditorSettings.class);
418
419        } catch (Exception e) {
420            settings = new EditorSettings();
421        }
422
423        final Button toggleHighlight = CmsToolBar.createButton(
424            FontOpenCms.HIGHLIGHT,
425            messages.key(Messages.GUI_BUTTON_TOGGLE_HIGHLIGHTING_0));
426        toggleHighlight.addClickListener(new ClickListener() {
427
428            private static final long serialVersionUID = 1L;
429
430            public void buttonClick(ClickEvent event) {
431
432                Button b = event.getButton();
433                boolean pressed = b.getStyleName().contains(OpenCmsTheme.BUTTON_PRESSED);
434                if (pressed) {
435                    b.removeStyleName(OpenCmsTheme.BUTTON_PRESSED);
436                } else {
437                    b.addStyleName(OpenCmsTheme.BUTTON_PRESSED);
438                }
439                m_codeMirror.setHighlighting(!pressed);
440            }
441
442        });
443        if (settings.m_highlighting) {
444            m_codeMirror.setHighlighting(true);
445            toggleHighlight.addStyleName(OpenCmsTheme.BUTTON_PRESSED);
446        } else {
447            m_codeMirror.setHighlighting(false);
448        }
449        context.addToolbarButtonRight(toggleHighlight);
450
451        final Button toggleLineWrap = CmsToolBar.createButton(
452            FontOpenCms.WRAP_LINES,
453            messages.key(Messages.GUI_BUTTON_TOGGLE_LINE_WRAPPING_0));
454        toggleLineWrap.addClickListener(new ClickListener() {
455
456            private static final long serialVersionUID = 1L;
457
458            public void buttonClick(ClickEvent event) {
459
460                Button b = event.getButton();
461                boolean pressed = b.getStyleName().contains(OpenCmsTheme.BUTTON_PRESSED);
462                if (pressed) {
463                    b.removeStyleName(OpenCmsTheme.BUTTON_PRESSED);
464                } else {
465                    b.addStyleName(OpenCmsTheme.BUTTON_PRESSED);
466                }
467                m_codeMirror.setLineWrapping(!pressed);
468            }
469
470        });
471        if (settings.m_lineWrapping) {
472            m_codeMirror.setLineWrapping(true);
473            toggleLineWrap.addStyleName(OpenCmsTheme.BUTTON_PRESSED);
474        } else {
475            m_codeMirror.setLineWrapping(false);
476        }
477        context.addToolbarButtonRight(toggleLineWrap);
478
479        final Button toggleBrackets = CmsToolBar.createButton(
480            FontOpenCms.BRACKETS,
481            messages.key(Messages.GUI_BUTTON_TOBBLE_BRACKET_AUTOCLOSE_0));
482        toggleBrackets.addClickListener(new ClickListener() {
483
484            private static final long serialVersionUID = 1L;
485
486            public void buttonClick(ClickEvent event) {
487
488                Button b = event.getButton();
489                boolean pressed = b.getStyleName().contains(OpenCmsTheme.BUTTON_PRESSED);
490                if (pressed) {
491                    b.removeStyleName(OpenCmsTheme.BUTTON_PRESSED);
492                } else {
493                    b.addStyleName(OpenCmsTheme.BUTTON_PRESSED);
494                }
495                m_codeMirror.setCloseBrackets(!pressed);
496            }
497
498        });
499        if (settings.m_closeBrackets) {
500            m_codeMirror.setCloseBrackets(true);
501            toggleBrackets.addStyleName(OpenCmsTheme.BUTTON_PRESSED);
502        } else {
503            m_codeMirror.setCloseBrackets(false);
504        }
505        context.addToolbarButtonRight(toggleBrackets);
506
507        final Button toggleTabs = CmsToolBar.createButton(
508            FontOpenCms.INVISIBLE_CHARS,
509            messages.key(Messages.GUI_BUTTON_TOGGLE_TAB_VISIBILITY_0));
510        toggleTabs.addClickListener(new ClickListener() {
511
512            private static final long serialVersionUID = 1L;
513
514            public void buttonClick(ClickEvent event) {
515
516                Button b = event.getButton();
517                boolean pressed = b.getStyleName().contains(OpenCmsTheme.BUTTON_PRESSED);
518                if (pressed) {
519                    b.removeStyleName(OpenCmsTheme.BUTTON_PRESSED);
520                } else {
521                    b.addStyleName(OpenCmsTheme.BUTTON_PRESSED);
522                }
523                m_codeMirror.setTabsVisible(!pressed);
524            }
525
526        });
527        if (settings.m_tabsVisible) {
528            m_codeMirror.setTabsVisible(true);
529            toggleTabs.addStyleName(OpenCmsTheme.BUTTON_PRESSED);
530        } else {
531            m_codeMirror.setTabsVisible(false);
532        }
533        context.addToolbarButtonRight(toggleTabs);
534
535        ComboBox modeSelect = new ComboBox();
536        modeSelect.setWidth("115px");
537        modeSelect.addStyleName(OpenCmsTheme.TOOLBAR_FIELD);
538        modeSelect.addStyleName(OpenCmsTheme.REQUIRED_BUTTON);
539        modeSelect.setNullSelectionAllowed(false);
540        modeSelect.setImmediate(true);
541        modeSelect.setNewItemsAllowed(false);
542        for (CodeMirrorLanguage lang : CodeMirrorLanguage.values()) {
543            modeSelect.addItem(lang);
544            modeSelect.setItemCaption(lang, lang.name());
545        }
546        CodeMirrorLanguage lang = getHighlightMode(resource);
547        modeSelect.setValue(lang);
548        m_codeMirror.setLanguage(lang);
549        modeSelect.addValueChangeListener(new ValueChangeListener() {
550
551            private static final long serialVersionUID = 1L;
552
553            public void valueChange(ValueChangeEvent event) {
554
555                m_codeMirror.setLanguage((CodeMirrorLanguage)event.getProperty().getValue());
556            }
557        });
558        context.addToolbarButtonRight(modeSelect);
559
560        ComboBox fontSizeSelect = new ComboBox();
561        fontSizeSelect.setWidth("75px");
562        fontSizeSelect.addStyleName(OpenCmsTheme.TOOLBAR_FIELD);
563        fontSizeSelect.addStyleName(OpenCmsTheme.REQUIRED_BUTTON);
564        fontSizeSelect.setNullSelectionAllowed(false);
565        fontSizeSelect.setImmediate(true);
566        fontSizeSelect.setNewItemsAllowed(false);
567        for (int i = 0; i < FONT_SIZES.length; i++) {
568            fontSizeSelect.addItem(FONT_SIZES[i]);
569        }
570        fontSizeSelect.setValue(settings.m_fontSize);
571        fontSizeSelect.addValueChangeListener(new ValueChangeListener() {
572
573            private static final long serialVersionUID = 1L;
574
575            public void valueChange(ValueChangeEvent event) {
576
577                m_codeMirror.setFontSize((String)event.getProperty().getValue());
578            }
579        });
580        context.addToolbarButtonRight(fontSizeSelect);
581        m_codeMirror.setFontSize(settings.m_fontSize);
582
583        m_exit = CmsToolBar.createButton(FontOpenCms.EXIT, messages.key(Messages.GUI_BUTTON_CANCEL_0), true);
584        m_exit.addClickListener(new ClickListener() {
585
586            private static final long serialVersionUID = 1L;
587
588            public void buttonClick(ClickEvent event) {
589
590                exit();
591            }
592
593        });
594        context.addToolbarButtonRight(m_exit);
595
596        try {
597            m_file = LockedFile.lockResource(A_CmsUI.getCmsObject(), resource);
598            String content = new String(m_file.getFile().getContents(), m_file.getEncoding());
599            m_codeMirror.setValue(content);
600        } catch (Exception e) {
601            CmsErrorDialog.showErrorDialog(e);
602        }
603        m_codeMirror.addValueChangeListener(new ValueChangeListener() {
604
605            private static final long serialVersionUID = 1L;
606
607            public void valueChange(ValueChangeEvent event) {
608
609                onChange((String)event.getProperty().getValue());
610            }
611        });
612    }
613
614    /**
615     * @see org.opencms.ui.editors.I_CmsEditor#matchesResource(org.opencms.file.CmsResource, boolean)
616     */
617    public boolean matchesResource(CmsResource resource, boolean plainText) {
618
619        I_CmsResourceType type = OpenCms.getResourceManager().getResourceType(resource);
620        return matchesType(type, plainText);
621    }
622
623    /**
624     * @see org.opencms.ui.editors.I_CmsEditor#matchesType(org.opencms.file.types.I_CmsResourceType, boolean)
625     */
626    public boolean matchesType(I_CmsResourceType type, boolean plainText) {
627
628        return !((type instanceof CmsResourceTypeBinary) || (type instanceof CmsResourceTypeImage));
629    }
630
631    /**
632     * @see org.opencms.ui.editors.I_CmsEditor#newInstance()
633     */
634    public I_CmsEditor newInstance() {
635
636        return new CmsSourceEditor();
637    }
638
639    /**
640     * @see org.opencms.ui.components.I_CmsWindowCloseListener#onWindowClose()
641     */
642    public void onWindowClose() {
643
644        clear();
645    }
646
647    /**
648     * Unlocks the edited file before leaving the editor.<p>
649     */
650    void clear() {
651
652        m_cleared = true;
653        m_changed = false;
654        m_file.tryUnlock();
655        OpenCms.getWorkplaceAppManager().storeAppSettings(
656            A_CmsUI.getCmsObject(),
657            EditorSettings.class,
658            getCurrentSettings());
659    }
660
661    /**
662     * Exits the editor without saving.<p>
663     * Will ask to confirm exit on changed contents.<p>
664     */
665    void exit() {
666
667        if (m_changed) {
668            CmsConfirmationDialog.show(
669                CmsVaadinUtils.getMessageText(org.opencms.ui.apps.Messages.GUI_EDITOR_CLOSE_CAPTION_0),
670                CmsVaadinUtils.getMessageText(org.opencms.ui.apps.Messages.GUI_EDITOR_CLOSE_TEXT_0),
671                new Runnable() {
672
673                    public void run() {
674
675                        exitInternal();
676                    }
677                });
678        } else {
679            exitInternal();
680        }
681
682    }
683
684    /**
685     * Exits the editor without saving.<p>
686     */
687    void exitInternal() {
688
689        clear();
690        CmsEditor.openBackLink(m_backLink);
691    }
692
693    /**
694     * Called on content change.<p>
695     *
696     * @param value the changed content value
697     */
698    void onChange(String value) {
699
700        m_changed = true;
701        m_save.setEnabled(true);
702        m_saveAndExit.setEnabled(true);
703    }
704
705    /**
706     * Saves the current editor content.<p>
707     */
708    void save() {
709
710        try {
711            byte[] content = m_codeMirror.getValue().getBytes(m_file.getEncoding());
712            m_file.getFile().setContents(content);
713            A_CmsUI.getCmsObject().writeFile(m_file.getFile());
714            m_changed = false;
715            m_save.setEnabled(false);
716            m_saveAndExit.setEnabled(false);
717        } catch (Exception e) {
718            CmsErrorDialog.showErrorDialog(e);
719        }
720    }
721
722    /**
723     * Saves the current editor content and leaves the editor.<p>
724     */
725    void saveAndExit() {
726
727        save();
728        exit();
729    }
730
731    /**
732     * Returns the current editor settings.<p>
733     *
734     * @return the current editor settings
735     */
736    private EditorSettings getCurrentSettings() {
737
738        EditorSettings result = new EditorSettings();
739        result.m_closeBrackets = m_codeMirror.isCloseBrackets();
740        result.m_lineWrapping = m_codeMirror.isLineWrapping();
741        result.m_highlighting = m_codeMirror.isHighlighting();
742        result.m_tabsVisible = m_codeMirror.isTabsVisible();
743        result.m_fontSize = m_codeMirror.getFontSize();
744        return result;
745    }
746}