| // Copyright (C) 2014 The Android Open Source Project |
| // |
| // Licensed under the Apache License, Version 2.0 (the "License"); |
| // you may not use this file except in compliance with the License. |
| // You may obtain a copy of the License at |
| // |
| // http://www.apache.org/licenses/LICENSE-2.0 |
| // |
| // Unless required by applicable law or agreed to in writing, software |
| // distributed under the License is distributed on an "AS IS" BASIS, |
| // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. |
| // See the License for the specific language governing permissions and |
| // limitations under the License. |
| |
| package com.google.gerrit.client.editor; |
| |
| import static com.google.gwt.dom.client.Style.Visibility.HIDDEN; |
| import static com.google.gwt.dom.client.Style.Visibility.VISIBLE; |
| |
| import com.google.gerrit.client.DiffWebLinkInfo; |
| import com.google.gerrit.client.Dispatcher; |
| import com.google.gerrit.client.Gerrit; |
| import com.google.gerrit.client.JumpKeys; |
| import com.google.gerrit.client.VoidResult; |
| import com.google.gerrit.client.account.EditPreferences; |
| import com.google.gerrit.client.changes.ChangeApi; |
| import com.google.gerrit.client.changes.ChangeEditApi; |
| import com.google.gerrit.client.diff.DiffApi; |
| import com.google.gerrit.client.diff.DiffInfo; |
| import com.google.gerrit.client.diff.Header; |
| import com.google.gerrit.client.info.ChangeInfo; |
| import com.google.gerrit.client.info.FileInfo; |
| import com.google.gerrit.client.patches.PatchUtil; |
| import com.google.gerrit.client.rpc.CallbackGroup; |
| import com.google.gerrit.client.rpc.GerritCallback; |
| import com.google.gerrit.client.rpc.HttpCallback; |
| import com.google.gerrit.client.rpc.HttpResponse; |
| import com.google.gerrit.client.rpc.NativeString; |
| import com.google.gerrit.client.rpc.Natives; |
| import com.google.gerrit.client.rpc.RestApi; |
| import com.google.gerrit.client.rpc.ScreenLoadCallback; |
| import com.google.gerrit.client.ui.InlineHyperlink; |
| import com.google.gerrit.client.ui.Screen; |
| import com.google.gerrit.common.PageLinks; |
| import com.google.gerrit.extensions.client.KeyMapType; |
| import com.google.gerrit.extensions.client.Theme; |
| import com.google.gerrit.reviewdb.client.Patch; |
| import com.google.gerrit.reviewdb.client.PatchSet; |
| import com.google.gwt.core.client.GWT; |
| import com.google.gwt.core.client.JsArray; |
| import com.google.gwt.core.client.Scheduler; |
| import com.google.gwt.core.client.Scheduler.ScheduledCommand; |
| import com.google.gwt.dom.client.Element; |
| import com.google.gwt.dom.client.Style.Unit; |
| import com.google.gwt.event.dom.client.ClickEvent; |
| import com.google.gwt.event.dom.client.KeyPressEvent; |
| import com.google.gwt.event.logical.shared.ResizeEvent; |
| import com.google.gwt.event.logical.shared.ResizeHandler; |
| import com.google.gwt.event.logical.shared.ValueChangeEvent; |
| import com.google.gwt.event.shared.HandlerRegistration; |
| import com.google.gwt.resources.client.CssResource; |
| import com.google.gwt.uibinder.client.UiBinder; |
| import com.google.gwt.uibinder.client.UiField; |
| import com.google.gwt.uibinder.client.UiHandler; |
| import com.google.gwt.user.client.Window; |
| import com.google.gwt.user.client.Window.ClosingEvent; |
| import com.google.gwt.user.client.Window.ClosingHandler; |
| import com.google.gwt.user.client.rpc.AsyncCallback; |
| import com.google.gwt.user.client.ui.Button; |
| import com.google.gwt.user.client.ui.CheckBox; |
| import com.google.gwt.user.client.ui.FlowPanel; |
| import com.google.gwt.user.client.ui.HTMLPanel; |
| import com.google.gwt.user.client.ui.ImageResourceRenderer; |
| import com.google.gwtexpui.globalkey.client.GlobalKey; |
| import com.google.gwtexpui.safehtml.client.SafeHtml; |
| import java.util.List; |
| import net.codemirror.addon.AddonInjector; |
| import net.codemirror.addon.Addons; |
| import net.codemirror.lib.CodeMirror; |
| import net.codemirror.lib.CodeMirror.ChangesHandler; |
| import net.codemirror.lib.CodeMirror.CommandRunner; |
| import net.codemirror.lib.Configuration; |
| import net.codemirror.lib.KeyMap; |
| import net.codemirror.lib.MergeView; |
| import net.codemirror.lib.Pos; |
| import net.codemirror.mode.ModeInfo; |
| import net.codemirror.mode.ModeInjector; |
| import net.codemirror.theme.ThemeLoader; |
| |
| public class EditScreen extends Screen { |
| interface Binder extends UiBinder<HTMLPanel, EditScreen> {} |
| |
| private static final Binder uiBinder = GWT.create(Binder.class); |
| |
| interface Style extends CssResource { |
| String fullWidth(); |
| |
| String base(); |
| |
| String hideBase(); |
| } |
| |
| private final PatchSet.Id revision; |
| private final String path; |
| private final int startLine; |
| private EditPreferences prefs; |
| private EditPreferencesAction editPrefsAction; |
| private MergeView mv; |
| private CodeMirror cmBase; |
| private CodeMirror cmEdit; |
| private HttpResponse<NativeString> content; |
| private HttpResponse<NativeString> baseContent; |
| private EditFileInfo editFileInfo; |
| private JsArray<DiffWebLinkInfo> diffLinks; |
| |
| @UiField Element header; |
| @UiField Element project; |
| @UiField Element filePath; |
| @UiField FlowPanel linkPanel; |
| @UiField Element cursLine; |
| @UiField Element cursCol; |
| @UiField Element dirty; |
| @UiField CheckBox showBase; |
| @UiField Button close; |
| @UiField Button save; |
| @UiField Element editor; |
| @UiField Style style; |
| |
| private HandlerRegistration resizeHandler; |
| private HandlerRegistration closeHandler; |
| private int generation; |
| |
| public EditScreen(Patch.Key patch, int startLine) { |
| this.revision = patch.getParentKey(); |
| this.path = patch.get(); |
| this.startLine = startLine - 1; |
| setRequiresSignIn(true); |
| add(uiBinder.createAndBindUi(this)); |
| addDomHandler(GlobalKey.STOP_PROPAGATION, KeyPressEvent.getType()); |
| } |
| |
| @Override |
| protected void onInitUI() { |
| super.onInitUI(); |
| setHeaderVisible(false); |
| setWindowTitle(FileInfo.getFileName(path)); |
| } |
| |
| @Override |
| protected void onLoad() { |
| super.onLoad(); |
| |
| prefs = EditPreferences.create(Gerrit.getEditPreferences()); |
| |
| CallbackGroup group1 = new CallbackGroup(); |
| final CallbackGroup group2 = new CallbackGroup(); |
| final CallbackGroup group3 = new CallbackGroup(); |
| |
| CodeMirror.initLibrary( |
| group1.add( |
| new AsyncCallback<Void>() { |
| final AsyncCallback<Void> themeCallback = group3.addEmpty(); |
| |
| @Override |
| public void onSuccess(Void result) { |
| // Load theme after CM library to ensure theme can override CSS. |
| ThemeLoader.loadTheme(prefs.theme(), themeCallback); |
| group2.done(); |
| |
| new AddonInjector() |
| .add(Addons.I.merge_bundled().getName()) |
| .inject( |
| new AsyncCallback<Void>() { |
| @Override |
| public void onFailure(Throwable caught) {} |
| |
| @Override |
| public void onSuccess(Void result) { |
| if (!prefs.showBase() || revision.get() > 0) { |
| group3.done(); |
| } |
| } |
| }); |
| } |
| |
| @Override |
| public void onFailure(Throwable caught) {} |
| })); |
| |
| ChangeApi.detail( |
| revision.getParentKey().get(), |
| group1.add( |
| new AsyncCallback<ChangeInfo>() { |
| @Override |
| public void onSuccess(ChangeInfo c) { |
| project.setInnerText(c.project()); |
| SafeHtml.setInnerHTML(filePath, Header.formatPath(path)); |
| } |
| |
| @Override |
| public void onFailure(Throwable caught) {} |
| })); |
| |
| if (revision.get() == 0) { |
| ChangeEditApi.getMeta( |
| revision, |
| path, |
| group1.add( |
| new AsyncCallback<EditFileInfo>() { |
| @Override |
| public void onSuccess(EditFileInfo editInfo) { |
| editFileInfo = editInfo; |
| } |
| |
| @Override |
| public void onFailure(Throwable e) {} |
| })); |
| |
| if (prefs.showBase()) { |
| ChangeEditApi.get( |
| revision, |
| path, |
| true /* base */, |
| group1.addFinal( |
| new HttpCallback<NativeString>() { |
| @Override |
| public void onSuccess(HttpResponse<NativeString> fc) { |
| baseContent = fc; |
| group3.done(); |
| } |
| |
| @Override |
| public void onFailure(Throwable e) {} |
| })); |
| } else { |
| group1.done(); |
| } |
| } else { |
| // TODO(davido): We probably want to create dedicated GET EditScreenMeta |
| // REST endpoint. Abuse GET diff for now, as it retrieves links we need. |
| DiffApi.diff(revision, path) |
| .webLinksOnly() |
| .get( |
| group1.addFinal( |
| new AsyncCallback<DiffInfo>() { |
| @Override |
| public void onSuccess(DiffInfo diffInfo) { |
| diffLinks = diffInfo.webLinks(); |
| } |
| |
| @Override |
| public void onFailure(Throwable e) {} |
| })); |
| } |
| |
| ChangeEditApi.get( |
| revision, |
| path, |
| group2.add( |
| new HttpCallback<NativeString>() { |
| final AsyncCallback<Void> modeCallback = group3.addEmpty(); |
| |
| @Override |
| public void onSuccess(HttpResponse<NativeString> fc) { |
| content = fc; |
| if (revision.get() > 0) { |
| baseContent = fc; |
| } |
| |
| if (prefs.syntaxHighlighting()) { |
| injectMode(fc.getContentType(), modeCallback); |
| } else { |
| modeCallback.onSuccess(null); |
| } |
| } |
| |
| @Override |
| public void onFailure(Throwable e) { |
| // "Not Found" means it's a new file. |
| if (RestApi.isNotFound(e)) { |
| content = null; |
| modeCallback.onSuccess(null); |
| } else { |
| GerritCallback.showFailure(e); |
| } |
| } |
| })); |
| |
| group3.addListener( |
| new ScreenLoadCallback<Void>(this) { |
| @Override |
| protected void preDisplay(Void result) { |
| initEditor(); |
| |
| renderLinks(editFileInfo, diffLinks); |
| editFileInfo = null; |
| diffLinks = null; |
| |
| showBase.setValue(prefs.showBase(), true); |
| cmBase.refresh(); |
| } |
| }); |
| } |
| |
| @Override |
| public void registerKeys() { |
| super.registerKeys(); |
| KeyMap localKeyMap = KeyMap.create(); |
| localKeyMap.on("Ctrl-L", gotoLine()).on("Cmd-L", gotoLine()).on("Cmd-S", save()); |
| |
| // TODO(davido): Find a better way to prevent key maps collisions |
| if (prefs.keyMapType() != KeyMapType.EMACS) { |
| localKeyMap.on("Ctrl-S", save()); |
| } |
| |
| cmBase.addKeyMap(localKeyMap); |
| cmEdit.addKeyMap(localKeyMap); |
| } |
| |
| private Runnable gotoLine() { |
| return new Runnable() { |
| @Override |
| public void run() { |
| cmEdit.execCommand("jumpToLine"); |
| } |
| }; |
| } |
| |
| @Override |
| public void onShowView() { |
| super.onShowView(); |
| Window.enableScrolling(false); |
| JumpKeys.enable(false); |
| if (prefs.hideTopMenu()) { |
| Gerrit.setHeaderVisible(false); |
| } |
| resizeHandler = |
| Window.addResizeHandler( |
| new ResizeHandler() { |
| @Override |
| public void onResize(ResizeEvent event) { |
| adjustHeight(); |
| } |
| }); |
| closeHandler = |
| Window.addWindowClosingHandler( |
| new ClosingHandler() { |
| @Override |
| public void onWindowClosing(ClosingEvent event) { |
| if (!cmEdit.isClean(generation)) { |
| event.setMessage(EditConstants.I.closeUnsavedChanges()); |
| } |
| } |
| }); |
| |
| generation = cmEdit.changeGeneration(true); |
| setClean(true); |
| cmEdit.on( |
| new ChangesHandler() { |
| @Override |
| public void handle(CodeMirror cm) { |
| setClean(cm.isClean(generation)); |
| } |
| }); |
| |
| adjustHeight(); |
| cmEdit.on("cursorActivity", updateCursorPosition()); |
| setShowTabs(prefs.showTabs()); |
| setLineLength(prefs.lineLength()); |
| cmEdit.refresh(); |
| cmEdit.focus(); |
| |
| if (startLine > 0) { |
| cmEdit.scrollToLine(startLine); |
| } |
| updateActiveLine(); |
| editPrefsAction = new EditPreferencesAction(this, prefs); |
| } |
| |
| @Override |
| protected void onUnload() { |
| super.onUnload(); |
| if (cmBase != null) { |
| cmBase.getWrapperElement().removeFromParent(); |
| } |
| if (cmEdit != null) { |
| cmEdit.getWrapperElement().removeFromParent(); |
| } |
| if (resizeHandler != null) { |
| resizeHandler.removeHandler(); |
| } |
| if (closeHandler != null) { |
| closeHandler.removeHandler(); |
| } |
| Window.enableScrolling(true); |
| Gerrit.setHeaderVisible(true); |
| JumpKeys.enable(true); |
| } |
| |
| CodeMirror getEditor() { |
| return cmEdit; |
| } |
| |
| @UiHandler("editSettings") |
| void onEditSetting(@SuppressWarnings("unused") ClickEvent e) { |
| editPrefsAction.show(); |
| } |
| |
| @UiHandler("save") |
| void onSave(@SuppressWarnings("unused") ClickEvent e) { |
| save().run(); |
| } |
| |
| @UiHandler("close") |
| void onClose(@SuppressWarnings("unused") ClickEvent e) { |
| if (cmEdit.isClean(generation) || Window.confirm(EditConstants.I.cancelUnsavedChanges())) { |
| upToChange(); |
| } |
| } |
| |
| private void displayBase() { |
| cmBase.getWrapperElement().getParentElement().removeClassName(style.hideBase()); |
| cmEdit.getWrapperElement().getParentElement().removeClassName(style.fullWidth()); |
| mv.getGapElement().removeClassName(style.hideBase()); |
| setCmBaseValue(); |
| setLineLength(prefs.lineLength()); |
| cmBase.refresh(); |
| } |
| |
| @UiHandler("showBase") |
| void onShowBase(ValueChangeEvent<Boolean> e) { |
| boolean shouldShow = e.getValue(); |
| if (shouldShow) { |
| if (baseContent == null) { |
| ChangeEditApi.get( |
| revision, |
| path, |
| true /* base */, |
| new HttpCallback<NativeString>() { |
| @Override |
| public void onSuccess(HttpResponse<NativeString> fc) { |
| baseContent = fc; |
| displayBase(); |
| } |
| |
| @Override |
| public void onFailure(Throwable e) {} |
| }); |
| } else { |
| displayBase(); |
| } |
| } else { |
| cmBase.getWrapperElement().getParentElement().addClassName(style.hideBase()); |
| cmEdit.getWrapperElement().getParentElement().addClassName(style.fullWidth()); |
| mv.getGapElement().addClassName(style.hideBase()); |
| } |
| mv.setShowDifferences(shouldShow); |
| } |
| |
| void setOption(String option, String value) { |
| cmBase.setOption(option, value); |
| cmEdit.setOption(option, value); |
| } |
| |
| void setOption(String option, boolean value) { |
| cmBase.setOption(option, value); |
| cmEdit.setOption(option, value); |
| } |
| |
| void setOption(String option, double value) { |
| cmBase.setOption(option, value); |
| cmEdit.setOption(option, value); |
| } |
| |
| void setTheme(final Theme newTheme) { |
| cmBase.operation( |
| new Runnable() { |
| @Override |
| public void run() { |
| cmBase.setOption("theme", newTheme.name().toLowerCase()); |
| } |
| }); |
| cmEdit.operation( |
| new Runnable() { |
| @Override |
| public void run() { |
| cmEdit.setOption("theme", newTheme.name().toLowerCase()); |
| } |
| }); |
| } |
| |
| void setLineLength(int length) { |
| int adjustedLength = Patch.COMMIT_MSG.equals(path) ? 72 : length; |
| cmBase.extras().lineLength(adjustedLength); |
| cmEdit.extras().lineLength(adjustedLength); |
| } |
| |
| void setIndentUnit(int indent) { |
| cmEdit.setOption("indentUnit", Patch.COMMIT_MSG.equals(path) ? 2 : indent); |
| } |
| |
| void setShowLineNumbers(boolean show) { |
| cmBase.setOption("lineNumbers", show); |
| cmEdit.setOption("lineNumbers", show); |
| } |
| |
| void setShowWhitespaceErrors(final boolean show) { |
| cmBase.operation( |
| new Runnable() { |
| @Override |
| public void run() { |
| cmBase.setOption("showTrailingSpace", show); |
| } |
| }); |
| cmEdit.operation( |
| new Runnable() { |
| @Override |
| public void run() { |
| cmEdit.setOption("showTrailingSpace", show); |
| } |
| }); |
| } |
| |
| void setShowTabs(boolean show) { |
| cmBase.extras().showTabs(show); |
| cmEdit.extras().showTabs(show); |
| } |
| |
| void adjustHeight() { |
| int height = header.getOffsetHeight(); |
| int rest = Gerrit.getHeaderFooterHeight() + height + 5; // Estimate |
| mv.getGapElement().getStyle().setHeight(Window.getClientHeight() - rest, Unit.PX); |
| cmBase.adjustHeight(height); |
| cmEdit.adjustHeight(height); |
| } |
| |
| void setSyntaxHighlighting(boolean b) { |
| ModeInfo modeInfo = ModeInfo.findMode(content.getContentType(), path); |
| final String mode = modeInfo != null ? modeInfo.mime() : null; |
| if (b && mode != null && !mode.isEmpty()) { |
| injectMode( |
| mode, |
| new AsyncCallback<Void>() { |
| @Override |
| public void onSuccess(Void result) { |
| cmBase.setOption("mode", mode); |
| cmEdit.setOption("mode", mode); |
| } |
| |
| @Override |
| public void onFailure(Throwable caught) { |
| prefs.syntaxHighlighting(false); |
| } |
| }); |
| } else { |
| cmBase.setOption("mode", (String) null); |
| cmEdit.setOption("mode", (String) null); |
| } |
| } |
| |
| private void upToChange() { |
| Gerrit.display(PageLinks.toChangeInEditMode(revision.getParentKey())); |
| } |
| |
| private void initEditor() { |
| ModeInfo mode = null; |
| String editContent = ""; |
| if (content != null && content.getResult() != null) { |
| editContent = content.getResult().asString(); |
| if (prefs.syntaxHighlighting()) { |
| mode = ModeInfo.findMode(content.getContentType(), path); |
| } |
| } |
| |
| Configuration cfg = |
| Configuration.create() |
| .set("autoCloseBrackets", prefs.autoCloseBrackets()) |
| .set("cursorBlinkRate", prefs.cursorBlinkRate()) |
| .set("cursorHeight", 0.85) |
| .set("indentUnit", prefs.indentUnit()) |
| .set("keyMap", prefs.keyMapType().name().toLowerCase()) |
| .set("lineNumbers", prefs.hideLineNumbers()) |
| .set("lineWrapping", prefs.lineWrapping()) |
| .set("indentWithTabs", prefs.indentWithTabs()) |
| .set("matchBrackets", prefs.matchBrackets()) |
| .set("mode", mode != null ? mode.mime() : null) |
| .set("origLeft", editContent) |
| .set("scrollbarStyle", "overlay") |
| .set("showTrailingSpace", prefs.showWhitespaceErrors()) |
| .set("styleSelectedText", true) |
| .set("tabSize", prefs.tabSize()) |
| .set("theme", prefs.theme().name().toLowerCase()) |
| .set("value", ""); |
| |
| if (editContent.contains("\r\n")) { |
| cfg.set("lineSeparator", "\r\n"); |
| } |
| |
| mv = MergeView.create(editor, cfg); |
| |
| cmBase = mv.leftOriginal(); |
| cmBase.getWrapperElement().addClassName(style.base()); |
| cmEdit = mv.editor(); |
| setCmBaseValue(); |
| cmEdit.setValue(editContent); |
| |
| CodeMirror.addCommand( |
| "save", |
| new CommandRunner() { |
| @Override |
| public void run(CodeMirror instance) { |
| save().run(); |
| } |
| }); |
| } |
| |
| private void renderLinks(EditFileInfo editInfo, JsArray<DiffWebLinkInfo> diffLinks) { |
| renderLinksToDiff(); |
| |
| if (editInfo != null) { |
| renderLinks(Natives.asList(editInfo.webLinks())); |
| } else if (diffLinks != null) { |
| renderLinks(Natives.asList(diffLinks)); |
| } |
| } |
| |
| private void renderLinks(List<DiffWebLinkInfo> links) { |
| if (links != null) { |
| for (DiffWebLinkInfo webLink : links) { |
| linkPanel.add(webLink.toAnchor()); |
| } |
| } |
| } |
| |
| private void renderLinksToDiff() { |
| InlineHyperlink sbs = new InlineHyperlink(); |
| sbs.setHTML(new ImageResourceRenderer().render(Gerrit.RESOURCES.sideBySideDiff())); |
| sbs.setTargetHistoryToken( |
| Dispatcher.toPatch("sidebyside", null, new Patch.Key(revision, path))); |
| sbs.setTitle(PatchUtil.C.sideBySideDiff()); |
| linkPanel.add(sbs); |
| |
| InlineHyperlink unified = new InlineHyperlink(); |
| unified.setHTML(new ImageResourceRenderer().render(Gerrit.RESOURCES.unifiedDiff())); |
| unified.setTargetHistoryToken( |
| Dispatcher.toPatch("unified", null, new Patch.Key(revision, path))); |
| unified.setTitle(PatchUtil.C.unifiedDiff()); |
| linkPanel.add(unified); |
| } |
| |
| private Runnable updateCursorPosition() { |
| return new Runnable() { |
| @Override |
| public void run() { |
| // The rendering of active lines has to be deferred. Reflow |
| // caused by adding and removing styles chokes Firefox when arrow |
| // key (or j/k) is held down. Performance on Chrome is fine |
| // without the deferral. |
| // |
| Scheduler.get() |
| .scheduleDeferred( |
| new ScheduledCommand() { |
| @Override |
| public void execute() { |
| cmEdit.operation( |
| new Runnable() { |
| @Override |
| public void run() { |
| updateActiveLine(); |
| } |
| }); |
| } |
| }); |
| } |
| }; |
| } |
| |
| private void updateActiveLine() { |
| Pos p = cmEdit.getCursor("end"); |
| cursLine.setInnerText(Integer.toString(p.line() + 1)); |
| cursCol.setInnerText(Integer.toString(p.ch() + 1)); |
| cmEdit.extras().activeLine(cmEdit.getLineHandleVisualStart(p.line())); |
| } |
| |
| private void setClean(boolean clean) { |
| save.setEnabled(!clean); |
| close.setEnabled(true); |
| dirty.getStyle().setVisibility(!clean ? VISIBLE : HIDDEN); |
| } |
| |
| private Runnable save() { |
| return new Runnable() { |
| @Override |
| public void run() { |
| if (!cmEdit.isClean(generation)) { |
| close.setEnabled(false); |
| String text = cmEdit.getValue(); |
| if (Patch.COMMIT_MSG.equals(path)) { |
| String trimmed = text.trim() + "\r"; |
| if (!trimmed.equals(text)) { |
| text = trimmed; |
| cmEdit.setValue(text); |
| } |
| } |
| final int g = cmEdit.changeGeneration(false); |
| ChangeEditApi.put( |
| revision.getParentKey().get(), |
| path, |
| text, |
| new GerritCallback<VoidResult>() { |
| @Override |
| public void onSuccess(VoidResult result) { |
| generation = g; |
| setClean(cmEdit.isClean(g)); |
| } |
| |
| @Override |
| public void onFailure(final Throwable caught) { |
| close.setEnabled(true); |
| } |
| }); |
| } |
| } |
| }; |
| } |
| |
| private void injectMode(String type, AsyncCallback<Void> cb) { |
| new ModeInjector().add(type).inject(cb); |
| } |
| |
| private void setCmBaseValue() { |
| cmBase.setValue( |
| baseContent != null && baseContent.getResult() != null |
| ? baseContent.getResult().asString() |
| : ""); |
| } |
| } |