bool FrameCaret::caretPositionIsValidForDocument( const Document& document) const { if (!isActive()) return true; return caretPosition().document() == document && !caretPosition().isOrphan(); }
void FrameCaret::paintCaret(GraphicsContext& context, const LayoutPoint& paintOffset) { if (m_caretVisibility == CaretVisibility::Hidden) return; if (!(isActive() && m_shouldPaintCaret)) return; updateCaretRect(caretPosition()); CaretBase::paintCaret(caretPosition().anchorNode(), context, paintOffset, DisplayItem::kCaret); }
bool FrameCaret::shouldBlinkCaret() const { if (m_caretVisibility != CaretVisibility::Visible || !isActive()) return false; Element* root = rootEditableElementOf(caretPosition().position()); if (!root) return false; Element* focusedElement = root->document().focusedElement(); if (!focusedElement) return false; return focusedElement->isShadowIncludingInclusiveAncestorOf( caretPosition().anchorNode()); }
void FrameCaret::updateAppearance() { // Paint a block cursor instead of a caret in overtype mode unless the caret // is at the end of a line (in this case the FrameSelection will paint a // blinking caret as usual). bool paintBlockCursor = m_shouldShowBlockCursor && isActive(); if (paintBlockCursor) { // TODO(editing-dev): Use of updateStyleAndLayoutIgnorePendingStylesheets // needs to be audited. see http://crbug.com/590369 for more details. // In the long term, we should defer the update of the caret's appearance // to prevent synchronous layout. m_frame->document()->updateStyleAndLayoutIgnorePendingStylesheets(); if (isLogicalEndOfLine(createVisiblePosition(caretPosition()))) paintBlockCursor = false; } bool shouldBlink = !paintBlockCursor && shouldBlinkCaret(); // If the caret moved, stop the blink timer so we can restart with a // black caret in the new location. if (!shouldBlink || shouldStopBlinkingDueToTypingCommand(m_frame)) stopCaretBlinkTimer(); // Start blinking with a black caret. Be sure not to restart if we're // already blinking in the right location. if (shouldBlink) startBlinkCaret(); }
static void restartTerm(Accessible *newTerm, AccessibleText *newTextTerm) { char *c,*d; const char *e; long i,len; char *text; if (curFocus) finiTerm(); Accessible_ref(curFocus = newTerm); curTerm = newTextTerm; logMessage(LOG_DEBUG,"new term %p",curTerm); text = AccessibleText_getText(curTerm,0,LONG_MAX); curNumRows = 0; if (curRows) { for (i=0;i<curNumRows;i++) free(curRows[i]); free(curRows); } free(curRowLengths); c = text; while (*c) { curNumRows++; if (!(c = strchr(c,'\n'))) break; c++; } logMessage(LOG_DEBUG,"%ld rows",curNumRows); curRows = malloc(curNumRows * sizeof(*curRows)); curRowLengths = malloc(curNumRows * sizeof(*curRowLengths)); i = 0; curNumCols = 0; for (c = text; *c; c = d+1) { d = strchr(c,'\n'); if (d) *d = 0; e = c; curRowLengths[i] = (len = my_mbsrtowcs(NULL,&e,0,NULL)) + (d != NULL); if (len > curNumCols) curNumCols = len; else if (len < 0) { if (len==-2) logMessage(LOG_ERR,"unterminated sequence %s",c); else if (len==-1) logSystemError("mbrlen"); curRowLengths[i] = (len = -1) + (d != NULL); } curRows[i] = malloc((len + (d!=NULL)) * sizeof(*curRows[i])); e = c; my_mbsrtowcs(curRows[i],&e,len,NULL); if (d) curRows[i][len]='\n'; else break; i++; } logMessage(LOG_DEBUG,"%ld cols",curNumCols); SPI_freeString(text); caretPosition(AccessibleText_getCaretOffset(curTerm)); }
int TextRendererStringOffsetProvider::offset(Qt::Key /*key*/) { if (!vis_ || !vis_->itemOrChildHasFocus()) return -1; auto tc = dynamic_cast<Visualization::TextCursor*> (vis_->scene()->mainCursor()); return tc ? tc->caretPosition() : -1; }
void FrameCaret::invalidateCaretRect(bool forceInvalidation) { if (!m_caretRectDirty) return; m_caretRectDirty = false; DCHECK(caretPositionIsValidForDocument(*m_frame->document())); LayoutObject* layoutObject = nullptr; LayoutRect newRect; PositionWithAffinity currentCaretPosition = caretPosition(); if (isActive()) newRect = localCaretRectOfPosition(currentCaretPosition, layoutObject); Node* newNode = layoutObject ? layoutObject->node() : nullptr; // The current selected node |newNode| could be a child multiple levels below // its associated "anchor node" ancestor, so we reference and keep around the // anchor node for checking editability. // TODO(wkorman): Consider storing previous Position, rather than Node, and // making use of EditingUtilies::isEditablePosition() directly. Node* newAnchorNode = currentCaretPosition.position().parentAnchoredEquivalent().anchorNode(); if (newNode && newAnchorNode && newNode != newAnchorNode && newAnchorNode->layoutObject() && newAnchorNode->layoutObject()->isBox()) { newNode->layoutObject()->mapToVisualRectInAncestorSpace( toLayoutBoxModelObject(newAnchorNode->layoutObject()), newRect); } // It's possible for the timer to be inactive even though we want to // invalidate the caret. For example, when running as a layout test the // caret blink interval could be zero and thus |m_caretBlinkTimer| will // never be started. We provide |forceInvalidation| for use by paint // invalidation internals where we need to invalidate the caret regardless // of timer state. if (!forceInvalidation && !m_caretBlinkTimer.isActive() && newNode == m_previousCaretNode && newRect == m_previousCaretRect && m_caretVisibility == m_previousCaretVisibility) return; if (m_previousCaretAnchorNode && shouldRepaintCaret(*m_previousCaretAnchorNode)) { invalidateLocalCaretRect(m_previousCaretAnchorNode.get(), m_previousCaretRect); } if (newAnchorNode && shouldRepaintCaret(*newAnchorNode)) invalidateLocalCaretRect(newAnchorNode, newRect); m_previousCaretNode = newNode; m_previousCaretAnchorNode = newAnchorNode; m_previousCaretRect = newRect; m_previousCaretVisibility = m_caretVisibility; }
IntRect FrameCaret::absoluteCaretBounds() { DCHECK_NE(m_frame->document()->lifecycle().state(), DocumentLifecycle::InPaintInvalidation); DCHECK(!m_frame->document()->needsLayoutTreeUpdate()); DocumentLifecycle::DisallowTransitionScope disallowTransition( m_frame->document()->lifecycle()); if (!isActive()) { clearCaretRect(); } else { if (enclosingTextFormControl(caretPosition().position())) { if (isVisuallyEquivalentCandidate(caretPosition().position())) updateCaretRect(caretPosition()); else updateCaretRect(createVisiblePosition(caretPosition())); } else { updateCaretRect(createVisiblePosition(caretPosition())); } } return absoluteBoundsForLocalRect(caretPosition().anchorNode(), localCaretRectWithoutUpdate()); }
static void evListenerCB(const AccessibleEvent *event, void *user_data) { static int running = 0; struct evList *ev = malloc(sizeof(*ev)); AccessibleEvent_ref(event); AccessibleText *newText; ev->next = evs; ev->ev = event; evs = ev; int state_changed_focused; /* this is not atomic but we can only be recursively called within calls * to the lib */ if (running) return; else running = 1; while (evs) { pthread_mutex_lock(&updateMutex); /* pickup a list of events to handle */ ev = evs; evs = NULL; for (; ev; AccessibleEvent_unref(ev->ev), ev = ev->next) { event = ev->ev; state_changed_focused = !strcmp(event->type,"object:state-changed:focused"); if (state_changed_focused && !event->detail1) { if (event->source == curFocus) finiTerm(); } else if (!strcmp(event->type,"focus:") || (state_changed_focused && event->detail1)) { if (!(newText = Accessible_getText(event->source))) { if (curFocus) finiTerm(); } else { AccessibleRole role = Accessible_getRole(event->source); if (typeAll || (typeText && ((role == SPI_ROLE_TEXT) || (role == SPI_ROLE_PASSWORD_TEXT) || (role == SPI_ROLE_PARAGRAPH))) || (typeTerminal && (role == SPI_ROLE_TERMINAL))) { restartTerm(event->source, newText); } else { logMessage(LOG_DEBUG,"AT SPI widget not for us"); if (curFocus) finiTerm(); } } } else if (!strcmp(event->type,"object:text-caret-moved")) { if (event->source != curFocus) continue; logMessage(LOG_DEBUG,"caret move to %lu",event->detail1); caretPosition(event->detail1); } else if (!strcmp(event->type,"object:text-changed:delete")) { long x,y,toDelete = event->detail2; long length = 0, toCopy; long downTo; /* line that will provide what will follow x */ logMessage(LOG_DEBUG,"delete %lu from %lu",event->detail2,event->detail1); if (event->source != curFocus) continue; findPosition(event->detail1,&x,&y); downTo = y; if (downTo < curNumRows) length = curRowLengths[downTo]; while (x+toDelete >= length) { downTo++; if (downTo <= curNumRows - 1) length += curRowLengths[downTo]; else { /* imaginary extra line doesn't provide more length, and shouldn't need to ! */ if (x+toDelete > length) { logMessage(LOG_ERR,"deleting past end of text !"); /* discarding */ toDelete = length - x; } break; /* deleting up to end */ } } if (length-toDelete>0) { /* still something on line y */ if (y!=downTo) { curRowLengths[y] = length-toDelete; curRows[y]=realloc(curRows[y],curRowLengths[y]*sizeof(*curRows[y])); } if ((toCopy = length-toDelete-x)) memmove(curRows[y]+x,curRows[downTo]+curRowLengths[downTo]-toCopy,toCopy*sizeof(*curRows[downTo])); if (y==downTo) { curRowLengths[y] = length-toDelete; curRows[y]=realloc(curRows[y],curRowLengths[y]*sizeof(*curRows[y])); } } else { /* kills this line as well ! */ y--; } if (downTo>=curNumRows) /* imaginary extra lines don't need to be deleted */ downTo=curNumRows-1; delRows(y+1,downTo-y); caretPosition(AccessibleText_getCaretOffset(curTerm)); } else if (!strcmp(event->type,"object:text-changed:insert")) { long len=event->detail2,semilen,x,y; char *added; const char *adding,*c; logMessage(LOG_DEBUG,"insert %lu from %lu",event->detail2,event->detail1); if (event->source != curFocus) continue; findPosition(event->detail1,&x,&y); adding = c = added = AccessibleTextChangedEvent_getChangeString(event); if (x && (c = strchr(adding,'\n'))) { /* splitting line */ addRows(y,1); semilen=my_mbslen(adding,c+1-adding); curRowLengths[y]=x+semilen; if (x+semilen-1>curNumCols) curNumCols=x+semilen-1; /* copy beginning */ curRows[y]=malloc(curRowLengths[y]*sizeof(*curRows[y])); memcpy(curRows[y],curRows[y+1],x*sizeof(*curRows[y])); /* add */ my_mbsrtowcs(curRows[y]+x,&adding,semilen,NULL); len-=semilen; adding=c+1; /* shift end */ curRowLengths[y+1]-=x; memmove(curRows[y+1],curRows[y+1]+x,curRowLengths[y+1]*sizeof(*curRows[y+1])); x=0; y++; } while ((c = strchr(adding,'\n'))) { /* adding lines */ addRows(y,1); semilen=my_mbslen(adding,c+1-adding); curRowLengths[y]=semilen; if (semilen-1>curNumCols) curNumCols=semilen-1; curRows[y]=malloc(semilen*sizeof(*curRows[y])); my_mbsrtowcs(curRows[y],&adding,semilen,NULL); len-=semilen; adding=c+1; y++; } if (len) { /* still length to add on the line following it */ if (y==curNumRows) { /* It won't insert ending \n yet */ addRows(y,1); curRows[y]=NULL; curRowLengths[y]=0; } curRowLengths[y] += len; curRows[y]=realloc(curRows[y],curRowLengths[y]*sizeof(*curRows[y])); memmove(curRows[y]+x+len,curRows[y]+x,(curRowLengths[y]-(x+len))*sizeof(*curRows[y])); my_mbsrtowcs(curRows[y]+x,&adding,len,NULL); if (curRowLengths[y]-(curRows[y][curRowLengths[y]-1]=='\n')>curNumCols) curNumCols=curRowLengths[y]-(curRows[y][curRowLengths[y]-1]=='\n'); } SPI_freeString(added); caretPosition(AccessibleText_getCaretOffset(curTerm)); } else logMessage(LOG_INFO,"event %s, source %p, detail1 %lu detail2 %lu",event->type,event->source,event->detail1,event->detail2); } pthread_mutex_unlock(&updateMutex); } running = 0; }