/*
* TextEditingTargetPresentationHelper.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.views.source.editors.text;
import org.rstudio.core.client.CommandWithArg;
import org.rstudio.core.client.StringUtil;
import org.rstudio.core.client.files.FileSystemItem;
import org.rstudio.core.client.regex.Pattern;
import org.rstudio.studio.client.RStudioGinjector;
import org.rstudio.studio.client.common.SimpleRequestCallback;
import org.rstudio.studio.client.common.presentation.model.SlideNavigation;
import org.rstudio.studio.client.common.presentation.model.SlideNavigationItem;
import org.rstudio.studio.client.workbench.views.console.shell.editor.InputEditorPosition;
import org.rstudio.studio.client.workbench.views.console.shell.editor.InputEditorSelection;
import org.rstudio.studio.client.workbench.views.presentation.model.PresentationServerOperations;
import org.rstudio.studio.client.workbench.views.source.editors.EditingTarget;
import org.rstudio.studio.client.workbench.views.source.editors.text.ace.Position;
import org.rstudio.studio.client.workbench.views.source.editors.text.status.StatusBarPopupMenu;
import org.rstudio.studio.client.workbench.views.source.editors.text.status.StatusBarPopupRequest;
import org.rstudio.studio.client.workbench.views.source.model.SourcePosition;
import com.google.gwt.core.client.Scheduler;
import com.google.gwt.core.client.Scheduler.ScheduledCommand;
import com.google.gwt.safehtml.shared.SafeHtmlBuilder;
import com.google.gwt.user.client.Command;
import com.google.gwt.user.client.ui.MenuItem;
import com.google.inject.Inject;
public class TextEditingTargetPresentationHelper
{
public static interface SlideNavigator
{
void navigateToSlide(int index);
}
public TextEditingTargetPresentationHelper(DocDisplay docDisplay)
{
docDisplay_ = docDisplay;
RStudioGinjector.INSTANCE.injectMembers(this);
}
@Inject
void initialize(PresentationServerOperations server)
{
server_ = server;
}
public String getCurrentSlide()
{
// search starting two lines ahead
Position cursorPos = docDisplay_.getCursorPosition();
Position searchPos = Position.create(cursorPos.getRow()+2, 0);
InputEditorSelection sel = docDisplay_.search(SLIDE_REGEX,
true,
false,
false,
false,
searchPos,
null,
true);
if (sel != null)
{
InputEditorPosition titlePos = sel.getStart().moveToPreviousLine();
String title = docDisplay_.getLine(
docDisplay_.selectionToPosition(titlePos).getRow());
title = title.trim();
if (title.length() > 0 && SLIDE_PATTERN.match(title, 0) == null)
return title;
else
return "(Untitled Slide)";
}
else
return "(No Slides)";
}
public void buildSlideMenu(
final String path,
boolean isDirty,
final EditingTarget editor,
final CommandWithArg<StatusBarPopupRequest> onCompleted)
{
// rpc response handler
SimpleRequestCallback<SlideNavigation> requestCallback =
new SimpleRequestCallback<SlideNavigation>() {
@Override
public void onResponseReceived(SlideNavigation slideNavigation)
{
// create the menu and make sure we have some slides to return
StatusBarPopupMenu menu = new StatusBarPopupMenu();
if (slideNavigation.getTotalSlides() == 0)
{
onCompleted.execute(new StatusBarPopupRequest(menu, null));
return;
}
MenuItem defaultMenuItem = null;
int length = slideNavigation.getItems().length();
for (int i=0; i<length; i++)
{
SlideNavigationItem item = slideNavigation.getItems().get(i);
String title = item.getTitle();
if (StringUtil.isNullOrEmpty(title))
title = "(Untitled Slide)";
StringBuilder indentBuilder = new StringBuilder();
for (int level=0; level<item.getIndent(); level++)
indentBuilder.append(" ");
SafeHtmlBuilder labelBuilder = new SafeHtmlBuilder();
labelBuilder.appendHtmlConstant(indentBuilder.toString());
labelBuilder.appendEscaped(title);
final int targetSlide = i;
final MenuItem menuItem = new MenuItem(
labelBuilder.toSafeHtml(),
new Command()
{
public void execute()
{
navigateToSlide(editor, targetSlide);
}
});
menu.addItem(menuItem);
// see if this is the default menu item
if (defaultMenuItem == null &&
item.getLine() >= (docDisplay_.getSelectionStart().getRow()))
{
defaultMenuItem = menuItem;
}
}
StatusBarPopupRequest request = new StatusBarPopupRequest(
menu,
defaultMenuItem);
onCompleted.execute(request);
}
};
// send code over the wire if we are dirty
if (isDirty)
{
server_.getSlideNavigationForCode(
docDisplay_.getCode(),
FileSystemItem.createFile(path).getParentPathString(),
requestCallback);
}
else
{
server_.getSlideNavigationForFile(path, requestCallback);
}
}
public static void navigateToSlide(final EditingTarget editor,
int slideIndex)
{
// scan for the specified slide
int currentSlide = 0;
Position navPos = null;
Position pos = Position.create(0, 0);
while ((pos = editor.search(pos, "^\\={3,}\\s*$")) != null)
{
if (currentSlide++ == slideIndex)
{
navPos = Position.create(pos.getRow() - 1, 0);
break;
}
pos = Position.create(pos.getRow() + 1, 0);
}
// navigate to the slide
if (navPos != null)
{
final Position navPosAlias = navPos;
Scheduler.get().scheduleDeferred(new ScheduledCommand() {
@Override
public void execute()
{
editor.navigateToPosition(
SourcePosition.create(navPosAlias.getRow(), 0),
false);
}
});
}
}
private final DocDisplay docDisplay_;
private PresentationServerOperations server_;
private static final String SLIDE_REGEX = "^\\={3,}\\s*$";
private static final Pattern SLIDE_PATTERN = Pattern.create(SLIDE_REGEX);
}