Changeset View
Changeset View
Standalone View
Standalone View
src/filewidgets/kdiroperator.cpp
Show First 20 Lines • Show All 223 Lines • ▼ Show 20 Line(s) | 172 | public: | |||
---|---|---|---|---|---|
224 | void _k_assureVisibleSelection(); | 224 | void _k_assureVisibleSelection(); | ||
225 | void _k_synchronizeSortingState(int, Qt::SortOrder); | 225 | void _k_synchronizeSortingState(int, Qt::SortOrder); | ||
226 | void _k_slotChangeDecorationPosition(); | 226 | void _k_slotChangeDecorationPosition(); | ||
227 | void _k_slotExpandToUrl(const QModelIndex &); | 227 | void _k_slotExpandToUrl(const QModelIndex &); | ||
228 | void _k_slotItemsChanged(); | 228 | void _k_slotItemsChanged(); | ||
229 | void _k_slotDirectoryCreated(const QUrl &); | 229 | void _k_slotDirectoryCreated(const QUrl &); | ||
230 | 230 | | |||
231 | void updateListViewGrid(); | 231 | void updateListViewGrid(); | ||
232 | void updatePreviewActionState(); | ||||
232 | int iconSizeForViewType(QAbstractItemView *itemView) const; | 233 | int iconSizeForViewType(QAbstractItemView *itemView) const; | ||
233 | 234 | | |||
234 | // private members | 235 | // private members | ||
235 | KDirOperator *parent; | 236 | KDirOperator *parent; | ||
236 | QStack<QUrl *> backStack; ///< Contains all URLs you can reach with the back button. | 237 | QStack<QUrl *> backStack; ///< Contains all URLs you can reach with the back button. | ||
237 | QStack<QUrl *> forwardStack; ///< Contains all URLs you can reach with the forward button. | 238 | QStack<QUrl *> forwardStack; ///< Contains all URLs you can reach with the forward button. | ||
238 | 239 | | |||
239 | QModelIndex lastHoveredIndex; | 240 | QModelIndex lastHoveredIndex; | ||
Show All 37 Lines | |||||
277 | 278 | | |||
278 | KNewFileMenu *newFileMenu; | 279 | KNewFileMenu *newFileMenu; | ||
279 | 280 | | |||
280 | KConfigGroup *configGroup; | 281 | KConfigGroup *configGroup; | ||
281 | 282 | | |||
282 | KFilePreviewGenerator *previewGenerator; | 283 | KFilePreviewGenerator *previewGenerator; | ||
283 | 284 | | |||
284 | bool showPreviews; | 285 | bool showPreviews; | ||
286 | bool calledFromUpdatePreviewActionState; | ||||
287 | bool showPreviewsConfigEntry; | ||||
288 | | ||||
285 | int iconsZoom; | 289 | int iconsZoom; | ||
286 | 290 | | |||
287 | bool isSaving; | 291 | bool isSaving; | ||
288 | 292 | | |||
289 | KActionMenu *decorationMenu; | 293 | KActionMenu *decorationMenu; | ||
290 | KToggleAction *leftAction; | 294 | KToggleAction *leftAction; | ||
291 | QList<QUrl> itemsToBeSetAsCurrent; | 295 | QList<QUrl> itemsToBeSetAsCurrent; | ||
292 | bool shouldFetchForItems; | 296 | bool shouldFetchForItems; | ||
Show All 18 Lines | 301 | KDirOperator::Private::Private(KDirOperator *_parent) : | |||
311 | progressDelayTimer(nullptr), | 315 | progressDelayTimer(nullptr), | ||
312 | dropOptions(0), | 316 | dropOptions(0), | ||
313 | actionMenu(nullptr), | 317 | actionMenu(nullptr), | ||
314 | actionCollection(nullptr), | 318 | actionCollection(nullptr), | ||
315 | newFileMenu(nullptr), | 319 | newFileMenu(nullptr), | ||
316 | configGroup(nullptr), | 320 | configGroup(nullptr), | ||
317 | previewGenerator(nullptr), | 321 | previewGenerator(nullptr), | ||
318 | showPreviews(false), | 322 | showPreviews(false), | ||
323 | calledFromUpdatePreviewActionState(false), | ||||
324 | showPreviewsConfigEntry(false), | ||||
319 | iconsZoom(0), | 325 | iconsZoom(0), | ||
320 | isSaving(false), | 326 | isSaving(false), | ||
321 | decorationMenu(nullptr), | 327 | decorationMenu(nullptr), | ||
322 | leftAction(nullptr), | 328 | leftAction(nullptr), | ||
323 | shouldFetchForItems(false), | 329 | shouldFetchForItems(false), | ||
324 | inlinePreviewState(NotForced) | 330 | inlinePreviewState(NotForced) | ||
325 | { | 331 | { | ||
326 | } | 332 | } | ||
▲ Show 20 Lines • Show All 322 Lines • ▼ Show 20 Line(s) | |||||
649 | void KDirOperator::Private::_k_toggleInlinePreviews(bool show) | 655 | void KDirOperator::Private::_k_toggleInlinePreviews(bool show) | ||
650 | { | 656 | { | ||
651 | if (showPreviews == show) { | 657 | if (showPreviews == show) { | ||
652 | return; | 658 | return; | ||
653 | } | 659 | } | ||
654 | 660 | | |||
655 | showPreviews = show; | 661 | showPreviews = show; | ||
656 | 662 | | |||
663 | if (!calledFromUpdatePreviewActionState) { | ||||
664 | showPreviewsConfigEntry = show; | ||||
665 | } | ||||
666 | | ||||
657 | if (!previewGenerator) { | 667 | if (!previewGenerator) { | ||
658 | return; | 668 | return; | ||
659 | } | 669 | } | ||
660 | 670 | | |||
661 | previewGenerator->setPreviewShown(show); | 671 | previewGenerator->setPreviewShown(show); | ||
662 | } | 672 | } | ||
663 | 673 | | |||
664 | void KDirOperator::Private::_k_slotOpenFileManager() | 674 | void KDirOperator::Private::_k_slotOpenFileManager() | ||
▲ Show 20 Lines • Show All 1213 Lines • ▼ Show 20 Line(s) | 1857 | { | |||
1878 | 1888 | | |||
1879 | QAction *action = new QAction(i18n("Delete"), this); | 1889 | QAction *action = new QAction(i18n("Delete"), this); | ||
1880 | d->actionCollection->addAction(QStringLiteral("delete"), action); | 1890 | d->actionCollection->addAction(QStringLiteral("delete"), action); | ||
1881 | action->setIcon(QIcon::fromTheme(QStringLiteral("edit-delete"))); | 1891 | action->setIcon(QIcon::fromTheme(QStringLiteral("edit-delete"))); | ||
1882 | action->setShortcut(Qt::SHIFT + Qt::Key_Delete); | 1892 | action->setShortcut(Qt::SHIFT + Qt::Key_Delete); | ||
1883 | connect(action, SIGNAL(triggered(bool)), this, SLOT(deleteSelected())); | 1893 | connect(action, SIGNAL(triggered(bool)), this, SLOT(deleteSelected())); | ||
1884 | 1894 | | |||
1885 | // the sort menu actions | 1895 | // the sort menu actions | ||
1886 | KActionMenu *sortMenu = new KActionMenu(i18n("Sorting"), this); | 1896 | KActionMenu *sortMenu = new KActionMenu(i18n("Sort by"), this); | ||
1887 | d->actionCollection->addAction(QStringLiteral("sorting menu"), sortMenu); | 1897 | d->actionCollection->addAction(QStringLiteral("sorting menu"), sortMenu); | ||
1898 | sortMenu->setIcon(QIcon::fromTheme(QStringLiteral("itemize"))); | ||||
1888 | 1899 | | |||
1889 | KToggleAction *byNameAction = new KToggleAction(i18n("By Name"), this); | 1900 | KToggleAction *byNameAction = new KToggleAction(i18n("Name"), this); | ||
1890 | d->actionCollection->addAction(QStringLiteral("by name"), byNameAction); | 1901 | d->actionCollection->addAction(QStringLiteral("by name"), byNameAction); | ||
1891 | connect(byNameAction, SIGNAL(triggered(bool)), this, SLOT(_k_slotSortByName())); | 1902 | connect(byNameAction, SIGNAL(triggered(bool)), this, SLOT(_k_slotSortByName())); | ||
1892 | 1903 | | |||
1893 | KToggleAction *bySizeAction = new KToggleAction(i18n("By Size"), this); | 1904 | KToggleAction *bySizeAction = new KToggleAction(i18n("Size"), this); | ||
1894 | d->actionCollection->addAction(QStringLiteral("by size"), bySizeAction); | 1905 | d->actionCollection->addAction(QStringLiteral("by size"), bySizeAction); | ||
1895 | connect(bySizeAction, SIGNAL(triggered(bool)), this, SLOT(_k_slotSortBySize())); | 1906 | connect(bySizeAction, SIGNAL(triggered(bool)), this, SLOT(_k_slotSortBySize())); | ||
1896 | 1907 | | |||
1897 | KToggleAction *byDateAction = new KToggleAction(i18n("By Date"), this); | 1908 | KToggleAction *byDateAction = new KToggleAction(i18n("Date"), this); | ||
1898 | d->actionCollection->addAction(QStringLiteral("by date"), byDateAction); | 1909 | d->actionCollection->addAction(QStringLiteral("by date"), byDateAction); | ||
1899 | connect(byDateAction, SIGNAL(triggered(bool)), this, SLOT(_k_slotSortByDate())); | 1910 | connect(byDateAction, SIGNAL(triggered(bool)), this, SLOT(_k_slotSortByDate())); | ||
1900 | 1911 | | |||
1901 | KToggleAction *byTypeAction = new KToggleAction(i18n("By Type"), this); | 1912 | KToggleAction *byTypeAction = new KToggleAction(i18n("Type"), this); | ||
1902 | d->actionCollection->addAction(QStringLiteral("by type"), byTypeAction); | 1913 | d->actionCollection->addAction(QStringLiteral("by type"), byTypeAction); | ||
1903 | connect(byTypeAction, SIGNAL(triggered(bool)), this, SLOT(_k_slotSortByType())); | 1914 | connect(byTypeAction, SIGNAL(triggered(bool)), this, SLOT(_k_slotSortByType())); | ||
1904 | 1915 | | |||
1905 | KToggleAction *descendingAction = new KToggleAction(i18n("Descending"), this); | 1916 | KToggleAction *descendingAction = new KToggleAction(i18n("Descending"), this); | ||
1906 | d->actionCollection->addAction(QStringLiteral("descending"), descendingAction); | 1917 | d->actionCollection->addAction(QStringLiteral("descending"), descendingAction); | ||
1907 | connect(descendingAction, SIGNAL(triggered(bool)), this, SLOT(_k_slotSortReversed(bool))); | 1918 | connect(descendingAction, SIGNAL(triggered(bool)), this, SLOT(_k_slotSortReversed(bool))); | ||
1908 | 1919 | | |||
1909 | KToggleAction *dirsFirstAction = new KToggleAction(i18n("Folders First"), this); | 1920 | KToggleAction *dirsFirstAction = new KToggleAction(i18n("Folders First"), this); | ||
▲ Show 20 Lines • Show All 145 Lines • ▼ Show 20 Line(s) | 2065 | if (whichActions & SortActions) { | |||
2055 | d->actionMenu->addAction(sortMenu); | 2066 | d->actionMenu->addAction(sortMenu); | ||
2056 | if (!(whichActions & ViewActions)) { | 2067 | if (!(whichActions & ViewActions)) { | ||
2057 | d->actionMenu->addSeparator(); | 2068 | d->actionMenu->addSeparator(); | ||
2058 | } | 2069 | } | ||
2059 | } | 2070 | } | ||
2060 | 2071 | | |||
2061 | if (whichActions & ViewActions) { | 2072 | if (whichActions & ViewActions) { | ||
2062 | d->actionMenu->addAction(d->actionCollection->action(QStringLiteral("view menu"))); | 2073 | d->actionMenu->addAction(d->actionCollection->action(QStringLiteral("view menu"))); | ||
2074 | d->actionMenu->addAction(d->actionCollection->action(QStringLiteral("reload"))); | ||||
2063 | d->actionMenu->addSeparator(); | 2075 | d->actionMenu->addSeparator(); | ||
2064 | } | 2076 | } | ||
2065 | 2077 | | |||
2066 | if (whichActions & FileActions) { | 2078 | if (whichActions & FileActions) { | ||
2067 | d->actionMenu->addAction(d->actionCollection->action(QStringLiteral("file manager"))); | 2079 | d->actionMenu->addAction(d->actionCollection->action(QStringLiteral("file manager"))); | ||
2068 | d->actionMenu->addAction(d->actionCollection->action(QStringLiteral("properties"))); | 2080 | d->actionMenu->addAction(d->actionCollection->action(QStringLiteral("properties"))); | ||
2069 | } | 2081 | } | ||
2070 | } | 2082 | } | ||
▲ Show 20 Lines • Show All 74 Lines • ▼ Show 20 Line(s) | 2114 | { | |||
2145 | } | 2157 | } | ||
2146 | if (configGroup.readEntry(QStringLiteral("Sort reversed"), DefaultSortReversed)) { | 2158 | if (configGroup.readEntry(QStringLiteral("Sort reversed"), DefaultSortReversed)) { | ||
2147 | sorting |= QDir::Reversed; | 2159 | sorting |= QDir::Reversed; | ||
2148 | } | 2160 | } | ||
2149 | d->updateSorting(sorting); | 2161 | d->updateSorting(sorting); | ||
2150 | 2162 | | |||
2151 | if (d->inlinePreviewState == Private::NotForced) { | 2163 | if (d->inlinePreviewState == Private::NotForced) { | ||
2152 | d->showPreviews = configGroup.readEntry(QStringLiteral("Previews"), false); | 2164 | d->showPreviews = configGroup.readEntry(QStringLiteral("Previews"), false); | ||
2165 | d->showPreviewsConfigEntry = d->showPreviews; | ||||
2153 | } | 2166 | } | ||
2154 | QStyleOptionViewItem::Position pos = (QStyleOptionViewItem::Position) configGroup.readEntry(QStringLiteral("Decoration position"), (int) QStyleOptionViewItem::Left); | 2167 | QStyleOptionViewItem::Position pos = (QStyleOptionViewItem::Position) configGroup.readEntry(QStringLiteral("Decoration position"), (int) QStyleOptionViewItem::Left); | ||
2155 | setDecorationPosition(pos); | 2168 | setDecorationPosition(pos); | ||
2156 | } | 2169 | } | ||
2157 | 2170 | | |||
2158 | void KDirOperator::writeConfig(KConfigGroup &configGroup) | 2171 | void KDirOperator::writeConfig(KConfigGroup &configGroup) | ||
2159 | { | 2172 | { | ||
2160 | QString sortBy = QStringLiteral("Name"); | 2173 | QString sortBy = QStringLiteral("Name"); | ||
▲ Show 20 Lines • Show All 47 Lines • ▼ Show 20 Line(s) | |||||
2208 | } else if (KFile::isTreeView(fv)) { | 2221 | } else if (KFile::isTreeView(fv)) { | ||
2209 | style = QStringLiteral("Tree"); | 2222 | style = QStringLiteral("Tree"); | ||
2210 | } else if (KFile::isDetailTreeView(fv)) { | 2223 | } else if (KFile::isDetailTreeView(fv)) { | ||
2211 | style = QStringLiteral("DetailTree"); | 2224 | style = QStringLiteral("DetailTree"); | ||
2212 | } | 2225 | } | ||
2213 | configGroup.writeEntry(QStringLiteral("View Style"), style); | 2226 | configGroup.writeEntry(QStringLiteral("View Style"), style); | ||
2214 | 2227 | | |||
2215 | if (d->inlinePreviewState == Private::NotForced) { | 2228 | if (d->inlinePreviewState == Private::NotForced) { | ||
2216 | configGroup.writeEntry(QStringLiteral("Previews"), d->showPreviews); | 2229 | configGroup.writeEntry(QStringLiteral("Previews"), d->showPreviewsConfigEntry); | ||
2217 | if (qobject_cast<QListView *>(d->itemView)) { | 2230 | if (qobject_cast<QListView *>(d->itemView)) { | ||
2218 | configGroup.writeEntry(QStringLiteral("listViewIconSize"), d->iconsZoom); | 2231 | configGroup.writeEntry(QStringLiteral("listViewIconSize"), d->iconsZoom); | ||
2219 | } else { | 2232 | } else { | ||
2220 | configGroup.writeEntry(QStringLiteral("detailedViewIconSize"), d->iconsZoom); | 2233 | configGroup.writeEntry(QStringLiteral("detailedViewIconSize"), d->iconsZoom); | ||
2221 | } | 2234 | } | ||
2222 | } | 2235 | } | ||
2223 | 2236 | | |||
2224 | configGroup.writeEntry(QStringLiteral("Decoration position"), (int) d->decorationPosition); | 2237 | configGroup.writeEntry(QStringLiteral("Decoration position"), (int) d->decorationPosition); | ||
Show All 19 Lines | 2241 | { | |||
2244 | if (hasPreview) { | 2257 | if (hasPreview) { | ||
2245 | d->previewWidth = sizes[1]; | 2258 | d->previewWidth = sizes[1]; | ||
2246 | } | 2259 | } | ||
2247 | 2260 | | |||
2248 | if (d->progressBar->parent() == this) { | 2261 | if (d->progressBar->parent() == this) { | ||
2249 | // might be reparented into a statusbar | 2262 | // might be reparented into a statusbar | ||
2250 | d->progressBar->move(2, height() - d->progressBar->height() - 2); | 2263 | d->progressBar->move(2, height() - d->progressBar->height() - 2); | ||
2251 | } | 2264 | } | ||
2265 | | ||||
2266 | d->updateListViewGrid(); | ||||
2252 | } | 2267 | } | ||
2253 | 2268 | | |||
2254 | void KDirOperator::setOnlyDoubleClickSelectsFiles(bool enable) | 2269 | void KDirOperator::setOnlyDoubleClickSelectsFiles(bool enable) | ||
2255 | { | 2270 | { | ||
2256 | d->onlyDoubleClickSelectsFiles = enable; | 2271 | d->onlyDoubleClickSelectsFiles = enable; | ||
2257 | // TODO: port to QAbstractItemModel | 2272 | // TODO: port to QAbstractItemModel | ||
2258 | //if (d->itemView != 0) { | 2273 | //if (d->itemView != 0) { | ||
2259 | // d->itemView->setOnlyDoubleClickSelectsFiles(enable); | 2274 | // d->itemView->setOnlyDoubleClickSelectsFiles(enable); | ||
▲ Show 20 Lines • Show All 296 Lines • ▼ Show 20 Line(s) | 2533 | { | |||
2556 | } | 2571 | } | ||
2557 | } | 2572 | } | ||
2558 | 2573 | | |||
2559 | void KDirOperator::Private::_k_slotItemsChanged() | 2574 | void KDirOperator::Private::_k_slotItemsChanged() | ||
2560 | { | 2575 | { | ||
2561 | completeListDirty = true; | 2576 | completeListDirty = true; | ||
2562 | } | 2577 | } | ||
2563 | 2578 | | |||
2579 | void KDirOperator::Private::updatePreviewActionState() | ||||
2580 | { | ||||
2581 | if (!itemView) { | ||||
2582 | return; | ||||
2583 | } | ||||
2584 | | ||||
2585 | const QFontMetrics metrics(itemView->viewport()->font()); | ||||
2586 | | ||||
2587 | // hide icon previews when they are too small | ||||
2588 | const bool iconSizeBigEnoughForPreview = itemView->iconSize().height() > metrics.height() * 2; | ||||
2589 | | ||||
2590 | KToggleAction *previewAction = qobject_cast<KToggleAction *>(actionCollection->action(QStringLiteral("inline preview"))); | ||||
2591 | previewAction->setEnabled(iconSizeBigEnoughForPreview); | ||||
2592 | | ||||
2593 | if (iconSizeBigEnoughForPreview) { | ||||
2594 | previewAction->setToolTip(i18n("Show Preview")); | ||||
2595 | } else { | ||||
2596 | previewAction->setToolTip(i18n("Automatically disabled for small icon sizes; increase icon size to see previews")); | ||||
2597 | } | ||||
2598 | | ||||
2599 | calledFromUpdatePreviewActionState = true; | ||||
2600 | previewAction->setChecked(iconSizeBigEnoughForPreview && showPreviewsConfigEntry); | ||||
2601 | calledFromUpdatePreviewActionState = false; | ||||
2602 | } | ||||
2603 | | ||||
2564 | void KDirOperator::Private::updateListViewGrid() | 2604 | void KDirOperator::Private::updateListViewGrid() | ||
2565 | { | 2605 | { | ||
2566 | if (!itemView) { | 2606 | if (!itemView) { | ||
2567 | return; | 2607 | return; | ||
2568 | } | 2608 | } | ||
2569 | 2609 | | |||
2610 | updatePreviewActionState(); | ||||
2611 | | ||||
2570 | QListView *view = qobject_cast<QListView *>(itemView); | 2612 | QListView *view = qobject_cast<QListView *>(itemView); | ||
2571 | 2613 | | |||
2572 | if (!view) { | 2614 | if (!view) { | ||
2573 | return; | 2615 | return; | ||
2574 | } | 2616 | } | ||
2575 | 2617 | | |||
2576 | const bool leftChecked = actionCollection->action(QStringLiteral("decorationAtLeft"))->isChecked(); | 2618 | const bool leftChecked = actionCollection->action(QStringLiteral("decorationAtLeft"))->isChecked(); | ||
2577 | 2619 | | |||
2578 | if (leftChecked) { | 2620 | if (leftChecked) { | ||
2579 | view->setGridSize(QSize()); | 2621 | view->setGridSize(QSize()); | ||
2580 | KFileItemDelegate *delegate = qobject_cast<KFileItemDelegate *>(view->itemDelegate()); | 2622 | KFileItemDelegate *delegate = qobject_cast<KFileItemDelegate *>(view->itemDelegate()); | ||
2581 | if (delegate) { | 2623 | if (delegate) { | ||
2582 | delegate->setMaximumSize(QSize()); | 2624 | delegate->setMaximumSize(QSize()); | ||
2583 | } | 2625 | } | ||
2584 | } else { | 2626 | } else { | ||
2585 | const QFontMetrics metrics(itemView->viewport()->font()); | 2627 | const QFontMetrics metrics(itemView->viewport()->font()); | ||
2586 | int size = itemView->iconSize().height() + metrics.height() * 2; | 2628 | | ||
2587 | // some heuristics for good looking. let's guess width = height * (3 / 2) is nice | 2629 | const int height = itemView->iconSize().height() + metrics.height() * 2.5; | ||
2588 | view->setGridSize(QSize(size * (3.0 / 2.0), size + metrics.height())); | 2630 | const int minWidth = qMax(height, metrics.height() * 5); | ||
2631 | | ||||
2632 | const int scrollBarWidth = itemView->verticalScrollBar()->sizeHint().width(); | ||||
2633 | | ||||
2634 | // Subtract 1 px to prevent flickering when resizing the window | ||||
2635 | // For Oxygen a column is missing after showing the dialog without resizing it, | ||||
2636 | // therefore subtract 4 more (scaled) pixels | ||||
2637 | const int viewPortWidth = itemView->contentsRect().width() - scrollBarWidth - 1 - 4 * itemView->devicePixelRatioF(); | ||||
2638 | const int itemsInRow = qMax(1, viewPortWidth / minWidth); | ||||
2639 | const int remainingWidth = viewPortWidth - (minWidth * itemsInRow); | ||||
2640 | const int width = minWidth + (remainingWidth / itemsInRow); | ||||
2641 | | ||||
2642 | const QSize itemSize(width, height); | ||||
2643 | | ||||
2644 | view->setGridSize(itemSize); | ||||
2589 | KFileItemDelegate *delegate = qobject_cast<KFileItemDelegate *>(view->itemDelegate()); | 2645 | KFileItemDelegate *delegate = qobject_cast<KFileItemDelegate *>(view->itemDelegate()); | ||
2590 | if (delegate) { | 2646 | if (delegate) { | ||
2591 | delegate->setMaximumSize(QSize(size * (3.0 / 2.0), size + metrics.height())); | 2647 | delegate->setMaximumSize(itemSize); | ||
2592 | } | 2648 | } | ||
2593 | } | 2649 | } | ||
2594 | } | 2650 | } | ||
2595 | 2651 | | |||
2596 | int KDirOperator::Private::iconSizeForViewType(QAbstractItemView *itemView) const | 2652 | int KDirOperator::Private::iconSizeForViewType(QAbstractItemView *itemView) const | ||
2597 | { | 2653 | { | ||
2598 | if (!itemView || !configGroup) { | 2654 | if (!itemView || !configGroup) { | ||
2599 | return 0; | 2655 | return 0; | ||
▲ Show 20 Lines • Show All 69 Lines • Show Last 20 Lines |