/Application/GUI/Controls/Playback.xaml.cs
C# | 953 lines | 623 code | 138 blank | 192 comment | 119 complexity | c94bcab539fe64c044bc14d377f4041f MD5 | raw file
1/** 2 * Playback.xaml.cs 3 * 4 * All buttons and controls used to manage playback such as 5 * play, pause, next and previous along with seek, volume and 6 * search. 7 * 8 * * * * * * * * * 9 * 10 * This code is part of the Stoffi Music Player Project. 11 * Visit our website at: stoffiplayer.com 12 * 13 * This program is free software; you can redistribute it and/or 14 * modify it under the terms of the GNU General Public License 15 * as published by the Free Software Foundation; either version 16 * 3 of the License, or (at your option) any later version. 17 * 18 * See stoffiplayer.com/license for more information. 19 **/ 20 21using System; 22using System.Collections.Generic; 23using System.Linq; 24using System.Text; 25using System.Windows; 26using System.Windows.Controls; 27using System.Windows.Data; 28using System.Windows.Documents; 29using System.Windows.Input; 30using System.Windows.Media; 31using System.Windows.Media.Imaging; 32using System.Windows.Navigation; 33using System.Windows.Threading; 34using System.Windows.Shapes; 35 36namespace Stoffi 37{ 38 /// <summary> 39 /// A playback control that contains all playback buttons, track information, volume and seek slides as well as a search box 40 /// </summary> 41 public partial class Playback : UserControl 42 { 43 #region Members 44 45 public StoffiWindow ParentWindow; 46 private BookmarkLayer bookmarkLayer; 47 48 // compression constants 49 private double startCompression = 800; 50 private double volumeMaxWidth = 70; 51 private double volumeMinWidth = 35; 52 private double repshuMaxMargin = 10; 53 private double repshuMinMargin = 0; 54 private double searchMaxWidth = 200; 55 private double searchMinWidth = 90; 56 private double volumeMaxMargin = 10; 57 private double volumeMinMargin = 0; 58 59 #endregion 60 61 #region Constructor 62 63 /// <summary> 64 /// Creates the playback control 65 /// </summary> 66 public Playback() 67 { 68 U.L(LogLevel.Debug, "PLAYBACK", "Initialize"); 69 InitializeComponent(); 70 U.L(LogLevel.Debug, "PLAYBACK", "Initialized"); 71 SettingsManager.PropertyChanged += new PropertyChangedWithValuesEventHandler(SettingsManager_PropertyChanged); 72 SongProgress.ValueChanged -= new RoutedPropertyChangedEventHandler<double>(SongProgress_ValueChanged); 73 SongProgress.Value = SettingsManager.Seek; 74 SongProgress.ValueChanged += new RoutedPropertyChangedEventHandler<double>(SongProgress_ValueChanged); 75 VolumeSlide.AutoToolTipPlacement = System.Windows.Controls.Primitives.AutoToolTipPlacement.TopLeft; 76 U.L(LogLevel.Debug, "PLAYBACK", "Created"); 77 } 78 79 #endregion 80 81 #region Methods 82 83 #region Public 84 85 /// <summary> 86 /// 87 /// </summary> 88 /// <param name="width"></param> 89 public void Compress(double width) 90 { 91 if (width < startCompression) 92 { 93 double stretchFactor = (width - ParentWindow.MinWidth) / (startCompression - ParentWindow.MinWidth); 94 95 double volumeWidth = volumeMinWidth + ((volumeMaxWidth - volumeMinWidth) * stretchFactor); 96 double volumeMargin = volumeMinMargin + ((volumeMaxMargin - volumeMinMargin) * stretchFactor); 97 double searchWidth = searchMinWidth + ((searchMaxWidth - searchMinWidth) * stretchFactor); 98 double repshuMargin = repshuMinMargin + ((repshuMaxMargin - repshuMinMargin) * stretchFactor); 99 100 VolumeSlide.Width = volumeWidth; 101 VolumeSlide.Margin = new Thickness(volumeMargin, 0, volumeMargin, 0); 102 Search.SearchContainer.Width = searchWidth; 103 RepeatShuffleContainer.Margin = new Thickness(repshuMargin, 0, repshuMargin, 0); 104 } 105 else 106 { 107 VolumeSlide.Width = volumeMaxWidth; 108 VolumeSlide.Margin = new Thickness(volumeMaxMargin, 0, volumeMaxMargin, 0); 109 Search.SearchContainer.Width = searchMaxWidth; 110 RepeatShuffleContainer.Margin = new Thickness(repshuMaxMargin, 0, repshuMaxMargin, 0); 111 } 112 } 113 114 /// <summary> 115 /// 116 /// </summary> 117 /// <param name="pos"></param> 118 public void AddBookmark(double pos) 119 { 120 while (bookmarkLayer == null) ; 121 bookmarkLayer.AddBookmark(pos); 122 } 123 124 /// <summary> 125 /// 126 /// </summary> 127 /// <param name="pos"></param> 128 public void RemoveBookmark(double pos) 129 { 130 while (bookmarkLayer == null) ; 131 bookmarkLayer.RemoveBookmark(pos); 132 } 133 134 /// <summary> 135 /// 136 /// </summary> 137 public void ClearBookmarks() 138 { 139 while (bookmarkLayer == null) ; 140 bookmarkLayer.ClearBookmarks(); 141 } 142 143 #endregion 144 145 #region Private 146 147 /// <summary> 148 /// Updates the shuffle button to reflect the current Shuffle state 149 /// </summary> 150 private void UpdateShuffle() 151 { 152 switch (SettingsManager.Shuffle) 153 { 154 case true: 155 ShuffleButton.Style = (Style)FindResource("ShuffleButtonStyle"); 156 break; 157 158 case false: 159 default: 160 ShuffleButton.Style = (Style)FindResource("ShuffleGrayButtonStyle"); 161 break; 162 } 163 } 164 165 /// <summary> 166 /// Updates the repeat button to reflect the current Repeat state 167 /// </summary> 168 private void UpdateRepeat() 169 { 170 switch (SettingsManager.Repeat) 171 { 172 case RepeatState.RepeatAll: 173 RepeatButton.Style = (Style)FindResource("RepeatAllButtonStyle"); 174 break; 175 176 case RepeatState.RepeatOne: 177 RepeatButton.Style = (Style)FindResource("RepeatOneButtonStyle"); 178 break; 179 180 case RepeatState.NoRepeat: 181 default: 182 RepeatButton.Style = (Style)FindResource("RepeatGrayButtonStyle"); 183 break; 184 } 185 } 186 187 /// <summary> 188 /// Updates the information about the currently loaded track 189 /// </summary> 190 private void UpdateInfo() 191 { 192 if (SettingsManager.CurrentTrack == null) 193 { 194 InfoName.Text = U.T("PlaybackEmpty"); 195 InfoTimeMinus.Content = "N/A"; 196 InfoTimePlus.Content = "N/A"; 197 SongProgress.ValueChanged -= new RoutedPropertyChangedEventHandler<double>(SongProgress_ValueChanged); 198 SongProgress.Value = SettingsManager.Seek; 199 SongProgress.SecondValueWidth = 0; 200 SongProgress.ValueChanged += new RoutedPropertyChangedEventHandler<double>(SongProgress_ValueChanged); 201 } 202 else 203 { 204 TrackData t = SettingsManager.CurrentTrack; 205 InfoName.Text = t.Artist + " - " + t.Title; 206 207 double pos = MediaManager.GetPosition(); 208 double len = MediaManager.GetLength(); 209 210 if (pos < 0) pos = 0; 211 if (len < 0) len = 0; 212 213 TimeSpan timePlus = new TimeSpan(0, 0, (int)pos); 214 TimeSpan timeMinus = new TimeSpan(0, 0, (int)(len - pos)); 215 216 if (timePlus.TotalSeconds < 0) 217 InfoTimePlus.Content = "N/A"; 218 else if (timePlus.TotalSeconds > 0) 219 InfoTimePlus.Content = U.TimeSpanToString(timePlus); 220 221 if (timeMinus.TotalSeconds < 0) 222 InfoTimeMinus.Content = "N/A"; 223 else if (timeMinus.TotalSeconds > 0) 224 InfoTimeMinus.Content = "-" + U.TimeSpanToString(timeMinus); 225 226 double seek = (pos / len) * 10; 227 if (timePlus.Seconds < 0 || Double.IsNaN(seek) || Double.IsInfinity(seek)) seek = 0; 228 229 if (seek > 0 || SettingsManager.MediaState != MediaState.Playing) 230 { 231 SongProgress.ValueChanged -= new RoutedPropertyChangedEventHandler<double>(SongProgress_ValueChanged); 232 SongProgress.Value = seek; 233 SongProgress.ValueChanged += new RoutedPropertyChangedEventHandler<double>(SongProgress_ValueChanged); 234 } 235 } 236 } 237 238 #endregion 239 240 #region Event handlers 241 242 /// <summary> 243 /// Invoked when the user clicks on pause/play 244 /// </summary> 245 /// <param name="sender">The sender of the event</param> 246 /// <param name="e">The event data</param> 247 private void PausePlay_Click(object sender, RoutedEventArgs e) 248 { 249 if (SettingsManager.MediaState == MediaState.Playing) 250 MediaManager.Pause(); 251 else if (SettingsManager.CurrentTrack != null) 252 MediaManager.Play(); 253 else if (ParentWindow.GetCurrentTrackList() != null) 254 { 255 if (ParentWindow.GetCurrentTrackList().SelectedItems.Count > 0) 256 { 257 TrackData track = (TrackData)ParentWindow.GetCurrentTrackList().SelectedItem; 258 MediaManager.Load(track); 259 MediaManager.Play(); 260 } 261 else if (ParentWindow.GetCurrentTrackList().Items.Count > 0) 262 { 263 TrackData track = (TrackData)ParentWindow.GetCurrentTrackList().Items[0]; 264 MediaManager.Load(track); 265 MediaManager.Play(); 266 } 267 } 268 } 269 270 /// <summary> 271 /// Invoked when the user clicks on next 272 /// </summary> 273 /// <param name="sender">The sender of the event</param> 274 /// <param name="e">The event data</param> 275 private void Next_Click(object sender, RoutedEventArgs e) 276 { 277 MediaManager.Next(true); 278 } 279 280 /// <summary> 281 /// Invoked when the user clicks on previous 282 /// </summary> 283 /// <param name="sender">The sender of the event</param> 284 /// <param name="e">The event data</param> 285 private void Previous_Click(object sender, RoutedEventArgs e) 286 { 287 MediaManager.Previous(); 288 } 289 290 /// <summary> 291 /// Invoked when the user clicks on repeat 292 /// </summary> 293 /// <param name="sender">The sender of the event</param> 294 /// <param name="e">The event data</param> 295 public void Repeat_Click(object sender, RoutedEventArgs e) 296 { 297 switch (SettingsManager.Repeat) 298 { 299 case RepeatState.RepeatAll: 300 SettingsManager.Repeat = RepeatState.RepeatOne; 301 break; 302 case RepeatState.RepeatOne: 303 SettingsManager.Repeat = RepeatState.NoRepeat; 304 break; 305 case RepeatState.NoRepeat: 306 default: 307 SettingsManager.Repeat = RepeatState.RepeatAll; 308 break; 309 } 310 UpdateRepeat(); 311 } 312 313 /// <summary> 314 /// Invoked when the user clicks on shuffle 315 /// </summary> 316 /// <param name="sender">The sender of the event</param> 317 /// <param name="e">The event data</param> 318 public void Shuffle_Click(object sender, RoutedEventArgs e) 319 { 320 SettingsManager.Shuffle = !SettingsManager.Shuffle; 321 UpdateShuffle(); 322 } 323 324 /// <summary> 325 /// Invoked when the user changes the volume slider 326 /// </summary> 327 /// <param name="sender">The sender of the event</param> 328 /// <param name="e">The event data</param> 329 private void VolumeSlide_ValueChanged(object sender, RoutedPropertyChangedEventArgs<double> e) 330 { 331 if (MediaManager.IsInitialized) 332 SettingsManager.Volume = VolumeSlide.Value; 333 } 334 335 /// <summary> 336 /// Invoked when the user scrolls over the volume slider 337 /// </summary> 338 /// <param name="sender">The sender of the event</param> 339 /// <param name="e">The event data</param> 340 private void VolumeSlide_MouseWheel(object sender, MouseWheelEventArgs e) 341 { 342 if (e.Delta > 0) 343 VolumeSlide.Value+=5; 344 else 345 VolumeSlide.Value-=5; 346 } 347 348 /// <summary> 349 /// Invoked when the user changes the seek slider 350 /// </summary> 351 /// <param name="sender">The sender of the event</param> 352 /// <param name="e">The event data</param> 353 public void SongProgress_ValueChanged(object sender, RoutedPropertyChangedEventArgs<double> e) 354 { 355 if (MediaManager.IsInitialized) 356 { 357 SettingsManager.Seek = SongProgress.Value; 358 359 double pos = (ParentWindow.PlaybackControls.SongProgress.Value / 10) * MediaManager.GetLength(); 360 double rem = MediaManager.GetLength() - pos; 361 TimeSpan timePlus = new TimeSpan(0, 0, (int)pos); 362 TimeSpan timeMinus = new TimeSpan(0, 0, (int)rem); 363 364 if (rem < 0) 365 ParentWindow.PlaybackControls.InfoTimePlus.Content = "N/A"; 366 else 367 ParentWindow.PlaybackControls.InfoTimePlus.Content = U.TimeSpanToString(timePlus); 368 369 if (rem < 0) 370 ParentWindow.PlaybackControls.InfoTimeMinus.Content = "N/A"; 371 else 372 ParentWindow.PlaybackControls.InfoTimeMinus.Content = "-" + U.TimeSpanToString(timeMinus); 373 } 374 } 375 376 /// <summary> 377 /// Invoked when the user modifies the text in the search box 378 /// </summary> 379 /// <param name="e">The event data</param> 380 private void Search_TextChanged(SearchBoxEventArgs e) 381 { 382 String csn = SettingsManager.CurrentSelectedNavigation; 383 if (csn == null) return; 384 385 // switch to currently active navigation 386 if (csn == "NowPlaying") 387 { 388 ParentWindow.NavigationPane.SwitchNavigation(SettingsManager.CurrentActiveNavigation); 389 Search.IsActive = true; 390 Search.Text = e.Text; 391 Search.Position = e.Text.Count(); 392 return; 393 } 394 395 if (ParentWindow == null) return; 396 ParentWindow.GetCurrentTrackList().Filter = e.Text; 397 398 if (csn == "YouTube") 399 { 400 ParentWindow.YouTubeContainer.Search(e.Text); 401 return; 402 } 403 404 if (csn == "History" || SettingsManager.SearchPolicy == SearchPolicy.Global) 405 SettingsManager.HistoryListConfig.Filter = e.Text; 406 407 if (csn == "Queue" || SettingsManager.SearchPolicy == SearchPolicy.Global) 408 SettingsManager.QueueListConfig.Filter = e.Text; 409 410 if (csn == "Library" || SettingsManager.SearchPolicy == SearchPolicy.Global) 411 SettingsManager.FileListConfig.Filter = e.Text; 412 413 if (SettingsManager.SearchPolicy == SearchPolicy.Global) 414 { 415 foreach (PlaylistData pl in SettingsManager.Playlists) 416 pl.ListConfig.Filter = e.Text; 417 } 418 else 419 { 420 if (csn.StartsWith("Playlist:") && SettingsManager.SearchPolicy == SearchPolicy.Partial) 421 foreach (PlaylistData pl in SettingsManager.Playlists) 422 pl.ListConfig.Filter = e.Text; 423 else if (csn.StartsWith("Playlist:")) 424 { 425 PlaylistData p = PlaylistManager.FindPlaylist(csn.Split(new[]{':'},2)[1]); 426 if (p != null) p.ListConfig.Filter = e.Text; 427 } 428 } 429 } 430 431 /// <summary> 432 /// Invoked when the user selects to add a search query to an existing playlist 433 /// </summary> 434 /// <param name="e">The event data</param> 435 private void Search_AddClicked(SearchBoxAddEventArgs e) 436 { 437 List<object> tracks = new List<object>(); 438 foreach (TrackData track in ParentWindow.GetCurrentTrackList().Items) 439 tracks.Add(track); 440 PlaylistManager.AddToPlaylist(tracks, e.PlaylistName); 441 } 442 443 /// <summary> 444 /// Invoked when the user selects to add a search query to a new playlist 445 /// </summary> 446 /// <param name="e">The event data</param> 447 private void Search_AddToNewClicked(SearchBoxAddToNewEventArgs e) 448 { 449 ParentWindow.NavigationPane.AddToPlaylistQueue.Clear(); 450 foreach (TrackData track in ParentWindow.GetCurrentTrackList().Items) 451 ParentWindow.NavigationPane.AddToPlaylistQueue.Add(track); 452 453 ParentWindow.NavigationPane.CreateNewPlaylistETB.IsInEditMode = true; 454 } 455 456 /// <summary> 457 /// Invoked when the user selects to remove a search query from a playlist 458 /// </summary> 459 /// <param name="e">The event data</param> 460 private void Search_RemoveClicked(SearchBoxRemoveEventArgs e) 461 { 462 List<TrackData> tracks = new List<TrackData>(); 463 foreach (TrackData track in ParentWindow.GetCurrentTrackList().Items) 464 tracks.Add(track); 465 PlaylistManager.RemoveFromPlaylist(tracks, e.PlaylistName); 466 } 467 468 /// <summary> 469 /// Invoked when the user clears the search box 470 /// </summary> 471 private void Search_Cleared() 472 { 473 //ParentWindow.GetCurrentTrackList().Items.Filter = null; 474 475 // TODO: move code below into a function 476 String csn = SettingsManager.CurrentSelectedNavigation; 477 478 if (csn == "History" || SettingsManager.SearchPolicy == SearchPolicy.Global) 479 SettingsManager.HistoryListConfig.Filter = ""; 480 481 if (csn == "Queue" || SettingsManager.SearchPolicy == SearchPolicy.Global) 482 SettingsManager.QueueListConfig.Filter = ""; 483 484 if (csn == "Library" || SettingsManager.SearchPolicy == SearchPolicy.Global) 485 SettingsManager.FileListConfig.Filter = ""; 486 487 if (SettingsManager.SearchPolicy == SearchPolicy.Global) 488 { 489 foreach (PlaylistData pl in SettingsManager.Playlists) 490 pl.ListConfig.Filter = ""; 491 } 492 else 493 { 494 if (csn.StartsWith("Playlist:") && SettingsManager.SearchPolicy == SearchPolicy.Partial) 495 foreach (PlaylistData pl in SettingsManager.Playlists) 496 pl.ListConfig.Filter = ""; 497 else if (csn.StartsWith("Playlist:")) 498 PlaylistManager.FindPlaylist(csn.Split(new[] { ':' }, 2)[1]).ListConfig.Filter = ""; 499 } 500 } 501 502 /// <summary> 503 /// Invoked when the control is loded 504 /// </summary> 505 /// <param name="sender">The sender of the event</param> 506 /// <param name="e">The event data</param> 507 private void Control_Loaded(object sender, RoutedEventArgs e) 508 { 509 bookmarkLayer = new BookmarkLayer(SongProgress); 510 bookmarkLayer.RemoveClicked += new EventHandler(RemoveBookmark_Click); 511 bookmarkLayer.Clicked += new BookmarkEventHandler(Bookmark_Clicked); 512 AdornerLayer al = AdornerLayer.GetAdornerLayer(SongProgress); 513 al.Add(bookmarkLayer); 514 515 if (SettingsManager.CurrentTrack != null) 516 { 517 foreach (double b in MediaManager.GetLibrarySourceTrack(SettingsManager.CurrentTrack).Bookmarks) 518 { 519 bookmarkLayer.AddBookmark(b); 520 } 521 } 522 523 if (System.Windows.Forms.VisualStyles.VisualStyleInformation.DisplayName == "") 524 { 525 InfoWindow.BorderThickness = new Thickness(1, 1, 0, 0); 526 InfoWindow.BorderBrush = SystemColors.ControlDarkBrush; 527 InfoWindow.CornerRadius = new CornerRadius(0); 528 529 InfoWindowBorder1.BorderThickness = new Thickness(0, 0, 1, 1); 530 InfoWindowBorder1.BorderBrush = SystemColors.ControlLightLightBrush; 531 InfoWindowBorder1.CornerRadius = new CornerRadius(0); 532 533 InfoWindowBorder2.BorderThickness = new Thickness(1, 1, 0, 0); 534 InfoWindowBorder2.BorderBrush = null; 535 InfoWindowBorder2.CornerRadius = new CornerRadius(0); 536 537 InfoWindowBorder3.BorderThickness = new Thickness(0, 0, 1, 1); 538 InfoWindowBorder3.BorderBrush = null; 539 InfoWindowBorder3.CornerRadius = new CornerRadius(0); 540 541 InfoWindowInner.Background = SystemColors.ControlBrush; 542 543 InfoTimeMinus.Foreground = SystemColors.ControlTextBrush; 544 InfoTimePlus.Foreground = SystemColors.ControlTextBrush; 545 InfoName.Foreground = SystemColors.ControlTextBrush; 546 } 547 548 UpdateInfo(); 549 UpdateShuffle(); 550 UpdateRepeat(); 551 } 552 553 /// <summary> 554 /// Invoked when the user removes a bookmark 555 /// </summary> 556 /// <param name="sender">The sender of the event</param> 557 /// <param name="e">The event data</param> 558 private void RemoveBookmark_Click(object sender, EventArgs e) 559 { 560 DispatchRemoveBookmarkClicked(sender, e); 561 } 562 563 /// <summary> 564 /// Invoked when the user clicks on a bookmark 565 /// </summary> 566 /// <param name="sender">The sender of the event</param> 567 /// <param name="e">The event data</param> 568 private void Bookmark_Clicked(object sender, BookmarkEventArgs e) 569 { 570 this.SongProgress.Value = e.Position / 10; 571 } 572 573 /// <summary> 574 /// Invoked when a property of the settings manager changes 575 /// </summary> 576 /// <param name="sender">The sender of the event</param> 577 /// <param name="e">The event data</param> 578 private void SettingsManager_PropertyChanged(object sender, PropertyChangedWithValuesEventArgs e) 579 { 580 Dispatcher.Invoke(DispatcherPriority.Normal, new Action(delegate() 581 { 582 switch (e.PropertyName) 583 { 584 case "Seek": 585 UpdateInfo(); 586 break; 587 588 case "BufferSize": 589 SongProgress.SecondValue = SettingsManager.BufferSize; 590 break; 591 592 case "Volume": 593 VolumeSlide.Value = SettingsManager.Volume; 594 Console.WriteLine("setting volume to: " + VolumeSlide.Value); 595 break; 596 597 case "Shuffle": 598 UpdateShuffle(); 599 break; 600 601 case "Repeat": 602 UpdateRepeat(); 603 break; 604 605 case "CurrentTrack": 606 UpdateInfo(); 607 ClearBookmarks(); 608 if (SettingsManager.CurrentTrack != null) 609 { 610 TrackData libraryTrack = MediaManager.GetLibrarySourceTrack(SettingsManager.CurrentTrack); 611 if (libraryTrack.Bookmarks != null) 612 foreach (double b in libraryTrack.Bookmarks) 613 AddBookmark(b); 614 } 615 break; 616 617 case "MediaState": 618 switch (SettingsManager.MediaState) 619 { 620 case MediaState.Playing: 621 PausePlayButton.Style = (Style)FindResource("PauseButtonStyle"); 622 break; 623 624 case MediaState.Paused: 625 case MediaState.Stopped: 626 PausePlayButton.Style = (Style)FindResource("PlayButtonStyle"); 627 break; 628 } 629 break; 630 } 631 })); 632 } 633 634 #endregion 635 636 #endregion 637 638 #region Events 639 640 /// <summary> 641 /// 642 /// </summary> 643 public event EventHandler RemoveBookmarkClicked; 644 645 /// <summary> 646 /// 647 /// </summary> 648 /// <param name="sender"></param> 649 /// <param name="e"></param> 650 public void DispatchRemoveBookmarkClicked(object sender, EventArgs e) 651 { 652 if (RemoveBookmarkClicked != null) 653 { 654 RemoveBookmarkClicked(sender, e); 655 } 656 } 657 658 #endregion 659 } 660 661 /// <summary> 662 /// Describes the layer above the seek bar which holds the bookmarks 663 /// </summary> 664 public class BookmarkLayer : Adorner 665 { 666 #region Members 667 668 private VisualCollection visualChildren; 669 private Grid grid; 670 private List<ColumnDefinition> spacers = new List<ColumnDefinition>(); 671 public List<Bookmark> bookmarks = new List<Bookmark>(); 672 public event EventHandler RemoveClicked; 673 public event BookmarkEventHandler Clicked; 674 675 #endregion 676 677 #region Constructor 678 679 /// <summary> 680 /// Creates a Bookmarklayer 681 /// </summary> 682 /// <param name="adornedElement"></param> 683 public BookmarkLayer(UIElement adornedElement) 684 : base(adornedElement) 685 { 686 visualChildren = new VisualCollection(this); 687 grid = new Grid(); 688 grid.Margin = new Thickness(0, 1, 0, 0); 689 visualChildren.Add(grid); 690 } 691 692 #endregion 693 694 #region Methods 695 696 #region Public 697 698 /// <summary> 699 /// 700 /// </summary> 701 /// <param name="pos"></param> 702 public void AddBookmark(double pos) 703 { 704 double bmWidth = 4.0; 705 double MyWidth = this.ActualWidth; 706 if (MyWidth < 147) MyWidth = 147; 707 double posInPixels = MyWidth * (pos / 100); 708 //if (posInPixels < (bmWidth / 2) || posInPixels > this.ActualWidth - (bmWidth / 2)) return; 709 710 double width = posInPixels; 711 double i = 0; 712 ColumnDefinition newCd; 713 ColumnDefinition bmCd; 714 Bookmark bm; 715 716 foreach (ColumnDefinition cd in spacers) 717 { 718 width = posInPixels - i; 719 i += cd.Width.Value; 720 if (i - (bmWidth / 2) < posInPixels && posInPixels <= i + (bmWidth * 1.5)) return; 721 722 if (posInPixels < i) 723 { 724 newCd = new ColumnDefinition(); 725 newCd.Width = new GridLength(width - (bmWidth/2)); 726 727 bmCd = new ColumnDefinition(); 728 bmCd.Width = new GridLength(bmWidth); 729 730 int c = grid.ColumnDefinitions.IndexOf(cd); 731 732 grid.ColumnDefinitions.Insert(c, bmCd); 733 grid.ColumnDefinitions.Insert(c, newCd); 734 735 cd.Width = new GridLength(cd.Width.Value - (width + (bmWidth * 1.5))); 736 737 bm = new Bookmark(); 738 bm.Position = pos; 739 bm.RemoveClicked += new EventHandler(Bookmark_RemoveClicked); 740 bm.Clicked += new BookmarkEventHandler(Bookmark_Clicked); 741 foreach (Bookmark b in bookmarks) 742 { 743 if (b.Position > bm.Position) 744 { 745 spacers.Insert(bookmarks.IndexOf(b), newCd); 746 bookmarks.Insert(bookmarks.IndexOf(b), bm); 747 break; 748 } 749 } 750 751 int j = 1; 752 foreach (Bookmark b in bookmarks) 753 { 754 j += 2; 755 if (b.Position > bm.Position) 756 Grid.SetColumn(b, j); 757 } 758 Grid.SetColumn(bm, c + 1); 759 grid.Children.Add(bm); 760 761 return; 762 } 763 i += 2.0; 764 } 765 766 width = posInPixels - i; 767 768 newCd = new ColumnDefinition(); 769 newCd.Width = new GridLength(width - (bmWidth / 2)); 770 spacers.Add(newCd); 771 772 773 bmCd = new ColumnDefinition(); 774 bmCd.Width = new GridLength(bmWidth); 775 776 grid.ColumnDefinitions.Add(newCd); 777 grid.ColumnDefinitions.Add(bmCd); 778 779 bm = new Bookmark(); 780 bm.Position = pos; 781 bm.RemoveClicked += new EventHandler(Bookmark_RemoveClicked); 782 bm.Clicked += new BookmarkEventHandler(Bookmark_Clicked); 783 bookmarks.Add(bm); 784 785 Grid.SetColumn(bm, grid.ColumnDefinitions.Count - 1); 786 grid.Children.Add(bm); 787 } 788 789 /// <summary> 790 /// 791 /// </summary> 792 /// <param name="pos"></param> 793 public void RemoveBookmark(double pos) 794 { 795 Bookmark bookmarkToRemove = null; 796 int i = 0; 797 foreach (Bookmark m in bookmarks) 798 { 799 if (m.Position == pos) 800 { 801 i = bookmarks.IndexOf(m); 802 bookmarkToRemove = m; 803 break; 804 } 805 } 806 807 if (bookmarkToRemove != null) 808 { 809 if (i == bookmarks.Count - 1) 810 { 811 grid.ColumnDefinitions.Remove(grid.ColumnDefinitions.Last()); 812 grid.ColumnDefinitions.Remove(grid.ColumnDefinitions.Last()); 813 grid.Children.Remove(bookmarks.Last()); 814 spacers.Remove(spacers.Last()); 815 bookmarks.Remove(bookmarks.Last()); 816 } 817 else 818 { 819 ColumnDefinition cd1 = grid.ColumnDefinitions[(2 * i)]; 820 ColumnDefinition cd2 = grid.ColumnDefinitions[(2 * i) + 1]; 821 ColumnDefinition cd3 = grid.ColumnDefinitions[(2 * i) + 2]; 822 cd3.Width = new GridLength(cd3.Width.Value + cd2.Width.Value + cd1.Width.Value); 823 824 grid.ColumnDefinitions.Remove(cd1); 825 grid.ColumnDefinitions.Remove(cd2); 826 grid.Children.Remove(bookmarkToRemove); 827 spacers.Remove(cd1); 828 bookmarks.Remove(bookmarkToRemove); 829 foreach (Bookmark b in bookmarks) 830 Grid.SetColumn(b, bookmarks.IndexOf(b) * 2 + 1); 831 } 832 } 833 } 834 835 /// <summary> 836 /// 837 /// </summary> 838 public void ClearBookmarks() 839 { 840 bookmarks.Clear(); 841 spacers.Clear(); 842 grid.ColumnDefinitions.Clear(); 843 grid.Children.Clear(); 844 } 845 846 #endregion 847 848 #region Event handlers 849 850 /// <summary> 851 /// 852 /// </summary> 853 /// <param name="sender"></param> 854 /// <param name="e"></param> 855 private void Bookmark_RemoveClicked(object sender, EventArgs e) 856 { 857 DispatchRemoveClicked(sender, e); 858 } 859 860 /// <summary> 861 /// 862 /// </summary> 863 /// <param name="sender"></param> 864 /// <param name="e"></param> 865 private void Bookmark_Clicked(object sender, BookmarkEventArgs e) 866 { 867 DispatchClicked(sender, e); 868 } 869 870 #endregion 871 872 #endregion 873 874 #region Overrides 875 876 /// <summary> 877 /// 878 /// </summary> 879 /// <param name="finalSize"></param> 880 /// <returns></returns> 881 protected override Size ArrangeOverride(Size finalSize) 882 { 883 double bmWidth = 4.0; 884 double x = 0; 885 double y = 0; 886 grid.Arrange(new Rect(x, y, finalSize.Width, finalSize.Height)); 887 888 double i = 0.0; 889 foreach (Bookmark bm in bookmarks) 890 { 891 double posInPixels = finalSize.Width * (bm.Position / 100); 892 893 ColumnDefinition cd = grid.ColumnDefinitions[(2 * bookmarks.IndexOf(bm))]; 894 grid.ColumnDefinitions[(2 * bookmarks.IndexOf(bm)) + 1].Width = new GridLength(bmWidth); 895 896 Grid.SetColumn(bm, 1+ (bookmarks.IndexOf(bm) * 2)); 897 898 if (posInPixels - i - (bmWidth / 2) > 0) 899 cd.Width = new GridLength(posInPixels - i - (bmWidth / 2)); 900 else 901 cd.Width = new GridLength(0); 902 903 i += cd.Width.Value + bmWidth; 904 } 905 906 return finalSize; 907 } 908 909 /// <summary> 910 /// 911 /// </summary> 912 protected override int VisualChildrenCount { get { return visualChildren.Count; } } 913 914 /// <summary> 915 /// 916 /// </summary> 917 /// <param name="index"></param> 918 /// <returns></returns> 919 protected override Visual GetVisualChild(int index) { return visualChildren[index]; } 920 921 #endregion 922 923 #region Events 924 925 /// <summary> 926 /// 927 /// </summary> 928 /// <param name="sender"></param> 929 /// <param name="e"></param> 930 public void DispatchRemoveClicked(object sender, EventArgs e) 931 { 932 if (RemoveClicked != null) 933 { 934 RemoveClicked(sender, e); 935 } 936 } 937 938 /// <summary> 939 /// 940 /// </summary> 941 /// <param name="sender"></param> 942 /// <param name="e"></param> 943 public void DispatchClicked(object sender, BookmarkEventArgs e) 944 { 945 if (Clicked != null) 946 { 947 Clicked(sender, e); 948 } 949 } 950 951 #endregion 952 } 953}