Package org.rstudio.studio.client.workbench.views.help

Source Code of org.rstudio.studio.client.workbench.views.help.HelpPane

/*
* HelpPane.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.help;

import com.google.gwt.core.client.Scheduler;
import com.google.gwt.core.client.Scheduler.RepeatingCommand;
import com.google.gwt.core.client.Scheduler.ScheduledCommand;
import com.google.gwt.dom.client.*;
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.dom.client.KeyUpEvent;
import com.google.gwt.event.dom.client.KeyUpHandler;
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.http.client.URL;
import com.google.gwt.user.client.Window;
import com.google.gwt.user.client.ui.*;
import com.google.inject.Inject;
import com.google.inject.Provider;

import org.rstudio.core.client.BrowseCap;
import org.rstudio.core.client.ElementIds;
import org.rstudio.core.client.StringUtil;
import org.rstudio.core.client.command.KeyboardShortcut;
import org.rstudio.core.client.command.ShortcutManager;
import org.rstudio.core.client.dom.ElementEx;
import org.rstudio.core.client.dom.IFrameElementEx;
import org.rstudio.core.client.dom.WindowEx;
import org.rstudio.core.client.events.NativeKeyDownEvent;
import org.rstudio.core.client.files.FileSystemItem;
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.RStudioFrame;
import org.rstudio.core.client.widget.SecondaryToolbar;
import org.rstudio.core.client.widget.SmallButton;
import org.rstudio.core.client.widget.Toolbar;
import org.rstudio.studio.client.common.AutoGlassPanel;
import org.rstudio.studio.client.common.GlobalDisplay;
import org.rstudio.studio.client.workbench.commands.Commands;
import org.rstudio.studio.client.workbench.ui.WorkbenchPane;
import org.rstudio.studio.client.workbench.views.help.Help.LinkMenu;
import org.rstudio.studio.client.workbench.views.help.events.HelpNavigateEvent;
import org.rstudio.studio.client.workbench.views.help.events.HelpNavigateHandler;
import org.rstudio.studio.client.workbench.views.help.model.VirtualHistory;
import org.rstudio.studio.client.workbench.views.help.search.HelpSearch;

public class HelpPane extends WorkbenchPane
                      implements Help.Display
{
   @Inject
   public HelpPane(Provider<HelpSearch> searchProvider,
                   GlobalDisplay globalDisplay,
                   Commands commands)
   {
      super("Help") ;
     
      searchProvider_ = searchProvider ;
      globalDisplay_ = globalDisplay;
      commands_ = commands;
   
      MenuItem clear = commands.clearHelpHistory().createMenuItem(false);
      history_ = new ToolbarLinkMenu(12, true, null, new MenuItem[] { clear }) ;

      Window.addResizeHandler(new ResizeHandler()
      {
         public void onResize(ResizeEvent event)
         {
            history_.getMenu().hide();
         }
      });

      ensureWidget();
   }

   @Override
   protected Widget createMainWidget()
   {
      frame_ = new RStudioFrame() ;
      frame_.setSize("100%", "100%");
      frame_.setStylePrimaryName("rstudio-HelpFrame") ;
      ElementIds.assignElementId(frame_.getElement(), ElementIds.HELP_FRAME);

      return new AutoGlassPanel(frame_);
   }

   @Override
   public void onResize()
   {
      manageTitleLabelMaxSize();

      super.onResize();
   }

   private void manageTitleLabelMaxSize()
   {
      if (title_ != null)
      {
         int offsetWidth = getOffsetWidth();
         if (offsetWidth > 0)
         {
            int newWidth = offsetWidth - 25;
            if (newWidth > 0)
               title_.getElement().getStyle().setPropertyPx("maxWidth", newWidth);
         }
      }
   }

   @Override
   protected void onLoad()
   {
      super.onLoad() ;

      if (!initialized_)
      {
         initialized_ = true;

         initHelpCallbacks() ;

         Scheduler.get().scheduleDeferred(new ScheduledCommand()
         {
            public void execute()
            {
               manageTitleLabelMaxSize();
            }
         });
      }
   }
  
   public final native void initHelpCallbacks() /*-{
      function addEventHandler(subject, eventName, handler) {
         if (subject.addEventListener) {
            subject.addEventListener(eventName, handler, false);
         }
         else {
            subject.attachEvent(eventName, handler);
         }
      }

      var thiz = this ;
      $wnd.helpNavigated = function(document, win) {
         thiz.@org.rstudio.studio.client.workbench.views.help.HelpPane::helpNavigated(Lcom/google/gwt/dom/client/Document;)(document);
         addEventHandler(win, "unload", function () {
            thiz.@org.rstudio.studio.client.workbench.views.help.HelpPane::unload()();
         });
      } ;
      $wnd.helpNavigate = function(url) {
         thiz.@org.rstudio.studio.client.workbench.views.help.HelpPane::showHelp(Ljava/lang/String;)(url);
      } ;
     
      $wnd.helpKeydown = function(e) {
         thiz.@org.rstudio.studio.client.workbench.views.help.HelpPane::handleKeyDown(Lcom/google/gwt/dom/client/NativeEvent;)(e);
      } ;
   }-*/;
  
  
  
   // delegate shortcuts which occur while Help has focus
   
   private void handleKeyDown(NativeEvent e)
   {
      // determine whether this key-combination means we should focus find
      int mod = KeyboardShortcut.getModifierValue(e);
      if ((mod == (BrowseCap.hasMetaKey() ? KeyboardShortcut.META
                                          : KeyboardShortcut.CTRL)) &&
           e.getKeyCode() == 'F')
      {
         e.preventDefault();
         e.stopPropagation();
         WindowEx.get().focus();
         findTextBox_.focus();
         findTextBox_.selectAll();
      }
     
      // delegate to the shortcut manager
      else
      {
         NativeKeyDownEvent evt = new NativeKeyDownEvent(e);
         ShortcutManager.INSTANCE.onKeyDown(evt);
         if (evt.isCanceled())
         {
            e.preventDefault();
            e.stopPropagation();
           
            // since this is a shortcut handled by the main window
            // we set focus to it
            WindowEx.get().focus();
         }
        
      }
   }
  
   private void helpNavigated(Document doc)
   {
      NodeList<Element> elements = doc.getElementsByTagName("a") ;
      for (int i = 0; i < elements.getLength(); i++)
      {
         ElementEx a = (ElementEx) elements.getItem(i) ;
         String href = a.getAttribute("href", 2) ;
         if (href == null)
            continue ;

         if (href.contains(":") || href.endsWith(".pdf"))
         {
            // external links
            AnchorElement aElement = a.cast();
            aElement.setTarget("_blank") ;
         }
         else
         {
            // Internal links need to be handled in JavaScript so that
            // they can participate in virtual session history. This
            // won't have any effect for right-click > Show in New Window
            // but that's a good thing.
            a.setAttribute("onclick",
                           "window.parent.helpNavigate(this.href);return false") ;
         }
      }
     
      String effectiveTitle = getDocTitle(doc);
      title_.setText(effectiveTitle) ;
      this.fireEvent(new HelpNavigateEvent(doc.getURL(), effectiveTitle)) ;
   }
  
   private String getDocTitle(Document doc)
   {
      String docUrl = StringUtil.notNull(doc.getURL());
      String docTitle = doc.getTitle();
     
      String previewPrefix = new String("/help/preview?file=");
      int previewLoc = docUrl.indexOf(previewPrefix);
      if (previewLoc != -1)
      {
         String file = docUrl.substring(previewLoc + previewPrefix.length());
         file = URL.decodeQueryString(file);
         FileSystemItem fsi = FileSystemItem.createFile(file);
         docTitle = fsi.getName();
      }
      else if (StringUtil.isNullOrEmpty(docTitle))
      {
         String url = new String(docUrl);
         url = url.split("\\?")[0];
         url = url.split("#")[0];
         String[] chunks = url.split("/");
         docTitle = chunks[chunks.length - 1];
      }
     
      return docTitle;
   }

   private void unload()
   {
      title_.setText("") ;
   }

   @Override
   protected Toolbar createMainToolbar()
   {
      Toolbar toolbar = new Toolbar();

      toolbar.addLeftWidget(commands_.helpBack().createToolbarButton());
      toolbar.addLeftWidget(commands_.helpForward().createToolbarButton());
      toolbar.addLeftWidget(commands_.helpHome().createToolbarButton());
      toolbar.addLeftSeparator();
      toolbar.addLeftWidget(commands_.printHelp().createToolbarButton());
      toolbar.addLeftWidget(commands_.helpPopout().createToolbarButton());
     
      toolbar.addLeftSeparator();
      toolbar.addLeftWidget(commands_.refreshHelp().createToolbarButton());
     
      toolbar.addRightWidget(searchProvider_.get().getSearchWidget());

      return toolbar;
   }
  
   @Override
   protected SecondaryToolbar createSecondaryToolbar()
   {
      SecondaryToolbar toolbar = new SecondaryToolbar() ;
      toolbar.addLeftPopupMenu(title_ = new Label(), history_.getMenu());
     
      if (isFindSupported())
      {
         final SmallButton btnNext = new SmallButton("&gt;", true);
         btnNext.setTitle("Find next (Enter)");
         btnNext.setVisible(false);
         btnNext.addClickHandler(new ClickHandler() {
            @Override
            public void onClick(ClickEvent event)
            {
               findNext();
           
         });
        
         final SmallButton btnPrev = new SmallButton("&lt;", true);
         btnPrev.setTitle("Find previous");
         btnPrev.setVisible(false);
         btnPrev.addClickHandler(new ClickHandler() {
            @Override
            public void onClick(ClickEvent event)
            {
               findPrev();
           
         });
        
        
         findTextBox_ = new FindTextBox("Find in Topic");
         findTextBox_.setOverrideWidth(90);
         toolbar.addLeftWidget(findTextBox_);
         findTextBox_.addKeyUpHandler(new KeyUpHandler() {
           
            @Override
            public void onKeyUp(KeyUpEvent event)
            {    
               WindowEx contentWindow = getContentWindow();
               if (contentWindow != null)
               {
                  // escape or tab means exit find mode and put focus
                  // into the main content window
                  if (event.getNativeKeyCode() == KeyCodes.KEY_ESCAPE ||
                      event.getNativeKeyCode() == KeyCodes.KEY_TAB)
                  {
                     event.preventDefault();
                     event.stopPropagation();
                     if (event.getNativeKeyCode() == KeyCodes.KEY_ESCAPE)
                        clearTerm();
                     contentWindow.focus();
                  }
                  else
                  {
                     // prevent two enter keys in rapid succession from
                     // minimizing or maximizing the help pane
                     if (event.getNativeKeyCode() == KeyCodes.KEY_ENTER)
                     {
                        event.preventDefault();
                        event.stopPropagation();
                     }
                     
                     // check for term
                     String term = findTextBox_.getValue().trim();
                    
                     // if there is a term then search for it
                     if (term.length() > 0)
                     {
                        // make buttons visible
                        setButtonVisibility(true);
                       
                        // perform the find (check for incremental)
                        if (isIncrementalFindSupported())
                        {
                           boolean incremental =
                            !event.isAnyModifierKeyDown() &&
                            (event.getNativeKeyCode() != KeyCodes.KEY_ENTER);  
                          
                           performFind(term, true, incremental);
                        }
                        else
                        {
                           if (event.getNativeKeyCode() == KeyCodes.KEY_ENTER)
                              performFind(term, true, false);
                        }
                     }
                    
                     // no term means clear term and remove selection
                     else
                     {
                        if (isIncrementalFindSupported())
                        {
                           clearTerm();
                           contentWindow.removeSelection();
                        }
                     }
                  }
               }
            }
           
            private void clearTerm()
            {
               findTextBox_.setValue("");
               setButtonVisibility(false);
            }
           
            private void setButtonVisibility(final boolean visible)
            {
               Scheduler.get().scheduleDeferred(new ScheduledCommand() {
                  @Override
                  public void execute()
                  {
                     btnNext.setVisible(visible);
                     btnPrev.setVisible(visible);
                  }
               });
            }
         });
      
         findTextBox_.addKeyDownHandler(new KeyDownHandler() {

            @Override
            public void onKeyDown(KeyDownEvent event)
            {
               // we handle these directly so prevent the browser
               // from handling them
               if (event.getNativeKeyCode() == KeyCodes.KEY_ESCAPE ||
                   event.getNativeKeyCode() == KeyCodes.KEY_TAB ||
                   event.getNativeKeyCode() == KeyCodes.KEY_ENTER)
               {
                  event.preventDefault();
                  event.stopPropagation();
               }
            }
           
         });
        
         if (isIncrementalFindSupported())
         {
            btnPrev.getElement().getStyle().setMarginRight(3, Unit.PX);
            toolbar.addLeftWidget(btnPrev);
            toolbar.addLeftWidget(btnNext);
         }
      
      }

      return toolbar ;
   }
  
   private String getTerm()
   {
      return findTextBox_.getValue().trim();
   }
  
   private void findNext()
   {
      String term = getTerm();
      if (term.length() > 0)
         performFind(term, true, false);
   }
  
   private void findPrev()
   {
      String term = getTerm();
      if (term.length() > 0)
         performFind(term, false, false);
   }
  
   private void performFind(String term,
                            boolean forwards,
                            boolean incremental)
   {
      WindowEx contentWindow = getContentWindow();
      if (contentWindow == null)
         return;
     
      // if this is an incremental search then reset the selection first
      if (incremental)
         contentWindow.removeSelection();
     
      contentWindow.find(term, false, !forwards, true, false);
   }
  
   private boolean isFindSupported()
   {
      return BrowseCap.INSTANCE.hasWindowFind();
   }
  
   // Firefox changes focus during our typeahead search (it must take
   // focus when you set the selection into the iframe) which breaks
   // typeahead entirely. rather than code around this we simply
   // disable it for Firefox
   private boolean isIncrementalFindSupported()
   {
      return isFindSupported() && !BrowseCap.isFirefox();
   }

   public String getUrl()
   {
      if (getIFrameEx() != null)
         return getIFrameEx().getContentWindow().getLocationHref() ;
      else
         return null;
   }
  
   public String getDocTitle()
   {
      return getIFrameEx().getContentDocument().getTitle() ;
   }

   public void showHelp(String url)
   {
      ensureWidget();
      bringToFront();
      navStack_.navigate(url) ;
      setLocation(url);
      navigated_ = true;
   }
    
   private void setLocation(final String url)
   {
      // allow subsequent calls to setLocation to override any previous
      // call (necessary so two consecutive calls like we get during
      // some startup scenarios don't result in the first url displaying
      // rather than the second)
      targetUrl_ = url;
     
      RepeatingCommand navigateCommand = new RepeatingCommand() {
         @Override
         public boolean execute()
         {
            if (getIFrameEx() != null &&
                  getIFrameEx().getContentWindow() != null)
            {
               if (targetUrl_.equals(getUrl()))
               {
                  getIFrameEx().getContentWindow().reload();
               }
               else
               {
                  getIFrameEx().getContentWindow().replaceLocationHref(targetUrl_);
                  frame_.setUrl(targetUrl_);
               }
               return false;
            }
            else
            {
               return true;
            }
         }
      };

      if (navigateCommand.execute())
         Scheduler.get().scheduleFixedDelay(navigateCommand, 100);     
   }
  
   public void refresh()
   {
      String url = getUrl();
      if (url != null)
         setLocation(url);
   }

   private WindowEx getContentWindow()
   {
      return getIFrameEx() != null ? getIFrameEx().getContentWindow() : null ;
   }

   public void back()
   {
      String backUrl = navStack_.back() ;
      if (backUrl != null)
         setLocation(backUrl) ;
   }

   public void forward()
   {
      String fwdUrl = navStack_.forward() ;
      if (fwdUrl != null)
         setLocation(fwdUrl) ;
   }

   public void print()
   {
      getContentWindow().focus() ;
      getContentWindow().print() ;
   }
  
   public void popout()
   {
      String href = getContentWindow().getLocationHref() ;    
      globalDisplay_.openWindow(href);
   }
  
   @Override
   public void focus()
   {
      WindowEx contentWindow = getContentWindow();
      if (contentWindow != null)
         contentWindow.focus();
   }
  
   public HandlerRegistration addHelpNavigateHandler(HelpNavigateHandler handler)
   {
      return addHandler(handler, HelpNavigateEvent.TYPE) ;
   }
  
   public LinkMenu getHistory()
   {
      return history_ ;
   }

   public boolean navigated()
   {
      return navigated_;
   }

   private IFrameElementEx getIFrameEx()
   {
      return frame_.getElement().cast();
   }
  
   private void findInTopic(String term, CanFocus findInputSource)
   {
      // get content window
      WindowEx contentWindow = getContentWindow();
      if (contentWindow == null)
         return;
         
      if (!contentWindow.find(term, false, false, true, false))
      {
         globalDisplay_.showMessage(MessageDialog.INFO,
               "Find in Topic",
               "No occurences found",
               findInputSource);
      }    
   }


   private final VirtualHistory navStack_ = new VirtualHistory() ;
   private final ToolbarLinkMenu history_ ;
   private Label title_ ;
   private RStudioFrame frame_ ;
   private FindTextBox findTextBox_;
   private final Provider<HelpSearch> searchProvider_ ;
   private GlobalDisplay globalDisplay_;
   private final Commands commands_;
   private boolean navigated_;
   private boolean initialized_;
   private String targetUrl_;
}
TOP

Related Classes of org.rstudio.studio.client.workbench.views.help.HelpPane

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.