webengine/osswebengine/WebKit/s60/misc/WebTabbedNavigation.cpp
changeset 15 60c5402cb945
parent 10 a359256acfc6
child 25 0ed94ceaa377
--- a/webengine/osswebengine/WebKit/s60/misc/WebTabbedNavigation.cpp	Thu Sep 24 12:53:48 2009 +0300
+++ b/webengine/osswebengine/WebKit/s60/misc/WebTabbedNavigation.cpp	Mon Oct 26 08:28:45 2009 +0200
@@ -81,14 +81,6 @@
 void WebTabbedNavigation::updateCursorPosition(const TPoint& pos)
 {
     m_focusPosition = pos;
-    WebFrame* frame = StaticObjectsContainer::instance()->webCursor()->getFrameAtPoint(pos);
-    TPoint point(frame->frameView()->viewCoordsInFrameCoords(pos));
-
-    Element* node = core(frame)->document()->elementFromPoint(point.iX, point.iY);
-    if (node->isFocusable() && !node->hasTagName(iframeTag) && !node->hasTagName(frameTag))
-        m_webView->page()->focusController()->setFocusedNode(node, core(frame));
-    else
-        m_webView->page()->focusController()->setFocusedNode(NULL, core(frame));
     m_selectedElementRect.SetRect(pos.iX, pos.iY, pos.iX + 1, pos.iY + 1);
 }
 
@@ -101,88 +93,202 @@
     m_focusPosition = StaticObjectsContainer::instance()->webCursor()->position();
 }
 
