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