All Downloads are FREE. Search and download functionalities are using the official Maven repository.

com.intellij.help.impl.IdeaHelpBroker Maven / Gradle / Ivy

/*
 * Copyright 2000-2013 JetBrains s.r.o.
 *
 * 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.intellij.help.impl;

import com.intellij.openapi.wm.IdeFocusManager;
import com.intellij.ui.AppUIUtil;
import com.intellij.ui.ScreenUtil;
import org.jetbrains.annotations.NotNull;

import javax.help.*;
import javax.help.Map.ID;
import javax.help.UnsupportedOperationException;
import javax.swing.*;
import java.awt.*;
import java.awt.event.*;
import java.lang.reflect.Method;
import java.net.URL;
import java.util.Enumeration;
import java.util.Locale;

/**
 * It a dirty patch! Help system is so ugly that it hangs when it open some "external" links.
 * To prevent this we open "external" links in nornal WEB browser.
 * This is copy-pasted version of DefaultHelpBroker class.
 *
 * @author Vladimir Kondratyev
 */
class IdeaHelpBroker extends DefaultHelpBroker implements KeyListener{

  private HelpSet myHelpSet=null;
  private JFrame myFrame=null;
  private JHelp jhelp=null;
  private Locale myLocale=null;
  private Font myFont=null;
  /**
   * The container for modally activated help
   * @since 1.1
   */
  private JDialog myDialog=null;
  /**
   * The modal Window that activated help
   * @since 1.1
   */
  private Window myOwnerWindow=null;
  /**
   * The flag for modally activated help. If true, help was activated from
   * a modal dialog. Can not be set to true for V1.1.
   * @since 1.1
   */
  private boolean myModallyActivated=false;

  /**
   * Constructor
   */

  public IdeaHelpBroker(HelpSet hs){
    setHelpSet(hs);
  }

  /**
   * Returns the default HelpSet
   */
  public HelpSet getHelpSet(){
    return myHelpSet;
  }

  /**
   * Changes the HelpSet for this broker.
   * @param hs The HelpSet to set for this broker.
   * A null hs is valid parameter.
   */
  public void setHelpSet(HelpSet hs){
    // If we already have a jhelp check if the HelpSet has changed.
    // If so change the model on the jhelp viewer.
    // This could be made smarter to cache the helpmodels per HelpSet
    if(hs!=null&&myHelpSet!=hs){
      if(jhelp!=null){
        TextHelpModel model=new DefaultHelpModel(hs);
        jhelp.setModel(model);
      }
      myHelpSet=hs;

    }
  }


  /**
   * Gets the locale of this component.
   * @return This component's locale. If this component does not
   * have a locale, the defaultLocale is returned.
   * @see #setLocale
   */
  public Locale getLocale(){
    if(myLocale==null){
      return Locale.getDefault();
    }
    return myLocale;
  }

  /**
   * Sets the locale of this HelpBroker. The locale is propagated to
   * the presentation.
   * @param l The locale to become this component's locale. A null locale
   * is the same as the defaultLocale.
   * @see #getLocale
   */
  public void setLocale(Locale l){
    myLocale=l;
    if(jhelp!=null){
      jhelp.setLocale(myLocale);
    }
  }

  /**
   * Gets the font for this HelpBroker.
   */
  public Font getFont(){
    createHelpWindow();

    if(myFont==null){
      return jhelp.getFont();
    }
    return myFont;
  }

  /**
   * Sets the font for this this HelpBroker.
   * @param f The font.
   */
  public void setFont(Font f){
    myFont=f;
    if(jhelp!=null){
      jhelp.setFont(myFont);
    }
  }

  /**
   * Set the currentView to the navigator with the same
   * name as the name parameter.
   *
   * @param name The name of the navigator to set as the
   * current view. If nav is null or not a valid Navigator
   * in this HelpBroker then an
   * IllegalArgumentException is thrown.
   * @throws IllegalArgumentException if nav is null or not a valid Navigator.
   */
  public void setCurrentView(String name){
    createHelpWindow();
    JHelpNavigator nav=null;

    for(Enumeration e=jhelp.getHelpNavigators();
        e.hasMoreElements();){
      nav=(JHelpNavigator)e.nextElement();
      if(nav.getNavigatorName().equals(name)){
        break;
      }
      nav=null;
    }

    if(nav==null){
      throw new IllegalArgumentException("Invalid view name");
    }
    jhelp.setCurrentNavigator(nav);
  }