-bool WebTabbedNavigation::navigate(int horizontalDir, int verticalDir)
+
+void WebTabbedNavigation::resetNavigationIfNeeded(TPoint& contentPos, TSize& contentSize, 
+                                                  Frame* focusedFrame, int horizontalDir, int verticalDir)
 {
-    if (handleSelectElementScrolling(m_webView, verticalDir)) {
-        StaticObjectsContainer::instance()->webCursor()->cursorUpdate(true);
-        return true;
-    }
-    // DOM can be changed so check if we are still inside the document
-    // If not reset tabbed navigation parameters to the closest point in document.
-    TSize contentSize = m_webView->mainFrame()->frameView()->contentSize();
-    TPoint contentPos = m_webView->mainFrame()->frameView()->contentPos();
-    TRect docRect = TRect(contentPos, contentSize - contentPos);
+    TPoint docBrViewCoord = kit(focusedFrame)->frameView()->frameCoordsInViewCoords(
+                                           TPoint(contentSize.iWidth, contentSize.iHeight));
+    TRect docRect = TRect(TPoint(0,0), docBrViewCoord);
     if (!docRect.Contains(m_focusPosition)) {
         TInt viewW = m_webView->Rect().Width();
         TInt viewH = m_webView->Rect().Height();
-        if (m_focusPosition.iX > contentSize.iWidth || 
-            m_focusPosition.iX < contentPos.iX) {
+        if (m_focusPosition.iX > contentSize.iWidth) {
             m_focusPosition.iX = (horizontalDir == -1) ? contentPos.iX + viewW : contentPos.iX;
         }
         
-        if (m_focusPosition.iY > contentSize.iHeight || 
-            m_focusPosition.iY < contentPos.iY) {
+        if (m_focusPosition.iY > contentSize.iHeight) {
             m_focusPosition.iY = (verticalDir == -1) ? contentPos.iY + viewH : contentPos.iY;
         }
-
-        m_selectedElementRect.SetRect(m_focusPosition.iX, m_focusPosition.iY, m_focusPosition.iX, m_focusPosition.iY);
+    
+        m_selectedElementRect.SetRect(m_focusPosition.iX, m_focusPosition.iY, 
+                                      m_focusPosition.iX, m_focusPosition.iY);
         m_node = NULL;    
     }
+}
+
+
+TPoint WebTabbedNavigation::focusPointFromFocusedNode(Frame* frame, int horizontalDir, int verticalDir)
+{
+    TPoint oldFocusPoint(m_focusPosition);
+    TPoint focusPosition(m_focusPosition);
+    TRect  selectedElementRect(m_selectedElementRect);
     
-    bool ret = m_firstNavigationOnPage;
-    Frame* focusedFrame = m_webView->page()->focusController()->focusedFrame();
-    if (focusedFrame == NULL) focusedFrame = m_webView->page()->mainFrame();
-    if (focusedFrame->document()) {
-        Node* focusNode = focusedFrame->document()->focusedNode();
-        if (focusNode) {
-            m_node = focusNode;
-            m_selectedElementRect = focusNode->getRect().Rect();
-            Frame* frame = focusNode->document()->frame();
-            m_selectedElementRect = TRect(kit(frame)->frameView()->frameCoordsInViewCoords(m_selectedElementRect.iTl), 
-                kit(frame)->frameView()->frameCoordsInViewCoords(m_selectedElementRect.iBr));
+    if (frame->document()) {
+        Node* focusedNode = frame->document()->focusedNode();
+        if (focusedNode) {
+            m_node = focusedNode;
+            selectedElementRect = focusedNode->getRect().Rect();
+            Frame* frame = focusedNode->document()->frame();
+            selectedElementRect = kit(frame)->frameView()->frameCoordsInViewCoords(selectedElementRect);
+
+            // Move the focus to the visible edge of the current object
+            TRect elemVisibleRect = selectedElementRect;
+            if (elemVisibleRect.Intersects(m_webView->Rect())) {
+                 
+                elemVisibleRect.Intersection(m_webView->Rect());
+                if (horizontalDir == -1) {
+                    focusPosition.iX = elemVisibleRect.iTl.iX;
+                }
+                else if (horizontalDir == 1) {
+                    focusPosition.iX = elemVisibleRect.iBr.iX;
+                }
+                
+                if (verticalDir == -1) {
+                    focusPosition.iY = elemVisibleRect.iTl.iY;
+                }
+                else if (verticalDir == 1) {
+                    focusPosition.iY = elemVisibleRect.iBr.iY;
+                }
+                
+                if ((verticalDir == 0) && (horizontalDir == 0)) {
+                    focusPosition = elemVisibleRect.Center();
+                }
+                m_focusPosition = focusPosition;
+            }
+            m_selectedElementRect = selectedElementRect;
         }
     }
-    TPoint oldFocusPoint(m_focusPosition);
-    // Move the focus to the edge of the current object
+    return oldFocusPoint;
+}
+
+
+void WebTabbedNavigation::calcSearchViewRect(int horizontalDir, int verticalDir, TRect& view)
+{
+    
+    TPoint br;
+    TPoint viewBr = m_webView->Rect().iBr;
+    br.iX = viewBr.iX * KMaxJumpPercent / m_webView->scalingFactor();
+    br.iY = viewBr.iY * KMaxJumpPercent / m_webView->scalingFactor();
+    // define the view rect where we are looking for a match
+    int x, y;
     if (horizontalDir == -1) {
-        m_focusPosition.iX = m_selectedElementRect.iTl.iX;
+        view.SetRect(m_focusPosition.iX - br.iX, m_focusPosition.iY - br.iY, 
+                m_focusPosition.iX , m_focusPosition.iY + br.iY);
     }
     else if (horizontalDir == 1) {
-        m_focusPosition.iX = m_selectedElementRect.iBr.iX;
+        view.SetRect(m_focusPosition.iX, m_focusPosition.iY - br.iY, 
+                m_focusPosition.iX + br.iX, m_focusPosition.iY + br.iY);
     }
-    if (verticalDir == -1) {
-        m_focusPosition.iY = m_selectedElementRect.iTl.iY;
+    else if (verticalDir == -1) {
+        view.SetRect(m_focusPosition.iX - br.iX, m_focusPosition.iY - br.iY, 
+                     m_focusPosition.iX + br.iX, m_focusPosition.iY);
     }
     else if (verticalDir == 1) {
-        m_focusPosition.iY = m_selectedElementRect.iBr.iY;
+        view.SetRect(m_focusPosition.iX - br.iX, m_focusPosition.iY, 
+                     m_focusPosition.iX + br.iX, m_focusPosition.iY + br.iY);
     }
-    wkDebug()<<"WebTabbedNavigation::navigate. x = "<<m_focusPosition.iX<<" y = "<<m_focusPosition.iY<<flush;
-    wkDebug()<<"x1 = "<<m_selectedElementRect.iTl.iX<<" y1 = "<<m_selectedElementRect.iTl.iY<<" x2 = "<<m_selectedElementRect.iBr.iX<<" y2 = "<<m_selectedElementRect.iBr.iY<<flush;
+}
+
+TPoint WebTabbedNavigation::updateCursorPosAfterScroll(Frame* frame, int horizontalDir, int verticalDir)
+{
+    WebCursor* cursor = StaticObjectsContainer::instance()->webCursor();
+    TPoint oldPos = cursor->position();
+    
+    focusPointFromFocusedNode(frame, horizontalDir, verticalDir);
+    if (m_node && !m_selectedElementRect.Intersects(m_webView->Rect())) {
+        static_cast<Node*>(m_node)->document()->setFocusedNode(NULL);
+        m_node = NULL;
+    }
     
-    // Adjust the move
-    TPoint br;
-    br.iX = m_webView->Rect().iBr.iX * KMaxJumpPercent / m_webView->scalingFactor();
-    br.iY = m_webView->Rect().iBr.iY * KMaxJumpPercent / m_webView->scalingFactor();
+    cursor->setPosition(m_focusPosition - TPoint(horizontalDir, verticalDir));
+    return oldPos;
+}
+
+bool WebTabbedNavigation::navigate(int horizontalDir, int verticalDir)
+{
+    WebCursor* cursor = StaticObjectsContainer::instance()->webCursor();
+    if (handleSelectElementScrolling(m_webView, verticalDir)) {
+        cursor->cursorUpdate(true);
+        return true;
+    }
+    Frame* mainFrame = core(m_webView->mainFrame());
+    FocusController* focusController = m_webView->page()->focusController();
+    Frame* focusedFrame = focusController->focusedOrMainFrame();        
+    TSize contentSize = kit(focusedFrame)->frameView()->contentSize();
+    TPoint contentPos = kit(focusedFrame)->frameView()->contentPos();
+    
+    // DOM can be changed so check if we are still inside the document
+    // If not reset tabbed navigation parameters to the closest point in document.
+    resetNavigationIfNeeded(contentPos, contentSize, focusedFrame, horizontalDir, verticalDir);
+    
+    
+    bool ret = m_firstNavigationOnPage;
+    
+    if (focusedFrame == NULL) focusedFrame = mainFrame;
+    TPoint oldFocusPoint =  focusPointFromFocusedNode(focusedFrame, horizontalDir, verticalDir);
     TRect view;
-    // define the view rect where we are looking for a match
-    if (horizontalDir == -1) {
-        view.SetRect(m_focusPosition.iX - br.iX, m_focusPosition.iY - br.iY, m_focusPosition.iX, m_focusPosition.iY + br.iY);
-    }
-    else if (horizontalDir == 1) {
-        view.SetRect(m_focusPosition.iX, m_focusPosition.iY - br.iY, m_focusPosition.iX + br.iX, m_focusPosition.iY + br.iY);
-    }
-    else if (verticalDir == -1) {
-        view.SetRect(m_focusPosition.iX - br.iX, m_focusPosition.iY - br.iY, m_focusPosition.iX + br.iX, m_focusPosition.iY);
-    }
-    else if (verticalDir == 1) {
-        view.SetRect(m_focusPosition.iX - br.iX, m_focusPosition.iY, m_focusPosition.iX + br.iX, m_focusPosition.iY + br.iY);
-    }
-    //wkDebug()<<"view x1 = "<<view.iTl.iX<<" y1 = "<<view.iTl.iY<<" x2 = "<<view.iBr.iX<<" y2 = "<<view.iBr.iY<<flush;
-    // walk all focusable nodes
-    Frame* f = m_webView->page()->mainFrame();
+    calcSearchViewRect(horizontalDir, verticalDir, view);
+
+    // walk all focusable nodes    
     TPoint selectedPoint(0, 0);
     TRect selectedRect(0, 0, 0, 0);
-    Node* selectedNode = NULL;
+    Node* selectedNode = bestFitFocusableNode(mainFrame, view, horizontalDir, verticalDir, 
+                                              selectedPoint, selectedRect);
+    
+    // Remember new selection
+    contentPos = kit(mainFrame)->frameView()->contentPos();
+    if (selectedNode) {
+        // Found an element to jump to
+        m_selectedElementRect = selectedRect;
+        m_focusPosition = selectedPoint;
+        m_node = selectedNode;
+        selectedNode->document()->setFocusedNode(selectedNode);
+        m_webView->page()->focusController()->setFocusedFrame(selectedNode->document()->frame());
+        
+        // And scroll to the selected element
+        RenderLayer *layer = selectedNode->renderer()->enclosingLayer();
+        if (layer) {
+            layer->scrollRectToVisible(selectedNode->getRect(), RenderLayer::gAlignCenterIfNeeded, RenderLayer::gAlignCenterIfNeeded);
+            WebFrameView* fv = kit(selectedNode->document()->frame())->frameView();
+            TRect newRect = fv->frameCoordsInViewCoords(selectedNode->getRect().Rect());
+            selectedRect = newRect;
+            selectedPoint = potentialFocusPoint(horizontalDir, verticalDir, newRect);
+            m_selectedElementRect = selectedRect;
+            m_focusPosition = selectedPoint;
+                        
+            cursor->updatePositionAndElemType(m_focusPosition);
+            // special handling for Select-Multi
+            if (m_webView->focusedElementType() == TBrCtlDefs::EElementSelectMultiBox) {
+                handleMultiSelect(horizontalDir, verticalDir);
+            }
+            
+            m_webView->sendMouseEventToEngine(TPointerEvent::EMove, cursor->position(), mainFrame);
+            ret = true;
+        }
+    }
+    else {
+        if (!m_firstNavigationOnPage) {
+            TInt vWidth = m_webView->Rect().Width();
+            TInt vHeight = m_webView->Rect().Height();
+            kit(mainFrame)->frameView()->scrollTo(contentPos + 
+                                                  TPoint(horizontalDir * vWidth / KScrollWhenNotFound, 
+                                                         verticalDir * vHeight / KScrollWhenNotFound));
+          
+            cursor->updatePositionAndElemType(m_focusPosition - TPoint(horizontalDir, verticalDir));
+        }
+    }
+    cursor->cursorUpdate(true);
+    return ret;
+}
+
+
+
+Node* WebTabbedNavigation::bestFitFocusableNode(Frame* topFrame, TRect& viewRect, int horizontalDir, int verticalDir, //input
+                                                TPoint& selectedPoint, TRect& selectedRect ) //output
+{
+   Node* selectedNode = NULL;
+   Frame* f = topFrame;
     while ( f ) {
         PassRefPtr<HTMLCollection> elements = f->document()->all();   
         TRect frameRect = kit(f)->frameView()->rectInGlobalCoords();
@@ -191,13 +297,11 @@
             if (n->isFocusable() && n->isElementNode() && !n->hasTagName(iframeTag) && !n->hasTagName(frameTag)) {
                 // Does the node intersect with the view rect?
                 TRect nodeRect = n->getRect().Rect();
-                //wkDebug()<<"Each node rect x1 = "<<nodeRect.iTl.iX<<" y1 = "<<nodeRect.iTl.iY<<" x2 = "<<nodeRect.iBr.iX<<" y2 = "<<nodeRect.iBr.iY<<flush;
-                nodeRect = TRect(kit(f)->frameView()->frameCoordsInViewCoords(nodeRect.iTl), 
-                    kit(f)->frameView()->frameCoordsInViewCoords(nodeRect.iBr));
-                if (nodeRect.Intersects(view)) {
+                nodeRect = kit(f)->frameView()->frameCoordsInViewCoords(nodeRect);
+                if (nodeRect.Intersects(viewRect) && 
+                    shouldConsiderRect(nodeRect, viewRect, horizontalDir, verticalDir)) {
                     // Compare nodes and select the best fit
                     TPoint newFocusPoint = potentialFocusPoint(horizontalDir, verticalDir, nodeRect);
-                    wkDebug()<<"Matching node rect x1 = "<<nodeRect.iTl.iX<<" y1 = "<<nodeRect.iTl.iY<<" x2 = "<<nodeRect.iBr.iX<<" y2 = "<<nodeRect.iBr.iY<<flush;
                     if (selectNode(horizontalDir, verticalDir, selectedRect, nodeRect, selectedPoint, newFocusPoint)) {
                         // found a better fit
                         selectedNode = n;
@@ -209,96 +313,55 @@
         } // for (Node* n = elements->firstItem(); n; n = elements->nextItem())
         f = f->tree()->traverseNext();
     } // while ( f )
-    // Remember new selection
-    contentPos = m_webView->mainFrame()->frameView()->contentPos();
-    if (selectedNode) {
-        // Found an element to jump to
-        m_selectedElementRect = selectedRect;
-        m_focusPosition = selectedPoint;
-        m_node = selectedNode;
-        selectedNode->document()->setFocusedNode(selectedNode);
-         m_webView->page()->focusController()->setFocusedFrame(selectedNode->document()->frame());
-        // And scroll to the selected element
-        RenderLayer *layer = selectedNode->renderer()->enclosingLayer();
-        if (layer) {
-            layer->scrollRectToVisible(selectedNode->getRect(), RenderLayer::gAlignCenterIfNeeded, RenderLayer::gAlignCenterIfNeeded);
-            TRect newRect = TRect(kit(selectedNode->document()->frame())->frameView()->frameCoordsInViewCoords(selectedNode->getRect().Rect().iTl), 
-                kit(selectedNode->document()->frame())->frameView()->frameCoordsInViewCoords(selectedNode->getRect().Rect().iBr));
-            selectedPoint += (newRect.iTl - selectedRect.iTl);
-            m_focusPosition = selectedPoint;
-            selectedRect = newRect;
-            m_selectedElementRect = selectedRect;
+    return selectedNode;
+}
+
 
-            int x, y;
-            selectedNode->renderer()->absolutePosition(x, y);
-            Vector<IntRect> rects;
-            selectedNode->renderer()->absoluteRects(rects, x, y);
-            WebFrameView* fv = kit(selectedNode->document()->frame())->frameView();
-            if (rects.size() > 0) {
-                 selectedPoint = TPoint(rects[0].x(), rects[0].y());
-                selectedPoint = fv->frameCoordsInViewCoords(selectedPoint);
-            }
-            StaticObjectsContainer::instance()->webCursor()->updatePositionAndElemType(selectedPoint);
-            // special handling for Select-Multi
-            if (m_webView->focusedElementType() == TBrCtlDefs::EElementSelectMultiBox) {
-                Element* e = static_cast<Element*>(m_node);
-                if (e->isControl()) {
-                    HTMLGenericFormElement* ie = static_cast<HTMLGenericFormElement*>( e );
-                    if (ie->type() == "select-multiple") {
-                        RenderListBox* render = static_cast<RenderListBox*>(e->renderer());
-                        HTMLSelectElement* selectElement = static_cast<HTMLSelectElement*>( e );
-                        IntRect itemRect = render->itemRect(0, 0, 0);
-                        TPoint cursorPoint(StaticObjectsContainer::instance()->webCursor()->position());
-                        int gap = (20 * m_webView->scalingFactor()) / 100;
-                        cursorPoint.iX = max(m_focusPosition.iX,  m_selectedElementRect.iTl.iX + gap);
-                        cursorPoint.iX = std::min(cursorPoint.iX,  m_selectedElementRect.iBr.iX - gap);
-                        if (verticalDir == -1) {
-                            cursorPoint.iY -= (itemRect.height() * m_webView->scalingFactor()) / 125;
-                        }
-                        if (cursorPoint != StaticObjectsContainer::instance()->webCursor()->position()) {
-                            StaticObjectsContainer::instance()->webCursor()->setPosition(cursorPoint);
-                        }
-                    }
-                }
-            }
-            TPointerEvent event;
-            event.iPosition = StaticObjectsContainer::instance()->webCursor()->position();
-            event.iModifiers = 0;
-            event.iType = TPointerEvent::EMove;
-            core(m_webView->mainFrame())->eventHandler()->handleMouseMoveEvent(PlatformMouseEvent(event));            
-            wkDebug()<<"Focus position x = "<<selectedPoint.iX<<" y = "<<selectedPoint.iY<<flush;
-            ret = true;
-        }
+bool WebTabbedNavigation::shouldConsiderRect(TRect& rect, TRect& searchRect, int horizontalDir, int verticalDir)
+{
+    bool considerX = false;
+    bool considerY = false;
+    
+    if (horizontalDir == 1) {
+        considerX = (rect.iTl.iX >= searchRect.iTl.iX);
+    }
+    else if (horizontalDir == -1) {
+        considerX = (rect.iBr.iX <= searchRect.iBr.iX);
+    }
+    
+    if (verticalDir == 1) {
+        considerY = (rect.iTl.iY >= searchRect.iTl.iY);
+    }
+    else if (verticalDir == -1) {
+        considerY = (rect.iBr.iY <= searchRect.iBr.iY);
     }
-    else {
-        if (!m_firstNavigationOnPage) {
-            m_webView->mainFrame()->frameView()->scrollTo(contentPos + TPoint(horizontalDir * m_webView->Rect().Width() / KScrollWhenNotFound, verticalDir * m_webView->Rect().Height() / KScrollWhenNotFound));
-            TPoint diff(m_webView->mainFrame()->frameView()->contentPos() - contentPos);
-            if (diff.iX || diff.iY) {
-                Frame* focusedFrame = m_webView->page()->focusController()->focusedFrame();
-                if (focusedFrame == NULL) focusedFrame = m_webView->page()->mainFrame();
-                Node* focusNode = focusedFrame->document()->focusedNode();
-                if (focusNode) {
-                    TRect selectedRect = focusNode->getRect().Rect();
-                    selectedRect = TRect(kit(focusedFrame)->frameView()->frameCoordsInViewCoords(selectedRect.iTl), 
-                        kit(focusedFrame)->frameView()->frameCoordsInViewCoords(selectedRect.iBr));
-                    if (!selectedRect.Intersects(kit(focusedFrame)->frameView()->visibleRect()))
-                        m_webView->page()->focusController()->setFocusedNode(NULL,0);
-                }
-                m_selectedElementRect.Move(diff);
-                m_focusPosition = oldFocusPoint + diff;
-                m_node = NULL;
-                StaticObjectsContainer::instance()->webCursor()->updatePositionAndElemType(m_focusPosition - m_webView->mainFrame()->frameView()->contentPos());
-                ret = true;
-            }
-            else
-            {
-                m_focusPosition = oldFocusPoint;
+    
+    return considerX || considerY;
+    
+}
+
+void WebTabbedNavigation::handleMultiSelect(int horizontalDir, int verticalDir)
+{
+    WebCursor* cursor = StaticObjectsContainer::instance()->webCursor();
+    Node* n = static_cast<Node*>(m_node);
+    WebFrameView* fv = kit(n->document()->frame())->frameView();
+    Element* e = static_cast<Element*>(m_node);
+    if (e->isControl()) {
+        HTMLGenericFormElement* ie = static_cast<HTMLGenericFormElement*>( e );
+        if (ie->type() == "select-multiple") {
+            RenderListBox* render = static_cast<RenderListBox*>(e->renderer());
+            HTMLSelectElement* selectElement = static_cast<HTMLSelectElement*>( e );
+            TRect itemRect = render->itemRect(0, 0, render->indexOffset()).Rect();
+            TRect itemRectViewCoord = fv->frameCoordsInViewCoords(itemRect);
+            itemRectViewCoord.Move(m_selectedElementRect.iTl);
+            itemRectViewCoord.Intersection(fv->topView()->Rect());           
+            
+            TPoint cursorPoint = itemRect.Center() + m_selectedElementRect.iTl;
+            if (cursorPoint != cursor->position()) {
+                cursor->setPosition(cursorPoint);
             }
         }
     }
-    StaticObjectsContainer::instance()->webCursor()->cursorUpdate(true);
-    return ret;
 }
 
 bool WebTabbedNavigation::selectNode(int horizontalDir, int verticalDir, TRect& selectedRect, TRect& newNodeRect, TPoint& selectedPoint, TPoint& newFocusPoint)
@@ -308,7 +371,7 @@
     }
     int selectedDist = distanceFunction(horizontalDir, verticalDir, selectedRect, selectedPoint);
     int newDist = distanceFunction(horizontalDir, verticalDir, newNodeRect, newFocusPoint);
-    wkDebug()<<"WebTabbedNavigation::selectNode. selected x = "<<selectedPoint.iX<<" y = "<<selectedPoint.iY<<" new x = "<<newFocusPoint.iX<<" y = "<<newFocusPoint.iY<<"old distance = "<<selectedDist<<" new distance = "<<newDist<<flush;
+
     return newDist < selectedDist;
 }
 
@@ -399,3 +462,4 @@
     Math::Int(o, sqrt(overlap));
     return ed + sameAxisDist + 2 * otherAxisDist - o;
 }
+