diff --git a/app/Sentinel.NLogViewer.App/ImportDestinationWindow.xaml b/app/Sentinel.NLogViewer.App/ImportDestinationWindow.xaml
new file mode 100644
index 0000000..0dbdbef
--- /dev/null
+++ b/app/Sentinel.NLogViewer.App/ImportDestinationWindow.xaml
@@ -0,0 +1,66 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/app/Sentinel.NLogViewer.App/ImportDestinationWindow.xaml.cs b/app/Sentinel.NLogViewer.App/ImportDestinationWindow.xaml.cs
new file mode 100644
index 0000000..0af17dd
--- /dev/null
+++ b/app/Sentinel.NLogViewer.App/ImportDestinationWindow.xaml.cs
@@ -0,0 +1,176 @@
+using System;
+using System.Collections.Generic;
+using System.Linq;
+using System.Windows;
+using System.Windows.Controls;
+using Microsoft.Extensions.DependencyInjection;
+using Sentinel.NLogViewer.App.Models;
+using Sentinel.NLogViewer.App.Services;
+
+namespace Sentinel.NLogViewer.App;
+
+///
+/// Dialog for choosing how imported log files are routed to tabs.
+///
+public partial class ImportDestinationWindow : Window
+{
+ private readonly IList _tabs;
+ private readonly bool _showExistingTabOption;
+ private readonly bool _showPerFileNewTabOption;
+ private readonly LogTabViewModel? _preferredExistingTab;
+
+ ///
+ /// The user's choice when is true.
+ ///
+ public LogFileImportBatchContext.DestinationMode SelectedMode { get; private set; }
+
+ ///
+ /// When is , the tab to append to.
+ ///
+ public LogTabViewModel? SelectedExistingTab { get; private set; }
+
+ ///
+ /// Creates the import destination picker.
+ ///
+ /// Open tabs used for "existing tab" mode.
+ /// Tab to pre-select when enabling existing-tab mode.
+ /// When false (no open tabs yet), hides existing-tab UI.
+ /// When false (single file only), hides the "new tab per file" choice.
+ public ImportDestinationWindow(IList tabs, LogTabViewModel? preferredExistingTab,
+ bool showExistingTabOption, bool showPerFileNewTabOption = true)
+ {
+ if (tabs == null)
+ throw new ArgumentNullException(nameof(tabs));
+
+ _tabs = tabs;
+ _showExistingTabOption = showExistingTabOption;
+ _showPerFileNewTabOption = showPerFileNewTabOption;
+ _preferredExistingTab = preferredExistingTab;
+
+ InitializeComponent();
+
+ TabsListBox.ItemsSource = _tabs;
+ UpdateExistingTabAvailability();
+ UpdatePerFileNewTabOptionVisibility();
+ ApplyInitialSelection();
+ UpdateOkEnabled();
+
+ KeyDown += (_, e) =>
+ {
+ if (e.Key == System.Windows.Input.Key.Escape)
+ DialogResult = false;
+ };
+ }
+
+ private void ApplyInitialSelection()
+ {
+ if (_showExistingTabOption && _tabs.Count > 0)
+ {
+ var fallback = _tabs.First();
+ TabsListBox.SelectedItem =
+ _preferredExistingTab != null && _tabs.Contains(_preferredExistingTab)
+ ? _preferredExistingTab
+ : fallback;
+ ExistingTabRadio.IsChecked = true;
+ TabsListBox.IsEnabled = true;
+ SingleNewTabRadio.IsChecked = false;
+ PerFileNewTabRadio.IsChecked = false;
+ }
+ else
+ {
+ SingleNewTabRadio.IsChecked = true;
+ ExistingTabRadio.IsChecked = false;
+ PerFileNewTabRadio.IsChecked = false;
+ TabsListBox.IsEnabled = false;
+ if (_tabs.Count > 0 && TabsListBox.SelectedItem == null)
+ TabsListBox.SelectedItem = _tabs.First();
+ }
+ }
+
+ private void UpdateExistingTabAvailability()
+ {
+ if (!_showExistingTabOption)
+ {
+ ExistingTabRadio.Visibility = Visibility.Collapsed;
+ TabsListBox.Visibility = Visibility.Collapsed;
+ return;
+ }
+
+ ExistingTabRadio.Visibility = Visibility.Visible;
+ TabsListBox.Visibility = Visibility.Visible;
+ TabsListBox.IsEnabled = ExistingTabRadio.IsChecked == true;
+ }
+
+ ///
+ /// Hides "new tab per file" when only one file is being imported (option is redundant).
+ ///
+ private void UpdatePerFileNewTabOptionVisibility()
+ {
+ PerFileNewTabRadio.Visibility = _showPerFileNewTabOption ? Visibility.Visible : Visibility.Collapsed;
+ }
+
+ private void ModeRadio_Checked(object sender, RoutedEventArgs e)
+ {
+ if (TabsListBox == null || ExistingTabRadio == null || OkButton == null)
+ return;
+
+ TabsListBox.IsEnabled = ExistingTabRadio.IsChecked == true;
+ if (!TabsListBox.IsEnabled && TabsListBox.Items.Count > 0 && TabsListBox.SelectedItem == null)
+ TabsListBox.SelectedIndex = 0;
+
+ UpdateOkEnabled();
+ }
+
+ private void TabsListBox_SelectionChanged(object sender, SelectionChangedEventArgs e)
+ {
+ UpdateOkEnabled();
+ }
+
+ private void UpdateOkEnabled()
+ {
+ var existingMode = ExistingTabRadio.IsChecked == true;
+ OkButton.IsEnabled = !existingMode || TabsListBox.SelectedItem is LogTabViewModel;
+ }
+
+ private void OkButton_Click(object sender, RoutedEventArgs e)
+ {
+ if (ExistingTabRadio.IsChecked == true)
+ {
+ if (TabsListBox.SelectedItem is not LogTabViewModel tab)
+ {
+ MessageBox.Show(
+ this,
+ GetMessage("ImportDestination_SelectTab", "Please select a tab."));
+ return;
+ }
+
+ SelectedMode = LogFileImportBatchContext.DestinationMode.ExistingTab;
+ SelectedExistingTab = tab;
+ }
+ else if (_showPerFileNewTabOption && PerFileNewTabRadio.IsChecked == true)
+ {
+ SelectedMode = LogFileImportBatchContext.DestinationMode.PerFileNewTab;
+ SelectedExistingTab = null;
+ }
+ else
+ {
+ SelectedMode = LogFileImportBatchContext.DestinationMode.SingleNewTab;
+ SelectedExistingTab = null;
+ }
+
+ DialogResult = true;
+ }
+
+ private static string GetMessage(string key, string fallback)
+ {
+ try
+ {
+ var loc = App.ServiceProvider?.GetService();
+ return loc != null ? loc.GetString(key, fallback) : fallback;
+ }
+ catch
+ {
+ return fallback;
+ }
+ }
+}
diff --git a/app/Sentinel.NLogViewer.App/MainWindow.xaml b/app/Sentinel.NLogViewer.App/MainWindow.xaml
index 3b6378f..3098502 100644
--- a/app/Sentinel.NLogViewer.App/MainWindow.xaml
+++ b/app/Sentinel.NLogViewer.App/MainWindow.xaml
@@ -18,11 +18,11 @@
-
+ ItemsSource="{Binding DataContext.LogEntries, RelativeSource={RelativeSource AncestorType=ContentPresenter}}"
+ MaxCount="{Binding DataContext.MaxCount, RelativeSource={RelativeSource AncestorType=ContentPresenter}}"/>
@@ -48,6 +48,12 @@
InputGestureText="Ctrl+,"/>