Changeset View
Changeset View
Standalone View
Standalone View
src/view/kateviewhelpers.cpp
Show First 20 Lines • Show All 1429 Lines • ▼ Show 20 Line(s) | 1424 | KateIconBorder::KateIconBorder(KateViewInternal *internalView, QWidget *parent) | |||
---|---|---|---|---|---|
1430 | , m_lineNumbersOn(false) | 1430 | , m_lineNumbersOn(false) | ||
1431 | , m_relLineNumbersOn(false) | 1431 | , m_relLineNumbersOn(false) | ||
1432 | , m_updateRelLineNumbers(false) | 1432 | , m_updateRelLineNumbers(false) | ||
1433 | , m_foldingMarkersOn(false) | 1433 | , m_foldingMarkersOn(false) | ||
1434 | , m_dynWrapIndicatorsOn(false) | 1434 | , m_dynWrapIndicatorsOn(false) | ||
1435 | , m_annotationBorderOn(false) | 1435 | , m_annotationBorderOn(false) | ||
1436 | , m_updatePositionToArea(true) | 1436 | , m_updatePositionToArea(true) | ||
1437 | , m_annotationItemDelegate(new KateAnnotationItemDelegate(m_viewInternal, this)) | 1437 | , m_annotationItemDelegate(new KateAnnotationItemDelegate(m_viewInternal, this)) | ||
1438 | , m_foldingPreview(nullptr) | | |||
1439 | , m_foldingRange(nullptr) | | |||
1440 | , m_nextHighlightBlock(-2) | | |||
1441 | , m_currentBlockLine(-1) | | |||
1442 | { | 1438 | { | ||
1443 | setAcceptDrops(true); | 1439 | setAcceptDrops(true); | ||
1444 | setAttribute(Qt::WA_StaticContents); | 1440 | setAttribute(Qt::WA_StaticContents); | ||
1445 | setSizePolicy(QSizePolicy::Fixed, QSizePolicy::Minimum); | 1441 | setSizePolicy(QSizePolicy::Fixed, QSizePolicy::Minimum); | ||
1446 | setMouseTracking(true); | 1442 | setMouseTracking(true); | ||
1447 | m_doc->setMarkDescription(MarkInterface::markType01, i18n("Bookmark")); | 1443 | m_doc->setMarkDescription(MarkInterface::markType01, i18n("Bookmark")); | ||
1448 | m_doc->setMarkPixmap(MarkInterface::markType01, QIcon::fromTheme(QStringLiteral("bookmarks")).pixmap(32, 32)); | 1444 | m_doc->setMarkPixmap(MarkInterface::markType01, QIcon::fromTheme(QStringLiteral("bookmarks")).pixmap(32, 32)); | ||
1449 | 1445 | | |||
1450 | connect(m_annotationItemDelegate, &AbstractAnnotationItemDelegate::sizeHintChanged, | 1446 | connect(m_annotationItemDelegate, &AbstractAnnotationItemDelegate::sizeHintChanged, | ||
1451 | this, &KateIconBorder::updateAnnotationBorderWidth); | 1447 | this, &KateIconBorder::updateAnnotationBorderWidth); | ||
1452 | 1448 | | |||
1453 | updateFont(); | 1449 | updateFont(); | ||
1454 | 1450 | | |||
1455 | m_delayFoldingHlTimer.setSingleShot(true); | 1451 | m_antiFlickerTimer.setSingleShot(true); | ||
1456 | m_delayFoldingHlTimer.setInterval(150); | 1452 | m_antiFlickerTimer.setInterval(300); | ||
1457 | connect(&m_delayFoldingHlTimer, SIGNAL(timeout()), this, SLOT(showBlock())); | 1453 | connect(&m_antiFlickerTimer, &QTimer::timeout, this, &KateIconBorder::highlightFolding); | ||
1458 | 1454 | | |||
1459 | // user interaction (scrolling) hides e.g. preview | 1455 | // user interaction (scrolling) hides e.g. preview | ||
1460 | connect(m_view, SIGNAL(displayRangeChanged(KTextEditor::ViewPrivate*)), this, SLOT(displayRangeChanged())); | 1456 | connect(m_view, SIGNAL(displayRangeChanged(KTextEditor::ViewPrivate*)), this, SLOT(displayRangeChanged())); | ||
1461 | } | 1457 | } | ||
1462 | 1458 | | |||
1463 | KateIconBorder::~KateIconBorder() | 1459 | KateIconBorder::~KateIconBorder() | ||
1464 | { | 1460 | { | ||
1465 | delete m_foldingPreview; | 1461 | delete m_foldingPreview; | ||
▲ Show 20 Lines • Show All 689 Lines • ▼ Show 20 Line(s) | 2143 | if (area != IconBorder && area != AnnotationBorder) { | |||
2155 | m_viewInternal->mousePressEvent(&forward); | 2151 | m_viewInternal->mousePressEvent(&forward); | ||
2156 | } | 2152 | } | ||
2157 | return e->accept(); | 2153 | return e->accept(); | ||
2158 | } | 2154 | } | ||
2159 | 2155 | | |||
2160 | QWidget::mousePressEvent(e); | 2156 | QWidget::mousePressEvent(e); | ||
2161 | } | 2157 | } | ||
2162 | 2158 | | |||
2163 | void KateIconBorder::showDelayedBlock(int line) | 2159 | void KateIconBorder::highlightFoldingDelayed(int line) | ||
2164 | { | 2160 | { | ||
2165 | // save the line over which the mouse hovers | 2161 | if ((line == m_currentLine) || (line >= m_doc->buffer().lines())) { | ||
2166 | // either we start the timer for delay, or we show the block immediately | 2162 | return; | ||
2167 | // if the moving range already exists | | |||
2168 | m_nextHighlightBlock = line; | | |||
2169 | if (!m_foldingRange) { | | |||
2170 | if (!m_delayFoldingHlTimer.isActive()) { | | |||
2171 | m_delayFoldingHlTimer.start(); | | |||
2172 | } | | |||
2173 | } else { | | |||
2174 | showBlock(); | | |||
2175 | } | | |||
2176 | } | 2163 | } | ||
2177 | 2164 | | |||
2178 | void KateIconBorder::showBlock() | 2165 | m_currentLine = line; | ||
2179 | { | 2166 | | ||
2180 | if (m_nextHighlightBlock == m_currentBlockLine) { | 2167 | if (m_foldingRange) { | ||
2181 | return; | 2168 | // We are for a while in the folding area, no need for delay | ||
2169 | highlightFolding(); | ||||
2170 | | ||||
2171 | } else if (!m_antiFlickerTimer.isActive()) { | ||||
2172 | m_antiFlickerTimer.start(); | ||||
2182 | } | 2173 | } | ||
2183 | m_currentBlockLine = m_nextHighlightBlock; | | |||
2184 | if (m_currentBlockLine >= m_doc->buffer().lines()) { | | |||
2185 | return; | | |||
2186 | } | 2174 | } | ||
2187 | 2175 | | |||
2176 | void KateIconBorder::highlightFolding() | ||||
2177 | { | ||||
2188 | /** | 2178 | /** | ||
2189 | * compute to which folding range we belong | 2179 | * compute to which folding range we belong | ||
2190 | * FIXME: optimize + perhaps have some better threshold or use timers! | 2180 | * FIXME: optimize + perhaps have some better threshold or use timers! | ||
2191 | */ | 2181 | */ | ||
2192 | KTextEditor::Range newRange = KTextEditor::Range::invalid(); | 2182 | KTextEditor::Range newRange = KTextEditor::Range::invalid(); | ||
2193 | for (int line = m_currentBlockLine; line >= qMax(0, m_currentBlockLine - 1024); --line) { | 2183 | for (int line = m_currentLine; line >= qMax(0, m_currentLine - 1024); --line) { | ||
2194 | /** | 2184 | /** | ||
2195 | * try if we have folding range from that line, should be fast per call | 2185 | * try if we have folding range from that line, should be fast per call | ||
2196 | */ | 2186 | */ | ||
2197 | KTextEditor::Range foldingRange = m_doc->buffer().computeFoldingRangeForStartLine(line); | 2187 | KTextEditor::Range foldingRange = m_doc->buffer().computeFoldingRangeForStartLine(line); | ||
2198 | if (!foldingRange.isValid()) { | 2188 | if (!foldingRange.isValid()) { | ||
2199 | continue; | 2189 | continue; | ||
2200 | } | 2190 | } | ||
2201 | 2191 | | |||
2202 | /** | 2192 | /** | ||
2203 | * does the range reach us? | 2193 | * does the range reach us? | ||
2204 | */ | 2194 | */ | ||
2205 | if (foldingRange.overlapsLine(m_currentBlockLine)) { | 2195 | if (foldingRange.overlapsLine(m_currentLine)) { | ||
2206 | newRange = foldingRange; | 2196 | newRange = foldingRange; | ||
2207 | break; | 2197 | break; | ||
2208 | } | 2198 | } | ||
2209 | } | 2199 | } | ||
2210 | 2200 | | |||
2211 | if (newRange.isValid() && m_foldingRange && *m_foldingRange == newRange) { | 2201 | if (newRange.isValid() && m_foldingRange && *m_foldingRange == newRange) { | ||
2212 | // new range equals the old one, nothing to do. | 2202 | // new range equals the old one, nothing to do. | ||
2213 | return; | 2203 | return; | ||
2214 | } else { // the ranges differ, delete the old, if it exists | 2204 | } | ||
2205 | | ||||
2206 | // the ranges differ, delete the old, if it exists | ||||
2215 | delete m_foldingRange; | 2207 | delete m_foldingRange; | ||
2216 | m_foldingRange = nullptr; | 2208 | m_foldingRange = nullptr; | ||
2217 | } | 2209 | // New range, new preview! | ||
2210 | delete m_foldingPreview; | ||||
2211 | | ||||
2212 | bool showPreview = false; | ||||
2218 | 2213 | | |||
2219 | if (newRange.isValid()) { | 2214 | if (newRange.isValid()) { | ||
2215 | // When next line is not visible we have a folded range, only then we want a preview! | ||||
2216 | showPreview = !m_view->textFolding().isLineVisible(newRange.start().line() + 1); | ||||
2217 | | ||||
2220 | //qCDebug(LOG_KTE) << "new folding hl-range:" << newRange; | 2218 | //qCDebug(LOG_KTE) << "new folding hl-range:" << newRange; | ||
2221 | m_foldingRange = m_doc->newMovingRange(newRange, KTextEditor::MovingRange::ExpandRight); | 2219 | m_foldingRange = m_doc->newMovingRange(newRange, KTextEditor::MovingRange::ExpandRight); | ||
2222 | KTextEditor::Attribute::Ptr attr(new KTextEditor::Attribute()); | 2220 | KTextEditor::Attribute::Ptr attr(new KTextEditor::Attribute()); | ||
2223 | 2221 | | |||
2224 | /** | 2222 | /** | ||
2225 | * create highlighting color | 2223 | * create highlighting color | ||
2226 | * we avoid alpha as overpainting leads to ugly lines (https://bugreports.qt.io/browse/QTBUG-66036) | 2224 | * we avoid alpha as overpainting leads to ugly lines (https://bugreports.qt.io/browse/QTBUG-66036) | ||
2227 | */ | 2225 | */ | ||
2228 | attr->setBackground(QBrush(m_view->renderer()->config()->foldingColor())); | 2226 | attr->setBackground(QBrush(m_view->renderer()->config()->foldingColor())); | ||
2229 | 2227 | | |||
2230 | m_foldingRange->setView(m_view); | 2228 | m_foldingRange->setView(m_view); | ||
2231 | // use z depth defined in moving ranges interface | 2229 | // use z depth defined in moving ranges interface | ||
2232 | m_foldingRange->setZDepth(-100.0); | 2230 | m_foldingRange->setZDepth(-100.0); | ||
2233 | m_foldingRange->setAttribute(attr); | 2231 | m_foldingRange->setAttribute(attr); | ||
2234 | } | 2232 | } | ||
2235 | 2233 | | |||
2236 | // show text preview, if a folded region starts here | 2234 | // show text preview, if a folded region starts here... | ||
2237 | bool foldUnderMouse = false; | 2235 | // ...but only when main window is active (#392396) | ||
2238 | if (m_foldingRange && m_view->config()->foldingPreview()) { | | |||
2239 | const QPoint globalPos = QCursor::pos(); | | |||
2240 | const QPoint pos = mapFromGlobal(globalPos); | | |||
2241 | const KateTextLayout &t = m_view->m_viewInternal->yToKateTextLayout(pos.y()); | | |||
2242 | if (t.isValid() && positionToArea(pos) == FoldingMarkers) { | | |||
2243 | | ||||
2244 | const int realLine = t.line(); | | |||
2245 | foldUnderMouse = !m_view->textFolding().isLineVisible(realLine + 1); | | |||
2246 | | ||||
2247 | // only show when main window is active (#392396) | | |||
2248 | const bool isWindowActive = !window() || window()->isActiveWindow(); | 2236 | const bool isWindowActive = !window() || window()->isActiveWindow(); | ||
2249 | if (foldUnderMouse && isWindowActive) { | 2237 | if (showPreview && m_view->config()->foldingPreview() && isWindowActive) { | ||
2250 | if (!m_foldingPreview) { | | |||
2251 | m_foldingPreview = new KateTextPreview(m_view, this); | 2238 | m_foldingPreview = new KateTextPreview(m_view, this); | ||
2252 | m_foldingPreview->setAttribute(Qt::WA_ShowWithoutActivating); | 2239 | m_foldingPreview->setAttribute(Qt::WA_ShowWithoutActivating); | ||
2253 | m_foldingPreview->setFrameStyle(QFrame::StyledPanel); | 2240 | m_foldingPreview->setFrameStyle(QFrame::StyledPanel); | ||
2254 | 2241 | | |||
2255 | // event filter to catch application WindowDeactivate event, to hide the preview window | 2242 | // Calc how many lines can be displayed in the popup | ||
2256 | // qApp->installEventFilter(this); | | |||
2257 | } | | |||
2258 | | ||||
2259 | // Calc how many lines can be displayed in popup | | |||
2260 | const int lineHeight = m_view->renderer()->lineHeight(); | 2243 | const int lineHeight = m_view->renderer()->lineHeight(); | ||
2244 | const int foldingStartLine = m_foldingRange->start().line(); | ||||
2245 | // FIXME Is there really no easier way to find lineInDisplay? | ||||
2246 | const QPoint pos = m_viewInternal->mapFrom(m_view, m_view->cursorToCoordinate(KTextEditor::Cursor(foldingStartLine, 0))); | ||||
2261 | const int lineInDisplay = pos.y() / lineHeight; | 2247 | const int lineInDisplay = pos.y() / lineHeight; | ||
2262 | // Allow slightly overpainting of the view bottom to proper cover all lines | 2248 | // Allow slightly overpainting of the view bottom to proper cover all lines | ||
2263 | const int extra = (m_viewInternal->height() % lineHeight) > (lineHeight * 0.6) ? 1 : 0; | 2249 | const int extra = (m_viewInternal->height() % lineHeight) > (lineHeight * 0.6) ? 1 : 0; | ||
2264 | const int lineCount = qMin(m_foldingRange->numberOfLines() + 1, | 2250 | const int lineCount = qMin(m_foldingRange->numberOfLines() + 1, | ||
2265 | m_viewInternal->linesDisplayed() - lineInDisplay + extra); | 2251 | m_viewInternal->linesDisplayed() - lineInDisplay + extra); | ||
2266 | 2252 | | |||
2267 | m_foldingPreview->resize(m_viewInternal->width(), lineCount * lineHeight + 2 * m_foldingPreview->frameWidth()); | 2253 | m_foldingPreview->resize(m_viewInternal->width(), lineCount * lineHeight + 2 * m_foldingPreview->frameWidth()); | ||
2268 | const int xGlobal = mapToGlobal(QPoint(width(), 0)).x(); | 2254 | const int xGlobal = mapToGlobal(QPoint(width(), 0)).x(); | ||
2269 | const int yGlobal = m_view->mapToGlobal(m_view->cursorToCoordinate(KTextEditor::Cursor(realLine, 0))).y(); | 2255 | const int yGlobal = m_view->mapToGlobal(m_view->cursorToCoordinate(KTextEditor::Cursor(foldingStartLine, 0))).y(); | ||
2270 | m_foldingPreview->move(QPoint(xGlobal, yGlobal) - m_foldingPreview->contentsRect().topLeft()); | 2256 | m_foldingPreview->move(QPoint(xGlobal, yGlobal) - m_foldingPreview->contentsRect().topLeft()); | ||
2271 | m_foldingPreview->setLine(realLine); | 2257 | m_foldingPreview->setLine(foldingStartLine); | ||
2272 | m_foldingPreview->setCenterView(false); | 2258 | m_foldingPreview->setCenterView(false); | ||
2273 | m_foldingPreview->setShowFoldedLines(true); | 2259 | m_foldingPreview->setShowFoldedLines(true); | ||
2274 | m_foldingPreview->raise(); | 2260 | m_foldingPreview->raise(); | ||
2275 | m_foldingPreview->show(); | 2261 | m_foldingPreview->show(); | ||
2276 | } | 2262 | } | ||
2277 | } | 2263 | } | ||
2278 | } | | |||
2279 | | ||||
2280 | if (!foldUnderMouse) { | | |||
2281 | delete m_foldingPreview; | | |||
2282 | } | | |||
2283 | | ||||
2284 | /** | | |||
2285 | * repaint | | |||
2286 | */ | | |||
2287 | repaint(); | | |||
2288 | } | | |||
2289 | 2264 | | |||
2290 | void KateIconBorder::hideBlock() | 2265 | void KateIconBorder::hideFolding() | ||
2291 | { | 2266 | { | ||
2292 | if (m_delayFoldingHlTimer.isActive()) { | 2267 | if (m_antiFlickerTimer.isActive()) { | ||
2293 | m_delayFoldingHlTimer.stop(); | 2268 | m_antiFlickerTimer.stop(); | ||
2294 | } | 2269 | } | ||
2295 | 2270 | | |||
2296 | m_nextHighlightBlock = -2; | 2271 | m_currentLine = -1; | ||
2297 | m_currentBlockLine = -1; | | |||
2298 | delete m_foldingRange; | 2272 | delete m_foldingRange; | ||
2299 | m_foldingRange = nullptr; | 2273 | m_foldingRange = nullptr; | ||
2300 | 2274 | | |||
2301 | delete m_foldingPreview; | 2275 | delete m_foldingPreview; | ||
2302 | } | 2276 | } | ||
2303 | 2277 | | |||
2304 | void KateIconBorder::leaveEvent(QEvent *event) | 2278 | void KateIconBorder::leaveEvent(QEvent *event) | ||
2305 | { | 2279 | { | ||
2306 | hideBlock(); | 2280 | hideFolding(); | ||
2307 | removeAnnotationHovering(); | 2281 | removeAnnotationHovering(); | ||
2308 | 2282 | | |||
2309 | QWidget::leaveEvent(event); | 2283 | QWidget::leaveEvent(event); | ||
2310 | } | 2284 | } | ||
2311 | 2285 | | |||
2312 | void KateIconBorder::mouseMoveEvent(QMouseEvent *e) | 2286 | void KateIconBorder::mouseMoveEvent(QMouseEvent *e) | ||
2313 | { | 2287 | { | ||
2314 | const KateTextLayout &t = m_viewInternal->yToKateTextLayout(e->y()); | 2288 | const KateTextLayout &t = m_viewInternal->yToKateTextLayout(e->y()); | ||
2315 | if (t.isValid()) { | 2289 | if (!t.isValid()) { | ||
2290 | // Cleanup everything which may be shown | ||||
2291 | removeAnnotationHovering(); | ||||
2292 | hideFolding(); | ||||
2293 | | ||||
2294 | } else { | ||||
2316 | const BorderArea area = positionToArea(e->pos()); | 2295 | const BorderArea area = positionToArea(e->pos()); | ||
2317 | if (area == FoldingMarkers) { | 2296 | if (area == FoldingMarkers) { | ||
2318 | showDelayedBlock(t.line()); | 2297 | highlightFoldingDelayed(t.line()); | ||
2319 | } else { | 2298 | } else { | ||
2320 | hideBlock(); | 2299 | hideFolding(); | ||
2321 | } | 2300 | } | ||
2322 | if (area == AnnotationBorder) { | 2301 | if (area == AnnotationBorder) { | ||
2323 | KTextEditor::AnnotationModel *model = m_view->annotationModel() ? | 2302 | KTextEditor::AnnotationModel *model = m_view->annotationModel() ? | ||
2324 | m_view->annotationModel() : m_doc->annotationModel(); | 2303 | m_view->annotationModel() : m_doc->annotationModel(); | ||
2325 | if (model) { | 2304 | if (model) { | ||
2326 | m_hoveredAnnotationGroupIdentifier = model->data( t.line(), | 2305 | m_hoveredAnnotationGroupIdentifier = model->data( t.line(), | ||
2327 | (Qt::ItemDataRole) KTextEditor::AnnotationModel::GroupIdentifierRole ).toString(); | 2306 | (Qt::ItemDataRole) KTextEditor::AnnotationModel::GroupIdentifierRole ).toString(); | ||
2328 | const QPoint viewRelativePos = m_view->mapFromGlobal(e->globalPos()); | 2307 | const QPoint viewRelativePos = m_view->mapFromGlobal(e->globalPos()); | ||
Show All 14 Lines | 2317 | } else { | |||
2343 | m_hoveredAnnotationGroupIdentifier.clear(); | 2322 | m_hoveredAnnotationGroupIdentifier.clear(); | ||
2344 | QTimer::singleShot(0, this, SLOT(update())); | 2323 | QTimer::singleShot(0, this, SLOT(update())); | ||
2345 | } | 2324 | } | ||
2346 | if (area != IconBorder) { | 2325 | if (area != IconBorder) { | ||
2347 | QPoint p = m_viewInternal->mapFromGlobal(e->globalPos()); | 2326 | QPoint p = m_viewInternal->mapFromGlobal(e->globalPos()); | ||
2348 | QMouseEvent forward(QEvent::MouseMove, p, e->button(), e->buttons(), e->modifiers()); | 2327 | QMouseEvent forward(QEvent::MouseMove, p, e->button(), e->buttons(), e->modifiers()); | ||
2349 | m_viewInternal->mouseMoveEvent(&forward); | 2328 | m_viewInternal->mouseMoveEvent(&forward); | ||
2350 | } | 2329 | } | ||
2351 | } else { | | |||
2352 | // remove hovering if it's still there | | |||
2353 | removeAnnotationHovering(); | | |||
2354 | } | 2330 | } | ||
2355 | 2331 | | |||
2356 | QWidget::mouseMoveEvent(e); | 2332 | QWidget::mouseMoveEvent(e); | ||
2357 | } | 2333 | } | ||
2358 | 2334 | | |||
2359 | void KateIconBorder::mouseReleaseEvent(QMouseEvent *e) | 2335 | void KateIconBorder::mouseReleaseEvent(QMouseEvent *e) | ||
2360 | { | 2336 | { | ||
2361 | const int cursorOnLine = m_viewInternal->yToKateTextLayout(e->y()).line(); | 2337 | const int cursorOnLine = m_viewInternal->yToKateTextLayout(e->y()).line(); | ||
▲ Show 20 Lines • Show All 323 Lines • ▼ Show 20 Line(s) | 2660 | if (newmodel) { | |||
2685 | connect(newmodel, SIGNAL(reset()), this, SLOT(updateAnnotationBorderWidth())); | 2661 | connect(newmodel, SIGNAL(reset()), this, SLOT(updateAnnotationBorderWidth())); | ||
2686 | connect(newmodel, SIGNAL(lineChanged(int)), this, SLOT(updateAnnotationLine(int))); | 2662 | connect(newmodel, SIGNAL(lineChanged(int)), this, SLOT(updateAnnotationLine(int))); | ||
2687 | } | 2663 | } | ||
2688 | updateAnnotationBorderWidth(); | 2664 | updateAnnotationBorderWidth(); | ||
2689 | } | 2665 | } | ||
2690 | 2666 | | |||
2691 | void KateIconBorder::displayRangeChanged() | 2667 | void KateIconBorder::displayRangeChanged() | ||
2692 | { | 2668 | { | ||
2693 | hideBlock(); | 2669 | hideFolding(); | ||
2694 | removeAnnotationHovering(); | 2670 | removeAnnotationHovering(); | ||
2695 | } | 2671 | } | ||
2696 | 2672 | | |||
2697 | //END KateIconBorder | 2673 | //END KateIconBorder | ||
2698 | 2674 | | |||
2699 | //BEGIN KateViewEncodingAction | 2675 | //BEGIN KateViewEncodingAction | ||
2700 | // According to http://www.iana.org/assignments/ianacharset-mib | 2676 | // According to http://www.iana.org/assignments/ianacharset-mib | ||
2701 | // the default/unknown mib value is 2. | 2677 | // the default/unknown mib value is 2. | ||
▲ Show 20 Lines • Show All 408 Lines • Show Last 20 Lines |