comparison m-toolbox/m/etc/cprintf.m @ 0:f0afece42f48

Import.
author Daniele Nicolodi <nicolodi@science.unitn.it>
date Wed, 23 Nov 2011 19:22:13 +0100
parents
children
comparison
equal deleted inserted replaced
-1:000000000000 0:f0afece42f48
1 function count = cprintf(style,format,varargin)
2 % CPRINTF displays styled formatted text in the Command Window
3 %
4 % Syntax:
5 % count = cprintf(style,format,...)
6 %
7 % Description:
8 % CPRINTF processes the specified text using the exact same FORMAT
9 % arguments accepted by the built-in SPRINTF and FPRINTF functions.
10 %
11 % CPRINTF then displays the text in the Command Window using the
12 % specified STYLE argument. The accepted styles are those used for
13 % Matlab's syntax highlighting (see: File / Preferences / Colors /
14 % M-file Syntax Highlighting Colors), and also user-defined colors.
15 %
16 % The possible pre-defined STYLE names are:
17 %
18 % 'Text' - default: black
19 % 'Keywords' - default: blue
20 % 'Comments' - default: green
21 % 'Strings' - default: purple
22 % 'UnterminatedStrings' - default: dark red
23 % 'SystemCommands' - default: orange
24 % 'Errors' - default: light red
25 % 'Hyperlinks' - default: underlined blue
26 %
27 % 'Black','Cyan','Magenta','Blue','Green','Red','Yellow','White'
28 %
29 % Note: styles beginning with '-' will be underlined. For example:
30 % '-Blue' is underlined blue, like 'Hyperlinks';
31 % '-Comments' is underlined green etc.
32 %
33 % STYLE also accepts a regular Matlab RGB vector, that can be negated
34 % for underlining. For example: -[0,1,1] means underlined cyan.
35 %
36 % STYLE is case-insensitive and accepts unique partial strings just
37 % like handle property names.
38 %
39 % Examples:
40 % cprintf('text', 'regular black text');
41 % cprintf('hyper', 'followed %s','by');
42 % cprintf('k', '%d colored', 4);
43 % cprintf('-comment','& underlined');
44 % cprintf('err', 'elements\n');
45 % cprintf('cyan', 'cyan');
46 % cprintf('-green', 'underlined green');
47 % cprintf(-[1,0,1], 'underlined magenta');
48 % cprintf([1,0.5,0],'and multi-\nline orange\n');
49 %
50 % Bugs and suggestions:
51 % Please send to Yair Altman (altmany at gmail dot com)
52 %
53 % Warning:
54 % This code heavily relies on undocumented and unsupported Matlab
55 % functionality. It works on Matlab 7+, but use at your own risk!
56 %
57 % A technical description of the implementation can be found at:
58 % <a href="http://undocumentedmatlab.com/blog/cprintf/">http://UndocumentedMatlab.com/blog/cprintf/</a>
59 %
60 % Limitations:
61 % 1. For unfortunate implementation reasons, a single space character
62 % is inserted at the beginning of each CPRINTF text segment.
63 % I hope to solve this in a future version of CPRINTF.
64 %
65 % 2. consecutive differently-colored multi-line CPRINTFs sometimes
66 % display incorrectly on the bottom line.
67 % As far as I could tell this is due to a Matlab bug. Examples:
68 % >> cprintf('-str','under\nline'); cprintf('err','red\n'); % hidden 'red', unhidden '_'
69 % >> cprintf('str','regu\nlar'); cprintf('err','red\n'); % underline red (not purple) 'lar'
70 %
71 % 3. Sometimes, non newline ('\n')-terminated segments display unstyled
72 % (black) when the command prompt cevron ('>>') regains focus on the
73 % continuation of that line (I can't pinpoint when this happens).
74 % To fix this, simply newline-terminate all command-prompt messages.
75 %
76 % 4. Multi-line styles only affect the first line on old Matlab versions
77 % (e.g., Matlab 7.1 R14). Single-line styles work as expected.
78 % R14 also appends a single space after underlined segments.
79 %
80 % Change log:
81 % 2009-05-13: First version posted on <a href="http://www.mathworks.com/matlabcentral/fileexchange/authors/27420">MathWorks File Exchange</a>
82 %
83 % See also:
84 % sprintf, fprintf
85
86 % License to use and modify this code is granted freely to all interested, as long as the original author is
87 % referenced and attributed as such. The original author maintains the right to be solely associated with this work.
88
89 % Programmed and Copyright by Yair M. Altman: altmany(at)gmail.com
90 % $Revision: 1.2 $ $Date: 2009/08/27 19:31:59 $
91
92 % The following is for debug use only:
93 %global docElement txt el
94 if ~exist('el','var') || isempty(el), el=handle([]); end %#ok mlint short-circuit error ("used before defined")
95 if isempty(style), return; end
96 if ishandle(style), dumpElement(style); return; end
97
98 % Process the text string
99 error(nargchk(2, inf, nargin, 'struct'));
100 str = sprintf(format,varargin{:});
101
102 % Get the normalized style name and underlining flag
103 [underlineFlag, style] = processStyleInfo(style);
104
105 % Set hyperlinking, is so requested
106 if underlineFlag
107 str = ['<a href=" ">' str '</a>'];
108 v = version;
109
110 % Matlab 7.1 R14 (possibly a few newer versions as well?)
111 % have a bug in rendering consecutive hyperlinks
112 % This is fixed by appending a single non-linked space
113 if str2double(v(1:3)) <= 7.1
114 str(end+1) = ' ';
115 end
116 end
117
118 % Get the current CW position
119 cmdWinDoc = com.mathworks.mde.cmdwin.CmdWinDocument.getInstance;
120 lastPos = cmdWinDoc.getLength;
121
122 % If not beginning of line
123 bolFlag = 0; %#ok
124 %if docElement.getEndOffset - docElement.getStartOffset > 1
125 % Display a hyperlink element in order to force element separation
126 % (otherwise adjacent elements on the same line will me merged)
127 if ~underlineFlag
128 fprintf('<a href=""> </a>');
129 else
130 fprintf(' ');
131 end
132 % drawnow;
133 bolFlag = 1;
134 %end
135
136 % Get a handle to the Command Window component
137 mde = com.mathworks.mde.desk.MLDesktop.getInstance;
138 cw = mde.getClient('Command Window');
139 xCmdWndView = cw.getComponent(0).getViewport.getComponent(0);
140
141 % Store the CW background color as a special color pref
142 % This way, if the CW bg color changes (via File/Preferences),
143 % it will also affect existing rendered strs
144 com.mathworks.services.Prefs.setColorPref('CW_BG_Color',xCmdWndView.getBackground);
145
146 % Display the text in the Command Window
147 count = fprintf(2,str);
148 %awtinvoke(cmdWinDoc,'remove',lastPos,1); % TODO: find out how to remove the extra '_'
149 drawnow;
150 docElement = cmdWinDoc.getParagraphElement(lastPos+1);
151 if bolFlag && ~underlineFlag
152 % Set the leading hyperlink space character ('_') to the bg color, effectively hiding it
153 % Note: old Matlab versions have a bug in hyperlinks that need to be accounted for...
154 %disp(' '); dumpElement(docElement)
155 setElementStyle(docElement,'CW_BG_Color',1+underlineFlag); %+getUrlsFix(docElement));
156 %disp(' '); dumpElement(docElement)
157 el(end+1) = handle(docElement);
158 end
159
160 % Fix a problem with some hidden hyperlinks becoming unhidden...
161 fixHyperlink(docElement);
162
163 % Get the Document Element(s) corresponding to the latest fprintf operation
164 while docElement.getStartOffset < cmdWinDoc.getLength
165 % Set the element style according to the current style
166 setElementStyle(docElement,style,underlineFlag);
167 docElement2 = cmdWinDoc.getParagraphElement(docElement.getEndOffset+1);
168 if isequal(docElement,docElement2), break; end
169 docElement = docElement2;
170 end
171
172 % Force a Command-Window repaint
173 % Note: this is important in case the rendered str was not '\n'-terminated
174 xCmdWndView.repaint;
175
176 % The following is for debug use only:
177 el(end+1) = handle(docElement); %#ok used in debug only
178 %elementStart = docElement.getStartOffset;
179 %elementLength = docElement.getEndOffset - elementStart;
180 %txt = cmdWinDoc.getText(elementStart,elementLength);
181
182 return; % debug breakpoint
183
184 % Process the requested style information
185 function [underlineFlag,style] = processStyleInfo(style)
186 underlineFlag = 0;
187
188 % Style = valid matlab RGB vector
189 if isnumeric(style) && length(style)==3 && all(style<=1) && all(abs(style)>=0)
190 if any(style<0)
191 underlineFlag = 1;
192 style = abs(style);
193 end
194 style = getColorStyle(style);
195
196 elseif ~ischar(style)
197 error('YMA:cprintf:InvalidStyle','Invalid style - see help section for a list of valid style values')
198
199 % Style name
200 else
201 % Styles starting with '-' should be underlined (using a no-target hyperlink hack)
202 if style(1)=='-'
203 underlineFlag = 1;
204 style = style(2:end);
205 end
206
207 % Try case-insensitive partial/full match with the accepted style names
208 validStyles = {'Text','Keywords','Comments','Strings','UnterminatedStrings','SystemCommands','Errors', ...
209 'Black','Cyan','Magenta','Blue','Green','Red','Yellow','White', ...
210 'Hyperlinks'};
211 matches = find(strncmpi(style,validStyles,length(style)));
212
213 % No match - error
214 if isempty(matches)
215 error('YMA:cprintf:InvalidStyle','Invalid style - see help section for a list of valid style values')
216
217 % Too many matches (ambiguous) - error
218 elseif length(matches) > 1
219 error('YMA:cprintf:AmbigStyle','Ambiguous style name - supply extra characters for uniqueness')
220
221 % Regular text
222 elseif matches == 1
223 style = '';
224
225 % Highlight preference style name
226 elseif matches < 8
227 style = ['Colors_M_' validStyles{matches}];
228
229 % Color name
230 elseif matches < length(validStyles)
231 colors = [0,0,0; 0,1,1; 1,0,1; 0,0,1; 0,1,0; 1,0,0; 1,1,0; 1,1,1];
232 requestedColor = colors(matches-7,:);
233 style = getColorStyle(requestedColor);
234
235 % Hyperlink
236 else
237 style = 'Colors_HTML_HTMLLinks'; % CWLink
238 underlineFlag = 1;
239 end
240 end
241
242 % Convert a Matlab RGB vector into a known style name (e.g., '[255,37,0]')
243 function styleName = getColorStyle(rgb)
244 intColor = int32(floor(rgb*255));
245 javaColor = java.awt.Color(intColor(1), intColor(2), intColor(3));
246 styleName = sprintf('[%d,%d,%d]',intColor);
247 com.mathworks.services.Prefs.setColorPref(styleName,javaColor);
248
249 % Fix a bug in some Matlab versions, where the number of URL segments
250 % is larger than the number of style segments in a doc element
251 function delta = getUrlsFix(docElement) %#ok currently unused
252 tokens = docElement.getAttribute('SyntaxTokens');
253 links = docElement.getAttribute('LinkStartTokens');
254 if length(links) > length(tokens(1))
255 delta = length(links) > length(tokens(1));
256 else
257 delta = 0;
258 end
259
260 % fprintf(2,str) causes all previous '_'s in the line to become red - fix this
261 function fixHyperlink(docElement)
262 try
263 tokens = docElement.getAttribute('SyntaxTokens');
264 urls = docElement.getAttribute('HtmlLink');
265 urls = urls(2);
266 links = docElement.getAttribute('LinkStartTokens');
267 offsets = tokens(1);
268 styles = tokens(2);
269 doc = docElement.getDocument;
270
271 % Loop over all segments in this docElement
272 for idx = 1 : length(offsets)-1
273 % If this is a hyperlink with no URL target and starts with ' ' and is collored as an error (red)...
274 if strcmp(styles(idx).char,'Colors_M_Errors')
275 character = char(doc.getText(offsets(idx)+docElement.getStartOffset,1));
276 if strcmp(character,' ')
277 if isempty(urls(idx)) && links(idx)==0
278 % Revert the style color to the CW background color (i.e., hide it!)
279 styles(idx) = java.lang.String('CW_BG_Color');
280 end
281 end
282 end
283 end
284 catch
285 % never mind...
286 end
287
288 % Set an element to a particular style (color)
289 function setElementStyle(docElement,style,hyperlinkFlag)
290 %global tokens links urls urlTargets % for debug only
291 if nargin<3, hyperlinkFlag=0; end
292 % Set the last Element token to the requested style:
293 % Colors:
294 tokens = docElement.getAttribute('SyntaxTokens');
295 try
296 styles = tokens(2);
297 %hyperlinkFlag = ~isempty(strmatch('CWLink',tokens(2)));
298 %hyperlinkFlag = 0 + any(cellfun(@(c)(~isempty(c)&&strcmp(c,'CWLink')),tokens(2).cell));
299 styles(end) = java.lang.String('');
300 styles(end-hyperlinkFlag) = java.lang.String(style); %#ok apparently unused but in reality used by Java
301 catch
302 % never mind for now
303 end
304
305 % Underlines (hyperlinks):
306 links = docElement.getAttribute('LinkStartTokens');
307 if isempty(links)
308 %docElement.addAttribute('LinkStartTokens',repmat(int32(-1),length(tokens(2)),1));
309 else
310 %TODO: remove hyperlink by setting the value to -1
311 end
312
313 % Correct empty URLs to be un-hyperlinkable (only underlined)
314 urls = docElement.getAttribute('HtmlLink');
315 if isempty(urls), return; end
316 urlTargets = urls(2);
317 for urlIdx = 1 : length(urlTargets)
318 try
319 if urlTargets(urlIdx).length < 1
320 urlTargets(urlIdx) = []; % '' => []
321 end
322 catch
323 % never mind...
324 a=1; %#ok used for debug breakpoint...
325 end
326 end
327
328 % Display information about element(s)
329 function dumpElement(docElements)
330 %return;
331 numElements = length(docElements);
332 for elementIdx = 1 : numElements
333 if numElements > 1, fprintf('Element #%d:\n',elementIdx); end
334 docElement = docElements(elementIdx);
335 if ~isjava(docElement), docElement = docElement.java; end
336 %docElement.dump(java.lang.System.out,1)
337 tokens = docElement.getAttribute('SyntaxTokens');
338 if isempty(tokens), continue; end
339 links = docElement.getAttribute('LinkStartTokens');
340 urls = docElement.getAttribute('HtmlLink');
341 try
342 data = [tokens(2).cell m2c(tokens(1)) m2c(links) m2c(urls(1)) cell(urls(2))];
343 catch
344 try
345 data = [tokens(2).cell m2c(tokens(1)) m2c(links)];
346 catch
347 disp([tokens(2).cell m2c(tokens(1))]);
348 try
349 data = [m2c(links) m2c(urls(1)) cell(urls(2))];
350 catch
351 % Mtlab 7.1 only has urls(1)...
352 data = [m2c(links) urls.cell];
353 end
354 end
355 end
356 disp(' ');
357 disp(docElement)
358 disp(data)
359 end
360
361 % Utility function to convert matrix => cell
362 function cells = m2c(data)
363 datasize = size(data);
364 cells = mat2cell(data,ones(1,datasize(1)),ones(1,datasize(2)));
365
366
367 %%%%%%%%%%%%%%%%%%%%%%%%%% TODO %%%%%%%%%%%%%%%%%%%%%%%%%
368 % - Fix: Remove leading space char (hidden underline '_')
369 % - Fix: Find workaround for multi-line quirks/limitations
370 % - Fix: Non-\n-terminated segments are displayed as black
371 % - Fix: Check whether the hyperlink fix for 7.1 is also needed on 7.2 etc.