[Tex/LaTex] How to customize the “go to slide number” navigation symbol

beamernavigation

I am using a reduced version of the navigation symbols bar that only includes \insertslidenavigationsymbol. My presentation only shows the frame numbers (instead of slide numbers) because I use many overlays and the number of slides grows too large. The problem is that if someone in the public ask me to go to a specific frame number (since this is the number they will see), then the navigation button doesn't take me to frame I want to go.

Is there a way to customize the \insertslidenavigationsymbol option so that it takes me to a frame number instead of a slide number?

In the following example you will see that if you go to slide 3 using the navigation symbol, it will take you to page 3 in frame 2 (this is the expected behavior, I guess). However, I want it to go to frame 3 instead. Any ideas? I tried with \insertframenavigationsymbol but that one only let me go to previous/next frame, not to a specific frame number.

\documentclass[compress]{beamer}

\useoutertheme[subsection=false]{miniframes}
\setbeamertemplate{navigation symbols}{\insertslidenavigationsymbol}

{
\defbeamertemplate{footline}{frame number and last}{%
  \usebeamercolor[fg]{page number in head/foot}%
  \usebeamerfont{page number in head/foot}%
   \hfill\insertframenumber\,/\,\inserttotalframenumber\kern1em\vskip2pt%
}

\setbeamertemplate{footline}[frame number and last]{}

\begin{document}
  \begin{frame}{This is frame 1}
    \onslide<1-2>{Some text\\}
    \onslide<2>{\invisible<1>{
    Some more text}}
  \end{frame}
   \begin{frame}{This is frame 2}
    This is frame 2
   \end{frame}
 \begin{frame}{This is frame 3}
    This is frame 3
 \end{frame}

\end{document}

Best Answer

The following solution uses JavaScript to jump to the specified frame. First the starting page numbers of each frame are remembered (in macro \FrPg@List). At the end of the document these data are written as JavaScript object FrameList. Also a function GoToFrame is defined that first asks the user for the frame that he wants to visit. After some validation of the answer the page number is calculated and the frame is viewed by a destination name that beamer sets for each page (Navigation + page number). If the user uses a frame number less than the first frame, then the very first page is selected. If the frame number is too large, then the starting page of the last frame is used.

\documentclass[compress]{beamer}

\usepackage{atbegshi}
\usepackage{ltxcmds}
\usepackage{atveryend}

\makeatletter
\def\FrPg@List{}
\def\FrPg@Max{-2147483647}
\AtBeginShipout{%
  \ifnum\FrPg@Max<\value{framenumber}%
    \xdef\FrPg@Max{\the\value{framenumber}}%
  \fi
  \ltx@IfUndefined{FrPg@\number\value{framenumber}}{%
    \expandafter
    \xdef\csname FrPg@\number\value{framenumber}\endcsname{%
      \number\value{page}%
    }%
    \ifx\FrPg@List\ltx@empty
      \xdef\FrPg@List{%
        \number\value{framenumber}:\number\value{page}%
      }%
    \else
      \xdef\FrPg@List{%
        \FrPg@List
        ,\number\value{framenumber}:\number\value{page}%
      }%
    \fi
  }{}%
}
\def\FrPg@Script{FrameNavigation}
\AtVeryEndDocument{%
  \immediate\pdfobj{(%
    var FrameList={\FrPg@List};%
    var FrameMax=\FrPg@Max;%
    function GoToFrame(){%
      var frame=app.response({%
        % cQuestion:"Which frame?",%
        cTitle:"Go To  Frame",%
        cLabel:"Frame:"%
      });%
      if(frame==null)return;%
      var result=frame.match(/%
        ^%
        \ltx@backslashchar\ltx@backslashchar d+%
        $%
      /);%
      if(result==null){%
        app.alert("Invalid number!");%
        return;%
      }%
      var page=FrameList[frame];
      if(page==null&&frame>FrameMax)%
        page=FrameList[FrameMax];%
      if(page==null){%
        if(frame<1){%
          app.execMenuItem("FirstPage");%
          return;%
        }%
        app.alert("Frame `"+frame+"' not found!");%
        return;%
      }%
      this.gotoNamedDest("Navigation"+page);%
    }%
  )}%
  \immediate\pdfobj{%
    <<%
      /S/JS%
      /JS \the\pdflastobj\space 0 R%
    >>%
  }%
  \immediate\pdfobj{%
    <<%
      /Names[(\FrPg@Script)\the\pdflastobj\space0 R]%
    >>%
  }%
  \pdfnames{%
    /JavaScript \the\pdflastobj\space0 R%
  }%
}
\begin{Form}[]
\end{Form}
\def\insertslidenavigationsymbol{%
  \begin{pgfpicture}{0pt}{-1.5pt}{20pt}{5.5pt}
    \pgfuseobject{beamerslidenavstrong}%
    \usebeamercolor[fg]{navigation symbols dimmed}
    \pgfuseobject{beamerslidenavlight}%
  \end{pgfpicture}\kern-20pt%
  \hyperlinkslideprev{\beamer@linkspace{6pt}}%
  % \Acrobatmenu{GoToPage}{\beamer@linkspace{8pt}}%
  \PushButton[
    borderwidth=0,
    bordercolor=white,
    onclick=GoToFrame();
  ]{\beamer@linkspace{8pt}}%
  \hyperlinkslidenext{\beamer@linkspace{6pt}}%
}

\useoutertheme[subsection=false]{miniframes}
\setbeamertemplate{navigation symbols}{%
  \insertslidenavigationsymbol
}

\defbeamertemplate{footline}{frame number and last}{%
  \usebeamercolor[fg]{page number in head/foot}%
  \usebeamerfont{page number in head/foot}%
   \hfill\insertframenumber\,/\,\inserttotalframenumber\kern1em\vskip2pt%
}

\setbeamertemplate{footline}[frame number and last]{}

\begin{document}
\makeatletter
  \begin{frame}{This is frame 1}
    \onslide<1-2>{Some text\\}  
    \onslide<2>{\invisible<1>{  
    Some more text}}
  \end{frame}
   \begin{frame}{This is frame 2}
    This is frame 2
   \end{frame}
 \begin{frame}{This is frame 3}
    This is frame 3
 \end{frame}

\end{document}
Related Question