  /**
   * Determines the current navigator.
   */
  public String getCurrentView(){
    createHelpWindow();
    return jhelp.getCurrentNavigator().getNavigatorName();
  }


  /**
   * Initializes the presentation.
   * This method allows the presentation to be initialized but not displayed.
   * Typically this would be done in a separate thread to reduce the
   * intialization time.
   */
  public void initPresentation(){
    createHelpWindow();
  }

  /**
   * Displays the presentation to the user.
   */
  public void setDisplayed(boolean visible){
    createHelpWindow();
    if(myModallyActivated){
      myDialog.setVisible(visible);
      if(visible){
        myDialog.setLocationRelativeTo(myDialog.getOwner());
      }
    } else{
      //myFrame.setLocationRelativeTo(null);
      myFrame.setVisible(visible);
      myFrame.setState(JFrame.NORMAL);
      IdeFocusManager focusManager = IdeFocusManager.findInstance();
      JComponent target = focusManager.getFocusTargetFor(myFrame.getRootPane());
      focusManager.requestFocus(target != null ? target : myFrame, true);
    }
  }

  /**
   * Determines if the presentation is displayed.
   */
  public boolean isDisplayed(){
    if(myModallyActivated){
      if(myDialog!=null){
        return myDialog.isShowing();
      } else{
        return false;
      }
    } else{
      if(myFrame!=null){
        if(!myFrame.isVisible()){
          return false;
        } else{
          return myFrame.getState() == JFrame.NORMAL;
        }
      } else{
        return false;
      }
    }
  }

  /**
   * Requests the presentation be located at a given position.
   * This operation may throw an UnsupportedOperationException if the
   * underlying implementation does not allow this.
   */
  public void setLocation(Point p) throws UnsupportedOperationException{
    createHelpWindow();
    if(myModallyActivated){
      myDialog.setLocation(p);
    } else{
      myFrame.setLocation(p);
    }
  }

  /**
   * Requests the location of the presentation.
   * @throws UnsupportedOperationException If the underlying implementation
   * does not allow this.
   * @throws IllegalComponentStateException If the presentation is not
   * displayed.
   * @return Point the location of the presentation.
   */
  public Point getLocation() throws UnsupportedOperationException{
    if(jhelp==null){
      throw new java.awt.IllegalComponentStateException("presentation not displayed");
    }
    if(myModallyActivated){
      if(myDialog!=null){
        return myDialog.getLocation();
      }
    } else{
      if(myFrame!=null){
        return myFrame.getLocation();
      }
    }
    return null;

  }

  /**
   * Requests the presentation be set to a given size.
   * This operation may throw an UnsupportedOperationException if the
   * underlying implementation does not allow this.
   */
  public void setSize(Dimension d) throws UnsupportedOperationException{
    HELP_WIDTH=d.width;
    HELP_HEIGHT=d.height;
    createHelpWindow();
    if(myModallyActivated){
      myDialog.setSize(d);
      myDialog.validate();
    } else{
      myFrame.setSize(d);
      myFrame.validate();
    }
  }

  /**
   * Requests the size of the presentation.
   * @throws UnsupportedOperationException If the underlying implementation
   * does not allow this.
   * @throws IllegalComponentStateException If the presentation is not
   * displayed.
   * @return Point the location of the presentation.
   */
  public Dimension getSize() throws UnsupportedOperationException{
    if(jhelp==null){
      throw new java.awt.IllegalComponentStateException("presentation not displayed");
    }
    if(myModallyActivated){
      if(myDialog!=null){
        return myDialog.getSize();
      }
    } else{
      if(myFrame!=null){
        return myFrame.getSize();
      }
    }
    return null;
  }

  /**
   * Hides/Shows view.
   */
  public void setViewDisplayed(boolean displayed){
    createHelpWindow();
    jhelp.setNavigatorDisplayed(displayed);
  }

  /**
   * Determines if the current view is visible.
   */
  public boolean isViewDisplayed(){
    createHelpWindow();
    return jhelp.isNavigatorDisplayed();
  }

