nux-1.16.0
|
00001 00002 00003 /* 00004 Copyright 2008 Google Inc. 00005 00006 Licensed under the Apache License, Version 2.0 (the "License"); 00007 you may not use this file except in compliance with the License. 00008 You may obtain a copy of the License at 00009 00010 http://www.apache.org/licenses/LICENSE-2.0 00011 00012 Unless required by applicable law or agreed to in writing, software 00013 distributed under the License is distributed on an "AS IS" BASIS, 00014 WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 00015 See the License for the specific language governing permissions and 00016 limitations under the License. 00017 */ 00018 00019 00020 #include "Nux.h" 00021 #include "Layout.h" 00022 #include "HLayout.h" 00023 #include "VLayout.h" 00024 #include "Validator.h" 00025 00026 #include "cairo/cairo.h" 00027 #include "pango/pango.h" 00028 #include "pango/pangocairo.h" 00029 #include "NuxImage/CairoGraphics.h" 00030 00031 #include "TextEntry.h" 00032 00033 namespace nux 00034 { 00035 static const int kInnerBorderX = 2; 00036 static const int kInnerBorderY = 0; //1; 00037 static const int kCursorBlinkTimeout = 400; 00038 static const double kStrongCursorLineWidth = 1; 00039 static const double kStrongCursorBarWidth = 1; 00040 static const double kWeakCursorLineWidth = 3; 00041 static const double kWeakCursorBarWidth = 3; 00042 static const Color kStrongCursorColor(0.9f, 0.9f, 0.9f, 1.0f); 00043 static const Color kWeakCursorColor(1.0f, 1.0f, 1.0f, 0.5f); 00044 static const Color kDefaultTextColor(0, 0, 0, 1.0f); 00045 static const Color kDefaultBackgroundColor(1, 1, 1, 1.0f); 00046 static const Color kDefaultSelectionBackgroundColor(0.5, 0.5, 0.5, 1.0f); 00047 static const Color kDefaultSelectionTextColor(1, 1, 1, 1.0f); 00048 static const t_u64 kTripleClickTimeout = 500; 00049 static const std::string kDefaultFontName = "Ubuntu"; 00050 00051 static t_u64 GetCurrentTime() 00052 { 00053 GTimeVal tv; 00054 g_get_current_time(&tv); 00055 return static_cast<t_u64>(tv.tv_sec) * 1000 + tv.tv_usec / 1000; 00056 } 00057 00058 static std::string CleanupLineBreaks(const char *source) 00059 { 00060 nuxAssert (source); 00061 std::string result; 00062 while (*source) { 00063 if (*source == '\r') { 00064 result += ' '; 00065 if (source[1] == '\n') 00066 source++; 00067 } else if (*source == '\n') { 00068 result += ' '; 00069 } else { 00070 result += *source; 00071 } 00072 source++; 00073 } 00074 return result; 00075 } 00076 00077 // Calculate pixel size based on the Windows DPI of 96 for compatibility 00078 // reasons. 00079 CairoFont::CairoFont(const std::string &family, 00080 /*PangoFontDescription *font,*/ 00081 double pt_size, 00082 Style style, 00083 Weight weight) 00084 : font_(pango_font_description_new()) 00085 , size_(pt_size * PANGO_SCALE * 96 / 72) 00086 , style_(style) 00087 , weight_(weight) 00088 { 00089 pango_font_description_set_family(font_, family.c_str()); 00090 pango_font_description_set_absolute_size(font_, size_); 00091 00092 if (weight_ == CairoFont::WEIGHT_BOLD) 00093 { 00094 pango_font_description_set_weight(font_, PANGO_WEIGHT_BOLD); 00095 } 00096 00097 if (style_ == CairoFont::STYLE_ITALIC) 00098 { 00099 pango_font_description_set_style(font_, PANGO_STYLE_ITALIC); 00100 } 00101 } 00102 00103 CairoFont::~CairoFont() 00104 { 00105 pango_font_description_free(font_); 00106 } 00107 00108 NUX_IMPLEMENT_OBJECT_TYPE (TextEntry); 00109 00110 TextEntry::TextEntry(const TCHAR* text, NUX_FILE_LINE_DECL) 00111 : View(NUX_FILE_LINE_PARAM) 00112 , _size_match_text(true) 00113 , _texture2D(nullptr) 00114 , _block_focus(false) 00115 , canvas_(nullptr) 00116 , cached_layout_(nullptr) 00117 , preedit_attrs_(nullptr) 00118 , last_dblclick_time_(0) 00119 , cursor_(0) 00120 , preedit_cursor_(0) 00121 , selection_bound_(0) 00122 , scroll_offset_x_(0) 00123 , scroll_offset_y_(0) 00124 , cursor_blink_timer_(0) 00125 , cursor_blink_status_(0) 00126 , visible_(true) 00127 , focused_(false) 00128 , need_im_reset_(false) 00129 , overwrite_(false) 00130 , select_words_(false) 00131 , select_lines_(false) 00132 , button_(false) 00133 , bold_(false) 00134 , underline_(false) 00135 , strikeout_(false) 00136 , italic_(false) 00137 , multiline_(false) 00138 , wrap_(false) 00139 , cursor_visible_(false) 00140 , readonly_(false) 00141 , content_modified_(false) 00142 , selection_changed_(false) 00143 , cursor_moved_(false) 00144 , update_canvas_(true) 00145 , font_family_("Ubuntu") 00146 , font_size_(12) 00147 , font_options_(cairo_font_options_create()) 00148 , font_dpi_(96.0) 00149 , _text_color(color::White) 00150 , align_(CairoGraphics::ALIGN_LEFT) 00151 , text_input_mode_(false) 00152 , key_nav_mode_(false) 00153 00154 { 00155 // Protected member from View, not passable to constructor. 00156 _can_pass_focus_to_composite_layout = false; 00157 00158 cairo_font_options_set_antialias(font_options_, CAIRO_ANTIALIAS_SUBPIXEL); 00159 cairo_font_options_set_hint_style(font_options_, CAIRO_HINT_STYLE_FULL); 00160 cairo_font_options_set_hint_metrics(font_options_, CAIRO_HINT_METRICS_ON); 00161 cairo_font_options_set_subpixel_order(font_options_, CAIRO_SUBPIXEL_ORDER_RGB); 00162 00163 mouse_down.connect(sigc::mem_fun(this, &TextEntry::RecvMouseDown)); 00164 mouse_drag.connect(sigc::mem_fun(this, &TextEntry::RecvMouseDrag)); 00165 mouse_up.connect(sigc::mem_fun(this, &TextEntry::RecvMouseUp)); 00166 mouse_double_click.connect(sigc::mem_fun(this, &TextEntry::RecvMouseDoubleClick)); 00167 00168 key_down.connect(sigc::mem_fun(this, &TextEntry::RecvKeyEvent)); 00169 00170 begin_key_focus.connect(sigc::mem_fun(this, &TextEntry::RecvStartKeyFocus)); 00171 end_key_focus.connect(sigc::mem_fun(this, &TextEntry::RecvEndKeyFocus)); 00172 00173 SetMinimumSize(DEFAULT_WIDGET_WIDTH, PRACTICAL_WIDGET_HEIGHT); 00174 SetText(text); 00175 00176 SetAcceptKeyboardEvent(true); 00177 EnableDoubleClick(true); 00178 } 00179 00180 TextEntry::~TextEntry () 00181 { 00182 if(cursor_blink_timer_) 00183 g_source_remove(cursor_blink_timer_); 00184 00185 cairo_font_options_destroy (font_options_); 00186 if (_texture2D) 00187 _texture2D->UnReference (); 00188 } 00189 00190 void TextEntry::PreLayoutManagement () 00191 { 00192 View::PreLayoutManagement (); 00193 } 00194 00195 long TextEntry::PostLayoutManagement (long layoutResult) 00196 { 00197 long result = View::PostLayoutManagement (layoutResult); 00198 MainDraw (); 00199 return result; 00200 } 00201 00202 void TextEntry::DoSetFocused (bool focused) 00203 { 00204 focused_ = focused; 00205 cursor_visible_ = focused; // visibilty of cursor depends on focus 00206 View::DoSetFocused (focused); 00207 if (focused == true) 00208 { 00209 _block_focus = true; 00210 SetCursor(cursor_); 00211 QueueRefresh(true, true); 00212 00213 Area *_parent = GetParentObject(); 00214 if (_parent == NULL) 00215 return; 00216 00217 if (_parent->IsView ()) 00218 { 00219 View *parent = (View*)_parent; 00220 parent->SetFocusControl (false); 00221 } 00222 else if (_parent->IsLayout ()) 00223 { 00224 Layout *parent = (Layout *)_parent; 00225 parent->SetFocusControl (false); 00226 } 00227 } 00228 else 00229 { 00230 QueueRefresh(true, false); // needed to hide cursor 00231 } 00232 } 00233 00234 void TextEntry::GeometryChanged () 00235 { 00236 00237 update_canvas_ = true; 00238 View::GeometryChanged(); 00239 00240 } 00241 00242 long TextEntry::ProcessEvent (IEvent& event, 00243 long traverseInfo, 00244 long processEventInfo) 00245 { 00246 long ret = traverseInfo; 00247 /* must do focus processing after sending events to children */ 00248 if (event.e_event == NUX_KEYDOWN && GetFocused ()) 00249 { 00250 FocusDirection direction; 00251 FocusEventType type; 00252 00253 direction = FOCUS_DIRECTION_NONE; 00254 00255 type = Focusable::GetFocusableEventType (event.e_event, 00256 event.GetKeySym(), 00257 event.GetText(), 00258 &direction); 00259 if (type == FOCUS_EVENT_DIRECTION && _block_focus == false) 00260 { 00261 if (direction == FOCUS_DIRECTION_PREV || direction == FOCUS_DIRECTION_NEXT || 00262 direction == FOCUS_DIRECTION_UP || direction == FOCUS_DIRECTION_DOWN) 00263 { 00264 Area *area = GetParentObject (); 00265 // if parent is null return, thats a valid usecase so no warnings. 00266 if (area) 00267 { 00268 long ret = 0; 00269 if ( area->IsView() ) 00270 { 00271 View *ic = NUX_STATIC_CAST (View *, area ); 00272 ret = ic->ProcessFocusEvent (event, ret, processEventInfo); 00273 } 00274 else if ( area->IsLayout() ) 00275 { 00276 Layout *layout = NUX_STATIC_CAST (Layout *, area ); 00277 layout->SetFocusControl (true); 00278 ret = layout->ProcessFocusEvent (event, ret, processEventInfo); 00279 } 00280 } 00281 } 00282 else 00283 { 00284 key_down.emit (event.e_event, event.GetKeySym(), 00285 event.GetKeyState(), event.GetText(), 00286 event.GetKeyRepeatCount()); 00287 //ret = PostProcessEvent2 (event, ret, processEventInfo); 00288 } 00289 } 00290 else 00291 { 00292 key_down.emit (event.e_event, event.GetKeySym(), 00293 event.GetKeyState(), event.GetText(), 00294 event.GetKeyRepeatCount()); 00295 } 00296 } 00297 00298 if (_block_focus) 00299 _block_focus = false; 00300 00301 00302 return ret; 00303 } 00304 00305 Area* TextEntry::FindAreaUnderMouse(const Point& mouse_position, NuxEventType event_type) 00306 { 00307 Area* area = View::FindAreaUnderMouse(mouse_position, event_type); 00308 00309 return area; 00310 } 00311 00312 void TextEntry::ProcessMouseEvent (int event_type, int x, int y, int dx, int dy, unsigned long button_flags, unsigned long key_flags) 00313 { 00314 if (GetEventButton (button_flags) != 1) 00315 return; 00316 00317 //ResetImContext(); 00318 //Event::Type type = event.GetType(); 00319 00320 int X = static_cast<int>(x /*round(event.GetX())*/) - kInnerBorderX - scroll_offset_x_; 00321 int Y = static_cast<int>(y /*round(event.GetY())*/) - kInnerBorderY - scroll_offset_y_; 00322 int index = XYToTextIndex(X, Y); 00323 int sel_start, sel_end; 00324 GetSelectionBounds(&sel_start, &sel_end); 00325 00326 t_u64 current_time = GetCurrentTime(); 00327 00328 if ((event_type == NUX_MOUSE_PRESSED) && (current_time - last_dblclick_time_ <= kTripleClickTimeout)) 00329 { 00330 SelectLine(); 00331 } 00332 else if (event_type == NUX_MOUSE_DOUBLECLICK) 00333 { 00334 SelectWord(); 00335 last_dblclick_time_ = current_time; 00336 } 00337 else if (event_type == NUX_MOUSE_PRESSED) 00338 { 00339 if (key_flags & NUX_STATE_SHIFT) 00340 { 00341 // If current click position is inside the selection range, then just 00342 // cancel the selection. 00343 if (index > sel_start && index < sel_end) 00344 SetCursor(index); 00345 else if (index <= sel_start) 00346 SetSelectionBounds(sel_end, index); 00347 else if (index >= sel_end) 00348 SetSelectionBounds(sel_start, index); 00349 } 00350 else 00351 { 00352 SetCursor(index); 00353 } 00354 } 00355 else if (event_type == NUX_MOUSE_MOVE) 00356 { 00357 SetSelectionBounds(selection_bound_, index); 00358 } 00359 00360 QueueRefresh(false, true); 00361 //return EVENT_RESULT_HANDLED; 00362 } 00363 00364 void TextEntry::ProcessKeyEvent ( 00365 unsigned long event_type , /*event type*/ 00366 unsigned long keysym , /*event keysym*/ 00367 unsigned long state , /*event state*/ 00368 const TCHAR* character , /*character*/ 00369 unsigned short keyCount /*key repeat count*/) 00370 { 00371 if (event_type == NUX_KEYDOWN) 00372 text_input_mode_ = true; 00373 00374 // GdkEventKey *gdk_event = static_cast<GdkEventKey *>(event.GetOriginalEvent()); 00375 // ASSERT(gdk_event); 00376 // 00377 // Event::Type type = event.GetType(); 00378 // Cause the cursor to stop blinking for a while. 00379 cursor_blink_status_ = 4; 00380 00381 // if (!readonly_ /*&& im_context_*/ && type != Event::EVENT_KEY_PRESS && 0/*&& gtk_im_context_filter_keypress(im_context_, gdk_event)*/) 00382 // { 00383 // need_im_reset_ = true; 00384 // QueueRefresh(false, true); 00385 // return EVENT_RESULT_HANDLED; 00386 // } 00387 00388 if (event_type == NUX_KEYUP) 00389 return; 00390 00391 // we need to ignore some characters 00392 if (keysym == NUX_VK_TAB) 00393 return; 00394 00395 if (keysym == NUX_VK_ENTER || keysym == NUX_KP_ENTER) 00396 { 00397 activated.emit (); 00398 return; 00399 } 00400 00401 unsigned int keyval = keysym; 00402 bool shift = (state & NUX_STATE_SHIFT); 00403 bool ctrl = (state & NUX_STATE_CTRL); 00404 00405 // DLOG("TextEntry::key_down(%d, shift:%d ctrl:%d)", keyval, shift, ctrl); 00406 00407 if (event_type == NUX_KEYDOWN) 00408 { 00409 if (keyval == NUX_VK_LEFT) 00410 { 00411 if (!ctrl) 00412 MoveCursor(VISUALLY, -1, shift); 00413 else 00414 MoveCursor(WORDS, -1, shift); 00415 } 00416 else if (keyval == NUX_VK_RIGHT) 00417 { 00418 if (!ctrl) 00419 MoveCursor(VISUALLY, 1, shift); 00420 else 00421 MoveCursor(WORDS, 1, shift); 00422 } 00423 else if (keyval == NUX_VK_UP) 00424 { 00425 MoveCursor(DISPLAY_LINES, -1, shift); 00426 } 00427 else if (keyval == NUX_VK_DOWN) 00428 { 00429 MoveCursor(DISPLAY_LINES, 1, shift); 00430 } 00431 else if (keyval == NUX_VK_HOME) 00432 { 00433 if (!ctrl) 00434 MoveCursor(DISPLAY_LINE_ENDS, -1, shift); 00435 else 00436 MoveCursor(BUFFER, -1, shift); 00437 } 00438 else if (keyval == NUX_VK_END) 00439 { 00440 if (!ctrl) 00441 MoveCursor(DISPLAY_LINE_ENDS, 1, shift); 00442 else 00443 MoveCursor(BUFFER, 1, shift); 00444 } 00445 else if (keyval == NUX_VK_PAGE_UP) 00446 { 00447 if (!ctrl) 00448 MoveCursor(PAGES, -1, shift); 00449 else 00450 MoveCursor(BUFFER, -1, shift); 00451 } 00452 else if (keyval == NUX_VK_PAGE_DOWN) 00453 { 00454 if (!ctrl) 00455 MoveCursor(PAGES, 1, shift); 00456 else 00457 MoveCursor(BUFFER, 1, shift); 00458 } 00459 else if (((keyval == NUX_VK_x) && ctrl && !shift) || ((keyval == NUX_VK_DELETE) && shift && !ctrl)) 00460 { 00461 CutClipboard (); 00462 } 00463 else if (((keyval == NUX_VK_c) && ctrl && (!shift)) || ((keyval == NUX_VK_INSERT) && ctrl && (!shift))) 00464 { 00465 CopyClipboard (); 00466 } 00467 else if (((keyval == NUX_VK_v) && ctrl && (!shift)) || ((keyval == NUX_VK_INSERT) && shift && (!ctrl))) 00468 { 00469 PasteClipboard (); 00470 } 00471 else if ((keyval == NUX_VK_a) && ctrl) 00472 { 00473 // Select all 00474 int text_length = static_cast<int>(_text.length()); 00475 SetSelectionBounds(0, text_length); 00476 QueueRefresh(false, true); 00477 return; 00478 } 00479 else if (keyval == NUX_VK_BACKSPACE) 00480 { 00481 if (!ctrl) 00482 BackSpace(VISUALLY); 00483 else 00484 BackSpace(WORDS); 00485 } 00486 else if ((keyval == NUX_VK_DELETE) && (!shift)) 00487 { 00488 if (!ctrl) 00489 Delete(VISUALLY); 00490 else 00491 Delete(WORDS); 00492 } 00493 else if ((keyval == NUX_VK_INSERT) && (!shift) && (!ctrl)) 00494 { 00495 ToggleOverwrite(); 00496 } 00497 // else 00498 // { 00499 // return EVENT_RESULT_UNHANDLED; 00500 // } 00501 } 00502 else 00503 { // EVENT_KEY_PRESS 00504 // if (keyval == GDK_Return || keyval == GDK_KP_Enter) 00505 // { 00506 // // If multiline_ is unset, just ignore new_line. 00507 // if (multiline_) 00508 // EnterText("\n"); 00509 // else 00510 // return; 00511 // } 00512 // else 00513 // { 00514 // return; 00515 // } 00516 } 00517 00518 if (character != 0 && (strlen (character) != 0)) 00519 { 00520 EnterText(character); 00521 } 00522 00523 QueueRefresh(false, true); 00524 return; 00525 } 00526 00527 void TextEntry::RecvMouseDoubleClick (int x, int y, unsigned long button_flags, unsigned long key_flags) 00528 { 00529 ProcessMouseEvent(NUX_MOUSE_DOUBLECLICK, x, y, 0, 0, button_flags, key_flags); 00530 } 00531 00532 void TextEntry::RecvMouseUp (int x, int y, unsigned long button_flags, unsigned long key_flags) 00533 { 00534 ProcessMouseEvent(NUX_MOUSE_RELEASED, x, y, 0, 0, button_flags, key_flags); 00535 } 00536 00537 void TextEntry::RecvMouseDown (int x, int y, unsigned long button_flags, unsigned long key_flags) 00538 { 00539 ProcessMouseEvent(NUX_MOUSE_PRESSED, x, y, 0, 0, button_flags, key_flags); 00540 } 00541 00542 void TextEntry::RecvMouseDrag (int x, int y, int dx, int dy, unsigned long button_flags, unsigned long key_flags) 00543 { 00544 ProcessMouseEvent(NUX_MOUSE_MOVE, x, y, dx, dy, button_flags, key_flags); 00545 } 00546 00547 void TextEntry::RecvKeyEvent ( 00548 unsigned long eventType , /*event type*/ 00549 unsigned long keysym , /*event keysym*/ 00550 unsigned long state , /*event state*/ 00551 const TCHAR* character , /*character*/ 00552 unsigned short keyCount /*key repeat count*/) 00553 { 00554 ProcessKeyEvent(eventType, keysym, state, character, keyCount); 00555 } 00556 00557 void TextEntry::RecvStartKeyFocus () 00558 { 00559 key_nav_mode_ = true; 00560 text_input_mode_ = false; 00561 00562 FocusInx (); 00563 } 00564 00565 void TextEntry::RecvEndKeyFocus () 00566 { 00567 key_nav_mode_ = false; 00568 text_input_mode_ = false; 00569 00570 FocusOutx (); 00571 } 00572 00573 void TextEntry::Draw (GraphicsEngine& gfxContext, bool forceDraw) 00574 { 00575 MainDraw (); 00576 Geometry base = GetGeometry (); 00577 00578 gfxContext.PushClippingRectangle (base); 00579 00580 nux::GetPainter().PaintBackground(gfxContext, base); 00581 00582 Color col = color::Black; 00583 col.alpha = 0; 00584 gfxContext.QRP_Color (base.x, 00585 base.y, 00586 base.width, 00587 base.height, 00588 col); 00589 00590 TexCoordXForm texxform; 00591 texxform.SetWrap (TEXWRAP_REPEAT, TEXWRAP_REPEAT); 00592 texxform.SetTexCoordType (TexCoordXForm::OFFSET_COORD); 00593 gfxContext.QRP_1Tex (base.x, 00594 base.y, 00595 base.width, 00596 base.height, 00597 _texture2D->GetDeviceTexture(), 00598 texxform, 00599 _text_color); 00600 00601 gfxContext.PopClippingRectangle (); 00602 } 00603 00604 void TextEntry::DrawContent (GraphicsEngine& gfxContext, bool forceDraw) 00605 { 00606 //MainDraw (); 00607 } 00608 00609 void TextEntry::PostDraw (GraphicsEngine& gfxContext, bool forceDraw) 00610 { 00611 // intentionally left empty 00612 } 00613 00614 void TextEntry::SetText (const char *text) 00615 { 00616 const char *end = NULL; 00617 g_utf8_validate(text, -1, &end); 00618 00619 std::string txt((text && *text && end > text) ? std::string(text, end) : ""); 00620 if (txt == _text) 00621 return; // prevent some redraws 00622 00623 _text = multiline_ ? txt : CleanupLineBreaks(txt.c_str()); 00624 cursor_ = 0; 00625 selection_bound_ = 0; 00626 need_im_reset_ = true; 00627 //ResetImContext(); 00628 QueueRefresh(true, true); 00629 sigTextChanged.emit (this); 00630 } 00631 00632 std::string const& TextEntry::GetText() const 00633 { 00634 return _text; 00635 } 00636 00637 void TextEntry::SetTextColor (const Color &text_color) 00638 { 00639 if (_text_color != text_color) 00640 { 00641 _text_color = text_color; 00642 QueueRefresh(true, true); 00643 } 00644 } 00645 00646 Color const& TextEntry::GetTextColor() const 00647 { 00648 return _text_color; 00649 } 00650 00651 00652 void TextEntry::MainDraw () 00653 { 00654 00655 CairoGraphics *edit_canvas = EnsureCanvas(); 00656 00657 if (update_canvas_ || !last_selection_region_.empty() || !selection_region_.empty()) 00658 { 00659 edit_canvas->PushState(); 00660 DrawText(edit_canvas); 00661 edit_canvas->PopState(); 00662 } 00663 00664 // if (background_) 00665 // background_->Draw(canvas, 0, 0, GetBaseWidth, GetBaseHeight); 00666 00667 CairoGraphics* final_canvas = new CairoGraphics (CAIRO_FORMAT_ARGB32, edit_canvas->GetWidth (), edit_canvas->GetHeight ()); 00668 00669 final_canvas->PushState(); 00670 final_canvas->IntersectRectClipRegion(kInnerBorderX, 00671 kInnerBorderY, 00672 GetBaseWidth() - kInnerBorderX, 00673 GetBaseHeight() - kInnerBorderY); 00674 final_canvas->DrawCanvas(0, 0, edit_canvas); 00675 final_canvas->PopState(); 00676 DrawCursor(final_canvas); 00677 00678 update_canvas_ = false; 00679 last_selection_region_ = selection_region_; 00680 last_cursor_region_ = cursor_region_; 00681 00682 NBitmapData* bitmap = final_canvas->GetBitmap (); 00683 delete final_canvas; 00684 00685 if (!_texture2D || _texture2D->GetWidth() != bitmap->GetWidth() || _texture2D->GetHeight() != bitmap->GetHeight()) 00686 { 00687 if (_texture2D) 00688 _texture2D->UnReference (); 00689 _texture2D = GetGraphicsDisplay()->GetGpuDevice()->CreateSystemCapableTexture (); 00690 } 00691 00692 _texture2D->Update (bitmap); 00693 delete bitmap; 00694 } 00695 00696 void TextEntry::FocusInx() 00697 { 00698 if (!focused_) 00699 { 00700 focused_ = true; 00701 if (!readonly_ /*&& im_context_*/) 00702 { 00703 need_im_reset_ = true; 00704 //gtk_im_context_focus_in(im_context_); 00705 //UpdateIMCursorLocation(); 00706 } 00707 cursor_visible_ = true; // show cursor when getting focus 00708 selection_changed_ = true; 00709 cursor_moved_ = true; 00710 // Don't adjust scroll. 00711 QueueRefresh(true, false); 00712 } 00713 } 00714 00715 void TextEntry::FocusOutx() 00716 { 00717 if (focused_) 00718 { 00719 focused_ = false; 00720 if (!readonly_ /*&& im_context_*/) 00721 { 00722 need_im_reset_ = true; 00723 //gtk_im_context_focus_out(im_context_); 00724 } 00725 cursor_visible_ = false; // hide cursor when losing focus 00726 selection_changed_ = true; 00727 cursor_moved_ = true; 00728 // Don't adjust scroll. 00729 QueueRefresh(true, false); 00730 } 00731 } 00732 00733 CairoGraphics* TextEntry::EnsureCanvas() 00734 { 00735 if (canvas_) 00736 { 00737 if ((GetBaseWidth () == canvas_->GetWidth()) && (GetBaseHeight () == canvas_->GetHeight())) 00738 { 00739 return canvas_; 00740 } 00741 else 00742 { 00743 nuxDebugMsg(TEXT("[TextEntry::EnsureCanvas] Recreate canvas")); 00744 delete canvas_; 00745 canvas_ = NULL; 00746 } 00747 } 00748 canvas_ = new CairoGraphics (CAIRO_FORMAT_ARGB32, GetBaseWidth (), GetBaseHeight ()); 00749 nuxAssert (canvas_); 00750 return canvas_; 00751 } 00752 00753 void TextEntry::AdjustScroll () 00754 { 00755 int old_offset_x = scroll_offset_x_; 00756 int old_offset_y = scroll_offset_y_; 00757 int display_width = GetBaseWidth () - kInnerBorderX * 2; 00758 int display_height = GetBaseHeight () - kInnerBorderY * 2; 00759 00760 PangoLayout *layout = EnsureLayout(); 00761 int text_width, text_height; 00762 pango_layout_get_pixel_size(layout, &text_width, &text_height); 00763 00764 int strong_x, strong_y, strong_height; 00765 int weak_x, weak_y, weak_height; 00766 GetCursorLocationInLayout(&strong_x, &strong_y, &strong_height, 00767 &weak_x, &weak_y, &weak_height); 00768 00769 if (!wrap_ && display_width > text_width) 00770 { 00771 PangoAlignment align = pango_layout_get_alignment(layout); 00772 if (align == PANGO_ALIGN_RIGHT) 00773 scroll_offset_x_ = display_width - text_width; 00774 else if (align == PANGO_ALIGN_CENTER) 00775 scroll_offset_x_ = (display_width - text_width) / 2; 00776 else 00777 scroll_offset_x_ = 0; 00778 } 00779 else 00780 { 00781 if (scroll_offset_x_ + strong_x < 0) 00782 scroll_offset_x_ = -strong_x; 00783 else if (scroll_offset_x_ + strong_x > display_width) 00784 scroll_offset_x_ = display_width - strong_x; 00785 00786 if (std::abs(weak_x - strong_x) < display_width) 00787 { 00788 if (scroll_offset_x_ + weak_x < 0) 00789 scroll_offset_x_ = - weak_x; 00790 else if (scroll_offset_x_ + weak_x > display_width) 00791 scroll_offset_x_ = display_width - weak_x; 00792 } 00793 } 00794 00795 if (display_height > text_height) 00796 { 00797 scroll_offset_y_ = 0; 00798 } 00799 else 00800 { 00801 if (scroll_offset_y_ + strong_y + strong_height > display_height) 00802 scroll_offset_y_ = display_height - strong_y - strong_height; 00803 if (scroll_offset_y_ + strong_y < 0) 00804 scroll_offset_y_ = -strong_y; 00805 } 00806 00807 if (old_offset_x != scroll_offset_x_ || old_offset_y != scroll_offset_y_) 00808 content_modified_ = true; 00809 } 00810 00811 void TextEntry::QueueRefresh(bool relayout, bool adjust_scroll) 00812 { 00813 if (relayout) 00814 ResetLayout(); 00815 00816 if (adjust_scroll) 00817 AdjustScroll(); 00818 00819 QueueTextDraw(); 00820 QueueCursorBlink(); 00821 } 00822 00823 void TextEntry::ResetImContext() 00824 { 00825 if (need_im_reset_) 00826 { 00827 need_im_reset_ = false; 00828 // if (im_context_) 00829 // gtk_im_context_reset(im_context_); 00830 ResetPreedit(); 00831 } 00832 } 00833 00834 void TextEntry::ResetPreedit() { 00835 // Reset layout if there were some content in preedit string 00836 if (_preedit.length()) 00837 ResetLayout(); 00838 00839 _preedit.clear(); 00840 preedit_cursor_ = 0; 00841 if (preedit_attrs_) { 00842 pango_attr_list_unref(preedit_attrs_); 00843 preedit_attrs_ = NULL; 00844 } 00845 } 00846 00847 void TextEntry::DrawText(CairoGraphics *canvas) 00848 { 00849 PangoLayout *layout = EnsureLayout(); 00850 00851 bool redraw_text = false; 00852 if (update_canvas_) 00853 { 00854 canvas->ClearCanvas(); 00855 canvas->PushState(); 00856 redraw_text = true; 00857 } 00858 else if (!last_selection_region_.empty()) 00859 { 00860 //last_selection_region_.Integerize(); 00861 canvas->PushState(); 00862 canvas->IntersectGeneralClipRegion(last_selection_region_); 00863 canvas->ClearRect(0, 0, GetBaseWidth(), GetBaseHeight()); 00864 redraw_text = true; 00865 } 00866 00867 if (redraw_text) 00868 { 00869 cairo_set_source_rgb(canvas->GetInternalContext(), 00870 _text_color.red, 00871 _text_color.green, 00872 _text_color.blue); 00873 00874 cairo_move_to(canvas->GetInternalContext(), 00875 scroll_offset_x_ + kInnerBorderX, 00876 scroll_offset_y_ + kInnerBorderY); 00877 00878 pango_cairo_show_layout(canvas->GetInternalContext(), layout); 00879 00880 canvas->PopState(); 00881 } 00882 00883 // Draw selection background. 00884 // Selection in a single line may be not continual, so we use pango to 00885 // get the x-ranges of each selection range in one line, and draw them 00886 // separately. 00887 if (!selection_region_.empty()) 00888 { 00889 canvas->PushState(); 00890 //selection_region_.Integerize(); 00891 canvas->IntersectGeneralClipRegion(selection_region_); 00892 00893 Color selection_color = GetSelectionBackgroundColor(); 00894 Color text_color = GetSelectionTextColor(); 00895 00896 cairo_set_source_rgb(canvas->GetInternalContext(), 00897 selection_color.red, 00898 selection_color.green, 00899 selection_color.blue); 00900 cairo_paint(canvas->GetInternalContext()); 00901 00902 cairo_move_to(canvas->GetInternalContext(), 00903 scroll_offset_x_ + kInnerBorderX, 00904 scroll_offset_y_ + kInnerBorderY); 00905 cairo_set_source_rgb(canvas->GetInternalContext(), 00906 text_color.red, 00907 text_color.green, 00908 text_color.blue); 00909 pango_cairo_show_layout(canvas->GetInternalContext(), layout); 00910 canvas->PopState(); 00911 } 00912 } 00913 00914 bool TextEntry::CursorBlinkCallback(TextEntry *self) 00915 { 00916 if (self->cursor_blink_status_) 00917 self->ShowCursor(); 00918 else 00919 self->HideCursor(); 00920 00921 if (--self->cursor_blink_status_ < 0) 00922 self->cursor_blink_status_ = 2; 00923 00924 return true; 00925 } 00926 00927 void TextEntry::QueueCursorBlink() 00928 { 00929 if (!cursor_blink_timer_) 00930 cursor_blink_timer_ = g_timeout_add(kCursorBlinkTimeout, 00931 (GSourceFunc)&CursorBlinkCallback, 00932 this); 00933 } 00934 00935 void TextEntry::ShowCursor() 00936 { 00937 if (!cursor_visible_) 00938 { 00939 cursor_visible_ = true; 00940 if (focused_ && !readonly_) 00941 { 00942 cursor_moved_ = true; 00943 QueueRefresh(false, false); 00944 } 00945 } 00946 } 00947 00948 void TextEntry::HideCursor() 00949 { 00950 if (cursor_visible_) 00951 { 00952 cursor_visible_ = false; 00953 if (focused_ && !readonly_) 00954 { 00955 cursor_moved_ = true; 00956 QueueRefresh(false, false); 00957 } 00958 } 00959 } 00960 00961 void TextEntry::GetCursorRects(Rect *strong, Rect *weak) 00962 { 00963 int strong_x, strong_y, strong_height; 00964 int weak_x, weak_y, weak_height; 00965 GetCursorLocationInLayout(&strong_x, &strong_y, &strong_height, 00966 &weak_x, &weak_y, &weak_height); 00967 00968 strong->x = 00969 strong_x + kInnerBorderX + scroll_offset_x_ - kStrongCursorBarWidth; 00970 strong->width = kStrongCursorBarWidth * 2; 00971 strong->y = strong_y + kInnerBorderY + scroll_offset_y_; 00972 strong->height = strong_height; 00973 00974 if (weak_x != strong_x) 00975 { 00976 weak->x = weak_x+ kInnerBorderX + scroll_offset_x_ - kWeakCursorBarWidth; 00977 weak->width = kWeakCursorBarWidth * 2; 00978 weak->y = weak_y+ kInnerBorderY + scroll_offset_y_; 00979 weak->height = weak_height; 00980 } 00981 else 00982 { 00983 *weak = *strong; 00984 } 00985 } 00986 00987 void TextEntry::UpdateCursorRegion() 00988 { 00989 cursor_region_.clear(); 00990 00991 Rect strong, weak; 00992 GetCursorRects(&strong, &weak); 00993 00994 cursor_region_.push_back(strong); 00995 cursor_region_.push_back(weak); 00996 } 00997 00998 00999 void TextEntry::DrawCursor(CairoGraphics *canvas) 01000 { 01001 if (!cursor_visible_) 01002 return; 01003 01004 int strong_x, strong_y, strong_height; 01005 int weak_x, weak_y, weak_height; 01006 GetCursorLocationInLayout(&strong_x, &strong_y, &strong_height, 01007 &weak_x, &weak_y, &weak_height); 01008 01009 // Draw strong cursor. 01010 // 0.5 is for cairo drawing between the grid 01011 canvas->DrawLine(strong_x + kInnerBorderX + scroll_offset_x_ + 0.5, 01012 strong_y + kInnerBorderY + scroll_offset_y_, 01013 strong_x + kInnerBorderX + scroll_offset_x_ + 0.5, 01014 strong_y + strong_height + kInnerBorderY + scroll_offset_y_, 01015 kStrongCursorLineWidth, kStrongCursorColor); 01016 // Draw a small arror towards weak cursor 01017 if (strong_x > weak_x) 01018 { 01019 canvas->DrawLine( 01020 strong_x + kInnerBorderX + scroll_offset_x_ - kStrongCursorBarWidth, 01021 strong_y + kInnerBorderY + scroll_offset_y_ + kStrongCursorLineWidth, 01022 strong_x + kInnerBorderX + scroll_offset_x_, 01023 strong_y + kInnerBorderY + scroll_offset_y_ + kStrongCursorLineWidth, 01024 kStrongCursorLineWidth, kStrongCursorColor); 01025 } 01026 else if (strong_x < weak_x) 01027 { 01028 canvas->DrawLine( 01029 strong_x + kInnerBorderX + scroll_offset_x_, 01030 strong_y + kInnerBorderY + scroll_offset_y_ + kStrongCursorLineWidth, 01031 strong_x + kInnerBorderX + scroll_offset_x_ + kStrongCursorBarWidth, 01032 strong_y + kInnerBorderY + scroll_offset_y_ + kStrongCursorLineWidth, 01033 kStrongCursorLineWidth, kStrongCursorColor); 01034 } 01035 01036 if (strong_x != weak_x ) 01037 { 01038 // Draw weak cursor. 01039 canvas->DrawLine(weak_x + kInnerBorderX + scroll_offset_x_, 01040 weak_y + kInnerBorderY + scroll_offset_y_, 01041 weak_x + kInnerBorderX + scroll_offset_x_, 01042 weak_y + weak_height + kInnerBorderY + scroll_offset_y_, 01043 kWeakCursorLineWidth, kWeakCursorColor); 01044 // Draw a small arror towards strong cursor 01045 if (weak_x > strong_x) 01046 { 01047 canvas->DrawLine( 01048 weak_x + kInnerBorderX + scroll_offset_x_ - kWeakCursorBarWidth, 01049 weak_y + kInnerBorderY + scroll_offset_y_ + kWeakCursorLineWidth, 01050 weak_x + kInnerBorderX + scroll_offset_x_, 01051 weak_y + kInnerBorderY + scroll_offset_y_ + kWeakCursorLineWidth, 01052 kWeakCursorLineWidth, kWeakCursorColor); 01053 } 01054 else 01055 { 01056 canvas->DrawLine( 01057 weak_x + kInnerBorderX + scroll_offset_x_, 01058 weak_y + kInnerBorderY + scroll_offset_y_ + kWeakCursorLineWidth, 01059 weak_x + kInnerBorderX + scroll_offset_x_ + kWeakCursorBarWidth, 01060 weak_y + kInnerBorderY + scroll_offset_y_ + kWeakCursorLineWidth, 01061 kWeakCursorLineWidth, kWeakCursorColor); 01062 } 01063 } 01064 } 01065 01066 Color TextEntry::GetSelectionBackgroundColor() 01067 { 01068 return kDefaultSelectionBackgroundColor; 01069 } 01070 01071 Color TextEntry::GetSelectionTextColor() 01072 { 01073 return kDefaultSelectionTextColor; 01074 } 01075 01076 void TextEntry::GetCursorLocationInLayout(int *strong_x, int *strong_y, 01077 int *strong_height, 01078 int *weak_x, int *weak_y, 01079 int *weak_height) 01080 { 01081 PangoLayout *layout = EnsureLayout(); 01082 int cursor_index = TextIndexToLayoutIndex(cursor_, true); 01083 01084 PangoRectangle strong, weak; 01085 pango_layout_get_cursor_pos(layout, cursor_index, &strong, &weak); 01086 01087 if (strong_x) 01088 *strong_x = PANGO_PIXELS(strong.x); 01089 if (strong_y) 01090 *strong_y = PANGO_PIXELS(strong.y); 01091 if (strong_height) 01092 *strong_height = PANGO_PIXELS(strong.height); 01093 if (weak_x) 01094 *weak_x = PANGO_PIXELS(weak.x); 01095 if (weak_y) 01096 *weak_y = PANGO_PIXELS(weak.y); 01097 if (weak_height) 01098 *weak_height = PANGO_PIXELS(weak.height); 01099 } 01100 01101 PangoLayout* TextEntry::EnsureLayout() 01102 { 01103 if (!cached_layout_) 01104 { 01105 cached_layout_ = CreateLayout(); 01106 } 01107 return cached_layout_; 01108 } 01109 01110 void TextEntry::QueueTextDraw() 01111 { 01112 if (content_modified_) 01113 { 01114 UpdateSelectionRegion(); 01115 UpdateCursorRegion(); 01116 QueueDraw(); //owner->QueueDraw(); 01117 content_modified_ = false; 01118 update_canvas_ = true; 01119 } 01120 else 01121 { 01122 if (selection_changed_) 01123 { 01124 UpdateSelectionRegion(); 01125 if (!last_selection_region_.empty()) 01126 QueueDraw(); //owner_->QueueDrawRegion(last_selection_region_); 01127 if (!selection_region_.empty()) 01128 QueueDraw(); //owner_->QueueDrawRegion(selection_region_); 01129 selection_changed_ = false; 01130 } 01131 if (cursor_moved_) 01132 { 01133 UpdateCursorRegion(); 01134 if (!last_cursor_region_.empty()) 01135 QueueDraw(); //owner_->QueueDrawRegion(last_cursor_region_); 01136 if (!cursor_region_.empty()) 01137 QueueDraw(); //owner_->QueueDrawRegion(cursor_region_); 01138 cursor_moved_ = false; 01139 } 01140 } 01141 } 01142 01143 void TextEntry::ResetLayout() 01144 { 01145 if (cached_layout_) 01146 { 01147 g_object_unref(cached_layout_); 01148 cached_layout_ = NULL; 01149 content_modified_ = true; 01150 } 01151 } 01152 01153 PangoLayout* TextEntry::CreateLayout() 01154 { 01155 // Creates the pango layout with a temporary canvas that is not zoomed. 01156 CairoGraphics *canvas = new CairoGraphics(CAIRO_FORMAT_ARGB32, 1, 1); 01157 PangoLayout *layout = pango_cairo_create_layout(canvas->GetInternalContext()); 01158 delete canvas; 01159 PangoAttrList *tmp_attrs = pango_attr_list_new(); 01160 std::string tmp_string; 01161 01162 /* Set necessary parameters */ 01163 pango_cairo_context_set_font_options (pango_layout_get_context (layout), 01164 font_options_); 01165 pango_cairo_context_set_resolution (pango_layout_get_context (layout), 01166 font_dpi_); 01167 01168 if (wrap_) 01169 { 01170 pango_layout_set_width(layout, (GetBaseWidth() - kInnerBorderX * 2) * PANGO_SCALE); 01171 pango_layout_set_wrap(layout, PANGO_WRAP_WORD_CHAR); 01172 } 01173 else 01174 { 01175 pango_layout_set_width(layout, -1); 01176 } 01177 01178 pango_layout_set_single_paragraph_mode(layout, !multiline_); 01179 01180 if (_preedit.length()) 01181 { 01182 size_t cursor_index = static_cast<size_t>(cursor_); 01183 size_t text_length = _text.length(); 01184 size_t preedit_length = _preedit.length(); 01185 if (visible_) 01186 { 01187 tmp_string = _text; 01188 tmp_string.insert(cursor_index, _preedit); 01189 } 01190 else 01191 { 01192 size_t nchars = g_utf8_strlen(_text.c_str(), text_length); 01193 size_t preedit_nchars = g_utf8_strlen(_preedit.c_str(), preedit_length); 01194 nchars += preedit_nchars; 01195 tmp_string.reserve(password_char_.length() * nchars); 01196 for (size_t i = 0; i < nchars; ++i) 01197 tmp_string.append(password_char_); 01198 size_t cursor_offset = 01199 g_utf8_pointer_to_offset(_text.c_str(), _text.c_str() + cursor_index); 01200 /* Fix cursor index and preedit_length */ 01201 cursor_index = cursor_offset * password_char_.length(); 01202 preedit_length = preedit_nchars * password_char_.length(); 01203 } 01204 if (preedit_attrs_) 01205 pango_attr_list_splice(tmp_attrs, preedit_attrs_, 01206 static_cast<int>(cursor_index), 01207 static_cast<int>(preedit_length)); 01208 } 01209 else 01210 { 01211 if(visible_) 01212 { 01213 tmp_string = _text; 01214 } 01215 else 01216 { 01217 size_t nchars = g_utf8_strlen(_text.c_str(), _text.length()); 01218 tmp_string.reserve(password_char_.length() * nchars); 01219 for (size_t i = 0; i < nchars; ++i) 01220 tmp_string.append(password_char_); 01221 } 01222 } 01223 01224 pango_layout_set_text(layout, tmp_string.c_str(), 01225 static_cast<int>(tmp_string.length())); 01226 01227 /* Set necessary attributes */ 01228 PangoAttribute *attr; 01229 if (underline_) 01230 { 01231 attr = pango_attr_underline_new(PANGO_UNDERLINE_SINGLE); 01232 attr->start_index = 0; 01233 attr->end_index = static_cast<guint>(tmp_string.length()); 01234 pango_attr_list_insert(tmp_attrs, attr); 01235 } 01236 if (strikeout_) 01237 { 01238 attr = pango_attr_strikethrough_new(TRUE); 01239 attr->start_index = 0; 01240 attr->end_index = static_cast<guint>(tmp_string.length()); 01241 pango_attr_list_insert(tmp_attrs, attr); 01242 } 01243 /* Set font desc */ 01244 { 01245 /* safe to down_cast here, because we know the actual implementation. */ 01246 CairoFont *font = new CairoFont ( 01247 font_family_.empty() ? kDefaultFontName : font_family_.c_str(), 01248 font_size_, 01249 italic_ ? CairoFont::STYLE_ITALIC : CairoFont::STYLE_NORMAL, 01250 bold_ ? CairoFont::WEIGHT_BOLD : CairoFont::WEIGHT_NORMAL); 01251 nuxAssert(font); 01252 attr = pango_attr_font_desc_new(font->GetFontDescription()); 01253 attr->start_index = 0; 01254 attr->end_index = static_cast<unsigned int>(tmp_string.length()); 01255 pango_attr_list_insert(tmp_attrs, attr); 01256 pango_layout_set_font_description (layout, font->GetFontDescription ()); 01257 font->Destroy(); 01258 } 01259 pango_layout_set_attributes(layout, tmp_attrs); 01260 pango_attr_list_unref(tmp_attrs); 01261 01262 /* Set alignment according to text direction. Only set layout's alignment 01263 * when it's not wrapped and in single line mode. 01264 */ 01265 if (!wrap_ && pango_layout_get_line_count(layout) <= 1 && 01266 align_ != CairoGraphics::ALIGN_CENTER) 01267 { 01268 PangoDirection dir; 01269 if (visible_) 01270 dir = pango_find_base_dir(tmp_string.c_str(), 01271 static_cast<int>(tmp_string.length())); 01272 else 01273 dir = PANGO_DIRECTION_NEUTRAL; 01274 01275 if (dir == PANGO_DIRECTION_NEUTRAL) 01276 { 01277 // GtkWidget *widget = GetWidgetAndCursorLocation(NULL); 01278 // if (widget && gtk_widget_get_direction(widget) == GTK_TEXT_DIR_RTL) 01279 // dir = PANGO_DIRECTION_RTL; 01280 // else 01281 // dir = PANGO_DIRECTION_LTR; 01282 01283 dir = PANGO_DIRECTION_LTR; 01284 } 01285 01286 // If wordWrap is false then "justify" alignment has no effect. 01287 PangoAlignment pango_align = (align_ == CairoGraphics::ALIGN_RIGHT ? 01288 PANGO_ALIGN_RIGHT : PANGO_ALIGN_LEFT); 01289 01290 // Invert the alignment if text direction is right to left. 01291 if (dir == PANGO_DIRECTION_RTL) 01292 { 01293 pango_align = (align_ == CairoGraphics::ALIGN_RIGHT ? 01294 PANGO_ALIGN_LEFT : PANGO_ALIGN_RIGHT); 01295 } 01296 01297 pango_layout_set_alignment(layout, pango_align); 01298 pango_layout_set_justify(layout, FALSE); 01299 } 01300 else if (align_ == CairoGraphics::ALIGN_JUSTIFY) 01301 { 01302 pango_layout_set_justify(layout, TRUE); 01303 pango_layout_set_alignment(layout, PANGO_ALIGN_LEFT); 01304 } 01305 else if (align_ == CairoGraphics::ALIGN_RIGHT) 01306 { 01307 pango_layout_set_justify(layout, FALSE); 01308 pango_layout_set_alignment(layout, PANGO_ALIGN_RIGHT); 01309 } 01310 else if (align_ == CairoGraphics::ALIGN_CENTER) 01311 { 01312 pango_layout_set_justify(layout, FALSE); 01313 pango_layout_set_alignment(layout, PANGO_ALIGN_CENTER); 01314 } 01315 else 01316 { 01317 pango_layout_set_justify(layout, FALSE); 01318 pango_layout_set_alignment(layout, PANGO_ALIGN_LEFT); 01319 } 01320 01321 { 01322 PangoContext *context; 01323 PangoFontMetrics *metrics; 01324 int ascent, descent; 01325 01326 context = pango_layout_get_context (layout); 01327 metrics = pango_context_get_metrics (context, 01328 pango_layout_get_font_description (layout), 01329 pango_context_get_language (context)); 01330 01331 ascent = pango_font_metrics_get_ascent (metrics); 01332 descent = pango_font_metrics_get_descent (metrics); 01333 01334 int full_height = PANGO_PIXELS (ascent + descent) + (kInnerBorderY * 2); 01335 SetMinimumHeight(full_height); 01336 01337 pango_font_metrics_unref (metrics); 01338 } 01339 01340 return layout; 01341 } 01342 01343 int TextEntry::TextIndexToLayoutIndex(int text_index, bool consider_preedit_cursor) 01344 { 01345 if (visible_) 01346 { 01347 if (text_index < cursor_) 01348 return text_index; 01349 01350 if (text_index == cursor_ && consider_preedit_cursor) 01351 return text_index + preedit_cursor_; 01352 01353 return text_index + static_cast<int>(_preedit.length()); 01354 } 01355 01356 const char *text = _text.c_str(); 01357 int offset = static_cast<int>( 01358 g_utf8_pointer_to_offset(text, text + text_index)); 01359 int preedit_offset = 0; 01360 int preedit_chars = 0; 01361 if (_preedit.length()) 01362 { 01363 const char *preedit_text = _preedit.c_str(); 01364 preedit_offset = static_cast<int>(g_utf8_pointer_to_offset( 01365 preedit_text, preedit_text + preedit_cursor_)); 01366 preedit_chars = static_cast<int>(g_utf8_strlen( 01367 preedit_text, _preedit.length())); 01368 } 01369 01370 int password_char_length = static_cast<int>(password_char_.length()); 01371 01372 if (text_index < cursor_) 01373 return offset * password_char_length; 01374 01375 if (text_index == cursor_ && consider_preedit_cursor) 01376 return (offset + preedit_offset) * password_char_length; 01377 01378 return (offset + preedit_chars) * password_char_length; 01379 } 01380 01381 01382 int TextEntry::LayoutIndexToTextIndex(int layout_index) 01383 { 01384 if (visible_) 01385 { 01386 if (layout_index < cursor_) 01387 return layout_index; 01388 01389 int preedit_length = static_cast<int>(_preedit.length()); 01390 if (layout_index >= cursor_ + preedit_length) 01391 return layout_index - preedit_length; 01392 01393 return cursor_; 01394 } 01395 01396 int password_char_length = static_cast<int>(password_char_.length()); 01397 nuxAssert(layout_index % password_char_length == 0); 01398 01399 int offset = layout_index / password_char_length; 01400 01401 const char *text = _text.c_str(); 01402 int cursor_offset = static_cast<int>( 01403 g_utf8_pointer_to_offset(text, text + cursor_)); 01404 int preedit_chars = static_cast<int>( 01405 g_utf8_strlen(_preedit.c_str(), _preedit.length())); 01406 01407 if (offset < cursor_offset) 01408 return static_cast<int>(g_utf8_offset_to_pointer(text, offset) - text); 01409 01410 if (offset >= cursor_offset + preedit_chars) 01411 return static_cast<int>( 01412 g_utf8_offset_to_pointer(text, offset - preedit_chars) - text); 01413 01414 return cursor_; 01415 } 01416 01417 int TextEntry::GetCharLength(int index) 01418 { 01419 const char *text = _text.c_str(); 01420 const char *ptr = text + index; 01421 const char *end = text + _text.length(); 01422 const char *next = g_utf8_find_next_char(ptr, end); 01423 return static_cast<int>(next ? static_cast<int>(next - ptr) : end - ptr); 01424 } 01425 01426 int TextEntry::GetPrevCharLength(int index) 01427 { 01428 const char *text = _text.c_str(); 01429 const char *ptr = text + index; 01430 const char *prev = g_utf8_find_prev_char(text, ptr); 01431 return static_cast<int>(prev ? static_cast<int>(ptr - prev) : ptr - text); 01432 } 01433 01434 void TextEntry::EnterText(const char *str) 01435 { 01436 if (readonly_ || !str || !*str) return; 01437 01438 if (GetSelectionBounds(NULL, NULL)) 01439 { 01440 DeleteSelection(); 01441 } 01442 else if (overwrite_ && cursor_ != static_cast<int>(_text.length())) 01443 { 01444 DeleteText(cursor_, cursor_ + GetCharLength(cursor_)); 01445 } 01446 01447 std::string tmp_text; 01448 if (!multiline_) 01449 { 01450 tmp_text = CleanupLineBreaks(str); 01451 str = tmp_text.c_str(); 01452 } 01453 01454 const char *end = NULL; 01455 g_utf8_validate(str, -1, &end); 01456 if (end > str) 01457 { 01458 size_t len = end - str; 01459 01460 _text.insert(cursor_, str, len); 01461 cursor_ += static_cast<int>(len); 01462 selection_bound_ += static_cast<int>(len); 01463 } 01464 01465 ResetLayout(); 01466 sigTextChanged.emit (this); 01467 } 01468 01469 void TextEntry::DeleteText(int start, int end) 01470 { 01471 if (readonly_) return; 01472 01473 int text_length = static_cast<int>(_text.length()); 01474 if (start < 0) 01475 start = 0; 01476 else if (start > text_length) 01477 start = text_length; 01478 01479 if (end < 0) 01480 end = 0; 01481 else if (end > text_length) 01482 end = text_length; 01483 01484 if (start > end) 01485 std::swap(start, end); 01486 else if (start == end) 01487 return; 01488 01489 _text.erase(start, end - start); 01490 01491 if (cursor_ >= end) 01492 cursor_ -= (end - start); 01493 if (selection_bound_ >= end) 01494 selection_bound_ -= (end - start); 01495 01496 ResetLayout(); 01497 sigTextChanged.emit (this); 01498 } 01499 01500 void TextEntry::SelectWord() 01501 { 01502 int selection_bound = MoveWords(cursor_, -1); 01503 int cursor = MoveWords(selection_bound, 1); 01504 SetSelectionBounds(selection_bound, cursor); 01505 } 01506 01507 void TextEntry::SelectLine() 01508 { 01509 int selection_bound = MoveLineEnds(cursor_, -1); 01510 int cursor = MoveLineEnds(selection_bound, 1); 01511 SetSelectionBounds(selection_bound, cursor); 01512 } 01513 01514 void TextEntry::Select(int start, int end) { 01515 int text_length = static_cast<int>(_text.length()); 01516 if (start == -1) 01517 start = text_length; 01518 if (end == -1) 01519 end = text_length; 01520 01521 start = Clamp(start, 0, text_length); 01522 end = Clamp(end, 0, text_length); 01523 SetSelectionBounds(start, end); 01524 QueueRefresh(false, true); 01525 } 01526 01527 void TextEntry::SelectAll() { 01528 SetSelectionBounds(0, static_cast<int>(_text.length())); 01529 QueueRefresh(false, true); 01530 } 01531 01532 CairoGraphics::Alignment TextEntry::GetAlign() const 01533 { 01534 return align_; 01535 } 01536 01537 void TextEntry::SetAlign(CairoGraphics::Alignment align) 01538 { 01539 align_ = align; 01540 QueueRefresh(true, true); 01541 } 01542 01543 void TextEntry::DeleteSelection() 01544 { 01545 int start, end; 01546 if (GetSelectionBounds(&start, &end)) 01547 DeleteText(start, end); 01548 } 01549 01550 void TextEntry::CopyClipboard() 01551 { 01552 // int start, end; 01553 // if (GetSelectionBounds(&start, &end)) 01554 // { 01555 // GtkWidget *widget = GetWidgetAndCursorLocation(NULL); 01556 // if (widget) 01557 // { 01558 // if (visible_) 01559 // { 01560 // gtk_clipboard_set_text( 01561 // gtk_widget_get_clipboard(widget, GDK_SELECTION_CLIPBOARD), 01562 // text_.c_str() + start, end - start); 01563 // } 01564 // else 01565 // { 01566 // // Don't copy real content if it's in invisible. 01567 // std::string content; 01568 // int nchars = static_cast<int>( 01569 // g_utf8_strlen(text_.c_str() + start, end - start)); 01570 // for (int i = 0; i < nchars; ++i) 01571 // content.append(password_char_); 01572 // gtk_clipboard_set_text( 01573 // gtk_widget_get_clipboard(widget, GDK_SELECTION_CLIPBOARD), 01574 // content.c_str(), static_cast<int>(content.length())); 01575 // } 01576 // } 01577 // } 01578 } 01579 01580 void TextEntry::CutClipboard() 01581 { 01582 CopyClipboard (); 01583 DeleteSelection (); 01584 } 01585 01586 void TextEntry::PasteClipboard() 01587 { 01588 // GtkWidget *widget = GetWidgetAndCursorLocation(NULL); 01589 // if (widget) 01590 // { 01591 // gtk_clipboard_request_text( 01592 // gtk_widget_get_clipboard(widget, GDK_SELECTION_CLIPBOARD), 01593 // PasteCallback, this); 01594 // } 01595 } 01596 01597 void TextEntry::BackSpace(MovementStep step) 01598 { 01599 if (GetSelectionBounds(NULL, NULL)) 01600 { 01601 DeleteSelection(); 01602 } 01603 else 01604 { 01605 if (cursor_ == 0) 01606 return; 01607 if (step == VISUALLY) 01608 { 01609 DeleteText(cursor_ - GetPrevCharLength(cursor_), cursor_); 01610 } 01611 else if (step == WORDS) 01612 { 01613 int new_cursor; 01614 new_cursor = MoveWords(cursor_, -1); 01615 DeleteText(new_cursor, cursor_); 01616 } 01617 } 01618 } 01619 01620 void TextEntry::Delete(MovementStep step) 01621 { 01622 if (GetSelectionBounds(NULL, NULL)) 01623 { 01624 DeleteSelection(); 01625 } 01626 else 01627 { 01628 if (cursor_ == static_cast<int>(_text.length())) 01629 return; 01630 if (step == VISUALLY) 01631 { 01632 DeleteText(cursor_, cursor_ + GetCharLength(cursor_)); 01633 } 01634 else if (step == WORDS) 01635 { 01636 int new_cursor; 01637 new_cursor = MoveWords(cursor_, 1); 01638 DeleteText(cursor_, new_cursor); 01639 } 01640 } 01641 } 01642 01643 void TextEntry::ToggleOverwrite() 01644 { 01645 overwrite_ = !overwrite_; 01646 } 01647 01648 void TextEntry::UpdateSelectionRegion() 01649 { 01650 selection_region_.clear(); 01651 01652 // Selection in a single line may be not continual, so we use pango to 01653 // get the x-ranges of each selection range in one line, and draw them 01654 // separately. 01655 int start_index, end_index; 01656 if (GetSelectionBounds(&start_index, &end_index)) 01657 { 01658 PangoLayout *layout = EnsureLayout(); 01659 PangoRectangle line_extents, pos; 01660 int draw_start, draw_end; 01661 int *ranges; 01662 int n_ranges; 01663 int n_lines = pango_layout_get_line_count(layout); 01664 01665 start_index = TextIndexToLayoutIndex(start_index, false); 01666 end_index = TextIndexToLayoutIndex(end_index, false); 01667 01668 for(int line_index = 0; line_index < n_lines; ++line_index) 01669 { 01670 #if PANGO_VERSION_CHECK(1,16,0) 01671 PangoLayoutLine *line = pango_layout_get_line_readonly(layout, line_index); 01672 #else 01673 PangoLayoutLine *line = pango_layout_get_line(layout, line_index); 01674 #endif 01675 if (line->start_index + line->length < start_index) 01676 continue; 01677 if (end_index < line->start_index) 01678 break; 01679 draw_start = Max<int>(start_index, line->start_index); 01680 draw_end = Min<int>(end_index, line->start_index + line->length); 01681 pango_layout_line_get_x_ranges(line, draw_start, draw_end, 01682 &ranges, &n_ranges); 01683 pango_layout_line_get_pixel_extents(line, NULL, &line_extents); 01684 pango_layout_index_to_pos(layout, line->start_index, &pos); 01685 for(int i = 0; i < n_ranges; ++i) 01686 { 01687 selection_region_.push_back(Rect( 01688 kInnerBorderX + scroll_offset_x_ + PANGO_PIXELS(ranges[i * 2]), 01689 kInnerBorderY + scroll_offset_y_ + PANGO_PIXELS(pos.y), 01690 PANGO_PIXELS(ranges[i * 2 + 1] - ranges[i * 2]), 01691 line_extents.height)); 01692 } 01693 g_free(ranges); 01694 } 01695 } 01696 } 01697 01698 void TextEntry::MoveCursor(MovementStep step, int count, bool extend_selection) 01699 { 01700 ResetImContext(); 01701 int new_cursor = 0; 01702 // Clear selection first if not extend it. 01703 if (!extend_selection) 01704 { 01705 selection_changed_ = true; 01706 cursor_moved_ = true; 01707 selection_bound_ = cursor_; 01708 cursor_moved.emit (cursor_); 01709 } 01710 01711 // Calculate the new offset after motion. 01712 switch(step) 01713 { 01714 case VISUALLY: 01715 new_cursor = MoveVisually(cursor_, count); 01716 break; 01717 case WORDS: 01718 new_cursor = MoveWords(cursor_, count); 01719 break; 01720 case DISPLAY_LINES: 01721 new_cursor = MoveDisplayLines(cursor_, count); 01722 break; 01723 case DISPLAY_LINE_ENDS: 01724 new_cursor = MoveLineEnds(cursor_, count); 01725 break; 01726 case PAGES: 01727 new_cursor = MovePages(cursor_, count); 01728 break; 01729 case BUFFER: 01730 nuxAssert(count == -1 || count == 1); 01731 new_cursor = static_cast<int>(count == -1 ? 0 : _text.length()); 01732 break; 01733 } 01734 01735 if (extend_selection) 01736 SetSelectionBounds(selection_bound_, new_cursor); 01737 else 01738 SetCursor(new_cursor); 01739 01740 QueueRefresh(true, true); 01741 } 01742 01743 int TextEntry::MoveVisually(int current_index, int count) 01744 { 01745 nuxAssert(current_index >= 0 && 01746 current_index <= static_cast<int>(_text.length())); 01747 nuxAssert(count); 01748 nuxAssert(_preedit.length() == 0); 01749 01750 PangoLayout *layout = EnsureLayout(); 01751 const char *text = pango_layout_get_text(layout); 01752 int index = TextIndexToLayoutIndex(current_index, false); 01753 int new_index = 0; 01754 int new_trailing = 0; 01755 while (count != 0) 01756 { 01757 if (count > 0) 01758 { 01759 --count; 01760 pango_layout_move_cursor_visually(layout, true, index, 0, 1, 01761 &new_index, &new_trailing); 01762 } 01763 else if (count < 0) 01764 { 01765 ++count; 01766 pango_layout_move_cursor_visually(layout, true, index, 0, -1, 01767 &new_index, &new_trailing); 01768 } 01769 index = new_index; 01770 if (index < 0 || index == G_MAXINT) 01771 return current_index; 01772 index = static_cast<int>(g_utf8_offset_to_pointer(text + index, new_trailing) - text); 01773 } 01774 return LayoutIndexToTextIndex(index); 01775 } 01776 01777 int TextEntry::MoveWords(int current_index, int count) 01778 { 01779 nuxAssert(current_index >= 0 && 01780 current_index <= static_cast<int>(_text.length())); 01781 nuxAssert(count); 01782 nuxAssert(_preedit.length() == 0); 01783 01784 if (!visible_) 01785 { 01786 return static_cast<int>(count > 0 ? _text.length() : 0); 01787 } 01788 01789 // The cursor movement direction shall be determined by the direction of 01790 // current text line. 01791 PangoLayout *layout = EnsureLayout(); 01792 int n_log_attrs; 01793 PangoLogAttr *log_attrs; 01794 pango_layout_get_log_attrs (layout, &log_attrs, &n_log_attrs); 01795 const char *text = pango_layout_get_text(layout); 01796 int index = TextIndexToLayoutIndex(current_index, false); 01797 int line_index; 01798 pango_layout_index_to_line_x(layout, index, FALSE, &line_index, NULL); 01799 01800 // Weird bug: line_index here may be >= than line count? 01801 int line_count = pango_layout_get_line_count(layout); 01802 if (line_index >= line_count) 01803 { 01804 line_index = line_count - 1; 01805 } 01806 01807 #if PANGO_VERSION_CHECK(1,16,0) 01808 PangoLayoutLine *line = pango_layout_get_line_readonly(layout, line_index); 01809 #else 01810 PangoLayoutLine *line = pango_layout_get_line(layout, line_index); 01811 #endif 01812 bool rtl = (line->resolved_dir == PANGO_DIRECTION_RTL); 01813 const char *ptr = text + index; 01814 int offset = static_cast<int>(g_utf8_pointer_to_offset(text, ptr)); 01815 while (count != 0) 01816 { 01817 if (((rtl && count < 0) || (!rtl && count > 0)) && *ptr) 01818 { 01819 if (log_attrs[offset].is_white) 01820 { 01821 while (ptr && *ptr && log_attrs[offset].is_white) 01822 { 01823 ptr = g_utf8_find_next_char(ptr, NULL); 01824 ++offset; 01825 } 01826 } 01827 else 01828 { 01829 if (ptr && *ptr) 01830 { 01831 ptr = g_utf8_find_next_char(ptr, NULL); 01832 ++offset; 01833 } 01834 } 01835 while (ptr && *ptr) 01836 { 01837 ptr = g_utf8_find_next_char(ptr, NULL); 01838 ++offset; 01839 if (log_attrs[offset].is_word_start || log_attrs[offset].is_word_end) 01840 break; 01841 } 01842 if (!ptr) 01843 { 01844 ptr = text; 01845 while (*ptr) ++ptr; 01846 } 01847 } 01848 else if (((rtl && count > 0) || (!rtl && count < 0)) && (ptr > text)) 01849 { 01850 if (offset > 0 && log_attrs[offset - 1].is_white) 01851 { 01852 while (ptr && offset > 0 && log_attrs[offset - 1].is_white) 01853 { 01854 ptr = g_utf8_find_prev_char(text, ptr); 01855 --offset; 01856 } 01857 } 01858 else 01859 { 01860 if (ptr) 01861 { 01862 ptr = g_utf8_find_prev_char(text, ptr); 01863 --offset; 01864 } 01865 } 01866 while (ptr /*&& *ptr*/) //fix: when at the end of the string, allow ctrl+left arrow to move backward to the start/end of the previous word. 01867 { 01868 ptr = g_utf8_find_prev_char(text, ptr); 01869 --offset; 01870 if (log_attrs[offset].is_word_start || log_attrs[offset].is_word_end) 01871 break; 01872 } 01873 if (!ptr) 01874 ptr = text; 01875 } 01876 else 01877 { 01878 break; 01879 } 01880 if (count > 0) 01881 --count; 01882 else 01883 ++count; 01884 } 01885 return LayoutIndexToTextIndex(static_cast<int>(ptr - text)); 01886 } 01887 01888 int TextEntry::MoveDisplayLines(int current_index, int count) 01889 { 01890 nuxAssert(current_index >= 0 && 01891 current_index <= static_cast<int>(_text.length())); 01892 nuxAssert(count); 01893 nuxAssert(_preedit.length() == 0); 01894 01895 PangoLayout *layout = EnsureLayout(); 01896 const char *text = pango_layout_get_text(layout); 01897 int index = TextIndexToLayoutIndex(current_index, false); 01898 int n_lines = pango_layout_get_line_count(layout); 01899 int line_index = 0; 01900 int x_off = 0; 01901 PangoRectangle rect; 01902 01903 // Find the current cursor X position in layout 01904 pango_layout_index_to_line_x(layout, index, FALSE, &line_index, &x_off); 01905 01906 // Weird bug: line_index here may be >= than line count? 01907 if (line_index >= n_lines) 01908 { 01909 line_index = n_lines - 1; 01910 } 01911 01912 pango_layout_get_cursor_pos(layout, index, &rect, NULL); 01913 x_off = rect.x; 01914 01915 line_index += count; 01916 01917 if (line_index < 0) 01918 { 01919 return 0; 01920 } 01921 else if (line_index >= n_lines) 01922 { 01923 return static_cast<int>(_text.length()); 01924 } 01925 01926 int trailing; 01927 #if PANGO_VERSION_CHECK(1,16,0) 01928 PangoLayoutLine *line = pango_layout_get_line_readonly(layout, line_index); 01929 #else 01930 PangoLayoutLine *line = pango_layout_get_line(layout, line_index); 01931 #endif 01932 // Find out the cursor x offset related to the new line position. 01933 if (line->resolved_dir == PANGO_DIRECTION_RTL) 01934 { 01935 pango_layout_get_cursor_pos(layout, line->start_index + line->length, 01936 &rect, NULL); 01937 } 01938 else 01939 { 01940 pango_layout_get_cursor_pos(layout, line->start_index, &rect, NULL); 01941 } 01942 01943 // rect.x is the left edge position of the line in the layout 01944 x_off -= rect.x; 01945 if (x_off < 0) x_off = 0; 01946 pango_layout_line_x_to_index(line, x_off, &index, &trailing); 01947 01948 index = static_cast<int>(g_utf8_offset_to_pointer(text + index, trailing) - text); 01949 return LayoutIndexToTextIndex(index); 01950 } 01951 01952 int TextEntry::MovePages(int current_index, int count) 01953 { 01954 nuxAssert(current_index >= 0 && 01955 current_index <= static_cast<int>(_text.length())); 01956 nuxAssert(count); 01957 nuxAssert(_preedit.length() == 0); 01958 01959 // Transfer pages to display lines. 01960 PangoLayout *layout = EnsureLayout(); 01961 int layout_height; 01962 pango_layout_get_pixel_size(layout, NULL, &layout_height); 01963 int n_lines = pango_layout_get_line_count(layout); 01964 int line_height = layout_height / n_lines; 01965 int page_lines = (GetBaseHeight () - kInnerBorderY * 2) / line_height; 01966 return MoveDisplayLines(current_index, count * page_lines); 01967 } 01968 01969 int TextEntry::MoveLineEnds(int current_index, int count) 01970 { 01971 nuxAssert(current_index >= 0 && 01972 current_index <= static_cast<int>(_text.length())); 01973 nuxAssert(count); 01974 nuxAssert(_preedit.length() == 0); 01975 01976 PangoLayout *layout = EnsureLayout(); 01977 int index = TextIndexToLayoutIndex(current_index, false); 01978 int line_index = 0; 01979 01980 // Find current line 01981 pango_layout_index_to_line_x(layout, index, FALSE, &line_index, NULL); 01982 01983 // Weird bug: line_index here may be >= than line count? 01984 int line_count = pango_layout_get_line_count(layout); 01985 if (line_index >= line_count) 01986 { 01987 line_index = line_count - 1; 01988 } 01989 01990 // #if PANGO_VERSION_CHECK(1,16,0) 01991 // PangoLayoutLine *line = pango_layout_get_line_readonly(layout, line_index); 01992 // #else 01993 PangoLayoutLine *line = pango_layout_get_line(layout, line_index); 01994 // #endif 01995 01996 if (line->length == 0) 01997 return current_index; 01998 01999 if ((line->resolved_dir == PANGO_DIRECTION_RTL && count < 0) || 02000 (line->resolved_dir != PANGO_DIRECTION_RTL && count > 0)) 02001 { 02002 index = line->start_index + line->length; 02003 } 02004 else 02005 { 02006 index = line->start_index; 02007 } 02008 return LayoutIndexToTextIndex(index); 02009 } 02010 02011 void TextEntry::SetCursor(int cursor) 02012 { 02013 if (cursor != cursor_) 02014 { 02015 ResetImContext(); 02016 // If there was a selection range, then the selection range will be cleared. 02017 // Then content_modified_ shall be set to true to force redrawing the text. 02018 if (cursor_ != selection_bound_) 02019 selection_changed_ = true; 02020 cursor_ = cursor; 02021 selection_bound_ = cursor; 02022 cursor_moved_ = true; 02023 02024 cursor_moved.emit (cursor); 02025 } 02026 } 02027 02028 int TextEntry::XYToTextIndex(int x, int y) 02029 { 02030 int width, height; 02031 PangoLayout *layout = EnsureLayout(); 02032 const char *text = pango_layout_get_text(layout); 02033 pango_layout_get_pixel_size(layout, &width, &height); 02034 02035 if (y < 0) 02036 { 02037 return 0; 02038 } 02039 else if (y >= height) 02040 { 02041 return static_cast<int>(_text.length()); 02042 } 02043 02044 int trailing; 02045 int index; 02046 pango_layout_xy_to_index(layout, x * PANGO_SCALE, y * PANGO_SCALE, 02047 &index, &trailing); 02048 index = static_cast<int>( 02049 g_utf8_offset_to_pointer(text + index, trailing) - text); 02050 02051 index = LayoutIndexToTextIndex(index); 02052 02053 // Adjust the offset if preedit is not empty and if the offset is after 02054 // current cursor. 02055 int preedit_length = static_cast<int>(_preedit.length()); 02056 if (preedit_length && index > cursor_) 02057 { 02058 if (index >= cursor_ + preedit_length) 02059 index -= preedit_length; 02060 else 02061 index = cursor_; 02062 } 02063 return Clamp(index, 0, static_cast<int>(_text.length())); 02064 } 02065 02066 bool TextEntry::GetSelectionBounds(int *start, int *end) 02067 { 02068 if (start) 02069 *start = Min<int>(selection_bound_, cursor_); 02070 if (end) 02071 *end = Max<int>(selection_bound_, cursor_); 02072 02073 return(selection_bound_ != cursor_); 02074 } 02075 02076 void TextEntry::SetSelectionBounds(int selection_bound, int cursor) 02077 { 02078 if (selection_bound_ != selection_bound || cursor_ != cursor) 02079 { 02080 selection_changed_ = true; 02081 selection_bound_ = selection_bound; 02082 if (cursor_ != cursor) 02083 { 02084 cursor_ = cursor; 02085 cursor_moved_ = true; 02086 cursor_moved.emit (cursor); 02087 } 02088 02089 //ResetImContext(); 02090 } 02091 } 02092 02093 void TextEntry::SetFontFamily (const char *font) 02094 { 02095 font_family_ = font; 02096 QueueRefresh(true, true); 02097 } 02098 02099 void TextEntry::SetFontSize (double font_size) 02100 { 02101 font_size_ = font_size; 02102 QueueRefresh(true, true); 02103 } 02104 02105 void TextEntry::SetFontOptions (const cairo_font_options_t *options) 02106 { 02107 g_return_if_fail (options); 02108 02109 cairo_font_options_destroy (font_options_); 02110 font_options_ = cairo_font_options_copy (options); 02111 02112 QueueRefresh(true, true); 02113 } 02114 02115 bool TextEntry::InspectKeyEvent(unsigned int eventType, 02116 unsigned int key_sym, 02117 const char* character) 02118 { 02119 if ((eventType == NUX_KEYDOWN) && (key_nav_mode_ == true) && (text_input_mode_ == false)) 02120 { 02121 if (key_sym == NUX_VK_ENTER || 02122 key_sym == NUX_KP_ENTER || 02123 key_sym == NUX_VK_UP || 02124 key_sym == NUX_VK_DOWN || 02125 key_sym == NUX_VK_LEFT || 02126 key_sym == NUX_VK_RIGHT || 02127 key_sym == NUX_VK_LEFT_TAB || 02128 key_sym == NUX_VK_TAB || 02129 key_sym == NUX_VK_ESCAPE) 02130 { 02131 return false; 02132 } 02133 } 02134 02135 if ((eventType == NUX_KEYDOWN) && (key_nav_mode_ == true) && (text_input_mode_ == true)) 02136 { 02137 // Enable to exit the TextEntry when in write mode (hack for unity dash) 02138 if (key_sym == NUX_VK_UP || 02139 key_sym == NUX_VK_DOWN || 02140 key_sym == NUX_VK_ESCAPE) 02141 { 02142 return false; 02143 } 02144 } 02145 02146 if ((eventType == NUX_KEYDOWN) && (key_nav_mode_ == false) && (text_input_mode_ == false)) 02147 { 02148 return false; 02149 } 02150 02151 return true; 02152 } 02153 }