1 /*
2 	khwin.cc	Hypertext Window
3 	Copyright (c) 1997-8,2000,2002,2003,2004,2007,2009 Kriang Lerdsuwanakij
4 	email:		lerdsuwa@users.sourceforge.net
5 
6 	This program is free software; you can redistribute it and/or modify
7 	it under the terms of the GNU General Public License as published by
8 	the Free Software Foundation; either version 2 of the License, or
9 	(at your option) any later version.
10 
11 	This program is distributed in the hope that it will be useful,
12 	but WITHOUT ANY WARRANTY; without even the implied warranty of
13 	MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
14 	GNU General Public License for more details.
15 
16 	You should have received a copy of the GNU General Public License
17 	along with this program; if not, write to the Free Software
18 	Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
19 */
20 
21 #include "khwin.h"
22 #include "cstrlib.h"
23 
24 /*************************************************************************
25 	khGeometryManager
26 *************************************************************************/
27 
28 bool	khGeometryManager::disableScrollBar = false;
29 
SetWinRect(int id,NCRect & rect)30 void	khGeometryManager::SetWinRect(int id, NCRect &rect)
31 {
32 	if (mode == modeNoScroll || disableScrollBar) {
33 		switch(id) {
34 			case idHyperWindow:
35 				rect.SetX(0);
36 				rect.SetY(0);
37 				rect.SetRow(LINES-2);
38 				rect.SetCol(COLS);
39 				break;
40 			case idStatWindow:
41 				rect.SetX(0);
42 				rect.SetY(LINES-2);
43 				rect.SetCol(COLS);
44 				rect.SetRow(1);
45 				break;
46 			case idURLWindow:
47 				rect.SetX(0);
48 				rect.SetY(LINES-1);
49 				rect.SetCol(COLS);
50 				rect.SetRow(1);
51 				break;
52 			case idHScrollBar:
53 				rect.SetX(COLS-20);
54 				rect.SetY(LINES-2);
55 				rect.SetCol(0);
56 				rect.SetRow(1);
57 				break;
58 			case idVScrollBar:
59 				rect.SetX(COLS-1);
60 				rect.SetY(0);
61 				rect.SetCol(1);
62 				rect.SetRow(0);
63 				break;
64 			case idScrollBox:
65 				rect.SetX(COLS-1);
66 				rect.SetY(LINES-2);
67 				rect.SetCol(0);
68 				rect.SetRow(0);
69 				break;
70 			case idMoreBar:
71 				rect.SetX(COLS-2);
72 				rect.SetY(0);
73 				rect.SetCol(1);
74 				rect.SetRow(0);
75 				break;
76 		}
77 	}
78 	else if (mode == modeHScroll) {
79 		switch(id) {
80 			case idHyperWindow:
81 				rect.SetX(0);
82 				rect.SetY(0);
83 				rect.SetRow(LINES-2);
84 				rect.SetCol(COLS-1);	// Reserve More bar
85 				break;
86 			case idStatWindow:
87 				rect.SetX(0);
88 				rect.SetY(LINES-2);
89 				rect.SetCol(COLS-20);
90 				rect.SetRow(1);
91 				break;
92 			case idURLWindow:
93 				rect.SetX(0);
94 				rect.SetY(LINES-1);
95 				rect.SetCol(COLS);
96 				rect.SetRow(1);
97 				break;
98 			case idHScrollBar:
99 				rect.SetX(COLS-20);
100 				rect.SetY(LINES-2);
101 				rect.SetCol(19);
102 				rect.SetRow(1);
103 				break;
104 			case idVScrollBar:
105 				rect.SetX(COLS-1);
106 				rect.SetY(0);
107 				rect.SetCol(1);
108 				rect.SetRow(0);
109 				break;
110 			case idScrollBox:
111 				rect.SetX(COLS-1);
112 				rect.SetY(LINES-2);
113 				rect.SetCol(1);
114 				rect.SetRow(1);
115 				break;
116 			case idMoreBar:
117 				rect.SetX(COLS-1);
118 				rect.SetY(0);
119 				rect.SetCol(1);
120 				rect.SetRow(LINES-2);
121 				break;
122 		}
123 	}
124 	else if (mode == modeVScroll) {
125 		switch(id) {
126 			case idHyperWindow:
127 				rect.SetX(0);
128 				rect.SetY(0);
129 				rect.SetRow(LINES-2);
130 				rect.SetCol(COLS-1);	// Vertical scroll bar
131 				break;
132 			case idStatWindow:
133 				rect.SetX(0);
134 				rect.SetY(LINES-2);
135 				rect.SetCol(COLS);
136 				rect.SetRow(1);
137 				break;
138 			case idURLWindow:
139 				rect.SetX(0);
140 				rect.SetY(LINES-1);
141 				rect.SetCol(COLS);
142 				rect.SetRow(1);
143 				break;
144 			case idHScrollBar:
145 				rect.SetX(COLS-20);
146 				rect.SetY(LINES-2);
147 				rect.SetCol(0);
148 				rect.SetRow(1);
149 				break;
150 			case idVScrollBar:
151 				rect.SetX(COLS-1);
152 				rect.SetY(0);
153 				rect.SetCol(1);
154 				rect.SetRow(LINES-2);
155 				break;
156 			case idScrollBox:
157 				rect.SetX(COLS-1);
158 				rect.SetY(LINES-2);
159 				rect.SetCol(0);
160 				rect.SetRow(0);
161 				break;
162 			case idMoreBar:
163 				rect.SetX(COLS-2);
164 				rect.SetY(0);
165 				rect.SetCol(1);
166 				rect.SetRow(0);
167 				break;
168 		}
169 	}
170 	else if (mode == modeHVScroll) {
171 		switch(id) {
172 			case idHyperWindow:
173 				rect.SetX(0);
174 				rect.SetY(0);
175 				rect.SetRow(LINES-2);
176 				rect.SetCol(COLS-2);
177 				break;
178 			case idStatWindow:
179 				rect.SetX(0);
180 				rect.SetY(LINES-2);
181 				rect.SetCol(COLS-20);
182 				rect.SetRow(1);
183 				break;
184 			case idURLWindow:
185 				rect.SetX(0);
186 				rect.SetY(LINES-1);
187 				rect.SetCol(COLS);
188 				rect.SetRow(1);
189 				break;
190 			case idHScrollBar:
191 				rect.SetX(COLS-20);
192 				rect.SetY(LINES-2);
193 				rect.SetCol(19);
194 				rect.SetRow(1);
195 				break;
196 			case idVScrollBar:
197 				rect.SetX(COLS-1);
198 				rect.SetY(0);
199 				rect.SetCol(1);
200 				rect.SetRow(LINES-2);
201 				break;
202 			case idScrollBox:
203 				rect.SetX(COLS-1);
204 				rect.SetY(LINES-2);
205 				rect.SetCol(1);
206 				rect.SetRow(1);
207 				break;
208 			case idMoreBar:
209 				rect.SetX(COLS-2);
210 				rect.SetY(0);
211 				rect.SetCol(1);
212 				rect.SetRow(LINES-2);
213 				break;
214 		}
215 	}
216 }
217 
218 /*************************************************************************
219 	khScreenManager
220 *************************************************************************/
221 
222 ATTR_TYPE	normalA, linkA, highlightA, barA, urlA, headerA, boldA,
223 		italicA, markA, linkBoldA, linkItalicA, highlightBoldA,
224 		highlightItalicA;
225 ATTR_TYPE	scrollArrowA, scrollBarA, scrollBlockA;
226 entity_id	scrollBarID, scrollBlockID;
227 
RequestResize()228 void	khScreenManager::RequestResize()
229 {
230 	if (COLS < 50 || LINES < 10) {
231 		lockScreen = true;
232 		clear();
233 		move(0, 0);
234 		addstr(_("Screen too small - please resize to make it bigger."));
235 		refresh();
236 	}
237 	else {
238 		lockScreen = false;
239 		NCScreenManager::RequestResize();
240 	}
241 }
242 
243 /* Preprocess any key pressed before passing to ProcessKey functions.  */
244 
ReadKeyboard()245 int	khScreenManager::ReadKeyboard()
246 {
247 	bool	isCtrlF = false;
248 	for ( ; ; ) {
249 		if (!lockScreen) {
250 			int	c  = NCScreenManager::ReadKeyboard();
251 			if (c == KEY_F(8))		// Refresh screen
252 				RequestRefresh();
253 							// ^R or ^L
254 			else if ((c == 'R'-'A'+1 && kcdConfig.cfgKey == keyBindingDefault)
255 				 || (c == 'L'-'A'+1 && kcdConfig.cfgKey == keyBindingVi))
256 				RequestRefresh();
257 							// ^F
258 			else if (c == 'F'-'A'+1 && kcdConfig.cfgKey == keyBindingDefault) {
259 				isCtrlF = !isCtrlF;	// Toggle state
260 				if (ctrlWin)
261 					ctrlWin->ProcessCtrl('F', isCtrlF);
262 			}
263 			else if (isCtrlF) {
264 				isCtrlF = false;
265 				if (c >= '0' && c <= '9') {
266 					if (c == '0')
267 						return KEY_F(10);
268 					else
269 						return KEY_F(c-'0');
270 				}
271 			}
272 			else
273 				return c;
274 		}
275 		else {
276 					// No display when window is too small
277 
278 			isCtrlF = false;		// Clear any previous
279 							// ^F
280 
281 					// Read and discard all input
282 			NCScreenManager::ReadKeyboard();
283 		}
284 	}
285 }
286 
InitAttr(int id,const AttrConfig & cfg,ATTR_TYPE & attr)287 void	khScreenManager::InitAttr(int id, const AttrConfig &cfg, ATTR_TYPE& attr)
288 {
289 	if (has_colors() == TRUE) {
290 		int	colorBackground = cfg.colorBackground;
291 		if (colorBackground == DEFAULT_COLOR)
292 			colorBackground = kcdConfig.cfgDefaultBackground;
293 		init_pair(id, cfg.colorForeground, colorBackground);
294 		attr = static_cast<ATTR_TYPE>(COLOR_PAIR(id) | cfg.colorAttr);
295 	}
296 	else
297 		attr = cfg.bwAttr;
298 }
299 
khScreenManager(NCGeometryManagerBase & geoMan_)300 khScreenManager::khScreenManager(NCGeometryManagerBase &geoMan_) :
301 			NCScreenManager(geoMan_), lockScreen(false)
302 {
303 	NCurses::InitMore(stdscr);	// Set term. echo, etc. for stdscr
304 
305 	InitAttr(1, kcdConfig.cfgAttrNormal, normalA);
306 	InitAttr(3, kcdConfig.cfgAttrLink, linkA);
307 	InitAttr(5, kcdConfig.cfgAttrHighlight, highlightA);
308 	InitAttr(7, kcdConfig.cfgAttrURL, urlA);
309 	InitAttr(9, kcdConfig.cfgAttrHeader, headerA);
310 	InitAttr(11, kcdConfig.cfgAttrBold, boldA);
311 	InitAttr(13, kcdConfig.cfgAttrItalic, italicA);
312 	InitAttr(15, kcdConfig.cfgAttrScrollArrow, scrollArrowA);
313 	InitAttr(17, kcdConfig.cfgAttrScrollBlock, scrollBlockA);
314 	InitAttr(19, kcdConfig.cfgAttrScrollBar, scrollBarA);
315 	InitAttr(21, kcdConfig.cfgAttrTitle, barA);
316 	InitAttr(23, kcdConfig.cfgAttrMore, markA);
317 	InitAttr(25, kcdConfig.cfgAttrLinkBold, linkBoldA);
318 	InitAttr(27, kcdConfig.cfgAttrLinkItalic, linkItalicA);
319 	InitAttr(29, kcdConfig.cfgAttrHighlightBold, highlightBoldA);
320 	InitAttr(31, kcdConfig.cfgAttrHighlightItalic, highlightItalicA);
321 
322 	scrollBlockID = EID_SPACE;
323 	if (isACSFallBack)		// No special symbols
324 		scrollBarID = EID_SPACE;
325 	else
326 		scrollBarID = EID_CKBOARD;
327 }
328 
329 
330 /*************************************************************************
331 	HyperWindow - the complete hypertext viewer
332 *************************************************************************/
333 
GetDocRow()334 int	HyperWindow::GetDocRow()
335 {
336 	return Max(GetRow(), html.GetNumRow());
337 }
338 
GetDocColumn()339 int	HyperWindow::GetDocColumn()
340 {
341 	return Max(GetCol(), html.GetNumColumn());
342 }
343 
GetNumChar(int y)344 int	HyperWindow::GetNumChar(int y)
345 {
346 	if (y >= html.GetNumRow())
347 		return 0;
348 	return html.numChar[y];
349 }
350 
HyperWindow(NCScreenManager & scrn_,int id_,HyperDocument & html_,StatusWindowBase & status_,StatusWindowBase & url_,ScrollBarBase & hscroll_,ScrollBarBase & vscroll_,const string & startSection_,int lockKey_,int row,int col)351 HyperWindow::HyperWindow(NCScreenManager &scrn_, int id_, HyperDocument &html_,
352 			StatusWindowBase &status_, StatusWindowBase &url_,
353 			ScrollBarBase &hscroll_, ScrollBarBase &vscroll_,
354 			const string &startSection_, int lockKey_, int row, int col)
355 			: NCWindowBase(scrn_, id_), marker(scrn_, idMoreBar),
356 			  html(html_), status(status_),
357 			  url(url_), hscroll(hscroll_), vscroll(vscroll_),
358 			  blankWin(NULL), newSelected("", 0, TYPE_NULL)
359 {
360 	viFoundG = false;
361 	viFoundZ = false;
362 	viFoundCount = false;
363 	viCount = 1;
364 
365 	emacsFoundAlt = false;
366 
367 	startSection = startSection_;
368 	lockKey = lockKey_;
369 
370 	initSize = 0;
371 
372 	isInitScreen = 0;
373 	InitObject();
374 
375 	if (row > 0 && col > 0) {
376 		initRow = row;
377 		initColumn = col;
378 		initSize = 1;		// Have size info
379 	}
380 
381 	PrepareDisplay(false);
382 }
383 
~HyperWindow()384 HyperWindow::~HyperWindow()
385 {
386 	if (blankWin) {
387 		delwin(blankWin);
388 		blankWin = NULL;
389 	}
390 }
391 
InitObject()392 void	HyperWindow::InitObject()		// Clear document-dependent
393 						// variables
394 {
395 	anchorSelected = NULL;
396 	prevHighlight = NULL;
397 }
398 
Init()399 void	HyperWindow::Init()
400 {
401 	NCWindowBase::Init();
402 	if (win) {			// Free previously used pad
403 		delwin(win);
404 		win = NULL;
405 	}
406 	if (blankWin) {			// Free previously used pad
407 		delwin(blankWin);
408 		blankWin = NULL;
409 	}
410 
411 	int	useHScroll = 0, useVScroll = 0;
412 	int	colLeft = COLS;
413 	int	rowLeft = LINES-2;	// Reserve 2 rows for status & URL
414 					// bar
415 	if (html.GetNumRow() > rowLeft) {
416 		useVScroll = 1;
417 		colLeft--;		// Vertical scroll bar taken
418 	}
419 	if (html.GetNumColumn() > colLeft) {
420 		useHScroll = 1;
421 		colLeft--;		// More bar taken
422 	}
423 	scrnMan.RequestChangeMode(useVScroll*2+useHScroll);
424 
425 					// From now on, GetRow() and
426 					// GetCol() returns correct
427 					// values
428 
429 	hscroll.SetTotalSize(GetDocColumn());
430 	hscroll.SetVisualSize(GetCol());
431 	vscroll.SetTotalSize(GetDocRow());
432 	vscroll.SetVisualSize(GetRow());
433 
434 					// Save actual columns used
435 	numRealColumn = html.GetNumColumn();
436 
437 					// Our new pad
438 	win = newpad(GetDocRow(), numRealColumn);
439 	if (win == NULL)
440 		throw bad_alloc();
441 
442 	if (win->_line == NULL) {	// Some macros are broken due to
443 					// different bool size
444 					// Better check available ?
445 		throw ErrorBadNCurses();
446 	}
447 
448 	NCurses::InitMore(win);
449 
450 	if (numRealColumn < GetCol()) {
451 		if (GetRow() && GetCol())
452 			blankWin = newwin(GetRow(), GetCol()-numRealColumn,
453 						0, numRealColumn);
454 		else
455 			blankWin = NULL;
456 
457 					// This shouldn't happen
458 		if (blankWin == NULL)
459 			throw bad_alloc();
460 
461 		wbkgd(blankWin, normalA);		// Set normal attr as background
462 		wattrset(blankWin, normalA);		// Set normal text attr
463 		werase(blankWin);			// Use werase(...) instead
464 							// of wclear(...) to
465 							// remove flicker
466 	}
467 	else
468 		blankWin = NULL;
469 
470 	wbkgd(win, normalA);		// Set normal attr as background
471 	wattrset(win, normalA);		// Set normal text attr
472 }
473 
Highlight()474 void	HyperWindow::Highlight()
475 {
476 	int	curR, curC;
477 	int	row = curRow+padRow, column = curColumn+padColumn;
478 	int	prev = 0, next = 0;	// Linked-list traverse direction
479 
480 	hscroll.SetPosition(padColumn);
481 	vscroll.SetPosition(padRow);
482 
483 	int	i, j;
484 	for (i = padRow, j = 0; j < GetRow(); i++, j++) {
485 		if (GetNumChar(i) <= padColumn + GetCol())
486 			marker.Mark(j, ' '|normalA);	// Use normalA so that
487 							// when the text is
488 							// selected (using mouse
489 							// under xterm/rxvt), the
490 							// attr is the same as
491 							// text in dir tree
492 		else
493 			marker.Mark(j, '+'|markA);
494 	}
495 	marker.DoUpdate();
496 
497 	if (prevHighlight) {
498 		curR = prevHighlight->rowFrom;
499 		curC = prevHighlight->colFrom;
500 		wmove(win, curR, curC);
501 
502 		wattrset(win, linkA);		// wattrset required to
503 						// force spaces to have the
504 						// same attribute as this
505 
506 		while (curR < prevHighlight->rowTo ||
507 		       (curR == prevHighlight->rowTo &&
508 			curC <= prevHighlight->colTo)) {
509 
510 			if (curC == GetNumChar(curR)) {	// End of row
511 				curC = 0;
512 				curR++;
513 				wmove(win, curR, curC);
514 			}
515 			else {
516 								// Replace attribute
517 #ifdef USE_UTF8_MODE
518 				if (IsUTF8Mode()) {
519 					cchar_t cc;
520 					win_wch(win, &cc);
521 					chtype	attr = cc.attr;
522 					if (attr == static_cast<chtype>(highlightA)) {
523 						wattrset(win, linkA);
524 						cc.attr = linkA;
525 					}
526 					else if (attr == static_cast<chtype>(highlightBoldA)) {
527 						wattrset(win, linkBoldA);
528 						cc.attr = linkBoldA;
529 					}
530 					else if (attr == static_cast<chtype>(highlightItalicA)) {
531 						wattrset(win, linkItalicA);
532 						cc.attr = linkItalicA;
533 					}
534 
535 					wadd_wch(win, &cc);
536 					curC++;
537 				}
538 				else
539 #endif
540 				{
541 					chtype	c, chr, attr;
542 					c = winch(win);
543 					attr = c & ~(A_CHARTEXT|A_ALTCHARSET);
544 					chr = c & (A_CHARTEXT|A_ALTCHARSET);
545 					if (attr == static_cast<chtype>(highlightA)) {
546 						wattrset(win, linkA);
547 					}
548 					else if (attr == static_cast<chtype>(highlightBoldA)) {
549 						wattrset(win, linkBoldA);
550 					}
551 					else if (attr == static_cast<chtype>(highlightItalicA)) {
552 						wattrset(win, linkItalicA);
553 					}
554 
555 					waddch(win, chr);
556 					curC++;
557 				}
558 			}
559 		}
560 		prevHighlight = NULL;
561 	}
562 
563 	for ( ; ; ) {
564 		if (prev && next) {		// Found nearest anchor
565 			url.SetTitle("");
566 			DoUpdate();
567 			return;
568 		}
569 
570 		if (PosAtAnchor(curAnchorIter, column, row)) {
571 					/* Found */
572 
573 			prevHighlight = (*curAnchorIter)();
574 
575 			curR = prevHighlight->rowFrom;
576 			curC = prevHighlight->colFrom;
577 			wmove(win, curR, curC);
578 
579 			wattrset(win, highlightA);
580 
581 			while (curR < prevHighlight->rowTo ||
582 			       (curR == prevHighlight->rowTo &&
583 				curC <= prevHighlight->colTo)) {
584 
585 				if (curC == GetNumChar(curR)) {	// End of row
586 					curC = 0;
587 					curR++;
588 					wmove(win, curR, curC);
589 				}
590 				else {
591 									// Replace attribute
592 									// Fixme Unicode
593 #ifdef USE_UTF8_MODE
594 					if (IsUTF8Mode()) {
595 						cchar_t cc;
596 						win_wch(win, &cc);
597 						chtype	attr = cc.attr;
598 						if (attr == static_cast<chtype>(linkA)) {
599 							wattrset(win, highlightA);
600 							cc.attr = highlightA;
601 						}
602 						else if (attr == static_cast<chtype>(linkBoldA)) {
603 							wattrset(win, highlightBoldA);
604 							cc.attr = highlightBoldA;
605 						}
606 						else if (attr == static_cast<chtype>(linkItalicA)) {
607 							wattrset(win, highlightItalicA);
608 							cc.attr = highlightItalicA;
609 						}
610 
611 						wadd_wch(win, &cc);
612 						curC++;
613 					}
614 					else
615 #endif
616 					{
617 						chtype	c, chr, attr;
618 						c = winch(win);
619 						attr = c & ~(A_CHARTEXT|A_ALTCHARSET);
620 						chr = c & (A_CHARTEXT|A_ALTCHARSET);
621 						if (attr == static_cast<chtype>(linkA)) {
622 							wattrset(win, highlightA);
623 						}
624 						else if (attr == static_cast<chtype>(linkBoldA)) {
625 							wattrset(win, highlightBoldA);
626 						}
627 						else if (attr == static_cast<chtype>(linkItalicA)) {
628 							wattrset(win, highlightItalicA);
629 						}
630 
631 						waddch(win, chr);
632 						curC++;
633 					}
634 				}
635 			}
636 			url.SetTitle((*curAnchorIter)->name);
637 			DoUpdate();
638 			return;
639 		}
640 
641 		if (PosBeforeAnchor(curAnchorIter, column, row)) {
642 				/* Have to use prev node of linked-list */
643 			if (curAnchorIter == html.GetAnchorList().begin()) {
644 					/* Ignore */
645 				url.SetTitle("");
646 				DoUpdate();
647 				return;
648 			}
649 			else
650 				--curAnchorIter;
651 			prev = 1;
652 			continue;
653 		}
654 
655 		if (PosAfterAnchor(curAnchorIter, column, row)) {
656 				/* Have to use next node of linked-list */
657 			if (curAnchorIter == --(html.GetAnchorList().end())) {
658 					/* Ignore */
659 				url.SetTitle("");
660 				DoUpdate();
661 				return;
662 			}
663 			else
664 				++curAnchorIter;
665 			next = 1;
666 			continue;
667 		}
668 	}
669 }
670 
PrepareDisplay(bool sameDocument)671 void	HyperWindow::PrepareDisplay(bool sameDocument)
672 {
673 	if (! sameDocument) {
674 
675 		InitObject();		// Clear document-dependent varialbles
676 
677 		InitGeometry();		// We need window size for GetRow and
678 					// GetCol below
679 		if (!initSize) {	// No size info available
680 			html.FormatDocument(NULL, LINES-2, COLS-2);
681 		}
682 		else {
683 			initSize = 0;		// size is used
684 			html.SetDocumentSize(initRow, initColumn, GetRow(), GetCol());
685 		}
686 
687 		Init();		// Allocate a new pad
688 
689 				// Output document
690 		html.FormatDocument(win, LINES-2, COLS-2);
691 
692 		status.SetTitle(html.GetTitle());
693 
694 	}
695 	else {
696 		anchorSelected = NULL;
697 					// Do not reset prevHighlight
698 	}
699 
700 	padRow = 0, padColumn = 0;	/* Pad position */
701 	curRow = 0, curColumn = 0;	/* Cursor position */
702 	const Anchor	*curSection = NULL;
703 
704 	curAnchorIter = html.GetAnchorList().begin();
705 
706 	if (html.GetAnchorList().size() == 0 && lockKey) {
707 		throw ErrorGenericSyntax(_("no <A HREF=...> tags present\n"
708 			   		   "Do not use the `-c\' option for this file"));
709 	}
710 
711 	if (startSection.size()) {
712 		for (iterType iter = html.GetSectionList().begin();
713 		     iter != html.GetSectionList().end(); ++iter) {
714 			if ((*iter)->name == startSection) {
715 				curSection = (*iter)();
716 				break;
717 			}
718 		}
719 
720 		if (curSection == NULL) {
721 			throw ErrorGenericSyntax(_("cannot find <A NAME=\"%$\">"),
722 						 startSection);
723 		}
724 	}
725 
726 	if (lockKey || curSection) {
727 		if (!lockKey) {
728 				/* Put cursor at specified section */
729 
730 				/* Center of the screen */
731 			padRow = (curSection->rowTo+curSection->rowFrom)/2 -
732 					(GetRow()-1)/2;
733 			padColumn = (curSection->colTo+curSection->colFrom)/2 -
734 					(GetCol()-1)/2;
735 
736 				/* Check lower limit */
737 			if (padRow < 0)
738 				padRow = 0;
739 			if (padColumn < 0)
740 				padColumn = 0;
741 
742 				/* Check upper limit */
743 			padRow = Min(padRow, GetDocRow()-GetRow());
744 			padColumn = Min(padColumn, GetDocColumn()-GetCol());
745 
746 				/* Calc. cursor position */
747 			curRow = curSection->rowFrom-padRow;
748 			curColumn = curSection->colFrom-padColumn;
749 		}
750 
751 		if (curSection) {
752 				/* Find anchor directly after curSection */
753 			for ( ; curAnchorIter != --(html.GetAnchorList().end()); ++curAnchorIter) {
754 				if ((*curAnchorIter)->rowFrom > curSection->rowFrom ||
755 				    ((*curAnchorIter)->rowFrom == curSection->rowFrom &&
756 				     (*curAnchorIter)->colFrom >= curSection->colFrom))
757 					break;
758 			}
759 
760 		}
761 
762 			/* Found first goto/exec */
763 		if (lockKey) {
764 				/* Put cursor at the link directly
765 				   after section name */
766 			CenterPad(curAnchorIter);
767 
768 				/* Check lower limit */
769 			if (padRow < 0)
770 				padRow = 0;
771 			if (padColumn < 0)
772 				padColumn = 0;
773 
774 				/* Check upper limit */
775 			padRow = Min(padRow, GetDocRow()-GetRow());
776 			padColumn = Min(padColumn, GetDocColumn()-GetCol());
777 
778 				/* Calc. cursor position */
779 			curRow = (*curAnchorIter)->rowFrom-padRow;
780 			curColumn = (*curAnchorIter)->colFrom-padColumn;
781 
782 			if ((*curAnchorIter)->colTo-(*curAnchorIter)->colFrom > GetCol()-1) {
783 					// Highlight bar too long - show only
784 					// left portion and discard parts
785 					// that go beyond the screen width
786 
787 					// Scroll left
788 				padColumn += curColumn;
789 				curColumn = 0;
790 			}
791 		}
792 	}
793 
794 	if (lockKey)		/* No need for cursor */
795 		cursor.Hide();
796 
797 	Highlight();
798 	scrnMan.RequestRestCursor();
799 }
800 
MatchSubString(iterType & curA,const string & findText)801 bool	HyperWindow::MatchSubString(iterType &curA, const string &findText)
802 {
803 	if ((*curA)->linkText.find(findText) != string::npos)
804 		return true;	// Found
805 	return false;		// Not found
806 }
807 
DoResize()808 void	HyperWindow::DoResize()
809 {
810 	InitGeometry();
811 
812 	if (!win)	// Window not initialized yet, due to mode changing
813 			// in Init().  This also prevents from infinite loop
814 			// since Init() is called below.
815 		return;
816 
817 	int	curAnchorIndex = 0;
818 	int	cursorRow = 0;
819 	int	cursorColumn = 0;
820 
821 			// Try to preserve cursor position after screen resize
822 	if (lockKey) {
823 			// Save the index of current highlighted anchor
824 
825 		for (iterType iter = html.GetAnchorList().begin();
826 		     iter != curAnchorIter; ++iter) {
827 			curAnchorIndex++;
828 		}
829 	}
830 	else {		// The best we can do now - may be changed later
831 		cursorRow = curRow+padRow;
832 		cursorColumn = curColumn+padColumn;
833 	}
834 
835 	InitObject();		// Clear document-dependent varialbles
836 
837 	html.FormatDocument(NULL, LINES-2, COLS-2);
838 	Init();
839 	html.FormatDocument(win, LINES-2, COLS-2);
840 
841 			// Restore cursor position
842 	if (lockKey) {
843 			// Seek to current highlighted anchor
844 
845 		for (curAnchorIter = html.GetAnchorList().begin();
846 		     curAnchorIndex > 0; ++curAnchorIter) {
847 			curAnchorIndex--;
848 		}
849 
850 				/* Put cursor at the link directly
851 				   after section name */
852 
853 				/* Center of the screen */
854 		CenterPad(curAnchorIter);
855 
856 				/* Check lower limit */
857 		if (padRow < 0)
858 			padRow = 0;
859 		if (padColumn < 0)
860 			padColumn = 0;
861 
862 				/* Check upper limit */
863 		padRow = Min(padRow, GetDocRow()-GetRow());
864 		padColumn = Min(padColumn, GetDocColumn()-GetCol());
865 
866 				/* Calc. cursor position */
867 		curRow = (*curAnchorIter)->rowFrom-padRow;
868 		curColumn = (*curAnchorIter)->colFrom-padColumn;
869 
870 		if ((*curAnchorIter)->colTo-(*curAnchorIter)->colFrom > GetCol()-1) {
871 				// Highlight bar too long - show only
872 				// left portion and discard parts
873 				// that go beyond the screen width
874 
875 				// Scroll left
876 			padColumn += curColumn;
877 			curColumn = 0;
878 		}
879 	}
880 	else {
881 				// Previous cursor position is no longer
882 				//	valid
883 		if (GetDocRow() < cursorRow)
884 			cursorRow = GetDocRow()-1;
885 		if (GetDocColumn() < cursorColumn)
886 			cursorColumn = GetDocColumn()-1;
887 
888 				/* Center of the screen */
889 		padRow = cursorRow-(GetRow()-1)/2;
890 		padColumn = cursorColumn-(GetCol()-1)/2;
891 
892 				/* Check lower limit */
893 		if (padRow < 0)
894 			padRow = 0;
895 		if (padColumn < 0)
896 			padColumn = 0;
897 
898 				/* Check upper limit */
899 		padRow = Min(padRow, GetDocRow()-GetRow());
900 		padColumn = Min(padColumn, GetDocColumn()-GetCol());
901 
902 				/* Calc. cursor position */
903 		curRow = cursorRow-padRow;
904 		curColumn = cursorColumn-padColumn;
905 	}
906 
907 	Highlight();
908 	scrnMan.RequestRestCursor();
909 }
910 
DoUpdate()911 void	HyperWindow::DoUpdate()
912 {
913 	if (numRealColumn < GetCol())
914 		pnoutrefresh(win, padRow, padColumn, GetY1(), GetX1(),
915 				GetY2(), GetX1()+numRealColumn-1);
916 	else
917 		pnoutrefresh(win, padRow, padColumn, GetY1(), GetX1(),
918 				GetY2(), GetX2());
919 	if (blankWin)
920 		wnoutrefresh(blankWin);
921 }
922 
FitHighlightHorizontal(iterType & newA)923 void	HyperWindow::FitHighlightHorizontal(iterType &newA)
924 {
925 	// Six cases to consider
926 	// Legends: |      | = screen boundary, **** = highlight
927 	// 1 **|**    |		==>   |****  |
928 	// 2 **|***** |		==>  *|******|
929 	// 3 **|******|*	==> **|******|*
930 	// 4   | **** |		=>    | **** |
931 	// 5   |    **|**	==>   |  ****|
932 	// 6   | *****|**	==>   |******|*
933 
934 	if (curColumn < 0) {
935 		if ((*newA)->colTo-(*newA)->colFrom <= GetCol()-1) {
936 				// Case 1
937 				// Scroll right
938 			padColumn += curColumn;
939 			curColumn = 0;
940 		}
941 		else if (curColumn+(*newA)->colTo-(*newA)->colFrom <= GetCol()-1) {
942 				// Case 2
943 			int diff = GetCol()-1
944 				   -curColumn-(*newA)->colTo+(*newA)->colFrom;
945 			padColumn += diff;
946 			curColumn -= diff;
947 		}
948 		else {
949 				// Case 3
950 			;
951 		}
952 	}
953 	else {
954 		if (curColumn+(*newA)->colTo-(*newA)->colFrom <= GetCol()-1) {
955 				// Case 4
956 			;
957 		}
958 		else if ((*newA)->colTo-(*newA)->colFrom <= GetCol()-1) {
959 				// Case 5
960 				// Scroll left
961 			int diff = curColumn+(*newA)->colTo-(*newA)->colFrom
962 				   -GetCol()+1;
963 			padColumn += diff;
964 			curColumn -= diff;
965 		}
966 		else {
967 				// Case 5
968 			padColumn += curColumn;
969 			curColumn = 0;
970 		}
971 	}
972 }
973 
FitHighlightTop(iterType & newA)974 void	HyperWindow::FitHighlightTop(iterType &newA)
975 {
976 	if (curRow < 0) {
977 					/* Scroll up */
978 		padRow += curRow;
979 		curRow = 0;
980 	}
981 	FitHighlightHorizontal(newA);
982 }
983 
CenterRowPad(iterType & newA)984 void	HyperWindow::CenterRowPad(iterType &newA)
985 {
986 					/* Center of the screen */
987 	padRow = ((*newA)->rowTo+(*newA)->rowFrom)/2 - (GetRow()-1)/2;
988 }
989 
CenterRowPadIfScroll(iterType & newA)990 void	HyperWindow::CenterRowPadIfScroll(iterType &newA)
991 {
992 	if ((*newA)->rowFrom < padRow || (*newA)->rowTo > padRow+GetRow()-1)
993 		CenterRowPad(newA);
994 }
995 
CenterPad(iterType & newA)996 void	HyperWindow::CenterPad(iterType &newA)
997 {
998 					/* Center of the screen */
999 	CenterRowPad(newA);
1000 	padColumn = ((*newA)->colTo+(*newA)->colFrom)/2 -
1001 			(GetCol()-1)/2;
1002 }
1003 
FitHighlightMiddle(iterType & newA)1004 void	HyperWindow::FitHighlightMiddle(iterType &newA)
1005 {
1006 					/* Check lower limit */
1007 	if (padRow < 0)
1008 		padRow = 0;
1009 
1010 					/* Check upper limit */
1011 	padRow = Min(padRow, GetDocRow()-GetRow());
1012 
1013 					/* Calc. cursor position */
1014 	curRow = (*newA)->rowFrom-padRow;
1015 	curColumn = (*newA)->colFrom-padColumn;
1016 
1017 	FitHighlightHorizontal(newA);
1018 }
1019 
FitHighlightBottom(iterType & newA)1020 void	HyperWindow::FitHighlightBottom(iterType &newA)
1021 {
1022 	if (curRow+(*newA)->rowTo-(*newA)->rowFrom > GetRow()-1) {
1023 					/* Scroll down */
1024 		padRow += curRow+(*newA)->rowTo-(*newA)->rowFrom-(GetRow()-1);
1025 		curRow = GetRow()-1-((*newA)->rowTo-(*newA)->rowFrom);
1026 	}
1027 
1028 	FitHighlightHorizontal(newA);
1029 }
1030 
PosBeforeAnchor(iterType & p,int x,int y)1031 bool	HyperWindow::PosBeforeAnchor(iterType &p, int x, int y)
1032 {
1033 	if (y < (*p)->rowFrom || (y == (*p)->rowFrom && x < (*p)->colFrom))
1034 		return true;
1035 	return false;
1036 }
1037 
PosAfterAnchor(iterType & p,int x,int y)1038 bool	HyperWindow::PosAfterAnchor(iterType &p, int x, int y)
1039 {
1040 	if (y > (*p)->rowTo || (y == (*p)->rowTo && x > (*p)->colTo))
1041 		return true;
1042 	return false;
1043 }
1044 
PosAtAnchor(iterType & p,int x,int y)1045 bool	HyperWindow::PosAtAnchor(iterType &p, int x, int y)
1046 {
1047 	return !(PosAfterAnchor(p, x, y) || PosBeforeAnchor(p, x, y));
1048 }
1049 
ProcessCall(Anchor *)1050 Anchor	HyperWindow::ProcessCall(Anchor * /*p*/)
1051 {
1052 	return Anchor("", 0, TYPE_NULL);		// Do nothing
1053 }
1054 
1055 #ifdef NCURSES_MOUSE_VERSION
ProcessMouse(MEVENT & event)1056 void	HyperWindow::ProcessMouse(MEVENT &event)
1057 {
1058 			// Only accepts mouse button 1
1059 	if (!(event.bstate & (BUTTON1_CLICKED | BUTTON1_DOUBLE_CLICKED
1060 			      | BUTTON1_TRIPLE_CLICKED | BUTTON1_RELEASED)))
1061 		return;
1062 
1063 	iterType saveIter = curAnchorIter;
1064 	if (html.GetAnchorList().empty())		// No links found
1065 		return;
1066 
1067 	int	absRow = event.y-GetY1()+padRow;
1068 	int	absColumn = event.x-GetX1()+padColumn;
1069 	if (PosAtAnchor(curAnchorIter, absColumn, absRow)) {
1070 		curRow = absRow-padRow;
1071 		curColumn = absColumn-padColumn;
1072 		ProcessKey('\r');
1073 	}
1074 	else if (PosBeforeAnchor(curAnchorIter, absColumn, absRow)) {
1075 		while (curAnchorIter != html.GetAnchorList().begin()
1076 		       && PosBeforeAnchor(curAnchorIter, absColumn, absRow)) {
1077 			--curAnchorIter;
1078 		}
1079 
1080 		if (PosAtAnchor(curAnchorIter, absColumn, absRow)) {
1081 			curRow = absRow-padRow;
1082 			curColumn = absColumn-padColumn;
1083 			ProcessKey('\r');
1084 		}
1085 		else
1086 			curAnchorIter = saveIter;
1087 	}
1088 	else {
1089 		while (curAnchorIter != --(html.GetAnchorList().end())
1090 		       && PosBeforeAnchor(curAnchorIter, absColumn, absRow)) {
1091 			++curAnchorIter;
1092 		}
1093 
1094 		if (PosAtAnchor(curAnchorIter, absColumn, absRow)) {
1095 			curRow = absRow-padRow;
1096 			curColumn = absColumn-padColumn;
1097 			ProcessKey('\r');
1098 		}
1099 		else
1100 			curAnchorIter = saveIter;
1101 	}
1102 }
1103 #endif
1104 
ProcessKeySelect()1105 void	HyperWindow::ProcessKeySelect()
1106 {
1107 	int	absRow = curRow+padRow;
1108 	int	absCol = curColumn+padColumn;
1109 
1110 					/* Look for anchor under
1111 					   cursor */
1112 	if (html.GetAnchorList().empty())
1113 		return;			/* Ignore */
1114 
1115 	if (PosAtAnchor(curAnchorIter, absCol, absRow)) {
1116 		anchorSelected = (*curAnchorIter)();
1117 	}
1118 	else			/* Cursor not on any link */
1119 		return;
1120 
1121 	if (anchorSelected) {
1122 		if (anchorSelected->type == TYPE_CALL) {
1123 			newSelected = *anchorSelected;
1124 			while (newSelected.type == TYPE_CALL) {
1125 				newSelected = ProcessCall(&newSelected);
1126 				if (newSelected.type == TYPE_NULL)
1127 					return;		// Ignore
1128 			}
1129 			anchorSelected = &newSelected;
1130 		}
1131 
1132 		if (anchorSelected->type == TYPE_GOTO) {
1133 
1134 			string	newHyperFile;
1135 
1136 			string::size_type s = anchorSelected->name.find('#');
1137 			if (s != string::npos) {
1138 				startSection = string(anchorSelected->name, s+1);
1139 				newHyperFile = string(anchorSelected->name, 0, s);
1140 			}
1141 			else {
1142 				startSection = "";
1143 				newHyperFile = anchorSelected->name;
1144 			}
1145 			anchorSelected = NULL;
1146 
1147 					/* Link to another file */
1148 			if (newHyperFile.size()) {
1149 #ifndef TRIM_NO_DOC_FILE
1150 				html.LoadDocument(newHyperFile);
1151 				PrepareDisplay(false);
1152 #else
1153 				;
1154 #endif
1155 			}
1156 			else {	// Link to the same file
1157 				PrepareDisplay(true);
1158 			}
1159 
1160 			Highlight();
1161 			scrnMan.RequestRestCursor();
1162 		}
1163 		else if (anchorSelected->type == TYPE_EXEC) {
1164 			scrnMan.SetFocus(NULL);
1165 			return;
1166 		}
1167 		else if (anchorSelected->type == TYPE_EXIT) {
1168 			scrnMan.SetFocus(NULL);
1169 			return;
1170 		}
1171 		/* else unknown anchor type */
1172 	}
1173 }
1174 
ProcessKeyCenter()1175 void	HyperWindow::ProcessKeyCenter()
1176 {
1177 	if (html.GetAnchorList().empty())
1178 		return;		/* Ignore */
1179 
1180 	int	absRow = curRow+padRow;
1181 	int	absCol = curColumn+padColumn;
1182 
1183 	if (PosAtAnchor(curAnchorIter, absCol, absRow)) {
1184 		CenterRowPad(curAnchorIter);
1185 		FitHighlightMiddle(curAnchorIter);
1186 		Highlight();
1187 		scrnMan.RequestRestCursor();
1188 	}
1189 	else		/* Cursor not on any link */
1190 			// FIXME: center screen
1191 		return;
1192 }
1193 
ProcessKeyNextStatus()1194 void	HyperWindow::ProcessKeyNextStatus()
1195 {
1196 	url.NextStatus();
1197 	Highlight();
1198 	scrnMan.RequestRestCursor();
1199 }
1200 
ProcessKeyExit()1201 void	HyperWindow::ProcessKeyExit()
1202 {
1203 	scrnMan.SetFocus(NULL);
1204 }
1205 
ProcessKeyPanLeft()1206 void	HyperWindow::ProcessKeyPanLeft()
1207 {
1208 	if (lockKey) {
1209 		if ((*curAnchorIter)->rowFrom == (*curAnchorIter)->rowTo) {
1210 
1211 				// Highlight bar not as wide as screen
1212 			if ((*curAnchorIter)->colTo-(*curAnchorIter)->colFrom+1 < GetCol()) {
1213 
1214 				// Make sure the entire
1215 				// highlight bar is displayed
1216 
1217 				if (padColumn+GetCol()-1 > (*curAnchorIter)->colTo) {
1218 					if (padColumn > 0) {
1219 						padColumn--;
1220 						curColumn++;
1221 					}
1222 				}
1223 			}
1224 
1225 				// Highlight bar wider than screen
1226 			else if ((*curAnchorIter)->colTo-(*curAnchorIter)->colFrom+1 > GetCol()) {
1227 				if (padColumn > (*curAnchorIter)->colFrom) {
1228 					padColumn--;
1229 					curColumn++;
1230 				}
1231 			}
1232 		}
1233 		else {
1234 				// Multi-line highlight
1235 
1236 			if (padColumn > 0) {
1237 				padColumn--;
1238 				curColumn++;
1239 			}
1240 		}
1241 	}
1242 	else {
1243 		if (padColumn > 0)
1244 			padColumn--;
1245 	}
1246 	Highlight();
1247 	scrnMan.RequestRestCursor();
1248 }
1249 
ProcessKeyPanRight()1250 void	HyperWindow::ProcessKeyPanRight()
1251 {
1252 	if (lockKey) {
1253 		if ((*curAnchorIter)->rowFrom == (*curAnchorIter)->rowTo) {
1254 
1255 				// Highlight bar not as wide as screen
1256 			if ((*curAnchorIter)->colTo-(*curAnchorIter)->colFrom+1 < GetCol()) {
1257 
1258 				// Make sure the entire
1259 				// highlight bar is displayed
1260 
1261 				if (padColumn < (*curAnchorIter)->colFrom)
1262 					if (padColumn+GetCol() < GetDocColumn()) {
1263 						padColumn++;
1264 						curColumn--;
1265 					}
1266 			}
1267 
1268 				// Highlight bar wider than screen
1269 			else if ((*curAnchorIter)->colTo-(*curAnchorIter)->colFrom+1 > GetCol()) {
1270 				if (padColumn+GetCol()-1 < (*curAnchorIter)->colTo) {
1271 					padColumn++;
1272 					curColumn--;
1273 				}
1274 			}
1275 		}
1276 		else {
1277 				// Multi-line highlight
1278 
1279 			if (padColumn+GetCol() < GetDocColumn()) {
1280 				padColumn++;
1281 				curColumn--;
1282 			}
1283 		}
1284 	}
1285 	else {
1286 		if (padColumn+GetCol() < GetDocColumn())
1287 			padColumn++;
1288 	}
1289 	Highlight();
1290 	scrnMan.RequestRestCursor();
1291 }
1292 
ProcessKeyUp()1293 void	HyperWindow::ProcessKeyUp()
1294 {
1295 	if (lockKey) {
1296 		iterType saveIter = curAnchorIter;
1297 		if (curAnchorIter != html.GetAnchorList().begin()) {
1298 					/* Skip links in current line */
1299 			do {
1300 				--curAnchorIter;
1301 			} while (curAnchorIter != html.GetAnchorList().begin()
1302 				 && ((*curAnchorIter)->rowFrom == (*saveIter)->rowFrom));
1303 
1304 						// Initialize in case this is the
1305 						// topmost anchor
1306 			iterType bestAnc = html.GetAnchorList().begin();
1307 			int	bestDiff = INT_MAX, diff;
1308 			int	bestOverlap = 0, overlap, overlapRow = -1;
1309 
1310 			/* Now link not in current line */
1311 			for ( ; ; ) {
1312 				int	from1 = (*curAnchorIter)->colFrom;
1313 				int	to1 = (*curAnchorIter)->colTo;
1314 				int	wrap1 = (*curAnchorIter)->rowTo-(*curAnchorIter)->rowFrom;
1315 				int	from2 = (*saveIter)->colFrom;
1316 				int	to2 = (*saveIter)->colTo;
1317 				int	wrap2 = (*saveIter)->rowTo-
1318 						(*saveIter)->rowFrom;
1319 
1320 				overlap = 0;
1321 
1322 				if (!wrap1 && !wrap2 &&
1323 				    (Max(from1, from2) <= Min(to1, to2)))
1324 					overlap = Min(to1, to2)-Max(from1, from2)+1;
1325 				else if (wrap1 && !wrap2 &&
1326 					 (from2 <= to1))
1327 					overlap = to1-from2+1;
1328 				else if (wrap1 && !wrap2 &&
1329 					 (to2 >= from1))
1330 					overlap = to2-from1+1;
1331 				else if (!wrap1 && wrap2 &&
1332 					 (from1 <= to2))
1333 					overlap = to2-from1+1;
1334 				else if (!wrap1 && wrap2 &&
1335 					 (to1 >= from2))
1336 					overlap = to1-from2+1;
1337 				else if (wrap1 && wrap2)
1338 					overlap = Min(to1, to2)+1+
1339 						  (GetCol()-Max(from1, from2)+1);
1340 
1341 				if (overlap) {
1342 			     			/* Found */
1343 			     		bestDiff = 0;
1344 			     		if (overlapRow == -1)
1345 			     			overlapRow = (*curAnchorIter)->rowFrom;
1346 
1347 			     		if (overlap > bestOverlap) {
1348 
1349 			     			/* Already have an overlap, ignore anchors in
1350 			     			   further lines.  */
1351 						if (overlapRow != (*curAnchorIter)->rowFrom)
1352 							break;
1353 
1354 						bestOverlap = overlap;
1355 						bestAnc = curAnchorIter;
1356 					}
1357 				}
1358 				else {
1359 					if (bestDiff == 0)
1360 						break;
1361 
1362 					diff = Min(abs(from2-to1),
1363 						   abs(from1-to2));
1364 					if (diff < bestDiff) {
1365 						bestDiff = diff;
1366 						bestAnc = curAnchorIter;
1367 					}
1368 				}
1369 
1370 				if (curAnchorIter == html.GetAnchorList().begin())
1371 					break;
1372 
1373 				--curAnchorIter;
1374 			}
1375 						/* Cannot find links aligned */
1376 			curAnchorIter = bestAnc;
1377 
1378 			curRow -= (*saveIter)->rowFrom - (*curAnchorIter)->rowFrom;
1379 			curColumn -= (*saveIter)->colFrom - (*curAnchorIter)->colFrom;
1380 			FitHighlightTop(curAnchorIter);
1381 		}
1382 	}
1383 	else {
1384 		if (curRow+padRow > 0) {
1385 			curRow--;
1386 			if (curRow < 0) {
1387 				curRow = 0;
1388 				padRow--;
1389 			}
1390 		}
1391 		else
1392 			return;
1393 	}
1394 
1395 	Highlight();
1396 	scrnMan.RequestRestCursor();
1397 }
1398 
ProcessKeyDown()1399 void	HyperWindow::ProcessKeyDown()
1400 {
1401 	if (lockKey) {
1402 		iterType saveIter = curAnchorIter;
1403 		if (curAnchorIter != --(html.GetAnchorList().end())) {
1404 
1405 					/* Skip links in current line */
1406 			do {
1407 				++curAnchorIter;
1408 			} while (curAnchorIter != --(html.GetAnchorList().end())
1409 				 && ((*curAnchorIter)->rowFrom == (*saveIter)->rowFrom));
1410 
1411 						// Initialize in case this is the
1412 						// bottommost anchor
1413 			iterType bestAnc = --(html.GetAnchorList().end());
1414 			int	bestDiff = INT_MAX, diff;
1415 			int	bestOverlap = 0, overlap, overlapRow = -1;
1416 				/* Now link not in current line */
1417 			for ( ; ; ) {
1418 				int	from1 = (*curAnchorIter)->colFrom;
1419 				int	to1 = (*curAnchorIter)->colTo;
1420 				int	wrap1 = (*curAnchorIter)->rowTo-(*curAnchorIter)->rowFrom;
1421 				int	from2 = (*saveIter)->colFrom;
1422 				int	to2 = (*saveIter)->colTo;
1423 				int	wrap2 = (*saveIter)->rowTo-
1424 						(*saveIter)->rowFrom;
1425 
1426 				overlap = 0;
1427 
1428 				if (!wrap1 && !wrap2 &&
1429 				    (Max(from1, from2) <= Min(to1, to2)))
1430 					overlap = Min(to1, to2)-Max(from1, from2)+1;
1431 				else if (wrap1 && !wrap2 &&
1432 					 (from2 <= to1))
1433 					overlap = to1-from2+1;
1434 				else if (wrap1 && !wrap2 &&
1435 					 (to2 >= from1))
1436 					overlap = to2-from1+1;
1437 				else if (!wrap1 && wrap2 &&
1438 					 (from1 <= to2))
1439 					overlap = to2-from1+1;
1440 				else if (!wrap1 && wrap2 &&
1441 					 (to1 >= from2))
1442 					overlap = to1-from2+1;
1443 				else if (wrap1 && wrap2)
1444 					overlap = Min(to1, to2)+1+
1445 						  (GetCol()-Max(from1, from2)+1);
1446 
1447 				if (overlap) {
1448 			     			/* Found */
1449 			     		bestDiff = 0;
1450 			     		if (overlapRow == -1)
1451 			     			overlapRow = (*curAnchorIter)->rowFrom;
1452 
1453 			     		if (overlap > bestOverlap) {
1454 
1455 			     			/* Already have an overlap, ignore anchors in
1456 			     			   further lines.  */
1457 						if (overlapRow != (*curAnchorIter)->rowFrom)
1458 							break;
1459 
1460 						bestOverlap = overlap;
1461 						bestAnc = curAnchorIter;
1462 					}
1463 				}
1464 				else {
1465 					if (bestDiff == 0)
1466 						break;
1467 
1468 					diff = Min(abs(from2-to1),
1469 						   abs(from1-to2));
1470 					if (diff < bestDiff) {
1471 						bestDiff = diff;
1472 						bestAnc = curAnchorIter;
1473 					}
1474 				}
1475 
1476 				if (curAnchorIter == --(html.GetAnchorList().end()))
1477 					break;
1478 
1479 				++curAnchorIter;
1480 
1481 			}
1482 
1483 						/* Cannot find links aligned */
1484 				curAnchorIter = bestAnc;
1485 
1486 			curRow -= (*saveIter)->rowFrom - (*curAnchorIter)->rowFrom;
1487 			curColumn -= (*saveIter)->colFrom - (*curAnchorIter)->colFrom;
1488 			FitHighlightBottom(curAnchorIter);
1489 		}
1490 	}
1491 	else {
1492 		if (curRow+padRow < GetDocRow()-1) {
1493 			curRow++;
1494 			if (curRow >= GetRow()) {
1495 				curRow = GetRow()-1;
1496 				padRow++;
1497 			}
1498 		}
1499 		else
1500 			return;
1501 	}
1502 
1503 	Highlight();
1504 	scrnMan.RequestRestCursor();
1505 }
1506 
ProcessKeyFirstLine()1507 void	HyperWindow::ProcessKeyFirstLine()
1508 {
1509 	if (lockKey) {
1510 		iterType saveIter = curAnchorIter;
1511 		if (curAnchorIter != html.GetAnchorList().begin()) {
1512 
1513 					/* Skip links till the first row of screen */
1514 			do {
1515 				--curAnchorIter;
1516 			} while (curAnchorIter != html.GetAnchorList().begin()
1517 				 && ((*curAnchorIter)->rowFrom == (*saveIter)->rowFrom
1518 					|| (*curAnchorIter)->rowFrom > padRow));
1519 			curRow -= (*saveIter)->rowFrom - (*curAnchorIter)->rowFrom;
1520 			curColumn -= (*saveIter)->colFrom - (*curAnchorIter)->colFrom;
1521 			FitHighlightTop(curAnchorIter);
1522 		}
1523 	}
1524 	else {
1525 		if (curRow+padRow > 0) {
1526 			curRow--;
1527 			if (curRow < 0) {
1528 				curRow = 0;
1529 				padRow--;
1530 			}
1531 		}
1532 		else
1533 			return;
1534 	}
1535 
1536 	Highlight();
1537 	scrnMan.RequestRestCursor();
1538 }
1539 
ProcessKeyLastLine()1540 void	HyperWindow::ProcessKeyLastLine()
1541 {
1542 	if (lockKey) {
1543 		iterType saveIter = curAnchorIter;
1544 		if (curAnchorIter != --(html.GetAnchorList().end())) {
1545 
1546 				/* Skip links till the last row of screen */
1547 			do {
1548 				++curAnchorIter;
1549 			} while (curAnchorIter != --(html.GetAnchorList().end())
1550 				 && ((*curAnchorIter)->rowFrom == (*saveIter)->rowFrom
1551 					|| (*curAnchorIter)->rowFrom < padRow+GetRow()-1));
1552 
1553 			curRow -= (*saveIter)->rowFrom - (*curAnchorIter)->rowFrom;
1554 			curColumn -= (*saveIter)->colFrom - (*curAnchorIter)->colFrom;
1555 			FitHighlightBottom(curAnchorIter);
1556 		}
1557 	}
1558 	else {
1559 		if (curRow+padRow < GetDocRow()-1) {
1560 			curRow++;
1561 			if (curRow >= GetRow()) {
1562 				curRow = GetRow()-1;
1563 				padRow++;
1564 			}
1565 		}
1566 		else
1567 			return;
1568 	}
1569 
1570 	Highlight();
1571 	scrnMan.RequestRestCursor();
1572 }
1573 
ProcessKeyScrollUpLines(size_t lines)1574 void	HyperWindow::ProcessKeyScrollUpLines(size_t lines)
1575 {
1576 	if (!lines)
1577 		return;
1578 	if (lockKey) {
1579 		iterType saveIter = curAnchorIter;
1580 		if (curAnchorIter != html.GetAnchorList().begin()) {
1581 			do {
1582 				--curAnchorIter;
1583 
1584 				if ((*curAnchorIter)->rowFrom <= (*saveIter)->rowFrom-static_cast<int>(lines)) {
1585 					padRow -= lines;
1586 					if (padRow < 0)
1587 						padRow = 0;
1588 					break;
1589 				}
1590 			} while(curAnchorIter != html.GetAnchorList().begin());
1591 
1592 			if (curAnchorIter == html.GetAnchorList().begin())
1593 				padRow = 0;
1594 
1595 			curRow = (*curAnchorIter)->rowFrom-padRow;
1596 			curColumn = (*curAnchorIter)->colFrom-padColumn;
1597 
1598 			if (curRow < 0) {
1599 				/* Scroll up */
1600 				padRow += curRow;
1601 				curRow = 0;
1602 			}
1603 			if (curRow+(*curAnchorIter)->rowTo-(*curAnchorIter)->rowFrom > GetRow()-1) {
1604 				/* Scroll down */
1605 				padRow += curRow+(*curAnchorIter)->rowTo-(*curAnchorIter)->rowFrom-
1606 						(GetRow()-1);
1607 				curRow = GetRow()-1-((*curAnchorIter)->rowTo-(*curAnchorIter)->rowFrom);
1608 			}
1609 			if (curColumn < 0) {
1610 				/* Scroll left */
1611 				padColumn += curColumn;
1612 				curColumn = 0;
1613 			}
1614 			if (curColumn+(*curAnchorIter)->colTo-(*curAnchorIter)->colFrom > GetCol()-1) {
1615 				if ((*curAnchorIter)->colTo-(*curAnchorIter)->colFrom > GetCol()-1) {
1616 					// Highlight bar too long - show only
1617 					// left portion and discard parts
1618 					// that go beyond the screen width
1619 
1620 					// Scroll left
1621 					padColumn += curColumn;
1622 					curColumn = 0;
1623 				}
1624 				else {	// Can fit highlight on the screen
1625 					/* Scroll right */
1626 					padColumn += curColumn+(*curAnchorIter)->colTo
1627 						-(*curAnchorIter)->colFrom-(GetCol()-1);
1628 					curColumn = GetCol()-1-((*curAnchorIter)->colTo-(*curAnchorIter)->colFrom);
1629 				}
1630 			}
1631 		}
1632 	}
1633 	else {
1634 		if (curRow+padRow > 0) {
1635 			padRow -= lines;
1636 			if (padRow < 0) {
1637 				padRow = 0;
1638 				curRow = 0;
1639 			}
1640 		}
1641 		else
1642 			return;
1643 	}
1644 
1645 	Highlight();
1646 	scrnMan.RequestRestCursor();
1647 }
1648 
ProcessKeyScrollDownLines(size_t lines)1649 void	HyperWindow::ProcessKeyScrollDownLines(size_t lines)
1650 {
1651 	if (!lines)
1652 		return;
1653 	if (lockKey) {
1654 		iterType saveIter = curAnchorIter;
1655 		if (curAnchorIter != --(html.GetAnchorList().end())) {
1656 			do {
1657 				++curAnchorIter;
1658 
1659 				if ((*curAnchorIter)->rowFrom >= (*saveIter)->rowFrom+static_cast<int>(lines)) {
1660 					padRow += lines;
1661 					if (padRow+GetRow() > GetDocRow())
1662 						padRow = GetDocRow()-GetRow();
1663 					break;
1664 				}
1665 			} while(curAnchorIter != --(html.GetAnchorList().end()));
1666 
1667 			if (curAnchorIter == --(html.GetAnchorList().end()))
1668 				padRow = GetDocRow()-GetRow();
1669 
1670 			curRow = (*curAnchorIter)->rowFrom-padRow;
1671 			curColumn = (*curAnchorIter)->colFrom-padColumn;
1672 
1673 			if (curRow+(*curAnchorIter)->rowTo-(*curAnchorIter)->rowFrom > GetRow()-1) {
1674 				/* Scroll down */
1675 				padRow += curRow+(*curAnchorIter)->rowTo-(*curAnchorIter)->rowFrom-
1676 						(GetRow()-1);
1677 				curRow = GetRow()-1-((*curAnchorIter)->rowTo-(*curAnchorIter)->rowFrom);
1678 			}
1679 			if (curRow < 0) {
1680 				/* Scroll up */
1681 				padRow += curRow;
1682 				curRow = 0;
1683 			}
1684 			if (padRow+GetRow() > GetDocRow()) {
1685 				/* Scroll down */
1686 				padRow = GetDocRow()-GetRow();
1687 				curRow = (*curAnchorIter)->rowFrom-padRow;
1688 			}
1689 			if (curColumn < 0) {
1690 				/* Scroll left */
1691 				padColumn += curColumn;
1692 				curColumn = 0;
1693 			}
1694 			if (curColumn+(*curAnchorIter)->colTo-(*curAnchorIter)->colFrom > GetCol()-1) {
1695 				if ((*curAnchorIter)->colTo-(*curAnchorIter)->colFrom > GetCol()-1) {
1696 					// Highlight bar too long - show only
1697 					// left portion and discard parts
1698 					// that go beyond the screen width
1699 
1700 					// Scroll left
1701 					padColumn += curColumn;
1702 					curColumn = 0;
1703 				}
1704 				else {	// Can fit highlight on the screen
1705 					/* Scroll right */
1706 					padColumn += curColumn+(*curAnchorIter)->colTo
1707 						-(*curAnchorIter)->colFrom-(GetCol()-1);
1708 					curColumn = GetCol()-1-((*curAnchorIter)->colTo-(*curAnchorIter)->colFrom);
1709 				}
1710 			}
1711 		}
1712 	}
1713 	else {
1714 		if (curRow+padRow < GetDocRow()-1) {
1715 			padRow += lines;
1716 			if (padRow+GetRow() > GetDocRow()) {
1717 				curRow += padRow+GetRow()-GetDocRow();
1718 				padRow = GetDocRow()-GetRow();
1719 			}
1720 			curRow = Min(curRow, GetRow()-1);
1721 		}
1722 		else
1723 			return;
1724 	}
1725 
1726 	Highlight();
1727 	scrnMan.RequestRestCursor();
1728 }
1729 
ProcessKeyFirstPage()1730 void	HyperWindow::ProcessKeyFirstPage()
1731 {
1732 	if (lockKey) {
1733 		if (curAnchorIter != html.GetAnchorList().begin()) {
1734 			curAnchorIter = html.GetAnchorList().begin();
1735 
1736 			padRow = 0;
1737 			padColumn = 0;
1738 			curRow = (*curAnchorIter)->rowFrom-padRow;
1739 			curColumn = (*curAnchorIter)->colFrom-padColumn;
1740 			FitHighlightTop(curAnchorIter);
1741 		}
1742 	}
1743 	else {
1744 		padRow = 0;
1745 		padColumn = 0;
1746 		curRow = 0;
1747 		curColumn = 0;
1748 	}
1749 
1750 	Highlight();
1751 	scrnMan.RequestRestCursor();
1752 }
1753 
ProcessKeyLastPage()1754 void	HyperWindow::ProcessKeyLastPage()
1755 {
1756 	if (lockKey) {
1757 		if (curAnchorIter != --(html.GetAnchorList().end())) {
1758 			curAnchorIter = html.GetAnchorList().end();
1759 			--curAnchorIter;
1760 
1761 			padRow = GetDocRow()-GetRow();
1762 			padColumn = 0;
1763 			curRow = (*curAnchorIter)->rowFrom-padRow;
1764 			curColumn = (*curAnchorIter)->colFrom-padColumn;
1765 			FitHighlightTop(curAnchorIter);
1766 		}
1767 	}
1768 	else {
1769 		padRow = GetDocRow()-GetRow();
1770 		padColumn = 0;
1771 		curRow = GetRow()-1;
1772 		curColumn = 0;
1773 	}
1774 
1775 	Highlight();
1776 	scrnMan.RequestRestCursor();
1777 }
1778 
ProcessKeyPrev()1779 void	HyperWindow::ProcessKeyPrev()
1780 {
1781 	if (lockKey) {
1782 		iterType saveIter = curAnchorIter;
1783 		if (curAnchorIter != html.GetAnchorList().begin()) {
1784 			--curAnchorIter;
1785 			curRow -= (*saveIter)->rowFrom - (*curAnchorIter)->rowFrom;
1786 			curColumn -= (*saveIter)->colFrom - (*curAnchorIter)->colFrom;
1787 			FitHighlightTop(curAnchorIter);
1788 		}
1789 	}
1790 	else {
1791 		if (curColumn+padColumn > 0) {
1792 			curColumn--;
1793 			if (curColumn < 0) {
1794 				curColumn = 0;
1795 				padColumn--;
1796 			}
1797 		}
1798 		else	/* Already reach left limit */
1799 			return;
1800 	}
1801 
1802 	Highlight();
1803 	scrnMan.RequestRestCursor();
1804 }
1805 
ProcessKeyNext()1806 void	HyperWindow::ProcessKeyNext()
1807 {
1808 	if (lockKey) {
1809 		iterType saveIter = curAnchorIter;
1810 		++curAnchorIter;
1811 		if (curAnchorIter != html.GetAnchorList().end()) {
1812 			curRow -= (*saveIter)->rowFrom - (*curAnchorIter)->rowFrom;
1813 			curColumn -= (*saveIter)->colFrom - (*curAnchorIter)->colFrom;
1814 			FitHighlightBottom(curAnchorIter);
1815 		}
1816 		else	/* Do not update curAnchorIter */
1817 			curAnchorIter = saveIter;
1818 	}
1819 	else {
1820 		if (curColumn+padColumn < GetDocColumn()-1) {
1821 			curColumn++;
1822 			if (curColumn >= GetCol()) {
1823 				curColumn = GetCol()-1;
1824 				padColumn++;
1825 			}
1826 		}
1827 		else	/* Already reach right limit */
1828 			return;
1829 	}
1830 
1831 	Highlight();
1832 	scrnMan.RequestRestCursor();
1833 }
1834 
ProcessKeyBeginLine()1835 void	HyperWindow::ProcessKeyBeginLine()
1836 {
1837 	if (lockKey) {
1838 		iterType saveIter = curAnchorIter;
1839 		while (curAnchorIter != html.GetAnchorList().begin()
1840 		       && (*curAnchorIter)->rowFrom == (*saveIter)->rowFrom)
1841 			--curAnchorIter;
1842 
1843 		if ((*curAnchorIter)->rowFrom != (*saveIter)->rowFrom)
1844 			++curAnchorIter;
1845 
1846 		if (curAnchorIter != saveIter) {
1847 			curRow -= (*saveIter)->rowFrom - (*curAnchorIter)->rowFrom;
1848 			curColumn -= (*saveIter)->colFrom - (*curAnchorIter)->colFrom;
1849 			FitHighlightTop(curAnchorIter);
1850 		}
1851 	}
1852 	else {
1853 		if (curColumn+padColumn > 0) {
1854 			curColumn = 0;
1855 			padColumn = 0;
1856 		}
1857 		else	/* Already reach left limit */
1858 			return;
1859 	}
1860 
1861 	Highlight();
1862 	scrnMan.RequestRestCursor();
1863 }
1864 
ProcessKeyEndLine()1865 void	HyperWindow::ProcessKeyEndLine()
1866 {
1867 	if (lockKey) {
1868 		iterType saveIter = curAnchorIter;
1869 		while (curAnchorIter != html.GetAnchorList().end()
1870 		       && (*curAnchorIter)->rowFrom == (*saveIter)->rowFrom)
1871 			++curAnchorIter;
1872 
1873 				// Rewind always required
1874 				// because html.GetAnchorList().end()
1875 				// is not valid
1876 		--curAnchorIter;
1877 
1878 		if (curAnchorIter != saveIter) {
1879 			curRow -= (*saveIter)->rowFrom - (*curAnchorIter)->rowFrom;
1880 			curColumn -= (*saveIter)->colFrom - (*curAnchorIter)->colFrom;
1881 			FitHighlightBottom(curAnchorIter);
1882 		}
1883 	}
1884 	else {
1885 		int diff = GetDocColumn()-1-curColumn-padColumn;
1886 		if (diff > 0) {
1887 			curColumn += diff;
1888 			if (curColumn >= GetCol()) {
1889 				curColumn = GetCol()-1;
1890 				padColumn += diff;
1891 			}
1892 		}
1893 		else	/* Already reach right limit */
1894 			return;
1895 	}
1896 
1897 	Highlight();
1898 	scrnMan.RequestRestCursor();
1899 }
1900 
1901 
ProcessKey(int ch)1902 void	HyperWindow::ProcessKey(int ch)
1903 {
1904 			// Key shared for all key binding
1905 
1906 	if (ch == ' ' || ch == '\r' || ch == '\n')
1907 		ProcessKeySelect();
1908 	else if (ch == KEY_B2)		// Center of keypad
1909 		ProcessKeyCenter();
1910 	else if (ch == KEY_F(9))
1911 		ProcessKeyNextStatus();
1912 	else if (ch == KEY_F(10) || ch == 'C'-'A'+1)
1913 		ProcessKeyExit();
1914 					// comma when shifted is `<'
1915 	else if (ch == ',' || ch == KEY_SLEFT)
1916 		ProcessKeyPanLeft();
1917 					// stop when shifted is `>'
1918 	else if (ch == '.' || ch == KEY_SRIGHT)
1919 		ProcessKeyPanRight();
1920 	else if (ch == KEY_UP || ch == KEY_SR)
1921 		ProcessKeyUp();
1922 	else if (ch == KEY_DOWN || ch == KEY_SF)
1923 		ProcessKeyDown();
1924 	else if (ch == KEY_SR)
1925 		ProcessKeyFirstLine();
1926 	else if (ch == KEY_SF)
1927 		ProcessKeyLastLine();
1928 	else if (ch == KEY_PPAGE)
1929 		ProcessKeyPrevPage();
1930 	else if (ch == KEY_NPAGE)
1931 		ProcessKeyNextPage();
1932 	else if (ch == KEY_HOME)
1933 		ProcessKeyFirstPage();
1934 	else if (ch == KEY_END)
1935 		ProcessKeyLastPage();
1936 	else if (ch == KEY_LEFT)
1937 		ProcessKeyPrev();
1938 	else if (ch == KEY_RIGHT)
1939 		ProcessKeyNext();
1940 
1941 	else if (kcdConfig.cfgKey == keyBindingVi) {
1942 		bool	processG = false;
1943 		bool	processZ = false;
1944 		bool	processCount = false;
1945 
1946 		if (ch >= '1' && ch <= '9') {
1947 			if (viFoundCount)
1948 				viCount = viCount*10 + ch-'0';
1949 			else {
1950 				viFoundCount = true;
1951 				viCount = ch-'0';
1952 			}
1953 			processCount = true;
1954 		}
1955 		else if (ch == '0' && viFoundCount) {
1956 			viCount = viCount*10;
1957 			processCount = true;
1958 		}
1959 		else if (ch == 'q' || ch == 'Q')	// q, ZQ
1960 			ProcessKeyExit();
1961 		else if (ch == 'h' || ch == 'b' || ch == 'B') {
1962 			for (size_t i = 0; i < viCount; ++i)
1963 				ProcessKeyPrev();
1964 		}
1965 		else if (ch == 'l' || ch == 'w' || ch == 'W') {
1966 			for (size_t i = 0; i < viCount; ++i)
1967 				ProcessKeyNext();
1968 		}
1969 		else if (ch == '0' || ch == '^')
1970 			ProcessKeyBeginLine();
1971 		else if (ch == '$')
1972 			ProcessKeyEndLine();
1973 		else if (ch == 'k' || ch == '-') {
1974 			for (size_t i = 0; i < viCount; ++i)
1975 				ProcessKeyUp();
1976 		}
1977 		else if (ch == 'j' || ch == '+' || ch == '_') {
1978 			for (size_t i = 0; i < viCount; ++i)
1979 				ProcessKeyDown();
1980 		}
1981 		else if (ch == 'B'-'A'+1) {	// ^B
1982 			for (size_t i = 0; i < viCount; ++i)
1983 				ProcessKeyPrevPage();
1984 		}
1985 		else if (ch == 'F'-'A'+1) {	// ^F
1986 			for (size_t i = 0; i < viCount; ++i)
1987 				ProcessKeyNextPage();
1988 		}
1989 		else if (ch == 'U'-'A'+1) {	// ^U
1990 			if (viFoundCount)
1991 				ProcessKeyScrollUpLines(viCount);
1992 			else
1993 				ProcessKeyScrollUpLines(GetRow()/2-1);
1994 		}
1995 		else if (ch == 'D'-'A'+1) {	// ^D
1996 			if (viFoundCount)
1997 				ProcessKeyScrollDownLines(viCount);
1998 			else
1999 				ProcessKeyScrollDownLines(GetRow()/2-1);
2000 		}
2001 		else if (ch == 'E'-'A'+1) {	// ^E
2002 			if (viCount) {
2003 				if (viCount > static_cast<size_t>(curRow)) {
2004 					ProcessKeyScrollDownLines(viCount-curRow);
2005 					viCount = curRow;
2006 				}
2007 
2008 				curRow -= viCount;
2009 				padRow += viCount;
2010 				Highlight();
2011 				scrnMan.RequestRestCursor();
2012 			}
2013 		}
2014 		else if (ch == 'Y'-'A'+1) {	// ^Y
2015 			if (viCount) {
2016 				if (viCount > static_cast<size_t>(GetRow()-1-curRow)) {
2017 					ProcessKeyScrollUpLines(viCount-(GetRow()-1-curRow));
2018 					viCount = GetRow()-1-curRow;
2019 				}
2020 
2021 				curRow += viCount;
2022 				padRow -= viCount;
2023 				Highlight();
2024 				scrnMan.RequestRestCursor();
2025 			}
2026 		}
2027 		else if (ch == 'g') {
2028 			processG = true;
2029 			if (viFoundG)		// gg
2030 				ProcessKeyFirstPage();
2031 			else
2032 				viFoundG = true;
2033 		}
2034 		else if (ch == 'G')
2035 			ProcessKeyLastPage();
2036 		else if (ch == 'Z') {
2037 			processZ = true;
2038 			if (viFoundZ)
2039 				ProcessKeyExit();
2040 			else
2041 				viFoundZ = true;
2042 		}
2043 
2044 		// FIXME: Maybe add zh, zl, zH, zL
2045 
2046 		if (!processG)			// Ignore the `g' in `gX'
2047 						// key sequence where X is
2048 						// not `g'
2049 			viFoundG = false;
2050 		if (!processZ)
2051 			viFoundZ = false;
2052 		if (!processCount) {
2053 			viFoundCount = false;
2054 			viCount = 1;
2055 		}
2056 	}
2057 	else if (kcdConfig.cfgKey == keyBindingEmacs) {
2058 		bool	processAlt = false;
2059 
2060 		if (ch == 27) {				// Esc, M-
2061 			emacsFoundAlt = !emacsFoundAlt;
2062 			processAlt = true;
2063 		}
2064 		else if (ch == 'A'-'A'+1)		// C-a
2065 			ProcessKeyBeginLine();
2066 		else if (ch == 'E'-'A'+1)		// C-e
2067 			ProcessKeyEndLine();
2068 		else if (ch == 'B'-'A'+1 || (ch == 'b' && emacsFoundAlt))	// C-b, M-b
2069 			ProcessKeyPrev();
2070 		else if (ch == 'F'-'A'+1 || (ch == 'f' && emacsFoundAlt))	// C-f, M-f
2071 			ProcessKeyNext();
2072 		else if (ch == 'P'-'A'+1)		// C-p
2073 			ProcessKeyUp();
2074 		else if (ch == 'N'-'A'+1)		// C-n
2075 			ProcessKeyDown();
2076 		else if (ch == 'v' && emacsFoundAlt)	// M-v
2077 			ProcessKeyPrevPage();
2078 		else if (ch == 'V'-'A'+1)		// C-v
2079 			ProcessKeyNextPage();
2080 		// FIXME: Emulate ^X^S, ^X^C
2081 
2082 		if (!processAlt)
2083 			emacsFoundAlt = false;
2084 	}
2085 }
2086 
KeyboardLoop()2087 void	HyperWindow::KeyboardLoop()
2088 {
2089 	NCWindowBase::KeyboardLoop();
2090 
2091 					// Exit keyboard loop
2092 
2093 	if (win) {			// Delete windows
2094 		delwin(win);
2095 		win = NULL;
2096 	}
2097 	if (blankWin) {
2098 		delwin(blankWin);
2099 		blankWin = NULL;
2100 	}
2101 
2102 					// Exit program, don't have
2103 					// to delete other windows
2104 
2105 	clear();			// Clear screen
2106 	refresh();
2107 	cursor.Restore();
2108 
2109 	NCurses::End();
2110 
2111 	startSection = "";		// No longer used
2112 
2113 	if (anchorSelected) {
2114 		if (anchorSelected->type == TYPE_EXEC) {
2115 			k_system(anchorSelected->name);
2116 			return;
2117 		}
2118 	}
2119 }
2120 
DoRestCursor()2121 void	HyperWindow::DoRestCursor()
2122 {
2123 	if (win == NULL)
2124 		return;
2125 
2126 					// blankWin must be refreshed first
2127 					// to avoid interfering the cursor
2128 					// position
2129 	if (blankWin)
2130 		wnoutrefresh(blankWin);
2131 
2132 	if (lockKey)
2133 		wmove(win, padRow+GetRow()-1, padColumn+GetCol()-1);
2134 	else
2135 		wmove(win, padRow+curRow, padColumn+curColumn);
2136 
2137 					// win's cursor position will be
2138 					// used by doupdate().
2139 	if (numRealColumn < GetCol())
2140 		pnoutrefresh(win, padRow, padColumn, GetY1(), GetX1(),
2141 				GetY2(), GetX1()+numRealColumn-1);
2142 	else
2143 		pnoutrefresh(win, padRow, padColumn, GetY1(), GetX1(),
2144 				GetY2(), GetX2());
2145 }
2146 
CursorMode()2147 int	khHyperWindowWithFind::CursorMode()
2148 {
2149 	if (lockKey)
2150 		return 1;
2151 	else
2152 		return 0;
2153 }
2154 
FindText(const string & text,bool find_backward)2155 void	khHyperWindowWithFind::FindText(const string &text, bool find_backward)
2156 {
2157 	if (text.size() == 0)
2158 		return;
2159 
2160 	iterType newAnchorIter = curAnchorIter;
2161 
2162 #ifdef	CLIB_HAVE_REGEX
2163 	string str = MakeString(text);
2164 	regex_t *reg = MakePathRegex(text);
2165 #endif
2166 
2167 	do {
2168 		if (MatchSubString(newAnchorIter, text)
2169 #ifdef	CLIB_HAVE_REGEX
2170 		    || (MatchStr(str, (*newAnchorIter)->name)
2171 			&& MatchPathRegex(reg, (*newAnchorIter)->name))
2172 #endif
2173 		    ) {
2174 			CenterRowPadIfScroll(newAnchorIter);
2175 			FitHighlightMiddle(newAnchorIter);
2176 			curAnchorIter = newAnchorIter;
2177 			break;
2178 		}
2179 		if (find_backward)
2180 			prev_loop(html.GetAnchorList(), newAnchorIter);
2181 		else
2182 			next_loop(html.GetAnchorList(), newAnchorIter);
2183 
2184 	} while (newAnchorIter != curAnchorIter);	/* Loop not repeated */
2185 
2186 	Highlight();
2187 	scrnMan.RequestRestCursor();
2188 
2189 #ifdef	CLIB_HAVE_REGEX
2190 	if (reg)
2191 		regfree(reg);
2192 #endif
2193 }
2194 
FindPrev(const string & text)2195 void	khHyperWindowWithFind::FindPrev(const string &text)
2196 {
2197 	iterType newAnchorIter = curAnchorIter;
2198 
2199 	prev_loop(html.GetAnchorList(), newAnchorIter);
2200 
2201 #ifdef	CLIB_HAVE_REGEX
2202 	string str = MakeString(text);
2203 	regex_t *reg = MakePathRegex(text);
2204 #endif
2205 
2206 	do {
2207 		if (MatchSubString(newAnchorIter, text)
2208 #ifdef	CLIB_HAVE_REGEX
2209 		    || (MatchStr(str, (*newAnchorIter)->name)
2210 			&& MatchPathRegex(reg, (*newAnchorIter)->name))
2211 #endif
2212 		    ) {
2213 			CenterRowPadIfScroll(newAnchorIter);
2214 			FitHighlightMiddle(newAnchorIter);
2215 			curAnchorIter = newAnchorIter;
2216 			break;
2217 		}
2218 		prev_loop(html.GetAnchorList(), newAnchorIter);
2219 
2220 	} while (newAnchorIter != curAnchorIter);	/* Loop not repeated */
2221 
2222 	Highlight();
2223 	scrnMan.RequestRestCursor();
2224 
2225 #ifdef	CLIB_HAVE_REGEX
2226 	if (reg)
2227 		regfree(reg);
2228 #endif
2229 }
2230 
FindNext(const string & text)2231 void	khHyperWindowWithFind::FindNext(const string &text)
2232 {
2233 	iterType newAnchorIter = curAnchorIter;
2234 
2235 	next_loop(html.GetAnchorList(), newAnchorIter);
2236 
2237 #ifdef	CLIB_HAVE_REGEX
2238 	string str = MakeString(text);
2239 	regex_t *reg = MakePathRegex(text);
2240 #endif
2241 
2242 	do {
2243 		if (MatchSubString(newAnchorIter, text)
2244 #ifdef	CLIB_HAVE_REGEX
2245 		    || (MatchStr(str, (*newAnchorIter)->name)
2246 			&& MatchPathRegex(reg, (*newAnchorIter)->name))
2247 #endif
2248 		    ) {
2249 			CenterRowPadIfScroll(newAnchorIter);
2250 			FitHighlightMiddle(newAnchorIter);
2251 			curAnchorIter = newAnchorIter;
2252 			break;
2253 		}
2254 		next_loop(html.GetAnchorList(), newAnchorIter);
2255 
2256 	} while (newAnchorIter != curAnchorIter);	/* Loop not repeated */
2257 
2258 	Highlight();
2259 	scrnMan.RequestRestCursor();
2260 
2261 #ifdef	CLIB_HAVE_REGEX
2262 	if (reg)
2263 		regfree(reg);
2264 #endif
2265 }
2266 
SelectText(const string &)2267 void	khHyperWindowWithFind::SelectText(const string & /*text*/)
2268 {
2269 	HyperWindow::ProcessKey('\n');
2270 }
2271 
FindProcessKey(int ch)2272 void	khHyperWindowWithFind::FindProcessKey(int ch)
2273 {
2274 	if (ch == KEY_F(10) || ch == KEY_F(9) || ch == 'C'-'A'+1)
2275 		HyperWindow::ProcessKey(ch);
2276 }
2277 
ProcessKey(int ch)2278 void	khHyperWindowWithFind::ProcessKey(int ch)
2279 {
2280 	bool	start_find = false;
2281 				// Non-special keys
2282 
2283 	if (kcdConfig.cfgKey == keyBindingDefault) {
2284 #ifdef USE_UTF8_MODE
2285 		if (IsUTF8Mode()) {
2286 			if ((ch <= 0x7F && isalnum(ch)) || (ch > 0x7F && ch <= 0xFF))
2287 				start_find = true;
2288 		}
2289 		else {
2290 			if (ch <= 0xFF && isalnum(ch))
2291 				start_find = true;
2292 		}
2293 #else
2294 		if (ch <= 0xFF && isalnum(ch))
2295 			start_find = true;
2296 #endif
2297 
2298 		if (start_find) {
2299 				// Start find
2300 
2301 				// Pass control of keyboard & cursor to
2302 				// status
2303 			scrnMan.SetFocus(&status);
2304 			scrnMan.SetCursor(&status);
2305 
2306 			status.ProcessKey(ch);
2307 		}
2308 		else
2309 			HyperWindow::ProcessKey(ch);
2310 	}
2311 	else if (kcdConfig.cfgKey == keyBindingVi) {
2312 
2313 		// FIXME: Add *, #
2314 
2315 		if (ch == '/' || ch == '?') {
2316 				// Pass control of keyboard & cursor to
2317 				// status
2318 			scrnMan.SetFocus(&status);
2319 			scrnMan.SetCursor(&status);
2320 
2321 			status.SetCount(viCount);
2322 			status.ProcessKey(ch);
2323 
2324 			viFoundG = false;
2325 			viFoundZ = false;
2326 			viFoundCount = false;
2327 			viCount = 1;
2328 		}
2329 		else
2330 			HyperWindow::ProcessKey(ch);
2331 	}
2332 	else if (kcdConfig.cfgKey == keyBindingEmacs) {
2333 		if (ch == 'S'-'A'+1 || ch == 'R'-'A'+1) {		// C-s, C-r
2334 				// Pass control of keyboard & cursor to
2335 				// status
2336 			scrnMan.SetFocus(&status);
2337 			scrnMan.SetCursor(&status);
2338 			status.ProcessKey(ch);
2339 
2340 			emacsFoundAlt = false;
2341 		}
2342 #ifdef USE_UTF8_MODE
2343 		else if (IsUTF8Mode()) {
2344 			if ((ch <= 0x7F && isalnum(ch)) || (ch > 0x7F && ch <= 0xFF))
2345 				start_find = true;
2346 		}
2347 		else {
2348 			if (ch <= 0xFF && isalnum(ch))
2349 				start_find = true;
2350 		}
2351 #else
2352 		else if (ch <= 0xFF && isalnum(ch))
2353 			start_find = true;
2354 #endif
2355 
2356 		if (start_find) {
2357 				// Pass control of keyboard & cursor to
2358 				// status
2359 			scrnMan.SetFocus(&status);
2360 			scrnMan.SetCursor(&status);
2361 			status.ProcessKey('S'-'A'+1);	// Assume C-s
2362 			status.ProcessKey(ch);
2363 
2364 			emacsFoundAlt = false;
2365 
2366 			emacsFoundAlt = false;
2367 		}
2368 		else
2369 			HyperWindow::ProcessKey(ch);
2370 	}
2371 }
2372 
ProcessCall(Anchor * p)2373 Anchor	khHyperWindowWithFind::ProcessCall(Anchor *p)
2374 {
2375 	extern	string	dirFile;
2376 
2377 	FILE	*file;
2378 
2379 	file = k_fopen(dirFile, "wb");
2380 	if (file == NULL) {			// Cannot create or truncate
2381 						// ~/.kcd.newdir
2382 		throw ErrorGenericFile(_("cannot create file %$"), dirFile);
2383 	}
2384 
2385 						// Output desired dir
2386 	k_fputs(p->name, file);
2387 	fclose(file);
2388 
2389 	return Anchor("", 0, TYPE_EXIT);		// Exit immediately
2390 }
2391 
khHyperWindowWithFind(NCScreenManager & scrn_,int id_,HyperDocument & html_,StatusWindowBase & status_,StatusWindowBase & url_,ScrollBarBase & hscroll_,ScrollBarBase & vscroll_,const string & startSection_,int lockKey_,int row,int col)2392 khHyperWindowWithFind::khHyperWindowWithFind(NCScreenManager &scrn_, int id_,
2393 				HyperDocument &html_,
2394 				StatusWindowBase &status_,
2395 				StatusWindowBase &url_,
2396 				ScrollBarBase &hscroll_,
2397 				ScrollBarBase &vscroll_,
2398 				const string &startSection_,
2399 				int lockKey_, int row, int col)
2400 					: HyperWindow(scrn_, id_, html_,
2401 						status_, url_, hscroll_,
2402 						vscroll_, startSection_,
2403 						lockKey_, row, col)
2404 {
2405 }
2406 
~khHyperWindowWithFind()2407 khHyperWindowWithFind::~khHyperWindowWithFind()
2408 {
2409 }
2410