1 /*
2 * Part of WCM Commander
3 * https://github.com/corporateshark/WCMCommander
4 * wcm@linderdaum.com
5 */
6
7 #include "globals.h"
8 #include "ncview.h"
9 #include "ncwin.h"
10 #include "wcm-config.h"
11 #include "color-style.h"
12 #include "nc.h"
13 #include "search-tools.h"
14 #include "charsetdlg.h"
15 #include "ltext.h"
16
17 #ifdef _WIN32
18 #include <time.h>
19 #endif
20
21 #include <algorithm>
22
23 using namespace wal;
24
25 #define VIEWER_COLS (2048)
26 #define CACHE_BLOCK_SIZE (0x4000) //(0x8000)
27
28
29 /*
30 Node must have
31 Node *queueNext
32 Node *queuePrev
33 */
34
35
36 template <class Node> class CacheQueue
37 {
38 Node* first, *last;
39 public:
CacheQueue()40 CacheQueue(): first( 0 ), last( 0 ) {}
Add(Node * p)41 void Add( Node* p ) { p->queuePrev = 0; p->queueNext = first; if ( first ) { first->queuePrev = p; } else { first = last = p; } }
Del(Node * p)42 void Del( Node* p )
43 {
44 if ( p->queuePrev ) { p->queuePrev->queueNext = p->queueNext; }
45 else { first = p->queueNext; }
46
47 if ( p->queueNext ) { p->queueNext->queuePrev = p->queuePrev; }
48 else { last = p->queuePrev; }
49 };
OnTop(Node * p)50 void OnTop( Node* p ) { if ( p->queuePrev ) { Del( p ); Add( p ); } }
Clear()51 void Clear() { first = last = 0; }
First()52 Node* First() { return first; }
Last()53 Node* Last() { return last; }
54 };
55
56 struct VFCNode: public iIntrusiveCounter
57 {
58 unsigned char data[CACHE_BLOCK_SIZE];
59 };
60
61 class VFile;
62
63 class ViewerString
64 {
65 friend class VFile;
66 public:
67 struct Node
68 {
69 unicode_t ch;
70 int p;
71 };
72 private:
73 seek_t begin;
74 int bytes;
75
76 int len;
77 int dataSize;
78 std::vector<Node> data;
79 public:
ViewerString()80 ViewerString(): begin( 0 ), bytes( 0 ), len( 0 ), dataSize( VIEWER_COLS ), data( VIEWER_COLS ) {}
Clear()81 void Clear() { len = 0; begin = 0; bytes = 0; }
Add(unicode_t c,int p)82 bool Add( unicode_t c, int p ) { if ( len >= dataSize ) { return false; } data[len].ch = c; data[len].p = p; len++; return true; }
Len() const83 int Len() const { return len; }
Size() const84 int Size() const { return dataSize; }
Begin() const85 seek_t Begin() const { return begin; }
Bytes() const86 int Bytes() const { return bytes; }
NextOffset() const87 seek_t NextOffset() const { return begin + bytes; }
Last()88 unicode_t Last() { return len > 0 ? data[len - 1].ch : 0; }
Pop()89 void Pop() { if ( len > 0 ) { len--; } }
Full() const90 bool Full() const { return len >= dataSize; }
Data()91 Node* Data() { return data.data(); }
~ViewerString()92 ~ViewerString() {}
93 };
94
95
96
97 class VFilePtr;
98
99 class VFile
100 {
101 friend class VFilePtr;
102 Mutex mutex;
103 int useCount;
104
105 clPtr<FS> fs;
106 FSPath path;
107 int fd;
108
109 enum { TABLESIZE = 331, MAXCOUNT = 128 };
110
111 int blockCount;
112
113 seek_t _offset;
114 seek_t _size;
115
116 int _tabSize;
117
118 struct Node
119 {
120 long bn;
121 clPtr<VFCNode> data;
122 Node* next;
123
124 Node* queueNext;
125 Node* queuePrev;
126 };
127
128 Node* htable[TABLESIZE];
129 CacheQueue<Node> queue;
130
131 void CacheNormalize();
132 clPtr<VFCNode> CacheGet( long bn );
133 clPtr<VFCNode> CacheSet( long bn, const clPtr<VFCNode>& data );
134 void CacheClear();
135 clPtr<VFCNode> _Get( long bn, FSCInfo* info, bool lockMutex );
136
137 void CheckOpen( FSCInfo* info );
138
139 time_t _lastMTime;
140 public:
141 VFile();
142 VFile( clPtr<FS> _fs, FSPath _path, seek_t _size, int tabSize );
Get(long bn,FSCInfo * info)143 clPtr<VFCNode> Get( long bn, FSCInfo* info ) { return _Get( bn, info, true ); }
144 bool CheckStat( FSCInfo* info );
Size() const145 seek_t Size() const { return _size; }
146 seek_t Align( seek_t offset, charset_struct* charset, FSCInfo* info );
147 seek_t GetPrevLine( seek_t pos, int* pCols, charset_struct* charset, bool* nlFound, FSCInfo* info );
148 int ReadBlock( seek_t pos, char* s, int size, FSCInfo* info );
149 bool ReadString( seek_t pos, ViewerString& str, charset_struct* charset, FSCInfo* info );
Uri()150 FSString Uri() { return fs.IsNull() ? FSString() : fs->Uri( path ); }
151 ~VFile();
152 };
153
154
VFile()155 VFile::VFile()
156 : useCount( 0 ), fd( -1 ), blockCount( 0 ), _offset( 0 ), _size( 0 ), _tabSize( 8 ),
157 _lastMTime( 0 )
158 {
159 int i;
160
161 for ( i = 0; i < TABLESIZE; i++ ) { htable[i] = 0; }
162 }
163
164
VFile(clPtr<FS> _fs,FSPath _path,seek_t size,int tabSize)165 VFile::VFile( clPtr<FS> _fs, FSPath _path, seek_t size, int tabSize )
166 : useCount( 0 ),
167 fs( _fs ),
168 path( _path ),
169 fd( -1 ),
170 blockCount( 0 ), _offset( 0 ), _size( size ),
171 _tabSize( tabSize ),
172 _lastMTime( 0 )
173 {
174 int i;
175
176 for ( i = 0; i < TABLESIZE; i++ ) { htable[i] = 0; }
177 }
178
CheckStat(FSCInfo * info)179 bool VFile::CheckStat( FSCInfo* info )
180 {
181 CheckOpen( info );
182 FSStat st;
183 int err = 0;
184
185 if ( fs->FStat( fd, &st, &err, info ) )
186 {
187 throw_msg( "can`t stat file '%s' :%s", fs->Uri( path ).GetUtf8(), fs->StrError( err ).GetUtf8() );
188 }
189
190 time_t t = ( time_t ) st.m_LastWriteTime;
191
192 if ( st.size != _size || t != _lastMTime )
193 {
194 _size = st.size;
195 _lastMTime = t;
196 CacheClear();
197 //printf("V file changed\n");
198 return true;
199 }
200
201 return false;
202 }
203
Align(seek_t x,charset_struct * charset,FSCInfo * info)204 seek_t VFile::Align( seek_t x, charset_struct* charset, FSCInfo* info )
205 {
206 if ( x <= 0 ) { return 0; }
207
208 if ( x >= _size ) { return _size; }
209
210 int a = 16;
211 int b = 16;
212
213 if ( a > x ) { a = int( x ); }
214
215 if ( x + b > _size )
216 {
217 b = int( _size - x );
218 }
219
220 char buf[64];
221 int n = a + b;
222
223 if ( n <= 0 ) { return x; }
224
225 int bytes = this->ReadBlock( x - a, buf, n, info );
226
227 if ( bytes <= n ) { return x; }
228
229 char* s = buf + a;
230
231 if ( ( *s >= 0 && *s <= ' ' ) || ( s[-1] >= 0 && s[-1] <= ' ' ) ) { return x; }
232
233 s = charset->GetPrev( s + 1, buf );
234 return s ? ( x - a ) + ( s - buf ) : x;
235 }
236
CacheClear()237 void VFile::CacheClear()
238 {
239
240 queue.Clear();
241
242 int i;
243
244 for ( i = 0; i < TABLESIZE; i++ )
245 {
246 Node* p = htable[i];
247
248 while ( p )
249 {
250 Node* t = p;
251 p = p->next;
252 delete t;
253 }
254
255 htable[i] = 0;
256 }
257 }
258
259
260
~VFile()261 VFile::~VFile()
262 {
263 CacheClear();
264
265 if ( fd >= 0 )
266 {
267 fs->Close( fd, 0, 0 );
268 }
269 }
270
271
CacheGet(long bn)272 clPtr<VFCNode> VFile::CacheGet( long bn )
273 {
274
275 {
276 Node* p = queue.First();
277
278 if ( p && p->bn == bn )
279 {
280 return p->data;
281 }
282 }
283
284
285 int n = bn % TABLESIZE;
286
287 for ( Node* p = htable[n]; p; p = p->next )
288 if ( p->bn == bn )
289 {
290 queue.OnTop( p );
291 return p->data;
292 }
293
294 return 0;
295 }
296
CacheNormalize()297 void VFile::CacheNormalize()
298 {
299 while ( blockCount >= MAXCOUNT )
300 {
301 Node* p = queue.Last();
302 ASSERT( p );
303
304 queue.Del( p );
305
306 int k = p->bn % TABLESIZE;
307
308 Node** t = &( htable[k] );
309
310 for ( ; *t; t = &( t[0]->next ) )
311 if ( t[0] == p )
312 {
313 t[0] = t[0]->next;
314 delete p;
315 t = 0;
316 break;
317 }
318
319 ASSERT( t == 0 );
320 blockCount --;
321 }
322 }
323
CacheSet(long bn,const clPtr<VFCNode> & data)324 clPtr<VFCNode> VFile::CacheSet( long bn, const clPtr<VFCNode>& data )
325 {
326 int n = bn % TABLESIZE;
327 Node* p;
328
329 for ( p = htable[n]; p; p = p->next )
330 if ( p->bn == bn )
331 {
332 p->data = data;
333 queue.OnTop( p );
334 return data;
335 }
336
337 CacheNormalize();
338
339 p = new Node;
340 p->bn = bn;
341 p->data = data;
342 p->next = htable[n];
343 htable[n] = p;
344
345 queue.Add( p );
346 return data;
347 }
348
CheckOpen(FSCInfo * info)349 void VFile::CheckOpen( FSCInfo* info )
350 {
351 if ( fd >= 0 ) { return; }
352
353 if ( !fs.Ptr() )
354 {
355 throw_msg( "BOTVA: VFile fs not defined" );
356 }
357
358 int err;
359 int ret = fs->OpenRead( path, FS::SHARE_READ | FS::SHARE_WRITE, &err, info );
360
361 //const char *ss = path.GetUtf8();
362
363 if ( ret == -2 )
364 {
365 throw_stop();
366 }
367
368 if ( ret < 0 )
369 {
370 throw_msg( "can`t open file '%s' :%s", fs->Uri( path ).GetUtf8(), fs->StrError( err ).GetUtf8() );
371 }
372
373 fd = ret;
374
375 if ( info && info->IsStopped() ) { throw_stop(); }
376 }
377
_Get(long bn,FSCInfo * info,bool lockMutex)378 clPtr<VFCNode> VFile::_Get( long bn, FSCInfo* info, bool lockMutex )
379 {
380 MutexLock lock( &mutex, lockMutex );
381
382 clPtr<VFCNode> ptr = CacheGet( bn );
383
384 if ( ptr ) { return ptr; }
385
386 CheckOpen( info );
387
388 int err;
389
390 seek_t pos = seek_t( bn ) * CACHE_BLOCK_SIZE;
391
392 if ( pos != _offset )
393 {
394 int ret = fs->Seek( fd, FSEEK_BEGIN, pos, 0, &err, info );
395
396 if ( ret )
397 {
398 if ( ret == -2 )
399 {
400 throw_stop();
401 }
402 else
403 {
404 throw_msg( "cant set file position (%s)", fs->StrError( err ).GetUtf8() );
405 }
406 }
407
408 _offset = pos;
409
410 if ( info && info->IsStopped() ) { throw_stop(); }
411 }
412
413 ptr = new VFCNode;
414
415 const unsigned char* s = reinterpret_cast<unsigned char*>( &(ptr->data) );
416 ASSERT( s );
417 memset( ( unsigned char* )s, 0, CACHE_BLOCK_SIZE );
418
419 int size = CACHE_BLOCK_SIZE;
420
421 //fs может читать клочками, а не сразу весь блок, поэтому цикл
422 while ( size > 0 )
423 {
424 int bytes = fs->Read( fd, ( unsigned char* )s, size, &err, info );
425
426 if ( !bytes ) { break; }
427
428 if ( bytes == -2 )
429 {
430 throw_stop();
431 }
432
433 if ( bytes < 0 )
434 {
435 throw_msg( "cant read file (%s)", fs->StrError( err ).GetUtf8() );
436 }
437
438 s += bytes;
439 size -= bytes;
440 _offset += bytes;
441 }
442
443
444 CacheSet( bn, ptr );
445
446 if ( info && info->IsStopped() ) { throw_stop(); }
447
448 return ptr;
449 }
450 /*
451 static const unsigned char* StrLastNL( const unsigned char* ptr, int n )
452 {
453 if ( !ptr ) { return 0; }
454
455 const unsigned char* last = 0;
456
457 for ( const unsigned char* s = ptr, *end = ptr + n; s < end; s++ )
458 if ( *s == '\n' ) { last = s; }
459
460 return last;
461 }
462
463 static const unsigned char* StrFirstNL( const unsigned char* ptr, int n )
464 {
465 if ( !ptr ) { return 0; }
466
467 for ( const unsigned char* s = ptr, *end = ptr + n; s < end; s++ )
468 if ( *s == '\n' ) { return s; }
469
470 return 0;
471 }
472 */
GetPrevLine(seek_t filePos,int * pCols,charset_struct * charset,bool * nlFound,FSCInfo * info)473 seek_t VFile::GetPrevLine( seek_t filePos, int* pCols, charset_struct* charset, bool* nlFound, FSCInfo* info )
474 {
475 char tabChar = charset->tabChar;
476
477 if ( filePos <= 0 ) { return filePos; }
478
479 if ( nlFound ) { *nlFound = false; }
480
481 char buf[0x100];
482 int n = int( (size_t) filePos > sizeof( buf ) ? sizeof( buf ) : filePos );
483 filePos -= n;
484 int count = ReadBlock( filePos, buf, n, info );
485
486 if ( count != n )
487 {
488 if ( nlFound ) { *nlFound = true; }
489
490 return 0;
491 }
492
493 char* s = buf + count;
494
495 if ( s[-1] == '\n' )
496 {
497 if ( s - 1 <= buf )
498 {
499 return filePos;
500 }
501
502 s--;
503
504 if ( s[-1] == '\r' )
505 {
506 if ( s - 1 <= buf )
507 {
508 return filePos;
509 }
510
511 s--;
512 }
513 }
514
515 int tabSize = _tabSize;
516 int freeCols = VIEWER_COLS;
517 int tab = 0;
518
519 while ( freeCols > 0 )
520 {
521 if ( s < buf + 16 && filePos > 0 )
522 {
523 int m = s - buf;
524 int n = sizeof( buf ) - m;
525
526 if ( n > filePos ) { n = int( filePos ); }
527
528 if ( m > 0 ) { memmove( buf + n, buf, m ); }
529
530 filePos -= n;
531 int count = ReadBlock( filePos, buf, n, info );
532
533 if ( count != n )
534 {
535 return 0;
536 }
537
538 s += n;
539 }
540
541 char* t = charset->GetPrev( s, buf );
542
543 if ( !t )
544 {
545 return filePos;
546 }
547
548 if ( *t == '\n' )
549 {
550 if ( nlFound ) { *nlFound = true; }
551
552 break;
553 }
554
555 if ( *t == tabChar/*'\t'*/ )
556 {
557 if ( freeCols < tabSize )
558 {
559 break;
560 }
561
562 tab = tabSize;
563 freeCols -= tabSize;
564 }
565 else
566 {
567 if ( tab > 0 )
568 {
569 if ( tab == 1 )
570 {
571 if ( freeCols < tabSize )
572 {
573 break;
574 }
575
576 tab = tabSize;
577 freeCols -= tabSize;
578 }
579 else
580 {
581 tab--;
582 }
583 }
584 else
585 {
586 freeCols--;
587 }
588 }
589
590 s = t;
591 }
592
593 if ( pCols ) { *pCols = VIEWER_COLS - freeCols; }
594
595 return filePos + ( s - buf );
596 }
597
ReadBlock(seek_t offset,char * s,int count,FSCInfo * info)598 int VFile::ReadBlock( seek_t offset, char* s, int count, FSCInfo* info )
599 {
600 int bn = int( offset / CACHE_BLOCK_SIZE );
601 int pos = int( offset % CACHE_BLOCK_SIZE );
602 clPtr<VFCNode> ptr = Get( bn, info );
603
604 if ( offset > _size ) { return 0; }
605
606 if ( count + offset > _size ) { count = int( _size - offset ); }
607
608 int ret = count;
609
610
611 while ( count > 0 )
612 {
613 int n = CACHE_BLOCK_SIZE - pos;
614
615 if ( n > count )
616 {
617 n = count;
618 }
619
620 if ( n > 0 )
621 {
622 memcpy( s, ptr->data + pos, n );
623 count -= n;
624 s += n;
625
626 if ( count <= 0 ) { break; }
627 }
628
629 bn++;
630 pos = 0;
631 ptr = Get( bn, info );
632 }
633
634 return ret;
635 }
636
ReadString(seek_t offset,ViewerString & str,charset_struct * charset,FSCInfo * info)637 bool VFile::ReadString( seek_t offset, ViewerString& str, charset_struct* charset, FSCInfo* info )
638 {
639 int tabSize = _tabSize;
640
641 str.Clear();
642 str.begin = offset;
643
644 if ( offset >= Size() )
645 {
646 return false;
647 }
648
649 char buf[0x100];
650 int pos = 0;
651
652 int count = ReadBlock( offset, buf, sizeof( buf ), info );
653 offset += count;
654 int lbPos = 0; //начало буфера от начала строки в байтах
655
656 char tabChar = charset->tabChar;
657
658 while ( true )
659 {
660 if ( count - pos < 32 )
661 {
662 if ( pos < count && pos > 0 )
663 {
664 memmove( buf, buf + pos, count - pos );
665 lbPos += pos;
666 count -= pos;
667 pos = 0;
668 }
669
670 int n = ReadBlock( offset, buf + count, sizeof( buf ) - count, info );
671
672 offset += n;
673 count += n;
674 }
675
676 if ( pos >= count ) { break; }
677
678 if ( buf[pos] == '\n' )
679 {
680 if ( str.Last() == '\r' ) { str.Pop(); }
681
682 pos++;
683 break;
684 }
685
686 if ( str.Full() ) { break; }
687
688
689 int cp = lbPos + pos;
690
691 if ( buf[pos] == tabChar /*'\t'*/ )
692 {
693 pos++;
694
695 for ( int n = tabSize - ( str.Len() % tabSize ); n > 0; n-- )
696 if ( !str.Add( ' ', cp ) )
697 {
698 return true;
699 }
700
701 continue;
702 }
703
704 //bool tab;
705 unicode_t c = charset->GetChar( buf + pos, buf + count );
706 char* s = charset->GetNext( buf + pos, buf + count );
707
708 pos = s ? s - buf : count;
709 str.Add( c, cp );
710
711 }
712
713 str.bytes = lbPos + pos;
714
715 return true;
716 }
717
718
719
720
721 class VFilePtr
722 {
723 VFile* ptr;
724
Clear()725 void Clear()
726 {
727 if ( ptr )
728 {
729 MutexLock lock( &ptr->mutex );
730 ptr->useCount--;
731
732 if ( ptr->useCount <= 0 )
733 {
734 lock.Unlock(); //!!!
735 delete ptr;
736 }
737
738 ptr = 0;
739 }
740 };
741 public:
VFilePtr()742 VFilePtr(): ptr( 0 ) {}
VFilePtr(VFile * p)743 VFilePtr( VFile* p ): ptr( p )
744 {
745 if ( ptr )
746 {
747 ptr->useCount++;
748 }
749 }
VFilePtr(const VFilePtr & a)750 VFilePtr( const VFilePtr& a )
751 : ptr( a.ptr )
752 {
753 if ( ptr )
754 {
755 MutexLock lock( &ptr->mutex );
756 ptr->useCount++;
757 }
758 }
759
operator =(const VFilePtr & a)760 VFilePtr& operator = ( const VFilePtr& a )
761 {
762 Clear();
763 ptr = a.ptr;
764
765 if ( ptr )
766 {
767 MutexLock lock( &ptr->mutex );
768 ptr->useCount++;
769 }
770
771 return *this;
772 }
773
Ptr()774 VFile* Ptr() { return ptr; }
operator ->()775 VFile* operator ->() { return ptr; }
~VFilePtr()776 ~VFilePtr() { Clear(); }
777 };
778
779
780
781 class FSCViewerInfo: public FSCInfo
782 {
783 public:
784 Mutex mutex;
785 bool stopped;
FSCViewerInfo()786 FSCViewerInfo(): stopped( false ) {}
Reset()787 void Reset() { MutexLock lock( &mutex ); stopped = false; }
SetStop()788 void SetStop() { MutexLock lock( &mutex ); stopped = true; }
789 virtual bool Stopped();
790 virtual ~FSCViewerInfo();
791 };
792
Stopped()793 bool FSCViewerInfo::Stopped()
794 {
795 MutexLock lock( &mutex );
796 return stopped;
797 }
798
~FSCViewerInfo()799 FSCViewerInfo::~FSCViewerInfo() {}
800
801
802
803 struct ViewerEvent
804 {
805 enum
806 {
807 NO = 0,
808 SET, //set offset, track must be in first line
809 VTRACK, HTRACK,
810 UP, DOWN, LEFT, RIGHT,
811 PAGEUP, PAGEDOWN, PAGELEFT, PAGERIGHT,
812 HOME, END,
813 LEFTSTEP, RIGHTSTEP,
814 FOUND
815 };
816
817 int type;
818 int64_t track;
819 int64_t e;
820
ViewerEventViewerEvent821 ViewerEvent(): type( 0 ), track( 0 ), e( 0 ) {}
ViewerEventViewerEvent822 ViewerEvent( int t ): type( t ), track( 0 ), e( 0 ) {}
ViewerEventViewerEvent823 ViewerEvent( int t, int64_t tr ): type( t ), track( tr ), e( 0 ) {}
ViewerEventViewerEvent824 ViewerEvent( int t, int64_t tr, int64_t n1 ): type( t ), track( tr ), e( n1 ) {}
ClearViewerEvent825 void Clear() { type = NO; }
826 };
827
828
829 class ViewerThreadData
830 {
831 static int NewTid();
832 int tid;
833 VFilePtr file;
834 public:
835 seek_t initOffset;
836
837 enum FLAGS { FSTOP = 1, FMODE = 2, FSIZE = 4, FEVENT = 8, FTIMER = 0x10 };
838
839 FSCViewerInfo info;
840
841 Mutex mutex;
842 Cond cond;
843
844 int inFlags;
845 ViewerEvent inEvent;
846 ViewerMode inMode;
847 ViewerSize inSize;
848
849 void SetEvent( const ViewerEvent& e );
850
851 VSData ret;
852 VFPos pos;
853 std::string m_Error;
854 int64_t loadStartTime;
855
Id() const856 int Id() const {return tid; }
857
ViewerThreadData(VFilePtr f)858 ViewerThreadData( VFilePtr f ): tid( NewTid() ), file( f ), initOffset( 0 ), inFlags( 0 ), loadStartTime( 0 ) {}
859
File()860 VFile* File() { return file.Ptr(); }
FilePtr()861 VFilePtr FilePtr() { return file; }
862
863 };
864
NewTid()865 int ViewerThreadData::NewTid()
866 {
867 static int lastId = 0;
868 lastId = ( lastId + 1 ) % 0x10000;
869 return lastId + 1;
870 }
871
SetEvent(const ViewerEvent & e)872 void ViewerThreadData::SetEvent( const ViewerEvent& e )
873 {
874 MutexLock lock( &mutex );
875 inEvent = e;
876 inFlags |= FEVENT;
877 cond.Signal();
878 }
879
VStrWrapCount(int lineCols,int cols)880 inline int VStrWrapCount( int lineCols, int cols )
881 {
882 if ( lineCols <= cols ) { return 1; }
883
884 if ( lineCols % cols )
885 {
886 return lineCols / cols + 1;
887 }
888
889 return lineCols / cols;
890 }
891
892
ViewerThread(void * param)893 void* ViewerThread( void* param )
894 {
895 static const int HSTEP = 20;
896 // static const int tabSize = 8;
897
898 ASSERT( param );
899 ViewerThreadData* tData = ( ViewerThreadData* ) param;
900
901 VSData ret;
902 VFPos pos;
903 VFile* file = tData->File();
904 ViewerString str;
905 VMarker marker;
906
907 bool toEndOnChange = false;
908
909 while ( true )
910 {
911 try
912 {
913 int flags;
914 ViewerMode mode;
915 ViewerSize size;
916 ViewerEvent event;
917
918 {
919 //lock
920 MutexLock lock( &tData->mutex );
921
922 if ( !tData->inFlags )
923 {
924 tData->cond.Wait( &tData->mutex );
925 }
926
927 flags = tData->inFlags;
928
929 if ( ( flags & ViewerThreadData::FSTOP ) != 0 ) { break; }
930
931 mode = tData->inMode;
932 size = tData->inSize;
933 event = tData->inEvent;
934 tData->inFlags = 0;
935 };
936
937 charset_struct* charset = mode.charset;
938
939 ret.SetSize( size );
940 ret.mode = mode;
941
942 if ( flags == ViewerThreadData::FTIMER )
943 {
944 if ( !file->CheckStat( &tData->info ) )
945 {
946 continue;
947 }
948
949 if ( !( flags & ViewerThreadData::FEVENT ) && toEndOnChange )
950 {
951 flags |= ViewerThreadData::FEVENT;
952 event.type = ViewerEvent::END;
953 }
954
955 }
956 else
957 {
958 MutexLock lock( &tData->mutex );
959 tData->loadStartTime = time( 0 );
960
961 file->CheckStat( &tData->info );
962 }
963
964 if ( ( flags & ViewerThreadData::FSIZE ) || ( flags & ViewerThreadData::FMODE ) )
965 {
966 pos.col = 0;
967 pos.begin = file->Align( pos.begin, charset, &tData->info );
968 marker.Clear();
969 toEndOnChange = false;
970 }
971
972 if ( flags & ViewerThreadData::FEVENT )
973 {
974 toEndOnChange = false;
975
976 switch ( event.type )
977 {
978 case ViewerEvent::FOUND:
979 marker.Set( event.track, event.e );
980 break;
981
982 case ViewerEvent::END:
983 toEndOnChange = true;
984
985 //no break
986 case ViewerEvent::VTRACK:
987 case ViewerEvent::UP:
988 case ViewerEvent::DOWN:
989 case ViewerEvent::PAGEUP:
990 case ViewerEvent::PAGEDOWN:
991 case ViewerEvent::HOME:
992 marker.Clear();
993 break;
994 }
995
996
997 if ( mode.hex )
998 {
999 seek_t fSize = file->Size();
1000 int bytes = size.cols * size.rows;
1001
1002 switch ( event.type )
1003 {
1004 case ViewerEvent::SET:
1005 pos.begin = std::max( ( int64_t )0, event.track );
1006 break;
1007
1008 case ViewerEvent::HOME:
1009 pos.begin = 0;
1010 break;
1011
1012 case ViewerEvent::END:
1013 if ( fSize >= bytes ) { pos.begin = fSize - bytes; }
1014
1015 break;
1016
1017 case ViewerEvent::DOWN:
1018 if ( pos.begin + bytes < fSize ) { pos.begin += size.cols; }
1019
1020 break;
1021
1022 case ViewerEvent::UP:
1023 pos.begin = pos.begin > size.cols ? pos.begin - size.cols : 0;
1024 break;
1025
1026 case ViewerEvent::LEFTSTEP:
1027 if ( pos.begin > 0 ) { pos.begin--; }
1028
1029 break;
1030
1031 case ViewerEvent::RIGHTSTEP:
1032 if ( pos.begin + 1 < fSize ) { pos.begin++; }
1033
1034 break;
1035
1036 case ViewerEvent::FOUND:
1037 case ViewerEvent::VTRACK:
1038 pos.begin = event.track;
1039
1040 if ( pos.begin < 0 ) { pos.begin = 0; }
1041
1042 break;
1043
1044 case ViewerEvent::PAGEUP:
1045 {
1046 int n = ( size.rows > 1 ? size.rows - 1 : 1 ) * size.cols;
1047 pos.begin = pos.begin > n ? pos.begin - n : 0;
1048 }
1049 break;
1050
1051 case ViewerEvent::PAGEDOWN:
1052 {
1053 int n = ( size.rows > 1 ? size.rows - 1 : 1 ) * size.cols;
1054
1055 if ( pos.begin + bytes < fSize )
1056 {
1057 pos.begin += n;
1058 }
1059 }
1060 break;
1061 };
1062 }
1063 else if ( mode.wrap )
1064 {
1065 seek_t p;
1066
1067 switch ( event.type )
1068 {
1069 case ViewerEvent::SET:
1070 pos.begin = std::max( ( int64_t )0, event.track );
1071 pos.col = 0;
1072 break;
1073
1074 case ViewerEvent::HOME:
1075 pos.begin = 0;
1076 pos.col = 0;
1077 break;
1078
1079 case ViewerEvent::END:
1080 {
1081 p = file->Size();
1082
1083 int col = 0;
1084 int r = size.rows;
1085
1086 while ( r > 0 )
1087 {
1088 int lineCols = 0;
1089 p = file->GetPrevLine( p, &lineCols, charset, 0, &tData->info );
1090
1091 if ( !p ) { break; }
1092
1093 int n = VStrWrapCount( lineCols, size.cols );
1094
1095 if ( n > r )
1096 {
1097 col = ( n - r ) * size.cols;
1098 break;
1099 }
1100 else
1101 {
1102 r -= n;
1103 }
1104 }
1105
1106 pos.begin = p;
1107 pos.col = col;
1108 }
1109 break;
1110
1111 case ViewerEvent::DOWN:
1112 if ( file->ReadString( pos.begin, str, charset, &tData->info ) )
1113 {
1114 if ( str.Len() - pos.col < size.cols )
1115 {
1116 p = str.Begin() + str.Bytes();
1117
1118 if ( p && p < file->Size() )
1119 {
1120 pos.begin = p;
1121 pos.col = 0;
1122 }
1123 }
1124 else
1125 {
1126 pos.col += size.cols;
1127 }
1128 }
1129
1130 break;
1131
1132 case ViewerEvent::UP:
1133 if ( pos.col > 0 )
1134 {
1135 if ( pos.col >= size.cols )
1136 {
1137 pos.col -= size.cols;
1138 }
1139 else
1140 {
1141 pos.col = 0;
1142 }
1143 }
1144 else
1145 {
1146 if ( pos.begin > 0 )
1147 {
1148 int lineCols = 0;
1149 p = file->GetPrevLine( pos.begin, &lineCols, charset, 0, &tData->info );
1150 pos.begin = p;
1151 pos.col = ( VStrWrapCount( lineCols, size.cols ) - 1 ) * size.cols;
1152 }
1153 }
1154
1155 break;
1156
1157 case ViewerEvent::VTRACK:
1158 {
1159 seek_t n = event.track;
1160
1161 if ( n < 0 ) { n = 0; }
1162
1163 pos.begin = file->GetPrevLine( file->Align( n, charset, &tData->info ), 0, charset, 0, &tData->info );
1164 pos.col = 0;
1165 }
1166 break;
1167
1168 //temp
1169 case ViewerEvent::FOUND:
1170 {
1171 bool realLine = false;
1172 int lineCols = 0;
1173 seek_t n = file->GetPrevLine( file->Align( event.track + 1, charset, &tData->info ), &lineCols, charset, &realLine, &tData->info );
1174
1175 if ( realLine )
1176 {
1177 pos.begin = n;
1178 pos.col = ( VStrWrapCount( lineCols, size.cols ) - 1 ) * size.cols;
1179 }
1180 else
1181 {
1182 pos.begin = event.track;
1183 pos.col = 0;
1184 }
1185 }
1186 break;
1187
1188 case ViewerEvent::PAGEUP:
1189 if ( pos.begin > 0 && size.cols > 0 )
1190 {
1191
1192 p = pos.begin;
1193
1194 int col = pos.col;
1195 int r = size.rows - 1;
1196
1197 if ( r < 1 ) { r = 1; }
1198
1199 int n = ( col + size.cols - 1 ) / size.cols;
1200
1201 while ( true )
1202 {
1203 if ( n <= 0 )
1204 {
1205 col = 0;
1206
1207 if ( p <= 0 ) { break; }
1208
1209 int lineCols = 0;
1210 p = file->GetPrevLine( p, &lineCols, charset, 0, &tData->info );
1211
1212 if ( !p ) { break; }
1213
1214 n = VStrWrapCount( lineCols, size.cols );
1215
1216 if ( n <= 0 ) { break; }
1217 }
1218
1219 if ( n >= r )
1220 {
1221 col = ( n - r ) * size.cols;
1222 break;
1223 }
1224 else
1225 {
1226 r -= n;
1227 n = 0;
1228 };
1229 }
1230
1231 pos.begin = p > 0 ? p : 0;
1232 pos.col = col;
1233
1234 }
1235
1236 break;
1237
1238 case ViewerEvent::PAGEDOWN:
1239 if ( size.cols > 0 )
1240 {
1241
1242 p = pos.begin;
1243
1244 int col = pos.col;
1245 int r = size.rows - 1;
1246
1247 if ( r < 1 ) { r = 1; }
1248
1249 while ( r > 0 )
1250 {
1251 if ( !file->ReadString( p, str, charset, &tData->info ) )
1252 {
1253 break;
1254 }
1255
1256 int n = VStrWrapCount( str.Len(), size.cols ) - ( col + size.cols - 1 ) / size.cols;
1257
1258 if ( n < 0 ) { break; }
1259
1260 if ( n >= r )
1261 {
1262 col += r * size.cols;
1263 break;
1264 }
1265
1266 col = 0;
1267 r -= n;
1268
1269 if ( str.Begin() + str.Bytes() >= file->Size() )
1270 {
1271 p = str.Begin();
1272 col = ( ( n > 0 ) ? n - 1 : 0 ) * size.cols;
1273 break;
1274 }
1275
1276 p = str.Begin() + str.Bytes();
1277 }
1278
1279 pos.begin = p;
1280 pos.col = col;
1281 }
1282
1283 break;
1284
1285 }
1286 }
1287 else
1288 {
1289 seek_t p;
1290
1291 switch ( event.type )
1292 {
1293 case ViewerEvent::SET:
1294 pos.begin = std::max( 0, ( int )event.track );
1295 pos.col = 0;
1296 break;
1297
1298 case ViewerEvent::HOME:
1299 pos.begin = 0;
1300 pos.col = 0;
1301 break;
1302
1303 case ViewerEvent::END:
1304 {
1305 p = file->Size();
1306
1307 for ( int i = 0; p > 0 && i < size.rows; i++ )
1308 {
1309 p = file->GetPrevLine( p, 0, charset, 0, &tData->info );
1310 }
1311
1312 pos.begin = p;
1313 pos.col = 0;
1314 }
1315 break;
1316
1317 case ViewerEvent::DOWN:
1318 if ( file->ReadString( pos.begin, str, charset, &tData->info ) )
1319 {
1320 if ( str.NextOffset() < file->Size() )
1321 {
1322 pos.begin = str.NextOffset();
1323 }
1324 }
1325
1326 break;
1327
1328 case ViewerEvent::UP:
1329 if ( pos.begin > 0 )
1330 {
1331 pos.begin = file->GetPrevLine( pos.begin, 0, charset, 0, &tData->info );
1332 }
1333
1334 break;
1335
1336 case ViewerEvent::LEFT:
1337 if ( pos.col > 0 ) { pos.col--; }
1338
1339 break;
1340
1341 case ViewerEvent::RIGHT:
1342 if ( pos.col + size.cols < pos.maxLine )
1343 {
1344 pos.col++;
1345 }
1346
1347 break;
1348
1349 case ViewerEvent::LEFTSTEP:
1350 pos.col = pos.col > HSTEP ? pos.col - HSTEP : 0;
1351 break;
1352
1353 case ViewerEvent::RIGHTSTEP:
1354 {
1355 int n = pos.maxLine - ( pos.col + size.cols );
1356
1357 if ( n > HSTEP ) { n = HSTEP; }
1358
1359 if ( n > 0 ) { pos.col += n; }
1360 }
1361 break;
1362
1363 case ViewerEvent::PAGELEFT:
1364 {
1365 int step = size.cols - 1;
1366
1367 if ( step > 0 )
1368 {
1369 pos.col = pos.col > step ? pos.col - step : 0;
1370 }
1371 }
1372 break;
1373
1374 case ViewerEvent::PAGERIGHT:
1375 {
1376 int step = size.cols - 1;
1377 int n = pos.maxLine - ( pos.col + size.cols );
1378
1379 if ( n > step ) { n = step; }
1380
1381 if ( n > 0 ) { pos.col += n; }
1382 }
1383 break;
1384
1385 case ViewerEvent::HTRACK:
1386 {
1387 seek_t n = event.track;
1388
1389 if ( n > pos.maxLine - size.cols ) { n = pos.maxLine - size.cols; }
1390
1391 if ( n < 0 ) { n = 0; }
1392
1393 pos.col = ( int )n;
1394 }
1395 break;
1396
1397 case ViewerEvent::VTRACK:
1398 {
1399 seek_t n = event.track;
1400
1401 if ( n < 0 ) { n = 0; }
1402
1403 pos.begin = file->GetPrevLine( file->Align( n, charset, &tData->info ), 0, charset, 0, &tData->info );
1404 }
1405 break;
1406
1407 //temp
1408 case ViewerEvent::FOUND:
1409 {
1410 bool realLine = false;
1411 int lineCols = 0;
1412 seek_t n = file->GetPrevLine( file->Align( event.track + 1, charset, &tData->info ), &lineCols, charset, &realLine, &tData->info );
1413
1414 if ( realLine )
1415 {
1416 pos.begin = n;
1417
1418 if ( pos.col + size.cols <= lineCols )
1419 {
1420 pos.col = lineCols - 5;
1421
1422 if ( pos.col < 0 ) { pos.col = 0; }
1423 }
1424 }
1425 else
1426 {
1427 pos.begin = event.track;
1428 pos.col = 0;
1429 }
1430 }
1431 break;
1432
1433 case ViewerEvent::PAGEUP:
1434 if ( pos.begin > 0 )
1435 {
1436 p = pos.begin;
1437
1438 for ( int i = 0; p > 0 && i < size.rows - 1; i++ )
1439 {
1440 p = file->GetPrevLine( p, 0, charset, 0, &tData->info );
1441 }
1442
1443 pos.begin = p;
1444 }
1445
1446 break;
1447
1448 case ViewerEvent::PAGEDOWN:
1449 {
1450 p = pos.begin;
1451
1452 for ( int i = 0; i < size.rows - 1; i++ )
1453 {
1454 if ( !file->ReadString( p, str, charset, &tData->info ) )
1455 {
1456 break;
1457 }
1458
1459 if ( str.NextOffset() >= file->Size() ) { break; }
1460
1461 p = str.NextOffset();
1462 }
1463
1464 if ( p ) { pos.begin = p; }
1465 }
1466 break;
1467
1468 };
1469 };
1470 }
1471
1472 //Sleep(1000);
1473
1474 if ( pos.begin >= file->Size() )
1475 {
1476 pos.begin = 0;
1477 pos.col = 0;
1478 }
1479
1480 if ( mode.hex )
1481 {
1482 int count = size.rows * size.cols;
1483 unicode_t* p = ret.data.data();
1484 char* attr = ret.attr.data();
1485
1486 seek_t offset = pos.begin;
1487
1488 while ( count > 0 )
1489 {
1490 unsigned char buf[0x100];
1491 long n = (size_t) count > sizeof( buf ) ? sizeof( buf ) : (size_t) count;
1492 n = file->ReadBlock( offset, ( char* )buf, n, &tData->info );
1493
1494 if ( n <= 0 ) { break; }
1495
1496 for ( int i = 0; i < n; i++, p++, attr++ )
1497 {
1498 *p = buf[i];
1499 *attr = marker.In( offset + i ) ? '\1' : '\0';
1500 }
1501
1502 offset += n;
1503 count -= n;
1504 }
1505
1506 for ( ; count > 0; count--, p++, attr++ )
1507 {
1508 *p = 0x100;
1509 *attr = 0;
1510 }
1511
1512 pos.col = 0;
1513 pos.end = pos.begin + size.cols * size.rows;
1514 pos.size = file->Size();
1515 }
1516 else if ( mode.wrap )
1517 {
1518 seek_t offset = pos.begin;
1519 ViewerString str;
1520 tData->File()->ReadString( offset, str, charset, &tData->info );
1521 int col = pos.col;
1522 unicode_t* p = ret.data.data();
1523 char* attr = ret.attr.data();
1524 int r;
1525
1526 for ( r = 0; r < size.rows; r++, p += size.cols, attr += size.cols )
1527 {
1528 int len = str.Len();
1529 int i;
1530
1531 for ( i = 0; i + col < len && i < size.cols; i++ )
1532 {
1533 unicode_t c = str.Data()[i + col].ch;
1534 p[i] = c;
1535 attr[i] = marker.In( str.Data()[i + col].p + str.Begin() ) ? 1 : 0;
1536 }
1537
1538 for ( ; i < size.cols; i++ )
1539 {
1540 p[i] = ' ';
1541 attr[i] = 0;
1542 }
1543
1544 col += size.cols;
1545
1546 if ( col >= len )
1547 {
1548 if ( str.NextOffset() >= file->Size() ||
1549 !file->ReadString( str.NextOffset(), str, charset, &tData->info ) )
1550 {
1551 r++;
1552 p += size.cols;
1553 attr += size.cols;
1554 break;
1555 }
1556
1557 col = 0;
1558 }
1559 }
1560
1561 for ( ; r < size.rows; r++, p += size.cols, attr += size.cols )
1562 {
1563 for ( int i = 0; i < size.cols; i++ )
1564 {
1565 p[i] = ' ';
1566 attr[i] = 0;
1567 }
1568 }
1569
1570 pos.end = str.NextOffset();
1571 pos.size = file->Size();
1572
1573 }
1574 else
1575 {
1576 seek_t offset = pos.begin;
1577 ViewerString str;
1578 unicode_t* p = ret.data.data();
1579 char* attr = ret.attr.data();
1580 int r;
1581
1582 for ( r = 0; r < size.rows;
1583 r++, p += size.cols, attr += size.cols )
1584 {
1585 if ( tData->File()->ReadString( offset, str, charset, &tData->info ) )
1586 {
1587
1588 if ( str.Len() > pos.maxLine )
1589 {
1590 pos.maxLine = str.Len();
1591 }
1592
1593 int i = 0;
1594
1595 for ( i = 0; i + pos.col < str.Len() && i < size.cols; i++ )
1596 {
1597 unicode_t c = str.Data()[i + pos.col].ch;
1598 p[i] = c;
1599 attr[i] = marker.In( str.Data()[i + pos.col].p + str.Begin() ) ? 1 : 0;
1600 }
1601
1602 for ( ; i < size.cols; i++ )
1603 {
1604 p[i] = ' ';
1605 attr[i] = 0;
1606 }
1607 }
1608 else
1609 {
1610 break;
1611 }
1612
1613 offset = str.NextOffset();
1614
1615 if ( offset >= file->Size() )
1616 {
1617 r++;
1618 p += size.cols;
1619 attr += size.cols;
1620 break;
1621 }
1622 }
1623
1624 for ( ; r < size.rows; r++, p += size.cols, attr += size.cols )
1625 {
1626 for ( int i = 0; i < size.cols; i++ )
1627 {
1628 p[i] = ' ';
1629 attr[i] = 0;
1630 }
1631 }
1632
1633 pos.end = str.NextOffset();
1634 pos.size = file->Size();
1635
1636 }
1637
1638 pos.marker = marker;
1639
1640 {
1641 //lock
1642 MutexLock lock( &tData->mutex );
1643 tData->ret = ret;
1644 tData->pos = pos;
1645 WinThreadSignal( 0 );
1646 }
1647
1648
1649 }
1650 catch ( cstop_exception* sx )
1651 {
1652 tData->info.Reset();
1653 sx->destroy();
1654 WinThreadSignal( 0 );
1655 }
1656 catch ( cexception* ex )
1657 {
1658 tData->m_Error = ex->message();
1659 ex->destroy();
1660 WinThreadSignal( 0 );
1661 }
1662 catch ( ... )
1663 {
1664 tData->m_Error = "BUG: unhandled exception in void *ViewerThread(void *param)";
1665 WinThreadSignal( 0 );
1666 }
1667
1668 {
1669 MutexLock lock( &tData->mutex );
1670 tData->loadStartTime = 0;
1671 }
1672 }
1673
1674 try
1675 {
1676 delete tData;
1677 }
1678 catch ( cexception* ex )
1679 {
1680 ex->destroy();
1681 }
1682 catch ( ... )
1683 {
1684 //???
1685 }
1686
1687 return 0;
1688 }
1689
1690
ViewWin(Win * parent)1691 ViewWin::ViewWin( Win* parent )
1692 : Win( WT_CHILD, 0, parent, 0, 0 )
1693 , _lo( 5, 5 )
1694 , threadData( 0 )
1695 , vscroll( 0, this, true, false )
1696 , hscroll( 0, this, false, true )
1697 , charset( charset_table[GetFirstOperCharsetId()] )
1698 , wrap( true )
1699 , hex( false )
1700 , loadingText( utf8_to_unicode( " ... Loading ... " ) )
1701 , drawLoading( false )
1702 , m_TempDirId( 0 )
1703 {
1704 vscroll.Enable();
1705 vscroll.Show();
1706 vscroll.SetManagedWin( this );
1707
1708 hscroll.Enable();
1709 hscroll.Show();
1710 hscroll.SetManagedWin( this );
1711
1712 _lo.AddWin( &vscroll, 0, 1 );
1713 _lo.SetLineGrowth( 0 );
1714 _lo.AddWin( &hscroll, 1, 0 );
1715
1716 _lo.AddRect( &viewRect, 0, 0 );
1717 _lo.SetColGrowth( 0 );
1718 _lo.AddRect( &emptyRect, 1, 1 );
1719
1720 SetLayout( &_lo );
1721 LSRange lr( 0, 10000, 1000 );
1722 LSize ls;
1723 ls.x = ls.y = lr;
1724 SetLSize( ls );
1725
1726 OnChangeStyles();
1727 SetTimer( 1, 1000 );
1728 }
1729
1730 int uiClassViewer = GetUiID( "Viewer" );
UiGetClassId()1731 int ViewWin::UiGetClassId() { return uiClassViewer; }
1732
EventTimer(int tid)1733 void ViewWin::EventTimer( int tid )
1734 {
1735 if ( threadData && tid == 1 )
1736 {
1737 MutexLock lock( &threadData->mutex );
1738 threadData->inFlags |= ViewerThreadData::FTIMER;
1739 threadData->cond.Signal();
1740
1741 int64_t tim = time( 0 );
1742
1743 if ( threadData->loadStartTime && tim != threadData->loadStartTime && !drawLoading )
1744 {
1745 drawLoading = true;
1746 Invalidate();
1747 }
1748 }
1749 }
1750
WrapUnwrap()1751 void ViewWin::WrapUnwrap()
1752 {
1753 if ( threadData )
1754 {
1755 MutexLock lock( &threadData->mutex );
1756 wrap = !wrap;
1757 threadData->inMode.wrap = wrap;
1758 threadData->inFlags |= ViewerThreadData::FMODE;
1759 threadData->inFlags &= ~ViewerThreadData::FEVENT; //clear key events
1760 threadData->cond.Signal();
1761 }
1762 }
1763
HexText()1764 void ViewWin::HexText()
1765 {
1766 if ( threadData )
1767 {
1768 MutexLock lock( &threadData->mutex );
1769 hex = !hex;
1770 threadData->inMode.hex = hex;
1771 threadData->inFlags |= ViewerThreadData::FMODE;
1772 threadData->inFlags &= ~ViewerThreadData::FEVENT; //clear key events
1773 threadData->cond.Signal();
1774 }
1775
1776 CalcSize();
1777 }
1778
NextCharset()1779 void ViewWin::NextCharset()
1780 {
1781 SetCharset( charset_table[GetNextOperCharsetId( charset->id )] );
1782 }
1783
SetCharset(int n)1784 void ViewWin::SetCharset( int n )
1785 {
1786 SetCharset( charset_table[n] );
1787 }
1788
1789
SetCharset(charset_struct * cs)1790 void ViewWin::SetCharset( charset_struct* cs )
1791 {
1792 charset = cs;
1793 SendChanges();
1794
1795 if ( threadData )
1796 {
1797 MutexLock lock( &threadData->mutex );
1798 threadData->inMode.charset = cs;
1799 threadData->inFlags |= ViewerThreadData::FMODE;
1800 threadData->inFlags &= ~ViewerThreadData::FEVENT; //clear key events
1801 threadData->cond.Signal();
1802 }
1803 }
1804
1805
CalcSize()1806 bool ViewWin::CalcSize()
1807 {
1808 if ( threadData )
1809 {
1810 MutexLock lock( &threadData->mutex );
1811 int r = 0, c = 0;
1812
1813 if ( threadData->inMode.hex )
1814 {
1815
1816 //w = (10+2+size.cols*3+1+size.cols + size.cols/8)*charW;
1817
1818 c = ( ( viewRect.Width() / charW - 13 ) * 8 ) / 33;
1819
1820 c = ( c / 8 ) * 8;
1821
1822
1823 if ( c < 8 ) { c = 8; }
1824
1825 r = viewRect.Height() / charH;
1826 }
1827 else
1828 {
1829 r = viewRect.Height() / charH;
1830 c = viewRect.Width() / charW;
1831 }
1832
1833 if ( !threadData->inSize.Eq( r, c ) )
1834 {
1835 threadData->inSize.rows = r;
1836 threadData->inSize.cols = c;
1837 threadData->inFlags |= ViewerThreadData::FSIZE;
1838 threadData->inFlags &= ~ViewerThreadData::FEVENT; //clear key events
1839 threadData->cond.Signal();
1840 return true;
1841 }
1842 }
1843
1844 return false;
1845 }
1846
ThreadSignal(int id,int data)1847 void ViewWin::ThreadSignal( int id, int data )
1848 {
1849 if ( threadData && threadData->Id() == id )
1850 {
1851 MutexLock lock( &threadData->mutex );
1852 drawLoading = false;
1853
1854 if ( !threadData->m_Error.empty() )
1855 {
1856 std::string s = threadData->m_Error;
1857
1858 lock.Unlock(); //!!!
1859 ClearFile();
1860 Parent()->Command( ID_QUIT, 0, this, 0 );
1861 NCMessageBox( ( NCDialogParent* )Parent(), "Viewer", s.data(), true );
1862 return; //!!!
1863
1864 }
1865
1866 if ( threadData->inFlags ) { return; } //есть еще неотработанное событие, подождем его выполнения
1867
1868 lastResult = threadData->ret;
1869 lastPos = threadData->pos;
1870
1871 }
1872
1873 SendChanges();
1874 CalcScroll();
1875 Invalidate(); //temp
1876 }
1877
EventSize(cevent_size * pEvent)1878 void ViewWin::EventSize( cevent_size* pEvent )
1879 {
1880 CalcSize();
1881 }
1882
EventKey(cevent_key * pEvent)1883 bool ViewWin::EventKey( cevent_key* pEvent )
1884 {
1885 if ( !threadData ) { return false; } //!!!
1886
1887 if ( pEvent->Type() == EV_KEYDOWN )
1888 {
1889 // bool shift = ( pEvent->Mod() & KM_SHIFT ) != 0;
1890 bool ctrl = ( pEvent->Mod() & KM_CTRL ) != 0;
1891
1892 if ( ctrl )
1893 {
1894 switch ( pEvent->Key() )
1895 {
1896 case VK_UP:
1897 if (pEvent->IsFromMouseWheel())
1898 {
1899 if ( g_MainWin ) g_MainWin->IncreaseFontSize(NCWin::VIEW);
1900 }
1901 return true;
1902
1903 case VK_DOWN:
1904 if (pEvent->IsFromMouseWheel())
1905 {
1906 if ( g_MainWin ) g_MainWin->DecreaseFontSize(NCWin::VIEW);
1907 }
1908 return true;
1909
1910 case VK_NEXT:
1911 threadData->SetEvent( ViewerEvent( ViewerEvent::END ) );
1912 return true;
1913
1914 case VK_PRIOR:
1915 threadData->SetEvent( ViewerEvent( ViewerEvent::HOME ) );
1916 return true;
1917
1918 case VK_RIGHT:
1919 threadData->SetEvent( ViewerEvent( ViewerEvent::RIGHTSTEP ) );
1920 return true;
1921
1922 case VK_LEFT:
1923 threadData->SetEvent( ViewerEvent( ViewerEvent::LEFTSTEP ) );
1924 return true;
1925 }
1926 }
1927
1928 switch ( pEvent->Key() )
1929 {
1930 case VK_RIGHT:
1931 threadData->SetEvent( ViewerEvent( ViewerEvent::RIGHT ) );
1932 break;
1933
1934 case VK_LEFT:
1935 threadData->SetEvent( ViewerEvent( ViewerEvent::LEFT ) );
1936 break;
1937
1938 case VK_DOWN:
1939 threadData->SetEvent( ViewerEvent( ViewerEvent::DOWN ) );
1940 break;
1941
1942 case VK_UP:
1943 threadData->SetEvent( ViewerEvent( ViewerEvent::UP ) );
1944 break;
1945
1946 case VK_HOME:
1947 threadData->SetEvent( ViewerEvent( ViewerEvent::HOME ) );
1948 break;
1949
1950 case VK_END:
1951 threadData->SetEvent( ViewerEvent( ViewerEvent::END ) );
1952 break;
1953
1954 case VK_NEXT:
1955 threadData->SetEvent( ViewerEvent( ViewerEvent::PAGEDOWN ) );
1956 break;
1957
1958 case VK_PRIOR:
1959 threadData->SetEvent( ViewerEvent( ViewerEvent::PAGEUP ) );
1960 break;
1961 };
1962
1963 return true;
1964
1965 }
1966
1967 return false;
1968 }
1969
EventMouse(cevent_mouse * pEvent)1970 bool ViewWin::EventMouse( cevent_mouse* pEvent )
1971 {
1972 if ( !threadData ) { return false; } //!!!
1973
1974 switch ( pEvent->Type() )
1975 {
1976
1977 case EV_MOUSE_PRESS:
1978 {
1979 if ( pEvent->Button() == MB_X1 )
1980 {
1981 threadData->SetEvent( ViewerEvent( ViewerEvent::PAGEUP ) );
1982 break;
1983 }
1984
1985 if ( pEvent->Button() == MB_X2 )
1986 {
1987 threadData->SetEvent( ViewerEvent( ViewerEvent::PAGEDOWN ) );
1988 break;
1989 }
1990 }
1991 break;
1992 };
1993
1994 return false;
1995 }
1996
1997 static int uiColorCtrl = GetUiID( "ctrl-color" );
1998 static int uiLoadColor = GetUiID( "load-color" );
1999 static int uiLoadBackground = GetUiID( "load-background" );
2000 static int uiHid = GetUiID( "hid" );
2001
OnChangeStyles()2002 void ViewWin::OnChangeStyles()
2003 {
2004 colors.bg = UiGetColor( uiBackground, 0, 0, 0xFFFFFF );
2005 colors.fg = UiGetColor( uiColor, 0, 0, 0 );
2006 colors.ctrl = UiGetColor( uiColorCtrl, 0, 0, 0xD00000 );
2007 colors.markFg = UiGetColor( uiMarkColor, 0, 0, 0xFFFFFF );
2008 colors.markBg = UiGetColor( uiMarkBackground, 0, 0, 0 );
2009 colors.lnFg = UiGetColor( uiLineColor, 0, 0, 0 ); //line number foreground (in hex mode)
2010 colors.hid = UiGetColor( uiHid, 0, 0, 0xD00000 );
2011 colors.loadBg = UiGetColor( uiLoadBackground, 0, 0, 0xFFFFFF );
2012 colors.loadFg = UiGetColor( uiLoadColor, 0, 0, 0xFF );
2013
2014 wal::GC gc( this );
2015 gc.Set( GetFont() );
2016 cpoint p = gc.GetTextExtents( ABCString );
2017 charW = p.x / ABCStringLen;
2018 charH = p.y;
2019
2020 if ( !charW ) { charW = 10; }
2021
2022 if ( !charH ) { charH = 10; }
2023
2024 if ( !CalcSize() ) { Invalidate(); }
2025 }
2026
2027 //ret type 0 - normal, 1-spec symbol ('.'), else marked
PrepareText(unicode_t * buf,char * typeBuf,int bufSize,unicode_t * s,char * attr,int count)2028 static int PrepareText( unicode_t* buf, char* typeBuf, int bufSize, unicode_t* s, char* attr, int count )
2029 {
2030 int n = ( bufSize > count ) ? count : bufSize;
2031
2032 for ( int t = n; t > 0; t--, buf++, typeBuf++, s++, attr++ )
2033 {
2034 char type;
2035
2036 if ( *s < 32 )
2037 {
2038 *buf = '.';
2039 type = 1;
2040 }
2041 else
2042 {
2043 type = 0;
2044 *buf = *s;
2045 }
2046
2047 if ( *attr ) { type = 2; }
2048
2049 *typeBuf = type;
2050 }
2051
2052 return n;
2053 }
2054
ViewPreparHexModeText(unicode_t * inStr,char * inAttr,unicode_t * u,char * attr,int count,charset_struct * charset)2055 static void ViewPreparHexModeText( unicode_t* inStr, char* inAttr, unicode_t* u, char* attr, int count, charset_struct* charset )
2056 {
2057 if ( count <= 0 ) { return; }
2058
2059 std::vector<char> str( count );
2060 int i;
2061
2062 for ( i = 0; i < count; i++ ) { str[i] = char( inStr[i] & 0xFF ); }
2063
2064 char* e = str.data() + count;
2065 char* s = str.data();
2066 i = 0;
2067
2068 while ( i < count )
2069 {
2070 //bool tab;
2071 unicode_t c = charset->GetChar( s, e );
2072 char a = 0;
2073
2074 if ( c < 32 /*|| c>=0x80 && c< 0xA0*/ )
2075 {
2076 c = '.';
2077 a = 1;
2078 }
2079
2080 if ( *inAttr ) { a = 2; }
2081
2082 inAttr++;
2083
2084 char* t = charset->GetNext( s, e/*, &tab*/ );
2085 *u = c;
2086 *attr = a;
2087
2088 u++;
2089 attr++;
2090 int n = ( t ? t : e ) - s;
2091 i++;
2092 s = t;
2093
2094 for ( int j = 1; j < n && i < count; j++, i++, u++, attr++, inAttr++ )
2095 {
2096 *u = c;
2097 *attr = 3;
2098
2099 if ( *inAttr ) { *attr = 2; }
2100 }
2101 }
2102 }
2103
ViewDrawPreparedText(ViewerColors * viewerColors,wal::GC & gc,int x,int y,unicode_t * s,char * type,int count,int charH,int charW)2104 static void ViewDrawPreparedText( ViewerColors* viewerColors, wal::GC& gc, int x, int y, unicode_t* s, char* type, int count, int charH, int charW )
2105 {
2106 while ( count > 0 )
2107 {
2108 char t = *type;
2109 int i = 1;
2110
2111 while ( i < count && type[i] == t ) { i++; }
2112
2113 int fg;
2114 int bg;
2115
2116 switch ( t )
2117 {
2118 case 0:
2119 fg = viewerColors->fg;
2120 bg = viewerColors->bg;
2121 break;
2122
2123 case 1:
2124 fg = viewerColors->ctrl;
2125 bg = viewerColors->bg;
2126 break;
2127
2128 case 3:
2129 fg = viewerColors->hid;
2130 bg = viewerColors->bg;
2131 break;
2132
2133 default:
2134 fg = viewerColors->markFg;
2135 bg = viewerColors->markBg;
2136 break;
2137 }
2138
2139 gc.SetTextColor( fg );
2140 gc.SetFillColor( bg );
2141 gc.TextOutF( x, y, s, i );
2142 count -= i;
2143 s += i;
2144 type += i;
2145 x += i * charW;
2146 }
2147 }
2148
2149
ViewDrawText(ViewerColors * viewerColors,wal::GC & gc,unicode_t * s,char * attr,int count,int x,int y,int charH,int charW)2150 static void ViewDrawText( ViewerColors* viewerColors, wal::GC& gc, unicode_t* s, char* attr, /*unicode_t *sPrev, char *attrPrev,*/ int count,
2151 int x, int y, int charH, int charW )
2152 {
2153 unicode_t buf[0x100];
2154 char typeBuf[0x100];
2155
2156 while ( count > 0 )
2157 {
2158 int n = PrepareText( buf, typeBuf, 0x100, s, attr, count );
2159 ViewDrawPreparedText( viewerColors, gc, x, y, buf, typeBuf, n, charH, charW );
2160 count -= n;
2161 s += n;
2162 attr += n;
2163 x += n * charW;
2164 }
2165 }
2166
2167
Paint(wal::GC & gc,const crect & paintRect)2168 void ViewWin::Paint( wal::GC& gc, const crect& paintRect )
2169 {
2170 crect rect = this->ClientRect();
2171
2172 ASSERT( lastResult.size.rows * lastResult.size.cols <= lastResult.dataSize );
2173
2174 ViewerSize size = lastResult.size;
2175 ViewerMode mode = lastResult.mode;
2176
2177 if ( !emptyRect.IsEmpty() )
2178 {
2179 gc.SetFillColor( colors.bg );
2180 gc.FillRect( emptyRect );
2181 }
2182
2183 gc.Set( GetFont() );
2184
2185 if ( mode.hex )
2186 {
2187 gc.SetFillColor( colors.bg );
2188 //gc.SetTextColor(0xFFFFFF);
2189 int y = viewRect.top;
2190 int x = viewRect.left;
2191 unicode_t* p = lastResult.data.data();
2192
2193 int bytes = size.cols * size.rows;
2194 std::vector<unicode_t> txtDataBuf( bytes );
2195 std::vector<char> txtAttrBuf( bytes );
2196
2197 ViewPreparHexModeText( lastResult.data.data(), lastResult.attr.data(), txtDataBuf.data(), txtAttrBuf.data(), bytes, charset );
2198
2199 char* txtAttr = txtAttrBuf.data();
2200 unicode_t* txt = txtDataBuf.data();
2201
2202 char* attr = lastResult.attr.data();
2203 seek_t offset = lastPos.begin;
2204
2205 int w = ( 10 + 2 + size.cols * 3 + 1 + size.cols + size.cols / 8 ) * charW;
2206
2207 for ( int r = 0; r < size.rows; r++, p += size.cols, txt += size.cols, attr += size.cols, txtAttr += size.cols, y += charH, offset += size.cols )
2208 {
2209 if ( *p == 0x100 )
2210 {
2211 crect rect( x, y, x + w, y + charH );
2212 gc.FillRect( rect );
2213 continue;
2214 }
2215
2216 gc.SetTextColor( colors.lnFg );
2217 int i;
2218 int lx = x;
2219 unicode_t buf[64], *s;
2220
2221 for ( i = 10, s = buf; i > 0; i--, s++ )
2222 {
2223 *s = GetHexChar( ( offset >> ( i - 1 ) * 4 ) & 0xF );
2224 }
2225
2226 *s = 0;
2227 gc.TextOutF( x, y, buf );
2228
2229 gc.SetTextColor( colors.fg );
2230 lx += 10 * charW;
2231 crect rect( lx, y, lx + charW * 2, y + charH );
2232 gc.FillRect( rect );
2233 lx += charW * 2;
2234
2235 for ( i = 0; i < size.cols; i++ )
2236 {
2237 unicode_t c = p[i];
2238
2239 if ( c != 0x100 )
2240 {
2241 buf[0] = GetHexChar( c >> 4 );
2242 buf[1] = GetHexChar( c );
2243 gc.TextOutF( lx, y, buf, 2 );
2244 }
2245 else
2246 {
2247 rect.Set( lx, y, lx + charW * 2, y + charH );
2248 gc.FillRect( rect );
2249 }
2250
2251 lx += 2 * charW;
2252
2253 int rw = i > 0 && ( ( i + 1 ) % 8 ) == 0 ? 2 * charW : charW;
2254
2255 rect.Set( lx, y, lx + rw, y + charH );
2256 gc.FillRect( rect );
2257
2258 lx += rw;
2259 }
2260
2261 rect.Set( lx, y, lx + charW, y + charH );
2262 gc.FillRect( rect );
2263 lx += charW;
2264
2265 for ( i = 0; i < size.cols; i++ )
2266 if ( p[i] == 0x100 ) { break; }
2267
2268 ViewDrawPreparedText( &colors, gc, lx, y, txt, txtAttr, size.cols, charH, charW );
2269 //ViewDrawText(gc, txt, attr, i , lx, y, charH, charW);
2270
2271 lx += i * charW;
2272
2273 if ( i < size.cols )
2274 {
2275 rect.Set( lx, y, lx + ( size.cols - i )*charW, y + charH );
2276 gc.FillRect( rect );
2277 lx += charW;
2278 }
2279 }
2280
2281
2282 if ( x + w < viewRect.right )
2283 {
2284 crect r = viewRect;
2285 r.left = x + w;
2286 gc.FillRect( r );
2287 }
2288
2289 if ( y < viewRect.Height() )
2290 {
2291 crect r = viewRect;
2292 r.top = y;
2293 gc.FillRect( r );
2294 }
2295
2296 }
2297 else
2298 {
2299 gc.SetFillColor( colors.bg );
2300 gc.SetTextColor( colors.fg );
2301
2302 int y = viewRect.top;
2303 int x = viewRect.left;
2304 unicode_t* p = lastResult.data.data();
2305 char* attr = lastResult.attr.data();
2306
2307 for ( int i = 0; i < size.rows; i++, p += size.cols, attr += size.cols, y += charH )
2308 {
2309 ViewDrawText( &colors, gc, p, attr, size.cols, x, y, charH, charW );
2310 }
2311
2312 if ( y < viewRect.Height() )
2313 {
2314 crect r = viewRect;
2315 r.top = y;
2316 gc.FillRect( r );
2317 }
2318
2319 if ( x + size.cols * charW < viewRect.right )
2320 {
2321 crect r = viewRect;
2322 r.left = x + size.cols * charW;
2323 gc.FillRect( r );
2324 }
2325 }
2326
2327 if ( drawLoading && loadingText.data() )
2328 {
2329 cpoint pt = gc.GetTextExtents( loadingText.data() );
2330 gc.SetFillColor( colors.loadBg );
2331 gc.SetTextColor( colors.loadFg );
2332 int x = ( rect.Width() - pt.x ) / 2;
2333 int y = ( rect.Height() - pt.y ) / 2;
2334 gc.FillRect( crect( x, y - pt.y, x + pt.x, y ) );
2335 gc.TextOutF( x, y, loadingText.data() );
2336 gc.FillRect( crect( x, y + pt.y, x + pt.x, y + 2 * pt.y ) );
2337 }
2338 }
2339
CalcScroll()2340 void ViewWin::CalcScroll()
2341 {
2342 if ( threadData )
2343 {
2344 MutexLock lock( &threadData->mutex );
2345 ScrollInfo hsi;
2346 hsi.m_PageSize = threadData->ret.size.cols;
2347
2348 if ( threadData->ret.mode.wrap || threadData->ret.mode.hex )
2349 {
2350 hsi.m_Size = 0;
2351 hsi.m_Pos = 0;
2352 }
2353 else
2354 {
2355 hsi.m_Size = threadData->pos.maxLine;
2356 hsi.m_Pos = threadData->pos.col;
2357 }
2358
2359 bool hVisible = hscroll.IsVisible();
2360 hscroll.Command( CMD_SCROLL_INFO, SCMD_SCROLL_HCHANGE, this, &hsi );
2361
2362 ScrollInfo vsi;
2363 vsi.m_PageSize = int( threadData->pos.end - threadData->pos.begin );
2364 vsi.m_Size = threadData->pos.size;
2365 vsi.m_Pos = threadData->pos.begin;
2366 bool vVisible = vscroll.IsVisible();
2367 vscroll.Command( CMD_SCROLL_INFO, SCMD_SCROLL_VCHANGE, this, &vsi );
2368
2369 if ( hVisible != hscroll.IsVisible() || vVisible != vscroll.IsVisible() )
2370 {
2371 lock.Unlock(); //!!!
2372 this->RecalcLayouts();
2373 CalcSize();
2374 }
2375 }
2376 }
2377
SetFile(clPtr<FS> fsp,FSPath & path,seek_t size,const unicode_t * HistoryUri,int TempDirId)2378 void ViewWin::SetFile( clPtr<FS> fsp, FSPath& path, seek_t size, const unicode_t* HistoryUri, int TempDirId )
2379 {
2380 m_HistoryUri = new_unicode_str( HistoryUri );
2381 m_TempDirId = TempDirId;
2382
2383 ClearFile();
2384 VFilePtr vf = new VFile( fsp, path, size, g_WcmConfig.editTabSize );
2385 threadData = new ViewerThreadData( vf );
2386 threadData->inMode.charset = charset;
2387 threadData->inMode.wrap = wrap;
2388 threadData->inMode.hex = hex;
2389
2390 try
2391 {
2392 ThreadCreate( threadData->Id(), ViewerThread, threadData );
2393 CalcSize();
2394 drawLoading = true;
2395
2396 }
2397 catch ( cexception* ex )
2398 {
2399 ex->destroy();
2400 delete threadData;
2401 threadData = 0;
2402 }
2403 catch ( ... )
2404 {
2405 delete threadData;
2406 threadData = 0;
2407 }
2408 }
2409
Command(int id,int subId,Win * win,void * data)2410 bool ViewWin::Command( int id, int subId, Win* win, void* data )
2411 {
2412 if ( id != CMD_SCROLL_INFO )
2413 {
2414 return false;
2415 }
2416
2417 switch ( subId )
2418 {
2419 case SCMD_SCROLL_LINE_RIGHT:
2420 threadData->SetEvent( ViewerEvent( ViewerEvent::RIGHT ) );
2421 break;
2422
2423 case SCMD_SCROLL_LINE_LEFT:
2424 threadData->SetEvent( ViewerEvent( ViewerEvent::LEFT ) );
2425 break;
2426
2427 case SCMD_SCROLL_LINE_DOWN:
2428 threadData->SetEvent( ViewerEvent( ViewerEvent::DOWN ) );
2429 break;
2430
2431 case SCMD_SCROLL_LINE_UP:
2432 threadData->SetEvent( ViewerEvent( ViewerEvent::UP ) );
2433 break;
2434
2435 case SCMD_SCROLL_PAGE_UP:
2436 threadData->SetEvent( ViewerEvent( ViewerEvent::PAGEUP ) );
2437 break;
2438
2439 case SCMD_SCROLL_PAGE_DOWN:
2440 threadData->SetEvent( ViewerEvent( ViewerEvent::PAGEDOWN ) );
2441 break;
2442
2443 case SCMD_SCROLL_PAGE_LEFT:
2444 threadData->SetEvent( ViewerEvent( ViewerEvent::PAGELEFT ) );
2445 break;
2446
2447 case SCMD_SCROLL_PAGE_RIGHT:
2448 threadData->SetEvent( ViewerEvent( ViewerEvent::PAGERIGHT ) );
2449 break;
2450
2451 case SCMD_SCROLL_TRACK:
2452 threadData->SetEvent( ViewerEvent( win == &hscroll ? ViewerEvent::HTRACK : ViewerEvent::VTRACK, ( ( int* )data )[0] ) );
2453 break;
2454 }
2455
2456 return true;
2457 }
2458
ClearFile()2459 void ViewWin::ClearFile()
2460 {
2461 if ( threadData )
2462 {
2463 lastResult.Clear();
2464 MutexLock lock( &threadData->mutex );
2465 threadData->inFlags |= ViewerThreadData::FSTOP;
2466 threadData->info.SetStop();
2467 threadData->cond.Signal(); //!!!
2468 threadData = 0;
2469 };
2470 }
2471
GetPercent()2472 int ViewWin::GetPercent()
2473 {
2474 VFPos pos = lastPos;
2475 return ( pos.size > 0 ) ? int( ( pos.end * 100 ) / pos.size ) : 100;
2476 }
2477
GetCol()2478 int ViewWin::GetCol()
2479 {
2480 if ( threadData )
2481 {
2482 return int( threadData->pos.begin );
2483 }
2484
2485 return -1;
2486 }
2487
SetCol(int Col)2488 void ViewWin::SetCol( int Col )
2489 {
2490 if ( Col < 0 ) { return; }
2491
2492 threadData->SetEvent( ViewerEvent( ViewerEvent::SET, Col ) );
2493 }
2494
2495
Uri()2496 FSString ViewWin::Uri()
2497 {
2498 if ( threadData )
2499 {
2500 return threadData->File()->Uri();
2501 }
2502
2503 return FSString();
2504 }
2505
2506
~ViewWin()2507 ViewWin::~ViewWin()
2508 {
2509 ClearFile();
2510 }
2511
2512
2513
2514 ///////////////////////////// search
2515
2516 struct VSTData
2517 {
2518 Mutex mutex;
2519 //in
2520 VFilePtr file;
2521 std::vector<unicode_t> str;
2522 charset_struct* charset;
2523 bool sensitive;
2524 FSCViewerInfo info;
2525 bool hex;
2526 seek_t from;
2527
2528 bool winClosed;
2529 bool threadStopped;
2530 //ret
2531 std::string m_Error;
2532 seek_t begin;
2533 seek_t end;
2534
VSTDataVSTData2535 VSTData( VFilePtr f, const unicode_t* s, bool sens, charset_struct* cs, bool h, seek_t offset )
2536 : mutex(), file( f ), str( new_unicode_str( s ) ), charset( cs ), sensitive( sens ), info(), hex( h ), from( offset ),
2537 winClosed( false ), threadStopped( false ), m_Error(), begin( 0 ), end( 0 )
2538 {}
2539 };
2540
2541
VSThreadFunc(void * ptr)2542 void* VSThreadFunc( void* ptr )
2543 {
2544 VSTData* data = ( VSTData* )ptr;
2545
2546 seek_t begin = 0;
2547 seek_t end = 0;
2548
2549 {
2550 //lock
2551 MutexLock lock( &data->mutex );
2552 }
2553
2554 try
2555 {
2556 VSearcher search;
2557 search.Set( data->str.data(), data->sensitive, data->charset );
2558
2559 int maxLen = search.MaxLen();
2560 int minLen = search.MinLen();
2561 int bufSize = 16000 + maxLen;
2562 std::vector<char> buf( bufSize );
2563 int count = 0;
2564
2565 seek_t offset = data->from;
2566
2567 if ( offset < 0 ) { offset = 0; }
2568
2569 seek_t nrSize = data->file->Size() - offset;
2570
2571 int n = bufSize > nrSize ? int( nrSize ) : bufSize;
2572 int bytes = data->file->ReadBlock( offset, buf.data(), n, &data->info );
2573
2574 if ( bytes > 0 )
2575 {
2576 nrSize -= bytes;
2577 count = bytes;
2578
2579 while ( true )
2580 {
2581 if ( count >= maxLen )
2582 {
2583 int n = count - maxLen + 1;
2584 int fBytes = 0;
2585 char* s = search.Search( buf.data(), buf.data() + n, &fBytes );
2586
2587 if ( s )
2588 {
2589 begin = offset + ( s - buf.data() );
2590 end = begin + fBytes;
2591 break;
2592 }
2593
2594 int t = count - n;
2595
2596 if ( t > 0 ) { memmove( buf.data(), buf.data() + n, t ); }
2597
2598 count = t;
2599 offset += n;
2600 }
2601
2602 if ( nrSize <= 0 )
2603 {
2604 break;
2605 }
2606
2607 int n = bufSize - count;
2608
2609 if ( n > nrSize ) { n = int( nrSize ); }
2610
2611 int bytes = data->file->ReadBlock( offset + count, buf.data() + count, n, &data->info );
2612
2613 if ( bytes <= 0 ) { break; }
2614
2615 nrSize -= bytes;
2616 count += bytes;
2617 }
2618
2619 if ( end == 0 )
2620 {
2621 if ( minLen < maxLen )
2622 {
2623 int n = maxLen - minLen;
2624
2625 for ( ; n > 0 && count < bufSize ; n-- )
2626 {
2627 buf[count++] = 0;
2628 }
2629
2630 if ( count >= maxLen )
2631 {
2632
2633 int n = count - maxLen + 1;
2634 int fBytes = 0;
2635 char* s = search.Search( buf.data(), buf.data() + n, &fBytes );
2636
2637 if ( s )
2638 {
2639 begin = offset + ( s - buf.data() );
2640 end = begin + fBytes;
2641 }
2642 }
2643
2644 }
2645 }
2646 }
2647
2648
2649 }
2650 catch ( cexception* ex )
2651 {
2652 try { data->m_Error = ex->message(); }
2653 catch ( cexception* x ) { x->destroy(); }
2654
2655 ex->destroy();
2656 }
2657 catch ( ... )
2658 {
2659 try { data->m_Error = "BOTVA: unhabdled exception: void *VSThreadFunc(void *ptr) "; }
2660 catch ( cexception* x ) { x->destroy(); }
2661 }
2662
2663 {
2664 //lock
2665 MutexLock lock( &data->mutex );
2666
2667 if ( data->winClosed )
2668 {
2669 lock.Unlock(); //!!!
2670 delete data;
2671 return 0;
2672 }
2673
2674 data->threadStopped = true;
2675 data->begin = begin;
2676 data->end = end;
2677 WinThreadSignal( 0 );
2678 }
2679
2680 return 0;
2681 }
2682
2683 class VSearchDialog: public NCDialog
2684 {
2685 public:
2686 VSTData* data;
VSearchDialog(NCDialogParent * parent,VFilePtr file,const unicode_t * str,bool sensitive,charset_struct * charset,bool hex,seek_t from)2687 VSearchDialog( NCDialogParent* parent, VFilePtr file, const unicode_t* str, bool sensitive, charset_struct* charset, bool hex, seek_t from )
2688 : NCDialog( ::createDialogAsChild, 0, parent, utf8_to_unicode( _LT( "Search" ) ).data(), bListCancel ) //, 0xD8E9EC, 0)
2689 {
2690 SetPosition();
2691 data = new VSTData( file, str, sensitive, charset, hex, from );
2692
2693 try
2694 {
2695 this->ThreadCreate( 1, VSThreadFunc, data );
2696 }
2697 catch ( ... )
2698 {
2699 delete data;
2700 data = 0;
2701 throw;
2702 }
2703 }
2704 virtual void ThreadStopped( int id, void* data );
2705 virtual ~VSearchDialog();
2706 };
2707
~VSearchDialog()2708 VSearchDialog::~VSearchDialog()
2709 {
2710 if ( data )
2711 {
2712 MutexLock lock( &data->mutex );
2713
2714 if ( data->threadStopped )
2715 {
2716 lock.Unlock(); //!!!
2717 delete data;
2718 data = 0;
2719 }
2720 else
2721 {
2722 data->winClosed = true;
2723 data->info.SetStop();
2724 data = 0;
2725 }
2726 }
2727 }
2728
ThreadStopped(int id,void * data)2729 void VSearchDialog::ThreadStopped( int id, void* data )
2730 {
2731 EndModal( CMD_OK );
2732 }
2733
2734
Search(const unicode_t * str,bool sensitive)2735 bool ViewWin::Search( const unicode_t* str, bool sensitive )
2736 {
2737 if ( !threadData ) { return true; }
2738
2739 seek_t offset = 0;
2740
2741 {
2742 //lock
2743 MutexLock lock( &threadData->mutex );
2744
2745 if ( threadData->pos.begin >= 0 ) { offset = threadData->pos.begin; }
2746
2747 if ( !threadData->pos.marker.Empty() && threadData->pos.marker.end > offset )
2748 {
2749 offset = threadData->pos.marker.end;
2750 }
2751 }
2752
2753 VSearchDialog dlg( ( NCDialogParent* )Parent(), this->threadData->FilePtr(), str, sensitive, charset, this->hex,
2754 offset
2755 );
2756
2757
2758 int ret = dlg.DoModal();
2759
2760 if ( ret == ::CMD_CANCEL ) { return true; }
2761
2762 if ( !dlg.data ) { return true; }
2763
2764 if ( !dlg.data->m_Error.empty() )
2765 {
2766 NCMessageBox( ( NCDialogParent* )Parent(), _LT( "Search" ), dlg.data->m_Error.c_str(), true );
2767 return true;
2768 }
2769
2770 if ( dlg.data->end == 0 ) { return false; }
2771
2772 threadData->SetEvent( ViewerEvent( ViewerEvent::FOUND, dlg.data->begin, dlg.data->end ) );
2773
2774 return true;;
2775 }
2776