Package org.rstudio.studio.client.rmarkdown.ui

Source Code of org.rstudio.studio.client.rmarkdown.ui.RmdOutputPanel

/*
* RmdOutputPanel.java
*
* Copyright (C) 2009-14 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.rmarkdown.ui;

import com.google.gwt.core.client.Scheduler;
import com.google.gwt.core.client.Scheduler.RepeatingCommand;
import com.google.gwt.dom.client.Document;
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.ClickHandler;
import com.google.gwt.event.dom.client.KeyCodes;
import com.google.gwt.event.dom.client.KeyDownEvent;
import com.google.gwt.event.dom.client.KeyDownHandler;
import com.google.gwt.event.shared.HandlerManager;
import com.google.gwt.event.shared.HandlerRegistration;
import com.google.gwt.user.client.Timer;
import com.google.gwt.user.client.ui.Label;
import com.google.gwt.user.client.ui.Widget;
import com.google.inject.Inject;

import org.rstudio.core.client.FilePosition;
import org.rstudio.core.client.ScrollUtil;
import org.rstudio.core.client.StringUtil;
import org.rstudio.core.client.dom.IFrameElementEx;
import org.rstudio.core.client.dom.WindowEx;
import org.rstudio.core.client.files.FileSystemItem;
import org.rstudio.core.client.theme.res.ThemeStyles;
import org.rstudio.core.client.widget.AnchorableFrame;
import org.rstudio.core.client.widget.CanFocus;
import org.rstudio.core.client.widget.FindTextBox;
import org.rstudio.core.client.widget.MessageDialog;
import org.rstudio.core.client.widget.Operation;
import org.rstudio.core.client.widget.SatelliteFramePanel;
import org.rstudio.core.client.widget.Toolbar;
import org.rstudio.core.client.widget.ToolbarButton;
import org.rstudio.core.client.widget.ToolbarLabel;
import org.rstudio.studio.client.RStudioGinjector;
import org.rstudio.studio.client.rmarkdown.model.RMarkdownServerOperations;
import org.rstudio.studio.client.rmarkdown.model.RmdPreviewParams;
import org.rstudio.studio.client.shiny.ShinyApps;
import org.rstudio.studio.client.shiny.ShinyFrameHelper;
import org.rstudio.studio.client.shiny.events.ShinyAppsActionEvent;
import org.rstudio.studio.client.workbench.commands.Commands;
import org.rstudio.studio.client.application.events.EventBus;
import org.rstudio.studio.client.common.filetypes.FileTypeRegistry;
import org.rstudio.studio.client.common.presentation.SlideNavigationMenu;
import org.rstudio.studio.client.common.presentation.SlideNavigationToolbarMenu;
import org.rstudio.studio.client.common.presentation.events.SlideIndexChangedEvent;
import org.rstudio.studio.client.common.presentation.events.SlideNavigationChangedEvent;
import org.rstudio.studio.client.common.presentation.events.SlideNavigationChangedEvent.Handler;
import org.rstudio.studio.client.common.presentation.model.SlideNavigation;
import org.rstudio.studio.client.common.satellite.Satellite;

public class RmdOutputPanel extends SatelliteFramePanel<AnchorableFrame>
                            implements RmdOutputPresenter.Display
{
   @Inject
   public RmdOutputPanel(Commands commands,
                         FileTypeRegistry fileTypeRegistry,
                         RMarkdownServerOperations server,
                         EventBus events,
                         ShinyApps shinyApps,
                         Satellite satellite)
   {
      super(commands);
      fileTypeRegistry_ = fileTypeRegistry;
      server_ = server;
      shinyFrame_ = new ShinyFrameHelper();
      events_ = events;
     
      // if this window is a satellite, ensure that the shinyapps instance
      // is initialized
      shinyApps.ensureSessionInit();
   }
  
   @Override
   public void showOutput(RmdPreviewParams params, boolean enablePublish,
                          boolean enableDeploy, boolean refresh)
   {
      // remember output parameters
      outputParms_ = params;

      // remember target file (for invoking editor)
      targetFile_ = FileSystemItem.createFile(params.getTargetFile());
     
      // slide navigation (may be null)
      slideNavigation_ = params.getResult().getSlideNavigation();
      handlerManager_.fireEvent(new SlideNavigationChangedEvent(slideNavigation_));
      slideChangeMonitor_.cancel();
     
      // file label
      if (params.isShinyDocument())
      {
         fileLabel_.setVisible(false);
         fileLabelSeparator_.setVisible(false);
         shinyUrl_ = StringUtil.makeAbsoluteUrl(params.getOutputUrl());
         isShiny_ = true;
      }
      else
      {
         fileLabel_.setVisible(true);
         fileLabelSeparator_.setVisible(true);
         fileLabel_.setText(FileSystemItem.createFile(
                                          params.getOutputFile()).getName());
         isShiny_ = false;
      }
     
      // RPubs
      boolean showPublish = enablePublish &&
                            params.getResult().isHtml() &&
                            params.getResult().getFormat() != null &&
                            params.getResult().getFormat().isSelfContained();
      publishButton_.setText(params.getResult().getRpubsPublished() ?
            "Republish" : "Publish");
      publishButton_.setVisible(showPublish);
      publishButtonSeparator_.setVisible(showPublish);
     
      // ShinyApps
      boolean showDeploy = enableDeploy && params.isShinyDocument();
      deployButton_.setVisible(showDeploy);
      deployButton_.setText("Publish");
      deployButtonSeparator_.setVisible(showDeploy);
     
      // find text box
      boolean showFind = params.getResult().isHtml() &&
                         !params.getResult().isHtmlPresentation();
      findTextBox_.setVisible(showFind);
      findSeparator_.setVisible(showFind);
     
      // when refreshing, reapply the current scroll position and anchor
      scrollPosition_ = refresh ?
            getScrollPosition() : params.getScrollPosition();
    
      // get the URL to load
      String url = getDocumentUrl();

      // check for an anchor implied by a preview_slide field
      String anchor = "";
      if (params.getResult().getPreviewSlide() > 0)
         anchor = String.valueOf(params.getResult().getPreviewSlide());
           
      // check for an explicit anchor if there wasn't one implied
      // by the preview_slide
      if (anchor.length() == 0)
         anchor = params.getAnchor();
     
      // add the anchor if necessary
      if (anchor.length() > 0)
         url += "#" + anchor;
     
      showUrl(url);
   }
  
   @Override
   protected void initToolbar (Toolbar toolbar, Commands commands)
   {
      slideNavigationMenu_ = new SlideNavigationToolbarMenu(toolbar,
                                                            400,
                                                            100,
                                                            true);
     
      fileLabel_ = new ToolbarLabel();
      fileLabel_.addStyleName(ThemeStyles.INSTANCE.subtitle());
      fileLabel_.getElement().getStyle().setMarginRight(7, Unit.PX);
      toolbar.addLeftWidget(fileLabel_);
      fileLabelSeparator_ = toolbar.addLeftSeparator();
      ToolbarButton popoutButton =
            commands.viewerPopout().createToolbarButton();
      popoutButton.setText("Open in Browser");
      toolbar.addLeftWidget(popoutButton);
      publishButtonSeparator_ = toolbar.addLeftSeparator();
      publishButton_ = commands.publishHTML().createToolbarButton(false);
      toolbar.addLeftWidget(publishButton_);

      deployButtonSeparator_ = toolbar.addLeftSeparator();
      deployButton_ = new ToolbarButton("Publish",
            commands.shinyAppsDeploy().getImageResource(),
            new ClickHandler()
      {
         @Override
         public void onClick(ClickEvent evt)
         {
            events_.fireEvent(new ShinyAppsActionEvent(
                  ShinyAppsActionEvent.ACTION_TYPE_DEPLOY,
                  targetFile_.getPath()));
         }
      });
      toolbar.addLeftWidget(deployButton_);

      findTextBox_ = new FindTextBox("Find");
      findTextBox_.setIconVisible(true);
      findTextBox_.setOverrideWidth(120);
      findTextBox_.getElement().getStyle().setMarginRight(6, Unit.PX);
      toolbar.addRightWidget(findTextBox_);
     
      findTextBox_.addKeyDownHandler(new KeyDownHandler() {
         @Override
         public void onKeyDown(KeyDownEvent event)
         {
            // enter key triggers a find
            if (event.getNativeKeyCode() == KeyCodes.KEY_ENTER)
            {
               event.preventDefault();
               event.stopPropagation();
               findInTopic(findTextBox_.getValue().trim(), findTextBox_);
               findTextBox_.focus();
            }
            else if (event.getNativeKeyCode() == KeyCodes.KEY_ESCAPE)
            {
               findTextBox_.setValue("");
            }      
         }
        
         private void findInTopic(String term, CanFocus findInputSource)
         {
            // get content window
            WindowEx contentWindow = getFrame().getWindow();
            if (contentWindow == null)
               return;
               
            if (!contentWindow.find(term, false, false, true, false))
            {
               RStudioGinjector.INSTANCE.getGlobalDisplay().showMessage(
                     MessageDialog.INFO,
                     "Find in Page",
                     "No occurences found",
                     findInputSource);
            }    
         }
        
      });
      toolbar.addRightWidget(findTextBox_);
      findSeparator_ = toolbar.addRightSeparator();
     
      toolbar.addRightWidget(commands.viewerRefresh().createToolbarButton());
   }
  
   @Override
   protected AnchorableFrame createFrame(String url)
   {
      AnchorableFrame frame = new AnchorableFrame();
     
      // allow full screen
      Element el = frame.getElement();
      el.setAttribute("webkitallowfullscreen", "");
      el.setAttribute("mozallowfullscreen", "");
      el.setAttribute("allowfullscreen", "");
     
      frame.navigate(url);
     
      final Operation initSlides = new Operation()
      {
         @Override
         public void execute()
         {
            if (getNavigationMenu().isVisible())
            { 
               fireSlideIndexChanged();
               slideChangeMonitor_.scheduleRepeating(100);
            }
         }
      };

      if (isShiny_)
      {
         shinyFrame_.initialize(url, new Operation()
         {
            @Override
            public void execute()
            {
               shinyFrame_.setScrollPosition(scrollPosition_);
               initSlides.execute();
            }
         });
      }
      else
      {
         // poll for document availability then perform initialization
         // tasks once it's available (addLoadHandler wasn't always
         // getting called at least under Cocoa WebKit)
         Scheduler.get().scheduleFixedDelay(new RepeatingCommand() {

            @Override
            public boolean execute()
            {
               // see if the document is ready
               AnchorableFrame frame = getFrame();
               if (frame == null)
                  return true;
              
               IFrameElementEx iframe = frame.getIFrame();
               if (iframe == null)
                  return true;
              
               Document doc = iframe.getContentDocument();
               if (doc == null)
                  return true;

               initSlides.execute();
              
               // Even though the document exists, it may not have rendered all
               // its content yet
               ScrollUtil.setScrollPositionOnLoad(frame, scrollPosition_);

               return false;
            }
           
         }, 50);
      }
      
      return frame;
   }
  
   private Timer slideChangeMonitor_ = new Timer() {

      @Override
      public void run()
      {
         String url = getCurrentUrl();
         if (!url.equals(lastUrl_))
         {
            lastUrl_ = url;
            fireSlideIndexChanged();
         }
      }
     
      private String lastUrl_ = null
   };
  
   @Override
   public void refresh()
   {
      // cache the scroll position, so we can re-apply it when the page loads
      scrollPosition_ = getScrollPosition();
     
      // recompute the URL (we can't pick up the URL from the document since
      // it may have encoding problems--see case 3994)
      String url = getDocumentUrl();
      String anchor = getAnchor();
      if (anchor.length() > 0)
         url += "#" + anchor;
     
      // re-initialize the shiny frame with the URL (so it waits for the new
      // window object to become available after refresh)
      if (isShiny_)
         shinyFrame_.initialize(url, null);

      showUrl(url);
   }

   @Override
   public int getScrollPosition()
   {
      // if we're asking for this at document creation/destruction time
      // it can sometimes have a null ref somewhere in the call chain,
      // in this case return 0
      try
      {
         if (isShiny_)
            return shinyFrame_.getScrollPosition();
         else
            return getFrame().getIFrame().getContentDocument().getScrollTop();
      }
      catch(Exception ex)
      {
         return 0;
      }
   }
  
   @Override
   public String getTitle()
   {
      return title_;
   }
  
   @Override
   public String getAnchor()
   {
      String url = getCurrentUrl();
      int anchorPos = url.lastIndexOf("#");
      return anchorPos > 0 ? url.substring(anchorPos + 1) : "";
   }
  
   @Override
   public void navigate(int index)
   {
      getFrame().getIFrame().focus();
      String hash = getSlideAnchor(index + 1);

      if (isShiny_)
         shinyFrame_.setHash(hash);
      else
         showUrl(getDocumentUrl() + "#" + hash);
   }

   @Override
   public void editCurrentSlide()
   {
      if (targetFile_ != null && slideNavigation_ != null)
      {
         // determine what slide we are on
         int index = getCurrentSlideIndex();
        
         // get the line of code associated with it
         int line = slideNavigation_.getItems().get(index).getLine();
        
         // invoke the editor
         fileTypeRegistry_.editFile(targetFile_, FilePosition.create(line, 1));
      }
   }
  
   @Override
   public SlideNavigationMenu getNavigationMenu()
   {
      return slideNavigationMenu_;
   }

   @Override
   public HandlerRegistration addSlideNavigationChangedHandler(Handler handler)
   {
      return handlerManager_.addHandler(SlideNavigationChangedEvent.TYPE,
                                        handler);
   }
  
   @Override
   public HandlerRegistration addSlideIndexChangedHandler(
                                 SlideIndexChangedEvent.Handler handler)
   {
      return handlerManager_.addHandler(SlideIndexChangedEvent.TYPE, handler);
   }
  
   // the current URL is the one currently showing in the frame, which may
   // reflect navigation occurring after initial load (e.g. anchor changes)
   private String getCurrentUrl()
   {
      String url = "";
      // guard against exceptions (can occur if the content document isn't
      // fully created yet or we don't have access to it)
      try
      {
         if (isShiny_)
            url = shinyFrame_.getUrl();
         else
            url = getFrame().getIFrame().getContentDocument().getURL();
      }
      catch (Exception ex)
      {
      }
      return url;
   }
 
   private void fireSlideIndexChanged()
   {
      handlerManager_.fireEvent(new SlideIndexChangedEvent(
                                                   getCurrentSlideIndex()));
   }
  
   private int getCurrentSlideIndex()
   {
      try
      {
         String anchor = getAnchor();
         if (anchor.length() == 0)
            anchor = "1";
        
         // remove parens if necessary
         anchor = anchor.replaceAll("[()]","");
        
         // return index
         return Integer.parseInt(anchor) - 1;
      }
      catch(NumberFormatException e)
      {
         return 0;
      }
   }
  
   private String getDocumentUrl()
   {
      return isShiny_ ?
            shinyUrl_ :
            server_.getApplicationURL(outputParms_.getOutputUrl());
   }
  
   private String getSlideAnchor(int index)
   {
      if (slideNavigation_.getUseAnchorParens())
         return "(" + index + ")";
      else
         return "" + index;
   }
  
   private SlideNavigationToolbarMenu slideNavigationMenu_;

   private Label fileLabel_;
   private Widget fileLabelSeparator_;
   private ToolbarButton publishButton_;
   private Widget publishButtonSeparator_;
   private ToolbarButton deployButton_;
   private Widget deployButtonSeparator_;
   private String title_;
  
   private final FileTypeRegistry fileTypeRegistry_;
   private final RMarkdownServerOperations server_;
   private final EventBus events_;

   private int scrollPosition_ = 0;
  
   private FileSystemItem targetFile_ = null;
   private SlideNavigation slideNavigation_ = null;
   private RmdPreviewParams outputParms_ = null;
  
   private FindTextBox findTextBox_;
   private Widget findSeparator_;

   private boolean isShiny_;
   private String shinyUrl_;
   private ShinyFrameHelper shinyFrame_;
  
   private HandlerManager handlerManager_ = new HandlerManager(this);
}
TOP

Related Classes of org.rstudio.studio.client.rmarkdown.ui.RmdOutputPanel

TOP
Copyright © 2018 www.massapi.com. All rights reserved.
All source code are property of their respective owners. Java is a trademark of Sun Microsystems, Inc and owned by ORACLE Inc. Contact coftware#gmail.com.