nux-1.16.0
TextEntry.cpp
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 }
 All Classes Namespaces Functions Variables Typedefs Enumerations Enumerator Friends