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