  /**
   * Shows this ID as content relative to the (top) HelpSet for the HelpBroker
   * instance--HelpVisitListeners are notified.
   *
   * @param id A string that identifies the topic to show for the loaded (top) HelpSet
   * @exception BadIDException The ID is not valid for the HelpSet
   */
  public void setCurrentID(String id) throws BadIDException{
    try{
      setCurrentID(ID.create(id,myHelpSet));
    } catch(InvalidHelpSetContextException ex){
      // this should not happen
      new Error("internal error?");
    }
  }

  /**
   * Displays this ID--HelpVisitListeners are notified.
   *
   * @param id a Map.ID indicating the URL to display
   * @exception InvalidHelpSetContextException if the current helpset does not contain
   * id.helpset
   */
  public void setCurrentID(ID id) throws InvalidHelpSetContextException{
    createJHelp();
    jhelp.getModel().setCurrentID(id);
  }

  /**
   * Determines which ID is displayed (if any).
   */
  public ID getCurrentID(){
    if(jhelp!=null){
      return jhelp.getModel().getCurrentID();
    } else{
      return null;
    }
  }

  /**
   * Displays this URL.
   * HelpVisitListeners are notified.
   * The currentID changes if there is a mathing ID for this URL
   * @param url The url to display. A null URL is a valid url.
   */
  public void setCurrentURL(URL url){
    createHelpWindow();

    jhelp.getModel().setCurrentURL(url);
    if(myModallyActivated){
      myDialog.setVisible(true);
      myDialog.setVisible(true);
    } else{
      myFrame.setVisible(true);
    }
  }

  /**
   * Determines which URL is displayed.
   */
  public URL getCurrentURL(){
    return jhelp.getModel().getCurrentURL();
  }


  // Context-Senstive methods
  /**
   * Enables the Help key on a Component. This method works best when
   * the component is the
   * rootPane of a JFrame in Swing implementations, or a java.awt.Window
   * (or subclass thereof) in AWT implementations.
   * This method sets the default
   * helpID and HelpSet for the Component and registers keyboard actions
   * to trap the "Help" keypress. When the "Help" key is pressed, if the
   * object with the current focus has a helpID, the helpID is displayed.
   * otherwise the default helpID is displayed.
   *
   * @param comp the Component to enable the keyboard actions on.
   * @param id the default HelpID to be displayed
   * @param hs the default HelpSet to be displayed. If hs is null the default HelpSet
   * will be assumed.
   */
  public void enableHelpKey(Component comp,@NotNull String id,HelpSet hs){
    CSH.setHelpIDString(comp,id);
    if(hs!=null){
      CSH.setHelpSet(comp,hs);
    }
    if(comp instanceof JComponent){
      JComponent root=(JComponent)comp;
      root.registerKeyboardAction(getDisplayHelpFromFocus(),
        KeyStroke.getKeyStroke(KeyEvent.VK_HELP,0),
        JComponent.WHEN_ANCESTOR_OF_FOCUSED_COMPONENT);
      root.registerKeyboardAction(getDisplayHelpFromFocus(),
        KeyStroke.getKeyStroke(KeyEvent.VK_F1,0),
        JComponent.WHEN_ANCESTOR_OF_FOCUSED_COMPONENT);
    } else{
      comp.addKeyListener(this);
    }
  }

  /**
   * Invoked when a key is typed. This event occurs when a
   * key press is followed by a key release.  Not intended to be overridden or extended.
   */
  public void keyTyped(KeyEvent e){
    //ignore
  }

  /**
   * Invoked when a key is pressed. Not intended to be overridden or extended.
   */
  public void keyPressed(KeyEvent e){
    //ingore
  }


  /**
   * Invoked when a key is released.  Not intended to be overridden or extended.
   */
  public void keyReleased(KeyEvent e){
    // simulate what is done in JComponents registerKeyboardActions.
    int code=e.getKeyCode();
    if(code==KeyEvent.VK_F1||code==KeyEvent.VK_HELP){
      ActionListener al=getDisplayHelpFromFocus();
      al.actionPerformed(new ActionEvent(e.getComponent(),
        ActionEvent.ACTION_PERFORMED,
        null));
    }

  }

