diff --git a/ProgramQueuer.sln b/ProgramQueuer.sln new file mode 100755 index 0000000..469107f --- /dev/null +++ b/ProgramQueuer.sln @@ -0,0 +1,20 @@ + +Microsoft Visual Studio Solution File, Format Version 11.00 +# Visual Studio 2010 +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "ProgramQueuer", "ProgramQueuer\ProgramQueuer.csproj", "{B1825A4D-32CB-45E2-9CFE-77622DCC1641}" +EndProject +Global + GlobalSection(SolutionConfigurationPlatforms) = preSolution + Debug|x86 = Debug|x86 + Release|x86 = Release|x86 + EndGlobalSection + GlobalSection(ProjectConfigurationPlatforms) = postSolution + {B1825A4D-32CB-45E2-9CFE-77622DCC1641}.Debug|x86.ActiveCfg = Debug|x86 + {B1825A4D-32CB-45E2-9CFE-77622DCC1641}.Debug|x86.Build.0 = Debug|x86 + {B1825A4D-32CB-45E2-9CFE-77622DCC1641}.Release|x86.ActiveCfg = Release|x86 + {B1825A4D-32CB-45E2-9CFE-77622DCC1641}.Release|x86.Build.0 = Release|x86 + EndGlobalSection + GlobalSection(SolutionProperties) = preSolution + HideSolutionNode = FALSE + EndGlobalSection +EndGlobal diff --git a/ProgramQueuer/App.xaml b/ProgramQueuer/App.xaml new file mode 100755 index 0000000..1aeb5f9 --- /dev/null +++ b/ProgramQueuer/App.xaml @@ -0,0 +1,8 @@ + + + + + diff --git a/ProgramQueuer/App.xaml.cs b/ProgramQueuer/App.xaml.cs new file mode 100755 index 0000000..29aa2a3 --- /dev/null +++ b/ProgramQueuer/App.xaml.cs @@ -0,0 +1,16 @@ +using System; +using System.Collections.Generic; +using System.Configuration; +using System.Data; +using System.Linq; +using System.Windows; + +namespace ProgramQueuer +{ + /// + /// Interaction logic for App.xaml + /// + public partial class App : Application + { + } +} diff --git a/ProgramQueuer/Helpers/BoolToVisibility.cs b/ProgramQueuer/Helpers/BoolToVisibility.cs new file mode 100755 index 0000000..48e02e6 --- /dev/null +++ b/ProgramQueuer/Helpers/BoolToVisibility.cs @@ -0,0 +1,50 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using System.Windows; +using System.Windows.Interop; +using System.Windows.Data; +using System.Windows.Media.Imaging; + +namespace ProgramQueuer.Helpers +{ + /// + /// Simple boolean value converter that converts it to a visbility value where true is visible and false is collapsed. + /// + [ValueConversion(typeof(bool), typeof(Visibility))] + public class BoolToVisibility : IValueConverter + { + /// + /// Convert from a boolean value of true to Visibility.Visible and false to Visibility.Collapsed. + /// + /// A boolean value to convert. + /// The type of the target value. This property is ignored. + /// Parameter to pass to the converter. This property is ignored. + /// A reference to the target CultureInfo. This property is ignored. + /// A Visibility value of either Visible or Collapsed depending on the value parameter. + public object Convert(object value, Type targetType, object parameter, System.Globalization.CultureInfo culture) + { + bool visible = (bool)value; + if (visible) + return Visibility.Visible; + return Visibility.Collapsed; + } + + /// + /// Convert back from a Visibility value to a boolean value where Visibility.Visible converts to true and Visibility.Collapsed to false. + /// + /// The visibility value to convert back to a boolean value. + /// The type of the target value. This property is ignored. + /// Parameter to pass to the converter. This property is ignored. + /// A reference to the target CultureInfo. This property is ignored. + /// A boolean value representing the visibility of the control. + public object ConvertBack(object value, Type targetType, object parameter, System.Globalization.CultureInfo culture) + { + var visible = (Visibility)value; + if (visible == Visibility.Collapsed) + return false; + return true; + } + } +} diff --git a/ProgramQueuer/Helpers/BooleanInverter.cs b/ProgramQueuer/Helpers/BooleanInverter.cs new file mode 100755 index 0000000..25d1789 --- /dev/null +++ b/ProgramQueuer/Helpers/BooleanInverter.cs @@ -0,0 +1,44 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using System.Windows; +using System.Windows.Interop; +using System.Windows.Data; +using System.Windows.Media.Imaging; + +namespace ProgramQueuer.Helpers +{ + /// + /// A simple boolean converter that inverts the boolean value. + /// + [ValueConversion(typeof(bool), typeof(bool))] + public class BooleanInverter : IValueConverter + { + /// + /// Convert a boolean value to the inverted value. Seriously, if I have to explain this, then you are in the wrong business. + /// + /// The boolean value to invert. + /// The type of the target value. This property is ignored. + /// Parameter to pass to the converter. This property is ignored. + /// A reference to the target CultureInfo. This property is ignored. + /// An inverted boolean value. + public object Convert(object value, Type targetType, object parameter, System.Globalization.CultureInfo culture) + { + return !(bool)value; + } + + /// + /// Convert an inverted boolean value to it's original value. Basically inverted. + /// + /// The boolean value to invert back. + /// The type of the target value. This property is ignored. + /// Parameter to pass to the converter. This property is ignored. + /// A reference to the target CultureInfo. This property is ignored. + /// An inverted boolean value. + public object ConvertBack(object value, Type targetType, object parameter, System.Globalization.CultureInfo culture) + { + return !(bool)value; + } + } +} diff --git a/ProgramQueuer/Helpers/DragAdorner.cs b/ProgramQueuer/Helpers/DragAdorner.cs new file mode 100755 index 0000000..53d7990 --- /dev/null +++ b/ProgramQueuer/Helpers/DragAdorner.cs @@ -0,0 +1,175 @@ +// Copyright (C) Josh Smith - January 2007 +using System; +using System.Collections.Generic; +using System.Text; +using System.Windows.Documents; +using System.Windows; +using System.Windows.Media; +using System.Windows.Shapes; +using System.Windows.Media.Animation; +using System.Windows.Controls; + +namespace WPF.JoshSmith.Adorners +{ + /// + /// Renders a visual which can follow the mouse cursor, + /// such as during a drag-and-drop operation. + /// + public class DragAdorner : Adorner + { + #region Data + + private Rectangle child = null; + private double offsetLeft = 0; + private double offsetTop = 0; + + #endregion // Data + + #region Constructor + + /// + /// Initializes a new instance of DragVisualAdorner. + /// + /// The element being adorned. + /// The size of the adorner. + /// A brush to with which to paint the adorner. + public DragAdorner(UIElement adornedElement, Size size, Brush brush) + : base(adornedElement) + { + Rectangle rect = new Rectangle(); + rect.Fill = brush; + rect.Width = size.Width; + rect.Height = size.Height; + rect.IsHitTestVisible = false; + this.child = rect; + } + + #endregion // Constructor + + #region Public Interface + + #region GetDesiredTransform + + /// + /// Override. + /// + /// + /// + public override GeneralTransform GetDesiredTransform(GeneralTransform transform) + { + GeneralTransformGroup result = new GeneralTransformGroup(); + result.Children.Add(base.GetDesiredTransform(transform)); + result.Children.Add(new TranslateTransform(this.offsetLeft, this.offsetTop)); + return result; + } + + #endregion // GetDesiredTransform + + #region OffsetLeft + + /// + /// Gets/sets the horizontal offset of the adorner. + /// + public double OffsetLeft + { + get { return this.offsetLeft; } + set + { + this.offsetLeft = value; + UpdateLocation(); + } + } + + #endregion // OffsetLeft + + #region SetOffsets + + /// + /// Updates the location of the adorner in one atomic operation. + /// + /// + /// + public void SetOffsets(double left, double top) + { + this.offsetLeft = left; + this.offsetTop = top; + this.UpdateLocation(); + } + + #endregion // SetOffsets + + #region OffsetTop + + /// + /// Gets/sets the vertical offset of the adorner. + /// + public double OffsetTop + { + get { return this.offsetTop; } + set + { + this.offsetTop = value; + UpdateLocation(); + } + } + + #endregion // OffsetTop + + #endregion // Public Interface + + #region Protected Overrides + + /// + /// Override. + /// + /// + /// + protected override Size MeasureOverride(Size constraint) + { + this.child.Measure(constraint); + return this.child.DesiredSize; + } + + /// + /// Override. + /// + /// + /// + protected override Size ArrangeOverride(Size finalSize) + { + this.child.Arrange(new Rect(finalSize)); + return finalSize; + } + + /// + /// Override. + /// + /// + /// + protected override Visual GetVisualChild(int index) + { + return this.child; + } + + /// + /// Override. Always returns 1. + /// + protected override int VisualChildrenCount + { + get { return 1; } + } + + #endregion // Protected Overrides + + #region Private Helpers + + private void UpdateLocation() + { + AdornerLayer adornerLayer = this.Parent as AdornerLayer; + if (adornerLayer != null) + adornerLayer.Update(this.AdornedElement); + } + + #endregion // Private Helpers + } +} \ No newline at end of file diff --git a/ProgramQueuer/Helpers/ListViewDragDropManager.cs b/ProgramQueuer/Helpers/ListViewDragDropManager.cs new file mode 100755 index 0000000..11c7743 --- /dev/null +++ b/ProgramQueuer/Helpers/ListViewDragDropManager.cs @@ -0,0 +1,864 @@ +// Copyright (C) Josh Smith - January 2007 +using System; +using System.Collections.ObjectModel; +using System.ComponentModel; +using System.Diagnostics; +using System.Windows; +using System.Windows.Controls; +using System.Windows.Controls.Primitives; +using System.Windows.Documents; +using System.Windows.Media; +using System.Windows.Input; +using WPF.JoshSmith.Adorners; +using WPF.JoshSmith.Controls.Utilities; + +namespace WPF.JoshSmith.ServiceProviders.UI +{ + #region ListViewDragDropManager + + /// + /// Manages the dragging and dropping of ListViewItems in a ListView. + /// The ItemType type parameter indicates the type of the objects in + /// the ListView's items source. The ListView's ItemsSource must be + /// set to an instance of ObservableCollection of ItemType, or an + /// Exception will be thrown. + /// + /// The type of the ListView's items. + public class ListViewDragDropManager where ItemType : class + { + #region Data + + bool canInitiateDrag; + DragAdorner dragAdorner; + double dragAdornerOpacity; + int indexToSelect; + bool isDragInProgress; + ItemType itemUnderDragCursor; + ListView listView; + Point ptMouseDown; + bool showDragAdorner; + + #endregion // Data + + #region Constructors + + /// + /// Initializes a new instance of ListViewDragManager. + /// + public ListViewDragDropManager() + { + this.canInitiateDrag = false; + this.dragAdornerOpacity = 0.7; + this.indexToSelect = -1; + this.showDragAdorner = true; + } + + /// + /// Initializes a new instance of ListViewDragManager. + /// + /// + public ListViewDragDropManager(ListView listView) + : this() + { + this.ListView = listView; + } + + /// + /// Initializes a new instance of ListViewDragManager. + /// + /// + /// + public ListViewDragDropManager(ListView listView, double dragAdornerOpacity) + : this(listView) + { + this.DragAdornerOpacity = dragAdornerOpacity; + } + + /// + /// Initializes a new instance of ListViewDragManager. + /// + /// + /// + public ListViewDragDropManager(ListView listView, bool showDragAdorner) + : this(listView) + { + this.ShowDragAdorner = showDragAdorner; + } + + #endregion // Constructors + + #region Public Interface + + #region DragAdornerOpacity + + /// + /// Gets/sets the opacity of the drag adorner. This property has no + /// effect if ShowDragAdorner is false. The default value is 0.7 + /// + public double DragAdornerOpacity + { + get { return this.dragAdornerOpacity; } + set + { + if (this.IsDragInProgress) + throw new InvalidOperationException("Cannot set the DragAdornerOpacity property during a drag operation."); + + if (value < 0.0 || value > 1.0) + throw new ArgumentOutOfRangeException("DragAdornerOpacity", value, "Must be between 0 and 1."); + + this.dragAdornerOpacity = value; + } + } + + #endregion // DragAdornerOpacity + + #region IsDragInProgress + + /// + /// Returns true if there is currently a drag operation being managed. + /// + public bool IsDragInProgress + { + get { return this.isDragInProgress; } + private set { this.isDragInProgress = value; } + } + + #endregion // IsDragInProgress + + #region ListView + + /// + /// Gets/sets the ListView whose dragging is managed. This property + /// can be set to null, to prevent drag management from occuring. If + /// the ListView's AllowDrop property is false, it will be set to true. + /// + public ListView ListView + { + get { return listView; } + set + { + if (this.IsDragInProgress) + throw new InvalidOperationException("Cannot set the ListView property during a drag operation."); + + if (this.listView != null) + { + #region Unhook Events + + this.listView.PreviewMouseLeftButtonDown -= listView_PreviewMouseLeftButtonDown; + this.listView.PreviewMouseMove -= listView_PreviewMouseMove; + this.listView.DragOver -= listView_DragOver; + this.listView.DragLeave -= listView_DragLeave; + this.listView.DragEnter -= listView_DragEnter; + this.listView.Drop -= listView_Drop; + + #endregion // Unhook Events + } + + this.listView = value; + + if (this.listView != null) + { + if (!this.listView.AllowDrop) + this.listView.AllowDrop = true; + + #region Hook Events + + this.listView.PreviewMouseLeftButtonDown += listView_PreviewMouseLeftButtonDown; + this.listView.PreviewMouseMove += listView_PreviewMouseMove; + this.listView.DragOver += listView_DragOver; + this.listView.DragLeave += listView_DragLeave; + this.listView.DragEnter += listView_DragEnter; + this.listView.Drop += listView_Drop; + + #endregion // Hook Events + } + } + } + + #endregion // ListView + + #region ProcessDrop [event] + + /// + /// Raised when a drop occurs. By default the dropped item will be moved + /// to the target index. Handle this event if relocating the dropped item + /// requires custom behavior. Note, if this event is handled the default + /// item dropping logic will not occur. + /// + public event EventHandler> ProcessDrop; + + #endregion // ProcessDrop [event] + + #region ShowDragAdorner + + /// + /// Gets/sets whether a visual representation of the ListViewItem being dragged + /// follows the mouse cursor during a drag operation. The default value is true. + /// + public bool ShowDragAdorner + { + get { return this.showDragAdorner; } + set + { + if (this.IsDragInProgress) + throw new InvalidOperationException("Cannot set the ShowDragAdorner property during a drag operation."); + + this.showDragAdorner = value; + } + } + + #endregion // ShowDragAdorner + + #endregion // Public Interface + + #region Event Handling Methods + + #region listView_PreviewMouseLeftButtonDown + + void listView_PreviewMouseLeftButtonDown(object sender, MouseButtonEventArgs e) + { + if (this.IsMouseOverScrollbar) + { + // 4/13/2007 - Set the flag to false when cursor is over scrollbar. + this.canInitiateDrag = false; + return; + } + + int index = this.IndexUnderDragCursor; + this.canInitiateDrag = index > -1; + + if (this.canInitiateDrag) + { + // Remember the location and index of the ListViewItem the user clicked on for later. + this.ptMouseDown = MouseUtilities.GetMousePosition(this.listView); + this.indexToSelect = index; + } + else + { + this.ptMouseDown = new Point(-10000, -10000); + this.indexToSelect = -1; + } + } + + #endregion // listView_PreviewMouseLeftButtonDown + + #region listView_PreviewMouseMove + + void listView_PreviewMouseMove(object sender, MouseEventArgs e) + { + if (!this.CanStartDragOperation) + return; + + // Select the item the user clicked on. + if (this.listView.SelectedIndex != this.indexToSelect) + this.listView.SelectedIndex = this.indexToSelect; + + // If the item at the selected index is null, there's nothing + // we can do, so just return; + if (this.listView.SelectedItem == null) + return; + + ListViewItem itemToDrag = this.GetListViewItem(this.listView.SelectedIndex); + if (itemToDrag == null) + return; + + AdornerLayer adornerLayer = this.ShowDragAdornerResolved ? this.InitializeAdornerLayer(itemToDrag) : null; + + this.InitializeDragOperation(itemToDrag); + this.PerformDragOperation(); + this.FinishDragOperation(itemToDrag, adornerLayer); + } + + #endregion // listView_PreviewMouseMove + + #region listView_DragOver + + void listView_DragOver(object sender, DragEventArgs e) + { + e.Effects = DragDropEffects.Move; + + if (this.ShowDragAdornerResolved) + this.UpdateDragAdornerLocation(); + + // Update the item which is known to be currently under the drag cursor. + int index = this.IndexUnderDragCursor; + this.ItemUnderDragCursor = index < 0 ? null : this.ListView.Items[index] as ItemType; + } + + #endregion // listView_DragOver + + #region listView_DragLeave + + void listView_DragLeave(object sender, DragEventArgs e) + { + if (!this.IsMouseOver(this.listView)) + { + if (this.ItemUnderDragCursor != null) + this.ItemUnderDragCursor = null; + + if (this.dragAdorner != null) + this.dragAdorner.Visibility = Visibility.Collapsed; + } + } + + #endregion // listView_DragLeave + + #region listView_DragEnter + + void listView_DragEnter(object sender, DragEventArgs e) + { + if (this.dragAdorner != null && this.dragAdorner.Visibility != Visibility.Visible) + { + // Update the location of the adorner and then show it. + this.UpdateDragAdornerLocation(); + this.dragAdorner.Visibility = Visibility.Visible; + } + } + + #endregion // listView_DragEnter + + #region listView_Drop + + void listView_Drop(object sender, DragEventArgs e) + { + if (this.ItemUnderDragCursor != null) + this.ItemUnderDragCursor = null; + + e.Effects = DragDropEffects.None; + + if (!e.Data.GetDataPresent(typeof(ItemType))) + return; + + // Get the data object which was dropped. + ItemType data = e.Data.GetData(typeof(ItemType)) as ItemType; + if (data == null) + return; + + // Get the ObservableCollection which contains the dropped data object. + ObservableCollection itemsSource = this.listView.ItemsSource as ObservableCollection; + if (itemsSource == null) + throw new Exception( + "A ListView managed by ListViewDragManager must have its ItemsSource set to an ObservableCollection."); + + int oldIndex = itemsSource.IndexOf(data); + int newIndex = this.IndexUnderDragCursor; + + if (newIndex < 0) + { + // The drag started somewhere else, and our ListView is empty + // so make the new item the first in the list. + if (itemsSource.Count == 0) + newIndex = 0; + + // The drag started somewhere else, but our ListView has items + // so make the new item the last in the list. + else if (oldIndex < 0) + newIndex = itemsSource.Count; + + // The user is trying to drop an item from our ListView into + // our ListView, but the mouse is not over an item, so don't + // let them drop it. + else + return; + } + + // Dropping an item back onto itself is not considered an actual 'drop'. + if (oldIndex == newIndex) + return; + + if (this.ProcessDrop != null) + { + // Let the client code process the drop. + ProcessDropEventArgs args = new ProcessDropEventArgs(itemsSource, data, oldIndex, newIndex, e.AllowedEffects); + this.ProcessDrop(this, args); + e.Effects = args.Effects; + } + else + { + // Move the dragged data object from it's original index to the + // new index (according to where the mouse cursor is). If it was + // not previously in the ListBox, then insert the item. + if (oldIndex > -1) + itemsSource.Move(oldIndex, newIndex); + else + itemsSource.Insert(newIndex, data); + + // Set the Effects property so that the call to DoDragDrop will return 'Move'. + e.Effects = DragDropEffects.Move; + } + } + + #endregion // listView_Drop + + #endregion // Event Handling Methods + + #region Private Helpers + + #region CanStartDragOperation + + bool CanStartDragOperation + { + get + { + if (Mouse.LeftButton != MouseButtonState.Pressed) + return false; + + if (!this.canInitiateDrag) + return false; + + if (this.indexToSelect == -1) + return false; + + if (!this.HasCursorLeftDragThreshold) + return false; + + return true; + } + } + + #endregion // CanStartDragOperation + + #region FinishDragOperation + + void FinishDragOperation(ListViewItem draggedItem, AdornerLayer adornerLayer) + { + // Let the ListViewItem know that it is not being dragged anymore. + ListViewItemDragState.SetIsBeingDragged(draggedItem, false); + + this.IsDragInProgress = false; + + if (this.ItemUnderDragCursor != null) + this.ItemUnderDragCursor = null; + + // Remove the drag adorner from the adorner layer. + if (adornerLayer != null) + { + adornerLayer.Remove(this.dragAdorner); + this.dragAdorner = null; + } + } + + #endregion // FinishDragOperation + + #region GetListViewItem + + ListViewItem GetListViewItem(int index) + { + if (this.listView.ItemContainerGenerator.Status != GeneratorStatus.ContainersGenerated) + return null; + + return this.listView.ItemContainerGenerator.ContainerFromIndex(index) as ListViewItem; + } + + ListViewItem GetListViewItem(ItemType dataItem) + { + if (this.listView.ItemContainerGenerator.Status != GeneratorStatus.ContainersGenerated) + return null; + + return this.listView.ItemContainerGenerator.ContainerFromItem(dataItem) as ListViewItem; + } + + #endregion // GetListViewItem + + #region HasCursorLeftDragThreshold + + bool HasCursorLeftDragThreshold + { + get + { + if (this.indexToSelect < 0) + return false; + + ListViewItem item = this.GetListViewItem(this.indexToSelect); + Rect bounds = VisualTreeHelper.GetDescendantBounds(item); + Point ptInItem = this.listView.TranslatePoint(this.ptMouseDown, item); + + // In case the cursor is at the very top or bottom of the ListViewItem + // we want to make the vertical threshold very small so that dragging + // over an adjacent item does not select it. + double topOffset = Math.Abs(ptInItem.Y); + double btmOffset = Math.Abs(bounds.Height - ptInItem.Y); + double vertOffset = Math.Min(topOffset, btmOffset); + + double width = SystemParameters.MinimumHorizontalDragDistance * 2; + double height = Math.Min(SystemParameters.MinimumVerticalDragDistance, vertOffset) * 2; + Size szThreshold = new Size(width, height); + + Rect rect = new Rect(this.ptMouseDown, szThreshold); + rect.Offset(szThreshold.Width / -2, szThreshold.Height / -2); + Point ptInListView = MouseUtilities.GetMousePosition(this.listView); + return !rect.Contains(ptInListView); + } + } + + #endregion // HasCursorLeftDragThreshold + + #region IndexUnderDragCursor + + /// + /// Returns the index of the ListViewItem underneath the + /// drag cursor, or -1 if the cursor is not over an item. + /// + int IndexUnderDragCursor + { + get + { + int index = -1; + for (int i = 0; i < this.listView.Items.Count; ++i) + { + ListViewItem item = this.GetListViewItem(i); + if (this.IsMouseOver(item)) + { + index = i; + break; + } + } + return index; + } + } + + #endregion // IndexUnderDragCursor + + #region InitializeAdornerLayer + + AdornerLayer InitializeAdornerLayer(ListViewItem itemToDrag) + { + // Create a brush which will paint the ListViewItem onto + // a visual in the adorner layer. + VisualBrush brush = new VisualBrush(itemToDrag); + + // Create an element which displays the source item while it is dragged. + this.dragAdorner = new DragAdorner(this.listView, itemToDrag.RenderSize, brush); + + // Set the drag adorner's opacity. + this.dragAdorner.Opacity = this.DragAdornerOpacity; + + AdornerLayer layer = AdornerLayer.GetAdornerLayer(this.listView); + layer.Add(dragAdorner); + + // Save the location of the cursor when the left mouse button was pressed. + this.ptMouseDown = MouseUtilities.GetMousePosition(this.listView); + + return layer; + } + + #endregion // InitializeAdornerLayer + + #region InitializeDragOperation + + void InitializeDragOperation(ListViewItem itemToDrag) + { + // Set some flags used during the drag operation. + this.IsDragInProgress = true; + this.canInitiateDrag = false; + + // Let the ListViewItem know that it is being dragged. + ListViewItemDragState.SetIsBeingDragged(itemToDrag, true); + } + + #endregion // InitializeDragOperation + + #region IsMouseOver + + bool IsMouseOver(Visual target) + { + // We need to use MouseUtilities to figure out the cursor + // coordinates because, during a drag-drop operation, the WPF + // mechanisms for getting the coordinates behave strangely. + + Rect bounds = VisualTreeHelper.GetDescendantBounds(target); + Point mousePos = MouseUtilities.GetMousePosition(target); + return bounds.Contains(mousePos); + } + + #endregion // IsMouseOver + + #region IsMouseOverScrollbar + + /// + /// Returns true if the mouse cursor is over a scrollbar in the ListView. + /// + bool IsMouseOverScrollbar + { + get + { + Point ptMouse = MouseUtilities.GetMousePosition(this.listView); + HitTestResult res = VisualTreeHelper.HitTest(this.listView, ptMouse); + if (res == null) + return false; + + DependencyObject depObj = res.VisualHit; + while (depObj != null) + { + if (depObj is ScrollBar) + return true; + + // VisualTreeHelper works with objects of type Visual or Visual3D. + // If the current object is not derived from Visual or Visual3D, + // then use the LogicalTreeHelper to find the parent element. + if (depObj is Visual || depObj is System.Windows.Media.Media3D.Visual3D) + depObj = VisualTreeHelper.GetParent(depObj); + else + depObj = LogicalTreeHelper.GetParent(depObj); + } + + return false; + } + } + + #endregion // IsMouseOverScrollbar + + #region ItemUnderDragCursor + + ItemType ItemUnderDragCursor + { + get { return this.itemUnderDragCursor; } + set + { + if (this.itemUnderDragCursor == value) + return; + + // The first pass handles the previous item under the cursor. + // The second pass handles the new one. + for (int i = 0; i < 2; ++i) + { + if (i == 1) + this.itemUnderDragCursor = value; + + if (this.itemUnderDragCursor != null) + { + ListViewItem listViewItem = this.GetListViewItem(this.itemUnderDragCursor); + if (listViewItem != null) + ListViewItemDragState.SetIsUnderDragCursor(listViewItem, i == 1); + } + } + } + } + + #endregion // ItemUnderDragCursor + + #region PerformDragOperation + + void PerformDragOperation() + { + ItemType selectedItem = this.listView.SelectedItem as ItemType; + DragDropEffects allowedEffects = DragDropEffects.Move | DragDropEffects.Move | DragDropEffects.Link; + if (DragDrop.DoDragDrop(this.listView, selectedItem, allowedEffects) != DragDropEffects.None) + { + // The item was dropped into a new location, + // so make it the new selected item. + this.listView.SelectedItem = selectedItem; + } + } + + #endregion // PerformDragOperation + + #region ShowDragAdornerResolved + + bool ShowDragAdornerResolved + { + get { return this.ShowDragAdorner && this.DragAdornerOpacity > 0.0; } + } + + #endregion // ShowDragAdornerResolved + + #region UpdateDragAdornerLocation + + void UpdateDragAdornerLocation() + { + if (this.dragAdorner != null) + { + Point ptCursor = MouseUtilities.GetMousePosition(this.ListView); + + double left = ptCursor.X - this.ptMouseDown.X; + + // 4/13/2007 - Made the top offset relative to the item being dragged. + ListViewItem itemBeingDragged = this.GetListViewItem(this.indexToSelect); + Point itemLoc = itemBeingDragged.TranslatePoint(new Point(0, 0), this.ListView); + double top = itemLoc.Y + ptCursor.Y - this.ptMouseDown.Y; + + this.dragAdorner.SetOffsets(left, top); + } + } + + #endregion // UpdateDragAdornerLocation + + #endregion // Private Helpers + } + + #endregion // ListViewDragDropManager + + #region ListViewItemDragState + + /// + /// Exposes attached properties used in conjunction with the ListViewDragDropManager class. + /// Those properties can be used to allow triggers to modify the appearance of ListViewItems + /// in a ListView during a drag-drop operation. + /// + public static class ListViewItemDragState + { + #region IsBeingDragged + + /// + /// Identifies the ListViewItemDragState's IsBeingDragged attached property. + /// This field is read-only. + /// + public static readonly DependencyProperty IsBeingDraggedProperty = + DependencyProperty.RegisterAttached( + "IsBeingDragged", + typeof(bool), + typeof(ListViewItemDragState), + new UIPropertyMetadata(false)); + + /// + /// Returns true if the specified ListViewItem is being dragged, else false. + /// + /// The ListViewItem to check. + public static bool GetIsBeingDragged(ListViewItem item) + { + return (bool)item.GetValue(IsBeingDraggedProperty); + } + + /// + /// Sets the IsBeingDragged attached property for the specified ListViewItem. + /// + /// The ListViewItem to set the property on. + /// Pass true if the element is being dragged, else false. + internal static void SetIsBeingDragged(ListViewItem item, bool value) + { + item.SetValue(IsBeingDraggedProperty, value); + } + + #endregion // IsBeingDragged + + #region IsUnderDragCursor + + /// + /// Identifies the ListViewItemDragState's IsUnderDragCursor attached property. + /// This field is read-only. + /// + public static readonly DependencyProperty IsUnderDragCursorProperty = + DependencyProperty.RegisterAttached( + "IsUnderDragCursor", + typeof(bool), + typeof(ListViewItemDragState), + new UIPropertyMetadata(false)); + + /// + /// Returns true if the specified ListViewItem is currently underneath the cursor + /// during a drag-drop operation, else false. + /// + /// The ListViewItem to check. + public static bool GetIsUnderDragCursor(ListViewItem item) + { + return (bool)item.GetValue(IsUnderDragCursorProperty); + } + + /// + /// Sets the IsUnderDragCursor attached property for the specified ListViewItem. + /// + /// The ListViewItem to set the property on. + /// Pass true if the element is underneath the drag cursor, else false. + internal static void SetIsUnderDragCursor(ListViewItem item, bool value) + { + item.SetValue(IsUnderDragCursorProperty, value); + } + + #endregion // IsUnderDragCursor + } + + #endregion // ListViewItemDragState + + #region ProcessDropEventArgs + + /// + /// Event arguments used by the ListViewDragDropManager.ProcessDrop event. + /// + /// The type of data object being dropped. + public class ProcessDropEventArgs : EventArgs where ItemType : class + { + #region Data + + ObservableCollection itemsSource; + ItemType dataItem; + int oldIndex; + int newIndex; + DragDropEffects allowedEffects = DragDropEffects.None; + DragDropEffects effects = DragDropEffects.None; + + #endregion // Data + + #region Constructor + + internal ProcessDropEventArgs( + ObservableCollection itemsSource, + ItemType dataItem, + int oldIndex, + int newIndex, + DragDropEffects allowedEffects) + { + this.itemsSource = itemsSource; + this.dataItem = dataItem; + this.oldIndex = oldIndex; + this.newIndex = newIndex; + this.allowedEffects = allowedEffects; + } + + #endregion // Constructor + + #region Public Properties + + /// + /// The items source of the ListView where the drop occurred. + /// + public ObservableCollection ItemsSource + { + get { return this.itemsSource; } + } + + /// + /// The data object which was dropped. + /// + public ItemType DataItem + { + get { return this.dataItem; } + } + + /// + /// The current index of the data item being dropped, in the ItemsSource collection. + /// + public int OldIndex + { + get { return this.oldIndex; } + } + + /// + /// The target index of the data item being dropped, in the ItemsSource collection. + /// + public int NewIndex + { + get { return this.newIndex; } + } + + /// + /// The drag drop effects allowed to be performed. + /// + public DragDropEffects AllowedEffects + { + get { return allowedEffects; } + } + + /// + /// The drag drop effect(s) performed on the dropped item. + /// + public DragDropEffects Effects + { + get { return effects; } + set { effects = value; } + } + + #endregion // Public Properties + } + + #endregion // ProcessDropEventArgs +} \ No newline at end of file diff --git a/ProgramQueuer/Helpers/MouseUtilities.cs b/ProgramQueuer/Helpers/MouseUtilities.cs new file mode 100755 index 0000000..5e5655e --- /dev/null +++ b/ProgramQueuer/Helpers/MouseUtilities.cs @@ -0,0 +1,58 @@ +using System; +using System.Collections.Generic; +using System.Text; +using System.Runtime.InteropServices; +using System.Windows; +using System.Windows.Media; + +namespace WPF.JoshSmith.Controls.Utilities +{ + /// + /// Provides access to the mouse location by calling unmanaged code. + /// + /// + /// This class was written by Dan Crevier (Microsoft). + /// http://blogs.msdn.com/llobo/archive/2006/09/06/Scrolling-Scrollviewer-on-Mouse-Drag-at-the-boundaries.aspx + /// + public class MouseUtilities + { + [StructLayout(LayoutKind.Sequential)] + private struct Win32Point + { + public Int32 X; + public Int32 Y; + }; + + [DllImport("user32.dll")] + private static extern bool GetCursorPos(ref Win32Point pt); + + [DllImport("user32.dll")] + private static extern bool ScreenToClient(IntPtr hwnd, ref Win32Point pt); + + /// + /// Returns the mouse cursor location. This method is necessary during + /// a drag-drop operation because the WPF mechanisms for retrieving the + /// cursor coordinates are unreliable. + /// + /// The Visual to which the mouse coordinates will be relative. + public static Point GetMousePosition(Visual relativeTo) + { + Win32Point mouse = new Win32Point(); + GetCursorPos(ref mouse); + + // Using PointFromScreen instead of Dan Crevier's code (commented out below) + // is a bug fix created by William J. Roberts. Read his comments about the fix + // here: http://www.codeproject.com/useritems/ListViewDragDropManager.asp?msg=1911611#xx1911611xx + return relativeTo.PointFromScreen(new Point((double)mouse.X, (double)mouse.Y)); + + #region Commented Out + //System.Windows.Interop.HwndSource presentationSource = + // (System.Windows.Interop.HwndSource)PresentationSource.FromVisual( relativeTo ); + //ScreenToClient( presentationSource.Handle, ref mouse ); + //GeneralTransform transform = relativeTo.TransformToAncestor( presentationSource.RootVisual ); + //Point offset = transform.Transform( new Point( 0, 0 ) ); + //return new Point( mouse.X - offset.X, mouse.Y - offset.Y ); + #endregion // Commented Out + } + } +} \ No newline at end of file diff --git a/ProgramQueuer/Helpers/ValueConverterGroup.cs b/ProgramQueuer/Helpers/ValueConverterGroup.cs new file mode 100755 index 0000000..16bdc26 --- /dev/null +++ b/ProgramQueuer/Helpers/ValueConverterGroup.cs @@ -0,0 +1,177 @@ +using System; +using System.Collections; +using System.Collections.Generic; +using System.Collections.ObjectModel; +using System.Collections.Specialized; +using System.Globalization; +using System.Reflection; +using System.Text; +using System.Windows.Data; + +namespace ProgramQueuer.Helpers +{ + /// + /// A value converter which contains a list of IValueConverters and invokes their Convert or ConvertBack methods + /// in the order that they exist in the list. The output of one converter is piped into the next converter + /// allowing for modular value converters to be chained together. If the ConvertBack method is invoked, the + /// value converters are executed in reverse order (highest to lowest index). Do not leave an element in the + /// Converters property collection null, every element must reference a valid IValueConverter instance. If a + /// value converter's type is not decorated with the ValueConversionAttribute, an InvalidOperationException will be + /// thrown when the converter is added to the Converters collection. + /// + [System.Windows.Markup.ContentProperty("Converters")] + public class ValueConverterGroup : IValueConverter + { + #region Data + + private readonly ObservableCollection converters = new ObservableCollection(); + private readonly Dictionary cachedAttributes = new Dictionary(); + + #endregion // Data + + #region Constructor + + public ValueConverterGroup() + { + this.converters.CollectionChanged += this.OnConvertersCollectionChanged; + } + + #endregion // Constructor + + #region Converters + + /// + /// Returns the list of IValueConverters contained in this converter. + /// + public ObservableCollection Converters + { + get { return this.converters; } + } + + #endregion // Converters + + #region IValueConverter Members + + object IValueConverter.Convert(object value, Type targetType, object parameter, CultureInfo culture) + { + object output = value; + + for (int i = 0; i < this.Converters.Count; i++) + { + //Run the converter. + output = this.Converters[i].Convert(output, this.GetTargetType(i, targetType, true), parameter, culture); + if (output == Binding.DoNothing) + break; + } + + return output; + } + + object IValueConverter.ConvertBack(object value, Type targetType, object parameter, CultureInfo culture) + { + object output = value; + + for (int i = this.Converters.Count - 1; i > -1; i--) + { + //Run the converter. + output = this.Converters[i].Convert(output, this.GetTargetType(i, targetType, true), parameter, culture); + if (output == Binding.DoNothing) + break; + } + + return output; + } + + #endregion // IValueConverter Members + + #region Private Helpers + + #region GetTargetType + + /// + /// Returns the target type for a conversion operation. + /// + /// The index of the current converter about to be executed. + /// The 'targetType' argument passed into the conversion method. + /// Pass true if calling from the Convert method, or false if calling from ConvertBack. + protected virtual Type GetTargetType(int converterIndex, Type finalTargetType, bool convert) + { + // If the current converter is not the last/first in the list, + // get a reference to the next/previous converter. + IValueConverter nextConverter = null; + int nextIndex = converterIndex; + + //Grab the index for the next converter. + if (convert && nextIndex < this.Converters.Count - 1) + nextIndex++; + else if (!convert && nextIndex > 0) + nextIndex--; + + //Check to see if we have a new index. + if (nextIndex != converterIndex) + //Make sure the converter at our new index is not null. + if (this.Converters[nextIndex] == null) + throw new InvalidOperationException("The Converters collection of the ValueConverterGroup contains a null reference at index: " + nextIndex); + else + //Get our next converter. + nextConverter = this.Converters[nextIndex]; + + //Check if we have to go through another converter. If this (current) is the last converter then this will be null. + if (nextConverter != null) + { + var conversionAttribute = cachedAttributes[nextConverter]; + + // If the Convert method is going to be called, we need to use the SourceType of the next + // converter in the list. If ConvertBack is called, use the TargetType. + return convert ? conversionAttribute.SourceType : conversionAttribute.TargetType; + } + + // If the current converter is the last one to be executed return the target type passed into the conversion method. + return finalTargetType; + } + + #endregion // GetTargetType + + #region OnConvertersCollectionChanged + + void OnConvertersCollectionChanged(object sender, NotifyCollectionChangedEventArgs e) + { + // The 'Converters' collection has been modified, so validate that each value converter it now + // contains is decorated with ValueConversionAttribute and then cache the attribute value. + + IList convertersToProcess = null; + if (e.Action == NotifyCollectionChangedAction.Add || + e.Action == NotifyCollectionChangedAction.Replace) + { + convertersToProcess = e.NewItems; + } + else if (e.Action == NotifyCollectionChangedAction.Remove) + { + foreach (IValueConverter converter in e.OldItems) + this.cachedAttributes.Remove(converter); + } + else if (e.Action == NotifyCollectionChangedAction.Reset) + { + this.cachedAttributes.Clear(); + convertersToProcess = this.converters; + } + + if (convertersToProcess != null && convertersToProcess.Count > 0) + { + foreach (IValueConverter converter in convertersToProcess) + { + object[] attributes = converter.GetType().GetCustomAttributes(typeof(ValueConversionAttribute), false); + + if (attributes.Length != 1) + throw new InvalidOperationException("All value converters added to a ValueConverterGroup must be decorated with the ValueConversionAttribute attribute exactly once."); + + this.cachedAttributes.Add(converter, attributes[0] as ValueConversionAttribute); + } + } + } + + #endregion // OnConvertersCollectionChanged + + #endregion // Private Helpers + } +} diff --git a/ProgramQueuer/Helpers/ValueConverters.cs b/ProgramQueuer/Helpers/ValueConverters.cs new file mode 100755 index 0000000..43665f9 --- /dev/null +++ b/ProgramQueuer/Helpers/ValueConverters.cs @@ -0,0 +1,45 @@ +using System; +using System.Collections.Generic; +using System.ComponentModel; +using System.Globalization; +using System.Linq; +using System.Text; +using System.Windows.Data; +using System.Windows; + +namespace ProgramQueuer.Helpers +{ + [ValueConversion(typeof(bool), typeof(Visibility))] + public class BoolToVisibililty : IValueConverter + { + public object Convert(object value, Type targetType, object parameter, System.Globalization.CultureInfo culture) + { + bool visible = (bool)value; + if (visible) + return Visibility.Visible; + return Visibility.Hidden; + } + + public object ConvertBack(object value, Type targetType, object parameter, System.Globalization.CultureInfo culture) + { + Visibility visible = (Visibility)value; + if (visible == Visibility.Hidden) + return false; + return true; + } + } + + [ValueConversion(typeof(bool), typeof(bool))] + public class InvertBool : IValueConverter + { + public object Convert(object value, Type targetType, object parameter, System.Globalization.CultureInfo culture) + { + return !(bool)value; + } + + public object ConvertBack(object value, Type targetType, object parameter, System.Globalization.CultureInfo culture) + { + return !(bool)value; + } + } +} diff --git a/ProgramQueuer/MainWindow.xaml b/ProgramQueuer/MainWindow.xaml new file mode 100755 index 0000000..14b5701 --- /dev/null +++ b/ProgramQueuer/MainWindow.xaml @@ -0,0 +1,95 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/ProgramQueuer/MainWindow.xaml.cs b/ProgramQueuer/MainWindow.xaml.cs new file mode 100755 index 0000000..9ba69c0 --- /dev/null +++ b/ProgramQueuer/MainWindow.xaml.cs @@ -0,0 +1,118 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using System.Windows; +using System.Windows.Controls; +using System.Windows.Data; +using System.Windows.Documents; +using System.Windows.Input; +using System.Windows.Media; +using System.Windows.Media.Imaging; +using System.Windows.Navigation; +using System.Windows.Shapes; +using ProgramQueuer.Queuer; +using WPF.JoshSmith.ServiceProviders.UI; + +namespace ProgramQueuer +{ + /// + /// Interaction logic for MainWindow.xaml + /// + public partial class MainWindow : Window + { + EntryManager _manager; + + public MainWindow() + { + InitializeComponent(); + + _manager = new EntryManager(); + this.DataContext = _manager; + this.listPrograms.ItemsSource = _manager.QueueList; + } + + private void Window_Loaded(object sender, RoutedEventArgs e) + { + new ListViewDragDropManager(this.listPrograms); + } + + private void GridSplitter_DragCompleted(object sender, System.Windows.Controls.Primitives.DragCompletedEventArgs e) + { + ProgramQueuer.Properties.Settings.Default.split_height = (int)((sender as GridSplitter).Parent as Grid).RowDefinitions[2].Height.Value; + } + + private void Window_Closing(object sender, System.ComponentModel.CancelEventArgs e) + { + if (_manager.Working) + if (MessageBox.Show("Are you sure you want to stop current worker and batch and exit application?", "You sure you want to exit?", MessageBoxButton.YesNo) == MessageBoxResult.No) + { + e.Cancel = true; + return; + } + ProgramQueuer.Properties.Settings.Default.Save(); + } + + private void ButtonExit_Click(object sender, RoutedEventArgs e) + { + this.Close(); + } + + private void ButtonWork_Click(object sender, RoutedEventArgs e) + { + _manager.RunQueuer(); + } + + private void ButtonStop_Click(object sender, RoutedEventArgs e) + { + if (MessageBox.Show("Are you sure you want to stop current batch?", "Stop worker", MessageBoxButton.YesNo) == MessageBoxResult.Yes) + { + _manager.ForceStop(); + } + } + + private void ListView_DragEnter(object sender, DragEventArgs e) + { + if (e.Data.GetDataPresent(DataFormats.FileDrop)) + e.Effects = DragDropEffects.Link; + else + e.Effects = DragDropEffects.None; + } + + private void ListView_Drop(object sender, DragEventArgs e) + { + if (e.Data.GetDataPresent(DataFormats.FileDrop)) + { + string[] files = (string[])e.Data.GetData(DataFormats.FileDrop, false); + foreach (string file in files) + { + _manager.QueueList.Add(new ProgramEntry { Name = file , Status = "Queued"}); + } + } + } + + private void buttonRemove_Click(object sender, RoutedEventArgs e) + { + _manager.QueueList.Remove((sender as Control).DataContext as ProgramEntry); + } + + private void buttonStopCurrent_Click(object sender, RoutedEventArgs e) + { + if (MessageBox.Show("Are you sure you want to stop current running process and continue to next?", "Stop current worker?", MessageBoxButton.YesNo) == MessageBoxResult.Yes) + { + _manager.ForceStopCurrent(); + } + } + + private void TextBox_TextChanged(object sender, TextChangedEventArgs e) + { + textboxStatus.CaretIndex = textboxStatus.Text.Length; + textboxStatus.ScrollToEnd(); + } + + private void buttonAdd_Click(object sender, RoutedEventArgs e) + { + + } + } +} diff --git a/ProgramQueuer/ProgramQueuer.csproj b/ProgramQueuer/ProgramQueuer.csproj new file mode 100755 index 0000000..f0a3638 --- /dev/null +++ b/ProgramQueuer/ProgramQueuer.csproj @@ -0,0 +1,142 @@ + + + + Debug + x86 + 8.0.30703 + 2.0 + {B1825A4D-32CB-45E2-9CFE-77622DCC1641} + WinExe + Properties + ProgramQueuer + ProgramQueuer + v3.5 + 512 + {60dc8134-eba5-43b8-bcc9-bb4bc16c2548};{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC} + 4 + + + x86 + true + full + false + bin\Debug\ + DEBUG;TRACE + prompt + 4 + + + x86 + pdbonly + true + bin\Release\ + TRACE + prompt + 4 + + + reports.ico + + + + + + + + + + + + + + + MSBuild:Compile + Designer + + + + + + + + + + + + + Settings.xaml + + + MSBuild:Compile + Designer + + + App.xaml + Code + + + MainWindow.xaml + Code + + + Designer + MSBuild:Compile + + + + + Code + + + True + True + Resources.resx + + + True + Settings.settings + True + + + ResXFileCodeGenerator + Resources.Designer.cs + + + + SettingsSingleFileGenerator + Settings.Designer.cs + + + + + + + + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/ProgramQueuer/Properties/AssemblyInfo.cs b/ProgramQueuer/Properties/AssemblyInfo.cs new file mode 100755 index 0000000..c18c9f5 --- /dev/null +++ b/ProgramQueuer/Properties/AssemblyInfo.cs @@ -0,0 +1,55 @@ +using System.Reflection; +using System.Resources; +using System.Runtime.CompilerServices; +using System.Runtime.InteropServices; +using System.Windows; + +// General Information about an assembly is controlled through the following +// set of attributes. Change these attribute values to modify the information +// associated with an assembly. +[assembly: AssemblyTitle("ProgramQueuer")] +[assembly: AssemblyDescription("")] +[assembly: AssemblyConfiguration("")] +[assembly: AssemblyCompany("Microsoft")] +[assembly: AssemblyProduct("ProgramQueuer")] +[assembly: AssemblyCopyright("Copyright © Microsoft 2012")] +[assembly: AssemblyTrademark("")] +[assembly: AssemblyCulture("")] + +// Setting ComVisible to false makes the types in this assembly not visible +// to COM components. If you need to access a type in this assembly from +// COM, set the ComVisible attribute to true on that type. +[assembly: ComVisible(false)] + +//In order to begin building localizable applications, set +//CultureYouAreCodingWith in your .csproj file +//inside a . For example, if you are using US english +//in your source files, set the to en-US. Then uncomment +//the NeutralResourceLanguage attribute below. Update the "en-US" in +//the line below to match the UICulture setting in the project file. + +//[assembly: NeutralResourcesLanguage("en-US", UltimateResourceFallbackLocation.Satellite)] + + +[assembly: ThemeInfo( + ResourceDictionaryLocation.None, //where theme specific resource dictionaries are located + //(used if a resource is not found in the page, + // or application resource dictionaries) + ResourceDictionaryLocation.SourceAssembly //where the generic resource dictionary is located + //(used if a resource is not found in the page, + // app, or any theme specific resource dictionaries) +)] + + +// Version information for an assembly consists of the following four values: +// +// Major Version +// Minor Version +// Build Number +// Revision +// +// You can specify all the values or you can default the Build and Revision Numbers +// by using the '*' as shown below: +// [assembly: AssemblyVersion("1.0.*")] +[assembly: AssemblyVersion("1.0.0.0")] +[assembly: AssemblyFileVersion("1.0.0.0")] diff --git a/ProgramQueuer/Properties/Resources.Designer.cs b/ProgramQueuer/Properties/Resources.Designer.cs new file mode 100755 index 0000000..88d729b --- /dev/null +++ b/ProgramQueuer/Properties/Resources.Designer.cs @@ -0,0 +1,71 @@ +//------------------------------------------------------------------------------ +// +// This code was generated by a tool. +// Runtime Version:4.0.30319.1 +// +// Changes to this file may cause incorrect behavior and will be lost if +// the code is regenerated. +// +//------------------------------------------------------------------------------ + +namespace ProgramQueuer.Properties +{ + + + /// + /// A strongly-typed resource class, for looking up localized strings, etc. + /// + // This class was auto-generated by the StronglyTypedResourceBuilder + // class via a tool like ResGen or Visual Studio. + // To add or remove a member, edit your .ResX file then rerun ResGen + // with the /str option, or rebuild your VS project. + [global::System.CodeDom.Compiler.GeneratedCodeAttribute("System.Resources.Tools.StronglyTypedResourceBuilder", "4.0.0.0")] + [global::System.Diagnostics.DebuggerNonUserCodeAttribute()] + [global::System.Runtime.CompilerServices.CompilerGeneratedAttribute()] + internal class Resources + { + + private static global::System.Resources.ResourceManager resourceMan; + + private static global::System.Globalization.CultureInfo resourceCulture; + + [global::System.Diagnostics.CodeAnalysis.SuppressMessageAttribute("Microsoft.Performance", "CA1811:AvoidUncalledPrivateCode")] + internal Resources() + { + } + + /// + /// Returns the cached ResourceManager instance used by this class. + /// + [global::System.ComponentModel.EditorBrowsableAttribute(global::System.ComponentModel.EditorBrowsableState.Advanced)] + internal static global::System.Resources.ResourceManager ResourceManager + { + get + { + if ((resourceMan == null)) + { + global::System.Resources.ResourceManager temp = new global::System.Resources.ResourceManager("ProgramQueuer.Properties.Resources", typeof(Resources).Assembly); + resourceMan = temp; + } + return resourceMan; + } + } + + /// + /// Overrides the current thread's CurrentUICulture property for all + /// resource lookups using this strongly typed resource class. + /// + [global::System.ComponentModel.EditorBrowsableAttribute(global::System.ComponentModel.EditorBrowsableState.Advanced)] + internal static global::System.Globalization.CultureInfo Culture + { + get + { + return resourceCulture; + } + set + { + resourceCulture = value; + } + } + } +} diff --git a/ProgramQueuer/Properties/Resources.resx b/ProgramQueuer/Properties/Resources.resx new file mode 100755 index 0000000..ffecec8 --- /dev/null +++ b/ProgramQueuer/Properties/Resources.resx @@ -0,0 +1,117 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + text/microsoft-resx + + + 2.0 + + + System.Resources.ResXResourceReader, System.Windows.Forms, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + + System.Resources.ResXResourceWriter, System.Windows.Forms, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + \ No newline at end of file diff --git a/ProgramQueuer/Properties/Settings.Designer.cs b/ProgramQueuer/Properties/Settings.Designer.cs new file mode 100755 index 0000000..cb7f332 --- /dev/null +++ b/ProgramQueuer/Properties/Settings.Designer.cs @@ -0,0 +1,98 @@ +//------------------------------------------------------------------------------ +// +// This code was generated by a tool. +// Runtime Version:4.0.30319.1 +// +// Changes to this file may cause incorrect behavior and will be lost if +// the code is regenerated. +// +//------------------------------------------------------------------------------ + +namespace ProgramQueuer.Properties { + + + [global::System.Runtime.CompilerServices.CompilerGeneratedAttribute()] + [global::System.CodeDom.Compiler.GeneratedCodeAttribute("Microsoft.VisualStudio.Editors.SettingsDesigner.SettingsSingleFileGenerator", "10.0.0.0")] + internal sealed partial class Settings : global::System.Configuration.ApplicationSettingsBase { + + private static Settings defaultInstance = ((Settings)(global::System.Configuration.ApplicationSettingsBase.Synchronized(new Settings()))); + + public static Settings Default { + get { + return defaultInstance; + } + } + + [global::System.Configuration.UserScopedSettingAttribute()] + [global::System.Diagnostics.DebuggerNonUserCodeAttribute()] + [global::System.Configuration.DefaultSettingValueAttribute("500")] + public double width { + get { + return ((double)(this["width"])); + } + set { + this["width"] = value; + } + } + + [global::System.Configuration.UserScopedSettingAttribute()] + [global::System.Diagnostics.DebuggerNonUserCodeAttribute()] + [global::System.Configuration.DefaultSettingValueAttribute("500")] + public double height { + get { + return ((double)(this["height"])); + } + set { + this["height"] = value; + } + } + + [global::System.Configuration.UserScopedSettingAttribute()] + [global::System.Diagnostics.DebuggerNonUserCodeAttribute()] + [global::System.Configuration.DefaultSettingValueAttribute("200")] + public double split_height { + get { + return ((double)(this["split_height"])); + } + set { + this["split_height"] = value; + } + } + + [global::System.Configuration.UserScopedSettingAttribute()] + [global::System.Diagnostics.DebuggerNonUserCodeAttribute()] + [global::System.Configuration.DefaultSettingValueAttribute("300")] + public double columnWidth1 { + get { + return ((double)(this["columnWidth1"])); + } + set { + this["columnWidth1"] = value; + } + } + + [global::System.Configuration.UserScopedSettingAttribute()] + [global::System.Diagnostics.DebuggerNonUserCodeAttribute()] + [global::System.Configuration.DefaultSettingValueAttribute("50")] + public double columnWidth2 { + get { + return ((double)(this["columnWidth2"])); + } + set { + this["columnWidth2"] = value; + } + } + + [global::System.Configuration.UserScopedSettingAttribute()] + [global::System.Diagnostics.DebuggerNonUserCodeAttribute()] + [global::System.Configuration.DefaultSettingValueAttribute("200")] + public double columnWidth3 { + get { + return ((double)(this["columnWidth3"])); + } + set { + this["columnWidth3"] = value; + } + } + } +} diff --git a/ProgramQueuer/Properties/Settings.settings b/ProgramQueuer/Properties/Settings.settings new file mode 100755 index 0000000..35c7044 --- /dev/null +++ b/ProgramQueuer/Properties/Settings.settings @@ -0,0 +1,24 @@ + + + + + + 500 + + + 500 + + + 200 + + + 300 + + + 50 + + + 200 + + + \ No newline at end of file diff --git a/ProgramQueuer/Queuer/EntryManager.cs b/ProgramQueuer/Queuer/EntryManager.cs new file mode 100755 index 0000000..4cada2e --- /dev/null +++ b/ProgramQueuer/Queuer/EntryManager.cs @@ -0,0 +1,175 @@ +using System; +using System.Diagnostics; +using System.ComponentModel; +using System.Collections.ObjectModel; +using System.Collections.Generic; +using System.Linq; +using System.IO; +using System.Text; + +namespace ProgramQueuer.Queuer +{ + public class EntryManager : INotifyPropertyChanged + { + bool _working; + bool _clearNext; + string _buffer; + ProgramEntry _currentEntry; + Process _currentProcess; + ProcessIOManager _processManager; + + public EntryManager() + { + QueueList = new ObservableCollection(); + _buffer = ""; + _currentEntry = null; + _processManager = new ProcessIOManager(); + _processManager.StderrTextRead += new StringReadEventHandler(_processManager_StdoutTextRead); + _processManager.StdoutTextRead += new StringReadEventHandler(_processManager_StdoutTextRead); + _currentProcess = new Process(); + _currentProcess.StartInfo.UseShellExecute = false; + _currentProcess.StartInfo.RedirectStandardOutput = true; + _currentProcess.StartInfo.RedirectStandardError = true; + _currentProcess.StartInfo.RedirectStandardInput = true; + _currentProcess.StartInfo.CreateNoWindow = true; + _currentProcess.EnableRaisingEvents = true; + _currentProcess.Exited += new EventHandler(_currentProcess_Exited); + } + + public ObservableCollection QueueList; + public event PropertyChangedEventHandler PropertyChanged = delegate { }; + + public ProgramEntry CurrentEntry + { + get { return _currentEntry; } + set + { + _currentEntry = value; + PropertyChanged(this, new PropertyChangedEventArgs("CurrentEntry")); + } + } + + public bool Working + { + get { return _working; } + set + { + _working = value; + PropertyChanged(this, new PropertyChangedEventArgs("Working")); + } + } + + public void RunQueuer() + { + if (QueueList.Count > 0) + { + Working = true; + RunProgram(QueueList[0]); + } + } + + public void ForceStopCurrent() + { + _currentProcess.Kill(); + } + + public void ForceStop() + { + this.Working = false; + _currentProcess.Kill(); + } + + private void RunProgram(ProgramEntry entry) + { + try + { + _currentEntry = entry; + _currentEntry.Output = ""; + _currentEntry.Working = true; + _currentEntry.Status = "Starting"; + _currentProcess.StartInfo.FileName = _currentEntry.Name; + _currentProcess.StartInfo.WorkingDirectory = new FileInfo(_currentEntry.Name).DirectoryName; + _currentProcess.Start(); + _processManager.RunningProcess = _currentProcess; + _processManager.StartProcessOutputRead(); + } + catch (Exception e) + { + _currentEntry.Working = false; + _currentEntry.Finished = false; + _currentEntry.Output += string.Format("Error while starting {0}:\n\n{1}", _currentEntry.Name, e.ToString()); + if (RunNext()) + { + _currentEntry = null; + this.Working = false; + } + } + } + + private bool RunNext() + { + _currentEntry.Status = "Finished"; + if (QueueList.IndexOf(_currentEntry) >= 0 && QueueList.IndexOf(_currentEntry) < QueueList.Count - 1) + RunProgram(QueueList[QueueList.IndexOf(_currentEntry) + 1]); + else + return true; + return false; + } + + void _currentProcess_Exited(object sender, EventArgs e) + { + _processManager.StopMonitoringProcessOutput(); + _currentEntry.Working = false; + _currentEntry.Finished = true; + if (!this.Working) + return; + if (RunNext()) + { + _currentEntry = null; + this.Working = false; + } + } + + void _processManager_StdoutTextRead(string text) + { + string[] lines = text.Split('\r'); + if (!text.EndsWith("\r") && !text.EndsWith("\n") && _clearNext) + { + _buffer += text; + return; + } + else + { + text = _buffer + text; + _buffer = ""; + } + + + if (_clearNext && text == "\n") + _clearNext = false; + + while (text.IndexOf('\b') >= 0) + { + if (_currentEntry.Output.Length > 0 && _currentEntry.Output[_currentEntry.Output.Length - 1] != '\n') + _currentEntry.Output = _currentEntry.Output.Remove(_currentEntry.Output.Length - 1); + text = text.Remove(text.IndexOf('\b'), 1); + } + + if (_clearNext && text.Replace("\n", "").Replace("\r", "").Trim() != "") + if (_currentEntry.Output.LastIndexOf('\n') < _currentEntry.Output.Length - 1) + _currentEntry.Output = _currentEntry.Output.Remove(_currentEntry.Output.LastIndexOf('\n') + 1) + text; + else + _currentEntry.Output += text; + else + _currentEntry.Output += text; + + if (text.Replace("\n", "").Trim() != "") + _currentEntry.Status = text.Replace("\n", "").Replace("\r", ""); + + if (lines.Length == 2 && lines[1] == "") + _clearNext = true; + else + _clearNext = false; + } + } +} diff --git a/ProgramQueuer/Queuer/ProcessIOManager.cs b/ProgramQueuer/Queuer/ProcessIOManager.cs new file mode 100755 index 0000000..cabd5a8 --- /dev/null +++ b/ProgramQueuer/Queuer/ProcessIOManager.cs @@ -0,0 +1,342 @@ +using System; +using System.Diagnostics; +using System.IO; +using System.Text; +using System.Threading; + +  +/*************************************************************************************** +* Author: Curt C. +* Email : harpyeaglecp@aol.com +* +* Project File: "Solving Problems of Monitoring Standard Output and Error Streams Of A Running Process" +* for http://www.codeproject.com +* +* This software is released under the Code Project Open License (CPOL) +* +* See official license description at: http://www.codeproject.com/info/cpol10.aspx +* +* This software is provided AS-IS without any warranty of any kind. +* +* >>> Please leave this header intact when using this file in other projects <<< +***************************************************************************************/ + +namespace ProgramQueuer.Queuer +{ + // Delegate definition used for events to receive notification + // of text read from stdout or stderr of a running process. + public delegate void StringReadEventHandler(string text); + + /// + /// Class that manages the reading of the output produced by a given 'Process' + /// and reports the output via events. + /// Both standard error (stderr) and standard output (stdout) are + /// managed and reported. + /// The stdout and stderr monitoring and reading are each performed + /// by separate background threads. Each thread blocks on a Read() + /// method, waiting for text in the stream being monitored to become available. + /// + /// Note the Process.RedirectStandardOutput must be set to true in order + /// to read standard output from the process, and the Process.RedirectStandardError + /// must be set to true to read standard error. + /// + public class ProcessIOManager + { + #region Private_Fields + // Command line process that is executing and being + // monitored by this class for stdout/stderr output. + private Process runningProcess; + + // Thread to monitor and read standard output (stdout) + private Thread stdoutThread; + + // Thread to monitor and read standard error (stderr) + private Thread stderrThread; + + // Buffer to hold characters read from either stdout, or stderr streams + private StringBuilder streambuffer; + #endregion + + #region Public_Properties_And_Events + /// + /// Gets the process being monitored + /// + /// The running process. + public Process RunningProcess + { + get { return runningProcess; } + set { runningProcess = value; } + } + + // Event to notify of a string read from stdout stream + public event StringReadEventHandler StdoutTextRead; + + // Event to notify of a string read from stderr stream + public event StringReadEventHandler StderrTextRead; + + #endregion + + #region Constructor_And_Initialization + + /// + /// Initializes a new instance of the class. + /// + /// + public ProcessIOManager() + { + this.streambuffer = new StringBuilder(256); + } + + /// + /// Initializes a new instance of the class. + /// + /// The process. + /// + /// Does not automatically start listening for stdout/stderr. + /// Call StartProcessOutputRead() to begin listening for process output. + /// + /// + public ProcessIOManager(Process process) + { + if (process == null) + throw new Exception("ProcessIoManager unable to set running process - null value is supplied"); + + if (process.HasExited == true) + throw new Exception("ProcessIoManager unable to set running process - the process has already existed."); + + this.runningProcess = process; + this.streambuffer = new StringBuilder(256); + } + + #endregion + + #region Public_Methods + /// + /// Starts the background threads reading any output produced (standard output, standard error) + /// that is produces by the running process. + /// + public void StartProcessOutputRead() + { + // Just to make sure there aren't previous threads running. + StopMonitoringProcessOutput(); + + // Make sure we have a valid, running process + CheckForValidProcess("Unable to start monitoring process output.", true); + + // If the stdout is redirected for the process, then start + // the stdout thread, which will manage the reading of stdout from the + // running process, and report text read via supplied events. + if (runningProcess.StartInfo.RedirectStandardOutput == true) + { + stdoutThread = new Thread(new ThreadStart(ReadStandardOutputThreadMethod)); + // Make thread a background thread - if it was foreground, then + // the thread could hang up the process from exiting. Background + // threads will be forced to stop on main thread termination. + stdoutThread.IsBackground = true; + stdoutThread.Start(); + } + + // If the stderr is redirected for the process, then start + // the stderr thread, which will manage the reading of stderr from the + // running process, and report text read via supplied events. + if (runningProcess.StartInfo.RedirectStandardError == true) + { + stderrThread = new Thread(new ThreadStart(ReadStandardErrorThreadMethod)); + stderrThread.IsBackground = true; + stderrThread.Start(); + } + } + + /// + /// Writes the supplied text string to the standard input (stdin) of the running process + /// + /// In order to be able to write to the Process, the StartInfo.RedirectStandardInput must be set to true. + /// The text to write to running process input stream. + public void WriteStdin(string text) + { + // Make sure we have a valid, running process + CheckForValidProcess("Unable to write to process standard input.", true); + if (runningProcess.StartInfo.RedirectStandardInput == true) + runningProcess.StandardInput.WriteLine(text); + } + #endregion + + #region Private_Methods + /// + /// Checks for valid (non-null Process), and optionally check to see if the process has exited. + /// Throws Exception if process is null, or if process has existed and checkForHasExited is true. + /// + /// The error message text to display if an exception is thrown. + /// if set to true [check if process has exited]. + private void CheckForValidProcess(string errorMessageText, bool checkForHasExited) + { + errorMessageText = (errorMessageText == null ? "" : errorMessageText.Trim()); + if (runningProcess == null) + throw new Exception(errorMessageText + " (Running process must be available)"); + + if (checkForHasExited && runningProcess.HasExited) + throw new Exception(errorMessageText + " (Process has exited)"); + } + + /// + /// Read characters from the supplied stream, and accumulate them in the + /// 'streambuffer' variable until there are no more characters to read. + /// + /// The first character that has already been read. + /// The stream reader to read text from. + /// if set to true the stream is assumed to be standard output, otherwise assumed to be standard error. + private void ReadStream(int firstCharRead, StreamReader streamReader, bool isstdout) + { + // One of the streams (stdout, stderr) has characters ready to be written + // Flush the supplied stream until no more characters remain to be read. + // The synchronized/ locked section of code to prevent the other thread from + // reading its stream at the same time, producing intermixed stderr/stdout results. + // If the threads were not synchronized, the threads + // could read from both stream simultaneously, and jumble up the text with + // stderr and stdout text intermixed. + lock (this) + { + // Single character read from either stdout or stderr + int ch; + // Clear the stream buffer to hold the text to be read + streambuffer.Length = 0; + + //Console.WriteLine("CHAR=" + firstCharRead); + streambuffer.Append((char)firstCharRead); + + // While there are more characters to be read + while (streamReader.Peek() > -1) + { + // Read the character in the queue + ch = streamReader.Read(); + + if (ch != '\n' && streambuffer.Length > 0 && streambuffer[streambuffer.Length - 1] == '\r') + NotifyAndFlushBufferText(streambuffer, isstdout); + + // Accumulate the characters read in the stream buffer + streambuffer.Append((char)ch); + + // Send text one line at a time - much more efficient than + // one character at a time + if (ch == '\n') + NotifyAndFlushBufferText(streambuffer, isstdout); + } + // Flush any remaining text in the buffer + NotifyAndFlushBufferText(streambuffer, isstdout); + } // End lock() + } + + /// + /// Invokes the OnStdoutTextRead (if isstdout==true)/ OnStderrTextRead events + /// with the supplied streambuilder 'textbuffer', then clears + /// textbuffer after event is invoked. + /// + /// The textbuffer containing the text string to pass to events. + /// if set to true, the stdout event is invoked, otherwise stedrr event is invoked. + private void NotifyAndFlushBufferText(StringBuilder textbuffer, bool isstdout) + { + if (textbuffer.Length > 0) + { + try + { + if (isstdout == true && StdoutTextRead != null) + { // Send notificatin of text read from stdout + StdoutTextRead(textbuffer.ToString()); + } + else if (isstdout == false && StderrTextRead != null) + { // Send notificatin of text read from stderr + StderrTextRead(textbuffer.ToString()); + } + } + catch (Exception e) + { + } + // 'Clear' the text buffer + textbuffer.Length = 0; + } + } + + /// + /// Method started in a background thread (stdoutThread) to manage the reading and reporting of + /// standard output text produced by the running process. + /// + private void ReadStandardOutputThreadMethod() + { + // Main entry point for thread - make sure the main entry method + // is surrounded with try catch, so an uncaught exception won't + // screw up the entire application + try + { + // character read from stdout + int ch; + + // The Read() method will block until something is available + while (runningProcess != null && (ch = runningProcess.StandardOutput.Read()) > -1) + { + // a character has become available for reading + // block the other thread and process this stream's input. + ReadStream(ch, runningProcess.StandardOutput, true); + } + } + catch (Exception ex) + { + Console.WriteLine("ProcessIoManager.ReadStandardOutputThreadMethod()- Exception caught:" + + ex.Message + "\nStack Trace:" + ex.StackTrace); + } + } + + /// + /// Method started in a background thread (stderrThread) to manage the reading and reporting of + /// standard error text produced by the running process. + /// + private void ReadStandardErrorThreadMethod() + { + try + { + // Character read from stderr + int ch; + // The Read() method will block until something is available + while (runningProcess != null && (ch = runningProcess.StandardError.Read()) > -1) + ReadStream(ch, runningProcess.StandardError, false); + } + catch (Exception ex) + { + Console.WriteLine("ProcessIoManager.ReadStandardErrorThreadMethod()- Exception caught:" + + ex.Message + "\nStack Trace:" + ex.StackTrace); + } + } + + /// + /// Stops both the standard input and stardard error background reader threads (via the Abort() method) + /// + public void StopMonitoringProcessOutput() + { + // Stop the stdout reader thread + try + { + if (stdoutThread != null) + stdoutThread.Abort(); + } + catch (Exception ex) + { + Console.WriteLine("ProcessIoManager.StopReadThreads()-Exception caught on stopping stdout thread.\n" + + "Exception Message:\n" + ex.Message + "\nStack Trace:\n" + ex.StackTrace); + } + + // Stop the stderr reader thread + try + { + if (stderrThread != null) + stderrThread.Abort(); + } + catch (Exception ex) + { + Console.WriteLine("ProcessIoManager.StopReadThreads()-Exception caught on stopping stderr thread.\n" + + "Exception Message:\n" + ex.Message + "\nStack Trace:\n" + ex.StackTrace); + } + stdoutThread = null; + stderrThread = null; + } + #endregion + } +} \ No newline at end of file diff --git a/ProgramQueuer/Queuer/ProgramEntry.cs b/ProgramQueuer/Queuer/ProgramEntry.cs new file mode 100755 index 0000000..86752f7 --- /dev/null +++ b/ProgramQueuer/Queuer/ProgramEntry.cs @@ -0,0 +1,85 @@ +using System; +using System.ComponentModel; +using System.Collections.Generic; +using System.Linq; +using System.Text; + +namespace ProgramQueuer.Queuer +{ + public class ProgramEntry : INotifyPropertyChanged + { + bool _error; + bool _working; + bool _finished; + string _name; + string _output; + string _status; + + public ProgramEntry() + { + Finished = false; + } + + public event PropertyChangedEventHandler PropertyChanged = delegate { }; + + public string Name + { + get { return _name; } + set + { + _name = value; + PropertyChanged(this, new PropertyChangedEventArgs("Name")); + } + } + + public string Output + { + get { return _output; } + set + { + _output = value; + PropertyChanged(this, new PropertyChangedEventArgs("Output")); + } + } + + public string Status + { + get { return _status; } + set + { + _status = value; + PropertyChanged(this, new PropertyChangedEventArgs("Status")); + } + } + + public bool Finished + { + get { return _finished; } + set + { + _finished = value; + PropertyChanged(this, new PropertyChangedEventArgs("Finished")); + } + } + + public bool Working + { + get { return _working; } + set + { + _working = value; + PropertyChanged(this, new PropertyChangedEventArgs("Working")); + } + } + + public bool Error + { + get { return _error; } + set + { + _error = value; + PropertyChanged(this, new PropertyChangedEventArgs("Error")); + } + } + } +} diff --git a/ProgramQueuer/Resources/accept.png b/ProgramQueuer/Resources/accept.png new file mode 100755 index 0000000..219bc71 Binary files /dev/null and b/ProgramQueuer/Resources/accept.png differ diff --git a/ProgramQueuer/Resources/add.png b/ProgramQueuer/Resources/add.png new file mode 100755 index 0000000..0a97508 Binary files /dev/null and b/ProgramQueuer/Resources/add.png differ diff --git a/ProgramQueuer/Resources/application.png b/ProgramQueuer/Resources/application.png new file mode 100755 index 0000000..e6ed513 Binary files /dev/null and b/ProgramQueuer/Resources/application.png differ diff --git a/ProgramQueuer/Resources/play.png b/ProgramQueuer/Resources/play.png new file mode 100755 index 0000000..c177242 Binary files /dev/null and b/ProgramQueuer/Resources/play.png differ diff --git a/ProgramQueuer/Resources/remove.png b/ProgramQueuer/Resources/remove.png new file mode 100755 index 0000000..7645cfa Binary files /dev/null and b/ProgramQueuer/Resources/remove.png differ diff --git a/ProgramQueuer/Resources/stop.png b/ProgramQueuer/Resources/stop.png new file mode 100755 index 0000000..c0904aa Binary files /dev/null and b/ProgramQueuer/Resources/stop.png differ diff --git a/ProgramQueuer/Resources/warning.png b/ProgramQueuer/Resources/warning.png new file mode 100755 index 0000000..77d3ff1 Binary files /dev/null and b/ProgramQueuer/Resources/warning.png differ diff --git a/ProgramQueuer/Resources/window.png b/ProgramQueuer/Resources/window.png new file mode 100755 index 0000000..494ccec Binary files /dev/null and b/ProgramQueuer/Resources/window.png differ diff --git a/ProgramQueuer/Settings.xaml b/ProgramQueuer/Settings.xaml new file mode 100755 index 0000000..c4865de --- /dev/null +++ b/ProgramQueuer/Settings.xaml @@ -0,0 +1,12 @@ + + + + + + + + + diff --git a/ProgramQueuer/Settings.xaml.cs b/ProgramQueuer/Settings.xaml.cs new file mode 100755 index 0000000..3794ab3 --- /dev/null +++ b/ProgramQueuer/Settings.xaml.cs @@ -0,0 +1,26 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using System.Windows; +using System.Windows.Controls; +using System.Windows.Data; +using System.Windows.Documents; +using System.Windows.Input; +using System.Windows.Media; +using System.Windows.Media.Imaging; +using System.Windows.Shapes; + +namespace ProgramQueuer +{ + /// + /// Interaction logic for Settings.xaml + /// + public partial class Settings : Window + { + public Settings() + { + InitializeComponent(); + } + } +} diff --git a/ProgramQueuer/app.config b/ProgramQueuer/app.config new file mode 100755 index 0000000..b549024 --- /dev/null +++ b/ProgramQueuer/app.config @@ -0,0 +1,30 @@ + + + + +
+ + + + + + 500 + + + 500 + + + 200 + + + 300 + + + 50 + + + 200 + + + + \ No newline at end of file diff --git a/ProgramQueuer/reports.ico b/ProgramQueuer/reports.ico new file mode 100755 index 0000000..7969ccb Binary files /dev/null and b/ProgramQueuer/reports.ico differ