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