/* * EditSnippetsPanel.java * * Copyright (C) 2009-12 by RStudio, Inc. * * Unless you have received this program directly from RStudio pursuant * to the terms of a commercial license agreement with RStudio, then * this program is licensed to you under the terms of version 3 of the * GNU Affero General Public License. This program is distributed WITHOUT * ANY EXPRESS OR IMPLIED WARRANTY, INCLUDING THOSE OF NON-INFRINGEMENT, * MERCHANTABILITY OR FITNESS FOR A PARTICULAR PURPOSE. Please refer to the * AGPL (http://www.gnu.org/licenses/agpl-3.0.txt) for more details. * */ package org.rstudio.studio.client.workbench.snippets.ui; import java.util.ArrayList; import javax.inject.Inject; import org.rstudio.core.client.BrowseCap; import org.rstudio.core.client.Size; import org.rstudio.core.client.command.KeyboardShortcut; import org.rstudio.core.client.dom.DomMetrics; import org.rstudio.core.client.widget.ModalDialogBase; import org.rstudio.core.client.widget.ModalDialogTracker; import org.rstudio.core.client.widget.ThemedButton; import org.rstudio.core.client.widget.WidgetListBox; import org.rstudio.studio.client.RStudioGinjector; import org.rstudio.studio.client.application.events.EventBus; import org.rstudio.studio.client.common.GlobalDisplay; import org.rstudio.studio.client.common.HelpLink; import org.rstudio.studio.client.common.filetypes.FileTypeRegistry; import org.rstudio.studio.client.common.filetypes.TextFileType; import org.rstudio.studio.client.server.Void; import org.rstudio.studio.client.server.VoidServerRequestCallback; import org.rstudio.studio.client.workbench.prefs.model.UIPrefs; import org.rstudio.studio.client.workbench.snippets.SnippetHelper; import org.rstudio.studio.client.workbench.snippets.model.SnippetData; import org.rstudio.studio.client.workbench.ui.FontSizeManager; import org.rstudio.studio.client.workbench.views.output.lint.model.LintServerOperations; import org.rstudio.studio.client.workbench.views.source.editors.text.AceEditor; import org.rstudio.studio.client.workbench.views.source.editors.text.DocDisplay; import org.rstudio.studio.client.workbench.views.source.editors.text.TextDisplay; import org.rstudio.studio.client.workbench.views.source.editors.text.TextEditingTarget; import com.google.gwt.core.client.JavaScriptException; import com.google.gwt.core.client.JsArray; import com.google.gwt.dom.client.NativeEvent; import com.google.gwt.dom.client.Style.Unit; import com.google.gwt.event.dom.client.ChangeEvent; import com.google.gwt.event.dom.client.ChangeHandler; import com.google.gwt.event.dom.client.ClickEvent; import com.google.gwt.event.dom.client.ClickHandler; import com.google.gwt.event.logical.shared.ValueChangeEvent; import com.google.gwt.event.logical.shared.ValueChangeHandler; import com.google.gwt.event.shared.HandlerRegistration; import com.google.gwt.user.client.Event; import com.google.gwt.user.client.ui.DockLayoutPanel; import com.google.gwt.user.client.ui.SimplePanel; import com.google.gwt.user.client.ui.Widget; public class EditSnippetsDialog extends ModalDialogBase implements TextDisplay { public EditSnippetsDialog() { setText("Edit Snippets"); RStudioGinjector.INSTANCE.injectMembers(this); addCancelButton(); ThemedButton saveButton = new ThemedButton("Save", new ClickHandler() { public void onClick(ClickEvent event) { attemptSaveAndClose(); } }); addButton(saveButton); addLeftWidget(new HelpLink("Using Code Snippets", "code_snippets")); } @Inject void initialize(EventBus events, GlobalDisplay globalDisplay, FontSizeManager fontSizeManager, UIPrefs uiPrefs, LintServerOperations server) { events_ = events; fontSizeManager_ = fontSizeManager; globalDisplay_ = globalDisplay; uiPrefs_ = uiPrefs; server_ = server; } @Override protected Widget createMainWidget() { panel_ = new DockLayoutPanel(Unit.PX); // compute the widget size and set it Size size = new Size(900, 900); size = DomMetrics.adjustedElementSize(size, null, 70, // pad 100); // client margin panel_.setWidth(size.width + "px"); panel_.setHeight(size.height + "px"); // snippet types snippetTypes_ = new WidgetListBox<EditableSnippets>(); snippetTypes_.addChangeHandler(new ChangeHandler() { @Override public void onChange(ChangeEvent event) { updateEditor(snippetTypes_.getSelectedItem()); } }); snippetTypes_.addItem(new EditableSnippets("R", FileTypeRegistry.R)); snippetTypes_.addItem(new EditableSnippets(FileTypeRegistry.CPP)); snippetTypes_.addItem(new EditableSnippets(FileTypeRegistry.MARKDOWN)); snippetTypes_.addItem(new EditableSnippets(FileTypeRegistry.TEX)); snippetTypes_.addItem(new EditableSnippets(FileTypeRegistry.JS)); snippetTypes_.addItem(new EditableSnippets(FileTypeRegistry.HTML)); snippetTypes_.addItem(new EditableSnippets(FileTypeRegistry.CSS)); snippetTypes_.addItem(new EditableSnippets(FileTypeRegistry.SQL)); snippetTypes_.addItem(new EditableSnippets(FileTypeRegistry.JAVA)); snippetTypes_.addItem(new EditableSnippets(FileTypeRegistry.PYTHON)); snippetTypes_.addItem(new EditableSnippets(FileTypeRegistry.STAN)); panel_.addWest(snippetTypes_, 150); // editor docDisplay_ = new AceEditor(); docDisplay_.setUseSoftTabs(false); docDisplay_.setTabSize(uiPrefs_.numSpacesForTab().getValue()); docDisplay_.addValueChangeHandler(new ValueChangeHandler<Void>() { @Override public void onValueChange(ValueChangeEvent<Void> event) { editorDirty_ = true; } }); docDisplay_.setFileType(FileTypeRegistry.SNIPPETS); SimplePanel panel = new SimplePanel(); panel.addStyleName("EditDialog"); panel.getElement().getStyle().setMarginLeft(8, Unit.PX); panel.setWidget(docDisplay_.asWidget()); panel_.add(panel); TextEditingTarget.syncFontSize(releaseOnDismiss_, events_, this, fontSizeManager_); snippetTypes_.setSelectedIndex(0, true); return panel_; } @Override public void onDialogShown() { docDisplay_.focus(); } private void attemptSaveAndClose() { // record pending edits recordEditorState(); // check for edits we need to save JsArray<SnippetData> changedSnippets = JsArray.createArray().cast(); for (int i = 0; i<snippetTypes_.getItemCount(); i++) { EditableSnippets snippets = snippetTypes_.getItemAtIdx(i); String edits = snippets.getPendingEdits(); if (edits != null) { String mode = snippets.getEditorMode(); JavaScriptException ex = SnippetHelper.loadSnippetsForMode(mode, edits); if (ex != null) { snippetTypes_.setSelectedIndex(i); globalDisplay_.showErrorMessage( "Error Applying Snippets (" + snippets.getFileTypeLabel() + ")", ex.getDescription()); return; // early return (don't close dialog) } changedSnippets.push(SnippetData.create(mode, edits)); } } // perform the save then close the dialog if (changedSnippets.length() > 0) { server_.saveSnippets(changedSnippets, new VoidServerRequestCallback() { @Override protected void onSuccess() { closeDialog(); } }); } else { closeDialog(); } } @Override public void onActivate() { docDisplay_.onActivate(); } @Override public void adaptToFileType(TextFileType fileType) { docDisplay_.setFileType(fileType, true); } @Override public void setFontSize(double size) { docDisplay_.setFontSize(size); } @Override public Widget asWidget() { return this; } @Override public void onPreviewNativeEvent(Event.NativePreviewEvent event) { if (!ModalDialogTracker.isTopMost(this)) return; if (event.getTypeInt() == Event.ONKEYDOWN) { NativeEvent ne = event.getNativeEvent(); int mod = KeyboardShortcut.getModifierValue(ne); if ((mod == KeyboardShortcut.META || (mod == KeyboardShortcut.CTRL && !BrowseCap.hasMetaKey()))) { if (ne.getKeyCode() == 'S') { ne.preventDefault(); ne.stopPropagation(); event.cancel(); attemptSaveAndClose(); } } } super.onPreviewNativeEvent(event); } @Override public void onUnload() { super.onUnload(); while (releaseOnDismiss_.size() > 0) releaseOnDismiss_.remove(0).removeHandler(); } private void updateEditor(EditableSnippets snippets) { // record editor state recordEditorState(); // set new snippets docDisplay_.setCode(snippets.getSnippetText(), false); activeSnippets_ = snippets; editorDirty_ = false; docDisplay_.scrollToY(activeSnippets_.getScrollPosition(), 0); } private void recordEditorState() { if (activeSnippets_ != null) { if (editorDirty_) activeSnippets_.recordPendingEdits(docDisplay_.getCode()); activeSnippets_.setScrollPosition(docDisplay_.getScrollTop()); } } private DockLayoutPanel panel_; private WidgetListBox<EditableSnippets> snippetTypes_; private DocDisplay docDisplay_; private boolean editorDirty_ = false; private EditableSnippets activeSnippets_ = null; private final ArrayList<HandlerRegistration> releaseOnDismiss_ = new ArrayList<HandlerRegistration>(); private EventBus events_; private GlobalDisplay globalDisplay_; private FontSizeManager fontSizeManager_; private UIPrefs uiPrefs_; private LintServerOperations server_; }