private static void DoActionItem(ShortcutItem ActiveShortcutItem, int itemIndex, ExtendedScreenshot ActionItemScreenshot) { if (ActiveShortcutItem == null || itemIndex >= ActiveShortcutItem.ActionChain.ActionItems.Count) { Trace.WriteLine("No more action items to execute", string.Format("Program.DoActionItem [{0}]", System.Threading.Thread.CurrentThread.Name)); //todo: Why can't the hook be reinstated on the action thread? Preview.BeginInvoke(new MethodInvoker(() => KeyboardHook.isPaused = false)); IconAnimation.CancelAsync(); return; } IActionItem CurrentActionItem = ActiveShortcutItem.ActionChain.ActionItems[itemIndex]; //don't try and invoke the scrolling screenshot actions if they're the first type in the action chain (eg, solo items for their shortcuts) and there's no active scrolling screenshot happening //at least this solves flashing the icon whenever 'End' key is pressed during normal computer use if (!Program.isTakingScrollingScreenshot && (CurrentActionItem.ActionType == ActionTypes.ContinueScrollingScreenshot || CurrentActionItem.ActionType == ActionTypes.EndScrollingScreenshot) && itemIndex == 0) return; KeyboardHook.isPaused = true; Trace.WriteLine(CurrentActionItem.ActionType, string.Format("Program.DoActionItem [{0}]", System.Threading.Thread.CurrentThread.Name)); //todo: figure out a better way to do these exclusions rather than harcoding behavior for scrolling screenshots if (!IconAnimation.IsBusy) if ((new[] { ActionTypes.ContinueScrollingScreenshot, ActionTypes.EndScrollingScreenshot }.Contains(CurrentActionItem.ActionType) && isTakingScrollingScreenshot) || !new[] { ActionTypes.ContinueScrollingScreenshot, ActionTypes.EndScrollingScreenshot }.Contains(CurrentActionItem.ActionType)) IconAnimation.RunWorkerAsync(); Task.Factory.StartNew<ExtendedScreenshot>(() => CurrentActionItem.Invoke(ActionItemScreenshot)) .ContinueWith(t => DoActionItem(ActiveShortcutItem, itemIndex + 1, t.Result)); }
private static void SpawnActionChain(ShortcutItem CurrentShortcutItem) { Trace.WriteLine("Beginning action chain for " + CurrentShortcutItem, string.Format("Program.SpawnActionChain [{0}]", System.Threading.Thread.CurrentThread.Name)); //Creating a STA thread manually here, because in order for SaveFileDialog to work it must be created on a STA thread. BackgroundWorker/ThreadPool is MTA. //todo: Is the above still relevant now that we safely invoke to the UI thread? var HookAction = new Thread(new ThreadStart(() => DoActionItem(CurrentShortcutItem, 0, Preview.CurrentScreenshot ?? History.LastOrDefault()))); HookAction.Name = "Hook Action " + HookAction.ManagedThreadId; HookAction.SetApartmentState(ApartmentState.STA); HookAction.Start(); }
private static void DoActionItem(ShortcutItem ActiveShortcutItem, int itemIndex, ExtendedScreenshot ActionItemScreenshot) { if (ActiveShortcutItem == null || itemIndex >= ActiveShortcutItem.ActionChain.ActionItems.Count) { Trace.WriteLine("No more action items to execute", string.Format("Program.DoActionItem [{0}]", System.Threading.Thread.CurrentThread.Name)); //todo: Why can't the hook be reinstated on the action thread? Preview.BeginInvoke(new MethodInvoker(() => KeyboardHook.isPaused = false)); IconAnimation.CancelAsync(); return; } IActionItem CurrentActionItem = ActiveShortcutItem.ActionChain.ActionItems[itemIndex]; //don't try and invoke the scrolling screenshot actions if they're the first type in the action chain (eg, solo items for their shortcuts) and there's no active scrolling screenshot happening //at least this solves flashing the icon whenever 'End' key is pressed during normal computer use if (!Program.isTakingScrollingScreenshot && (CurrentActionItem.ActionType == ActionTypes.ContinueScrollingScreenshot || CurrentActionItem.ActionType == ActionTypes.EndScrollingScreenshot) && itemIndex == 0) { return; } KeyboardHook.isPaused = true; Trace.WriteLine(CurrentActionItem.ActionType, string.Format("Program.DoActionItem [{0}]", System.Threading.Thread.CurrentThread.Name)); //todo: figure out a better way to do these exclusions rather than harcoding behavior for scrolling screenshots if (!IconAnimation.IsBusy) { if ((new[] { ActionTypes.ContinueScrollingScreenshot, ActionTypes.EndScrollingScreenshot }.Contains(CurrentActionItem.ActionType) && isTakingScrollingScreenshot) || !new[] { ActionTypes.ContinueScrollingScreenshot, ActionTypes.EndScrollingScreenshot }.Contains(CurrentActionItem.ActionType)) { IconAnimation.RunWorkerAsync(); } } Task.Factory.StartNew <ExtendedScreenshot>(() => CurrentActionItem.Invoke(ActionItemScreenshot)) .ContinueWith(t => DoActionItem(ActiveShortcutItem, itemIndex + 1, t.Result)); }
private static void DoActionItem(ShortcutItem ActiveShortcutItem, int itemIndex) { if (ActiveShortcutItem == null || itemIndex >= ActiveShortcutItem.ActionChain.ActionItems.Count) { Trace.WriteLine("No more action items to execute", string.Format("Program.DoActionItem [{0}]", Thread.CurrentThread.Name)); //todo: Why can't the hook be reinstated on the action thread? Preview.BeginInvoke(new MethodInvoker(() => KeyboardHook.isPaused = false)); IconAnimation.CancelAsync(); return; } KeyboardHook.isPaused = true; IActionItem CurrentActionItem = ActiveShortcutItem.ActionChain.ActionItems[itemIndex]; Trace.WriteLine(CurrentActionItem.ActionType, string.Format("Program.DoActionItem [{0}]", Thread.CurrentThread.Name)); //todo: figure out a better way to do these exclusions rather than harcoding behavior for scrolling screenshots if (!IconAnimation.IsBusy) if ((new[] { ActionTypes.ContinueScrollingScreenshot, ActionTypes.EndScrollingScreenshot }.Contains(CurrentActionItem.ActionType) && isTakingScrollingScreenshot) || !new[] { ActionTypes.ContinueScrollingScreenshot, ActionTypes.EndScrollingScreenshot }.Contains(CurrentActionItem.ActionType)) IconAnimation.RunWorkerAsync(); switch (CurrentActionItem.ActionType) { case ActionTypes.TakeForegroundScreenshot: { var CurrentActionConfig = CurrentActionItem as TakeForegroundScreenshotAction; try { var ForegroundWindowScreenshot = new ExtendedScreenshot(CurrentActionConfig.Method, CurrentActionConfig.SolidGlass); History.Add(ForegroundWindowScreenshot); } catch (Exception e) { Trace.WriteLine(string.Format("Exception in TakeForegroundScreenshot: {0}", e.GetBaseException()), string.Format("Program.DoActionItem [{0}]", Thread.CurrentThread.Name)); ReportListener reporter = Trace.Listeners.Cast<TraceListener>().Where(tl => tl is ReportListener).FirstOrDefault() as ReportListener; File.WriteAllText(Path.Combine(Configuration.LocalPath, "report.txt"), string.Join("\n", reporter.Messages.Select(m => string.Format("{0} {1}: {2}", m.Timestamp, m.Category, m.Message)).ToArray())); } DoActionItem(ActiveShortcutItem, itemIndex + 1); } break; case ActionTypes.TakeRegionScreenshot: { var CurrentActionConfig = CurrentActionItem as TakeRegionScreenshotAction; Rectangle SelectedRegion = CurrentActionConfig.Region; try { if (CurrentActionConfig.UseRegionSelector) { lock (_actionlock) { var e = new PreviewEventArgs() { ActionItem = CurrentActionItem }; ShowPreviewEvent(null, e); Monitor.Wait(_actionlock); SelectedRegion = (Rectangle)e.Result; } } if (!SelectedRegion.IsEmpty) { var RegionScreenshot = new ExtendedScreenshot(SelectedRegion); History.Add(RegionScreenshot); DoActionItem(ActiveShortcutItem, itemIndex + 1); } else { DoActionItem(null, itemIndex + 1); } } catch (Exception e) { Trace.WriteLine(string.Format("Exception in TakeRegionScreenshot: {0}", e.GetBaseException()), string.Format("Program.DoActionItem [{0}]", Thread.CurrentThread.Name)); ReportListener reporter = Trace.Listeners.Cast<TraceListener>().Where(tl => tl is ReportListener).FirstOrDefault() as ReportListener; File.WriteAllText(Path.Combine(Configuration.LocalPath, "report.txt"), string.Join("\n", reporter.Messages.Select(m => string.Format("{0} {1}: {2}", m.Timestamp, m.Category, m.Message)).ToArray())); DoActionItem(null, itemIndex + 1); } } break; case ActionTypes.ShowPreview: { if (Configuration.PreviewDelayTime != 0 && History.Count > 0) ShowPreviewEvent(History.Last(), new PreviewEventArgs()); DoActionItem(ActiveShortcutItem, itemIndex + 1); } break; case ActionTypes.Heart: { var LatestScreenshot = History.LastOrDefault(); if (LatestScreenshot != null) { lock (_actionlock) { ShowPreviewEvent(LatestScreenshot, new PreviewEventArgs() { ActionItem = CurrentActionItem }); Monitor.Wait(_actionlock); DoActionItem(ActiveShortcutItem, itemIndex + 1); } } } break; case ActionTypes.Save: { var LatestScreenshot = History.LastOrDefault(); if (LatestScreenshot != null) { var Save = CurrentActionItem as SaveAction; Trace.WriteLine(string.Format("Prompting for save: {0}", Save.Prompt), string.Format("Program.DoActionItem Save [{0}]", Thread.CurrentThread.Name)); if (Save.Prompt) { lock (_actionlock) { ShowPreviewEvent(LatestScreenshot, new PreviewEventArgs() { ActionItem = CurrentActionItem }); Monitor.Wait(_actionlock); DoActionItem(ActiveShortcutItem, itemIndex + 1); } } else { string FileName = Environment.ExpandEnvironmentVariables(Helper.ExpandParameters(Save.FilePath, LatestScreenshot)); if (!string.IsNullOrEmpty(FileName)) { try { var dir = Path.GetDirectoryName(FileName); if (!Directory.Exists(dir)) { Trace.WriteLine("Attempting to create directory...", string.Format("Program.DoActionItem Save [{0}]", Thread.CurrentThread.Name)); Directory.CreateDirectory(dir); } LatestScreenshot.ComposedScreenshotImage.Save(FileName, Helper.ExtToImageFormat(Path.GetExtension(FileName))); LatestScreenshot.SavedFileName = FileName; } catch (Exception e) { MessageBox.Show(string.Format("There was a problem saving your screenshot:\r\n\r\n{0}", e.GetBaseException().Message), "ProSnap", MessageBoxButtons.OK, MessageBoxIcon.Error, MessageBoxDefaultButton.Button1); } } else { MessageBox.Show(string.Format("There was a problem saving your screenshot:\r\n\r\n{0}", "No file name has been provided."), "ProSnap", MessageBoxButtons.OK, MessageBoxIcon.Error, MessageBoxDefaultButton.Button1); } DoActionItem(ActiveShortcutItem, itemIndex + 1); } } } break; case ActionTypes.Upload: { var LatestScreenshot = History.LastOrDefault(); if (LatestScreenshot != null) { lock (_actionlock) { ShowPreviewEvent(LatestScreenshot, new PreviewEventArgs() { ActionItem = CurrentActionItem }); Monitor.Wait(_actionlock); DoActionItem(ActiveShortcutItem, itemIndex + 1); } } } break; case ActionTypes.ApplyEdits: { var LatestScreenshot = History.LastOrDefault(); if (LatestScreenshot != null) { var CurrentActionConfig = CurrentActionItem as ApplyEditsAction; switch (CurrentActionConfig.DefaultBorderRounding) { case ApplyEditsAction.ApplicationMode.Automatic: LatestScreenshot.withBorderRounding = !LatestScreenshot.isMaximized && LatestScreenshot.isRounded && !(FMUtils.WinApi.Helper.OperatingSystem == FMUtils.WinApi.Helper.OperatingSystems.Win8); break; case ApplyEditsAction.ApplicationMode.On: LatestScreenshot.withBorderRounding = true; break; case ApplyEditsAction.ApplicationMode.Off: LatestScreenshot.withBorderRounding = false; break; } switch (CurrentActionConfig.DefaultBorderShadow) { case ApplyEditsAction.ApplicationMode.Automatic: LatestScreenshot.withBorderShadow = !LatestScreenshot.isMaximized; break; case ApplyEditsAction.ApplicationMode.On: LatestScreenshot.withBorderShadow = true; break; case ApplyEditsAction.ApplicationMode.Off: LatestScreenshot.withBorderShadow = false; break; } switch (CurrentActionConfig.ShowMouseCursor) { case ApplyEditsAction.ApplicationMode.Automatic: LatestScreenshot.withCursor = LatestScreenshot.CompositionRect.Contains(LatestScreenshot.CursorLocation); break; case ApplyEditsAction.ApplicationMode.On: LatestScreenshot.withCursor = true; break; case ApplyEditsAction.ApplicationMode.Off: LatestScreenshot.withCursor = false; break; } } DoActionItem(ActiveShortcutItem, itemIndex + 1); } break; case ActionTypes.Run: { var LatestScreenshot = History.LastOrDefault(); if (LatestScreenshot != null) { var Run = CurrentActionItem as RunAction; if (!File.Exists(LatestScreenshot.InternalFileName)) { Directory.CreateDirectory(Path.GetDirectoryName(LatestScreenshot.InternalFileName)); LatestScreenshot.ComposedScreenshotImage.Save(LatestScreenshot.InternalFileName, ImageFormat.Png); } var parameters = Helper.ExpandParameters(Run.Parameters, LatestScreenshot).Replace(":file", LatestScreenshot.InternalFileName); var working = Environment.ExpandEnvironmentVariables(Helper.ExpandParameters(Run.WorkingDirectory, LatestScreenshot)); switch (Run.Mode) { case RunAction.Modes.ShellVerb: { Windowing.ShellExecute(IntPtr.Zero, Run.ShellVerb, LatestScreenshot.InternalFileName, parameters, working, Windowing.ShowCommands.SW_NORMAL); } break; case RunAction.Modes.FilePath: { var psi = new ProcessStartInfo(Environment.ExpandEnvironmentVariables(Helper.ExpandParameters(Run.ApplicationPath, LatestScreenshot)), parameters) { UseShellExecute = false }; if (!string.IsNullOrEmpty(working)) psi.WorkingDirectory = working; Process.Start(psi); } break; } } DoActionItem(ActiveShortcutItem, itemIndex + 1); } break; case ActionTypes.Delete: { var LatestScreenshot = History.LastOrDefault(); if (LatestScreenshot != null) { lock (_actionlock) { ShowPreviewEvent(LatestScreenshot, new PreviewEventArgs() { ActionItem = CurrentActionItem }); Monitor.Wait(_actionlock); DoActionItem(ActiveShortcutItem, itemIndex + 1); } } } break; case ActionTypes.HidePreview: { lock (_actionlock) { ShowPreviewEvent(null, new PreviewEventArgs() { ActionItem = CurrentActionItem }); Monitor.Wait(_actionlock); DoActionItem(ActiveShortcutItem, itemIndex + 1); } } break; case ActionTypes.BeginScrollingScreenshot: { isTakingScrollingScreenshot = true; timelapse.Clear(); timelapse.Add(new ExtendedScreenshot()); DoActionItem(ActiveShortcutItem, itemIndex + 1); } break; case ActionTypes.ContinueScrollingScreenshot: { if (isTakingScrollingScreenshot) timelapse.Add(new ExtendedScreenshot()); DoActionItem(ActiveShortcutItem, itemIndex + 1); } break; case ActionTypes.EndScrollingScreenshot: { if (isTakingScrollingScreenshot) { isTakingScrollingScreenshot = false; Bitmap final = null; Bitmap b = new Bitmap(timelapse.First().BaseScreenshotImage.Width, timelapse.First().BaseScreenshotImage.Height * timelapse.Count); Graphics g = Graphics.FromImage(b); int yoffset = 0; int working_height = 0; int ignore_header = 100; int ignore_footer = 10; var offsets = new Dictionary<int, int>(); //foreach (var ss in timelapse) for (int c = 0; c < timelapse.Count; c++) { var debug = Path.Combine(Environment.GetFolderPath(Environment.SpecialFolder.Desktop), @"prosnap-debug"); if (Directory.Exists(debug)) { timelapse[c].BaseScreenshotImage.Save(Path.Combine(debug, @"ss-" + c + ".png"), ImageFormat.Png); } if (c == 0) { g.DrawImageUnscaled(timelapse[c].BaseScreenshotImage, new Point(0, 0)); working_height = timelapse[c].BaseScreenshotImage.Height; offsets.Add(c, timelapse[c].BaseScreenshotImage.Height); } else { int old_yoffset = yoffset; yoffset = GetYOffset(timelapse[c - 1].BaseScreenshotImage, timelapse[c].BaseScreenshotImage, ignore_header, ignore_footer, old_yoffset, working_height, c); offsets.Add(c, yoffset); int sum = 0; if (offsets.Count > 0) for (int k = offsets.First().Key; k <= offsets.Last().Key; k++) sum += offsets[k]; working_height = sum; g.DrawImage(timelapse[c].BaseScreenshotImage, new RectangleF(0, working_height - (timelapse[c].BaseScreenshotImage.Height - ignore_header), timelapse[c].BaseScreenshotImage.Width, timelapse[c].BaseScreenshotImage.Height - ignore_header), new RectangleF(0, ignore_header, timelapse[c].BaseScreenshotImage.Width, timelapse[c].BaseScreenshotImage.Height - ignore_header), GraphicsUnit.Pixel); } g.Flush(); g.Save(); final = new Bitmap(b.Width, working_height); Graphics gfinal = Graphics.FromImage(final); gfinal.DrawImage(b, new Rectangle(0, 0, final.Width, final.Height), new Rectangle(0, 0, b.Width, working_height), GraphicsUnit.Pixel); gfinal.Flush(); gfinal.Save(); if (Directory.Exists(debug)) { final.Save(Path.Combine(debug, @"final-" + c + ".png"), System.Drawing.Imaging.ImageFormat.Png); } } timelapse.First().ReplaceWithBitmap(final); History.Add(timelapse.First()); DoActionItem(ActiveShortcutItem, itemIndex + 1); } else { //Must call, so that the action chain is properly ended DoActionItem(null, 0); } } break; } }