  /**
   * Enables help for a Component. This method sets a
   * component's helpID and HelpSet.
   *
   * @param comp the Component to set the id and hs on.
   * @param id the String value of an Map.ID.
   * @param hs the HelpSet the id is in. If hs is null the default HelpSet
   * will be assumed.
   */
  public void enableHelp(Component comp,@NotNull String id,HelpSet hs){
    CSH.setHelpIDString(comp,id);
    if(hs!=null){
      CSH.setHelpSet(comp,hs);
    }
  }

  /**
   * Enables help for a MenuItem. This method sets a
   * component's helpID and HelpSet.
   *
   * @param comp the MenuItem to set the id and hs on.
   * @param id the String value of an Map.ID.
   * @param hs the HelpSet the id is in. If hs is null the default HelpSet
   * will be assumed.
   */
  public void enableHelp(MenuItem comp,@NotNull String id,HelpSet hs){
    CSH.setHelpIDString(comp,id);
    if(hs!=null){
      CSH.setHelpSet(comp,hs);
    }
  }

  /**
   * Enables help for a Component. This method sets a
   * Component's helpID and HelpSet and adds an ActionListener.
   * When an action is performed
   * it displays the Component's helpID and HelpSet in the default viewer.
   *
   * @param comp the Component to set the id and hs on. If the Component is not
   * a javax.swing.AbstractButton or a
   * java.awt.Button an IllegalArgumentException is thrown.
   * @param id the String value of an Map.ID.
   * @param hs the HelpSet the id is in. If hs is null the default HelpSet
   * will be assumed.
   *
   * @see javax.swing.AbstractButton
   * @see java.awt.Button
   * @throws IllegalArgumentException if comp is not a button.
   */
  public void enableHelpOnButton(Component comp,@NotNull String id,HelpSet hs){
    if(!(comp instanceof AbstractButton)&&!(comp instanceof Button)){
      throw new IllegalArgumentException("Invalid Component. comp must be either a javax.swing.AbstractButton or a java.awt.Button");
    }
    CSH.setHelpIDString(comp,id);
    if(hs!=null){
      CSH.setHelpSet(comp,hs);
    }
    if(comp instanceof AbstractButton){
      AbstractButton button=(AbstractButton)comp;
      button.addActionListener(getDisplayHelpFromSource());
    }else{
      Button button=(Button)comp;
      button.addActionListener(getDisplayHelpFromSource());
    }
  }

  /**
   * Enables help for a MenuItem. This method sets a
   * Component's helpID and HelpSet and adds an ActionListener.
   * When an action is performed
   * it displays the Component's helpID and HelpSet in the default viewer.
   *
   * @param comp the MenuItem to set the id and hs on. If comp is null
   * an IllegalAgrumentException is thrown.
   * @param id the String value of an Map.ID.
   * @param hs the HelpSet the id is in. If hs is null the default HelpSet
   * will be assumed.
   * @see java.awt.MenuItem
   * @throws IllegalArgumentException if comp is null.
   */
  public void enableHelpOnButton(@NotNull MenuItem comp,@NotNull String id,HelpSet hs){
    CSH.setHelpIDString(comp,id);
    if(hs!=null){
      CSH.setHelpSet(comp,hs);
    }
    comp.addActionListener(getDisplayHelpFromSource());
  }

  /**
   * Returns the default DisplayHelpFromFocus listener.
   */
  protected ActionListener getDisplayHelpFromFocus(){
    if(displayHelpFromFocus==null){
      displayHelpFromFocus=new CSH.DisplayHelpFromFocus(this);
    }
    return displayHelpFromFocus;
  }

  /**
   * Returns the default DisplayHelpFromSource listener.
   */
  protected ActionListener getDisplayHelpFromSource(){
    if(displayHelpFromSource==null){
      displayHelpFromSource=new CSH.DisplayHelpFromSource(this);
    }
    return displayHelpFromSource;
  }

  /**
   * Set the activation window. If the window is an instance of a
   * Dialog and the is modal, modallyActivated help is set to true and
   * ownerDialog is set to the window. In all other instances
   * modallyActivated is set to false and ownerDialog is set to null.
   * @param window the activating window
   * @since 1.1
   */
  public void setActivationWindow(Window window){
    if (window instanceof Dialog) {
      Dialog tmpDialog = (Dialog)window;
      if (tmpDialog.isModal()) {
        myOwnerWindow = window;
        myModallyActivated = true;
      }
      else {
        myOwnerWindow = null;
        myModallyActivated = false;
      }
    }
    else {
      myOwnerWindow = null;
      myModallyActivated = false;
    }
  }


