function hPopupPanel = popupPanel(contents, position, highlightedWords)
% popupPanel Displays a popup panel with specified help topic, HTML or webpage
%
% Syntax:
% hPopupPanel = popupPanel(contents, position, highlightedWords)
%
% Description:
% popupPanel(CONTENTS) displays Matlab's help popup (available on Matlab
% releases R2007b onward) with the specified CONTENTS, which is one of:
% - Help topic string (e.g. 'surf' or 'myFunction')
% - HTML text (e.g. '<b>bold</b> <i>italic</i> text')
% - Webpage URL (e.g. 'UndocumentedMatlab.com')
%
% popupPanel(CONTENTS,POSITION) indicates the panel's position and
% size in screen-relative regular Matlab format: [x,y,width,height].
% If POSITION is empty or unspecified, then [x,y,500,300] is assumed,
% where (x,y) is the current pointer's location.
%
% popupPanel(CONTENTS,POSITION,HIGHLIGHTEDWORDS) specifies a string term
% or cell-array of string terms that should be highlighted in the
% presented panel's CONTENTS. This is not supported in R2013a and later.
%
% hPopupPanel = popupPanel(...) returns the Java handle reference of the
% created panel, allowing access to many useful properties and callbacks.
% Type "get(hPopupPanel)", "methodsview(hPopupPanel)" for details.
%
% Examples:
% popupPanel('surf'); % display the specified documentation page
% popupPanel('myFunction'); % display a user-created help topic
% popupPanel('myFunction.m'); % display a user-created help topic
% popupPanel('<b>bold</b> <i>italic</i> text'); % display HTML text
% popupPanel('C:\myLocalTextPage.txt'); % display a local file
% popupPanel('myLocalHtmlPage.html'); % display a local webpage
% popupPanel('UndocumentedMatlab.com'); % display an online webpage
% popupPanel('surf',[200,300,400,500]); % display 400x500 popup at [200,300]
% popupPanel('surf',[],'surf'); % display popup, highlight 'surf' terms
% popupPanel('surf',[],{'surf','surfc'}); % highlight several terms
%
% Warning:
% This code heavily relies on undocumented and unsupported Matlab
% functionality. It works on Matlab 7.5+ (R2007b+), but use at your own risk!
%
% Bugs and suggestions:
% Please send to Yair Altman (altmany at gmail dot com)
%
% Change log:
% 2013-06-28: Several fixes for modern Matlab releases
% 2009-12-01: Fixed compatibility problem with R2009 highlightedWords (browser = HTMLRenderer)
% 2009-11-30: First version posted on <a href="http://www.mathworks.com/matlabcentral/fileexchange/loadAuthor.do?objectType=author&mfx=1&objectId=1096533#">MathWorks File Exchange</a>
%
% Notes:
% <a href="http://undocumentedmatlab.com/blog/customizing-help-popup-contents">Technical details on the UndocumentedMatlab.com website</a>
% Technical details: http://undocumentedmatlab.com/blog/customizing-help-popup-contents
% Programmed by Yair M. Altman: altmany(at)gmail.com
% $Revision: 1.1 $ $Date: 2013/06/28 14:27:45 $
try
% Sanity checks before starting...
error(nargchk(1,3,nargin,'struct'));
% Require Java engine to run
if ~usejava('jvm')
error([mfilename ' requires Java to run.']);
end
if ~ischar(contents)
error('YMA:popupPanel:badContents','CONTENTS must be a string');
end
% Default position = 500x300 at current pointer location
oldunits = get(0,'Units');
set(0,'Units','pixels');
screenSize = get(0,'ScreenSize');
set(0,'Units',oldunits);
if nargin<2 || isempty(position)
oldunits = get(0,'Units');
set(0,'Units','pixels');
curPosition = get(0,'PointerLocation');
set(0,'Units',oldunits);
position = [curPosition 500,300];
elseif ~isnumeric(position) || length(position)~=4
error('YMA:popupPanel:badPosition','POSITION must be a 4-element numeric array: [x,y,width,height]');
elseif all(position<=1)
% normalized units - convert to pixels
position = position .* screenSize([3,4,3,4]);
end
% Convert to Java X,Y position
posX = position(1) - 1; % Java X starts from screen left = 0
posY = screenSize(4) - position(4) - position(2); % Java Y starts from screen top = 0
% Default highlighted words = none
if nargin<3 || isempty(highlightedWords)
highlightedWords = {};
elseif ischar(highlightedWords) % single string - convert to cell array
highlightedWords = {highlightedWords};
end
% Display the popup with default contents
jDesktop = com.mathworks.mde.desk.MLDesktop.getInstance;
jTextArea = jDesktop.getMainFrame.getFocusOwner;
jClassName = 'com.mathworks.mlwidgets.help.HelpPopup';
jPosition = java.awt.Rectangle(posX,posY,position(3),position(4));
helpTopic = '';
if ~isempty(helpfunc(contents))
helpTopic = contents;
end
try
javaMethodEDT('showHelp',jClassName,jTextArea,[],jPosition,helpTopic);
catch
% probably Matlab R2007b
jniSig = 'showHelp(Ljavax.swing.JComponent;Lcom.mathworks.mwswing.binding.KeyStrokeList;Ljava.awt.Rectangle;Ljava.lang.String;)';
awtinvoke(jClassName,jniSig,jTextArea,[],jPosition,helpTopic);
end
% Find the Help popup window
jPopup = getPopupHandle;
% If the popup was already displayed, its position & size are not modified so manually set them
jPopup.setSize(jPosition.getSize);
jPopup.setLocation(jPosition.getLocation);
% Update the contents with the user-specified contents
if ~isempty(jPopup)
% Add a promotional link at the bottom (status bar)
promoLink(jPopup);
% Get the handle of the internal browser container
browserPanel = jPopup.getContentPane.getComponent(1);
browser = browserPanel.getComponent(0);
oldBrowser = true;
if isa(browser,'com.mathworks.mwswing.MJToolBar') % R2013a's new JXBrowser
browser = browserPanel.getComponent(browserPanel.getComponentCount-1).getComponent(0);
oldBrowser = false;
end
% If there is valid help text, then display it
url = '';
if ~isempty(helpTopic)
%Highlight the displayed text
try
pause(0.1); drawnow;
if oldBrowser
try
htmlRenderer = browser.getHTMLRenderer;
catch
htmlRenderer = browser;
end
contents = char(htmlRenderer.getHtmlText);
url = char(htmlRenderer.getCurrentLocation);
else % R2013a's new JXBrowser
contents = char(browser.getContent);
url = char(browser.getLocationURL);
end
baseUrl = fileparts(url);
% Relative links in Matlab's help don't work (e.g. for 'surf', 'mesh')
% so we must convert relative links to absolute links
%contents = regexprep(contents,'file:../../techdoc/','file://'); %([^/])',[baseUrl '/$1']);
contents = regexprep(contents,'file:../../techdoc',fileparts(baseUrl));
catch
a=1; %#ok never mind... - debug point
end
elseif (~isempty(which(contents)) || ~isempty(dir(contents))) && isempty(strfind(lower(contents),'.htm'))
% Local file found - display within <PRE>...</PRE>
url = contents;
htmlStr = ['<html><h2>Loading ' url ' - please wait...'];
try
browser.setHtmlText(htmlStr);
catch
browser.setContent(htmlStr);
end
contents = evalc(['type(''' url ''')']);
contents = ['<html><pre>' contents '</pre></html>'];
elseif isempty(strfind(contents,' '))
% No spaces in the contents so treat the contents as a webpage URL
%browser.setCurrentLocation(contents); % asynchronous - not good...
%text = browser.getHTMLRenderer.getHtmlText;
try
url = contents;
%domain = regexprep(url,'[\\/].*','');
if isempty(strfind(url,'://'))
if ~isempty(dir(contents))
url = ['file:///' url];
else
url = ['http://' url];
end
end
htmlStr = ['<html><h2>Loading ' url ' - please wait...'];
try
browser.setHtmlText(htmlStr);
catch
browser.setContent(htmlStr);
end
contents = urlread(url);
catch
a=1; %#ok never mind... - debug point
end
else
% Otherwise, treat as a displayable HTML text
% ensure we have both <HTML> and <BODY>, otherwise some text may disappear
contents = ['<html><body>' contents];
end
% Correct the HTML <base> location so that images appear correctly
newBaseStr = ['<BASE href=''' url '''>' 10];
%contents = regexprep(contents, '<BASE href=''''>',newBaseStr,'ignorecase'); % no good - rewritten by setHtmlText() !!!
contents = regexprep(contents, '<head>',['<head>' newBaseStr],'ignorecase');
% Update the popup-panel's contents
try
browser.setHtmlTextAndHighlightKeywords(contents,highlightedWords);
catch
% Probably R2013a's new JXBRowser
browser.setContent(contents);
end
end
% Initialize output var, if requested
if nargout
hPopupPanel = jPopup;
end
% Error handling
catch
err = lasterror; %#ok
err.message = regexprep(err.message,'Error using ==> [^\n]+\n','');
if isempty(strfind(err.message,mfilename))
% Indicate error origin, if not already stated within the error message
err.message = [mfilename ': ' err.message];
end
rethrow(err);
end
%% Get the popup panel's handle
function jPopup = getPopupHandle
jPopup = [];
retryIdx = 1;
while isempty(jPopup) && retryIdx < 10
pause(0.05*retryIdx); drawnow;
jWindows = com.mathworks.mwswing.MJDialog.getWindows;
for idx=1 : length(jWindows)
if strcmp(get(jWindows(idx),'Name'),'HelpPopup')
if jWindows(idx).isVisible
jPopup = jWindows(idx);
break;
end
end
end
end
%% Prepare the promo link
function promoLink(jPopup)
hyperlink = jPopup.findComponentAt(15,jPopup.getHeight-10);
% Prepare the new promo hyperlink
blogLabel = javax.swing.JLabel('<html><center><a href="">UndocumentedMatlab.com</a></center></html>');
try blogLabel = javaObjectEDT(blogLabel); catch, end
oldWarn = warning('off','MATLAB:hg:JavaSetHGProperty');
warning('off','MATLAB:hg:PossibleDeprecatedJavaSetHGProperty');
set(blogLabel,'MouseClickedCallback',@promoLink_Callback);
warning(oldWarn);
blogLabel.setCursor(java.awt.Cursor(java.awt.Cursor.HAND_CURSOR));
blogLabel.setOpaque(true);
% Place the new hyperlink in the panel's statusbar
if isa(hyperlink,'com.mathworks.widgets.HyperlinkLabel$Hyperlink')
hyperlink = handle(hyperlink,'CallbackProperties');
%hyperlink.removeListener;
set(hyperlink, 'MouseClickedCallback',@promoLink_Callback, 'Text',''); %'<html><a href="">UndocumentedMatlab.com</a></html>');
hyperlabel = hyperlink.getParent;
%set(hyperlabel, 'MouseClickedCallback',@promoLink_Callback);
hyperlabel.getParent.add(blogLabel);
hyperlabel.revalidate;
elseif isa(hyperlink,'com.mathworks.mwswing.MJEditorPane')
jPanel = hyperlink.getParent;
%jPanel.removeAll;
jPanel.add(blogLabel);
jAncestor = jPanel.getParent.getParent;
jAncestor.setVisible(false); jAncestor.setVisible(true); % solve R2013 bug
jPanel.repaint;
end
%% Promo link callback
function promoLink_Callback(hObject, eventData, varargin) %#ok
%eventData.consume;
url = 'http://undocumentedmatlab.com/blog/customizing-help-popup-contents';
web(url,'-browser'); % integrated browser is slow and does not display all URLs