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