  /**
   * Private methods.
   */
  private int HELP_WIDTH = (int)(ScreenUtil.getMainScreenBounds().width * 0.8);
  private int HELP_HEIGHT = (int)(ScreenUtil.getMainScreenBounds().height * 0.8);

  private synchronized void createJHelp(){
    if(jhelp==null){
      jhelp=new IdeaJHelp(myHelpSet);
      if(myFont!=null){
        jhelp.setFont(myFont);
      }
      if(myLocale!=null){
        jhelp.setLocale(myLocale);
      }
    }
  }

  private WindowListener dl;
  private boolean modalDeactivated=true;

  private synchronized void createHelpWindow(){
    JDialog tmpDialog=null;
    Dimension size=null;
    Point pos=null;
    boolean resize = false;

    createJHelp();
    // Get the title from the HelpSet
    String helpTitle=myHelpSet.getTitle();

    if(myModallyActivated){
      // replace dialog.getOwner() with the following code
      Window owner=null;
      try{
        Method m=Window.class.getMethod("getOwner",null);

        if(m!=null&&myDialog!=null){
          owner=(Window)m.invoke(myDialog,null);
        }
      } catch(NoSuchMethodError ex){
        // as in JDK1.1
      } catch(NoSuchMethodException ex){
        // as in JDK1.1
      } catch(java.lang.reflect.InvocationTargetException ex){
        //
      } catch(java.lang.IllegalAccessException ex){
        //
      }

      if(myDialog==null||owner!=myOwnerWindow||modalDeactivated){
        if(myFrame!=null){
          pos=myFrame.getLocation();
          size=myFrame.getSize();
          myFrame.dispose();
        }
        if(myDialog!=null){
          pos=myDialog.getLocation();
          size=myDialog.getSize();
          tmpDialog=myDialog;
        }

        myDialog=new JDialog((Dialog)myOwnerWindow,helpTitle);

        // Modal dialogs are really tricky. When the modal dialog
        // is dismissed the JDialog will be dismissed as well.
        // When that happens we need to make sure the ownerWindow
        // is set to null so that a new dialog will be created so
        // that events aren't blocked in the HelpViewer.
        dl=new WindowAdapter(){
          public void windowClosing(WindowEvent e){
            // JDK1.2.1 bug not closing owned windows
            if(myDialog.isShowing()){
              myDialog.hide();
            }
            if (myOwnerWindow != null) {
              myOwnerWindow.removeWindowListener(dl);
            }
            myOwnerWindow=null;
            modalDeactivated=true;
          }
        };
        myOwnerWindow.addWindowListener(dl);
        modalDeactivated=false;

        if(size!=null){
          myDialog.setSize(size);
        } else{
          myDialog.setSize(HELP_WIDTH,HELP_HEIGHT);
        }
        if(pos!=null){
          myDialog.setLocation(pos);
        }
        myDialog.getContentPane().add(jhelp);
        if(tmpDialog!=null){
          tmpDialog.dispose();
        }
      }
    } else{
      if (myFrame == null) {
        myFrame = new JFrame(helpTitle);
        resize = true;
        AppUIUtil.updateWindowIcon(myFrame);
        WindowListener l = new WindowAdapter() {
          public void windowClosing(WindowEvent e) {
            myFrame.setVisible(false);
          }

          public void windowClosed(WindowEvent e) {
            myFrame.setVisible(false);
          }
        };
        myFrame.addWindowListener(l);
      }
      else {
        pos = myFrame.getLocation();
      }
      if(myDialog!=null){
        pos=myDialog.getLocation();
        size=myDialog.getSize();
        myDialog.dispose();
        myDialog=null;
        myOwnerWindow=null;
      }
      if(size!=null){
        myFrame.setSize(size);
      } else if (resize) {
        myFrame.setSize(HELP_WIDTH,HELP_HEIGHT);
      }
      if(pos!=null){
        myFrame.setLocation(pos);
      }
      myFrame.getContentPane().add(jhelp);
      myFrame.setTitle(myHelpSet.getTitle());
    }

  }

  // the listeners.
  private ActionListener displayHelpFromFocus;
  private ActionListener displayHelpFromSource;
}





© 2015 - 2025 Weber Informatics LLC | Privacy Policy