1 //------------------------------------------------------------------------------
2 // emTextFilePanel.cpp
3 //
4 // Copyright (C) 2004-2010,2014-2019 Oliver Hamann.
5 //
6 // Homepage: http://eaglemode.sourceforge.net/
7 //
8 // This program is free software: you can redistribute it and/or modify it under
9 // the terms of the GNU General Public License version 3 as published by the
10 // Free Software Foundation.
11 //
12 // This program is distributed in the hope that it will be useful, but WITHOUT
13 // ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
14 // FOR A PARTICULAR PURPOSE. See the GNU General Public License version 3 for
15 // more details.
16 //
17 // You should have received a copy of the GNU General Public License version 3
18 // along with this program. If not, see <http://www.gnu.org/licenses/>.
19 //------------------------------------------------------------------------------
20 
21 #include <emText/emTextFilePanel.h>
22 #include <emCore/emToolkit.h>
23 
24 
emTextFilePanel(ParentArg parent,const emString & name,emTextFileModel * fileModel,bool updateFileModel,bool alternativeView)25 emTextFilePanel::emTextFilePanel(
26 	ParentArg parent, const emString & name, emTextFileModel * fileModel,
27 	bool updateFileModel, bool alternativeView
28 )
29 	: emFilePanel(parent,name)
30 {
31 	AlternativeView=alternativeView;
32 	Model=NULL;
33 	AddWakeUpSignal(GetVirFileStateSignal());
34 	SetFileModel(fileModel,updateFileModel);
35 }
36 
37 
SetFileModel(emFileModel * fileModel,bool updateFileModel)38 void emTextFilePanel::SetFileModel(
39 	emFileModel * fileModel, bool updateFileModel
40 )
41 {
42 	Model=dynamic_cast<emTextFileModel*>(fileModel);
43 	emFilePanel::SetFileModel(Model,updateFileModel);
44 }
45 
46 
GetIconFileName() const47 emString emTextFilePanel::GetIconFileName() const
48 {
49 	if (IsVFSGood()) {
50 		if (Model->GetCharEncoding()!=emTextFileModel::CE_BINARY) {
51 			return "plain_text.tga";
52 		}
53 	}
54 	return emFilePanel::GetIconFileName();
55 }
56 
57 
Cycle()58 bool emTextFilePanel::Cycle()
59 {
60 	static const char * const ALT_ERROR="Hex display is not an alternative.";
61 
62 	if (IsSignaled(GetVirFileStateSignal())) {
63 		InvalidateControlPanel(); //??? very cheap solution, but okay for now.
64 		InvalidatePainting();
65 		if (IsVFSGood()) {
66 			if (AlternativeView && Model->GetCharEncoding()==emTextFileModel::CE_BINARY) {
67 				SetCustomError(ALT_ERROR);
68 			}
69 		}
70 		else if (GetCustomError()==ALT_ERROR) {
71 			switch (Model->GetFileState()) {
72 			case emFileModel::FS_LOADED:
73 			case emFileModel::FS_UNSAVED:
74 			case emFileModel::FS_SAVING:
75 				if (AlternativeView && Model->GetCharEncoding()==emTextFileModel::CE_BINARY) {
76 					break;
77 				}
78 			default:
79 				ClearCustomError();
80 			}
81 		}
82 	}
83 	return emFilePanel::Cycle();
84 }
85 
86 
IsOpaque() const87 bool emTextFilePanel::IsOpaque() const
88 {
89 	if (IsVFSGood()) {
90 		return false;
91 	}
92 	else {
93 		return emFilePanel::IsOpaque();
94 	}
95 }
96 
97 
Paint(const emPainter & painter,emColor canvasColor) const98 void emTextFilePanel::Paint(const emPainter & painter, emColor canvasColor) const
99 {
100 	if (IsVFSGood()) {
101 
102 		painter.LeaveUserSpace(); //!!!
103 
104 		if (Model->GetCharEncoding()==emTextFileModel::CE_BINARY || AlternativeView) {
105 			PaintAsHex(painter,canvasColor);
106 		}
107 		else {
108 			PaintAsText(painter,canvasColor);
109 		}
110 
111 		painter.EnterUserSpace(); //!!!
112 
113 	}
114 	else {
115 		emFilePanel::Paint(painter,canvasColor);
116 	}
117 }
118 
119 
CreateControlPanel(ParentArg parent,const emString & name)120 emPanel * emTextFilePanel::CreateControlPanel(
121 	ParentArg parent, const emString & name
122 )
123 {
124 	emRasterGroup * grp;
125 	const char * p;
126 
127 	if (
128 		IsVFSGood() &&
129 		Model->GetCharEncoding()!=emTextFileModel::CE_BINARY &&
130 		!AlternativeView
131 	) {
132 
133 		grp=new emRasterGroup(
134 			parent,
135 			name,
136 			"Text File Info"
137 		);
138 
139 		grp->SetRowByRow();
140 		grp->SetPrefChildTallness(0.1);
141 
142 		switch (Model->GetCharEncoding()) {
143 			case emTextFileModel::CE_7BIT   : p="7-Bit"    ; break;
144 			case emTextFileModel::CE_8BIT   : p="8-Bit"    ; break;
145 			case emTextFileModel::CE_UTF8   : p="UTF-8"    ; break;
146 			case emTextFileModel::CE_UTF16LE: p="UTF-16LE" ; break;
147 			case emTextFileModel::CE_UTF16BE: p="UTF-16BE" ; break;
148 			default                         : p="Binary"   ;
149 		}
150 		new emTextField(
151 			grp,
152 			"enc",
153 			"Character Encoding",
154 			emString(),
155 			emImage(),
156 			p
157 		);
158 
159 		switch (Model->GetLineBreakEncoding()) {
160 			case emTextFileModel::LBE_MAC  : p="MAC (CR)"  ; break;
161 			case emTextFileModel::LBE_DOS  : p="DOS (CRLF)"; break;
162 			case emTextFileModel::LBE_UNIX : p="UNIX (LF)" ; break;
163 			case emTextFileModel::LBE_MIXED: p="Mixed"     ; break;
164 			default                        : p="None"      ;
165 		}
166 		new emTextField(
167 			grp,
168 			"lbenc",
169 			"Line Break Encoding",
170 			emString(),
171 			emImage(),
172 			p
173 		);
174 
175 		new emTextField(
176 			grp,
177 			"lines",
178 			"Number of Lines",
179 			emString(),
180 			emImage(),
181 			emString::Format("%d",Model->GetLineCount())
182 		);
183 
184 		new emTextField(
185 			grp,
186 			"columns",
187 			"Number of Columns",
188 			emString(),
189 			emImage(),
190 			emString::Format("%d",Model->GetColumnCount())
191 		);
192 		return grp;
193 	}
194 	else {
195 		return emFilePanel::CreateControlPanel(parent,name);
196 	}
197 }
198 
199 
PaintAsText(const emPainter & painter,emColor canvasColor) const200 void emTextFilePanel::PaintAsText(
201 	const emPainter & painter, emColor canvasColor
202 ) const
203 {
204 	static const emColor textBgColor(255,255,255);
205 	static const emColor textFgColor(0,0,0);
206 	static const emColor textFgColor96(textFgColor,96);
207 	const char * pContent, * pRow;
208 	int i1,i2,i3,row,row2,col,cols,rows,page,pages,pagerows,step;
209 	double h,ch,cw,f,t,pagew,gap,x,y,clipx1,clipy1,clipx2,clipy2;
210 
211 	h=GetHeight();
212 
213 	clipx1=painter.GetUserClipX1();
214 	clipy1=painter.GetUserClipY1();
215 	clipx2=painter.GetUserClipX2();
216 	clipy2=painter.GetUserClipY2();
217 
218 	pContent=Model->GetContent();
219 	rows=Model->GetLineCount();
220 	cols=Model->GetColumnCount();
221 	if (cols<8) cols=8;
222 
223 	f=painter.GetTextSize("X",1.0,false);
224 	gap=1.0;
225 	t=0.5*gap/(cols+gap);
226 	pages=(int)floor(t+sqrt((2*rows/(h*f*gap)+t)*t));
227 	// pages*h/rows*f*(cols*pages+gap*(pages-1)) <= 1.0
228 	if (pages<1) {
229 		pages=1;
230 		pagerows=rows;
231 		cw=1.0/cols;
232 		ch=cw/f;
233 		pagew=1.0;
234 		gap*=cw;
235 	}
236 	else {
237 		pagerows=(rows+pages-1)/pages;
238 		ch=h/pagerows;
239 		cw=ch*f;
240 		gap*=cw;
241 		pagew=(1.0-(pages-1)*gap)/pages;
242 	}
243 
244 	page=(int)(clipx1/(pagew+gap));
245 	if (page<0) page=0;
246 	x=page*(pagew+gap);
247 	for (; page<pages && x<clipx2; page++, x+=pagew+gap) {
248 
249 		painter.PaintRect(
250 			x,
251 			0.0,
252 			pagew,
253 			h,
254 			textBgColor,
255 			canvasColor
256 		);
257 
258 		row=(int)(clipy1/ch);
259 		if (row<0) row=0;
260 		y=row*ch;
261 		row+=page*pagerows;
262 		row2=(int)ceil(clipy2/ch);
263 		if (row2>pagerows) row2=pagerows;
264 		row2+=page*pagerows;
265 		if (row2>rows) row2=rows;
266 
267 		if (ch*GetViewedWidth()<0.5) {
268 			step=(int)(0.5/(ch*GetViewedWidth()));
269 			if (step<1) step=1;
270 			row=((row-1)/step+1)*step;
271 			while (row<row2) {
272 				f=cols*cw/255.0;
273 				painter.PaintRect(
274 					x+Model->GetRelativeLineIndent(row)*f,
275 					y+ch*0.1,
276 					Model->GetRelativeLineWidth(row)*f,
277 					ch*step*0.8,
278 					textFgColor96,
279 					textBgColor
280 				);
281 				y+=ch*step;
282 				row+=step;
283 			}
284 		}
285 		else {
286 			while (row<row2) {
287 				i1=Model->GetLineStart(row);
288 				pRow=pContent+i1;
289 				i3=Model->GetLineEnd(row)-i1;
290 				i2=0;
291 				col=0;
292 				for (;;) {
293 					switch (Model->GetCharEncoding()) {
294 					case emTextFileModel::CE_UTF16LE:
295 						while (i2<i3 && (((emByte)pRow[i2])|(((emByte)pRow[i2+1])<<8))==0x09) {
296 							col=(col+8)&~7;
297 							i2+=2;
298 						}
299 						i1=i2;
300 						while (i2<i3 && (((emByte)pRow[i2])|(((emByte)pRow[i2+1])<<8))!=0x09) {
301 							i2+=2;
302 						}
303 						break;
304 					case emTextFileModel::CE_UTF16BE:
305 						while (i2<i3 && ((((emByte)pRow[i2])<<8)|((emByte)pRow[i2+1]))==0x09) {
306 							col=(col+8)&~7;
307 							i2+=2;
308 						}
309 						i1=i2;
310 						while (i2<i3 && ((((emByte)pRow[i2])<<8)|((emByte)pRow[i2+1]))!=0x09) {
311 							i2+=2;
312 						}
313 						break;
314 					default:
315 						while (i2<i3 && pRow[i2]==0x09) { col=(col+8)&~7; i2++; }
316 						i1=i2;
317 						while (i2<i3 && pRow[i2]!=0x09) i2++;
318 						break;
319 					}
320 					if (i1>=i2) break;
321 					switch (Model->GetCharEncoding()) {
322 					case emTextFileModel::CE_8BIT:
323 						if (emIsUtf8System()) {
324 							col+=PaintTextLatin1(
325 								painter,
326 								x+col*cw,
327 								y,
328 								cw,
329 								ch,
330 								pRow+i1,
331 								i2-i1,
332 								textFgColor,
333 								textBgColor
334 							);
335 						}
336 						else {
337 							painter.PaintText(
338 								x+col*cw,
339 								y,
340 								pRow+i1,
341 								ch,
342 								1.0,
343 								textFgColor,
344 								textBgColor,
345 								i2-i1
346 							);
347 							col+=emGetDecodedCharCount(pRow+i1,i2-i1);
348 						}
349 						break;
350 					case emTextFileModel::CE_UTF8:
351 						col+=PaintTextUtf8(
352 							painter,
353 							x+col*cw,
354 							y,
355 							cw,
356 							ch,
357 							pRow+i1,
358 							i2-i1,
359 							textFgColor,
360 							textBgColor
361 						);
362 						break;
363 					case emTextFileModel::CE_UTF16LE:
364 					case emTextFileModel::CE_UTF16BE:
365 						col+=PaintTextUtf16(
366 							painter,
367 							x+col*cw,
368 							y,
369 							cw,
370 							ch,
371 							pRow+i1,
372 							i2-i1,
373 							textFgColor,
374 							textBgColor
375 						);
376 						break;
377 					default:
378 						painter.PaintText(
379 							x+col*cw,
380 							y,
381 							pRow+i1,
382 							ch,
383 							1.0,
384 							textFgColor,
385 							textBgColor,
386 							i2-i1
387 						);
388 						col+=i2-i1;
389 						break;
390 					}
391 				}
392 				y+=ch;
393 				row++;
394 			}
395 		}
396 	}
397 }
398 
399 
PaintTextLatin1(const emPainter & painter,double x,double y,double charWidth,double charHeight,const char * text,int textLen,emColor color,emColor canvasColor) const400 int emTextFilePanel::PaintTextLatin1(
401 	const emPainter & painter, double x, double y, double charWidth,
402 	double charHeight, const char * text, int textLen,
403 	emColor color, emColor canvasColor
404 ) const
405 {
406 	char buf[256+EM_MB_LEN_MAX];
407 	int i,l,c,bufPos;
408 
409 	bufPos=0;
410 	l=0;
411 	emMBState mbState;
412 	for (i=0; i<textLen; i++) {
413 		if (l>=(int)sizeof(buf)-EM_MB_LEN_MAX) {
414 			painter.PaintText(
415 				x+bufPos*charWidth,
416 				y,
417 				buf,
418 				charHeight,
419 				1.0,
420 				color,
421 				canvasColor,
422 				l
423 			);
424 			bufPos=i;
425 			l=0;
426 		}
427 		c=(unsigned char)text[i];
428 		if (c>=0x80) {
429 			if (c<=0x9F) {
430 				static const int latin1ExtraTap[32]={
431 					0x20AC,0x0081,0x201A,0x0192,0x201E,0x2026,0x2020,0x2021,
432 					0x02C6,0x2030,0x0160,0x2039,0x0152,0x0164,0x017D,0x0179,
433 					0x0090,0x2035,0x2032,0x2036,0x2033,0x2022,0x2013,0x2014,
434 					0x02DC,0x2122,0x0161,0x203A,0x0153,0x0165,0x017E,0x0178
435 				};
436 				c=latin1ExtraTap[c-0x80];
437 			}
438 			l+=emEncodeChar(buf+l,c,&mbState);
439 		}
440 		else {
441 			buf[l++]=(char)c;
442 		}
443 	}
444 	if (l>0) {
445 		painter.PaintText(
446 			x+bufPos*charWidth,
447 			y,
448 			buf,
449 			charHeight,
450 			1.0,
451 			color,
452 			canvasColor,
453 			l
454 		);
455 	}
456 	return textLen;
457 }
458 
459 
PaintTextUtf8(const emPainter & painter,double x,double y,double charWidth,double charHeight,const char * text,int textLen,emColor color,emColor canvasColor) const460 int emTextFilePanel::PaintTextUtf8(
461 	const emPainter & painter, double x, double y, double charWidth,
462 	double charHeight, const char * text, int textLen,
463 	emColor color, emColor canvasColor
464 ) const
465 {
466 	char buf[256+EM_MB_LEN_MAX];
467 	int i,l,c,pos,bufPos;
468 
469 	if (emIsUtf8System()) {
470 		painter.PaintText(x,y,text,charHeight,1.0,color,canvasColor,textLen);
471 		return emGetDecodedCharCount(text,textLen);
472 	}
473 
474 	pos=0;
475 	bufPos=0;
476 	l=0;
477 	emMBState mbState;
478 	for (i=0; i<textLen; ) {
479 		if (l>=(int)sizeof(buf)-EM_MB_LEN_MAX) {
480 			painter.PaintText(
481 				x+bufPos*charWidth,
482 				y,
483 				buf,
484 				charHeight,
485 				1.0,
486 				color,
487 				canvasColor,
488 				l
489 			);
490 			bufPos=pos;
491 			l=0;
492 		}
493 		c=(unsigned char)text[i];
494 		if (c>=128) {
495 			i+=emDecodeUtf8Char(&c,text+i,textLen-i);
496 			l+=emEncodeChar(buf+l,c,&mbState);
497 		}
498 		else {
499 			i++;
500 			buf[l++]=(char)c;
501 		}
502 		pos++;
503 	}
504 	if (l>0) {
505 		painter.PaintText(
506 			x+bufPos*charWidth,
507 			y,
508 			buf,
509 			charHeight,
510 			1.0,
511 			color,
512 			canvasColor,
513 			l
514 		);
515 	}
516 	return pos;
517 }
518 
519 
PaintTextUtf16(const emPainter & painter,double x,double y,double charWidth,double charHeight,const char * text,int textLen,emColor color,emColor canvasColor) const520 int emTextFilePanel::PaintTextUtf16(
521 	const emPainter & painter, double x, double y, double charWidth,
522 	double charHeight, const char * text, int textLen,
523 	emColor color, emColor canvasColor
524 ) const
525 {
526 	char buf[256+EM_MB_LEN_MAX];
527 	int i,l,c,c2,pos,bufPos,sh1,sh2;
528 
529 	if (Model->GetCharEncoding()==emTextFileModel::CE_UTF16LE) { sh1=0; sh2=8; }
530 	else { sh1=8; sh2=0; }
531 	pos=0;
532 	bufPos=0;
533 	l=0;
534 	emMBState mbState;
535 	for (i=0; i<textLen; ) {
536 		if (l>=(int)sizeof(buf)-EM_MB_LEN_MAX) {
537 			painter.PaintText(
538 				x+bufPos*charWidth,
539 				y,
540 				buf,
541 				charHeight,
542 				1.0,
543 				color,
544 				canvasColor,
545 				l
546 			);
547 			bufPos=pos;
548 			l=0;
549 		}
550 		c=((((emByte)text[i])<<sh1)|(((emByte)text[i+1])<<sh2));
551 		i+=2;
552 		if (c<128) {
553 			buf[l++]=(char)c;
554 			pos++;
555 		}
556 		else if (c!=0xFEFF) {
557 			if (c>=0xD800 && c<=0xDBFF && i<textLen) {
558 				c2=((((emByte)text[i])<<sh1)|(((emByte)text[i+1])<<sh2));
559 				if (c2>=0xDC00 && c2<=0xDFFF) {
560 					i+=2;
561 					c=0x10000+((c&0x03FF)<<10)+(c2&0x03FF);
562 				}
563 			}
564 			l+=emEncodeChar(buf+l,c,&mbState);
565 			pos++;
566 		}
567 	}
568 	if (l>0) {
569 		painter.PaintText(
570 			x+bufPos*charWidth,
571 			y,
572 			buf,
573 			charHeight,
574 			1.0,
575 			color,
576 			canvasColor,
577 			l
578 		);
579 	}
580 	return pos;
581 }
582 
583 
PaintAsHex(const emPainter & painter,emColor canvasColor) const584 void emTextFilePanel::PaintAsHex(
585 	const emPainter & painter, emColor canvasColor
586 ) const
587 {
588 	static const emColor colBg(0,0,0);
589 	static const emColor colAddr(64,128,64);
590 	static const emColor colHex(128,128,64);
591 	static const emColor colAsc(64,96,128);
592 	static const emColor colAddr64(colAddr,64);
593 	static const emColor colHex48(colHex,48);
594 	static const emColor colAsc64(colAsc,64);
595 	static const emColor colAddr96(colAddr,96);
596 	static const emColor colHex96(colHex,96);
597 	char buf[256];
598 	char buf2[32];
599 	const char * pStart, * pEnd, * p;
600 	int i,j,k,count,row,cols,rows,page,pages,pagerows;
601 	double h,cw,ch,f,t,pagex,gap,pagew,bx,rowy,clipx1,clipy1,clipx2,clipy2;
602 
603 	count=Model->GetContent().GetCount();
604 	pStart=Model->GetContent();
605 	pEnd=pStart+count;
606 
607 	h=GetHeight();
608 	clipx1=painter.GetUserClipX1();
609 	clipy1=painter.GetUserClipY1();
610 	clipx2=painter.GetUserClipX2();
611 	clipy2=painter.GetUserClipY2();
612 
613 	painter.PaintRect(0,0,1,h,colBg,canvasColor);
614 
615 	rows=(int)(((unsigned)count+15)/16);
616 	if (!rows) return;
617 	cols=73;
618 
619 
620 	f=painter.GetTextSize("X",1.0,false);
621 	gap=2.0;
622 	t=0.5*gap/(cols+gap);
623 	pages=(int)floor(t+sqrt((2*rows/(h*f*gap)+t)*t));
624 	// pages*h/rows*f*(cols*pages+gap*(pages-1)) <= 1.0
625 	if (pages<1) {
626 		pages=1;
627 		pagerows=rows;
628 		cw=1.0/cols;
629 		ch=cw/f;
630 	}
631 	else {
632 		pagerows=(rows+pages-1)/pages;
633 		ch=h/pagerows;
634 		cw=ch*f;
635 	}
636 	gap*=cw;
637 	pagew=cols*cw+gap;
638 
639 	p=pStart;
640 	page=0;
641 	pagex=0;
642 	if (pagex+pagew<=clipx1) {
643 		page=(int)((clipx1-pagex)/pagew);
644 		pagex+=page*pagew;
645 		p+=page*pagerows*16;
646 	}
647 	if (ch*GetViewedWidth()<1.0) {
648 		for (; page<pages && pagex<clipx2; page++, pagex+=pagew) {
649 			f=(pEnd-p+15)/16*ch;
650 			if (f>h) f=h;
651 			painter.PaintRect(
652 				pagex,
653 				0,
654 				cw*8,
655 				f,
656 				colAddr64,
657 				colBg
658 			);
659 			painter.PaintRect(
660 				pagex+cw*9,
661 				0,
662 				cw*47,
663 				f,
664 				colHex48,
665 				colBg
666 			);
667 			painter.PaintRect(
668 				pagex+cw*(9+48),
669 				0,
670 				cw*16,
671 				f,
672 				colAsc64,
673 				colBg
674 			);
675 			p+=16*pagerows;
676 		}
677 	}
678 	else if (ch*GetViewedWidth()<3.0) {
679 		for (; page<pages && pagex<clipx2; page++, pagex+=pagew) {
680 			row=0;
681 			rowy=0;
682 			if (rowy+ch<=clipy1) {
683 				row=(int)((clipy1-rowy)/ch);
684 				rowy+=row*ch;
685 				p+=row*16;
686 			}
687 			while (row<pagerows && rowy<clipy2 && p<pEnd) {
688 				bx=pagex;
689 				painter.PaintRect(
690 					bx,
691 					rowy+ch*0.1,
692 					cw*8,
693 					ch*0.8,
694 					colAddr96,
695 					colBg
696 				);
697 				bx+=9*cw;
698 				for (i=0, j=0; i<16 && p<pEnd; i++, p++) {
699 					k=(unsigned char)*p;
700 					if (((unsigned)(k-0x20))<0x60) j++;
701 					painter.PaintRect(
702 						bx+3*i*cw,
703 						rowy+ch*0.1,
704 						cw*2,
705 						ch*0.8,
706 						colHex96,
707 						colBg
708 					);
709 				}
710 				painter.PaintRect(
711 					bx+48*cw,
712 					rowy+ch*0.1,
713 					i*cw,
714 					ch*0.8,
715 					emColor(colAsc,(emByte)(32+j*64/i)),
716 					colBg
717 				);
718 				row++;
719 				rowy+=ch;
720 			}
721 			p+=16*(pagerows-row);
722 		}
723 	}
724 	else {
725 		for (; page<pages && pagex<clipx2; page++, pagex+=pagew) {
726 			row=0;
727 			rowy=0;
728 			if (rowy+ch<=clipy1) {
729 				row=(int)((clipy1-rowy)/ch);
730 				rowy+=row*ch;
731 				p+=row*16;
732 			}
733 			while (row<pagerows && rowy<clipy2 && p<pEnd) {
734 				sprintf(buf,"%08X",(unsigned int)(p-pStart));
735 				bx=pagex;
736 				painter.PaintText(bx,rowy,buf,ch,1.0,colAddr,colBg);
737 				bx+=9*cw;
738 				for (i=0; i<16 && p<pEnd; i++, p++) {
739 					k=(unsigned char)*p;
740 					j=(k>>4)+'0';
741 					if (j>'9') j+='A'-'9'-1;
742 					buf[0]=(char)j;
743 					j=(k&0xF)+'0';
744 					if (j>'9') j+='A'-'9'-1;
745 					buf[1]=(char)j;
746 					if (((unsigned)(k-0x20))>=0x60) k='.';
747 					buf2[i]=(char)k;
748 					painter.PaintText(bx+3*i*cw,rowy,buf,ch,1.0,colHex,colBg,2);
749 				}
750 				painter.PaintText(bx+48*cw,rowy,buf2,ch,1.0,colAsc,colBg,i);
751 				row++;
752 				rowy+=ch;
753 			}
754 			p+=16*(pagerows-row);
755 		}
756 	}
757 }
758