diff --git a/gerrit-gwtui/src/main/java/com/google/gerrit/client/diff/ChunkManager.java b/gerrit-gwtui/src/main/java/com/google/gerrit/client/diff/ChunkManager.java index 91582b2f1a..c5a0c252ef 100644 --- a/gerrit-gwtui/src/main/java/com/google/gerrit/client/diff/ChunkManager.java +++ b/gerrit-gwtui/src/main/java/com/google/gerrit/client/diff/ChunkManager.java @@ -147,7 +147,7 @@ class ChunkManager { void adjustPadding() { if (paddingDivs != null) { - double h = host.getLineHeightPx(); + double h = cmB.extras().lineHeightPx(); for (Element div : paddingDivs) { int lines = div.getPropertyInt(DATA_LINES); div.getStyle().setHeight(lines * h, Unit.PX); @@ -291,7 +291,9 @@ class ChunkManager { return new Runnable() { @Override public void run() { - int line = cm.hasActiveLine() ? cm.getLineNumber(cm.activeLine()) : 0; + int line = cm.extras().hasActiveLine() + ? cm.getLineNumber(cm.extras().activeLine()) + : 0; int res = Collections.binarySearch( chunks, new DiffChunkInfo(cm.side(), line, 0, false), diff --git a/gerrit-gwtui/src/main/java/com/google/gerrit/client/diff/CommentManager.java b/gerrit-gwtui/src/main/java/com/google/gerrit/client/diff/CommentManager.java index 8d8f117dc2..887978fdf3 100644 --- a/gerrit-gwtui/src/main/java/com/google/gerrit/client/diff/CommentManager.java +++ b/gerrit-gwtui/src/main/java/com/google/gerrit/client/diff/CommentManager.java @@ -91,8 +91,8 @@ class CommentManager { // It is only necessary to search one side to find a comment // on either side of the editor pair. SortedMap map = map(src.side()); - int line = src.hasActiveLine() - ? src.getLineNumber(src.activeLine()) + 1 + int line = src.extras().hasActiveLine() + ? src.getLineNumber(src.extras().activeLine()) + 1 : 0; if (dir == Direction.NEXT) { map = map.tailMap(line + 1); @@ -295,9 +295,9 @@ class CommentManager { return new Runnable() { @Override public void run() { - if (cm.hasActiveLine()) { + if (cm.extras().hasActiveLine()) { CommentGroup w = map(cm.side()).get( - cm.getLineNumber(cm.activeLine()) + 1); + cm.getLineNumber(cm.extras().activeLine()) + 1); if (w != null) { w.openCloseLast(); } @@ -310,9 +310,9 @@ class CommentManager { return new Runnable() { @Override public void run() { - if (cm.hasActiveLine()) { + if (cm.extras().hasActiveLine()) { CommentGroup w = map(cm.side()).get( - cm.getLineNumber(cm.activeLine()) + 1); + cm.getLineNumber(cm.extras().activeLine()) + 1); if (w != null) { w.openCloseAll(); } @@ -327,8 +327,8 @@ class CommentManager { @Override public void run() { String token = host.getToken(); - if (cm.hasActiveLine()) { - LineHandle handle = cm.activeLine(); + if (cm.extras().hasActiveLine()) { + LineHandle handle = cm.extras().activeLine(); int line = cm.getLineNumber(handle) + 1; token += "@" + (cm.side() == DisplaySide.A ? "a" : "") + line; } @@ -340,7 +340,7 @@ class CommentManager { return new Runnable() { @Override public void run() { - if (cm.hasActiveLine()) { + if (cm.extras().hasActiveLine()) { newDraft(cm); } } @@ -348,7 +348,7 @@ class CommentManager { } private void newDraft(CodeMirror cm) { - int line = cm.getLineNumber(cm.activeLine()) + 1; + int line = cm.getLineNumber(cm.extras().activeLine()) + 1; if (cm.somethingSelected()) { FromTo fromTo = cm.getSelectedRange(); Pos end = fromTo.to(); diff --git a/gerrit-gwtui/src/main/java/com/google/gerrit/client/diff/DiffTable.java b/gerrit-gwtui/src/main/java/com/google/gerrit/client/diff/DiffTable.java index 8bdc7477d1..10b2640e6a 100644 --- a/gerrit-gwtui/src/main/java/com/google/gerrit/client/diff/DiffTable.java +++ b/gerrit-gwtui/src/main/java/com/google/gerrit/client/diff/DiffTable.java @@ -48,14 +48,11 @@ class DiffTable extends Composite { String dark(); String diff(); String noIntraline(); - String activeLine(); String range(); String rangeHighlight(); - String showTabs(); String showLineNumbers(); String hideA(); String hideB(); - String columnMargin(); String padding(); } diff --git a/gerrit-gwtui/src/main/java/com/google/gerrit/client/diff/DiffTable.ui.xml b/gerrit-gwtui/src/main/java/com/google/gerrit/client/diff/DiffTable.ui.xml index 1420d3b7e3..a0b84468f2 100644 --- a/gerrit-gwtui/src/main/java/com/google/gerrit/client/diff/DiffTable.ui.xml +++ b/gerrit-gwtui/src/main/java/com/google/gerrit/client/diff/DiffTable.ui.xml @@ -18,12 +18,11 @@ limitations under the License. xmlns:g='urn:import:com.google.gwt.user.client.ui' xmlns:d='urn:import:com.google.gerrit.client.diff'> - @external .CodeMirror, .CodeMirror-lines, .CodeMirror-selectedtext; + @external .CodeMirror, .CodeMirror-selectedtext; @external .CodeMirror-linenumber; @external .CodeMirror-overlayscroll-vertical, .CodeMirror-scroll; @external .CodeMirror-dialog-bottom; @external .cm-animate-fat-cursor, .CodeMirror-cursor; - @external .cm-searching, .cm-trailingspace, .cm-tab; .fullscreen { background-color: #f7f7f7; @@ -39,13 +38,10 @@ limitations under the License. -ms-user-select: none; } - .difftable .CodeMirror-lines { padding: 0; } .difftable .CodeMirror pre { - padding: 0; overflow: hidden; border-right: 0; width: auto; - line-height: normal; } /* Preserve space for underscores. If this changes @@ -106,23 +102,12 @@ limitations under the License. overflow-x: auto; } - .activeLine .CodeMirror-linenumber { - background-color: #bcf !important; - color: #000; - } - .range { background-color: #ffd500 !important; } .rangeHighlight { background-color: #ffff00 !important; } - .cm-searching { - background-color: #ffa !important; - } - .cm-trailingspace { - background-color: red !important; - } .difftable .CodeMirror-selectedtext { background-color: inherit !important; } @@ -154,24 +139,10 @@ limitations under the License. bottom: auto; left: auto; } - .showTabs .cm-tab:before { - position: absolute; - content: "\00bb"; - color: #f00; - } .showLineNumbers .padding { margin-left: 21px; border-left: 2px solid #d64040; } - .columnMargin { - position: absolute; - top: 0; - bottom: 0; - width: 0; - border-right: 1px dashed #ffa500; - z-index: 2; - cursor: text; - } .diff_header { font-size: 12px; diff --git a/gerrit-gwtui/src/main/java/com/google/gerrit/client/diff/Scrollbar.css b/gerrit-gwtui/src/main/java/com/google/gerrit/client/diff/Scrollbar.css index a5351151f4..383f2782cf 100644 --- a/gerrit-gwtui/src/main/java/com/google/gerrit/client/diff/Scrollbar.css +++ b/gerrit-gwtui/src/main/java/com/google/gerrit/client/diff/Scrollbar.css @@ -13,26 +13,6 @@ * limitations under the License. */ -@external .CodeMirror; -@external .CodeMirror-overlayscroll-horizontal; -@external .CodeMirror-overlayscroll-vertical; - -.CodeMirror-overlayscroll-horizontal div { - min-width: 25px; -} -.CodeMirror-overlayscroll-vertical div { - min-height: 25px; -} - -.CodeMirror .CodeMirror-overlayscroll-vertical { - z-index: inherit; -} -.CodeMirror .CodeMirror-overlayscroll-horizontal div, -.CodeMirror .CodeMirror-overlayscroll-vertical div { - background-color: rgba(128, 128, 128, 0.50); - z-index: 8; -} - .comment, .draft, .insert, .delete, .edit { min-height: 5px; position: absolute; diff --git a/gerrit-gwtui/src/main/java/com/google/gerrit/client/diff/SideBySide2.java b/gerrit-gwtui/src/main/java/com/google/gerrit/client/diff/SideBySide2.java index 42b49d3121..2675235e57 100644 --- a/gerrit-gwtui/src/main/java/com/google/gerrit/client/diff/SideBySide2.java +++ b/gerrit-gwtui/src/main/java/com/google/gerrit/client/diff/SideBySide2.java @@ -48,7 +48,6 @@ import com.google.gwt.core.client.Scheduler.RepeatingCommand; import com.google.gwt.core.client.Scheduler.ScheduledCommand; import com.google.gwt.dom.client.Element; import com.google.gwt.dom.client.NativeEvent; -import com.google.gwt.dom.client.Style; import com.google.gwt.event.dom.client.FocusEvent; import com.google.gwt.event.dom.client.FocusHandler; import com.google.gwt.event.dom.client.KeyCodes; @@ -58,7 +57,6 @@ import com.google.gwt.event.logical.shared.ResizeHandler; import com.google.gwt.event.shared.HandlerRegistration; import com.google.gwt.uibinder.client.UiBinder; import com.google.gwt.uibinder.client.UiField; -import com.google.gwt.user.client.DOM; import com.google.gwt.user.client.Window; import com.google.gwt.user.client.rpc.AsyncCallback; import com.google.gwt.user.client.ui.FlowPanel; @@ -71,7 +69,6 @@ import com.google.gwtexpui.globalkey.client.ShowHelpCommand; import net.codemirror.lib.CodeMirror; import net.codemirror.lib.CodeMirror.BeforeSelectionChangeHandler; import net.codemirror.lib.CodeMirror.GutterClickHandler; -import net.codemirror.lib.CodeMirror.LineClassWhere; import net.codemirror.lib.CodeMirror.LineHandle; import net.codemirror.lib.Configuration; import net.codemirror.lib.KeyMap; @@ -120,10 +117,6 @@ public class SideBySide2 extends Screen { private CodeMirror cmA; private CodeMirror cmB; - private Element columnMarginA; - private Element columnMarginB; - private double charWidthPx; - private double lineHeightPx; private HandlerRegistration resizeHandler; private ScrollSynchronizer scrollSynchronizer; @@ -268,12 +261,10 @@ public class SideBySide2 extends Screen { } }); - final int height = getCodeMirrorHeight(); operation(new Runnable() { @Override public void run() { - cmA.setHeight(height); - cmB.setHeight(height); + resizeCodeMirror(); chunkManager.adjustPadding(); cmA.refresh(); cmB.refresh(); @@ -297,6 +288,7 @@ public class SideBySide2 extends Screen { if (startSide != null && startLine > 0) { int line = startLine - 1; CodeMirror cm = getCmFromSide(startSide); + int height = cm.getHeight(); if (cm.lineAtHeight(height - 20) < line) { cm.scrollToY(cm.heightAtLine(line, "local") - 0.5 * height); } @@ -549,24 +541,21 @@ public class SideBySide2 extends Screen { private void display(final CommentsCollections comments) { setThemeStyles(prefs.theme().isDark()); - setShowTabs(prefs.showTabs()); setShowIntraline(prefs.intralineDifference()); if (prefs.showLineNumbers()) { diffTable.addStyleName(DiffTable.style.showLineNumbers()); } - cmA = newCM(diff.meta_a(), diff.text_a(), diffTable.cmA).side(DisplaySide.A); - cmB = newCM(diff.meta_b(), diff.text_b(), diffTable.cmB).side(DisplaySide.B); + cmA = newCM(diff.meta_a(), diff.text_a(), diffTable.cmA); + cmB = newCM(diff.meta_b(), diff.text_b(), diffTable.cmB); + + cmA.extras().side(DisplaySide.A); + cmB.extras().side(DisplaySide.B); + setShowTabs(prefs.showTabs()); + chunkManager = new ChunkManager(this, cmA, cmB, diffTable.scrollbar); skipManager = new SkipManager(this, commentManager); - columnMarginA = DOM.createDiv(); - columnMarginB = DOM.createDiv(); - columnMarginA.setClassName(DiffTable.style.columnMargin()); - columnMarginB.setClassName(DiffTable.style.columnMargin()); - cmA.mover().appendChild(columnMarginA); - cmB.mover().appendChild(columnMarginB); - if (prefs.renderEntireFile() && !canEnableRenderEntireFile(prefs)) { // CodeMirror is too slow to layout an entire huge file. prefs.renderEntireFile(false); @@ -679,61 +668,14 @@ public class SideBySide2 extends Screen { } } - void setShowTabs(boolean b) { - if (b) { - diffTable.addStyleName(DiffTable.style.showTabs()); - } else { - diffTable.removeStyleName(DiffTable.style.showTabs()); - } + void setShowTabs(boolean show) { + cmA.extras().showTabs(show); + cmB.extras().showTabs(show); } void setLineLength(int columns) { - double w = columns * getCharWidthPx(); - columnMarginA.getStyle().setMarginLeft(w, Style.Unit.PX); - columnMarginB.getStyle().setMarginLeft(w, Style.Unit.PX); - } - - double getLineHeightPx() { - if (lineHeightPx <= 1) { - Element p = DOM.createDiv(); - int lines = 1; - for (int i = 0; i < lines; i++) { - Element e = DOM.createDiv(); - p.appendChild(e); - - Element pre = DOM.createElement("pre"); - pre.setInnerText("gqyŚŻŹŃ"); - e.appendChild(pre); - } - - cmB.measure().appendChild(p); - lineHeightPx = ((double) p.getOffsetHeight()) / lines; - p.removeFromParent(); - } - return lineHeightPx; - } - - private double getCharWidthPx() { - if (charWidthPx <= 1) { - int len = 100; - StringBuilder s = new StringBuilder(); - for (int i = 0; i < len; i++) { - s.append('m'); - } - Element e = DOM.createSpan(); - e.getStyle().setDisplay(Style.Display.INLINE_BLOCK); - e.setInnerText(s.toString()); - - cmA.measure().appendChild(e); - double a = ((double) e.getOffsetWidth()) / len; - e.removeFromParent(); - - cmB.measure().appendChild(e); - double b = ((double) e.getOffsetWidth()) / len; - e.removeFromParent(); - charWidthPx = Math.max(a, b); - } - return charWidthPx; + cmA.extras().lineLength(columns); + cmB.extras().lineLength(columns); } void setShowLineNumbers(boolean b) { @@ -815,15 +757,6 @@ public class SideBySide2 extends Screen { return chunkManager.getLineMapper().lineOnOther(side, line); } - private void clearActiveLine(CodeMirror cm) { - if (cm.hasActiveLine()) { - LineHandle activeLine = cm.activeLine(); - cm.removeLineClass(activeLine, - LineClassWhere.WRAP, DiffTable.style.activeLine()); - cm.activeLine(null); - } - } - private Runnable updateActiveLine(final CodeMirror cm) { final CodeMirror other = otherCm(cm); return new Runnable() { @@ -842,22 +775,16 @@ public class SideBySide2 extends Screen { public void run() { LineHandle handle = cm.getLineHandleVisualStart(cm.getCursor("end").line()); - if (cm.hasActiveLine() && cm.activeLine().equals(handle)) { + if (!cm.extras().activeLine(handle)) { return; } - clearActiveLine(cm); - clearActiveLine(other); - cm.activeLine(handle); - cm.addLineClass( - handle, LineClassWhere.WRAP, DiffTable.style.activeLine()); LineOnOtherInfo info = lineOnOther(cm.side(), cm.getLineNumber(handle)); if (info.isAligned()) { - LineHandle oLineHandle = other.getLineHandle(info.getLine()); - other.activeLine(oLineHandle); - other.addLineClass(oLineHandle, LineClassWhere.WRAP, - DiffTable.style.activeLine()); + other.extras().activeLine(other.getLineHandle(info.getLine())); + } else { + other.extras().clearActiveLine(); } } }); @@ -877,8 +804,8 @@ public class SideBySide2 extends Screen { && !clickEvent.getAltKey() && !clickEvent.getCtrlKey() && !clickEvent.getShiftKey()) { - if (!(cm.hasActiveLine() && - cm.getLineNumber(cm.activeLine()) == line)) { + if (!(cm.extras().hasActiveLine() && + cm.getLineNumber(cm.extras().activeLine()) == line)) { cm.setCursor(Pos.create(line)); } Scheduler.get().scheduleDeferred(new ScheduledCommand() { @@ -928,10 +855,10 @@ public class SideBySide2 extends Screen { return new Runnable() { @Override public void run() { - if (cmSrc.hasActiveLine()) { + if (cmSrc.extras().hasActiveLine()) { cmDst.setCursor(Pos.create(lineOnOther( sideSrc, - cmSrc.getLineNumber(cmSrc.activeLine())).getLine())); + cmSrc.getLineNumber(cmSrc.extras().activeLine())).getLine())); } cmDst.focus(); } @@ -977,17 +904,9 @@ public class SideBySide2 extends Screen { } void resizeCodeMirror() { - int height = getCodeMirrorHeight(); - cmA.setHeight(height); - cmB.setHeight(height); - } - - private int getCodeMirrorHeight() { - int rest = Gerrit.getHeaderFooterHeight() - + header.getOffsetHeight() - + diffTable.getHeaderHeight() - + 5; // Estimate - return Window.getClientHeight() - rest; + int hdr = header.getOffsetHeight() + diffTable.getHeaderHeight(); + cmA.adjustHeight(hdr); + cmB.adjustHeight(hdr); } void syncScroll(DisplaySide masterSide) { diff --git a/gerrit-gwtui/src/main/java/com/google/gerrit/client/editor/EditConstants.java b/gerrit-gwtui/src/main/java/com/google/gerrit/client/editor/EditConstants.java new file mode 100644 index 0000000000..fcd03e50af --- /dev/null +++ b/gerrit-gwtui/src/main/java/com/google/gerrit/client/editor/EditConstants.java @@ -0,0 +1,25 @@ +// Copyright (C) 2015 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 com.google.gwt.core.client.GWT; +import com.google.gwt.i18n.client.Constants; + +interface EditConstants extends Constants { + static final EditConstants I = GWT.create(EditConstants.class); + + String closeUnsavedChanges(); + String cancelUnsavedChanges(); +} diff --git a/gerrit-gwtui/src/main/java/com/google/gerrit/client/editor/EditConstants.properties b/gerrit-gwtui/src/main/java/com/google/gerrit/client/editor/EditConstants.properties new file mode 100644 index 0000000000..f90d8287f1 --- /dev/null +++ b/gerrit-gwtui/src/main/java/com/google/gerrit/client/editor/EditConstants.properties @@ -0,0 +1,5 @@ +closeUnsavedChanges = Unsaved changes were made to this file. + +cancelUnsavedChanges = Unsaved changes were made to this file.\n\ + \n\ + Discard unsaved changes? diff --git a/gerrit-gwtui/src/main/java/com/google/gerrit/client/editor/EditScreen.java b/gerrit-gwtui/src/main/java/com/google/gerrit/client/editor/EditScreen.java index 5c2e5efbeb..70855ce8c7 100644 --- a/gerrit-gwtui/src/main/java/com/google/gerrit/client/editor/EditScreen.java +++ b/gerrit-gwtui/src/main/java/com/google/gerrit/client/editor/EditScreen.java @@ -15,6 +15,7 @@ package com.google.gerrit.client.editor; import com.google.gerrit.client.Gerrit; +import com.google.gerrit.client.JumpKeys; import com.google.gerrit.client.VoidResult; import com.google.gerrit.client.account.DiffPreferences; import com.google.gerrit.client.changes.ChangeApi; @@ -30,13 +31,20 @@ import com.google.gerrit.common.PageLinks; 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.Scheduler; +import com.google.gwt.core.client.Scheduler.ScheduledCommand; import com.google.gwt.dom.client.Element; 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.shared.HandlerRegistration; 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.HTMLPanel; @@ -44,9 +52,13 @@ import com.google.gwtexpui.globalkey.client.GlobalKey; import com.google.gwtexpui.safehtml.client.SafeHtml; import net.codemirror.lib.CodeMirror; +import net.codemirror.lib.CodeMirror.ChangesHandler; import net.codemirror.lib.Configuration; +import net.codemirror.lib.KeyMap; +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 {} @@ -58,12 +70,17 @@ public class EditScreen extends Screen { private CodeMirror cm; private String type; + @UiField Element header; @UiField Element project; @UiField Element filePath; - @UiField Button cancel; + @UiField Button close; @UiField Button save; @UiField Element editor; + private HandlerRegistration resizeHandler; + private HandlerRegistration closeHandler; + private int generation; + public EditScreen(Patch.Key patch) { this.revision = patch.getParentKey(); this.path = patch.get(); @@ -84,9 +101,22 @@ public class EditScreen extends Screen { super.onLoad(); CallbackGroup cmGroup = new CallbackGroup(); - CodeMirror.initLibrary(cmGroup. addEmpty()); - CallbackGroup group = new CallbackGroup(); - if (!Patch.COMMIT_MSG.equals(path)) { + final CallbackGroup group = new CallbackGroup(); + CodeMirror.initLibrary(cmGroup.add(new AsyncCallback() { + final AsyncCallback themeCallback = group.addEmpty(); + + @Override + public void onSuccess(Void result) { + // Load theme after CM library to ensure theme can override CSS. + ThemeLoader.loadTheme(prefs.theme(), themeCallback); + } + + @Override + public void onFailure(Throwable caught) { + } + })); + + if (prefs.syntaxHighlighting() && !Patch.COMMIT_MSG.equals(path)) { final AsyncCallback modeInjectorCb = group.addEmpty(); ChangeFileApi.getContentType(revision, path, cmGroup.add(new GerritCallback() { @@ -121,60 +151,151 @@ public class EditScreen extends Screen { @Override public void onShowView() { super.onShowView(); + Window.enableScrolling(false); + JumpKeys.enable(false); if (prefs.hideTopMenu()) { Gerrit.setHeaderVisible(false); } - int rest = Gerrit.getHeaderFooterHeight() - + 30; // Estimate - cm.setHeight(Window.getClientHeight() - rest); + resizeHandler = Window.addResizeHandler(new ResizeHandler() { + @Override + public void onResize(ResizeEvent event) { + cm.adjustHeight(header.getOffsetHeight()); + } + }); + closeHandler = Window.addWindowClosingHandler(new ClosingHandler() { + @Override + public void onWindowClosing(ClosingEvent event) { + if (!cm.isClean(generation)) { + event.setMessage(EditConstants.I.closeUnsavedChanges()); + } + } + }); + + generation = cm.changeGeneration(true); + save.setEnabled(false); + cm.on(new ChangesHandler() { + @Override + public void handle(CodeMirror cm) { + save.setEnabled(!cm.isClean(generation)); + } + }); + + cm.adjustHeight(header.getOffsetHeight()); + cm.on("cursorActivity", updateCursorPosition()); + cm.extras().showTabs(prefs.showTabs()); + cm.extras().lineLength(prefs.lineLength()); cm.refresh(); cm.focus(); + updateActiveLine(); } @Override protected void onUnload() { super.onUnload(); + if (cm != null) { + cm.getWrapperElement().removeFromParent(); + } + if (resizeHandler != null) { + resizeHandler.removeHandler(); + } + if (closeHandler != null) { + closeHandler.removeHandler(); + } + Window.enableScrolling(true); Gerrit.setHeaderVisible(true); + JumpKeys.enable(true); } @UiHandler("save") void onSave(@SuppressWarnings("unused") ClickEvent e) { - ChangeFileApi.putContentOrMessage(revision, path, cm.getValue(), - new GerritCallback() { - @Override - public void onSuccess(VoidResult result) { - Gerrit.display(PageLinks.toChangeInEditMode( - revision.getParentKey())); - } - }); + save().run(); } - @UiHandler("cancel") - void onCancel(@SuppressWarnings("unused") ClickEvent e) { + @UiHandler("close") + void onClose(@SuppressWarnings("unused") ClickEvent e) { + if (cm.isClean(generation) + || Window.confirm(EditConstants.I.cancelUnsavedChanges())) { + upToChange(); + } + } + + private void upToChange() { Gerrit.display(PageLinks.toChangeInEditMode(revision.getParentKey())); } private void initEditor(String content) { - cm = CodeMirror.create(editor, getConfig()); - cm.setValue(content); + ModeInfo mode = prefs.syntaxHighlighting() + ? ModeInfo.findMode(type, path) + : null; + cm = CodeMirror.create(editor, Configuration.create() + .set("value", content) + .set("readOnly", false) + .set("cursorBlinkRate", 0) + .set("cursorHeight", 0.85) + .set("lineNumbers", true) + .set("tabSize", prefs.tabSize()) + .set("lineWrapping", false) + .set("scrollbarStyle", "overlay") + .set("styleSelectedText", true) + .set("showTrailingSpace", true) + .set("keyMap", "default") + .set("theme", prefs.theme().name().toLowerCase()) + .set("mode", mode != null ? mode.mode() : null)); + cm.addKeyMap(KeyMap.create() + .on("Cmd-S", save()) + .on("Ctrl-S", save())); + } + + 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() { + cm.operation(new Runnable() { + @Override + public void run() { + updateActiveLine(); + } + }); + } + }); + } + }; + } + + private void updateActiveLine() { + Pos p = cm.getCursor("end"); + cm.extras().activeLine(cm.getLineHandleVisualStart(p.line())); + } + + private Runnable save() { + return new Runnable() { + @Override + public void run() { + if (!cm.isClean(generation)) { + String text = cm.getValue(); + final int g = cm.changeGeneration(false); + ChangeFileApi.putContentOrMessage(revision, path, text, + new GerritCallback() { + @Override + public void onSuccess(VoidResult result) { + generation = g; + save.setEnabled(!cm.isClean(g)); + } + }); + } + } + }; } private void injectMode(String type, AsyncCallback cb) { new ModeInjector().add(type).inject(cb); } - - private Configuration getConfig() { - // TODO(davido): Retrieve user preferences from AllUsers repository - return Configuration.create() - .set("readOnly", false) - .set("cursorBlinkRate", 0) - .set("cursorHeight", 0.85) - .set("lineNumbers", true) - .set("tabSize", 4) - .set("lineWrapping", false) - .set("styleSelectedText", true) - .set("showTrailingSpace", true) - .set("keyMap", "default") - .set("mode", type); - } } diff --git a/gerrit-gwtui/src/main/java/com/google/gerrit/client/editor/EditScreen.ui.xml b/gerrit-gwtui/src/main/java/com/google/gerrit/client/editor/EditScreen.ui.xml index 8033bbf48e..50bd3bc0c8 100644 --- a/gerrit-gwtui/src/main/java/com/google/gerrit/client/editor/EditScreen.ui.xml +++ b/gerrit-gwtui/src/main/java/com/google/gerrit/client/editor/EditScreen.ui.xml @@ -31,7 +31,9 @@ limitations under the License. } .headerButtons button:disabled { - background-color: #999; + background-color: #ddd; + font-weight: normal; + cursor: default; } .headerButtons button { @@ -62,13 +64,13 @@ limitations under the License. } -
+
- + title='Close file and return to change'> -
Cancel
+
Close