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 "telnetview.h"
22 #endif
23 
24 #include <unistd.h>
25 #include <errno.h>
26 
27 #include <glib/gi18n.h>
28 
29 #include <cstring>
30 
31 #include <gdk/gdkkeysyms.h>
32 #include <cctype>
33 #include <cstring>
34 #include <cstdlib>
35 #include <algorithm>
36 
37 #include "telnetview.h"
38 #include "telnetcon.h"
39 #include "uao241.h"
40 #include "uao250.h"
41 
42 #if !defined(MOZ_PLUGIN)
43 #include "mainframe.h"
44 #include "stringutil.h"
45 #include "appconfig.h"
46 
47 CMainFrame* CTelnetView::m_pParentFrame = NULL;
48 #endif /* !defined(MOZ_PLUGIN) */
49 
CTelnetView()50 CTelnetView::CTelnetView()
51 	: CTermView()
52 {
53 #if defined(USE_IPLOOKUP) && !defined(MOZ_PLUGIN)
54   m_pIpSeeker = seeker_new(AppConfig.GetConfigDirPath().append("/qqwry.dat").c_str());
55 #endif
56 }
57 
~CTelnetView()58 CTelnetView::~CTelnetView()
59 {
60 #if defined(USE_IPLOOKUP) && !defined(MOZ_PLUGIN)
61   if (m_pIpSeeker) seeker_delete(m_pIpSeeker);
62 #endif
63 }
64 
65 string CTelnetView::m_WebBrowser;
66 string CTelnetView::m_MailClient;
67 
68 static GtkWidget* input_menu_item = NULL;
69 static GtkWidget* websearch_menu_item = NULL;
70 
OnTextInput(const gchar * text)71 void CTelnetView::OnTextInput(const gchar* text)
72 {
73 	gsize l;
74 	gchar* _text = NULL;
75 	/* UAO input support */
76 	switch (m_UAO) {
77 		case 2:
78 			_text = uao250_u2b(text, strlen(text), &l);
79 			break;
80 		case 1:
81 			_text = uao241_u2b(text, strlen(text), &l);
82 			break;
83 		default:
84 			_text = g_convert(text, strlen(text), GetCon()->m_Site.m_Encoding.c_str(), "UTF-8", NULL, &l, NULL);
85 			break;
86 	}
87 	if( _text )
88 	{
89 		((CTelnetCon*)m_pTermData)->Send(_text, l);
90 		g_free(_text);
91 	}
92 	// clear the old selection
93 	// Workaround FIXME please
94 	if (!m_pTermData->m_Sel->Empty())
95 	{
96 		GdkEventButton t_PseudoEvent;
97 		t_PseudoEvent.x = 0;
98 		t_PseudoEvent.y = 0;
99 		t_PseudoEvent.type = GDK_BUTTON_PRESS;
100 		CTermView::OnLButtonDown(&t_PseudoEvent);
101 		CTermView::OnLButtonUp(&t_PseudoEvent);
102 	}
103 }
104 
105 #define	GDK_MODIFIER_DOWN(key, mod)	(key & (mod|(~GDK_SHIFT_MASK&~GDK_CONTROL_MASK&~GDK_MOD1_MASK)))
106 
DrawCharWrapper(int row,int col,void * data)107 static int DrawCharWrapper( int row, int col, void *data )
108 {
109 	CTermView *tv = (CTermView *) data;
110 
111 	return tv->DrawChar( row, col );
112 }
113 
EnterWrapper(CTelnetCon * Con,CTermCharAttr * Attr,int pos)114 void EnterWrapper( CTelnetCon* Con, CTermCharAttr* Attr, int pos)
115 {
116 	if( Con->DetectDBChar() && Attr[pos].GetCharSet() == CTermCharAttr::CS_MBCS1 )
117 		Con->SendString("\x1bOC\x1bOC");
118 	else
119 		Con->SendString("\x1bOC");
120 }
121 
LeaveWrapper(CTelnetCon * Con,CTermCharAttr * Attr,int pos)122 void LeaveWrapper( CTelnetCon* Con, CTermCharAttr* Attr, int pos)
123 {
124 	if( Con->DetectDBChar() && pos > 0 && Attr[pos-1].GetCharSet() == CTermCharAttr::CS_MBCS2 )
125 		Con->SendString("\x1bOD\x1bOD");
126 	else
127 		Con->SendString("\x1bOD");
128 }
129 
OnKeyDown(GdkEventKey * evt)130 bool CTelnetView::OnKeyDown(GdkEventKey* evt)
131 {
132 	INFO("CTelnetView::OnKeyDown (keyval=0x%x, state=0x%x)", evt->keyval, evt->state);
133 	CTermCharAttr* pAttr = m_pTermData->GetLineAttr(
134 			m_pTermData->m_Screen[m_pTermData->m_CaretPos.y] );
135 	int x = m_pTermData->m_CaretPos.x;
136 	bool clear = true;
137 	bool reconnect = false;
138 
139 	if( evt->keyval < 127 && GDK_MODIFIER_DOWN(evt->state, GDK_CONTROL_MASK))// Ctrl down
140 	{
141 		char ch = toupper(char(evt->keyval));
142 		if( ch >= '@' && ch <= '_'	&& !isdigit(ch) )
143 		{
144 			// clear the old selection
145 			if (!m_pTermData->m_Sel->Empty())
146 				ClearSelection();
147 
148 			ch -= '@';
149 			GetCon()->SendRawString(&ch,1);
150 			return true;
151 		}
152 	}
153 
154 	switch(evt->keyval)
155 	{
156 	case GDK_Left:
157 	case GDK_KP_Left:
158 		LeaveWrapper(GetCon(), pAttr, x);
159 		break;
160 	case GDK_Right:
161 	case GDK_KP_Right:
162 		EnterWrapper(GetCon(), pAttr, x);
163 		break;
164 	case GDK_Up:
165 	case GDK_KP_Up:
166 		GetCon()->SendString("\x1bOA");
167 		break;
168 	case GDK_Down:
169 	case GDK_KP_Down:
170 		GetCon()->SendString("\x1bOB");
171 		break;
172 	case GDK_BackSpace:
173 		if (GetCon()->DetectDBChar() && x > 0 && pAttr[x-1].GetCharSet() == CTermCharAttr::CS_MBCS2)
174 			GetCon()->SendString("\b\b");
175 		else
176 			GetCon()->SendString("\b");
177 		break;
178 	case GDK_Return:
179 	case GDK_KP_Enter:
180 		reconnect = GetCon()->IsClosed();
181 		GetCon()->SendString("\r");
182 		break;
183 	case GDK_Delete:
184 	case GDK_KP_Delete:
185 		if (GetCon()->DetectDBChar() && pAttr[x].GetCharSet() == CTermCharAttr::CS_MBCS1)
186 			GetCon()->SendString("\x1b[3~\x1b[3~");
187 		else
188 			GetCon()->SendString("\x1b[3~");
189 		break;
190 	case GDK_Insert:
191 	case GDK_KP_Insert:
192 		GetCon()->SendString("\x1b[2~");
193 		break;
194 	case GDK_Home:
195 	case GDK_KP_Home:
196 		GetCon()->SendString("\x1b[1~");
197 		break;
198 	case GDK_End:
199 	case GDK_KP_End:
200 		GetCon()->SendString("\x1b[4~");
201 		break;
202 //	case GDK_Prior:
203 	case GDK_Page_Up:
204 	case GDK_KP_Page_Up:
205 		GetCon()->SendString("\x1b[5~");
206 		break;
207 //	case GDK_Next:
208 	case GDK_Page_Down:
209 	case GDK_KP_Page_Down:
210 		GetCon()->SendString("\x1b[6~");
211 		break;
212 	case GDK_Tab:
213 		GetCon()->SendString("\t");
214 		break;
215 	case GDK_Escape:
216 		GetCon()->SendString("\x1b");
217 		break;
218 // F1-F12 keys
219 	case GDK_F1:
220 	case GDK_KP_F1:
221 		GetCon()->SendString("\x1bOP");
222 		break;
223 	case GDK_F2:
224 	case GDK_KP_F2:
225 		GetCon()->SendString("\x1bOQ");
226 		break;
227 	case GDK_F3:
228 	case GDK_KP_F3:
229 		GetCon()->SendString("\x1bOR");
230 		break;
231 	case GDK_F4:
232 	case GDK_KP_F4:
233 		GetCon()->SendString("\x1bOS");
234 		break;
235 	case GDK_F5:
236 		GetCon()->SendString("\x1b[15~");
237 		break;
238 	case GDK_F6:
239 		GetCon()->SendString("\x1b[17~");
240 		break;
241 	case GDK_F7:
242 	    GetCon()->SendString("\x1b[18~");
243 		break;
244 	case GDK_F8:
245 		GetCon()->SendString("\x1b[19~");
246 		break;
247 	case GDK_F9:
248 		GetCon()->SendString("\x1b[20~");
249 		break;
250 	case GDK_F10:
251 		GetCon()->SendString("\x1b[21~");
252 		break;
253 	case GDK_F11:
254 		GetCon()->SendString("\x1b[23~");
255 		break;
256 	case GDK_F12:
257 		GetCon()->SendString("\x1b[24~");
258 		break;
259 	default:
260 		clear = false;
261 	}
262 
263 	// Only clear selection if we handled the key
264 	if (clear)
265 		ClearSelection();
266 
267 	if (reconnect)
268 		GetCon()->Reconnect();
269 
270 	return true;
271 }
272 
on_hyperlink_copy(GtkMenuItem * item UNUSED,bool * do_copy)273 static void on_hyperlink_copy(GtkMenuItem* item UNUSED, bool *do_copy)
274 {
275 	*do_copy = true;
276 }
277 
278 #if defined(USE_IPLOOKUP) && !defined(MOZ_PLUGIN)
ipstr2int(const char * str)279 static inline unsigned int ipstr2int(const char *str)
280 {
281 	unsigned char ip[4];
282 	if (sscanf(str, " %hhu . %hhu . %hhu . %hhu"
283 				, ip + 3, ip + 2, ip + 1, ip) != 4)
284 		return 0;
285 	return *((unsigned int*)ip);
286 }
287 #endif
288 
OnMouseMove(GdkEventMotion * evt)289 void CTelnetView::OnMouseMove(GdkEventMotion* evt)
290 {
291   if( !m_pTermData )
292     return;
293 
294   int x = (int)evt->x;
295   int y = (int)evt->y;
296   bool left;
297 
298   INFO("x=%d, y=%d, grab=%d", x, y, HasCapture());
299 
300   this->PointToLineCol( &x, &y, &left );
301   if( HasCapture() )	//	Selecting text.
302     {
303       if ( m_pTermData->m_Sel->m_End.row != y
304 	   || m_pTermData->m_Sel->m_End.col != x
305 	   || m_pTermData->m_Sel->m_End.left != left )
306 	{
307 	  // Always remember to hide the caret before drawing.
308 	  m_Caret.Hide();
309 
310 	  m_pTermData->m_Sel->ChangeEnd( y, x, left, DrawCharWrapper, this );
311 
312 	  // Show the caret again but only set its visibility without
313 	  // display it immediatly.
314 	  m_Caret.Show( false );
315 #ifdef USE_MOUSE
316 	  {gdk_window_set_cursor(m_Widget->window, NULL);m_CursorState=0;}
317 #endif
318 	}
319     }
320 #if !defined(MOZ_PLUGIN)
321   else
322   {
323 #ifdef USE_MOUSE
324     CTermCharAttr* pattr = m_pTermData->GetLineAttr(m_pTermData->m_Screen[ y ]);
325 #endif
326 
327 #if defined(USE_IPLOOKUP)
328     // Update status bar for ip address lookup.
329     m_pParentFrame->PopStatus("show ip");
330     if( x > 0 && x < m_pTermData->m_ColsPerPage && pattr[x].IsIpAddr() )
331     {
332       int ip_beg, ip_end;
333       for (ip_beg = x; ip_beg >= 0 && pattr[ip_beg].IsIpAddr(); ip_beg--);
334       ip_beg++;
335       for (ip_end = x; ip_end < m_pTermData->m_ColsPerPage && pattr[ip_end].IsIpAddr(); ip_end++);
336       string ipstr(m_pTermData->m_Screen[y] + ip_beg, ip_end - ip_beg);
337       string::iterator star = find(ipstr.begin(), ipstr.end(), '*');
338       while (star != ipstr.end())
339       {
340 	*star = '0';
341 	star = find(star + 1, ipstr.end(), '*');
342       }
343 
344       char buf[255];
345       if (m_pIpSeeker)
346       {
347 	seeker_lookup(m_pIpSeeker, ipstr2int(ipstr.c_str()), buf, sizeof(buf));
348 	gchar *location = g_convert_with_fallback(buf, -1, "utf8", "gbk", "?", NULL, NULL, NULL);
349 	snprintf(buf, sizeof(buf), "%s %s (%s)"
350 	    , _("Detected IP address:"), ipstr.c_str(), location);
351 	g_free(location);
352       }
353       else
354 	snprintf(buf, sizeof(buf), "%s %s (%s)"
355 	    , _("Detected IP address:"), ipstr.c_str()
356 	    , _("Download qqwry.dat to get IP location lookup"));
357 
358       m_pParentFrame->PushStatus("show ip", buf);
359     }
360 #endif // defined(USE_IPLOOKUP)
361 
362 #if defined(USE_MOUSE)
363     if ( AppConfig.MouseSupport == true )
364     {
365       if( x > 0 && x < m_pTermData->m_ColsPerPage && pattr[x].IsHyperLink() )
366       {gdk_window_set_cursor(m_Widget->window, m_HandCursor);m_CursorState=-1;}
367       else
368       {
369 	switch( ((CTelnetCon*)m_pTermData)->GetPageState() )
370 	{
371 	  case -1: //NORMAL
372 	    gdk_window_set_cursor(m_Widget->window, NULL);
373 	    m_CursorState=0;
374 	    break;
375 	  case 1: //LIST
376 	    if ( y>2 && y < m_pTermData->m_RowsPerPage-1 )
377 	    {
378 	      if ( x <= 6 )
379 	      {gdk_window_set_cursor(m_Widget->window, m_ExitCursor);m_CursorState=1;}
380 	      else if ( x >= m_pTermData->m_ColsPerPage-16 )
381 	      {
382 		if ( y > m_pTermData->m_RowsPerPage /2 )
383 		{gdk_window_set_cursor(m_Widget->window, m_PageDownCursor);m_CursorState=3;}
384 		else
385 		{gdk_window_set_cursor(m_Widget->window, m_PageUpCursor);m_CursorState=4;}
386 	      }
387 	      else
388 	      {gdk_window_set_cursor(m_Widget->window, m_BullsEyeCursor);m_CursorState=2;}
389 	    }
390 	    else if ( y==1 || y==2 )
391 	    {gdk_window_set_cursor(m_Widget->window, m_PageUpCursor);m_CursorState=4;}
392 	    else if ( y==0 )
393 	    {gdk_window_set_cursor(m_Widget->window, m_HomeCursor);m_CursorState=6;}
394 	    else //if ( y = m_pTermData->m_RowsPerPage-1)
395 	    {gdk_window_set_cursor(m_Widget->window, m_EndCursor);m_CursorState=5;}
396 	    break;
397 	  case 2: //READING
398 	    if ( y == m_pTermData->m_RowsPerPage-1)
399 	    {gdk_window_set_cursor(m_Widget->window, m_EndCursor);m_CursorState=5;}
400 	    else if ( x<7 )
401 	    {gdk_window_set_cursor(m_Widget->window, m_ExitCursor);m_CursorState=1;}
402 	    else if ( y < (m_pTermData->m_RowsPerPage-1)/2 )
403 	    {gdk_window_set_cursor(m_Widget->window, m_PageUpCursor);m_CursorState=4;}
404 	    else
405 	    {gdk_window_set_cursor(m_Widget->window, m_PageDownCursor);m_CursorState=3;}
406 	    break;
407 	  case 0: //MENU
408 	    if ( y>0 && y < m_pTermData->m_RowsPerPage-1 )
409 	    {
410 	      if (x>7)
411 	      {gdk_window_set_cursor(m_Widget->window, m_BullsEyeCursor);m_CursorState=2;}
412 	      else
413 	      {gdk_window_set_cursor(m_Widget->window, m_ExitCursor);m_CursorState=1;}
414 	    }
415 	    else
416 	    {gdk_window_set_cursor(m_Widget->window, NULL);m_CursorState=0;}
417 	    break;
418 	  default:
419 	    break;
420 	}
421       }
422     }
423     else
424     {
425       CTermCharAttr* pattr = m_pTermData->GetLineAttr(m_pTermData->m_Screen[ y ]);
426       if( x > 0 && x < m_pTermData->m_ColsPerPage && pattr[x].IsHyperLink() )
427 	gdk_window_set_cursor(m_Widget->window, m_HandCursor);
428       else
429 	gdk_window_set_cursor(m_Widget->window, NULL);;
430       m_CursorState=0;
431     }
432 #endif // defined(USE_MOUSE)
433   }
434 #endif // !defined(MOZ_PLUGIN)
435 }
436 
437 #if defined(USE_MOUSE) && !defined(MOZ_PLUGIN)
OnMouseScroll(GdkEventScroll * evt)438 void CTelnetView::OnMouseScroll(GdkEventScroll* evt)
439 {
440 	if( !m_pTermData )
441 		return;
442 
443 	if ( AppConfig.MouseSupport != true )
444 		return;
445 
446 	GdkScrollDirection i = evt->direction;;
447 	if ( i == GDK_SCROLL_UP )
448 		GetCon()->SendString("\x1bOA");
449 	if ( i == GDK_SCROLL_DOWN )
450 		GetCon()->SendString("\x1bOB");
451 }
452 
OnMButtonDown(GdkEventButton * evt)453 void CTelnetView::OnMButtonDown(GdkEventButton* evt)
454 {
455 	if ( AppConfig.MouseSupport != true )
456 	{
457 		PasteFromClipboard(true);
458 		return;
459 	}
460 
461 	if ( AppConfig.WithMiddleButton != true )
462 		return;
463 
464 	CTermCharAttr* pAttr = m_pTermData->GetLineAttr(
465 			m_pTermData->m_Screen[m_pTermData->m_CaretPos.y] );
466 	int x = m_pTermData->m_CaretPos.x;
467 	LeaveWrapper(GetCon(), pAttr, x);
468 }
469 
OnLButtonUp(GdkEventButton * evt)470 void CTelnetView::OnLButtonUp(GdkEventButton* evt)
471 {
472 	CTermView::OnLButtonUp(evt);
473 
474 	if( !m_pTermData )
475 		return;
476 
477 	if ( AppConfig.MouseSupport != true )
478 		return;
479 
480 	int x = (int)evt->x;
481 	int y = (int)evt->y;
482 	bool left;
483 	this->PointToLineCol( &x, &y, &left );
484 
485   int start, end;
486   // Don't send mouse action when the user click on hyperlinks
487   if( HyperLinkHitTest( x, y, &start, &end ) )
488 		  return;
489 
490 	//some text is selected
491 	if ( m_CancelSel
492 	     || m_pTermData->m_Sel->m_End.row != y
493 	     || m_pTermData->m_Sel->m_End.col != x
494 	     || m_pTermData->m_Sel->m_End.left != left
495 	     || m_pTermData->m_Sel->m_Start.row != y
496 	     || m_pTermData->m_Sel->m_Start.col != x
497 	     || m_pTermData->m_Sel->m_Start.left != left )
498 	  return;
499 
500 	if ( AppConfig.WithMiddleButton == true ){
501 		CTermCharAttr* pAttr = m_pTermData->GetLineAttr(
502 				m_pTermData->m_Screen[m_pTermData->m_CaretPos.y] );
503 		int x = m_pTermData->m_CaretPos.x;
504 		EnterWrapper(GetCon(), pAttr, x);
505 	}
506 	else
507 	{
508 	  int cur = m_CursorState;
509 	  int ps = ((CTelnetCon*)m_pTermData)->GetPageState();
510 
511 	  if ( cur == 2 ) // mouse on entering mode
512 	  {
513 		switch (ps)
514 		{
515 	      case 1: // list
516 		  {
517 		    int n = y - m_pTermData->m_CaretPos.y;
518 		    if ( n>0 )
519 		      while(n)
520 		      {
521 				GetCon()->SendString("\x1bOB");
522 				n--;
523 		      }
524 		    if ( n<0 )
525 		    {
526 			  n=-n;
527 		      while(n)
528 			  {
529 			    GetCon()->SendString("\x1bOA");
530 			    n--;
531 			  }
532 		    }
533 			GetCon()->SendString("\r"); //return key
534 			break;
535 		  }
536 		  case 0: // menu
537 		  {
538 			char cMenu = ((CTelnetCon*)m_pTermData)->GetMenuChar(y);
539 			GetCon()->SendRawString( &cMenu, 1 );
540 			GetCon()->SendString("\r");
541 			break;
542 		  }
543 		  case -1: // normal
544 			GetCon()->SendString("\r");
545 		  break;
546 			default:
547 		  break;
548 		}
549 	  }
550 	  else if (cur == 1)
551 		GetCon()->SendString("\x1bOD"); //exiting mode
552 	  else if (cur == 6)
553 		GetCon()->SendString("\x1b[1~"); //home
554 	  else if (cur == 5)
555 		GetCon()->SendString("\x1b[4~"); //end
556 	  else if (cur == 4)
557 		GetCon()->SendString("\x1b[5~"); //pageup
558 	  else if (cur == 3)
559 		GetCon()->SendString("\x1b[6~"); //pagedown
560 	  else
561 		GetCon()->SendString("\r");
562 	}
563 }
564 #endif  // defined(USE_MOUSE) && !defined(MOZ_PLUGIN)
565 
OnRButtonDown(GdkEventButton * evt)566 void CTelnetView::OnRButtonDown(GdkEventButton* evt)
567 {
568 #if !defined(MOZ_PLUGIN)
569 	if( !m_ContextMenu )
570 		return;
571 #endif
572 
573 	if( m_pTermData )	// Copy URL popup menu.
574 	{
575 		int x = (int)evt->x;
576 		int y = (int)evt->y;
577 		PointToLineCol( &x, &y );
578 		int start, end;
579 		if( HyperLinkHitTest( x, y, &start, &end ) )
580 		{
581 			char* pline = m_pTermData->m_Screen[y];
582 			bool do_copy = false;
583 			// Show the "Copy Hyperlink" menu.
584 			GtkWidget* popup = gtk_menu_new();
585 			GtkWidget* item = gtk_image_menu_item_new_with_mnemonic( _("_Copy URL to Clipboard") );
586 			GtkWidget* icon = gtk_image_new_from_stock (GTK_STOCK_COPY, GTK_ICON_SIZE_MENU);
587 			gtk_image_menu_item_set_image (GTK_IMAGE_MENU_ITEM (item), icon);
588 			g_signal_connect( G_OBJECT(item), "activate",
589 							G_CALLBACK(on_hyperlink_copy), &do_copy);
590 
591 			gtk_menu_shell_append  ((GtkMenuShell *)popup, item );
592 			gtk_widget_show_all(popup);
593 			g_signal_connect( G_OBJECT(popup), "deactivate",
594 							G_CALLBACK(gtk_main_quit), this);
595 			gtk_menu_popup( (GtkMenu*)popup, NULL, NULL, NULL, NULL, evt->button, evt->time );
596 			gtk_main();		// Don't return until the menu is closed.
597 
598 			if( do_copy )
599 			{
600 				// Note by Hong Jen Yee (PCMan):
601 				// Theoratically, there is no non-ASCII characters in standard URL,
602 				// so we don't need to do UTF-8 conversion at all.
603 				// However, users are always right.
604 				string url( (pline+start), (int)(end-start) );
605 				gsize wl = 0;
606 				const gchar* purl = g_convert_with_fallback( url.c_str(), url.length(),
607 						"utf-8", m_pTermData->m_Encoding.c_str(), (gchar *) "?", NULL, &wl, NULL);
608 				if(purl)
609 				{
610 					m_s_ANSIColorStr = "";
611 					GtkClipboard* clipboard = gtk_clipboard_get( GDK_NONE );
612 					gtk_clipboard_set_text(clipboard, purl, wl );
613 					clipboard = gtk_clipboard_get(  GDK_SELECTION_PRIMARY);
614 					gtk_clipboard_set_text(clipboard, purl, wl );
615 					g_free((void*)purl);
616 				}
617 			}
618 			gtk_widget_destroy(popup);
619 			return;
620 		}
621 	}
622 #if !defined(MOZ_PLUGIN)
623 	if( input_menu_item )
624 		gtk_widget_destroy( input_menu_item );
625 	input_menu_item = gtk_menu_item_new_with_mnemonic (_("Input _Methods"));
626 	gtk_widget_show (input_menu_item);
627 	GtkWidget* submenu = gtk_menu_new ();
628 	gtk_menu_item_set_submenu (GTK_MENU_ITEM (input_menu_item), submenu);
629 
630 	// Show Web Search only when text selected
631 	if(websearch_menu_item)
632 		gtk_widget_destroy(websearch_menu_item);
633 	string selected_text = m_pTermData->GetSelectedText(false);
634 	if(! selected_text.empty()) {
635 		gsize wl = 0;
636 		gchar websearch_text[128];
637 		gchar* search_content = g_convert_with_fallback(
638 							selected_text.c_str(), selected_text.length(),
639 							"utf-8", m_pTermData->m_Encoding.c_str(),
640 							(gchar *) "?", NULL, &wl, NULL);
641 		if(g_utf8_strlen(search_content, selected_text.length()) > 15) {
642 			g_utf8_strncpy(search_content, search_content, 15);
643 			strcat(search_content, _("..."));
644 		}
645 		websearch_menu_item = gtk_menu_item_new_with_mnemonic(_("_Web Search: \"%s\""));
646 		sprintf(websearch_text, gtk_menu_item_get_label(GTK_MENU_ITEM(websearch_menu_item)), search_content);
647 		gtk_menu_item_set_label(GTK_MENU_ITEM(websearch_menu_item), websearch_text);
648 
649 		gtk_widget_show(websearch_menu_item);
650 		g_signal_connect(G_OBJECT(websearch_menu_item), "activate", G_CALLBACK(CTelnetView::OnWebSearch), this);
651 		gtk_menu_shell_append(GTK_MENU_SHELL(m_ContextMenu), websearch_menu_item );
652 		g_free(search_content);
653 	}
654 
655 	gtk_menu_shell_append (GTK_MENU_SHELL (m_ContextMenu), input_menu_item);
656 
657 	gtk_im_multicontext_append_menuitems (GTK_IM_MULTICONTEXT (m_IMContext),
658 					GTK_MENU_SHELL (submenu));
659 	gtk_menu_popup( m_ContextMenu, NULL, NULL, NULL, NULL, evt->button, evt->time );
660 #endif
661 }
662 
PreKeyDown(GdkEventKey * evt UNUSED)663 bool CTelnetView::PreKeyDown(GdkEventKey* evt UNUSED)
664 {
665 /*	if( GDK_MODIFIER_DOWN( evt->state, GDK_MOD1_MASK)
666 			|| GDK_MODIFIER_DOWN( evt->state, GDK_CONTROL_MASK)
667 			&& ((evt->keyval > GDK_0 && evt->keyval > GDK_9)
668 				 || (evt->keyval > GDK_KP_0 && evt->keyval > GDK_KP_9) )
669 			)
670 	{
671 		int i = evt->keyval > GDK_KP_0 ? (evt->keyval - GDK_KP_0):(evt->keyval - GDK_0);
672 		m_pParentFrame->SwitchToTab(i);
673 		return true;
674 	}
675 */	return false;
676 }
677 
DoPasteFromClipboard(string text,bool contain_ansi_color)678 void CTelnetView::DoPasteFromClipboard(string text, bool contain_ansi_color)
679 {
680     int lines_count = 0, last_line_count = 0;
681     string raw_str;
682 
683     if( ConvStr2SiteEncoding(text, contain_ansi_color, raw_str, lines_count, last_line_count) )
684         GetCon()->SendRawString(raw_str.c_str(), raw_str.length());
685 }
686 
ConvStr2SiteEncoding(string text,bool contain_ansi_color,string & text2,int & lines_count,int & last_line_count)687 bool CTelnetView::ConvStr2SiteEncoding(string text, bool contain_ansi_color, string & text2, int & lines_count, int & last_line_count)
688 {
689 	if( GetCon() )
690 	{
691 		lines_count = 0;
692 		last_line_count = 0;
693 
694 		if( contain_ansi_color )
695 		{
696 			bool is_replace_crlf = false;
697 			gchar* locale_text = NULL;
698 			const char* p = NULL;
699 			const char* crlf = GetCon()->m_Site.GetCRLF();
700 			string esc = GetCon()->m_Site.GetEscapeChar();
701 
702 			if( m_s_CharSet != GetCon()->m_Site.m_Encoding.c_str() )
703 			{
704 			  INFO("Charset Conversion from %s to %s",m_s_CharSet.c_str(),GetCon()->m_Site.m_Encoding.c_str());
705 
706 			  gsize convl;
707 			  gchar* locale_text = g_convert(text.c_str(), text.length(),GetCon()->m_Site.m_Encoding.c_str(),m_s_CharSet.c_str(), NULL, &convl, NULL);
708 			  if( !locale_text )
709 				return false;
710 
711 			  p = locale_text;
712 			}
713 			else
714 			{
715 				INFO("color text: %s",text.c_str());
716 				p = text.c_str();
717 				is_replace_crlf = true;
718 			}
719 
720 			bool is_ctrl_word = false;
721 			while(*p)
722 			{
723 				if(*p == '\x1b'){
724 					text2 += esc;
725 					is_ctrl_word = true;
726 				}
727 				else if( *p == '\n' ) {
728 					if(is_replace_crlf)
729 						text2 += crlf;
730 					else
731 						text2 += *p;
732 					lines_count++;
733 					last_line_count = 0;
734 				}
735 				else {
736 					text2 += *p;
737 
738 					if(!is_ctrl_word)
739 						last_line_count++;
740 					if(*p == 'm') // end of color control word
741 						is_ctrl_word = false;
742 				}
743 				p++;
744 			}
745 
746 			if(locale_text)
747 				g_free(locale_text);
748 
749 		}
750 		else
751 		{
752 			// Only when no control character is in this string can
753 			// autowrap be enabled
754 			unsigned int len = 0, max_len = GetCon()->m_Site.m_AutoWrapOnPaste;
755 			gsize convl = 0;
756 			gchar* locale_text = NULL;
757 
758 			/* UAO paste support */
759 			switch (m_UAO) {
760 				case 2:
761 					locale_text = uao250_u2b(text.c_str(), 0, &convl);
762 					break;
763 				case 1:
764 					locale_text = uao241_u2b(text.c_str(), 0, &convl);
765 					break;
766 				default:
767 					locale_text = g_convert(text.c_str(), text.length(), GetCon()->m_Site.m_Encoding.c_str(), "UTF-8", NULL, &convl, NULL);
768 					break;
769 			}
770 			if( !locale_text )
771 				return false;
772 			// FIXME: Convert UTF-8 string to locale string.to prevent invalid UTF-8 string
773 			// caused by the auto-wrapper.
774 			// Just a workaround.  This needs to be modified in the future.
775 			const char* ptext = locale_text;
776 			const char* crlf = GetCon()->m_Site.GetCRLF();
777 			if( GetCon()->m_Site.m_AutoWrapOnPaste > 0 )
778 			{
779 				string str2;
780 				const char* pstr = locale_text;
781 				for( ; *pstr; ++pstr )
782 				{
783 					size_t word_len = 1;
784 					const char* pword = pstr;
785 					if( ((unsigned char)*pstr) < 128 )		// This is a ASCII character
786 					{
787 						if( *pstr == '\n' || *pstr == '\r' )
788 							len = 0;
789 						else
790 						{
791 							while( *pstr && ((unsigned char)*(pstr+1)) && ((unsigned char)*(pstr+1)) < 128  && !strchr(" \t\n\r", *pstr) )
792 								++pstr;
793 							word_len = (pstr - pword) + (*pstr != '\t' ? 1 : 4);	// assume tab width = 4, may be changed in the future
794 						}
795 					}
796 					else
797 					{
798 						++pstr;
799 						word_len = ( *pstr ? 2 : 1 );
800 					}
801 
802 					if( (len + word_len) > max_len )
803 					{
804 						len = 0;
805 						str2 += '\n';
806 					}
807 					len += word_len;
808 					while( pword <= pstr )
809 					{
810 						str2 += *pword;
811 						pword ++;
812 					}
813 					if( *pstr == '\n' || *pstr == '\r' )
814 						len = 0;
815 				}
816 				text = str2;
817 				ptext = text.c_str();
818 			}
819 
820 			for( const char* pstr = ptext; *pstr; ++pstr )
821 			{
822 				if( *pstr == '\n' )
823 				{
824 					text2 += crlf;
825 					lines_count++;
826 					last_line_count = 0;
827 				}
828 				else
829 				{
830 					text2 += *pstr;
831 					if( *pstr < 128 )
832 						last_line_count ++;
833 				}
834 			}
835 
836 			g_free( locale_text );
837 		}
838 	}
839 
840 	return true;
841 }
842 
843 
OnDestroy()844 void CTelnetView::OnDestroy()
845 {
846 	if( m_pTermData )
847 	{
848 		delete m_pTermData;
849 		m_pTermData = NULL;
850 	}
851 }
852 
OnHyperlinkClicked(string sURL)853 void CTelnetView::OnHyperlinkClicked(string sURL)
854 {
855 	gchar *cmdAndURL[3] = {0, 0, 0};
856 	GError *err = NULL;
857 
858 #if !defined(MOZ_PLUGIN)
859 	if( 0 == strncmpi( sURL.c_str(), "telnet:", 7) )
860 	{
861 		const char* psURL = sURL.c_str() + 7;
862 		while( *psURL == '/' )
863 			++psURL;
864 		if( !*psURL )
865 			return;
866 		sURL = psURL;
867 		if( '/' == sURL[sURL.length()-1] )
868 			sURL = string( sURL.c_str(), 0, sURL.length()-1 );
869 		m_pParentFrame->NewCon( sURL, sURL );
870 		return;
871 	}
872 #endif /* !defined(MOZ_PLUGIN) */
873 
874 	string app;
875 	if( !strstr( sURL.c_str(), "://") && strchr(sURL.c_str(), '@'))
876 	{
877 		app = m_MailClient;
878 		if( strncmpi( sURL.c_str(), "mailto:", 7 ) )
879 			sURL.insert( 0, "mailto:" );
880 	}
881 	else
882 		app = m_WebBrowser;
883 
884 	// Remove %s for backward compatibility, the legacy setting is "xdg-open %s"
885 	size_t legacyAppSymOffset = string::npos;
886 
887 	legacyAppSymOffset = app.find(" %s");
888 	if ( legacyAppSymOffset != string::npos)
889 	{
890 		app.erase(legacyAppSymOffset, 3);
891 	}
892 
893 	// Launch app
894 	INFO("Launch app with URL: %s %s", app.c_str(), sURL.c_str());
895 	cmdAndURL[0] = (gchar *) app.c_str();
896 	cmdAndURL[1] = (gchar *) sURL.c_str();
897 
898 	bool rval = g_spawn_async(NULL, cmdAndURL, NULL, G_SPAWN_SEARCH_PATH, NULL, NULL, NULL, &err);
899 	if (!rval)
900 	{
901 		g_print("can not run %s: %s\n", app.c_str(), err->message);
902 	}
903 }
904 
OnWebSearch(GtkMenuItem * mitem UNUSED,CTelnetView * _this)905 void CTelnetView::OnWebSearch(GtkMenuItem* mitem UNUSED, CTelnetView* _this)
906 {
907 	_this->OnWebSearchSelected();
908 }
909 
910 #define  SEARCH_URL ("http://www.google.com.tw/search?&ie=UTF-8&q=")
OnWebSearchSelected()911 void CTelnetView::OnWebSearchSelected()
912 {
913 	gchar *cmdAndURL[3] = {0, 0, 0};
914 	GError *err = NULL;
915 	string selectedText = CTermView::m_pTermData->GetSelectedText(false);
916 
917 	// Convert to utf8
918     // FIXME: Search keyword (Big5-UAO) may not be converted to UTF8 correctly
919 	gsize wl = 0;
920 	gchar* selectedTextUTF8 = g_convert_with_fallback(
921 				   selectedText.c_str(), selectedText.length(),
922 				   "utf-8", m_pTermData->m_Encoding.c_str(),
923 				   (gchar *) "?", NULL, &wl, NULL);
924 
925 	if (selectedTextUTF8 == NULL)
926 	{
927 		   return;
928 	}
929 
930 	INFO("Try to OnWebSearchSelected: %s (%d)", selectedTextUTF8, (int) g_utf8_strlen(selectedTextUTF8, -1) );
931 
932 	// Compose URL
933 	string searchURL;
934 	searchURL.append(SEARCH_URL);
935 	searchURL.append(selectedTextUTF8);
936 
937 	string app = m_WebBrowser;
938 
939 	// Remove %s for backward compatibility.
940 	// the legacy setting is "xdg-open %s"
941 	size_t legacyAppSymOffset = string::npos;
942 
943 	legacyAppSymOffset = app.find(" %s");
944 	if ( legacyAppSymOffset != string::npos)
945 	{
946 		// copy on write, will not pollute parent m_WebBrowser
947 		app.erase(legacyAppSymOffset, 3);
948 	}
949 
950 	INFO("Seach App with URL: %s %s", app.c_str(), searchURL.c_str());
951 
952 	// Launch browser
953 	cmdAndURL[0] = (gchar *) app.c_str();
954 	cmdAndURL[1] = (gchar *) searchURL.c_str();
955 
956 	bool rval = g_spawn_async(NULL, cmdAndURL, NULL, G_SPAWN_SEARCH_PATH, NULL, NULL, NULL, &err);
957 	if (!rval)
958 	{
959 		g_print("can not run %s: %s\n", app.c_str(), err->message);
960 	}
961 
962 	g_free((void*)selectedTextUTF8);
963 }
964 
965 
966 /* vim: set fileencodings=utf-8 tabstop=4 noexpandtab shiftwidth=4 softtabstop=4: */
967