/** * Yudit Unicode Editor Source File * * GNU Copyright (C) 1997-2006 Gaspar Sinai * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License, version 2, * dated June 1991. See file COPYYING for details. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. */ #include "swidget/STextEdit.h" #include "stoolkit/SEncoder.h" SDClick::SDClick (void) { clickCount = 0; timer = 0; } SDClick::~SDClick () { if (timer) delete timer; } void SDClick::start (unsigned int millisec) { if (timer == 0) { timer = STimer::newTimer(300, this); clickCount = 0; } else { clickCount++; } } /** * The timer event */ bool SDClick::timeout (const SEventSource* s) { if (timer == 0) { fprintf (stderr, "strange. timeout..\n"); return false; } if (clickCount == 1) return true; /* again */ clickCount = 0; delete timer; timer = 0; return false; /* don't call me again */ } #define SS_LEFT_MARGIN 2 /** * A text area is an editable area of text. * This one creates one with empty text. */ STextEdit::STextEdit (void) : textView ("") { currentHistorySize = 0; historySize = 0; sliderListener = 0; yuditInput = 0; editable = true; textView.setEditable(true); focused = false; selecting = false; editor.setInterface (this); column = 0; resized = false; statusHeight = 0; textView.move (SLocation((int)border.getBorderSize().width + SS_LEFT_MARGIN, (int)border.getBorderSize().height)); clip (true); textView.setWindowInterface (this); caret.resize (SDimension(textView.lineHeight, textView.lineHeight)); updateCaretPosition (); updateCaretLocation (); caret.setWindowInterface (this); } bool STextEdit::isFocused () const { return focused; } /** * Deletes a text area. */ STextEdit::~STextEdit () { if (yuditInput) { //fprintf (stderr, "deleting yuditinput\n"); delete yuditInput; } } /** * Create a text area with pre-set utf8 text. * @param utf8 is the initial text. */ STextEdit::STextEdit (const SString& utf8) : textView (utf8) { currentHistorySize = 0; historySize = 0; sliderListener = 0; yuditInput = 0; statusHeight = 0; editable = true; textView.setEditable(true); focused = false; resized = false; editor.setInterface (this); textView.textData.move (STextIndex(0,0)); updateCaretPosition (); selecting = false; column = 0; textView.move (SLocation((int)border.getBorderSize().width + SS_LEFT_MARGIN, (int)border.getBorderSize().height)); clip (true); textView.textData.fireEvent (); textView.setWindowInterface (this); caret.resize (SDimension(textView.lineHeight, textView.lineHeight)); updateCaretLocation (); caret.setWindowInterface (this); } /** * Set the editor that works on this widget. */ void STextEdit::setEditor (const SEditor& _editor) { editor = _editor; if (yuditInput) yuditInput->clear(); endSelect(); deselectText(); editor.setInterface (this); } /** * Add a text edit listener to the list. * @param ls is the listener to be added. */ void STextEdit::addTextEditLS (STextEditLS* ls) { listeners.append (ls); } /** * Remove the text edit listener from the list. * @param ls is the listener to be removed. */ void STextEdit::removeTextEditLS (STextEditLS* ls) { for (unsigned int i=0; iisVisible()) { setInputMethod(imWaiting); } } /** * Turn clipping at the border on and off. * @param on is true if we turn on clipping. */ void STextEdit::clip (bool on) { if (!on) { window->removeClippingArea (); textView.setClippingArea (0,0,0,0); return; } window->setClippingArea ( (int) border.getBorderSize().width , (int) border.getBorderSize().height, size.width - 2 * border.getBorderSize().width, size.height - 2 * border.getBorderSize().height); textView.setClippingArea ( (int) border.getBorderSize().width , (int) border.getBorderSize().height, size.width - 2 * border.getBorderSize().width, size.height - 2 * border.getBorderSize().height); } /** * Pass this to the editor. */ void STextEdit::keyPressed (SWindow * w, SKey key, const SString& s, bool ctrl, bool shift, bool meta) { /* fprintf (stderr, "try: %*.*s %*.*s %*.*s\n", SSARGS(window->getInputMethod()), SSARGS(inputMethod), SSARGS(s)); */ if (yuditInput) { yuditInput->keyPressed (key, s, ctrl, shift, meta); } else if ( (window->getInputMethod() == "x-ascii" || window->getInputMethod() == "") && inputMethod == SS_KINPUT2_IM && shift == true && s == " ") { //setInputMethod (SS_KINPUT2_IM); if (!window->startInputMethod ("_JAPANESE_CONVERSION",getProperties())) { fprintf (stderr, "Can not start kinput2 (_JAPANESE_CONVERSION) input method.\n"); } } else { editor.keyPressed (key, s, ctrl, shift, meta); } } /** * Pass this to the editor. */ void STextEdit::keyReleased (SWindow * w, SKey key, const SString& s, bool ctrl, bool shift, bool meta) { //if (!yuditInput) editor.keyReleased (key, s, ctrl, shift, meta); /* end selection needs this. */ editor.keyReleased (key, s, ctrl, shift, meta); } /** * Pass this to the editor. Start a timer to measure * the double click. Get the focus. */ void STextEdit::buttonPressed (SWindow * w, int button, int x, int y) { if (yuditInput) yuditInput->clear(); window->getKeyboardFocus(); SCursorIndex textIndex = textView.getCursorIndex (SLocation (x, y)); setFocus (); /* double click check */ clicks.start (500); editor.buttonPressed (button, textIndex); } /** * A button was released. */ void STextEdit::buttonReleased (SWindow * w, int button, int x, int y) { if (yuditInput) yuditInput->clear(); editor.buttonReleased (button, textView.getCursorIndex (SLocation (x, y))); if (clicks.clickCount == 1) { editor.multiClicked (button, textView.getCursorIndex (SLocation (x, y)), 2); } else if (clicks.clickCount == 2) { editor.multiClicked (button, textView.getCursorIndex (SLocation (x, y)), 3); } } void STextEdit::buttonDragged (SWindow * w, int button, int x, int y) { if (yuditInput) yuditInput->clear(); editor.buttonDragged (button, textView.getCursorIndex (SLocation (x, y))); } /** * Keyboard focus lost. * In some environment this is called *after* the * mouse clicked is called. This results in some serious problem: * you can not select text with the mouse if * if (isSelecting()) * { * endSelect (); * } * is present. */ void STextEdit::lostKeyboardFocus (SWindow* w) { focused = false; caret.animate (false); for (unsigned int i=0; ifocusChanged (this, false); } } /** * Text has gone from clipboard. */ void STextEdit::lostClipSelection (SWindow* w) { deselectText(); } /** * keyboard focus lost. */ void STextEdit::gainedKeyboardFocus (SWindow* w) { focused = true; if (editable) { caret.animate (true); } for (unsigned int i=0; ifocusChanged (this, true); } } /** * Resize the component * @param d is the new size */ void STextEdit::resize(const SDimension& d) { if (getSize() == d) return; SPanel::resize (d); border.resize (d); resizeTextView(false); } void STextEdit::resizeTextView(bool scroll) { SDimension td = size; if (size.width + 2 * SS_LEFT_MARGIN> border.getBorderSize().width * 2) { td.width = size.width - border.getBorderSize().width * 2 - 2 * SS_LEFT_MARGIN; } if (size.height > border.getBorderSize().height * 2) { td.height = size.height - border.getBorderSize().height * 2; if (td.height > statusHeight) { td.height = td.height - statusHeight; } if (scroll) { window->redraw (true, 0, 0, size.width, size.height); } } clip (true); textView.resize (td); resized = true; } /** * Move the component * @param l is the new location */ void STextEdit::move(const SLocation& l) { SPanel::move (l); } /** * getPreferredSize. * This is calculated form the preferred size of the textView and * adding the border size to it. */ const SDimension& STextEdit::getPreferredSize() { preferredSize = textView.getPreferredSize(); SDimension d = border.getBorderSize(); preferredSize.width += d.width * 2; preferredSize.height += d.height * 2; return preferredSize; } /** * Gain keyboard focus * @return true if focus could be gained. */ void STextEdit::setFocus () { window->getKeyboardFocus(); } /** * Scroll the area up or down. * Move text up - * Move text down + * @param value is positive if up. * @param value is negative is down. */ void STextEdit::scrollVertical (int value, bool notify) { if (value ==0) return; int eheight = (int)size.height - (int)2 * border.getBorderSize().height; if (eheight <= 0) return; int ewidth = (int)size.width - (int)2 * border.getBorderSize().width; if (ewidth <= 0) return; SLocation v = textView.getViewPort(); v.y = v.y + value; textView.setViewPort (v); /* outof range. */ if ((value < 0 && -value >= eheight) || (value > 0 && value >= eheight)) { window->redraw (true, (int) border.getBorderSize().width , (int) border.getBorderSize().height, ewidth, eheight); } /* copy the area and redraw the rest */ else if (value < 0) { value = -value; /* copy up */ window->copy ((int) border.getBorderSize().width, value + (int) border.getBorderSize().height, ewidth, eheight-value, border.getBorderSize().width, border.getBorderSize().height); /* redraw down 5 + 4 oeverdraw */ window->redraw (true, (int) border.getBorderSize().width , (int) border.getBorderSize().height + eheight - value - 5, ewidth, value + 9); } else { /* copy down */ window->copy ((int) border.getBorderSize().width, (int) border.getBorderSize().height, ewidth, eheight-value, border.getBorderSize().width, (int) border.getBorderSize().height + value); /* redraw up */ window->redraw (true, (int) border.getBorderSize().width , (int) border.getBorderSize().height, ewidth, value + 4); } if (notify) { notifySlider(); } } void STextEdit::scrollHorizontal (int value, bool notify) { if (value ==0) return; SLocation v = textView.getViewPort(); v.x = v.x + value; textView.setViewPort (v); int eheight = (int)size.height - (int)2 * border.getBorderSize().height; if (eheight <= 0) return; int ewidth = (int)size.width - (int)2 * border.getBorderSize().width + 2 * SS_LEFT_MARGIN; if (ewidth <= 0) return; /* outof range. */ if ((value < 0 && -value >= ewidth) || (value > 0 && value >= ewidth)) { window->redraw (true, (int) border.getBorderSize().width , (int) border.getBorderSize().height, ewidth, eheight); } /* copy the area and redraw the rest */ else if (value < 0) { value = -value; /* copy up */ window->copy (value + (int) border.getBorderSize().width, (int) border.getBorderSize().height, ewidth-value, eheight, border.getBorderSize().width, border.getBorderSize().height); /* redraw down 2+2 overdraw */ window->redraw (true, (int) ewidth-value - 2, (int) border.getBorderSize().height, value + 4, eheight); } else { /* copy down */ window->copy ((int) border.getBorderSize().width, (int) border.getBorderSize().height, ewidth-value, eheight, border.getBorderSize().width + value, (int) border.getBorderSize().height); /* redraw up + 4 overdraw */ window->redraw (true, (int) border.getBorderSize().width , (int) border.getBorderSize().height, value + 4, ewidth); } if (notify) { notifySlider(); } } /** * Shift the text, and caret so that caret is always visible. */ void STextEdit::setCaretLocationVisible () { if (isMultiline()) { setCaretVisibleVertical (); } else { setCaretVisibleHorizontal (); } } /** * Do the vertical part of caret visible trickies. */ void STextEdit::setCaretVisibleVertical() { /* * I dont like this type of scrolling... It also * has a bug ath the end of the page. */ SDimension ts = textView.getSize(); /* It happens before resize. */ if (ts.height==0) return; SLocation tb = textView.getLocation(); /* t=text-widget c=caret tf=text-full*/ SCursorIndex ci = caret.getCursorIndex(); SLocation cl = textView.getCursorLocation(ci); /* this is negative for scrolls to left */ SLocation viewp = textView.getViewPort(); /* do we need to move ? */ int movey = 0; if (cl.y + (int)textView.lineHeight > (int)ts.height + tb.y) { /* negative - left */ movey = (int) ts.height + tb.y - cl.y - (int) textView.lineHeight; } else if (cl.y < tb.y) { /* positive - right */ movey = tb.y - cl.y; } if (viewp.y + movey > 0) movey = -viewp.y; /* fits one page */ if (viewp.y + movey < -((int)textView.getDocumentHeight() - (int)ts.height)) { movey = -(int)textView.getDocumentHeight()-viewp.y+(int)ts.height; } /* check overscroll */ if (textView.getDocumentHeight() <= ts.height) { movey = -viewp.y; } if (movey!=0) { scrollVertical (movey, true); } } /** * Do the vertical part of caret visible trickies. */ void STextEdit::centerCaretVertical() { /* t=text-widget c=caret tf=text-full*/ SLocation cb = caret.getLocation(); int delta = ((int)(textView.getSize().height/2))-cb.y; /* it would make the first line appear down below */ if (textView.getViewPort().y + delta >= 0) { delta = - textView.getViewPort().y; } /* it would make the last line appear up below */ else if (textView.getViewPort().y + delta + (int)textView.getDocumentHeight() < (int)textView.getSize().height) { delta = ((int)textView.getSize().height) - textView.getViewPort().y - (int)textView.getDocumentHeight(); /* one page text may over-request. */ if (textView.getViewPort().y + delta >= 0) { delta = 0; } } if (delta != 0) { scrollVertical (delta, true); /* move the caret silently */ updateCaretPosition (); updateCaretLocation (false); window->setInputMethodProperties (getProperties()); } return; } /** * Do the horizontal part of caret visible trickies. */ void STextEdit::setCaretVisibleHorizontal() { SDimension ts = textView.getSize(); /* It happens before resize. */ if (ts.width == 0) return; SLocation tb = textView.getLocation(); /* t=text-widget c=caret tf=text-full*/ SCursorIndex ci = caret.getCursorIndex(); SLocation cl = textView.getCursorLocation(ci); bool islr = textView.textData.isLR(ci.textIndex.line); /* this is negative for scrolls to left */ SLocation viewp = textView.getViewPort(); /* do we need to move ? */ int movex = 0; if (islr) { if (cl.x >= 8 * (int)ts.width/ 10 + tb.x) { /* negative - left */ movex = (6 * (int) ts.width / 10 + tb.x) - cl.x; } else if (cl.x <= 2 * ((int)ts.width) / 10 + tb.x) { /* positive - right */ movex = (4 * ((int) ts.width) / 10 + tb.x) - cl.x; } if (viewp.x + movex > 0) movex = -viewp.x; if (movex!=0) { scrollHorizontal (movex, true); } } else /* no support for scrolling yet */ { #if 0 /* if (cl.x <= 6 * (int)ts.width/ 10 + tb.x) { movex = (8 * (int) ts.width / 10 + tb.x) - cl.x; } else if (cl.x >= 4 * ((int)ts.width) / 10 + tb.x) { movex = (2 * ((int) ts.width) / 10 + tb.x) - cl.x; } if (viewp.x + movex > 0) movex = -viewp.x; if (movex!=0) { scrollHorizontal (movex, true); } */ #endif if (viewp.x != 0) { scrollHorizontal (-viewp.x, true); } } return; } /** * If show <- newline characters * @param lineend is true if lineend is shown. */ void STextEdit::setLineEndMark (bool lineend) { textView.setLineEndMark (lineend); resized = true; } /** * Is new line shown? * @return true if newline characters are shown. */ bool STextEdit::getLineEndMark () const { return textView.getLineEndMark (); } /** * Chose between fonts. * @param fnt is the font to chose. * @param size is the size of the font or zero if old size is used. */ void STextEdit::setFont (const SString& fnt, double _size) { textView.setFont (fnt, _size); /* extents may change */ notifySlider(); caret.resize (SDimension(textView.lineHeight, textView.lineHeight)); resized = true; updateCaretLocation (); redraw (); } /** * Set the size of the font. */ void STextEdit::setFontSize (double _size) { textView.setFontSize (_size); /* extents may change */ notifySlider(); caret.resize (SDimension(textView.lineHeight, textView.lineHeight)); resized = true; updateCaretLocation (); redraw (); } /** * redraw the whole thing */ void STextEdit::redraw () { int eheight = (int)size.height - (int)2 * border.getBorderSize().height; if (eheight <= 0) return; int ewidth = (int)size.width - (int)2 * border.getBorderSize().width + 2 * SS_LEFT_MARGIN; resized = true; window->redraw (true, (int) border.getBorderSize().width , (int) border.getBorderSize().height, ewidth + 2 * SS_LEFT_MARGIN, eheight); } /*----------- This is the implementation of SEditorIF ---------------*/ /** * Chose between fonts. * @param fnt is the font to chose. */ void STextEdit::setFont (const SString& fnt) { setFont (fnt, 0.0); } /** * Chose between input methods. * @patam im is the input to chose. */ void STextEdit::setInputMethod (const SString& _im) { //fprintf (stderr, "Starting -----------%*.*s\n", SSARGS(im)); /* Stop current xinput method. */ inputMethod = _im; if (!window->isVisible()) { imWaiting = inputMethod; return; } imWaiting.clear(); unsigned int oldH = statusHeight; statusHeight = 0; SDimension d = getSize(); if (yuditInput) { yuditInput->clear(); delete yuditInput; yuditInput=0;; } else { } if (inputMethod == SS_DEFAULT_IM) { if (oldH != statusHeight) resizeTextView (true); window->startInputMethod ("x-ascii", SProperties()); } else if (inputMethod == SS_KINPUT2_IM) { statusHeight = (textView.lineHeight < 24) ? 24: textView.lineHeight; resizeTextView (true); if (!window->startInputMethod ("_JAPANESE_CONVERSION",getProperties())) { fprintf (stderr, "Can not start kinput2 (_JAPANESE_CONVERSION) input method.\n"); } if (window->getInputMethod()=="x-utf-8" || window->getInputMethod() == "x-ascii" || window->getInputMethod() == "" || ximProperties.get ("InputStyle") == 0 || (ximProperties["InputStyle"] != "preedit-under-status-under" && ximProperties["InputStyle"] != "preedit-over-status-under")) { statusHeight = 0; resizeTextView (true); } } else if (inputMethod == SS_WINDOWS_IM) { statusHeight = (textView.lineHeight < 24) ? 24: textView.lineHeight; resizeTextView (true); if (!window->startInputMethod ("_JAPANESE_CONVERSION",getProperties())) { fprintf (stderr, "Can not start Window input method.\n"); } if (window->getInputMethod()=="x-utf-8" || window->getInputMethod() == "x-ascii" || window->getInputMethod() == "" || ximProperties.get ("InputStyle") == 0 || (ximProperties["InputStyle"] != "preedit-under-status-under" && ximProperties["InputStyle"] != "preedit-over-status-under")) { statusHeight = 0; resizeTextView (true); } } /* X Input method. */ else if (inputMethod.size() > 2 && (inputMethod[0] == 'x' || inputMethod[0] == 'X') && (inputMethod[1] == '-' || inputMethod[1] == '-') ) { statusHeight = (textView.lineHeight < 24) ? 24: textView.lineHeight; resizeTextView (true); if (!window->startInputMethod (inputMethod, getProperties())) { fprintf (stderr, "Can not start %*.*s input method.\n", SSARGS(inputMethod)); } if (window->getInputMethod()=="x-utf-8" || window->getInputMethod() == "x-ascii" || window->getInputMethod() == "" || ximProperties.get ("InputStyle") == 0 || (ximProperties["InputStyle"] != "preedit-under-status-under" && ximProperties["InputStyle"] != "preedit-over-status-under")) { statusHeight = 0; resizeTextView (true); } } else { if (oldH != statusHeight) resizeTextView (true); window->startInputMethod ("x-ascii", SProperties()); yuditInput = new SYuditInput (inputMethod, this, &editor); } } /** * Get text from clipboard and put it here. */ void STextEdit::insertClipboardText () { SString str = window->getClipUTF8(); if (str.size()==0) { return; } if (isMultiline()) { textView.textData.insert (str); textView.textData.fireEvent(); updateCaretPosition (); updateCaretLocation (); fireTextChanged (); return; } /* multiline */ SEncoder oc; SV_UCS4 s4 = oc.decode (str); SV_UCS4 vle; for (unsigned int i=0; iputClipUTF8 (str); } /** * Dirty text may conta8ing newline characters and the line. * This routine is calling insertText for multiline widget. * For a signle line widget it is chopping the text at newline * and calls the textEntered callback. * * @param str is an utf8 string that may contain line-breaks as well. * @return the index to the previous insertion point. */ void STextEdit::insertDirtyText (const SString& str, bool embed) { if (!editable) { return ; } if (selecting) { deselectText(); } if (isMultiline()) { insertText (str, embed); return; } int ind0 = str.find (SS_LB_DOS); int ind1 = str.find (SS_LB_MAC); int ind2 = str.find (SS_LB_UNIX); int ind3 = str.find (SS_LB_LS); if (ind0 < 0) ind0 = str.size(); if (ind1 < 0) ind1 = str.size(); if (ind2 < 0) ind2 = str.size(); if (ind3 < 0) ind3 = str.size(); int smallest = ind0; if (ind1 < smallest) smallest = ind1; if (ind2 < smallest) smallest = ind2; if (ind3 < smallest) smallest = ind3; if (smallest < (int)str.size()) { SString s = str; s.truncate ((int)smallest); insertText (s, embed); for (unsigned i=0; itextEntered (this); } return; } insertText (str, embed); return; } /** * Insert the text in str. The text won't be affected by keys. * @param str is an utf8 string that may contain line-breaks as well. * @param embed is true if embed marks need to be added. * undo, paste does not need them. * @return the index to the previous insertion point. */ void STextEdit::insertText (const SString& _str, bool embed) { /* find out what text it is */ SEmbedState s = caret.getEmbedState(); SString str = _str; if (embed && s.getExplicitLevel()!=0) { SV_UCS4 ucs = s.getEmbeddingMarks(0); unsigned int i = ucs.size();; /* we have to go the opposite order to get the right order... */ while (i>0) { i--; switch (ucs[i]) { case SD_CD_LRO: str.insert(0, SS_LB_LRO); str.append (SS_LB_PDF); break; case SD_CD_RLO: str.insert(0, SS_LB_RLO); str.append (SS_LB_PDF); break; case SD_CD_LRE: str.insert(0, SS_LB_LRE); str.append (SS_LB_PDF); break; case SD_CD_RLE: str.insert(0, SS_LB_RLE); str.append (SS_LB_PDF); break; } } } textView.textData.insert (str); textView.textData.fireEvent(); /* move to the logical end of previous character */ updateCaretPosition (); updateCaretLocation (); fireTextChanged (); return; } /** * Scroll it up. */ void STextEdit::pageUp () { if (textView.lineHeight == 0 && !isMultiline() || textView.textData.size ()==0) { return; } unsigned int h0 = textView.getDocumentHeight(); SCursorIndex indexNow = caret.getCursorIndex(); SLocation l0 = textView.getCursorLocation (indexNow); /* half pages */ int decrement = ((int)textView.getSize().height/2)/textView.lineHeight + 1; decrement = decrement * textView.lineHeight; SLocation l1 = SLocation (0, l0.y - decrement); if (l0.y-l1.y > -textView.getViewPort().y) { l1.y = l0.y + textView.getViewPort().y; } int scrollVle = l0.y - l1.y; if (scrollVle == 0) { /* size changed - redraw whole window */ unsigned int h1 = textView.getDocumentHeight(); if (h1 != h0) { redraw(); } return; } caret.redraw(); scrollVertical (scrollVle, true); SCursorIndex indexNext = textView.getCursorIndex (l0); indexNext.textIndex.index = column; if (scrollVle <= (int)textView.lineHeight && indexNext.textIndex.line > 1) { indexNext.textIndex.line -= 1; } if (selecting) { selectText (indexNext); } else { deselectText(); textView.textData.move (indexNext.textIndex); textView.textData.fireEvent(); updateCaretPosition (false); updateCaretLocation (); } /* size changed - redraw whole window */ unsigned int h1 = textView.getDocumentHeight(); if (h1 != h0) { redraw(); } return; } /** * Scroll it down. */ void STextEdit::pageDown () { if (textView.lineHeight == 0 && !isMultiline() || textView.textData.size ()==0) { return; } SCursorIndex indexNow = caret.getCursorIndex(); SLocation l0 = textView.getCursorLocation (indexNow); /* half pages */ int increment = ((int)textView.getSize().height/2)/textView.lineHeight + 1; increment = increment * textView.lineHeight; SLocation l1 = SLocation (0, l0.y + increment); int docHeight = (int) textView.getDocumentHeight(); /* so what if we scroll */ int scrollVle = l1.y - l0.y; if (scrollVle - textView.getViewPort().y > docHeight - (int) textView.getSize().height) { scrollVle = docHeight + textView.getViewPort().y - (int) textView.getSize().height; } if (scrollVle ==0) return; if (textView.getViewPort().y -scrollVle > 0) return; caret.redraw(); scrollVertical (-scrollVle, true); SCursorIndex indexNext = textView.getCursorIndex (l0); indexNext.textIndex.index = column; if (scrollVle <= (int)textView.lineHeight && textView.textData.size() > indexNext.textIndex.line+1) { indexNext.textIndex.line += 1; } if (selecting) { selectText (indexNext); } else { deselectText(); textView.textData.move (indexNext.textIndex); textView.textData.fireEvent(); updateCaretPosition (false); updateCaretLocation (); } return; } /** * Move caret up. if possible. */ void STextEdit::caretUp () { unsigned int h0 = textView.getDocumentHeight(); SCursorIndex indexNow = caret.getCursorIndex (); if (indexNow.textIndex.line==0) { historyUp (); return; } SCursorIndex ti = SCursorIndex (indexNow.textIndex.line-1, column); unsigned int sz = textView.textData.size (ti.textIndex.line); if (sz < ti.textIndex.index) ti.textIndex.index = sz; if (sz > 0 && ti.textIndex.index == sz && textView.textData.isProperLine (ti.textIndex.line)) { ti.textIndex.index--; } if (selecting) { selectText (ti); } else { deselectText(); textView.textData.move (ti.textIndex); textView.textData.fireEvent(); updateCaretPosition (false); updateCaretLocation (); } /* size changed - redraw whole window */ unsigned int h1 = textView.getDocumentHeight(); if (h1 != h0) { redraw(); } return; } /** * Move caret downwards. if possible. */ void STextEdit::caretDown () { SCursorIndex indexNow = caret.getCursorIndex (); if (!textView.textData.isProperLine (indexNow.textIndex.line) || indexNow.textIndex.line + 1 > textView.textData.size()) { historyDown (); return; } SCursorIndex ti = SCursorIndex (indexNow.textIndex.line+1, column); unsigned int sz = textView.textData.size (ti.textIndex.line); if (sz < ti.textIndex.index) ti.textIndex.index = sz; if (sz > 0 && ti.textIndex.index == sz && textView.textData.isProperLine (ti.textIndex.line)) { ti.textIndex.index--; } if (selecting) { selectText (ti); } else { deselectText(); textView.textData.move (ti.textIndex); textView.textData.fireEvent(); //fprintf (stderr, "Y1 column=%u\n", column); updateCaretPosition (false); updateCaretLocation (); } historyDown (); return; } /** * Move caret to the left. if possible. */ void STextEdit::caretLeft () { SCursorIndex indexNow = caret.getCursorIndex(); SCursorIndex indexPrev = textView.leftOf (indexNow); if (indexPrev == indexNow) { return; } if (selecting) { selectText (indexPrev); } else { deselectText(); textView.textData.move (indexPrev.getTextIndex()); textView.textData.fireEvent(); setCursorIndex (indexPrev); } STextIndex ndx = textView.textData.getTextIndex(); column = ndx.index; return; } /** * Move caret to the right. if possible. */ void STextEdit::caretRight () { SCursorIndex indexNow = caret.getCursorIndex(); SCursorIndex indexNext = textView.rightOf (indexNow); if (indexNext == indexNow) { return; } if (selecting) { selectText (indexNext); } else { deselectText(); textView.textData.move (indexNext.getTextIndex()); textView.textData.fireEvent(); setCursorIndex (indexNext); } STextIndex ndx = textView.textData.getTextIndex(); column = ndx.index; return; } /** * Put state machine into a 'selecting' state. */ void STextEdit::startSelect () { deselectText(); startSelection = textView.textData.getTextIndex(); endSelection = startSelection; selecting = true; } /** * End the selection. */ void STextEdit::endSelect () { selecting = false; if (startSelection != endSelection) { clipSelection(); } } /** * Drag select the text. * @param till indicates new endselction. */ void STextEdit::selectText (const SCursorIndex& _till) { if (!selecting) { return; } STextIndex now = _till.getTextIndex(); if (now == endSelection) { setCursorIndex(_till); return; } /* make sure selection doesn't exceed boundaries (for example, because of pressing the END key). Addition by Maarten van Gompel */ unsigned int maxsize = textView.textData.size(now.line); if (textView.textData.isProperLine(now.line)) maxsize--; if (now.index > maxsize) { STextIndex maxindex(now.line, maxsize); now = maxindex; } //textIndex = endSelection; if (now > endSelection) { /* crossed startSelection - delete */ textView.textData.move (endSelection); if (endSelection < startSelection) { if (now < startSelection) { textView.textData.select (now, false); } else { textView.textData.select (startSelection, false); } } textView.textData.select (now, true); } else /* moving down */ { /* crossed startSelection - delete */ textView.textData.move (endSelection); if (endSelection > startSelection) { if (now > startSelection) { textView.textData.select (now, false); } else { textView.textData.select (startSelection, false); } } textView.textData.select (now, true); } endSelection = now; textView.textData.fireEvent (); textView.textData.move (now); setCursorIndex(_till); } /** * Wherever the current index is, select a word there. */ void STextEdit::selectWord () { if (selecting) return; deselectText(); STextIndex index = textView.textData.getTextIndex(); if (index.line == textView.textData.size()) { return; } unsigned int start; for (start=index.index; start>0; start--) { //the new function isDelimiter is now used instead of the //isWhitespace function, so words are now carefully being //selected based on hundreds of possible unicode delimiters. //addition by Maarten van Gompel if (textView.textData.isDelimiter (STextIndex (index.line, start-1))) { break; } } unsigned int end; for (end=index.index; end 0) { nextIndex.index--; } if (index==nextIndex) return; startSelection = STextIndex(index.line, 0); endSelection = nextIndex; textView.textData.move (startSelection); textView.textData.select (endSelection); textView.textData.fireEvent (); /* caret did not move */ textView.textData.move (index); clipSelection(); } /** * get the index after new line mark. that is already on * the next line, or if there is no new line, this is the same line. */ STextIndex STextEdit::getIndexAfterLineBreak () { STextIndex index = textView.textData.getTextIndex(); if (index.line == textView.textData.size()) { return STextIndex(index); } if (textView.textData.isProperLine (index.line)) { index.line = index.line+1; index.index = 0; } else { index.index = textView.textData.size(index.line); } return STextIndex(index); } /** * The text hoghlighted should be unhighlighted. */ void STextEdit::deselectText () { selecting = false; if (startSelection == endSelection) { startSelection = textView.textData.getTextIndex(); endSelection = textView.textData.getTextIndex(); return; } STextIndex now = textView.textData.getTextIndex(); textView.textData.move (startSelection); textView.textData.select (endSelection, false); textView.textData.fireEvent(); /* caret did not move */ textView.textData.move (now); startSelection = now; endSelection = now; } /** * getSelectedIndex * @param hi is true if we want the upper index. */ STextIndex STextEdit::getSelectedIndex (bool hi) { if (startSelection < endSelection) { return hi ? endSelection : startSelection; } else { return hi ? startSelection : startSelection; } } /** * Remove the selected text. * Return the text that was remove - if any. */ SString STextEdit::eraseSelectedText () { if (!editable) return (SString("")); selecting = false; if (startSelection == endSelection) { startSelection = textView.textData.getTextIndex(); endSelection = textView.textData.getTextIndex(); return SString(""); } textView.textData.move (startSelection); SString ret = textView.textData.getText (endSelection); textView.textData.remove (endSelection); textView.textData.fireEvent(); startSelection = textView.textData.getTextIndex(); endSelection = textView.textData.getTextIndex(); fireTextChanged (); updateCaretPosition (); updateCaretLocation (); return SString(ret); } /** * erase a glyph - that is one glyph back. * text deleted are always <- direction. * in an LR context this should be backspace. * @return the text erased (if any) */ SString STextEdit::backspace () { if (selecting || !editable) return SString(""); STextIndex indexNow = textView.textData.getTextIndex (); STextIndex indexPrev = textView.textData.getTextIndex (-1, true); if (indexPrev == indexNow) return SString(""); SString ret = textView.textData.getText (indexPrev); textView.textData.remove (indexPrev); textView.textData.fireEvent(); fireTextChanged (); updateCaretPosition (); updateCaretLocation (); return SString(ret); } /** * Delete text - that is one position forward. * text deleted are always -> direction. * in an LR context this should be delete. * @return the text deleted (if any) */ SString STextEdit::erase () { if (selecting || !editable) return (SString("")); STextIndex indexNow = textView.textData.getTextIndex (); STextIndex indexPrev = textView.textData.getTextIndex (-1, true); STextIndex indexNext = textView.textData.getTextIndex (1, true); if (indexNext == indexNow) return SString(""); SString ret = textView.textData.getText (indexNext); textView.textData.remove (indexNext); textView.textData.fireEvent(); fireTextChanged (); updateCaretPosition (); updateCaretLocation (); return SString(ret); } /** * Remove composing letter to the point ponterd away from with cursor */ SS_UCS4 STextEdit::removeComposing() { STextIndex afrom = getCaretArrowFrom(); /* saving it */ STextIndex before = textView.textData.getTextIndex(); STextIndex ci(afrom.line, afrom.index); textView.textData.move (ci); SS_UCS4 ret = textView.textData.removeComposing (true); textView.textData.move (before); if (ret) { textView.textData.fireEvent(); updateCaretLocation(); fireTextChanged (); } return ret; } /** * Add composing letter to the point ponterd away from with cursor */ bool STextEdit::addComposing(SS_UCS4 c) { STextIndex afrom = getCaretArrowFrom(); /* saving it */ STextIndex before = textView.textData.getTextIndex(); STextIndex ci(afrom.line, afrom.index); textView.textData.move (ci); bool ret = textView.textData.addComposing (c, true); textView.textData.move (before); if (ret) { textView.textData.fireEvent(); updateCaretLocation(); fireTextChanged (); } return ret; } const SCursorIndex& STextEdit::getCursorIndex () const { return caret.getCursorIndex(); } /** * We should put away the focus to somewhere else. */ void STextEdit::focusOut () { for (unsigned int i=0; ifocusOutRequest (this); } } void STextEdit::fireTextChanged () { for (unsigned int i=0; itextChanged (this); } } void STextEdit::fireCaretMoved () { SCursorIndex cin = caret.getCursorIndex(); for (unsigned int i=0; icaretMoved (this, cin.textIndex.line, cin.textIndex.index, cin.before); } } void STextEdit::setEditable (bool _editable) { if (editable == _editable) return; editable = _editable; caret.animate (editable && focused); //textView.setEditable(_editable); } bool STextEdit::isEditable () const { return editable; } bool STextEdit::isSelecting () const { return selecting; } /** * Insert text from pre-edit, bypassing undo */ void STextEdit::insertPreEditText (const SString& str) { STextIndex before = textView.textData.getTextIndex (); /* this will move caret */ insertDirtyText (str); STextIndex now = textView.textData.getTextIndex (); textView.textData.underline (before); textView.textData.move (now); textView.textData.fireEvent (); return; } /** * erase text between this and 'till'. * @return the erased text. */ SString STextEdit::eraseText (const STextIndex& till) { if (selecting || !editable) { //fprintf (stderr, "selecting=%d editable=%d\n", // (int) selecting, (int) editable); return SString(""); } STextIndex index = textView.textData.getTextIndex(); if (index == till) { //fprintf (stderr, "index=%u %u till=%u %u\n", // index.line, index.index, till.line, till.index); return SString(""); } SString ret = textView.textData.getText (till); textView.textData.remove (till); textView.textData.fireEvent(); fireTextChanged (); updateCaretPosition (); updateCaretLocation (); return SString(ret); } SString STextEdit::getText (const STextIndex& from, const STextIndex& till) { return textView.textData.getText (from, till); } SString STextEdit::getText () { return textView.textData.getText (); } void STextEdit::setUnderlineColor (const SColor& c) { textView.setUnderlineColor(c); } /** * This is called by a slibadle and it shows that vales have been changed. */ void STextEdit::valueChanged (SSlidable* _slidable, SSlideType _type) { SLocation diff = textView.getViewPort() + _slidable->value; /* it can not change anything by the viewport. */ if (isMultiline()) /* only vertical */ { if (diff.y == 0) return; SCursorIndex ci = caret.getCursorIndex(); caret.redraw(); /* set see what we get */ scrollVertical (-diff.y, false); SLocation l = textView.getCursorLocation(ci); SDimension tsize = textView.getSize(); SLocation tloc = textView.getLocation(); if (l.y > (int) tsize.height - tloc.y ) { l.y = (int) tsize.height - textView.lineHeight - tloc.y; } if (l.y < tloc.y + (int) textView.lineHeight +1) { l.y = tloc.y + (int) textView.lineHeight +1; } SCursorIndex index = textView.getCursorIndex(l); if (index != ci) { l.x = 0; index = textView.getCursorIndex(l); STextIndex wanted = index.getTextIndex(); /* get our limitation */ textView.textData.move (wanted); STextIndex real = textView.textData.getTextIndex(); SCursorIndex realIndex (index); /* wanted is more */ if (wanted != real) { if (real.index == 0) { realIndex = SCursorIndex(real.line, real.index, true); } else { realIndex = SCursorIndex(real.line, real.index-1, false); } } else { realIndex = index; } internalMoveCaret(realIndex); /* not necessarily visible */ fireCaretMoved(); //column = ci.textIndex.index ; } updateCaretLocation (false); } else /* only horizontal */ { if (diff.x == 0) return; scrollHorizontal (-diff.x, false); updateCaretLocation(false); } } /** * calculate value, step, page and max and send it to * the listener */ void STextEdit::notifySlider () { if (!sliderListener) return; /* FIXME: add horizontal one */ SDimension d = SDimension (0, textView.getDocumentHeight()); SSlidable s; s.max = d - textView.getSize(); s.step = SDimension (0, textView.lineHeight); s.value = SLocation (0, -textView.getViewPort().y); s.page = textView.getSize(); if (slidable==s) return; slidable = s; if (isMultiline()) /* only vertical */ { sliderListener->valueChanged (&slidable, SSliderListener::SS_VERTICAL); } } /** * @param l is the listener. */ SSlidable* STextEdit::setSliderListener (SSliderListener* l) { sliderListener = l; return &slidable; } bool STextEdit::undo () { return editor.undo(); } /** * Clear all states * return treu if it had a state. */ bool STextEdit::clearState() { bool had=false; selecting = false; if (yuditInput && yuditInput->clear(false)) { had = true; } if (startSelection != endSelection) { had=true; clipSelection(); } deselectText(); return had; } bool STextEdit::redo () { return editor.redo(); } /** * Set the ParagraphSeparator */ void STextEdit::setParagraphSeparator (const SString& lbr, bool change) { editor.setParagraphSeparator(lbr); if (!change) return; if (textView.textData.setParagraphSeparator(lbr)) { for (unsigned i=0; itextChanged (this); } window->redraw (true, 0, 0, size.width, size.height); } } void STextEdit::clear() { if (yuditInput) yuditInput->clear(); startSelection = STextIndex(0,0); endSelection = STextIndex(0,0); selecting = false; editor.clear(); textView.textData.clear(); textView.textData.fireEvent(); updateCaretPosition(); updateCaretLocation(); fireCaretMoved(); fireTextChanged(); } void STextEdit::setText (const SString& text) { clear(); textView.textData.setText (text); textView.textData.fireEvent(); updateCaretPosition(); updateCaretLocation(); fireCaretMoved(); fireTextChanged(); editor.clear(); } /** * Insert text through editor. */ void STextEdit::insertEditorText (const SString& text) { editor.keyPressed (SWindowListener::Key_Send, text, false, false, false); } const SStringVector& STextEdit::getHistory() { return history; } /** * history is for singles */ void STextEdit::putHistory (const SString& str) { if (historySize==0) return; if (isMultiline()) return; unsigned int i; for (i=0; i historySize) { history.remove (0); } currentHistorySize = history.size(); } void STextEdit::setHistorySize (unsigned int siz) { historySize = siz; if (history.size() > siz) history.truncate (siz); } /** * set text from history */ void STextEdit::historyDown () { if (history.size() == 0 || historySize==0) return; if (currentHistorySize+1 >= history.size()) return; if (isMultiline()) return; editor.clear(); clear(); currentHistorySize++; setText (history[currentHistorySize]); setCursorIndex(SCursorIndex(0,10000)); } /** * set text from history */ void STextEdit::historyUp () { if (history.size() == 0 || historySize==0) return; if (currentHistorySize == 0) return; if (isMultiline()) return; editor.clear(); clear(); currentHistorySize--; //fprintf (stderr, "history up %u \n", currentHistorySize); setText (history[currentHistorySize]); setCursorIndex(SCursorIndex(0,10000)); } void STextEdit::historyEnd() { currentHistorySize = history.size(); } /** * If there is selected text unselect it increment the index. * and try find again. */ bool STextEdit::find(const SString& str) { if (isSelecting ()) { endSelect (); } /* just find */ STextIndex fs; if (startSelection == endSelection) { fs = textView.textData.find(str); if (fs == STextIndex(0,0)) { setCursorIndex(SCursorIndex(0,0)); fs = textView.textData.find(str); if (fs == STextIndex(0,0)) { return false; } } } /* select the text between current index and fs */ else { STextIndex indexNext = textView.textData.getTextIndex (1, true); textView.textData.move (indexNext); updateCaretPosition(); fs = textView.textData.find(str); if (fs == STextIndex(0,0)) { setCursorIndex(SCursorIndex(0,0)); fs = textView.textData.find(str); if (fs == STextIndex(0,0)) { deselectText(); return false; } } } STextIndex currentIndex = textView.textData.getTextIndex(); if (startSelection != endSelection) { textView.textData.move (startSelection); textView.textData.select (endSelection, false); textView.textData.fireEvent(); textView.textData.move (currentIndex); } startSelection = fs; endSelection = fs; /* select text moves stuff around so we might want to deselect previous now */ selecting = true; selectText (SCursorIndex (currentIndex.line, currentIndex.index, true)); selecting = false; /* this scrolles and leaves old selected text behind */ centerCaretVertical(); return true; } /** * replace text. */ bool STextEdit::replace (const SString& orig, const SString& repl) { if (isSelecting()) { endSelect (); } if (orig.size()==0) { return false; } SString str; if (startSelection != endSelection) { str = cleanEmbed(textView.textData.getText (startSelection, endSelection)); } /* do if it is the same as search string */ if (startSelection == endSelection || str != orig) { return find (orig); } else { /* re-embed */ SEmbedState es = textView.textData.getEmbedState( (startSelection > endSelection) ? endSelection : startSelection); editor.keyPressed (SWindowListener::Key_Delete, SString(""), false, false, false); if (repl.size()) { /* replace with the current embed state */ SV_UCS4 emark = es.getEmbeddingMarks(0); SEncoder utf8enc; SString str = utf8enc.encode (emark); str.append (repl); for (unsigned int i=0; i= textView.textData.size()) return 0; if (ti.index >= textView.textData.size(ti.line)) return 0; return &(textView.textData.glyphAt (ti)); } /** * Return the visual index of where to caret is pointing from: * returned index is index + 1 */ STextIndex STextEdit::getCaretArrowFrom () { SCursorIndex ci = caret.getCursorIndex(); /* before or after, this, visually */ bool clr = caret.isLR(); SCursorIndex cr = (clr) ? textView.leftOf (ci) : textView.rightOf (ci); if (cr == ci) { bool plr = textView.textData.isLR(ci.textIndex.line); if ((plr && !clr) || (!plr && clr)) { if (textView.textData.isProperLine (cr.textIndex.line)) { return STextIndex(cr.textIndex.line, textView.textData.size(cr.textIndex.line)); } return STextIndex(cr.textIndex.line, textView.textData.size(cr.textIndex.line)+1); } return STextIndex(cr.textIndex.line, 0); } return STextIndex (cr.textIndex.line, cr.textIndex.index+1); } void STextEdit::setHighlightMode (STextView::SS_HighlightMode hlMode) { textView.setHighlightMode(hlMode); redraw (); } STextView::SS_HighlightMode STextEdit::getHighlightMode () const { return textView.getHighlightMode(); } void STextEdit::setWordWrap (bool lbm) { SCursorIndex xy = getCursorIndex(); textView.setWordWrap(lbm); /* extents may change */ notifySlider(); setCursorIndex (xy); setCaretLocationVisible (); redraw (); } bool STextEdit::getWordWrap () const { return textView.getWordWrap(); } void STextEdit::setDocumentEmbedding (SS_Embedding e) { if (textView.textData.getDocumentEmbedding()==e) return; textView.textData.setDocumentEmbedding (e); textView.textData.fireEvent (); updateCaretPosition(); updateCaretLocation(); fireCaretMoved(); window->redraw (true, 0, 0, size.width, size.height); } SS_Embedding STextEdit::getDocumentEmbedding () const { return textView.textData.getDocumentEmbedding(); } /** * Set the direction of the caret. If the direction of * The case is not simpliy SS_DR_L or SS_DR_R, add * necessary extra embedding levels to caret if necessary * direction is different. * @return true if the caret direction was different. */ void STextEdit::setDirection (SS_DR_Dir dir) { STextIndex ti = textView.textData.getTextIndex(); SCursorIndex ci = caret.getCursorIndex(); SEmbedState embed = textView.textData.getEmbedState(ci.textIndex); caret.setEmbedState (embed); if (dir==SS_DR_L || dir==SS_DR_R) { caret.setDirection (dir); caret.setEmbedState (embed); return; } SV_UCS4 v = embed.getEmbeddingMarks(0); SS_UCS4 current = (v.size()) ? v[v.size()-1] : 0; SS_UCS4 needed = 0; switch (dir) { case SS_DR_LE: if (current != SD_CD_LRE) needed = SD_CD_LRE; break; case SS_DR_RE: if (current != SD_CD_RLE) needed = SD_CD_RLE; break; case SS_DR_LO: if (current != SD_CD_LRO) needed = SD_CD_LRO; break; case SS_DR_RO: if (current != SD_CD_RLO) needed = SD_CD_RLO; break; default: break; } if (needed != 0) { v.append (needed); embed.setEmbeddingMarks(v); caret.setEmbedState(embed); } caret.setDirection (dir); return; } /** * get the current direction of the caret. */ SS_DR_Dir STextEdit::getDirection () const { return caret.getDirection(); } /** * return the explicit embed level at cursor */ bool STextEdit::isEmbedStateLR() const { SEmbedState ec = caret.getEmbedState(); SCursorIndex ci = caret.getCursorIndex(); bool isEL = ((ec.getExplicitLevel() % 2)==0); if (ec.getExplicitLevel() == 0) { isEL = textView.textData.isLR(ci.textIndex.line); } return isEL; } /** * Change the direction of the selected text. * If no text is selected insert new direction. * @return true if the direction of cursor succeded, false * if only text embedding has been changed. */ bool STextEdit::changeDirection (SS_DR_Dir dir) { if (isSelecting()) { endSelect (); } /* if we want to change the same emvedding dont do it */ SEmbedState ec = caret.getEmbedState(); SCursorIndex ci = caret.getCursorIndex(); bool skip = false; bool isEL = ((ec.getExplicitLevel() % 2)==0); if (ec.getExplicitLevel() == 0) { isEL = textView.textData.isLR(ci.textIndex.line); } bool isOve = ec.isOverride(); SS_DR_Dir cdir = caret.getDirection(); switch (dir) { case SS_DR_LE: if (isEL&& !isOve) skip = true; if (cdir==dir) skip = true; if (cdir==SS_DR_RE) skip = true; break; case SS_DR_RE: if (!isEL && !isOve) skip = true; if (cdir==dir) skip = true; if (cdir==SS_DR_LE) skip = true; break; case SS_DR_LO: if (isEL && isOve) skip = true; if (cdir==dir) skip = true; if (cdir==SS_DR_RO) skip = true; break; case SS_DR_RO: if (cdir==dir) skip = true; if (!isEL && isOve) skip = true; if (cdir==SS_DR_LO) skip = true; break; default: break; } if (skip && startSelection!=endSelection) { endSelect(); deselectText(); } /* just reset */ if (skip || (dir == SS_DR_L || dir == SS_DR_R) && startSelection == endSelection) { /* pop embedding direction and return 1 if * next direction is different. Also lower the * embedding if there is selected text. */ SEmbedState et = textView.textData.getEmbedState(ci.textIndex); if (ec == et) { /* do a loop while we have this state or bigger */ unsigned int i=ci.textIndex.index; SV_UCS4 m0 = ec.getEmbeddingMarks(0); if (m0.size()==0) { setCursorIndex (caret.getCursorIndex()); return true; } /* find the end of this embeding */ while (isetInputMethodProperties (getProperties()); } } /** * Move caret to index. if possible. * @param index is the index to go to. */ void STextEdit::setCursorIndex (const SCursorIndex& index) { STextIndex wanted = index.getTextIndex(); /* get our limitation */ textView.textData.move (wanted); STextIndex real = textView.textData.getTextIndex(); textView.textData.fireEvent(); SCursorIndex realIndex (index); /* wanted is more */ if (wanted != real) { if (real.index == 0) { realIndex = SCursorIndex(real.line, real.index, true); } else { realIndex = SCursorIndex(real.line, real.index-1, false); } } else { realIndex = index; } internalMoveCaret(realIndex); updateCaretLocation (); fireCaretMoved(); notifySlider(); } /** * Internal move caret with all fancy shape settings */ void STextEdit::internalMoveCaret (const SCursorIndex& realIndex) { /* This will make sure it wont blink for a while now */ caret.on (true); column = realIndex.textIndex.index; caret.move (realIndex); bool lr = textView.textData.isLR(realIndex.textIndex); SEmbedState ec = textView.textData.getEmbedState(realIndex.textIndex); caret.setEmbedState (ec); caret.setDirection (lr ? SS_DR_L : SS_DR_R); /* check if previous embed state is different */ /* if this embedding state is different from sorrounding embedding state change cursor shape tu current embedding state. */ unsigned int line = realIndex.textIndex.line; unsigned int max = textView.textData.size(line); STextIndex bbox (line, max); /* if the thind does not move get the paragraph embedding */ SCursorIndex right = textView.rightOf (realIndex); SEmbedState rec = (right == realIndex) ? textView.textData.getEmbedState (bbox) : textView.textData.getEmbedState (right.textIndex); SCursorIndex left = textView.leftOf (realIndex); SEmbedState lec = (left == realIndex) ? textView.textData.getEmbedState (bbox) : textView.textData.getEmbedState (left.textIndex); SV_UCS4 marks = ec.getEmbeddingMarks(0); if ((ec != lec || ec != rec) && marks.size()!=0) { switch(marks[marks.size()-1]) { case SD_CD_LRO: caret.setDirection (SS_DR_LO); break; case SD_CD_RLO: caret.setDirection (SS_DR_RO); break; case SD_CD_LRE: caret.setDirection (SS_DR_LE); break; case SD_CD_RLE: caret.setDirection (SS_DR_RE); break; } } } /** * Encode the glyph with current keymap */ SString STextEdit::encode (const SV_UCS4& v) const { SString ret; if (yuditInput!=0) { ret = yuditInput->encode (v); } return SString(ret); } /** * get rid of embedding marks */ SString STextEdit::cleanEmbed(const SString str) const { SEncoder utf8enc; SV_UCS4 u4 = utf8enc.decode (str); SV_UCS4 res; /* remove direction marks */ for (unsigned int i=0; igetWindowID(); sprintf (arr, "%lu", wid); p.put ("InputWindow", arr); SLocation vl = textView.getLocation(); vl.x = vl.x - (int) SS_LEFT_MARGIN; SDimension vd = textView.getSize(); vd.width = vd.width + (unsigned int) 2 * SS_LEFT_MARGIN; vl.y = vl.y + (int) vd.height; sprintf (arr, "%d,%d", vl.x, vl.y); p.put ("InputStatusLocation", arr); unsigned int statusw = 2 * statusHeight; if (statusw < 48) statusw = 48; sprintf (arr, "%u,%u", statusw, statusHeight); p.put ("InputStatusSize", arr); vl.x = (int) statusw; sprintf (arr, "%d,%d", vl.x, vl.y); p.put ("InputClientLocation", arr); vd.width = vd.width + (unsigned int) 2 * SS_LEFT_MARGIN; if (vd.width > statusw) vd.width = vd.width - statusw; sprintf (arr, "%u,%u", vd.width, statusHeight); p.put ("InputClientSize", arr); } /* add required and optional parameters if any */ if (p["InputStyle"] == SString("preedit-over-status-under")) { SLocation vl = textView.getLocation(); vl.x = vl.x - (int) SS_LEFT_MARGIN; SDimension vd = textView.getSize(); vd.width = vd.width + (unsigned int) 2 * SS_LEFT_MARGIN; vl.y = vl.y + (int) vd.height; sprintf (arr, "%d,%d", vl.x, vl.y); p.put ("InputStatusLocation", arr); sprintf (arr, "%u,%u", vd.width, statusHeight); p.put ("InputStatusSize", arr); } return SProperties (p); } /** * Set X Input Method properties. * This method is called if the default * InputStyle="preedit-over-status-over" * needs to be overridden. * Accepted InputStyles: * preedit-root-status-root * preedit-over-status-over * preedit-over-status-under * preedit-under-status-under * Optional: * InputWindow=id - an integer window id * InputStatusLocation="x,y" - the status position * InputStatusSize="width,height" - the status size * InputClientColor=colorbg,colorfg - input status background,foreground * InputClientLocation="x,y" - the client position * InputClientSize="width,height" - the client size * @param props are the properties. */ void STextEdit::setXIMProperties (const SProperties& props) { ximProperties = props; }