1 /* -*- coding: utf-8; indent-tabs-mode: t; tab-width: 4; c-basic-offset: 4; -*- */
2 /**
3  * Copyright (c) 2005 PCMan <pcman.tw@gmail.com>
4  *
5  * This program is free software; you can redistribute it and/or modify
6  * it under the terms of the GNU General Public License as published by
7  * the Free Software Foundation; either version 2 of the License, or
8  * (at your option) any later version.
9  *
10  * This program is distributed in the hope that it will be useful,
11  * but WITHOUT ANY WARRANTY; without even the implied warranty of
12  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
13  * GNU General Public License for more details.
14  *
15  * You should have received a copy of the GNU General Public License
16  * along with this program; if not, write to the Free Software Foundation,
17  * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
18  */
19 
20 #ifdef __GNUG__
21   #pragma implementation "termview.h"
22 #endif
23 
24 
25 #include "termview.h"
26 #include "termdata.h"
27 #include "termsel.h"
28 #include "uao241.h"
29 #include "uao250.h"
30 
31 #include <string>
32 
33 #include <gdk/gdkx.h>
34 #include <pango/pangoxft.h>
35 #include "font.h"
36 #include "cfontconfig.h"
37 
38 using namespace std;
39 
on_key_pressed(GtkWidget * wnd UNUSED,GdkEventKey * evt,CTermView * _this)40 static gboolean on_key_pressed(GtkWidget* wnd UNUSED, GdkEventKey *evt, CTermView* _this)
41 {
42 	bool ret = gtk_im_context_filter_keypress(_this->m_IMContext, evt );
43 	if( !_this->PreKeyDown(evt) && !ret )
44 		ret = _this->OnKeyDown(evt);
45 	return ret;
46 }
47 
on_im_commit(GtkIMContext * im UNUSED,gchar * arg,CTermView * _this)48 static void on_im_commit(GtkIMContext *im UNUSED, gchar *arg, CTermView* _this)
49 {
50 	_this->OnTextInput(arg);
51 }
52 
on_mouse_down(GtkWidget * widget UNUSED,GdkEventButton * evt,CTermView * _this)53 static gboolean on_mouse_down(GtkWidget* widget UNUSED, GdkEventButton* evt, CTermView* _this)
54 {
55 	switch(evt->button)
56 	{
57 	case 1:
58 		_this->OnLButtonDown(evt);
59 		break;
60 	case 2:
61 		_this->OnMButtonDown(evt);
62 		break;
63 	case 3:
64 		_this->OnRButtonDown(evt);
65 	}
66 	return true;
67 }
68 
on_mouse_up(GtkWidget * widget UNUSED,GdkEventButton * evt,CTermView * _this)69 static gboolean on_mouse_up(GtkWidget* widget UNUSED, GdkEventButton* evt, CTermView* _this)
70 {
71 	switch(evt->button)
72 	{
73 	case 1:
74 		_this->OnLButtonUp(evt);
75 		break;
76 	case 3:
77 		_this->OnRButtonUp(evt);
78 	}
79 	return true;
80 }
81 
on_mouse_move(GtkWidget * widget UNUSED,GdkEventMotion * evt,CTermView * _this)82 static gboolean on_mouse_move(GtkWidget* widget UNUSED, GdkEventMotion* evt, CTermView* _this)
83 {
84 	if (evt->is_hint)
85 	{
86 		int x, y;
87 		GdkModifierType state;
88 		gdk_window_get_pointer (evt->window, &x, &y, &state);
89 		evt->x = x;	evt->y = y;
90 		evt->state = state;
91 	}
92 	_this->OnMouseMove(evt);
93 	return true;
94 }
95 
on_mouse_scroll(GtkWidget * widget UNUSED,GdkEventScroll * evt,CTermView * _this)96 static gboolean on_mouse_scroll(GtkWidget* widget UNUSED, GdkEventScroll* evt, CTermView* _this)
97 {
98 	_this->OnMouseScroll(evt);
99 	return true;
100 }
101 
OnBeforeDestroy(GtkWidget * widget UNUSED,CTermView * _this)102 void CTermView::OnBeforeDestroy( GtkWidget* widget UNUSED, CTermView* _this)
103 {
104 	XftDrawDestroy( _this->m_XftDraw);
105 	_this->m_XftDraw = NULL;
106 	DEBUG("unrealize, destroy XftDraw");
107 }
108 
109 GdkCursor* CTermView::m_HandCursor = NULL;
110 GdkCursor* CTermView::m_ExitCursor = NULL;
111 GdkCursor* CTermView::m_BullsEyeCursor = NULL;
112 GdkCursor* CTermView::m_PageDownCursor = NULL;
113 GdkCursor* CTermView::m_PageUpCursor = NULL;
114 GdkCursor* CTermView::m_EndCursor = NULL;
115 GdkCursor* CTermView::m_HomeCursor = NULL;
116 int CTermView::m_CursorState = 0;
117 
CTermView()118 CTermView::CTermView()
119         : CView(), m_pColorTable(CTermCharAttr::GetDefaultColorTable())
120 {
121 	m_pTermData = NULL;
122 	m_GC = NULL;
123 	m_ShowBlink = false;
124 	for (int i = FONT_START; i != FONT_END; ++i)
125 	    m_Font[i] = NULL;
126 	m_XftDraw = NULL;
127 	m_CharW = 18;
128 	m_CharH = 18;
129 	m_LeftMargin = 0;
130 	m_TopMargin = 0;
131 	m_bHorizontalCenterAlign = false;
132 	m_bVerticalCenterAlign = false;
133 	m_UAO = 0;
134 
135 	m_CancelSel = false;
136 
137 	gtk_widget_add_events(m_Widget, GDK_EXPOSURE_MASK
138 		 | GDK_KEY_PRESS_MASK
139 		 | GDK_BUTTON_PRESS_MASK
140 		 | GDK_BUTTON_MOTION_MASK
141 		 | GDK_BUTTON_RELEASE_MASK
142 		 | GDK_POINTER_MOTION_MASK
143 		 | GDK_POINTER_MOTION_HINT_MASK
144 		 | GDK_ALL_EVENTS_MASK);
145 
146 	GTK_WIDGET_SET_FLAGS(m_Widget, GTK_CAN_FOCUS);
147 	gtk_widget_set_double_buffered(m_Widget, false);
148 
149 	g_signal_connect(G_OBJECT(m_Widget), "unrealize", G_CALLBACK(CTermView::OnBeforeDestroy), this);
150 
151 	g_signal_connect(G_OBJECT(m_Widget), "key_press_event", G_CALLBACK(on_key_pressed), this);
152 
153 	g_signal_connect(G_OBJECT(m_Widget), "button_press_event", G_CALLBACK(on_mouse_down), this);
154 
155 	g_signal_connect(G_OBJECT(m_Widget), "button_release_event", G_CALLBACK(on_mouse_up), this);
156 
157 	g_signal_connect(G_OBJECT(m_Widget), "motion_notify_event", G_CALLBACK(on_mouse_move), this);
158 
159 	g_signal_connect(G_OBJECT(m_Widget), "scroll_event", G_CALLBACK(on_mouse_scroll), this);
160 
161 	m_CharPaddingX = m_CharPaddingY = 0;
162 	m_AutoFontSize = true;
163 	m_pHyperLinkColor = NULL;
164 
165 	m_IMContext = gtk_im_multicontext_new();
166 	gtk_im_context_set_use_preedit( m_IMContext, FALSE );
167 	g_signal_connect( G_OBJECT(m_IMContext), "commit", G_CALLBACK(on_im_commit), this );
168 
169 	if( m_HandCursor )
170 		gdk_cursor_ref(m_HandCursor);
171 	else
172 		m_HandCursor = gdk_cursor_new_for_display(gdk_display_get_default(), GDK_HAND2);
173 	if( m_ExitCursor )
174 	  gdk_cursor_ref(m_ExitCursor);
175 	else
176 	  m_ExitCursor = gdk_cursor_new_for_display(gdk_display_get_default(), GDK_SB_LEFT_ARROW);
177 	if( m_BullsEyeCursor )
178 	  gdk_cursor_ref(m_BullsEyeCursor);
179 	else
180 	  m_BullsEyeCursor = gdk_cursor_new_for_display(gdk_display_get_default(), GDK_SB_RIGHT_ARROW);
181 	if( m_PageUpCursor )
182 	  gdk_cursor_ref(m_PageUpCursor);
183 	else
184 	  m_PageUpCursor = gdk_cursor_new_for_display(gdk_display_get_default(), GDK_SB_UP_ARROW);
185 	if( m_PageDownCursor )
186 	  gdk_cursor_ref(m_PageDownCursor);
187 	else
188 	  m_PageDownCursor = gdk_cursor_new_for_display(gdk_display_get_default(), GDK_SB_DOWN_ARROW);
189 	if( m_EndCursor )
190 	  gdk_cursor_ref(m_EndCursor);
191 	else
192 	  m_EndCursor = gdk_cursor_new_for_display(gdk_display_get_default(), GDK_BOTTOM_SIDE);
193 	if( m_HomeCursor )
194 	  gdk_cursor_ref(m_HomeCursor);
195 	else
196 	  m_HomeCursor = gdk_cursor_new_for_display(gdk_display_get_default(), GDK_TOP_SIDE);
197 
198 }
199 
200 
OnPaint(GdkEventExpose * evt)201 void CTermView::OnPaint(GdkEventExpose* evt)
202 {
203 	// Hide the caret to prevent drawing problems.
204 	m_Caret.Hide();
205 
206 	GdkDrawable* dc = m_Widget->window;
207 	if(!GDK_IS_DRAWABLE(dc))
208 	{
209 		DEBUG("WARNNING! Draw on DELETED widget!");
210 		return;
211 	}
212 
213 	int w = m_Widget->allocation.width, h = m_Widget->allocation.height;
214 
215 	if( m_pTermData )
216 	{
217 		// Only redraw the invalid area to greatly enhance performance.
218 		int top = evt->area.y;		int bottom = top + evt->area.height;
219 		int left = evt->area.x;		int right = left + evt->area.width;
220 		this->PointToLineCol( &left, &top );
221 		this->PointToLineCol( &right, &bottom );
222 
223 		if(right < m_pTermData->m_ColsPerPage)	right++;
224 		if(bottom < m_pTermData->m_RowsPerPage)	bottom++;
225 		if(top > 0)	top-=top>1?2:1;
226 
227 		for( int row = top; row < bottom; row++ )
228 		{
229 			for( int col = left; col < right; )
230 				col += DrawChar( row, col );
231 		}
232 
233 		gdk_gc_set_rgb_fg_color(m_GC, CTermCharAttr::GetDefaultColorTable(0));
234 		left = m_pTermData->m_ColsPerPage*m_CharW-2;
235 
236 		/* repaint some region that should be repainted */
237 		gdk_draw_rectangle(dc, m_GC, true, 0, 0, m_LeftMargin, h ); // fill left margin
238 		gdk_draw_rectangle(dc, m_GC, true, left + m_LeftMargin, 0, w-left, h ); // fill right marin
239 
240 		top = m_pTermData->m_RowsPerPage*m_CharH;
241 		gdk_draw_rectangle(dc, m_GC, true, 0, 0, w, m_TopMargin ); // fill top margin
242 		gdk_draw_rectangle(dc, m_GC, true, 0, top + m_TopMargin, w, h - top ); // fill bottom margin
243 
244 		m_Caret.Show();
245 	}
246 	else
247 	{
248 		gdk_gc_set_rgb_bg_color(m_GC, CTermCharAttr::GetDefaultColorTable(0));
249 		gdk_draw_rectangle(dc, m_GC, true, 0, 0, w, h );
250 	}
251 
252 }
253 
OnSetFocus(GdkEventFocus * evt UNUSED)254 void CTermView::OnSetFocus(GdkEventFocus* evt UNUSED)
255 {
256 	gtk_im_context_focus_in(m_IMContext);
257 }
258 
259 
OnKeyDown(GdkEventKey * evt UNUSED)260 bool CTermView::OnKeyDown(GdkEventKey* evt UNUSED)
261 {
262 	return true;
263 }
264 
265 
OnTextInput(const gchar * string UNUSED)266 void CTermView::OnTextInput(const gchar* string UNUSED)
267 {
268     // Override this function to process text input.
269 }
270 
OnCreate()271 void CTermView::OnCreate()
272 {
273 	CWidget::OnCreate();
274 	gtk_im_context_set_client_window(m_IMContext, m_Widget->window);
275 
276 	m_XftDraw = XftDrawCreate(
277 		GDK_WINDOW_XDISPLAY(m_Widget->window),
278 		GDK_WINDOW_XWINDOW(m_Widget->window),
279 		GDK_VISUAL_XVISUAL(gdk_drawable_get_visual(m_Widget->window)),
280 		GDK_COLORMAP_XCOLORMAP (gdk_drawable_get_colormap(m_Widget->window)));
281 	XftDrawSetSubwindowMode(m_XftDraw, IncludeInferiors);
282 
283 
284 	for (int i = FONT_START; i != FONT_END; ++i) {
285 	    if (!m_Font[i])
286 		m_Font[i] = new CFont("WenQuanYi Micro Hei Mono", 16);
287 	}
288 
289 	m_GC = gdk_gc_new(m_Widget->window);
290 	gdk_gc_copy(m_GC, m_Widget->style->black_gc);
291 
292 	m_Caret.Create(this);
293 	m_Caret.Show();
294 }
295 
DrawSpaceFillingChar(const char * ch,int len UNUSED,int x,int y,GdkRectangle * clip UNUSED,GdkColor * clr UNUSED)296 bool CTermView::DrawSpaceFillingChar(const char* ch, int len UNUSED, int x, int y, GdkRectangle* clip UNUSED, GdkColor* clr UNUSED)
297 {
298 	GdkDrawable* dc = m_Widget->window;
299 	guchar* uchar = (guchar*)ch;
300 // NOTE: Omit this check to increase performance.
301 // IsSpaceFillingChar should be called prior to calling this method.
302 //	if( len >= 3 && (int)uchar[0] == 0xe2 )
303 	{
304 //		gdk_gc_set_rgb_fg_color( m_GC, clr );
305 		switch( uchar[1] )
306 		{
307 		case 0x96:
308 			if( uchar[2] >= 0x81 && uchar[2] <= 0x88 )
309 			{
310 				int h = m_CharH * (uchar[2] - 0x80) / 8;
311 				gdk_draw_rectangle( dc, m_GC, true, x , y + m_CharH - h, m_CharW * 2, h );
312 			}
313 			else if( uchar[2] >= 0x89 && uchar[2] <= 0x8f )
314 			{
315 				// FIXME: There are still some potential bugs here.
316 				// See the welcome screen of telnet://ptt.cc for example
317 				int w = m_CharW * 2 * (8 - (uchar[2] - 0x88)) / 8;
318 				gdk_draw_rectangle( dc, m_GC, true, x, y, w, m_CharH );
319 			}
320 /*
321 			else if( uchar[2] == 0xa0 )
322 			{
323 				gdk_draw_rectangle( dc, m_GC, true, rc->x, rc->y, m_CharW * 2, m_CharH );
324 			}
325 			else if( uchar[2] == 0xa1 )
326 			{
327 				int w = m_CharW * 2 * (uchar[2] - 0x88) / 8;
328 				gdk_draw_rectangle( dc, m_GC, false, rc->x, rc->y, w, m_CharH );
329 			}
330 */			else
331 				return false;
332 			return true;
333 		case 0x95:
334 			return false;
335 		case 0x94:
336 			return false;
337 		case 0x97:
338 			{
339 				GdkTrapezoid tz;
340 
341 				tz.y1 = y;
342 				tz.y2 = y + m_CharH;
343 				tz.x11 = tz.x12 = x;
344 				tz.x21 =	tz.x22 = x + m_CharW * 2;
345 
346 				switch( uchar[2] )
347 				{
348 				case 0xa2:
349 					tz.x11 = tz.x21;
350 					break;
351 				case 0xa3:
352 					tz.x21 = tz.x11;
353 					break;
354 				case 0xa4:
355 					tz.x22 = tz.x12;
356 					break;
357 				case 0xa5:
358 					tz.x12 = tz.x22;
359 					break;
360 				default:
361 					return false;
362 				}
363 
364 				gdk_draw_trapezoids( dc, m_GC, &tz, 1 );
365 				return true;
366 			}
367 		}
368 	}
369 	return false;
370 }
371 
DrawChar(int row,int col)372 int CTermView::DrawChar(int row, int col)
373 {
374 	GdkDrawable* dc = m_Widget->window;
375 	if(!GDK_IS_DRAWABLE(dc) && m_XftDraw == NULL)
376 	{
377 //		g_warning("Draw on DELETED widget!\n");
378 		return 1;
379 	}
380 
381 	const char* pLine = m_pTermData->m_Screen[m_pTermData->m_FirstLine + row];
382 	CTermCharAttr* pAttr = m_pTermData->GetLineAttr(pLine);
383 	int w = 2;
384 	bool is_mbcs2 = false;
385 	switch( pAttr[col].GetCharSet() )
386 	{
387 	case CTermCharAttr::CS_MBCS1:
388 		break;
389 	case CTermCharAttr::CS_MBCS2:
390 		col--;
391 		is_mbcs2 = true;
392 //		This will not cause any problem at any time because 'col' always > 0.
393 //		In CTermData_this->DetectCharSets() I've done some checks to ensure that the first
394 //		character of every lines cannot be marked as second bytes of MBCS characters.
395 		break;
396 	default:
397 //	case CTermCharAttr::CS_ASCII:
398 		w = 1;
399 	}
400 	pLine += col;
401 	pAttr += col;
402 
403 	int loop_times = w;
404 	bool bSel[2];
405 	bSel[0] = m_pTermData->m_Sel->Has( row, col );
406 	if ( w > 1 )
407 	{
408 		bSel[1] = m_pTermData->m_Sel->Has( row, col + 1 );
409 		// two cells have the same attributes
410 		if(col < (m_pTermData->m_ColsPerPage - 1) &&
411 			pAttr[0] == pAttr[1] &&
412 			bSel[0] == bSel[1] )
413 			loop_times = 1;
414 	}
415 
416 	int left = m_CharW * col + m_LeftMargin;
417 	int top = m_CharH * row + m_TopMargin;
418 	int bgw = m_CharW * w;
419 
420 	GdkColor iFg, iBg;
421 
422 	for( int i = 0; i < loop_times; i++ )	//	do the drawing
423 	{
424 		GdkColor* Fg = pAttr[i].GetFgColor( m_pColorTable );
425 		GdkColor* Bg = pAttr[i].GetBgColor( m_pColorTable );
426 		// if it's property is inverse, GetFgColor & GetBgColor will swap Bg & Fg for us.
427 
428 		if( bSel[i] )	// if it's selected, reverse two colors.
429 		{
430 			iFg.red = ~Fg->red;
431 			iFg.green = ~Fg->green;
432 			iFg.blue = ~Fg->blue;
433 			Fg = &iFg;
434 			iBg.red = ~Bg->red;
435 			iBg.green = ~Bg->green;
436 			iBg.blue = ~Bg->blue;
437 			Bg = &iBg;
438 		}
439 
440 		XftColor xftclr;
441 		xftclr.pixel = 0;
442 		xftclr.color.red = Fg->red;
443 		xftclr.color.green = Fg->green;
444 		xftclr.color.blue = Fg->blue;
445 		xftclr.color.alpha = 0xffff;
446 
447 		// set clip mask
448 		GdkRectangle rect;
449 		XRectangle xrect;
450 		xrect.x = rect.x = left + m_CharW * i;
451 		xrect.y = rect.y = top;
452 		xrect.width = rect.width = m_CharW * ( w - i );
453 		xrect.height = rect.height = m_CharH;
454 		gdk_gc_set_clip_origin( m_GC, 0, 0 );
455 		gdk_gc_set_clip_rectangle( m_GC, &rect );
456 		Region xregion = XCreateRegion();
457 		XUnionRectWithRegion( &xrect, xregion, xregion );
458 		XftDrawSetClip( m_XftDraw, xregion );
459 		XDestroyRegion( xregion );
460 
461 		gdk_gc_set_rgb_fg_color( m_GC, Bg );
462 		gdk_draw_rectangle( dc, m_GC, true, left , top, bgw, m_CharH );
463 
464 		gdk_gc_set_rgb_fg_color( m_GC, Fg );
465 
466 		if( !pAttr[i].IsBlink() || m_ShowBlink )	// If text should be drawn.
467 		{
468 			if( ' ' != *pLine && '\0' != *pLine )
469 			{
470 				gsize wl = 0;
471 				gchar* utf8 = NULL;
472 
473 				/* UAO display support */
474 				switch (m_UAO) {
475 					case 2:
476 						utf8 = uao250_b2u(pLine, w, &wl);
477 						break;
478 					case 1:
479 						utf8 = uao241_b2u(pLine, w, &wl);
480 						break;
481 					default:
482 						utf8 = g_convert(pLine, w, "UTF-8", m_pTermData->m_Encoding.c_str(), NULL, &wl, NULL);
483 						break;
484 				}
485 
486 				if (utf8 != NULL) {
487 					gunichar* ucs4 = NULL;
488 					XftFont* font = NULL;
489 
490 					if (isascii (utf8[0])) {
491 						font = m_Font[FONT_EN]->GetXftFont();
492 					} else {
493 						font = m_Font[FONT_DEFAULT]->GetXftFont();
494 					}
495 
496 					ucs4 = g_utf8_to_ucs4_fast(utf8, -1, NULL);
497 
498 					if (ucs4 != NULL && XftCharExists(gdk_x11_get_default_xdisplay(), font, (FcChar32) *ucs4) == FcFalse) {
499 						XftFont* fallback = CFontConfig::Instance()->SearchFontFor((FcChar32) *ucs4);
500 						if (fallback != NULL) {
501 							XftDrawStringUtf8( m_XftDraw, &xftclr, fallback, left, top + fallback->ascent, (FcChar8*)utf8, wl );
502 						} else {
503 							XftDrawStringUtf8( m_XftDraw, &xftclr, font, left, top + font->ascent, (FcChar8*)utf8, wl );
504 						}
505 					} else if ( !IsSpaceFillingChar(utf8, wl) || !DrawSpaceFillingChar( utf8, wl, left, top, &rect, Fg ) ) {
506 						XftDrawStringUtf8( m_XftDraw, &xftclr, font, left, top + font->ascent, (FcChar8*)utf8, wl );
507 					}
508 
509 					g_free(ucs4);
510 					g_free(utf8);
511 				}
512 			}
513 			if( pAttr[i].IsUnderLine() )
514 			{
515 				int y = top + m_CharH - 1;
516 				gdk_draw_line( dc, m_GC, left, y, left + bgw - 1, y );
517 			}
518 		}
519 		// 2004.08.07 Added by PCMan: Draw the underline of hyperlinks.
520 		if( pAttr[i].IsHyperLink() )
521 		{
522 //			dc.SetPen(wxPen(m_HyperLinkColor, 1, wxSOLID));
523 //			int bottom = top + m_CharH - 1;
524 //			dc.DrawLine(left, bottom, left+bgw, bottom);
525 
526 			if(m_pHyperLinkColor)
527 	 			gdk_gc_set_rgb_fg_color( m_GC, m_pHyperLinkColor );
528 			int y = top + m_CharH - 1;
529 			gdk_draw_line( dc, m_GC, left, y, left + bgw - 1, y );
530 		}
531 
532 	}
533 	gdk_gc_set_clip_rectangle( m_GC, NULL );
534 	XftDrawSetClip( m_XftDraw, NULL );
535 
536 	return is_mbcs2 ? 1 : w;
537 }
538 
PointToLineCol(int * x,int * y,bool * left)539 void CTermView::PointToLineCol(int *x, int *y, bool *left)
540 {
541 	*x -= m_LeftMargin;
542 
543 	int pos = *x % m_CharW;
544 
545 	*x /= m_CharW;
546 	if (*x < 0)
547 	{
548 		*x = 0;
549 		pos = 0; // so that *left = true
550 	}
551 	else if( *x >= m_pTermData->m_ColsPerPage )
552 	{
553 		*x = m_pTermData->m_ColsPerPage - 1;
554 		pos = m_CharW; // so taht *left = false
555 	}
556 
557 	*y -= m_TopMargin;
558 	*y /= m_CharH;
559 	if(*y <0 )
560 		*y = 0;
561 	else if( *y >= m_pTermData->m_RowsPerPage )
562 		*y = m_pTermData->m_RowsPerPage-1;
563 
564 	if ( left )
565 	{
566 		const char* pLine = m_pTermData->m_Screen[m_pTermData->m_FirstLine + *y];
567 		CTermCharAttr* pAttr = m_pTermData->GetLineAttr( pLine );
568 
569 		switch( pAttr[*x].GetCharSet() )
570 		{
571 		case CTermCharAttr::CS_MBCS1:
572 			*left = true;
573 			break;
574 		case CTermCharAttr::CS_MBCS2:
575 			*left = false;
576 			break;
577 		default:
578 			*left = pos < ( m_CharW + 1 ) / 2;
579 			break;
580 		}
581 	}
582 }
583 
OnSize(GdkEventConfigure * evt UNUSED)584 void CTermView::OnSize(GdkEventConfigure* evt UNUSED)
585 {
586 	if( !m_AutoFontSize || !m_pTermData )
587 		return;
588 
589 	int w, h;
590 	GetCellSize( w, h );
591 
592 	for (int i = FONT_START; i != FONT_END; ++i) {
593 	    m_Font[i]->SetFont( m_Font[i]->GetName(), w, h, m_Font[i]->GetCompact(), m_Font[i]->GetAntiAlias());
594 	}
595 	RecalcCharDimension();
596 }
597 
DrawCharWrapper(int row,int col,void * data)598 static int DrawCharWrapper( int row, int col, void *data )
599 {
600 	CTermView *tv = (CTermView *) data;
601 
602 	return tv->DrawChar( row, col );
603 }
604 
ClearSelection()605 void CTermView::ClearSelection()
606 {
607 	m_CancelSel = true;
608 
609 	m_Caret.Hide();
610 	m_pTermData->m_Sel->Unselect( DrawCharWrapper, this );
611 	m_Caret.Show( false );
612 }
613 
ExtendSelection(int row,int col,bool left UNUSED)614 void CTermView::ExtendSelection( int row, int col, bool left UNUSED )
615 {
616 	row += m_pTermData->m_FirstLine;
617 
618 	const char* pLine = m_pTermData->m_Screen[row];
619 	CTermCharAttr* pAttr = m_pTermData->GetLineAttr( pLine );
620 
621 	if( pAttr[col].GetCharSet() == CTermCharAttr::CS_MBCS2 )
622 		col--;
623 
624 	int klass = m_pTermData->GetCharClass( row, col );
625 	int i;
626 
627 	/* decide start */
628 	for( i = col - 1; i >= 0; i-- )
629 	{
630 		int w = 1;
631 		if( pAttr[col].GetCharSet() == CTermCharAttr::CS_MBCS2 )
632 		{
633 			i--;
634 			w++;
635 		}
636 
637 		if( m_pTermData->GetCharClass( row, i ) != klass )
638 		{
639 			i += w;
640 			break;
641 		}
642 	}
643 	if( i < 0 )
644 		i = 0;
645 
646 	m_pTermData->m_Sel->NewStart( row, i, true );
647 
648 	/* decide end */
649 	for( i = col + 1; i < m_pTermData->m_ColsPerPage; i++ )
650 	{
651 		int w = 1;
652 		if( pAttr[col].GetCharSet() == CTermCharAttr::CS_MBCS2 )
653 		{
654 			i++;
655 			w++;
656 		}
657 
658 		if( m_pTermData->GetCharClass( row, i ) != klass )
659 		{
660 			i -= w;
661 			break;
662 		}
663 	}
664 	if( i >= m_pTermData->m_ColsPerPage )
665 		i = m_pTermData->m_ColsPerPage - 1;
666 
667 	m_pTermData->m_Sel->ChangeEnd( row, i, false, DrawCharWrapper, this );
668 }
669 
OnLButtonDown(GdkEventButton * evt)670 void CTermView::OnLButtonDown(GdkEventButton* evt)
671 {
672 	SetFocus();
673 	m_CancelSel = false;
674 
675 	if( !m_pTermData )
676 		return;
677 
678 	int x = (int)evt->x;
679 	int y = (int)evt->y;
680 	bool left;
681 
682 	PointToLineCol( &x, &y, &left );
683 
684 	if( evt->type == GDK_3BUTTON_PRESS )
685 	{
686 		m_pTermData->m_Sel->NewStart( y, 0, true );
687 		m_pTermData->m_Sel->ChangeEnd( y, m_pTermData->m_ColsPerPage - 1,
688 				               false, DrawCharWrapper, this );
689 	}
690 	else if( evt->type == GDK_2BUTTON_PRESS )
691 		ExtendSelection( y, x, left );
692 	else
693 	{
694 		// clear the old selection
695 		if( !m_pTermData->m_Sel->Empty() )
696 			ClearSelection();
697 
698 		SetCapture();
699 
700 		INFO("x=%d, y=%d, grab=%d", x, y, HasCapture());
701 
702 		m_pTermData->m_Sel->NewStart( y, x, left,
703 				(evt->state & (GDK_SHIFT_MASK|GDK_MOD1_MASK|GDK_CONTROL_MASK)) );
704 	}
705 }
706 
707 
OnMouseMove(GdkEventMotion * evt UNUSED)708 void CTermView::OnMouseMove(GdkEventMotion* evt UNUSED)
709 {
710 
711 }
712 
713 
OnMouseScroll(GdkEventScroll * evt UNUSED)714 void CTermView::OnMouseScroll(GdkEventScroll* evt UNUSED)
715 {
716 
717 }
718 
OnRButtonDown(GdkEventButton * evt UNUSED)719 void CTermView::OnRButtonDown(GdkEventButton* evt UNUSED)
720 {
721 
722 }
723 
OnLButtonUp(GdkEventButton * evt)724 void CTermView::OnLButtonUp(GdkEventButton* evt)
725 {
726 	if( !m_pTermData )
727 		return;
728 	ReleaseCapture();
729 
730 	m_pTermData->m_Sel->Canonicalize();
731 
732 	//	2004.08.07 Added by PCMan.  Hyperlink detection.
733 	//	If no text is selected, consider hyperlink.
734 	if( m_pTermData->m_Sel->Empty() )
735 	{
736 		int x = (int)evt->x;
737 		int y = (int)evt->y;
738 		PointToLineCol( &x, &y );
739 
740 		int start, end;
741 		if( HyperLinkHitTest( x, y, &start, &end ) )
742 		{
743 			char* pline = m_pTermData->m_Screen[y];
744 			OnHyperlinkClicked( string( (pline+start), (int)(end-start) ) );
745 		}
746 	}
747 	else	// if there is a selected area
748 	{
749 		CopyToClipboard(true, false, false);
750 	}
751 
752 }
753 
OnRButtonUp(GdkEventButton * evt UNUSED)754 void CTermView::OnRButtonUp(GdkEventButton* evt UNUSED)
755 {
756 }
757 
OnKillFocus(GdkEventFocus * evt UNUSED)758 void CTermView::OnKillFocus(GdkEventFocus *evt UNUSED)
759 {
760 	gtk_im_context_focus_out(m_IMContext);
761 }
762 
763 // Preview the GdkEventKey before it's sent to OnKeyDown
764 // regardless of the returned value of im_context_filter_key_press.
PreKeyDown(GdkEventKey * evt UNUSED)765 bool CTermView::PreKeyDown(GdkEventKey *evt UNUSED)
766 {
767 	return false;
768 }
769 
OnBlinkTimer()770 void CTermView::OnBlinkTimer()
771 {
772 	m_ShowBlink = !m_ShowBlink;
773 	if(m_pTermData)
774 	{
775 //		const int left = 0;	const int right = m_pTermData->m_ColsPerPage;
776 		for( int row = 0; row < m_pTermData->m_RowsPerPage; row++ )
777 		{
778 //			DrawLine( dc, i, y, true , left, right );
779 			CTermCharAttr* attr = m_pTermData->GetLineAttr(
780 					m_pTermData->m_Screen[m_pTermData->m_FirstLine + row] );
781 
782 			for( int col = 0; col < m_pTermData->m_ColsPerPage; )
783 			{
784 				if( attr[col].IsBlink())
785 					col += DrawChar( row, col );
786 				else
787 					col++;
788 			}
789 		}
790 	}
791 	m_Caret.Blink();
792 }
793 
OnMButtonDown(GdkEventButton * evt UNUSED)794 void CTermView::OnMButtonDown(GdkEventButton* evt UNUSED)
795 {
796 	PasteFromClipboard(true);
797 }
798 
799 
800 string CTermView::m_s_ANSIColorStr;
801 string CTermView::m_s_CharSet;
802 
PasteFromClipboard(bool primary)803 void CTermView::PasteFromClipboard(bool primary)
804 {
805 	string text;
806 	if(m_s_ANSIColorStr.empty())
807 	{
808 		GtkClipboard* clipboard = gtk_clipboard_get( primary ? GDK_SELECTION_PRIMARY : GDK_NONE);
809 		INFO("paste");
810 		gchar* utext = gtk_clipboard_wait_for_text(clipboard);
811 		if( !utext )
812 			return;
813 		INFO("%s", utext);
814 
815 		DoPasteFromClipboard( string(utext), false);
816 		g_free(utext);
817 	}
818 	else
819 		DoPasteFromClipboard(m_s_ANSIColorStr, true);
820 }
821 
DoPasteFromClipboard(string text UNUSED,bool contain_ansi_color UNUSED)822 void CTermView::DoPasteFromClipboard(string text UNUSED, bool contain_ansi_color UNUSED)
823 {
824 
825 }
826 
CopyToClipboard(bool primary,bool with_color,bool trim)827 void CTermView::CopyToClipboard(bool primary, bool with_color, bool trim)
828 {
829 	string text;
830 	if(!m_pTermData)
831 		return;
832 	m_s_ANSIColorStr = "";
833 	if( with_color ) {
834 		text = m_pTermData->GetSelectedTextWithColor(trim);
835 		m_s_ANSIColorStr = text;
836 		m_s_CharSet = m_pTermData->m_Encoding;
837 		INFO("copy(with color): %s", text.c_str());
838 	}
839 	else {
840 		text = m_pTermData->GetSelectedText(trim);
841 		gsize wl = 0;
842 		const gchar* utext = NULL;
843 		/* UAO copy support */
844 		switch (m_UAO) {
845 			case 2:
846 				utext = uao250_b2u(text.c_str(), text.length(), &wl);
847 				break;
848 			case 1:
849 				utext = uao241_b2u(text.c_str(), text.length(), &wl);
850 				break;
851 			default:
852 				utext = g_convert_with_fallback(
853 						text.c_str(), text.length(),
854 						"utf-8", m_pTermData->m_Encoding.c_str(),
855 						(gchar *) "?", NULL, &wl, NULL);
856 				break;
857 		}
858 		if(!utext)
859 			return;
860 
861 		GtkClipboard* clipboard = gtk_clipboard_get(  primary ? GDK_SELECTION_PRIMARY : GDK_NONE );
862 		gtk_clipboard_set_text(clipboard, utext, wl );
863 		INFO("copy(without color): %s", utext);
864 		g_free((void*)utext);
865 	}
866 }
867 
GetCellSize(int & w,int & h)868 void CTermView::GetCellSize( int &w, int &h )
869 {
870 	if( !m_pTermData->m_ColsPerPage || !m_pTermData->m_RowsPerPage )
871 	{
872 		w = 0;
873 		h = 0;
874 
875 		return;
876 	}
877 
878 	w = ( m_Widget->allocation.width / m_pTermData->m_ColsPerPage ) - m_CharPaddingX;
879 	h = ( m_Widget->allocation.height / m_pTermData->m_RowsPerPage ) - m_CharPaddingY;
880 }
881 
SetFont(CFont * font,int font_type)882 void CTermView::SetFont( CFont* font, int font_type )
883 {
884 	g_assert(font_type < FONT_END && font_type >= FONT_DEFAULT);
885 
886 	if( !font || m_AutoFontSize)
887 		return;
888 
889 	if( m_Font[font_type] )
890 		delete m_Font[font_type];
891 
892 	if( m_AutoFontSize )
893 	{
894 		int w, h;
895 		GetCellSize( w, h );
896 		m_Font[font_type] = new CFont( font->GetName(), w, h, font->GetCompact(), font->GetAntiAlias() );
897 		delete font;
898 	}
899 	else
900 		m_Font[font_type] = font;
901 
902 	RecalcCharDimension();
903 }
904 
SetFont(string name,int pt_size,bool compact,bool anti_alias,int font_type)905 void CTermView::SetFont( string name, int pt_size, bool compact, bool anti_alias, int font_type)
906 {
907 	g_assert(font_type < FONT_END && font_type >= FONT_START);
908 
909 	if( m_Font[font_type] )
910 		delete m_Font[font_type];
911 
912 	if( m_AutoFontSize )
913 	{
914 		int w, h;
915 		GetCellSize( w, h );
916 		m_Font[font_type] = new CFont( name, w, h, compact, anti_alias );
917 	}
918 	else
919 		m_Font[font_type] = new CFont( name, pt_size, compact, anti_alias );
920 
921 	RecalcCharDimension();
922 }
923 
SetFontFamily(string name,int font_type)924 void CTermView::SetFontFamily( string name, int font_type )
925 {
926 	g_assert(font_type < FONT_END && font_type >= FONT_START);
927 
928 	if( m_AutoFontSize )
929 	{
930 		int w, h;
931 		GetCellSize( w, h );
932 		m_Font[font_type]->SetFont( name, w, h, m_Font[font_type]->GetCompact(), m_Font[font_type]->GetAntiAlias() );
933 	}
934 	else
935 		m_Font[font_type]->SetFontFamily( name );
936 
937 	RecalcCharDimension();
938 }
939 
SetHorizontalCenterAlign(bool is_hcenter)940 void CTermView::SetHorizontalCenterAlign( bool is_hcenter )
941 {
942 	if( m_bHorizontalCenterAlign == is_hcenter || !m_pTermData )
943 		return;
944 
945 	if( (m_bHorizontalCenterAlign = is_hcenter) && GTK_WIDGET_REALIZED(m_Widget) )
946 		m_LeftMargin = (m_Widget->allocation.width - m_CharW * m_pTermData->m_ColsPerPage ) / 2 ;
947 	else
948 		m_LeftMargin = 0;
949 
950 	if( IsVisible() )
951 		Refresh();
952 	UpdateCaretPos();
953 }
954 
SetVerticalCenterAlign(bool is_vcenter)955 void CTermView::SetVerticalCenterAlign( bool is_vcenter )
956 {
957 	if( m_bVerticalCenterAlign == is_vcenter || !m_pTermData )
958 		return;
959 
960 	if( (m_bVerticalCenterAlign = is_vcenter) && GTK_WIDGET_REALIZED(m_Widget) )
961 		m_TopMargin = (m_Widget->allocation.height - m_CharH * m_pTermData->m_RowsPerPage ) / 2 ;
962 	else
963 		m_TopMargin = 0;
964 
965 	if( IsVisible() )
966 		Refresh();
967 	UpdateCaretPos();
968 }
969 
SetUAO(gint idx)970 void CTermView::SetUAO( gint idx )
971 {
972 	const char* encoding = m_pTermData->m_Encoding.c_str();
973 	if (strncasecmp (encoding, "BIG5", 4) == 0 ||
974 		strncasecmp (encoding, "BIG-5", 5) == 0) {
975 		m_UAO = idx;
976 	}
977 }
978 
UpdateCaretPos()979 void CTermView::UpdateCaretPos()
980 {
981 	if( !m_pTermData )
982 		return;
983 
984 	int x = m_pTermData->m_CaretPos.x * m_CharW + m_LeftMargin;
985 	int y = (m_pTermData->m_CaretPos.y + 1) * m_CharH - 2 + m_TopMargin;
986 	m_Caret.Move( x, y );
987 
988 	GdkRectangle rc;
989 	rc.x = x;	rc.y = y; 	rc.width = 0;	rc.height = 0;
990 	gtk_im_context_set_cursor_location(m_IMContext, &rc);
991 }
992 
993 
HyperLinkHitTest(int x,int y,int * start,int * end)994 bool CTermView::HyperLinkHitTest(int x, int y, int* start, int* end)
995 {
996 	char* pline = m_pTermData->m_Screen[y];
997 	CTermCharAttr* pattr = m_pTermData->GetLineAttr(pline);
998 	if( x > 0 && x < m_pTermData->m_ColsPerPage && pattr[x].IsHyperLink() )
999 	{
1000 		int _start, _end;
1001 		for( _start = (x - 1); _start > 0 &&
1002 		                       pattr[_start].IsHyperLink(); _start-- )
1003 			;
1004 		if( !pattr[_start].IsHyperLink() )
1005 			_start++;
1006 		for( _end = (x + 1); _end < m_pTermData->m_ColsPerPage &&
1007 		                     pattr[_end].IsHyperLink(); _end++ )
1008 			;
1009 		*start = _start;
1010 		*end = _end;
1011 		INFO("%d, %d : %d, %d", x, y, _start, _end);
1012 		return true;
1013 	}
1014 	return false;
1015 }
1016 
1017 
OnDestroy()1018 void CTermView::OnDestroy()
1019 {
1020 	for (int i = FONT_START; i != FONT_END; ++i)
1021 	    delete m_Font[i];
1022 
1023 	if( m_pTermData )
1024 		m_pTermData->m_pView = NULL;
1025 
1026 	if( m_HandCursor )
1027 		gdk_cursor_unref(m_HandCursor);
1028 	if( m_HandCursor->ref_count <= 0 )
1029 		m_HandCursor = NULL;
1030 
1031 	CView::OnDestroy();	// Remember to destruct parent
1032 }
1033 
RecalcCharDimension()1034 void CTermView::RecalcCharDimension()
1035 {
1036 	m_CharW = m_Font[FONT_DEFAULT]->GetWidth() + m_CharPaddingX;
1037 	m_CharH = m_Font[FONT_DEFAULT]->GetHeight() + m_CharPaddingY;
1038 
1039 	if( m_bHorizontalCenterAlign )
1040 		m_LeftMargin = (m_Widget->allocation.width - m_CharW * m_pTermData->m_ColsPerPage ) / 2;
1041 	else
1042 		m_LeftMargin = 0;
1043 
1044 	if( m_bVerticalCenterAlign )
1045 		m_TopMargin = (m_Widget->allocation.height - m_CharH * m_pTermData->m_RowsPerPage ) / 2;
1046 	else
1047 		m_TopMargin = 0;
1048 
1049 	m_Caret.SetSize(m_CharW, 2);
1050 	UpdateCaretPos();
1051 	m_Caret.Show();
1052 }
1053 
OnHyperlinkClicked(string url UNUSED)1054 void CTermView::OnHyperlinkClicked(string url UNUSED)	// Overriden in derived classes
1055 {
1056 
1057 }
1058 /* vim: set fileencodings=utf-8 tabstop=4 noexpandtab shiftwidth=4 softtabstop=4: */
1059