// Path: src/main/java/com/athaydes/logfx/iterable/IterableUtils.java
// public final class IterableUtils {
//     public static <N> Optional<N> getFirst( Iterable<N> iterable ) {
//         N item = null;
//         var iter = iterable.iterator();
//         if ( iter.hasNext() ) {
//             item = iter.next();
//         }
//         return Optional.ofNullable( item );
//     }
// 
//     public static <N> Optional<N> getLast( Iterable<N> iterable ) {
//         N item = null;
//         for ( N n : iterable ) {
//             item = n;
//         }
//         return Optional.ofNullable( item );
//     }
// 
//     public static <T> Collection<T> append( T item, Collection<T> collection ) {
//         var result = new ArrayList<T>( collection.size() + 1 );
//         result.add( item );
//         result.addAll( collection );
//         return result;
//     }
// 
//     public static <T> T getAt( int index, List<T> collection, T defaultValue ) {
//         if ( index >= 0 && !collection.isEmpty() && index < collection.size() ) {
//             return collection.get( index );
//         }
//         return defaultValue;
//     }
// 
//     public static double midPoint( List<Double> collection, int index ) {
//         if ( collection.isEmpty() ) return 0.5;
//         var before = getAt( Math.min( index, collection.size() ) - 1, collection, 0.0 );
//         var after = getAt( index, collection, 1.0 );
//         return before + ( ( after - before ) / 2.0 );
//     }
// }


import com.athaydes.logfx.iterable.IterableUtils;
import javafx.collections.FXCollections;
import javafx.collections.ListChangeListener;
import javafx.collections.ObservableSet;
import javafx.scene.Node;
import javafx.scene.Parent;
import javafx.scene.input.ClipboardContent;
import javafx.scene.input.MouseButton;
import javafx.scene.input.MouseEvent;
import java.util.ArrayList;
import java.util.LinkedHashSet;
import java.util.List;
import java.util.Optional;
import java.util.concurrent.CompletableFuture;
import java.util.concurrent.CompletionStage;
import java.util.stream.Collectors;
            while ( target != root.getNode() && !( target instanceof SelectableNode ) ) {
                target = target.getParent();
            }

            return target;
        } else {
            return root.getNode();
        }
    }

    Optional<ClipboardContent> getSelection() {
        return selectionManager.getContent();
    }

    ObservableSet<SelectableNode> getSelectedItems() {
        return selectionManager.getSelectedItems();
    }

    void select( SelectableNode node ) {
        selectionManager.unselectAll();

        selectionManager.select( node, true );
        // only scroll to view every 10th line to avoid jumping around too much
//        var scrollToView = node.getLineIndex() % 10 == 0;
//        if (scrollToView) {
        root.scrollToView( node );
//        }
    }

    CompletionStage<? extends SelectableNode> getNextItem() {
// Path: src/main/java/com/athaydes/logfx/iterable/IterableUtils.java
// public final class IterableUtils {
//     public static <N> Optional<N> getFirst( Iterable<N> iterable ) {
//         N item = null;
//         var iter = iterable.iterator();
//         if ( iter.hasNext() ) {
//             item = iter.next();
//         }
//         return Optional.ofNullable( item );
//     }
// 
//     public static <N> Optional<N> getLast( Iterable<N> iterable ) {
//         N item = null;
//         for ( N n : iterable ) {
//             item = n;
//         }
//         return Optional.ofNullable( item );
//     }
// 
//     public static <T> Collection<T> append( T item, Collection<T> collection ) {
//         var result = new ArrayList<T>( collection.size() + 1 );
//         result.add( item );
//         result.addAll( collection );
//         return result;
//     }
// 
//     public static <T> T getAt( int index, List<T> collection, T defaultValue ) {
//         if ( index >= 0 && !collection.isEmpty() && index < collection.size() ) {
//             return collection.get( index );
//         }
//         return defaultValue;
//     }
// 
//     public static double midPoint( List<Double> collection, int index ) {
//         if ( collection.isEmpty() ) return 0.5;
//         var before = getAt( Math.min( index, collection.size() ) - 1, collection, 0.0 );
//         var after = getAt( index, collection, 1.0 );
//         return before + ( ( after - before ) / 2.0 );
//     }
// }


// Path: src/main/java/com/athaydes/logfx/ui/SelectionHandler.java
import com.athaydes.logfx.iterable.IterableUtils;
import javafx.collections.FXCollections;
import javafx.collections.ListChangeListener;
import javafx.collections.ObservableSet;
import javafx.scene.Node;
import javafx.scene.Parent;
import javafx.scene.input.ClipboardContent;
import javafx.scene.input.MouseButton;
import javafx.scene.input.MouseEvent;
import java.util.ArrayList;
import java.util.LinkedHashSet;
import java.util.List;
import java.util.Optional;
import java.util.concurrent.CompletableFuture;
import java.util.concurrent.CompletionStage;
import java.util.stream.Collectors;

            while ( target != root.getNode() && !( target instanceof SelectableNode ) ) {
                target = target.getParent();
            }

            return target;
        } else {
            return root.getNode();
        }
    }

    Optional<ClipboardContent> getSelection() {
        return selectionManager.getContent();
    }

    ObservableSet<SelectableNode> getSelectedItems() {
        return selectionManager.getSelectedItems();
    }

    void select( SelectableNode node ) {
        selectionManager.unselectAll();

        selectionManager.select( node, true );
        // only scroll to view every 10th line to avoid jumping around too much
//        var scrollToView = node.getLineIndex() % 10 == 0;
//        if (scrollToView) {
        root.scrollToView( node );
//        }
    }

    CompletionStage<? extends SelectableNode> getNextItem() {
        return IterableUtils.getLast( getSelectedItems() ).map( last -> {