bool ProcessCmdKeyImpl(ref Message msg, Keys keyData)
        {
            switch (keyData)
            {
            case Keys.Left: _fileIndex = LoadPictures(_fileIndex, -1, -1); break;

            case Keys.Right: _fileIndex = LoadPictures(_fileIndex, +1, +1); break;

            case Keys.Control | Keys.Left: _fileIndex = LoadPictures(_fileIndex, -1, 0); break;

            case Keys.Control | Keys.Shift | Keys.Left: _fileIndex = LoadPictures(_fileIndex, 0, -1); break;

            case Keys.Control | Keys.Right: _fileIndex = LoadPictures(_fileIndex, 1, 0); break;

            case Keys.Control | Keys.Shift | Keys.Right: _fileIndex = LoadPictures(_fileIndex, 0, 1); break;

            case Keys.PageDown: OpenNextFolder(_currentDirectory, FolderDirection.Forward); break;

            case Keys.PageUp: OpenNextFolder(_currentDirectory, FolderDirection.Backward); break;

            case Keys.NumPad1:
            case Keys.D1:
                ArchiveLeftPicture();
                break;

            case Keys.NumPad2:
            case Keys.D2:
                ArchiveRightPicture();
                break;

            default:
                return(base.ProcessCmdKey(ref msg, keyData));
            }
            return(true);
        }
        void buttonUndo_Click(object sender, EventArgs e)
        {
            if (_shelvedFiles.Count == 0)
            {
                return;
            }
            if (_shelvedFiles.Count == 1)
            {
                buttonUndo.Enabled = false;
            }

            var userPrefs   = new PersistedUserPreferences();
            var shelvedFile = _shelvedFiles.Pop();
            var host        = new ImageHost(
                new FileNameHandler(userPrefs),
                userPrefs.ShelfName,
                new FileInfo(shelvedFile.Item1)
                )
            {
                Parent = _currentFiles
            };

            host.ShelvePicture();
            var nextNode = _currentFiles.First;

            while (nextNode != null &&
                   string.Compare(nextNode.Value.FileInfo.Name, host.FileInfo.Name, StringComparison.InvariantCultureIgnoreCase) < 0
                   )
            {
                nextNode = nextNode.Next;
            }
            NodesTuple displayLocation;

            if (nextNode == null)
            {
                _currentFiles.AddLast(host);
                displayLocation = _currentFiles.Count == 1
                    ? new NodesTuple(_currentFiles.First, _currentFiles.First)
                    : new NodesTuple(_currentFiles.Last.Previous, _currentFiles.Last);
            }
            else
            {
                var newNode = _currentFiles.AddBefore(nextNode, host);
                if (shelvedFile.Item2 == Side.Right && newNode.Previous != null)
                {
                    displayLocation = new NodesTuple(newNode.Previous, newNode);
                }
                else
                {
                    displayLocation = new NodesTuple(newNode, newNode.Next);
                }
            }
            _fileIndex = LoadPictures(displayLocation, 0, 0, noRelease: true);
        }
        NodesTuple LoadPictures(NodesTuple idx, int stepLeft, int stepRight, bool noRelease = false)
        {
            var rc = SelectIndexes(idx, stepLeft, stepRight);

            if (rc.Left == null || rc.Right == null)
            {
                return(null);
            }
            Trace.WriteLine($"LoadPictures({rc.Left.Value.FileInfo.Name}, {rc.Right.Value.FileInfo.Name}, {stepLeft}, {stepRight}, {noRelease})");
            rc.Left?.Value?.Render(pictureBox1, labelLeft);
            rc.Right?.Value?.Render(pictureBox2, labelRight);
            if (!noRelease)
            {
                idx?.Left?.Value?.Release();
                idx?.Right?.Value?.Release();
            }
            Trace.WriteLine($"LoadPictures returns({rc.Left.Value.FileInfo.Name}, {rc.Right.Value.FileInfo.Name})");
            return(rc);
        }
        void OpenFolderImpl(Func <string, bool> fileNameMatcher, DirectoryInfo selectedPath, FileNameHandler fileNameHandler, string shelf)
        {
            _shelvedFiles.Clear();
            buttonUndo.Enabled = false;

            _currentDirectory = selectedPath;
            Text          = _currentDirectory.FullName;
            _currentFiles = new LinkedList <ImageHost>(
                _currentDirectory
                .EnumerateFiles("*", SearchOption.TopDirectoryOnly)
                .Where(f => fileNameMatcher(f.Name))
                .OrderBy(f => f.Name)
                .Select(f => new ImageHost(fileNameHandler, shelf, f))
                );
            _currentFiles.ForEach(ih => ih.Parent = _currentFiles);
            switch (_currentFiles.Count)
            {
            case 0:
                // MessageBox.Show($"There are no pictures to sort in folder {selectedPath}", "Warning", MessageBoxButtons.OK, MessageBoxIcon.Warning);
                _fileIndex = new NodesTuple(null, null);
                return;

            case 1:
                _fileIndex = LoadPictures(new NodesTuple(_currentFiles.First, _currentFiles.First), 0, 0, noRelease: true);
                break;

            default:
                _fileIndex = LoadPictures(new NodesTuple(_currentFiles.First, _currentFiles.First.Next), 0, 0, noRelease: true);
                break;
            }
            buttonShelfLeft.Image = buttonShelfRight.Image =
                _currentDirectory.Name.Equals(shelf, StringComparison.InvariantCultureIgnoreCase)
                    ? Properties.Resources.SmallUnshelve
                    : Properties.Resources.SmallShelve;
            Focus();
        }
 void buttonNavigateRightRight_Click(object sender, EventArgs e)
 {
     _fileIndex = LoadPictures(_fileIndex, 0, 1);
 }
 void buttonNavigateBothLeft_Click(object sender, EventArgs e)
 {
     _fileIndex = LoadPictures(_fileIndex, -1, -1);
 }
 void rightPreviousToolStripMenuItem_Click(object sender, EventArgs e)
 {
     _fileIndex = LoadPictures(_fileIndex, 0, -1);
 }
 void leftNextToolStripMenuItem_Click(object sender, EventArgs e)
 {
     _fileIndex = LoadPictures(_fileIndex, 1, 0);
 }
 void ArchivePicture(ImageHost imageHost, int step1, int step2)
 {
     _fileIndex = LoadPictures(_fileIndex, step1, step2);
     _currentFiles.Remove(imageHost);
     imageHost.Dispose();
 }
 static NodesTuple SelectIndexes(NodesTuple idx, int step1, int step2)
 => new NodesTuple(idx?.Left.SafeStep(step1), idx?.Right.SafeStep(step2));