// Copyright (c) 2017 schick Informatik // Description: The main application window // using System; using System.Collections.Generic; using System.Collections.ObjectModel; using System.Diagnostics; using System.Windows; using System.Windows.Input; using System.Windows.Media; using System.Windows.Media.Imaging; using System.Threading; using System.Timers; using bsmd.database; using System.Windows.Controls; using ENI2.Controls; using ENI2.EditControls; using ENI2.Util; using log4net; using System.ComponentModel; using ENI2.Locode; using System.Text; namespace ENI2 { /// /// Interaction logic for MainWindow.xaml /// public partial class MainWindow : Window { #region Fields private ReportingPartyControl rpControl; private POListControl poControl; private ServerStatusControl statusControl; private readonly SucheControl sucheControl; private CompareExcelDialog compareExcelDialog; private bool efMode = false; private bool dbConnected; private readonly ScaleTransform _transform = new ScaleTransform(1.0, 1.0); private readonly Dictionary openTabs = new Dictionary(); private readonly Dictionary lockedCores = new Dictionary(); private int failedLogonCount; private ReportingParty userEntity; private readonly ILog _log = LogManager.GetLogger(typeof(MainWindow)); private readonly DatabaseEntityWatchdog _dbWatchDog; private readonly Dictionary showIdDict = new Dictionary(); #endregion #region Construction public MainWindow() { Thread.Sleep(500); InitializeComponent(); App.SplashScreen.ShowMessage("loading.."); this.sucheControl = new SucheControl(); this.tabSearch.Content = this.sucheControl; this.sucheControl.buttonSuche.IsDefault = true; this.sucheControl.MessageCoreSelected += AnmeldungenControl_MessageCoreSelected; this.mainPanel.LayoutTransform = this._transform; this._dbWatchDog = new DatabaseEntityWatchdog(); this._dbWatchDog.DatabaseEntityChanged += _dbWatchDog_DatabaseEntityChanged; this._dbWatchDog.VisitTransitIdUpdated += _dbWatchDog_VisitTransitIdUpdated; App.SplashScreen.ShowMessage("done"); Thread.Sleep(500); App.SplashScreen.LoadComplete(); } #endregion #region Search related event handler private void AnmeldungenControl_MessageCoreSelected(MessageCore aMessageCore) { if(aMessageCore != null) { if (!openTabs.ContainsKey(aMessageCore.Id.Value)) { ClosableTabItem searchResultItem = new ClosableTabItem(); // try to lock the item Guid lockedUserId = Guid.Empty; if (!(aMessageCore.Cancelled ?? false)) { try { lockedUserId = App.LockingServiceClient.Lock(aMessageCore.Id.Value, this.userEntity.Id.Value); if (lockedUserId == Guid.Empty) { this.lockedCores[searchResultItem] = aMessageCore.Id.Value; } } catch (Exception ex) { // TODO: wenn der Locking Service nicht erreichbar ist sollte das Ganze trotzdem noch irgendwie funktionieren _log.ErrorFormat("LockingService.Lock: {0}", ex.Message); } } bool iDidLockIt = (lockedUserId == Guid.Empty) && !(aMessageCore.Cancelled ?? false); searchResultItem.TabClosing += SearchResultItem_TabClosing; DateTime? eta = aMessageCore.IsTransit ? aMessageCore.ETAKielCanal : aMessageCore.ETA; searchResultItem.SetHeaderText(string.Format("{0} [{1}-{2}]", aMessageCore.Shipname, aMessageCore.PoC, eta.HasValue ? eta.Value.ToShortDateString() : ""), iDidLockIt); searchResultItem.IsCancelled = aMessageCore.Cancelled ?? false; DetailRootControl drc = new DetailRootControl(aMessageCore); drc.LockedByOtherUser = !iDidLockIt; if (!(aMessageCore.Cancelled ?? false)) { drc.LockedBy = iDidLockIt ? this.userEntity : DBManager.Instance.GetReportingPartyDict()[lockedUserId]; } searchResultItem.Content = drc; this.mainFrame.Items.Add(searchResultItem); Dispatcher.BeginInvoke((Action)(() => this.mainFrame.SelectedIndex = (this.mainFrame.Items.Count - 1))); this.openTabs.Add(aMessageCore.Id.Value, searchResultItem); this._dbWatchDog.Register(aMessageCore); drc.HighlightReset += Drc_HighlightReset; drc.OpenNewCoreRequested += (core) => this.AnmeldungenControl_MessageCoreSelected(core); drc.ReloadCoreRequested += Drc_ReloadCoreRequested; } else { Dispatcher.BeginInvoke((Action)(() => this.mainFrame.SelectedItem = openTabs[aMessageCore.Id.Value])); } } } private void Drc_ReloadCoreRequested(Guid obj) { if(openTabs.ContainsKey(obj)) { if (openTabs[obj].Content is DetailRootControl drc) { drc.ReloadCore(); } } } private void Drc_HighlightReset(DatabaseEntity entity) { if (entity is MessageCore resetCore) { if (openTabs.ContainsKey(resetCore.Id.Value)) { openTabs[resetCore.Id.Value].IsHighlighted = false; } } } private void SearchResultItem_TabClosing(object sender, CancelEventArgs e) { if (sender is ClosableTabItem tabItem) { DetailRootControl drc = tabItem.Content as DetailRootControl; // Test for unsaved changes if (drc.HasUnsavedChanges) { if (MessageBox.Show(Properties.Resources.textConfirmWithoutSaving, Properties.Resources.textConfirmation, MessageBoxButton.YesNo, MessageBoxImage.Question, MessageBoxResult.No) == MessageBoxResult.No) e.Cancel = true; } // Test for unsent messages List unSentMessages = drc.HasUnsentMessages; if (!e.Cancel && (unSentMessages.Count > 0)) { StringBuilder mBuilder = new StringBuilder(); foreach (string messageType in unSentMessages) { mBuilder.Append(messageType); mBuilder.Append(" "); } mBuilder.AppendLine(); mBuilder.Append(Properties.Resources.textConfirmUnsentMessages); if (MessageBox.Show(mBuilder.ToString(), Properties.Resources.textConfirmation, MessageBoxButton.YesNo, MessageBoxImage.Question, MessageBoxResult.No) == MessageBoxResult.No) e.Cancel = true; } // Test for unconfirmed messages List unConfirmedMessages = drc.HasUnConfirmedMessages; if(!e.Cancel && (unConfirmedMessages.Count > 0)) { StringBuilder mBuilder = new StringBuilder(); foreach (string messageType in unConfirmedMessages) { mBuilder.Append(messageType); mBuilder.Append(" "); } mBuilder.AppendLine(); mBuilder.Append(Properties.Resources.textConfirmUnconfirmedMessages); if (MessageBox.Show(mBuilder.ToString(), Properties.Resources.textConfirmation, MessageBoxButton.YesNo, MessageBoxImage.Question, MessageBoxResult.No) == MessageBoxResult.No) e.Cancel = true; } if (!e.Cancel) { if (lockedCores.ContainsKey(tabItem)) { try { App.LockingServiceClient.Unlock(lockedCores[tabItem], this.userEntity.Id.Value); lockedCores.Remove(tabItem); } catch (Exception ex) { _log.ErrorFormat("LockingService.Unlock: {0}", ex.Message); } } if (openTabs.ContainsKey(drc.Core.Id.Value)) { this._dbWatchDog.UnRegister(drc.Core); openTabs.Remove(drc.Core.Id.Value); } } } } #endregion #region Window control click event handler private void logoImage_MouseUp(object sender, MouseButtonEventArgs e) { if(efMode) Process.Start("http://www.eintracht-frankfurt.de/home.html"); else Process.Start("http://www.eureport.de/"); } private void buttonAbout_Click(object sender, RoutedEventArgs e) { AboutDialog ad = new AboutDialog(); ad.Show(); } private void radioButton_Click(object sender, RoutedEventArgs e) { this.rootContainer.Children.Clear(); bool newButtonsVisible = false; if(sender == this.buttonNotifications) { this.rootContainer.Children.Add(this.mainFrame); newButtonsVisible = true; } else if(sender == this.buttonUserAdmin) { if (this.rpControl == null) { this.rpControl = new ReportingPartyControl(); Dictionary repPartyDict = DBManager.Instance.GetReportingPartyDict(); this.rpControl.ReportingParties = new ObservableCollection(repPartyDict.Values); } this.rootContainer.Children.Add(this.rpControl); } else if(sender == this.buttonPOListe) { if (this.poControl == null) { this.poControl = new POListControl(); } this.rootContainer.Children.Add(poControl); } else if(sender == this.buttonStatus) { if(this.statusControl == null) { this.statusControl = new ServerStatusControl(); } this.rootContainer.Children.Add(this.statusControl); } this.buttonNewId.Visibility = newButtonsVisible ? Visibility.Visible : Visibility.Hidden; this.buttonNewWithId.Visibility = newButtonsVisible ? Visibility.Visible : Visibility.Hidden; } private void buttonCompareSheets_Click(object sender, RoutedEventArgs ev) { // Open compare dialog if(compareExcelDialog == null) { this.compareExcelDialog = new CompareExcelDialog(); this.compareExcelDialog.Closed += (o, e) => this.compareExcelDialog = null; compareExcelDialog.Show(); } else { compareExcelDialog.BringUp(); } } #endregion #region window lifetime event handler private void Window_Loaded(object sender, RoutedEventArgs e) { // if (Debugger.IsAttached) this.busyIndicator.IsBusy = false; // not for me :-P this.dbConnected = DBManager.Instance.Connect(Properties.Settings.Default.ConnectionString); labelGeneralStatus.Text = dbConnected ? "DB Connected" : "DB Connect failed"; labelVersion.Text = "V. " + System.Reflection.Assembly.GetExecutingAssembly().GetName().Version; labelUsername.Text = System.Security.Principal.WindowsIdentity.GetCurrent().Name; if (Keyboard.IsKeyDown(Key.LeftShift)) { efMode = true; logoImage.Source = new BitmapImage(new Uri("pack://application:,,,/Resources/ef_logo.png")); } Microsoft.Win32.SystemEvents.SessionEnded += SystemEvents_SessionEnded; } private void SystemEvents_SessionEnded(object sender, Microsoft.Win32.SessionEndedEventArgs e) { this.UnlockOpenCores(); } private void Window_Closing(object sender, System.ComponentModel.CancelEventArgs e) { this.UnlockOpenCores(); DBManager.Instance.Disconnect(); Properties.Settings.Default.MainWindowPlacement = this.GetPlacement(); Properties.Settings.Default.Save(); Microsoft.Win32.SystemEvents.SessionEnded -= SystemEvents_SessionEnded; } private void Window_SourceInitialized(object sender, EventArgs e) { this.SetPlacement(Properties.Settings.Default.MainWindowPlacement); this.textUsername.Focus(); } #endregion #region Command event handler private void ExecutedClearCommand(object sender, ExecutedRoutedEventArgs e) { if (!(e.OriginalSource is Xceed.Wpf.Toolkit.DateTimePicker dtPicker)) dtPicker = CustomCommands.FindParent(e.OriginalSource as DependencyObject); if (dtPicker != null) { dtPicker.Value = null; } // das funktioniert auch für Comboboxen :P if (!(e.OriginalSource is ComboBox cb)) { cb = CustomCommands.FindParent(e.OriginalSource as DependencyObject); } if (cb != null) { cb.SelectedIndex = -1; LocalValueEnumerator localSetProperties = cb.GetLocalValueEnumerator(); while(localSetProperties.MoveNext()) { if(localSetProperties.Current.Property.Name == "SelectedIndex") { cb.ClearValue(localSetProperties.Current.Property); } } } } private void CanExecuteClearCommand(object sender, CanExecuteRoutedEventArgs e) { // validate? e.CanExecute = true; } #endregion #region window control events private void buttonNewTransitIdClick(object sender, RoutedEventArgs e) { MessageCore newCore = new MessageCore(); VisitIdDialog visitIdDialog = new VisitIdDialog(); visitIdDialog.Core = newCore; visitIdDialog.Closed += (senderDialog, closeArgs) => { VisitIdDialog closedDialog = senderDialog as VisitIdDialog; if(closedDialog.IsOK) { Util.UIHelper.SetBusyState(); if (!closedDialog.Core.IsDK) { // deutsche Häfen fordern eine Visit-Id an, für DK erfolgt hier nur die Anlage eines Datensatzes closedDialog.Core.BSMDStatusInternal = MessageCore.BSMDStatus.TOSEND; } if (closedDialog.Core.PoC.Equals("ZZNOK")) closedDialog.Core.IsTransit = true; closedDialog.Core.Incoming = true; closedDialog.Core.DefaultReportingPartyId = this.userEntity.Id; DBManager.GetSingleCon(Properties.Settings.Default.ConnectionString).Save(closedDialog.Core); // Meldeklassen für neuen Anlauf erzeugen bsmd.database.Util.CreateMessagesForCore(closedDialog.Core, null, userEntity); // watchdog registrieren this._dbWatchDog.Register(closedDialog.Core); // Wartedialog anzeigen ShowIdDialog showIdDialog = new ShowIdDialog(closedDialog.Core); showIdDialog.Closed += (sid, showIdArgs) => { if (((ShowIdDialog)sid).OpenCore) { Dispatcher.BeginInvoke((Action)(() => { this.AnmeldungenControl_MessageCoreSelected(closedDialog.Core); // in einem neuen Reiter öffnen })); } // wenn der Dialog vorzeitig geschlossen wird erkennt man später dass man die Id dort nicht updaten braucht if (this.showIdDict.ContainsKey(closedDialog.Core.Id.Value)) this.showIdDict[closedDialog.Core.Id.Value] = null; this.UpdateWaitIdLabel(); }; this.showIdDict.Add(closedDialog.Core.Id.Value, showIdDialog); showIdDialog.Show(); showIdDialog.Activate(); this.UpdateWaitIdLabel(); } }; visitIdDialog.Show(); } /// /// Callback Neuanlage mit vorhandener ID (zur Abfrage der verfügbaren Meldeklassen) /// private void buttonNewWithIdClick(object sender, RoutedEventArgs e) { NewWithIdDialog newWithIdDialog = new NewWithIdDialog(); newWithIdDialog.OKClicked += new Action(() => { if (newWithIdDialog.ValidId) { MessageCore newCore = new MessageCore(); newCore.Incoming = true; newCore.InitialHIS = Message.NSWProvider.DUDR; bool alreadyInSystem = false; if (bsmd.database.Util.IsTransitId(newWithIdDialog.VisitTransitId)) { newCore.TransitId = newWithIdDialog.VisitTransitId; newCore.IsTransit = true; alreadyInSystem = (DBManager.GetSingleCon(Properties.Settings.Default.ConnectionString).GetMessageCoreByTransitId(newWithIdDialog.VisitTransitId) != null); } else { newCore.VisitId = newWithIdDialog.VisitTransitId; newCore.IsTransit = false; alreadyInSystem = (DBManager.GetSingleCon(Properties.Settings.Default.ConnectionString).GetMessageCoreByVisitId(newWithIdDialog.VisitTransitId) != null); } if (alreadyInSystem) { MessageBox.Show(Properties.Resources.textVisitTransitAlreadyInDatabase, Properties.Resources.textCaptionError, MessageBoxButton.OK, MessageBoxImage.Error); } else { newCore.DefaultReportingPartyId = this.userEntity.Id; newCore.PoC = newWithIdDialog.VisitTransitId.Substring(0, 5); newCore.Portname = LocodeDB.PortNameFromLocode(newCore.PoC); newCore.IMO = newWithIdDialog.IMO; newCore.ENI = newWithIdDialog.ENI; newCore.InitialHIS = newWithIdDialog.SelectedHIS; if (newCore.IsTransit) newCore.ETAKielCanal = newWithIdDialog.ETA; else newCore.ETA = newWithIdDialog.ETA; DBManager.GetSingleCon(Properties.Settings.Default.ConnectionString).Save(newCore); // Meldeklassen für neuen Anlauf erzeugen: bsmd.database.Util.CreateMessagesForCore(newCore, null, userEntity); this.AnmeldungenControl_MessageCoreSelected(newCore); // in einem neuen Reiter öffnen // watchdog registrieren, damit die "grüne" Markierung erscheint, sobald die Anmeldung durch den Excel-Prozess gelaufen ist. this._dbWatchDog.Register(newCore); } } }); newWithIdDialog.Show(); } private void closeButton_Click(object sender, RoutedEventArgs e) { // close particular tab if (e.Source is TabItem tabitem) { this.mainFrame.Items.Remove(tabitem); } } private void _dbWatchDog_DatabaseEntityChanged(DatabaseEntity entity) { if (entity is MessageCore changedCore) { // tab färben if (this.openTabs.ContainsKey(changedCore.Id.Value)) { TabItem tabitem = this.openTabs[changedCore.Id.Value]; this.Dispatcher.BeginInvoke(new Action(() => { DetailRootControl drc = tabitem.Content as DetailRootControl; if (tabitem != this.mainFrame.SelectedItem) drc.ReloadCore(); // hoffentlich ist das nicht "too much" bool respondHighlight = false; if (changedCore.BSMDStatusInternal == MessageCore.BSMDStatus.RESPONDED) respondHighlight = true; if (tabitem is ClosableTabItem closableTabItem) { if (respondHighlight) (closableTabItem).IsHighlightResponded = true; else (closableTabItem).IsHighlighted = true; } })); changedCore.IsHighlighted = false; } } } private void _dbWatchDog_VisitTransitIdUpdated(DatabaseEntity entity) { if (entity is MessageCore changedCore) { if (showIdDict.ContainsKey(changedCore.Id.Value)) { if (this.showIdDict[changedCore.Id.Value] != null) { this.showIdDict[changedCore.Id.Value].UpdateId(changedCore.VisitId.IsNullOrEmpty() ? changedCore.TransitId : changedCore.VisitId); } else { // Wartedialog nochmal this.Dispatcher.Invoke(new Action(() => { ShowIdDialog showIdDialog = new ShowIdDialog(changedCore); showIdDialog.Closed += (sid, showIdArgs) => { if (((ShowIdDialog)sid).OpenCore) this.AnmeldungenControl_MessageCoreSelected(changedCore); }; showIdDialog.Show(); showIdDialog.Activate(); showIdDialog.UpdateId(changedCore.VisitId.IsNullOrEmpty() ? changedCore.TransitId : changedCore.VisitId); })); } this.showIdDict.Remove(changedCore.Id.Value); // this._dbWatchDog.UnRegister(changedCore); // wird ggf später abgeräumt wenn der Tab geschlossen wird this.Dispatcher.Invoke(new Action(() => { UpdateWaitIdLabel(); })); } if (this.openTabs.ContainsKey(changedCore.Id.Value)) { TabItem tabitem = this.openTabs[changedCore.Id.Value]; this.Dispatcher.BeginInvoke(new Action(() => { DetailRootControl drc = tabitem.Content as DetailRootControl; drc?.CoreChanged(changedCore); })); } } } #endregion #region mouse wheel / zooming events protected override void OnPreviewMouseWheel(MouseWheelEventArgs e) { base.OnPreviewMouseWheel(e); if (Keyboard.IsKeyDown(Key.LeftCtrl) || Keyboard.IsKeyDown(Key.RightCtrl)) { this._transform.ScaleX += (e.Delta > 0) ? 0.05 : -0.05; this._transform.ScaleY += (e.Delta > 0) ? 0.05 : -0.05; } } protected override void OnPreviewMouseDown(MouseButtonEventArgs e) { base.OnPreviewMouseDown(e); if (e.ChangedButton == MouseButton.Middle) { if (Keyboard.IsKeyDown(Key.LeftCtrl) || Keyboard.IsKeyDown(Key.RightCtrl)) { this._transform.ScaleX = 1.0; this._transform.ScaleY = 1.0; } } } #endregion #region logon box event handlers private void buttonLogin_Click(object sender, RoutedEventArgs e) { if(this.textPassword.Password.IsNullOrEmpty() || this.textUsername.Text.IsNullOrEmpty()) { this.labelLoginResult.Content = Properties.Resources.textUserNamePasswordEmpty; return; } switch(ReportingParty.Login(this.textUsername.Text, this.textPassword.Password, out this.userEntity)) { case ReportingParty.LogonResult.OK: this.busyIndicator.IsBusy = false; this.labelStatusBar.Text = string.Format("Rep.Party: {0} {1} [{2}]", this.userEntity.FirstName, this.userEntity.LastName, this.userEntity.Logon); App.UserId = this.userEntity.Id; ReportingParty.CurrentReportingParty = this.userEntity; if (this.userEntity.IsAdmin) { this.buttonUserAdmin.Visibility = Visibility.Visible; this.buttonPOListe.Visibility = Visibility.Visible; this.sucheControl.AdminMode = true; } break; case ReportingParty.LogonResult.FAILED: this.labelLoginResult.Content = Properties.Resources.textWrongPassword; failedLogonCount++; break; case ReportingParty.LogonResult.USERUKN: this.labelLoginResult.Content = Properties.Resources.textUsernameUnknown; failedLogonCount++; break; } if (failedLogonCount == 3) { this.buttonLogin.IsEnabled = false; MessageBox.Show(Properties.Resources.textWrongPasswordThreeTimes, Properties.Resources.textCaptionError); } } private void buttonExit_Click(object sender, RoutedEventArgs e) { this.Close(); } #endregion #region private methods private void UnlockOpenCores() { try { // unlock all cores foreach (ClosableTabItem tabItem in this.lockedCores.Keys) { App.LockingServiceClient.Unlock(lockedCores[tabItem], this.userEntity.Id.Value); } } catch (Exception ex) { _log.ErrorFormat("LockingService.Unlock: {0}", ex.Message); } } private void UpdateWaitIdLabel() { if(this.showIdDict.Count == 0) { this.labelStatusId.Content = ""; } else { this.labelStatusId.Content = string.Format("waiting for {0} id(s)..", this.showIdDict.Count); } } #endregion } }