[
  {
    "path": ".ai/ReadMe.md",
    "content": "## Files\n\n- `developer-info.md` - Developer information about the solution, which will be used when creating the repository-analysis.instructions.md file.\n- `instructions.md` - Main AI Agent System Message instructions used by AI Agents, like CLINE or CoPilot.\n- `repository-analysis.instructions.md` - Autogenerated Repository Analysis file that is supplied to the AI Agent as a System message after instructions.md.\n- `repository-analysis.prompt.md` - User Message prompt that instructs AI to create repository-analysis.instructions.md.\n- `Update-AgentInstructions.ps1` - Sets all `*instruction.md` files as AI agent system message instruction files. Used by AI agents, like CLINE or Copilot.\n\n## Custom instructions: GitHub Copilot\n\nWith GitHub Copilot, you can receive chat responses tailored to your team'ss workflow, preferred tools, and project specifics.\nInstead of adding this contextual detail to each chat query, you can create a file that supplies this information automatically.\nWhile this additional context won't appear in the chat, it is available to GitHub Copilot, allowing it to generate more accurate and relevant responses.\n\n**How to Enable Custom Instructions**\n\nEnable the feature via Tools > Options > GitHub > Copilot > and check (Preview) Enable custom instructions to be loaded from .github/copilot-instructions.md files and added to requests.\nAdd copilot-instruction.md in the root of your respository inside the .github file, create the file if it doesn't already exist.\nGitHub Copilot Enable Custom Instructions\n\nLearn more about creating custom instructions:\nhttps://docs.github.com/en/enterprise-cloud@latest/copilot/customizing-copilot/adding-custom-instructions-for-github-copilot\n\n## Custom instructions: CLINE\n\nAll CLINE instructions are stored inside the .clinerules\\ folder.\nAll \\*.md files will be loaded as system messages.\n\n## Custom instructions: OpenAI Codex\n\nInstructions are stored in `%USERPROFILE%\\.codex\\instructions.md` file.\n\n\n## User Instructions to create `repository-analysis.md`\n\nNote: Switch AI agent to `Plan` mode before submitting this message. This will yield better results.\n\n\n### LLM Selection Guideline\n\nIf your deployment environment allows, prefer a newer, higher-capacity model—such as Anthropic Claude Sonnet 4 or Google Gemini 2.5 Pro - instead of the current OpenAI GPT 4 lineup.  These frontier models typically deliver stronger reasoning, broader context windows, and more consistent output quality on complex tasks.\n\n### Install the Mermaid preview plug-in\n\n\t1. Open Extensions (Ctrl + Shift + X).\n\t2. Search: Markdown Preview Mermaid Support (publisher: Matt Bierner).\n\t3. Click Install – or hit Ctrl + P.\n\t\n### Open Preview\n\n\t1. Open the menu on the tab header of the file.\n\t2. Click \"Open Preview\" – or hit Ctrl + Shift + V.\n\t\n### Re Open Preview\n\n\t1. Open the menu on the tab header of the file.\n\t2. Click \"Reopen Editor With...\"\n\t3. Click \"Markdown Preview (Built-In)\"\n"
  },
  {
    "path": ".ai/coding-guideline.instructions.md",
    "content": "﻿# Coding Guidelines\r\n\r\n- If the qdrant-mcp-server is running, use it for all permanent memory operations (e.g. storing user information).\r\n- After making changes, ALWAYS start a new server for testing.\r\n- Kill all existing related servers from previous testing before starting a new server.\r\n- Prefer the simplest viable solution; avoid over-engineering.\r\n- Do not add broad try/catch or wrapper layers unless required by a failing test or explicit requirement; if you catch, rethrow to preserve the stack.\r\n- Before writing new code, actively look for existing utilities or functions that can be reused instead of duplicated.\r\n- New helper methods or classes must be justified with a clear, documented need for functionality that is unavailable elsewhere in the codebase.\r\n- Always iterate on and reuse existing code instead of creating new implementations.\r\n- Avoid adding layers of abstraction that do not deliver clear value.\r\n- Do not drastically change established patterns before iterating on them.\r\n- No duplication / SSOT: update or move existing code instead of adding parallel implementations. If you introduce a replacement, remove the old one **in the same change**.\r\n- Write code that accounts for different environments (dev, test, and prod).\r\n- Only modify what is explicitly requested or clearly necessary; do **not** create new files or modules unless explicitly requested.\r\n- When fixing bugs, exhaust current implementations before introducing new patterns; if new methods are used, remove the old ones.\r\n- Keep the codebase clean and organized.\r\n- Avoid one-off scripts unless absolutely necessary.\r\n- Use mocks only for tests, not for dev or prod.\r\n- Never add stubbing or fake data in dev or prod environments.\r\n- Never overwrite the .env file without explicit confirmation.\r\n- Focus solely on areas relevant to the task; leave unrelated code untouched.\r\n- Write thorough tests for all major functionality.\r\n- Avoid major changes to the existing architecture unless explicitly instructed.\r\n- Always consider the impact on other methods and areas of the code.\r\n- Prefer to wrap long lines for better readability.\r\n- Preserve existing formatting; limit formatting to lines you changed and match surrounding style. Also remove any unused imports/usings or dead code **introduced by your edits**.\r\n- Stability primitives (repeatability > cleverness): when the repo provides an established way to perform an operation (repo-owned script, documented command snippet, standard PS1 under .ai/Scripts), treat it as the single source of truth. Use it verbatim instead of synthesizing “equivalent” commands. Only deviate when explicitly asked or when the primitive is proven broken in this environment (and then fix the primitive, not invent a parallel path).\r\n- No code file that you **create or modify** may exceed **6000 tokens (~24 KB)** once your changes are applied.\r\n  - If your changes alone would push the file past this limit, either trim the change or ask for explicit permission to refactor; do **not** alter unrelated code solely to meet the limit.\r\n  - Existing oversized files are left untouched unless the user explicitly requests a refactor.\r\n\r\n## Source Control Conventions\r\n\r\nNaming conventions for issues, branches, and pull requests.\r\n\r\n### Categories\r\n\r\n| Category | Use for |\r\n|----------|---------|\r\n| `FEAT`   | New features |\r\n| `FIX`    | Bug fixes |\r\n| `TECH`   | Infrastructure, dependencies, refactoring |\r\n| `DOCS`   | Documentation |\r\n\r\n### Naming Patterns\r\n\r\n| Item       | Pattern | Example |\r\n|------------|---------|---------|\r\n| **Issue**  | `{CATEGORY}: {Description}` | `FEAT: Download logs to CSV` |\r\n| **Branch** | `{CATEGORY}-{issue#}-{lowercase-dashed-name}` | `FEAT-21-download-logs-to-csv` |\r\n| **PR**     | `PR: #{issue#}: {CATEGORY}: {Description}` | `PR: #21: FEAT: Download logs to CSV` |\r\n\r\n- Branch names: lowercase, words separated by dashes, non-ASCII replaced with a single dash, derived from issue title but may be shortened.\r\n- PR body must reference the issue with `Closes #{issue#}`.\r\n- Merge via PR only — no direct pushes to `main` or `master`.\r\n- Feature branches are always created from `main` or `master`. Never merge one feature branch into another — only merge `main` if you need to catch up.\r\n- Always create branches from latest origin/main or origin/main, never merge sub-branches, confirm before risky git ops.\r\n- Do not add Co-Authored-By {AI model}/{AI company} lines to commits.\r\n- Never close issues before a release is published with the fix.\r\n\r\nUse the following guidelines:\r\n\r\n1. Doc Comment Enhancement for IntelliSense\r\n\r\n    - Replace or augment simple comments with relevant doc comment syntax that is supported by IntelliSense as needed.\r\n    - Preserve the original intent and wording of existing comments wherever possible.\r\n\r\n2. Code Layout for Clarity\r\n\r\n    - Place the most important or user-editable sections at the top if logically appropriate.\r\n    - Insert headings or separators within the code to clearly delineate where customizations or key logic sections can be adjusted.\r\n\r\n3. No Extraneous Code Comments\r\n\r\n    - Do not include \"one-off\" or user-directed commentary in the code.\r\n    - Confine all clarifications or additional suggestions to explanations outside of the code snippet.\r\n\r\n4. Avoid Outdated or Deprecated Methods\r\n\r\n    - Refrain from introducing or relying on obsolete or deprecated methods and libraries.\r\n    - If the current code relies on potentially deprecated approaches, ask for clarification or provide viable, modern alternatives that align with best practices.\r\n\r\n5. Testing and Validation\r\n\r\n    - Suggest running unit tests or simulations on the modified segments to confirm that the changes fix the issue without impacting overall functionality.\r\n    - Ensure that any proposed improvements, including doc comment upgrades, integrate seamlessly with the existing codebase.\r\n    - After all code modifications, navigate to the affected project directory and build C# then Angular to confirm the application compiles without errors:\r\n\t\tcd {PROJECT} && dotnet build {PROJECT}.csproj\r\n\t\tcd {PROJECT}/ClientApp && ng build\r\n    - Run relevant unit tests if code changes affect core logic.\r\n    - If the developer certificate is not trusted, then execute: dotnet dev-certs https --trust\r\n    - To launch project use: dotnet watch run --project {PROJECT}/{PROJECT}.csproj --launch-profile \"{PROJECT} (NG Build)\"\r\n\r\n6. Rationale and Explanation\r\n\r\n    - For every change (including comment conversions), provide a concise explanation detailing how the modification resolves the identified issue while preserving the original design and context.\r\n    - Clearly highlight only the modifications made, ensuring that no previously validated progress is altered.\r\n    - NOTE: Summarize reasoning for the user, but do NOT expose full chain-of-thought. Keep internal deliberations internal; surface only the concise rationale needed to justify each change.\r\n\r\n7. Contextual Analysis\r\n\r\n    - Use all available context—such as code history, inline documentation, style guidelines—to understand the intended functionality.\r\n    - When inspecting an existing file for understanding, prefer reading the whole file in a\r\n      single `read_file` call when it comfortably fits in context; switch to targeted slices\r\n      only when the file is too large, the tool truncates it, or a specific anchor line is\r\n      already known.\r\n    - If the role or intent behind a code segment is ambiguous, ask for clarification rather than making assumptions.\r\n\r\n8. Targeted, Incremental Changes\r\n\r\n    - Identify and isolate only the problematic code segments (including places where IntelliSense doc comments can replace simple comments).\r\n    - Provide minimal code snippets that address the issue without rewriting larger sections.\r\n    - For each suggested code change, explicitly indicate the exact location in the code (e.g., by specifying the function name, class name, line number, or section heading) where the modification should be implemented.\r\n\r\n9. Preservation of Context\r\n\r\n    - Maintain all developer comments, annotations, and workarounds exactly as they appear, transforming them to doc comment format only when it improves IntelliSense support.\r\n    - Do not modify or remove any non-code context unless explicitly instructed.\r\n    - Avoid introducing new, irrelevant comments in the code.\r\n\r\n10. Launching {PROJECT} Correctly:\r\n    - Navigate to the {PROJECT} project folder.\r\n    - Run the following command to launch the project with live reload and proper debugging configuration:\r\n      dotnet watch run --launch-profile \"{PROJECT} (NG Build)\" --project {PROJECT}/{PROJECT}.csproj\r\n    - This command will start the {PROJECT} project on the designated debugging session URL.\r\n    - Ensure that any previous {PROJECT} instances are terminated before running this command.\r\n"
  },
  {
    "path": ".ai/instructions.md",
    "content": "﻿## Role\n\nYour role is to analyze and improve code by making only localized, targeted changes. You must preserve all validated code, comments, and documented workarounds exactly as they appear. Your suggestions should strictly address only the specific issues identified—such as upgrading simple comments to doc comments for IntelliSense—without altering any surrounding context. Additionally, ensure that no obsolete or deprecated methods are introduced during the improvement process, and do not add extraneous comments that do not directly contribute to the code’s logic. Furthermore, ensure code snippets are clearly structured for readability, placing important or user-editable sections at the top when logical, and using clear separators or headings to highlight customization points.\nWherever beneficial, convert simple comments into recognized documentation comment syntax (e.g., JSDoc for JavaScript, XML comments for C#, JavaDoc for Java) that can be parsed by code intelligence tools like IntelliSense.\nMaintain the original meaning of these comments, but structure them in a way that provides maximum benefit for automated tools and refactoring methods.\nApply chain-of-thought reasoning to identify code segments best served by doc comments, analyze the existing context of each comment, and then make precise, incremental modifications that enhance IntelliSense compatibility while preserving existing functionality.\n\n## Output\n\nWrap any and all code—including regular code snippets, inline code segments, outputs, pseudocode, or any text that represents code—in Markdown code blocks with a language identifier (e.g., ```typescript, ```powershell).\n"
  },
  {
    "path": ".ai/repository-analysis.instructions.md",
    "content": "# Repository Analysis\n\n## 1. Repository Overview\n\nThis document provides a factual reference for the Jocys.com FocusLogger repository, aimed at developers and AI coding agents working on the codebase.\n\n**FocusLogger** is a Windows desktop utility that monitors and logs which process or program takes window focus. It targets users (especially gamers and power users) who experience unexpected focus stealing — where a background process briefly grabs foreground focus, interrupting gameplay or work. The tool logs every focus change with timestamps, process details, window class names, and focus-state flags, allowing users to identify the culprit.\n\n- **Repository:** https://github.com/JocysCom/FocusLogger\n- **License:** GNU General Public License v3.0\n- **Current version:** 1.2.6\n- **Target platform:** Windows 10+ with .NET 8.0\n- **Primary audiences:** Gamers, power users, IT support personnel diagnosing focus-stealing issues.\n\n## 2. Top-Level Structure\n\nThis section maps every top-level directory and file to help navigate the repository quickly.\n\n| Path | Purpose |\n|------|---------|\n| `FocusLogger/` | Main application project (WPF, .NET 8.0). Contains all app source code, shared library, and resources. |\n| `FocusLogger.Tests/` | MSTest test project. Unit tests for CSV export and UI automation tests. |\n| `Documents/` | Release engineering: signing scripts, zip packaging scripts, screenshot tooling, and pre-built release files. |\n| `Resources/` | Solution-level shared scripts (currently `ZipFiles.ps1` for checksum-aware zip packaging). |\n| `.ai/` | AI agent instructions, coding guidelines, repository analysis, and skills. |\n| `JocysCom.FocusLogger.slnx` | Solution file (XML-based `.slnx` format) referencing the two projects. |\n| `README.md` | Project overview, download link, system requirements, screenshot. |\n| `LICENSE` | GPLv3 license text. |\n| `SECURITY.md` | Security vulnerability reporting policy (support@jocys.com). |\n| `Settings.XamlStyler` | XamlStyler configuration for consistent XAML formatting. |\n| `Solution_Cleanup.ps1` | PowerShell script for cleaning build artifacts. |\n\n## 3. Technology Stack & Key Dependencies\n\nThis section lists verified technologies and versions drawn from project files.\n\n| Technology | Version / Detail | Evidence |\n|------------|-----------------|----------|\n| .NET | 8.0 (`net8.0-windows`) | `JocysCom.FocusLogger.csproj` TargetFramework |\n| C# | Implicit (SDK default for .NET 8) | SDK-style project |\n| WPF | `<UseWPF>true</UseWPF>` | csproj |\n| Windows Forms interop | `<UseWindowsForms>true</UseWindowsForms>` | csproj — used for P/Invoke helpers and DPI awareness |\n| MSTest | v3.x (`MSTest.TestFramework 3.*`, `MSTest.TestAdapter 3.*`) | Test csproj PackageReference |\n| Microsoft.NET.Test.Sdk | 17.x | Test csproj PackageReference |\n| Windows API (user32.dll) | P/Invoke | `NativeMethods.cs` |\n| PowerShell | Scripts for build, sign, zip, cleanup | `Documents/`, `Resources/`, root |\n| XamlStyler | Config present | `Settings.XamlStyler` |\n\n**No NuGet package dependencies** in the main application project — all functionality comes from .NET SDK and the embedded `JocysCom.ClassLibrary`.\n\n## 4. Architecture & Runtime Model\n\nThis section describes how the application is structured and how it operates at runtime.\n\nFocusLogger is a **single-executable WPF desktop application** that polls Windows API functions to detect focus changes. It does not persist log data between sessions (in-memory only) but provides CSV export for offline analysis.\n\n### Architectural layers\n\n```mermaid\ngraph TD\n    subgraph UI[\"UI Layer (WPF)\"]\n        App[\"App.xaml.cs<br/>Entry point, DPI aware\"]\n        MW[\"MainWindow.xaml.cs<br/>Main window frame\"]\n        DLC[\"DataListControl.xaml.cs<br/>Core logging UI + logic\"]\n    end\n\n    subgraph Core[\"Core Logic\"]\n        DI[\"DataItem.cs<br/>Log entry model\"]\n        DIT[\"DataItemType.cs<br/>Entry type enum\"]\n        NM[\"NativeMethods.cs<br/>P/Invoke declarations\"]\n        CSV[\"CSV Export<br/>BuildCsvContent, CsvEscape\"]\n    end\n\n    subgraph Shared[\"JocysCom.ClassLibrary (embedded)\"]\n        Config[\"Configuration<br/>SettingsData, SettingsItem, AssemblyInfo\"]\n        CompModel[\"ComponentModel<br/>SortableBindingList, BindingListInvoked\"]\n        Controls[\"Controls<br/>ControlsHelper, ItemFormattingConverter,<br/>InfoControl, MessageBoxWindow\"]\n        Other[\"Collections, IO, Text, Runtime, Data\"]\n    end\n\n    subgraph WinAPI[\"Windows OS\"]\n        User32[\"user32.dll\"]\n    end\n\n    App --> MW --> DLC\n    DLC --> DI\n    DLC --> NM\n    DLC --> CSV\n    NM --> User32\n    DI --> Config\n    DLC --> CompModel\n    DLC --> Controls\n    MW --> Controls\n```\n\n### Key architectural decisions\n\n- **Polling via timer:** A `System.Timers.Timer` with 1ms interval (non-auto-reset) continuously polls `GetActiveWindow()` and `GetForegroundWindow()`. Duplicate events are suppressed via `DataItem.IsSame()`.\n- **Thread safety:** Timer fires on a thread-pool thread; UI updates are marshalled via `ControlsHelper.BeginInvoke()`. A `lock(AddLock)` synchronizes the polling logic.\n- **Embedded shared library:** `JocysCom.ClassLibrary` files are included directly in `FocusLogger/JocysCom/` rather than as a compiled DLL or NuGet package.\n- **No MVVM framework:** Code-behind pattern with data binding. `DataListControl.xaml.cs` contains both view-model-like logic and model interaction.\n\n## 5. Project Inventory\n\nThis section lists each project in the solution with its key metadata.\n\n### 5.1 JocysCom.FocusLogger (main application)\n\n| Property | Value |\n|----------|-------|\n| Path | `FocusLogger/JocysCom.FocusLogger.csproj` |\n| Output type | `WinExe` |\n| Target framework | `net8.0-windows` |\n| Assembly name | `JocysCom.FocusLogger` |\n| Description | Find out which process or program is taking the window focus. In game, mouse and keyboard could temporarily stop responding if another program takes the focus. This tool could help diagnose which program is stealing the focus. |\n| Version | 1.2.6 |\n| NuGet dependencies | None |\n| Embedded resources | `Resources/BuildDate.txt` (auto-generated), `Resources/AiAnalysisPrompt.md` |\n\n**Source structure:**\n\n| Directory | Contents |\n|-----------|----------|\n| `FocusLogger/` (root) | `App.xaml(.cs)`, `MainWindow.xaml(.cs)`, `AssemblyInfo.cs`, `App.ico` |\n| `FocusLogger/Common/` | `DataItem.cs`, `DataItemType.cs`, `NativeMethods.cs` |\n| `FocusLogger/Controls/` | `DataListControl.xaml(.cs)` — core logging control |\n| `FocusLogger/JocysCom/` | Embedded `JocysCom.ClassLibrary` (~30 files across Collections, Common, ComponentModel, Configuration, Controls, Data, IO, Runtime, Text) |\n| `FocusLogger/Resources/` | `AiAnalysisPrompt.md`, `BuildDate.txt`, `Icons/` (SVG sources, XAML icons, conversion scripts) |\n| `FocusLogger/Properties/` | Publish profiles |\n\n### 5.2 JocysCom.FocusLogger.Tests (test project)\n\n| Property | Value |\n|----------|-------|\n| Path | `FocusLogger.Tests/JocysCom.FocusLogger.Tests.csproj` |\n| Target framework | `net8.0-windows` |\n| Test framework | MSTest v3.x |\n| Project reference | `FocusLogger/JocysCom.FocusLogger.csproj` |\n\n**Test files:**\n\n| File | Purpose |\n|------|---------|\n| `CsvExportTests.cs` | Unit tests for `CsvEscape` and `BuildCsvContent` methods |\n| `UIAutomationTests.cs` | UI automation tests using `System.Windows.Automation` — launches the built app and interacts with controls by AutomationId |\n\nRun tests with: `dotnet test FocusLogger.Tests/JocysCom.FocusLogger.Tests.csproj`\n\n## 6. Dependency & Data Flow\n\nThis section explains how the projects and components relate to each other and how data moves through the system.\n\n### Project dependency graph\n\n```mermaid\ngraph LR\n    Tests[\"FocusLogger.Tests<br/>(MSTest v3)\"] -->|ProjectReference| App[\"JocysCom.FocusLogger<br/>(WPF App)\"]\n    App -->|embedded source| Shared[\"JocysCom.ClassLibrary<br/>(in FocusLogger/JocysCom/)\"]\n    App -->|P/Invoke| WinAPI[\"user32.dll\"]\n```\n\n### Runtime data flow\n\n1. `System.Timers.Timer` fires (1ms interval, non-auto-reset).\n2. `DataListControl.UpdateInfo()` acquires `AddLock`.\n3. Calls `NativeMethods.GetActiveWindow()` and `NativeMethods.GetForegroundWindow()`.\n4. For each handle, `GetItemFromHandle()` creates a `DataItem` with timestamp, focus flags (mouse/keyboard/caret), window title, and window class name.\n5. `IsSame()` checks if the event differs from the previous one; if not, it is skipped.\n6. `UpdateFromProcess()` enriches the `DataItem` with process name and path (with error handling for restricted processes).\n7. The item is inserted at position 0 of `SortableBindingList<DataItem>` via `ControlsHelper.BeginInvoke()` (UI thread dispatch).\n8. The WPF `DataGrid` updates via data binding. `ItemFormattingConverter` translates boolean flags to icons.\n\n### CSV export flow\n\n1. User clicks \"Save CSV\" button.\n2. `SaveFileDialog` prompts for file location.\n3. `BuildCsvContent()` iterates all `DataItem` entries, writing CSV with headers: Date, PID, Process Name, Active, Mouse, Keyboard, Caret, Window Title, Window Class, Path.\n4. File is written as UTF-8.\n5. \"Explore\" button opens the saved file location in Explorer.\n6. \"AI Prompt Example\" button shows the embedded `AiAnalysisPrompt.md` in a `MessageBoxWindow` for users to copy and paste into an AI assistant along with their CSV.\n\n## 7. Build, Test, CI/CD & Operational Workflows\n\nThis section documents how the project is built, tested, and released based on repository evidence.\n\n### Build\n\n```bash\ndotnet build JocysCom.FocusLogger.slnx\n```\n\n- **Pre-build event:** Generates `Resources/BuildDate.txt` with the current ISO 8601 timestamp via PowerShell.\n- **Output:** Single `JocysCom.FocusLogger.exe` in `bin/{Configuration}/net8.0-windows/`.\n- **Debug configuration:** Embedded PDB symbols (`DebugType: embedded`).\n\n### Test\n\n```bash\ndotnet test FocusLogger.Tests/JocysCom.FocusLogger.Tests.csproj\n```\n\n- **Framework:** MSTest v3.x with Microsoft.NET.Test.Sdk 17.x.\n- **Unit tests:** `CsvExportTests` — validates CSV escaping and content generation.\n- **UI automation tests:** `UIAutomationTests` — launches the built application and interacts via `System.Windows.Automation`. Requires a prior build of the main project.\n\n### Release / packaging scripts\n\n| Script | Purpose |\n|--------|---------|\n| `Documents/App_1_Sign.ps1` | Code-signs the application executable. |\n| `Documents/App_2_Zip.ps1` | Packages the signed executable into a release ZIP. |\n| `Resources/ZipFiles.ps1` | Shared utility for checksum-aware ZIP creation (compares source/dest checksums before rebuilding). |\n| `Documents/Take_Screenshot.ps1` | Captures application screenshot for documentation. |\n| `Documents/Take_Screenshot.ps1.cs` | C# helper compiled by the screenshot script. |\n| `Solution_Cleanup.ps1` | Cleans `bin/`, `obj/`, and other build artifacts. |\n\n### Icon workflow\n\nSVG icon sources are stored in `FocusLogger/Resources/Icons/Icons_Default/`. The script `Convert_SVG_to_XAML.ps1` converts them to XAML resource dictionaries (`Icons_Default.xaml`).\n\n### CI/CD\n\nNo CI/CD workflow files were found under `.github/workflows/`. Builds and releases appear to be performed locally.\n\n## 8. Documentation Map\n\nThis section identifies where documentation lives in the repository.\n\n| Location | Audience | Content |\n|----------|----------|---------|\n| `README.md` | End users, contributors | Project overview, download link, system requirements, screenshot |\n| `SECURITY.md` | Security researchers | Vulnerability reporting policy |\n| `LICENSE` | All | GPLv3 full text |\n| `.ai/ReadMe.md` | AI agents, developers | Explains the purpose of each file in the `.ai/` directory and how custom instructions work for Copilot, CLINE, and Codex |\n| `.ai/instructions.md` | AI agents | Role definition and output formatting rules for AI-assisted edits |\n| `.ai/coding-guideline.instructions.md` | AI agents | Detailed coding guidelines, source control conventions (branch/PR naming), testing workflow, and constraints for AI agents |\n| `.ai/repository-analysis.instructions.md` | AI agents, developers | This file — comprehensive repository reference |\n| `.ai/skills/` | AI agents | Skill definitions (e.g., `ai-self-improvement`) for agent-assisted workflows |\n| `FocusLogger/Resources/AiAnalysisPrompt.md` | End users | Prompt template for users to paste into AI assistants alongside exported CSV logs |\n| `Documents/Images/` | README, users | Application screenshot |\n| `Settings.XamlStyler` | Developers | XamlStyler formatting configuration |\n\n### Documentation taxonomy\n\n```mermaid\ngraph TD\n    subgraph EndUsers[\"End-User Documentation\"]\n        README[\"README.md<br/>Overview, download, requirements\"]\n        SECURITY[\"SECURITY.md<br/>Vulnerability reporting\"]\n        LICENSE[\"LICENSE<br/>GPLv3\"]\n        AiPrompt[\"FocusLogger/Resources/<br/>AiAnalysisPrompt.md<br/>Prompt template for CSV analysis\"]\n        Screenshot[\"Documents/Images/<br/>Application screenshot\"]\n    end\n\n    subgraph AIAgents[\"AI Agent Instructions\"]\n        AiReadMe[\".ai/ReadMe.md<br/>Directory guide\"]\n        AiInstr[\".ai/instructions.md<br/>Role & output rules\"]\n        AiCoding[\".ai/coding-guideline<br/>.instructions.md<br/>Coding & SCM conventions\"]\n        AiRepo[\".ai/repository-analysis<br/>.instructions.md<br/>This file\"]\n        AiSkills[\".ai/skills/<br/>Agent skill definitions\"]\n    end\n\n    subgraph DevConfig[\"Developer Configuration\"]\n        XamlStyler[\"Settings.XamlStyler<br/>XAML formatting\"]\n    end\n```\n\n## 9. AI-Agent-Relevant Conventions and Constraints\n\nThis section captures rules and patterns that materially affect automated edits. The full set of coding and source-control conventions is defined in `.ai/coding-guideline.instructions.md`; the highlights below are the items most likely to cause mistakes if overlooked.\n\n1. **Coding style:** Follow Microsoft C# conventions. PascalCase for public members, camelCase for locals. Some private fields use `_PascalCase` (e.g., `_Date`). Preserve existing naming patterns in each file. Prefer the simplest viable solution; avoid over-engineering.\n\n2. **Doc comments:** Per `.ai/instructions.md`, convert simple comments to XML documentation comments where beneficial for IntelliSense. Do not alter surrounding code when doing so.\n\n3. **Shared library files (`FocusLogger/JocysCom/`):** These are embedded from a shared `JocysCom.ClassLibrary`. Exercise caution when editing — changes here may diverge from the upstream library.\n\n4. **XAML formatting:** The repository uses XamlStyler (see `Settings.XamlStyler`). XAML edits should conform to the configured style.\n\n5. **No NuGet packages in the main app:** All dependencies are framework-provided or embedded source. Do not introduce NuGet package dependencies without explicit approval.\n\n6. **Test project uses MSTest v3:** New tests should follow MSTest v3 patterns (`[TestClass]`, `[TestMethod]`, `Assert.*`).\n\n7. **UI automation tests depend on a built executable:** `UIAutomationTests` locate the app at a relative path from the test output. Building the main project before running these tests is required.\n\n8. **Pre-build event:** The csproj generates `Resources/BuildDate.txt` via PowerShell. This file should not be manually edited or committed.\n\n9. **Solution format:** Uses `.slnx` (XML-based solution format), not the older `.sln` text format.\n\n10. **No CI/CD pipelines:** All build and release steps are manual/local. Scripts in `Documents/` handle signing and packaging.\n\n11. **File size limit:** No code file that is created or modified may exceed 6000 tokens (~24 KB).\n\n12. **Source control conventions:** Issues use `{CATEGORY}: {Description}` (FEAT, FIX, TECH, DOCS). Branches use `{CATEGORY}-{issue#}-{lowercase-dashed-name}`. PRs use `PR: #{issue#}: {CATEGORY}: {Description}` and must reference the issue with `Closes #{issue#}`. Merge via PR only — no direct pushes to `main`.\n\n13. **No Co-Authored-By lines:** Do not add `Co-Authored-By` AI model/company lines to commits.\n\n14. **Issue closure:** Never close issues before a release is published with the fix.\n\n15. **No duplication / SSOT:** Update or move existing code instead of adding parallel implementations. If introducing a replacement, remove the old one in the same change.\n\n16. **Stability primitives:** When the repo provides an established script or command, use it verbatim. Only deviate when it is proven broken, and then fix the primitive rather than inventing a parallel path.\n"
  },
  {
    "path": ".ai/skills/ai-self-improvement/SKILL.md",
    "content": "---\r\nname: ai-self-improvement\r\ndescription: Update, create, improve, and synchronise this repository's AI agent instructions and related assets (including skills). Use when the user asks to create or edit a skill/SKILL.md, modify the agent's own instructions/processes, restructure instruction governance, migrate instruction content into skills, or run/adjust the sync pipeline that publishes `.ai/` sources into agent-specific folders. Load this skill before writing any SKILL.md, .instructions.md, or touching any skills/ folder (.ai/, .claude/, .roo/, .github/). It tells you the correct location (.ai/) and the sync step, so files end up in the right place.\r\n---\r\n\r\n# AI Self-Improvement (Instructions + Skills)\r\n\r\n## Critical: `.ai/` is the Primary Source for ALL Agents\r\n\r\nThe `.ai/` folder is the **single source of truth** for all AI agent configurations in this repository. This applies to:\r\n\r\n- **CLINE / Roo Code** — synced to `.roo/rules/` and `.roo/skills/`\r\n- **GitHub Copilot** — synced to `.github/copilot-instructions.md`\r\n- **OpenAI Codex / AGENTS.md** — synced to `AGENTS.md` at repo root\r\n- **Claude Code** — synced to `.claude/*.instructions.md` and `.claude/skills/`\r\n\r\n**IMPORTANT:** When asked to modify skills, instructions, or perform any AI self-improvement task, you MUST:\r\n\r\n1. Locate the source file under `.ai/` (not the agent-specific output)\r\n2. Make changes to the `.ai/` source\r\n3. Run the sync script to propagate changes to all agents\r\n\r\n## Path Mapping Reference\r\n\r\nWhen you encounter a path in an agent-specific folder, map it to `.ai/`:\r\n\r\n| Agent-Specific Path | Source Path (Edit Here) |\r\n|---------------------|------------------------|\r\n| `.roo/rules/*.md` | `.ai/*.instructions.md` |\r\n| `.roo/skills/<name>/SKILL.md` | `.ai/skills/<name>/SKILL.md` |\r\n| `.github/copilot-instructions.md` | `.ai/instructions.md` (generated) |\r\n| `AGENTS.md` | `.ai/instructions.md` (generated) |\r\n| `.claude/*.instructions.md` | `.ai/*.instructions.md` |\r\n| `.claude/skills/<name>/SKILL.md` | `.ai/skills/<name>/SKILL.md` |\r\n\r\n**Example:** If asked to update `.roo/skills/ai-self-improvement/SKILL.md`, you must edit `.ai/skills/ai-self-improvement/SKILL.md` instead.\r\n\r\n## Editable instruction files (sources of truth)\r\n\r\nYou can update your own instruction files under `.ai/`:\r\n\r\n- `.ai/instructions.md` — the main system instructions file\r\n- `.ai/*instructions.md` — additional instruction files (auto-included)\r\n- `.ai/*instructions-detail.md` — detailed instruction files (read only when needed)\r\n- `.ai/skills/<name>/SKILL.md` — skill definition files\r\n\r\n## Workflow\r\n\r\n1. Treat `.ai/` as the **single source of truth** for agent instructions **and skills**.\r\n2. When creating or migrating a skill, create/update it under `.ai/skills/`.\r\n3. Make instruction changes in `.ai/instructions.md` and related `*.instructions.md` / `*.instructions-detail.md` files.\r\n4. Do **not** edit generated outputs directly (they are produced by the sync script):\r\n   - `.roo/rules/`\r\n   - `.roo/skills/`\r\n   - `.github/copilot-instructions.md`\r\n   - `AGENTS.md`\r\n   - `.claude/`\r\n5. **Test changes before syncing** — verify scripts execute correctly and changes work as expected.\r\n6. After testing, run the sync script to apply to all agents.\r\n\r\n## Testing Before Sync\r\n\r\nBefore running the sync script, always verify your changes work correctly:\r\n\r\n- **For script changes**: Execute the modified script and verify output is correct\r\n- **For instruction changes**: Review the markdown renders properly and instructions are clear\r\n- **For skill changes**: Test any bundled tools or scripts included in the skill\r\n\r\n**Example**: If you modify a PowerShell script in a skill, run it directly from `.ai/skills/<name>/scripts/` to confirm it works before syncing.\r\n\r\n## Activation process\r\n\r\nAfter editing instruction files (or master skills), run from repository root:\r\n\r\n```powershell\r\n.\\.ai\\skills\\ai-self-improvement\\scripts\\Sync-AgentAssets.ps1 AUTO\r\n```\r\n\r\nThis script synchronizes changes from `.ai/` to all agent-specific folders.\r\n\r\n## Single source of truth\r\n\r\n**Never embed template content in instructions — reference template files instead.**\r\n\r\nExample:\r\n\r\n- ✅ \"Template maintained in `pr/checklist.template.md`\"\r\n- ❌ Pasting template content into instructions\r\n\r\n## Bundled scripts\r\n\r\n- Sync entrypoint (instructions + skills): `.ai/skills/ai-self-improvement/scripts/Sync-AgentAssets.ps1`\r\n"
  },
  {
    "path": ".ai/skills/ai-self-improvement/scripts/Sync-AgentAssets.ps1",
    "content": "# Script: Sync-AgentAssets.ps1\r\n# Location: .ai/skills/ai-self-improvement/scripts/Sync-AgentAssets.ps1\r\n# Description:\r\n#   Synchronises AI agent instruction files and skills from master sources under `.ai/`.\r\n#   - Instructions: copies `*.instructions.md` from `.ai/` into agent-specific outputs.\r\n#   - Skills: mirrors `.ai/skills/*` into agent skill folders (e.g. `.roo/skills/*`).\r\n#\r\n# Options for Mode:\r\n#   ALL  - update all known agent outputs\r\n#   AUTO - update only agents that exist in this repository (default usage)\r\n#   Or a specific agent name: CLINE, ROO CODE, GitHub CoPilot, OpenAI Codex, Claude Code\r\n\r\nparam(\r\n    [Parameter(Position = 0)]\r\n    [string]$Mode,\r\n\r\n    [switch]$NoClear\r\n)\r\n\r\n# Combine remaining args so Windows PowerShell (-File) invocations like:\r\n#   Sync-AgentAssets.ps1 GitHub CoPilot\r\n# work the same as:\r\n#   Sync-AgentAssets.ps1 \"GitHub CoPilot\"\r\nif ($args.Count -gt 0) {\r\n    $ModeFromArgs = ($args -join ' ')\r\n    if (-not $Mode -or $Mode -eq '') {\r\n        $Mode = $ModeFromArgs\r\n    }\r\n}\r\n\r\n# Allow calling via the old filename (if invoked through a copied/renamed script).\r\n# This only affects displayed script name in prompts/logs.\r\n$scriptName = [System.IO.Path]::GetFileName($MyInvocation.MyCommand.Path)\r\n\r\n# Strict mode\r\nSet-StrictMode -Version Latest\r\n$ErrorActionPreference = \"Stop\"\r\n\r\nfunction Ensure-Directory {\r\n    param(\r\n        [Parameter(Mandatory = $true)]\r\n        [string]$Path\r\n    )\r\n\r\n    if (-not (Test-Path -Path $Path -PathType Container)) {\r\n        New-Item -ItemType Directory -Force -Path $Path | Out-Null\r\n    }\r\n}\r\n\r\n# Function to check if instruction files exist in a directory\r\nfunction Test-HasInstructionFiles {\r\n    param(\r\n        [Parameter(Mandatory = $true)]\r\n        [string]$Path,\r\n        [string]$Filter = '*instructions.md'\r\n    )\r\n\r\n    if (Test-Path $Path -PathType Container) {\r\n        $files = @(Get-ChildItem $Path -Filter $Filter -File -ErrorAction SilentlyContinue)\r\n        return ($files.Length -gt 0)\r\n    }\r\n\r\n    return $false\r\n}\r\n\r\n# Function to pause at the end (unless -NoWait is specified)\r\nfunction Invoke-Pause {\r\n    Write-Host \"Pausing for 2 seconds...\"\r\n    Start-Sleep -Seconds 2\r\n}\r\n\r\nfunction Copy-FileIfDifferent {\r\n    param(\r\n        [Parameter(Mandatory = $true)]\r\n        [string]$SourcePath,\r\n        [Parameter(Mandatory = $true)]\r\n        [string]$TargetPath\r\n    )\r\n\r\n    $targetDir = Split-Path -Path $TargetPath -Parent\r\n    Ensure-Directory -Path $targetDir\r\n\r\n    if (-not (Test-Path -Path $TargetPath -PathType Leaf)) {\r\n        Copy-Item -LiteralPath $SourcePath -Destination $TargetPath -Force\r\n        $relative = $TargetPath.Substring($repoRoot.Length + 1)\r\n        Write-Host \"Created: $relative\"\r\n        return\r\n    }\r\n\r\n    $srcBytes = [System.IO.File]::ReadAllBytes($SourcePath)\r\n    $dstBytes = [System.IO.File]::ReadAllBytes($TargetPath)\r\n\r\n    if ($srcBytes.Length -eq $dstBytes.Length) {\r\n        $same = $true\r\n        for ($i = 0; $i -lt $srcBytes.Length; $i++) {\r\n            if ($srcBytes[$i] -ne $dstBytes[$i]) { $same = $false; break }\r\n        }\r\n\r\n        if ($same) {\r\n            $relative = $TargetPath.Substring($repoRoot.Length + 1)\r\n            Write-Host \"Up-to-date: $relative\"\r\n            return\r\n        }\r\n    }\r\n\r\n    Copy-Item -LiteralPath $SourcePath -Destination $TargetPath -Force\r\n    $relative = $TargetPath.Substring($repoRoot.Length + 1)\r\n    Write-Host \"Updated: $relative\"\r\n}\r\n\r\nfunction Get-TextAuto {\r\n    param(\r\n        [Parameter(Mandatory = $true)]\r\n        [string]$Path\r\n    )\r\n\r\n    # .NET StreamReader detects BOM for UTF-8/UTF-16/UTF-32 automatically.\r\n    $sr = New-Object System.IO.StreamReader($Path, $true)\r\n    try {\r\n        return $sr.ReadToEnd()\r\n    }\r\n    finally {\r\n        $sr.Dispose()\r\n    }\r\n}\r\n\r\nfunction Write-Utf8NoBom {\r\n    param(\r\n        [Parameter(Mandatory = $true)]\r\n        [string]$Path,\r\n        [Parameter(Mandatory = $true)]\r\n        [string]$Content\r\n    )\r\n\r\n    $dir = Split-Path -Path $Path -Parent\r\n    Ensure-Directory -Path $dir\r\n\r\n    $utf8NoBom = New-Object System.Text.UTF8Encoding($false)\r\n    [System.IO.File]::WriteAllText($Path, $Content, $utf8NoBom)\r\n}\r\n\r\nfunction Assert-InstructionSync {\r\n    param(\r\n        [Parameter(Mandatory = $true)]\r\n        [string]$SourceDirectory,\r\n        [Parameter(Mandatory = $true)]\r\n        [string]$TargetDirectory,\r\n        [Parameter(Mandatory = $true)]\r\n        [System.IO.FileSystemInfo[]]$SourceFiles\r\n    )\r\n\r\n    $srcDir = Join-Path $repoRoot $SourceDirectory\r\n    $dstDir = Join-Path $repoRoot $TargetDirectory\r\n\r\n    foreach ($sourceFile in $SourceFiles) {\r\n        $srcPath = Join-Path $srcDir $sourceFile.Name\r\n        $dstPath = Join-Path $dstDir $sourceFile.Name\r\n\r\n        if (-not (Test-Path $dstPath -PathType Leaf)) {\r\n            throw \"Binary comparison failed. Destination file missing: $dstPath\"\r\n        }\r\n\r\n        $srcBytes = [System.IO.File]::ReadAllBytes($srcPath)\r\n        $dstBytes = [System.IO.File]::ReadAllBytes($dstPath)\r\n\r\n        if ($srcBytes.Length -ne $dstBytes.Length) {\r\n            throw \"Binary comparison failed. Source and target size mismatch in binary: Source: $srcPath Target: $dstPath\"\r\n        }\r\n\r\n        for ($i = 0; $i -lt $srcBytes.Length; $i++) {\r\n            if ($srcBytes[$i] -ne $dstBytes[$i]) {\r\n                throw \"Binary comparison failed. Source and target content mismatch in binary: Source: $srcPath Target: $dstPath\"\r\n            }\r\n        }\r\n    }\r\n}\r\n\r\n# Function to update agents that use multiple separate instruction files\r\nfunction Update-MultipleFileAgent {\r\n    param(\r\n        [Parameter(Mandatory = $true)]\r\n        [string]$AgentName,\r\n        [Parameter(Mandatory = $true)]\r\n        [string]$TargetDirectory,\r\n        [Parameter(Mandatory = $true)]\r\n        [System.IO.FileSystemInfo[]]$SourceFiles,\r\n        [Parameter(Mandatory = $true)]\r\n        [string]$RepoRoot\r\n    )\r\n\r\n    Write-Host \"`r`n--- Updating $AgentName Instructions ---\"\r\n    $targetDir = Join-Path $RepoRoot $TargetDirectory\r\n\r\n    foreach ($sourceFile in $SourceFiles) {\r\n        $targetFile = Join-Path $targetDir $sourceFile.Name\r\n        Copy-FileIfDifferent -SourcePath $sourceFile.FullName -TargetPath $targetFile\r\n    }\r\n\r\n    Assert-InstructionSync -SourceDirectory \".ai\" -TargetDirectory $TargetDirectory -SourceFiles $SourceFiles\r\n}\r\n\r\n# Function to update agents that use a single combined instruction file\r\nfunction Update-SingleFileAgent {\r\n    param(\r\n        [Parameter(Mandatory = $true)]\r\n        [string]$AgentName,\r\n        [Parameter(Mandatory = $true)]\r\n        [string]$TargetFilePath,\r\n        [Parameter(Mandatory = $true)]\r\n        [System.IO.FileSystemInfo[]]$SourceFiles,\r\n        [Parameter(Mandatory = $true)]\r\n        [string]$RepoRoot\r\n    )\r\n\r\n    Write-Host \"`r`n--- Updating $AgentName Instructions ---\"\r\n    $targetFile = Join-Path $RepoRoot $TargetFilePath\r\n    $relativeTarget = $targetFile.Substring($repoRoot.Length + 1)\r\n\r\n    $allInstructionsContent = New-Object System.Text.StringBuilder\r\n    $firstFile = $true\r\n\r\n    foreach ($sourceFile in $SourceFiles) {\r\n        $sourceContent = Get-TextAuto -Path $sourceFile.FullName\r\n\r\n        if ([string]::IsNullOrWhiteSpace($sourceContent)) {\r\n            Write-Warning \"Skipping empty file: $($sourceFile.Name)\"\r\n            continue\r\n        }\r\n\r\n        if (-not $firstFile) {\r\n            [void]$allInstructionsContent.AppendLine(\"\")\r\n        }\r\n\r\n        [void]$allInstructionsContent.AppendLine(\"==== START OF INSTRUCTIONS FROM: $($sourceFile.Name) ====\")\r\n        [void]$allInstructionsContent.AppendLine(\"\")\r\n\r\n        [void]$allInstructionsContent.AppendLine(\"# Instructions from: $($sourceFile.Name)\")\r\n        [void]$allInstructionsContent.AppendLine(\"\")\r\n\r\n        [void]$allInstructionsContent.AppendLine($sourceContent.Trim())\r\n\r\n        [void]$allInstructionsContent.AppendLine(\"\")\r\n        [void]$allInstructionsContent.AppendLine(\"==== END OF INSTRUCTIONS FROM: $($sourceFile.Name) ====\")\r\n\r\n        $firstFile = $false\r\n    }\r\n\r\n    $finalContent = $allInstructionsContent.ToString()\r\n\r\n    $existing = if (Test-Path -Path $targetFile -PathType Leaf) { Get-TextAuto -Path $targetFile } else { $null }\r\n    if ($null -ne $existing -and $existing -eq $finalContent) {\r\n        Write-Host \"Up-to-date: $relativeTarget\"\r\n        return\r\n    }\r\n\r\n    Write-Utf8NoBom -Path $targetFile -Content $finalContent\r\n    Write-Host \"Updated: $relativeTarget\"\r\n}\r\n\r\nfunction Invoke-RoboCopyMirror {\r\n    param(\r\n        [Parameter(Mandatory = $true)]\r\n        [string]$SourceDirectory,\r\n        [Parameter(Mandatory = $true)]\r\n        [string]$DestinationDirectory,\r\n        [Parameter(Mandatory = $true)]\r\n        [string]$Label\r\n    )\r\n\r\n    if (-not (Test-Path $SourceDirectory -PathType Container)) {\r\n        Write-Host \"No skills folder found at: $SourceDirectory\"\r\n        return\r\n    }\r\n\r\n    Ensure-Directory -Path $DestinationDirectory\r\n\r\n    Write-Host \"`r`n--- Mirroring skills to $Label ---\"\r\n    Write-Host \"Source:      $SourceDirectory\"\r\n    Write-Host \"Destination: $DestinationDirectory\"\r\n\r\n    # /MIR      = mirror (copy + delete removed)\r\n    # /FFT      = tolerate 2s timestamp granularity\r\n    # /R:1 /W:1 = retry quickly\r\n    # /NFL/NDL  = no file/dir listing (keep output compact)\r\n    # /NJH/NJS  = no job header/summary\r\n    # /NP       = no progress\r\n    # /XD       = exclude version control/build dirs\r\n    $excludedDirs = @('.git', '.vs', 'bin', 'obj')\r\n\r\n    $args = @(\r\n        $SourceDirectory,\r\n        $DestinationDirectory,\r\n        '/MIR',\r\n        '/FFT',\r\n        '/R:1',\r\n        '/W:1',\r\n        '/NFL',\r\n        '/NDL',\r\n        '/NJH',\r\n        '/NJS',\r\n        '/NP'\r\n    )\r\n\r\n    foreach ($d in $excludedDirs) {\r\n        $args += '/XD'\r\n        $args += $d\r\n    }\r\n\r\n    $exe = 'robocopy'\r\n    # Do not echo the full robocopy command; it is noisy and can wrap in some terminals.\r\n    Write-Host \"robocopy <source> <destination> /MIR /NFL /NDL /NJH /NJS /NP ...\"\r\n\r\n    & $exe @args | Out-Null\r\n    $exitCode = $LASTEXITCODE\r\n\r\n    # Robocopy uses bitmask exit codes.\r\n    # 0-7 are success with various flags; >= 8 indicates failure.\r\n    if ($exitCode -ge 8) {\r\n        throw \"Robocopy failed with exit code $exitCode. Command: $cmd\"\r\n    }\r\n\r\n    # IMPORTANT: robocopy returns 1+ for successful copies.\r\n    # Ensure PowerShell script does not propagate a non-zero exit code for success cases.\r\n    $global:LASTEXITCODE = 0\r\n\r\n    Write-Host \"Mirrored skills to $Label (robocopy exit code $exitCode).\"\r\n}\r\n\r\nfunction Sync-SkillsToRoo {\r\n    param(\r\n        [Parameter(Mandatory = $true)]\r\n        [string]$RepoRoot\r\n    )\r\n\r\n    $srcSkillsRoot = Join-Path $RepoRoot \".ai\\skills\"\r\n    $rooSkillsRoot = Join-Path $RepoRoot \".roo\\skills\"\r\n\r\n    Invoke-RoboCopyMirror -SourceDirectory $srcSkillsRoot -DestinationDirectory $rooSkillsRoot -Label \"Roo (.roo\\\\skills)\"\r\n}\r\n\r\nfunction Sync-SkillsToGitHub {\r\n    param(\r\n        [Parameter(Mandatory = $true)]\r\n        [string]$RepoRoot\r\n    )\r\n\r\n    $srcSkillsRoot = Join-Path $RepoRoot \".ai\\skills\"\r\n    $githubSkillsRoot = Join-Path $RepoRoot \".github\\skills\"\r\n\r\n    Invoke-RoboCopyMirror -SourceDirectory $srcSkillsRoot -DestinationDirectory $githubSkillsRoot -Label \"GitHub (.github\\\\skills)\"\r\n}\r\n\r\nfunction Sync-SkillsToClaude {\r\n    param(\r\n        [Parameter(Mandatory = $true)]\r\n        [string]$RepoRoot\r\n    )\r\n\r\n    $srcSkillsRoot = Join-Path $RepoRoot \".ai\\skills\"\r\n    $claudeSkillsRoot = Join-Path $RepoRoot \".claude\\skills\"\r\n\r\n    Invoke-RoboCopyMirror -SourceDirectory $srcSkillsRoot -DestinationDirectory $claudeSkillsRoot -Label \"Claude Code (.claude\\\\skills)\"\r\n}\r\n\r\n# --- Main Script ---\r\nif (-not $NoClear) {\r\n    Clear-Host\r\n}\r\n\r\n# We are located under `.ai/skills/<skill>/tools`. Find repo root by going up 4 levels.\r\n$scriptDir = $PSScriptRoot\r\n$repoRoot = (Join-Path -Path $scriptDir -ChildPath \"..\\..\\..\\..\" | Resolve-Path).Path\r\n\r\n# `.ai` folder path\r\n$aiDir = Join-Path $repoRoot \".ai\"\r\n\r\n# Discover source files matching *instructions.md in the .ai folder\r\n[System.IO.FileSystemInfo[]]$sourceInstructionFiles = Get-ChildItem -Path $aiDir -Filter \"*instructions.md\" -File | Sort-Object Name\r\n\r\nif ($null -eq $sourceInstructionFiles -or $sourceInstructionFiles.Length -eq 0) {\r\n    Write-Warning \"No '*instructions.md' files found in '$aiDir'. Nothing to process.\"\r\n    exit 0\r\n}\r\n\r\nWrite-Host \"Found the following source instruction files in '$aiDir':\"\r\n$sourceInstructionFiles | ForEach-Object { Write-Host \"- $($_.Name)\" }\r\n\r\n# Mode parameter handling: if 'ALL' or 'AUTO', skip interactive prompt\r\nif ($Mode -eq 'ALL') {\r\n    Write-Host \"Selected: ALL (parameter mode)\"\r\n    $updateCline = $true\r\n    $updateCopilot = $true\r\n    $updateRooCode = $true\r\n    $updateCodex = $true\r\n    $updateClaude = $true\r\n}\r\nelseif ($Mode -eq 'AUTO') {\r\n    Write-Host \"Selected: AUTO (parameter mode)\"\r\n    # Determine available agents based on instruction files\r\n    $updateCline = Test-HasInstructionFiles -Path (Join-Path $repoRoot '.clinerules')\r\n    $updateRooCode = Test-HasInstructionFiles -Path (Join-Path $repoRoot '.roo\\rules')\r\n    $updateCopilot = Test-Path (Join-Path $repoRoot '.github\\copilot-instructions.md') -PathType Leaf\r\n    $updateCodex = Test-Path (Join-Path $repoRoot 'AGENTS.md') -PathType Leaf\r\n    $updateClaude = Test-HasInstructionFiles -Path (Join-Path $repoRoot '.claude')\r\n    Write-Host \"Agents to update based on available instruction files:\"\r\n    if ($updateCline) { Write-Host \"- CLINE\" }\r\n    if ($updateRooCode) { Write-Host \"- ROO CODE\" }\r\n    if ($updateCopilot) { Write-Host \"- GitHub CoPilot\" }\r\n    if ($updateCodex) { Write-Host \"- OpenAI Codex\" }\r\n    if ($updateClaude) { Write-Host \"- Claude Code\" }\r\n}\r\nelseif ($Mode -and $Mode -ne '') {\r\n    # Specific agent mode (e.g., CLINE, \"ROO CODE\", etc.)\r\n    $updateCline = ($Mode -eq 'CLINE')\r\n    $updateCopilot = ($Mode -eq 'GitHub CoPilot')\r\n    $updateRooCode = ($Mode -eq 'ROO CODE')\r\n    $updateCodex = ($Mode -eq 'OpenAI Codex')\r\n    $updateClaude = ($Mode -eq 'Claude Code')\r\n    Write-Host \"Selected: $Mode (parameter mode)\"\r\n}\r\nelse {\r\n    # User prompt for agent selection\r\n    # Detect available agents for interactive menu\r\n    $hasCline = Test-HasInstructionFiles -Path (Join-Path $repoRoot '.clinerules')\r\n    $hasRooCode = Test-HasInstructionFiles -Path (Join-Path $repoRoot '.roo\\rules')\r\n    $hasCopilot = Test-Path (Join-Path $repoRoot '.github\\copilot-instructions.md') -PathType Leaf\r\n    $hasCodex = Test-Path (Join-Path $repoRoot 'AGENTS.md') -PathType Leaf\r\n    $hasClaude = Test-HasInstructionFiles -Path (Join-Path $repoRoot '.claude')\r\n\r\n    Write-Host \"`r`nDetected AI agents with instruction files:\"\r\n    if ($hasCline) { Write-Host \"- CLINE\" }\r\n    if ($hasRooCode) { Write-Host \"- ROO CODE\" }\r\n    if ($hasCopilot) { Write-Host \"- GitHub CoPilot\" }\r\n    if ($hasCodex) { Write-Host \"- OpenAI Codex\" }\r\n    if ($hasClaude) { Write-Host \"- Claude Code\" }\r\n\r\n    Write-Host \"\"\r\n    Write-Host \"==============================================================\"\r\n    Write-Host \"Select Agent Instruction Set to Update\"\r\n    Write-Host \"--------------------------------------------------------------\"\r\n    Write-Host \"1. AUTO           - Update only agents with instruction files (default)\"\r\n    Write-Host \"2. ALL            - Update instructions for all AI agents\"\r\n    Write-Host \"3. CLINE          - Update instructions for CLINE\"\r\n    Write-Host \"4. ROO CODE       - Update instructions for ROO CODE\"\r\n    Write-Host \"5. GitHub CoPilot - Update instructions for GitHub CoPilot\"\r\n    Write-Host \"6. OpenAI Codex   - Update instructions for OpenAI Codex\"\r\n    Write-Host \"7. Claude Code    - Update instructions for Claude Code\"\r\n    Write-Host \"0. Exit\"\r\n    Write-Host \"==============================================================\"\r\n    $selection = Read-Host \"Enter the number of your choice (0-7)\"\r\n\r\n    # Initialize flags\r\n    $updateCline = $false\r\n    $updateCopilot = $false\r\n    $updateRooCode = $false\r\n    $updateCodex = $false\r\n    $updateClaude = $false\r\n\r\n    switch ($selection) {\r\n        '1' {\r\n            $updateCline = Test-HasInstructionFiles -Path (Join-Path $repoRoot '.clinerules')\r\n            $updateRooCode = Test-HasInstructionFiles -Path (Join-Path $repoRoot '.roo\\rules')\r\n            $updateCopilot = Test-Path (Join-Path $repoRoot '.github\\copilot-instructions.md') -PathType Leaf\r\n            $updateCodex = Test-Path (Join-Path $repoRoot 'AGENTS.md') -PathType Leaf\r\n            $updateClaude = Test-HasInstructionFiles -Path (Join-Path $repoRoot '.claude')\r\n            Write-Host \"Selected: AUTO\"\r\n        }\r\n        '2' {\r\n            $updateCline = $true\r\n            $updateCopilot = $true\r\n            $updateRooCode = $true\r\n            $updateCodex = $true\r\n            $updateClaude = $true\r\n            Write-Host \"Selected: ALL\"\r\n        }\r\n        '3' { $updateCline = $true; Write-Host \"Selected: CLINE\" }\r\n        '4' { $updateRooCode = $true; Write-Host \"Selected: ROO CODE\" }\r\n        '5' { $updateCopilot = $true; Write-Host \"Selected: GitHub CoPilot\" }\r\n        '6' { $updateCodex = $true; Write-Host \"Selected: OpenAI Codex\" }\r\n        '7' { $updateClaude = $true; Write-Host \"Selected: Claude Code\" }\r\n        '0' { Write-Host \"Operation cancelled by user.\"; exit 0 }\r\n        default { throw \"Invalid selection. Exiting.\" }\r\n    }\r\n}\r\n\r\n# --- Multiple-File Agent Updates ---\r\nif ($updateCline) {\r\n    Update-MultipleFileAgent -AgentName \"CLINE\" -TargetDirectory \".clinerules\" -SourceFiles $sourceInstructionFiles -RepoRoot $repoRoot\r\n}\r\n\r\nif ($updateRooCode) {\r\n    Update-MultipleFileAgent -AgentName \"ROO CODE\" -TargetDirectory \".roo\\rules\" -SourceFiles $sourceInstructionFiles -RepoRoot $repoRoot\r\n}\r\n\r\n# --- Single-File Agent Updates ---\r\nif ($updateCopilot) {\r\n    $copilotTarget = \".github\\copilot-instructions.md\"\r\n    $githubInstructionsDir = Join-Path $repoRoot \".github\\instructions\"\r\n\r\n    if (Test-Path $githubInstructionsDir -PathType Container) {\r\n        Write-Host \"`r`n--- Updating GitHub CoPilot Instructions (folder-based) ---\"\r\n\r\n        $mainName = \"instructions.md\"\r\n        $mainSource = $sourceInstructionFiles | Where-Object { $_.Name -ieq $mainName } | Select-Object -First 1\r\n        if ($null -eq $mainSource) {\r\n            throw \"Expected source '$mainName' under .ai but none found.\"\r\n        }\r\n\r\n        Copy-FileIfDifferent -SourcePath $mainSource.FullName -TargetPath (Join-Path $repoRoot $copilotTarget)\r\n\r\n        foreach ($sf in $sourceInstructionFiles) {\r\n            if ($sf.Name -ieq $mainName) {\r\n                continue\r\n            }\r\n\r\n            $destination = Join-Path $githubInstructionsDir $sf.Name\r\n            Copy-FileIfDifferent -SourcePath $sf.FullName -TargetPath $destination\r\n        }\r\n    }\r\n    else {\r\n        Update-SingleFileAgent -AgentName \"GitHub CoPilot\" -TargetFilePath $copilotTarget -SourceFiles $sourceInstructionFiles -RepoRoot $repoRoot\r\n    }\r\n}\r\n\r\nif ($updateCodex) {\r\n    Update-SingleFileAgent -AgentName \"OpenAI Codex\" -TargetFilePath \"AGENTS.md\" -SourceFiles $sourceInstructionFiles -RepoRoot $repoRoot\r\n}\r\n\r\n# --- Claude Code (multiple-file agent) ---\r\nif ($updateClaude) {\r\n    Update-MultipleFileAgent -AgentName \"Claude Code\" -TargetDirectory \".claude\" -SourceFiles $sourceInstructionFiles -RepoRoot $repoRoot\r\n}\r\n\r\n# --- Skills mirroring ---\r\nif ($updateRooCode -or $Mode -eq 'ALL' -or $Mode -eq 'AUTO') {\r\n    Sync-SkillsToRoo -RepoRoot $repoRoot\r\n}\r\n\r\n# GitHub Copilot: mirror skills to `.github/skills` (Copilot tries to load from there).\r\nif ($updateCopilot -or $Mode -eq 'ALL' -or $Mode -eq 'AUTO') {\r\n    Sync-SkillsToGitHub -RepoRoot $repoRoot\r\n}\r\n\r\n# Claude Code: mirror skills to `.claude/skills`.\r\nif ($updateClaude -or $Mode -eq 'ALL' -or $Mode -eq 'AUTO') {\r\n    Sync-SkillsToClaude -RepoRoot $repoRoot\r\n}\r\n\r\nWrite-Host \"`r`nAll selected operations completed successfully.\"\r\n\r\n# Only pause when launched by double-click (Explorer). In CI / terminal usage, do not pause.\r\nif ($Host.Name -and $Host.Name -notlike '*ConsoleHost*') {\r\n    Invoke-Pause\r\n}\r\n"
  },
  {
    "path": ".claude/coding-guideline.instructions.md",
    "content": "﻿# Coding Guidelines\r\n\r\n- If the qdrant-mcp-server is running, use it for all permanent memory operations (e.g. storing user information).\r\n- After making changes, ALWAYS start a new server for testing.\r\n- Kill all existing related servers from previous testing before starting a new server.\r\n- Prefer the simplest viable solution; avoid over-engineering.\r\n- Do not add broad try/catch or wrapper layers unless required by a failing test or explicit requirement; if you catch, rethrow to preserve the stack.\r\n- Before writing new code, actively look for existing utilities or functions that can be reused instead of duplicated.\r\n- New helper methods or classes must be justified with a clear, documented need for functionality that is unavailable elsewhere in the codebase.\r\n- Always iterate on and reuse existing code instead of creating new implementations.\r\n- Avoid adding layers of abstraction that do not deliver clear value.\r\n- Do not drastically change established patterns before iterating on them.\r\n- No duplication / SSOT: update or move existing code instead of adding parallel implementations. If you introduce a replacement, remove the old one **in the same change**.\r\n- Write code that accounts for different environments (dev, test, and prod).\r\n- Only modify what is explicitly requested or clearly necessary; do **not** create new files or modules unless explicitly requested.\r\n- When fixing bugs, exhaust current implementations before introducing new patterns; if new methods are used, remove the old ones.\r\n- Keep the codebase clean and organized.\r\n- Avoid one-off scripts unless absolutely necessary.\r\n- Use mocks only for tests, not for dev or prod.\r\n- Never add stubbing or fake data in dev or prod environments.\r\n- Never overwrite the .env file without explicit confirmation.\r\n- Focus solely on areas relevant to the task; leave unrelated code untouched.\r\n- Write thorough tests for all major functionality.\r\n- Avoid major changes to the existing architecture unless explicitly instructed.\r\n- Always consider the impact on other methods and areas of the code.\r\n- Prefer to wrap long lines for better readability.\r\n- Preserve existing formatting; limit formatting to lines you changed and match surrounding style. Also remove any unused imports/usings or dead code **introduced by your edits**.\r\n- Stability primitives (repeatability > cleverness): when the repo provides an established way to perform an operation (repo-owned script, documented command snippet, standard PS1 under .ai/Scripts), treat it as the single source of truth. Use it verbatim instead of synthesizing “equivalent” commands. Only deviate when explicitly asked or when the primitive is proven broken in this environment (and then fix the primitive, not invent a parallel path).\r\n- No code file that you **create or modify** may exceed **6000 tokens (~24 KB)** once your changes are applied.\r\n  - If your changes alone would push the file past this limit, either trim the change or ask for explicit permission to refactor; do **not** alter unrelated code solely to meet the limit.\r\n  - Existing oversized files are left untouched unless the user explicitly requests a refactor.\r\n\r\n## Source Control Conventions\r\n\r\nNaming conventions for issues, branches, and pull requests.\r\n\r\n### Categories\r\n\r\n| Category | Use for |\r\n|----------|---------|\r\n| `FEAT`   | New features |\r\n| `FIX`    | Bug fixes |\r\n| `TECH`   | Infrastructure, dependencies, refactoring |\r\n| `DOCS`   | Documentation |\r\n\r\n### Naming Patterns\r\n\r\n| Item       | Pattern | Example |\r\n|------------|---------|---------|\r\n| **Issue**  | `{CATEGORY}: {Description}` | `FEAT: Download logs to CSV` |\r\n| **Branch** | `{CATEGORY}-{issue#}-{lowercase-dashed-name}` | `FEAT-21-download-logs-to-csv` |\r\n| **PR**     | `PR: #{issue#}: {CATEGORY}: {Description}` | `PR: #21: FEAT: Download logs to CSV` |\r\n\r\n- Branch names: lowercase, words separated by dashes, non-ASCII replaced with a single dash, derived from issue title but may be shortened.\r\n- PR body must reference the issue with `Closes #{issue#}`.\r\n- Merge via PR only — no direct pushes to `main` or `master`.\r\n- Feature branches are always created from `main` or `master`. Never merge one feature branch into another — only merge `main` if you need to catch up.\r\n- Always create branches from latest origin/main or origin/main, never merge sub-branches, confirm before risky git ops.\r\n- Do not add Co-Authored-By {AI model}/{AI company} lines to commits.\r\n- Never close issues before a release is published with the fix.\r\n\r\nUse the following guidelines:\r\n\r\n1. Doc Comment Enhancement for IntelliSense\r\n\r\n    - Replace or augment simple comments with relevant doc comment syntax that is supported by IntelliSense as needed.\r\n    - Preserve the original intent and wording of existing comments wherever possible.\r\n\r\n2. Code Layout for Clarity\r\n\r\n    - Place the most important or user-editable sections at the top if logically appropriate.\r\n    - Insert headings or separators within the code to clearly delineate where customizations or key logic sections can be adjusted.\r\n\r\n3. No Extraneous Code Comments\r\n\r\n    - Do not include \"one-off\" or user-directed commentary in the code.\r\n    - Confine all clarifications or additional suggestions to explanations outside of the code snippet.\r\n\r\n4. Avoid Outdated or Deprecated Methods\r\n\r\n    - Refrain from introducing or relying on obsolete or deprecated methods and libraries.\r\n    - If the current code relies on potentially deprecated approaches, ask for clarification or provide viable, modern alternatives that align with best practices.\r\n\r\n5. Testing and Validation\r\n\r\n    - Suggest running unit tests or simulations on the modified segments to confirm that the changes fix the issue without impacting overall functionality.\r\n    - Ensure that any proposed improvements, including doc comment upgrades, integrate seamlessly with the existing codebase.\r\n    - After all code modifications, navigate to the affected project directory and build C# then Angular to confirm the application compiles without errors:\r\n\t\tcd {PROJECT} && dotnet build {PROJECT}.csproj\r\n\t\tcd {PROJECT}/ClientApp && ng build\r\n    - Run relevant unit tests if code changes affect core logic.\r\n    - If the developer certificate is not trusted, then execute: dotnet dev-certs https --trust\r\n    - To launch project use: dotnet watch run --project {PROJECT}/{PROJECT}.csproj --launch-profile \"{PROJECT} (NG Build)\"\r\n\r\n6. Rationale and Explanation\r\n\r\n    - For every change (including comment conversions), provide a concise explanation detailing how the modification resolves the identified issue while preserving the original design and context.\r\n    - Clearly highlight only the modifications made, ensuring that no previously validated progress is altered.\r\n    - NOTE: Summarize reasoning for the user, but do NOT expose full chain-of-thought. Keep internal deliberations internal; surface only the concise rationale needed to justify each change.\r\n\r\n7. Contextual Analysis\r\n\r\n    - Use all available context—such as code history, inline documentation, style guidelines—to understand the intended functionality.\r\n    - When inspecting an existing file for understanding, prefer reading the whole file in a\r\n      single `read_file` call when it comfortably fits in context; switch to targeted slices\r\n      only when the file is too large, the tool truncates it, or a specific anchor line is\r\n      already known.\r\n    - If the role or intent behind a code segment is ambiguous, ask for clarification rather than making assumptions.\r\n\r\n8. Targeted, Incremental Changes\r\n\r\n    - Identify and isolate only the problematic code segments (including places where IntelliSense doc comments can replace simple comments).\r\n    - Provide minimal code snippets that address the issue without rewriting larger sections.\r\n    - For each suggested code change, explicitly indicate the exact location in the code (e.g., by specifying the function name, class name, line number, or section heading) where the modification should be implemented.\r\n\r\n9. Preservation of Context\r\n\r\n    - Maintain all developer comments, annotations, and workarounds exactly as they appear, transforming them to doc comment format only when it improves IntelliSense support.\r\n    - Do not modify or remove any non-code context unless explicitly instructed.\r\n    - Avoid introducing new, irrelevant comments in the code.\r\n\r\n10. Launching {PROJECT} Correctly:\r\n    - Navigate to the {PROJECT} project folder.\r\n    - Run the following command to launch the project with live reload and proper debugging configuration:\r\n      dotnet watch run --launch-profile \"{PROJECT} (NG Build)\" --project {PROJECT}/{PROJECT}.csproj\r\n    - This command will start the {PROJECT} project on the designated debugging session URL.\r\n    - Ensure that any previous {PROJECT} instances are terminated before running this command.\r\n"
  },
  {
    "path": ".claude/instructions.md",
    "content": "﻿## Role\n\nYour role is to analyze and improve code by making only localized, targeted changes. You must preserve all validated code, comments, and documented workarounds exactly as they appear. Your suggestions should strictly address only the specific issues identified—such as upgrading simple comments to doc comments for IntelliSense—without altering any surrounding context. Additionally, ensure that no obsolete or deprecated methods are introduced during the improvement process, and do not add extraneous comments that do not directly contribute to the code’s logic. Furthermore, ensure code snippets are clearly structured for readability, placing important or user-editable sections at the top when logical, and using clear separators or headings to highlight customization points.\nWherever beneficial, convert simple comments into recognized documentation comment syntax (e.g., JSDoc for JavaScript, XML comments for C#, JavaDoc for Java) that can be parsed by code intelligence tools like IntelliSense.\nMaintain the original meaning of these comments, but structure them in a way that provides maximum benefit for automated tools and refactoring methods.\nApply chain-of-thought reasoning to identify code segments best served by doc comments, analyze the existing context of each comment, and then make precise, incremental modifications that enhance IntelliSense compatibility while preserving existing functionality.\n\n## Output\n\nWrap any and all code—including regular code snippets, inline code segments, outputs, pseudocode, or any text that represents code—in Markdown code blocks with a language identifier (e.g., ```typescript, ```powershell).\n"
  },
  {
    "path": ".claude/repository-analysis.instructions.md",
    "content": "# Repository Analysis\n\n## 1. Repository Overview\n\nThis document provides a factual reference for the Jocys.com FocusLogger repository, aimed at developers and AI coding agents working on the codebase.\n\n**FocusLogger** is a Windows desktop utility that monitors and logs which process or program takes window focus. It targets users (especially gamers and power users) who experience unexpected focus stealing — where a background process briefly grabs foreground focus, interrupting gameplay or work. The tool logs every focus change with timestamps, process details, window class names, and focus-state flags, allowing users to identify the culprit.\n\n- **Repository:** https://github.com/JocysCom/FocusLogger\n- **License:** GNU General Public License v3.0\n- **Current version:** 1.2.6\n- **Target platform:** Windows 10+ with .NET 8.0\n- **Primary audiences:** Gamers, power users, IT support personnel diagnosing focus-stealing issues.\n\n## 2. Top-Level Structure\n\nThis section maps every top-level directory and file to help navigate the repository quickly.\n\n| Path | Purpose |\n|------|---------|\n| `FocusLogger/` | Main application project (WPF, .NET 8.0). Contains all app source code, shared library, and resources. |\n| `FocusLogger.Tests/` | MSTest test project. Unit tests for CSV export and UI automation tests. |\n| `Documents/` | Release engineering: signing scripts, zip packaging scripts, screenshot tooling, and pre-built release files. |\n| `Resources/` | Solution-level shared scripts (currently `ZipFiles.ps1` for checksum-aware zip packaging). |\n| `.ai/` | AI agent instructions, coding guidelines, repository analysis, and skills. |\n| `JocysCom.FocusLogger.slnx` | Solution file (XML-based `.slnx` format) referencing the two projects. |\n| `README.md` | Project overview, download link, system requirements, screenshot. |\n| `LICENSE` | GPLv3 license text. |\n| `SECURITY.md` | Security vulnerability reporting policy (support@jocys.com). |\n| `Settings.XamlStyler` | XamlStyler configuration for consistent XAML formatting. |\n| `Solution_Cleanup.ps1` | PowerShell script for cleaning build artifacts. |\n\n## 3. Technology Stack & Key Dependencies\n\nThis section lists verified technologies and versions drawn from project files.\n\n| Technology | Version / Detail | Evidence |\n|------------|-----------------|----------|\n| .NET | 8.0 (`net8.0-windows`) | `JocysCom.FocusLogger.csproj` TargetFramework |\n| C# | Implicit (SDK default for .NET 8) | SDK-style project |\n| WPF | `<UseWPF>true</UseWPF>` | csproj |\n| Windows Forms interop | `<UseWindowsForms>true</UseWindowsForms>` | csproj — used for P/Invoke helpers and DPI awareness |\n| MSTest | v3.x (`MSTest.TestFramework 3.*`, `MSTest.TestAdapter 3.*`) | Test csproj PackageReference |\n| Microsoft.NET.Test.Sdk | 17.x | Test csproj PackageReference |\n| Windows API (user32.dll) | P/Invoke | `NativeMethods.cs` |\n| PowerShell | Scripts for build, sign, zip, cleanup | `Documents/`, `Resources/`, root |\n| XamlStyler | Config present | `Settings.XamlStyler` |\n\n**No NuGet package dependencies** in the main application project — all functionality comes from .NET SDK and the embedded `JocysCom.ClassLibrary`.\n\n## 4. Architecture & Runtime Model\n\nThis section describes how the application is structured and how it operates at runtime.\n\nFocusLogger is a **single-executable WPF desktop application** that polls Windows API functions to detect focus changes. It does not persist log data between sessions (in-memory only) but provides CSV export for offline analysis.\n\n### Architectural layers\n\n```mermaid\ngraph TD\n    subgraph UI[\"UI Layer (WPF)\"]\n        App[\"App.xaml.cs<br/>Entry point, DPI aware\"]\n        MW[\"MainWindow.xaml.cs<br/>Main window frame\"]\n        DLC[\"DataListControl.xaml.cs<br/>Core logging UI + logic\"]\n    end\n\n    subgraph Core[\"Core Logic\"]\n        DI[\"DataItem.cs<br/>Log entry model\"]\n        DIT[\"DataItemType.cs<br/>Entry type enum\"]\n        NM[\"NativeMethods.cs<br/>P/Invoke declarations\"]\n        CSV[\"CSV Export<br/>BuildCsvContent, CsvEscape\"]\n    end\n\n    subgraph Shared[\"JocysCom.ClassLibrary (embedded)\"]\n        Config[\"Configuration<br/>SettingsData, SettingsItem, AssemblyInfo\"]\n        CompModel[\"ComponentModel<br/>SortableBindingList, BindingListInvoked\"]\n        Controls[\"Controls<br/>ControlsHelper, ItemFormattingConverter,<br/>InfoControl, MessageBoxWindow\"]\n        Other[\"Collections, IO, Text, Runtime, Data\"]\n    end\n\n    subgraph WinAPI[\"Windows OS\"]\n        User32[\"user32.dll\"]\n    end\n\n    App --> MW --> DLC\n    DLC --> DI\n    DLC --> NM\n    DLC --> CSV\n    NM --> User32\n    DI --> Config\n    DLC --> CompModel\n    DLC --> Controls\n    MW --> Controls\n```\n\n### Key architectural decisions\n\n- **Polling via timer:** A `System.Timers.Timer` with 1ms interval (non-auto-reset) continuously polls `GetActiveWindow()` and `GetForegroundWindow()`. Duplicate events are suppressed via `DataItem.IsSame()`.\n- **Thread safety:** Timer fires on a thread-pool thread; UI updates are marshalled via `ControlsHelper.BeginInvoke()`. A `lock(AddLock)` synchronizes the polling logic.\n- **Embedded shared library:** `JocysCom.ClassLibrary` files are included directly in `FocusLogger/JocysCom/` rather than as a compiled DLL or NuGet package.\n- **No MVVM framework:** Code-behind pattern with data binding. `DataListControl.xaml.cs` contains both view-model-like logic and model interaction.\n\n## 5. Project Inventory\n\nThis section lists each project in the solution with its key metadata.\n\n### 5.1 JocysCom.FocusLogger (main application)\n\n| Property | Value |\n|----------|-------|\n| Path | `FocusLogger/JocysCom.FocusLogger.csproj` |\n| Output type | `WinExe` |\n| Target framework | `net8.0-windows` |\n| Assembly name | `JocysCom.FocusLogger` |\n| Description | Find out which process or program is taking the window focus. In game, mouse and keyboard could temporarily stop responding if another program takes the focus. This tool could help diagnose which program is stealing the focus. |\n| Version | 1.2.6 |\n| NuGet dependencies | None |\n| Embedded resources | `Resources/BuildDate.txt` (auto-generated), `Resources/AiAnalysisPrompt.md` |\n\n**Source structure:**\n\n| Directory | Contents |\n|-----------|----------|\n| `FocusLogger/` (root) | `App.xaml(.cs)`, `MainWindow.xaml(.cs)`, `AssemblyInfo.cs`, `App.ico` |\n| `FocusLogger/Common/` | `DataItem.cs`, `DataItemType.cs`, `NativeMethods.cs` |\n| `FocusLogger/Controls/` | `DataListControl.xaml(.cs)` — core logging control |\n| `FocusLogger/JocysCom/` | Embedded `JocysCom.ClassLibrary` (~30 files across Collections, Common, ComponentModel, Configuration, Controls, Data, IO, Runtime, Text) |\n| `FocusLogger/Resources/` | `AiAnalysisPrompt.md`, `BuildDate.txt`, `Icons/` (SVG sources, XAML icons, conversion scripts) |\n| `FocusLogger/Properties/` | Publish profiles |\n\n### 5.2 JocysCom.FocusLogger.Tests (test project)\n\n| Property | Value |\n|----------|-------|\n| Path | `FocusLogger.Tests/JocysCom.FocusLogger.Tests.csproj` |\n| Target framework | `net8.0-windows` |\n| Test framework | MSTest v3.x |\n| Project reference | `FocusLogger/JocysCom.FocusLogger.csproj` |\n\n**Test files:**\n\n| File | Purpose |\n|------|---------|\n| `CsvExportTests.cs` | Unit tests for `CsvEscape` and `BuildCsvContent` methods |\n| `UIAutomationTests.cs` | UI automation tests using `System.Windows.Automation` — launches the built app and interacts with controls by AutomationId |\n\nRun tests with: `dotnet test FocusLogger.Tests/JocysCom.FocusLogger.Tests.csproj`\n\n## 6. Dependency & Data Flow\n\nThis section explains how the projects and components relate to each other and how data moves through the system.\n\n### Project dependency graph\n\n```mermaid\ngraph LR\n    Tests[\"FocusLogger.Tests<br/>(MSTest v3)\"] -->|ProjectReference| App[\"JocysCom.FocusLogger<br/>(WPF App)\"]\n    App -->|embedded source| Shared[\"JocysCom.ClassLibrary<br/>(in FocusLogger/JocysCom/)\"]\n    App -->|P/Invoke| WinAPI[\"user32.dll\"]\n```\n\n### Runtime data flow\n\n1. `System.Timers.Timer` fires (1ms interval, non-auto-reset).\n2. `DataListControl.UpdateInfo()` acquires `AddLock`.\n3. Calls `NativeMethods.GetActiveWindow()` and `NativeMethods.GetForegroundWindow()`.\n4. For each handle, `GetItemFromHandle()` creates a `DataItem` with timestamp, focus flags (mouse/keyboard/caret), window title, and window class name.\n5. `IsSame()` checks if the event differs from the previous one; if not, it is skipped.\n6. `UpdateFromProcess()` enriches the `DataItem` with process name and path (with error handling for restricted processes).\n7. The item is inserted at position 0 of `SortableBindingList<DataItem>` via `ControlsHelper.BeginInvoke()` (UI thread dispatch).\n8. The WPF `DataGrid` updates via data binding. `ItemFormattingConverter` translates boolean flags to icons.\n\n### CSV export flow\n\n1. User clicks \"Save CSV\" button.\n2. `SaveFileDialog` prompts for file location.\n3. `BuildCsvContent()` iterates all `DataItem` entries, writing CSV with headers: Date, PID, Process Name, Active, Mouse, Keyboard, Caret, Window Title, Window Class, Path.\n4. File is written as UTF-8.\n5. \"Explore\" button opens the saved file location in Explorer.\n6. \"AI Prompt Example\" button shows the embedded `AiAnalysisPrompt.md` in a `MessageBoxWindow` for users to copy and paste into an AI assistant along with their CSV.\n\n## 7. Build, Test, CI/CD & Operational Workflows\n\nThis section documents how the project is built, tested, and released based on repository evidence.\n\n### Build\n\n```bash\ndotnet build JocysCom.FocusLogger.slnx\n```\n\n- **Pre-build event:** Generates `Resources/BuildDate.txt` with the current ISO 8601 timestamp via PowerShell.\n- **Output:** Single `JocysCom.FocusLogger.exe` in `bin/{Configuration}/net8.0-windows/`.\n- **Debug configuration:** Embedded PDB symbols (`DebugType: embedded`).\n\n### Test\n\n```bash\ndotnet test FocusLogger.Tests/JocysCom.FocusLogger.Tests.csproj\n```\n\n- **Framework:** MSTest v3.x with Microsoft.NET.Test.Sdk 17.x.\n- **Unit tests:** `CsvExportTests` — validates CSV escaping and content generation.\n- **UI automation tests:** `UIAutomationTests` — launches the built application and interacts via `System.Windows.Automation`. Requires a prior build of the main project.\n\n### Release / packaging scripts\n\n| Script | Purpose |\n|--------|---------|\n| `Documents/App_1_Sign.ps1` | Code-signs the application executable. |\n| `Documents/App_2_Zip.ps1` | Packages the signed executable into a release ZIP. |\n| `Resources/ZipFiles.ps1` | Shared utility for checksum-aware ZIP creation (compares source/dest checksums before rebuilding). |\n| `Documents/Take_Screenshot.ps1` | Captures application screenshot for documentation. |\n| `Documents/Take_Screenshot.ps1.cs` | C# helper compiled by the screenshot script. |\n| `Solution_Cleanup.ps1` | Cleans `bin/`, `obj/`, and other build artifacts. |\n\n### Icon workflow\n\nSVG icon sources are stored in `FocusLogger/Resources/Icons/Icons_Default/`. The script `Convert_SVG_to_XAML.ps1` converts them to XAML resource dictionaries (`Icons_Default.xaml`).\n\n### CI/CD\n\nNo CI/CD workflow files were found under `.github/workflows/`. Builds and releases appear to be performed locally.\n\n## 8. Documentation Map\n\nThis section identifies where documentation lives in the repository.\n\n| Location | Audience | Content |\n|----------|----------|---------|\n| `README.md` | End users, contributors | Project overview, download link, system requirements, screenshot |\n| `SECURITY.md` | Security researchers | Vulnerability reporting policy |\n| `LICENSE` | All | GPLv3 full text |\n| `.ai/ReadMe.md` | AI agents, developers | Explains the purpose of each file in the `.ai/` directory and how custom instructions work for Copilot, CLINE, and Codex |\n| `.ai/instructions.md` | AI agents | Role definition and output formatting rules for AI-assisted edits |\n| `.ai/coding-guideline.instructions.md` | AI agents | Detailed coding guidelines, source control conventions (branch/PR naming), testing workflow, and constraints for AI agents |\n| `.ai/repository-analysis.instructions.md` | AI agents, developers | This file — comprehensive repository reference |\n| `.ai/skills/` | AI agents | Skill definitions (e.g., `ai-self-improvement`) for agent-assisted workflows |\n| `FocusLogger/Resources/AiAnalysisPrompt.md` | End users | Prompt template for users to paste into AI assistants alongside exported CSV logs |\n| `Documents/Images/` | README, users | Application screenshot |\n| `Settings.XamlStyler` | Developers | XamlStyler formatting configuration |\n\n### Documentation taxonomy\n\n```mermaid\ngraph TD\n    subgraph EndUsers[\"End-User Documentation\"]\n        README[\"README.md<br/>Overview, download, requirements\"]\n        SECURITY[\"SECURITY.md<br/>Vulnerability reporting\"]\n        LICENSE[\"LICENSE<br/>GPLv3\"]\n        AiPrompt[\"FocusLogger/Resources/<br/>AiAnalysisPrompt.md<br/>Prompt template for CSV analysis\"]\n        Screenshot[\"Documents/Images/<br/>Application screenshot\"]\n    end\n\n    subgraph AIAgents[\"AI Agent Instructions\"]\n        AiReadMe[\".ai/ReadMe.md<br/>Directory guide\"]\n        AiInstr[\".ai/instructions.md<br/>Role & output rules\"]\n        AiCoding[\".ai/coding-guideline<br/>.instructions.md<br/>Coding & SCM conventions\"]\n        AiRepo[\".ai/repository-analysis<br/>.instructions.md<br/>This file\"]\n        AiSkills[\".ai/skills/<br/>Agent skill definitions\"]\n    end\n\n    subgraph DevConfig[\"Developer Configuration\"]\n        XamlStyler[\"Settings.XamlStyler<br/>XAML formatting\"]\n    end\n```\n\n## 9. AI-Agent-Relevant Conventions and Constraints\n\nThis section captures rules and patterns that materially affect automated edits. The full set of coding and source-control conventions is defined in `.ai/coding-guideline.instructions.md`; the highlights below are the items most likely to cause mistakes if overlooked.\n\n1. **Coding style:** Follow Microsoft C# conventions. PascalCase for public members, camelCase for locals. Some private fields use `_PascalCase` (e.g., `_Date`). Preserve existing naming patterns in each file. Prefer the simplest viable solution; avoid over-engineering.\n\n2. **Doc comments:** Per `.ai/instructions.md`, convert simple comments to XML documentation comments where beneficial for IntelliSense. Do not alter surrounding code when doing so.\n\n3. **Shared library files (`FocusLogger/JocysCom/`):** These are embedded from a shared `JocysCom.ClassLibrary`. Exercise caution when editing — changes here may diverge from the upstream library.\n\n4. **XAML formatting:** The repository uses XamlStyler (see `Settings.XamlStyler`). XAML edits should conform to the configured style.\n\n5. **No NuGet packages in the main app:** All dependencies are framework-provided or embedded source. Do not introduce NuGet package dependencies without explicit approval.\n\n6. **Test project uses MSTest v3:** New tests should follow MSTest v3 patterns (`[TestClass]`, `[TestMethod]`, `Assert.*`).\n\n7. **UI automation tests depend on a built executable:** `UIAutomationTests` locate the app at a relative path from the test output. Building the main project before running these tests is required.\n\n8. **Pre-build event:** The csproj generates `Resources/BuildDate.txt` via PowerShell. This file should not be manually edited or committed.\n\n9. **Solution format:** Uses `.slnx` (XML-based solution format), not the older `.sln` text format.\n\n10. **No CI/CD pipelines:** All build and release steps are manual/local. Scripts in `Documents/` handle signing and packaging.\n\n11. **File size limit:** No code file that is created or modified may exceed 6000 tokens (~24 KB).\n\n12. **Source control conventions:** Issues use `{CATEGORY}: {Description}` (FEAT, FIX, TECH, DOCS). Branches use `{CATEGORY}-{issue#}-{lowercase-dashed-name}`. PRs use `PR: #{issue#}: {CATEGORY}: {Description}` and must reference the issue with `Closes #{issue#}`. Merge via PR only — no direct pushes to `main`.\n\n13. **No Co-Authored-By lines:** Do not add `Co-Authored-By` AI model/company lines to commits.\n\n14. **Issue closure:** Never close issues before a release is published with the fix.\n\n15. **No duplication / SSOT:** Update or move existing code instead of adding parallel implementations. If introducing a replacement, remove the old one in the same change.\n\n16. **Stability primitives:** When the repo provides an established script or command, use it verbatim. Only deviate when it is proven broken, and then fix the primitive rather than inventing a parallel path.\n"
  },
  {
    "path": ".claude/skills/ai-self-improvement/SKILL.md",
    "content": "---\r\nname: ai-self-improvement\r\ndescription: Update, create, improve, and synchronise this repository's AI agent instructions and related assets (including skills). Use when the user asks to create or edit a skill/SKILL.md, modify the agent's own instructions/processes, restructure instruction governance, migrate instruction content into skills, or run/adjust the sync pipeline that publishes `.ai/` sources into agent-specific folders. Load this skill before writing any SKILL.md, .instructions.md, or touching any skills/ folder (.ai/, .claude/, .roo/, .github/). It tells you the correct location (.ai/) and the sync step, so files end up in the right place.\r\n---\r\n\r\n# AI Self-Improvement (Instructions + Skills)\r\n\r\n## Critical: `.ai/` is the Primary Source for ALL Agents\r\n\r\nThe `.ai/` folder is the **single source of truth** for all AI agent configurations in this repository. This applies to:\r\n\r\n- **CLINE / Roo Code** — synced to `.roo/rules/` and `.roo/skills/`\r\n- **GitHub Copilot** — synced to `.github/copilot-instructions.md`\r\n- **OpenAI Codex / AGENTS.md** — synced to `AGENTS.md` at repo root\r\n- **Claude Code** — synced to `.claude/*.instructions.md` and `.claude/skills/`\r\n\r\n**IMPORTANT:** When asked to modify skills, instructions, or perform any AI self-improvement task, you MUST:\r\n\r\n1. Locate the source file under `.ai/` (not the agent-specific output)\r\n2. Make changes to the `.ai/` source\r\n3. Run the sync script to propagate changes to all agents\r\n\r\n## Path Mapping Reference\r\n\r\nWhen you encounter a path in an agent-specific folder, map it to `.ai/`:\r\n\r\n| Agent-Specific Path | Source Path (Edit Here) |\r\n|---------------------|------------------------|\r\n| `.roo/rules/*.md` | `.ai/*.instructions.md` |\r\n| `.roo/skills/<name>/SKILL.md` | `.ai/skills/<name>/SKILL.md` |\r\n| `.github/copilot-instructions.md` | `.ai/instructions.md` (generated) |\r\n| `AGENTS.md` | `.ai/instructions.md` (generated) |\r\n| `.claude/*.instructions.md` | `.ai/*.instructions.md` |\r\n| `.claude/skills/<name>/SKILL.md` | `.ai/skills/<name>/SKILL.md` |\r\n\r\n**Example:** If asked to update `.roo/skills/ai-self-improvement/SKILL.md`, you must edit `.ai/skills/ai-self-improvement/SKILL.md` instead.\r\n\r\n## Editable instruction files (sources of truth)\r\n\r\nYou can update your own instruction files under `.ai/`:\r\n\r\n- `.ai/instructions.md` — the main system instructions file\r\n- `.ai/*instructions.md` — additional instruction files (auto-included)\r\n- `.ai/*instructions-detail.md` — detailed instruction files (read only when needed)\r\n- `.ai/skills/<name>/SKILL.md` — skill definition files\r\n\r\n## Workflow\r\n\r\n1. Treat `.ai/` as the **single source of truth** for agent instructions **and skills**.\r\n2. When creating or migrating a skill, create/update it under `.ai/skills/`.\r\n3. Make instruction changes in `.ai/instructions.md` and related `*.instructions.md` / `*.instructions-detail.md` files.\r\n4. Do **not** edit generated outputs directly (they are produced by the sync script):\r\n   - `.roo/rules/`\r\n   - `.roo/skills/`\r\n   - `.github/copilot-instructions.md`\r\n   - `AGENTS.md`\r\n   - `.claude/`\r\n5. **Test changes before syncing** — verify scripts execute correctly and changes work as expected.\r\n6. After testing, run the sync script to apply to all agents.\r\n\r\n## Testing Before Sync\r\n\r\nBefore running the sync script, always verify your changes work correctly:\r\n\r\n- **For script changes**: Execute the modified script and verify output is correct\r\n- **For instruction changes**: Review the markdown renders properly and instructions are clear\r\n- **For skill changes**: Test any bundled tools or scripts included in the skill\r\n\r\n**Example**: If you modify a PowerShell script in a skill, run it directly from `.ai/skills/<name>/scripts/` to confirm it works before syncing.\r\n\r\n## Activation process\r\n\r\nAfter editing instruction files (or master skills), run from repository root:\r\n\r\n```powershell\r\n.\\.ai\\skills\\ai-self-improvement\\scripts\\Sync-AgentAssets.ps1 AUTO\r\n```\r\n\r\nThis script synchronizes changes from `.ai/` to all agent-specific folders.\r\n\r\n## Single source of truth\r\n\r\n**Never embed template content in instructions — reference template files instead.**\r\n\r\nExample:\r\n\r\n- ✅ \"Template maintained in `pr/checklist.template.md`\"\r\n- ❌ Pasting template content into instructions\r\n\r\n## Bundled scripts\r\n\r\n- Sync entrypoint (instructions + skills): `.ai/skills/ai-self-improvement/scripts/Sync-AgentAssets.ps1`\r\n"
  },
  {
    "path": ".claude/skills/ai-self-improvement/scripts/Sync-AgentAssets.ps1",
    "content": "# Script: Sync-AgentAssets.ps1\r\n# Location: .ai/skills/ai-self-improvement/scripts/Sync-AgentAssets.ps1\r\n# Description:\r\n#   Synchronises AI agent instruction files and skills from master sources under `.ai/`.\r\n#   - Instructions: copies `*.instructions.md` from `.ai/` into agent-specific outputs.\r\n#   - Skills: mirrors `.ai/skills/*` into agent skill folders (e.g. `.roo/skills/*`).\r\n#\r\n# Options for Mode:\r\n#   ALL  - update all known agent outputs\r\n#   AUTO - update only agents that exist in this repository (default usage)\r\n#   Or a specific agent name: CLINE, ROO CODE, GitHub CoPilot, OpenAI Codex, Claude Code\r\n\r\nparam(\r\n    [Parameter(Position = 0)]\r\n    [string]$Mode,\r\n\r\n    [switch]$NoClear\r\n)\r\n\r\n# Combine remaining args so Windows PowerShell (-File) invocations like:\r\n#   Sync-AgentAssets.ps1 GitHub CoPilot\r\n# work the same as:\r\n#   Sync-AgentAssets.ps1 \"GitHub CoPilot\"\r\nif ($args.Count -gt 0) {\r\n    $ModeFromArgs = ($args -join ' ')\r\n    if (-not $Mode -or $Mode -eq '') {\r\n        $Mode = $ModeFromArgs\r\n    }\r\n}\r\n\r\n# Allow calling via the old filename (if invoked through a copied/renamed script).\r\n# This only affects displayed script name in prompts/logs.\r\n$scriptName = [System.IO.Path]::GetFileName($MyInvocation.MyCommand.Path)\r\n\r\n# Strict mode\r\nSet-StrictMode -Version Latest\r\n$ErrorActionPreference = \"Stop\"\r\n\r\nfunction Ensure-Directory {\r\n    param(\r\n        [Parameter(Mandatory = $true)]\r\n        [string]$Path\r\n    )\r\n\r\n    if (-not (Test-Path -Path $Path -PathType Container)) {\r\n        New-Item -ItemType Directory -Force -Path $Path | Out-Null\r\n    }\r\n}\r\n\r\n# Function to check if instruction files exist in a directory\r\nfunction Test-HasInstructionFiles {\r\n    param(\r\n        [Parameter(Mandatory = $true)]\r\n        [string]$Path,\r\n        [string]$Filter = '*instructions.md'\r\n    )\r\n\r\n    if (Test-Path $Path -PathType Container) {\r\n        $files = @(Get-ChildItem $Path -Filter $Filter -File -ErrorAction SilentlyContinue)\r\n        return ($files.Length -gt 0)\r\n    }\r\n\r\n    return $false\r\n}\r\n\r\n# Function to pause at the end (unless -NoWait is specified)\r\nfunction Invoke-Pause {\r\n    Write-Host \"Pausing for 2 seconds...\"\r\n    Start-Sleep -Seconds 2\r\n}\r\n\r\nfunction Copy-FileIfDifferent {\r\n    param(\r\n        [Parameter(Mandatory = $true)]\r\n        [string]$SourcePath,\r\n        [Parameter(Mandatory = $true)]\r\n        [string]$TargetPath\r\n    )\r\n\r\n    $targetDir = Split-Path -Path $TargetPath -Parent\r\n    Ensure-Directory -Path $targetDir\r\n\r\n    if (-not (Test-Path -Path $TargetPath -PathType Leaf)) {\r\n        Copy-Item -LiteralPath $SourcePath -Destination $TargetPath -Force\r\n        $relative = $TargetPath.Substring($repoRoot.Length + 1)\r\n        Write-Host \"Created: $relative\"\r\n        return\r\n    }\r\n\r\n    $srcBytes = [System.IO.File]::ReadAllBytes($SourcePath)\r\n    $dstBytes = [System.IO.File]::ReadAllBytes($TargetPath)\r\n\r\n    if ($srcBytes.Length -eq $dstBytes.Length) {\r\n        $same = $true\r\n        for ($i = 0; $i -lt $srcBytes.Length; $i++) {\r\n            if ($srcBytes[$i] -ne $dstBytes[$i]) { $same = $false; break }\r\n        }\r\n\r\n        if ($same) {\r\n            $relative = $TargetPath.Substring($repoRoot.Length + 1)\r\n            Write-Host \"Up-to-date: $relative\"\r\n            return\r\n        }\r\n    }\r\n\r\n    Copy-Item -LiteralPath $SourcePath -Destination $TargetPath -Force\r\n    $relative = $TargetPath.Substring($repoRoot.Length + 1)\r\n    Write-Host \"Updated: $relative\"\r\n}\r\n\r\nfunction Get-TextAuto {\r\n    param(\r\n        [Parameter(Mandatory = $true)]\r\n        [string]$Path\r\n    )\r\n\r\n    # .NET StreamReader detects BOM for UTF-8/UTF-16/UTF-32 automatically.\r\n    $sr = New-Object System.IO.StreamReader($Path, $true)\r\n    try {\r\n        return $sr.ReadToEnd()\r\n    }\r\n    finally {\r\n        $sr.Dispose()\r\n    }\r\n}\r\n\r\nfunction Write-Utf8NoBom {\r\n    param(\r\n        [Parameter(Mandatory = $true)]\r\n        [string]$Path,\r\n        [Parameter(Mandatory = $true)]\r\n        [string]$Content\r\n    )\r\n\r\n    $dir = Split-Path -Path $Path -Parent\r\n    Ensure-Directory -Path $dir\r\n\r\n    $utf8NoBom = New-Object System.Text.UTF8Encoding($false)\r\n    [System.IO.File]::WriteAllText($Path, $Content, $utf8NoBom)\r\n}\r\n\r\nfunction Assert-InstructionSync {\r\n    param(\r\n        [Parameter(Mandatory = $true)]\r\n        [string]$SourceDirectory,\r\n        [Parameter(Mandatory = $true)]\r\n        [string]$TargetDirectory,\r\n        [Parameter(Mandatory = $true)]\r\n        [System.IO.FileSystemInfo[]]$SourceFiles\r\n    )\r\n\r\n    $srcDir = Join-Path $repoRoot $SourceDirectory\r\n    $dstDir = Join-Path $repoRoot $TargetDirectory\r\n\r\n    foreach ($sourceFile in $SourceFiles) {\r\n        $srcPath = Join-Path $srcDir $sourceFile.Name\r\n        $dstPath = Join-Path $dstDir $sourceFile.Name\r\n\r\n        if (-not (Test-Path $dstPath -PathType Leaf)) {\r\n            throw \"Binary comparison failed. Destination file missing: $dstPath\"\r\n        }\r\n\r\n        $srcBytes = [System.IO.File]::ReadAllBytes($srcPath)\r\n        $dstBytes = [System.IO.File]::ReadAllBytes($dstPath)\r\n\r\n        if ($srcBytes.Length -ne $dstBytes.Length) {\r\n            throw \"Binary comparison failed. Source and target size mismatch in binary: Source: $srcPath Target: $dstPath\"\r\n        }\r\n\r\n        for ($i = 0; $i -lt $srcBytes.Length; $i++) {\r\n            if ($srcBytes[$i] -ne $dstBytes[$i]) {\r\n                throw \"Binary comparison failed. Source and target content mismatch in binary: Source: $srcPath Target: $dstPath\"\r\n            }\r\n        }\r\n    }\r\n}\r\n\r\n# Function to update agents that use multiple separate instruction files\r\nfunction Update-MultipleFileAgent {\r\n    param(\r\n        [Parameter(Mandatory = $true)]\r\n        [string]$AgentName,\r\n        [Parameter(Mandatory = $true)]\r\n        [string]$TargetDirectory,\r\n        [Parameter(Mandatory = $true)]\r\n        [System.IO.FileSystemInfo[]]$SourceFiles,\r\n        [Parameter(Mandatory = $true)]\r\n        [string]$RepoRoot\r\n    )\r\n\r\n    Write-Host \"`r`n--- Updating $AgentName Instructions ---\"\r\n    $targetDir = Join-Path $RepoRoot $TargetDirectory\r\n\r\n    foreach ($sourceFile in $SourceFiles) {\r\n        $targetFile = Join-Path $targetDir $sourceFile.Name\r\n        Copy-FileIfDifferent -SourcePath $sourceFile.FullName -TargetPath $targetFile\r\n    }\r\n\r\n    Assert-InstructionSync -SourceDirectory \".ai\" -TargetDirectory $TargetDirectory -SourceFiles $SourceFiles\r\n}\r\n\r\n# Function to update agents that use a single combined instruction file\r\nfunction Update-SingleFileAgent {\r\n    param(\r\n        [Parameter(Mandatory = $true)]\r\n        [string]$AgentName,\r\n        [Parameter(Mandatory = $true)]\r\n        [string]$TargetFilePath,\r\n        [Parameter(Mandatory = $true)]\r\n        [System.IO.FileSystemInfo[]]$SourceFiles,\r\n        [Parameter(Mandatory = $true)]\r\n        [string]$RepoRoot\r\n    )\r\n\r\n    Write-Host \"`r`n--- Updating $AgentName Instructions ---\"\r\n    $targetFile = Join-Path $RepoRoot $TargetFilePath\r\n    $relativeTarget = $targetFile.Substring($repoRoot.Length + 1)\r\n\r\n    $allInstructionsContent = New-Object System.Text.StringBuilder\r\n    $firstFile = $true\r\n\r\n    foreach ($sourceFile in $SourceFiles) {\r\n        $sourceContent = Get-TextAuto -Path $sourceFile.FullName\r\n\r\n        if ([string]::IsNullOrWhiteSpace($sourceContent)) {\r\n            Write-Warning \"Skipping empty file: $($sourceFile.Name)\"\r\n            continue\r\n        }\r\n\r\n        if (-not $firstFile) {\r\n            [void]$allInstructionsContent.AppendLine(\"\")\r\n        }\r\n\r\n        [void]$allInstructionsContent.AppendLine(\"==== START OF INSTRUCTIONS FROM: $($sourceFile.Name) ====\")\r\n        [void]$allInstructionsContent.AppendLine(\"\")\r\n\r\n        [void]$allInstructionsContent.AppendLine(\"# Instructions from: $($sourceFile.Name)\")\r\n        [void]$allInstructionsContent.AppendLine(\"\")\r\n\r\n        [void]$allInstructionsContent.AppendLine($sourceContent.Trim())\r\n\r\n        [void]$allInstructionsContent.AppendLine(\"\")\r\n        [void]$allInstructionsContent.AppendLine(\"==== END OF INSTRUCTIONS FROM: $($sourceFile.Name) ====\")\r\n\r\n        $firstFile = $false\r\n    }\r\n\r\n    $finalContent = $allInstructionsContent.ToString()\r\n\r\n    $existing = if (Test-Path -Path $targetFile -PathType Leaf) { Get-TextAuto -Path $targetFile } else { $null }\r\n    if ($null -ne $existing -and $existing -eq $finalContent) {\r\n        Write-Host \"Up-to-date: $relativeTarget\"\r\n        return\r\n    }\r\n\r\n    Write-Utf8NoBom -Path $targetFile -Content $finalContent\r\n    Write-Host \"Updated: $relativeTarget\"\r\n}\r\n\r\nfunction Invoke-RoboCopyMirror {\r\n    param(\r\n        [Parameter(Mandatory = $true)]\r\n        [string]$SourceDirectory,\r\n        [Parameter(Mandatory = $true)]\r\n        [string]$DestinationDirectory,\r\n        [Parameter(Mandatory = $true)]\r\n        [string]$Label\r\n    )\r\n\r\n    if (-not (Test-Path $SourceDirectory -PathType Container)) {\r\n        Write-Host \"No skills folder found at: $SourceDirectory\"\r\n        return\r\n    }\r\n\r\n    Ensure-Directory -Path $DestinationDirectory\r\n\r\n    Write-Host \"`r`n--- Mirroring skills to $Label ---\"\r\n    Write-Host \"Source:      $SourceDirectory\"\r\n    Write-Host \"Destination: $DestinationDirectory\"\r\n\r\n    # /MIR      = mirror (copy + delete removed)\r\n    # /FFT      = tolerate 2s timestamp granularity\r\n    # /R:1 /W:1 = retry quickly\r\n    # /NFL/NDL  = no file/dir listing (keep output compact)\r\n    # /NJH/NJS  = no job header/summary\r\n    # /NP       = no progress\r\n    # /XD       = exclude version control/build dirs\r\n    $excludedDirs = @('.git', '.vs', 'bin', 'obj')\r\n\r\n    $args = @(\r\n        $SourceDirectory,\r\n        $DestinationDirectory,\r\n        '/MIR',\r\n        '/FFT',\r\n        '/R:1',\r\n        '/W:1',\r\n        '/NFL',\r\n        '/NDL',\r\n        '/NJH',\r\n        '/NJS',\r\n        '/NP'\r\n    )\r\n\r\n    foreach ($d in $excludedDirs) {\r\n        $args += '/XD'\r\n        $args += $d\r\n    }\r\n\r\n    $exe = 'robocopy'\r\n    # Do not echo the full robocopy command; it is noisy and can wrap in some terminals.\r\n    Write-Host \"robocopy <source> <destination> /MIR /NFL /NDL /NJH /NJS /NP ...\"\r\n\r\n    & $exe @args | Out-Null\r\n    $exitCode = $LASTEXITCODE\r\n\r\n    # Robocopy uses bitmask exit codes.\r\n    # 0-7 are success with various flags; >= 8 indicates failure.\r\n    if ($exitCode -ge 8) {\r\n        throw \"Robocopy failed with exit code $exitCode. Command: $cmd\"\r\n    }\r\n\r\n    # IMPORTANT: robocopy returns 1+ for successful copies.\r\n    # Ensure PowerShell script does not propagate a non-zero exit code for success cases.\r\n    $global:LASTEXITCODE = 0\r\n\r\n    Write-Host \"Mirrored skills to $Label (robocopy exit code $exitCode).\"\r\n}\r\n\r\nfunction Sync-SkillsToRoo {\r\n    param(\r\n        [Parameter(Mandatory = $true)]\r\n        [string]$RepoRoot\r\n    )\r\n\r\n    $srcSkillsRoot = Join-Path $RepoRoot \".ai\\skills\"\r\n    $rooSkillsRoot = Join-Path $RepoRoot \".roo\\skills\"\r\n\r\n    Invoke-RoboCopyMirror -SourceDirectory $srcSkillsRoot -DestinationDirectory $rooSkillsRoot -Label \"Roo (.roo\\\\skills)\"\r\n}\r\n\r\nfunction Sync-SkillsToGitHub {\r\n    param(\r\n        [Parameter(Mandatory = $true)]\r\n        [string]$RepoRoot\r\n    )\r\n\r\n    $srcSkillsRoot = Join-Path $RepoRoot \".ai\\skills\"\r\n    $githubSkillsRoot = Join-Path $RepoRoot \".github\\skills\"\r\n\r\n    Invoke-RoboCopyMirror -SourceDirectory $srcSkillsRoot -DestinationDirectory $githubSkillsRoot -Label \"GitHub (.github\\\\skills)\"\r\n}\r\n\r\nfunction Sync-SkillsToClaude {\r\n    param(\r\n        [Parameter(Mandatory = $true)]\r\n        [string]$RepoRoot\r\n    )\r\n\r\n    $srcSkillsRoot = Join-Path $RepoRoot \".ai\\skills\"\r\n    $claudeSkillsRoot = Join-Path $RepoRoot \".claude\\skills\"\r\n\r\n    Invoke-RoboCopyMirror -SourceDirectory $srcSkillsRoot -DestinationDirectory $claudeSkillsRoot -Label \"Claude Code (.claude\\\\skills)\"\r\n}\r\n\r\n# --- Main Script ---\r\nif (-not $NoClear) {\r\n    Clear-Host\r\n}\r\n\r\n# We are located under `.ai/skills/<skill>/tools`. Find repo root by going up 4 levels.\r\n$scriptDir = $PSScriptRoot\r\n$repoRoot = (Join-Path -Path $scriptDir -ChildPath \"..\\..\\..\\..\" | Resolve-Path).Path\r\n\r\n# `.ai` folder path\r\n$aiDir = Join-Path $repoRoot \".ai\"\r\n\r\n# Discover source files matching *instructions.md in the .ai folder\r\n[System.IO.FileSystemInfo[]]$sourceInstructionFiles = Get-ChildItem -Path $aiDir -Filter \"*instructions.md\" -File | Sort-Object Name\r\n\r\nif ($null -eq $sourceInstructionFiles -or $sourceInstructionFiles.Length -eq 0) {\r\n    Write-Warning \"No '*instructions.md' files found in '$aiDir'. Nothing to process.\"\r\n    exit 0\r\n}\r\n\r\nWrite-Host \"Found the following source instruction files in '$aiDir':\"\r\n$sourceInstructionFiles | ForEach-Object { Write-Host \"- $($_.Name)\" }\r\n\r\n# Mode parameter handling: if 'ALL' or 'AUTO', skip interactive prompt\r\nif ($Mode -eq 'ALL') {\r\n    Write-Host \"Selected: ALL (parameter mode)\"\r\n    $updateCline = $true\r\n    $updateCopilot = $true\r\n    $updateRooCode = $true\r\n    $updateCodex = $true\r\n    $updateClaude = $true\r\n}\r\nelseif ($Mode -eq 'AUTO') {\r\n    Write-Host \"Selected: AUTO (parameter mode)\"\r\n    # Determine available agents based on instruction files\r\n    $updateCline = Test-HasInstructionFiles -Path (Join-Path $repoRoot '.clinerules')\r\n    $updateRooCode = Test-HasInstructionFiles -Path (Join-Path $repoRoot '.roo\\rules')\r\n    $updateCopilot = Test-Path (Join-Path $repoRoot '.github\\copilot-instructions.md') -PathType Leaf\r\n    $updateCodex = Test-Path (Join-Path $repoRoot 'AGENTS.md') -PathType Leaf\r\n    $updateClaude = Test-HasInstructionFiles -Path (Join-Path $repoRoot '.claude')\r\n    Write-Host \"Agents to update based on available instruction files:\"\r\n    if ($updateCline) { Write-Host \"- CLINE\" }\r\n    if ($updateRooCode) { Write-Host \"- ROO CODE\" }\r\n    if ($updateCopilot) { Write-Host \"- GitHub CoPilot\" }\r\n    if ($updateCodex) { Write-Host \"- OpenAI Codex\" }\r\n    if ($updateClaude) { Write-Host \"- Claude Code\" }\r\n}\r\nelseif ($Mode -and $Mode -ne '') {\r\n    # Specific agent mode (e.g., CLINE, \"ROO CODE\", etc.)\r\n    $updateCline = ($Mode -eq 'CLINE')\r\n    $updateCopilot = ($Mode -eq 'GitHub CoPilot')\r\n    $updateRooCode = ($Mode -eq 'ROO CODE')\r\n    $updateCodex = ($Mode -eq 'OpenAI Codex')\r\n    $updateClaude = ($Mode -eq 'Claude Code')\r\n    Write-Host \"Selected: $Mode (parameter mode)\"\r\n}\r\nelse {\r\n    # User prompt for agent selection\r\n    # Detect available agents for interactive menu\r\n    $hasCline = Test-HasInstructionFiles -Path (Join-Path $repoRoot '.clinerules')\r\n    $hasRooCode = Test-HasInstructionFiles -Path (Join-Path $repoRoot '.roo\\rules')\r\n    $hasCopilot = Test-Path (Join-Path $repoRoot '.github\\copilot-instructions.md') -PathType Leaf\r\n    $hasCodex = Test-Path (Join-Path $repoRoot 'AGENTS.md') -PathType Leaf\r\n    $hasClaude = Test-HasInstructionFiles -Path (Join-Path $repoRoot '.claude')\r\n\r\n    Write-Host \"`r`nDetected AI agents with instruction files:\"\r\n    if ($hasCline) { Write-Host \"- CLINE\" }\r\n    if ($hasRooCode) { Write-Host \"- ROO CODE\" }\r\n    if ($hasCopilot) { Write-Host \"- GitHub CoPilot\" }\r\n    if ($hasCodex) { Write-Host \"- OpenAI Codex\" }\r\n    if ($hasClaude) { Write-Host \"- Claude Code\" }\r\n\r\n    Write-Host \"\"\r\n    Write-Host \"==============================================================\"\r\n    Write-Host \"Select Agent Instruction Set to Update\"\r\n    Write-Host \"--------------------------------------------------------------\"\r\n    Write-Host \"1. AUTO           - Update only agents with instruction files (default)\"\r\n    Write-Host \"2. ALL            - Update instructions for all AI agents\"\r\n    Write-Host \"3. CLINE          - Update instructions for CLINE\"\r\n    Write-Host \"4. ROO CODE       - Update instructions for ROO CODE\"\r\n    Write-Host \"5. GitHub CoPilot - Update instructions for GitHub CoPilot\"\r\n    Write-Host \"6. OpenAI Codex   - Update instructions for OpenAI Codex\"\r\n    Write-Host \"7. Claude Code    - Update instructions for Claude Code\"\r\n    Write-Host \"0. Exit\"\r\n    Write-Host \"==============================================================\"\r\n    $selection = Read-Host \"Enter the number of your choice (0-7)\"\r\n\r\n    # Initialize flags\r\n    $updateCline = $false\r\n    $updateCopilot = $false\r\n    $updateRooCode = $false\r\n    $updateCodex = $false\r\n    $updateClaude = $false\r\n\r\n    switch ($selection) {\r\n        '1' {\r\n            $updateCline = Test-HasInstructionFiles -Path (Join-Path $repoRoot '.clinerules')\r\n            $updateRooCode = Test-HasInstructionFiles -Path (Join-Path $repoRoot '.roo\\rules')\r\n            $updateCopilot = Test-Path (Join-Path $repoRoot '.github\\copilot-instructions.md') -PathType Leaf\r\n            $updateCodex = Test-Path (Join-Path $repoRoot 'AGENTS.md') -PathType Leaf\r\n            $updateClaude = Test-HasInstructionFiles -Path (Join-Path $repoRoot '.claude')\r\n            Write-Host \"Selected: AUTO\"\r\n        }\r\n        '2' {\r\n            $updateCline = $true\r\n            $updateCopilot = $true\r\n            $updateRooCode = $true\r\n            $updateCodex = $true\r\n            $updateClaude = $true\r\n            Write-Host \"Selected: ALL\"\r\n        }\r\n        '3' { $updateCline = $true; Write-Host \"Selected: CLINE\" }\r\n        '4' { $updateRooCode = $true; Write-Host \"Selected: ROO CODE\" }\r\n        '5' { $updateCopilot = $true; Write-Host \"Selected: GitHub CoPilot\" }\r\n        '6' { $updateCodex = $true; Write-Host \"Selected: OpenAI Codex\" }\r\n        '7' { $updateClaude = $true; Write-Host \"Selected: Claude Code\" }\r\n        '0' { Write-Host \"Operation cancelled by user.\"; exit 0 }\r\n        default { throw \"Invalid selection. Exiting.\" }\r\n    }\r\n}\r\n\r\n# --- Multiple-File Agent Updates ---\r\nif ($updateCline) {\r\n    Update-MultipleFileAgent -AgentName \"CLINE\" -TargetDirectory \".clinerules\" -SourceFiles $sourceInstructionFiles -RepoRoot $repoRoot\r\n}\r\n\r\nif ($updateRooCode) {\r\n    Update-MultipleFileAgent -AgentName \"ROO CODE\" -TargetDirectory \".roo\\rules\" -SourceFiles $sourceInstructionFiles -RepoRoot $repoRoot\r\n}\r\n\r\n# --- Single-File Agent Updates ---\r\nif ($updateCopilot) {\r\n    $copilotTarget = \".github\\copilot-instructions.md\"\r\n    $githubInstructionsDir = Join-Path $repoRoot \".github\\instructions\"\r\n\r\n    if (Test-Path $githubInstructionsDir -PathType Container) {\r\n        Write-Host \"`r`n--- Updating GitHub CoPilot Instructions (folder-based) ---\"\r\n\r\n        $mainName = \"instructions.md\"\r\n        $mainSource = $sourceInstructionFiles | Where-Object { $_.Name -ieq $mainName } | Select-Object -First 1\r\n        if ($null -eq $mainSource) {\r\n            throw \"Expected source '$mainName' under .ai but none found.\"\r\n        }\r\n\r\n        Copy-FileIfDifferent -SourcePath $mainSource.FullName -TargetPath (Join-Path $repoRoot $copilotTarget)\r\n\r\n        foreach ($sf in $sourceInstructionFiles) {\r\n            if ($sf.Name -ieq $mainName) {\r\n                continue\r\n            }\r\n\r\n            $destination = Join-Path $githubInstructionsDir $sf.Name\r\n            Copy-FileIfDifferent -SourcePath $sf.FullName -TargetPath $destination\r\n        }\r\n    }\r\n    else {\r\n        Update-SingleFileAgent -AgentName \"GitHub CoPilot\" -TargetFilePath $copilotTarget -SourceFiles $sourceInstructionFiles -RepoRoot $repoRoot\r\n    }\r\n}\r\n\r\nif ($updateCodex) {\r\n    Update-SingleFileAgent -AgentName \"OpenAI Codex\" -TargetFilePath \"AGENTS.md\" -SourceFiles $sourceInstructionFiles -RepoRoot $repoRoot\r\n}\r\n\r\n# --- Claude Code (multiple-file agent) ---\r\nif ($updateClaude) {\r\n    Update-MultipleFileAgent -AgentName \"Claude Code\" -TargetDirectory \".claude\" -SourceFiles $sourceInstructionFiles -RepoRoot $repoRoot\r\n}\r\n\r\n# --- Skills mirroring ---\r\nif ($updateRooCode -or $Mode -eq 'ALL' -or $Mode -eq 'AUTO') {\r\n    Sync-SkillsToRoo -RepoRoot $repoRoot\r\n}\r\n\r\n# GitHub Copilot: mirror skills to `.github/skills` (Copilot tries to load from there).\r\nif ($updateCopilot -or $Mode -eq 'ALL' -or $Mode -eq 'AUTO') {\r\n    Sync-SkillsToGitHub -RepoRoot $repoRoot\r\n}\r\n\r\n# Claude Code: mirror skills to `.claude/skills`.\r\nif ($updateClaude -or $Mode -eq 'ALL' -or $Mode -eq 'AUTO') {\r\n    Sync-SkillsToClaude -RepoRoot $repoRoot\r\n}\r\n\r\nWrite-Host \"`r`nAll selected operations completed successfully.\"\r\n\r\n# Only pause when launched by double-click (Explorer). In CI / terminal usage, do not pause.\r\nif ($Host.Name -and $Host.Name -notlike '*ConsoleHost*') {\r\n    Invoke-Pause\r\n}\r\n"
  },
  {
    "path": ".editorconfig",
    "content": "root = true\n\n[*]\ncharset = utf-8\n# Line endings: Let Git handle normalization via .gitattributes and core.autocrlf\n# - Repository stores LF (normalized by Git)\n# - Working tree uses OS-native line endings (CRLF on Windows, LF on Unix)\n# - Uncomment below to force editors to use LF (useful for LF-everywhere workflows):\n# end_of_line = lf\ninsert_final_newline = true\ntrim_trailing_whitespace = true\n\n# Spaces at the end can be meaningful in Markdown (hard line breaks)\n[*.md]\ntrim_trailing_whitespace = false\n\n# Windows PowerShell 5.1 can misinterpret UTF-8 without BOM when non-ASCII exists.\n# If you are PowerShell 7+ only, you may prefer charset = utf-8 here.\n[*.ps1]\ncharset = utf-8-bom\n"
  },
  {
    "path": ".github/copilot-instructions.md",
    "content": "﻿## Role\n\nYour role is to analyze and improve code by making only localized, targeted changes. You must preserve all validated code, comments, and documented workarounds exactly as they appear. Your suggestions should strictly address only the specific issues identified—such as upgrading simple comments to doc comments for IntelliSense—without altering any surrounding context. Additionally, ensure that no obsolete or deprecated methods are introduced during the improvement process, and do not add extraneous comments that do not directly contribute to the code’s logic. Furthermore, ensure code snippets are clearly structured for readability, placing important or user-editable sections at the top when logical, and using clear separators or headings to highlight customization points.\nWherever beneficial, convert simple comments into recognized documentation comment syntax (e.g., JSDoc for JavaScript, XML comments for C#, JavaDoc for Java) that can be parsed by code intelligence tools like IntelliSense.\nMaintain the original meaning of these comments, but structure them in a way that provides maximum benefit for automated tools and refactoring methods.\nApply chain-of-thought reasoning to identify code segments best served by doc comments, analyze the existing context of each comment, and then make precise, incremental modifications that enhance IntelliSense compatibility while preserving existing functionality.\n\n## Output\n\nWrap any and all code—including regular code snippets, inline code segments, outputs, pseudocode, or any text that represents code—in Markdown code blocks with a language identifier (e.g., ```typescript, ```powershell).\n"
  },
  {
    "path": ".github/instructions/coding-guideline.instructions.md",
    "content": "﻿# Coding Guidelines\r\n\r\n- If the qdrant-mcp-server is running, use it for all permanent memory operations (e.g. storing user information).\r\n- After making changes, ALWAYS start a new server for testing.\r\n- Kill all existing related servers from previous testing before starting a new server.\r\n- Prefer the simplest viable solution; avoid over-engineering.\r\n- Do not add broad try/catch or wrapper layers unless required by a failing test or explicit requirement; if you catch, rethrow to preserve the stack.\r\n- Before writing new code, actively look for existing utilities or functions that can be reused instead of duplicated.\r\n- New helper methods or classes must be justified with a clear, documented need for functionality that is unavailable elsewhere in the codebase.\r\n- Always iterate on and reuse existing code instead of creating new implementations.\r\n- Avoid adding layers of abstraction that do not deliver clear value.\r\n- Do not drastically change established patterns before iterating on them.\r\n- No duplication / SSOT: update or move existing code instead of adding parallel implementations. If you introduce a replacement, remove the old one **in the same change**.\r\n- Write code that accounts for different environments (dev, test, and prod).\r\n- Only modify what is explicitly requested or clearly necessary; do **not** create new files or modules unless explicitly requested.\r\n- When fixing bugs, exhaust current implementations before introducing new patterns; if new methods are used, remove the old ones.\r\n- Keep the codebase clean and organized.\r\n- Avoid one-off scripts unless absolutely necessary.\r\n- Use mocks only for tests, not for dev or prod.\r\n- Never add stubbing or fake data in dev or prod environments.\r\n- Never overwrite the .env file without explicit confirmation.\r\n- Focus solely on areas relevant to the task; leave unrelated code untouched.\r\n- Write thorough tests for all major functionality.\r\n- Avoid major changes to the existing architecture unless explicitly instructed.\r\n- Always consider the impact on other methods and areas of the code.\r\n- Prefer to wrap long lines for better readability.\r\n- Preserve existing formatting; limit formatting to lines you changed and match surrounding style. Also remove any unused imports/usings or dead code **introduced by your edits**.\r\n- Stability primitives (repeatability > cleverness): when the repo provides an established way to perform an operation (repo-owned script, documented command snippet, standard PS1 under .ai/Scripts), treat it as the single source of truth. Use it verbatim instead of synthesizing “equivalent” commands. Only deviate when explicitly asked or when the primitive is proven broken in this environment (and then fix the primitive, not invent a parallel path).\r\n- No code file that you **create or modify** may exceed **6000 tokens (~24 KB)** once your changes are applied.\r\n  - If your changes alone would push the file past this limit, either trim the change or ask for explicit permission to refactor; do **not** alter unrelated code solely to meet the limit.\r\n  - Existing oversized files are left untouched unless the user explicitly requests a refactor.\r\n\r\n## Source Control Conventions\r\n\r\nNaming conventions for issues, branches, and pull requests.\r\n\r\n### Categories\r\n\r\n| Category | Use for |\r\n|----------|---------|\r\n| `FEAT`   | New features |\r\n| `FIX`    | Bug fixes |\r\n| `TECH`   | Infrastructure, dependencies, refactoring |\r\n| `DOCS`   | Documentation |\r\n\r\n### Naming Patterns\r\n\r\n| Item       | Pattern | Example |\r\n|------------|---------|---------|\r\n| **Issue**  | `{CATEGORY}: {Description}` | `FEAT: Download logs to CSV` |\r\n| **Branch** | `{CATEGORY}-{issue#}-{lowercase-dashed-name}` | `FEAT-21-download-logs-to-csv` |\r\n| **PR**     | `PR: #{issue#}: {CATEGORY}: {Description}` | `PR: #21: FEAT: Download logs to CSV` |\r\n\r\n- Branch names: lowercase, words separated by dashes, non-ASCII replaced with a single dash, derived from issue title but may be shortened.\r\n- PR body must reference the issue with `Closes #{issue#}`.\r\n- Merge via PR only — no direct pushes to `main` or `master`.\r\n- Feature branches are always created from `main` or `master`. Never merge one feature branch into another — only merge `main` if you need to catch up.\r\n- Always create branches from latest origin/main or origin/main, never merge sub-branches, confirm before risky git ops.\r\n- Do not add Co-Authored-By {AI model}/{AI company} lines to commits.\r\n- Never close issues before a release is published with the fix.\r\n\r\nUse the following guidelines:\r\n\r\n1. Doc Comment Enhancement for IntelliSense\r\n\r\n    - Replace or augment simple comments with relevant doc comment syntax that is supported by IntelliSense as needed.\r\n    - Preserve the original intent and wording of existing comments wherever possible.\r\n\r\n2. Code Layout for Clarity\r\n\r\n    - Place the most important or user-editable sections at the top if logically appropriate.\r\n    - Insert headings or separators within the code to clearly delineate where customizations or key logic sections can be adjusted.\r\n\r\n3. No Extraneous Code Comments\r\n\r\n    - Do not include \"one-off\" or user-directed commentary in the code.\r\n    - Confine all clarifications or additional suggestions to explanations outside of the code snippet.\r\n\r\n4. Avoid Outdated or Deprecated Methods\r\n\r\n    - Refrain from introducing or relying on obsolete or deprecated methods and libraries.\r\n    - If the current code relies on potentially deprecated approaches, ask for clarification or provide viable, modern alternatives that align with best practices.\r\n\r\n5. Testing and Validation\r\n\r\n    - Suggest running unit tests or simulations on the modified segments to confirm that the changes fix the issue without impacting overall functionality.\r\n    - Ensure that any proposed improvements, including doc comment upgrades, integrate seamlessly with the existing codebase.\r\n    - After all code modifications, navigate to the affected project directory and build C# then Angular to confirm the application compiles without errors:\r\n\t\tcd {PROJECT} && dotnet build {PROJECT}.csproj\r\n\t\tcd {PROJECT}/ClientApp && ng build\r\n    - Run relevant unit tests if code changes affect core logic.\r\n    - If the developer certificate is not trusted, then execute: dotnet dev-certs https --trust\r\n    - To launch project use: dotnet watch run --project {PROJECT}/{PROJECT}.csproj --launch-profile \"{PROJECT} (NG Build)\"\r\n\r\n6. Rationale and Explanation\r\n\r\n    - For every change (including comment conversions), provide a concise explanation detailing how the modification resolves the identified issue while preserving the original design and context.\r\n    - Clearly highlight only the modifications made, ensuring that no previously validated progress is altered.\r\n    - NOTE: Summarize reasoning for the user, but do NOT expose full chain-of-thought. Keep internal deliberations internal; surface only the concise rationale needed to justify each change.\r\n\r\n7. Contextual Analysis\r\n\r\n    - Use all available context—such as code history, inline documentation, style guidelines—to understand the intended functionality.\r\n    - When inspecting an existing file for understanding, prefer reading the whole file in a\r\n      single `read_file` call when it comfortably fits in context; switch to targeted slices\r\n      only when the file is too large, the tool truncates it, or a specific anchor line is\r\n      already known.\r\n    - If the role or intent behind a code segment is ambiguous, ask for clarification rather than making assumptions.\r\n\r\n8. Targeted, Incremental Changes\r\n\r\n    - Identify and isolate only the problematic code segments (including places where IntelliSense doc comments can replace simple comments).\r\n    - Provide minimal code snippets that address the issue without rewriting larger sections.\r\n    - For each suggested code change, explicitly indicate the exact location in the code (e.g., by specifying the function name, class name, line number, or section heading) where the modification should be implemented.\r\n\r\n9. Preservation of Context\r\n\r\n    - Maintain all developer comments, annotations, and workarounds exactly as they appear, transforming them to doc comment format only when it improves IntelliSense support.\r\n    - Do not modify or remove any non-code context unless explicitly instructed.\r\n    - Avoid introducing new, irrelevant comments in the code.\r\n\r\n10. Launching {PROJECT} Correctly:\r\n    - Navigate to the {PROJECT} project folder.\r\n    - Run the following command to launch the project with live reload and proper debugging configuration:\r\n      dotnet watch run --launch-profile \"{PROJECT} (NG Build)\" --project {PROJECT}/{PROJECT}.csproj\r\n    - This command will start the {PROJECT} project on the designated debugging session URL.\r\n    - Ensure that any previous {PROJECT} instances are terminated before running this command.\r\n"
  },
  {
    "path": ".github/instructions/repository-analysis.instructions.md",
    "content": "# Repository Analysis\n\n## 1. Repository Overview\n\nThis document provides a factual reference for the Jocys.com FocusLogger repository, aimed at developers and AI coding agents working on the codebase.\n\n**FocusLogger** is a Windows desktop utility that monitors and logs which process or program takes window focus. It targets users (especially gamers and power users) who experience unexpected focus stealing — where a background process briefly grabs foreground focus, interrupting gameplay or work. The tool logs every focus change with timestamps, process details, window class names, and focus-state flags, allowing users to identify the culprit.\n\n- **Repository:** https://github.com/JocysCom/FocusLogger\n- **License:** GNU General Public License v3.0\n- **Current version:** 1.2.6\n- **Target platform:** Windows 10+ with .NET 8.0\n- **Primary audiences:** Gamers, power users, IT support personnel diagnosing focus-stealing issues.\n\n## 2. Top-Level Structure\n\nThis section maps every top-level directory and file to help navigate the repository quickly.\n\n| Path | Purpose |\n|------|---------|\n| `FocusLogger/` | Main application project (WPF, .NET 8.0). Contains all app source code, shared library, and resources. |\n| `FocusLogger.Tests/` | MSTest test project. Unit tests for CSV export and UI automation tests. |\n| `Documents/` | Release engineering: signing scripts, zip packaging scripts, screenshot tooling, and pre-built release files. |\n| `Resources/` | Solution-level shared scripts (currently `ZipFiles.ps1` for checksum-aware zip packaging). |\n| `.ai/` | AI agent instructions, coding guidelines, repository analysis, and skills. |\n| `JocysCom.FocusLogger.slnx` | Solution file (XML-based `.slnx` format) referencing the two projects. |\n| `README.md` | Project overview, download link, system requirements, screenshot. |\n| `LICENSE` | GPLv3 license text. |\n| `SECURITY.md` | Security vulnerability reporting policy (support@jocys.com). |\n| `Settings.XamlStyler` | XamlStyler configuration for consistent XAML formatting. |\n| `Solution_Cleanup.ps1` | PowerShell script for cleaning build artifacts. |\n\n## 3. Technology Stack & Key Dependencies\n\nThis section lists verified technologies and versions drawn from project files.\n\n| Technology | Version / Detail | Evidence |\n|------------|-----------------|----------|\n| .NET | 8.0 (`net8.0-windows`) | `JocysCom.FocusLogger.csproj` TargetFramework |\n| C# | Implicit (SDK default for .NET 8) | SDK-style project |\n| WPF | `<UseWPF>true</UseWPF>` | csproj |\n| Windows Forms interop | `<UseWindowsForms>true</UseWindowsForms>` | csproj — used for P/Invoke helpers and DPI awareness |\n| MSTest | v3.x (`MSTest.TestFramework 3.*`, `MSTest.TestAdapter 3.*`) | Test csproj PackageReference |\n| Microsoft.NET.Test.Sdk | 17.x | Test csproj PackageReference |\n| Windows API (user32.dll) | P/Invoke | `NativeMethods.cs` |\n| PowerShell | Scripts for build, sign, zip, cleanup | `Documents/`, `Resources/`, root |\n| XamlStyler | Config present | `Settings.XamlStyler` |\n\n**No NuGet package dependencies** in the main application project — all functionality comes from .NET SDK and the embedded `JocysCom.ClassLibrary`.\n\n## 4. Architecture & Runtime Model\n\nThis section describes how the application is structured and how it operates at runtime.\n\nFocusLogger is a **single-executable WPF desktop application** that polls Windows API functions to detect focus changes. It does not persist log data between sessions (in-memory only) but provides CSV export for offline analysis.\n\n### Architectural layers\n\n```mermaid\ngraph TD\n    subgraph UI[\"UI Layer (WPF)\"]\n        App[\"App.xaml.cs<br/>Entry point, DPI aware\"]\n        MW[\"MainWindow.xaml.cs<br/>Main window frame\"]\n        DLC[\"DataListControl.xaml.cs<br/>Core logging UI + logic\"]\n    end\n\n    subgraph Core[\"Core Logic\"]\n        DI[\"DataItem.cs<br/>Log entry model\"]\n        DIT[\"DataItemType.cs<br/>Entry type enum\"]\n        NM[\"NativeMethods.cs<br/>P/Invoke declarations\"]\n        CSV[\"CSV Export<br/>BuildCsvContent, CsvEscape\"]\n    end\n\n    subgraph Shared[\"JocysCom.ClassLibrary (embedded)\"]\n        Config[\"Configuration<br/>SettingsData, SettingsItem, AssemblyInfo\"]\n        CompModel[\"ComponentModel<br/>SortableBindingList, BindingListInvoked\"]\n        Controls[\"Controls<br/>ControlsHelper, ItemFormattingConverter,<br/>InfoControl, MessageBoxWindow\"]\n        Other[\"Collections, IO, Text, Runtime, Data\"]\n    end\n\n    subgraph WinAPI[\"Windows OS\"]\n        User32[\"user32.dll\"]\n    end\n\n    App --> MW --> DLC\n    DLC --> DI\n    DLC --> NM\n    DLC --> CSV\n    NM --> User32\n    DI --> Config\n    DLC --> CompModel\n    DLC --> Controls\n    MW --> Controls\n```\n\n### Key architectural decisions\n\n- **Polling via timer:** A `System.Timers.Timer` with 1ms interval (non-auto-reset) continuously polls `GetActiveWindow()` and `GetForegroundWindow()`. Duplicate events are suppressed via `DataItem.IsSame()`.\n- **Thread safety:** Timer fires on a thread-pool thread; UI updates are marshalled via `ControlsHelper.BeginInvoke()`. A `lock(AddLock)` synchronizes the polling logic.\n- **Embedded shared library:** `JocysCom.ClassLibrary` files are included directly in `FocusLogger/JocysCom/` rather than as a compiled DLL or NuGet package.\n- **No MVVM framework:** Code-behind pattern with data binding. `DataListControl.xaml.cs` contains both view-model-like logic and model interaction.\n\n## 5. Project Inventory\n\nThis section lists each project in the solution with its key metadata.\n\n### 5.1 JocysCom.FocusLogger (main application)\n\n| Property | Value |\n|----------|-------|\n| Path | `FocusLogger/JocysCom.FocusLogger.csproj` |\n| Output type | `WinExe` |\n| Target framework | `net8.0-windows` |\n| Assembly name | `JocysCom.FocusLogger` |\n| Description | Find out which process or program is taking the window focus. In game, mouse and keyboard could temporarily stop responding if another program takes the focus. This tool could help diagnose which program is stealing the focus. |\n| Version | 1.2.6 |\n| NuGet dependencies | None |\n| Embedded resources | `Resources/BuildDate.txt` (auto-generated), `Resources/AiAnalysisPrompt.md` |\n\n**Source structure:**\n\n| Directory | Contents |\n|-----------|----------|\n| `FocusLogger/` (root) | `App.xaml(.cs)`, `MainWindow.xaml(.cs)`, `AssemblyInfo.cs`, `App.ico` |\n| `FocusLogger/Common/` | `DataItem.cs`, `DataItemType.cs`, `NativeMethods.cs` |\n| `FocusLogger/Controls/` | `DataListControl.xaml(.cs)` — core logging control |\n| `FocusLogger/JocysCom/` | Embedded `JocysCom.ClassLibrary` (~30 files across Collections, Common, ComponentModel, Configuration, Controls, Data, IO, Runtime, Text) |\n| `FocusLogger/Resources/` | `AiAnalysisPrompt.md`, `BuildDate.txt`, `Icons/` (SVG sources, XAML icons, conversion scripts) |\n| `FocusLogger/Properties/` | Publish profiles |\n\n### 5.2 JocysCom.FocusLogger.Tests (test project)\n\n| Property | Value |\n|----------|-------|\n| Path | `FocusLogger.Tests/JocysCom.FocusLogger.Tests.csproj` |\n| Target framework | `net8.0-windows` |\n| Test framework | MSTest v3.x |\n| Project reference | `FocusLogger/JocysCom.FocusLogger.csproj` |\n\n**Test files:**\n\n| File | Purpose |\n|------|---------|\n| `CsvExportTests.cs` | Unit tests for `CsvEscape` and `BuildCsvContent` methods |\n| `UIAutomationTests.cs` | UI automation tests using `System.Windows.Automation` — launches the built app and interacts with controls by AutomationId |\n\nRun tests with: `dotnet test FocusLogger.Tests/JocysCom.FocusLogger.Tests.csproj`\n\n## 6. Dependency & Data Flow\n\nThis section explains how the projects and components relate to each other and how data moves through the system.\n\n### Project dependency graph\n\n```mermaid\ngraph LR\n    Tests[\"FocusLogger.Tests<br/>(MSTest v3)\"] -->|ProjectReference| App[\"JocysCom.FocusLogger<br/>(WPF App)\"]\n    App -->|embedded source| Shared[\"JocysCom.ClassLibrary<br/>(in FocusLogger/JocysCom/)\"]\n    App -->|P/Invoke| WinAPI[\"user32.dll\"]\n```\n\n### Runtime data flow\n\n1. `System.Timers.Timer` fires (1ms interval, non-auto-reset).\n2. `DataListControl.UpdateInfo()` acquires `AddLock`.\n3. Calls `NativeMethods.GetActiveWindow()` and `NativeMethods.GetForegroundWindow()`.\n4. For each handle, `GetItemFromHandle()` creates a `DataItem` with timestamp, focus flags (mouse/keyboard/caret), window title, and window class name.\n5. `IsSame()` checks if the event differs from the previous one; if not, it is skipped.\n6. `UpdateFromProcess()` enriches the `DataItem` with process name and path (with error handling for restricted processes).\n7. The item is inserted at position 0 of `SortableBindingList<DataItem>` via `ControlsHelper.BeginInvoke()` (UI thread dispatch).\n8. The WPF `DataGrid` updates via data binding. `ItemFormattingConverter` translates boolean flags to icons.\n\n### CSV export flow\n\n1. User clicks \"Save CSV\" button.\n2. `SaveFileDialog` prompts for file location.\n3. `BuildCsvContent()` iterates all `DataItem` entries, writing CSV with headers: Date, PID, Process Name, Active, Mouse, Keyboard, Caret, Window Title, Window Class, Path.\n4. File is written as UTF-8.\n5. \"Explore\" button opens the saved file location in Explorer.\n6. \"AI Prompt Example\" button shows the embedded `AiAnalysisPrompt.md` in a `MessageBoxWindow` for users to copy and paste into an AI assistant along with their CSV.\n\n## 7. Build, Test, CI/CD & Operational Workflows\n\nThis section documents how the project is built, tested, and released based on repository evidence.\n\n### Build\n\n```bash\ndotnet build JocysCom.FocusLogger.slnx\n```\n\n- **Pre-build event:** Generates `Resources/BuildDate.txt` with the current ISO 8601 timestamp via PowerShell.\n- **Output:** Single `JocysCom.FocusLogger.exe` in `bin/{Configuration}/net8.0-windows/`.\n- **Debug configuration:** Embedded PDB symbols (`DebugType: embedded`).\n\n### Test\n\n```bash\ndotnet test FocusLogger.Tests/JocysCom.FocusLogger.Tests.csproj\n```\n\n- **Framework:** MSTest v3.x with Microsoft.NET.Test.Sdk 17.x.\n- **Unit tests:** `CsvExportTests` — validates CSV escaping and content generation.\n- **UI automation tests:** `UIAutomationTests` — launches the built application and interacts via `System.Windows.Automation`. Requires a prior build of the main project.\n\n### Release / packaging scripts\n\n| Script | Purpose |\n|--------|---------|\n| `Documents/App_1_Sign.ps1` | Code-signs the application executable. |\n| `Documents/App_2_Zip.ps1` | Packages the signed executable into a release ZIP. |\n| `Resources/ZipFiles.ps1` | Shared utility for checksum-aware ZIP creation (compares source/dest checksums before rebuilding). |\n| `Documents/Take_Screenshot.ps1` | Captures application screenshot for documentation. |\n| `Documents/Take_Screenshot.ps1.cs` | C# helper compiled by the screenshot script. |\n| `Solution_Cleanup.ps1` | Cleans `bin/`, `obj/`, and other build artifacts. |\n\n### Icon workflow\n\nSVG icon sources are stored in `FocusLogger/Resources/Icons/Icons_Default/`. The script `Convert_SVG_to_XAML.ps1` converts them to XAML resource dictionaries (`Icons_Default.xaml`).\n\n### CI/CD\n\nNo CI/CD workflow files were found under `.github/workflows/`. Builds and releases appear to be performed locally.\n\n## 8. Documentation Map\n\nThis section identifies where documentation lives in the repository.\n\n| Location | Audience | Content |\n|----------|----------|---------|\n| `README.md` | End users, contributors | Project overview, download link, system requirements, screenshot |\n| `SECURITY.md` | Security researchers | Vulnerability reporting policy |\n| `LICENSE` | All | GPLv3 full text |\n| `.ai/ReadMe.md` | AI agents, developers | Explains the purpose of each file in the `.ai/` directory and how custom instructions work for Copilot, CLINE, and Codex |\n| `.ai/instructions.md` | AI agents | Role definition and output formatting rules for AI-assisted edits |\n| `.ai/coding-guideline.instructions.md` | AI agents | Detailed coding guidelines, source control conventions (branch/PR naming), testing workflow, and constraints for AI agents |\n| `.ai/repository-analysis.instructions.md` | AI agents, developers | This file — comprehensive repository reference |\n| `.ai/skills/` | AI agents | Skill definitions (e.g., `ai-self-improvement`) for agent-assisted workflows |\n| `FocusLogger/Resources/AiAnalysisPrompt.md` | End users | Prompt template for users to paste into AI assistants alongside exported CSV logs |\n| `Documents/Images/` | README, users | Application screenshot |\n| `Settings.XamlStyler` | Developers | XamlStyler formatting configuration |\n\n### Documentation taxonomy\n\n```mermaid\ngraph TD\n    subgraph EndUsers[\"End-User Documentation\"]\n        README[\"README.md<br/>Overview, download, requirements\"]\n        SECURITY[\"SECURITY.md<br/>Vulnerability reporting\"]\n        LICENSE[\"LICENSE<br/>GPLv3\"]\n        AiPrompt[\"FocusLogger/Resources/<br/>AiAnalysisPrompt.md<br/>Prompt template for CSV analysis\"]\n        Screenshot[\"Documents/Images/<br/>Application screenshot\"]\n    end\n\n    subgraph AIAgents[\"AI Agent Instructions\"]\n        AiReadMe[\".ai/ReadMe.md<br/>Directory guide\"]\n        AiInstr[\".ai/instructions.md<br/>Role & output rules\"]\n        AiCoding[\".ai/coding-guideline<br/>.instructions.md<br/>Coding & SCM conventions\"]\n        AiRepo[\".ai/repository-analysis<br/>.instructions.md<br/>This file\"]\n        AiSkills[\".ai/skills/<br/>Agent skill definitions\"]\n    end\n\n    subgraph DevConfig[\"Developer Configuration\"]\n        XamlStyler[\"Settings.XamlStyler<br/>XAML formatting\"]\n    end\n```\n\n## 9. AI-Agent-Relevant Conventions and Constraints\n\nThis section captures rules and patterns that materially affect automated edits. The full set of coding and source-control conventions is defined in `.ai/coding-guideline.instructions.md`; the highlights below are the items most likely to cause mistakes if overlooked.\n\n1. **Coding style:** Follow Microsoft C# conventions. PascalCase for public members, camelCase for locals. Some private fields use `_PascalCase` (e.g., `_Date`). Preserve existing naming patterns in each file. Prefer the simplest viable solution; avoid over-engineering.\n\n2. **Doc comments:** Per `.ai/instructions.md`, convert simple comments to XML documentation comments where beneficial for IntelliSense. Do not alter surrounding code when doing so.\n\n3. **Shared library files (`FocusLogger/JocysCom/`):** These are embedded from a shared `JocysCom.ClassLibrary`. Exercise caution when editing — changes here may diverge from the upstream library.\n\n4. **XAML formatting:** The repository uses XamlStyler (see `Settings.XamlStyler`). XAML edits should conform to the configured style.\n\n5. **No NuGet packages in the main app:** All dependencies are framework-provided or embedded source. Do not introduce NuGet package dependencies without explicit approval.\n\n6. **Test project uses MSTest v3:** New tests should follow MSTest v3 patterns (`[TestClass]`, `[TestMethod]`, `Assert.*`).\n\n7. **UI automation tests depend on a built executable:** `UIAutomationTests` locate the app at a relative path from the test output. Building the main project before running these tests is required.\n\n8. **Pre-build event:** The csproj generates `Resources/BuildDate.txt` via PowerShell. This file should not be manually edited or committed.\n\n9. **Solution format:** Uses `.slnx` (XML-based solution format), not the older `.sln` text format.\n\n10. **No CI/CD pipelines:** All build and release steps are manual/local. Scripts in `Documents/` handle signing and packaging.\n\n11. **File size limit:** No code file that is created or modified may exceed 6000 tokens (~24 KB).\n\n12. **Source control conventions:** Issues use `{CATEGORY}: {Description}` (FEAT, FIX, TECH, DOCS). Branches use `{CATEGORY}-{issue#}-{lowercase-dashed-name}`. PRs use `PR: #{issue#}: {CATEGORY}: {Description}` and must reference the issue with `Closes #{issue#}`. Merge via PR only — no direct pushes to `main`.\n\n13. **No Co-Authored-By lines:** Do not add `Co-Authored-By` AI model/company lines to commits.\n\n14. **Issue closure:** Never close issues before a release is published with the fix.\n\n15. **No duplication / SSOT:** Update or move existing code instead of adding parallel implementations. If introducing a replacement, remove the old one in the same change.\n\n16. **Stability primitives:** When the repo provides an established script or command, use it verbatim. Only deviate when it is proven broken, and then fix the primitive rather than inventing a parallel path.\n"
  },
  {
    "path": ".github/skills/ai-self-improvement/SKILL.md",
    "content": "---\r\nname: ai-self-improvement\r\ndescription: Update, create, improve, and synchronise this repository's AI agent instructions and related assets (including skills). Use when the user asks to create or edit a skill/SKILL.md, modify the agent's own instructions/processes, restructure instruction governance, migrate instruction content into skills, or run/adjust the sync pipeline that publishes `.ai/` sources into agent-specific folders. Load this skill before writing any SKILL.md, .instructions.md, or touching any skills/ folder (.ai/, .claude/, .roo/, .github/). It tells you the correct location (.ai/) and the sync step, so files end up in the right place.\r\n---\r\n\r\n# AI Self-Improvement (Instructions + Skills)\r\n\r\n## Critical: `.ai/` is the Primary Source for ALL Agents\r\n\r\nThe `.ai/` folder is the **single source of truth** for all AI agent configurations in this repository. This applies to:\r\n\r\n- **CLINE / Roo Code** — synced to `.roo/rules/` and `.roo/skills/`\r\n- **GitHub Copilot** — synced to `.github/copilot-instructions.md`\r\n- **OpenAI Codex / AGENTS.md** — synced to `AGENTS.md` at repo root\r\n- **Claude Code** — synced to `.claude/*.instructions.md` and `.claude/skills/`\r\n\r\n**IMPORTANT:** When asked to modify skills, instructions, or perform any AI self-improvement task, you MUST:\r\n\r\n1. Locate the source file under `.ai/` (not the agent-specific output)\r\n2. Make changes to the `.ai/` source\r\n3. Run the sync script to propagate changes to all agents\r\n\r\n## Path Mapping Reference\r\n\r\nWhen you encounter a path in an agent-specific folder, map it to `.ai/`:\r\n\r\n| Agent-Specific Path | Source Path (Edit Here) |\r\n|---------------------|------------------------|\r\n| `.roo/rules/*.md` | `.ai/*.instructions.md` |\r\n| `.roo/skills/<name>/SKILL.md` | `.ai/skills/<name>/SKILL.md` |\r\n| `.github/copilot-instructions.md` | `.ai/instructions.md` (generated) |\r\n| `AGENTS.md` | `.ai/instructions.md` (generated) |\r\n| `.claude/*.instructions.md` | `.ai/*.instructions.md` |\r\n| `.claude/skills/<name>/SKILL.md` | `.ai/skills/<name>/SKILL.md` |\r\n\r\n**Example:** If asked to update `.roo/skills/ai-self-improvement/SKILL.md`, you must edit `.ai/skills/ai-self-improvement/SKILL.md` instead.\r\n\r\n## Editable instruction files (sources of truth)\r\n\r\nYou can update your own instruction files under `.ai/`:\r\n\r\n- `.ai/instructions.md` — the main system instructions file\r\n- `.ai/*instructions.md` — additional instruction files (auto-included)\r\n- `.ai/*instructions-detail.md` — detailed instruction files (read only when needed)\r\n- `.ai/skills/<name>/SKILL.md` — skill definition files\r\n\r\n## Workflow\r\n\r\n1. Treat `.ai/` as the **single source of truth** for agent instructions **and skills**.\r\n2. When creating or migrating a skill, create/update it under `.ai/skills/`.\r\n3. Make instruction changes in `.ai/instructions.md` and related `*.instructions.md` / `*.instructions-detail.md` files.\r\n4. Do **not** edit generated outputs directly (they are produced by the sync script):\r\n   - `.roo/rules/`\r\n   - `.roo/skills/`\r\n   - `.github/copilot-instructions.md`\r\n   - `AGENTS.md`\r\n   - `.claude/`\r\n5. **Test changes before syncing** — verify scripts execute correctly and changes work as expected.\r\n6. After testing, run the sync script to apply to all agents.\r\n\r\n## Testing Before Sync\r\n\r\nBefore running the sync script, always verify your changes work correctly:\r\n\r\n- **For script changes**: Execute the modified script and verify output is correct\r\n- **For instruction changes**: Review the markdown renders properly and instructions are clear\r\n- **For skill changes**: Test any bundled tools or scripts included in the skill\r\n\r\n**Example**: If you modify a PowerShell script in a skill, run it directly from `.ai/skills/<name>/scripts/` to confirm it works before syncing.\r\n\r\n## Activation process\r\n\r\nAfter editing instruction files (or master skills), run from repository root:\r\n\r\n```powershell\r\n.\\.ai\\skills\\ai-self-improvement\\scripts\\Sync-AgentAssets.ps1 AUTO\r\n```\r\n\r\nThis script synchronizes changes from `.ai/` to all agent-specific folders.\r\n\r\n## Single source of truth\r\n\r\n**Never embed template content in instructions — reference template files instead.**\r\n\r\nExample:\r\n\r\n- ✅ \"Template maintained in `pr/checklist.template.md`\"\r\n- ❌ Pasting template content into instructions\r\n\r\n## Bundled scripts\r\n\r\n- Sync entrypoint (instructions + skills): `.ai/skills/ai-self-improvement/scripts/Sync-AgentAssets.ps1`\r\n"
  },
  {
    "path": ".github/skills/ai-self-improvement/scripts/Sync-AgentAssets.ps1",
    "content": "# Script: Sync-AgentAssets.ps1\r\n# Location: .ai/skills/ai-self-improvement/scripts/Sync-AgentAssets.ps1\r\n# Description:\r\n#   Synchronises AI agent instruction files and skills from master sources under `.ai/`.\r\n#   - Instructions: copies `*.instructions.md` from `.ai/` into agent-specific outputs.\r\n#   - Skills: mirrors `.ai/skills/*` into agent skill folders (e.g. `.roo/skills/*`).\r\n#\r\n# Options for Mode:\r\n#   ALL  - update all known agent outputs\r\n#   AUTO - update only agents that exist in this repository (default usage)\r\n#   Or a specific agent name: CLINE, ROO CODE, GitHub CoPilot, OpenAI Codex, Claude Code\r\n\r\nparam(\r\n    [Parameter(Position = 0)]\r\n    [string]$Mode,\r\n\r\n    [switch]$NoClear\r\n)\r\n\r\n# Combine remaining args so Windows PowerShell (-File) invocations like:\r\n#   Sync-AgentAssets.ps1 GitHub CoPilot\r\n# work the same as:\r\n#   Sync-AgentAssets.ps1 \"GitHub CoPilot\"\r\nif ($args.Count -gt 0) {\r\n    $ModeFromArgs = ($args -join ' ')\r\n    if (-not $Mode -or $Mode -eq '') {\r\n        $Mode = $ModeFromArgs\r\n    }\r\n}\r\n\r\n# Allow calling via the old filename (if invoked through a copied/renamed script).\r\n# This only affects displayed script name in prompts/logs.\r\n$scriptName = [System.IO.Path]::GetFileName($MyInvocation.MyCommand.Path)\r\n\r\n# Strict mode\r\nSet-StrictMode -Version Latest\r\n$ErrorActionPreference = \"Stop\"\r\n\r\nfunction Ensure-Directory {\r\n    param(\r\n        [Parameter(Mandatory = $true)]\r\n        [string]$Path\r\n    )\r\n\r\n    if (-not (Test-Path -Path $Path -PathType Container)) {\r\n        New-Item -ItemType Directory -Force -Path $Path | Out-Null\r\n    }\r\n}\r\n\r\n# Function to check if instruction files exist in a directory\r\nfunction Test-HasInstructionFiles {\r\n    param(\r\n        [Parameter(Mandatory = $true)]\r\n        [string]$Path,\r\n        [string]$Filter = '*instructions.md'\r\n    )\r\n\r\n    if (Test-Path $Path -PathType Container) {\r\n        $files = @(Get-ChildItem $Path -Filter $Filter -File -ErrorAction SilentlyContinue)\r\n        return ($files.Length -gt 0)\r\n    }\r\n\r\n    return $false\r\n}\r\n\r\n# Function to pause at the end (unless -NoWait is specified)\r\nfunction Invoke-Pause {\r\n    Write-Host \"Pausing for 2 seconds...\"\r\n    Start-Sleep -Seconds 2\r\n}\r\n\r\nfunction Copy-FileIfDifferent {\r\n    param(\r\n        [Parameter(Mandatory = $true)]\r\n        [string]$SourcePath,\r\n        [Parameter(Mandatory = $true)]\r\n        [string]$TargetPath\r\n    )\r\n\r\n    $targetDir = Split-Path -Path $TargetPath -Parent\r\n    Ensure-Directory -Path $targetDir\r\n\r\n    if (-not (Test-Path -Path $TargetPath -PathType Leaf)) {\r\n        Copy-Item -LiteralPath $SourcePath -Destination $TargetPath -Force\r\n        $relative = $TargetPath.Substring($repoRoot.Length + 1)\r\n        Write-Host \"Created: $relative\"\r\n        return\r\n    }\r\n\r\n    $srcBytes = [System.IO.File]::ReadAllBytes($SourcePath)\r\n    $dstBytes = [System.IO.File]::ReadAllBytes($TargetPath)\r\n\r\n    if ($srcBytes.Length -eq $dstBytes.Length) {\r\n        $same = $true\r\n        for ($i = 0; $i -lt $srcBytes.Length; $i++) {\r\n            if ($srcBytes[$i] -ne $dstBytes[$i]) { $same = $false; break }\r\n        }\r\n\r\n        if ($same) {\r\n            $relative = $TargetPath.Substring($repoRoot.Length + 1)\r\n            Write-Host \"Up-to-date: $relative\"\r\n            return\r\n        }\r\n    }\r\n\r\n    Copy-Item -LiteralPath $SourcePath -Destination $TargetPath -Force\r\n    $relative = $TargetPath.Substring($repoRoot.Length + 1)\r\n    Write-Host \"Updated: $relative\"\r\n}\r\n\r\nfunction Get-TextAuto {\r\n    param(\r\n        [Parameter(Mandatory = $true)]\r\n        [string]$Path\r\n    )\r\n\r\n    # .NET StreamReader detects BOM for UTF-8/UTF-16/UTF-32 automatically.\r\n    $sr = New-Object System.IO.StreamReader($Path, $true)\r\n    try {\r\n        return $sr.ReadToEnd()\r\n    }\r\n    finally {\r\n        $sr.Dispose()\r\n    }\r\n}\r\n\r\nfunction Write-Utf8NoBom {\r\n    param(\r\n        [Parameter(Mandatory = $true)]\r\n        [string]$Path,\r\n        [Parameter(Mandatory = $true)]\r\n        [string]$Content\r\n    )\r\n\r\n    $dir = Split-Path -Path $Path -Parent\r\n    Ensure-Directory -Path $dir\r\n\r\n    $utf8NoBom = New-Object System.Text.UTF8Encoding($false)\r\n    [System.IO.File]::WriteAllText($Path, $Content, $utf8NoBom)\r\n}\r\n\r\nfunction Assert-InstructionSync {\r\n    param(\r\n        [Parameter(Mandatory = $true)]\r\n        [string]$SourceDirectory,\r\n        [Parameter(Mandatory = $true)]\r\n        [string]$TargetDirectory,\r\n        [Parameter(Mandatory = $true)]\r\n        [System.IO.FileSystemInfo[]]$SourceFiles\r\n    )\r\n\r\n    $srcDir = Join-Path $repoRoot $SourceDirectory\r\n    $dstDir = Join-Path $repoRoot $TargetDirectory\r\n\r\n    foreach ($sourceFile in $SourceFiles) {\r\n        $srcPath = Join-Path $srcDir $sourceFile.Name\r\n        $dstPath = Join-Path $dstDir $sourceFile.Name\r\n\r\n        if (-not (Test-Path $dstPath -PathType Leaf)) {\r\n            throw \"Binary comparison failed. Destination file missing: $dstPath\"\r\n        }\r\n\r\n        $srcBytes = [System.IO.File]::ReadAllBytes($srcPath)\r\n        $dstBytes = [System.IO.File]::ReadAllBytes($dstPath)\r\n\r\n        if ($srcBytes.Length -ne $dstBytes.Length) {\r\n            throw \"Binary comparison failed. Source and target size mismatch in binary: Source: $srcPath Target: $dstPath\"\r\n        }\r\n\r\n        for ($i = 0; $i -lt $srcBytes.Length; $i++) {\r\n            if ($srcBytes[$i] -ne $dstBytes[$i]) {\r\n                throw \"Binary comparison failed. Source and target content mismatch in binary: Source: $srcPath Target: $dstPath\"\r\n            }\r\n        }\r\n    }\r\n}\r\n\r\n# Function to update agents that use multiple separate instruction files\r\nfunction Update-MultipleFileAgent {\r\n    param(\r\n        [Parameter(Mandatory = $true)]\r\n        [string]$AgentName,\r\n        [Parameter(Mandatory = $true)]\r\n        [string]$TargetDirectory,\r\n        [Parameter(Mandatory = $true)]\r\n        [System.IO.FileSystemInfo[]]$SourceFiles,\r\n        [Parameter(Mandatory = $true)]\r\n        [string]$RepoRoot\r\n    )\r\n\r\n    Write-Host \"`r`n--- Updating $AgentName Instructions ---\"\r\n    $targetDir = Join-Path $RepoRoot $TargetDirectory\r\n\r\n    foreach ($sourceFile in $SourceFiles) {\r\n        $targetFile = Join-Path $targetDir $sourceFile.Name\r\n        Copy-FileIfDifferent -SourcePath $sourceFile.FullName -TargetPath $targetFile\r\n    }\r\n\r\n    Assert-InstructionSync -SourceDirectory \".ai\" -TargetDirectory $TargetDirectory -SourceFiles $SourceFiles\r\n}\r\n\r\n# Function to update agents that use a single combined instruction file\r\nfunction Update-SingleFileAgent {\r\n    param(\r\n        [Parameter(Mandatory = $true)]\r\n        [string]$AgentName,\r\n        [Parameter(Mandatory = $true)]\r\n        [string]$TargetFilePath,\r\n        [Parameter(Mandatory = $true)]\r\n        [System.IO.FileSystemInfo[]]$SourceFiles,\r\n        [Parameter(Mandatory = $true)]\r\n        [string]$RepoRoot\r\n    )\r\n\r\n    Write-Host \"`r`n--- Updating $AgentName Instructions ---\"\r\n    $targetFile = Join-Path $RepoRoot $TargetFilePath\r\n    $relativeTarget = $targetFile.Substring($repoRoot.Length + 1)\r\n\r\n    $allInstructionsContent = New-Object System.Text.StringBuilder\r\n    $firstFile = $true\r\n\r\n    foreach ($sourceFile in $SourceFiles) {\r\n        $sourceContent = Get-TextAuto -Path $sourceFile.FullName\r\n\r\n        if ([string]::IsNullOrWhiteSpace($sourceContent)) {\r\n            Write-Warning \"Skipping empty file: $($sourceFile.Name)\"\r\n            continue\r\n        }\r\n\r\n        if (-not $firstFile) {\r\n            [void]$allInstructionsContent.AppendLine(\"\")\r\n        }\r\n\r\n        [void]$allInstructionsContent.AppendLine(\"==== START OF INSTRUCTIONS FROM: $($sourceFile.Name) ====\")\r\n        [void]$allInstructionsContent.AppendLine(\"\")\r\n\r\n        [void]$allInstructionsContent.AppendLine(\"# Instructions from: $($sourceFile.Name)\")\r\n        [void]$allInstructionsContent.AppendLine(\"\")\r\n\r\n        [void]$allInstructionsContent.AppendLine($sourceContent.Trim())\r\n\r\n        [void]$allInstructionsContent.AppendLine(\"\")\r\n        [void]$allInstructionsContent.AppendLine(\"==== END OF INSTRUCTIONS FROM: $($sourceFile.Name) ====\")\r\n\r\n        $firstFile = $false\r\n    }\r\n\r\n    $finalContent = $allInstructionsContent.ToString()\r\n\r\n    $existing = if (Test-Path -Path $targetFile -PathType Leaf) { Get-TextAuto -Path $targetFile } else { $null }\r\n    if ($null -ne $existing -and $existing -eq $finalContent) {\r\n        Write-Host \"Up-to-date: $relativeTarget\"\r\n        return\r\n    }\r\n\r\n    Write-Utf8NoBom -Path $targetFile -Content $finalContent\r\n    Write-Host \"Updated: $relativeTarget\"\r\n}\r\n\r\nfunction Invoke-RoboCopyMirror {\r\n    param(\r\n        [Parameter(Mandatory = $true)]\r\n        [string]$SourceDirectory,\r\n        [Parameter(Mandatory = $true)]\r\n        [string]$DestinationDirectory,\r\n        [Parameter(Mandatory = $true)]\r\n        [string]$Label\r\n    )\r\n\r\n    if (-not (Test-Path $SourceDirectory -PathType Container)) {\r\n        Write-Host \"No skills folder found at: $SourceDirectory\"\r\n        return\r\n    }\r\n\r\n    Ensure-Directory -Path $DestinationDirectory\r\n\r\n    Write-Host \"`r`n--- Mirroring skills to $Label ---\"\r\n    Write-Host \"Source:      $SourceDirectory\"\r\n    Write-Host \"Destination: $DestinationDirectory\"\r\n\r\n    # /MIR      = mirror (copy + delete removed)\r\n    # /FFT      = tolerate 2s timestamp granularity\r\n    # /R:1 /W:1 = retry quickly\r\n    # /NFL/NDL  = no file/dir listing (keep output compact)\r\n    # /NJH/NJS  = no job header/summary\r\n    # /NP       = no progress\r\n    # /XD       = exclude version control/build dirs\r\n    $excludedDirs = @('.git', '.vs', 'bin', 'obj')\r\n\r\n    $args = @(\r\n        $SourceDirectory,\r\n        $DestinationDirectory,\r\n        '/MIR',\r\n        '/FFT',\r\n        '/R:1',\r\n        '/W:1',\r\n        '/NFL',\r\n        '/NDL',\r\n        '/NJH',\r\n        '/NJS',\r\n        '/NP'\r\n    )\r\n\r\n    foreach ($d in $excludedDirs) {\r\n        $args += '/XD'\r\n        $args += $d\r\n    }\r\n\r\n    $exe = 'robocopy'\r\n    # Do not echo the full robocopy command; it is noisy and can wrap in some terminals.\r\n    Write-Host \"robocopy <source> <destination> /MIR /NFL /NDL /NJH /NJS /NP ...\"\r\n\r\n    & $exe @args | Out-Null\r\n    $exitCode = $LASTEXITCODE\r\n\r\n    # Robocopy uses bitmask exit codes.\r\n    # 0-7 are success with various flags; >= 8 indicates failure.\r\n    if ($exitCode -ge 8) {\r\n        throw \"Robocopy failed with exit code $exitCode. Command: $cmd\"\r\n    }\r\n\r\n    # IMPORTANT: robocopy returns 1+ for successful copies.\r\n    # Ensure PowerShell script does not propagate a non-zero exit code for success cases.\r\n    $global:LASTEXITCODE = 0\r\n\r\n    Write-Host \"Mirrored skills to $Label (robocopy exit code $exitCode).\"\r\n}\r\n\r\nfunction Sync-SkillsToRoo {\r\n    param(\r\n        [Parameter(Mandatory = $true)]\r\n        [string]$RepoRoot\r\n    )\r\n\r\n    $srcSkillsRoot = Join-Path $RepoRoot \".ai\\skills\"\r\n    $rooSkillsRoot = Join-Path $RepoRoot \".roo\\skills\"\r\n\r\n    Invoke-RoboCopyMirror -SourceDirectory $srcSkillsRoot -DestinationDirectory $rooSkillsRoot -Label \"Roo (.roo\\\\skills)\"\r\n}\r\n\r\nfunction Sync-SkillsToGitHub {\r\n    param(\r\n        [Parameter(Mandatory = $true)]\r\n        [string]$RepoRoot\r\n    )\r\n\r\n    $srcSkillsRoot = Join-Path $RepoRoot \".ai\\skills\"\r\n    $githubSkillsRoot = Join-Path $RepoRoot \".github\\skills\"\r\n\r\n    Invoke-RoboCopyMirror -SourceDirectory $srcSkillsRoot -DestinationDirectory $githubSkillsRoot -Label \"GitHub (.github\\\\skills)\"\r\n}\r\n\r\nfunction Sync-SkillsToClaude {\r\n    param(\r\n        [Parameter(Mandatory = $true)]\r\n        [string]$RepoRoot\r\n    )\r\n\r\n    $srcSkillsRoot = Join-Path $RepoRoot \".ai\\skills\"\r\n    $claudeSkillsRoot = Join-Path $RepoRoot \".claude\\skills\"\r\n\r\n    Invoke-RoboCopyMirror -SourceDirectory $srcSkillsRoot -DestinationDirectory $claudeSkillsRoot -Label \"Claude Code (.claude\\\\skills)\"\r\n}\r\n\r\n# --- Main Script ---\r\nif (-not $NoClear) {\r\n    Clear-Host\r\n}\r\n\r\n# We are located under `.ai/skills/<skill>/tools`. Find repo root by going up 4 levels.\r\n$scriptDir = $PSScriptRoot\r\n$repoRoot = (Join-Path -Path $scriptDir -ChildPath \"..\\..\\..\\..\" | Resolve-Path).Path\r\n\r\n# `.ai` folder path\r\n$aiDir = Join-Path $repoRoot \".ai\"\r\n\r\n# Discover source files matching *instructions.md in the .ai folder\r\n[System.IO.FileSystemInfo[]]$sourceInstructionFiles = Get-ChildItem -Path $aiDir -Filter \"*instructions.md\" -File | Sort-Object Name\r\n\r\nif ($null -eq $sourceInstructionFiles -or $sourceInstructionFiles.Length -eq 0) {\r\n    Write-Warning \"No '*instructions.md' files found in '$aiDir'. Nothing to process.\"\r\n    exit 0\r\n}\r\n\r\nWrite-Host \"Found the following source instruction files in '$aiDir':\"\r\n$sourceInstructionFiles | ForEach-Object { Write-Host \"- $($_.Name)\" }\r\n\r\n# Mode parameter handling: if 'ALL' or 'AUTO', skip interactive prompt\r\nif ($Mode -eq 'ALL') {\r\n    Write-Host \"Selected: ALL (parameter mode)\"\r\n    $updateCline = $true\r\n    $updateCopilot = $true\r\n    $updateRooCode = $true\r\n    $updateCodex = $true\r\n    $updateClaude = $true\r\n}\r\nelseif ($Mode -eq 'AUTO') {\r\n    Write-Host \"Selected: AUTO (parameter mode)\"\r\n    # Determine available agents based on instruction files\r\n    $updateCline = Test-HasInstructionFiles -Path (Join-Path $repoRoot '.clinerules')\r\n    $updateRooCode = Test-HasInstructionFiles -Path (Join-Path $repoRoot '.roo\\rules')\r\n    $updateCopilot = Test-Path (Join-Path $repoRoot '.github\\copilot-instructions.md') -PathType Leaf\r\n    $updateCodex = Test-Path (Join-Path $repoRoot 'AGENTS.md') -PathType Leaf\r\n    $updateClaude = Test-HasInstructionFiles -Path (Join-Path $repoRoot '.claude')\r\n    Write-Host \"Agents to update based on available instruction files:\"\r\n    if ($updateCline) { Write-Host \"- CLINE\" }\r\n    if ($updateRooCode) { Write-Host \"- ROO CODE\" }\r\n    if ($updateCopilot) { Write-Host \"- GitHub CoPilot\" }\r\n    if ($updateCodex) { Write-Host \"- OpenAI Codex\" }\r\n    if ($updateClaude) { Write-Host \"- Claude Code\" }\r\n}\r\nelseif ($Mode -and $Mode -ne '') {\r\n    # Specific agent mode (e.g., CLINE, \"ROO CODE\", etc.)\r\n    $updateCline = ($Mode -eq 'CLINE')\r\n    $updateCopilot = ($Mode -eq 'GitHub CoPilot')\r\n    $updateRooCode = ($Mode -eq 'ROO CODE')\r\n    $updateCodex = ($Mode -eq 'OpenAI Codex')\r\n    $updateClaude = ($Mode -eq 'Claude Code')\r\n    Write-Host \"Selected: $Mode (parameter mode)\"\r\n}\r\nelse {\r\n    # User prompt for agent selection\r\n    # Detect available agents for interactive menu\r\n    $hasCline = Test-HasInstructionFiles -Path (Join-Path $repoRoot '.clinerules')\r\n    $hasRooCode = Test-HasInstructionFiles -Path (Join-Path $repoRoot '.roo\\rules')\r\n    $hasCopilot = Test-Path (Join-Path $repoRoot '.github\\copilot-instructions.md') -PathType Leaf\r\n    $hasCodex = Test-Path (Join-Path $repoRoot 'AGENTS.md') -PathType Leaf\r\n    $hasClaude = Test-HasInstructionFiles -Path (Join-Path $repoRoot '.claude')\r\n\r\n    Write-Host \"`r`nDetected AI agents with instruction files:\"\r\n    if ($hasCline) { Write-Host \"- CLINE\" }\r\n    if ($hasRooCode) { Write-Host \"- ROO CODE\" }\r\n    if ($hasCopilot) { Write-Host \"- GitHub CoPilot\" }\r\n    if ($hasCodex) { Write-Host \"- OpenAI Codex\" }\r\n    if ($hasClaude) { Write-Host \"- Claude Code\" }\r\n\r\n    Write-Host \"\"\r\n    Write-Host \"==============================================================\"\r\n    Write-Host \"Select Agent Instruction Set to Update\"\r\n    Write-Host \"--------------------------------------------------------------\"\r\n    Write-Host \"1. AUTO           - Update only agents with instruction files (default)\"\r\n    Write-Host \"2. ALL            - Update instructions for all AI agents\"\r\n    Write-Host \"3. CLINE          - Update instructions for CLINE\"\r\n    Write-Host \"4. ROO CODE       - Update instructions for ROO CODE\"\r\n    Write-Host \"5. GitHub CoPilot - Update instructions for GitHub CoPilot\"\r\n    Write-Host \"6. OpenAI Codex   - Update instructions for OpenAI Codex\"\r\n    Write-Host \"7. Claude Code    - Update instructions for Claude Code\"\r\n    Write-Host \"0. Exit\"\r\n    Write-Host \"==============================================================\"\r\n    $selection = Read-Host \"Enter the number of your choice (0-7)\"\r\n\r\n    # Initialize flags\r\n    $updateCline = $false\r\n    $updateCopilot = $false\r\n    $updateRooCode = $false\r\n    $updateCodex = $false\r\n    $updateClaude = $false\r\n\r\n    switch ($selection) {\r\n        '1' {\r\n            $updateCline = Test-HasInstructionFiles -Path (Join-Path $repoRoot '.clinerules')\r\n            $updateRooCode = Test-HasInstructionFiles -Path (Join-Path $repoRoot '.roo\\rules')\r\n            $updateCopilot = Test-Path (Join-Path $repoRoot '.github\\copilot-instructions.md') -PathType Leaf\r\n            $updateCodex = Test-Path (Join-Path $repoRoot 'AGENTS.md') -PathType Leaf\r\n            $updateClaude = Test-HasInstructionFiles -Path (Join-Path $repoRoot '.claude')\r\n            Write-Host \"Selected: AUTO\"\r\n        }\r\n        '2' {\r\n            $updateCline = $true\r\n            $updateCopilot = $true\r\n            $updateRooCode = $true\r\n            $updateCodex = $true\r\n            $updateClaude = $true\r\n            Write-Host \"Selected: ALL\"\r\n        }\r\n        '3' { $updateCline = $true; Write-Host \"Selected: CLINE\" }\r\n        '4' { $updateRooCode = $true; Write-Host \"Selected: ROO CODE\" }\r\n        '5' { $updateCopilot = $true; Write-Host \"Selected: GitHub CoPilot\" }\r\n        '6' { $updateCodex = $true; Write-Host \"Selected: OpenAI Codex\" }\r\n        '7' { $updateClaude = $true; Write-Host \"Selected: Claude Code\" }\r\n        '0' { Write-Host \"Operation cancelled by user.\"; exit 0 }\r\n        default { throw \"Invalid selection. Exiting.\" }\r\n    }\r\n}\r\n\r\n# --- Multiple-File Agent Updates ---\r\nif ($updateCline) {\r\n    Update-MultipleFileAgent -AgentName \"CLINE\" -TargetDirectory \".clinerules\" -SourceFiles $sourceInstructionFiles -RepoRoot $repoRoot\r\n}\r\n\r\nif ($updateRooCode) {\r\n    Update-MultipleFileAgent -AgentName \"ROO CODE\" -TargetDirectory \".roo\\rules\" -SourceFiles $sourceInstructionFiles -RepoRoot $repoRoot\r\n}\r\n\r\n# --- Single-File Agent Updates ---\r\nif ($updateCopilot) {\r\n    $copilotTarget = \".github\\copilot-instructions.md\"\r\n    $githubInstructionsDir = Join-Path $repoRoot \".github\\instructions\"\r\n\r\n    if (Test-Path $githubInstructionsDir -PathType Container) {\r\n        Write-Host \"`r`n--- Updating GitHub CoPilot Instructions (folder-based) ---\"\r\n\r\n        $mainName = \"instructions.md\"\r\n        $mainSource = $sourceInstructionFiles | Where-Object { $_.Name -ieq $mainName } | Select-Object -First 1\r\n        if ($null -eq $mainSource) {\r\n            throw \"Expected source '$mainName' under .ai but none found.\"\r\n        }\r\n\r\n        Copy-FileIfDifferent -SourcePath $mainSource.FullName -TargetPath (Join-Path $repoRoot $copilotTarget)\r\n\r\n        foreach ($sf in $sourceInstructionFiles) {\r\n            if ($sf.Name -ieq $mainName) {\r\n                continue\r\n            }\r\n\r\n            $destination = Join-Path $githubInstructionsDir $sf.Name\r\n            Copy-FileIfDifferent -SourcePath $sf.FullName -TargetPath $destination\r\n        }\r\n    }\r\n    else {\r\n        Update-SingleFileAgent -AgentName \"GitHub CoPilot\" -TargetFilePath $copilotTarget -SourceFiles $sourceInstructionFiles -RepoRoot $repoRoot\r\n    }\r\n}\r\n\r\nif ($updateCodex) {\r\n    Update-SingleFileAgent -AgentName \"OpenAI Codex\" -TargetFilePath \"AGENTS.md\" -SourceFiles $sourceInstructionFiles -RepoRoot $repoRoot\r\n}\r\n\r\n# --- Claude Code (multiple-file agent) ---\r\nif ($updateClaude) {\r\n    Update-MultipleFileAgent -AgentName \"Claude Code\" -TargetDirectory \".claude\" -SourceFiles $sourceInstructionFiles -RepoRoot $repoRoot\r\n}\r\n\r\n# --- Skills mirroring ---\r\nif ($updateRooCode -or $Mode -eq 'ALL' -or $Mode -eq 'AUTO') {\r\n    Sync-SkillsToRoo -RepoRoot $repoRoot\r\n}\r\n\r\n# GitHub Copilot: mirror skills to `.github/skills` (Copilot tries to load from there).\r\nif ($updateCopilot -or $Mode -eq 'ALL' -or $Mode -eq 'AUTO') {\r\n    Sync-SkillsToGitHub -RepoRoot $repoRoot\r\n}\r\n\r\n# Claude Code: mirror skills to `.claude/skills`.\r\nif ($updateClaude -or $Mode -eq 'ALL' -or $Mode -eq 'AUTO') {\r\n    Sync-SkillsToClaude -RepoRoot $repoRoot\r\n}\r\n\r\nWrite-Host \"`r`nAll selected operations completed successfully.\"\r\n\r\n# Only pause when launched by double-click (Explorer). In CI / terminal usage, do not pause.\r\nif ($Host.Name -and $Host.Name -notlike '*ConsoleHost*') {\r\n    Invoke-Pause\r\n}\r\n"
  },
  {
    "path": ".gitignore",
    "content": "## Ignore Visual Studio temporary files, build results, and\r\n## files generated by popular Visual Studio add-ons.\r\n##\r\n## Get latest from https://github.com/github/gitignore/blob/master/VisualStudio.gitignore\r\n\r\n# User-specific files\r\n*.rsuser\r\n*.suo\r\n*.user\r\n*.userosscache\r\n*.sln.docstates\r\n\r\n# User-specific files (MonoDevelop/Xamarin Studio)\r\n*.userprefs\r\n\r\n# Mono auto generated files\r\nmono_crash.*\r\n\r\n# Build results\r\n[Dd]ebug/\r\n[Dd]ebugPublic/\r\n[Rr]elease/\r\n[Rr]eleases/\r\nx64/\r\nx86/\r\n[Aa][Rr][Mm]/\r\n[Aa][Rr][Mm]64/\r\nbld/\r\n[Bb]in/\r\n[Oo]bj/\r\n[Ll]og/\r\n[Ll]ogs/\r\n\r\n# Visual Studio 2015/2017 cache/options directory\r\n.vs/\r\n# Uncomment if you have tasks that create the project's static files in wwwroot\r\n#wwwroot/\r\n\r\n# Visual Studio 2017 auto generated files\r\nGenerated\\ Files/\r\n\r\n# MSTest test Results\r\n[Tt]est[Rr]esult*/\r\n[Bb]uild[Ll]og.*\r\n\r\n# NUnit\r\n*.VisualState.xml\r\nTestResult.xml\r\nnunit-*.xml\r\n\r\n# Build Results of an ATL Project\r\n[Dd]ebugPS/\r\n[Rr]eleasePS/\r\ndlldata.c\r\n\r\n# Benchmark Results\r\nBenchmarkDotNet.Artifacts/\r\n\r\n# .NET Core\r\nproject.lock.json\r\nproject.fragment.lock.json\r\nartifacts/\r\n\r\n# StyleCop\r\nStyleCopReport.xml\r\n\r\n# Files built by Visual Studio\r\n*_i.c\r\n*_p.c\r\n*_h.h\r\n*.ilk\r\n*.meta\r\n*.obj\r\n*.iobj\r\n*.pch\r\n*.pdb\r\n*.ipdb\r\n*.pgc\r\n*.pgd\r\n*.rsp\r\n*.sbr\r\n*.tlb\r\n*.tli\r\n*.tlh\r\n*.tmp\r\n*.tmp_proj\r\n*_wpftmp.csproj\r\n*.log\r\n*.vspscc\r\n*.vssscc\r\n.builds\r\n*.pidb\r\n*.svclog\r\n*.scc\r\n\r\n# Chutzpah Test files\r\n_Chutzpah*\r\n\r\n# Visual C++ cache files\r\nipch/\r\n*.aps\r\n*.ncb\r\n*.opendb\r\n*.opensdf\r\n*.sdf\r\n*.cachefile\r\n*.VC.db\r\n*.VC.VC.opendb\r\n\r\n# Visual Studio profiler\r\n*.psess\r\n*.vsp\r\n*.vspx\r\n*.sap\r\n\r\n# Visual Studio Trace Files\r\n*.e2e\r\n\r\n# TFS 2012 Local Workspace\r\n$tf/\r\n\r\n# Guidance Automation Toolkit\r\n*.gpState\r\n\r\n# ReSharper is a .NET coding add-in\r\n_ReSharper*/\r\n*.[Rr]e[Ss]harper\r\n*.DotSettings.user\r\n\r\n# TeamCity is a build add-in\r\n_TeamCity*\r\n\r\n# DotCover is a Code Coverage Tool\r\n*.dotCover\r\n\r\n# AxoCover is a Code Coverage Tool\r\n.axoCover/*\r\n!.axoCover/settings.json\r\n\r\n# Visual Studio code coverage results\r\n*.coverage\r\n*.coveragexml\r\n\r\n# NCrunch\r\n_NCrunch_*\r\n.*crunch*.local.xml\r\nnCrunchTemp_*\r\n\r\n# MightyMoose\r\n*.mm.*\r\nAutoTest.Net/\r\n\r\n# Web workbench (sass)\r\n.sass-cache/\r\n\r\n# Installshield output folder\r\n[Ee]xpress/\r\n\r\n# DocProject is a documentation generator add-in\r\nDocProject/buildhelp/\r\nDocProject/Help/*.HxT\r\nDocProject/Help/*.HxC\r\nDocProject/Help/*.hhc\r\nDocProject/Help/*.hhk\r\nDocProject/Help/*.hhp\r\nDocProject/Help/Html2\r\nDocProject/Help/html\r\n\r\n# Click-Once directory\r\npublish/\r\n\r\n# Publish Web Output\r\n*.[Pp]ublish.xml\r\n*.azurePubxml\r\n# Note: Comment the next line if you want to checkin your web deploy settings,\r\n# but database connection strings (with potential passwords) will be unencrypted\r\n*.pubxml\r\n*.publishproj\r\n\r\n# Microsoft Azure Web App publish settings. Comment the next line if you want to\r\n# checkin your Azure Web App publish settings, but sensitive information contained\r\n# in these scripts will be unencrypted\r\nPublishScripts/\r\n\r\n# NuGet Packages\r\n*.nupkg\r\n# NuGet Symbol Packages\r\n*.snupkg\r\n# The packages folder can be ignored because of Package Restore\r\n**/[Pp]ackages/*\r\n# except build/, which is used as an MSBuild target.\r\n!**/[Pp]ackages/build/\r\n# Uncomment if necessary however generally it will be regenerated when needed\r\n#!**/[Pp]ackages/repositories.config\r\n# NuGet v3's project.json files produces more ignorable files\r\n*.nuget.props\r\n*.nuget.targets\r\n\r\n# Microsoft Azure Build Output\r\ncsx/\r\n*.build.csdef\r\n\r\n# Microsoft Azure Emulator\r\necf/\r\nrcf/\r\n\r\n# Windows Store app package directories and files\r\nAppPackages/\r\nBundleArtifacts/\r\nPackage.StoreAssociation.xml\r\n_pkginfo.txt\r\n*.appx\r\n*.appxbundle\r\n*.appxupload\r\n\r\n# Visual Studio cache files\r\n# files ending in .cache can be ignored\r\n*.[Cc]ache\r\n# but keep track of directories ending in .cache\r\n!?*.[Cc]ache/\r\n\r\n# Others\r\nClientBin/\r\n~$*\r\n*~\r\n*.dbmdl\r\n*.dbproj.schemaview\r\n*.jfm\r\n*.pfx\r\n*.publishsettings\r\norleans.codegen.cs\r\n\r\n# Including strong name files can present a security risk\r\n# (https://github.com/github/gitignore/pull/2483#issue-259490424)\r\n#*.snk\r\n\r\n# Since there are multiple workflows, uncomment next line to ignore bower_components\r\n# (https://github.com/github/gitignore/pull/1529#issuecomment-104372622)\r\n#bower_components/\r\n\r\n# RIA/Silverlight projects\r\nGenerated_Code/\r\n\r\n# Backup & report files from converting an old project file\r\n# to a newer Visual Studio version. Backup files are not needed,\r\n# because we have git ;-)\r\n_UpgradeReport_Files/\r\nBackup*/\r\nUpgradeLog*.XML\r\nUpgradeLog*.htm\r\nServiceFabricBackup/\r\n*.rptproj.bak\r\n\r\n# SQL Server files\r\n*.mdf\r\n*.ldf\r\n*.ndf\r\n\r\n# Business Intelligence projects\r\n*.rdl.data\r\n*.bim.layout\r\n*.bim_*.settings\r\n*.rptproj.rsuser\r\n*- [Bb]ackup.rdl\r\n*- [Bb]ackup ([0-9]).rdl\r\n*- [Bb]ackup ([0-9][0-9]).rdl\r\n\r\n# Microsoft Fakes\r\nFakesAssemblies/\r\n\r\n# GhostDoc plugin setting file\r\n*.GhostDoc.xml\r\n\r\n# Node.js Tools for Visual Studio\r\n.ntvs_analysis.dat\r\nnode_modules/\r\n\r\n# Visual Studio 6 build log\r\n*.plg\r\n\r\n# Visual Studio 6 workspace options file\r\n*.opt\r\n\r\n# Visual Studio 6 auto-generated workspace file (contains which files were open etc.)\r\n*.vbw\r\n\r\n# Visual Studio LightSwitch build output\r\n**/*.HTMLClient/GeneratedArtifacts\r\n**/*.DesktopClient/GeneratedArtifacts\r\n**/*.DesktopClient/ModelManifest.xml\r\n**/*.Server/GeneratedArtifacts\r\n**/*.Server/ModelManifest.xml\r\n_Pvt_Extensions\r\n\r\n# Paket dependency manager\r\n.paket/paket.exe\r\npaket-files/\r\n\r\n# FAKE - F# Make\r\n.fake/\r\n\r\n# CodeRush personal settings\r\n.cr/personal\r\n\r\n# Python Tools for Visual Studio (PTVS)\r\n__pycache__/\r\n*.pyc\r\n\r\n# Cake - Uncomment if you are using it\r\n# tools/**\r\n# !tools/packages.config\r\n\r\n# Tabs Studio\r\n*.tss\r\n\r\n# Telerik's JustMock configuration file\r\n*.jmconfig\r\n\r\n# BizTalk build output\r\n*.btp.cs\r\n*.btm.cs\r\n*.odx.cs\r\n*.xsd.cs\r\n\r\n# OpenCover UI analysis results\r\nOpenCover/\r\n\r\n# Azure Stream Analytics local run output\r\nASALocalRun/\r\n\r\n# MSBuild Binary and Structured Log\r\n*.binlog\r\n\r\n# NVidia Nsight GPU debugger configuration file\r\n*.nvuser\r\n\r\n# MFractors (Xamarin productivity tool) working folder\r\n.mfractor/\r\n\r\n# Local History for Visual Studio\r\n.localhistory/\r\n\r\n# BeatPulse healthcheck temp database\r\nhealthchecksdb\r\n\r\n# Backup folder for Package Reference Convert tool in Visual Studio 2017\r\nMigrationBackup/\r\n\r\n# Ionide (cross platform F# VS Code tools) working folder\r\n.ionide/\r\n/FocusLogger/Resources/BuildDate.txt\r\n/FocusLogger/Documents/Files\r\n/.claude/settings.local.json\r\nDocuments/Files\r\n"
  },
  {
    "path": ".markdownlint.json",
    "content": "// https://github.com/DavidAnson/markdownlint\n{\n    \"default\": true,\n    \"MD013\": false, // There are lines that are longer than the configured line_length (default: 80 characters).\n    \"MD033\": false, // Raw HTML is used in a markdown document.\n    \"MD041\": false, // The first line in a document is not a top-level heading.\n}"
  },
  {
    "path": "Documents/App_1_Sign.ps1",
    "content": "Import-Module \"d:\\_Backup\\Configuration\\SSL\\Tools\\app_signModule.ps1\" -Force\r\n\r\n[string[]]$appFiles = @(\r\n    \"..\\FocusLogger\\bin\\Release\\publish\\JocysCom.FocusLogger.exe\"\r\n)\r\n[string]$appName = \"Jocys.com Focus Logger\"\r\n[string]$appLink = \"https://www.jocys.com\"\r\n\r\nProcessFiles $appName $appLink $appFiles\r\npause\r\n"
  },
  {
    "path": "Documents/App_2_Zip.ps1",
    "content": "\r\n# Make sure the output directories exist\r\n$filesDir = Join-Path $PSScriptRoot \"Files\"\r\n$binDir = Join-Path $PSScriptRoot \"..\\Resources\"\r\n\r\n$file1=\"JocysCom.FocusLogger.exe\"\r\nif (-not [System.IO.File]::Exists([System.IO.Path]::Combine($filesDir, $file1))){\r\n    [System.IO.File]::Copy([System.IO.Path]::Combine($PSScriptRoot, \"..\\App\\bin\\Release\\publish\\\", $file1), [System.IO.Path]::Combine($filesDir, $file1))\r\n}\r\n& \"$binDir\\ZipFiles.ps1\" $filesDir \"$filesDir\\JocysCom.FocusLogger.zip\" $file1 $true\r\n\r\n"
  },
  {
    "path": "Documents/Take_Screenshot.ps1",
    "content": "# Take_Screenshot.ps1\n# Launches FocusLogger, Notepad, and Explorer, switches between them\n# to generate focus log entries, then captures a screenshot using PrintWindow.\n#\n# Usage: Right-click > Run with PowerShell, or run from terminal:\n#   powershell -ExecutionPolicy Bypass -File Take_Screenshot.ps1\n\nAdd-Type -AssemblyName System.Windows.Forms\nAdd-Type -AssemblyName System.Drawing\n\n# Load C# helper class (supports re-running in the same session).\n$csFilePath = Join-Path (Split-Path -Parent $MyInvocation.MyCommand.Path) \"Take_Screenshot.ps1.cs\"\n$csFileContent = Get-Content -Path $csFilePath -Raw\n$fileHash = Get-FileHash -InputStream ([System.IO.MemoryStream]::new([System.Text.Encoding]::UTF8.GetBytes($csFileContent))) -Algorithm SHA256\n$className = \"TakeScreenshot\"\nif (-not $script:loadedClasses) { $script:loadedClasses = @{} }\nif (-not $script:loadedClasses.ContainsKey($fileHash.Hash)) {\n    $className += (Get-Date -Format \"yyyyMMddHHmmss\")\n    $csCode = $csFileContent -replace \"TakeScreenshot\", $className\n    Add-Type -TypeDefinition $csCode -ReferencedAssemblies System.Windows.Forms, System.Drawing\n    $script:loadedClasses[$fileHash.Hash] = $className\n} else {\n    $className = $script:loadedClasses[$fileHash.Hash]\n}\n$helper = [Type]$className\n\n$scriptDir = Split-Path -Parent $MyInvocation.MyCommand.Path\n$screenshotPath = Join-Path $scriptDir \"Images\\JocysCom.FocusLogger.png\"\n$tempFile = Join-Path $env:TEMP \"My Document.txt\"\n\n# Find FocusLogger executable.\n$appPath = Join-Path $scriptDir \"..\\FocusLogger\\bin\\Debug\\net8.0-windows\\JocysCom.FocusLogger.exe\"\nif (-not (Test-Path $appPath)) {\n    $appPath = Join-Path $scriptDir \"..\\FocusLogger\\bin\\Release\\net8.0-windows\\JocysCom.FocusLogger.exe\"\n}\nif (-not (Test-Path $appPath)) {\n    Write-Error \"FocusLogger not found. Build the project first.\"\n    exit 1\n}\n$appPath = Resolve-Path $appPath\n\n$focusLogger = $null\n$notepad = $null\n$explorerProc = $null\n\ntry {\n    # 1. Start FocusLogger (centered).\n    Write-Host \"1. Starting FocusLogger...\"\n    $focusLogger = Start-Process -FilePath $appPath -PassThru\n    Start-Sleep -Seconds 2\n    $focusLogger.Refresh()\n    $flHwnd = $focusLogger.MainWindowHandle\n    $helper::CenterAndResize($flHwnd, 920, 480)\n    Start-Sleep -Milliseconds 500\n\n    # 2. Start Notepad with a document.\n    Write-Host \"2. Starting Notepad...\"\n    \"Sample document for Focus Logger screenshot.\" | Out-File -FilePath $tempFile -Encoding UTF8\n    $notepadPidsBefore = @(Get-Process -Name \"Notepad\" -ErrorAction SilentlyContinue | ForEach-Object { $_.Id })\n    Start-Process -FilePath \"notepad.exe\" -ArgumentList \"`\"$tempFile`\"\"\n    Start-Sleep -Seconds 2\n    $notepad = Get-Process -Name \"Notepad\" -ErrorAction SilentlyContinue |\n        Where-Object { $notepadPidsBefore -notcontains $_.Id -and $_.MainWindowHandle -ne [IntPtr]::Zero } |\n        Select-Object -First 1\n    if (-not $notepad) {\n        $notepad = Get-Process -Name \"Notepad\" -ErrorAction SilentlyContinue |\n            Where-Object { $_.MainWindowHandle -ne [IntPtr]::Zero } |\n            Select-Object -First 1\n    }\n    $npHwnd = if ($notepad) { $notepad.MainWindowHandle } else { [IntPtr]::Zero }\n    if ($npHwnd -ne [IntPtr]::Zero) {\n        $helper::CenterAndResize($npHwnd, 1040, 680, -50)\n        $helper::SetForegroundWindow($npHwnd) | Out-Null\n        Start-Sleep -Milliseconds 500\n    }\n\n    # 3. Start Explorer.\n    Write-Host \"3. Starting Explorer...\"\n    $explorerHwndsBefore = @(Get-Process explorer -ErrorAction SilentlyContinue |\n        Where-Object { $_.MainWindowHandle -ne [IntPtr]::Zero } |\n        ForEach-Object { $_.MainWindowHandle })\n    Start-Process \"explorer.exe\" -ArgumentList \"C:\\Windows\"\n    Start-Sleep -Seconds 2\n    $explorerProc = Get-Process explorer -ErrorAction SilentlyContinue |\n        Where-Object { $_.MainWindowHandle -ne [IntPtr]::Zero -and $explorerHwndsBefore -notcontains $_.MainWindowHandle } |\n        Select-Object -First 1\n    if (-not $explorerProc) {\n        $explorerProc = Get-Process explorer -ErrorAction SilentlyContinue |\n            Where-Object { $_.MainWindowTitle -match \"Windows\" -and $_.MainWindowHandle -ne [IntPtr]::Zero } |\n            Select-Object -First 1\n    }\n    $exHwnd = if ($explorerProc) { $explorerProc.MainWindowHandle } else { [IntPtr]::Zero }\n    if ($exHwnd -ne [IntPtr]::Zero) {\n        $helper::CenterAndResize($exHwnd, 700, 400)\n        $helper::SetForegroundWindow($exHwnd) | Out-Null\n        Start-Sleep -Milliseconds 500\n    }\n\n    # 4. Switch focus between windows to generate log entries.\n    Write-Host \"4. Switching focus...\"\n    if ($npHwnd -ne [IntPtr]::Zero) { $helper::SetForegroundWindow($npHwnd) | Out-Null; Start-Sleep -Milliseconds 800 }\n    if ($exHwnd -ne [IntPtr]::Zero) { $helper::SetForegroundWindow($exHwnd) | Out-Null; Start-Sleep -Milliseconds 800 }\n    if ($npHwnd -ne [IntPtr]::Zero) { $helper::SetForegroundWindow($npHwnd) | Out-Null; Start-Sleep -Milliseconds 800 }\n    if ($exHwnd -ne [IntPtr]::Zero) { $helper::SetForegroundWindow($exHwnd) | Out-Null; Start-Sleep -Milliseconds 800 }\n    if ($npHwnd -ne [IntPtr]::Zero) { $helper::SetForegroundWindow($npHwnd) | Out-Null; Start-Sleep -Milliseconds 800 }\n\n    # 5. Move Notepad behind FocusLogger as white background, then capture.\n    Write-Host \"5. Taking screenshot...\"\n    if ($npHwnd -ne [IntPtr]::Zero) {\n        $helper::CenterAndResize($npHwnd, 1040, 680, -50)\n        $helper::SetForegroundWindow($npHwnd) | Out-Null\n        Start-Sleep -Milliseconds 300\n    }\n    $helper::SetForegroundWindow($flHwnd) | Out-Null\n    Start-Sleep -Seconds 1\n    $helper::CaptureWindow($flHwnd, $screenshotPath)\n    Write-Host \"   Screenshot saved to: $screenshotPath\"\n\n} finally {\n    Write-Host \"6. Cleaning up...\"\n    if ($notepad -and -not $notepad.HasExited) { $notepad.Kill(); $notepad.WaitForExit(3000) | Out-Null }\n    if ($focusLogger -and -not $focusLogger.HasExited) { $focusLogger.Kill(); $focusLogger.WaitForExit(3000) | Out-Null }\n    if ($explorerProc -and -not $explorerProc.HasExited) { $explorerProc.CloseMainWindow() | Out-Null }\n    Start-Sleep -Milliseconds 500\n    Remove-Item $tempFile -ErrorAction SilentlyContinue\n    Write-Host \"Done.\"\n}\n"
  },
  {
    "path": "Documents/Take_Screenshot.ps1.cs",
    "content": "using System;\nusing System.Drawing;\nusing System.Drawing.Imaging;\nusing System.Runtime.InteropServices;\nusing System.Windows.Forms;\n\npublic class TakeScreenshot\n{\n\t[DllImport(\"user32.dll\")]\n\tpublic static extern bool SetForegroundWindow(IntPtr hWnd);\n\t[DllImport(\"user32.dll\")]\n\tpublic static extern bool ShowWindow(IntPtr hWnd, int nCmdShow);\n\t[DllImport(\"user32.dll\")]\n\tpublic static extern bool SetWindowPos(IntPtr hWnd, IntPtr hWndInsertAfter, int X, int Y, int cx, int cy, uint uFlags);\n\t[DllImport(\"user32.dll\")]\n\tpublic static extern bool GetWindowRect(IntPtr hWnd, out RECT lpRect);\n\t[DllImport(\"user32.dll\")]\n\tpublic static extern bool PrintWindow(IntPtr hWnd, IntPtr hdcBlt, uint nFlags);\n\t[DllImport(\"dwmapi.dll\")]\n\tpublic static extern int DwmGetWindowAttribute(IntPtr hwnd, int dwAttribute, out RECT pvAttribute, int cbAttribute);\n\n\t[StructLayout(LayoutKind.Sequential)]\n\tpublic struct RECT\n\t{\n\t\tpublic int Left, Top, Right, Bottom;\n\t}\n\n\tpublic static RECT GetExtendedFrameBounds(IntPtr hwnd)\n\t{\n\t\tRECT rect;\n\t\tDwmGetWindowAttribute(hwnd, 9, out rect, Marshal.SizeOf(typeof(RECT)));\n\t\treturn rect;\n\t}\n\n\tpublic static void CenterAndResize(IntPtr hwnd, int w, int h, int yOffset = 0)\n\t{\n\t\tvar screen = Screen.PrimaryScreen.WorkingArea;\n\t\tint x = (screen.Width - w) / 2 + screen.Left;\n\t\tint y = (screen.Height - h) / 2 + screen.Top + yOffset;\n\t\tSetWindowPos(hwnd, IntPtr.Zero, x, y, w, h, 0x0040);\n\t}\n\n\t/// <summary>\n\t/// Captures a window using CopyFromScreen with DWM extended frame bounds\n\t/// for accurate visible area (excludes invisible shadow border).\n\t/// </summary>\n\tpublic static void CaptureWindow(IntPtr hwnd, string filePath)\n\t{\n\t\tvar rect = GetExtendedFrameBounds(hwnd);\n\t\tint w = rect.Right - rect.Left;\n\t\tint h = rect.Bottom - rect.Top;\n\t\tusing (var bitmap = new Bitmap(w, h, PixelFormat.Format32bppArgb))\n\t\t{\n\t\t\tusing (var graphics = Graphics.FromImage(bitmap))\n\t\t\t{\n\t\t\t\tgraphics.CopyFromScreen(rect.Left, rect.Top, 0, 0, new Size(w, h));\n\t\t\t}\n\t\t\tbitmap.Save(filePath, ImageFormat.Png);\n\t\t}\n\t}\n}\n"
  },
  {
    "path": "FocusLogger/App.xaml",
    "content": "﻿<Application\r\n\tx:Class=\"JocysCom.FocusLogger.App\"\r\n\txmlns=\"http://schemas.microsoft.com/winfx/2006/xaml/presentation\"\r\n\txmlns:x=\"http://schemas.microsoft.com/winfx/2006/xaml\"\r\n\txmlns:local=\"clr-namespace:JocysCom.FocusLogger\"\r\n\tStartupUri=\"MainWindow.xaml\">\r\n\t<Application.Resources>\r\n\t\t<ResourceDictionary>\r\n\t\t\t<ResourceDictionary.MergedDictionaries>\r\n\t\t\t\t<ResourceDictionary Source=\"JocysCom/Controls/Themes/Default.xaml\" />\r\n\t\t\t\t<ResourceDictionary Source=\"JocysCom/Controls/Themes/Icons.xaml\" />\r\n\t\t\t\t<ResourceDictionary Source=\"Resources/Icons/Icons_Default.xaml\" />\r\n\t\t\t</ResourceDictionary.MergedDictionaries>\r\n\t\t</ResourceDictionary>\r\n\t</Application.Resources>\r\n</Application>\r\n"
  },
  {
    "path": "FocusLogger/App.xaml.cs",
    "content": "﻿using System;\nusing System.Windows;\n\nnamespace JocysCom.FocusLogger\n{\n\n\tpublic partial class App : Application\n\t{\n\t\tpublic App()\n\t\t{\n\t\t\tSetDPIAware();\n\t\t}\n\n\t\tinternal class NativeMethods\n\t\t{\n\t\t\t[System.Runtime.InteropServices.DllImport(\"user32.dll\")]\n\t\t\tinternal static extern bool SetProcessDPIAware();\n\t\t}\n\n\t\tpublic static void SetDPIAware()\n\t\t{\n\t\t\t// DPI aware property must be set before application window is created.\n\t\t\tif (Environment.OSVersion.Version.Major >= 6)\n\t\t\t\tNativeMethods.SetProcessDPIAware();\n\t\t}\n\n\t}\n}\n"
  },
  {
    "path": "FocusLogger/AssemblyInfo.cs",
    "content": "using System.Windows;\r\n\r\n[assembly: ThemeInfo(\r\n\tResourceDictionaryLocation.None, //where theme specific resource dictionaries are located\r\n\t\t\t\t\t\t\t\t\t //(used if a resource is not found in the page,\r\n\t\t\t\t\t\t\t\t\t // or application resource dictionaries)\r\n\tResourceDictionaryLocation.SourceAssembly //where the generic resource dictionary is located\r\n\t\t\t\t\t\t\t\t\t\t\t  //(used if a resource is not found in the page,\r\n\t\t\t\t\t\t\t\t\t\t\t  // app, or any theme specific resource dictionaries)\r\n)]\r\n"
  },
  {
    "path": "FocusLogger/Common/DataItem.cs",
    "content": "﻿using JocysCom.ClassLibrary.Configuration;\r\nusing System;\r\nusing System.ComponentModel;\r\nusing System.Runtime.CompilerServices;\r\n\r\nnamespace JocysCom.FocusLogger\r\n{\r\n\tpublic class DataItem : SettingsItem\r\n\t{\r\n\r\n\t\tpublic DateTime Date { get => _Date; set => SetProperty(ref _Date, value); }\r\n\t\tDateTime _Date;\r\n\r\n\t\tpublic int ProcessId { get => _ProcessId; set => SetProperty(ref _ProcessId, value); }\r\n\t\tint _ProcessId;\r\n\r\n\t\tpublic string ProcessName { get => _ProcessName; set => SetProperty(ref _ProcessName, value); }\r\n\t\tstring _ProcessName;\r\n\r\n\t\tpublic string ProcessPath { get => _ProcessPath; set => SetProperty(ref _ProcessPath, value); }\r\n\t\tstring _ProcessPath;\r\n\r\n\t\tpublic string WindowTitle { get => _WindowTitle; set => SetProperty(ref _WindowTitle, value); }\r\n\t\tstring _WindowTitle;\r\n\r\n\t\tpublic string WindowClassName { get => _WindowClassName; set => SetProperty(ref _WindowClassName, value); }\r\n\t\tstring _WindowClassName;\r\n\r\n\t\tpublic bool HasMouse { get => _HasMouse; set => SetProperty(ref _HasMouse, value); }\r\n\t\tbool _HasMouse;\r\n\r\n\t\tpublic bool HasKeyboard { get => _HasKeyboard; set => SetProperty(ref _HasKeyboard, value); }\r\n\t\tbool _HasKeyboard;\r\n\r\n\t\tpublic bool HasCaret { get => _HasCaret; set => SetProperty(ref _HasCaret, value); }\r\n\t\tbool _HasCaret;\r\n\r\n\t\tpublic bool IsActive { get => _IsActive; set => SetProperty(ref _IsActive, value); }\r\n\t\tbool _IsActive;\r\n\r\n\t\tpublic bool NonPath { get => _IsError; set => SetProperty(ref _IsError, value); }\r\n\t\tbool _IsError;\r\n\r\n\t\tpublic bool IsSame(DataItem item)\r\n\t\t{\r\n\t\t\treturn\r\n\t\t\titem.ProcessId == ProcessId &&\r\n\t\t\titem.HasMouse == HasMouse &&\r\n\t\t\titem.HasKeyboard == HasKeyboard &&\r\n\t\t\titem.HasCaret == HasCaret &&\r\n\t\t\titem.IsActive == IsActive;\r\n\t\t}\r\n\r\n\t\tpublic System.Windows.MessageBoxImage StatusCode { get => _StatusCode; set => SetProperty(ref _StatusCode, value); }\r\n\t\tSystem.Windows.MessageBoxImage _StatusCode;\r\n\r\n\t}\r\n}\r\n"
  },
  {
    "path": "FocusLogger/Common/DataItemType.cs",
    "content": "﻿namespace JocysCom.FocusLogger\r\n{\r\n\tpublic enum DataItemType\r\n\t{\r\n\t\tNone = 0,\r\n\t}\r\n}\r\n"
  },
  {
    "path": "FocusLogger/Common/NativeMethods.cs",
    "content": "﻿using System;\r\nusing System.Runtime.InteropServices;\r\nusing System.Text;\r\n\r\nnamespace JocysCom.FocusLogger\r\n{\r\n\tinternal class NativeMethods\r\n\t{\r\n\t\t// https://docs.microsoft.com/en-gb/windows/win32/api/winuser/\r\n\r\n\t\t/// <summary>\r\n\t\t/// Get handle to the window with the keyboard focus.\r\n\t\t/// </summary>\r\n\t\t/// <returns>\r\n\t\t/// The return value is the handle to the window with the keyboard focus.\r\n\t\t/// If the calling thread's message queue does not have an associated window with the keyboard focus, the return value is NULL.\r\n\t\t/// </returns>\r\n\t\t[DllImport(\"user32.dll\", CharSet = CharSet.Auto, ExactSpelling = true)]\r\n\t\tinternal static extern IntPtr GetFocus();\r\n\r\n\t\t/// <summary>\r\n\t\t/// Window which is active.\r\n\t\t/// </summary>\r\n\t\t/// <returns>\r\n\t\t/// The return value is the handle to the active window attached to the calling thread's message queue.\r\n\t\t/// Otherwise, the return value is NULL.\r\n\t\t/// </returns>\r\n\t\t[DllImport(\"user32.dll\", CharSet = CharSet.Auto, ExactSpelling = true)]\r\n\t\tinternal static extern IntPtr GetActiveWindow();\r\n\r\n\t\t/// <summary>\r\n\t\t/// Get handle to the child window at the top of the Z order.\r\n\t\t/// </summary>\r\n\t\t[DllImport(\"user32.dll\", CharSet = CharSet.Auto, ExactSpelling = true)]\r\n\t\tinternal static extern IntPtr GetTopWindow(IntPtr hWnd);\r\n\r\n\t\t[DllImport(\"user32.dll\", CharSet = CharSet.Auto, ExactSpelling = true)]\r\n\t\tinternal static extern bool IsWindowVisible(IntPtr hWnd);\r\n\r\n\t\t[DllImport(\"user32.dll\", CharSet = CharSet.Auto, ExactSpelling = true)]\r\n\t\tinternal static extern int GetWindowTextLengthW(IntPtr hWnd);\r\n\r\n\t\t/// <summary>\r\n\t\t/// Copies the text of the specified window's title bar (if it has one) into a buffer.\r\n\t\t/// </summary>\r\n\t\t/// <param name=\"hWnd\">A handle to the window or control containing the text.</param>\r\n\t\t/// <param name=\"lpString\">The buffer that will receive the text.</param>\r\n\t\t/// <param name=\"nMaxCount\">The maximum number of characters to copy to the buffer, including the null character.</param>\r\n\t\t/// <returns>If the function succeeds, the return value is the length, in characters, of the copied string, not including the terminating null character.</returns>\r\n\t\t[DllImport(\"user32.dll\", CharSet = CharSet.Unicode, SetLastError = true)]\r\n\t\tstatic extern int GetWindowText(IntPtr hWnd, StringBuilder lpString, int nMaxCount);\r\n\r\n\t\t/// <summary>\r\n\t\t/// A handle to the window that will receive the keyboard input. \r\n\t\t/// </summary>\r\n\t\t/// <returns>\r\n\t\t/// The return value is a handle to the foreground window.\r\n\t\t/// The foreground window can be NULL in certain circumstances, such as when a window is losing activation.\r\n\t\t/// </returns>\r\n\t\t[DllImport(\"user32.dll\", CharSet = CharSet.Auto, ExactSpelling = true)]\r\n\t\tinternal static extern IntPtr GetForegroundWindow();\r\n\r\n\t\t/// <summary>\r\n\t\t/// Retrieves the identifier of the thread that created the specified window and,\r\n\t\t/// optionally, the identifier of the process that created the window.\r\n\t\t/// </summary>\r\n\t\t/// <param name=\"hWnd\">A handle to the window.</param>\r\n\t\t/// <param name=\"lpdwProcessId\">A pointer to a variable that receives the process identifier</param>\r\n\t\t/// <returns>Identifier of the thread that created the window.</returns>\r\n\t\t[DllImport(\"user32.dll\", CharSet = CharSet.Auto, SetLastError = true)]\r\n\t\tinternal static extern int GetWindowThreadProcessId(IntPtr hWnd, out int lpdwProcessId);\r\n\r\n\t\t/// <summary>\r\n\t\t/// Retrieves information about the active window or a specified GUI thread.\r\n\t\t/// </summary>\r\n\t\t/// <param name=\"idThread\">\r\n\t\t/// The identifier for the thread for which information is to be retrieved.\r\n\t\t/// To retrieve this value, use the GetWindowThreadProcessId function.\r\n\t\t/// If this parameter is NULL, the function returns information for the foreground thread.\r\n\t\t/// </param>\r\n\t\t/// <param name=\"pgui\">\r\n\t\t/// A pointer to a GUITHREADINFO structure that receives information describing the thread. \r\n\t\t/// </param>\r\n\t\t/// <returns>If the function succeeds, the return value is nonzero.</returns>\r\n\t\t[DllImport(\"user32.dll\", CharSet = CharSet.Auto, SetLastError = true)]\r\n\t\tinternal static extern bool GetGUIThreadInfo(int idThread, ref GUITHREADINFO pgui);\r\n\r\n\t\t[StructLayout(LayoutKind.Sequential)]\r\n\t\tinternal struct RECT\r\n\t\t{\r\n\t\t\tpublic int iLeft;\r\n\t\t\tpublic int iTop;\r\n\t\t\tpublic int iRight;\r\n\t\t\tpublic int iBottom;\r\n\t\t}\r\n\r\n\t\t[Flags]\r\n\t\tinternal enum GUI\r\n\t\t{\r\n\t\t\t/// <summary>The caret's blink state. This bit is set if the caret is visible.</summary>\r\n\t\t\tGUI_CARETBLINKING = 0x00000001,\r\n\t\t\t/// <summary>The thread's menu state. This bit is set if the thread is in menu mode.</summary>\r\n\t\t\tGUI_INMENUMODE = 0x00000004,\r\n\t\t\t/// <summary>The thread's move state. This bit is set if the thread is in a move or size loop.</summary>\r\n\t\t\tGUI_INMOVESIZE = 0x00000002,\r\n\t\t\t/// <summary>The thread's pop-up menu state. This bit is set if the thread has an active pop-up menu.</summary>\r\n\t\t\tGUI_POPUPMENUMODE = 0x00000010,\r\n\t\t\t/// <summary>The thread's system menu state. This bit is set if the thread is in a system menu mode.</summary>\r\n\t\t\tGUI_SYSTEMMENUMODE = 0x00000008,\r\n\t\t}\r\n\r\n\t\t[StructLayout(LayoutKind.Sequential)]\r\n\t\tinternal struct GUITHREADINFO\r\n\t\t{\r\n\t\t\t/// <summary>The size of this structure, in bytes.</summary>\r\n\t\t\tpublic int cbSize;\r\n\t\t\t/// <summary>The thread state.</summary>\r\n\t\t\tpublic GUI flags;\r\n\t\t\t/// <summary>A handle to the active window within the thread.</summary>\r\n\t\t\tpublic IntPtr hwndActive;\r\n\t\t\t/// <summary>A handle to the window that has the keyboard focus.</summary>\r\n\t\t\tpublic IntPtr hwndFocus;\r\n\t\t\t/// <summary>A handle to the window that has captured the mouse.</summary>\r\n\t\t\tpublic IntPtr hwndCapture;\r\n\t\t\t/// <summary>A handle to the window that owns any active menus.</summary>\r\n\t\t\tpublic IntPtr hwndMenuOwner;\r\n\t\t\t/// <summary>A handle to the window in a move or size loop.</summary>\r\n\t\t\tpublic IntPtr hwndMoveSize;\r\n\t\t\t/// <summary>A handle to the window that is displaying the caret.</summary>\r\n\t\t\tpublic IntPtr hwndCaret;\r\n\t\t\t/// <summary>The caret's bounding rectangle, in client coordinates, relative to the window specified by the hwndCaret member.</summary>\r\n\t\t\tpublic RECT rectCaret;\r\n\t\t}\r\n\r\n\t\tinternal static GUITHREADINFO? GetInfo(IntPtr hWnd)\r\n\t\t{\r\n\t\t\tint lpdwProcessId;\r\n\t\t\tint threadId = GetWindowThreadProcessId(hWnd, out lpdwProcessId);\r\n\t\t\tvar pgui = new GUITHREADINFO();\r\n\t\t\tpgui.cbSize = Marshal.SizeOf(pgui);\r\n\t\t\tif (GetGUIThreadInfo(threadId, ref pgui))\r\n\t\t\t\treturn pgui;\r\n\t\t\treturn null;\r\n\t\t}\r\n\r\n\t\t[DllImport(\"user32.dll\", CharSet = CharSet.Unicode, SetLastError = true)]\r\n\t\tstatic extern int GetClassName(IntPtr hWnd, StringBuilder lpClassName, int nMaxCount);\r\n\r\n\t\tinternal static string GetWindowClassName(IntPtr hWnd)\r\n\t\t{\r\n\t\t\tvar sb = new StringBuilder(256);\r\n\t\t\tvar length = GetClassName(hWnd, sb, sb.Capacity);\r\n\t\t\treturn length > 0 ? sb.ToString() : \"\";\r\n\t\t}\r\n\r\n\t\tinternal static string GetWindowText(IntPtr hWnd)\r\n\t\t{\r\n\t\t\tint textLength = GetWindowTextLengthW(hWnd);\r\n\t\t\tvar lpString = new StringBuilder(textLength + 1);\r\n\t\t\tvar length = GetWindowText(hWnd, lpString, lpString.Capacity);\r\n\t\t\treturn lpString.ToString();\r\n\t\t}\r\n\r\n\t}\r\n}\r\n"
  },
  {
    "path": "FocusLogger/Controls/DataListControl.xaml",
    "content": "<UserControl\r\n\tx:Class=\" JocysCom.FocusLogger.Controls.DataListControl\"\r\n\txmlns=\"http://schemas.microsoft.com/winfx/2006/xaml/presentation\"\r\n\txmlns:x=\"http://schemas.microsoft.com/winfx/2006/xaml\"\r\n\txmlns:converters=\"clr-namespace:JocysCom.ClassLibrary.Controls\"\r\n\txmlns:d=\"http://schemas.microsoft.com/expression/blend/2008\"\r\n\txmlns:mc=\"http://schemas.openxmlformats.org/markup-compatibility/2006\"\r\n\tx:Name=\"This\"\r\n\tHorizontalAlignment=\"Stretch\"\r\n\tVerticalAlignment=\"Stretch\"\r\n\td:DesignHeight=\"270\"\r\n\td:DesignWidth=\"480\"\r\n\tLoaded=\"UserControl_Loaded\"\r\n\tUnloaded=\"UserControl_Unloaded\"\r\n\tmc:Ignorable=\"d\">\r\n\t<Grid>\r\n\t\t<Grid.ColumnDefinitions>\r\n\t\t\t<ColumnDefinition Width=\"*\" />\r\n\t\t</Grid.ColumnDefinitions>\r\n\t\t<Grid.RowDefinitions>\r\n\t\t\t<RowDefinition Height=\"Auto\" />\r\n\t\t\t<RowDefinition Height=\"*\" />\r\n\t\t</Grid.RowDefinitions>\r\n\t\t<StackPanel\r\n\t\t\tGrid.Row=\"0\"\r\n\t\t\tMargin=\"3,3,0,0\"\r\n\t\t\tHorizontalAlignment=\"Left\"\r\n\t\t\tOrientation=\"Horizontal\">\r\n\t\t\t<Button\r\n\t\t\t\tName=\"SaveCsvButton\"\r\n\t\t\t\tMargin=\"2\"\r\n\t\t\t\tPadding=\"3,0,3,0\"\r\n\t\t\t\tAutomationProperties.AutomationId=\"SaveCsvButton\"\r\n\t\t\t\tBackground=\"Transparent\"\r\n\t\t\t\tClick=\"SaveCsvButton_Click\">\r\n\t\t\t\t<StackPanel Style=\"{StaticResource ButtonStackPanel}\">\r\n\t\t\t\t\t<ContentControl Content=\"{StaticResource Icon_Save}\" Focusable=\"False\" />\r\n\t\t\t\t\t<Label Content=\"Save CSV\" Style=\"{StaticResource ButtonLabel}\" />\r\n\t\t\t\t</StackPanel>\r\n\t\t\t</Button>\r\n\t\t\t<Button\r\n\t\t\t\tName=\"ExploreCsvButton\"\r\n\t\t\t\tMargin=\"2\"\r\n\t\t\t\tPadding=\"3,0,3,0\"\r\n\t\t\t\tAutomationProperties.AutomationId=\"ExploreCsvButton\"\r\n\t\t\t\tBackground=\"Transparent\"\r\n\t\t\t\tClick=\"ExploreCsvButton_Click\">\r\n\t\t\t\t<StackPanel Style=\"{StaticResource ButtonStackPanel}\">\r\n\t\t\t\t\t<ContentControl Content=\"{StaticResource Icon_FolderOpen}\" Focusable=\"False\" />\r\n\t\t\t\t\t<Label Content=\"Explore\" Style=\"{StaticResource ButtonLabel}\" />\r\n\t\t\t\t</StackPanel>\r\n\t\t\t</Button>\r\n\t\t\t<Button\r\n\t\t\t\tName=\"CopyAiPromptButton\"\r\n\t\t\t\tMargin=\"2\"\r\n\t\t\t\tPadding=\"3,0,3,0\"\r\n\t\t\t\tAutomationProperties.AutomationId=\"CopyAiPromptButton\"\r\n\t\t\t\tBackground=\"Transparent\"\r\n\t\t\t\tClick=\"CopyAiPromptButton_Click\">\r\n\t\t\t\t<StackPanel Style=\"{StaticResource ButtonStackPanel}\">\r\n\t\t\t\t\t<ContentControl Content=\"{StaticResource Icon_list}\" Focusable=\"False\" />\r\n\t\t\t\t\t<Label Content=\"AI Prompt Example\" Style=\"{StaticResource ButtonLabel}\" />\r\n\t\t\t\t</StackPanel>\r\n\t\t\t</Button>\r\n\t\t</StackPanel>\r\n\t\t<ToolBarPanel Grid.Row=\"0\" HorizontalAlignment=\"Right\" Style=\"{StaticResource MainToolBarPanel}\">\r\n\t\t\t<Button\r\n\t\t\t\tName=\"ClearButton\"\r\n\t\t\t\tMargin=\"2\"\r\n\t\t\t\tPadding=\"3,0,3,0\"\r\n\t\t\t\tAutomationProperties.AutomationId=\"ClearButton\"\r\n\t\t\t\tBackground=\"Transparent\"\r\n\t\t\t\tClick=\"ClearButton_Click\">\r\n\t\t\t\t<StackPanel Style=\"{StaticResource ButtonStackPanel}\">\r\n\t\t\t\t\t<ContentControl Content=\"{StaticResource Icon_Delete}\" Focusable=\"False\" />\r\n\t\t\t\t\t<Label Content=\"Clear\" Style=\"{StaticResource ButtonLabel}\" />\r\n\t\t\t\t</StackPanel>\r\n\t\t\t</Button>\r\n\t\t</ToolBarPanel>\r\n\t\t<DataGrid\r\n\t\t\tx:Name=\"MainDataGrid\"\r\n\t\t\tGrid.Row=\"1\"\r\n\t\t\tHorizontalAlignment=\"Stretch\"\r\n\t\t\tVerticalAlignment=\"Stretch\"\r\n\t\t\tx:FieldModifier=\"public\"\r\n\t\t\tAutoGenerateColumns=\"False\"\r\n\t\t\tAutomationProperties.AutomationId=\"MainDataGrid\"\r\n\t\t\tBackground=\"{DynamicResource {x:Static SystemColors.WindowBrushKey}}\"\r\n\t\t\tBorderThickness=\"0\"\r\n\t\t\tIsReadOnly=\"True\"\r\n\t\t\tScrollViewer.CanContentScroll=\"True\"\r\n\t\t\tScrollViewer.HorizontalScrollBarVisibility=\"Auto\"\r\n\t\t\tScrollViewer.VerticalScrollBarVisibility=\"Auto\"\r\n\t\t\tSelectionMode=\"Extended\">\r\n\t\t\t<DataGrid.Resources>\r\n\t\t\t\t<converters:ItemFormattingConverter x:Key=\"_MainDataGridFormattingConverter\" />\r\n\t\t\t</DataGrid.Resources>\r\n\t\t\t<DataGrid.Columns>\r\n\t\t\t\t<DataGridTextColumn\r\n\t\t\t\t\tx:Name=\"DateColumn\"\r\n\t\t\t\t\tWidth=\"Auto\"\r\n\t\t\t\t\tEditingElementStyle=\"{StaticResource TextBlockCell}\"\r\n\t\t\t\t\tElementStyle=\"{StaticResource TextBlockCell}\"\r\n\t\t\t\t\tHeader=\"Date\">\r\n\t\t\t\t\t<DataGridTextColumn.Binding>\r\n\t\t\t\t\t\t<MultiBinding Converter=\"{StaticResource _MainDataGridFormattingConverter}\">\r\n\t\t\t\t\t\t\t<Binding RelativeSource=\"{RelativeSource Self}\" />\r\n\t\t\t\t\t\t\t<Binding RelativeSource=\"{RelativeSource TemplatedParent}\" />\r\n\t\t\t\t\t\t\t<Binding Path=\"Date\" />\r\n\t\t\t\t\t\t</MultiBinding>\r\n\t\t\t\t\t</DataGridTextColumn.Binding>\r\n\t\t\t\t</DataGridTextColumn>\r\n\t\t\t\t<DataGridTextColumn\r\n\t\t\t\t\tx:Name=\"ProcessIdColumn\"\r\n\t\t\t\t\tBinding=\"{Binding ProcessId}\"\r\n\t\t\t\t\tEditingElementStyle=\"{StaticResource TextBoxCell}\"\r\n\t\t\t\t\tElementStyle=\"{StaticResource TextBlockRightCell}\"\r\n\t\t\t\t\tHeader=\"PID\" />\r\n\t\t\t\t<DataGridTextColumn\r\n\t\t\t\t\tx:Name=\"ProcessNameColumn\"\r\n\t\t\t\t\tBinding=\"{Binding ProcessName}\"\r\n\t\t\t\t\tEditingElementStyle=\"{StaticResource TextBoxCell}\"\r\n\t\t\t\t\tElementStyle=\"{StaticResource TextBlockCell}\"\r\n\t\t\t\t\tHeader=\"Process Name\" />\r\n\t\t\t\t<DataGridTemplateColumn\r\n\t\t\t\t\tx:Name=\"IsActiveImageColumn\"\r\n\t\t\t\t\tWidth=\"SizeToCells\"\r\n\t\t\t\t\tCanUserResize=\"False\"\r\n\t\t\t\t\tIsReadOnly=\"True\">\r\n\t\t\t\t\t<DataGridTemplateColumn.Header>\r\n\t\t\t\t\t\t<TextBlock HorizontalAlignment=\"Center\" Text=\"A\" ToolTip=\"Window is Active\" />\r\n\t\t\t\t\t</DataGridTemplateColumn.Header>\r\n\t\t\t\t\t<DataGridTemplateColumn.CellTemplate>\r\n\t\t\t\t\t\t<DataTemplate>\r\n\t\t\t\t\t\t\t<ContentControl x:Name=\"IsActiveIcon\" Width=\"12\" Height=\"12\">\r\n\t\t\t\t\t\t\t\t<ContentControl.Content>\r\n\t\t\t\t\t\t\t\t\t<MultiBinding Converter=\"{StaticResource _MainDataGridFormattingConverter}\">\r\n\t\t\t\t\t\t\t\t\t\t<Binding RelativeSource=\"{RelativeSource Self}\" />\r\n\t\t\t\t\t\t\t\t\t\t<Binding RelativeSource=\"{RelativeSource TemplatedParent}\" />\r\n\t\t\t\t\t\t\t\t\t\t<Binding Path=\"IsActive\" />\r\n\t\t\t\t\t\t\t\t\t</MultiBinding>\r\n\t\t\t\t\t\t\t\t</ContentControl.Content>\r\n\t\t\t\t\t\t\t</ContentControl>\r\n\t\t\t\t\t\t</DataTemplate>\r\n\t\t\t\t\t</DataGridTemplateColumn.CellTemplate>\r\n\t\t\t\t</DataGridTemplateColumn>\r\n\t\t\t\t<DataGridTextColumn\r\n\t\t\t\t\tx:Name=\"IsActiveColumn\"\r\n\t\t\t\t\tEditingElementStyle=\"{StaticResource TextBoxCell}\"\r\n\t\t\t\t\tElementStyle=\"{StaticResource TextBlockCell}\"\r\n\t\t\t\t\tHeader=\"Active\"\r\n\t\t\t\t\tVisibility=\"Collapsed\">\r\n\t\t\t\t\t<DataGridTextColumn.Binding>\r\n\t\t\t\t\t\t<MultiBinding Converter=\"{StaticResource _MainDataGridFormattingConverter}\">\r\n\t\t\t\t\t\t\t<Binding RelativeSource=\"{RelativeSource Self}\" />\r\n\t\t\t\t\t\t\t<Binding RelativeSource=\"{RelativeSource TemplatedParent}\" />\r\n\t\t\t\t\t\t\t<Binding Path=\"IsActive\" />\r\n\t\t\t\t\t\t</MultiBinding>\r\n\t\t\t\t\t</DataGridTextColumn.Binding>\r\n\t\t\t\t</DataGridTextColumn>\r\n\t\t\t\t<DataGridTemplateColumn\r\n\t\t\t\t\tx:Name=\"HasMouseImageColumn\"\r\n\t\t\t\t\tWidth=\"SizeToCells\"\r\n\t\t\t\t\tCanUserResize=\"False\"\r\n\t\t\t\t\tIsReadOnly=\"True\">\r\n\t\t\t\t\t<DataGridTemplateColumn.Header>\r\n\t\t\t\t\t\t<TextBlock HorizontalAlignment=\"Center\" Text=\"M\" ToolTip=\"Window has Mouse\" />\r\n\t\t\t\t\t</DataGridTemplateColumn.Header>\r\n\t\t\t\t\t<DataGridTemplateColumn.CellTemplate>\r\n\t\t\t\t\t\t<DataTemplate>\r\n\t\t\t\t\t\t\t<ContentControl x:Name=\"HasMouseIcon\" Width=\"12\" Height=\"12\">\r\n\t\t\t\t\t\t\t\t<ContentControl.Content>\r\n\t\t\t\t\t\t\t\t\t<MultiBinding Converter=\"{StaticResource _MainDataGridFormattingConverter}\">\r\n\t\t\t\t\t\t\t\t\t\t<Binding RelativeSource=\"{RelativeSource Self}\" />\r\n\t\t\t\t\t\t\t\t\t\t<Binding RelativeSource=\"{RelativeSource TemplatedParent}\" />\r\n\t\t\t\t\t\t\t\t\t\t<Binding Path=\"HasMouse\" />\r\n\t\t\t\t\t\t\t\t\t</MultiBinding>\r\n\t\t\t\t\t\t\t\t</ContentControl.Content>\r\n\t\t\t\t\t\t\t</ContentControl>\r\n\t\t\t\t\t\t</DataTemplate>\r\n\t\t\t\t\t</DataGridTemplateColumn.CellTemplate>\r\n\t\t\t\t</DataGridTemplateColumn>\r\n\t\t\t\t<DataGridTextColumn\r\n\t\t\t\t\tx:Name=\"HasMouseColumn\"\r\n\t\t\t\t\tEditingElementStyle=\"{StaticResource TextBoxCell}\"\r\n\t\t\t\t\tElementStyle=\"{StaticResource TextBlockCell}\"\r\n\t\t\t\t\tHeader=\"Mouse\"\r\n\t\t\t\t\tVisibility=\"Collapsed\">\r\n\t\t\t\t\t<DataGridTextColumn.Binding>\r\n\t\t\t\t\t\t<MultiBinding Converter=\"{StaticResource _MainDataGridFormattingConverter}\">\r\n\t\t\t\t\t\t\t<Binding RelativeSource=\"{RelativeSource Self}\" />\r\n\t\t\t\t\t\t\t<Binding RelativeSource=\"{RelativeSource TemplatedParent}\" />\r\n\t\t\t\t\t\t\t<Binding Path=\"HasMouse\" />\r\n\t\t\t\t\t\t</MultiBinding>\r\n\t\t\t\t\t</DataGridTextColumn.Binding>\r\n\t\t\t\t</DataGridTextColumn>\r\n\t\t\t\t<DataGridTemplateColumn\r\n\t\t\t\t\tx:Name=\"HasKeyboardImageColumn\"\r\n\t\t\t\t\tWidth=\"SizeToCells\"\r\n\t\t\t\t\tCanUserResize=\"False\"\r\n\t\t\t\t\tIsReadOnly=\"True\">\r\n\t\t\t\t\t<DataGridTemplateColumn.Header>\r\n\t\t\t\t\t\t<TextBlock Text=\"K\" ToolTip=\"Window has Keyboard\" />\r\n\t\t\t\t\t</DataGridTemplateColumn.Header>\r\n\t\t\t\t\t<DataGridTemplateColumn.CellTemplate>\r\n\t\t\t\t\t\t<DataTemplate>\r\n\t\t\t\t\t\t\t<ContentControl x:Name=\"HasKeyboardIcon\" Width=\"12\" Height=\"12\">\r\n\t\t\t\t\t\t\t\t<ContentControl.Content>\r\n\t\t\t\t\t\t\t\t\t<MultiBinding Converter=\"{StaticResource _MainDataGridFormattingConverter}\">\r\n\t\t\t\t\t\t\t\t\t\t<Binding RelativeSource=\"{RelativeSource Self}\" />\r\n\t\t\t\t\t\t\t\t\t\t<Binding RelativeSource=\"{RelativeSource TemplatedParent}\" />\r\n\t\t\t\t\t\t\t\t\t\t<Binding Path=\"HasKeyboard\" />\r\n\t\t\t\t\t\t\t\t\t</MultiBinding>\r\n\t\t\t\t\t\t\t\t</ContentControl.Content>\r\n\t\t\t\t\t\t\t</ContentControl>\r\n\t\t\t\t\t\t</DataTemplate>\r\n\t\t\t\t\t</DataGridTemplateColumn.CellTemplate>\r\n\t\t\t\t</DataGridTemplateColumn>\r\n\t\t\t\t<DataGridTextColumn\r\n\t\t\t\t\tx:Name=\"HasKeyboardColumn\"\r\n\t\t\t\t\tEditingElementStyle=\"{StaticResource TextBoxCell}\"\r\n\t\t\t\t\tElementStyle=\"{StaticResource TextBlockCell}\"\r\n\t\t\t\t\tHeader=\"Keyboard\"\r\n\t\t\t\t\tVisibility=\"Collapsed\">\r\n\t\t\t\t\t<DataGridTextColumn.Binding>\r\n\t\t\t\t\t\t<MultiBinding Converter=\"{StaticResource _MainDataGridFormattingConverter}\">\r\n\t\t\t\t\t\t\t<Binding RelativeSource=\"{RelativeSource Self}\" />\r\n\t\t\t\t\t\t\t<Binding RelativeSource=\"{RelativeSource TemplatedParent}\" />\r\n\t\t\t\t\t\t\t<Binding Path=\"HasKeyboard\" />\r\n\t\t\t\t\t\t</MultiBinding>\r\n\t\t\t\t\t</DataGridTextColumn.Binding>\r\n\t\t\t\t</DataGridTextColumn>\r\n\t\t\t\t<DataGridTemplateColumn\r\n\t\t\t\t\tx:Name=\"HasCaretImageColumn\"\r\n\t\t\t\t\tWidth=\"SizeToCells\"\r\n\t\t\t\t\tCanUserResize=\"False\"\r\n\t\t\t\t\tIsReadOnly=\"True\">\r\n\t\t\t\t\t<DataGridTemplateColumn.Header>\r\n\t\t\t\t\t\t<TextBlock HorizontalAlignment=\"Center\" Text=\"C\" ToolTip=\"Window has Caret\" />\r\n\t\t\t\t\t</DataGridTemplateColumn.Header>\r\n\t\t\t\t\t<DataGridTemplateColumn.CellTemplate>\r\n\t\t\t\t\t\t<DataTemplate>\r\n\t\t\t\t\t\t\t<ContentControl x:Name=\"HasCaretIcon\" Width=\"12\" Height=\"12\">\r\n\t\t\t\t\t\t\t\t<ContentControl.Content>\r\n\t\t\t\t\t\t\t\t\t<MultiBinding Converter=\"{StaticResource _MainDataGridFormattingConverter}\">\r\n\t\t\t\t\t\t\t\t\t\t<Binding RelativeSource=\"{RelativeSource Self}\" />\r\n\t\t\t\t\t\t\t\t\t\t<Binding RelativeSource=\"{RelativeSource TemplatedParent}\" />\r\n\t\t\t\t\t\t\t\t\t\t<Binding Path=\"HasCaret\" />\r\n\t\t\t\t\t\t\t\t\t</MultiBinding>\r\n\t\t\t\t\t\t\t\t</ContentControl.Content>\r\n\t\t\t\t\t\t\t</ContentControl>\r\n\t\t\t\t\t\t</DataTemplate>\r\n\t\t\t\t\t</DataGridTemplateColumn.CellTemplate>\r\n\t\t\t\t</DataGridTemplateColumn>\r\n\r\n\t\t\t\t<DataGridTextColumn\r\n\t\t\t\t\tx:Name=\"HasCaretColumn\"\r\n\t\t\t\t\tEditingElementStyle=\"{StaticResource TextBoxCell}\"\r\n\t\t\t\t\tElementStyle=\"{StaticResource TextBlockCell}\"\r\n\t\t\t\t\tHeader=\"Caret\"\r\n\t\t\t\t\tVisibility=\"Collapsed\">\r\n\t\t\t\t\t<DataGridTextColumn.Binding>\r\n\t\t\t\t\t\t<MultiBinding Converter=\"{StaticResource _MainDataGridFormattingConverter}\">\r\n\t\t\t\t\t\t\t<Binding RelativeSource=\"{RelativeSource Self}\" />\r\n\t\t\t\t\t\t\t<Binding RelativeSource=\"{RelativeSource TemplatedParent}\" />\r\n\t\t\t\t\t\t\t<Binding Path=\"HasCaret\" />\r\n\t\t\t\t\t\t</MultiBinding>\r\n\t\t\t\t\t</DataGridTextColumn.Binding>\r\n\t\t\t\t</DataGridTextColumn>\r\n\t\t\t\t<DataGridTextColumn\r\n\t\t\t\t\tx:Name=\"WindowTiteColumn\"\r\n\t\t\t\t\tBinding=\"{Binding WindowTitle}\"\r\n\t\t\t\t\tEditingElementStyle=\"{StaticResource TextBoxCell}\"\r\n\t\t\t\t\tElementStyle=\"{StaticResource TextBlockCell}\"\r\n\t\t\t\t\tHeader=\"Window Title\" />\r\n\t\t\t\t<DataGridTextColumn\r\n\t\t\t\t\tx:Name=\"ProcessPathColumn\"\r\n\t\t\t\t\tWidth=\"Auto\"\r\n\t\t\t\t\tx:FieldModifier=\"public\"\r\n\t\t\t\t\tEditingElementStyle=\"{StaticResource TextBoxCell}\"\r\n\t\t\t\t\tElementStyle=\"{StaticResource TextBlockCell}\"\r\n\t\t\t\t\tHeader=\"Path\">\r\n\t\t\t\t\t<DataGridTextColumn.Binding>\r\n\t\t\t\t\t\t<MultiBinding Converter=\"{StaticResource _MainDataGridFormattingConverter}\">\r\n\t\t\t\t\t\t\t<Binding RelativeSource=\"{RelativeSource Self}\" />\r\n\t\t\t\t\t\t\t<Binding RelativeSource=\"{RelativeSource TemplatedParent}\" />\r\n\t\t\t\t\t\t\t<Binding Path=\"ProcessPath\" />\r\n\t\t\t\t\t\t</MultiBinding>\r\n\t\t\t\t\t</DataGridTextColumn.Binding>\r\n\t\t\t\t</DataGridTextColumn>\r\n\t\t\t\t<DataGridTextColumn\r\n\t\t\t\t\tx:Name=\"WindowClassNameColumn\"\r\n\t\t\t\t\tBinding=\"{Binding WindowClassName}\"\r\n\t\t\t\t\tEditingElementStyle=\"{StaticResource TextBoxCell}\"\r\n\t\t\t\t\tElementStyle=\"{StaticResource TextBlockCell}\"\r\n\t\t\t\t\tHeader=\"Window Class\" />\r\n\t\t\t</DataGrid.Columns>\r\n\t\t\t<DataGrid.CellStyle>\r\n\t\t\t\t<Style TargetType=\"DataGridCell\">\r\n\t\t\t\t\t<Setter Property=\"Padding\" Value=\"4\" />\r\n\t\t\t\t</Style>\r\n\t\t\t</DataGrid.CellStyle>\r\n\t\t</DataGrid>\r\n\t</Grid>\r\n</UserControl>\r\n"
  },
  {
    "path": "FocusLogger/Controls/DataListControl.xaml.cs",
    "content": "﻿using JocysCom.ClassLibrary.ComponentModel;\r\nusing JocysCom.ClassLibrary.Controls;\r\nusing JocysCom.FocusLogger.Resources.Icons;\r\nusing Microsoft.Win32;\r\nusing System;\r\nusing System.ComponentModel;\r\nusing System.Diagnostics;\r\nusing System.Globalization;\r\nusing System.IO;\r\nusing System.Linq;\r\nusing System.Text;\r\nusing System.Windows;\r\nusing System.Windows.Controls;\r\n\r\nnamespace JocysCom.FocusLogger.Controls\r\n{\r\n\t/// <summary>\r\n\t/// Interaction logic for DataListControl.xaml\r\n\t/// </summary>\r\n\tpublic partial class DataListControl : UserControl\r\n\t{\r\n\t\tpublic DataListControl()\r\n\t\t{\r\n\t\t\tInitializeComponent();\r\n\t\t\tif (ControlsHelper.IsDesignMode(this))\r\n\t\t\t\treturn;\r\n\t\t\t// Configure converter.\r\n\t\t\tMainDataGrid.ItemsSource = DataItems;\r\n\t\t\tvar gridFormattingConverter = MainDataGrid.Resources.Values.OfType<ItemFormattingConverter>().First();\r\n\t\t\tgridFormattingConverter.ConvertFunction = _MainDataGridFormattingConverter_Convert;\r\n\t\t}\r\n\r\n\t\tobject _MainDataGridFormattingConverter_Convert(object[] values, Type targetType, object parameter, CultureInfo culture)\r\n\t\t{\r\n\t\t\tvar sender = (FrameworkElement)values[0];\r\n\t\t\tvar template = (FrameworkElement)values[1];\r\n\t\t\tvar value = values[2];\r\n\t\t\tvar cell = (DataGridCell)(template ?? sender).Parent;\r\n\t\t\tvar item = (DataItem)cell.DataContext;\r\n\t\t\tif (cell.Column == IsActiveImageColumn)\r\n\t\t\t{\r\n\t\t\t\treturn item.IsActive ? Icons_Default.Current[Icons_Default.Icon_window] : null;\r\n\t\t\t}\r\n\t\t\tif (cell.Column == IsActiveColumn)\r\n\t\t\t{\r\n\t\t\t\tcell.Opacity = 0.5;\r\n\t\t\t\treturn item.IsActive ? \"Active\" : \"\";\r\n\t\t\t}\r\n\t\t\t// Mouse.\r\n\t\t\tif (cell.Column == HasMouseColumn)\r\n\t\t\t\treturn item.HasMouse ? \"Mouse\" : \"\";\r\n\t\t\tif (cell.Column == HasMouseImageColumn)\r\n\t\t\t\treturn item.HasMouse ? Icons_Default.Current[Icons_Default.Icon_mouse2] : null;\r\n\t\t\t// Keyboard.\r\n\t\t\tif (cell.Column == HasKeyboardImageColumn)\r\n\t\t\t\treturn item.HasKeyboard ? Icons_Default.Current[Icons_Default.Icon_keyboard] : null;\r\n\t\t\tif (cell.Column == HasKeyboardColumn)\r\n\t\t\t\treturn item.HasKeyboard ? \"Keyboard\" : \"\";\r\n\t\t\t// Caret.\r\n\t\t\tif (cell.Column == HasCaretImageColumn)\r\n\t\t\t\treturn item.HasCaret ? Icons_Default.Current[Icons_Default.Icon_text_field] : null;\r\n\t\t\tif (cell.Column == HasCaretColumn)\r\n\t\t\t\treturn item.HasCaret ? \"Caret\" : \"\";\r\n\t\t\tif (cell.Column == DateColumn)\r\n\t\t\t{\r\n\t\t\t\tvalue = string.Format(\"{0:HH:mm:ss:fff}\", item.Date);\r\n\t\t\t\tcell.Opacity = 0.5;\r\n\t\t\t}\r\n\t\t\tif (cell.Column == ProcessPathColumn)\r\n\t\t\t{\r\n\t\t\t\tif (item.NonPath)\r\n\t\t\t\t\tcell.Opacity = 0.3;\r\n\t\t\t}\r\n\t\t\t// Other.\r\n\t\t\treturn value;\r\n\t\t}\r\n\r\n\t\tpublic SortableBindingList<DataItem> DataItems { get; set; } = new SortableBindingList<DataItem>();\r\n\r\n\t\tprivate void UserControl_Loaded(object sender, RoutedEventArgs e)\r\n\t\t{\r\n\t\t\tif (ControlsHelper.IsDesignMode(this))\r\n\t\t\t\treturn;\r\n\t\t\tInitTimer();\r\n\t\t}\r\n\r\n\t\tprivate string _lastSavedCsvPath;\r\n\r\n\t\tprivate void SaveCsvButton_Click(object sender, RoutedEventArgs e)\r\n\t\t{\r\n\t\t\tif (DataItems.Count == 0)\r\n\t\t\t{\r\n\t\t\t\tMessageBox.Show(\"No log entries to save.\", \"Save CSV\", MessageBoxButton.OK, MessageBoxImage.Information);\r\n\t\t\t\treturn;\r\n\t\t\t}\r\n\t\t\tvar dialog = new SaveFileDialog\r\n\t\t\t{\r\n\t\t\t\tFilter = \"CSV files (*.csv)|*.csv\",\r\n\t\t\t\tDefaultExt = \".csv\",\r\n\t\t\t\tFileName = $\"FocusLog_{DateTime.Now:yyyy-MM-dd_HHmmss}.csv\",\r\n\t\t\t};\r\n\t\t\tif (dialog.ShowDialog() != true)\r\n\t\t\t\treturn;\r\n\t\t\tvar csv = BuildCsvContent(DataItems);\r\n\t\t\tFile.WriteAllText(dialog.FileName, csv, Encoding.UTF8);\r\n\t\t\t_lastSavedCsvPath = dialog.FileName;\r\n\t\t}\r\n\r\n\t\tprivate void ExploreCsvButton_Click(object sender, RoutedEventArgs e)\r\n\t\t{\r\n\t\t\tif (string.IsNullOrEmpty(_lastSavedCsvPath) || !File.Exists(_lastSavedCsvPath))\r\n\t\t\t{\r\n\t\t\t\tMessageBox.Show(\"No CSV file saved yet. Use 'Save CSV' first.\", \"Explore\", MessageBoxButton.OK, MessageBoxImage.Information);\r\n\t\t\t\treturn;\r\n\t\t\t}\r\n\t\t\tProcess.Start(\"explorer.exe\", $\"/select,\\\"{_lastSavedCsvPath}\\\"\");\r\n\t\t}\r\n\r\n\t\tpublic static string BuildCsvContent(System.Collections.Generic.IEnumerable<DataItem> items)\r\n\t\t{\r\n\t\t\tvar sb = new StringBuilder();\r\n\t\t\tsb.AppendLine(\"Date,PID,Process Name,Active,Mouse,Keyboard,Caret,Window Title,Window Class,Path\");\r\n\t\t\tforeach (var item in items)\r\n\t\t\t{\r\n\t\t\t\tsb.AppendLine(string.Join(\",\",\r\n\t\t\t\t\tCsvEscape(item.Date.ToString(\"yyyy-MM-dd HH:mm:ss.fff\")),\r\n\t\t\t\t\titem.ProcessId,\r\n\t\t\t\t\tCsvEscape(item.ProcessName),\r\n\t\t\t\t\titem.IsActive,\r\n\t\t\t\t\titem.HasMouse,\r\n\t\t\t\t\titem.HasKeyboard,\r\n\t\t\t\t\titem.HasCaret,\r\n\t\t\t\t\tCsvEscape(item.WindowTitle),\r\n\t\t\t\t\tCsvEscape(item.WindowClassName),\r\n\t\t\t\t\tCsvEscape(item.ProcessPath)\r\n\t\t\t\t));\r\n\t\t\t}\r\n\t\t\treturn sb.ToString();\r\n\t\t}\r\n\r\n\t\tpublic static string CsvEscape(string value)\r\n\t\t{\r\n\t\t\tif (string.IsNullOrEmpty(value))\r\n\t\t\t\treturn \"\";\r\n\t\t\tif (value.Contains(\",\") || value.Contains(\"\\\"\") || value.Contains(\"\\n\"))\r\n\t\t\t\treturn \"\\\"\" + value.Replace(\"\\\"\", \"\\\"\\\"\") + \"\\\"\";\r\n\t\t\treturn value;\r\n\t\t}\r\n\r\n\t\tprivate void CopyAiPromptButton_Click(object sender, RoutedEventArgs e)\r\n\t\t{\r\n\t\t\tvar prompt = ClassLibrary.Helper.FindResource<string>(\"Resources/AiAnalysisPrompt.md\").TrimEnd();\r\n\t\t\tvar box = new MessageBoxWindow();\r\n\t\t\tbox.SetSize(720, 400);\r\n\t\t\tbox.ShowPrompt(prompt, \"AI Analysis Prompt - Copy and paste into your AI assistant\", MessageBoxButton.OK, MessageBoxImage.Information, MessageBoxResult.OK);\r\n\t\t}\r\n\r\n\t\tprivate void ClearButton_Click(object sender, RoutedEventArgs e)\r\n\t\t{\r\n\t\t\tDataItems.Clear();\r\n\t\t}\r\n\r\n\t\tprivate void UserControl_Unloaded(object sender, RoutedEventArgs e)\r\n\t\t{\r\n\t\t\tif (ControlsHelper.IsDesignMode(this))\r\n\t\t\t\treturn;\r\n\t\t}\r\n\r\n\t\tobject AddLock = new object();\r\n\r\n\t\tpublic bool IsElevated\r\n\t\t{\r\n\t\t\tget\r\n\t\t\t{\r\n\t\t\t\tvar id = System.Security.Principal.WindowsIdentity.GetCurrent();\r\n\t\t\t\treturn id.Owner != id.User;\r\n\t\t\t}\r\n\t\t}\r\n\r\n\t\tpublic void UpdateFromProcess(DataItem item)\r\n\t\t{\r\n\t\t\tusing (var process = Process.GetProcessById(item.ProcessId))\r\n\t\t\t{\r\n\t\t\t\titem.ProcessName = process.ProcessName;\r\n\t\t\t\tif (item.ProcessId == 0)\r\n\t\t\t\t{\r\n\t\t\t\t\titem.ProcessPath = \"System Idle Process\";\r\n\t\t\t\t\titem.NonPath = true;\r\n\t\t\t\t}\r\n\t\t\t\tif (item.ProcessId > 0)\r\n\t\t\t\t{\r\n\t\t\t\t\ttry\r\n\t\t\t\t\t{\r\n\t\t\t\t\t\titem.ProcessPath = process.MainModule?.FileName;\r\n\t\t\t\t\t}\r\n\t\t\t\t\tcatch (Exception ex)\r\n\t\t\t\t\t{\r\n\t\t\t\t\t\tconst int E_FAIL = unchecked((int)0x80004005); // -2147467259\r\n\t\t\t\t\t\titem.ProcessPath = $\"Error: {ex.Message}\";\r\n\t\t\t\t\t\titem.NonPath = true;\r\n\t\t\t\t\t\t// If Win32 Acccess is denied exception, then...\r\n\t\t\t\t\t\tif (ex is Win32Exception && ex.HResult == E_FAIL)\r\n\t\t\t\t\t\t\titem.ProcessPath += \" Run as Administrator\";\r\n\r\n\t\t\t\t\t}\r\n\t\t\t\t}\r\n\t\t\t}\r\n\t\t}\r\n\r\n\t\tSystem.Timers.Timer _Timer;\r\n\r\n\t\tvoid InitTimer()\r\n\t\t{\r\n\t\t\t_Timer = new System.Timers.Timer();\r\n\t\t\t_Timer.Elapsed += _Timer_Elapsed;\r\n\t\t\t_Timer.AutoReset = false;\r\n\t\t\t_Timer.Interval = 1;\r\n\t\t\t_Timer.Start();\r\n\t\t}\r\n\r\n\t\tprivate void _Timer_Elapsed(object sender, System.Timers.ElapsedEventArgs e)\r\n\t\t{\r\n\t\t\tif (MainWindow.IsClosing)\r\n\t\t\t\treturn;\r\n\t\t\tUpdateInfo();\r\n\t\t\t_Timer.Start();\r\n\t\t}\r\n\r\n\t\tDataItem oldActiveItem = new DataItem();\r\n\t\tDataItem oldForegroundItem = new DataItem();\r\n\r\n\t\tpublic void UpdateInfo()\r\n\t\t{\r\n\t\t\t// Active window -  Window that appears in the foreground with a highlighted title bar.\r\n\t\t\t// Foreground window - Window with which the user is currently working.\r\n\t\t\t//   The system assigns a slightly higher priority to the thread used to create the foreground window.\r\n\t\t\t// Focus window - Window that is currently receiving keyboard input.\r\n\t\t\t//   The focus window is always the active window, a descendent of the active window, or NULL.\r\n\t\t\t// Top-Level window -  A window that has no parent window.\r\n\t\t\t//\r\n\t\t\tlock (AddLock)\r\n\t\t\t{\r\n\t\t\t\t// Get window which or child window of which receives keyboard input.\r\n\t\t\t\tvar activeHandle = NativeMethods.GetActiveWindow();\r\n\t\t\t\tvar activeItem = GetItemFromHandle(activeHandle, true);\r\n\t\t\t\t// If active window changed then...\r\n\t\t\t\tif (!activeItem.IsSame(oldActiveItem))\r\n\t\t\t\t{\r\n\t\t\t\t\toldActiveItem = activeItem;\r\n\t\t\t\t\tUpdateFromProcess(activeItem);\r\n\t\t\t\t\tControlsHelper.BeginInvoke(() => DataItems.Insert(0, activeItem));\r\n\t\t\t\t}\r\n\t\t\t\t// Get foreground window.\r\n\t\t\t\tvar foregroundHandle = NativeMethods.GetForegroundWindow();\r\n\t\t\t\tvar foregroundItem = GetItemFromHandle(foregroundHandle);\r\n\t\t\t\t// If foreground window changed then...\r\n\t\t\t\tif (!foregroundItem.IsSame(oldForegroundItem))\r\n\t\t\t\t{\r\n\t\t\t\t\toldForegroundItem = foregroundItem;\r\n\t\t\t\t\tUpdateFromProcess(foregroundItem);\r\n\t\t\t\t\tControlsHelper.BeginInvoke(() => DataItems.Insert(0, foregroundItem));\r\n\t\t\t\t}\r\n\t\t\t}\r\n\t\t}\r\n\r\n\t\tDataItem GetItemFromHandle(IntPtr hWnd, bool isActive = false)\r\n\t\t{\r\n\t\t\tvar item = new DataItem();\r\n\t\t\titem.Date = DateTime.Now;\r\n\t\t\titem.IsActive = isActive;\r\n\t\t\tvar info = NativeMethods.GetInfo(hWnd);\r\n\t\t\tif (info.HasValue)\r\n\t\t\t{\r\n\t\t\t\titem.HasMouse = info.Value.hwndCapture != IntPtr.Zero;\r\n\t\t\t\titem.HasKeyboard = info.Value.hwndFocus != IntPtr.Zero;\r\n\t\t\t\titem.HasCaret = info.Value.hwndCaret != IntPtr.Zero;\r\n\t\t\t}\r\n\t\t\tint processId;\r\n\t\t\tif (isActive)\r\n\t\t\t{\r\n\t\t\t\thWnd = NativeMethods.GetTopWindow(hWnd);\r\n\t\t\t}\r\n\t\t\titem.WindowTitle = NativeMethods.GetWindowText(hWnd);\r\n\t\t\titem.WindowClassName = NativeMethods.GetWindowClassName(hWnd);\r\n\t\t\tNativeMethods.GetWindowThreadProcessId(hWnd, out processId);\r\n\t\t\titem.ProcessId = processId;\r\n\t\t\treturn item;\r\n\t\t}\r\n\r\n\t}\r\n}"
  },
  {
    "path": "FocusLogger/JocysCom/Collections/CollectionsHelper.cs",
    "content": "using System.Collections.Generic;\r\nusing System.Collections.ObjectModel;\r\nusing System.Linq;\r\n\r\nnamespace JocysCom.ClassLibrary.Collections\r\n{\r\n\tpublic static partial class CollectionsHelper\r\n\t{\r\n\r\n\t\t/// <summary>\r\n\t\t/// Synchronizes target list to match source list:\r\n\t\t/// removes items not in source, inserts missing items,\r\n\t\t/// and reorders existing elements to mirror source order.\r\n\t\t/// </summary>\r\n\t\t/// <typeparam name=\"T\">Type of elements in the lists.</typeparam>\r\n\t\t/// <param name=\"source\">The source list whose items and order to mirror; must not be null.</param>\r\n\t\t/// <param name=\"target\">The target list to update; must support Insert and RemoveAt; must not be null.</param>\r\n\t\t/// <param name=\"comparer\">Optional equality comparer; defaults to <see cref=\"EqualityComparer{T}.Default\"/>.</param>\r\n\t\t/// <remarks>\r\n\t\t/// Uses a <see cref=\"Dictionary{TKey, TValue}\"/> for fast source lookups; overall time complexity is O(n^2) due to list insert/remove operations.\r\n\t\t/// Try to use quick sort algorithm by using uniqueSortName/index.\r\n\t\t/// When target is <see cref=\"ObservableCollection{T}\"/>, uses <see cref=\"ObservableCollection{T}.Move(int, int)\"/> to avoid triggering remove and insert events.\r\n\t\t/// </remarks>\r\n\t\tpublic static void Synchronize<T>(IList<T> source, IList<T> target, IEqualityComparer<T> comparer = null)\r\n\t\t{\r\n\t\t\tcomparer = comparer ?? EqualityComparer<T>.Default;\r\n\t\t\t// Create a dictionary for fast lookup in source list\r\n\t\t\tvar sourceSet = new Dictionary<T, int>();\r\n\t\t\tfor (int i = 0; i < source.Count; i++)\r\n\t\t\t\tsourceSet[source[i]] = i;\r\n\t\t\t// Iterate over the target, remove items not in source\r\n\t\t\tfor (int i = target.Count - 1; i >= 0; i--)\r\n\t\t\t\tif (!sourceSet.Keys.Contains(target[i], comparer))\r\n\t\t\t\t\ttarget.RemoveAt(i);\r\n\t\t\t// Iterate over source\r\n\t\t\tfor (int si = 0; si < source.Count; si++)\r\n\t\t\t{\r\n\t\t\t\t// If item is not present in target, insert it.\r\n\t\t\t\t// Note: Only check items that have not been synchornized in the target.\r\n\t\t\t\tif (!target.Skip(si).Contains(source[si], comparer))\r\n\t\t\t\t{\r\n\t\t\t\t\ttarget.Insert(si, source[si]);\r\n\t\t\t\t\tcontinue;\r\n\t\t\t\t}\r\n\t\t\t\t// If item is present in target but not at the right position, move it.\r\n\t\t\t\tint ti = -1;\r\n\t\t\t\t// Note: Only check items that have not been synchornized in the target.\r\n\t\t\t\tfor (int i = si; i < target.Count; i++)\r\n\t\t\t\t{\r\n\t\t\t\t\tif (comparer.Equals(target[i], source[si]))\r\n\t\t\t\t\t{\r\n\t\t\t\t\t\tti = i;\r\n\t\t\t\t\t\tbreak;\r\n\t\t\t\t\t}\r\n\t\t\t\t}\r\n\t\t\t\tif (ti != si)\r\n\t\t\t\t{\r\n\t\t\t\t\tvar oc = target as ObservableCollection<T>;\r\n\t\t\t\t\t// If observable collection then\r\n\t\t\t\t\tif (oc != null)\r\n\t\t\t\t\t{\r\n\t\t\t\t\t\t// Move item without triggering remove and insert events.\r\n\t\t\t\t\t\toc.Move(si, ti);\r\n\t\t\t\t\t}\r\n\t\t\t\t\telse\r\n\t\t\t\t\t{\r\n\t\t\t\t\t\t// Removes and inserts item and\r\n\t\t\t\t\t\t// can disrupt data binding in WPF controls.\r\n\t\t\t\t\t\tvar item = target[ti];\r\n\t\t\t\t\t\ttarget.RemoveAt(ti);\r\n\t\t\t\t\t\ttarget.Insert(si, item);\r\n\t\t\t\t\t}\r\n\t\t\t\t}\r\n\t\t\t}\r\n\t\t\t// Remove items at the end of target that exceed source's length\r\n\t\t\twhile (target.Count > source.Count)\r\n\t\t\t\ttarget.RemoveAt(target.Count - 1);\r\n\t\t}\r\n\r\n\t}\r\n}\r\n"
  },
  {
    "path": "FocusLogger/JocysCom/Common/Helper.cs",
    "content": "using System;\r\nusing System.Collections.Concurrent;\r\nusing System.Diagnostics;\r\nusing System.IO;\r\nusing System.Linq;\r\nusing System.Reflection;\r\nusing System.Text.RegularExpressions;\r\nusing System.Threading;\r\nusing System.Threading.Tasks;\r\nusing System.Windows.Threading;\r\n\r\nnamespace JocysCom.ClassLibrary\r\n{\r\n\tpublic partial class Helper : IDisposable\r\n\t{\r\n\t\t#region Control Resources\r\n\r\n\t\t/// <summary>\r\n\t\t/// Write application header title to CLI interface.\r\n\t\t/// </summary>\r\n\t\tpublic static void WriteAppHeader()\r\n\t\t{\r\n\t\t\tvar assembly = Assembly.GetExecutingAssembly();\r\n\t\t\tWriteAppHeader(assembly);\r\n\t\t}\r\n\r\n\t\tpublic static void WriteAppHeader(Assembly assembly)\r\n\t\t{\r\n\t\t\t// Write title.\r\n\t\t\t// Microsoft (R) SQL Server Database Publishing Wizard 1.1.1.0\r\n\t\t\t// Copyright (C) Microsoft Corporation. All rights reserved.\r\n\t\t\tvar a = new Configuration.AssemblyInfo(assembly);\r\n\t\t\tConsole.WriteLine(a.Title + \" \" + a.Version.ToString());\r\n\t\t\tConsole.WriteLine(a.Copyright);\r\n\t\t\tConsole.WriteLine(a.Description);\r\n\t\t}\r\n\r\n\t\t/// <summary>\r\n\t\t/// Write help text from help.txt file.\r\n\t\t/// </summary>\r\n\t\tpublic static void WriteAppHelp()\r\n\t\t{\r\n\t\t\tConsole.Write(GetResource<string>(\"Documents/Help.txt\"));\r\n\t\t}\r\n\r\n\t\t/// <summary>\r\n\t\t/// Get resource from class *.resx file by full name.\r\n\t\t/// </summary>\r\n\t\tpublic static T FindResource<T>(string name, object o)\r\n\t\t{\r\n\t\t\tif (o is null)\r\n\t\t\t\tthrow new ArgumentNullException(nameof(o));\r\n\t\t\tvar resources = new System.ComponentModel.ComponentResourceManager(o.GetType());\r\n\t\t\treturn (T)(resources.GetObject(name));\r\n\t\t}\r\n\r\n\t\t/// <summary>\r\n\t\t/// Find resource in all loaded assemblies if not specified by full or partial (EndsWith) name.\r\n\t\t/// Look inside \"Build Action: Embedded Resource\".\r\n\t\t/// </summary>\r\n\t\tpublic static T FindResource<T>(string name, params Assembly[] assemblies)\r\n\t\t{\r\n\t\t\tvar name1 = name.Replace(\"/\", \".\").Replace(@\"\\\", \".\");\r\n\t\t\tvar name2 = name1.Replace(' ', '_');\r\n\t\t\tif (assemblies.Length == 0)\r\n\t\t\t\tassemblies = GetAssemblies();\r\n\t\t\tforeach (var assembly in assemblies)\r\n\t\t\t{\r\n\t\t\t\tvar resourceNames = assembly.GetManifestResourceNames();\r\n\t\t\t\tforeach (var resourceName in resourceNames)\r\n\t\t\t\t{\r\n\t\t\t\t\tif (!resourceName.EndsWith(name1) && !resourceName.EndsWith(name2))\r\n\t\t\t\t\t\tcontinue;\r\n\t\t\t\t\tvar stream = assembly.GetManifestResourceStream(resourceName);\r\n\t\t\t\t\treturn ConvertResource<T>(stream);\r\n\t\t\t\t}\r\n\t\t\t}\r\n\t\t\treturn default(T);\r\n\t\t}\r\n\r\n\t\t/// <summary>\r\n\t\t/// Project Build Action: \"Resource\".\r\n\t\t/// </summary>\r\n\t\tpublic static string[] GetResourceKeys(Assembly assembly)\r\n\t\t{\r\n\t\t\tstring resName = assembly.GetName().Name + \".g.resources\";\r\n\t\t\tusing (var stream = assembly.GetManifestResourceStream(resName))\r\n\t\t\tusing (var reader = new System.Resources.ResourceReader(stream))\r\n\t\t\t\treturn reader.Cast<System.Collections.DictionaryEntry>().Select(x => (string)x.Key).ToArray();\r\n\t\t}\r\n\r\n\t\t/// <summary>\r\n\t\t/// Project Build Action: \"Resource\".\r\n\t\t/// </summary>\r\n\t\tpublic static Stream GetResourceValue(string name, Assembly assembly)\r\n\t\t{\r\n\t\t\tstring resName = assembly.GetName().Name + \".g.resources\";\r\n\t\t\tusing (var stream = assembly.GetManifestResourceStream(resName))\r\n\t\t\tusing (var reader = new System.Resources.ResourceReader(stream))\r\n\t\t\t{\r\n\t\t\t\tvar value = reader.Cast<System.Collections.DictionaryEntry>()\r\n\t\t\t\t\t.Where(x => (string)x.Key == name)\r\n\t\t\t\t\t.Select(x => x.Value).FirstOrDefault();\r\n\t\t\t\treturn (Stream)value;\r\n\t\t\t\t//var path = string.Format(\"{0};component/{1}\", assembly.GetName().Name, name);\r\n\t\t\t\t//var s = System.Windows.Application.GetResourceStream(new Uri(path, UriKind.Relative));\r\n\t\t\t\t//return s.Stream;\r\n\t\t\t}\r\n\t\t}\r\n\r\n\t\t/// <summary>\r\n\t\t/// Get embedded resource from type (*.resx file).\r\n\t\t/// </summary>\r\n\t\tpublic static T GetResource<TSource, T>(string name)\r\n\t\t{\r\n\t\t\tvar resources = new System.ComponentModel.ComponentResourceManager(typeof(T));\r\n\t\t\treturn (T)resources.GetObject(name);\r\n\t\t}\r\n\r\n\t\t/// <summary>\r\n\t\t/// Get embedded resource by its full name.\r\n\t\t/// </summary>\r\n\t\tpublic static T GetResource<T>(string name, params Assembly[] assemblies)\r\n\t\t{\r\n\t\t\tvar name1 = name.Replace(\"/\", \".\").Replace(@\"\\\", \".\");\r\n\t\t\tvar name2 = name1.Replace(' ', '_');\r\n\t\t\tif (assemblies.Length == 0)\r\n\t\t\t\tassemblies = GetAssemblies();\r\n\t\t\tforeach (var assembly in assemblies)\r\n\t\t\t{\r\n\t\t\t\tvar resourceNames = assembly.GetManifestResourceNames();\r\n\t\t\t\tforeach (var resourceName in resourceNames)\r\n\t\t\t\t{\r\n\t\t\t\t\tif (resourceName != name1 && resourceName != name2)\r\n\t\t\t\t\t\tcontinue;\r\n\t\t\t\t\tvar stream = assembly.GetManifestResourceStream(resourceName);\r\n\t\t\t\t\treturn ConvertResource<T>(stream);\r\n\t\t\t\t}\r\n\t\t\t}\r\n\t\t\tthrow new Exception(\"Resource not found\");\r\n\t\t}\r\n\r\n\t\t/// <summary>Converts a resource Stream to the specified type T: returns Stream, string (BOM-aware), System.Drawing.Image (.NET Framework), or byte[].</summary>\r\n\t\tstatic T ConvertResource<T>(Stream stream)\r\n\t\t{\r\n\t\t\tif (typeof(T) == typeof(Stream))\r\n\t\t\t\treturn (T)(object)stream;\r\n\t\t\tvar results = default(T);\r\n\t\t\tif (typeof(T) == typeof(string))\r\n\t\t\t{\r\n\t\t\t\t// File must contain Byte Order Mark (BOM) header in order for bytes correctly encoded to string.\r\n\t\t\t\t// If header is missing then get resource as byte[] type and encode manually.\r\n\t\t\t\tvar streamReader = new StreamReader(stream, true);\r\n\t\t\t\treturn (T)(object)streamReader.ReadToEnd();\r\n\t\t\t}\r\n#if NETFRAMEWORK // .NET Framework\r\n\t\t\telse if (typeof(T) == typeof(System.Drawing.Image) || typeof(T) == typeof(System.Drawing.Bitmap))\r\n\t\t\t{\r\n\t\t\t\treturn (T)(object)System.Drawing.Image.FromStream(stream);\r\n\t\t\t}\r\n#endif\r\n\t\t\telse\r\n\t\t\t{\r\n\t\t\t\tvar bytes = new byte[stream.Length];\r\n#if NET7_0_OR_GREATER\r\n\t\t\t\tstream.ReadExactly(bytes, 0, (int)stream.Length);\r\n#else\r\n\t\t\t\tstream.Read(bytes, 0, (int)stream.Length);\r\n#endif\r\n\t\t\t\tresults = (T)(object)bytes;\r\n\t\t\t}\r\n\t\t\treturn results;\r\n\t\t}\r\n\r\n\t\t/// <summary>Retrieves all loaded assemblies, prioritizing the executing, calling, and entry assemblies for resource lookup.</summary>\r\n\t\tstatic Assembly[] GetAssemblies()\r\n\t\t{\r\n\t\t\tvar assemblies = AppDomain.CurrentDomain.GetAssemblies().ToList();\r\n\t\t\tvar orderDesc = new Assembly[]\r\n\t\t\t{\r\n\t\t\t\tAssembly.GetExecutingAssembly(),\r\n\t\t\t\tAssembly.GetCallingAssembly(),\r\n\t\t\t\tAssembly.GetEntryAssembly(),\r\n\t\t\t};\r\n\t\t\t// Move assemblies to top.\r\n\t\t\tforeach (var item in orderDesc)\r\n\t\t\t{\r\n\t\t\t\tif (assemblies.Contains(item))\r\n\t\t\t\t{\r\n\t\t\t\t\tassemblies.Remove(item);\r\n\t\t\t\t\tassemblies.Insert(0, item);\r\n\t\t\t\t}\r\n\t\t\t}\r\n\t\t\treturn assemblies.ToArray();\r\n\t\t}\r\n\r\n\t\t#endregion\r\n\r\n\t\t/* LongDelay example with CancellationToken:\r\n\t\t// Create a token that auto-cancels after 10 seconds.\r\n\t\tvar source = new CancellationTokenSource(10000);\r\n\t\t// Delay for 20 seconds.\r\n\t\ttry { LongDelay(20000, source.Token).Wait(); }\r\n\t\tcatch (TaskCanceledException) { } // Cancel silently.\r\n\t\tcatch (Exception) { throw; }\r\n\t\t */\r\n\r\n\t\t/// <summary>Allow to delay Task for 292,471,209 years.</summary>\r\n\t\t/// <remarks>Usage makes sense if the process won't be recycled before the delay expires.</remarks>\r\n\t\tpublic static async Task LongDelay(\r\n\t\t\tTimeSpan delay,\r\n\t\t\tCancellationToken cancellationToken = default(CancellationToken)\r\n\t\t) => await LongDelay((long)delay.TotalMilliseconds, cancellationToken).ConfigureAwait(false);\r\n\r\n\t\t/// <summary>Allow to delay Task for 292,471,209 years.</summary>\r\n\t\t/// <remarks>Usage makes sense if the process won't be recycled before the delay expires.</remarks>\r\n\t\tpublic static async Task LongDelay(\r\n\t\t\tlong millisecondsDelay,\r\n\t\t\tCancellationToken cancellationToken = default(CancellationToken)\r\n\t\t)\r\n\t\t{\r\n\t\t\t// Use 'do' to run Task.Delay at least once to reproduce the same behavior.\r\n\t\t\tdo\r\n\t\t\t{\r\n\t\t\t\tvar delay = (int)Math.Min(int.MaxValue, millisecondsDelay);\r\n\t\t\t\tawait Task.Delay(delay, cancellationToken).ConfigureAwait(false);\r\n\t\t\t\tmillisecondsDelay -= delay;\r\n\t\t\t} while (millisecondsDelay > 0);\r\n\t\t}\r\n\r\n\t\t#region Debounce Execution\r\n\r\n\t\t/// <summary>\r\n\t\t/// Contains the CancellationTokenSource for each delegate to manage debouncing.\r\n\t\t/// </summary>\r\n\t\tstatic ConcurrentDictionary<Delegate, DebounceData> DebounceActions = new ConcurrentDictionary<Delegate, DebounceData>();\r\n\r\n\t\t/// <summary>\r\n\t\t/// Holds debouncing information for a specific delegate.\r\n\t\t/// </summary>\r\n\t\tclass DebounceData\r\n\t\t{\r\n\t\t\tpublic int Counter = 0;\r\n\t\t\tpublic object LockObject = new object();\r\n\t\t}\r\n\r\n\t\t[Obsolete(\"Use `async Task Debounce(Action action, int? delay = null, params object[] args)` instead.\")]\r\n\t\tpublic static async Task Delay(Action action, int? delay = null, params object[] args)\r\n\t\t\t=> await _Debounce(action, delay, args);\r\n\r\n\t\t[Obsolete(\"Use `async Task Debounce(Func<Task> action, int? delay = null, params object[] args)` instead.\")]\r\n\t\tpublic static async Task Delay(Func<Task> action, int? delay = null, params object[] args)\r\n\t\t\t=> await _Debounce(action, delay, args);\r\n\r\n\t\t/// <summary>\r\n\t\t/// Executes an action after a delay, canceling any previous pending executions of the same action.\r\n\t\t/// This method ensures that the action is invoked only after the specified delay has elapsed since the last invocation request.\r\n\t\t/// </summary>\r\n\t\t/// <param name=\"action\">The action to debounce.</param>\r\n\t\t/// <param name=\"delay\">The delay in milliseconds to wait before invoking the action. Defaults to 500 milliseconds if not specified.</param>\r\n\t\t/// <returns>A Task representing the asynchronous debounced operation.</returns>\r\n\t\tpublic static async Task Debounce(Action action, int? delay = null)\r\n\t\t\t=> await _Debounce(action, delay);\r\n\r\n\t\t/// <summary>\r\n\t\t/// Executes an action after a delay, canceling any previous pending executions of the same action.\r\n\t\t/// This method ensures that the action is invoked only after the specified delay has elapsed since the last invocation request.\r\n\t\t/// </summary>\r\n\t\t/// <param name=\"action\">The action to debounce.</param>\r\n\t\t/// <param name=\"delay\">The delay in milliseconds to wait before invoking the action. Defaults to 500 milliseconds if not specified.</param>\r\n\t\t/// <returns>A Task representing the asynchronous debounced operation.</returns>\r\n\t\tpublic static async Task Debounce<T>(Action<T> action, T arg, int? delay = null)\r\n\t\t\t=> await _Debounce(action, delay, new object[] { arg });\r\n\r\n\t\t/// <summary>\r\n\t\t/// Executes an asynchronous function after a delay, canceling any previous pending executions of the same function.\r\n\t\t/// This method ensures that the action is invoked only after the specified delay has elapsed since the last invocation request.\r\n\t\t/// </summary>\r\n\t\t/// <param name=\"action\">The asynchronous function to debounce.</param>\r\n\t\t/// <param name=\"delay\">The delay in milliseconds to wait before invoking the function. Defaults to 500 milliseconds if not specified.</param>\r\n\t\t/// <returns>A Task representing the asynchronous debounced operation.</returns>\r\n\t\tpublic static async Task Debounce(Func<Task> action, int? delay = null)\r\n\t\t\t=> await _Debounce(action, delay);\r\n\r\n\t\t/// <summary>\r\n\t\t/// Debounces the specified action, ensuring it's only invoked after a specified delay since the last call.\r\n\t\t/// Subsequent calls within the delay period reset the timer.\r\n\t\t/// This method automatically detects WPF UI thread requirements and marshals execution appropriately.\r\n\t\t/// </summary>\r\n\t\t/// <param name=\"action\">The delegate to debounce.</param>\r\n\t\t/// <param name=\"delay\">The delay in milliseconds before the delegate is invoked. Defaults to 500 milliseconds if not specified.</param>\r\n\t\t/// <param name=\"args\">Optional arguments to pass to the delegate when invoked.</param>\r\n\t\t/// <returns>A Task representing the asynchronous debounced operation.</returns>\r\n\t\tpublic static async Task _Debounce(Delegate action, int? delay = null, params object[] args)\r\n\t\t{\r\n\t\t\tif (action == null)\r\n\t\t\t\treturn;\r\n\t\t\tint delayValue = delay ?? 500;\r\n\t\t\tvar debounceData = DebounceActions.GetOrAdd(action, new DebounceData());\r\n\t\t\tint currentCount;\r\n\t\t\tlock (debounceData.LockObject)\r\n\t\t\t{\r\n\t\t\t\tdebounceData.Counter++;\r\n\t\t\t\tcurrentCount = debounceData.Counter;\r\n\t\t\t}\r\n\t\t\tawait Task.Delay(delayValue);\r\n\r\n\t\t\tbool shouldExecute = false;\r\n\t\t\tlock (debounceData.LockObject)\r\n\t\t\t{\r\n\t\t\t\t// This is the latest scheduled call; mark for execution\r\n\t\t\t\tshouldExecute = currentCount == debounceData.Counter;\r\n\t\t\t}\r\n\r\n\t\t\tif (shouldExecute)\r\n\t\t\t{\r\n\t\t\t\t// Smart UI thread marshaling - detect and marshal to UI thread if needed\r\n\t\t\t\tawait ExecuteWithUIThreadMarshaling(action, args);\r\n\t\t\t}\r\n\t\t}\r\n\r\n\t\t/// <summary>\r\n\t\t/// Executes the given delegate with automatic UI thread marshaling if WPF is available and needed.\r\n\t\t/// </summary>\r\n\t\tprivate static async Task ExecuteWithUIThreadMarshaling(Delegate action, params object[] args)\r\n\t\t{\r\n\t\t\t// Check if WPF is available and we need UI thread marshaling\r\n\t\t\tvar dispatcher = GetWpfDispatcher();\r\n\t\t\tif (dispatcher != null && !dispatcher.CheckAccess())\r\n\t\t\t{\r\n\t\t\t\t// We're not on the UI thread, marshal the call\r\n\t\t\t\tawait dispatcher.BeginInvoke(new Action(() =>\r\n\t\t\t\t{\r\n\t\t\t\t\ttry\r\n\t\t\t\t\t{\r\n\t\t\t\t\t\taction.DynamicInvoke(args);\r\n\t\t\t\t\t}\r\n\t\t\t\t\tcatch (Exception ex)\r\n\t\t\t\t\t{\r\n\t\t\t\t\t\tSystem.Diagnostics.Debug.WriteLine($\"Error in debounced UI action: {ex}\");\r\n\t\t\t\t\t}\r\n\t\t\t\t}));\r\n\t\t\t\treturn;\r\n\t\t\t}\r\n\r\n\t\t\t// Execute normally (either no WPF, already on UI thread, or non-WPF environment)\r\n\t\t\taction.DynamicInvoke(args);\r\n\t\t}\r\n\r\n\t\t/// <summary>\r\n\t\t/// Attempts to get the WPF Dispatcher for the current application.\r\n\t\t/// Returns null if WPF is not available or no dispatcher is found.\r\n\t\t/// </summary>\r\n\t\tprivate static Dispatcher GetWpfDispatcher()\r\n\t\t{\r\n\t\t\ttry\r\n\t\t\t{\r\n\t\t\t\t// Try to get dispatcher from current thread first\r\n\t\t\t\tvar currentDispatcher = Dispatcher.FromThread(Thread.CurrentThread);\r\n\t\t\t\tif (currentDispatcher != null)\r\n\t\t\t\t\treturn currentDispatcher;\r\n\r\n\t\t\t\t// Try to get dispatcher from application\r\n\t\t\t\tvar app = System.Windows.Application.Current;\r\n\t\t\t\tif (app != null)\r\n\t\t\t\t\treturn app.Dispatcher;\r\n\r\n\t\t\t\t// Try to get any available dispatcher\r\n\t\t\t\treturn Dispatcher.CurrentDispatcher;\r\n\t\t\t}\r\n\t\t\tcatch\r\n\t\t\t{\r\n\t\t\t\t// WPF might not be available or initialized\r\n\t\t\t\treturn null;\r\n\t\t\t}\r\n\t\t}\r\n\r\n\t\t#endregion\r\n\r\n#if NETFRAMEWORK // .NET Framework\r\n\r\n\t\t#region Disk Activity\r\n\r\n\t\t// Sometimes it is good to pause if there is too much disk activity.\r\n\t\t// By letting windows/SQL to commit all changes to disk we can improve speed.\r\n\r\n\t\tprivate PerformanceCounter _diskReadCounter = new PerformanceCounter();\r\n\t\tprivate PerformanceCounter _diskWriteCounter = new PerformanceCounter();\r\n\r\n\t\t/// <summary>Reads the specified PerformanceCounter (category, counter, instance) and returns its next value.</summary>\r\n\t\tprivate static double GetCounterValue(PerformanceCounter pc, string categoryName, string counterName, string instanceName)\r\n\t\t{\r\n\t\t\tpc.CategoryName = categoryName;\r\n\t\t\tpc.CounterName = counterName;\r\n\t\t\tpc.InstanceName = instanceName;\r\n\t\t\treturn pc.NextValue();\r\n\t\t}\r\n\r\n\t\t/// <summary>Specifies disk I/O metric types: ReadAndWrite, Read-only, or Write-only operations.</summary>\r\n\t\tpublic enum DiskData { ReadAndWrite, Read, Write };\r\n\r\n\t\t/// <summary>Gets disk I/O bytes per second using the specified DiskData metric via PhysicalDisk _Total counters.</summary>\r\n\t\tpublic double GetDiskData(DiskData dd)\r\n\t\t{\r\n\t\t\treturn dd == DiskData.Read ?\r\n\t\t\t\t\tGetCounterValue(_diskReadCounter, \"PhysicalDisk\", \"Disk Read Bytes/sec\", \"_Total\") :\r\n\t\t\t\t\tdd == DiskData.Write ?\r\n\t\t\t\t\tGetCounterValue(_diskWriteCounter, \"PhysicalDisk\", \"Disk Write Bytes/sec\", \"_Total\") :\r\n\t\t\t\t\tdd == DiskData.ReadAndWrite ?\r\n\t\t\t\t\tGetCounterValue(_diskReadCounter, \"PhysicalDisk\", \"Disk Read Bytes/sec\", \"_Total\") +\r\n\t\t\t\t\tGetCounterValue(_diskWriteCounter, \"PhysicalDisk\", \"Disk Write Bytes/sec\", \"_Total\") :\r\n\t\t\t\t\t0;\r\n\t\t}\r\n\r\n\t\t#endregion\r\n\r\n#endif\r\n\r\n\t\t#region Comparisons\r\n\r\n\t\tprivate static Regex _GuidRegex;\r\n\r\n\t\t/// <summary>Regex matching GUID strings in various formats: 32 digits, hyphenated, with braces or parentheses, or hex-coded lists.</summary>\r\n\t\tpublic static Regex GuidRegex\r\n\t\t{\r\n\t\t\tget\r\n\t\t\t{\r\n\t\t\t\tif (_GuidRegex is null)\r\n\t\t\t\t{\r\n\t\t\t\t\t_GuidRegex = new Regex(\r\n\t\t\t\t\"^[A-Fa-f0-9]{32}$|\" +\r\n\t\t\t\t\"^({|\\\\()?[A-Fa-f0-9]{8}-([A-Fa-f0-9]{4}-){3}[A-Fa-f0-9]{12}(}|\\\\))?$|\" +\r\n\t\t\t\t\"^({)?[0xA-Fa-f0-9]{3,10}(, {0,1}[0xA-Fa-f0-9]{3,6}){2}, {0,1}({)([0xA-Fa-f0-9]{3,4}, {0,1}){7}[0xA-Fa-f0-9]{3,4}(}})$\");\r\n\t\t\t\t}\r\n\t\t\t\treturn _GuidRegex;\r\n\t\t\t}\r\n\t\t}\r\n\r\n\t\t/// <summary>Determines whether the specified string is a valid GUID format; returns false if null or empty.</summary>\r\n\t\tpublic static bool IsGuid(string s)\r\n\t\t{\r\n\t\t\treturn string.IsNullOrEmpty(s)\r\n\t\t\t\t? false\r\n\t\t\t\t: GuidRegex.IsMatch(s);\r\n\t\t}\r\n\r\n\t\t/// <summary>\r\n\t\t/// Returns true if two ranges overlap.\r\n\t\t/// </summary>\r\n\t\tpublic static bool IsOverlap<T>(\r\n\t\t\tT? min1, T? max1,\r\n\t\t\tT? min2, T? max2 = default, bool inclusive = false\r\n\t\t) where T : struct, IComparable<T>\r\n\t\t{\r\n\t\t\t// Check arguments.\r\n\t\t\tif (min1 != null && max1 != null && min1.Value.CompareTo(max1.Value) > 0)\r\n\t\t\t\tthrow new ArgumentException($\"{nameof(min1)} can not be after {nameof(max1)}.\");\r\n\t\t\tif (min2 != null && max2 != null && min2.Value.CompareTo(max2.Value) > 0)\r\n\t\t\t\tthrow new ArgumentException($\"{nameof(min2)} can not be after {nameof(max2)}.\");\r\n\t\t\t// The first range begins before the second ends AND\r\n\t\t\t// The second range begins before the first ends.\r\n\t\t\t// -----|...|---------\r\n\t\t\t// ---------|...|-----\r\n\t\t\t// Null is treated as a full range.\r\n\t\t\treturn\r\n\t\t\t(min1 is null || max2 is null || min1.Value.CompareTo(max2.Value) <= (inclusive ? 0 : -1)) &&\r\n\t\t\t(min2 is null || max1 is null || min2.Value.CompareTo(max1.Value) <= (inclusive ? 0 : -1));\r\n\t\t}\r\n\r\n\t\t#endregion\r\n\r\n\t\t#region Run functions synchronously.\r\n\r\n\t\t/// <summary>\r\n\t\t/// Runs the specified asynchronous function synchronously.\r\n\t\t/// </summary>\r\n\t\t/// <param name=\"asyncFunc\">The asynchronous function to run.</param>\r\n\t\t/// <remarks>\r\n\t\t/// This method avoids deadlocks by temporarily removing the current SynchronizationContext,\r\n\t\t/// allowing the asynchronous function to execute without waiting for the context to be available.\r\n\t\t/// The main disadvantage when compared to the Task.RunSynchronously() method is that\r\n\t\t/// it bypasses the Task scheduler, which could lead to potential performance issues.\r\n\t\t/// </remarks>\r\n\t\tpublic static void RunSynchronously(Func<Task> asyncFunc)\r\n\t\t{\r\n\t\t\t// Save the current synchronization context\r\n\t\t\tvar context = SynchronizationContext.Current;\r\n\r\n\t\t\t// Temporarily remove the current synchronization context\r\n\t\t\tSynchronizationContext.SetSynchronizationContext(null);\r\n\r\n\t\t\ttry\r\n\t\t\t{\r\n\t\t\t\t// Execute the asynchronous function and wait for it to complete\r\n\t\t\t\tasyncFunc().GetAwaiter().GetResult();\r\n\t\t\t}\r\n\t\t\tfinally\r\n\t\t\t{\r\n\t\t\t\t// Restore the original synchronization context\r\n\t\t\t\tSynchronizationContext.SetSynchronizationContext(context);\r\n\t\t\t}\r\n\t\t}\r\n\r\n\t\t/// <summary>\r\n\t\t/// Runs the specified asynchronous function synchronously and returns the result.\r\n\t\t/// </summary>\r\n\t\t/// <typeparam name=\"TResult\">The type of the result.</typeparam>\r\n\t\t/// <param name=\"asyncFunc\">The asynchronous function to run.</param>\r\n\t\t/// <returns>The result of the asynchronous function.</returns>\r\n\t\t/// <remarks>\r\n\t\t/// This method avoids deadlocks by temporarily removing the current SynchronizationContext,\r\n\t\t/// allowing the asynchronous function to execute without waiting for the context to be available.\r\n\t\t/// The main disadvantage when compared to the Task.RunSynchronously() method is that\r\n\t\t/// it bypasses the Task scheduler, which could lead to potential performance issues.\r\n\t\t/// </remarks>\r\n\t\tpublic static TResult RunSynchronously<TResult>(Func<Task<TResult>> asyncFunc)\r\n\t\t{\r\n\t\t\t// Save the current synchronization context\r\n\t\t\tvar context = SynchronizationContext.Current;\r\n\r\n\t\t\t// Temporarily remove the current synchronization context\r\n\t\t\tSynchronizationContext.SetSynchronizationContext(null);\r\n\r\n\t\t\ttry\r\n\t\t\t{\r\n\t\t\t\t// Execute the asynchronous function and wait for it to complete, then return the result\r\n\t\t\t\treturn asyncFunc().GetAwaiter().GetResult();\r\n\t\t\t}\r\n\t\t\tfinally\r\n\t\t\t{\r\n\t\t\t\t// Restore the original synchronization context\r\n\t\t\t\tSynchronizationContext.SetSynchronizationContext(context);\r\n\t\t\t}\r\n\t\t}\r\n\r\n\t\t#endregion\r\n\r\n\t\t#region IDisposable\r\n\r\n\t\t// Dispose() calls Dispose(true)\r\n\t\tpublic void Dispose()\r\n\t\t{\r\n\t\t\tDispose(true);\r\n\t\t\tGC.SuppressFinalize(this);\r\n\t\t}\r\n\r\n\t\t// The bulk of the clean-up code is implemented in Dispose(bool)\r\n\t\tprotected virtual void Dispose(bool disposing)\r\n\t\t{\r\n\t\t\tif (disposing)\r\n\t\t\t{\r\n\r\n#if NETFRAMEWORK // .NET Framework\r\n\r\n\t\t\t\t// Free managed resources.\r\n\t\t\t\tif (_diskReadCounter != null)\r\n\t\t\t\t{\r\n\t\t\t\t\t_diskReadCounter.Dispose();\r\n\t\t\t\t\t_diskReadCounter = null;\r\n\t\t\t\t}\r\n\t\t\t\tif (_diskWriteCounter != null)\r\n\t\t\t\t{\r\n\t\t\t\t\t_diskWriteCounter.Dispose();\r\n\t\t\t\t\t_diskWriteCounter = null;\r\n\t\t\t\t}\r\n#endif\r\n\t\t\t}\r\n\t\t}\r\n\r\n\t\t#endregion\r\n\r\n\t}\r\n\r\n}"
  },
  {
    "path": "FocusLogger/JocysCom/ComponentModel/BindingListInvoked.cs",
    "content": "using System;\r\nusing System.Collections.Generic;\r\nusing System.ComponentModel;\r\nusing System.Reflection;\r\nusing System.Threading;\r\nusing System.Threading.Tasks;\r\n\r\nnamespace JocysCom.ClassLibrary.ComponentModel\r\n{\r\n\t/// <summary>Marshals list modifications and notifications to a TaskScheduler (e.g., UI thread) to prevent cross-thread errors.</summary>\r\n\t/// <remarks>\r\n\t/// Provides AddRange for bulk addition and overrides to dispatch operations via SynchronizingObject, with optional async invocation.\r\n\t/// Commented-out FixLeak method offers a WPF-specific memory leak workaround.\r\n\t/// </remarks>\r\n\tpublic class BindingListInvoked<T> : BindingList<T>\r\n\t{\r\n\t\tpublic BindingListInvoked() : base() { }\r\n\r\n\t\tpublic BindingListInvoked(IList<T> list)\r\n\t\t\t: base(list) { }\r\n\r\n\t\tpublic BindingListInvoked(IEnumerable<T> enumeration)\r\n\t\t\t: base(new List<T>(enumeration)) { }\r\n\r\n\t\tpublic void AddRange(IEnumerable<T> list)\r\n\t\t{\r\n\t\t\tforeach (T item in list)\r\n\t\t\t{ Add(item); }\r\n\t\t}\r\n\r\n\t\t#region ISynchronizeInvoker\r\n\r\n\t\t/// <summary>TaskScheduler used to marshal list operations; null disables synchronization, executing operations on the calling thread.</summary>\r\n\t\tpublic TaskScheduler SynchronizingObject { get; set; }\r\n\r\n\t\tdelegate void ItemDelegate(int index, T item);\r\n\r\n\t\t/// <summary>When true, invocation uses Task.Factory.StartNew to queue asynchronously; when false, runs synchronously on the TaskScheduler.</summary>\r\n\t\tpublic bool AsynchronousInvoke { get; set; }\r\n\r\n\t\t// Dispatches the delegate to SynchronizingObject's TaskScheduler when required; respects AsynchronousInvoke for async vs sync execution.\r\n\t\tvoid Invoke(Delegate method, params object[] args)\r\n\t\t{\r\n\t\t\tvar so = SynchronizingObject;\r\n\t\t\tif (so is null || !JocysCom.ClassLibrary.Controls.ControlsHelper.InvokeRequired)\r\n\t\t\t{\r\n\t\t\t\tDynamicInvoke(method, args);\r\n\t\t\t}\r\n\t\t\telse\r\n\t\t\t{\r\n\t\t\t\t// Note that Control.Invoke(...) is a synchronous action on the main GUI thread,\r\n\t\t\t\t// and will wait for EnableBackControl() to return.\r\n\t\t\t\t// so.Invoke(...) line could freeze if main GUI thread is busy and can't give\r\n\t\t\t\t// attention to any .Invoke requests from background threads.\r\n\t\t\t\t// \r\n\t\t\t\t// Main GUI thread could be blocked because:\r\n\t\t\t\t// a) Modal dialog is up (which means that it's not listening to new requests).\r\n\t\t\t\t// b) It is checking something in a tight continuous loop.\r\n\t\t\t\t// c) Main thread crashed because of exception.\r\n\t\t\t\t// \r\n\t\t\t\t// Try inserting a Application.DoEvents() in the loop, which will pause\r\n\t\t\t\t// execution and force the main thread to process messages and any outstanding .Invoke requests.\r\n\t\t\t\tif (AsynchronousInvoke)\r\n\t\t\t\t\tTask.Factory.StartNew(() =>\r\n\t\t\t\t\t{\r\n\t\t\t\t\t\tDynamicInvoke(method, args);\r\n\t\t\t\t\t}, CancellationToken.None, TaskCreationOptions.None, so);\r\n\t\t\t\telse\r\n\t\t\t\t{\r\n\t\t\t\t\tvar task = new Task(() =>\r\n\t\t\t\t\t{\r\n\t\t\t\t\t\tDynamicInvoke(method, args);\r\n\t\t\t\t\t});\r\n\t\t\t\t\ttask.RunSynchronously(so);\r\n\t\t\t\t}\r\n\t\t\t}\r\n\t\t}\r\n\r\n\t\t// Lock to serialize concurrent list modifications.\r\n\t\tobject OneChangeAtTheTime = new object();\r\n\r\n\t\t// Executes the delegate under a lock and enriches exceptions with type and SynchronizingObject context data.\r\n\t\tvoid DynamicInvoke(Delegate method, params object[] args)\r\n\t\t{\r\n\t\t\ttry\r\n\t\t\t{\r\n\t\t\t\tlock (OneChangeAtTheTime)\r\n\t\t\t\t{\r\n\t\t\t\t\tmethod.DynamicInvoke(args);\r\n\t\t\t\t}\r\n\t\t\t}\r\n\t\t\tcatch (Exception ex)\r\n\t\t\t{\r\n\t\t\t\t// Add data to help with debugging.\r\n\t\t\t\tvar prefix = string.Format(\"{0}<T>\", nameof(BindingListInvoked<T>)) + \".\";\r\n\t\t\t\tex.Data.Add(prefix + \"T\", typeof(T).FullName);\r\n\t\t\t\tex.Data.Add(prefix + \"SynchronizingObject\", SynchronizingObject?.GetType().FullName);\r\n\t\t\t\tex.Data.Add(prefix + \"AsynchronousInvoke\", AsynchronousInvoke);\r\n\t\t\t\tthrow;\r\n\t\t\t}\r\n\t\t}\r\n\r\n\t\tprotected override void RemoveItem(int index)\r\n\t\t{\r\n\t\t\tInvoke((Action<int>)base.RemoveItem, index);\r\n\t\t}\r\n\r\n\t\tprotected override void InsertItem(int index, T item)\r\n\t\t{\r\n\t\t\tInvoke((ItemDelegate)base.InsertItem, index, item);\r\n\t\t}\r\n\r\n\t\tprotected override void SetItem(int index, T item)\r\n\t\t{\r\n\t\t\tInvoke((ItemDelegate)base.SetItem, index, item);\r\n\t\t}\r\n\r\n\t\tprotected override void OnListChanged(ListChangedEventArgs e)\r\n\t\t{\r\n\t\t\tInvoke((Action<ListChangedEventArgs>)base.OnListChanged, e);\r\n\t\t}\r\n\r\n\t\tprotected override void OnAddingNew(AddingNewEventArgs e)\r\n\t\t{\r\n\t\t\tInvoke((Action<AddingNewEventArgs>)base.OnAddingNew, e);\r\n\t\t}\r\n\r\n\t\t//public void FixLeak()\r\n\t\t//{\r\n\t\t//\tvar flags = BindingFlags.Instance | BindingFlags.NonPublic;\r\n\t\t//\tvar fi = GetType().BaseType.BaseType.GetField(\"onListChanged\", flags);\r\n\t\t//\tvar d = (Delegate)fi.GetValue(this);\r\n\t\t//\tif (d != null)\r\n\t\t//\t{\r\n\t\t//\t\tif (d.Target is System.Windows.Data.BindingListCollectionView view)\r\n\t\t//\t\t{\r\n\t\t//\t\t\tview.DetachFromSourceCollection();\r\n\t\t//\t\t\tvar vfi = view.GetType().BaseType.GetField(\"_currentItem\", flags);\r\n\t\t//\t\t\tvfi.SetValue(view, null);\r\n\t\t//\t\t\tfi.SetValue(this, null);\r\n\t\t//\t\t}\r\n\t\t//\t}\r\n\t\t//}\r\n\r\n\t\t#endregion\r\n\t}\r\n}\r\n"
  },
  {
    "path": "FocusLogger/JocysCom/ComponentModel/NotifyPropertyChanged.cs",
    "content": "using System;\r\nusing System.ComponentModel;\r\nusing System.Runtime.CompilerServices;\r\nusing System.Threading;\r\n\r\nnamespace JocysCom.ClassLibrary.ComponentModel\r\n{\r\n\t/// <summary>\r\n\t/// INotifyPropertyChanged base with optional WPF Dispatcher marshalling of property-change notifications to the UI thread.\r\n\t/// </summary>\r\n\t/// <remarks>\r\n\t/// Supports MVVM data binding scenarios, with optional UI-thread invocation of property change events.\r\n\t/// </remarks>\r\n\tpublic class NotifyPropertyChanged : INotifyPropertyChanged\r\n\t{\r\n\r\n\t\t#region ■ INotifyPropertyChanged\r\n\r\n\t\tprivate readonly SynchronizationContext _ctx = SynchronizationContext.Current ?? new SynchronizationContext();\r\n\r\n\t\t/// <summary>\r\n\t\t/// Notifies clients that a property value has changed.\r\n\t\t/// </summary>\r\n\t\t// SUPPRESS: CWE-502: Deserialization of Untrusted Data\r\n\t\t// Fix: Apply [field: NonSerialized] attribute to an event inside class with [Serialized] attribute.\r\n\t\t[field: NonSerialized]\r\n\t\tpublic event PropertyChangedEventHandler PropertyChanged;\r\n\r\n\t\t/// <summary>\r\n\t\t/// Raises the PropertyChanged event. When UseApplicationDispatcher is true, the invocation is marshaled to the WPF UI thread via Application.Current.Dispatcher.\r\n\t\t/// </summary>\r\n\t\t/// <param name=\"propertyName\">Name of the property.</param>\r\n\t\tprotected virtual void OnPropertyChanged([CallerMemberName] string propertyName = null)\r\n\t\t{\r\n\t\t\tif (UseApplicationDispatcher)\r\n\t\t\t{\r\n\t\t\t\t// If already on different thread then...\r\n\t\t\t\tif (SynchronizationContext.Current != _ctx)\r\n\t\t\t\t{\r\n\t\t\t\t\tvoid RaiseCore() => PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(propertyName));\r\n\t\t\t\t\t_ctx.Post(_ => RaiseCore(), null);\r\n\t\t\t\t\treturn;\r\n\t\t\t\t}\r\n\t\t\t}\r\n\t\t\tPropertyChanged?.Invoke(this, new PropertyChangedEventArgs(propertyName));\r\n\t\t}\r\n\r\n\t\t/// <summary>\r\n\t\t/// When true, marshals property-change notifications to the WPF UI thread via Application.Current.Dispatcher. Defaults to false.\r\n\t\t/// </summary>\r\n\t\t[field: NonSerialized, DefaultValue(false)]\r\n\t\tpublic bool UseApplicationDispatcher = false;\r\n\r\n\t\t/// <summary>\r\n\t\t/// Sets the backing field if the new value differs (per Equals), then invokes OnPropertyChanged. Skips notifications when value is unchanged.\r\n\t\t/// </summary>\r\n\t\t/// <typeparam name=\"T\">Type of the backing field.</typeparam>\r\n\t\t/// <param name=\"property\">Reference to the backing field.</param>\r\n\t\t/// <param name=\"value\">New value to assign.</param>\r\n\t\t/// <param name=\"propertyName\">Name of the property; supplied by CallerMemberName automatically.</param>\r\n\t\tprotected void SetProperty<T>(ref T property, T value, [CallerMemberName] string propertyName = null)\r\n\t\t{\r\n\t\t\tif (Equals(property, value))\r\n\t\t\t\treturn;\r\n\t\t\tproperty = value;\r\n\t\t\t// Invoke overridden OnPropertyChanged method in the most derived class of the object.\r\n\t\t\tOnPropertyChanged(propertyName);\r\n\t\t}\r\n\r\n\r\n\t\t#endregion\r\n\t}\r\n}\r\n"
  },
  {
    "path": "FocusLogger/JocysCom/ComponentModel/PropertyComparer.cs",
    "content": "using System;\r\nusing System.Collections.Generic;\r\nusing System.ComponentModel;\r\n\r\nnamespace JocysCom.ClassLibrary.ComponentModel\r\n{\r\n    /// <summary>Implements IComparer&lt;T&gt; to sort items by a single PropertyDescriptor or a ListSortDescriptionCollection.</summary>\r\n    /// <remarks>\r\n    /// Part of Be.Timvw.Framework.ComponentModel (http://betimvwframework.codeplex.com/).\r\n    /// Used by SortableBindingList&lt;T&gt; to apply simple and advanced sorting in data-binding contexts.\r\n    /// A parallel SortComparer&lt;T&gt; in the Collections namespace provides an alternative implementation.\r\n    /// </remarks>\r\n    public class PropertyComparer<T> : IComparer<T>\r\n    {\r\n        private readonly ListSortDescriptionCollection _SortCollection = null;\r\n        private readonly PropertyDescriptor _PropDesc = null;\r\n        private readonly ListSortDirection _Direction = ListSortDirection.Ascending;\r\n\r\n        public PropertyComparer(PropertyDescriptor propDesc, ListSortDirection direction)\r\n        {\r\n            _PropDesc = propDesc;\r\n            _Direction = direction;\r\n        }\r\n\r\n        public PropertyComparer(ListSortDescriptionCollection sortCollection)\r\n        {\r\n            _SortCollection = sortCollection;\r\n        }\r\n\r\n        int IComparer<T>.Compare(T x, T y)\r\n        {\r\n            return Compare(x, y);\r\n        }\r\n\r\n        /// <summary>\r\n        /// Compares x and y using the configured sort criteria.\r\n        /// Uses the single PropertyDescriptor and direction if provided; otherwise applies the ListSortDescriptionCollection recursively.\r\n        /// Returns 0 when no sort criteria are specified.\r\n        /// </summary>\r\n        protected int Compare(T x, T y)\r\n        {\r\n            if (_PropDesc != null)\r\n            {\r\n                var xValue = _PropDesc.GetValue(x);\r\n                var yValue = _PropDesc.GetValue(y);\r\n                return CompareValues(xValue, yValue, _Direction);\r\n            }\r\n            else if (_SortCollection != null && _SortCollection.Count > 0)\r\n                return RecursiveCompareInternal(x, y, 0);\r\n            else\r\n                return 0;\r\n        }\r\n\r\n        /// <summary>\r\n        /// Compares two property values, using IComparable when available or falling back to string comparison, and applies sort direction.\r\n        /// </summary>\r\n        private int CompareValues(object xValue, object yValue, ListSortDirection direction)\r\n        {\r\n            int retValue;\r\n            if (xValue is null && yValue is null)\r\n                retValue = 0;\r\n            else if (xValue is IComparable)\r\n                retValue = ((IComparable)xValue).CompareTo(yValue);\r\n            else if (yValue is IComparable)\r\n                retValue = ((IComparable)yValue).CompareTo(xValue);\r\n            else if (!xValue.Equals(yValue))\r\n                retValue = xValue.ToString().CompareTo(yValue.ToString());\r\n            else\r\n                retValue = 0;\r\n            return (direction == ListSortDirection.Ascending ? 1 : -1) * retValue;\r\n        }\r\n\r\n        /// <summary>\r\n        /// Recursively compares items by each sort description in the collection until a non-zero result is found or the end is reached.\r\n        /// </summary>\r\n        private int RecursiveCompareInternal(T x, T y, int index)\r\n        {\r\n            if (index >= _SortCollection.Count)\r\n                return 0;\r\n            var listSortDesc = _SortCollection[index];\r\n            var xValue = listSortDesc.PropertyDescriptor.GetValue(x);\r\n            var yValue = listSortDesc.PropertyDescriptor.GetValue(y);\r\n            var retValue = CompareValues(xValue, yValue, listSortDesc.SortDirection);\r\n            return (retValue == 0)\r\n                ? RecursiveCompareInternal(x, y, ++index)\r\n                : retValue;\r\n        }\r\n    }\r\n}"
  },
  {
    "path": "FocusLogger/JocysCom/ComponentModel/SortableBindingList.cs",
    "content": "﻿using System;\r\nusing System.Collections.Generic;\r\nusing System.ComponentModel;\r\n\r\nnamespace JocysCom.ClassLibrary.ComponentModel\r\n{\r\n\t/// <summary>\r\n\t/// Be.Timvw.Framework.ComponentModel\r\n\t/// http://betimvwframework.codeplex.com/\r\n\t/// </summary>\r\n\t/// <typeparam name=\"T\"></typeparam>\r\n\t[Serializable]\r\n\tpublic class SortableBindingList<T> : BindingListInvoked<T>, IBindingListView, IRaiseItemChangedEvents\r\n\t{\r\n\t\tpublic SortableBindingList() : base() { }\r\n\r\n\t\tpublic SortableBindingList(IList<T> list)\r\n\t\t\t: base(list) { }\r\n\r\n\t\tpublic SortableBindingList(IEnumerable<T> enumeration)\r\n\t\t\t: base(new List<T>(enumeration)) { }\r\n\r\n\t\tpublic static SortableBindingList<T> From(IEnumerable<T> list)\r\n\t\t{\r\n\t\t\treturn new SortableBindingList<T>(list);\r\n\t\t}\r\n\r\n\t\tprotected override bool SupportsSearchingCore => true;\r\n\t\tprotected override bool SupportsSortingCore => true;\r\n\t\tprotected override bool IsSortedCore => _Sorted;\r\n\t\tprotected override ListSortDirection SortDirectionCore => _SortDirection;\r\n\t\tprotected override PropertyDescriptor SortPropertyCore => _SortProperty;\r\n\r\n\t\tListSortDescriptionCollection IBindingListView.SortDescriptions => SortDescriptions;\r\n\t\tprotected ListSortDescriptionCollection SortDescriptions => _SortDescriptions;\r\n\r\n\t\tbool IBindingListView.SupportsAdvancedSorting => SupportsAdvancedSorting;\r\n\t\tprotected bool SupportsAdvancedSorting => true;\r\n\r\n\t\tbool IBindingListView.SupportsFiltering => SupportsFiltering;\r\n\t\tprotected bool SupportsFiltering => true;\r\n\r\n\t\tbool IRaiseItemChangedEvents.RaisesItemChangedEvents => RaisesItemChangedEvents;\r\n\t\tprotected bool RaisesItemChangedEvents => true;\r\n\r\n\t\tprivate bool _Sorted = false;\r\n\t\tprivate bool _Filtered = false;\r\n\t\tprivate string _FilterString = null;\r\n\t\tprivate ListSortDirection _SortDirection = ListSortDirection.Ascending;\r\n\r\n\t\t[NonSerialized]\r\n\t\tprivate PropertyDescriptor _SortProperty = null;\r\n\r\n\t\t[NonSerialized]\r\n\t\tprivate ListSortDescriptionCollection _SortDescriptions = new ListSortDescriptionCollection();\r\n\t\tprivate readonly List<T> _OriginalCollection = new List<T>();\r\n\t\tbool IBindingList.AllowNew => CheckReadOnly();\r\n\t\tbool IBindingList.AllowRemove => CheckReadOnly();\r\n\t\tprivate bool CheckReadOnly() { return !_Sorted && !_Filtered; }\r\n\r\n\t\tprotected override int FindCore(PropertyDescriptor property, object key)\r\n\t\t{\r\n\t\t\t// Simple iteration:\r\n\t\t\tfor (var i = 0; i < Count; i++)\r\n\t\t\t{\r\n\t\t\t\tvar item = this[i];\r\n\t\t\t\tif (property.GetValue(item).Equals(key))\r\n\t\t\t\t\treturn i;\r\n\t\t\t}\r\n\t\t\treturn -1; // Not found\r\n\t\t}\r\n\r\n\t\tprotected override void ApplySortCore(PropertyDescriptor property, ListSortDirection direction)\r\n\t\t{\r\n\t\t\t_SortDirection = direction;\r\n\t\t\t_SortProperty = property;\r\n\t\t\tvar comparer = new PropertyComparer<T>(property, direction);\r\n\t\t\tApplySortInternal(comparer);\r\n\t\t}\r\n\r\n\t\tvoid IBindingListView.ApplySort(ListSortDescriptionCollection sorts)\r\n\t\t{\r\n\t\t\tApplySort(sorts);\r\n\t\t}\r\n\r\n\t\tprotected void ApplySort(ListSortDescriptionCollection sorts)\r\n\t\t{\r\n\t\t\t_SortProperty = null;\r\n\t\t\t_SortDescriptions = sorts;\r\n\t\t\tvar comparer = new PropertyComparer<T>(sorts);\r\n\t\t\tApplySortInternal(comparer);\r\n\t\t}\r\n\r\n\t\tprivate void ApplySortInternal(PropertyComparer<T> comparer)\r\n\t\t{\r\n\t\t\tif (_OriginalCollection.Count == 0)\r\n\t\t\t\t_OriginalCollection.AddRange(this);\r\n\t\t\tvar listRef = Items as List<T>;\r\n\t\t\tif (listRef is null)\r\n\t\t\t\treturn;\r\n\t\t\tlistRef.Sort(comparer);\r\n\t\t\t_Sorted = true;\r\n\t\t\tOnListChanged(new ListChangedEventArgs(ListChangedType.Reset, -1));\r\n\t\t}\r\n\r\n\t\tprotected override void RemoveSortCore()\r\n\t\t{\r\n\t\t\tif (!_Sorted)\r\n\t\t\t\treturn;\r\n\t\t\tClear();\r\n\t\t\tforeach (var item in _OriginalCollection)\r\n\t\t\t\tAdd(item);\r\n\t\t\t_OriginalCollection.Clear();\r\n\t\t\t_SortProperty = null;\r\n\t\t\t_SortDescriptions = null;\r\n\t\t\t_Sorted = false;\r\n\t\t}\r\n\r\n\t\tstring IBindingListView.Filter { get { return Filter; }  set { Filter = value; }  }\r\n\r\n\t\tprotected string Filter\r\n\t\t{\r\n\t\t\tget { return _FilterString; }\r\n\t\t\tset\r\n\t\t\t{\r\n\t\t\t\t_FilterString = value;\r\n\t\t\t\t_Filtered = true;\r\n\t\t\t\tUpdateFilter();\r\n\t\t\t}\r\n\t\t}\r\n\r\n\t\tvoid IBindingListView.RemoveFilter() { RemoveFilter(); }\r\n\t\tprotected void RemoveFilter()\r\n\t\t{\r\n\t\t\tif (!_Filtered)\r\n\t\t\t\treturn;\r\n\t\t\t_FilterString = null;\r\n\t\t\t_Filtered = false;\r\n\t\t\t_Sorted = false;\r\n\t\t\t_SortDescriptions = null;\r\n\t\t\t_SortProperty = null;\r\n\t\t\tClear();\r\n\t\t\tforeach (var item in _OriginalCollection)\r\n\t\t\t\tAdd(item);\r\n\t\t\t_OriginalCollection.Clear();\r\n\t\t}\r\n\r\n\t\tprotected virtual void UpdateFilter()\r\n\t\t{\r\n\t\t\tvar equalsPos = _FilterString.IndexOf('=');\r\n\t\t\t// Get property name\r\n\t\t\tvar propName = _FilterString.Substring(0, equalsPos).Trim();\r\n\t\t\t// Get filter criteria\r\n\t\t\tvar criteria = _FilterString.Substring(equalsPos + 1,\r\n\t\t\t   _FilterString.Length - equalsPos - 1).Trim();\r\n\t\t\t// Strip leading and trailing quotes\r\n\t\t\tcriteria = criteria.Trim('\\'', '\"');\r\n\t\t\t// Get a property descriptor for the filter property\r\n\t\t\tvar propDesc = TypeDescriptor.GetProperties(typeof(T))[propName];\r\n\t\t\tif (_OriginalCollection.Count == 0)\r\n\t\t\t\t_OriginalCollection.AddRange(this);\r\n\t\t\tvar currentCollection = new List<T>(this);\r\n\t\t\tClear();\r\n\t\t\tforeach (var item in currentCollection)\r\n\t\t\t{\r\n\t\t\t\tvar value = propDesc.GetValue(item);\r\n\t\t\t\tif (string.Format(\"{0}\", value) == criteria)\r\n\t\t\t\t\tAdd(item);\r\n\t\t\t}\r\n\t\t}\r\n\r\n\t\tprotected override void InsertItem(int index, T item)\r\n\t\t{\r\n\t\t\tforeach (PropertyDescriptor propDesc in TypeDescriptor.GetProperties(item))\r\n\t\t\t{\r\n\t\t\t\tif (propDesc.SupportsChangeEvents)\r\n\t\t\t\t\tpropDesc.AddValueChanged(item, OnItemChanged);\r\n\t\t\t}\r\n\t\t\tbase.InsertItem(index, item);\r\n\t\t}\r\n\r\n\t\tprotected override void RemoveItem(int index)\r\n\t\t{\r\n\t\t\tvar item = Items[index];\r\n\t\t\tvar propDescs = TypeDescriptor.GetProperties(item);\r\n\t\t\tforeach (PropertyDescriptor propDesc in propDescs)\r\n\t\t\t{\r\n\t\t\t\tif (propDesc.SupportsChangeEvents)\r\n\t\t\t\t\tpropDesc.RemoveValueChanged(item, OnItemChanged);\r\n\t\t\t}\r\n\t\t\tbase.RemoveItem(index);\r\n\t\t}\r\n\r\n\t\tprivate void OnItemChanged(object sender, EventArgs args)\r\n\t\t{\r\n\t\t\tvar index = Items.IndexOf((T)sender);\r\n\t\t\tOnListChanged(new ListChangedEventArgs(ListChangedType.ItemChanged, index));\r\n\t\t}\r\n\r\n\t\tpublic void RemoveAll(Func<object, bool> value)\r\n\t\t{\r\n\t\t\tthrow new NotImplementedException();\r\n\t\t}\r\n\t}\r\n\r\n}\r\n"
  },
  {
    "path": "FocusLogger/JocysCom/Configuration/Arguments.cs",
    "content": "using JocysCom.ClassLibrary.Runtime;\nusing System;\nusing System.Collections.Generic;\nusing System.Linq;\nusing System.Text.RegularExpressions;\n\nnamespace JocysCom.ClassLibrary.Configuration\n{\n    /// <summary>\n    /// Represents a case-insensitive command-line arguments parser built on Dictionary&lt;string,string&gt;.\n    /// Splits parameters by -, --, /, =, or :, and removes enclosing quotes from values.\n    /// </summary>\n    /// <remarks>\n    /// Shares parsing logic with <see cref=\"InstallContext\"/>, but outputs to this dictionary and does not include logging or flag helper methods.\n    /// </remarks>\n    public class Arguments : Dictionary<string, string>\n    //public class Arguments : StringDictionary // Case insensitive.\n    {\n        /// <summary>\n        /// Initializes and parses the specified command-line arguments.\n        /// </summary>\n        /// <param name=\"args\">Array of command-line arguments to parse.</param>\n        /// <param name=\"comparer\">Comparer for key matching; defaults to InvariantCultureIgnoreCase if null.</param>\n        public Arguments(string[] args, StringComparer comparer = null) : base(comparer ?? StringComparer.InvariantCultureIgnoreCase)\n        {\n            Regex spliter = new Regex(@\"^-{1,2}|^/|=|:\",\n                RegexOptions.IgnoreCase | RegexOptions.Compiled);\n\n\t\t\tRegex remover = new Regex(@\"^['\"\"]?(.*?)['\"\"]?$\",\n\t\t\tRegexOptions.IgnoreCase | RegexOptions.Compiled);\n\n            string Parameter = null;\n            string[] Parts;\n\n            // Valid parameters forms:\n            // (-|--|/)param( |=|:)[(\"|')]value[(\"|')]\n            // Examples: \n            //   -param1 value1 --param2 /param3:\"Test-:-work\" \n            //   /param4=happy -param5 '--=nice=--'\n            foreach (string Txt in args)\n            {\n                // Look for new parameters (-,/ or --) and a\n                // possible enclosed value (=,:)\n                Parts = spliter.Split(Txt, 3);\n                switch (Parts.Length)\n                {\n                    // Found a value (for the last parameter \n                    // found using space separator)\n                    case 1:\n                        if (Parameter != null)\n                        {\n                            if (!base.ContainsKey(Parameter))\n                            {\n                                Parts[0] = remover.Replace(Parts[0], \"$1\");\n                                base.Add(Parameter, Parts[0]);\n                            }\n                            Parameter = null;\n                        }\n                        // else Error: no parameter waiting for a value (skipped)\n                        break;\n\n                    // Found just a parameter\n                    case 2:\n                        // The last parameter is still waiting. \n                        // With no value, set it to true.\n                        if (Parameter != null)\n                        {\n                            if (!base.ContainsKey(Parameter))\n                                base.Add(Parameter, null);\n                        }\n                        Parameter = Parts[1];\n                        break;\n\n                    // Parameter with enclosed value\n                    case 3:\n                        // The last parameter is still waiting. \n                        // With no value, set it to true.\n                        if (Parameter != null)\n                        {\n                            if (!base.ContainsKey(Parameter))\n                                base.Add(Parameter, null);\n                        }\n\n                        Parameter = Parts[1];\n\n                        // Remove possible enclosing characters (\",')\n                        if (!base.ContainsKey(Parameter))\n                        {\n                            Parts[2] = remover.Replace(Parts[2], \"$1\");\n                            base.Add(Parameter, Parts[2]);\n                        }\n                        Parameter = null;\n                        break;\n                }\n            }\n            // In case a parameter is still waiting\n            if (Parameter != null)\n            {\n                if (!base.ContainsKey(Parameter))\n                    base.Add(Parameter, null);\n            }\n        }\n\n        /// <summary>\n        /// Retrieves the value associated with the specified key, or null if not found.\n        /// </summary>\n        /// <param name=\"key\">The key to locate in the arguments.</param>\n        /// <param name=\"ignoreCase\">True to ignore case during key comparison; otherwise, false.</param>\n        /// <returns>The value associated with the key; or null if not present.</returns>\n        public string GetValue(string key, bool ignoreCase = false)\n        {\n            var keyValue = Keys.Cast<string>().FirstOrDefault(x => string.Compare(x, key, ignoreCase) == 0);\n            return keyValue is null ? null : this[keyValue];\n        }\n\n        /// <summary>\n        /// Determines whether an entry with the specified key exists, using optional case-insensitive comparison.\n        /// </summary>\n        /// <param name=\"key\">The key to locate in the arguments.</param>\n        /// <param name=\"ignoreCase\">True to ignore case during key comparison; otherwise, false.</param>\n        /// <returns>True if an entry with the specified key exists; otherwise, false.</returns>\n        public bool ContainsKey(string key, bool ignoreCase)\n        {\n            return Keys\n                .Cast<string>()\n                .Any(x => string.Compare(x, key, ignoreCase) == 0);\n        }\n\n        //public T GetValue<T>(string key, bool ignoreCase = false, T defaultValue = default) where T : struct\n        //{\n        //    var valueString = GetValue(key, ignoreCase);\n        //    return RuntimeHelper.TryParse<T>(valueString, defaultValue);\n        //}\n    }\n}\n"
  },
  {
    "path": "FocusLogger/JocysCom/Configuration/AssemblyInfo.cs",
    "content": "using System;\r\nusing System.Diagnostics;\r\nusing System.IO;\r\nusing System.Reflection;\r\nusing System.Runtime.InteropServices;\r\n\r\nnamespace JocysCom.ClassLibrary.Configuration\r\n{\r\n\t/// <summary>\r\n\t/// Encapsulates assembly metadata and utilities, including entry assembly detection,\r\n\t/// build timestamps, version/title formatting, and application data path resolution.\r\n\t/// </summary>\r\n\tpublic partial class AssemblyInfo\r\n\t{\r\n\t\t/// <summary>\r\n\t\t/// Initializes AssemblyInfo for the entry assembly by using GetEntryAssembly and fallbacks\r\n\t\t/// (stack scan, AppDomain scan, calling/executing assemblies).\r\n\t\t/// </summary>\r\n\t\tpublic AssemblyInfo()\r\n\t\t{\r\n\t\t\tAssembly =\r\n\t\t\t\tAssembly.GetEntryAssembly() ??\r\n\t\t\t\tFindEntryAssembly1() ??\r\n\t\t\t\tFindEntryAssembly2() ??\r\n\t\t\t\tAssembly.GetCallingAssembly() ??\r\n\t\t\t\tAssembly.GetExecutingAssembly();\r\n\t\t}\r\n\r\n\t\tpublic static object _EntryLock = new object();\r\n\t\t/// <summary>\r\n\t\t/// Gets or sets the singleton entry AssemblyInfo instance; thread-safe.\r\n\t\t/// </summary>\r\n\t\tpublic static AssemblyInfo Entry\r\n\t\t{\r\n\t\t\tget\r\n\t\t\t{\r\n\t\t\t\tlock (_EntryLock)\r\n\t\t\t\t{\r\n\t\t\t\t\tif (_Entry is null)\r\n\t\t\t\t\t\t_Entry = new AssemblyInfo();\r\n\t\t\t\t\treturn _Entry;\r\n\t\t\t\t}\r\n\t\t\t}\r\n\t\t\tset\r\n\t\t\t{\r\n\t\t\t\tlock (_EntryLock)\r\n\t\t\t\t{\r\n\t\t\t\t\t_Entry = value;\r\n\t\t\t\t}\r\n\t\t\t}\r\n\t\t}\r\n\t\tstatic AssemblyInfo _Entry;\r\n\r\n\t\tpublic AssemblyInfo(string strValFile)\r\n\t\t{\r\n\t\t\tAssembly = Assembly.LoadFile(strValFile);\r\n\t\t}\r\n\r\n\t\tpublic AssemblyInfo(Assembly assembly)\r\n\t\t{\r\n\t\t\tAssembly = assembly;\r\n\t\t}\r\n\r\n\t\t#region Entry assembly\r\n\r\n\t\t/// <summary>\r\n\t\t/// Assembly.GetEntryAssembly() returns null in web applications. Mark assembly as the entry assembly\r\n\t\t/// by adding this attribute inside Properties\\AssemblyInfo.cs file:\r\n\t\t/// [assembly: JocysCom.ClassLibrary.Configuration.AssemblyInfo.EntryAssembly]\r\n\t\t/// </summary>\r\n\t\t[AttributeUsage(AttributeTargets.Assembly)]\r\n\t\tpublic sealed class EntryAssemblyAttribute : Attribute { }\r\n\r\n\t\t// Method 1 better works on multiple assemblies marked as entry.\r\n\t\t/// <summary>\r\n\t\t/// Finds entry assembly by scanning reversed call stack for EntryAssemblyAttribute markers.\r\n\t\t/// </summary>\r\n\t\tAssembly FindEntryAssembly1()\r\n\t\t{\r\n\t\t\tvar frames = new StackTrace().GetFrames();\r\n\t\t\tArray.Reverse(frames);\r\n\t\t\tforeach (var frame in frames)\r\n\t\t\t{\r\n\t\t\t\tvar declaringType = frame.GetMethod().DeclaringType;\r\n\t\t\t\tvar assembly = Assembly.GetAssembly(declaringType);\r\n\t\t\t\tvar attribute = assembly.GetCustomAttributes(typeof(EntryAssemblyAttribute), false);\r\n\t\t\t\tif (attribute.Length > 0)\r\n\t\t\t\t\treturn assembly;\r\n\t\t\t}\r\n\t\t\treturn null;\r\n\t\t}\r\n\r\n\t\t// Find on current domain.\r\n\t\t/// <summary>\r\n\t\t/// Finds entry assembly by scanning loaded AppDomain assemblies for EntryAssemblyAttribute markers.\r\n\t\t/// </summary>\r\n\t\tAssembly FindEntryAssembly2()\r\n\t\t{\r\n\t\t\tvar assemblies = AppDomain.CurrentDomain.GetAssemblies();\r\n\t\t\tforeach (var assembly in assemblies)\r\n\t\t\t{\r\n\t\t\t\tvar attribute = assembly.GetCustomAttributes(typeof(EntryAssemblyAttribute), false);\r\n\t\t\t\tif (attribute.Length > 0)\r\n\t\t\t\t\treturn assembly;\r\n\t\t\t}\r\n\t\t\treturn null;\r\n\t\t}\r\n\r\n\t\t#endregion\r\n\r\n\t\tpublic Assembly Assembly { get; set; }\r\n\r\n\t\tDateTime? _BuildDateTime;\r\n\t\tobject BuildDateTimeLock = new object();\r\n\r\n\t\t/// <summary>\r\n\t\t/// Gets the assembly's build timestamp from metadata, cached per instance (thread-safe).\r\n\t\t/// </summary>\r\n\t\tpublic DateTime BuildDateTime\r\n\t\t{\r\n\t\t\tget\r\n\t\t\t{\r\n\t\t\t\tlock (BuildDateTimeLock)\r\n\t\t\t\t{\r\n\t\t\t\t\tif (!_BuildDateTime.HasValue)\r\n\t\t\t\t\t\t_BuildDateTime = GetBuildDateTime(Assembly);\r\n\t\t\t\t\treturn _BuildDateTime.Value;\r\n\t\t\t\t}\r\n\t\t\t}\r\n\t\t}\r\n\r\n\t\tobject FullTitleLock = new object();\r\n\t\tstring _FullTitle;\r\n\r\n\t\t/// <summary>\r\n\t\t/// Gets the full application title including version, build metadata, run mode, architecture, and description (thread-safe).\r\n\t\t/// </summary>\r\n\t\tpublic string FullTitle\r\n\t\t{\r\n\t\t\tget\r\n\t\t\t{\r\n\t\t\t\tlock (FullTitleLock)\r\n\t\t\t\t{\r\n\t\t\t\t\tif (string.IsNullOrEmpty(_FullTitle))\r\n\t\t\t\t\t\t_FullTitle = GetTitle();\r\n\t\t\t\t\treturn _FullTitle;\r\n\t\t\t\t}\r\n\t\t\t}\r\n\t\t}\r\n\r\n\t\t/// <summary>\r\n\t\t/// Gets the configured runtime mode; currently returns empty until configuration provider is standardized.\r\n\t\t/// </summary>\r\n\t\tpublic string RunMode\r\n\t\t{\r\n\t\t\tget\r\n\t\t\t{\r\n\t\t\t\tif (_RunMode is null)\r\n\t\t\t\t\t// TODO: Standardize configuration provider XML, JSON, INI, Registry, etc...\r\n\t\t\t\t\t// https://docs.microsoft.com/en-us/dotnet/core/extensions/configuration-providers\r\n\t\t\t\t\t//_RunMode = SettingsParser.Current.Parse(\"RunMode\", \"\");\r\n\t\t\t\t\treturn \"\";\r\n\t\t\t\treturn _RunMode;\r\n\t\t\t}\r\n\t\t}\r\n\t\tpublic string _RunMode;\r\n\r\n\t\t/// <summary>\r\n\t\t/// Builds a descriptive title string for the assembly, including version, release stage (Alpha/Beta/RC/RTM/GA),\r\n\t\t/// optional run mode, build date, architecture, description, and user context.\r\n\t\t/// </summary>\r\n\t\tpublic string GetTitle(bool showBuild = true, bool showRunMode = true, bool showBuildDate = true, bool showArchitecture = true, bool showDescription = true, int versionNumbers = 3)\r\n\t\t{\r\n\t\t\tvar s = string.Format(\"{0} {1} {2}\", Company, Product, Version.ToString(versionNumbers));\r\n\t\t\tif (showBuild)\r\n\t\t\t{\r\n\t\t\t\t// Version = major.minor.build.revision\r\n\t\t\t\tswitch (Version.Build)\r\n\t\t\t\t{\r\n\t\t\t\t\tcase 0: s += \" Alpha\"; break;  // Alpha Release (AR)\r\n\t\t\t\t\tcase 1: s += \" Beta 1\"; break; // Master Beta (MB)\r\n\t\t\t\t\tcase 2: s += \" Beta 2\"; break; // Feature Complete (FC)\r\n\t\t\t\t\tcase 3: s += \" Beta 3\"; break; // Technical Preview (TP)\r\n\t\t\t\t\tcase 4: s += \" RC\"; break;     // Release Candidate (RC)\r\n\t\t\t\t\tcase 5: s += \" RTM\"; break;    // Release to Manufacturing (RTM)\r\n\t\t\t\t\tdefault: break;                // General Availability (GA) - Gold\r\n\t\t\t\t}\r\n\t\t\t}\r\n\r\n\t\t\tvar haveRunMode = !string.IsNullOrEmpty(RunMode);\r\n\t\t\t// If run mode is not specified then assume live.\r\n\t\t\tvar nonLive = haveRunMode && string.Compare(RunMode, \"LIVE\", true) != 0;\r\n\t\t\tif (showBuildDate || (showRunMode && nonLive))\r\n\t\t\t{\r\n\t\t\t\ts += \" (\";\r\n\t\t\t\tif (showRunMode && nonLive)\r\n\t\t\t\t{\r\n\t\t\t\t\ts += string.Format(\"{0}\", RunMode);\r\n\t\t\t\t\tif (showBuildDate) s += \" \";\r\n\t\t\t\t}\r\n\t\t\t\tif (showBuildDate)\r\n\t\t\t\t{\r\n\t\t\t\t\ts += string.Format(\"Build: {0:yyyy-MM-dd}\", BuildDateTime);\r\n\t\t\t\t}\r\n\t\t\t\ts += \")\";\r\n\t\t\t}\r\n\t\t\tif (showArchitecture)\r\n\t\t\t{\r\n\t\t\t\tswitch (RuntimeInformation.ProcessArchitecture)\r\n\t\t\t\t{\r\n\t\t\t\t\tcase Architecture.X64:\r\n\t\t\t\t\tcase Architecture.Arm64:\r\n\t\t\t\t\t\ts += \" 64-bit\";\r\n\t\t\t\t\t\tbreak;\r\n\t\t\t\t\tcase Architecture.X86:\r\n\t\t\t\t\tcase Architecture.Arm:\r\n\t\t\t\t\t\ts += \" 32-bit\";\r\n\t\t\t\t\t\tbreak;\r\n\t\t\t\t\tdefault: // Default is MSIL: Any CPU, show nothing/\r\n\t\t\t\t\t\tbreak;\r\n\t\t\t\t}\r\n\t\t\t}\r\n\t\t\tif (showDescription && !string.IsNullOrEmpty(Description) && !s.Contains(Description))\r\n\t\t\t{\r\n\t\t\t\ts += \" - \" + Description;\r\n\t\t\t}\r\n\r\n#if NETFRAMEWORK // .NET Framework\r\n\r\n\t\t\t// Add elevated tag.\r\n\t\t\tvar identity = System.Security.Principal.WindowsIdentity.GetCurrent();\r\n\t\t\tvar isElevated = identity.Owner != identity.User;\r\n\t\t\t// Add running user.\r\n\t\t\tstring windowsDomain = GetWindowsDomainName();\r\n\t\t\tstring windowsUser = GetWindowsUserName();\r\n\t\t\tstring processDomain = Environment.UserDomainName;\r\n\t\t\tstring processUser = Environment.UserName;\r\n\t\t\tif (string.Compare(windowsDomain, processDomain, true) != 0 || string.Compare(windowsUser, processUser, true) != 0)\r\n\t\t\t\ts += string.Format(\" ({0}\\\\{1})\", processDomain, processUser);\r\n\t\t\telse if (isElevated)\r\n\t\t\t\ts += \" (Administrator)\";\r\n\t\t\t// if (WinAPI.IsVista && WinAPI.IsElevated() && WinAPI.IsInAdministratorRole) Text += \" (Administrator)\";\r\n#endif\r\n\t\t\treturn s.Trim();\r\n\t\t}\r\n\r\n#if NETFRAMEWORK // .NET Framework\r\n\r\n\t\tinternal partial class NativeMethods\r\n\t\t{\r\n\t\t\t[DllImport(\"wtsapi32.dll\")]\r\n\t\t\tinternal static extern bool WTSQuerySessionInformationW(\r\n\t\t\t\tIntPtr hServer,\r\n\t\t\t\tint SessionId,\r\n\t\t\t\tint WTSInfoClass,\r\n\t\t\t\tout IntPtr ppBuffer,\r\n\t\t\t\tout IntPtr pBytesReturned\r\n\t\t\t);\r\n\r\n\t\t}\r\n\r\n\t\t/// <summary>\r\n\t\t/// Retrieves the current Windows session domain name via WTSQuerySessionInformationW.\r\n\t\t/// </summary>\r\n\t\tpublic string GetWindowsDomainName() { return GetInformation(7); }\r\n\r\n\t\t/// <summary>\r\n\t\t/// Retrieves the current Windows session user name via WTSQuerySessionInformationW.\r\n\t\t/// </summary>\r\n\t\tpublic string GetWindowsUserName() { return GetInformation(5); }\r\n\r\n\t\t/// <summary>\r\n\t\t/// Invokes WTSQuerySessionInformation to query session-specific information (e.g., domain or user name).\r\n\t\t/// </summary>\r\n\t\tprivate static string GetInformation(int WTSInfoClass)\r\n\t\t{\r\n\t\t\t// Use current context.\r\n\t\t\tvar WTS_CURRENT_SERVER_HANDLE = IntPtr.Zero;\r\n\t\t\tvar p = System.Diagnostics.Process.GetCurrentProcess();\r\n\t\t\tIntPtr AnswerBytes;\r\n\t\t\tIntPtr AnswerCount;\r\n\t\t\t// Get domain name.\r\n\t\t\tvar success = NativeMethods.WTSQuerySessionInformationW(\r\n\t\t\t\tWTS_CURRENT_SERVER_HANDLE,\r\n\t\t\t\tp.SessionId,\r\n\t\t\t\tWTSInfoClass,\r\n\t\t\t\tout AnswerBytes,\r\n\t\t\t\tout AnswerCount\r\n\t\t\t);\r\n\t\t\treturn Marshal.PtrToStringUni(AnswerBytes);\r\n\t\t}\r\n\r\n#endif\r\n\r\n\t\t/// <summary>\r\n\t\t/// Reads the PE header timestamp from the specified file to determine its build time; not valid for deterministic builds.\r\n\t\t/// </summary>\r\n\t\t/// <remarks>\r\n\t\t/// The C# compiler (Roslyn) supports deterministic builds since Visual Studio 2015.\r\n\t\t/// This means that compiling assemblies under the same conditions (permalink)\r\n\t\t/// would produce byte-for-byte equivalent binaries.\r\n\t\t/// </remarks>\r\n\t\tpublic static DateTime GetBuildDateTime(string filePath)\r\n\t\t{\r\n\t\t\t// Constants related to the Windows PE file format.\r\n\t\t\tconst int PE_HEADER_OFFSET = 60; // 0x3C\r\n\t\t\tconst int LINKER_TIMESTAMP_OFFSET = 8;\r\n\t\t\t// Read header from file\r\n\t\t\tbyte[] b = new byte[2048];\r\n\t\t\tStream s = null;\r\n\t\t\ttry\r\n\t\t\t{\r\n\t\t\t\ts = new FileStream(filePath, FileMode.Open, FileAccess.Read);\r\n\t\t\t\tvar bytesToRead = (int)Math.Min(s.Length, b.Length);\r\n#if NET7_0_OR_GREATER\r\n\t\t\t\ts.ReadExactly(b, 0, bytesToRead);\r\n#else\r\n\t\t\t\ts.Read(b, 0, bytesToRead);\r\n#endif\r\n\t\t\t}\r\n\t\t\tfinally\r\n\t\t\t{\r\n\t\t\t\tif (s != null)\r\n\t\t\t\t\ts.Close();\r\n\t\t\t}\r\n\t\t\t// Read the linker TimeStamp\r\n\t\t\tvar offset = BitConverter.ToInt32(b, PE_HEADER_OFFSET);\r\n\t\t\tvar secondsSince1970 = BitConverter.ToInt32(b, offset + LINKER_TIMESTAMP_OFFSET);\r\n\t\t\tvar dt = GetDateTime(secondsSince1970);\r\n\t\t\treturn dt;\r\n\t\t}\r\n\r\n\t\t/// <summary>\r\n\t\t/// Reads build time from an assembly, using embedded resource fallback; requires workaround for deterministic builds.\r\n\t\t/// </summary>\r\n\t\t/// <remarks>\r\n\t\t/// You have two options:\r\n\t\t/// \r\n\t\t/// Option 1: Disable Deterministic build by adding\r\n\t\t/// \r\n\t\t///      &lt;Deterministic&gt;False&lt;/Deterministic&gt; inside a &lt;PropertyGroup&gt; section of .csproj\r\n\t\t///\r\n\t\t/// Option 2:\r\n\t\t/// \r\n\t\t///     Create \"Resources\\BuildDate.txt\" and set its \"Build Action: Embedded Resource\"\r\n\t\t///     Add to pre-build event to work with latest .NET builds:\r\n\t\t///\r\n\t\t///     PowerShell.exe -Command \"New-Item -ItemType Directory -Force -Path \\\"$(ProjectDir)Resources\\\" | Out-Null\"\r\n\t\t///     PowerShell.exe -Command \"(Get-Date).ToString(\\\"o\\\") | Out-File \\\"$(ProjectDir)Resources\\BuildDate.txt\\\"\"\r\n\t\t///\r\n\t\t/// Note:\r\n\t\t/// The C# compiler (Roslyn) supports deterministic builds since Visual Studio 2015.\r\n\t\t/// This means that compiling assemblies under the same conditions (permalink)\r\n\t\t/// would produce byte-for-byte equivalent binaries.\r\n\t\t/// </remarks>\r\n\t\tpublic static DateTime GetBuildDateTime(Assembly assembly, TimeZoneInfo tzi = null)\r\n\t\t{\r\n\t\t\tif (assembly is null)\r\n\t\t\t\tthrow new ArgumentNullException(nameof(assembly));\r\n\t\t\tvar names = assembly.GetManifestResourceNames();\r\n\t\t\tvar dt = default(DateTime);\r\n\t\t\tforeach (var name in names)\r\n\t\t\t{\r\n\t\t\t\tif (!name.EndsWith(\"BuildDate.txt\"))\r\n\t\t\t\t\tcontinue;\r\n\t\t\t\tvar stream = assembly.GetManifestResourceStream(name);\r\n\t\t\t\tusing (var reader = new StreamReader(stream))\r\n\t\t\t\t{\r\n\t\t\t\t\tvar date = reader.ReadToEnd();\r\n\t\t\t\t\tdt = DateTime.Parse(date);\r\n\t\t\t\t\tdt = TimeZoneInfo.ConvertTime(dt, tzi ?? TimeZoneInfo.Local);\r\n\t\t\t\t\treturn dt;\r\n\t\t\t\t}\r\n\t\t\t}\r\n#if NETFRAMEWORK // .NET Framework\r\n\r\n\t\t\t// Constants related to the Windows PE file format.\r\n\t\t\tconst int PE_HEADER_OFFSET = 60;\r\n\t\t\tconst int LINKER_TIMESTAMP_OFFSET = 8;\r\n\t\t\t// Discover the base memory address where our assembly is loaded\r\n\t\t\tvar entryModule = assembly.ManifestModule;\r\n\t\t\tvar hMod = Marshal.GetHINSTANCE(entryModule);\r\n\t\t\tif (hMod == IntPtr.Zero - 1)\r\n\t\t\t\tthrow new Exception(\"Failed to get HINSTANCE.\");\r\n\t\t\t// Read the linker TimeStamp\r\n\t\t\tvar offset = Marshal.ReadInt32(hMod, PE_HEADER_OFFSET);\r\n\t\t\tvar secondsSince1970 = Marshal.ReadInt32(hMod, offset + LINKER_TIMESTAMP_OFFSET);\r\n\t\t\tdt = GetDateTime(secondsSince1970);\r\n#endif\r\n\t\t\treturn dt;\r\n\t\t}\r\n\r\n\t\t/// <summary>\r\n\t\t/// Converts seconds since Unix epoch to a DateTime in the specified timezone.\r\n\t\t/// </summary>\r\n\t\tstatic DateTime GetDateTime(int secondsSince1970, TimeZoneInfo tzi = null)\r\n\t\t{\r\n\t\t\tvar epoch = new DateTime(1970, 1, 1, 0, 0, 0, DateTimeKind.Utc);\r\n\t\t\tvar linkTimeUtc = epoch.AddSeconds(secondsSince1970);\r\n\t\t\treturn TimeZoneInfo.ConvertTimeFromUtc(linkTimeUtc, tzi ?? TimeZoneInfo.Local);\r\n\t\t}\r\n\r\n\t\t/// <summary>\r\n\t\t/// Gets the unescaped local file path of the loaded assembly.\r\n\t\t/// </summary>\r\n\t\tpublic string AssemblyPath\r\n\t\t{\r\n\t\t\tget\r\n\t\t\t{\r\n\t\t\t\tvar codeBase = Assembly.Location;\r\n\t\t\t\tif (string.IsNullOrEmpty(codeBase))\r\n\t\t\t\t\treturn codeBase;\r\n\t\t\t\tvar uri = new UriBuilder(codeBase);\r\n\t\t\t\tvar path = Uri.UnescapeDataString(uri.Path);\r\n\t\t\t\treturn path;\r\n\t\t\t}\r\n\t\t}\r\n\r\n\t\tpublic string AssemblyFullName { get { return Assembly.GetName().FullName.ToString(); } }\r\n\t\tpublic string AssemblyName { get { return Assembly.GetName().Name.ToString(); } }\r\n\t\tpublic string CodeBase { get { return Assembly.Location; } }\r\n\r\n\t\tpublic string Company { get { return GetAttribute<AssemblyCompanyAttribute>(a => a.Company); } }\r\n\t\tpublic string Product { get { return GetAttribute<AssemblyProductAttribute>(a => a.Product); } }\r\n\t\tpublic string Copyright { get { return GetAttribute<AssemblyCopyrightAttribute>(a => a.Copyright); } }\r\n\t\tpublic string Trademark { get { return GetAttribute<AssemblyTrademarkAttribute>(a => a.Trademark); } }\r\n\t\tpublic string Title { get { return GetAttribute<AssemblyTitleAttribute>(a => a.Title); } }\r\n\t\tpublic string Description { get { return GetAttribute<AssemblyDescriptionAttribute>(a => a.Description); } }\r\n\t\tpublic string Configuration { get { return GetAttribute<AssemblyConfigurationAttribute>(a => a.Configuration); } }\r\n\t\tpublic string FileVersion { get { return GetAttribute<AssemblyFileVersionAttribute>(a => a.Version); } }\r\n\t\tpublic string ProductGuid { get { return GetAttribute<GuidAttribute>(a => a.Value); } }\r\n\r\n\t\tpublic Version Version { get { return Assembly.GetName().Version; } }\r\n\r\n\t\tstring GetAttribute<T>(Func<T, string> value) where T : Attribute\r\n\t\t{\r\n\t\t\tT attribute = (T)Attribute.GetCustomAttribute(Assembly, typeof(T));\r\n\t\t\treturn attribute is null\r\n\t\t\t\t? \"\"\r\n\t\t\t\t: value.Invoke(attribute);\r\n\t\t}\r\n\r\n\t\t/// <summary>\r\n\t\t/// Constructs a path under user or common application data folder for this assembly's Company/Product and optional file name.\r\n\t\t/// </summary>\r\n\t\tpublic string GetAppDataPath(bool userLevel = false, string format = \"\", params object[] args)\r\n\t\t{\r\n\t\t\t// Get writable application folder.\r\n\t\t\tvar specialFolder = userLevel\r\n\t\t\t\t? Environment.SpecialFolder.ApplicationData\r\n\t\t\t\t: Environment.SpecialFolder.CommonApplicationData;\r\n\t\t\tvar folder = string.Format(\"{0}\\\\{1}\\\\{2}\",\r\n\t\t\t\tEnvironment.GetFolderPath(specialFolder),\r\n\t\t\t\tCompany,\r\n\t\t\t\tProduct);\r\n\t\t\t// Get file name.\r\n\t\t\tvar file = string.Format(format, args);\r\n\t\t\tvar path = Path.Combine(folder, file);\r\n\t\t\treturn path;\r\n\t\t}\r\n\r\n\t\t/// <summary>\r\n\t\t/// Creates a FileInfo for a file in the application data directory for this assembly.\r\n\t\t/// </summary>\r\n\t\tpublic FileInfo GetAppDataFile(bool userLevel = false, string format = \"\", params object[] args)\r\n\t\t{\r\n\t\t\tvar path = GetAppDataPath(userLevel, format, args);\r\n\t\t\treturn new FileInfo(path);\r\n\t\t}\r\n\r\n\t}\r\n}\r\n"
  },
  {
    "path": "FocusLogger/JocysCom/Configuration/ISettingsData.cs",
    "content": "using System;\r\nusing System.ComponentModel;\r\nusing System.IO;\r\n\r\nnamespace JocysCom.ClassLibrary.Configuration\r\n{\r\n\t/// <summary>Contract for managing settings data persisted as XML, including reset, load, save, and individual file operations.</summary>\r\n\tpublic interface ISettingsData\r\n\t{\r\n\t\t/// <summary>Reset settings data to default values.</summary>\r\n\t\t/// <returns>True if operation was successful; otherwise, False.</returns>\r\n\t\tbool ResetToDefault();\r\n\r\n\t\t/// <summary>Save current settings data via XML serialization to the default file location.</summary>\r\n\t\tvoid Save(object[] items = null);\r\n\r\n\t\t/// <summary>Save current settings data via XML serialization to the specified file.</summary>\r\n\t\t/// <param name=\"fileName\">Destination file name or path.</param>\r\n\t\tvoid SaveAs(string fileName, object[] items = null);\r\n\r\n\t\t/// <summary>Load settings data from the default XML file.</summary>\r\n\t\tvoid Load();\r\n\r\n\t\t/// <summary>Load settings data from the specified XML file.</summary>\r\n\t\t/// <param name=\"fileName\">Source file name or path.</param>\r\n\t\tvoid LoadFrom(string fileName);\r\n\r\n\t\t/// <summary>FileInfo for the XML file that stores settings data.</summary>\r\n\t\tFileInfo XmlFile { get; }\r\n\r\n\t\t/// <summary>Collection of settings items for data binding.</summary>\r\n\t\tIBindingList Items { get; }\r\n\r\n\t\t/// <summary>Raised when a file associated with the settings data changes.</summary>\r\n\t\tevent EventHandler FilesChanged;\r\n\r\n\t\t/// <summary>Indicates whether there are pending save operations.</summary>\r\n\t\tbool IsSavePending { get; set; }\r\n\r\n\t\t/// <summary>Delete the specified settings item file and update settings data accordingly.</summary>\r\n\t\t/// <param name=\"itemFile\">Settings item file to delete.</param>\r\n\t\t/// <returns>A message indicating the result of the delete operation.</returns>\r\n\t\tstring DeleteItem(ISettingsFileItem itemFile);\r\n\r\n\t\t/// <summary>Rename the specified settings item file and update settings data and file system accordingly.</summary>\r\n\t\t/// <param name=\"itemFile\">Settings item file to rename.</param>\r\n\t\t/// <param name=\"newName\">New name for the settings item file.</param>\r\n\t\t/// <returns>A message indicating the outcome of the rename operation.</returns>\r\n\t\tstring RenameItem(ISettingsFileItem itemFile, string newName);\r\n\t}\r\n}"
  },
  {
    "path": "FocusLogger/JocysCom/Configuration/ISettingsFileItem.cs",
    "content": "using System;\r\nusing System.ComponentModel;\r\nusing System.Xml.Serialization;\r\n\r\nnamespace JocysCom.ClassLibrary.Configuration\r\n{\r\n\t/// <summary>File-based ISettingsItem: exposes Path, Name, BaseName, WriteTime, and IsReadOnlyFile.</summary>\r\n\tpublic interface ISettingsFileItem : ISettingsItem\r\n\t{\r\n\t\t/// <summary>Relative directory path of the settings file.</summary>\r\n\t\t[DefaultValue(null)]\r\n\t\tstring Path { get; set; }\r\n\r\n\t\t/// <summary>File name of the settings file without extension.</summary>\r\n\t\t[DefaultValue(null)]\r\n\t\tstring Name { get; set; }\r\n\r\n\t\t/// <summary>Internal base name (without extension) for identifying the settings file.</summary>\r\n\t\t[DefaultValue(null)]\r\n\t\tstring BaseName { get; set; }\r\n\r\n\t\t/// <summary>Timestamp of the last write to the settings file, for change tracking.</summary>\r\n\t\tDateTime WriteTime { get; set; }\r\n\r\n\t\t/// <summary>Excludes this item from being saved to a separate file when true.</summary>\r\n\t\t[XmlIgnore, DefaultValue(false)]\r\n\t\tbool IsReadOnlyFile { get; set; }\r\n\r\n\t\t/* Example:\r\n \r\n\t\t#region ■ ISettingsItemFile\r\n\r\n\t\t[XmlIgnore]\r\n\t\tstring ISettingsItemFile.BaseName { get => Name; set => Name = value; }\r\n\r\n\t\t[XmlIgnore]\r\n\t\tDateTime ISettingsItemFile.WriteTime { get; set; }\r\n\r\n\t\t#endregion\r\n\r\n\t\tprotected void OnPropertyChanged([CallerMemberName] string propertyName = null)\r\n\t\t{\r\n\t\t\t((ISettingsItemFile)this).WriteTime = DateTime.Now;\r\n\t\t}\r\n\r\n\t\t*/\r\n\t}\r\n}"
  },
  {
    "path": "FocusLogger/JocysCom/Configuration/ISettingsItem.cs",
    "content": "using System.ComponentModel;\r\n\r\nnamespace JocysCom.ClassLibrary.Configuration\r\n{\r\n\t\r\n\t/// <summary>Defines a settings item that notifies on property changes and indicates whether it is enabled or empty.</summary>\r\n\tpublic interface ISettingsItem : INotifyPropertyChanged\r\n\t{\r\n\t\t/// <summary>Indicates whether the item is enabled. Implementers must raise PropertyChanged when this value changes.</summary>\r\n\t\tbool IsEnabled { get; set; }\r\n\r\n\t\t/// <summary>Indicates whether the item is uninitialized or contains no data.</summary>\r\n\t\tbool IsEmpty { get; }\r\n\t}\r\n}"
  },
  {
    "path": "FocusLogger/JocysCom/Configuration/ISettingsListFileItem.cs",
    "content": "using System;\r\nusing System.Windows.Media;\r\n\r\nnamespace JocysCom.ClassLibrary.Configuration\r\n{\r\n\t/// <summary>Defines a settings file item for list-based user interfaces with selection, status, and icon serialization.</summary>\r\n\t/// <remarks>\r\n\t/// Provides grouping metadata properties for UI grouping support:\r\n\t/// ListGroupTimeSortKey, ListGroupPathSortKey, ListGroupNameSortKey,\r\n\t/// ListGroupTime, ListGroupPath, and ListGroupName.\r\n\t/// </remarks>\r\n\tpublic interface ISettingsListFileItem : ISettingsFileItem\r\n\t{\r\n\t\tbool IsChecked { get; set; }\r\n\t\tstring StatusText { get; set; }\r\n\t\tSystem.Windows.MessageBoxImage StatusCode { get; set; }\r\n\t\tDrawingImage Icon { get; }\r\n\r\n\t\t/// <summary>Icon file extension or type (e.g. \".svg\").</summary>\r\n\t\tstring IconType { get; set; }\r\n\r\n\t\t/// <summary>Base64-encoded icon data.</summary>\r\n\t\tstring IconData { get; set; }\r\n\r\n\t\t/// <summary>Sets the icon data by encoding the provided contents as base64.</summary>\r\n\t\t/// <param name=\"contents\">Raw icon content (e.g. SVG).</param>\r\n\t\t/// <param name=\"type\">File extension/type (default \".svg\").</param>\r\n\t\tvoid SetIcon(string contents, string type = \".svg\");\r\n\r\n\t\t// List Properties.\r\n\t\tbool IsPinned { get; set; }\r\n\t\tDateTime? Created { get; set; }\r\n\t\tDateTime? Modified { get; set; }\r\n\t\tint ListGroupTimeSortKey { get; }\r\n\t\tstring ListGroupPathSortKey { get; }\r\n\t\tstring ListGroupNameSortKey { get; }\r\n\t\tstring ListGroupTime { get; }\r\n\t\tstring ListGroupPath { get; }\r\n\t\tstring ListGroupName { get; }\r\n\t}\r\n}"
  },
  {
    "path": "FocusLogger/JocysCom/Configuration/SettingsData.cs",
    "content": "﻿using JocysCom.ClassLibrary.Collections;\r\nusing JocysCom.ClassLibrary.ComponentModel;\r\nusing JocysCom.ClassLibrary.Runtime;\r\nusing System;\r\nusing System.Collections.Generic;\r\nusing System.ComponentModel;\r\nusing System.Diagnostics;\r\nusing System.IO;\r\nusing System.Linq;\r\nusing System.Reflection;\r\nusing System.Runtime.Serialization;\r\nusing System.Text;\r\nusing System.Text.Json.Serialization;\r\nusing System.Windows;\r\nusing System.Xml;\r\nusing System.Xml.Serialization;\r\n\r\nnamespace JocysCom.ClassLibrary.Configuration\r\n{\r\n\t/// <summary>\r\n\t/// Represents a container for managing settings data T.\r\n\t/// Enables saving and loading of settings either as a single or multiple XML files.\r\n\t/// </summary>\r\n\t/// <typeparam name=\"T\">The type of settings data this container will manage.</typeparam>\r\n\t[Serializable, XmlRoot(\"Data\"), DataContract]\r\n\tpublic class SettingsData<T> : ISettingsData\r\n\t{\r\n\t\t/// <summary>\r\n\t\t/// Initializes a new instance of the SettingsData class with default settings.\r\n\t\t/// </summary>\r\n\t\tpublic SettingsData()\r\n\t\t{\r\n\t\t\tInitialize(null, false, null, null);\r\n\t\t}\r\n\r\n\r\n\t\t/// <summary>\r\n\t\t/// Initializes a new instance of the SettingsData class with specific settings.\r\n\t\t/// </summary>\r\n\t\t/// <param name=\"overrideFileName\">Specifies a custom file name for the settings file. If null, a default name based on the type T is used.</param>\r\n\t\t/// <param name=\"userLevel\">Determines the storage location of the XML settings file. True to use user-specific storage, False for common storage, Null for executable directory.</param>\r\n\t\t/// <param name=\"comment\">A comment to include within the XML settings file.</param>\r\n\t\t/// <param name=\"assembly\">The assembly to use for retrieving default company and product name for folder path generation.</param>\r\n\t\t/// <remarks>\r\n\t\t/// userLevel param defines where to store XML settings file:\r\n\t\t///   True  - Environment.SpecialFolder.ApplicationData\r\n\t\t///   False - Environment.SpecialFolder.CommonApplicationData\r\n\t\t///   Null  - Use ./{ExecutableBaseName}.xml settings file\r\n\t\t/// </remarks>\r\n\t\tpublic SettingsData(string overrideFileName = null, bool? userLevel = false, string comment = null, Assembly assembly = null)\r\n\t\t{\r\n\t\t\tInitialize(overrideFileName, userLevel, comment, assembly);\r\n\t\t}\r\n\r\n\t\tprivate List<Assembly> _Assemblies;\r\n\t\tprivate string _Company;\r\n\t\tprivate string _Product;\r\n\r\n\t\tprivate string GetAppDataPath(bool userLevel = false)\r\n\t\t{\r\n\t\t\tvar mainAssembly = _Assemblies.First();\r\n\t\t\t_Company = ((AssemblyCompanyAttribute)Attribute.GetCustomAttribute(mainAssembly, typeof(AssemblyCompanyAttribute))).Company;\r\n\t\t\t_Product = ((AssemblyProductAttribute)Attribute.GetCustomAttribute(mainAssembly, typeof(AssemblyProductAttribute))).Product;\r\n\t\t\t// Get writable application folder.\r\n\t\t\tvar specialFolder = userLevel\r\n\t\t\t\t? Environment.SpecialFolder.ApplicationData\r\n\t\t\t\t: Environment.SpecialFolder.CommonApplicationData;\r\n\t\t\tvar path = string.Format(\"{0}\\\\{1}\\\\{2}\", Environment.GetFolderPath(specialFolder), _Company, _Product);\r\n\t\t\treturn path;\r\n\t\t}\r\n\r\n\t\t/// <summary>\r\n\t\t/// Initialize class.\r\n\t\t/// </summary>\r\n\t\tprivate void Initialize(string overrideFileName, bool? userLevel, string comment, Assembly assembly)\r\n\t\t{\r\n\t\t\t// Wraps all methods into lock.\r\n\t\t\t//var items = System.Collections.ArrayList.Synchronized(Items);\r\n\t\t\tItems = new SortableBindingList<T>();\r\n\t\t\t_Comment = comment;\r\n\t\t\t// Get assemblies which will be used to select default (fists) and search for resources.\r\n\t\t\t_Assemblies = new List<Assembly>{\r\n\t\t\t\tassembly,\r\n\t\t\t\tAssembly.GetEntryAssembly(),\r\n\t\t\t\tAssembly.GetExecutingAssembly(),\r\n\t\t\t}.Where(x => x != null)\r\n\t\t\t.Distinct()\r\n\t\t\t.ToList();\r\n\t\t\tstring folder;\r\n\t\t\tstring fileBaseName;\r\n\t\t\t// Check if there is a folder with the same name as executable.\r\n\t\t\tfolder = GetLocalSettingsDirectory();\r\n\t\t\tif (userLevel.HasValue)\r\n\t\t\t{\r\n\t\t\t\tif (string.IsNullOrEmpty(folder))\r\n\t\t\t\t\tfolder = GetAppDataPath(userLevel.Value);\r\n\t\t\t\tfileBaseName = typeof(T).Name;\r\n\t\t\t}\r\n\t\t\telse\r\n\t\t\t{\r\n\t\t\t\tvar fullName = System.Diagnostics.Process.GetCurrentProcess().MainModule.FileName;\r\n\t\t\t\tif (string.IsNullOrEmpty(folder))\r\n\t\t\t\t\tfolder = System.IO.Path.GetDirectoryName(fullName);\r\n\t\t\t\tfileBaseName = System.IO.Path.GetFileNameWithoutExtension(fullName);\r\n\t\t\t}\r\n\t\t\tstring fileName = fileBaseName + \".xml\";\r\n\t\t\t// If override file name is set then override the file name.\r\n\t\t\tif (!string.IsNullOrEmpty(overrideFileName))\r\n\t\t\t\tfileName = overrideFileName;\r\n\t\t\tvar path = Path.Combine(folder, fileName);\r\n\t\t\t_XmlFile = new FileInfo(path);\r\n\t\t}\r\n\r\n\t\t/// <summary>\r\n\t\t/// Retrieves the directory path used for storing local settings, typically named after the executable.\r\n\t\t/// </summary>\r\n\t\t/// <returns>The directory path or null if the directory does not exist.</returns>\r\n\t\tpublic string GetLocalSettingsDirectory()\r\n\t\t{\r\n\t\t\tvar moduleFileName = System.Diagnostics.Process.GetCurrentProcess().MainModule.FileName;\r\n\t\t\tvar fi = new FileInfo(moduleFileName);\r\n\t\t\tvar settingsFolderName = System.IO.Path.GetFileNameWithoutExtension(fi.Name);\r\n\t\t\t// Parse command line settings.\r\n\t\t\tvar args = Environment.GetCommandLineArgs();\r\n\t\t\tvar ic = new JocysCom.ClassLibrary.Configuration.Arguments(args);\r\n\t\t\t// ------------------------------------------------\r\n\t\t\tif (ic.ContainsKey(\"SettingsPath\"))\r\n\t\t\t{\r\n\t\t\t\tvar settingsPath = ic[\"SettingsPath\"];\r\n\t\t\t\tif (!string.IsNullOrEmpty(settingsPath))\r\n\t\t\t\t{\r\n\t\t\t\t\tvar sdi = new DirectoryInfo(settingsPath);\r\n\t\t\t\t\tif (sdi.Exists)\r\n\t\t\t\t\t\treturn sdi.FullName;\r\n\t\t\t\t}\r\n\t\t\t}\r\n\t\t\t// Check if folder with settings exists in the same folder as executable.\r\n\t\t\tvar path = Path.Combine(fi.Directory.FullName, settingsFolderName);\r\n\t\t\tvar di = new DirectoryInfo(path);\r\n\t\t\tif (di.Exists)\r\n\t\t\t\treturn di.FullName;\r\n\t\t\treturn null;\r\n\t\t}\r\n\r\n\r\n\t\t/// <summary>\r\n\t\t/// Indicates whether saving the settings is pending. This can be used to optimize write operations by delaying them until necessary.\r\n\t\t/// </summary>\r\n\t\t[XmlIgnore, JsonIgnore]\r\n\t\tpublic bool IsSavePending { get; set; }\r\n\r\n\t\t/// <summary>\r\n\t\t/// Indicates whether loading the settings is pending. Useful for deferring the loading operation until it's required.\r\n\t\t/// </summary>\r\n\t\t[XmlIgnore, JsonIgnore]\r\n\t\tpublic bool IsLoadPending { get; set; }\r\n\r\n\t\t/// <summary>\r\n\t\t/// Determines whether settings are stored in separate files.\r\n\t\t/// </summary>\r\n\t\t[XmlIgnore, JsonIgnore]\r\n\t\tpublic bool UseSeparateFiles { get; set; }\r\n\r\n\t\t/// <summary>\r\n\t\t/// Gets or sets the FileInfo object for the XML file that stores the settings data.\r\n\t\t/// </summary>\r\n\t\t[XmlIgnore, JsonIgnore]\r\n\t\tpublic FileInfo XmlFile { get { return _XmlFile; } set { _XmlFile = value; } }\r\n\r\n\t\t[NonSerialized]\r\n\t\tprotected FileInfo _XmlFile;\r\n\r\n\t\t[NonSerialized]\r\n\t\tprotected string _Comment;\r\n\r\n\t\t/// <summary>\r\n\t\t/// A list of settings items managed by this instance.\r\n\t\t/// </summary>\r\n\t\t[DataMember]\r\n\t\tpublic SortableBindingList<T> Items { get; set; }\r\n\r\n\t\t[NonSerialized]\r\n\t\tprivate object _SyncRoot;\r\n\r\n\t\t/// <summary>\r\n\t\t/// Synchronization root object for thread-safe operations.\r\n\t\t/// </summary>\r\n\t\tpublic virtual object SyncRoot\r\n\t\t{\r\n\t\t\tget\r\n\t\t\t{\r\n\t\t\t\tif (_SyncRoot is null)\r\n\t\t\t\t\tSystem.Threading.Interlocked.CompareExchange<object>(ref _SyncRoot, new object(), null);\r\n\t\t\t\treturn _SyncRoot;\r\n\t\t\t}\r\n\t\t}\r\n\r\n\t\t/// <summary>\r\n\t\t/// Converts the items in the collection to an array and returns it.\r\n\t\t/// This operation is synchronized to prevent data inconsistency during the conversion.\r\n\t\t/// </summary>\r\n\t\t/// <returns>An array of items.</returns>\r\n\t\tpublic T[] ItemsToArraySynchronized()\r\n\t\t{\r\n\t\t\tlock (SyncRoot)\r\n\t\t\t\treturn Items.ToArray();\r\n\t\t}\r\n\r\n\t\t[XmlIgnore, JsonIgnore]\r\n\t\tIBindingList ISettingsData.Items { get { return Items; } }\r\n\r\n\t\tpublic delegate void ApplyOrderDelegate(SettingsData<T> source);\r\n\r\n\t\t[XmlIgnore, JsonIgnore, NonSerialized]\r\n\t\tpublic ApplyOrderDelegate ApplyOrder;\r\n\r\n\t\t/// <summary>\r\n\t\t/// File Version.\r\n\t\t/// </summary>\r\n\t\t[XmlAttribute]\r\n\t\tpublic int Version { get; set; }\r\n\r\n\t\t[XmlIgnore, JsonIgnore, NonSerialized]\r\n\t\tobject saveReadFileLock = new object();\r\n\r\n\t\t/// <summary>\r\n\t\t/// Occurs when the settings data is about to be saved to the XML file, allowing for pre-save operations or validation.\r\n\t\t/// </summary>\r\n\t\tpublic event EventHandler Saving;\r\n\r\n\t\t/// <summary>\r\n\t\t/// Saves the current settings into an XML file at the specified path. Compresses the file if the file extension is .gz.\r\n\t\t/// </summary>\r\n\t\t/// <param name=\"path\">The file path where the settings will be saved.</param>\r\n\t\t/// <param name=\"items\">Specific items to save. If null then save all items.</param>\r\n\t\tpublic void SaveAs(string path, object[] items = null)\r\n\t\t{\r\n\t\t\tSetFileMonitoring(false);\r\n\t\t\tvar ev = Saving;\r\n\t\t\tif (ev != null)\r\n\t\t\t\tev(this, new EventArgs());\r\n\t\t\tvar tItems = items?.Cast<T>().ToArray() ?? ItemsToArraySynchronized();\r\n\t\t\tlock (saveReadFileLock)\r\n\t\t\t{\r\n\t\t\t\t// Remove unique primary keys.\r\n\t\t\t\tvar type = tItems.FirstOrDefault()?.GetType();\r\n\t\t\t\tif (type != null && type.Name.EndsWith(\"EntityObject\"))\r\n\t\t\t\t{\r\n\t\t\t\t\tvar pi = type.GetProperty(\"EntityKey\");\r\n\t\t\t\t\tfor (int i = 0; i < tItems.Length; i++)\r\n\t\t\t\t\t\tpi.SetValue(tItems[i], null);\r\n\t\t\t\t}\r\n\t\t\t\tvar fi = new FileInfo(path);\r\n\t\t\t\tvar compress = fi.Name.EndsWith(\".gz\", StringComparison.OrdinalIgnoreCase);\r\n\t\t\t\t// If each item will be saved to a separate file.\r\n\t\t\t\tif (UseSeparateFiles)\r\n\t\t\t\t{\r\n\t\t\t\t\tvar di = GetRootDirectory(fi);\r\n\t\t\t\t\t// Create directory because it will be watched for changes.\r\n\t\t\t\t\tif (!di.Exists)\r\n\t\t\t\t\t\tdi.Create();\r\n\t\t\t\t\tfor (int i = 0; i < tItems.Length; i++)\r\n\t\t\t\t\t{\r\n\t\t\t\t\t\tvar fileItem = (ISettingsFileItem)tItems[i];\r\n\t\t\t\t\t\tif (fileItem.IsReadOnlyFile)\r\n\t\t\t\t\t\t\tcontinue;\r\n\t\t\t\t\t\tvar fileFullName = GetFileItemFullName(path, fileItem);\r\n\t\t\t\t\t\tvar fiItem = new FileInfo(fileFullName);\r\n\t\t\t\t\t\tif (!fiItem.Directory.Exists)\r\n\t\t\t\t\t\t\tfiItem.Directory.Create();\r\n\t\t\t\t\t\tvar bytes = Serialize(fileItem);\r\n\t\t\t\t\t\tif (compress)\r\n\t\t\t\t\t\t\tbytes = SettingsHelper.Compress(bytes);\r\n\t\t\t\t\t\tif (!AllowWriteFile(fiItem))\r\n\t\t\t\t\t\t\tcontinue;\r\n\t\t\t\t\t\tif (!SettingsHelper.WriteIfDifferent(fileFullName, bytes))\r\n\t\t\t\t\t\t\tcontinue;\r\n\t\t\t\t\t\tfi.Refresh();\r\n\t\t\t\t\t\tfileItem.WriteTime = new FileInfo(fileFullName).LastWriteTime;\r\n\t\t\t\t\t\t// Update last write time.\r\n\t\t\t\t\t\tfiItem.Refresh();\r\n\t\t\t\t\t\tSetLastWriteTime(fiItem);\r\n\t\t\t\t\t}\r\n\t\t\t\t}\r\n\t\t\t\telse\r\n\t\t\t\t{\r\n\t\t\t\t\tif (!fi.Directory.Exists)\r\n\t\t\t\t\t\tfi.Directory.Create();\r\n\t\t\t\t\tvar bytes = Serialize(this);\r\n\t\t\t\t\tif (compress)\r\n\t\t\t\t\t\tbytes = SettingsHelper.Compress(bytes);\r\n\t\t\t\t\tif (AllowWriteFile(fi))\r\n\t\t\t\t\t{\r\n\t\t\t\t\t\tif (SettingsHelper.WriteIfDifferent(fi.FullName, bytes))\r\n\t\t\t\t\t\t{\r\n\t\t\t\t\t\t\t// Update last write time.\r\n\t\t\t\t\t\t\tfi.Refresh();\r\n\t\t\t\t\t\t\tSetLastWriteTime(fi);\r\n\t\t\t\t\t\t}\r\n\t\t\t\t\t}\r\n\t\t\t\t}\r\n\t\t\t}\r\n\t\t\tIsSavePending = false;\r\n\t\t\tSetFileMonitoring(true);\r\n\t\t}\r\n\r\n\t\tpublic static string RemoveInvalidPathChars(string name)\r\n\t\t{\r\n\t\t\tvar invalidChars = Path.GetInvalidPathChars();\r\n\t\t\treturn new string(name.Where(c => !invalidChars.Contains(c)).ToArray());\r\n\t\t}\r\n\r\n\t\tpublic static string RemoveInvalidFileNameChars(string name)\r\n\t\t{\r\n\t\t\tvar invalidChars = Path.GetInvalidFileNameChars();\r\n\t\t\treturn new string(name.Where(c => !invalidChars.Contains(c)).ToArray());\r\n\t\t}\r\n\r\n\t\t/// <summary>\r\n\t\t/// Save items.\r\n\t\t/// </summary>\r\n\t\t/// <param name=\"items\"> If items is null then save all items.</param>\r\n\t\tpublic void Save(object[] items = null)\r\n\t\t{\r\n\t\t\tSaveAs(_XmlFile.FullName, items);\r\n\t\t}\r\n\r\n\t\t/// <summary>\r\n\t\t/// Settings root directory.\r\n\t\t/// </summary>\r\n\t\tpublic DirectoryInfo RootDirectory =>\r\n\t\t\tUseSeparateFiles\r\n\t\t\t\t? GetRootDirectory(_XmlFile)\r\n\t\t\t\t: _XmlFile.Directory;\r\n\r\n\t\t/// <summary>\r\n\t\t/// Adds an array of items to the current collection of settings data.\r\n\t\t/// </summary>\r\n\t\t/// <param name=\"items\">The array of items to add.</param>\r\n\t\tpublic void Add(params T[] items)\r\n\t\t{\r\n\t\t\tlock (SyncRoot)\r\n\t\t\t\tforeach (var item in items)\r\n\t\t\t\t\tItems.Add(item);\r\n\t\t}\r\n\r\n\t\t/// <summary>\r\n\t\t/// Removes an array of items from the current collection of settings data.\r\n\t\t/// </summary>\r\n\t\t/// <param name=\"items\">The array of items to remove.</param>\r\n\t\tpublic void Remove(params T[] items)\r\n\t\t{\r\n\t\t\tlock (SyncRoot)\r\n\t\t\t\tforeach (var item in items)\r\n\t\t\t\t\tItems.Remove(item);\r\n\t\t}\r\n\r\n\t\tpublic class SettingsDataEventArgs : EventArgs\r\n\t\t{\r\n\t\t\tpublic SettingsDataEventArgs(IList<T> items)\r\n\t\t\t{\r\n\t\t\t\tItems = items;\r\n\t\t\t}\r\n\t\t\tpublic IList<T> Items { get; }\r\n\t\t\tpublic bool Handled { get; set; }\r\n\t\t}\r\n\r\n\t\tpublic class ItemPropertyChangedEventArgs : PropertyChangedEventArgs\r\n\t\t{\r\n\t\t\t/// <summary>The old value of the property.</summary>\r\n\t\t\tpublic object OldValue { get; }\r\n\t\t\t/// <summary>The new value of the property.</summary>\r\n\t\t\tpublic object NewValue { get; }\r\n\t\t\tpublic ItemPropertyChangedEventArgs(string propertyName, object oldValue, object newValue)\r\n\t\t\t\t: base(propertyName)\r\n\t\t\t{\r\n\t\t\t\tOldValue = oldValue;\r\n\t\t\t\tNewValue = newValue;\r\n\t\t\t}\r\n\t\t}\r\n\r\n\t\tpublic delegate IList<T> ValidateDataDelegate(IList<T> items);\r\n\r\n\t\t[XmlIgnore, JsonIgnore, NonSerialized]\r\n\t\tpublic ValidateDataDelegate ValidateData;\r\n\r\n\t\t/// <summary>\r\n\t\t/// Occurs when data validation is required, providing an opportunity to perform custom validation logic on settings data.\r\n\t\t/// </summary>\r\n\t\tpublic event EventHandler<SettingsDataEventArgs> OnValidateData;\r\n\r\n\t\t#region Last Write Time\r\n\r\n\t\t[XmlIgnore, JsonIgnore]\r\n\t\tpublic bool PreventWriteToNewerFiles { get; set; } = true;\r\n\r\n\t\t[XmlIgnore, JsonIgnore, NonSerialized]\r\n\t\tprivate Dictionary<string, DateTime> LastWriteTimes = new Dictionary<string, DateTime>();\r\n\r\n\t\t/// <summary>\r\n\t\t/// Record the LastWriteTime when loading for later comparison when saving.\r\n\t\t/// </summary>\r\n\t\tprivate void SetLastWriteTime(FileInfo fi)\r\n\t\t{\r\n\t\t\t// If file was deleted or don't exists.\r\n\t\t\tif (!fi.Exists)\r\n\t\t\t\treturn;\r\n\t\t\tif (LastWriteTimes.ContainsKey(fi.FullName))\r\n\t\t\t\tLastWriteTimes[fi.FullName] = fi.LastWriteTime;\r\n\t\t\telse\r\n\t\t\t\tLastWriteTimes.Add(fi.FullName, fi.LastWriteTime);\r\n\t\t}\r\n\r\n\t\tprivate bool IsNewerOnDisk(FileInfo fi)\r\n\t\t{\r\n\t\t\tfi.Refresh();\r\n\t\t\t// If file was deleted or don't exists.\r\n\t\t\tif (!fi.Exists)\r\n\t\t\t\treturn false;\r\n\t\t\tif (!LastWriteTimes.ContainsKey(fi.FullName))\r\n\t\t\t\treturn false;\r\n\t\t\treturn fi.Exists && fi.LastWriteTime > LastWriteTimes[fi.FullName];\r\n\t\t}\r\n\r\n\t\tprivate bool AllowWriteFile(FileInfo fi)\r\n\t\t{\r\n\t\t\tfi.Refresh();\r\n\t\t\t// If file was deleted or don't exists.\r\n\t\t\tif (!fi.Exists)\r\n\t\t\t\treturn true;\r\n\t\t\tif (!PreventWriteToNewerFiles)\r\n\t\t\t\treturn true;\r\n\t\t\treturn !IsNewerOnDisk(fi);\r\n\t\t}\r\n\r\n\t\t#endregion\r\n\r\n\t\tpublic void Load()\r\n\t\t{\r\n\t\t\tLoadFrom(_XmlFile.FullName);\r\n\t\t}\r\n\r\n\t\tstatic DirectoryInfo GetRootDirectory(FileInfo fi)\r\n\t\t{\r\n\t\t\tvar compress = fi.Name.EndsWith(\".gz\", StringComparison.OrdinalIgnoreCase);\r\n\t\t\tvar dirName = Path.GetFileNameWithoutExtension(fi.FullName);\r\n\t\t\tif (compress)\r\n\t\t\t\tdirName = Path.GetFileNameWithoutExtension(dirName);\r\n\t\t\tvar dirPath = Path.Combine(fi.Directory.FullName, dirName);\r\n\t\t\tvar di = new DirectoryInfo(dirPath);\r\n\t\t\treturn di;\r\n\t\t}\r\n\r\n\t\t/// <summary>\r\n\t\t/// Loads settings data from an XML file specified by the fileName.\r\n\t\t/// </summary>\r\n\t\t/// <param name=\"fileName\">The file name/path from which to load settings data.</param>\r\n\t\tpublic void LoadFrom(string fileName)\r\n\t\t{\r\n\t\t\tvar settingsLoaded = false;\r\n\t\t\tvar fi = new FileInfo(fileName);\r\n\t\t\tvar di = GetRootDirectory(fi);\r\n\t\t\tvar compress = fi.Name.EndsWith(\".gz\", StringComparison.OrdinalIgnoreCase);\r\n\t\t\t// If configuration file exists then...\r\n\t\t\tif (fi.Exists || di.Exists)\r\n\t\t\t{\r\n\t\t\t\tSettingsData<T> data = null;\r\n\t\t\t\t// Try to read file until success.\r\n\t\t\t\twhile (true)\r\n\t\t\t\t{\r\n\t\t\t\t\t// Deserialize and load data.\r\n\t\t\t\t\tlock (saveReadFileLock)\r\n\t\t\t\t\t{\r\n\t\t\t\t\t\ttry\r\n\t\t\t\t\t\t{\r\n\r\n\t\t\t\t\t\t\t// If each item will be saved to a separate file.\r\n\t\t\t\t\t\t\tif (UseSeparateFiles)\r\n\t\t\t\t\t\t\t{\r\n\t\t\t\t\t\t\t\tdata = new SettingsData<T>();\r\n\t\t\t\t\t\t\t\tvar files = di.GetFiles(\"*\" + fi.Extension, SearchOption.AllDirectories);\r\n\t\t\t\t\t\t\t\tvar fileItems = new List<ISettingsFileItem>();\r\n\t\t\t\t\t\t\t\tfor (int i = 0; i < files.Length; i++)\r\n\t\t\t\t\t\t\t\t{\r\n\t\t\t\t\t\t\t\t\tvar file = files[i];\r\n\t\t\t\t\t\t\t\t\t// Record the LastWriteTime for later comparison.\r\n\t\t\t\t\t\t\t\t\tSetLastWriteTime(file);\r\n\t\t\t\t\t\t\t\t\tvar bytes = System.IO.File.ReadAllBytes(file.FullName);\r\n\t\t\t\t\t\t\t\t\ttry\r\n\t\t\t\t\t\t\t\t\t{\r\n\t\t\t\t\t\t\t\t\t\tvar item = DeserializeItem(bytes, compress);\r\n\t\t\t\t\t\t\t\t\t\tvar fileItem = (ISettingsFileItem)item;\r\n\t\t\t\t\t\t\t\t\t\tfileItem.WriteTime = file.LastWriteTime;\r\n\t\t\t\t\t\t\t\t\t\t// Set Name property value to the same as the file.\r\n\t\t\t\t\t\t\t\t\t\tvar name = RemoveInvalidFileNameChars(file.Name);\r\n\t\t\t\t\t\t\t\t\t\tvar fileBaseName = Path.GetFileNameWithoutExtension(file.Name);\r\n\t\t\t\t\t\t\t\t\t\tvar path = IO.PathHelper.GetRelativePath(di.FullName + \"\\\\\", file.Directory.FullName + \"\\\\\");\r\n\t\t\t\t\t\t\t\t\t\tpath = path.TrimEnd('.', '\\\\', '/');\r\n\t\t\t\t\t\t\t\t\t\tfileItem.Path = string.IsNullOrWhiteSpace(path) ? null : path;\r\n\t\t\t\t\t\t\t\t\t\tif (fileItem.BaseName != fileBaseName)\r\n\t\t\t\t\t\t\t\t\t\t\tfileItem.BaseName = fileBaseName;\r\n\t\t\t\t\t\t\t\t\t\tfileItems.Add(fileItem);\r\n\t\t\t\t\t\t\t\t\t\tvar listItem = fileItem as ISettingsListFileItem;\r\n\t\t\t\t\t\t\t\t\t\tif (listItem != null)\r\n\t\t\t\t\t\t\t\t\t\t{\r\n\t\t\t\t\t\t\t\t\t\t\t// Created can be later than file creation.\r\n\t\t\t\t\t\t\t\t\t\t\tif (listItem.Created == DateTime.MinValue || listItem.Created > file.CreationTime)\r\n\t\t\t\t\t\t\t\t\t\t\t\tlistItem.Created = file.CreationTime;\r\n\t\t\t\t\t\t\t\t\t\t\t// Modified can be earlier than Created.\r\n\t\t\t\t\t\t\t\t\t\t\tif (listItem.Modified < listItem.Created)\r\n\t\t\t\t\t\t\t\t\t\t\t\tlistItem.Modified = listItem.Created;\r\n\r\n\t\t\t\t\t\t\t\t\t\t}\r\n\t\t\t\t\t\t\t\t\t}\r\n\t\t\t\t\t\t\t\t\tcatch { }\r\n\t\t\t\t\t\t\t\t}\r\n\t\t\t\t\t\t\t\tSortList(fileItems);\r\n\t\t\t\t\t\t\t\tforeach (var fileItem1 in fileItems)\r\n\t\t\t\t\t\t\t\t\tdata.Add((T)fileItem1);\r\n\t\t\t\t\t\t\t}\r\n\t\t\t\t\t\t\telse\r\n\t\t\t\t\t\t\t{\r\n\t\t\t\t\t\t\t\t// Record the LastWriteTime for later comparison.\r\n\t\t\t\t\t\t\t\tSetLastWriteTime(fi);\r\n\t\t\t\t\t\t\t\tvar bytes = System.IO.File.ReadAllBytes(fi.FullName);\r\n\t\t\t\t\t\t\t\tdata = DeserializeData(bytes, compress);\r\n\t\t\t\t\t\t\t}\r\n\t\t\t\t\t\t\tbreak;\r\n\t\t\t\t\t\t}\r\n\t\t\t\t\t\tcatch (Exception ex)\r\n\t\t\t\t\t\t{\r\n\t\t\t\t\t\t\tvar backupFile = fi.FullName + \".bak\";\r\n\t\t\t\t\t\t\tvar sb = new StringBuilder();\r\n\t\t\t\t\t\t\tsb.AppendFormat(\"{0} file has become corrupted.\\r\\n\\r\\n\" +\r\n\t\t\t\t\t\t\t\t\"Reason: \" + ex.Message + \"\\r\\n\\r\\n\" +\r\n\t\t\t\t\t\t\t\t\"Program must reset {0} file in order to continue.\\r\\n\\r\\n\" +\r\n\t\t\t\t\t\t\t\t\"   Click [Yes] to reset and continue.\\r\\n\" +\r\n\t\t\t\t\t\t\t\t\"   Click [No] if you wish to attempt manual repair.\\r\\n\\r\\n\" +\r\n\t\t\t\t\t\t\t\t\" File: {1}\", fi.Name, fi.FullName);\r\n\t\t\t\t\t\t\tsb.AppendLine();\r\n\t\t\t\t\t\t\tsb.Append('-', 64);\r\n\t\t\t\t\t\t\tsb.AppendLine();\r\n\t\t\t\t\t\t\tsb.AppendLine(ex.ToString());\r\n\t\t\t\t\t\t\tvar caption = string.Format(\"Corrupt {0} of {1}\", fi.Name, _Product);\r\n\t\t\t\t\t\t\t//var form = new MessageBox();\r\n\t\t\t\t\t\t\t//form.StartPosition = FormStartPosition.CenterParent;\r\n\t\t\t\t\t\t\tvar text = sb.ToString();\r\n\t\t\t\t\t\t\tbool reset;\r\n\t\t\t\t\t\t\tvar result = MessageBox.Show(text, caption, MessageBoxButton.YesNo, MessageBoxImage.Error);\r\n\t\t\t\t\t\t\treset = result == MessageBoxResult.Yes;\r\n\t\t\t\t\t\t\tif (reset)\r\n\t\t\t\t\t\t\t{\r\n\t\t\t\t\t\t\t\tif (System.IO.File.Exists(backupFile))\r\n\t\t\t\t\t\t\t\t{\r\n\t\t\t\t\t\t\t\t\tSystem.IO.File.Copy(backupFile, fi.FullName, true);\r\n\t\t\t\t\t\t\t\t\tfi.Refresh();\r\n\t\t\t\t\t\t\t\t}\r\n\t\t\t\t\t\t\t\telse\r\n\t\t\t\t\t\t\t\t{\r\n\t\t\t\t\t\t\t\t\tSystem.IO.File.Delete(fi.FullName);\r\n\t\t\t\t\t\t\t\t\tbreak;\r\n\t\t\t\t\t\t\t\t}\r\n\t\t\t\t\t\t\t}\r\n\t\t\t\t\t\t\telse\r\n\t\t\t\t\t\t\t{\r\n\t\t\t\t\t\t\t\t// Avoid the inevitable crash by killing application first.\r\n\t\t\t\t\t\t\t\tProcess.GetCurrentProcess().Kill();\r\n\t\t\t\t\t\t\t\treturn;\r\n\t\t\t\t\t\t\t}\r\n\t\t\t\t\t\t}\r\n\t\t\t\t\t}\r\n\t\t\t\t}\r\n\t\t\t\t// If data read was successful then...\r\n\t\t\t\tif (data != null)\r\n\t\t\t\t{\r\n\t\t\t\t\t// Reorder data of order method exists.\r\n\t\t\t\t\tvar ao = ApplyOrder;\r\n\t\t\t\t\tif (ao != null)\r\n\t\t\t\t\t\tao(data);\r\n\t\t\t\t\tVersion = data.Version;\r\n\t\t\t\t\tLoadAndValidateData(data.Items);\r\n\t\t\t\t\tsettingsLoaded = true;\r\n\t\t\t\t}\r\n\t\t\t}\r\n\t\t\t// If settings failed to load then...\r\n\t\t\tif (!settingsLoaded)\r\n\t\t\t{\r\n\t\t\t\tResetToDefault();\r\n\t\t\t\tSave();\r\n\t\t\t}\r\n\t\t}\r\n\r\n\t\t/// <summary>Sort the list with the fewest UI changes.</summary>\r\n\t\tpublic void SortList<T1>(IList<T1> items) where T1 : ISettingsFileItem\r\n\t\t{\r\n\t\t\t// Move works with special characters to the end.\r\n\t\t\tvar newItems = items\r\n\t\t\t\t.OrderBy(x => x.Path)\r\n\t\t\t\t.ThenBy(x => x.Name.StartsWith(\"®\"))\r\n\t\t\t\t.ThenBy(x => x.Name)\r\n\t\t\t\t.ToList();\r\n\t\t\tCollectionsHelper.Synchronize(newItems, items);\r\n\t\t}\r\n\r\n\t\t#region Use Separate Files\r\n\r\n\t\t/// <summary>\r\n\t\t/// Get the full path for a file based on a filename with extension.\r\n\t\t/// </summary>\r\n\t\tpublic string GetFileItemFullName(ISettingsFileItem fileItem, string overrideBaseName = null)\r\n\t\t{\r\n\t\t\tvar fileFullName = GetFileItemFullName(_XmlFile.FullName, fileItem, overrideBaseName);\r\n\t\t\tvar fiItem = new FileInfo(fileFullName);\r\n\t\t\tif (!fiItem.Directory.Exists)\r\n\t\t\t\tfiItem.Directory.Create();\r\n\t\t\treturn fileFullName;\r\n\t\t}\r\n\r\n\t\tpublic string GetFileItemFullBaseName(ISettingsFileItem fileItem)\r\n\t\t{\r\n\t\t\tvar fi = new FileInfo(_XmlFile.FullName);\r\n\t\t\tvar di = GetRootDirectory(fi);\r\n\t\t\tvar fileName = RemoveInvalidFileNameChars(fileItem.BaseName);\r\n\t\t\tvar itemPath = fileItem.Path;\r\n\t\t\tif (!string.IsNullOrEmpty(itemPath))\r\n\t\t\t\titemPath = RemoveInvalidPathChars(itemPath);\r\n\t\t\tvar fileFullName = string.IsNullOrEmpty(itemPath)\r\n\t\t\t\t? Path.Combine(di.FullName, fileName)\r\n\t\t\t\t: Path.Combine(di.FullName, itemPath, fileName);\r\n\t\t\treturn fileFullName;\r\n\t\t}\r\n\r\n\t\t/// <summary>\r\n\t\t/// Get item path when using separte files.\r\n\t\t/// </summary>\r\n\t\tpublic static string GetFileItemFullName(string rootPath, ISettingsFileItem fileItem, string overrideBaseName = null)\r\n\t\t{\r\n\t\t\tvar fi = new FileInfo(rootPath);\r\n\t\t\tvar di = GetRootDirectory(fi);\r\n\t\t\tvar fileName = RemoveInvalidFileNameChars(overrideBaseName ?? fileItem.BaseName) + fi.Extension;\r\n\t\t\tvar itemPath = fileItem.Path;\r\n\t\t\tif (!string.IsNullOrEmpty(itemPath))\r\n\t\t\t\titemPath = RemoveInvalidPathChars(itemPath);\r\n\t\t\tvar fileFullName = string.IsNullOrEmpty(itemPath)\r\n\t\t\t\t? Path.Combine(di.FullName, fileName)\r\n\t\t\t\t: Path.Combine(di.FullName, itemPath, fileName);\r\n\t\t\treturn fileFullName;\r\n\t\t}\r\n\r\n\r\n\t\t/// <summary>\r\n\t\t/// Renames the specified folder to a new name, managing potential case-sensitivity issues on certain file systems by temporarily renaming to a GUID-based name.\r\n\t\t/// </summary>\r\n\t\t/// <param name=\"currentPath\">The current path of the folder to be renamed.</param>\r\n\t\t/// <param name=\"newFolderName\">The new name for the folder.</param>\r\n\t\t/// <returns>A message indicating success, error, or null if the operation is successful.</returns>\r\n\t\tpublic string RenameFolder(string currentPath, string newFolderName)\r\n\t\t{\r\n\t\t\ttry\r\n\t\t\t{\r\n\t\t\t\t// If directory don't exists\r\n\t\t\t\tif (!Directory.Exists(currentPath))\r\n\t\t\t\t\treturn null;\r\n\t\t\t\tvar directoryInfo = new DirectoryInfo(currentPath);\r\n\t\t\t\tvar parentDirectory = directoryInfo.Parent.FullName;\r\n\t\t\t\tvar newPath = Path.Combine(parentDirectory, newFolderName);\r\n\t\t\t\t// check if the new folder name is different from the current one (ignoring the case)\r\n\t\t\t\tif (string.Equals(directoryInfo.Name, newFolderName, StringComparison.OrdinalIgnoreCase))\r\n\t\t\t\t{\r\n\t\t\t\t\t// rename to temp folder first if only the casing is changed\r\n\t\t\t\t\tvar tempPath = Path.Combine(parentDirectory, Guid.NewGuid().ToString());\r\n\t\t\t\t\tdirectoryInfo.MoveTo(tempPath);\r\n\t\t\t\t}\r\n\t\t\t\telse if (Directory.Exists(newPath))\r\n\t\t\t\t{\r\n\t\t\t\t\treturn \"Folder with the same name already exists.\";\r\n\t\t\t\t}\r\n\t\t\t\tdirectoryInfo.MoveTo(newPath);\r\n\t\t\t}\r\n\t\t\tcatch (Exception ex)\r\n\t\t\t{\r\n\t\t\t\treturn \"An error occurred: \" + ex.Message;\r\n\t\t\t}\r\n\t\t\treturn null;\r\n\t\t}\r\n\r\n\t\t/// <summary>\r\n\t\t/// Occurs when item was renamed.\r\n\t\t/// </summary>\r\n\t\tpublic event EventHandler<ItemPropertyChangedEventArgs> ItemRenamed;\r\n\r\n\t\t/// <summary>\r\n\t\t/// Renames a settings item file to a new name, ensuring file system consistency and updating internal metadata accordingly.\r\n\t\t/// If folder with the same name exists. then rename the folder too.\r\n\t\t/// </summary>\r\n\t\t/// <param name=\"fileItem\">The settings item file object to be renamed.</param>\r\n\t\t/// <param name=\"newName\">The new name for the settings item file.</param>\r\n\t\t/// <returns>A message indicating the outcome of the operation or null if the operation is successful.</returns>\r\n\t\tpublic string RenameItem(ISettingsFileItem fileItem, string newName)\r\n\t\t{\r\n\t\t\tlock (saveReadFileLock)\r\n\t\t\t{\r\n\t\t\t\tvar oldName = RemoveInvalidFileNameChars(fileItem.BaseName);\r\n\t\t\t\t// If the names are exactly the same (case sensitive comparison), no renaming is needed.\r\n\t\t\t\tif (string.Equals(oldName, newName, StringComparison.Ordinal))\r\n\t\t\t\t\treturn null;\r\n\t\t\t\tif (string.IsNullOrEmpty(newName))\r\n\t\t\t\t\treturn \"The file name cannot be empty. Please provide a valid name.\";\r\n\t\t\t\t//newName = RemoveInvalidFileNameChars(newName);\r\n\t\t\t\tvar invalidChars = newName.Intersect(Path.GetInvalidFileNameChars());\r\n\t\t\t\tif (invalidChars.Any())\r\n\t\t\t\t\treturn $\"The file name contains invalid character(s): {string.Join(\"\", invalidChars)}\";\r\n\t\t\t\tvar oldPath = GetFileItemFullName(fileItem, oldName);\r\n\t\t\t\tvar file = new FileInfo(oldPath);\r\n\t\t\t\tvar newPath = GetFileItemFullName(fileItem, newName);\r\n\t\t\t\t// Disable monitoring in order not to trigger reloading.\r\n\t\t\t\tSetFileMonitoring(false);\r\n\t\t\t\ttry\r\n\t\t\t\t{\r\n\t\t\t\t\t// Rename folder if folder with the same name exists.\r\n\t\t\t\t\tvar folderPath = Path.Combine(file.Directory.FullName, oldName);\r\n\t\t\t\t\tvar error = RenameFolder(folderPath, newName);\r\n\t\t\t\t\tif (!string.IsNullOrEmpty(error))\r\n\t\t\t\t\t\treturn error;\r\n\t\t\t\t\t// Rename file.\r\n\t\t\t\t\t// If only case changed then...\r\n\t\t\t\t\tif (string.Equals(oldName, newName, StringComparison.OrdinalIgnoreCase))\r\n\t\t\t\t\t{\r\n\t\t\t\t\t\t// rename to temp file first.\r\n\t\t\t\t\t\tvar tempFilePath = Path.Combine(Path.GetDirectoryName(oldPath), Guid.NewGuid().ToString() + Path.GetExtension(oldPath));\r\n\t\t\t\t\t\tfile.MoveTo(tempFilePath);\r\n\t\t\t\t\t}\r\n\t\t\t\t\tif (File.Exists(newPath))\r\n\t\t\t\t\t{\r\n\t\t\t\t\t\tvar fi = new FileInfo(newPath);\r\n\t\t\t\t\t\treturn $\"A file named '{fi.Name}' ({fi.Length} bytes) already exists. Please remove or rename the existing file and try again.\";\r\n\t\t\t\t\t}\r\n\t\t\t\t\tif (file.Exists)\r\n\t\t\t\t\t{\r\n\t\t\t\t\t\tvar oldBaseName = fileItem.BaseName;\r\n\t\t\t\t\t\tfile.MoveTo(newPath);\r\n\t\t\t\t\t\tfileItem.BaseName = newName;\r\n\t\t\t\t\t\tfileItem.WriteTime = file.LastWriteTime;\r\n\t\t\t\t\t\tvar e = new ItemPropertyChangedEventArgs(nameof(fileItem.BaseName), oldBaseName, newName);\r\n\t\t\t\t\t\tItemRenamed?.Invoke(fileItem, e);\r\n\t\t\t\t\t}\r\n\t\t\t\t}\r\n\t\t\t\tcatch (Exception)\r\n\t\t\t\t{\r\n\t\t\t\t\tthrow;\r\n\t\t\t\t}\r\n\t\t\t\tfinally\r\n\t\t\t\t{\r\n\t\t\t\t\tSetFileMonitoring(true);\r\n\t\t\t\t}\r\n\t\t\t\treturn null;\r\n\t\t\t}\r\n\t\t}\r\n\r\n\t\t/// <summary>\r\n\t\t/// Deletes the file associated with a settings item and removes the item from the internal collection, ensuring data integrity and freeing up resources.\r\n\t\t/// </summary>\r\n\t\t/// <param name=\"fileItem\">The settings item file object to be deleted.</param>\r\n\t\t/// <returns>A message indicating the result of the delete operation, or null if successful.</returns>\r\n\t\tpublic string DeleteItem(ISettingsFileItem fileItem)\r\n\t\t{\r\n\t\t\tlock (saveReadFileLock)\r\n\t\t\t{\r\n\t\t\t\tvar oldName = RemoveInvalidFileNameChars(fileItem.BaseName);\r\n\t\t\t\tvar oldPath = GetFileItemFullName(fileItem, oldName);\r\n\t\t\t\tvar fi = new FileInfo(oldPath);\r\n\t\t\t\t// Rename folder if folder with the same name exists.\r\n\t\t\t\tvar folderPath = Path.Combine(fi.Directory.FullName, oldName);\r\n\r\n\t\t\t\ttry\r\n\t\t\t\t{\r\n\t\t\t\t\tif (Directory.Exists(folderPath))\r\n\t\t\t\t\t\tDirectory.Delete(folderPath, true);\r\n\t\t\t\t}\r\n\t\t\t\tcatch (Exception ex)\r\n\t\t\t\t{\r\n\t\t\t\t\treturn ex.Message;\r\n\t\t\t\t}\r\n\t\t\t\ttry\r\n\t\t\t\t{\r\n\t\t\t\t\tif (fi.Exists)\r\n\t\t\t\t\t\tfi.Delete();\r\n\t\t\t\t}\r\n\t\t\t\tcatch (Exception ex)\r\n\t\t\t\t{\r\n\t\t\t\t\treturn ex.Message;\r\n\t\t\t\t}\r\n\t\t\t\tItems.Remove((T)fileItem);\r\n\t\t\t\treturn null;\r\n\t\t\t}\r\n\t\t}\r\n\r\n\t\t#endregion\r\n\r\n\t\t/// <summary>\r\n\t\t/// Indicates whether the items collection should be cleared when loading new data.\r\n\t\t/// </summary>\r\n\t\t[XmlIgnore, JsonIgnore]\r\n\t\tpublic bool ClearWhenLoading = false;\r\n\r\n\t\tvoid LoadAndValidateData(IList<T> data)\r\n\t\t{\r\n\t\t\tif (data is null)\r\n\t\t\t\tdata = new SortableBindingList<T>();\r\n\t\t\t// Filter data if filter method exists.\r\n\t\t\tvar fl = ValidateData;\r\n\t\t\tvar items = (fl is null)\r\n\t\t\t\t? data\r\n\t\t\t\t: fl(data);\r\n\t\t\t// Filter data if filter method exists.\r\n\t\t\tvar e = new SettingsDataEventArgs(items);\r\n\t\t\tOnValidateData?.Invoke(this, e);\r\n\t\t\tif (ClearWhenLoading)\r\n\t\t\t{\r\n\t\t\t\t// Clear original data.\r\n\t\t\t\tItems.Clear();\r\n\t\t\t\tfor (int i = 0; i < items.Count; i++)\r\n\t\t\t\t\tItems.Add(items[i]);\r\n\t\t\t}\r\n\t\t\telse if (!(e?.Handled == true))\r\n\t\t\t{\r\n\t\t\t\tvar oldList = GetHashValues(Items);\r\n\t\t\t\tvar newList = GetHashValues(data).ToArray();\r\n\t\t\t\tvar newData = new List<T>();\r\n\t\t\t\t// Step 1: Update new list with the old items if they are exactly the same.\r\n\t\t\t\tfor (int i = 0; i < newList.Length; i++)\r\n\t\t\t\t{\r\n\t\t\t\t\tvar newItem = newList[i];\r\n\t\t\t\t\t// Find same item from the old list.\r\n\t\t\t\t\tvar oldItem = oldList.FirstOrDefault(x => x.Value.SequenceEqual(newItem.Value));\r\n\t\t\t\t\t// If same item found then use it...\r\n\t\t\t\t\tif (oldItem.Key != null)\r\n\t\t\t\t\t{\r\n\t\t\t\t\t\tnewData.Add(oldItem.Key);\r\n\t\t\t\t\t\toldList.Remove(oldItem.Key);\r\n\t\t\t\t\t}\r\n\t\t\t\t\telse\r\n\t\t\t\t\t{\r\n\t\t\t\t\t\tnewData.Add(newItem.Key);\r\n\t\t\t\t\t}\r\n\t\t\t\t}\r\n\t\t\t\tCollectionsHelper.Synchronize(newData, Items);\r\n\t\t\t}\r\n\t\t}\r\n\r\n\t\t#region Synchronize\r\n\r\n\t\t/// <summary>\r\n\t\t/// Synchronizes the content of the source collection with the target collection.\r\n\t\t/// </summary>\r\n\t\t/// <param name=\"source\">The source collection to sync from.</param>\r\n\t\t/// <param name=\"target\">The target collection to sync to.</param>\r\n\t\t/// <remarks>\r\n\t\t/// Same Code:\r\n\t\t/// JocysCom\\Controls\\SearchHelper.cs\r\n\t\t/// </remarks>\r\n\t\tpublic static void Synchronize(IList<T> source, IList<T> target)\r\n\t\t{\r\n\t\t\t// Convert to array to avoid modification of collection during processing.\r\n\t\t\tvar sList = source.ToArray();\r\n\t\t\tvar t = 0;\r\n\t\t\tfor (var s = 0; s < sList.Length; s++)\r\n\t\t\t{\r\n\t\t\t\tvar item = sList[s];\r\n\t\t\t\t// If item exists in destination and is in the correct position then continue\r\n\t\t\t\tif (t < target.Count && target[t].Equals(item))\r\n\t\t\t\t{\r\n\t\t\t\t\tt++;\r\n\t\t\t\t\tcontinue;\r\n\t\t\t\t}\r\n\t\t\t\t// If item is in destination but not at the correct position, remove it.\r\n\t\t\t\tvar indexInDestination = target.IndexOf(item);\r\n\t\t\t\tif (indexInDestination != -1)\r\n\t\t\t\t\ttarget.RemoveAt(indexInDestination);\r\n\t\t\t\t// Insert item at the correct position.\r\n\t\t\t\ttarget.Insert(s, item);\r\n\t\t\t\tt = s + 1;\r\n\t\t\t}\r\n\t\t\t// Remove extra items.\r\n\t\t\twhile (target.Count > sList.Length)\r\n\t\t\t\ttarget.RemoveAt(target.Count - 1);\r\n\t\t}\r\n\r\n\r\n\t\t/// <summary>\r\n\t\t/// Return list of items their SHA256 hash.\r\n\t\t/// </summary>\r\n\t\tDictionary<T, byte[]> GetHashValues(IList<T> items)\r\n\t\t{\r\n\t\t\tvar list = new Dictionary<T, byte[]>();\r\n\t\t\tvar algorithm = System.Security.Cryptography.SHA256.Create();\r\n\t\t\tforeach (var item in items)\r\n\t\t\t{\r\n\t\t\t\tvar bytes = Serialize(item);\r\n\t\t\t\tvar byteHash = algorithm.ComputeHash(bytes);\r\n\t\t\t\tlist.Add(item, byteHash);\r\n\t\t\t}\r\n\t\t\treturn list;\r\n\t\t}\r\n\r\n\t\t#endregion\r\n\r\n\t\t/// <summary>\r\n\t\t/// Resets settings to default values defined within the resource files of the specified assemblies.\r\n\t\t/// </summary>\r\n\t\t/// <returns>True if default settings were successfully loaded; otherwise, False.</returns>\r\n\t\tpublic bool ResetToDefault()\r\n\t\t{\r\n\t\t\t// Clear original data.\r\n\t\t\tItems.Clear();\r\n\t\t\tSettingsData<T> data = null;\r\n\t\t\tvar success = false;\r\n\t\t\tfor (int a = 0; a < _Assemblies.Count; a++)\r\n\t\t\t{\r\n\t\t\t\tvar assembly = _Assemblies[a];\r\n\t\t\t\tvar names = assembly.GetManifestResourceNames();\r\n\t\t\t\t// Get compressed resource name.\r\n\t\t\t\tvar name = names.FirstOrDefault(x => x.EndsWith(_XmlFile.Name + \".gz\", StringComparison.OrdinalIgnoreCase));\r\n\t\t\t\tif (string.IsNullOrEmpty(name))\r\n\t\t\t\t{\r\n\t\t\t\t\t// Get uncompressed resource name.\r\n\t\t\t\t\tname = names.FirstOrDefault(x => x.EndsWith(_XmlFile.Name, StringComparison.OrdinalIgnoreCase));\r\n\t\t\t\t}\r\n\t\t\t\t// If internal preset was found.\r\n\t\t\t\tif (!string.IsNullOrEmpty(name))\r\n\t\t\t\t{\r\n\t\t\t\t\tvar resource = assembly.GetManifestResourceStream(name);\r\n\t\t\t\t\tvar sr = new StreamReader(resource);\r\n\t\t\t\t\tbyte[] bytes;\r\n\t\t\t\t\tusing (var memstream = new MemoryStream())\r\n\t\t\t\t\t{\r\n\t\t\t\t\t\tsr.BaseStream.CopyTo(memstream);\r\n\t\t\t\t\t\tbytes = memstream.ToArray();\r\n\t\t\t\t\t}\r\n\t\t\t\t\tsr.Dispose();\r\n\t\t\t\t\tdata = DeserializeData(bytes, name.EndsWith(\".gz\", StringComparison.OrdinalIgnoreCase));\r\n\t\t\t\t\tsuccess = true;\r\n\t\t\t\t\tbreak;\r\n\t\t\t\t}\r\n\t\t\t}\r\n\t\t\tLoadAndValidateData(data is null ? null : data.Items);\r\n\t\t\treturn success;\r\n\t\t}\r\n\r\n\t\t#region Serialization\r\n\r\n\t\tbyte[] Serialize(object fileItem)\r\n\t\t{\r\n\t\t\treturn Serializer.SerializeToXmlBytes(fileItem, Encoding.UTF8, true, _Comment);\r\n\t\t}\r\n\r\n\t\t/// <summary>\r\n\t\t/// Deserializes settings data from a byte array, potentially decompressing it first if indicated.\r\n\t\t/// </summary>\r\n\t\t/// <param name=\"bytes\">The byte array containing serialized settings data.</param>\r\n\t\t/// <param name=\"compressed\">Indicates whether the byte array is compressed and requires decompression.</param>\r\n\t\t/// <returns>A <see cref=\"SettingsData{T}\"/> instance deserialized from the byte array.</returns>\r\n\t\tpublic SettingsData<T> DeserializeData(byte[] bytes, bool compressed)\r\n\t\t{\r\n\t\t\tif (compressed)\r\n\t\t\t\tbytes = SettingsHelper.Decompress(bytes);\r\n\t\t\tvar data = Serializer.DeserializeFromXmlBytes<SettingsData<T>>(bytes);\r\n\t\t\treturn data;\r\n\t\t}\r\n\r\n\t\t/// <summary>\r\n\t\t/// Deserializes a single settings item from a byte array, optionally decompressing it. This method facilitates the reconstruction of individual settings from file storage.\r\n\t\t/// </summary>\r\n\t\t/// <param name=\"bytes\">The byte array containing the serialized settings item.</param>\r\n\t\t/// <param name=\"compressed\">Indicates whether the byte array is compressed.</param>\r\n\t\t/// <returns>The deserialized settings item.</returns>\r\n\t\tpublic T DeserializeItem(byte[] bytes, bool compressed)\r\n\t\t{\r\n\t\t\tif (compressed)\r\n\t\t\t\tbytes = SettingsHelper.Decompress(bytes);\r\n\t\t\tvar item = Serializer.DeserializeFromXmlBytes<T>(bytes);\r\n\t\t\treturn item;\r\n\t\t}\r\n\r\n\t\t#endregion\r\n\r\n\t\t#region Folder Monitoring\r\n\r\n\t\t/// <summary>\r\n\t\t/// Enables or disables monitoring on the settings file directory. When enabled, changes to the files are detected, allowing for the application to respond accordingly.\r\n\t\t/// </summary>\r\n\t\t/// <param name=\"enabled\">true to enable monitoring; false to disable it.</param>\r\n\t\tpublic void SetFileMonitoring(bool enabled)\r\n\t\t{\r\n\t\t\t// Allow to monitor if items are in separate files.\r\n\t\t\tif (!UseSeparateFiles)\r\n\t\t\t\treturn;\r\n\t\t\tvar fi = new FileInfo(XmlFile.FullName);\r\n\t\t\tvar di = GetRootDirectory(fi);\r\n\t\t\tSetFileMonitoring(enabled, di.FullName, \"*.xml\");\r\n\t\t}\r\n\r\n\t\tprivate FileSystemWatcher _folderWatcher;\r\n\r\n\t\t/// <summary>\r\n\t\t/// Raises an event when files in the monitored settings directory change, ensuring settings are reloaded or updated accordingly.\r\n\t\t/// </summary>\r\n\t\tpublic event EventHandler FilesChanged;\r\n\r\n\t\t[DefaultValue(false)]\r\n\t\tpublic bool IsFolderMonitored { get; set; }\r\n\r\n\r\n\t\t/// <summary>\r\n\t\t/// Enables or disables file monitoring for changes to settings files. When enabled, changes to the settings file on disk will trigger a reload of settings to reflect the new state.\r\n\t\t/// </summary>\r\n\t\t/// <param name=\"enabled\">Indicates whether file monitoring should be enabled (true) or disabled (false).</param>\r\n\t\t/// <param name=\"folderPath\">The path to the folder to monitor.</param>\r\n\t\t/// <param name=\"filePattern\">The pattern of the file names to monitor within the folder.</param>\r\n\t\t/// <remarks>\r\n\t\t/// Monitoring settings files is crucial in scenarios where settings might be changed externally or by different instances, ensuring the application operates with the most up-to-date configuration.\r\n\t\t/// </remarks>\r\n\t\tpublic void SetFileMonitoring(bool enabled, string folderPath, string filePattern)\r\n\t\t{\r\n\t\t\tIsFolderMonitored = enabled;\r\n\r\n\t\t\tif (enabled)\r\n\t\t\t{\r\n\t\t\t\tif (_folderWatcher != null)\r\n\t\t\t\t{\r\n\t\t\t\t\t_folderWatcher.EnableRaisingEvents = false;\r\n\t\t\t\t\t_folderWatcher.Dispose();\r\n\t\t\t\t}\r\n\r\n\t\t\t\t_folderWatcher = new FileSystemWatcher(folderPath, filePattern)\r\n\t\t\t\t{\r\n\t\t\t\t\tNotifyFilter = NotifyFilters.LastWrite | NotifyFilters.FileName | NotifyFilters.DirectoryName,\r\n\t\t\t\t\tIncludeSubdirectories = true,\r\n\t\t\t\t};\r\n\r\n\t\t\t\t_folderWatcher.Changed += OnChanged;\r\n\t\t\t\t_folderWatcher.Created += OnCreated;\r\n\t\t\t\t_folderWatcher.Deleted += OnDeleted;\r\n\t\t\t\t_folderWatcher.Renamed += OnRenamed;\r\n\t\t\t\t_folderWatcher.EnableRaisingEvents = true;\r\n\t\t\t}\r\n\t\t\telse\r\n\t\t\t{\r\n\t\t\t\tif (_folderWatcher != null)\r\n\t\t\t\t{\r\n\t\t\t\t\t_folderWatcher.EnableRaisingEvents = false;\r\n\t\t\t\t\t_folderWatcher.Dispose();\r\n\t\t\t\t\t_folderWatcher = null;\r\n\t\t\t\t}\r\n\t\t\t}\r\n\t\t}\r\n\r\n\t\tprivate void OnChanged(object sender, FileSystemEventArgs e)\r\n\t\t\t => _ = Helper.Debounce(FilesChangedDebounced);\r\n\r\n\t\tprivate void OnCreated(object sender, FileSystemEventArgs e)\r\n\t\t\t=> _ = Helper.Debounce(FilesChangedDebounced);\r\n\r\n\t\tprivate void OnDeleted(object sender, FileSystemEventArgs e)\r\n\t\t\t=> _ = Helper.Debounce(FilesChangedDebounced);\r\n\r\n\t\tprivate void OnRenamed(object sender, RenamedEventArgs e)\r\n\t\t\t=> _ = Helper.Debounce(FilesChangedDebounced);\r\n\r\n\t\tprivate void FilesChangedDebounced()\r\n\t\t\t=> FilesChanged?.Invoke(this, EventArgs.Empty);\r\n\r\n\t\t#endregion\r\n\t}\r\n}\r\n"
  },
  {
    "path": "FocusLogger/JocysCom/Configuration/SettingsHelper.cs",
    "content": "using System;\r\nusing System.IO;\r\nusing System.IO.Compression;\r\nusing System.Linq;\r\nusing System.Reflection;\r\nusing System.Text;\r\n\r\nnamespace JocysCom.ClassLibrary.Configuration\r\n{\r\n\t/// <summary>\r\n\t/// Provides utilities for managing application settings, including file comparisons,\r\n\t/// data compression/decompression, and content transformation.\r\n\t/// </summary>\r\n\tpublic static partial class SettingsHelper\r\n\t{\r\n\t\t#region Compression\r\n\r\n\t\t/// <summary>\r\n\t\t/// Compresses the given byte array using GZip compression.\r\n\t\t/// </summary>\r\n\t\t/// <param name=\"bytes\">The byte array to compress.</param>\r\n\t\t/// <returns>The compressed byte array.</returns>\r\n\t\tpublic static byte[] Compress(byte[] bytes)\r\n\t\t{\r\n\t\t\tint numRead;\r\n\t\t\tvar srcStream = new MemoryStream(bytes);\r\n\t\t\tvar dstStream = new MemoryStream();\r\n\t\t\tsrcStream.Position = 0;\r\n\t\t\tvar stream = new GZipStream(dstStream, CompressionMode.Compress);\r\n\t\t\tbyte[] buffer = new byte[0x1000];\r\n\t\t\twhile (true)\r\n\t\t\t{\r\n\t\t\t\tnumRead = srcStream.Read(buffer, 0, buffer.Length);\r\n\t\t\t\tif (numRead == 0) break;\r\n\t\t\t\tstream.Write(buffer, 0, numRead);\r\n\t\t\t}\r\n\t\t\tstream.Close();\r\n\t\t\tsrcStream.Close();\r\n\t\t\treturn dstStream.ToArray();\r\n\t\t}\r\n\r\n\t\t/// <summary>\r\n\t\t/// Decompresses a previously compressed byte array using GZip.\r\n\t\t/// </summary>\r\n\t\t/// <param name=\"bytes\">The compressed byte array to decompress.</param>\r\n\t\t/// <returns>The original byte array.</returns>\r\n\t\tpublic static byte[] Decompress(byte[] bytes)\r\n\t\t{\r\n\t\t\tint numRead;\r\n\t\t\tvar srcStream = new MemoryStream(bytes);\r\n\t\t\tvar dstStream = new MemoryStream();\r\n\t\t\tsrcStream.Position = 0;\r\n\t\t\tvar stream = new GZipStream(srcStream, CompressionMode.Decompress);\r\n\t\t\tvar buffer = new byte[0x1000];\r\n\t\t\twhile (true)\r\n\t\t\t{\r\n\t\t\t\tnumRead = stream.Read(buffer, 0, buffer.Length);\r\n\t\t\t\tif (numRead == 0) break;\r\n\t\t\t\tdstStream.Write(buffer, 0, numRead);\r\n\t\t\t}\r\n\t\t\tdstStream.Close();\r\n\t\t\tstream.Close();\r\n\t\t\treturn dstStream.ToArray();\r\n\t\t}\r\n\r\n\t\t#endregion\r\n\r\n\t\t#region Writing\r\n\r\n\t\t/// <summary>\r\n\t\t/// Returns true if the file at the specified path differs from the provided byte array (by existence, size, or SHA-256 checksum); otherwise false.\r\n\t\t/// </summary>\r\n\t\t/// <param name=\"path\">The path to the file to compare.</param>\r\n\t\t/// <param name=\"bytes\">The byte array to compare against the file's contents.</param>\r\n\t\t/// <returns>True if the file is considered different; otherwise, false.</returns>\r\n\t\tpublic static bool IsDifferent(string path, byte[] bytes)\r\n\t\t{\r\n\t\t\tif (bytes is null)\r\n\t\t\t\tthrow new ArgumentNullException(nameof(bytes));\r\n\t\t\tvar fileInfo = new FileInfo(path);\r\n\t\t\t// If the file does not exist or the size is different, then it is considered different.\r\n\t\t\tif (!fileInfo.Exists || fileInfo.Length != bytes.Length)\r\n\t\t\t\treturn true;\r\n\t\t\t// Compare checksums.\r\n\t\t\tusing (var algorithm = System.Security.Cryptography.SHA256.Create())\r\n\t\t\t{\r\n\t\t\t\tvar byteHash = algorithm.ComputeHash(bytes);\r\n\t\t\t\tvar fileBytes = File.ReadAllBytes(fileInfo.FullName);\r\n\t\t\t\tvar fileHash = algorithm.ComputeHash(fileBytes);\r\n\t\t\t\tvar isDifferent = !byteHash.SequenceEqual(fileHash);\r\n\t\t\t\treturn isDifferent;\r\n\t\t\t}\r\n\t\t}\r\n\r\n\t\t/// <summary>\r\n\t\t/// Writes the byte array to the specified path only if its content differs (comparing size and SHA-256 checksum).\r\n\t\t/// To avoid truncating the file on disk-full or other IO errors, writes to a temporary file first and then renames it.\r\n\t\t/// </summary>\r\n\t\t/// <param name=\"path\">The path where the file will be written.</param>\r\n\t\t/// <param name=\"bytes\">The byte array to write.</param>\r\n\t\t/// <returns>True if the file was written; false if the contents were the same and no write occurred.</returns>\r\n\t\tpublic static bool WriteIfDifferent(string path, byte[] bytes)\r\n\t\t{\r\n\t\t\tif (!IsDifferent(path, bytes))\r\n\t\t\t\treturn false;\r\n\t\t\tif (IsEnoughSpaceAvailable(path, bytes.Length))\r\n\t\t\t{\r\n\t\t\t\tFile.WriteAllBytes(path, bytes);\r\n\t\t\t\treturn true;\r\n\t\t\t}\r\n\t\t\t// Generate a temporary filename in the same directory for an atomic-like replacement.\r\n\t\t\t// Writing to the same directory helps avoid cross-volume moves.\r\n\t\t\tvar directory = Path.GetDirectoryName(path) ?? throw new InvalidOperationException(\"Directory not found.\");\r\n\t\t\tvar tempFileName = Path.Combine(directory, Path.GetRandomFileName());\r\n\t\t\ttry\r\n\t\t\t{\r\n\t\t\t\t// Write all bytes to the temporary file first.\r\n\t\t\t\tFile.WriteAllBytes(tempFileName, bytes);\r\n\t\t\t\t// If we have .NET 6 or later, we could do: File.Move(tempFileName, path, overwrite: true);\r\n\t\t\t\t// Otherwise, we can delete the existing file (if any) and then rename the temp file.\r\n\t\t\t\tif (File.Exists(path))\r\n\t\t\t\t\tFile.Delete(path);\r\n\t\t\t\t// Rename the temp file to the final path (nearly atomic on Windows).\r\n\t\t\t\tFile.Move(tempFileName, path);\r\n\t\t\t\treturn true;\r\n\t\t\t}\r\n\t\t\tcatch\r\n\t\t\t{\r\n\t\t\t\t// Clean up the temp file if something goes wrong.\r\n\t\t\t\tif (File.Exists(tempFileName))\r\n\t\t\t\t\tFile.Delete(tempFileName);\r\n\t\t\t\t// Rethrow the exception or handle it as needed.\r\n\t\t\t\tthrow;\r\n\t\t\t}\r\n\t\t}\r\n\r\n\t\t/// <summary>\r\n\t\t/// Reads the content of a file into a string using the detected encoding of the file.\r\n\t\t/// </summary>\r\n\t\t/// <param name=\"name\">The path to the file to read.</param>\r\n\t\t/// <param name=\"encoding\">The encoding used to read the file. Detected automatically if left null.</param>\r\n\t\t/// <returns>The content of the file as a string.</returns>\r\n\t\tpublic static string ReadFileContent(string name, out Encoding encoding)\r\n\t\t{\r\n\t\t\tusing (var reader = new System.IO.StreamReader(name, true))\r\n\t\t\t{\r\n\t\t\t\tencoding = reader.CurrentEncoding;\r\n\t\t\t\treturn reader.ReadToEnd();\r\n\t\t\t}\r\n\t\t}\r\n\r\n\t\t/// <summary>\r\n\t\t/// Converts a string content into a byte array with an optional encoding header.\r\n\t\t/// </summary>\r\n\t\t/// <param name=\"content\">The string content to convert.</param>\r\n\t\t/// <param name=\"encoding\">The encoding to use for the byte array. If null, the default encoding is used.</param>\r\n\t\t/// <returns>The byte array representation of the content, including the encoding header if specified.</returns>\r\n\t\tpublic static byte[] GetFileContentBytes(string content, Encoding encoding = null)\r\n\t\t{\r\n\t\t\tvar ms = new MemoryStream();\r\n\t\t\t// Encoding header will be added to content.\r\n\t\t\tvar sw = new StreamWriter(ms, encoding);\r\n\t\t\tsw.Write(content);\r\n\t\t\tsw.Flush();\r\n\t\t\tvar bytes = ms.ToArray();\r\n\t\t\tsw.Dispose();\r\n\t\t\treturn bytes;\r\n\t\t}\r\n\r\n\t\t/// <summary>\r\n\t\t/// Determines if the drive containing the specified path has enough free space to accommodate required bytes plus a buffer (10 MB or 5% of required size).\r\n\t\t/// </summary>\r\n\t\t/// <param name=\"path\">The file path whose drive to check.</param>\r\n\t\t/// <param name=\"requiredBytes\">The number of bytes intended to be written.</param>\r\n\t\t/// <returns>True if available free space exceeds requiredBytes plus buffer; otherwise, false.</returns>\r\n\t\tpublic static bool IsEnoughSpaceAvailable(string path, long requiredBytes)\r\n\t\t{\r\n\t\t\t// Convert a relative path to an absolute path\r\n\t\t\tvar fullPath = Path.GetFullPath(path);\r\n\t\t\t// Extract the drive from the full path\r\n\t\t\tvar driveRoot = Path.GetPathRoot(fullPath);\r\n\t\t\tif (string.IsNullOrEmpty(driveRoot))\r\n\t\t\t\treturn false;\r\n\t\t\tvar drive = new DriveInfo(driveRoot);\r\n\t\t\t// A rule of thumb buffer: 10 MB or 5% of file size, whichever is greater.\r\n\t\t\tlong buffer = Math.Max(10 * 1024 * 1024, (long)(requiredBytes * 0.05));\r\n\t\t\tlong totalNeeded = requiredBytes + buffer;\r\n\t\t\treturn drive.AvailableFreeSpace > totalNeeded;\r\n\t\t}\r\n\r\n\t\t#endregion\r\n\r\n\t\t#region Saving \r\n\r\n\t\t/// <summary>\r\n\t\t/// Saves the byte array to a file and appends a CRC32 (Cyclic Redundancy Check) checksum to the filename for integrity verification.\r\n\t\t/// Ensures file contents are not tampered with and remain consistent across operations.\r\n\t\t/// </summary>\r\n\t\t/// <param name=\"name\">The name of the file to save, without the checksum.</param>\r\n\t\t/// <param name=\"bytes\">The byte array containing the data to be saved to the file.</param>\r\n\t\t/// <returns>FileInfo object representing the saved file, including its checksum in the filename.</returns>\r\n\t\tpublic static FileInfo SaveFileWithChecksum(string name, byte[] bytes)\r\n\t\t{\r\n\t\t\tvar assembly = Assembly.GetEntryAssembly();\r\n\t\t\tvar company = ((AssemblyCompanyAttribute)Attribute.GetCustomAttribute(assembly, typeof(AssemblyCompanyAttribute))).Company;\r\n\t\t\tvar product = ((AssemblyProductAttribute)Attribute.GetCustomAttribute(assembly, typeof(AssemblyProductAttribute))).Product;\r\n\t\t\t// Get writable application folder.\r\n\t\t\tvar specialFolder = Environment.SpecialFolder.CommonApplicationData;\r\n\t\t\tvar folder = string.Format(\"{0}\\\\{1}\\\\{2}\", Environment.GetFolderPath(specialFolder), company, product);\r\n\t\t\tvar hash = ComputeCRC32Checksum(bytes);\r\n\t\t\t// Put file into sub folder because file name must match with LoadLibrary() argument. \r\n\t\t\tvar chName = string.Format(\"{0}.{1:X8}\\\\{0}\", name, hash);\r\n\t\t\tvar fileName = System.IO.Path.Combine(folder, \"Temp\", chName);\r\n\t\t\tvar fi = new FileInfo(fileName);\r\n\t\t\tif (fi.Exists)\r\n\t\t\t\treturn fi;\r\n\t\t\tif (!fi.Directory.Exists)\r\n\t\t\t\tfi.Directory.Create();\r\n\t\t\tFile.WriteAllBytes(fileName, bytes);\r\n\t\t\tfi.Refresh();\r\n\t\t\treturn fi;\r\n\t\t}\r\n\r\n\t\t/// <summary>\r\n\t\t/// Calculates the 32-bit Cyclic Redundancy Check (CRC32) checksum for the given byte array.\r\n\t\t/// </summary>\r\n\t\t/// <param name=\"bytes\">The byte array to calculate the checksum for.</param>\r\n\t\t/// <returns>The calculated CRC32 checksum as an unsigned 32-bit integer.</returns>\r\n\t\tpublic static uint ComputeCRC32Checksum(byte[] bytes)\r\n\t\t{\r\n\t\t\tuint poly = 0xedb88320;\r\n\t\t\tuint[] table = new uint[256];\r\n\t\t\tuint temp;\r\n\t\t\tfor (uint i = 0; i < table.Length; ++i)\r\n\t\t\t{\r\n\t\t\t\ttemp = i;\r\n\t\t\t\tfor (int j = 8; j > 0; --j)\r\n\t\t\t\t\ttemp = (temp & 1) == 1 ? (temp >> 1) ^ poly : temp >> 1;\r\n\t\t\t\ttable[i] = temp;\r\n\t\t\t}\r\n\t\t\tuint crc = 0xffffffff;\r\n\t\t\tfor (int i = 0; i < bytes.Length; ++i)\r\n\t\t\t\tcrc = (crc >> 8) ^ table[(byte)(((crc) & 0xff) ^ bytes[i])];\r\n\t\t\treturn ~crc;\r\n\t\t}\r\n\r\n\t\t#endregion\r\n\r\n\t}\r\n}"
  },
  {
    "path": "FocusLogger/JocysCom/Configuration/SettingsItem.cs",
    "content": "using JocysCom.ClassLibrary.ComponentModel;\r\nusing System.ComponentModel;\r\n\r\nnamespace JocysCom.ClassLibrary.Configuration\r\n{\r\n\t/// <summary>Base configuration entity implementing ISettingsItem with change notification support.</summary>\r\n\t/// <remarks>\r\n\t/// Exposes enabled state and emptiness checks.\r\n\t/// Extended by SettingsFileItem for file-based entries.\r\n\t/// Edited via a WinForms SettingsItemForm.\r\n\t/// </remarks>\r\n\tpublic class SettingsItem : NotifyPropertyChanged, ISettingsItem\r\n\t{\r\n\t\t/// <inheritdoc />\r\n\t\t[DefaultValue(true)]\r\n\t\tpublic bool IsEnabled { get => _IsEnabled; set => SetProperty(ref _IsEnabled, value); }\r\n\t\tbool _IsEnabled = true;\r\n\r\n\t\t/// <inheritdoc />\r\n\t\tpublic virtual bool IsEmpty => true;\r\n\r\n\t}\r\n}\r\n"
  },
  {
    "path": "FocusLogger/JocysCom/Configuration/SettingsParser.cs",
    "content": "using System;\r\nusing System.Linq;\r\n#if NETFRAMEWORK // .NET Framework...\r\nusing System.Configuration;\r\n#endif\r\n#if __MOBILE__\r\n    using Xamarin.Forms;\r\n#endif\r\n\r\nnamespace JocysCom.ClassLibrary.Configuration\r\n{\r\n    /// <summary>\r\n    /// Parse application setting values.\r\n    /// Parses app settings across .NET Framework, .NET Standard, .NET Core, and Xamarin; converts raw strings to target types.\r\n    /// </summary>\r\n    public partial class SettingsParser\r\n    {\r\n\r\n        public SettingsParser(string configPrefix = \"\")\r\n        {\r\n            ConfigPrefix = configPrefix;\r\n        }\r\n\r\n        public string ConfigPrefix { get; set; }\r\n        public static SettingsParser Current { get; } = new SettingsParser();\r\n\r\n        /// <summary>\r\n        /// Parse all IConvertible types, like value types, with one function.\r\n        /// Retrieves the setting identified by <paramref name=\"name\"/> (prefixed with <see cref=\"ConfigPrefix\"/>)\r\n        /// and converts it to <typeparamref name=\"T\"/>. Falls back to <paramref name=\"defaultValue\"/> if missing or conversion fails.\r\n        /// </summary>\r\n        /// <typeparam name=\"T\">Target type for conversion.</typeparam>\r\n        /// <param name=\"name\">Setting key without prefix.</param>\r\n        /// <param name=\"defaultValue\">Value returned if missing or conversion fails.</param>\r\n        /// <returns>Converted value or <paramref name=\"defaultValue\"/>.</returns>\r\n        public T Parse<T>(string name, T defaultValue = default(T))\r\n        {\r\n            if (_GetValue is null)\r\n                return defaultValue;\r\n            var v = _GetValue(ConfigPrefix + name);\r\n            return ParseValue<T>(v, defaultValue);\r\n        }\r\n\r\n        /// <summary>Converts a string to the specified <paramref name=\"t\"/> type.</summary>\r\n        /// <remarks>\r\n        /// Supports System.Drawing.Color (FromName), static Parse(string), enums (case-insensitive), and IConvertible via Convert.ChangeType.\r\n        /// Returns <paramref name=\"defaultValue\"/> if <paramref name=\"v\"/> is null or no converter is found.\r\n        /// </remarks>\r\n        /// <param name=\"t\">Target type; must not be null.</param>\r\n        /// <param name=\"v\">Input string value.</param>\r\n        /// <param name=\"defaultValue\">Fallback value.</param>\r\n        /// <returns>Converted object or <paramref name=\"defaultValue\"/>.</returns>\r\n        /// <exception cref=\"ArgumentNullException\">Thrown when <paramref name=\"t\"/> is null.</exception>\r\n        public static object ParseValue(Type t, string v, object defaultValue = null)\r\n        {\r\n            if (t is null)\r\n                throw new ArgumentNullException(nameof(t));\r\n            if (v is null)\r\n                return defaultValue;\r\n            if (typeof(System.Drawing.Color).IsAssignableFrom(t))\r\n                return System.Drawing.Color.FromName(v);\r\n            // Get Parse method with string parameter.\r\n            var m = t.GetMethod(\"Parse\", new[] { typeof(string) });\r\n            if (m != null)\r\n                return m.Invoke(null, new[] { v });\r\n            //if (typeof(IPAddress).IsAssignableFrom(t))\r\n            //    return IPAddress.Parse(v);\r\n            //if (typeof(TimeSpan).IsAssignableFrom(t))\r\n            //    return TimeSpan.Parse(v, CultureInfo.InvariantCulture);\r\n            if (t.IsEnum)\r\n                return Enum.Parse(t, v, true);\r\n            // If type can be converted then convert.\r\n            if (typeof(IConvertible).IsAssignableFrom(t))\r\n                return System.Convert.ChangeType(v, t);\r\n            return defaultValue;\r\n        }\r\n\r\n        /// <summary>Attempts to convert the string to <typeparamref name=\"T\"/>, returning <paramref name=\"defaultValue\"/> on error.</summary>\r\n        /// <typeparam name=\"T\">Target type to parse.</typeparam>\r\n        /// <param name=\"v\">Input string.</param>\r\n        /// <param name=\"defaultValue\">Fallback value.</param>\r\n        /// <returns>Parsed value or <paramref name=\"defaultValue\"/>.</returns>\r\n        public static T TryParseValue<T>(string v, T defaultValue = default(T))\r\n        {\r\n            try\r\n            {\r\n                return (T)ParseValue(typeof(T), v, defaultValue);\r\n            }\r\n            catch (Exception)\r\n            {\r\n                return defaultValue;\r\n            }\r\n        }\r\n\r\n        public static T ParseValue<T>(string v, T defaultValue = default(T))\r\n        {\r\n            return (T)ParseValue(typeof(T), v, defaultValue);\r\n        }\r\n\r\n#if NETFRAMEWORK // .NET Framework...\r\n\r\n        /// <summary>Delegate to fetch configuration values by key; defaults to ConfigurationManager.AppSettings in .NET Framework.</summary>\r\n        public static Func<string, string> _GetValue = (name)\r\n            => ConfigurationManager.AppSettings[name];\r\n\r\n#else // .NET (Core/5+)\r\n\r\n        /// <summary>Delegate to fetch configuration values by key; initialize via InitializeParser in .NET Core.</summary>\r\n        public static Func<string, string> _GetValue;\r\n\r\n#endif\r\n\r\n    }\r\n}"
  },
  {
    "path": "FocusLogger/JocysCom/Controls/ControlsHelper.WPF.UseWindowsForms.cs",
    "content": "using System.Windows;\r\nusing System.Linq;\r\nusing System;\r\nusing System.Windows.Media;\r\nusing System.IO;\r\nusing System.Data;\r\n\r\nnamespace JocysCom.ClassLibrary.Controls\r\n{\r\n\tpublic partial class ControlsHelper\r\n\t{\r\n\r\n\t\t/// <summary>\r\n\t\t/// Set form TopMost if one of the application forms is top most.\r\n\t\t/// </summary>\r\n\t\t/// <param name=\"win\"></param>\r\n\t\tpublic static void CheckTopMost(Window win)\r\n\t\t{\r\n\t\t\t// If this form is not set as TopMost but one of the application forms is on TopMost then...\r\n\t\t\t// Make this dialog form TopMost too or user won't be able to access it.\r\n\t\t\tif (!win.Topmost && System.Windows.Forms.Application.OpenForms.Cast<System.Windows.Forms.Form>().Any(x => x.TopMost))\r\n\t\t\t\twin.Topmost = true;\r\n\t\t}\r\n\r\n\t\tpublic static void AutoSizeByOpenForms(Window win, int addSize = -64)\r\n\t\t{\r\n\t\t\tvar form = System.Windows.Forms.Application.OpenForms.Cast<System.Windows.Forms.Form>().FirstOrDefault();\r\n\t\t\tif (form == null)\r\n\t\t\t\treturn;\r\n\t\t\twin.Width = form.Width + addSize;\r\n\t\t\twin.Height = form.Height + addSize;\r\n\t\t\twin.Top = form.Top - addSize / 2;\r\n\t\t\twin.Left = form.Left - addSize / 2;\r\n\t\t}\r\n\r\n\t\t/// <summary>\r\n\t\t/// Convert Bitmap to image source.\r\n\t\t/// </summary>\r\n\t\t///\t<remarks>\r\n\t\t///\tRequires NuGet Package on .NET Core: System.Drawing.Common or...\r\n\t\t///\tset property <UseWindowsForms>true</UseWindowsForms> inside the project.\r\n\t\t///\t</remarks>\r\n\t\tpublic static ImageSource GetImageSource(System.Drawing.Bitmap bitmap)\r\n\t\t{\r\n\t\t\tvar bi = new System.Windows.Media.Imaging.BitmapImage();\r\n\t\t\tvar ms = new MemoryStream();\r\n\t\t\tbitmap.Save(ms, System.Drawing.Imaging.ImageFormat.Png);\r\n\t\t\tbi.BeginInit();\r\n\t\t\tbi.CacheOption = System.Windows.Media.Imaging.BitmapCacheOption.OnLoad;\r\n\t\t\tbi.StreamSource = ms;\r\n\t\t\tbi.EndInit();\r\n\t\t\tms.Dispose();\r\n\t\t\treturn bi;\r\n\t\t}\r\n\r\n\t\t#region Center Window\r\n\r\n\t\t/// <summary>\r\n\t\t/// Center window on Owner window.\r\n\t\t/// </summary>\r\n\t\t/// <param name=\"window\"></param>\r\n\t\tpublic static void CenterWindowOnApplication(Window window)\r\n\t\t{\r\n\t\t\t// Get WFF window first.\r\n\t\t\tvar win = window.Owner;\r\n\t\t\tSystem.Drawing.Rectangle? r = null;\r\n\t\t\tvar isNormal = false;\r\n\t\t\tif (win != null)\r\n\t\t\t{\r\n\t\t\t\tr = new System.Drawing.Rectangle((int)win.Left, (int)win.Top, (int)win.Width, (int)win.Height);\r\n\t\t\t\tisNormal = win.WindowState == WindowState.Normal;\r\n\t\t\t}\r\n\t\t\telse\r\n\t\t\t{\r\n\t\t\t\t// Try to get top windows form.\r\n\t\t\t\tvar form = System.Windows.Forms.Application.OpenForms.Cast<System.Windows.Forms.Form>().FirstOrDefault();\r\n\t\t\t\tif (form != null)\r\n\t\t\t\t{\r\n\t\t\t\t\tdouble l;\r\n\t\t\t\t\tdouble t;\r\n\t\t\t\t\tdouble w;\r\n\t\t\t\t\tdouble h;\r\n\t\t\t\t\tTransformToUnits(form.Left, form.Top, out l, out t);\r\n\t\t\t\t\tTransformToUnits(form.Width, form.Height, out w, out h);\r\n\t\t\t\t\tr = new System.Drawing.Rectangle((int)l, (int)t, (int)w, (int)h);\r\n\t\t\t\t\tisNormal = form.WindowState == System.Windows.Forms.FormWindowState.Normal;\r\n\t\t\t\t}\r\n\t\t\t}\r\n\t\t\tif (r.HasValue)\r\n\t\t\t{\r\n\t\t\t\tif (isNormal)\r\n\t\t\t\t{\r\n\t\t\t\t\twindow.Left = r.Value.X + ((r.Value.Width - window.ActualWidth) / 2);\r\n\t\t\t\t\twindow.Top = r.Value.Y + ((r.Value.Height - window.ActualHeight) / 2);\r\n\t\t\t\t}\r\n\t\t\t\telse\r\n\t\t\t\t{\r\n\t\t\t\t\t// Get the form screen.\r\n\t\t\t\t\tvar screen = System.Windows.Forms.Screen.FromRectangle(r.Value);\r\n\t\t\t\t\tdouble screenWidth = screen.WorkingArea.Width;\r\n\t\t\t\t\tdouble screenHeight = screen.WorkingArea.Height;\r\n\t\t\t\t\twindow.Left = (screenWidth / 2) - (window.Width / 2);\r\n\t\t\t\t\twindow.Top = (screenHeight / 2) - (window.Height / 2);\r\n\t\t\t\t}\r\n\t\t\t}\r\n\t\t}\r\n\r\n\t\t/// <summary>\r\n\t\t/// Transforms device independent units (1/96 of an inch) to pixels.\r\n\t\t/// </summary>\r\n\t\tprivate static void TransformToPixels(double unitX, double unitY, out int pixelX, out int pixelY)\r\n\t\t{\r\n\t\t\tusing (var g = System.Drawing.Graphics.FromHwnd(IntPtr.Zero))\r\n\t\t\t{\r\n\t\t\t\tpixelX = (int)((g.DpiX / 96) * unitX);\r\n\t\t\t\tpixelY = (int)((g.DpiY / 96) * unitY);\r\n\t\t\t}\r\n\t\t}\r\n\r\n\t\t/// <summary>\r\n\t\t/// Transforms device pixels to independent units (1/96 of an inch).\r\n\t\t/// </summary>\r\n\t\tprivate static void TransformToUnits(int pixelX, int pixelY, out double unitX, out double unitY)\r\n\t\t{\r\n\t\t\tusing (var g = System.Drawing.Graphics.FromHwnd(IntPtr.Zero))\r\n\t\t\t{\r\n\t\t\t\tunitX = (double)pixelX / (g.DpiX / 96);\r\n\t\t\t\tunitY = (double)pixelY / (g.DpiX / 96);\r\n\t\t\t}\r\n\t\t}\r\n\r\n\t\tpublic static bool GetMainFormTopMost()\r\n\t\t{\r\n\t\t\tvar win = System.Windows.Application.Current?.MainWindow;\r\n\t\t\tif (win != null)\r\n\t\t\t\treturn win.Topmost;\r\n\t\t\tvar form = System.Windows.Forms.Application.OpenForms.Cast<System.Windows.Forms.Form>().FirstOrDefault();\r\n\t\t\tif (form != null)\r\n\t\t\t\treturn form.TopMost;\r\n\t\t\treturn false;\r\n\t\t}\r\n\r\n\t\t#endregion\r\n\r\n\r\n\t}\r\n}\r\n"
  },
  {
    "path": "FocusLogger/JocysCom/Controls/ControlsHelper.WPF.cs",
    "content": "﻿using System;\r\nusing System.Collections.Generic;\r\nusing System.ComponentModel;\r\nusing System.IO;\r\nusing System.Linq;\r\nusing System.Threading.Tasks;\r\nusing System.Windows;\r\nusing System.Windows.Controls;\r\nusing System.Windows.Controls.Primitives;\r\nusing System.Windows.Documents;\r\nusing System.Windows.Input;\r\nusing System.Windows.Media;\r\nusing System.Xml;\r\n\r\nnamespace JocysCom.ClassLibrary.Controls\r\n{\r\n\tpublic partial class ControlsHelper\r\n\t{\r\n\t\tpublic static void EnableWithDelay(UIElement control)\r\n\t\t{\r\n\t\t\tTask.Run(async delegate\r\n\t\t\t{\r\n\t\t\t\t// Logical delay without blocking the current hardware thread.\r\n\t\t\t\tawait Task.Delay(500).ConfigureAwait(true);\r\n\t\t\t\tcontrol.Dispatcher.Invoke(() => control.IsEnabled = true);\r\n\t\t\t});\r\n\t\t}\r\n\r\n\t\tprivate static bool? _IsDesignModeWPF;\r\n\r\n\t\tpublic static bool IsDesignMode(UIElement component)\r\n\t\t{\r\n\t\t\tif (!_IsDesignModeWPF.HasValue)\r\n\t\t\t\t_IsDesignModeWPF = IsDesignMode1(component);\r\n\t\t\treturn _IsDesignModeWPF.Value;\r\n\t\t}\r\n\r\n\t\tprivate static bool IsDesignMode1(UIElement component)\r\n\t\t{\r\n\t\t\t// Check 1.\r\n\t\t\tif (DesignerProperties.GetIsInDesignMode(component))\r\n\t\t\t\treturn true;\r\n\t\t\t//If WPF hosted in WinForms.\r\n\t\t\tvar ea = System.Reflection.Assembly.GetEntryAssembly();\r\n\t\t\tif (ea != null && ea.Location.Contains(\"VisualStudio\"))\r\n\t\t\t\treturn true;\r\n\t\t\t//If WPF hosted in WinForms.\r\n\t\t\tea = System.Reflection.Assembly.GetExecutingAssembly();\r\n\t\t\tif (ea != null && ea.Location.Contains(\"VisualStudio\"))\r\n\t\t\t\treturn true;\r\n\t\t\treturn false;\r\n\t\t}\r\n\r\n\t\tpublic static T Clone<T>(T o)\r\n\t\t{\r\n\t\t\tvar sb = new System.Text.StringBuilder();\r\n\t\t\tvar writer = XmlWriter.Create(sb, new XmlWriterSettings\r\n\t\t\t{\r\n\t\t\t\tIndent = true,\r\n\t\t\t\tConformanceLevel = ConformanceLevel.Fragment,\r\n\t\t\t\tOmitXmlDeclaration = true,\r\n\t\t\t\tNamespaceHandling = NamespaceHandling.OmitDuplicates,\r\n\t\t\t\t// XmlReader normalizes all newlines and converts '\\r\\n' to '\\n'.\r\n\t\t\t\t// This requires to save NewLines with  option which\r\n\t\t\t\t// \"Entitize\" option replace '\\r' with '&#xD;' in text node values.\r\n\t\t\t\tNewLineHandling = NewLineHandling.Entitize,\r\n\t\t\t});\r\n\t\t\tvar manager = new System.Windows.Markup.XamlDesignerSerializationManager(writer);\r\n\t\t\tmanager.XamlWriterMode = System.Windows.Markup.XamlWriterMode.Expression;\r\n\t\t\tSystem.Windows.Markup.XamlWriter.Save(o, manager);\r\n\t\t\tvar stringReader = new StringReader(sb.ToString());\r\n\t\t\tvar xmlReader = XmlReader.Create(stringReader);\r\n\t\t\tvar item = System.Windows.Markup.XamlReader.Load(xmlReader);\r\n\t\t\tif (item is null)\r\n\t\t\t\tthrow new ArgumentNullException(\"Could not be cloned.\");\r\n\t\t\treturn (T)item;\r\n\t\t}\r\n\r\n\t\t/// <summary>\r\n\t\t/// Change value if it is different only.\r\n\t\t/// This helps not to trigger control events when doing frequent events.\r\n\t\t/// </summary>\r\n\t\tpublic static void SetText(Label control, string format, params object[] args)\r\n\t\t{\r\n\t\t\tif (control is null)\r\n\t\t\t\tthrow new ArgumentNullException(nameof(control));\r\n\t\t\tvar text = args?.Count() > 0\r\n\t\t\t\t? string.Format(format ?? \"\", args)\r\n\t\t\t\t: format;\r\n\t\t\tif (control.Content as string != text)\r\n\t\t\t\tcontrol.Content = text;\r\n\t\t}\r\n\r\n\t\tpublic static void SetText(PasswordBox control, string format, params object[] args)\r\n\t\t{\r\n\t\t\tif (control is null)\r\n\t\t\t\tthrow new ArgumentNullException(nameof(control));\r\n\t\t\tvar text = args?.Count() > 0\r\n\t\t\t\t? string.Format(format ?? \"\", args)\r\n\t\t\t\t: format;\r\n\t\t\tif (control.Password != text)\r\n\t\t\t\tcontrol.Password = text;\r\n\t\t}\r\n\r\n\t\t/// <summary>\r\n\t\t/// Change value if it is different only.\r\n\t\t/// This helps not to trigger control events when doing frequent events.\r\n\t\t/// </summary>\r\n\t\tpublic static void SetText(GroupBox control, string format, params object[] args)\r\n\t\t{\r\n\t\t\tif (control is null)\r\n\t\t\t\tthrow new ArgumentNullException(nameof(control));\r\n\t\t\tvar text = args?.Count() > 0\r\n\t\t\t\t? string.Format(format ?? \"\", args)\r\n\t\t\t\t: format;\r\n\t\t\tif (control.Header as string != text)\r\n\t\t\t\tcontrol.Header = text;\r\n\t\t}\r\n\r\n\t\t/// <summary>\r\n\t\t/// Change value if it is different only.\r\n\t\t/// This helps not to trigger control events when doing frequent events.\r\n\t\t/// </summary>\r\n\t\tpublic static void SetText(TextBox control, string format, params object[] args)\r\n\t\t{\r\n\t\t\tif (control is null)\r\n\t\t\t\tthrow new ArgumentNullException(nameof(control));\r\n\t\t\tvar text = args?.Count() > 0\r\n\t\t\t\t? string.Format(format ?? \"\", args)\r\n\t\t\t\t: format;\r\n\t\t\tif (control.Text != text)\r\n\t\t\t\tcontrol.Text = text;\r\n\t\t}\r\n\r\n\t\t/// <summary>\r\n\t\t/// Change value if it is different only.\r\n\t\t/// This helps not to trigger control events when doing frequent events.\r\n\t\t/// </summary>\r\n\t\tpublic static void SetText(TextBlock control, string format, params object[] args)\r\n\t\t{\r\n\t\t\tif (control is null)\r\n\t\t\t\tthrow new ArgumentNullException(nameof(control));\r\n\t\t\tvar text = args?.Count() > 0\r\n\t\t\t\t? string.Format(format ?? \"\", args)\r\n\t\t\t\t: format;\r\n\t\t\tif (control.Text != text)\r\n\t\t\t\tcontrol.Text = text;\r\n\t\t}\r\n\r\n\t\tpublic static void SetTextFromResource(RichTextBox box, byte[] rtf)\r\n\t\t{\r\n\t\t\tvar ms = new MemoryStream(rtf);\r\n\t\t\tvar textRange = new TextRange(box.Document.ContentStart, box.Document.ContentEnd);\r\n\t\t\ttextRange.Load(ms, DataFormats.Rtf);\r\n\t\t\tms.Dispose();\r\n\t\t\tbox.Document.PagePadding = new Thickness(8);\r\n\t\t\tbox.IsDocumentEnabled = true;\r\n\t\t\tHookHyperlinks(box, null);\r\n\t\t}\r\n\r\n\t\t/// <summary>\r\n\t\t/// Change value if it is different only.\r\n\t\t/// This helps not to trigger control events when doing frequent events.\r\n\t\t/// </summary>\r\n\t\tpublic static void SetChecked(System.Windows.Controls.Primitives.ToggleButton control, bool check)\r\n\t\t{\r\n\t\t\tif (control is null)\r\n\t\t\t\tthrow new ArgumentNullException(nameof(control));\r\n\t\t\tif (control.IsChecked != check)\r\n\t\t\t\tcontrol.IsChecked = check;\r\n\t\t}\r\n\r\n\t\t/// <summary>\r\n\t\t/// Change value if it is different only.\r\n\t\t/// This helps not to trigger control events when doing frequent events.\r\n\t\t/// </summary>\r\n\t\tpublic static void SetEnabled(UIElement control, bool enabled)\r\n\t\t{\r\n\t\t\tif (control is null)\r\n\t\t\t\tthrow new ArgumentNullException(nameof(control));\r\n\t\t\tif (control.IsEnabled != enabled)\r\n\t\t\t\tcontrol.IsEnabled = enabled;\r\n\t\t}\r\n\r\n\t\t/// <summary>\r\n\t\t/// Change value if it is different only.\r\n\t\t/// This helps not to trigger control events when doing frequent events.\r\n\t\t/// </summary>\r\n\t\tpublic static void SetVisible(UIElement control, bool enabled)\r\n\t\t{\r\n\t\t\tif (control is null)\r\n\t\t\t\tthrow new ArgumentNullException(nameof(control));\r\n\t\t\tvar visibility = enabled ? Visibility.Visible : Visibility.Collapsed;\r\n\t\t\tif (control.Visibility != visibility)\r\n\t\t\t\tcontrol.Visibility = visibility;\r\n\t\t}\r\n\r\n\r\n\t\tpublic static void SetItemsSource(DataGridComboBoxColumn grid, IBindingList list)\r\n\t\t{\r\n\t\t\tif (list is null)\r\n\t\t\t{\r\n\t\t\t\tif (grid.ItemsSource is System.Windows.Data.BindingListCollectionView view)\r\n\t\t\t\t{\r\n\t\t\t\t\tgrid.ItemsSource = null;\r\n\t\t\t\t\tview.DetachFromSourceCollection();\r\n\t\t\t\t}\r\n\t\t\t\treturn;\r\n\t\t\t}\r\n\t\t\tvar currentView = (System.Windows.Data.BindingListCollectionView)grid.ItemsSource;\r\n\t\t\t// If same list then...\r\n\t\t\tif (currentView?.SourceCollection == list)\r\n\t\t\t\treturn;\r\n\t\t\tvar newView = new System.Windows.Data.BindingListCollectionView(list);\r\n\t\t\tgrid.ItemsSource = newView;\r\n\t\t}\r\n\r\n\r\n\t\tpublic static void SetItemsSource(ItemsControl grid, IBindingList list)\r\n\t\t{\r\n\t\t\tif (list is null)\r\n\t\t\t{\r\n\t\t\t\tif (grid.ItemsSource is System.Windows.Data.BindingListCollectionView view)\r\n\t\t\t\t{\r\n\t\t\t\t\tgrid.ItemsSource = null;\r\n\t\t\t\t\tview.DetachFromSourceCollection();\r\n\t\t\t\t}\r\n\t\t\t\treturn;\r\n\t\t\t}\r\n\t\t\tvar currentView = (System.Windows.Data.BindingListCollectionView)grid.ItemsSource;\r\n\t\t\t// If same list then...\r\n\t\t\tif (currentView?.SourceCollection == list)\r\n\t\t\t\treturn;\r\n\t\t\tvar newView = new System.Windows.Data.BindingListCollectionView(list);\r\n\t\t\t// Clear Items to avoid exception: \"Items collection must be empty before using ItemsSource\"\r\n\t\t\tgrid.Items.Clear();\r\n\t\t\tgrid.ItemsSource = newView;\r\n\t\t}\r\n\r\n\t\tprivate static void HookHyperlinks(object sender, TextChangedEventArgs e)\r\n\t\t{\r\n\t\t\tvar doc = (sender as RichTextBox).Document;\r\n\t\t\tfor (var position = doc.ContentStart;\r\n\t\t\t\tposition != null && position.CompareTo(doc.ContentEnd) <= 0;\r\n\t\t\t\tposition = position.GetNextContextPosition(LogicalDirection.Forward))\r\n\t\t\t{\r\n\t\t\t\tif (position.GetPointerContext(LogicalDirection.Forward) == TextPointerContext.ElementEnd)\r\n\t\t\t\t{\r\n\t\t\t\t\tif (position.Parent is Hyperlink link)\r\n\t\t\t\t\t\tlink.RequestNavigate += link_RequestNavigate;\r\n\t\t\t\t\telse if (position.Parent is Span span)\r\n\t\t\t\t\t{\r\n\t\t\t\t\t\tvar range = new TextRange(span.ContentStart, span.ContentEnd);\r\n\t\t\t\t\t\tif (Uri.TryCreate(range.Text, UriKind.Absolute, out var uriResult))\r\n\t\t\t\t\t\t{\r\n\t\t\t\t\t\t\tif (uriResult.Scheme == Uri.UriSchemeHttp || uriResult.Scheme == Uri.UriSchemeHttps)\r\n\t\t\t\t\t\t\t{\r\n\t\t\t\t\t\t\t\tvar h = new Hyperlink(range.Start, range.End);\r\n\t\t\t\t\t\t\t\th.RequestNavigate += link_RequestNavigate;\r\n\t\t\t\t\t\t\t\th.NavigateUri = new Uri(range.Text);\r\n\t\t\t\t\t\t\t\th.Cursor = System.Windows.Input.Cursors.Hand;\r\n\t\t\t\t\t\t\t}\r\n\t\t\t\t\t\t}\r\n\t\t\t\t\t}\r\n\t\t\t\t}\r\n\t\t\t}\r\n\t\t}\r\n\t\tprivate static void link_RequestNavigate(object sender, System.Windows.Navigation.RequestNavigateEventArgs e)\r\n\t\t{\r\n\t\t\tvar link = (Hyperlink)sender;\r\n\t\t\tOpenUrl(link.NavigateUri.AbsoluteUri);\r\n\t\t\te.Handled = true;\r\n\t\t}\r\n\r\n\t\t#region IsVisibleToUser\r\n\r\n\t\tpublic static Point[] GetPoints(Control control, bool relative = false)\r\n\t\t{\r\n\t\t\tif (control is null)\r\n\t\t\t\tthrow new ArgumentNullException(nameof(control));\r\n\t\t\tvar pos = relative\r\n\t\t\t\t? new Point(0, 0)\r\n\t\t\t\t// Get control position on the screen\r\n\t\t\t\t: control.PointToScreen(new Point(0, 0));\r\n\t\t\tvar pointsToCheck =\r\n\t\t\t\tnew Point[]\r\n\t\t\t\t\t{\r\n\t\t\t\t\t\t// Top-Left.\r\n\t\t\t\t\t\tpos,\r\n\t\t\t\t\t\t// Top-Right.\r\n\t\t\t\t\t\tnew Point(pos.X + control.ActualWidth - 1, pos.Y),\r\n\t\t\t\t\t\t// Bottom-Left.\r\n\t\t\t\t\t\tnew Point(pos.X, pos.Y + control.ActualHeight - 1),\r\n\t\t\t\t\t\t// Bottom-Right.\r\n\t\t\t\t\t\tnew Point(pos.X + control.ActualWidth - 1, pos.Y + control.ActualHeight - 1),\r\n\t\t\t\t\t\t// Middle-Centre.\r\n\t\t\t\t\t\tnew Point(pos.X + control.ActualWidth/2, pos.Y + control.ActualHeight/2)\r\n\t\t\t\t\t};\r\n\t\t\treturn pointsToCheck;\r\n\t\t}\r\n\r\n\t\t/*\r\n\t\tpublic static bool IsControlVisibleToUser(Control control)\r\n\t\t{\r\n\t\t\tif (control is null)\r\n\t\t\t\tthrow new ArgumentNullException(nameof(control));\r\n\t\t\tvar handle = (PresentationSource.FromVisual(control) as System.Windows.Interop.HwndSource)?.Handle;\r\n\t\t\tif (!handle.HasValue)\r\n\t\t\t\treturn false;\r\n\t\t\tvar children = GetAll<DependencyObject>(control, true);\r\n\t\t\t// Return true if any of the controls is visible.\r\n\t\t\tvar pointsToCheck = GetPoints(control, true);\r\n\t\t\tforeach (var p in pointsToCheck)\r\n\t\t\t{\r\n\t\t\t\t//var hwnd = NativeMethods.WindowFromPoint(p);\r\n\t\t\t\t//if (hwnd == IntPtr.Zero)\r\n\t\t\t\t//\tcontinue;\r\n\t\t\t\tvar result = VisualTreeHelper.HitTest(control, p);\r\n\t\t\t\tif (result is null)\r\n\t\t\t\t\tcontinue;\r\n\t\t\t\tif (children.Contains(result.VisualHit))\r\n\t\t\t\t\treturn true;\r\n\t\t\t\t//var other = Control.FromChildHandle(hwnd);\r\n\t\t\t\t//if (other is null)\r\n\t\t\t\t//\tcontinue;\r\n\t\t\t\t//if (GetAll(control, null, true).Contains(other))\r\n\t\t\t}\r\n\t\t\treturn false;\r\n\t\t}\r\n\t\t*/\r\n\t\t/// <summary>\r\n\t\t/// Get parent control of specific type.\r\n\t\t/// </summary>\r\n\t\tpublic static T GetParent<T>(DependencyObject control, bool includeTop = false) where T : class\r\n\t\t{\r\n\t\t\tif (control is null)\r\n\t\t\t\tthrow new ArgumentNullException(nameof(control));\r\n\t\t\tvar parent = control;\r\n\t\t\twhile (parent != null)\r\n\t\t\t{\r\n\t\t\t\tif (parent is T && (includeTop || parent != control))\r\n\t\t\t\t\treturn (T)(object)parent;\r\n\t\t\t\tvar p = VisualTreeHelper.GetParent(parent);\r\n\t\t\t\tif (p is null)\r\n\t\t\t\t\tp = LogicalTreeHelper.GetParent(parent);\r\n\t\t\t\tparent = p;\r\n\t\t\t}\r\n\t\t\treturn null;\r\n\t\t}\r\n\r\n\t\tpublic static void AddWeakHandlerOnWindowClosing(DependencyObject control, EventHandler<CancelEventArgs> handler)\r\n\t\t{\r\n\t\t\tvar w = GetParent<Window>(control);\r\n\t\t\tif (w is null)\r\n\t\t\t\treturn;\r\n\t\t\tWeakEventManager<Window, CancelEventArgs>.AddHandler(w, nameof(Window.Closing), handler);\r\n\t\t}\r\n\r\n\t\tpublic static void RemoveFromParent(FrameworkElement element)\r\n\t\t{\r\n\t\t\tif (element == null)\r\n\t\t\t\treturn;\r\n\t\t\tvar lParent = LogicalTreeHelper.GetParent(element);\r\n\t\t\tvar vParent = VisualTreeHelper.GetParent(element);\r\n\r\n\t\t\tif (vParent is ItemsControl items)\r\n\t\t\t\titems.Items.Remove(element);\r\n\t\t\tif (vParent is StackPanel panel)\r\n\t\t\t\tpanel.Children.Remove(element);\r\n\t\t\tif (vParent is Panel grid)\r\n\t\t\t\tgrid.Children.Remove(element);\r\n\t\t\tif (vParent is ContentPresenter window)\r\n\t\t\t\twindow.Content = null;\r\n\t\t\tif (vParent is Decorator border)\r\n\t\t\t\tborder.Child = null;\r\n\t\t\t// Remove visual and logical children.\r\n\t\t\tvar flags = System.Reflection.BindingFlags.Instance | System.Reflection.BindingFlags.NonPublic;\r\n\t\t\tif (vParent is FrameworkElement)\r\n\t\t\t{\r\n\t\t\t\tvar methodInfo = vParent.GetType().GetMethod(\"RemoveVisualChild\", flags);\r\n\t\t\t\tmethodInfo.Invoke(vParent, new object[] { element });\r\n\t\t\t}\r\n\t\t\tif (lParent is FrameworkElement)\r\n\t\t\t{\r\n\t\t\t\tvar methodInfo = lParent.GetType().GetMethod(\"RemoveLogicalChild\", flags);\r\n\t\t\t\tmethodInfo.Invoke(lParent, new object[] { element });\r\n\t\t\t}\r\n\t\t}\r\n\r\n\t\t/// <summary>\r\n\t\t/// Get all child controls with path.\r\n\t\t/// Use regex to make shorter tabbed path:\r\n\t\t/// var rx = new Regex(\"[^.]+[.]+\");\r\n\t\t/// var tabbedPath = rx.Replace(item.Path, \"\\t\");\r\n\t\t/// </summary>\r\n\t\tpublic static Dictionary<string, DependencyObject> GetAll(string path, DependencyObject control, Type type = null, bool includeTop = false)\r\n\t\t{\r\n\t\t\tvar controls = _GetAll(path, control, includeTop);\r\n\t\t\t// If type is set then...\r\n\t\t\tif (type is null)\r\n\t\t\t\treturn controls;\r\n\t\t\tvar filtered = type.IsInterface\r\n\t\t\t\t? controls.Where(x => x.Value.GetType().GetInterfaces().Contains(type))\r\n\t\t\t\t: controls.Where(x => type.IsAssignableFrom(x.Value.GetType()));\r\n\t\t\tvar results = filtered.ToDictionary(x => x.Key, y => y.Value);\r\n\t\t\treturn results;\r\n\t\t}\r\n\r\n\t\tprivate static Dictionary<string, DependencyObject> _GetAll(string path, DependencyObject control, bool includeTop = false)\r\n\t\t{\r\n\t\t\tif (control is null)\r\n\t\t\t\tthrow new ArgumentNullException(nameof(control));\r\n\t\t\t// Create new list.\r\n\t\t\tvar controls = new Dictionary<string, DependencyObject>();\r\n\t\t\tif (string.IsNullOrEmpty(path))\r\n\t\t\t\tpath = $\"{control.GetType().Name} {(control as FrameworkElement)?.Name}\".TrimEnd();\r\n\t\t\t// Add top control if required.\r\n\t\t\tif (includeTop && !controls.Values.Contains(control))\r\n\t\t\t{\r\n\t\t\t\tcontrols.Add(path, control);\r\n\t\t\t}\r\n\t\t\t// If control is Visual then then...\r\n\t\t\tif (control is Visual || control is System.Windows.Media.Media3D.Visual3D)\r\n\t\t\t{\r\n\t\t\t\tvar childrenCount = VisualTreeHelper.GetChildrenCount(control);\r\n\t\t\t\tfor (int i = 0; i < childrenCount; i++)\r\n\t\t\t\t{\r\n\t\t\t\t\tvar child = VisualTreeHelper.GetChild(control, i);\r\n\t\t\t\t\tvar childKey = $\"{path}[{i}].{child.GetType().Name} {(child as FrameworkElement)?.Name}\".TrimEnd();\r\n\t\t\t\t\t//controls.Add(childKey, child);\r\n\t\t\t\t\t// Get children of children.\r\n\t\t\t\t\tvar pairs = _GetAll(childKey, child, true);\r\n\t\t\t\t\tforeach (var pair in pairs)\r\n\t\t\t\t\t{\r\n\t\t\t\t\t\tif (!controls.ContainsValue(pair.Value))\r\n\t\t\t\t\t\t\tcontrols.Add(pair.Key, pair.Value);\r\n\t\t\t\t\t}\r\n\t\t\t\t}\r\n\t\t\t}\r\n\t\t\t// If contorl is FrameworkElement then...\r\n\t\t\tif (control is FrameworkElement || control is FrameworkContentElement)\r\n\t\t\t{\r\n\t\t\t\tvar logicalChildren = LogicalTreeHelper.GetChildren(control).OfType<DependencyObject>().ToList();\r\n\t\t\t\tfor (int i = 0; i < logicalChildren.Count; i++)\r\n\t\t\t\t{\r\n\t\t\t\t\tvar child = logicalChildren[i];\r\n\t\t\t\t\tvar childKey = $\"{path}[{i}].{child.GetType().Name} {(child as FrameworkElement)?.Name}\".TrimEnd();\r\n\t\t\t\t\t//controls.Add(childKey, child);\r\n\t\t\t\t\t// Get children of children.\r\n\t\t\t\t\tvar pairs = _GetAll(childKey, child, true);\r\n\t\t\t\t\tforeach (var pair in pairs)\r\n\t\t\t\t\t{\r\n\t\t\t\t\t\tif (!controls.ContainsValue(pair.Value))\r\n\t\t\t\t\t\t\tcontrols.Add(pair.Key, pair.Value);\r\n\t\t\t\t\t}\r\n\t\t\t\t}\r\n\t\t\t}\r\n\t\t\treturn controls;\r\n\t\t}\r\n\r\n\t\t/// <summary>\r\n\t\t/// Get all child controls.\r\n\t\t/// </summary>\r\n\t\tpublic static IEnumerable<DependencyObject> GetAll(DependencyObject control, Type type = null, bool includeTop = false)\r\n\t\t{\r\n\t\t\treturn GetAll(null, control, type, includeTop).Values.ToList();\r\n\t\t}\r\n\r\n\t\t/// <summary>\r\n\t\t/// Get all child controls.\r\n\t\t/// </summary>\r\n\t\tpublic static T[] GetAll<T>(FrameworkElement control, bool includeTop = false)\r\n\t\t{\r\n\t\t\tif (control is null)\r\n\t\t\t\treturn new T[0];\r\n\t\t\treturn GetAll(control, typeof(T), includeTop).Cast<T>().ToArray();\r\n\t\t}\r\n\r\n\t\tpublic static void GetActiveControl(FrameworkElement control, out FrameworkElement activeControl, out string activePath)\r\n\t\t{\r\n\t\t\tstring _activePath = null;\r\n\t\t\tInvoke(() =>\r\n\t\t\t{\r\n\t\t\t\t_activePath = string.Format(\"/{0}\", control?.Name);\r\n\t\t\t});\r\n\t\t\tactivePath = _activePath;\r\n\t\t\t// Return current control by default.\r\n\t\t\tactiveControl = control;\r\n\t\t\t// If control can contains active controls.\r\n\t\t\tvar container = control as DependencyObject;\r\n\t\t\twhile (container != null)\r\n\t\t\t{\r\n\t\t\t\tInvoke(() =>\r\n\t\t\t\t{\r\n\t\t\t\t\tcontrol = System.Windows.Input.FocusManager.GetFocusedElement(control) as FrameworkElement;\r\n\t\t\t\t});\r\n\t\t\t\tif (control is null)\r\n\t\t\t\t\tbreak;\r\n\t\t\t\tInvoke(() =>\r\n\t\t\t\t{\r\n\t\t\t\t\t_activePath = string.Format(\"/{0}\", control?.Name);\r\n\t\t\t\t});\r\n\r\n\t\t\t\tactivePath += _activePath;\r\n\t\t\t\tactiveControl = control;\r\n\t\t\t\tcontainer = control;\r\n\t\t\t}\r\n\t\t}\r\n\r\n\t\t#endregion\r\n\r\n\t\t#region Apply Grid Border Style\r\n\r\n\t\tpublic static void ApplyBorderStyle(DataGrid grid)\r\n\t\t{\r\n\t\t\tif (grid is null)\r\n\t\t\t\tthrow new ArgumentNullException(nameof(grid));\r\n\t\t\tgrid.Background = new SolidColorBrush(Color.FromRgb(255, 255, 255));\r\n\t\t\t//grid.BorderThickness = BorderStyle.None;\r\n\t\t\t//grid.EnableHeadersVisualStyles = false;\r\n\t\t\t//grid.ColumnHeadersBorderStyle = DataGridViewHeaderBorderStyle.Single;\r\n\t\t\t//grid.ColumnHeadersDefaultCellStyle.BackColor = SystemColors.Control;\r\n\t\t\t//grid.ColumnHeadersDefaultCellStyle.WrapMode = DataGridViewTriState.False;\r\n\t\t\t//grid.RowHeadersBorderStyle = DataGridViewHeaderBorderStyle.Single;\r\n\t\t\t//grid.RowHeadersDefaultCellStyle.BackColor = SystemColors.Control;\r\n\t\t\t//grid.BackColor = SystemColors.Window;\r\n\t\t\t//grid.DefaultCellStyle.BackColor = SystemColors.Window;\r\n\t\t\t//grid.CellPainting += Grid_CellPainting;\r\n\t\t\t//grid.SelectionChanged += Grid_SelectionChanged;\r\n\t\t\t//grid.CellFormatting += Grid_CellFormatting;\r\n\t\t\t//if (updateEnabledProperty)\r\n\t\t\t//\tgrid.CellClick += Grid_CellClick;\r\n\t\t}\r\n\r\n\t\t/*\r\n\t\tprivate static void Grid_CellClick(object sender, DataGridViewCellEventArgs e)\r\n\t\t{\r\n\t\t\tif (e.RowIndex < 0 || e.ColumnIndex < 0)\r\n\t\t\t\treturn;\r\n\t\t\tvar grid = (DataGridView)sender;\r\n\t\t\t// If add new record row.\r\n\t\t\tif (grid.AllowUserToAddRows && e.RowIndex + 1 == grid.Rows.Count)\r\n\t\t\t\treturn;\r\n\t\t\tvar column = grid.Columns[e.ColumnIndex];\r\n\t\t\tvar item = grid.Rows[e.RowIndex].DataBoundItem;\r\n\t\t\tif (column.DataPropertyName == \"Enabled\" || column.DataPropertyName == \"IsEnabled\")\r\n\t\t\t{\r\n\t\t\t\tSetEnabled(item, !GetEnabled(item));\r\n\t\t\t\tgrid.Invalidate();\r\n\t\t\t}\r\n\t\t}\r\n\r\n\t\tprivate static void Grid_CellFormatting(object sender, DataGridViewCellFormattingEventArgs e)\r\n\t\t{\r\n\t\t\tif (e.RowIndex < 0 || e.ColumnIndex < 0)\r\n\t\t\t\treturn;\r\n\t\t\tvar grid = (DataGridView)sender;\r\n\t\t\t// If add new record row.\r\n\t\t\tif (grid.AllowUserToAddRows && e.RowIndex + 1 == grid.Rows.Count)\r\n\t\t\t\treturn;\r\n\t\t\tvar row = grid.Rows[e.RowIndex];\r\n\t\t\tif (e.RowIndex > -1 && e.ColumnIndex > -1)\r\n\t\t\t{\r\n\t\t\t\tvar item = row.DataBoundItem;\r\n\t\t\t\t// If grid is virtual then...\r\n\t\t\t\tif (item is null)\r\n\t\t\t\t{\r\n\t\t\t\t\tvar list = grid.DataSource as IBindingList;\r\n\t\t\t\t\tif (list != null)\r\n\t\t\t\t\t\titem = list[e.RowIndex];\r\n\t\t\t\t}\r\n\t\t\t\tvar enabled = true;\r\n\t\t\t\tif (item != null)\r\n\t\t\t\t\tenabled = GetEnabled(item);\r\n\t\t\t\tvar fore = enabled ? grid.DefaultCellStyle.ForeColor : SystemColors.ControlDark;\r\n\t\t\t\tvar selectedBack = enabled ? grid.DefaultCellStyle.SelectionBackColor : SystemColors.ControlDark;\r\n\t\t\t\t// Apply style to row header.\r\n\t\t\t\tif (row.HeaderCell.Style.ForeColor != fore)\r\n\t\t\t\t\trow.HeaderCell.Style.ForeColor = fore;\r\n\t\t\t\tif (row.HeaderCell.Style.SelectionBackColor != selectedBack)\r\n\t\t\t\t\trow.HeaderCell.Style.SelectionBackColor = selectedBack;\r\n\t\t\t\t// Apply style to cell\r\n\t\t\t\tvar cell = grid.Rows[e.RowIndex].Cells[e.ColumnIndex];\r\n\t\t\t\tif (cell.Style.ForeColor != fore)\r\n\t\t\t\t\tcell.Style.ForeColor = fore;\r\n\t\t\t\tif (cell.Style.SelectionBackColor != selectedBack)\r\n\t\t\t\t\tcell.Style.SelectionBackColor = selectedBack;\r\n\t\t\t}\r\n\t\t}\r\n\r\n\t\tprivate static void Grid_SelectionChanged(object sender, EventArgs e)\r\n\t\t{\r\n\t\t\t// Sort issue with paint artifcats.\r\n\t\t\tvar grid = (DataGridView)sender;\r\n\t\t\tgrid.Invalidate();\r\n\t\t}\r\n\r\n\t\tprivate static void SetEnabled(object item, bool enabled)\r\n\t\t{\r\n\t\t\tvar enabledProperty = item.GetType().GetProperties().FirstOrDefault(x => x.Name == \"Enabled\" || x.Name == \"IsEnabled\");\r\n\t\t\tif (enabledProperty != null)\r\n\t\t\t{\r\n\t\t\t\tenabledProperty.SetValue(item, enabled, null);\r\n\t\t\t}\r\n\t\t}\r\n\r\n\t\tprivate static bool GetEnabled(object item)\r\n\t\t{\r\n\t\t\tvar enabledProperty = item.GetType().GetProperties().FirstOrDefault(x => x.Name == \"Enabled\" || x.Name == \"IsEnabled\");\r\n\t\t\tvar enabled = enabledProperty is null ? true : (bool)enabledProperty.GetValue(item, null);\r\n\t\t\treturn enabled;\r\n\t\t}\r\n\r\n\t\tprivate static void Grid_CellPainting(object sender, DataGridViewCellPaintingEventArgs e)\r\n\t\t{\r\n\t\t\t// Header and cell borders must be set to \"Single\" style.\r\n\t\t\tvar grid = (DataGridView)sender;\r\n\t\t\tvar firstVisibleColumn = grid.Columns.Cast<DataGridViewColumn>().Where(x => x.Displayed).Min(x => x.Index);\r\n\t\t\tvar lastVisibleColumn = grid.Columns.Cast<DataGridViewColumn>().Where(x => x.Displayed).Max(x => x.Index);\r\n\t\t\tvar selected = e.RowIndex > -1 ? grid.Rows[e.RowIndex].Selected : false;\r\n\t\t\te.Paint(e.CellBounds, DataGridViewPaintParts.All & ~DataGridViewPaintParts.Border);\r\n\t\t\tvar bounds = e.CellBounds;\r\n\t\t\tvar tl = new Point(bounds.X, bounds.Y);\r\n\t\t\tvar tr = new Point(bounds.X + bounds.Width - 1, bounds.Y);\r\n\t\t\tvar bl = new Point(bounds.X, bounds.Y + bounds.Height - 1);\r\n\t\t\tvar br = new Point(bounds.X + bounds.Width - 1, bounds.Y + bounds.Height - 1);\r\n\t\t\tColor backColor;\r\n\t\t\t// If top left corner and column header then...\r\n\t\t\tif (e.RowIndex == -1)\r\n\t\t\t{\r\n\t\t\t\tbackColor = selected\r\n\t\t\t\t\t? grid.ColumnHeadersDefaultCellStyle.SelectionBackColor\r\n\t\t\t\t\t: grid.ColumnHeadersDefaultCellStyle.BackColor;\r\n\t\t\t}\r\n\t\t\t// If row header then...\r\n\t\t\telse if (e.ColumnIndex == -1 && e.RowIndex > -1)\r\n\t\t\t{\r\n\t\t\t\tvar row = grid.Rows[e.RowIndex];\r\n\t\t\t\tbackColor = selected\r\n\t\t\t\t\t? row.HeaderCell.Style.SelectionBackColor\r\n\t\t\t\t\t: grid.RowHeadersDefaultCellStyle.BackColor;\r\n\t\t\t}\r\n\t\t\t// If normal cell then...\r\n\t\t\telse\r\n\t\t\t{\r\n\t\t\t\tvar row = grid.Rows[e.RowIndex];\r\n\t\t\t\tvar cell = row.Cells[e.ColumnIndex];\r\n\t\t\t\tbackColor = selected\r\n\t\t\t\t\t? cell.InheritedStyle.SelectionBackColor\r\n\t\t\t\t\t: cell.InheritedStyle.BackColor;\r\n\t\t\t}\r\n\t\t\t// Cell background colour.\r\n\t\t\tvar back = new Pen(backColor, 1);\r\n\t\t\t// Border colour.\r\n\t\t\tvar border = new Pen(SystemColors.Control, 1);\r\n\t\t\t// Do not draw borders for selected device.\r\n\t\t\tPen c;\r\n\t\t\t// Top\r\n\t\t\te.Graphics.DrawLine(back, tl, tr);\r\n\t\t\t// Left (only if not first)\r\n\t\t\tc = !selected && e.ColumnIndex > firstVisibleColumn ? border : back;\r\n\t\t\te.Graphics.DrawLine(c, bl, tl);\r\n\t\t\t// Right (always)\r\n\t\t\tc = back;\r\n\t\t\te.Graphics.DrawLine(c, tr, br);\r\n\t\t\t// Bottom (always)\r\n\t\t\tc = border;\r\n\t\t\te.Graphics.DrawLine(c, bl, br);\r\n\t\t\tback.Dispose();\r\n\t\t\tborder.Dispose();\r\n\t\t\te.Handled = true;\r\n\t\t}\r\n\r\n\t\t*/\r\n\r\n\t\t#endregion\r\n\r\n\t\t#region Data Grid Functions\r\n\r\n\t\t/// <summary>\r\n\t\t/// Get list of primary keys of items selected in the grid.\r\n\t\t/// </summary>\r\n\t\t/// <typeparam name=\"T\">Type of Primary key.</typeparam>\r\n\t\t/// <param name=\"grid\">Grid for getting selection</param>\r\n\t\t/// <param name=\"keyPropertyName\">Primary key name.</param>\r\n\t\tpublic static List<T> GetSelection<T>(DataGrid grid, string keyPropertyName = null)\r\n\t\t{\r\n\t\t\tif (grid is null)\r\n\t\t\t\tthrow new ArgumentNullException(nameof(grid));\r\n\t\t\tvar list = new List<T>();\r\n\t\t\tvar items = grid.SelectedItems.Cast<object>().ToArray();\r\n\t\t\t// If nothing selected then try to get rows from cells.\r\n\t\t\tif (items.Length == 0)\r\n\t\t\t\titems = grid.SelectedCells.Cast<DataGridCellInfo>().Select(x => x.Item).Distinct().ToArray();\r\n\t\t\t// If nothing selected then return.\r\n\t\t\tif (items.Length == 0)\r\n\t\t\t\treturn list;\r\n\t\t\tvar pi = GetPropertyInfo(keyPropertyName, items[0]);\r\n\t\t\tfor (var i = 0; i < items.Length; i++)\r\n\t\t\t{\r\n\t\t\t\tvar value = GetValue<T>(items[i], keyPropertyName, pi);\r\n\t\t\t\tlist.Add(value);\r\n\t\t\t}\r\n\t\t\treturn list;\r\n\t\t}\r\n\r\n\t\t[Obsolete(\"Use `bool SetSelection<T>(DataGrid grid, string keyPropertyName, List<T> list, int selectIndex = 0) instead.`\")]\r\n\t\tpublic static void RestoreSelection<T>(DataGrid grid, string keyPropertyName, List<T> list, bool selectFirst = true)\r\n\t\t\t=> SetSelection(grid, keyPropertyName, list, selectFirst ? 0 : -1);\r\n\r\n\t\t[Obsolete(\"Use `bool SetSelection<T>(DataGrid grid, string keyPropertyName, List<T> list, int selectIndex = 0)` instead.\")]\r\n\t\tpublic static void RestoreSelection<T>(DataGrid grid, string keyPropertyName, List<T> list, int selectIndex = 0)\r\n\t\t\t=> SetSelection(grid, keyPropertyName, list, selectIndex);\r\n\r\n\t\tpublic static bool SetSelection<T>(DataGrid grid, string keyPropertyName, List<T> list, int selectIndex = 0)\r\n\t\t{\r\n\t\t\tif (grid is null)\r\n\t\t\t\tthrow new ArgumentNullException(nameof(grid));\r\n\t\t\tvar items = grid.Items.Cast<object>().ToArray();\r\n\t\t\t// Return if grid is empty.\r\n\t\t\tif (items.Length == 0)\r\n\t\t\t\treturn false;\r\n\t\t\t// If something to restore then...\r\n\t\t\tif (list?.Count > 0)\r\n\t\t\t{\r\n\t\t\t\tvar selectedItems = new List<object>();\r\n\t\t\t\tvar pi = GetPropertyInfo(keyPropertyName, items[0]);\r\n\t\t\t\tfor (var i = 0; i < items.Length; i++)\r\n\t\t\t\t{\r\n\t\t\t\t\tvar item = items[i];\r\n\t\t\t\t\tvar val = GetValue<T>(item, keyPropertyName, pi);\r\n\t\t\t\t\tif (list.Contains(val))\r\n\t\t\t\t\t\tselectedItems.Add(item);\r\n\t\t\t\t}\r\n\t\t\t\tif (grid.SelectionMode == DataGridSelectionMode.Single)\r\n\t\t\t\t{\r\n\t\t\t\t\tgrid.SelectedItem = selectedItems.FirstOrDefault();\r\n\t\t\t\t}\r\n\t\t\t\telse\r\n\t\t\t\t{\r\n\t\t\t\t\t// Remove items which should not be selected.\r\n\t\t\t\t\tvar itemsToUnselect = grid.SelectedItems.Cast<object>().Except(selectedItems).ToArray();\r\n\t\t\t\t\tforeach (var item in itemsToUnselect)\r\n\t\t\t\t\t\tgrid.SelectedItems.Remove(item);\r\n\t\t\t\t\tvar itemsToSelect = selectedItems.Except(grid.SelectedItems.Cast<object>());\r\n\t\t\t\t\tforeach (var item in itemsToSelect)\r\n\t\t\t\t\t\tgrid.SelectedItems.Add(item);\r\n\t\t\t\t}\r\n\t\t\t}\r\n\t\t\t// If nothing was selected and must select index then...\r\n\t\t\tif (grid.SelectedItems.Count == 0 && selectIndex >= 0 && selectIndex < grid.Items.Count)\r\n\t\t\t\tgrid.SelectedItem = items[selectIndex];\r\n\t\t\treturn grid.SelectedItems.Count > 0;\r\n\t\t}\r\n\r\n\t\t#endregion\r\n\r\n\t\t#region TextBoxBase - EnableAutoScroll\r\n\r\n\t\tpublic static VerticalAlignment GetScrollVerticalAlignment(ScrollViewer control)\r\n\t\t{\r\n\t\t\t// Vertical scroll position.\r\n\t\t\tvar offset = control.VerticalOffset;\r\n\t\t\t// Vertical size of the scrollable content area.\r\n\t\t\tvar height = control.ExtentHeight;\r\n\t\t\t// Vertical size of the visible content area.\r\n\t\t\tvar visibleView = control.ViewportHeight;\r\n\t\t\t// Allow flexibility of 2 pixels.\r\n\t\t\tvar flex = 2;\r\n\t\t\tif (height - offset - visibleView < flex)\r\n\t\t\t\treturn VerticalAlignment.Bottom;\r\n\t\t\tif (offset < flex)\r\n\t\t\t\treturn VerticalAlignment.Top;\r\n\t\t\treturn VerticalAlignment.Center;\r\n\t\t}\r\n\r\n\t\tpublic static void AutoScroll(Control control)\r\n\t\t{\r\n\t\t\tScrollViewer sv = null;\r\n\t\t\tif (!(control is ScrollViewer))\r\n\t\t\t{\r\n\t\t\t\tvar all = GetAll<ScrollViewer>(control);\r\n\t\t\t\t// Try to get one with visible vertical bar first otherwise get default.\r\n\t\t\t\tsv = all\r\n\t\t\t\t\t.Where(x => x.ComputedVerticalScrollBarVisibility == Visibility.Visible)\r\n\t\t\t\t\t.FirstOrDefault() ?? all.FirstOrDefault();\r\n\t\t\t}\r\n\t\t\tif (sv != null)\r\n\t\t\t{\r\n\t\t\t\tvar scrollPosition = GetScrollVerticalAlignment(sv);\r\n\t\t\t\tif (scrollPosition == VerticalAlignment.Bottom && control.IsVisible)\r\n\t\t\t\t\tsv.ScrollToEnd();\r\n\t\t\t}\r\n\t\t}\r\n\r\n\t\tpublic static void EnableAutoScroll(TextBoxBase control, bool enable = true)\r\n\t\t{\r\n\t\t\tcontrol.TextChanged -= TextBoxBase_TextChanged;\r\n\t\t\tcontrol.IsVisibleChanged -= TextBoxBase_IsVisibleChanged;\r\n\t\t\tcontrol.Unloaded -= TextBoxBase_Unloaded;\r\n\t\t\tif (enable)\r\n\t\t\t{\r\n\t\t\t\tcontrol.TextChanged += TextBoxBase_TextChanged;\r\n\t\t\t\tcontrol.IsVisibleChanged += TextBoxBase_IsVisibleChanged;\r\n\t\t\t\tcontrol.Unloaded += TextBoxBase_Unloaded;\r\n\t\t\t}\r\n\t\t}\r\n\r\n\t\tprivate static void TextBoxBase_Unloaded(object sender, RoutedEventArgs e)\r\n\t\t\t=> EnableAutoScroll((TextBox)sender, false);\r\n\r\n\t\tprivate static void TextBoxBase_TextChanged(object sender, TextChangedEventArgs e)\r\n\t\t\t=> AutoScroll((TextBox)sender);\r\n\r\n\t\tprivate static void TextBoxBase_IsVisibleChanged(object sender, DependencyPropertyChangedEventArgs e)\r\n\t\t\t=> AutoScroll((TextBox)sender);\r\n\r\n\t\t#endregion\r\n\r\n\t\t#region TextBoxBase - AppendText - Logging\r\n\r\n\t\tpublic static void AppendText(TextBox control, string text, int maxSize = 65535)\r\n\t\t{\r\n\t\t\t// Check for a null control\r\n\t\t\tif (control == null) throw new ArgumentNullException(nameof(control));\r\n\r\n\t\t\t// Invoke UI thread if necessary, to perform UI updates\r\n\t\t\tAppInvoke(() =>\r\n\t\t\t{\r\n\t\t\t\t// Calculate new text size\r\n\t\t\t\tvar newTextSize = System.Text.Encoding.UTF8.GetByteCount(control.Text + text);\r\n\t\t\t\t// Ensure the final text size does not exceed maxSize\r\n\t\t\t\tif (newTextSize > maxSize)\r\n\t\t\t\t{\r\n\t\t\t\t\tvar lines = control.Text.Split(new[] { Environment.NewLine }, StringSplitOptions.None);\r\n\t\t\t\t\tint linesToRemove = 0;\r\n\t\t\t\t\tint sizeRemoved = 0;\r\n\r\n\t\t\t\t\t// Determine how many lines to remove from the start to stay within maxSize\r\n\t\t\t\t\twhile (sizeRemoved < newTextSize - maxSize && linesToRemove < lines.Length)\r\n\t\t\t\t\t{\r\n\t\t\t\t\t\tsizeRemoved += System.Text.Encoding.UTF8.GetByteCount(lines[linesToRemove] + Environment.NewLine);\r\n\t\t\t\t\t\tlinesToRemove++;\r\n\t\t\t\t\t}\r\n\t\t\t\t\t// Rebuild the remaining text after removing oldest lines\r\n\t\t\t\t\tvar remainingText = string.Join(Environment.NewLine, lines, linesToRemove, lines.Length - linesToRemove);\r\n\t\t\t\t\tcontrol.Text = remainingText;\r\n\t\t\t\t}\r\n\t\t\t\t// Append the new text\r\n\t\t\t\tif (control.Text.Length > 0)\r\n\t\t\t\t\tcontrol.AppendText(Environment.NewLine + text);\r\n\t\t\t\telse\r\n\t\t\t\t\tcontrol.AppendText(text);\r\n\t\t\t});\r\n\t\t}\r\n\r\n\t\t#endregion\r\n\r\n\t\tpublic static void AppInvoke(Action action)\r\n\t\t{\r\n\t\t\t// Check if we are on the UI thread\r\n\t\t\tif (Application.Current.Dispatcher.CheckAccess())\r\n\t\t\t{\r\n\t\t\t\t// If on UI thread, update the UI elements directly\r\n\t\t\t\taction.Invoke();\r\n\t\t\t}\r\n\t\t\telse\r\n\t\t\t{\r\n\t\t\t\t// If not on UI thread, invoke on the UI thread\r\n\t\t\t\tApplication.Current.Dispatcher.Invoke(action);\r\n\t\t\t}\r\n\t\t}\r\n\r\n\t\tpublic static void AppBeginInvoke(Action action)\r\n\t\t{\r\n\t\t\t// Check if we are on the UI thread\r\n\t\t\tif (Application.Current.Dispatcher.CheckAccess())\r\n\t\t\t{\r\n\t\t\t\t// If on UI thread, update the UI elements directly\r\n\t\t\t\t//_ = action.BeginInvoke(action.EndInvoke, null);\r\n\t\t\t\tApplication.Current.Dispatcher.BeginInvoke(action);\r\n\t\t\t}\r\n\t\t\telse\r\n\t\t\t{\r\n\t\t\t\t// If not on UI thread, invoke on the UI thread\r\n\t\t\t\tApplication.Current.Dispatcher.BeginInvoke(action);\r\n\t\t\t}\r\n\t\t}\r\n\r\n\t\t// Contains unique list of control IDs for the applicaiton.\r\n\t\tprivate static SortedSet<int> LoadedControls = new SortedSet<int>();\r\n\r\n\t\t/// <summary>\r\n\t\t/// Returnd false if displayed in desing mode (IDE).\r\n\t\t/// Return true if control is not in the list of loaded controls.\r\n\t\t/// Add control to the list of loaded controls.\r\n\t\t/// IMPORTANT! Must be used in pair with AllowUnload.\r\n\t\t/// </summary>\r\n\t\tpublic static bool AllowLoad(FrameworkElement control)\r\n\t\t{\r\n\t\t\tif (IsDesignMode(control))\r\n\t\t\t\treturn false;\r\n\t\t\tvar code = control.GetHashCode();\r\n\t\t\treturn LoadedControls.Add(code);\r\n\t\t}\r\n\r\n\t\t/// <summary>\r\n\t\t/// Returnd false if displayed in desing mode (IDE).\r\n\t\t/// Return true if control is in the list of loaded controls.\r\n\t\t/// Remove control from the list of loaded controls.\r\n\t\t/// IMPORTANT! Must be used in pair with AllowLoad.\r\n\t\t/// </summary>\r\n\t\tpublic static bool AllowUnload(FrameworkElement control)\r\n\t\t{\r\n\t\t\tif (IsDesignMode(control))\r\n\t\t\t\treturn false;\r\n\t\t\tvar code = control.GetHashCode();\r\n\t\t\treturn LoadedControls.Remove(code);\r\n\t\t}\r\n\r\n\r\n\t\t/// <summary>\r\n\t\t/// Returnd false if displayed in desing mode (IDE).\r\n\t\t/// Return true if control is in the list of loaded controls.\r\n\t\t/// Remove control from the list of loaded controls.\r\n\t\t/// </summary>\r\n\t\tpublic static bool IsLoaded(FrameworkElement control)\r\n\t\t{\r\n\t\t\tif (IsDesignMode(control))\r\n\t\t\t\treturn false;\r\n\t\t\tvar code = control.GetHashCode();\r\n\t\t\treturn LoadedControls.Contains(code);\r\n\t\t}\r\n\r\n\t\t#region File Explorer Behaviour\r\n\r\n\t\t/*\r\n\t\tBehavior:\r\n\r\n\t\t1. Selecting a row by clicking on the row (excluding the checkbox) should select the row and check its box.\r\n\t\t2. Deselecting a row by clicking on the selected row (excluding the checkbox) should deselect the row and uncheck its box.\r\n\t\t3. Checking the box should only affect its associated row, selecting it. All other rows and checkboxes should remain unaffected.\r\n\t\t4. Unchecking the box should only deselect its associated row. All other rows and checkboxes should remain unaffected.\r\n\r\n\t\tIn summary, both selection and multi-selection operate normally and mirrored on checkboxes.\r\n\t\tChecking or unchecking a box affects the selection of its associated row only.\r\n\t\t\r\n\r\n\t\t<!--  Used for File Explorer selection behaviour  -->\r\n\t\t<DataGridTemplateColumn x:Name=\"IsCheckedColumn\" Width=\"Auto\" CanUserSort=\"False\">\r\n\t\t\t<DataGridTemplateColumn.CellTemplate>\r\n\t\t\t\t<DataTemplate>\r\n\t\t\t\t\t<CheckBox x:Name=\"IsCheckedCheckBox\" IsChecked=\"{Binding IsSelected, Mode=TwoWay, RelativeSource={RelativeSource FindAncestor, AncestorType=DataGridRow}}\" PreviewMouseDown=\"CheckBox_PreviewMouseDown\" />\r\n\t\t\t\t</DataTemplate>\r\n\t\t\t</DataGridTemplateColumn.CellTemplate>\r\n\t\t\t<DataGridTemplateColumn.Header>\r\n\t\t\t\t<CheckBox\r\n\t\t\t\t\tx:Name=\"IsCheckedColumnCheckBox\"\r\n\t\t\t\t\tMargin=\"0\"\r\n\t\t\t\t\tPadding=\"0\"\r\n\t\t\t\t\tIsEnabled=\"False\" />\r\n\t\t\t</DataGridTemplateColumn.Header>\r\n\t\t</DataGridTemplateColumn>\r\n\t\t \r\n\t\t */\r\n\r\n\t\tstatic T GetParent<T>(DependencyObject source) where T : class\r\n\t\t{\r\n\t\t\twhile (source != null && !(source is T))\r\n\t\t\t\tsource = VisualTreeHelper.GetParent(source);\r\n\t\t\treturn source as T;\r\n\t\t}\r\n\r\n\t\t/// <summary>\r\n\t\t/// Workaround: Without this event, \"mouse down\" will select the checkbox, but \"mouse up\" will deselect it immediately.\r\n\t\t/// </summary>\r\n\t\tpublic static void FileExplorer_DataGrid_CheckBox_PreviewMouseDown(object sender, MouseButtonEventArgs e)\r\n\t\t{\r\n\t\t\tvar checkBox = (CheckBox)sender;\r\n\t\t\tvar dataGridRow = GetParent<DataGridRow>((DependencyObject)e.OriginalSource);\r\n\t\t\tif (dataGridRow != null)\r\n\t\t\t{\r\n\t\t\t\tdataGridRow.IsSelected = !(checkBox.IsChecked == true);\r\n\t\t\t\te.Handled = true;\r\n\t\t\t}\r\n\t\t}\r\n\r\n\t\t#endregion\r\n\r\n\t\t/// <summary>\r\n\t\t/// Checks if the specified control within its parent TabControls is selected.\r\n\t\t/// </summary>\r\n\t\t/// <param name=\"control\">The control to check for selection.</param>\r\n\t\t/// <returns>True if the TabItem is selected, otherwise false.</returns>\r\n\t\tpublic static bool IsTabItemSelected(FrameworkElement control)\r\n\t\t{\r\n\t\t\tvar parent = control.Parent as FrameworkElement;\r\n\t\t\twhile (parent != null)\r\n\t\t\t{\r\n\t\t\t\tif (parent is TabItem tabItem)\r\n\t\t\t\t{\r\n\t\t\t\t\tif (!tabItem.IsSelected)\r\n\t\t\t\t\t\treturn false;\r\n\t\t\t\t}\r\n\t\t\t\telse if (parent is TabControl tabControl)\r\n\t\t\t\t{\r\n\t\t\t\t\tforeach (TabItem item in tabControl.Items)\r\n\t\t\t\t\t{\r\n\t\t\t\t\t\tif (item.Content == control)\r\n\t\t\t\t\t\t{\r\n\t\t\t\t\t\t\tif (!item.IsSelected)\r\n\t\t\t\t\t\t\t\treturn false;\r\n\t\t\t\t\t\t\tbreak;\r\n\t\t\t\t\t\t}\r\n\t\t\t\t\t}\r\n\t\t\t\t}\r\n\t\t\t\tparent = parent.Parent as FrameworkElement;\r\n\t\t\t}\r\n\t\t\treturn true;\r\n\t\t}\r\n\r\n\t\t/// <summary>\r\n\t\t/// Ensures that the specified control is selected within its parent TabControls.\r\n\t\t/// </summary>\r\n\t\t/// <param name=\"control\">The control to be selected.</param>\r\n\t\tpublic static void EnsureTabItemSelected(FrameworkElement control)\r\n\t\t{\r\n\t\t\tif (IsTabItemSelected(control))\r\n\t\t\t\treturn;\r\n\t\t\tvar parent = control.Parent as FrameworkElement;\r\n\t\t\twhile (parent != null)\r\n\t\t\t{\r\n\t\t\t\tif (parent is TabItem tabItem)\r\n\t\t\t\t{\r\n\t\t\t\t\tif (!tabItem.IsSelected)\r\n\t\t\t\t\t\ttabItem.IsSelected = true;\r\n\t\t\t\t}\r\n\t\t\t\telse if (parent is TabControl tabControl)\r\n\t\t\t\t{\r\n\t\t\t\t\tforeach (TabItem item in tabControl.Items)\r\n\t\t\t\t\t{\r\n\t\t\t\t\t\tif (item.Content == control)\r\n\t\t\t\t\t\t{\r\n\t\t\t\t\t\t\tif (!item.IsSelected)\r\n\t\t\t\t\t\t\t\titem.IsSelected = true;\r\n\t\t\t\t\t\t\tbreak;\r\n\t\t\t\t\t\t}\r\n\t\t\t\t\t}\r\n\t\t\t\t}\r\n\t\t\t\tparent = parent.Parent as FrameworkElement;\r\n\t\t\t}\r\n\t\t}\r\n\r\n\t}\r\n}\r\n"
  },
  {
    "path": "FocusLogger/JocysCom/Controls/ControlsHelper.cs",
    "content": "﻿using System;\r\nusing System.Collections.Concurrent;\r\nusing System.Data;\r\nusing System.Diagnostics;\r\nusing System.Linq;\r\nusing System.Reflection;\r\nusing System.Runtime.InteropServices;\r\nusing System.Threading;\r\nusing System.Threading.Tasks;\r\n\r\nnamespace JocysCom.ClassLibrary.Controls\r\n{\r\n\tpublic static partial class ControlsHelper\r\n\t{\r\n\t\t#region Invoke and BeginInvoke\r\n\r\n\t\t/// <summary>\r\n\t\t/// Call this method from main form constructor for BeginInvoke to work.\r\n\t\t/// </summary>\r\n\t\tpublic static void InitInvokeContext()\r\n\t\t{\r\n\t\t\tif (MainTaskScheduler != null)\r\n\t\t\t\treturn;\r\n\t\t\t_MainThreadId = Thread.CurrentThread.ManagedThreadId;\r\n\t\t\t// Create a TaskScheduler that wraps the SynchronizationContext returned from\r\n\t\t\t// System.Threading.SynchronizationContext.Current\r\n\t\t\tMainTaskScheduler = TaskScheduler.FromCurrentSynchronizationContext();\r\n\t\t}\r\n\r\n\t\t/// <summary>\r\n\t\t/// Object that handles the low-level work of queuing tasks onto main User Interface (GUI) thread.\r\n\t\t/// </summary>\r\n\t\tpublic static TaskScheduler MainTaskScheduler { get; private set; }\r\n\r\n\t\tpublic static int MainThreadId => _MainThreadId;\r\n\t\tprivate static int _MainThreadId;\r\n\r\n\t\tpublic static bool InvokeRequired\r\n\t\t\t=> _MainThreadId != Thread.CurrentThread.ManagedThreadId;\r\n\r\n\t\t/*\r\n\r\n\t\tpublic static void TestTasks(TaskCreationOptions childOptions)\r\n\t\t{\r\n\t\t\tvar i = 5000000;\r\n\t\t\tConsole.WriteLine(\"//\");\r\n\t\t\tConsole.WriteLine(\"TestTasks(TaskCreationOptions.{0});\", childOptions);\r\n\t\t\tConsole.WriteLine(\"// Parent starting\");\r\n\t\t\tvar parent = Task.Factory.StartNew(() =>\r\n\t\t\t{\r\n\t\t\t\tConsole.WriteLine(\"// Parent started\");\r\n\t\t\t\tConsole.WriteLine(\"// Child starting\");\r\n\t\t\t\tvar child = Task.Factory.StartNew(() =>\r\n\t\t\t\t{\r\n\t\t\t\t\tConsole.WriteLine(\"// Child started\");\r\n\t\t\t\t\tThread.SpinWait(i);\r\n\t\t\t\t\tConsole.WriteLine(\"// Child completing\");\r\n\t\t\t\t}, childOptions);\r\n\t\t\t\t//child.Wait();\r\n\t\t\t\t//Console.WriteLine(\"// Child completed\");\r\n\t\t\t\tConsole.WriteLine(\"// Parent completing\");\r\n\t\t\t});\r\n\t\t\tparent.Wait();\r\n\t\t\tConsole.WriteLine(\"// Parent completed\");\r\n\t\t\tThread.SpinWait(i * 4);\r\n\t\t}\r\n\t\t\r\n\t\t// Attached and Detached Child Tasks.\r\n\t\t//\r\n\t\t// TaskCreationOptions.AttachedToParent:\r\n\t\t//\r\n\t\t//     - Parent task waits for child tasks to complete.\r\n\t\t//     - Parent task propagates exceptions thrown by child tasks.\r\n\t\t//     - Status of parent task depends on status of child task.\r\n\t    //\r\n\t\tTestTasks(TaskCreationOptions.AttachedToParent);\r\n\t\t//\r\n\t\t// Parent starting\r\n\t\t// Parent started\r\n\t\t// Child starting\r\n\t\t// Parent completing\r\n\t\t// Child started\r\n\t\t// Child completing\r\n\t\t// Parent completed\r\n\t\t//\r\n\t\tTestTasks(TaskCreationOptions.None);\r\n\t\t//\r\n\t\t// Parent starting\r\n\t\t// Parent started\r\n\t\t// Child starting\r\n\t\t// Parent completing\r\n\t\t// Parent completed\r\n\t\t// Child started\r\n\t\t// Child completing\r\n\r\n\t\t*/\r\n\r\n\t\t/// <summary>Executes the specified action delegate asynchronously on main Graphical User Interface (GUI) Thread.</summary>\r\n\t\t/// <param name=\"action\">The action delegate to execute asynchronously.</param>\r\n\t\t/// <returns>The started System.Threading.Tasks.Task.</returns>\r\n\t\tpublic static Task BeginInvoke(Action action, int? millisecondsDelay = null)\r\n\t\t{\r\n\t\t\tif (millisecondsDelay.HasValue)\r\n\t\t\t{\r\n\t\t\t\treturn Task.Run(async () =>\r\n\t\t\t\t{\r\n\t\t\t\t\t// Wait 1 second, which will allow to release the button.\r\n\t\t\t\t\t// Logical delay without blocking the current hardware thread.\r\n\t\t\t\t\tawait Task.Delay(millisecondsDelay.Value).ConfigureAwait(true);\r\n\t\t\t\t\tawait BeginInvoke(action);\r\n\t\t\t\t});\r\n\t\t\t}\r\n\t\t\tInitInvokeContext();\r\n\t\t\treturn Task.Factory.StartNew(action,\r\n\t\t\t\tCancellationToken.None, TaskCreationOptions.DenyChildAttach, MainTaskScheduler);\r\n\t\t}\r\n\r\n\t\t/// <summary>Executes the specified action delegate asynchronously on main User Interface (UI) Thread.</summary>\r\n\t\t/// <param name=\"method\">The delegate to execute asynchronously.</param>\r\n\t\t/// <param name=\"args\">The arguments to pass to the delegate.</param>\r\n\t\t/// <returns>The started System.Threading.Tasks.Task.</returns>\r\n\t\tpublic static Task BeginInvoke(Delegate method, params object[] args)\r\n\t\t{\r\n\t\t\tInitInvokeContext();\r\n\t\t\treturn Task.Factory.StartNew(() => { method.DynamicInvoke(args); },\r\n\t\t\t\tCancellationToken.None, TaskCreationOptions.DenyChildAttach, MainTaskScheduler);\r\n\t\t}\r\n\r\n\t\t/// <summary>Executes the specified action delegate synchronously on main Graphical User Interface (GUI) Thread.</summary>\r\n\t\t/// <param name=\"action\">The action delegate to execute synchronously.</param>\r\n\t\tpublic static void Invoke(Action action)\r\n\t\t{\r\n\t\t\tif (action is null)\r\n\t\t\t\tthrow new ArgumentNullException(nameof(action));\r\n\t\t\tInitInvokeContext();\r\n\t\t\tif (InvokeRequired)\r\n\t\t\t{\r\n\t\t\t\tvar t = new Task(action);\r\n\t\t\t\tt.RunSynchronously(MainTaskScheduler);\r\n\t\t\t}\r\n\t\t\telse\r\n\t\t\t{\r\n\t\t\t\taction.DynamicInvoke();\r\n\t\t\t}\r\n\t\t}\r\n\r\n\t\t/// <summary>Executes the specified action delegate synchronously on main Graphical User Interface (GUI) Thread.</summary>\r\n\t\t/// <param name=\"method\">The delegate to execute synchronously.</param>\r\n\t\t/// <param name=\"args\">The arguments to pass to the delegate.</param>\r\n\t\tpublic static object Invoke(Delegate method, params object[] args)\r\n\t\t{\r\n\t\t\tif (method is null)\r\n\t\t\t\tthrow new ArgumentNullException(nameof(method));\r\n\t\t\t// Run method on main Graphical User Interface thread.\r\n\t\t\tif (InvokeRequired)\r\n\t\t\t{\r\n\t\t\t\tvar t = new Task<object>(() => method.DynamicInvoke(args));\r\n\t\t\t\tt.RunSynchronously(MainTaskScheduler);\r\n\t\t\t\treturn t.Result;\r\n\t\t\t}\r\n\t\t\telse\r\n\t\t\t{\r\n\t\t\t\treturn method.DynamicInvoke(args);\r\n\t\t\t}\r\n\t\t}\r\n\r\n\t\t#endregion\r\n\r\n\t\t#region Open Path or URL\r\n\r\n\t\tpublic static void OpenUrl(string url)\r\n\t\t{\r\n\t\t\ttry\r\n\t\t\t{\r\n\t\t\t\tif (RuntimeInformation.IsOSPlatform(OSPlatform.Windows))\r\n\t\t\t\t\tProcess.Start(new ProcessStartInfo(url) { UseShellExecute = true });\r\n\t\t\t\telse if (RuntimeInformation.IsOSPlatform(OSPlatform.Linux))\r\n\t\t\t\t\tProcess.Start(\"xdg-open\", url);\r\n\t\t\t\telse if (RuntimeInformation.IsOSPlatform(OSPlatform.OSX))\r\n\t\t\t\t\tProcess.Start(\"open\", url);\r\n\t\t\t}\r\n\t\t\tcatch (System.ComponentModel.Win32Exception winEx)\r\n\t\t\t{\r\n\t\t\t\tif (winEx.ErrorCode == -2147467259)\r\n\t\t\t\t\tMessageBoxShow(winEx.Message);\r\n\t\t\t}\r\n\t\t\tcatch (System.Exception ex)\r\n\t\t\t{\r\n\t\t\t\tMessageBoxShow(ex.Message);\r\n\t\t\t}\r\n\t\t}\r\n\r\n\t\tprivate static void MessageBoxShow(string message)\r\n\t\t{\r\n#if NETFRAMEWORK // .NET Framework\r\n\t\t\t// Requires: PresentationFramework.dll\r\n\t\t\tSystem.Windows.MessageBox.Show(message);\r\n#else\r\n\t\t\tSystem.Windows.MessageBox.Show(message);\r\n#endif\r\n\t\t}\r\n\r\n\t\t/// <summary>\r\n\t\t/// Open file with associated program.\r\n\t\t/// </summary>\r\n\t\t/// <param name=\"path\">file to open.</param>\r\n\t\tpublic static void OpenPath(string path, string arguments = null)\r\n\t\t{\r\n\t\t\ttry\r\n\t\t\t{\r\n\r\n\t\t\t\tvar psi = new System.Diagnostics.ProcessStartInfo();\r\n\t\t\t\tif (Uri.TryCreate(path, UriKind.Absolute, out Uri uri) && uri.Scheme != Uri.UriSchemeFile)\r\n\t\t\t\t{\r\n\t\t\t\t\t// Open URL\r\n\t\t\t\t\tpsi.UseShellExecute = true;\r\n\t\t\t\t\tpsi.FileName = uri.AbsoluteUri;\r\n\t\t\t\t\tif (arguments != null)\r\n\t\t\t\t\t\tpsi.Arguments = arguments;\r\n\t\t\t\t\tpsi.WorkingDirectory = Environment.GetFolderPath(Environment.SpecialFolder.UserProfile);\r\n\t\t\t\t}\r\n\t\t\t\telse\r\n\t\t\t\t{\r\n\t\t\t\t\t// Open file/directory\r\n\t\t\t\t\tpsi.FileName = path;\r\n\t\t\t\t\tif (arguments != null)\r\n\t\t\t\t\t\tpsi.Arguments = arguments;\r\n\t\t\t\t\tvar fi = new System.IO.FileInfo(path);\r\n\t\t\t\t\tpsi.UseShellExecute = true;\r\n\t\t\t\t\tpsi.ErrorDialog = true;\r\n\t\t\t\t\tpsi.WorkingDirectory = fi.Directory?.FullName ?? Environment.CurrentDirectory;\r\n\t\t\t\t}\r\n\t\t\t\tSystem.Diagnostics.Process.Start(psi);\r\n\t\t\t}\r\n\t\t\tcatch { }\r\n\t\t}\r\n\r\n\t\t#endregion\r\n\r\n\t\tpublic static PropertyInfo GetPrimaryKeyPropertyInfo(object item)\r\n\t\t{\r\n\t\t\tif (item is null)\r\n\t\t\t\treturn null;\r\n\t\t\tvar t = item.GetType();\r\n\t\t\tPropertyInfo pi = null;\r\n#if NETFRAMEWORK\r\n\t\t\t// Try to find property by EntityFramework EdmScalarPropertyAttribute (System.Data.Entity.dll).\r\n\t\t\tpi = t.GetProperties()\r\n\t\t\t\t.Where(x =>\r\n\t\t\t\t\tx.GetCustomAttributes(typeof(System.Data.Objects.DataClasses.EdmScalarPropertyAttribute), true)\r\n\t\t\t\t\t.Cast<System.Data.Objects.DataClasses.EdmScalarPropertyAttribute>()\r\n\t\t\t\t\t.Any(a => a.EntityKeyProperty))\r\n\t\t\t\t.FirstOrDefault();\r\n\t\t\tif (pi != null)\r\n\t\t\t\treturn pi;\r\n#else\r\n\t\t\t// Try to find property by KeyAttribute.\r\n\t\t\tpi = t.GetProperties()\r\n\t\t\t\t.Where(x => Attribute.IsDefined(x, typeof(System.ComponentModel.DataAnnotations.KeyAttribute), true))\r\n\t\t\t\t.FirstOrDefault();\r\n\t\t\tif (pi != null)\r\n\t\t\t\treturn pi;\r\n#endif\r\n\t\t\treturn null;\r\n\t\t}\r\n\r\n\t\t/// <summary>\r\n\t\t/// Get DataViewRow, DataRow or item property value.\r\n\t\t/// </summary>\r\n\t\t/// <typeparam name=\"T\">Return value type.</typeparam>\r\n\t\t/// <param name=\"item\">DataViewRow, DataRow or another type.</param>\r\n\t\t/// <param name=\"keyPropertyName\">Data property or column name.</param>\r\n\t\t/// <param name=\"pi\">Optional property info cache.</param>\r\n\t\t/// <returns></returns>\r\n\t\tprivate static T GetValue<T>(object item, string keyPropertyName, PropertyInfo pi = null)\r\n\t\t{\r\n\t\t\t// Return object value if property info supplied.\r\n\t\t\tif (pi != null)\r\n\t\t\t\treturn (T)pi.GetValue(item, null);\r\n\t\t\t// Get DataRow.\r\n\t\t\tvar row = item is System.Data.DataRowView rowView\r\n\t\t\t\t? rowView.Row\r\n\t\t\t\t: (System.Data.DataRow)item;\r\n\t\t\t// Return DataRow value.\r\n\t\t\treturn row.IsNull(keyPropertyName)\r\n\t\t\t\t\t? default\r\n\t\t\t\t\t: (T)row[keyPropertyName];\r\n\t\t}\r\n\r\n\t\t/// <summary>\r\n\t\t///  Get Property info \r\n\t\t/// </summary>\r\n\t\t/// <param name=\"keyPropertyName\"></param>\r\n\t\t/// <param name=\"item\"></param>\r\n\t\tprivate static PropertyInfo GetPropertyInfo(string keyPropertyName, object item)\r\n\t\t{\r\n\t\t\t// Get property info if not DataRowView or DataRow.\r\n\t\t\tPropertyInfo pi = null;\r\n\t\t\tif (!(item is DataRowView) && !(item is DataRow))\r\n\t\t\t\tpi = string.IsNullOrEmpty(keyPropertyName)\r\n\t\t\t\t\t? GetPrimaryKeyPropertyInfo(item)\r\n\t\t\t\t\t: item.GetType().GetProperty(keyPropertyName);\r\n\t\t\treturn pi;\r\n\t\t}\r\n\r\n\t\t#region Add cool downs to controls.\r\n\r\n\t\t// Default cool-down 1 second.\r\n\t\tpublic static TimeSpan ControlCooldown = new TimeSpan(0, 0, 1);\r\n\r\n\t\tpublic static ConcurrentDictionary<int, DateTime> ControlCooldowns { get; } = new ConcurrentDictionary<int, DateTime>();\r\n\r\n\t\t/// <summary>\r\n\t\t/// Returns true if control is on cool-down.\r\n\t\t/// </summary>\r\n\t\t/// <param name=\"control\">Control to check.</param>\r\n\t\tpublic static bool IsOnCooldown(object control, int? milliseconds = null)\r\n\t\t{\r\n\t\t\tlock (ControlCooldowns)\r\n\t\t\t{\r\n\t\t\t\tvar now = DateTime.UtcNow;\r\n\t\t\t\tint hashCode = control.GetHashCode();\r\n\t\t\t\t// Cleanup expired cooldowns.\r\n\t\t\t\tvar expiredKeys = ControlCooldowns.Where(kv => now > kv.Value).Select(kv => kv.Key).ToList();\r\n\t\t\t\tforeach (var key in expiredKeys)\r\n\t\t\t\t\tControlCooldowns.TryRemove(key, out _);\r\n\t\t\t\t// If on cool-down then...\r\n\t\t\t\tif (ControlCooldowns.ContainsKey(hashCode))\r\n\t\t\t\t\treturn true;\r\n\t\t\t\tvar newTime = milliseconds.HasValue\r\n\t\t\t\t\t? now.AddMilliseconds(milliseconds.Value)\r\n\t\t\t\t\t: now.Add(ControlCooldown);\r\n\t\t\t\tControlCooldowns.TryAdd(hashCode, newTime);\r\n\t\t\t\treturn false;\r\n\t\t\t}\r\n\t\t}\r\n\r\n\t\t#endregion\r\n\r\n\t}\r\n}\r\n"
  },
  {
    "path": "FocusLogger/JocysCom/Controls/InfoControl.xaml",
    "content": "﻿<UserControl\r\n\tx:Class=\"JocysCom.ClassLibrary.Controls.InfoControl\"\r\n\txmlns=\"http://schemas.microsoft.com/winfx/2006/xaml/presentation\"\r\n\txmlns:x=\"http://schemas.microsoft.com/winfx/2006/xaml\"\r\n\txmlns:d=\"http://schemas.microsoft.com/expression/blend/2008\"\r\n\txmlns:mc=\"http://schemas.openxmlformats.org/markup-compatibility/2006\"\r\n\tLoaded=\"UserControl_Loaded\"\r\n\tUnloaded=\"UserControl_Unloaded\"\r\n\tmc:Ignorable=\"d\">\r\n\t<UserControl.Resources>\r\n\t\t<ResourceDictionary>\r\n\t\t\t<ResourceDictionary.MergedDictionaries>\r\n\t\t\t\t<ResourceDictionary Source=\"Themes/Default.xaml\" />\r\n\t\t\t\t<ResourceDictionary Source=\"Themes/Icons.xaml\" />\r\n\t\t\t</ResourceDictionary.MergedDictionaries>\r\n\t\t</ResourceDictionary>\r\n\t</UserControl.Resources>\r\n\t<StackPanel x:Name=\"InfoPanel\">\r\n\t\t<Grid Grid.Row=\"0\" Background=\"{DynamicResource {x:Static SystemColors.InfoBrushKey}}\">\r\n\t\t\t<Grid.RowDefinitions>\r\n\t\t\t\t<RowDefinition Height=\"Auto\" />\r\n\t\t\t\t<RowDefinition Height=\"Auto\" />\r\n\t\t\t\t<RowDefinition Height=\"*\" />\r\n\t\t\t</Grid.RowDefinitions>\r\n\t\t\t<Grid.ColumnDefinitions>\r\n\t\t\t\t<ColumnDefinition Width=\"Auto\" />\r\n\t\t\t\t<ColumnDefinition Width=\"*\" />\r\n\t\t\t\t<ColumnDefinition Width=\"Auto\" />\r\n\t\t\t</Grid.ColumnDefinitions>\r\n\t\t\t<Label\r\n\t\t\t\tName=\"HeadLabel\"\r\n\t\t\t\tGrid.Row=\"0\"\r\n\t\t\t\tGrid.Column=\"0\"\r\n\t\t\t\tGrid.ColumnSpan=\"2\"\r\n\t\t\t\tMargin=\"8,3,3,3\"\r\n\t\t\t\tPadding=\"0\"\r\n\t\t\t\tx:FieldModifier=\"public\"\r\n\t\t\t\tContent=\"[HeadLabel]\"\r\n\t\t\t\tFontWeight=\"Bold\" />\r\n\t\t\t<ContentControl\r\n\t\t\t\tName=\"LeftIcon\"\r\n\t\t\t\tGrid.Row=\"1\"\r\n\t\t\t\tGrid.Column=\"0\"\r\n\t\t\t\tWidth=\"24\"\r\n\t\t\t\tHeight=\"24\"\r\n\t\t\t\tMargin=\"8,0,3,3\"\r\n\t\t\t\tPadding=\"0\"\r\n\t\t\t\tContent=\"{StaticResource Icon_Information}\"\r\n\t\t\t\tFocusable=\"False\" />\r\n\t\t\t<TextBox\r\n\t\t\t\tName=\"BodyLabel\"\r\n\t\t\t\tGrid.Row=\"1\"\r\n\t\t\t\tGrid.Column=\"1\"\r\n\t\t\t\tMargin=\"3,0,3,3\"\r\n\t\t\t\tPadding=\"0\"\r\n\t\t\t\tVerticalAlignment=\"Center\"\r\n\t\t\t\tx:FieldModifier=\"public\"\r\n\t\t\t\tBackground=\"Transparent\"\r\n\t\t\t\tFocusable=\"False\"\r\n\t\t\t\tIsReadOnly=\"True\"\r\n\t\t\t\tMaxLines=\"2\"\r\n\t\t\t\tText=\"[BodyLabel]\"\r\n\t\t\t\tTextWrapping=\"Wrap\" />\r\n\t\t\t<ContentControl\r\n\t\t\t\tName=\"RightIcon\"\r\n\t\t\t\tGrid.Row=\"0\"\r\n\t\t\t\tGrid.RowSpan=\"2\"\r\n\t\t\t\tGrid.Column=\"2\"\r\n\t\t\t\tWidth=\"48\"\r\n\t\t\t\tHeight=\"48\"\r\n\t\t\t\tMargin=\"4,4,8,4\"\r\n\t\t\t\tPadding=\"0\"\r\n\t\t\t\tx:FieldModifier=\"public\"\r\n\t\t\t\tContent=\"{StaticResource Icon_Information}\" />\r\n\t\t\t<ContentControl\r\n\t\t\t\tName=\"BusyIcon\"\r\n\t\t\t\tGrid.RowSpan=\"2\"\r\n\t\t\t\tGrid.Column=\"2\"\r\n\t\t\t\tWidth=\"48\"\r\n\t\t\t\tHeight=\"48\"\r\n\t\t\t\tMargin=\"4,4,8,4\"\r\n\t\t\t\tPadding=\"0\"\r\n\t\t\t\tContent=\"{StaticResource Icon_ProcessRight}\"\r\n\t\t\t\tVisibility=\"Hidden\">\r\n\t\t\t\t<ContentControl.RenderTransformOrigin>\r\n\t\t\t\t\t<Point X=\"0.5\" Y=\"0.5\" />\r\n\t\t\t\t</ContentControl.RenderTransformOrigin>\r\n\t\t\t\t<ContentControl.RenderTransform>\r\n\t\t\t\t\t<RotateTransform Angle=\"0\" />\r\n\t\t\t\t</ContentControl.RenderTransform>\r\n\t\t\t</ContentControl>\r\n\t\t\t<Label\r\n\t\t\t\tName=\"BusyCount\"\r\n\t\t\t\tGrid.Row=\"0\"\r\n\t\t\t\tGrid.RowSpan=\"2\"\r\n\t\t\t\tGrid.Column=\"2\"\r\n\t\t\t\tWidth=\"48\"\r\n\t\t\t\tHeight=\"48\"\r\n\t\t\t\tMargin=\"4,4,8,4\"\r\n\t\t\t\tPadding=\"0\"\r\n\t\t\t\tHorizontalContentAlignment=\"Center\"\r\n\t\t\t\tVerticalContentAlignment=\"Center\"\r\n\t\t\t\tFocusable=\"False\"\r\n\t\t\t\tOpacity=\"0.5\" />\r\n\t\t</Grid>\r\n\t</StackPanel>\r\n</UserControl>\r\n"
  },
  {
    "path": "FocusLogger/JocysCom/Controls/InfoControl.xaml.cs",
    "content": "﻿using JocysCom.ClassLibrary.Controls.Themes;\r\nusing System;\r\nusing System.ComponentModel;\r\nusing System.Reflection;\r\nusing System.Threading.Tasks;\r\nusing System.Windows;\r\nusing System.Windows.Controls;\r\nusing System.Windows.Media;\r\nusing System.Windows.Media.Animation;\r\n\r\nnamespace JocysCom.ClassLibrary.Controls\r\n{\r\n\t/// <summary>\r\n\t/// Interaction logic for InfoControl.xaml\r\n\t/// </summary>\r\n\tpublic partial class InfoControl : UserControl\r\n\t{\r\n\r\n\r\n\t\tpublic InfoControl()\r\n\t\t{\r\n\t\t\tInitHelper.InitTimer(this, InitializeComponent);\r\n\t\t\tif (ControlsHelper.IsDesignMode(this))\r\n\t\t\t\treturn;\r\n\t\t\t// Get assemblies which will be used to select default (fists) and search for resources.\r\n\t\t\tvar assembly = Assembly.GetEntryAssembly() ?? Assembly.GetExecutingAssembly();\r\n\t\t\t//var company = ((AssemblyCompanyAttribute)Attribute.GetCustomAttribute(assembly, typeof(AssemblyCompanyAttribute)))?.Company;\r\n\t\t\tvar product = ((AssemblyProductAttribute)Attribute.GetCustomAttribute(assembly, typeof(AssemblyProductAttribute)))?.Product;\r\n\t\t\tvar description = ((AssemblyDescriptionAttribute)Attribute.GetCustomAttribute(assembly, typeof(AssemblyDescriptionAttribute)))?.Description;\r\n\t\t\tDefaultHead = product;\r\n\t\t\tDefaultBody = description;\r\n\t\t\tReset();\r\n\t\t\tCreateBusyIconAnimation();\r\n\t\t\t// InitRotation();\r\n\t\t\tHelpProvider.OnMouseEnter += HelpProvider_OnMouseEnter;\r\n\t\t\tHelpProvider.OnMouseLeave += HelpProvider_OnMouseLeave;\r\n\r\n\t\t}\r\n\r\n\t\tpublic DoubleAnimationUsingKeyFrames animationBusyIcon = new DoubleAnimationUsingKeyFrames();\r\n\t\tpublic Storyboard storyboardBusyIcon = new Storyboard();\r\n\t\tprivate void CreateBusyIconAnimation()\r\n\t\t{\r\n\t\t\t// Animation properties.\r\n\t\t\tStoryboard.SetTarget(animationBusyIcon, BusyIcon);\r\n\t\t\tStoryboard.SetTargetProperty(animationBusyIcon, new PropertyPath(\"(UIElement.RenderTransform).(RotateTransform.Angle)\"));\r\n\t\t\tanimationBusyIcon.KeyFrames.Add(new DiscreteDoubleKeyFrame(0, KeyTime.FromTimeSpan(TimeSpan.FromSeconds(0))));\r\n\t\t\tanimationBusyIcon.KeyFrames.Add(new LinearDoubleKeyFrame(360, KeyTime.FromTimeSpan(TimeSpan.FromSeconds(6))));\r\n\t\t\t// Storyboard properties.\r\n\t\t\tstoryboardBusyIcon.RepeatBehavior = RepeatBehavior.Forever;\r\n\t\t\tstoryboardBusyIcon.Children.Add(animationBusyIcon);\r\n\t\t\tstoryboardBusyIcon.Begin();\r\n\t\t}\r\n\r\n\t\tprivate void HelpProvider_OnMouseEnter(object sender, EventArgs e)\r\n\t\t{\r\n\t\t\tvar control = (Control)sender;\r\n\t\t\tvar head = HelpProvider.GetHelpHead(control);\r\n\t\t\tvar body = HelpProvider.GetHelpBody(control, BodyMaxLength, true);\r\n\t\t\tvar image = HelpProvider.GetHelpImage(control);\r\n\t\t\tSetHead(head);\r\n\t\t\tSetBody(image, body);\r\n\t\t}\r\n\t\tpublic int BodyMaxLength { get; set; } = int.MaxValue;\r\n\r\n\t\tprivate void HelpProvider_OnMouseLeave(object sender, EventArgs e)\r\n\t\t{\r\n\t\t\tReset();\r\n\t\t}\r\n\r\n\t\tpublic InfoHelpProvider HelpProvider { get; set; } = new InfoHelpProvider();\r\n\r\n\t\t#region ■ Properties\r\n\r\n\t\tpublic string DefaultHead { get; set; }\r\n\t\tpublic string DefaultBody { get; set; }\r\n\r\n\t\tpublic object RightIconContent\r\n\t\t{\r\n\t\t\tget => RightIcon.GetValue(ContentProperty);\r\n\t\t\tset => RightIcon.SetValue(ContentProperty, value);\r\n\t\t}\r\n\r\n\t\tobject _Image;\r\n\r\n\t\tpublic void SetImage(object resource)\r\n\t\t{\r\n\t\t\t_Image = resource;\r\n\t\t\tRightIcon.Content = _Image;\r\n\t\t}\r\n\r\n\t\t#endregion\r\n\r\n\t\t#region Set Text\r\n\r\n\t\tpublic void Reset()\r\n\t\t{\r\n\t\t\tSetHead(DefaultHead);\r\n\t\t\tSetBodyInfo(DefaultBody);\r\n\t\t}\r\n\r\n\t\tpublic void SetTitle(string format, params object[] args)\r\n\t\t{\r\n\t\t\tvar win = System.Windows.Window.GetWindow(this);\r\n\t\t\tif (win is null)\r\n\t\t\t\treturn;\r\n\t\t\twin.Title = (args.Length == 0)\r\n\t\t\t\t? format\r\n\t\t\t\t: string.Format(format, args);\r\n\t\t}\r\n\r\n\t\tpublic void SetHead(string format, params object[] args)\r\n\t\t{\r\n\t\t\t// Apply format.\r\n\t\t\tif (format is null)\r\n\t\t\t\tformat = DefaultHead;\r\n\t\t\telse if (args.Length > 0)\r\n\t\t\t\tformat = string.Format(format, args);\r\n\t\t\tif (HeadLabel.Content as string != format)\r\n\t\t\t\tHeadLabel.Content = format;\r\n\t\t}\r\n\r\n\t\tpublic void SetBodyError(string content, params object[] args)\r\n\t\t{\r\n\t\t\t// Apply format.\r\n\t\t\tif (content is null)\r\n\t\t\t\tcontent = DefaultBody;\r\n\t\t\telse if (args.Length > 0)\r\n\t\t\t\tcontent = string.Format(content, args);\r\n\t\t\t// Set info with time.\r\n\t\t\tSetBody(MessageBoxImage.Error, \"{0: yyyy-MM-dd HH:mm:ss}: {1}\", DateTime.Now, content);\r\n\t\t}\r\n\r\n\t\tpublic void SetBodyInfo(string content, params object[] args)\r\n\t\t{\r\n\t\t\t// Apply format.\r\n\t\t\tif (content is null)\r\n\t\t\t\tcontent = DefaultBody;\r\n\t\t\telse if (args.Length > 0)\r\n\t\t\t\tcontent = string.Format(content, args);\r\n\t\t\t// Set info with time.\r\n\t\t\tSetBody(MessageBoxImage.Information, content);\r\n\t\t}\r\n\r\n\t\tpublic async void SetWithTimeout(MessageBoxImage image, string content = null, params object[] args)\r\n\t\t{\r\n\t\t\tSetBody(image, content, args);\r\n\t\t\tvar bodyText = BodyLabel.Text;\r\n\t\t\t// The average minimal reading speed for adults is 16 characters per second.\r\n\t\t\t// Use reading speed for adults as 14 characters per second.\r\n\t\t\t// Add 4 extra seconds for realization and focus.\r\n\t\t\tvar waitSeconds = 4 + bodyText.Length / 14.0;\r\n\t\t\t// Task code which waits for waitSeconds and executes code below.\r\n\t\t\tawait Task.Delay(TimeSpan.FromSeconds(waitSeconds));\r\n\t\t\tif (bodyText == BodyLabel.Text)\r\n\t\t\t\tReset();\r\n\t\t}\r\n\r\n\r\n\t\tpublic void SetBody(MessageBoxImage image, string content = null, params object[] args)\r\n\t\t{\r\n\t\t\tif (content is null)\r\n\t\t\t\tcontent = DefaultBody;\r\n\t\t\telse if (args.Length > 0)\r\n\t\t\t\tcontent = string.Format(content, args);\r\n\t\t\tBodyLabel.Text = content;\r\n\t\t\t// Set body color and icon.\r\n\t\t\tswitch (image)\r\n\t\t\t{\r\n\t\t\t\tcase MessageBoxImage.Error:\r\n\t\t\t\t\tBodyLabel.Foreground = new SolidColorBrush(Colors.DarkRed);\r\n\t\t\t\t\tLeftIcon.Content = Icons.Current[Icons.Icon_Error];\r\n\t\t\t\t\tbreak;\r\n\t\t\t\tcase MessageBoxImage.Question:\r\n\t\t\t\t\tBodyLabel.Foreground = new SolidColorBrush(Colors.DarkBlue);\r\n\t\t\t\t\tLeftIcon.Content = Icons.Current[Icons.Icon_Question];\r\n\t\t\t\t\tbreak;\r\n\t\t\t\tcase MessageBoxImage.Warning:\r\n\t\t\t\t\tBodyLabel.Foreground = new SolidColorBrush(Colors.DarkOrange);\r\n\t\t\t\t\tLeftIcon.Content = Icons.Current[Icons.Icon_Warning];\r\n\t\t\t\t\tbreak;\r\n\t\t\t\tdefault:\r\n\t\t\t\t\tBodyLabel.Foreground = SystemColors.ControlTextBrush;\r\n\t\t\t\t\tLeftIcon.Content = Icons.Current[Icons.Icon_Information];\r\n\t\t\t\t\tbreak;\r\n\t\t\t}\r\n\t\t}\r\n\r\n\t\t#endregion\r\n\r\n\t\t#region Task and Rotating Icon\r\n\r\n\t\tprivate readonly object TasksLock = new object();\r\n\t\tpublic readonly BindingList<object> Tasks = new BindingList<object>();\r\n\r\n\t\t/// <summary>Activate busy spinner.</summary>\r\n\t\tpublic void AddTask(object name)\r\n\t\t{\r\n\t\t\tlock (TasksLock)\r\n\t\t\t{\r\n\t\t\t\tif (!Tasks.Contains(name))\r\n\t\t\t\t\tTasks.Add(name);\r\n\t\t\t\tUpdateIcon();\r\n\t\t\t}\r\n\t\t}\r\n\r\n\t\t/// <summary>Deactivate busy spinner if all tasks are gone.</summary>\r\n\t\tpublic void RemoveTask(object name)\r\n\t\t{\r\n\t\t\tlock (TasksLock)\r\n\t\t\t{\r\n\t\t\t\tif (Tasks.Contains(name))\r\n\t\t\t\t\tTasks.Remove(name);\r\n\t\t\t\tUpdateIcon();\r\n\t\t\t}\r\n\t\t}\r\n\r\n\t\tpublic void UpdateIcon()\r\n\t\t{\r\n\t\t\tDispatcher.Invoke(() =>\r\n\t\t\t{\r\n\t\t\t\tBusyCount.Content = Tasks.Count > 1 ? $\"{Tasks.Count}\" : \"\";\r\n\t\t\t\tif (Tasks.Count > 0)\r\n\t\t\t\t{\r\n\t\t\t\t\tBusyIcon.Visibility = Visibility.Visible;\r\n\t\t\t\t\tRightIcon.Visibility = Visibility.Hidden;\r\n\t\t\t\t\tstoryboardBusyIcon.Begin();\r\n\t\t\t\t}\r\n\t\t\t\telse\r\n\t\t\t\t{\r\n\t\t\t\t\tstoryboardBusyIcon.Pause();\r\n\t\t\t\t\tBusyIcon.Visibility = Visibility.Hidden;\r\n\t\t\t\t\tRightIcon.Visibility = Visibility.Visible;\r\n\t\t\t\t}\r\n\t\t\t});\r\n\t\t}\r\n\r\n\t\t#endregion\r\n\r\n\t\tprivate void UserControl_Loaded(object sender, RoutedEventArgs e)\r\n\t\t{\r\n\t\t\tif (!ControlsHelper.AllowLoad(this))\r\n\t\t\t\treturn;\r\n\t\t}\r\n\r\n\t\tprivate void UserControl_Unloaded(object sender, RoutedEventArgs e)\r\n\t\t{\r\n\t\t\tif (!ControlsHelper.AllowUnload(this))\r\n\t\t\t\treturn;\r\n\t\t\t_Image = null;\r\n\t\t}\r\n\r\n\t}\r\n}\r\n"
  },
  {
    "path": "FocusLogger/JocysCom/Controls/InfoHelpProvider.cs",
    "content": "﻿using System;\r\nusing System.Collections.Generic;\r\nusing System.Text.RegularExpressions;\r\nusing System.Windows;\r\n\r\nnamespace JocysCom.ClassLibrary.Controls\r\n{\r\n\tpublic class InfoHelpProvider\r\n\t{\r\n\t\t/// <summary>\r\n\t\t/// Dictionary to hold controls and their corresponding help texts.\r\n\t\t/// </summary>\r\n\t\tpublic Dictionary<UIElement, object[]> Controls { get; set; } = new Dictionary<UIElement, object[]>();\r\n\r\n\t\t/// <summary>\r\n\t\t/// Event to trigger when mouse enters any control from `Controls`.\r\n\t\t/// </summary>\r\n\t\tpublic event EventHandler OnMouseEnter;\r\n\r\n\t\t/// <summary>\r\n\t\t/// Event to trigger when mouse leaves all controls from `Control`.\r\n\t\t/// </summary>\r\n\t\tpublic event EventHandler OnMouseLeave;\r\n\r\n\t\tpublic string GetHelpHead(UIElement control)\r\n\t\t{\r\n\t\t\tif (!Controls.ContainsKey(control))\r\n\t\t\t\treturn \"\";\r\n\t\t\treturn (string)Controls[control][0];\r\n\t\t}\r\n\r\n\t\tpublic MessageBoxImage GetHelpImage(UIElement control)\r\n\t\t{\r\n\t\t\tif (!Controls.ContainsKey(control))\r\n\t\t\t\treturn MessageBoxImage.Information;\r\n\t\t\treturn (MessageBoxImage)Controls[control][2];\r\n\t\t}\r\n\r\n\t\tpublic string GetHelpBody(UIElement control, int? maxLength = null, bool removeMultispace = false)\r\n\t\t{\r\n\t\t\tif (!Controls.ContainsKey(control))\r\n\t\t\t\treturn \"\";\r\n\t\t\tvar body = (string)Controls[control][1];\r\n\t\t\tif (removeMultispace)\r\n\t\t\t\tbody = RemoveMultispace(body);\r\n\t\t\tif (maxLength != null)\r\n\t\t\t\tbody = CropText(body, maxLength);\r\n\t\t\treturn body;\r\n\t\t}\r\n\r\n\t\t// Method to add control and its help text to HelpControls and attach MouseEnter and MouseLeave events\r\n\t\tpublic void Add(UIElement control, string helpHead, string helpBody = \"\", MessageBoxImage image = MessageBoxImage.Information)\r\n\t\t{\r\n\t\t\tif (Controls.ContainsKey(control))\r\n\t\t\t\tControls.Remove(control);\r\n\t\t\tControls.Add(control, new object[] { helpHead, helpBody, image });\r\n\t\t\tcontrol.MouseEnter += (s, e) => OnMouseEnter?.Invoke(s, e);\r\n\t\t\tcontrol.MouseLeave += (s, e) =>\r\n\t\t\t{\r\n\t\t\t\tbool isMouseOutsideAll = true;\r\n\t\t\t\tforeach (var item in Controls.Keys)\r\n\t\t\t\t{\r\n\t\t\t\t\tif (item.IsMouseOver)\r\n\t\t\t\t\t{\r\n\t\t\t\t\t\tisMouseOutsideAll = false;\r\n\t\t\t\t\t\tbreak;\r\n\t\t\t\t\t}\r\n\t\t\t\t}\r\n\t\t\t\tif (isMouseOutsideAll) OnMouseLeave?.Invoke(s, e);\r\n\t\t\t};\r\n\t\t}\r\n\r\n\t\t// Method to remove control from HelpControls and detach MouseEnter and MouseLeave events\r\n\t\tpublic void Remove(UIElement control)\r\n\t\t{\r\n\t\t\tif (!Controls.ContainsKey(control))\r\n\t\t\t\treturn;\r\n\t\t\tcontrol.MouseEnter -= (s, e) => OnMouseEnter?.Invoke(s, e);\r\n\t\t\tcontrol.MouseLeave -= (s, e) =>\r\n\t\t\t{\r\n\t\t\t\tbool isMouseOutsideAll = true;\r\n\t\t\t\tforeach (var item in Controls.Keys)\r\n\t\t\t\t{\r\n\t\t\t\t\tif (item.IsMouseOver)\r\n\t\t\t\t\t{\r\n\t\t\t\t\t\tisMouseOutsideAll = false;\r\n\t\t\t\t\t\tbreak;\r\n\t\t\t\t\t}\r\n\t\t\t\t}\r\n\t\t\t\tif (isMouseOutsideAll) OnMouseLeave?.Invoke(s, e);\r\n\t\t\t};\r\n\t\t\tControls.Remove(control);\r\n\t\t}\r\n\r\n\t\tpublic const int CropTextDefauldMaxLength = 128;\r\n\r\n\t\t/// <summary>\r\n\t\t/// if maxLength == -1, return string.Empty\r\n\t\t/// if maxLength == 0 return s\r\n\t\t/// </summary>\r\n\t\t/// <param name=\"so\"></param>\r\n\t\t/// <param name=\"maxLength\"></param>\r\n\t\t/// <returns></returns>\r\n\t\tpublic static string CropText(object so, int? maxLength = 0)\r\n\t\t{\r\n\t\t\tvar s = string.Format(\"{0}\", so);\r\n\t\t\tif (!maxLength.HasValue)\r\n\t\t\t\tmaxLength = CropTextDefauldMaxLength;\r\n\t\t\tif (string.IsNullOrEmpty(s) || maxLength == -1)\r\n\t\t\t\treturn \"\";\r\n\t\t\tif (maxLength == 0)\r\n\t\t\t\treturn s;\r\n\t\t\tif (maxLength == 0)\r\n\t\t\t\tmaxLength = CropTextDefauldMaxLength;\r\n\t\t\tif (s.Length > maxLength)\r\n\t\t\t{\r\n\t\t\t\ts = s.Substring(0, maxLength.Value - 3);\r\n\t\t\t\t// Find last separator and crop there...\r\n\t\t\t\tvar ls = s.LastIndexOf(' ');\r\n\t\t\t\tif (ls > 0) s = s.Substring(0, ls);\r\n\t\t\t\ts += \"...\";\r\n\t\t\t}\r\n\t\t\treturn s;\r\n\t\t}\r\n\r\n\t\tpublic static readonly Regex RxMultiSpace = new Regex(\"[ \\u00A0\\r\\n\\t]+\", RegexOptions.Multiline);\r\n\r\n\t\tpublic static string RemoveMultispace(string s)\r\n\t\t{\r\n\t\t\tif (string.IsNullOrEmpty(s))\r\n\t\t\t\treturn s;\r\n\t\t\t// Replace multiple spaces.\r\n\t\t\ts = RxMultiSpace.Replace(s, \" \");\r\n\t\t\treturn s;\r\n\t\t}\r\n\r\n\r\n\r\n\t}\r\n}\r\n"
  },
  {
    "path": "FocusLogger/JocysCom/Controls/InitHelper.cs",
    "content": "﻿using System;\r\nusing System.Collections.Generic;\r\nusing System.Diagnostics;\r\nusing System.Windows;\r\nusing System.Linq;\r\n\r\nnamespace JocysCom.ClassLibrary.Controls\r\n{\r\n\t/// <summary>\r\n\t/// Helps to output initialize stats when form initialize.\r\n\t/// </summary>\r\n\tpublic class InitHelper\r\n\t{\r\n\r\n\t\tpublic InitHelper()\r\n\t\t{\r\n\t\t\t_Timer = new System.Timers.Timer();\r\n\t\t\t_Timer.AutoReset = false;\r\n\t\t\t_Timer.Interval = 2000;\r\n\t\t\t_Timer.Elapsed += _Timer_Elapsed;\r\n\t\t}\r\n\r\n\t\tinternal FrameworkElement Control;\r\n\t\tinternal DateTime StartDate;\r\n\t\tinternal DateTime EndDate;\r\n\t\tinternal int _PropertyChangedCount;\r\n\t\tSystem.Timers.Timer _Timer;\r\n\r\n\t\tpublic void WriteLine(string prefix)\r\n\t\t{\r\n\t\t\tvar s = string.Format(\"-4-> {0,4}. {1} - {2}: {3} changes\",\r\n\t\t\t\t_InitEndCount, prefix, Control.GetType(), _PropertyChangedCount);\r\n\t\t\tif (EndDate.Ticks > 0)\r\n\t\t\t\ts += string.Format(\", {0:# ##0} ms\", EndDate.Subtract(StartDate).TotalMilliseconds);\r\n\t\t\tDebug.WriteLine(s);\r\n\t\t}\r\n\r\n\t\t#region ■ Static \r\n\r\n\t\tinternal static int _InitEndCount;\r\n\t\tprivate static object TimersLock = new object();\r\n\t\tprivate static List<InitHelper> Timers = new List<InitHelper>();\r\n\r\n\t\tpublic static List<string> LoadedControlNames = new List<string>();\r\n\r\n\t\tpublic static bool IsDebug\r\n\t\t{\r\n\t\t\tget\r\n\t\t\t{\r\n#if DEBUG\r\n\t\t\t\treturn true;\r\n#else\r\n\t\t\t\treturn false;\r\n#endif\r\n\t\t\t}\r\n\t\t}\r\n\r\n\t\t/// <summary>\r\n\t\t/// In release mode invoke only, in debug mode use times and monitor object.\r\n\t\t/// </summary>\r\n\t\tpublic static void InitTimer(FrameworkElement control, Action InitializeComponent)\r\n\t\t{\r\n\t\t\tif (!IsDebug)\r\n\t\t\t{\r\n\t\t\t\tInitializeComponent.Invoke();\r\n\t\t\t\treturn;\r\n\t\t\t}\r\n\t\t\tvar ih = new InitHelper();\r\n\t\t\tih.Control = control;\r\n\t\t\tih.StartDate = DateTime.Now;\r\n\t\t\tih.WriteLine(\"INIT START\");\r\n\t\t\tih.EndDate = DateTime.Now;\r\n\t\t\tInitializeComponent.Invoke();\r\n\t\t\tih.EndDate = DateTime.Now;\r\n\t\t\tih.WriteLine(\"INIT CON  \");\r\n\t\t\tlock (TimersLock)\r\n\t\t\t\tTimers.Add(ih);\r\n\t\t\tih.Control.Loaded += Control_Loaded;\r\n\t\t\tih.Control.Unloaded += Control_Unloaded;\r\n\t\t\tih.Control.IsVisibleChanged += Control_IsVisibleChanged;\r\n\t\t\t//ih.Control.PropertyChanged += Control_PropertyChanged;\r\n\t\t\tih._Timer.Start();\r\n\t\t}\r\n\r\n\t\tprivate static void Control_Loaded(object sender, RoutedEventArgs e)\r\n\t\t{\r\n\t\t\tvar control = (FrameworkElement)sender;\r\n\t\t\tvar name = $\"{control.GetType()} {control.Name} {control.GetHashCode()}\";\r\n\t\t\tif (LoadedControlNames.Contains(name))\r\n\t\t\t\tConsole.WriteLine($\"WARN: Control is loaded already: {name}\");\r\n\t\t\tLoadedControlNames.Add(name);\r\n\t\t}\r\n\r\n\t\tprivate static void Control_Unloaded(object sender, RoutedEventArgs e)\r\n\t\t{\r\n\t\t\tvar control = (FrameworkElement)sender;\r\n\t\t\tvar name = $\"{control.GetType()} {control.Name} {control.GetHashCode()}\";\r\n\t\t\tLoadedControlNames.Remove(name);\r\n\t\t\tDebug.WriteLine($\"-5-> LoadedControlNames.Count = {LoadedControlNames.Count()} // Unloaded: {name}\");\r\n\t\t\t// Remove events.\r\n\t\t\tcontrol.Loaded -= Control_Loaded;\r\n\t\t\tcontrol.Unloaded -= Control_Unloaded;\r\n\t\t\tcontrol.IsVisibleChanged -= Control_IsVisibleChanged;\r\n\t\t\t//control.PropertyChanged -= Control_PropertyChanged;\r\n\t\t}\r\n\r\n\t\tprivate static void Control_IsVisibleChanged(object sender, System.Windows.DependencyPropertyChangedEventArgs e)\r\n\t\t{\r\n\t\t\tRestartTimer(sender);\r\n\t\t}\r\n\r\n\t\t/// <summary>\r\n\t\t/// Xamarin.\r\n\t\t/// </summary>\r\n\t\tprivate static void Control_PropertyChanged(object sender, EventArgs e)\r\n\t\t{\r\n\t\t\tRestartTimer(sender);\r\n\t\t}\r\n\r\n\r\n\t\tprivate static void RestartTimer(object sender)\r\n\t\t{\r\n\t\t\tInitHelper ih = null;\r\n\t\t\tlock (TimersLock)\r\n\t\t\t\tih = Timers.FirstOrDefault(x => Equals(x.Control, sender));\r\n\t\t\tif (ih is null)\r\n\t\t\t\treturn;\r\n\t\t\tih._PropertyChangedCount++;\r\n\t\t\tih.EndDate = DateTime.Now;\r\n\t\t\tih._Timer.Stop();\r\n\t\t\tih._Timer.Start();\r\n\t\t}\r\n\r\n\t\t/// <summary>\r\n\t\t/// This function will trigger once, after 2000ms when control stops visible changing.\r\n\t\t/// </summary>\r\n\t\tprivate static void _Timer_Elapsed(object sender, System.Timers.ElapsedEventArgs e)\r\n\t\t{\r\n\t\t\tInitHelper ih = null;\r\n\t\t\t// Find InitHelper by timer.\r\n\t\t\tlock (TimersLock)\r\n\t\t\t{\r\n\t\t\t\tih = Timers.FirstOrDefault(x => Equals(x._Timer, sender));\r\n\t\t\t\tif (ih is null)\r\n\t\t\t\t\treturn;\r\n\t\t\t\tTimers.Remove(ih);\r\n\t\t\t\t_InitEndCount++;\r\n\t\t\t\tih.WriteLine(\"INIT END\");\r\n\t\t\t\t// Disconnect all links.\r\n\t\t\t\tih.Control.IsVisibleChanged -= Control_IsVisibleChanged;\r\n\t\t\t\t//ih.Control.PropertyChanged -= Control_PropertyChanged;\r\n\t\t\t\tih.Control = null;\r\n\t\t\t\tih._Timer.Dispose();\r\n\t\t\t}\r\n\t\t}\r\n\r\n\t\t#endregion\r\n\r\n\r\n\t}\r\n}\r\n"
  },
  {
    "path": "FocusLogger/JocysCom/Controls/ItemFormattingConverter.cs",
    "content": "﻿using System;\r\nusing System.Globalization;\r\nusing System.Windows.Data;\r\n\r\nnamespace JocysCom.ClassLibrary.Controls\r\n{\r\n\tpublic class ItemFormattingConverter : IMultiValueConverter\r\n\t{\r\n\r\n\t\tpublic Func<object[], Type, object, CultureInfo, object> ConvertFunction;\r\n\t\tpublic Func<object, Type[], object, CultureInfo, object[]> ConvertBackFunction;\r\n\r\n\t\tpublic object Convert(object[] values, Type targetType, object parameter, CultureInfo culture)\r\n\t\t\t=> ConvertFunction?.Invoke(values, targetType, parameter, culture);\r\n\r\n\t\tpublic object[] ConvertBack(object value, Type[] targetTypes, object parameter, CultureInfo culture)\r\n\t\t\t=> ConvertBackFunction?.Invoke(value, targetTypes, parameter, culture);\r\n\r\n\t}\r\n}\r\n"
  },
  {
    "path": "FocusLogger/JocysCom/Controls/MessageBoxWindow.xaml",
    "content": "﻿<Window\r\n\tx:Class=\"JocysCom.ClassLibrary.Controls.MessageBoxWindow\"\r\n\txmlns=\"http://schemas.microsoft.com/winfx/2006/xaml/presentation\"\r\n\txmlns:x=\"http://schemas.microsoft.com/winfx/2006/xaml\"\r\n\txmlns:d=\"http://schemas.microsoft.com/expression/blend/2008\"\r\n\txmlns:mc=\"http://schemas.openxmlformats.org/markup-compatibility/2006\"\r\n\tMinWidth=\"240\"\r\n\tClosed=\"Window_Closed\"\r\n\tLoaded=\"Window_Loaded\"\r\n\tSizeToContent=\"WidthAndHeight\"\r\n\tUnloaded=\"Window_Unloaded\"\r\n\tmc:Ignorable=\"d\">\r\n\t<Window.Resources>\r\n\t\t<ResourceDictionary>\r\n\t\t\t<ResourceDictionary.MergedDictionaries>\r\n\t\t\t\t<ResourceDictionary Source=\"Themes/Default.xaml\" />\r\n\t\t\t\t<ResourceDictionary Source=\"Themes/Icons.xaml\" />\r\n\t\t\t</ResourceDictionary.MergedDictionaries>\r\n\t\t</ResourceDictionary>\r\n\t</Window.Resources>\r\n\t<Grid HorizontalAlignment=\"Stretch\" VerticalAlignment=\"Stretch\">\r\n\t\t<Grid.RowDefinitions>\r\n\t\t\t<RowDefinition Height=\"*\" />\r\n\t\t\t<RowDefinition Height=\"Auto\" />\r\n\t\t</Grid.RowDefinitions>\r\n\t\t<Grid HorizontalAlignment=\"Stretch\" VerticalAlignment=\"Stretch\">\r\n\t\t\t<Grid.ColumnDefinitions>\r\n\t\t\t\t<ColumnDefinition Width=\"Auto\" />\r\n\t\t\t\t<ColumnDefinition Width=\"*\" />\r\n\t\t\t</Grid.ColumnDefinitions>\r\n\t\t\t<StackPanel Grid.Column=\"0\" Background=\"{DynamicResource {x:Static SystemColors.WindowBrushKey}}\">\r\n\t\t\t\t<ContentControl\r\n\t\t\t\t\tName=\"IconContent\"\r\n\t\t\t\t\tWidth=\"48\"\r\n\t\t\t\t\tHeight=\"48\"\r\n\t\t\t\t\tMargin=\"8\"\r\n\t\t\t\t\tVerticalAlignment=\"Top\"\r\n\t\t\t\t\tContent=\"{StaticResource Icon_OK}\" />\r\n\t\t\t</StackPanel>\r\n\t\t\t<DockPanel\r\n\t\t\t\tGrid.Column=\"1\"\r\n\t\t\t\tMargin=\"0\"\r\n\t\t\t\tHorizontalAlignment=\"Stretch\"\r\n\t\t\t\tVerticalAlignment=\"Stretch\"\r\n\t\t\t\tBackground=\"{DynamicResource {x:Static SystemColors.WindowBrushKey}}\"\r\n\t\t\t\tLastChildFill=\"True\">\r\n\t\t\t\t<TextBlock\r\n\t\t\t\t\tx:Name=\"MessageTextBlock\"\r\n\t\t\t\t\tMargin=\"0,8,8,8\"\r\n\t\t\t\t\tVerticalAlignment=\"Top\"\r\n\t\t\t\t\tText=\"Message\"\r\n\t\t\t\t\tTextWrapping=\"Wrap\"\r\n\t\t\t\t\tVisibility=\"Collapsed\">\r\n\t\t\t\t\t<TextBlock.ContextMenu>\r\n\t\t\t\t\t\t<ContextMenu>\r\n\t\t\t\t\t\t\t<MenuItem Click=\"CopyMessage_Click\" Header=\"Copy\" />\r\n\t\t\t\t\t\t</ContextMenu>\r\n\t\t\t\t\t</TextBlock.ContextMenu>\r\n\t\t\t\t</TextBlock>\r\n\t\t\t\t<TextBox\r\n\t\t\t\t\tx:Name=\"MessageTextBox\"\r\n\t\t\t\t\tMargin=\"0,8,8,8\"\r\n\t\t\t\t\tHorizontalAlignment=\"Stretch\"\r\n\t\t\t\t\tVerticalAlignment=\"Stretch\"\r\n\t\t\t\t\tVerticalContentAlignment=\"Top\"\r\n\t\t\t\t\tx:FieldModifier=\"public\"\r\n\t\t\t\t\tAcceptsReturn=\"True\"\r\n\t\t\t\t\tAcceptsTab=\"True\"\r\n\t\t\t\t\tHorizontalScrollBarVisibility=\"Disabled\"\r\n\t\t\t\t\tKeyUp=\"MessageTextBox_KeyUp\"\r\n\t\t\t\t\tMinLines=\"2\"\r\n\t\t\t\t\tSpellCheck.IsEnabled=\"True\"\r\n\t\t\t\t\tTextWrapping=\"Wrap\"\r\n\t\t\t\t\tVerticalScrollBarVisibility=\"Auto\" />\r\n\t\t\t</DockPanel>\r\n\t\t</Grid>\r\n\t\t<Grid Grid.Row=\"1\">\r\n\t\t\t<Grid.ColumnDefinitions>\r\n\t\t\t\t<ColumnDefinition Width=\"*\" />\r\n\t\t\t\t<ColumnDefinition Width=\"auto\" />\r\n\t\t\t</Grid.ColumnDefinitions>\r\n\t\t\t<TextBlock x:Name=\"LinkTextBlock\" Grid.Column=\"0\">\r\n\t\t\t\t<Hyperlink\r\n\t\t\t\t\tx:Name=\"MainHyperLink\"\r\n\t\t\t\t\tNavigateUri=\"http://localhost\"\r\n\t\t\t\t\tRequestNavigate=\"MainHyperLink_RequestNavigate\"\r\n\t\t\t\t\tTextDecorations=\"None\">\r\n\t\t\t\t\t<TextBlock x:Name=\"LinkLabel\" Text=\"MainHyperLink\" />\r\n\t\t\t\t</Hyperlink>\r\n\t\t\t\t<TextBlock x:Name=\"SizeLabel\" Opacity=\"0.5\" Text=\"0\" />\r\n\t\t\t</TextBlock>\r\n\t\t\t<StackPanel\r\n\t\t\t\tGrid.Column=\"1\"\r\n\t\t\t\tMargin=\"8,8,8,8\"\r\n\t\t\t\tHorizontalAlignment=\"Right\"\r\n\t\t\t\tOrientation=\"Horizontal\">\r\n\t\t\t\t<Button Name=\"Button1\" Margin=\"0,0,0,0\" Click=\"Button_Click\">\r\n\t\t\t\t\t<StackPanel>\r\n\t\t\t\t\t\t<ContentControl Name=\"Button1Content\" Content=\"{StaticResource Icon_OK}\" />\r\n\t\t\t\t\t\t<Label Name=\"Button1Label\" Content=\"Button 1\" />\r\n\t\t\t\t\t</StackPanel>\r\n\t\t\t\t</Button>\r\n\t\t\t\t<Button Name=\"Button2\" Margin=\"8,0,0,0\" Click=\"Button_Click\">\r\n\t\t\t\t\t<StackPanel>\r\n\t\t\t\t\t\t<ContentControl Name=\"Button2Content\" Content=\"{StaticResource Icon_Cancel}\" />\r\n\t\t\t\t\t\t<Label Name=\"Button2Label\" Content=\"Button 2\" />\r\n\t\t\t\t\t</StackPanel>\r\n\t\t\t\t</Button>\r\n\t\t\t\t<Button Name=\"Button3\" Margin=\"8,0,0,0\" Click=\"Button_Click\">\r\n\t\t\t\t\t<StackPanel>\r\n\t\t\t\t\t\t<ContentControl Name=\"Button3Content\" Content=\"{StaticResource Icon_Exit}\" />\r\n\t\t\t\t\t\t<Label Name=\"Button3Label\" Content=\"Button 3\" />\r\n\t\t\t\t\t</StackPanel>\r\n\t\t\t\t</Button>\r\n\t\t\t</StackPanel>\r\n\t\t</Grid>\r\n\t</Grid>\r\n</Window>\r\n"
  },
  {
    "path": "FocusLogger/JocysCom/Controls/MessageBoxWindow.xaml.cs",
    "content": "﻿using System;\r\nusing System.Globalization;\r\nusing System.Windows;\r\nusing System.Windows.Controls;\r\nusing System.Windows.Media;\r\n\r\nnamespace JocysCom.ClassLibrary.Controls\r\n{\r\n\t/// <summary>\r\n\t/// Interaction logic for MessageBoxWindow.xaml\r\n\t/// </summary>\r\n\t/// <remarks>Make sure to set the Owner property to be disposed properly after closing.</remarks>\r\n\tpublic partial class MessageBoxWindow : Window\r\n\t{\r\n\t\t/// <summary>\r\n\t\t/// Make sure to set window Owner to properly dispose after closing.\r\n\t\t/// </summary>\r\n\t\tpublic MessageBoxWindow()\r\n\t\t{\r\n\t\t\tInitHelper.InitTimer(this, InitializeComponent);\r\n\t\t\tLinkTextBlock.Visibility = Visibility.Collapsed;\r\n\t\t\t// Center owner.\r\n\t\t\tvar owner = Application.Current?.MainWindow;\r\n\t\t\tif (owner != null)\r\n\t\t\t{\r\n\t\t\t\tOwner = owner;\r\n\t\t\t\tWindowStartupLocation = WindowStartupLocation.CenterOwner;\r\n\t\t\t}\r\n\t\t}\r\n\r\n\t\t/// <summary>Displays a message box that has a message, title bar caption, button, and icon; and that accepts a default message box result, complies with the specified options, and returns a result.</summary>\r\n\t\t/// <param name=\"message\">A that specifies the text to display.</param>\r\n\t\t/// <param name=\"caption\">A that specifies the title bar caption to display.</param>\r\n\t\t/// <param name=\"button\">A value that specifies which button or buttons to display.</param>\r\n\t\t/// <param name=\"icon\">A value that specifies the icon to display.</param>\r\n\t\t/// <param name=\"defaultResult\">A value that specifies the default result of the message box.</param>\r\n\t\t/// <param name=\"options\">A value object that specifies the options.</param>\r\n\t\tpublic static MessageBoxResult Show(\r\n\t\t\tstring message,\r\n\t\t\tstring caption = \"\",\r\n\t\t\tMessageBoxButton button = MessageBoxButton.OK,\r\n\t\t\tMessageBoxImage icon = MessageBoxImage.None,\r\n\t\t\tMessageBoxResult defaultResult = MessageBoxResult.None,\r\n\t\t\tMessageBoxOptions options = MessageBoxOptions.None\r\n\t\t)\r\n\t\t{\r\n\t\t\tvar box = new MessageBoxWindow();\r\n\t\t\treturn box.ShowDialog(message, caption, button, icon, defaultResult, options);\r\n\t\t}\r\n\r\n\t\t/// <summary>Displays a message box that has a message, title bar caption, button, and icon; and that accepts a default message box result, complies with the specified options, and returns a result.</summary>\r\n\t\t/// <param name=\"message\">A that specifies the text to display.</param>\r\n\t\t/// <param name=\"caption\">A that specifies the title bar caption to display.</param>\r\n\t\t/// <param name=\"button\">A value that specifies which button or buttons to display.</param>\r\n\t\t/// <param name=\"icon\">A value that specifies the icon to display.</param>\r\n\t\t/// <param name=\"defaultResult\">A value that specifies the default result of the message box.</param>\r\n\t\t/// <param name=\"options\">A value object that specifies the options.</param>\r\n\t\tpublic MessageBoxResult ShowDialog(\r\n\t\t  string message,\r\n\t\t  string caption = \"\",\r\n\t\t  MessageBoxButton button = MessageBoxButton.OK,\r\n\t\t  MessageBoxImage icon = MessageBoxImage.None,\r\n\t\t  MessageBoxResult defaultResult = MessageBoxResult.None,\r\n\t\t  MessageBoxOptions options = MessageBoxOptions.None)\r\n\t\t{\r\n\t\t\tMessageTextBlock.Visibility = Visibility.Visible;\r\n\t\t\tMessageTextBox.Visibility = Visibility.Collapsed;\r\n\t\t\tLinkLabel.Visibility = Visibility.Collapsed;\r\n\t\t\tSizeLabel.Visibility = Visibility.Visible;\r\n\t\t\tTitle = caption;\r\n\t\t\tMessageTextBlock.Text = message;\r\n\t\t\t_SwitchButton(button, defaultResult);\r\n\t\t\t_SwitchIcon(icon);\r\n\t\t\t// Get text size.\r\n\t\t\tvar size = MeasureString(message, MessageTextBlock);\r\n\t\t\tif (size.Width > 960)\r\n\t\t\t{\r\n\t\t\t\tsize = ApplyAspectRatio(size);\r\n\t\t\t\tMessageTextBlock.MaxWidth = Math.Round(size.Width, 0);\r\n\t\t\t}\r\n\t\t\tif (ControlsHelper.GetMainFormTopMost())\r\n\t\t\t\tTopmost = true;\r\n\t\t\t// Show form.\r\n\t\t\tvar result = ShowDialog();\r\n\t\t\treturn Result;\r\n\t\t}\r\n\r\n\t\t/// <summary>Displays a message box that has a message, title bar caption, button, and icon; and that accepts a default message box result, complies with the specified options, and returns a result.</summary>\r\n\t\t/// <param name=\"message\">A that specifies the text to display.</param>\r\n\t\t/// <param name=\"caption\">A that specifies the title bar caption to display.</param>\r\n\t\t/// <param name=\"button\">A value that specifies which button or buttons to display.</param>\r\n\t\t/// <param name=\"icon\">A value that specifies the icon to display.</param>\r\n\t\t/// <param name=\"defaultResult\">A value that specifies the default result of the message box.</param>\r\n\t\t/// <param name=\"options\">A value object that specifies the options.</param>\r\n\t\tpublic MessageBoxResult ShowPrompt(\r\n\t\t\tstring message,\r\n\t\t\tstring caption = \"\",\r\n\t\t\tMessageBoxButton button = MessageBoxButton.OKCancel,\r\n\t\t\tMessageBoxImage icon = MessageBoxImage.Information,\r\n\t\t\tMessageBoxResult defaultResult = MessageBoxResult.OK,\r\n\t\t\tMessageBoxOptions options = MessageBoxOptions.None\r\n\t\t)\r\n\t\t{\r\n\t\t\tMessageTextBlock.Visibility = Visibility.Collapsed;\r\n\t\t\tMessageTextBox.Visibility = Visibility.Visible;\r\n\t\t\tLinkLabel.Visibility = Visibility.Collapsed;\r\n\t\t\tSizeLabel.Visibility = Visibility.Visible;\r\n\t\t\tTitle = caption;\r\n\t\t\tMessageTextBox.Text = message;\r\n\t\t\t_SwitchButton(button, defaultResult);\r\n\t\t\t_SwitchIcon(icon);\r\n\t\t\t// Set size.\r\n\t\t\tLoaded -= MessageBoxWindow_Loaded;\r\n\t\t\tLoaded += MessageBoxWindow_Loaded;\r\n\t\t\t// Update size label.\r\n\t\t\tUpdateSizeLabel();\r\n\t\t\tif (ControlsHelper.GetMainFormTopMost())\r\n\t\t\t\tTopmost = true;\r\n\t\t\t// Attach a new Loaded event handler specifically for focusing the message text box\r\n\t\t\tLoaded += FocusMessageTextBox;\r\n\t\t\t// Show form.\r\n\t\t\tvar result = ShowDialog();\r\n\t\t\t// Clean up by removing the event handler after the dialog is closed\r\n\t\t\tLoaded -= FocusMessageTextBox;\r\n\t\t\treturn Result;\r\n\t\t}\r\n\r\n\t\tprivate void FocusMessageTextBox(object sender, RoutedEventArgs e)\r\n\t\t{\r\n\t\t\t// Set focus to the MessageTextBox control.\r\n\t\t\tMessageTextBox.Focus();\r\n\t\t\t// Set the caret position to the end of the text\r\n\t\t\tMessageTextBox.CaretIndex = MessageTextBox.Text.Length;\r\n\t\t}\r\n\r\n\t\tpublic void SetSize(double width = 0, double height = 0)\r\n\t\t{\r\n\t\t\tif (width > 0 && height > 0)\r\n\t\t\t{\r\n\t\t\t\tSizeToContent = SizeToContent.Manual;\r\n\t\t\t\tWidth = width;\r\n\t\t\t\tHeight = height;\r\n\t\t\t}\r\n\t\t\telse\r\n\t\t\t{\r\n\t\t\t\tSizeToContent = SizeToContent.WidthAndHeight;\r\n\t\t\t}\r\n\t\t}\r\n\r\n\t\tprivate void MessageBoxWindow_Loaded(object sender, RoutedEventArgs e)\r\n\t\t{\r\n\t\t\tif (SizeToContent == SizeToContent.Manual)\r\n\t\t\t{\r\n\t\t\t\tControlsHelper.CenterWindowOnApplication(this);\r\n\t\t\t}\r\n\t\t\telse\r\n\t\t\t{\r\n\t\t\t\t// Get text size (from 256 to 512).\r\n\t\t\t\tvar measureSize = Math.Min(Math.Max(256, MessageTextBlock.Text.Length), 512);\r\n\t\t\t\tvar measureMessage = new string('a', measureSize);\r\n\t\t\t\tvar size = MeasureString(measureMessage, MessageTextBlock);\r\n\t\t\t\tsize = ApplyAspectRatio(size);\r\n\t\t\t\tvar boxWidth = Math.Round(size.Width, 0);\r\n\t\t\t\tvar boxHeight = Math.Round(size.Height, 0);\r\n\t\t\t\t// Set window size.\r\n\t\t\t\tvar winWidthDif = Width - MessageTextBox.ActualWidth;\r\n\t\t\t\tvar winHeightDif = Height - MessageTextBox.ActualHeight;\r\n\t\t\t\tSizeToContent = SizeToContent.Manual;\r\n\t\t\t\tWidth = boxWidth + winWidthDif;\r\n\t\t\t\tHeight = boxHeight + winHeightDif;\r\n\t\t\t}\r\n\t\t}\r\n\r\n\t\tvoid EnableButtons(MessageBoxResult r1, MessageBoxResult r2 = MessageBoxResult.None, MessageBoxResult r3 = MessageBoxResult.None)\r\n\t\t{\r\n\t\t\tButton1.Tag = r1;\r\n\t\t\tButton1Label.Content = r1.ToString();\r\n\t\t\tButton1.Visibility = r1 == MessageBoxResult.None ? Visibility.Collapsed : Visibility.Visible;\r\n\t\t\tButton2.Tag = r2;\r\n\t\t\tButton2Label.Content = r2.ToString();\r\n\t\t\tButton2.Visibility = r2 == MessageBoxResult.None ? Visibility.Collapsed : Visibility.Visible;\r\n\t\t\tButton3.Tag = r3;\r\n\t\t\tButton3Label.Content = r3.ToString();\r\n\t\t\tButton3.Visibility = r3 == MessageBoxResult.None ? Visibility.Collapsed : Visibility.Visible;\r\n\t\t}\r\n\r\n\t\tprivate MessageBoxResult Result = MessageBoxResult.None;\r\n\r\n\t\tprivate void _SwitchButton(MessageBoxButton button, MessageBoxResult defaultResult)\r\n\t\t{\r\n\t\t\tswitch (button)\r\n\t\t\t{\r\n\t\t\t\tcase MessageBoxButton.OK:\r\n\t\t\t\t\tEnableButtons(MessageBoxResult.OK);\r\n\t\t\t\t\tbreak;\r\n\t\t\t\tcase MessageBoxButton.OKCancel:\r\n\t\t\t\t\tEnableButtons(MessageBoxResult.OK, MessageBoxResult.None, MessageBoxResult.Cancel);\r\n\t\t\t\t\tbreak;\r\n\t\t\t\tcase MessageBoxButton.YesNoCancel:\r\n\t\t\t\t\tEnableButtons(MessageBoxResult.Yes, MessageBoxResult.No, MessageBoxResult.Cancel);\r\n\t\t\t\t\tbreak;\r\n\t\t\t\tcase MessageBoxButton.YesNo:\r\n\t\t\t\t\tEnableButtons(MessageBoxResult.Yes, MessageBoxResult.No);\r\n\t\t\t\t\tbreak;\r\n\t\t\t\tdefault:\r\n\t\t\t\t\tbreak;\r\n\t\t\t}\r\n\t\t\tvar buttons = new[] { Button1, Button2, Button3 };\r\n\t\t\tforeach (var b in buttons)\r\n\t\t\t{\r\n\t\t\t\tif ((MessageBoxResult)b.Tag == MessageBoxResult.Cancel)\r\n\t\t\t\t\tb.IsCancel = true;\r\n\t\t\t\tif ((MessageBoxResult)b.Tag == defaultResult)\r\n\t\t\t\t\tb.IsDefault = true;\r\n\t\t\t}\r\n\t\t}\r\n\r\n\t\tprivate void _SwitchIcon(MessageBoxImage icon)\r\n\t\t{\r\n\t\t\tswitch (icon)\r\n\t\t\t{\r\n\t\t\t\tcase MessageBoxImage.Error:\r\n\t\t\t\t\tIconContent.Content = Resources[\"Icon_Error\"];\r\n\t\t\t\t\tbreak;\r\n\t\t\t\tcase MessageBoxImage.Question:\r\n\t\t\t\t\tIconContent.Content = Resources[\"Icon_Question\"];\r\n\t\t\t\t\tbreak;\r\n\t\t\t\tcase MessageBoxImage.Warning:\r\n\t\t\t\t\tIconContent.Content = Resources[\"Icon_Warning\"];\r\n\t\t\t\t\tbreak;\r\n\t\t\t\tdefault:\r\n\t\t\t\t\tIconContent.Content = Resources[\"Icon_Information\"];\r\n\t\t\t\t\tbreak;\r\n\t\t\t}\r\n\t\t}\r\n\r\n\t\tprivate void CopyMessage_Click(object sender, RoutedEventArgs e)\r\n\t\t{\r\n\t\t\tvar text = MessageTextBox.Visibility == Visibility.Visible\r\n\t\t\t\t? MessageTextBox.Text\r\n\t\t\t\t: MessageTextBlock.Text;\r\n\t\t\tif (!string.IsNullOrEmpty(text))\r\n\t\t\t\tClipboard.SetText(text);\r\n\t\t}\r\n\r\n\t\tprivate void Button_Click(object sender, RoutedEventArgs e)\r\n\t\t{\r\n\t\t\tResult = (MessageBoxResult)((Button)sender).Tag;\r\n\t\t\tDialogResult = true;\r\n\t\t}\r\n\r\n\t\tpublic void SetLink(string text = null, Uri uri = null)\r\n\t\t{\r\n\t\t\tLinkTextBlock.Visibility = string.IsNullOrEmpty(text)\r\n\t\t\t\t? Visibility.Collapsed\r\n\t\t\t\t: Visibility.Visible;\r\n\t\t\tMainHyperLink.NavigateUri = uri;\r\n\t\t\tLinkLabel.Text = text;\r\n\t\t}\r\n\r\n\t\tprivate void MainHyperLink_RequestNavigate(object sender, System.Windows.Navigation.RequestNavigateEventArgs e)\r\n\t\t{\r\n\t\t\tOpenUrl(e.Uri.AbsoluteUri);\r\n\t\t}\r\n\r\n\t\t#region Helper Methods\r\n\r\n\t\tpublic static void OpenUrl(string url)\r\n\t\t{\r\n\t\t\ttry\r\n\t\t\t{\r\n\t\t\t\tSystem.Diagnostics.Process.Start(url);\r\n\t\t\t}\r\n\t\t\tcatch (System.ComponentModel.Win32Exception noBrowser)\r\n\t\t\t{\r\n\t\t\t\tif (noBrowser.ErrorCode == -2147467259)\r\n\t\t\t\t\tMessageBox.Show(noBrowser.Message);\r\n\t\t\t}\r\n\t\t\tcatch (Exception other)\r\n\t\t\t{\r\n\t\t\t\tMessageBox.Show(other.Message);\r\n\t\t\t}\r\n\t\t}\r\n\r\n\t\tprivate static Size ApplyAspectRatio(Size s, double w = 16, double h = 9)\r\n\t\t{\r\n\t\t\tvar area = s.Width * s.Height;\r\n\t\t\t// w * x * h * x = area;\r\n\t\t\tvar x = Math.Sqrt(area / w / h);\r\n\t\t\treturn new Size(w * x, h * x);\r\n\t\t}\r\n\r\n\t\tprivate static Size MeasureString(string candidate, TextBlock control)\r\n\t\t{\r\n\t\t\tvar formattedText = new FormattedText(\r\n\t\t\t\tcandidate,\r\n\t\t\t\tCultureInfo.CurrentCulture,\r\n\t\t\t\tFlowDirection.LeftToRight,\r\n\t\t\t\tnew Typeface(control.FontFamily, control.FontStyle, control.FontWeight, control.FontStretch),\r\n\t\t\t\tcontrol.FontSize,\r\n\t\t\t\tBrushes.Black,\r\n\t\t\t\tnew NumberSubstitution(),\r\n\t\t\t\t1);\r\n\t\t\treturn new Size(formattedText.Width, formattedText.Height);\r\n\t\t}\r\n\r\n\t\t#endregion\r\n\r\n\r\n\r\n\t\tprivate void MessageTextBox_KeyUp(object sender, System.Windows.Input.KeyEventArgs e)\r\n\t\t{\r\n\t\t\tUpdateSizeLabel();\r\n\t\t}\r\n\r\n\t\tvoid UpdateSizeLabel()\r\n\t\t{\r\n\t\t\tvar text = (MessageTextBox.MaxLength - MessageTextBox.Text.Length).ToString();\r\n\t\t\tControlsHelper.SetText(SizeLabel, text);\r\n\t\t\tControlsHelper.SetVisible(SizeLabel, MessageTextBox.MaxLength > 0);\r\n\t\t}\r\n\r\n\t\tprivate void Window_Loaded(object sender, RoutedEventArgs e)\r\n\t\t{\r\n\t\t\tif (!ControlsHelper.AllowLoad(this))\r\n\t\t\t\treturn;\r\n\t\t\t// Center message box window in application.\r\n\t\t\tif (Owner is null)\r\n\t\t\t\tControlsHelper.CenterWindowOnApplication(this);\r\n\t\t}\r\n\r\n\t\tprivate void Window_Unloaded(object sender, RoutedEventArgs e)\r\n\t\t{\r\n\t\t\tif (!ControlsHelper.AllowUnload(this))\r\n\t\t\t\treturn;\r\n\t\t}\r\n\r\n\t\tprivate void Window_Closed(object sender, EventArgs e)\r\n\t\t{\r\n\t\t\tOwner = null;\r\n\t\t}\r\n\t}\r\n}\r\n"
  },
  {
    "path": "FocusLogger/JocysCom/Controls/TabIndexConverter.cs",
    "content": "﻿using System;\r\nusing System.Globalization;\r\nusing System.Linq;\r\nusing System.Windows;\r\nusing System.Windows.Controls;\r\nusing System.Windows.Data;\r\n\r\nnamespace JocysCom.ClassLibrary.Controls\r\n{\r\n    public class TabIndexConverter : IValueConverter\r\n    {\r\n        public object Convert(object value, Type targetType, object parameter, CultureInfo culture)\r\n        {\r\n            var item = value as TabItem;\r\n            if (item is null)\r\n                return \"\";\r\n            var container = ItemsControl.ItemsControlFromItemContainer(item).ItemContainerGenerator;\r\n            var items = container.Items.Cast<TabItem>().Where(x => x.Visibility == Visibility.Visible).ToList();\r\n            var count = items.Count();\r\n            var index = items.IndexOf(item);\r\n            var result = \"\";\r\n            if (index == 0)\r\n                result += \"First\";\r\n            if (item.IsSelected)\r\n                result += \"Selected\";\r\n            return result;\r\n        }\r\n\r\n        public object ConvertBack(object value, Type targetType, object parameter, CultureInfo culture)\r\n        {\r\n            return DependencyProperty.UnsetValue;\r\n        }\r\n    }\r\n    \r\n}\r\n"
  },
  {
    "path": "FocusLogger/JocysCom/Controls/Themes/Convert_SVG_to_XAML.ps1",
    "content": "<#\r\n.SYNOPSIS\r\n    Convert Folder with SVG image files into XAML Resource file.\r\n.NOTES\r\n    Author:     Evaldas Jocys <evaldas@jocys.com>\r\n    Modified:   2021-11-06\r\n.LINK\r\n    http://www.jocys.com\r\n\r\n.REMARKS\r\n\r\n    Requires Installation of InkScape 1.2 from https://inkscape.org/release/\r\n\thttps://inkscape.org/release/inkscape-1.2.2/windows/64-bit/compressed-7z/dl/\r\n\r\n\tHow to include icons resource into App.xaml file:\r\n\r\n\t\t<Application\r\n\t\t\tx:Class=\"JocysCom.SomeApp\"\r\n\t\t\txmlns=\"http://schemas.microsoft.com/winfx/2006/xaml/presentation\"\r\n\t\t\txmlns:x=\"http://schemas.microsoft.com/winfx/2006/xaml\"\r\n\t\t\tStartupUri=\"MainWindow.xaml\">\r\n\t\t\t<Application.Resources>\r\n\t\t\t\t<ResourceDictionary>\r\n\t\t\t\t\t<ResourceDictionary.MergedDictionaries>\r\n\t\t\t\t\t\t<ResourceDictionary Source=\"Resources/Icons/Icons_Default.xaml\" />\r\n\t\t\t\t\t</ResourceDictionary.MergedDictionaries>\r\n\t\t\t\t</ResourceDictionary>\r\n\t\t\t</Application.Resources>\r\n\t\t</Application>\r\n\t\r\n\tHow to display image inside the XAML with style:\r\n\t\r\n\t\t<ContentControl\tx:Name=\"MyIcon\" Width=\"24\" Height=\"24\" Content=\"{StaticResource Icon_IconFileName}\" />\t\r\n\r\n\tHow to set image to Content control from code behind:\r\n\r\n\t\tMyIcon.Content = Icons_Default.Current[Icons_Default.Icon_IconFileName];\r\n\t\t\r\n#>\r\nusing namespace System;\r\nusing namespace System.IO;\r\nusing namespace System.Linq;\r\nusing namespace System.Xml.Linq;\r\nusing namespace System.Text.RegularExpressions;\r\nusing namespace System.Collections.Generic;\r\n\r\n[Reflection.Assembly]::LoadWithPartialName(\"System.Xml.Linq\") | Out-Null;\r\n\r\nClear-Host;\r\n\r\n# ----------------------------------------------------------------------------\r\n# Get current command path.\r\n[string]$current = $MyInvocation.MyCommand.Path;\r\n# Get calling command path.\r\n[string]$calling = @(Get-PSCallStack)[1].InvocationInfo.MyCommand.Path;\r\n# If executed directly then...\r\nif ($calling -ne \"\") {\r\n    $current = $calling;\r\n}\r\n# ----------------------------------------------------------------------------\r\n[FileInfo]$file = New-Object FileInfo($current);\r\n# Set public parameters.\r\n$global:scriptName = $file.Basename;\r\n$global:scriptPath = $file.Directory.FullName;\r\n# Change current directory.\r\n[Console]::WriteLine(\"Script Path: {0}\", $scriptPath);\r\n[Environment]::CurrentDirectory = $scriptPath;\r\nSet-Location $scriptPath;\r\n# ----------------------------------------------------------------------------\r\n[DirectoryInfo]$root = New-Object DirectoryInfo($scriptPath);\r\n# ----------------------------------------------------------------------------\r\nfunction RemoveAttributes\r\n{\r\n    param([XElement]$Node,[string]$Name);\r\n    #----------------------------------------------------------\r\n    foreach ($attr in $Node.Attributes())\r\n    {\r\n        if ($attr.Name -eq $Name)\r\n        {\r\n            $attr.Remove();\r\n        }\r\n    }\r\n    foreach ($child in $Node.Descendants())\r\n    {\r\n        RemoveAttributes -Node $child -Name $Name;\r\n    }\r\n}\r\n# ----------------------------------------------------------------------------\r\nfunction FindParentFile\r\n{\r\n    [OutputType([FileInfo[]])] param([string]$pattern);\r\n    #----------------------------------------------------------\r\n    [DirectoryInfo]$di = new-Object DirectoryInfo $scriptPath;\r\n    do\r\n    {\r\n        $files = $di.GetFiles($pattern);\r\n        # Return if project files were found.\r\n        if ($files.Count -gt 0)\r\n        {\r\n            return $files;\r\n        }\r\n        # Continue to parent.\r\n        $di = $di.Parent;\r\n    } while($null -ne $di);\r\n    return $null;\r\n}\r\n# ----------------------------------------------------------------------------\r\nfunction GetProjectValue\r\n{\r\n    [OutputType([string])] param([string]$path, [string]$name);\r\n    #----------------------------------------------------------\r\n    [string]$content = [File]::ReadAllText($path);\r\n\t[Regex]$rx = New-Object Regex(\"(?<p><$name>)(?<v>[^<]*)(?<s><\\/$name>)\");\r\n\t$match = $rx.Match($content);\r\n\tif ($match.Success -eq $true) {\r\n\t\treturn $match.Groups[\"v\"].Value;\r\n\t}\r\n\treturn $null;\r\n}\r\n# ----------------------------------------------------------------------------\r\nfunction FindExistingFile\r\n{\r\n    [OutputType([string])] param([string]$path);\r\n    #----------------------------------------------------------\r\n    [FileInfo]$fi = $null;\r\n    # Paths to look for executable.\r\n    $ps = @(\r\n        $path,\r\n        \"${env:ProgramFiles}\\$path\",\r\n        \"${env:ProgramFiles(x86)}\\$path\",\r\n        \"D:\\Program Files\\$path\",\r\n        \"D:\\Program Files (x86)\\$dfe\"\r\n    );\r\n    foreach ($p in $ps) {\r\n        # Fix dot notations.\r\n        $fullPath = [Path]::GetFullPath($p);\r\n        #Write-Host \"Check... $fullPath\";\r\n        if ([File]::Exists($fullPath)) {\r\n            $fi = new-Object FileInfo $fullPath;\r\n            break;\r\n        }\r\n    }\r\n    return $fi;\r\n}\r\n# ----------------------------------------------------------------------------\r\nfunction SHA256CheckSum\r\n{\r\n    param($filePath);\r\n    #----------------------------------------------------------\r\n    $SHA256 = [System.Security.Cryptography.SHA256Managed]::Create();\r\n    $fileStream = [System.IO.File]::OpenRead($filePath);\r\n    $bytes = $SHA256.ComputeHash($fileStream);\r\n    $hash = ($bytes|ForEach-Object ToString X2) -join '';\r\n    $fileStream.Dispose();\r\n    $SHA256.Dispose();\r\n    return $hash;\r\n}\r\n# ----------------------------------------------------------------------------\r\nfunction FindProjectFile\r\n{\r\n    [FileInfo[]]$list = FindParentFile \"*.*proj\";\r\n    if ($list -ne $null -and $list.Count -gt 0){\r\n        # Order by date descendign to most recent file.\r\n        $list = [Enumerable]::OrderByDescending($list, [Func[object,object]]{ param($x) $x.LastWriteTime });\r\n        $list = [Enumerable]::ToArray($list);\r\n        return $list[0];\r\n    }\r\n    return $null;\r\n}\r\n# ----------------------------------------------------------------------------\r\n# Show menu\r\n# ----------------------------------------------------------------------------\r\nfunction ShowOptionsMenu\r\n{\r\n    param($items);\r\n    #----------------------------------------------------------\r\n    # Get local configurations.\r\n\t$keys = \"1234567890ABCDEFGHIJKLMNOPQRSTUVWXYZ\";\r\n    Write-Host \"Options:\";\r\n    Write-Host;\r\n    for ($i = 0; $i -lt $items.Count; $i++)\r\n    {\r\n        $item = $items[$i];\r\n        Write-Host \"    $($keys[$i]) - $($item)\";\r\n    }\r\n    Write-Host;\r\n    $m = Read-Host -Prompt \"Type option and press ENTER to continue\";\r\n    $m = $m.ToUpper();\r\n    $keyIndex = $keys.IndexOf($m);\r\n    # If wrong choice then...\r\n    if ($keyIndex -eq -1)\r\n    {\r\n        return $null;\r\n    }\r\n    return $items[$keyIndex];\r\n}\r\n# ----------------------------------------------------------------------------\r\n\r\nWrite-Host;\r\n\r\n#------------------------------\r\n# Inkscape program location, which will be used for conversion from SVG format to XAML format.\r\n#------------------------------\r\n\r\n$inkscape = FindExistingFile \"Inkscape\\bin\\inkscape.exe\";\r\nif ($null -eq $inkscape) {\r\n    Write-Host \"Inkscape program not found!\";\r\n    Write-Host \"Download from https://inkscape.org/release/\";\r\n    return;\r\n}\r\nWrite-Host \"Inkscape:  $($inkscape.FullName)\";\r\n\r\n#------------------------------\r\n# Get Project file.\r\n#------------------------------\r\n\r\n[FileInfo]$project = FindProjectFile;\r\nif ($null -eq $project) {\r\n    Write-Host \"Project file not found.\";\r\n    return;\r\n}\r\nWrite-Host \"Project:   $($project.FullName)\";\r\n\r\n#------------------------------\r\n# Get Default namespace.\r\n#------------------------------\r\n\r\n# Get from project file.\r\n$defaultNamespace = GetProjectValue $project.FullName \"RootNamespace\";\r\n# If default namespace not found.\r\nif (\"\" -eq \"$defaultNamespace\") {\r\n    # Visual studio use Project file name as default assembly and root namespace.\r\n    $defaultNamespace = $project.BaseName;\r\n}\r\n# If namespace not found then...\r\nif (\"\" -eq \"$defaultNamespace\") {\r\n\tWrite-Host \"Please provide default namespace\";\r\n    $defaultNamespace = Host-Read;\r\n}\r\n#Write-Host \"Default Namespace: $defaultNamespace\";\r\n\r\n# Get Relative namespace.\r\n$relativeNamespace = $scriptPath.Substring($project.Directory.FullName.Length).Replace(\"\\\", \".\");\r\n#Write-Host \"Relative Namespace: $relativeNamespace\";\r\n$namespace = $defaultNamespace + $relativeNamespace;\r\n\r\nWrite-Host;\r\nWrite-Host \"Namespace: $namespace\";\r\n\r\n#------------------------------\r\n# Get Class Name\r\n#------------------------------\r\n\r\n# Get forders with *.svg files inside.\r\n$dirs = $file.Directory.GetDirectories();\r\n$dirNames = new-Object List[string];\r\nforeach ($dir in $dirs) {\r\n    $filePattern = \"*.svg\";\r\n    # If folder contains images then...\r\n    $dirFiles = $dir.GetFiles($filePattern);\r\n    if ($dirFiles.Length -gt 0){\r\n        $dirNames.Add($dir.Name);\r\n    }\r\n    #Write-Host \"Source: $($dir.Name), $filePattern Files: $($dirFiles.Length)\";\r\n}\r\n\r\n[string]$className = $null;\r\n\r\nif ($dirNames.Count -eq 1){\r\n    $className = $dirNames[0];\r\n} elseif ($dirNames.Count -gt 1){\r\n    $className = ShowOptionsMenu $dirNames;\r\n}\r\n\r\nif ($null -eq $className){\r\n    Write-Host \"Folder with images not found!\";\r\n    return;\r\n}\r\n\r\n$sourceDir = New-Object DirectoryInfo($root.FullName + \"\\\" + $className);\r\n\r\nWrite-Host \"Class:     $className\";\r\nWrite-Host;\r\nWrite-Host \"Source:    $($sourceDir.Name)\\\";\r\nWrite-Host \"Target:    $className.xaml + $className.xaml.cs\";\r\nWrite-Host;\r\npause;\r\n\r\n#------------------------------\r\n# Generate images.\r\n#------------------------------\r\n\r\nWrite-Host;\r\n\r\n#Write-Host \"Done. Press any key to continue...\";\r\n#$null = $Host.UI.RawUI.ReadKey(\"NoEcho,IncludeKeyDown\");\r\n\r\n# Get files.\r\n$files = $sourceDir.GetFiles(\"*.svg\");\r\n# If no SVG images found then skip.\r\nif ($files.Length -eq 0){\r\n    continue;\r\n}\r\n\r\n# Create regular expressions for key and names generation.\r\n$RxAllExceptNumbersAndLetters = New-Object Regex(\"[^a-zA-Z0-9]\", [RegexOptions]::IgnoreCase);\r\n$UsRx = New-Object Regex(\"_+\");\r\n# Crate output file name.\r\n$fileName = $RxAllExceptNumbersAndLetters.Replace($sourceDir.Name, \"_\");\r\n$fileName = $UsRx.Replace($fileName, \"_\");\r\n$fileName = \"$className.xaml\";\r\n$fileNameCs = \"$className.xaml.cs\";\r\nif ($files.Length -eq 1){\r\n    Write-Host \"Convert  $($files.Length) image:\";\r\n}else{\r\n    Write-Host \"Convert $($files.Length) images:\";\r\n}\r\n# Start <ResourceName>.xaml file.\r\n$xNs = \"http://schemas.microsoft.com/winfx/2006/xaml\";\r\nif ([File]::Exists($fileName) -ne $true)\r\n{\r\n    [File]::WriteAllText($fileName, \"<ResourceDictionary xmlns=`\"http://schemas.microsoft.com/winfx/2006/xaml/presentation`\" xmlns:x=`\"$xNs`\"\");\r\n    [File]::AppendAllText($fileName,\"`r`nx:Class=`\"$($namespace).$($className)`\"\");\r\n    [File]::AppendAllText($fileName,\"`r`nx:ClassModifier=`\"public`\"\");\r\n    [File]::AppendAllText($fileName,'>');\r\n    [File]::AppendAllText($fileName,\"`r`n`r`n</ResourceDictionary>\");\r\n}\r\n[XDocument]$xaml = [XDocument]::Load($fileName); \r\n# Create list from existing nodes.\r\n$nodes = $xaml.Root.Nodes();\r\n#$nodes = $xaml.Root.Elements(\"Viewbox\").ToArray();\r\n#$nodes = $xaml.DocumentElement.SelectNodes(\"/*[local-name() = 'ResourceDictionary']/*[local-name() = 'Viewbox']\");\r\n\r\n$nodeList = new-Object System.Collections.Generic.Dictionary[string`,object];\r\n[XElement]$node = $null;\r\nforeach ($node in $nodes)\r\n{\r\n    $nodeKey = $node.Attribute([XName]::Get(\"Key\", $xNs)).Value;\r\n    $nodeList.Add($nodeKey, $node);\r\n    #Write-Host \"Key $nodeKey\";\r\n}\r\n# Cleanup old nodes.\r\n$xaml.Root.RemoveNodes();\r\n\r\n# Start <ResourceName>.xaml.cs file.\r\n[File]::WriteAllText($fileNameCs, \"using System.Windows;`r`n\");\r\n[File]::AppendAllText($fileNameCs, \"`r`n\");\r\n[File]::AppendAllText($fileNameCs, \"namespace $namespace`r`n\");\r\n[File]::AppendAllText($fileNameCs, \"{`r`n\");\r\n[File]::AppendAllText($fileNameCs, \"`tpartial class $className : ResourceDictionary`r`n\");\r\n[File]::AppendAllText($fileNameCs, \"`t{`r`n\");\r\n[File]::AppendAllText($fileNameCs, \"`t`tpublic $className()`r`n\");\r\n[File]::AppendAllText($fileNameCs, \"`t`t{`r`n\");\r\n[File]::AppendAllText($fileNameCs, \"`t`t`tInitializeComponent();`r`n\");\r\n[File]::AppendAllText($fileNameCs, \"`t`t}`r`n\");\r\n[File]::AppendAllText($fileNameCs, \"`r`n\");\r\n[File]::AppendAllText($fileNameCs, \"`t`tpublic static $className Current => _Current = _Current ?? new $className();`r`n\");\r\n[File]::AppendAllText($fileNameCs, \"`t`tprivate static $className _Current;`r`n\");\r\n[File]::AppendAllText($fileNameCs, \"`r`n\");\r\n\r\nWrite-Host;\r\n\r\n# Process files.\r\nfor ($f = 0; $f -lt $files.Length; $f++) {\r\n    $file = $files[$f];\r\n    $fileHash = SHA256CheckSum -filePath $file.FullName;\r\n    $fileHashNodeName = \"Tag\";\r\n    $nodeXml = $null;\r\n    # Create unique key.\r\n    $key = \"Icon_$($file.BaseName)\";\r\n    # Get existing node or create new.\r\n\t$action = \"Insert:\";\r\n    if ($nodeList.ContainsKey($key))\r\n    {\r\n        $action = \"Update:\";\r\n        [XElement]$oldNode = $nodeList[$key];\r\n        # Get hash of current node.\r\n        $oldHash = $oldNode.Attribute([XName]::Get($fileHashNodeName)).Value;\r\n        if ($oldHash -eq \"SHA256_$fileHash\")\r\n        {\r\n            $nodeXml = $oldNode;\r\n            $action = \"Keep:  \";\r\n        }\r\n    }\r\n    $isNew = ($null -eq $nodeXml);\r\n    if ($isNew)\r\n    {\r\n        $nodeXml = Get-Content \"$($file.FullName)\" | & $inkscape --pipe --export-type=xaml | Out-String;\r\n    }\r\n    # Show file name.\r\n    Write-Host \"`t$action $($sourceDir.Name)\\$($file.Name)\";\r\n    # Remove name attributes.\r\n    [XDocument]$node = [XDocument]::Parse($nodeXml);\r\n    # Remove \"Name\" attributes.\r\n    RemoveAttributes -Node $node.Root -Name \"Name\";\r\n\r\n    RemoveAttributes -Node $node.Root -Name \"Key\";\r\n    RemoveAttributes -Node $node.Root -Name \"Shared\";\r\n\r\n    # Add image XML to XAML document.\r\n    $xaml.Root.Add($node.Root);\r\n    # Remove old attributes.\r\n    if ($isNew)\r\n    {\r\n        # Get node which was just added.\r\n        $ln = $xaml.Root.LastNode;\r\n        # Give node unique name.\r\n        $ln.SetAttributeValue([XName]::Get(\"Key\", $xNs), $key);\r\n        # Make sure that image copy is made when it is used.\r\n        $ln.SetAttributeValue([XName]::Get(\"Shared\", $xNs), \"False\");\r\n        # Set file hash.\r\n        $ln.SetAttributeValue([XName]::Get($fileHashNodeName), \"SHA256_$fileHash\");\r\n    }\r\n    # Write unique name to code file.\r\n    [File]::AppendAllText($fileNameCs, \"`t`tpublic const string $key = nameof($key);`r`n\");\r\n}\r\n\r\n# Save XAML file.\r\n$xaml.Save($fileName);\r\n# End <ResourceName>.xaml.cs file.\r\n[File]::AppendAllText($fileNameCs, \"`r`n\");\r\n[File]::AppendAllText($fileNameCs, \"`t}`r`n\");\r\n[File]::AppendAllText($fileNameCs, \"}`r`n\");\r\n\r\nWrite-Host;\r\nWrite-Host \"Done. Press any key to continue...\";\r\n$null = $Host.UI.RawUI.ReadKey(\"NoEcho,IncludeKeyDown\");\r\n"
  },
  {
    "path": "FocusLogger/JocysCom/Controls/Themes/Default.xaml",
    "content": "﻿<ResourceDictionary\r\n\txmlns=\"http://schemas.microsoft.com/winfx/2006/xaml/presentation\"\r\n\txmlns:x=\"http://schemas.microsoft.com/winfx/2006/xaml\"\r\n\txmlns:JcControls=\"clr-namespace:JocysCom.ClassLibrary.Controls\"\r\n\txmlns:sys=\"clr-namespace:System;assembly=mscorlib\">\r\n\r\n\t<!--\r\n\t\tDynamicResource vs StaticResource: StaticResource could cause controls to be garbage collected.\r\n\t\tUse DynamicResource, except for some properties like BasedOn or Converter.\r\n\t-->\r\n\t<!--  Colors  -->\r\n\r\n\t<SolidColorBrush x:Key=\"BackgroundTransparent\" Color=\"Transparent\" />\r\n\t<SolidColorBrush x:Key=\"BackgroundWhite\" Color=\"#ffffff\" />\r\n\t<SolidColorBrush x:Key=\"BackgroundLight\" Color=\"#f0f0f0\" />\r\n\t<SolidColorBrush x:Key=\"BackgroundDark\" Color=\"#dedede\" />\r\n\t<SolidColorBrush x:Key=\"BackgroundDarkSelected\" Color=\"#c2e7ff\" />\r\n\t<SolidColorBrush x:Key=\"BackgroundDarkPressed\" Color=\"#c2ffe7\" />\r\n\r\n\t<SolidColorBrush x:Key=\"BackgroundWarning\" Color=\"#ffe0e0\" />\r\n\t<SolidColorBrush x:Key=\"BackgroundHelp\" Color=\"#ffffe1\" />\r\n\t<SolidColorBrush x:Key=\"BorderDark\" Color=\"#cccccc\" />\r\n\t<!--<SolidColorBrush x:Key=\"BorderDarkSelected\" Color=\"#a4d8fa\" />-->\r\n\r\n\t<SolidColorBrush x:Key=\"ForegroundBlack\" Color=\"#000000\" />\r\n\t<SolidColorBrush x:Key=\"ForegroundWarning\" Color=\"#800000\" />\r\n\r\n\t<SolidColorBrush x:Key=\"SelectedForeground\" Color=\"#FFFFFFFF\" />\r\n\t<SolidColorBrush x:Key=\"MouseOverBackground\" Color=\"#FFCCCCCC\" />\r\n\t<SolidColorBrush x:Key=\"SelectedBackground\" Color=\"#FF0078D7\" />\r\n\t<SolidColorBrush x:Key=\"ColorNormal\" Color=\"#FF6699FF\" />\r\n\t<SolidColorBrush x:Key=\"ColorActive\" Color=\"#FF42C765\" />\r\n\t<SolidColorBrush x:Key=\"ColorOver\" Color=\"#FFFFCC66\" />\r\n\t<SolidColorBrush x:Key=\"ColorRecord\" Color=\"#FFFF6B66\" />\r\n\r\n\t<SolidColorBrush x:Key=\"SolidColorBrush_BFF\">#ff000000</SolidColorBrush>\r\n\r\n\t<!--  HUE Color Degrees - Light (hue: X, saturation: 10, lightness: 100)  -->\r\n\t<SolidColorBrush x:Key=\"SolidColorBrush_000L\">#FFE6E6</SolidColorBrush>\r\n\t<SolidColorBrush x:Key=\"SolidColorBrush_030L\">#FFF2E6</SolidColorBrush>\r\n\t<SolidColorBrush x:Key=\"SolidColorBrush_060L\">#FFFFE6</SolidColorBrush>\r\n\t<SolidColorBrush x:Key=\"SolidColorBrush_090L\">#F2FFE6</SolidColorBrush>\r\n\t<SolidColorBrush x:Key=\"SolidColorBrush_120L\">#E6FFE6</SolidColorBrush>\r\n\t<SolidColorBrush x:Key=\"SolidColorBrush_150L\">#E6FFF2</SolidColorBrush>\r\n\t<SolidColorBrush x:Key=\"SolidColorBrush_180L\">#E6FFFF</SolidColorBrush>\r\n\t<SolidColorBrush x:Key=\"SolidColorBrush_210L\">#E6F2FF</SolidColorBrush>\r\n\t<SolidColorBrush x:Key=\"SolidColorBrush_240L\">#E6E6FF</SolidColorBrush>\r\n\t<SolidColorBrush x:Key=\"SolidColorBrush_270L\">#F2E6FF</SolidColorBrush>\r\n\t<SolidColorBrush x:Key=\"SolidColorBrush_300L\">#FFE6FF</SolidColorBrush>\r\n\t<SolidColorBrush x:Key=\"SolidColorBrush_330L\">#FFE6F2</SolidColorBrush>\r\n\r\n\t<!--  HUE Color Degrees - Dark (hue: X, saturation: 100, lightness: 42)  -->\r\n\t<SolidColorBrush x:Key=\"SolidColorBrush_000D\">#D60000</SolidColorBrush>\r\n\t<SolidColorBrush x:Key=\"SolidColorBrush_030D\">#D66700</SolidColorBrush>\r\n\t<SolidColorBrush x:Key=\"SolidColorBrush_060D\">#D6D600</SolidColorBrush>\r\n\t<SolidColorBrush x:Key=\"SolidColorBrush_090D\">#67D600</SolidColorBrush>\r\n\t<SolidColorBrush x:Key=\"SolidColorBrush_120D\">#00D600</SolidColorBrush>\r\n\t<SolidColorBrush x:Key=\"SolidColorBrush_150D\">#00D667</SolidColorBrush>\r\n\t<SolidColorBrush x:Key=\"SolidColorBrush_180D\">#00D6D6</SolidColorBrush>\r\n\t<SolidColorBrush x:Key=\"SolidColorBrush_210D\">#0067D6</SolidColorBrush>\r\n\t<SolidColorBrush x:Key=\"SolidColorBrush_240D\">#0000D6</SolidColorBrush>\r\n\t<SolidColorBrush x:Key=\"SolidColorBrush_270D\">#6700D6</SolidColorBrush>\r\n\t<SolidColorBrush x:Key=\"SolidColorBrush_300D\">#D600D6</SolidColorBrush>\r\n\t<SolidColorBrush x:Key=\"SolidColorBrush_330D\">#D60067</SolidColorBrush>\r\n\r\n\t<!--  GradientBrush  -->\r\n\t<LinearGradientBrush x:Key=\"GradientBrushStyle\" StartPoint=\"1,0\" EndPoint=\"1,1\">\r\n\t\t<GradientStop Offset=\"0\" Color=\"{DynamicResource {x:Static SystemColors.WindowColorKey}}\" />\r\n\t\t<GradientStop Offset=\"1\" Color=\"{DynamicResource {x:Static SystemColors.ControlColorKey}}\" />\r\n\t</LinearGradientBrush>\r\n\r\n\t<Style TargetType=\"Canvas\">\r\n\t\t<Setter Property=\"SnapsToDevicePixels\" Value=\"False\" />\r\n\t</Style>\r\n\r\n\t<!--  Focus Visual Style  -->\r\n\t<Style x:Key=\"FocusVisualStyle_Default\">\r\n\t\t<Setter Property=\"Control.Template\">\r\n\t\t\t<Setter.Value>\r\n\t\t\t\t<ControlTemplate>\r\n\t\t\t\t\t<Rectangle\r\n\t\t\t\t\t\tRadiusX=\"5\"\r\n\t\t\t\t\t\tRadiusY=\"5\"\r\n\t\t\t\t\t\tStroke=\"Black\"\r\n\t\t\t\t\t\tStrokeDashArray=\"1 2\"\r\n\t\t\t\t\t\tStrokeThickness=\"1\" />\r\n\t\t\t\t</ControlTemplate>\r\n\t\t\t</Setter.Value>\r\n\t\t</Setter>\r\n\t</Style>\r\n\r\n\t<Style x:Key=\"FocusVisualStyle_TabItem\">\r\n\t\t<Setter Property=\"Control.Template\">\r\n\t\t\t<Setter.Value>\r\n\t\t\t\t<ControlTemplate>\r\n\t\t\t\t\t<Rectangle\r\n\t\t\t\t\t\tMargin=\"0,0,3,-1\"\r\n\t\t\t\t\t\tRadiusX=\"5\"\r\n\t\t\t\t\t\tRadiusY=\"5\"\r\n\t\t\t\t\t\tStroke=\"Black\"\r\n\t\t\t\t\t\tStrokeDashArray=\"1 2\"\r\n\t\t\t\t\t\tStrokeThickness=\"1\" />\r\n\t\t\t\t</ControlTemplate>\r\n\t\t\t</Setter.Value>\r\n\t\t</Setter>\r\n\t</Style>\r\n\r\n\t<Style x:Key=\"FocusVisualStyle_None\">\r\n\t\t<Setter Property=\"Control.Template\">\r\n\t\t\t<Setter.Value>\r\n\t\t\t\t<ControlTemplate>\r\n\t\t\t\t\t<Rectangle\r\n\t\t\t\t\t\tRadiusX=\"5\"\r\n\t\t\t\t\t\tRadiusY=\"5\"\r\n\t\t\t\t\t\tStroke=\"Black\"\r\n\t\t\t\t\t\tStrokeDashArray=\"1 2\"\r\n\t\t\t\t\t\tStrokeThickness=\"0\" />\r\n\t\t\t\t</ControlTemplate>\r\n\t\t\t</Setter.Value>\r\n\t\t</Setter>\r\n\t</Style>\r\n\r\n\t<!--  Window  -->\r\n\t<Style x:Key=\"Window_Default\" TargetType=\"Window\">\r\n\t\t<Setter Property=\"SnapsToDevicePixels\" Value=\"True\" />\r\n\t\t<!--<Setter Property=\"Background\" Value=\"{DynamicResource {x:Static SystemColors.ControlBrushKey}}\" />-->\r\n\t</Style>\r\n\r\n\t<!--  RichTextBox  -->\r\n\t<Style x:Key=\"RichTextBox_Default\" TargetType=\"RichTextBox\">\r\n\t\t<Setter Property=\"Padding\" Value=\"0,0,0,0\" />\r\n\t\t<Setter Property=\"IsDocumentEnabled\" Value=\"True\" />\r\n\t\t<Setter Property=\"IsReadOnly\" Value=\"True\" />\r\n\t\t<Setter Property=\"BorderThickness\" Value=\"0\" />\r\n\t\t<Setter Property=\"VerticalScrollBarVisibility\" Value=\"Auto\" />\r\n\t\t<Setter Property=\"VerticalAlignment\" Value=\"Stretch\" />\r\n\t\t<Setter Property=\"TextOptions.TextFormattingMode\" Value=\"Ideal\" />\r\n\t\t<Setter Property=\"LayoutTransform\">\r\n\t\t\t<Setter.Value>\r\n\t\t\t\t<ScaleTransform ScaleX=\"0.8\" ScaleY=\"0.8\" />\r\n\t\t\t</Setter.Value>\r\n\t\t</Setter>\r\n\t</Style>\r\n\r\n\t<!--  StatusBar  -->\r\n\t<Style x:Key=\"StatusBar_Default\" TargetType=\"StatusBar\">\r\n\t\t<Setter Property=\"SnapsToDevicePixels\" Value=\"True\" />\r\n\t\t<Setter Property=\"Background\" Value=\"{StaticResource BackgroundLight}\" />\r\n\t</Style>\r\n\r\n\t<!--  UserControl  -->\r\n\t<Style x:Key=\"UserControl_Default\" TargetType=\"UserControl\">\r\n\t\t<Setter Property=\"Background\" Value=\"{StaticResource BackgroundLight}\" />\r\n\t</Style>\r\n\r\n\t<!--  Border Description Normal  -->\r\n\t<Style x:Key=\"BorderDescriptionNormal\" TargetType=\"Border\">\r\n\t\t<Setter Property=\"Margin\" Value=\"3\" />\r\n\t\t<Setter Property=\"CornerRadius\" Value=\"5\" />\r\n\t\t<Setter Property=\"Background\" Value=\"{StaticResource BackgroundHelp}\" />\r\n\t\t<Style.Resources>\r\n\t\t\t<Style TargetType=\"TextBlock\">\r\n\t\t\t\t<Setter Property=\"TextWrapping\" Value=\"Wrap\" />\r\n\t\t\t\t<Setter Property=\"Padding\" Value=\"8,5,8,6\" />\r\n\t\t\t</Style>\r\n\t\t</Style.Resources>\r\n\t</Style>\r\n\t<!--  Border Description Warning  -->\r\n\t<Style x:Key=\"BorderDescriptionWarning\" BasedOn=\"{StaticResource BorderDescriptionNormal}\" TargetType=\"Border\">\r\n\t\t<Setter Property=\"Background\" Value=\"{StaticResource BackgroundWarning}\" />\r\n\t\t<Style.Resources>\r\n\t\t\t<Style TargetType=\"TextBlock\">\r\n\t\t\t\t<Setter Property=\"Foreground\" Value=\"{StaticResource ForegroundWarning}\" />\r\n\t\t\t\t<Setter Property=\"TextWrapping\" Value=\"Wrap\" />\r\n\t\t\t\t<Setter Property=\"Padding\" Value=\"7,5,7,6\" />\r\n\t\t\t</Style>\r\n\t\t</Style.Resources>\r\n\t</Style>\r\n\r\n\t<!--  Slider  -->\r\n\t<!--\r\n\t<Style x:Key=\"TickBar_Default\" TargetType=\"TickBar\">\r\n\t\t<Setter Property=\"Fill\" Value=\"{StaticResource BorderDark}\" />\r\n\t</Style>-->\r\n\r\n\t<Style x:Key=\"Slider_Default\" TargetType=\"Slider\">\r\n\t\t<!--<Setter Property=\"TickPlacement\" Value=\"None\" />-->\r\n\t\t<Setter Property=\"IsSnapToTickEnabled\" Value=\"True\" />\r\n\t\t<Setter Property=\"FocusVisualStyle\" Value=\"{StaticResource FocusVisualStyle_Default}\" />\r\n\t\t<Setter Property=\"Margin\" Value=\"3\" />\r\n\t\t<Setter Property=\"VerticalAlignment\" Value=\"Center\" />\r\n\t\t<Setter Property=\"Foreground\" Value=\"{StaticResource BackgroundDark}\" />\r\n\t\t<Setter Property=\"Width\" Value=\"200\" />\r\n\t</Style>\r\n\r\n\t<!--  Grid  -->\r\n\t<Style x:Key=\"Grid_Default\" TargetType=\"Grid\" />\r\n\r\n\t<!--  GridSplitter  -->\r\n\t<Style x:Key=\"GridSplitterHorizontal\" TargetType=\"GridSplitter\">\r\n\t\t<Setter Property=\"Padding\" Value=\"0,0,0,0\" />\r\n\t\t<Setter Property=\"HorizontalAlignment\" Value=\"Stretch\" />\r\n\t\t<Setter Property=\"VerticalAlignment\" Value=\"Stretch\" />\r\n\t\t<Setter Property=\"ResizeBehavior\" Value=\"PreviousAndNext\" />\r\n\t\t<Setter Property=\"Template\">\r\n\t\t\t<Setter.Value>\r\n\t\t\t\t<ControlTemplate TargetType=\"{x:Type GridSplitter}\">\r\n\t\t\t\t\t<!--  The \"Transparent\" background is necessary for the resize icon to be visible beneath the mouse correctly.  -->\r\n\t\t\t\t\t<Grid Background=\"Transparent\">\r\n\t\t\t\t\t\t<StackPanel Margin=\"3\" VerticalAlignment=\"Center\" Orientation=\"Vertical\">\r\n\t\t\t\t\t\t\t<StackPanel.LayoutTransform>\r\n\t\t\t\t\t\t\t\t<RotateTransform Angle=\"0\" />\r\n\t\t\t\t\t\t\t</StackPanel.LayoutTransform>\r\n\t\t\t\t\t\t\t<Canvas Width=\"28\" Height=\"3\">\r\n\t\t\t\t\t\t\t\t<Ellipse\r\n\t\t\t\t\t\t\t\t\tCanvas.Left=\"0\"\r\n\t\t\t\t\t\t\t\t\tWidth=\"3\"\r\n\t\t\t\t\t\t\t\t\tHeight=\"3\"\r\n\t\t\t\t\t\t\t\t\tFill=\"LightGray\" />\r\n\t\t\t\t\t\t\t\t<Ellipse\r\n\t\t\t\t\t\t\t\t\tCanvas.Left=\"6\"\r\n\t\t\t\t\t\t\t\t\tWidth=\"3\"\r\n\t\t\t\t\t\t\t\t\tHeight=\"3\"\r\n\t\t\t\t\t\t\t\t\tFill=\"LightGray\" />\r\n\t\t\t\t\t\t\t\t<Ellipse\r\n\t\t\t\t\t\t\t\t\tCanvas.Left=\"12\"\r\n\t\t\t\t\t\t\t\t\tWidth=\"3\"\r\n\t\t\t\t\t\t\t\t\tHeight=\"3\"\r\n\t\t\t\t\t\t\t\t\tFill=\"LightGray\" />\r\n\t\t\t\t\t\t\t\t<Ellipse\r\n\t\t\t\t\t\t\t\t\tCanvas.Left=\"18\"\r\n\t\t\t\t\t\t\t\t\tWidth=\"3\"\r\n\t\t\t\t\t\t\t\t\tHeight=\"3\"\r\n\t\t\t\t\t\t\t\t\tFill=\"LightGray\" />\r\n\t\t\t\t\t\t\t\t<Ellipse\r\n\t\t\t\t\t\t\t\t\tCanvas.Left=\"24\"\r\n\t\t\t\t\t\t\t\t\tWidth=\"3\"\r\n\t\t\t\t\t\t\t\t\tHeight=\"3\"\r\n\t\t\t\t\t\t\t\t\tFill=\"LightGray\" />\r\n\t\t\t\t\t\t\t</Canvas>\r\n\t\t\t\t\t\t</StackPanel>\r\n\t\t\t\t\t\t<StackPanel Height=\"1\" VerticalAlignment=\"Top\" Background=\"{StaticResource BorderDark}\" />\r\n\t\t\t\t\t\t<StackPanel Height=\"1\" VerticalAlignment=\"Bottom\" Background=\"{StaticResource BorderDark}\" />\r\n\t\t\t\t\t</Grid>\r\n\t\t\t\t</ControlTemplate>\r\n\t\t\t</Setter.Value>\r\n\t\t</Setter>\r\n\t</Style>\r\n\r\n\t<Style x:Key=\"GridSplitterVertical\" TargetType=\"GridSplitter\">\r\n\t\t<Setter Property=\"Padding\" Value=\"0,0,0,0\" />\r\n\t\t<Setter Property=\"HorizontalAlignment\" Value=\"Stretch\" />\r\n\t\t<Setter Property=\"VerticalAlignment\" Value=\"Stretch\" />\r\n\t\t<Setter Property=\"ResizeBehavior\" Value=\"PreviousAndNext\" />\r\n\t\t<Setter Property=\"Template\">\r\n\t\t\t<Setter.Value>\r\n\t\t\t\t<ControlTemplate TargetType=\"{x:Type GridSplitter}\">\r\n\t\t\t\t\t<!--  The \"Transparent\" background is necessary for the resize icon to be visible beneath the mouse correctly.  -->\r\n\t\t\t\t\t<Grid Background=\"Transparent\">\r\n\t\t\t\t\t\t<StackPanel Margin=\"3\" VerticalAlignment=\"Center\" Orientation=\"Vertical\">\r\n\t\t\t\t\t\t\t<StackPanel.LayoutTransform>\r\n\t\t\t\t\t\t\t\t<RotateTransform Angle=\"90\" />\r\n\t\t\t\t\t\t\t</StackPanel.LayoutTransform>\r\n\t\t\t\t\t\t\t<Canvas Width=\"28\" Height=\"3\">\r\n\t\t\t\t\t\t\t\t<Ellipse\r\n\t\t\t\t\t\t\t\t\tCanvas.Left=\"0\"\r\n\t\t\t\t\t\t\t\t\tWidth=\"3\"\r\n\t\t\t\t\t\t\t\t\tHeight=\"3\"\r\n\t\t\t\t\t\t\t\t\tFill=\"LightGray\" />\r\n\t\t\t\t\t\t\t\t<Ellipse\r\n\t\t\t\t\t\t\t\t\tCanvas.Left=\"6\"\r\n\t\t\t\t\t\t\t\t\tWidth=\"3\"\r\n\t\t\t\t\t\t\t\t\tHeight=\"3\"\r\n\t\t\t\t\t\t\t\t\tFill=\"LightGray\" />\r\n\t\t\t\t\t\t\t\t<Ellipse\r\n\t\t\t\t\t\t\t\t\tCanvas.Left=\"12\"\r\n\t\t\t\t\t\t\t\t\tWidth=\"3\"\r\n\t\t\t\t\t\t\t\t\tHeight=\"3\"\r\n\t\t\t\t\t\t\t\t\tFill=\"LightGray\" />\r\n\t\t\t\t\t\t\t\t<Ellipse\r\n\t\t\t\t\t\t\t\t\tCanvas.Left=\"18\"\r\n\t\t\t\t\t\t\t\t\tWidth=\"3\"\r\n\t\t\t\t\t\t\t\t\tHeight=\"3\"\r\n\t\t\t\t\t\t\t\t\tFill=\"LightGray\" />\r\n\t\t\t\t\t\t\t\t<Ellipse\r\n\t\t\t\t\t\t\t\t\tCanvas.Left=\"24\"\r\n\t\t\t\t\t\t\t\t\tWidth=\"3\"\r\n\t\t\t\t\t\t\t\t\tHeight=\"3\"\r\n\t\t\t\t\t\t\t\t\tFill=\"LightGray\" />\r\n\t\t\t\t\t\t\t</Canvas>\r\n\t\t\t\t\t\t</StackPanel>\r\n\t\t\t\t\t\t<StackPanel Width=\"1\" HorizontalAlignment=\"Left\" Background=\"{StaticResource BorderDark}\" />\r\n\t\t\t\t\t\t<StackPanel Width=\"1\" HorizontalAlignment=\"Right\" Background=\"{StaticResource BorderDark}\" />\r\n\t\t\t\t\t</Grid>\r\n\t\t\t\t</ControlTemplate>\r\n\t\t\t</Setter.Value>\r\n\t\t</Setter>\r\n\t</Style>\r\n\r\n\t<!--  DropDown Control Border  -->\r\n\t<Style x:Key=\"DropDownControlBorder\" TargetType=\"Border\">\r\n\t\t<Setter Property=\"Height\" Value=\"26\" />\r\n\t\t<Setter Property=\"MinWidth\" Value=\"26\" />\r\n\t\t<Setter Property=\"Padding\" Value=\"5,0,5,0\" />\r\n\t\t<Setter Property=\"VerticalAlignment\" Value=\"Center\" />\r\n\t\t<Setter Property=\"Background\" Value=\"{StaticResource BackgroundDark}\" />\r\n\t\t<Setter Property=\"CornerRadius\" Value=\"5\" />\r\n\t</Style>\r\n\r\n\t<!--  DropDown Control Triangle  -->\r\n\t<Style x:Key=\"DropDownControlTriangle\" TargetType=\"Path\">\r\n\t\t<Setter Property=\"Grid.Column\" Value=\"1\" />\r\n\t\t<Setter Property=\"Width\" Value=\"10\" />\r\n\t\t<Setter Property=\"Height\" Value=\"5\" />\r\n\t\t<Setter Property=\"Margin\" Value=\"1\" />\r\n\t\t<Setter Property=\"VerticalAlignment\" Value=\"Center\" />\r\n\t\t<Setter Property=\"HorizontalAlignment\" Value=\"Center\" />\r\n\t\t<Setter Property=\"Data\" Value=\"M 0,0 L 10,0 L 5,5 Z\" />\r\n\t\t<Setter Property=\"Fill\" Value=\"Black\" />\r\n\t\t<Setter Property=\"RenderTransformOrigin\" Value=\"0.5,0.5\" />\r\n\t\t<Setter Property=\"RenderTransform\">\r\n\t\t\t<Setter.Value>\r\n\t\t\t\t<ScaleTransform ScaleX=\"1.4\" ScaleY=\"1.4\" />\r\n\t\t\t</Setter.Value>\r\n\t\t</Setter>\r\n\t</Style>\r\n\r\n\t<!--  First MenuItem, containing other MenuItems, as drop-down  -->\r\n\t<Style x:Key=\"MenuItem_First\" TargetType=\"MenuItem\">\r\n\t\t<Setter Property=\"Template\">\r\n\t\t\t<Setter.Value>\r\n\t\t\t\t<ControlTemplate TargetType=\"MenuItem\">\r\n\t\t\t\t\t<Grid>\r\n\t\t\t\t\t\t<Border Name=\"DropDownControlBorder\" Style=\"{StaticResource DropDownControlBorder}\">\r\n\t\t\t\t\t\t\t<Grid>\r\n\t\t\t\t\t\t\t\t<Grid.ColumnDefinitions>\r\n\t\t\t\t\t\t\t\t\t<ColumnDefinition />\r\n\t\t\t\t\t\t\t\t\t<ColumnDefinition Width=\"16\" />\r\n\t\t\t\t\t\t\t\t</Grid.ColumnDefinitions>\r\n\t\t\t\t\t\t\t\t<Label\r\n\t\t\t\t\t\t\t\t\tName=\"MenuLabel\"\r\n\t\t\t\t\t\t\t\t\tMargin=\"0,0,5,0\"\r\n\t\t\t\t\t\t\t\t\tVerticalAlignment=\"Center\"\r\n\t\t\t\t\t\t\t\t\tContent=\"{TemplateBinding Header}\" />\r\n\t\t\t\t\t\t\t\t<Path Style=\"{StaticResource DropDownControlTriangle}\" />\r\n\t\t\t\t\t\t\t</Grid>\r\n\t\t\t\t\t\t</Border>\r\n\t\t\t\t\t\t<Popup\r\n\t\t\t\t\t\t\tName=\"PART_MenuPopup\"\r\n\t\t\t\t\t\t\tAllowsTransparency=\"True\"\r\n\t\t\t\t\t\t\tIsOpen=\"{Binding Path=IsMouseOver, RelativeSource={RelativeSource TemplatedParent}, Mode=OneWay}\"\r\n\t\t\t\t\t\t\tPlacement=\"Bottom\">\r\n\t\t\t\t\t\t\t<Border Background=\"{StaticResource BackgroundDark}\" CornerRadius=\"5\">\r\n\t\t\t\t\t\t\t\t<StackPanel IsItemsHost=\"True\" />\r\n\t\t\t\t\t\t\t</Border>\r\n\t\t\t\t\t\t</Popup>\r\n\t\t\t\t\t</Grid>\r\n\t\t\t\t\t<ControlTemplate.Triggers>\r\n\t\t\t\t\t\t<Trigger Property=\"IsMouseOver\" Value=\"True\">\r\n\t\t\t\t\t\t\t<Setter TargetName=\"DropDownControlBorder\" Property=\"Background\" Value=\"{StaticResource BackgroundDarkSelected}\" />\r\n\t\t\t\t\t\t</Trigger>\r\n\t\t\t\t\t\t<DataTrigger Binding=\"{Binding RelativeSource={RelativeSource Self}, Path=Header}\" Value=\"\">\r\n\t\t\t\t\t\t\t<Setter TargetName=\"MenuLabel\" Property=\"Visibility\" Value=\"Collapsed\" />\r\n\t\t\t\t\t\t</DataTrigger>\r\n\t\t\t\t\t</ControlTemplate.Triggers>\r\n\t\t\t\t</ControlTemplate>\r\n\t\t\t</Setter.Value>\r\n\t\t</Setter>\r\n\t</Style>\r\n\r\n\t<!--  MenuItem  -->\r\n\t<Style x:Key=\"MenuItem_Default\" TargetType=\"MenuItem\">\r\n\t\t<Setter Property=\"HorizontalContentAlignment\" Value=\"Left\" />\r\n\t\t<Setter Property=\"VerticalContentAlignment\" Value=\"Center\" />\r\n\t</Style>\r\n\r\n\t<!--  Menu  -->\r\n\t<Style x:Key=\"Menu_Default3\" TargetType=\"Menu\">\r\n\t\t<Style.Resources>\r\n\t\t\t<!--  MenuItem  -->\r\n\t\t\t<Style TargetType=\"MenuItem\">\r\n\t\t\t\t<Setter Property=\"Template\">\r\n\t\t\t\t\t<Setter.Value>\r\n\t\t\t\t\t\t<ControlTemplate TargetType=\"MenuItem\">\r\n\t\t\t\t\t\t\t<Border Name=\"DropDownControlBorder\" Style=\"{StaticResource DropDownControlBorder}\">\r\n\t\t\t\t\t\t\t\t<Grid>\r\n\t\t\t\t\t\t\t\t\t<Popup\r\n\t\t\t\t\t\t\t\t\t\tName=\"PART_Popup\"\r\n\t\t\t\t\t\t\t\t\t\tAllowsTransparency=\"True\"\r\n\t\t\t\t\t\t\t\t\t\tIsOpen=\"{Binding Path=IsMouseOver, RelativeSource={RelativeSource TemplatedParent}, Mode=OneWay}\"\r\n\t\t\t\t\t\t\t\t\t\tPlacement=\"Bottom\">\r\n\t\t\t\t\t\t\t\t\t\t<Border Background=\"{StaticResource BackgroundDark}\" CornerRadius=\"5\">\r\n\t\t\t\t\t\t\t\t\t\t\t<StackPanel IsItemsHost=\"True\" />\r\n\t\t\t\t\t\t\t\t\t\t</Border>\r\n\t\t\t\t\t\t\t\t\t</Popup>\r\n\t\t\t\t\t\t\t\t\t<ContentPresenter Content=\"{TemplateBinding Header}\" />\r\n\t\t\t\t\t\t\t\t</Grid>\r\n\t\t\t\t\t\t\t</Border>\r\n\t\t\t\t\t\t\t<!--  Triggers  -->\r\n\t\t\t\t\t\t\t<ControlTemplate.Triggers>\r\n\t\t\t\t\t\t\t\t<Trigger Property=\"IsMouseOver\" Value=\"True\">\r\n\t\t\t\t\t\t\t\t\t<Setter TargetName=\"DropDownControlBorder\" Property=\"Background\" Value=\"{StaticResource BackgroundDarkSelected}\" />\r\n\t\t\t\t\t\t\t\t</Trigger>\r\n\t\t\t\t\t\t\t</ControlTemplate.Triggers>\r\n\t\t\t\t\t\t</ControlTemplate>\r\n\t\t\t\t\t</Setter.Value>\r\n\t\t\t\t</Setter>\r\n\t\t\t</Style>\r\n\t\t</Style.Resources>\r\n\t</Style>\r\n\r\n\t<!--  Menu template  -->\r\n\t<Style x:Key=\"Menu_Default\" TargetType=\"Menu\">\r\n\t\t<Setter Property=\"Template\">\r\n\t\t\t<Setter.Value>\r\n\t\t\t\t<ControlTemplate TargetType=\"Menu\">\r\n\t\t\t\t\t<Grid>\r\n\t\t\t\t\t\t<Border Name=\"DropDownControlBorder\" Style=\"{StaticResource DropDownControlBorder}\">\r\n\t\t\t\t\t\t\t<Grid>\r\n\t\t\t\t\t\t\t\t<Grid.ColumnDefinitions>\r\n\t\t\t\t\t\t\t\t\t<ColumnDefinition />\r\n\t\t\t\t\t\t\t\t\t<ColumnDefinition Width=\"16\" />\r\n\t\t\t\t\t\t\t\t</Grid.ColumnDefinitions>\r\n\t\t\t\t\t\t\t\t<Label Margin=\"0,0,5,0\" VerticalAlignment=\"Center\" Content=\"{TemplateBinding Tag}\" />\r\n\t\t\t\t\t\t\t\t<Path Style=\"{StaticResource DropDownControlTriangle}\" />\r\n\t\t\t\t\t\t\t</Grid>\r\n\t\t\t\t\t\t</Border>\r\n\t\t\t\t\t\t<Popup\r\n\t\t\t\t\t\t\tName=\"PART_MenuPopup\"\r\n\t\t\t\t\t\t\tAllowsTransparency=\"True\"\r\n\t\t\t\t\t\t\tIsOpen=\"{Binding Path=IsMouseOver, RelativeSource={RelativeSource TemplatedParent}, Mode=OneWay}\"\r\n\t\t\t\t\t\t\tPlacement=\"Bottom\">\r\n\t\t\t\t\t\t\t<Border Background=\"{StaticResource BackgroundDark}\" CornerRadius=\"5\">\r\n\t\t\t\t\t\t\t\t<StackPanel IsItemsHost=\"True\" />\r\n\t\t\t\t\t\t\t</Border>\r\n\t\t\t\t\t\t</Popup>\r\n\t\t\t\t\t</Grid>\r\n\t\t\t\t\t<ControlTemplate.Triggers>\r\n\t\t\t\t\t\t<Trigger Property=\"IsMouseOver\" Value=\"True\">\r\n\t\t\t\t\t\t\t<Setter TargetName=\"DropDownControlBorder\" Property=\"Background\" Value=\"{StaticResource BackgroundDarkSelected}\" />\r\n\t\t\t\t\t\t</Trigger>\r\n\t\t\t\t\t</ControlTemplate.Triggers>\r\n\t\t\t\t</ControlTemplate>\r\n\t\t\t</Setter.Value>\r\n\t\t</Setter>\r\n\t\t<Style.Resources>\r\n\t\t\t<!--  MenuItem Template  -->\r\n\t\t\t<Style TargetType=\"MenuItem\">\r\n\t\t\t\t<Setter Property=\"Template\">\r\n\t\t\t\t\t<Setter.Value>\r\n\t\t\t\t\t\t<ControlTemplate TargetType=\"MenuItem\">\r\n\t\t\t\t\t\t\t<Border Name=\"PART_Border\" Padding=\"10,5,10,5\" CornerRadius=\"5\">\r\n\t\t\t\t\t\t\t\t<Grid>\r\n\t\t\t\t\t\t\t\t\t<Popup\r\n\t\t\t\t\t\t\t\t\t\tName=\"PART_Popup\"\r\n\t\t\t\t\t\t\t\t\t\tAllowsTransparency=\"True\"\r\n\t\t\t\t\t\t\t\t\t\tIsOpen=\"{Binding Path=IsMouseOver, RelativeSource={RelativeSource TemplatedParent}, Mode=OneWay}\"\r\n\t\t\t\t\t\t\t\t\t\tPlacement=\"Bottom\">\r\n\t\t\t\t\t\t\t\t\t\t<Border Background=\"{StaticResource BackgroundDark}\" CornerRadius=\"5\">\r\n\t\t\t\t\t\t\t\t\t\t\t<StackPanel IsItemsHost=\"True\" />\r\n\t\t\t\t\t\t\t\t\t\t</Border>\r\n\t\t\t\t\t\t\t\t\t</Popup>\r\n\t\t\t\t\t\t\t\t\t<ContentPresenter Content=\"{TemplateBinding Header}\" />\r\n\t\t\t\t\t\t\t\t</Grid>\r\n\t\t\t\t\t\t\t</Border>\r\n\t\t\t\t\t\t\t<!--  Triggers  -->\r\n\t\t\t\t\t\t\t<ControlTemplate.Triggers>\r\n\t\t\t\t\t\t\t\t<Trigger Property=\"IsMouseOver\" Value=\"True\">\r\n\t\t\t\t\t\t\t\t\t<Setter TargetName=\"PART_Border\" Property=\"Background\" Value=\"{StaticResource BackgroundDarkSelected}\" />\r\n\t\t\t\t\t\t\t\t</Trigger>\r\n\t\t\t\t\t\t\t</ControlTemplate.Triggers>\r\n\t\t\t\t\t\t</ControlTemplate>\r\n\t\t\t\t\t</Setter.Value>\r\n\t\t\t\t</Setter>\r\n\t\t\t</Style>\r\n\t\t</Style.Resources>\r\n\t</Style>\r\n\r\n\t<!--  Label  -->\r\n\t<Style x:Key=\"Label_Default\" TargetType=\"Label\">\r\n\t\t<Setter Property=\"SnapsToDevicePixels\" Value=\"False\" />\r\n\t\t<Setter Property=\"VerticalContentAlignment\" Value=\"Center\" />\r\n\t\t<Setter Property=\"Padding\" Value=\"0\" />\r\n\t\t<Setter Property=\"Margin\" Value=\"0\" />\r\n\t</Style>\r\n\r\n\t<!--  StackPanel  -->\r\n\t<Style x:Key=\"Container\" TargetType=\"StackPanel\">\r\n\t\t<Setter Property=\"Margin\" Value=\"0\" />\r\n\t</Style>\r\n\r\n\t<!--  ContentControl  -->\r\n\t<Style x:Key=\"ContentControl_Default\" TargetType=\"ContentControl\">\r\n\t\t<Setter Property=\"Template\">\r\n\t\t\t<Setter.Value>\r\n\t\t\t\t<ControlTemplate TargetType=\"ContentControl\">\r\n\t\t\t\t\t<ControlTemplate.Resources>\r\n\t\t\t\t\t\t<Style TargetType=\"Image\">\r\n\t\t\t\t\t\t\t<Setter Property=\"Width\" Value=\"16\" />\r\n\t\t\t\t\t\t\t<Setter Property=\"Height\" Value=\"16\" />\r\n\t\t\t\t\t\t</Style>\r\n\t\t\t\t\t\t<Style TargetType=\"Label\">\r\n\t\t\t\t\t\t\t<Setter Property=\"Padding\" Value=\"0\" />\r\n\t\t\t\t\t\t\t<Setter Property=\"Margin\" Value=\"5,0,0,0\" />\r\n\t\t\t\t\t\t\t<Setter Property=\"VerticalAlignment\" Value=\"Center\" />\r\n\t\t\t\t\t\t</Style>\r\n\t\t\t\t\t</ControlTemplate.Resources>\r\n\t\t\t\t\t<!--  Template  -->\r\n\t\t\t\t\t<ContentPresenter HorizontalAlignment=\"Center\" VerticalAlignment=\"Center\" />\r\n\t\t\t\t</ControlTemplate>\r\n\t\t\t</Setter.Value>\r\n\t\t</Setter>\r\n\t</Style>\r\n\r\n\t<!--  BUTTON StackPanel  -->\r\n\t<Style x:Key=\"ButtonStackPanel\" TargetType=\"StackPanel\">\r\n\t\t<Setter Property=\"Orientation\" Value=\"Horizontal\" />\r\n\t\t<Setter Property=\"IsHitTestVisible\" Value=\"False\" />\r\n\t</Style>\r\n\r\n\t<!--  ContentControl - Button  -->\r\n\t<Style x:Key=\"ButtonContentControl\" TargetType=\"ContentControl\">\r\n\t\t<Setter Property=\"Margin\" Value=\"0,2,0,2\" />\r\n\t\t<Setter Property=\"Padding\" Value=\"0,0,0,0\" />\r\n\t\t<Setter Property=\"Width\" Value=\"16\" />\r\n\t\t<Setter Property=\"Height\" Value=\"16\" />\r\n\t</Style>\r\n\r\n\t<!--  Label  -->\r\n\t<Style x:Key=\"ButtonLabel\" TargetType=\"Label\">\r\n\t\t<Setter Property=\"VerticalContentAlignment\" Value=\"Center\" />\r\n\t\t<Setter Property=\"HorizontalContentAlignment\" Value=\"Center\" />\r\n\t</Style>\r\n\r\n\t<!--  Button  -->\r\n\t<Style x:Key=\"Button_Default\" TargetType=\"Button\">\r\n\t\t<Setter Property=\"FocusVisualStyle\" Value=\"{StaticResource FocusVisualStyle_Default}\" />\r\n\t\t<Setter Property=\"HorizontalAlignment\" Value=\"Left\" />\r\n\t\t<Setter Property=\"VerticalAlignment\" Value=\"Center\" />\r\n\t\t<Setter Property=\"HorizontalContentAlignment\" Value=\"Center\" />\r\n\t\t<Setter Property=\"SnapsToDevicePixels\" Value=\"True\" />\r\n\t\t<Setter Property=\"MinWidth\" Value=\"26\" />\r\n\t\t<Setter Property=\"Margin\" Value=\"3\" />\r\n\t\t<Setter Property=\"Padding\" Value=\"5\" />\r\n\t\t<Setter Property=\"BorderThickness\" Value=\"0\" />\r\n\t\t<Setter Property=\"Background\" Value=\"{StaticResource BackgroundDark}\" />\r\n\t\t<Setter Property=\"Template\">\r\n\t\t\t<Setter.Value>\r\n\t\t\t\t<ControlTemplate TargetType=\"Button\">\r\n\t\t\t\t\t<ControlTemplate.Resources>\r\n\t\t\t\t\t\t<Style TargetType=\"Border\">\r\n\t\t\t\t\t\t\t<Setter Property=\"Margin\" Value=\"0\" />\r\n\t\t\t\t\t\t\t<Setter Property=\"CornerRadius\" Value=\"5\" />\r\n\t\t\t\t\t\t</Style>\r\n\t\t\t\t\t\t<Style TargetType=\"ContentPresenter\">\r\n\t\t\t\t\t\t\t<Setter Property=\"HorizontalAlignment\" Value=\"Center\" />\r\n\t\t\t\t\t\t\t<Setter Property=\"VerticalAlignment\" Value=\"Center\" />\r\n\t\t\t\t\t\t</Style>\r\n\t\t\t\t\t\t<Style TargetType=\"StackPanel\">\r\n\t\t\t\t\t\t\t<Setter Property=\"Orientation\" Value=\"Horizontal\" />\r\n\t\t\t\t\t\t\t<Setter Property=\"HorizontalAlignment\" Value=\"Center\" />\r\n\t\t\t\t\t\t</Style>\r\n\t\t\t\t\t\t<Style TargetType=\"ContentControl\">\r\n\t\t\t\t\t\t\t<Setter Property=\"Focusable\" Value=\"False\" />\r\n\t\t\t\t\t\t\t<Setter Property=\"Width\" Value=\"16\" />\r\n\t\t\t\t\t\t\t<Setter Property=\"Height\" Value=\"16\" />\r\n\t\t\t\t\t\t</Style>\r\n\t\t\t\t\t\t<Style TargetType=\"Image\">\r\n\t\t\t\t\t\t\t<Setter Property=\"Width\" Value=\"16\" />\r\n\t\t\t\t\t\t\t<Setter Property=\"Height\" Value=\"16\" />\r\n\t\t\t\t\t\t</Style>\r\n\t\t\t\t\t\t<Style TargetType=\"Label\">\r\n\t\t\t\t\t\t\t<Setter Property=\"Padding\" Value=\"0\" />\r\n\t\t\t\t\t\t\t<Setter Property=\"Margin\" Value=\"5,0,0,0\" />\r\n\t\t\t\t\t\t\t<Setter Property=\"VerticalAlignment\" Value=\"Center\" />\r\n\t\t\t\t\t\t</Style>\r\n\t\t\t\t\t</ControlTemplate.Resources>\r\n\t\t\t\t\t<!--  Template  -->\r\n\t\t\t\t\t<Border\r\n\t\t\t\t\t\tName=\"ButtonBorder\"\r\n\t\t\t\t\t\tPadding=\"{TemplateBinding Padding}\"\r\n\t\t\t\t\t\tBackground=\"{TemplateBinding Background}\"\r\n\t\t\t\t\t\tBorderBrush=\"{TemplateBinding BorderBrush}\"\r\n\t\t\t\t\t\tBorderThickness=\"{TemplateBinding BorderThickness}\">\r\n\t\t\t\t\t\t<ContentPresenter HorizontalAlignment=\"{TemplateBinding HorizontalContentAlignment}\" />\r\n\t\t\t\t\t</Border>\r\n\t\t\t\t\t<!--  Triggers  -->\r\n\t\t\t\t\t<ControlTemplate.Triggers>\r\n\t\t\t\t\t\t<!--  New MultiTrigger for IsMouseOver and IsPressed  -->\r\n\t\t\t\t\t\t<MultiTrigger>\r\n\t\t\t\t\t\t\t<MultiTrigger.Conditions>\r\n\t\t\t\t\t\t\t\t<Condition Property=\"IsMouseOver\" Value=\"True\" />\r\n\t\t\t\t\t\t\t\t<Condition Property=\"IsEnabled\" Value=\"True\" />\r\n\t\t\t\t\t\t\t\t<Condition Property=\"IsPressed\" Value=\"True\" />\r\n\t\t\t\t\t\t\t</MultiTrigger.Conditions>\r\n\t\t\t\t\t\t\t<Setter TargetName=\"ButtonBorder\" Property=\"Background\" Value=\"{StaticResource BackgroundDarkPressed}\" />\r\n\t\t\t\t\t\t</MultiTrigger>\r\n\r\n\t\t\t\t\t\t<!--  Existing MultiTrigger for IsMouseOver  -->\r\n\t\t\t\t\t\t<MultiTrigger>\r\n\t\t\t\t\t\t\t<MultiTrigger.Conditions>\r\n\t\t\t\t\t\t\t\t<Condition Property=\"IsMouseOver\" Value=\"True\" />\r\n\t\t\t\t\t\t\t\t<Condition Property=\"IsEnabled\" Value=\"True\" />\r\n\t\t\t\t\t\t\t\t<Condition Property=\"IsPressed\" Value=\"False\" />\r\n\t\t\t\t\t\t\t</MultiTrigger.Conditions>\r\n\t\t\t\t\t\t\t<Setter TargetName=\"ButtonBorder\" Property=\"Background\" Value=\"{StaticResource BackgroundDarkSelected}\" />\r\n\t\t\t\t\t\t</MultiTrigger>\r\n\r\n\t\t\t\t\t\t<!--  Existing Trigger for IsEnabled  -->\r\n\t\t\t\t\t\t<Trigger Property=\"IsEnabled\" Value=\"False\">\r\n\t\t\t\t\t\t\t<Setter Property=\"Opacity\" Value=\"0.5\" />\r\n\t\t\t\t\t\t\t<Setter Property=\"Focusable\" Value=\"False\" />\r\n\t\t\t\t\t\t</Trigger>\r\n\t\t\t\t\t</ControlTemplate.Triggers>\r\n\r\n\t\t\t\t</ControlTemplate>\r\n\t\t\t</Setter.Value>\r\n\t\t</Setter>\r\n\t</Style>\r\n\r\n\t<!--  Button for ToolBar  -->\r\n\t<Style x:Key=\"ToolBarButton\" TargetType=\"Button\">\r\n\t\t<Setter Property=\"Margin\" Value=\"0\" />\r\n\t\t<Setter Property=\"Padding\" Value=\"2\" />\r\n\t\t<Setter Property=\"BorderThickness\" Value=\"0\" />\r\n\t\t<Setter Property=\"Background\" Value=\"Transparent\" />\r\n\t\t<Style.Resources>\r\n\t\t\t<Style TargetType=\"Border\">\r\n\t\t\t\t<Setter Property=\"CornerRadius\" Value=\"5\" />\r\n\t\t\t</Style>\r\n\t\t</Style.Resources>\r\n\t\t<Style.Triggers>\r\n\t\t\t<Trigger Property=\"IsEnabled\" Value=\"False\">\r\n\t\t\t\t<Setter Property=\"Opacity\" Value=\"0.5\" />\r\n\t\t\t</Trigger>\r\n\t\t\t<MultiTrigger>\r\n\t\t\t\t<MultiTrigger.Conditions>\r\n\t\t\t\t\t<Condition Property=\"IsMouseOver\" Value=\"True\" />\r\n\t\t\t\t\t<Condition Property=\"IsEnabled\" Value=\"True\" />\r\n\t\t\t\t</MultiTrigger.Conditions>\r\n\t\t\t</MultiTrigger>\r\n\t\t</Style.Triggers>\r\n\t</Style>\r\n\r\n\t<!--  ListBox  -->\r\n\t<Style x:Key=\"ListBox_Default\" TargetType=\"ListBox\">\r\n\t\t<Setter Property=\"Template\">\r\n\t\t\t<Setter.Value>\r\n\t\t\t\t<ControlTemplate TargetType=\"ListBox\">\r\n\t\t\t\t\t<Border BorderBrush=\"{StaticResource BorderDark}\" BorderThickness=\"1\" CornerRadius=\"5\">\r\n\t\t\t\t\t\t<Grid RenderOptions.EdgeMode=\"Aliased\">\r\n\t\t\t\t\t\t\t<Grid.OpacityMask>\r\n\t\t\t\t\t\t\t\t<VisualBrush Visual=\"{Binding ElementName=OpacityMaskBorder}\" />\r\n\t\t\t\t\t\t\t</Grid.OpacityMask>\r\n\t\t\t\t\t\t\t<Border Name=\"OpacityMaskBorder\" Background=\"{StaticResource BackgroundDark}\" CornerRadius=\"5\" />\r\n\t\t\t\t\t\t\t<ScrollViewer ClipToBounds=\"True\">\r\n\t\t\t\t\t\t\t\t<ItemsPresenter />\r\n\t\t\t\t\t\t\t</ScrollViewer>\r\n\t\t\t\t\t\t</Grid>\r\n\t\t\t\t\t</Border>\r\n\t\t\t\t</ControlTemplate>\r\n\t\t\t</Setter.Value>\r\n\t\t</Setter>\r\n\t</Style>\r\n\r\n\r\n\t<!--  ListBoxItem  -->\r\n\t<Style x:Key=\"ListBoxItem_Default\" TargetType=\"ListBoxItem\">\r\n\t\t<Setter Property=\"BorderThickness\" Value=\"0\" />\r\n\t\t<Setter Property=\"Padding\" Value=\"5\" />\r\n\t\t<Setter Property=\"Template\">\r\n\t\t\t<Setter.Value>\r\n\t\t\t\t<ControlTemplate TargetType=\"ListBoxItem\">\r\n\t\t\t\t\t<Border\r\n\t\t\t\t\t\tMargin=\"{TemplateBinding Margin}\"\r\n\t\t\t\t\t\tPadding=\"{TemplateBinding Padding}\"\r\n\t\t\t\t\t\tBackground=\"{TemplateBinding Background}\"\r\n\t\t\t\t\t\tBorderBrush=\"{TemplateBinding BorderBrush}\"\r\n\t\t\t\t\t\tBorderThickness=\"{TemplateBinding BorderThickness}\">\r\n\t\t\t\t\t\t<ContentPresenter HorizontalAlignment=\"{TemplateBinding HorizontalContentAlignment}\" VerticalAlignment=\"{TemplateBinding VerticalContentAlignment}\" SnapsToDevicePixels=\"{TemplateBinding SnapsToDevicePixels}\" />\r\n\t\t\t\t\t</Border>\r\n\t\t\t\t\t<ControlTemplate.Triggers>\r\n\t\t\t\t\t\t<Trigger Property=\"IsSelected\" Value=\"True\">\r\n\t\t\t\t\t\t\t<Setter Property=\"Background\" Value=\"{StaticResource SelectedBackground}\" />\r\n\t\t\t\t\t\t\t<Setter Property=\"Foreground\" Value=\"{StaticResource SelectedForeground}\" />\r\n\t\t\t\t\t\t</Trigger>\r\n\t\t\t\t\t\t<Trigger Property=\"IsEnabled\" Value=\"False\">\r\n\t\t\t\t\t\t\t<Setter Property=\"Opacity\" Value=\"0.5\" />\r\n\t\t\t\t\t\t</Trigger>\r\n\t\t\t\t\t</ControlTemplate.Triggers>\r\n\t\t\t\t</ControlTemplate>\r\n\t\t\t</Setter.Value>\r\n\t\t</Setter>\r\n\t</Style>\r\n\r\n\t<!--  TabControl  -->\r\n\t<!--  This style is making control to fail garbage collection (leak memory)  -->\r\n\t<Style x:Key=\"TabControl_Default\" TargetType=\"TabControl\">\r\n\t\t<Setter Property=\"VerticalAlignment\" Value=\"Stretch\" />\r\n\t\t<Setter Property=\"VerticalContentAlignment\" Value=\"Stretch\" />\r\n\t\t<Setter Property=\"Template\">\r\n\t\t\t<Setter.Value>\r\n\t\t\t\t<ControlTemplate TargetType=\"TabControl\">\r\n\t\t\t\t\t<Grid x:Name=\"templateRoot\" KeyboardNavigation.TabNavigation=\"Local\">\r\n\t\t\t\t\t\t<!--  Define RowDefinitions with names for use in triggers  -->\r\n\t\t\t\t\t\t<Grid.RowDefinitions>\r\n\t\t\t\t\t\t\t<RowDefinition x:Name=\"RowDefinition0\" Height=\"Auto\" />\r\n\t\t\t\t\t\t\t<RowDefinition x:Name=\"RowDefinition1\" Height=\"*\" />\r\n\t\t\t\t\t\t</Grid.RowDefinitions>\r\n\r\n\t\t\t\t\t\t<!--  TabPanel for tab headers  -->\r\n\t\t\t\t\t\t<TabPanel\r\n\t\t\t\t\t\t\tx:Name=\"HeaderPanel\"\r\n\t\t\t\t\t\t\tGrid.Row=\"0\"\r\n\t\t\t\t\t\t\tPanel.ZIndex=\"1\"\r\n\t\t\t\t\t\t\tIsItemsHost=\"True\" />\r\n\r\n\t\t\t\t\t\t<!--  Border around the content area  -->\r\n\t\t\t\t\t\t<Border\r\n\t\t\t\t\t\t\tx:Name=\"MainBorder\"\r\n\t\t\t\t\t\t\tGrid.Row=\"1\"\r\n\t\t\t\t\t\t\tBorderBrush=\"{StaticResource BorderDark}\"\r\n\t\t\t\t\t\t\tBorderThickness=\"1\"\r\n\t\t\t\t\t\t\tCornerRadius=\"0,5,5,5\"\r\n\t\t\t\t\t\t\tSnapsToDevicePixels=\"True\" />\r\n\r\n\t\t\t\t\t\t<!--  Content area  -->\r\n\t\t\t\t\t\t<Grid x:Name=\"ContentPanel\" Grid.Row=\"1\" Margin=\"1\">\r\n\t\t\t\t\t\t\t<Grid.OpacityMask>\r\n\t\t\t\t\t\t\t\t<VisualBrush Visual=\"{Binding ElementName=OpacityMaskBorder}\" />\r\n\t\t\t\t\t\t\t</Grid.OpacityMask>\r\n\t\t\t\t\t\t\t<Border Name=\"OpacityMaskBorder\" Background=\"{StaticResource BackgroundLight}\" CornerRadius=\"0,5,5,5\" />\r\n\t\t\t\t\t\t\t<ContentPresenter VerticalAlignment=\"{TemplateBinding VerticalContentAlignment}\" ClipToBounds=\"True\" ContentSource=\"SelectedContent\" />\r\n\t\t\t\t\t\t</Grid>\r\n\t\t\t\t\t</Grid>\r\n\r\n\t\t\t\t\t<!--  ControlTemplate Triggers to respond to TabStripPlacement  -->\r\n\t\t\t\t\t<ControlTemplate.Triggers>\r\n\t\t\t\t\t\t<!--  Trigger for TabStripPlacement=\"Bottom\"  -->\r\n\t\t\t\t\t\t<Trigger Property=\"TabStripPlacement\" Value=\"Bottom\">\r\n\t\t\t\t\t\t\t<!--  Swap the positions of the HeaderPanel and ContentPanel  -->\r\n\t\t\t\t\t\t\t<Setter TargetName=\"HeaderPanel\" Property=\"Grid.Row\" Value=\"1\" />\r\n\t\t\t\t\t\t\t<Setter TargetName=\"MainBorder\" Property=\"Grid.Row\" Value=\"0\" />\r\n\t\t\t\t\t\t\t<Setter TargetName=\"ContentPanel\" Property=\"Grid.Row\" Value=\"0\" />\r\n\r\n\t\t\t\t\t\t\t<!--  Swap the heights of the RowDefinitions  -->\r\n\t\t\t\t\t\t\t<Setter TargetName=\"RowDefinition0\" Property=\"Height\" Value=\"*\" />\r\n\t\t\t\t\t\t\t<Setter TargetName=\"RowDefinition1\" Property=\"Height\" Value=\"Auto\" />\r\n\r\n\t\t\t\t\t\t\t<!--  Adjust CornerRadius for the borders  -->\r\n\t\t\t\t\t\t\t<Setter TargetName=\"MainBorder\" Property=\"CornerRadius\" Value=\"5,5,5,0\" />\r\n\t\t\t\t\t\t\t<Setter TargetName=\"OpacityMaskBorder\" Property=\"CornerRadius\" Value=\"5,5,5,0\" />\r\n\t\t\t\t\t\t</Trigger>\r\n\t\t\t\t\t\t<!--  You can add triggers for Left and Right placements if needed  -->\r\n\t\t\t\t\t</ControlTemplate.Triggers>\r\n\t\t\t\t</ControlTemplate>\r\n\t\t\t</Setter.Value>\r\n\t\t</Setter>\r\n\r\n\t\t<!--  Style Resources for TabItems  -->\r\n\t\t<Style.Resources>\r\n\t\t\t<!--  Convertor used in your original style (ensure it's defined in your namespace)  -->\r\n\t\t\t<JcControls:TabIndexConverter x:Key=\"_TabIndexConverter\" />\r\n\r\n\t\t\t<!--  Style for TabItem  -->\r\n\t\t\t<Style TargetType=\"TabItem\">\r\n\t\t\t\t<Setter Property=\"SnapsToDevicePixels\" Value=\"True\" />\r\n\t\t\t\t<Setter Property=\"FocusVisualStyle\" Value=\"{StaticResource FocusVisualStyle_TabItem}\" />\r\n\t\t\t\t<Setter Property=\"Background\" Value=\"{StaticResource BackgroundWhite}\" />\r\n\t\t\t\t<Setter Property=\"Template\">\r\n\t\t\t\t\t<Setter.Value>\r\n\t\t\t\t\t\t<ControlTemplate TargetType=\"TabItem\">\r\n\t\t\t\t\t\t\t<ControlTemplate.Resources>\r\n\t\t\t\t\t\t\t\t<Style TargetType=\"Label\">\r\n\t\t\t\t\t\t\t\t\t<Setter Property=\"Padding\" Value=\"0\" />\r\n\t\t\t\t\t\t\t\t\t<Setter Property=\"Margin\" Value=\"3,0,0,0\" />\r\n\t\t\t\t\t\t\t\t\t<Setter Property=\"VerticalAlignment\" Value=\"Center\" />\r\n\t\t\t\t\t\t\t\t</Style>\r\n\t\t\t\t\t\t\t\t<Style TargetType=\"ContentControl\">\r\n\t\t\t\t\t\t\t\t\t<Setter Property=\"Width\" Value=\"16\" />\r\n\t\t\t\t\t\t\t\t\t<Setter Property=\"Height\" Value=\"16\" />\r\n\t\t\t\t\t\t\t\t\t<Setter Property=\"VerticalAlignment\" Value=\"Center\" />\r\n\t\t\t\t\t\t\t\t\t<Setter Property=\"Focusable\" Value=\"False\" />\r\n\t\t\t\t\t\t\t\t</Style>\r\n\t\t\t\t\t\t\t</ControlTemplate.Resources>\r\n\t\t\t\t\t\t\t<Grid Name=\"TiGrid\" Margin=\"0,0,3,-1\">\r\n\t\t\t\t\t\t\t\t<Border\r\n\t\t\t\t\t\t\t\t\tName=\"TiBorder\"\r\n\t\t\t\t\t\t\t\t\tMargin=\"0,0,0,1\"\r\n\t\t\t\t\t\t\t\t\tPadding=\"5,4,5,4\"\r\n\t\t\t\t\t\t\t\t\tBackground=\"{StaticResource BackgroundDark}\"\r\n\t\t\t\t\t\t\t\t\tBorderBrush=\"{StaticResource BackgroundDark}\"\r\n\t\t\t\t\t\t\t\t\tBorderThickness=\"1,1,1,0\"\r\n\t\t\t\t\t\t\t\t\tCornerRadius=\"5,5,0,0\">\r\n\t\t\t\t\t\t\t\t\t<ContentPresenter\r\n\t\t\t\t\t\t\t\t\t\tName=\"TiContent\"\r\n\t\t\t\t\t\t\t\t\t\tHorizontalAlignment=\"Center\"\r\n\t\t\t\t\t\t\t\t\t\tVerticalAlignment=\"Center\"\r\n\t\t\t\t\t\t\t\t\t\tContentSource=\"Header\" />\r\n\t\t\t\t\t\t\t\t</Border>\r\n\t\t\t\t\t\t\t\t<Border\r\n\t\t\t\t\t\t\t\t\tName=\"BoBorder\"\r\n\t\t\t\t\t\t\t\t\tMargin=\"1,0,1,0\"\r\n\t\t\t\t\t\t\t\t\tBorderBrush=\"{StaticResource BorderDark}\"\r\n\t\t\t\t\t\t\t\t\tBorderThickness=\"0,0,0,1\" />\r\n\t\t\t\t\t\t\t</Grid>\r\n\t\t\t\t\t\t\t<!--  Triggers for TabItem  -->\r\n\t\t\t\t\t\t\t<ControlTemplate.Triggers>\r\n\t\t\t\t\t\t\t\t<Trigger Property=\"IsSelected\" Value=\"True\">\r\n\t\t\t\t\t\t\t\t\t<!--  Use Binding instead of TemplateBinding  -->\r\n\t\t\t\t\t\t\t\t\t<Setter TargetName=\"TiBorder\" Property=\"Background\" Value=\"{Binding Background, RelativeSource={RelativeSource TemplatedParent}}\" />\r\n\t\t\t\t\t\t\t\t\t<Setter TargetName=\"TiBorder\" Property=\"BorderBrush\" Value=\"{StaticResource BorderDark}\" />\r\n\t\t\t\t\t\t\t\t\t<Setter TargetName=\"BoBorder\" Property=\"BorderBrush\" Value=\"{Binding Background, RelativeSource={RelativeSource TemplatedParent}}\" />\r\n\t\t\t\t\t\t\t\t</Trigger>\r\n\t\t\t\t\t\t\t\t<MultiTrigger>\r\n\t\t\t\t\t\t\t\t\t<MultiTrigger.Conditions>\r\n\t\t\t\t\t\t\t\t\t\t<Condition Property=\"IsMouseOver\" Value=\"True\" />\r\n\t\t\t\t\t\t\t\t\t\t<Condition Property=\"IsSelected\" Value=\"False\" />\r\n\t\t\t\t\t\t\t\t\t</MultiTrigger.Conditions>\r\n\t\t\t\t\t\t\t\t\t<MultiTrigger.Setters>\r\n\t\t\t\t\t\t\t\t\t\t<Setter TargetName=\"TiBorder\" Property=\"BorderBrush\" Value=\"{StaticResource BackgroundDarkSelected}\" />\r\n\t\t\t\t\t\t\t\t\t\t<Setter TargetName=\"TiBorder\" Property=\"Background\" Value=\"{StaticResource BackgroundDarkSelected}\" />\r\n\t\t\t\t\t\t\t\t\t</MultiTrigger.Setters>\r\n\t\t\t\t\t\t\t\t</MultiTrigger>\r\n\r\n\t\t\t\t\t\t\t\t<!--  DataTrigger to adjust CornerRadius when TabStripPlacement is Bottom  -->\r\n\t\t\t\t\t\t\t\t<DataTrigger Binding=\"{Binding RelativeSource={RelativeSource AncestorType=TabControl}, Path=TabStripPlacement}\" Value=\"Bottom\">\r\n\t\t\t\t\t\t\t\t\t<Setter TargetName=\"TiBorder\" Property=\"CornerRadius\" Value=\"0,0,5,5\" />\r\n\t\t\t\t\t\t\t\t\t<Setter TargetName=\"TiBorder\" Property=\"Margin\" Value=\"0,1,0,0\" />\r\n\t\t\t\t\t\t\t\t\t<Setter TargetName=\"TiBorder\" Property=\"BorderThickness\" Value=\"1,0,1,1\" />\r\n\t\t\t\t\t\t\t\t\t<Setter TargetName=\"BoBorder\" Property=\"BorderThickness\" Value=\"0,1,0,0\" />\r\n\t\t\t\t\t\t\t\t\t<Setter TargetName=\"TiGrid\" Property=\"Margin\" Value=\"0,-1,3,0\" />\r\n\t\t\t\t\t\t\t\t</DataTrigger>\r\n\r\n\t\t\t\t\t\t\t</ControlTemplate.Triggers>\r\n\t\t\t\t\t\t</ControlTemplate>\r\n\t\t\t\t\t</Setter.Value>\r\n\t\t\t\t</Setter>\r\n\t\t\t</Style>\r\n\t\t</Style.Resources>\r\n\t</Style>\r\n\r\n\t<!--  TextBlock  -->\r\n\t<Style x:Key=\"TextBlock_Default\" TargetType=\"TextBlock\">\r\n\t\t<Setter Property=\"Height\" Value=\"Auto\" />\r\n\t\t<Setter Property=\"Margin\" Value=\"3\" />\r\n\t\t<Setter Property=\"Padding\" Value=\"3\" />\r\n\t\t<Setter Property=\"VerticalAlignment\" Value=\"Center\" />\r\n\t</Style>\r\n\r\n\t<!--  Shared TextBox Style  -->\r\n\t<Style x:Key=\"SharedTextBoxStyle\" TargetType=\"Control\">\r\n\t\t<Setter Property=\"Height\" Value=\"Auto\" />\r\n\t\t<Setter Property=\"Margin\" Value=\"3\" />\r\n\t\t<Setter Property=\"Padding\" Value=\"5,4,5,4\" />\r\n\t\t<Setter Property=\"MinHeight\" Value=\"26\" />\r\n\t\t<Setter Property=\"VerticalAlignment\" Value=\"Center\" />\r\n\t\t<Setter Property=\"VerticalContentAlignment\" Value=\"Center\" />\r\n\t\t<Setter Property=\"BorderBrush\" Value=\"{StaticResource BackgroundDark}\" />\r\n\t\t<Setter Property=\"BorderThickness\" Value=\"1\" />\r\n\t\t<Setter Property=\"SnapsToDevicePixels\" Value=\"True\" />\r\n\t\t<Setter Property=\"FocusVisualStyle\" Value=\"{StaticResource FocusVisualStyle_Default}\" />\r\n\t\t<Style.Resources>\r\n\t\t\t<Style TargetType=\"Border\">\r\n\t\t\t\t<Setter Property=\"CornerRadius\" Value=\"5\" />\r\n\t\t\t</Style>\r\n\t\t</Style.Resources>\r\n\t</Style>\r\n\r\n\t<!--  TextBox  -->\r\n\t<Style x:Key=\"TextBox_Default\" BasedOn=\"{StaticResource SharedTextBoxStyle}\" TargetType=\"TextBox\">\r\n\t\t<Setter Property=\"Template\">\r\n\t\t\t<Setter.Value>\r\n\t\t\t\t<ControlTemplate TargetType=\"TextBox\">\r\n\t\t\t\t\t<Border\r\n\t\t\t\t\t\tName=\"TextBoxBorder\"\r\n\t\t\t\t\t\tBackground=\"{TemplateBinding Background}\"\r\n\t\t\t\t\t\tBorderBrush=\"{TemplateBinding BorderBrush}\"\r\n\t\t\t\t\t\tBorderThickness=\"{TemplateBinding BorderThickness}\"\r\n\t\t\t\t\t\tSnapsToDevicePixels=\"True\">\r\n\t\t\t\t\t\t<ScrollViewer\r\n\t\t\t\t\t\t\tx:Name=\"PART_ContentHost\"\r\n\t\t\t\t\t\t\tFocusable=\"false\"\r\n\t\t\t\t\t\t\tHorizontalScrollBarVisibility=\"Hidden\"\r\n\t\t\t\t\t\t\tVerticalScrollBarVisibility=\"Hidden\" />\r\n\t\t\t\t\t</Border>\r\n\t\t\t\t\t<ControlTemplate.Triggers>\r\n\t\t\t\t\t\t<!--  IsEnabled  -->\r\n\t\t\t\t\t\t<Trigger Property=\"IsEnabled\" Value=\"False\">\r\n\t\t\t\t\t\t\t<Setter Property=\"BorderThickness\" Value=\"0\" />\r\n\t\t\t\t\t\t\t<Setter Property=\"Padding\" Value=\"6,4,6,4\" />\r\n\t\t\t\t\t\t\t<Setter Property=\"Focusable\" Value=\"False\" />\r\n\t\t\t\t\t\t\t<Setter Property=\"Background\" Value=\"{StaticResource BackgroundDark}\" />\r\n\t\t\t\t\t\t</Trigger>\r\n\t\t\t\t\t\t<!--  IsReadOnly  -->\r\n\t\t\t\t\t\t<Trigger Property=\"IsReadOnly\" Value=\"True\">\r\n\t\t\t\t\t\t\t<Setter Property=\"BorderThickness\" Value=\"0\" />\r\n\t\t\t\t\t\t\t<Setter Property=\"Padding\" Value=\"6,4,6,4\" />\r\n\t\t\t\t\t\t\t<!--<Setter Property=\"Focusable\" Value=\"False\" />-->\r\n\t\t\t\t\t\t\t<Setter Property=\"Background\" Value=\"{StaticResource BackgroundDark}\" />\r\n\t\t\t\t\t\t</Trigger>\r\n\t\t\t\t\t\t<!--  IsKeyboardFocused  -->\r\n\t\t\t\t\t\t<Trigger Property=\"IsKeyboardFocused\" Value=\"true\">\r\n\t\t\t\t\t\t\t<Setter TargetName=\"TextBoxBorder\" Property=\"BorderBrush\" Value=\"{StaticResource BackgroundDarkSelected}\" />\r\n\t\t\t\t\t\t</Trigger>\r\n\t\t\t\t\t\t<MultiTrigger>\r\n\t\t\t\t\t\t\t<!--  MouseOver  -->\r\n\t\t\t\t\t\t\t<MultiTrigger.Conditions>\r\n\t\t\t\t\t\t\t\t<Condition Property=\"IsMouseOver\" Value=\"True\" />\r\n\t\t\t\t\t\t\t\t<Condition Property=\"IsEnabled\" Value=\"True\" />\r\n\t\t\t\t\t\t\t\t<Condition Property=\"IsReadOnly\" Value=\"False\" />\r\n\t\t\t\t\t\t\t</MultiTrigger.Conditions>\r\n\t\t\t\t\t\t\t<Setter TargetName=\"TextBoxBorder\" Property=\"BorderBrush\" Value=\"{StaticResource BackgroundDarkSelected}\" />\r\n\t\t\t\t\t\t</MultiTrigger>\r\n\t\t\t\t\t</ControlTemplate.Triggers>\r\n\t\t\t\t</ControlTemplate>\r\n\t\t\t</Setter.Value>\r\n\t\t</Setter>\r\n\t</Style>\r\n\r\n\t<!--  PasswordBox  -->\r\n\t<Style x:Key=\"PasswordBox_Default\" BasedOn=\"{StaticResource SharedTextBoxStyle}\" TargetType=\"PasswordBox\">\r\n\t\t<Setter Property=\"Template\">\r\n\t\t\t<Setter.Value>\r\n\t\t\t\t<ControlTemplate TargetType=\"PasswordBox\">\r\n\t\t\t\t\t<Border\r\n\t\t\t\t\t\tName=\"PasswordBoxBorder\"\r\n\t\t\t\t\t\tBackground=\"{TemplateBinding Background}\"\r\n\t\t\t\t\t\tBorderBrush=\"{TemplateBinding BorderBrush}\"\r\n\t\t\t\t\t\tBorderThickness=\"{TemplateBinding BorderThickness}\"\r\n\t\t\t\t\t\tCornerRadius=\"5\">\r\n\t\t\t\t\t\t<ScrollViewer x:Name=\"PART_ContentHost\" />\r\n\t\t\t\t\t</Border>\r\n\t\t\t\t\t<ControlTemplate.Triggers>\r\n\t\t\t\t\t\t<!--  IsEnabled  -->\r\n\t\t\t\t\t\t<Trigger Property=\"IsEnabled\" Value=\"False\">\r\n\t\t\t\t\t\t\t<Setter Property=\"BorderThickness\" Value=\"0\" />\r\n\t\t\t\t\t\t\t<Setter Property=\"Padding\" Value=\"6,4,6,4\" />\r\n\t\t\t\t\t\t\t<Setter Property=\"Focusable\" Value=\"False\" />\r\n\t\t\t\t\t\t\t<Setter Property=\"Background\" Value=\"{StaticResource BackgroundDark}\" />\r\n\t\t\t\t\t\t</Trigger>\r\n\t\t\t\t\t\t<!--  IsKeyboardFocused  -->\r\n\t\t\t\t\t\t<Trigger Property=\"IsKeyboardFocused\" Value=\"true\">\r\n\t\t\t\t\t\t\t<Setter TargetName=\"PasswordBoxBorder\" Property=\"BorderBrush\" Value=\"{StaticResource BackgroundDarkSelected}\" />\r\n\t\t\t\t\t\t</Trigger>\r\n\t\t\t\t\t\t<MultiTrigger>\r\n\t\t\t\t\t\t\t<!--  MouseOver  -->\r\n\t\t\t\t\t\t\t<MultiTrigger.Conditions>\r\n\t\t\t\t\t\t\t\t<Condition Property=\"IsMouseOver\" Value=\"True\" />\r\n\t\t\t\t\t\t\t\t<Condition Property=\"IsEnabled\" Value=\"True\" />\r\n\t\t\t\t\t\t\t</MultiTrigger.Conditions>\r\n\t\t\t\t\t\t\t<Setter TargetName=\"PasswordBoxBorder\" Property=\"BorderBrush\" Value=\"{StaticResource BackgroundDarkSelected}\" />\r\n\t\t\t\t\t\t</MultiTrigger>\r\n\t\t\t\t\t</ControlTemplate.Triggers>\r\n\t\t\t\t</ControlTemplate>\r\n\t\t\t</Setter.Value>\r\n\t\t</Setter>\r\n\t</Style>\r\n\r\n\t<!--  TextBox - ResultsLog  -->\r\n\t<Style x:Key=\"TextBoxResultsLog\" BasedOn=\"{StaticResource TextBox_Default}\" TargetType=\"TextBox\">\r\n\t\t<Setter Property=\"HorizontalAlignment\" Value=\"Stretch\" />\r\n\t\t<Setter Property=\"VerticalAlignment\" Value=\"Stretch\" />\r\n\t\t<Setter Property=\"VerticalContentAlignment\" Value=\"Top\" />\r\n\t\t<Setter Property=\"AcceptsReturn\" Value=\"True\" />\r\n\t\t<!--<Setter Property=\"Background\" Value=\"{DynamicResource {x:Static SystemColors.ControlBrushKey}}\" />-->\r\n\t\t<Setter Property=\"FontFamily\" Value=\"Consolas\" />\r\n\t\t<Setter Property=\"MinLines\" Value=\"5\" />\r\n\t\t<Setter Property=\"ScrollViewer.HorizontalScrollBarVisibility\" Value=\"Disabled\" />\r\n\t\t<Setter Property=\"ScrollViewer.VerticalScrollBarVisibility\" Value=\"Auto\" />\r\n\t\t<Setter Property=\"TextWrapping\" Value=\"Wrap\" />\r\n\t\t<Setter Property=\"MinLines\" Value=\"5\" />\r\n\t</Style>\r\n\r\n\t<!--  TextBox - Cell  -->\r\n\t<Style x:Key=\"TextBoxCell\" BasedOn=\"{StaticResource TextBox_Default}\" TargetType=\"TextBox\">\r\n\t\t<Setter Property=\"BorderThickness\" Value=\"0\" />\r\n\t\t<Setter Property=\"Padding\" Value=\"0\" />\r\n\t\t<Setter Property=\"Margin\" Value=\"1\" />\r\n\t\t<Setter Property=\"MinHeight\" Value=\"21\" />\r\n\t\t<Setter Property=\"VerticalAlignment\" Value=\"Stretch\" />\r\n\t\t<Setter Property=\"VerticalContentAlignment\" Value=\"Center\" />\r\n\t\t<Setter Property=\"HorizontalAlignment\" Value=\"Stretch\" />\r\n\t\t<Style.Resources>\r\n\t\t\t<Style TargetType=\"Border\">\r\n\t\t\t\t<Setter Property=\"CornerRadius\" Value=\"0\" />\r\n\t\t\t</Style>\r\n\t\t</Style.Resources>\r\n\t</Style>\r\n\r\n\t<Style x:Key=\"TextBoxRightCell\" BasedOn=\"{StaticResource TextBoxCell}\" TargetType=\"TextBox\">\r\n\t\t<Setter Property=\"TextAlignment\" Value=\"Right\" />\r\n\t</Style>\r\n\r\n\t<Style x:Key=\"TextBoxCenterCell\" BasedOn=\"{StaticResource TextBoxCell}\" TargetType=\"TextBox\">\r\n\t\t<Setter Property=\"TextAlignment\" Value=\"Center\" />\r\n\t</Style>\r\n\r\n\t<!--  TextBlock - Cell  -->\r\n\t<Style x:Key=\"TextBlockCell\" BasedOn=\"{StaticResource TextBlock_Default}\" TargetType=\"TextBlock\">\r\n\t\t<Setter Property=\"Padding\" Value=\"0\" />\r\n\t\t<Setter Property=\"HorizontalAlignment\" Value=\"Stretch\" />\r\n\t\t<Setter Property=\"Margin\" Value=\"3,0,16,0\" />\r\n\t\t<Setter Property=\"VerticalAlignment\" Value=\"Center\" />\r\n\t</Style>\r\n\r\n\t<Style x:Key=\"TextBlockCellM3\" BasedOn=\"{StaticResource TextBlockCell}\" TargetType=\"TextBlock\">\r\n\t\t<Setter Property=\"Margin\" Value=\"3,0,3,0\" />\r\n\t</Style>\r\n\r\n\t<Style x:Key=\"TextBlockCellM3W500\" BasedOn=\"{StaticResource TextBlockCell}\" TargetType=\"TextBlock\">\r\n\t\t<Setter Property=\"Margin\" Value=\"3,0,3,0\" />\r\n\t\t<Setter Property=\"MinWidth\" Value=\"500\" />\r\n\t</Style>\r\n\r\n\t<Style x:Key=\"TextBlockLeftCell\" BasedOn=\"{StaticResource TextBlockCell}\" TargetType=\"TextBlock\">\r\n\t\t<Setter Property=\"TextAlignment\" Value=\"Left\" />\r\n\t</Style>\r\n\r\n\t<Style x:Key=\"TextBlockRightCell\" BasedOn=\"{StaticResource TextBlockCell}\" TargetType=\"TextBlock\">\r\n\t\t<Setter Property=\"TextAlignment\" Value=\"Right\" />\r\n\t</Style>\r\n\r\n\t<Style x:Key=\"TextBlockCenterCell\" BasedOn=\"{StaticResource TextBlockCell}\" TargetType=\"TextBlock\">\r\n\t\t<Setter Property=\"TextAlignment\" Value=\"Center\" />\r\n\t</Style>\r\n\r\n\t<!--  CheckBox Template  -->\r\n\t<Style x:Key=\"CheckBox_Default\" TargetType=\"CheckBox\">\r\n\t\t<Setter Property=\"FocusVisualStyle\" Value=\"{StaticResource FocusVisualStyle_Default}\" />\r\n\t\t<Setter Property=\"Template\" Value=\"{DynamicResource CheckBox_Template}\" />\r\n\t\t<Setter Property=\"Margin\" Value=\"0,0,3,0\" />\r\n\t\t<Setter Property=\"Padding\" Value=\"3\" />\r\n\t\t<Setter Property=\"HorizontalAlignment\" Value=\"Left\" />\r\n\t\t<Setter Property=\"Control.Template\">\r\n\t\t\t<Setter.Value>\r\n\t\t\t\t<ControlTemplate TargetType=\"CheckBox\">\r\n\t\t\t\t\t<StackPanel\r\n\t\t\t\t\t\tHorizontalAlignment=\"{TemplateBinding HorizontalAlignment}\"\r\n\t\t\t\t\t\tVerticalAlignment=\"{TemplateBinding VerticalAlignment}\"\r\n\t\t\t\t\t\tBackground=\"Transparent\"\r\n\t\t\t\t\t\tIsHitTestVisible=\"True\"\r\n\t\t\t\t\t\tOrientation=\"Horizontal\">\r\n\t\t\t\t\t\t<Border\r\n\t\t\t\t\t\t\tx:Name=\"checkBoxBorder\"\r\n\t\t\t\t\t\t\tMargin=\"{TemplateBinding Padding}\"\r\n\t\t\t\t\t\t\tPadding=\"1\"\r\n\t\t\t\t\t\t\tVerticalAlignment=\"Center\"\r\n\t\t\t\t\t\t\tBackground=\"White\"\r\n\t\t\t\t\t\t\tBorderBrush=\"{StaticResource BackgroundDark}\"\r\n\t\t\t\t\t\t\tBorderThickness=\"1\"\r\n\t\t\t\t\t\t\tCornerRadius=\"3\">\r\n\t\t\t\t\t\t\t<Grid x:Name=\"markGrid\">\r\n\t\t\t\t\t\t\t\t<Path\r\n\t\t\t\t\t\t\t\t\tx:Name=\"optionMark\"\r\n\t\t\t\t\t\t\t\t\tMargin=\"2\"\r\n\t\t\t\t\t\t\t\t\tData=\"F1M9.97498,1.22334L4.6983,9.09834 4.52164,9.09834 0,5.19331 1.27664,3.52165 4.255,6.08833 8.33331,1.52588E-05 9.97498,1.22334z\"\r\n\t\t\t\t\t\t\t\t\tFill=\"Black\"\r\n\t\t\t\t\t\t\t\t\tOpacity=\"0\"\r\n\t\t\t\t\t\t\t\t\tStretch=\"None\" />\r\n\t\t\t\t\t\t\t\t<Rectangle\r\n\t\t\t\t\t\t\t\t\tx:Name=\"indeterminateMark\"\r\n\t\t\t\t\t\t\t\t\tMargin=\"2\"\r\n\t\t\t\t\t\t\t\t\tFill=\"Black\"\r\n\t\t\t\t\t\t\t\t\tOpacity=\"0\" />\r\n\t\t\t\t\t\t\t</Grid>\r\n\t\t\t\t\t\t</Border>\r\n\t\t\t\t\t\t<ContentPresenter\r\n\t\t\t\t\t\t\tx:Name=\"contentPresenter\"\r\n\t\t\t\t\t\t\tMargin=\"0,1,5,0\"\r\n\t\t\t\t\t\t\tHorizontalAlignment=\"{TemplateBinding HorizontalAlignment}\"\r\n\t\t\t\t\t\t\tVerticalAlignment=\"Center\"\r\n\t\t\t\t\t\t\tContent=\"{TemplateBinding Content}\"\r\n\t\t\t\t\t\t\tContentStringFormat=\"{TemplateBinding ContentStringFormat}\"\r\n\t\t\t\t\t\t\tContentTemplate=\"{TemplateBinding ContentTemplate}\"\r\n\t\t\t\t\t\t\tFocusable=\"False\"\r\n\t\t\t\t\t\t\tRecognizesAccessKey=\"True\"\r\n\t\t\t\t\t\t\tSnapsToDevicePixels=\"{TemplateBinding SnapsToDevicePixels}\" />\r\n\t\t\t\t\t</StackPanel>\r\n\t\t\t\t\t<!--  Triggers  -->\r\n\t\t\t\t\t<ControlTemplate.Triggers>\r\n\t\t\t\t\t\t<Trigger Property=\"IsChecked\" Value=\"True\">\r\n\t\t\t\t\t\t\t<Setter TargetName=\"optionMark\" Property=\"Opacity\" Value=\"1\" />\r\n\t\t\t\t\t\t\t<Setter TargetName=\"indeterminateMark\" Property=\"Opacity\" Value=\"0\" />\r\n\t\t\t\t\t\t</Trigger>\r\n\t\t\t\t\t\t<Trigger Property=\"IsChecked\" Value=\"{x:Null}\">\r\n\t\t\t\t\t\t\t<Setter TargetName=\"optionMark\" Property=\"Opacity\" Value=\"0\" />\r\n\t\t\t\t\t\t\t<Setter TargetName=\"indeterminateMark\" Property=\"Opacity\" Value=\"1\" />\r\n\t\t\t\t\t\t</Trigger>\r\n\t\t\t\t\t\t<Trigger Property=\"IsEnabled\" Value=\"False\">\r\n\t\t\t\t\t\t\t<Setter Property=\"Focusable\" Value=\"False\" />\r\n\t\t\t\t\t\t\t<Setter Property=\"Background\" Value=\"{StaticResource BackgroundDark}\" />\r\n\t\t\t\t\t\t\t<Setter Property=\"Opacity\" Value=\"0.5\" />\r\n\t\t\t\t\t\t</Trigger>\r\n\t\t\t\t\t\t<Trigger Property=\"IsEnabled\" Value=\"False\">\r\n\t\t\t\t\t\t\t<Setter TargetName=\"checkBoxBorder\" Property=\"Opacity\" Value=\"0.5\" />\r\n\t\t\t\t\t\t</Trigger>\r\n\t\t\t\t\t\t<MultiTrigger>\r\n\t\t\t\t\t\t\t<MultiTrigger.Conditions>\r\n\t\t\t\t\t\t\t\t<Condition Property=\"IsMouseOver\" Value=\"True\" />\r\n\t\t\t\t\t\t\t\t<Condition Property=\"IsEnabled\" Value=\"True\" />\r\n\t\t\t\t\t\t\t</MultiTrigger.Conditions>\r\n\t\t\t\t\t\t\t<Setter TargetName=\"checkBoxBorder\" Property=\"BorderBrush\" Value=\"{StaticResource BackgroundDarkSelected}\" />\r\n\t\t\t\t\t\t</MultiTrigger>\r\n\t\t\t\t\t\t<DataTrigger Binding=\"{Binding Path=Content, RelativeSource={RelativeSource Mode=TemplatedParent}, Mode=OneTime}\" Value=\"\">\r\n\t\t\t\t\t\t\t<Setter TargetName=\"contentPresenter\" Property=\"Visibility\" Value=\"Collapsed\" />\r\n\t\t\t\t\t\t</DataTrigger>\r\n\t\t\t\t\t</ControlTemplate.Triggers>\r\n\t\t\t\t</ControlTemplate>\r\n\t\t\t</Setter.Value>\r\n\t\t</Setter>\r\n\t</Style>\r\n\r\n\t<Style x:Key=\"ImageCell_Default\" TargetType=\"Image\">\r\n\t\t<Setter Property=\"Margin\" Value=\"3,0,3,0\" />\r\n\t\t<Setter Property=\"Width\" Value=\"16\" />\r\n\t\t<Setter Property=\"Height\" Value=\"16\" />\r\n\t\t<Setter Property=\"VerticalAlignment\" Value=\"Center\" />\r\n\t\t<Setter Property=\"HorizontalAlignment\" Value=\"Left\" />\r\n\t</Style>\r\n\r\n\t<Style x:Key=\"ContentControlCell_Default\" BasedOn=\"{StaticResource ContentControl_Default}\" TargetType=\"ContentControl\">\r\n\t\t<Setter Property=\"Margin\" Value=\"3,0,3,0\" />\r\n\t\t<Setter Property=\"Width\" Value=\"16\" />\r\n\t\t<Setter Property=\"Height\" Value=\"16\" />\r\n\t\t<Setter Property=\"VerticalAlignment\" Value=\"Center\" />\r\n\t\t<Setter Property=\"HorizontalAlignment\" Value=\"Left\" />\r\n\t</Style>\r\n\r\n\t<Style x:Key=\"CheckBoxCell_Default\" BasedOn=\"{StaticResource CheckBox_Default}\" TargetType=\"CheckBox\" />\r\n\r\n\t<Style x:Key=\"CheckBoxCell\" BasedOn=\"{StaticResource CheckBox_Default}\" TargetType=\"CheckBox\">\r\n\t\t<Setter Property=\"Margin\" Value=\"3,0,3,0\" />\r\n\t\t<Setter Property=\"Padding\" Value=\"0,0,0,0\" />\r\n\t\t<Setter Property=\"VerticalAlignment\" Value=\"Center\" />\r\n\t</Style>\r\n\r\n\t<!--  Column Header  -->\r\n\t<Style x:Key=\"DataGridColumnHeader_Default\" TargetType=\"DataGridColumnHeader\">\r\n\t\t<Setter Property=\"Padding\" Value=\"3,0,16,0\" />\r\n\t\t<Setter Property=\"Margin\" Value=\"0\" />\r\n\t\t<Setter Property=\"MinHeight\" Value=\"24\" />\r\n\t\t<Setter Property=\"VerticalContentAlignment\" Value=\"Center\" />\r\n\t\t<Setter Property=\"SnapsToDevicePixels\" Value=\"True\" />\r\n\t\t<!--<Setter Property=\"BorderThickness\" Value=\"0\" />-->\r\n\t\t<Setter Property=\"Background\" Value=\"{DynamicResource GradientBrushStyle}\" />\r\n\t\t<!--<Style.Triggers> -->\r\n\t\t<!--  Apply style to first column only  -->\r\n\t\t<!-- <Trigger Property=\"DisplayIndex\" Value=\"0\">\r\n\t\t\t\t<Setter Property=\"Margin\" Value=\"0\" />\r\n\t\t\t</Trigger>\r\n\t\t</Style.Triggers>-->\r\n\t</Style>\r\n\r\n\t<Style x:Key=\"DataGridHeader_Sorting\" BasedOn=\"{StaticResource DataGridColumnHeader_Default}\" TargetType=\"DataGridColumnHeader\">\r\n\t\t<Setter Property=\"Template\">\r\n\t\t\t<Setter.Value>\r\n\t\t\t\t<ControlTemplate TargetType=\"DataGridColumnHeader\">\r\n\t\t\t\t\t<Grid Background=\"{DynamicResource GradientBrushStyle}\">\r\n\t\t\t\t\t\t<!--  Title  -->\r\n\t\t\t\t\t\t<ContentPresenter Margin=\"3,0,16,0\" VerticalAlignment=\"Center\" Content=\"{TemplateBinding Content}\" />\r\n\t\t\t\t\t\t<!--  Sorting Triangle  -->\r\n\t\t\t\t\t\t<Path\r\n\t\t\t\t\t\t\tx:Name=\"SortingIndicator\"\r\n\t\t\t\t\t\t\tWidth=\"10\"\r\n\t\t\t\t\t\t\tHeight=\"5\"\r\n\t\t\t\t\t\t\tMargin=\"0,0,3,0\"\r\n\t\t\t\t\t\t\tHorizontalAlignment=\"Right\"\r\n\t\t\t\t\t\t\tVerticalAlignment=\"Center\"\r\n\t\t\t\t\t\t\tData=\"M 5,0 L 0,5 L 10,5 Z\"\r\n\t\t\t\t\t\t\tFill=\"Black\"\r\n\t\t\t\t\t\t\tOpacity=\"1\"\r\n\t\t\t\t\t\t\tRenderTransformOrigin=\"0.5,0.5\"\r\n\t\t\t\t\t\t\tVisibility=\"Hidden\" />\r\n\t\t\t\t\t</Grid>\r\n\t\t\t\t\t<!--  Header Triggers  -->\r\n\t\t\t\t\t<ControlTemplate.Triggers>\r\n\t\t\t\t\t\t<Trigger Property=\"SortDirection\" Value=\"Ascending\">\r\n\t\t\t\t\t\t\t<Setter TargetName=\"SortingIndicator\" Property=\"Visibility\" Value=\"Visible\" />\r\n\t\t\t\t\t\t\t<Setter TargetName=\"SortingIndicator\" Property=\"RenderTransform\">\r\n\t\t\t\t\t\t\t\t<Setter.Value>\r\n\t\t\t\t\t\t\t\t\t<TransformGroup>\r\n\t\t\t\t\t\t\t\t\t\t<ScaleTransform ScaleY=\"-1\" />\r\n\t\t\t\t\t\t\t\t\t</TransformGroup>\r\n\t\t\t\t\t\t\t\t</Setter.Value>\r\n\t\t\t\t\t\t\t</Setter>\r\n\t\t\t\t\t\t</Trigger>\r\n\t\t\t\t\t\t<Trigger Property=\"SortDirection\" Value=\"Descending\">\r\n\t\t\t\t\t\t\t<Setter TargetName=\"SortingIndicator\" Property=\"Visibility\" Value=\"Visible\" />\r\n\t\t\t\t\t\t\t<Setter TargetName=\"SortingIndicator\" Property=\"RenderTransform\">\r\n\t\t\t\t\t\t\t\t<Setter.Value>\r\n\t\t\t\t\t\t\t\t\t<TransformGroup>\r\n\t\t\t\t\t\t\t\t\t\t<ScaleTransform ScaleY=\"1\" />\r\n\t\t\t\t\t\t\t\t\t</TransformGroup>\r\n\t\t\t\t\t\t\t\t</Setter.Value>\r\n\t\t\t\t\t\t\t</Setter>\r\n\t\t\t\t\t\t</Trigger>\r\n\t\t\t\t\t</ControlTemplate.Triggers>\r\n\t\t\t\t</ControlTemplate>\r\n\t\t\t</Setter.Value>\r\n\t\t</Setter>\r\n\t</Style>\r\n\r\n\t<Style x:Key=\"ToggleButton_Default\" TargetType=\"ToggleButton\">\r\n\t\t<Setter Property=\"Background\" Value=\"{StaticResource BackgroundLight}\" />\r\n\t\t<Setter Property=\"Template\">\r\n\t\t\t<Setter.Value>\r\n\t\t\t\t<ControlTemplate TargetType=\"ToggleButton\">\r\n\t\t\t\t\t<Border Margin=\"0,0,0,1\" Background=\"{TemplateBinding Background}\">\r\n\t\t\t\t\t\t<ContentPresenter />\r\n\t\t\t\t\t</Border>\r\n\t\t\t\t\t<ControlTemplate.Triggers>\r\n\t\t\t\t\t\t<Trigger Property=\"IsMouseOver\" Value=\"True\">\r\n\t\t\t\t\t\t\t<Setter Property=\"Background\" Value=\"{StaticResource BackgroundDarkSelected}\" />\r\n\t\t\t\t\t\t</Trigger>\r\n\t\t\t\t\t\t<!--<Trigger Property=\"IsChecked\" Value=\"True\">\r\n\t\t\t\t\t\t\t<Setter Property=\"BorderBrush\" Value=\"DarkGray\"/>\r\n\t\t\t\t\t\t</Trigger>-->\r\n\t\t\t\t\t</ControlTemplate.Triggers>\r\n\t\t\t\t</ControlTemplate>\r\n\t\t\t</Setter.Value>\r\n\t\t</Setter>\r\n\t</Style>\r\n\r\n\t<!--  Expander  -->\r\n\t<Style x:Key=\"Expander_Default\" TargetType=\"Expander\">\r\n\t\t<Setter Property=\"Template\">\r\n\t\t\t<Setter.Value>\r\n\t\t\t\t<ControlTemplate TargetType=\"Expander\">\r\n\t\t\t\t\t<StackPanel>\r\n\t\t\t\t\t\t<ToggleButton\r\n\t\t\t\t\t\t\tName=\"HeaderSite\"\r\n\t\t\t\t\t\t\tHeight=\"24\"\r\n\t\t\t\t\t\t\tIsChecked=\"{Binding Path=IsExpanded, Mode=TwoWay, RelativeSource={RelativeSource TemplatedParent}}\"\r\n\t\t\t\t\t\t\tStyle=\"{StaticResource ToggleButton_Default}\">\r\n\t\t\t\t\t\t\t<DockPanel>\r\n\t\t\t\t\t\t\t\t<Path\r\n\t\t\t\t\t\t\t\t\tName=\"ExpandIndicator\"\r\n\t\t\t\t\t\t\t\t\tWidth=\"16\"\r\n\t\t\t\t\t\t\t\t\tHeight=\"16\"\r\n\t\t\t\t\t\t\t\t\tMargin=\"3,0,3,0\"\r\n\t\t\t\t\t\t\t\t\tVerticalAlignment=\"Center\"\r\n\t\t\t\t\t\t\t\t\tData=\"M 6,2 L 12,8 L 6,14\"\r\n\t\t\t\t\t\t\t\t\tOpacity=\"0.2\"\r\n\t\t\t\t\t\t\t\t\tRenderTransformOrigin=\"0.5,0.5\"\r\n\t\t\t\t\t\t\t\t\tStroke=\"Black\"\r\n\t\t\t\t\t\t\t\t\tStrokeEndLineCap=\"Round\"\r\n\t\t\t\t\t\t\t\t\tStrokeLineJoin=\"Round\"\r\n\t\t\t\t\t\t\t\t\tStrokeStartLineCap=\"Round\"\r\n\t\t\t\t\t\t\t\t\tStrokeThickness=\"1.3\" />\r\n\t\t\t\t\t\t\t\t<ContentPresenter Content=\"{TemplateBinding Header}\" />\r\n\t\t\t\t\t\t\t</DockPanel>\r\n\t\t\t\t\t\t</ToggleButton>\r\n\t\t\t\t\t\t<ContentPresenter Name=\"ExpandSite\" Visibility=\"Collapsed\" />\r\n\t\t\t\t\t</StackPanel>\r\n\t\t\t\t\t<ControlTemplate.Triggers>\r\n\t\t\t\t\t\t<Trigger Property=\"IsExpanded\" Value=\"True\">\r\n\t\t\t\t\t\t\t<Setter TargetName=\"ExpandSite\" Property=\"Visibility\" Value=\"Visible\" />\r\n\t\t\t\t\t\t\t<Setter TargetName=\"ExpandIndicator\" Property=\"RenderTransform\">\r\n\t\t\t\t\t\t\t\t<Setter.Value>\r\n\t\t\t\t\t\t\t\t\t<TransformGroup>\r\n\t\t\t\t\t\t\t\t\t\t<RotateTransform Angle=\"90\" />\r\n\t\t\t\t\t\t\t\t\t</TransformGroup>\r\n\t\t\t\t\t\t\t\t</Setter.Value>\r\n\t\t\t\t\t\t\t</Setter>\r\n\t\t\t\t\t\t</Trigger>\r\n\t\t\t\t\t</ControlTemplate.Triggers>\r\n\t\t\t\t</ControlTemplate>\r\n\t\t\t</Setter.Value>\r\n\t\t</Setter>\r\n\t</Style>\r\n\r\n\t<!--  Cell  -->\r\n\t<Style x:Key=\"DataGridCell_Default\" TargetType=\"DataGridCell\">\r\n\t\t<Setter Property=\"Padding\" Value=\"3,0,3,0\" />\r\n\t\t<Setter Property=\"Margin\" Value=\"0\" />\r\n\t\t<Setter Property=\"SnapsToDevicePixels\" Value=\"True\" />\r\n\t\t<Setter Property=\"MinHeight\" Value=\"25\" />\r\n\t\t<Setter Property=\"VerticalContentAlignment\" Value=\"Center\" />\r\n\t\t<Setter Property=\"BorderBrush\" Value=\"Transparent\" />\r\n\t\t<Setter Property=\"BorderThickness\" Value=\"0\" />\r\n\t\t<Style.Triggers>\r\n\t\t\t<!--  Selected  -->\r\n\t\t\t<Trigger Property=\"IsSelected\" Value=\"True\">\r\n\t\t\t\t<Setter Property=\"BorderBrush\" Value=\"{Binding RelativeSource={RelativeSource Self}, Path=Background}\" />\r\n\t\t\t</Trigger>\r\n\t\t\t<!--  First column only  -->\r\n\t\t\t<!--<DataTrigger Binding=\"{Binding Column.DisplayIndex, RelativeSource={RelativeSource Self}}\" Value=\"0\">\r\n\t\t\t\t<Setter Property=\"BorderThickness\" Value=\"0\" />\r\n\t\t\t</DataTrigger>-->\r\n\t\t\t<!--  IsReadOnly and IsSelected  -->\r\n\t\t\t<!--<MultiDataTrigger>\r\n                <MultiDataTrigger.Conditions>\r\n                    <Condition Binding=\"{Binding RelativeSource={RelativeSource AncestorType=DataGrid}, Path=IsReadOnly}\" Value=\"True\" />\r\n                    <Condition Binding=\"{Binding RelativeSource={RelativeSource Self}, Path=IsSelected}\" Value=\"False\" />\r\n                </MultiDataTrigger.Conditions>\r\n                <Setter Property=\"Background\" Value=\"Transparent\" />\r\n                <Setter Property=\"IsHitTestVisible\" Value=\"False\" />\r\n            </MultiDataTrigger>-->\r\n\t\t</Style.Triggers>\r\n\t</Style>\r\n\r\n\t<!--  DataGrid  -->\r\n\t<Style x:Key=\"DataGrid_Default\" TargetType=\"DataGrid\">\r\n\t\t<Setter Property=\"Background\" Value=\"{StaticResource BackgroundWhite}\" />\r\n\t\t<Setter Property=\"ClipToBounds\" Value=\"True\" />\r\n\t\t<Setter Property=\"BorderThickness\" Value=\"0\" />\r\n\t\t<Setter Property=\"CanUserAddRows\" Value=\"False\" />\r\n\t\t<Setter Property=\"CanUserDeleteRows\" Value=\"False\" />\r\n\t\t<Setter Property=\"GridLinesVisibility\" Value=\"Horizontal\" />\r\n\t\t<Setter Property=\"HeadersVisibility\" Value=\"Column\" />\r\n\t\t<Setter Property=\"HorizontalGridLinesBrush\" Value=\"{StaticResource BackgroundLight}\" />\r\n\t\t<Setter Property=\"VerticalGridLinesBrush\" Value=\"{StaticResource BackgroundLight}\" />\r\n\t\t<Setter Property=\"VerticalScrollBarVisibility\" Value=\"Auto\" />\r\n\t\t<Style.Resources>\r\n\t\t\t<!--  Make selected row color of inactive grid as same as active grid.  -->\r\n\t\t\t<SolidColorBrush x:Key=\"{x:Static SystemColors.InactiveSelectionHighlightBrushKey}\" Color=\"{x:Static SystemColors.HighlightColor}\" />\r\n\t\t\t<SolidColorBrush x:Key=\"{x:Static SystemColors.InactiveSelectionHighlightTextBrushKey}\" Color=\"{x:Static SystemColors.HighlightTextColor}\" />\r\n\t\t\t<!--  DataGridColumnHeader  -->\r\n\t\t\t<Style BasedOn=\"{StaticResource DataGridColumnHeader_Default}\" TargetType=\"DataGridColumnHeader\" />\r\n\t\t\t<!--<Style TargetType=\"DataGridRowHeader\">\r\n\t\t\t\t<Setter Property=\"Background\" Value=\"Green\" />\r\n\t\t\t</Style>-->\r\n\t\t\t<!--  DataGridRow  -->\r\n\t\t\t<Style TargetType=\"DataGridRow\">\r\n\t\t\t\t<Setter Property=\"Background\" Value=\"Transparent\" />\r\n\t\t\t</Style>\r\n\t\t\t<!--  DataGridCell  -->\r\n\t\t\t<Style BasedOn=\"{StaticResource DataGridCell_Default}\" TargetType=\"DataGridCell\" />\r\n\t\t</Style.Resources>\r\n\t</Style>\r\n\r\n\t<!--  Cell HUE Degree Pastel Colours  -->\r\n\t<Style x:Key=\"DataGridCell_D000\" TargetType=\"DataGridCell\">\r\n\t\t<Style.Triggers>\r\n\t\t\t<Trigger Property=\"DataGridCell.IsSelected\" Value=\"True\">\r\n\t\t\t\t<Setter Property=\"Foreground\" Value=\"{DynamicResource SolidColorBrush_000L}\" />\r\n\t\t\t\t<Setter Property=\"Background\" Value=\"{DynamicResource SolidColorBrush_000D}\" />\r\n\t\t\t\t<Setter Property=\"BorderBrush\" Value=\"{DynamicResource SolidColorBrush_000D}\" />\r\n\t\t\t</Trigger>\r\n\t\t\t<Trigger Property=\"DataGridCell.IsSelected\" Value=\"False\">\r\n\t\t\t\t<Setter Property=\"Background\" Value=\"{DynamicResource SolidColorBrush_000L}\" />\r\n\t\t\t\t<Setter Property=\"BorderBrush\" Value=\"{DynamicResource SolidColorBrush_000L}\" />\r\n\t\t\t</Trigger>\r\n\t\t</Style.Triggers>\r\n\t</Style>\r\n\r\n\t<Style x:Key=\"DataGridCell_D030\" TargetType=\"DataGridCell\">\r\n\t\t<Style.Triggers>\r\n\t\t\t<Trigger Property=\"DataGridCell.IsSelected\" Value=\"True\">\r\n\t\t\t\t<Setter Property=\"Foreground\" Value=\"{DynamicResource SolidColorBrush_030L}\" />\r\n\t\t\t\t<Setter Property=\"Background\" Value=\"{DynamicResource SolidColorBrush_030D}\" />\r\n\t\t\t\t<Setter Property=\"BorderBrush\" Value=\"{DynamicResource SolidColorBrush_030D}\" />\r\n\t\t\t</Trigger>\r\n\t\t\t<Trigger Property=\"DataGridCell.IsSelected\" Value=\"False\">\r\n\t\t\t\t<Setter Property=\"Background\" Value=\"{DynamicResource SolidColorBrush_030L}\" />\r\n\t\t\t\t<Setter Property=\"BorderBrush\" Value=\"{DynamicResource SolidColorBrush_030L}\" />\r\n\t\t\t</Trigger>\r\n\t\t</Style.Triggers>\r\n\t</Style>\r\n\r\n\t<Style x:Key=\"DataGridCell_D060\" TargetType=\"DataGridCell\">\r\n\t\t<Style.Triggers>\r\n\t\t\t<Trigger Property=\"DataGridCell.IsSelected\" Value=\"True\">\r\n\t\t\t\t<Setter Property=\"Foreground\" Value=\"{DynamicResource SolidColorBrush_060L}\" />\r\n\t\t\t\t<Setter Property=\"Background\" Value=\"{DynamicResource SolidColorBrush_060D}\" />\r\n\t\t\t\t<Setter Property=\"BorderBrush\" Value=\"{DynamicResource SolidColorBrush_060D}\" />\r\n\t\t\t</Trigger>\r\n\t\t\t<Trigger Property=\"DataGridCell.IsSelected\" Value=\"False\">\r\n\t\t\t\t<Setter Property=\"Background\" Value=\"{DynamicResource SolidColorBrush_060L}\" />\r\n\t\t\t\t<Setter Property=\"BorderBrush\" Value=\"{DynamicResource SolidColorBrush_060L}\" />\r\n\t\t\t</Trigger>\r\n\t\t</Style.Triggers>\r\n\t</Style>\r\n\r\n\t<Style x:Key=\"DataGridCell_D090\" TargetType=\"DataGridCell\">\r\n\t\t<Style.Triggers>\r\n\t\t\t<Trigger Property=\"DataGridCell.IsSelected\" Value=\"True\">\r\n\t\t\t\t<Setter Property=\"Foreground\" Value=\"{DynamicResource SolidColorBrush_090L}\" />\r\n\t\t\t\t<Setter Property=\"Background\" Value=\"{DynamicResource SolidColorBrush_090D}\" />\r\n\t\t\t\t<Setter Property=\"BorderBrush\" Value=\"{DynamicResource SolidColorBrush_090D}\" />\r\n\t\t\t</Trigger>\r\n\t\t\t<Trigger Property=\"DataGridCell.IsSelected\" Value=\"False\">\r\n\t\t\t\t<Setter Property=\"Background\" Value=\"{DynamicResource SolidColorBrush_090L}\" />\r\n\t\t\t\t<Setter Property=\"BorderBrush\" Value=\"{DynamicResource SolidColorBrush_090L}\" />\r\n\t\t\t</Trigger>\r\n\t\t</Style.Triggers>\r\n\t</Style>\r\n\r\n\t<Style x:Key=\"DataGridCell_D120\" TargetType=\"DataGridCell\">\r\n\t\t<Style.Triggers>\r\n\t\t\t<Trigger Property=\"DataGridCell.IsSelected\" Value=\"True\">\r\n\t\t\t\t<Setter Property=\"Foreground\" Value=\"{DynamicResource SolidColorBrush_120L}\" />\r\n\t\t\t\t<Setter Property=\"Background\" Value=\"{DynamicResource SolidColorBrush_120D}\" />\r\n\t\t\t\t<Setter Property=\"BorderBrush\" Value=\"{DynamicResource SolidColorBrush_120D}\" />\r\n\t\t\t</Trigger>\r\n\t\t\t<Trigger Property=\"DataGridCell.IsSelected\" Value=\"False\">\r\n\t\t\t\t<Setter Property=\"Background\" Value=\"{DynamicResource SolidColorBrush_120L}\" />\r\n\t\t\t\t<Setter Property=\"BorderBrush\" Value=\"{DynamicResource SolidColorBrush_120L}\" />\r\n\t\t\t</Trigger>\r\n\t\t</Style.Triggers>\r\n\t</Style>\r\n\r\n\t<Style x:Key=\"DataGridCell_D150\" TargetType=\"DataGridCell\">\r\n\t\t<Style.Triggers>\r\n\t\t\t<Trigger Property=\"DataGridCell.IsSelected\" Value=\"True\">\r\n\t\t\t\t<Setter Property=\"Foreground\" Value=\"{DynamicResource SolidColorBrush_150L}\" />\r\n\t\t\t\t<Setter Property=\"Background\" Value=\"{DynamicResource SolidColorBrush_150D}\" />\r\n\t\t\t\t<Setter Property=\"BorderBrush\" Value=\"{DynamicResource SolidColorBrush_150D}\" />\r\n\t\t\t</Trigger>\r\n\t\t\t<Trigger Property=\"DataGridCell.IsSelected\" Value=\"False\">\r\n\t\t\t\t<Setter Property=\"Background\" Value=\"{DynamicResource SolidColorBrush_150L}\" />\r\n\t\t\t\t<Setter Property=\"BorderBrush\" Value=\"{DynamicResource SolidColorBrush_150L}\" />\r\n\t\t\t</Trigger>\r\n\t\t</Style.Triggers>\r\n\t</Style>\r\n\r\n\t<Style x:Key=\"DataGridCell_D180\" TargetType=\"DataGridCell\">\r\n\t\t<Style.Triggers>\r\n\t\t\t<Trigger Property=\"DataGridCell.IsSelected\" Value=\"True\">\r\n\t\t\t\t<Setter Property=\"Foreground\" Value=\"{DynamicResource SolidColorBrush_180L}\" />\r\n\t\t\t\t<Setter Property=\"Background\" Value=\"{DynamicResource SolidColorBrush_180D}\" />\r\n\t\t\t\t<Setter Property=\"BorderBrush\" Value=\"{DynamicResource SolidColorBrush_180D}\" />\r\n\t\t\t</Trigger>\r\n\t\t\t<Trigger Property=\"DataGridCell.IsSelected\" Value=\"False\">\r\n\t\t\t\t<Setter Property=\"Background\" Value=\"{DynamicResource SolidColorBrush_180L}\" />\r\n\t\t\t\t<Setter Property=\"BorderBrush\" Value=\"{DynamicResource SolidColorBrush_180L}\" />\r\n\t\t\t</Trigger>\r\n\t\t</Style.Triggers>\r\n\t</Style>\r\n\r\n\t<Style x:Key=\"DataGridCell_D210\" TargetType=\"DataGridCell\">\r\n\t\t<Style.Triggers>\r\n\t\t\t<Trigger Property=\"DataGridCell.IsSelected\" Value=\"True\">\r\n\t\t\t\t<Setter Property=\"Foreground\" Value=\"{DynamicResource SolidColorBrush_210L}\" />\r\n\t\t\t\t<Setter Property=\"Background\" Value=\"{DynamicResource SolidColorBrush_210D}\" />\r\n\t\t\t\t<Setter Property=\"BorderBrush\" Value=\"{DynamicResource SolidColorBrush_210D}\" />\r\n\t\t\t</Trigger>\r\n\t\t\t<Trigger Property=\"DataGridCell.IsSelected\" Value=\"False\">\r\n\t\t\t\t<Setter Property=\"Background\" Value=\"{DynamicResource SolidColorBrush_210L}\" />\r\n\t\t\t\t<Setter Property=\"BorderBrush\" Value=\"{DynamicResource SolidColorBrush_210L}\" />\r\n\t\t\t</Trigger>\r\n\t\t</Style.Triggers>\r\n\t</Style>\r\n\r\n\r\n\t<Style x:Key=\"DataGridCell_D240\" TargetType=\"DataGridCell\">\r\n\t\t<Style.Triggers>\r\n\t\t\t<Trigger Property=\"DataGridCell.IsSelected\" Value=\"True\">\r\n\t\t\t\t<Setter Property=\"Foreground\" Value=\"{DynamicResource SolidColorBrush_240L}\" />\r\n\t\t\t\t<Setter Property=\"Background\" Value=\"{DynamicResource SolidColorBrush_240D}\" />\r\n\t\t\t\t<Setter Property=\"BorderBrush\" Value=\"{DynamicResource SolidColorBrush_240D}\" />\r\n\t\t\t</Trigger>\r\n\t\t\t<Trigger Property=\"DataGridCell.IsSelected\" Value=\"False\">\r\n\t\t\t\t<Setter Property=\"Background\" Value=\"{DynamicResource SolidColorBrush_240L}\" />\r\n\t\t\t\t<Setter Property=\"BorderBrush\" Value=\"{DynamicResource SolidColorBrush_240L}\" />\r\n\t\t\t</Trigger>\r\n\t\t</Style.Triggers>\r\n\t</Style>\r\n\r\n\t<Style x:Key=\"DataGridCell_D270\" TargetType=\"DataGridCell\">\r\n\t\t<Style.Triggers>\r\n\t\t\t<Trigger Property=\"DataGridCell.IsSelected\" Value=\"True\">\r\n\t\t\t\t<Setter Property=\"Foreground\" Value=\"{DynamicResource SolidColorBrush_270L}\" />\r\n\t\t\t\t<Setter Property=\"Background\" Value=\"{DynamicResource SolidColorBrush_270D}\" />\r\n\t\t\t\t<Setter Property=\"BorderBrush\" Value=\"{DynamicResource SolidColorBrush_270D}\" />\r\n\t\t\t</Trigger>\r\n\t\t\t<Trigger Property=\"DataGridCell.IsSelected\" Value=\"False\">\r\n\t\t\t\t<Setter Property=\"Background\" Value=\"{DynamicResource SolidColorBrush_270L}\" />\r\n\t\t\t\t<Setter Property=\"BorderBrush\" Value=\"{DynamicResource SolidColorBrush_270L}\" />\r\n\t\t\t</Trigger>\r\n\t\t</Style.Triggers>\r\n\t</Style>\r\n\r\n\t<Style x:Key=\"DataGridCell_D300\" TargetType=\"DataGridCell\">\r\n\t\t<Style.Triggers>\r\n\t\t\t<Trigger Property=\"DataGridCell.IsSelected\" Value=\"True\">\r\n\t\t\t\t<Setter Property=\"Foreground\" Value=\"{DynamicResource SolidColorBrush_300L}\" />\r\n\t\t\t\t<Setter Property=\"Background\" Value=\"{DynamicResource SolidColorBrush_300D}\" />\r\n\t\t\t\t<Setter Property=\"BorderBrush\" Value=\"{DynamicResource SolidColorBrush_300D}\" />\r\n\t\t\t</Trigger>\r\n\t\t\t<Trigger Property=\"DataGridCell.IsSelected\" Value=\"False\">\r\n\t\t\t\t<Setter Property=\"Background\" Value=\"{DynamicResource SolidColorBrush_300L}\" />\r\n\t\t\t\t<Setter Property=\"BorderBrush\" Value=\"{DynamicResource SolidColorBrush_300L}\" />\r\n\t\t\t</Trigger>\r\n\t\t</Style.Triggers>\r\n\t</Style>\r\n\r\n\t<Style x:Key=\"DataGridCell_D330\" TargetType=\"DataGridCell\">\r\n\t\t<Style.Triggers>\r\n\t\t\t<Trigger Property=\"DataGridCell.IsSelected\" Value=\"True\">\r\n\t\t\t\t<Setter Property=\"Foreground\" Value=\"{DynamicResource SolidColorBrush_330L}\" />\r\n\t\t\t\t<Setter Property=\"Background\" Value=\"{DynamicResource SolidColorBrush_330D}\" />\r\n\t\t\t\t<Setter Property=\"BorderBrush\" Value=\"{DynamicResource SolidColorBrush_330D}\" />\r\n\t\t\t</Trigger>\r\n\t\t\t<Trigger Property=\"DataGridCell.IsSelected\" Value=\"False\">\r\n\t\t\t\t<Setter Property=\"Background\" Value=\"{DynamicResource SolidColorBrush_330L}\" />\r\n\t\t\t\t<Setter Property=\"BorderBrush\" Value=\"{DynamicResource SolidColorBrush_330L}\" />\r\n\t\t\t</Trigger>\r\n\t\t</Style.Triggers>\r\n\t</Style>\r\n\r\n\t<Style x:Key=\"LabelGrid\" TargetType=\"Label\">\r\n\t\t<Setter Property=\"Padding\" Value=\"4,2,4,2\" />\r\n\t\t<Setter Property=\"VerticalContentAlignment\" Value=\"Center\" />\r\n\t</Style>\r\n\r\n\t<!--  ToolBarPanel  -->\r\n\t<Style x:Key=\"MainToolBarPanel\" TargetType=\"ToolBarPanel\">\r\n\t\t<Setter Property=\"HorizontalAlignment\" Value=\"Stretch\" />\r\n\t\t<Setter Property=\"VerticalAlignment\" Value=\"Top\" />\r\n\t\t<Setter Property=\"ClipToBounds\" Value=\"True\" />\r\n\t\t<Setter Property=\"Margin\" Value=\"0,0,0,0\" />\r\n\t</Style>\r\n\r\n\t<!--  ToolBar  -->\r\n\t<Style x:Key=\"MainToolBar\" TargetType=\"ToolBar\">\r\n\t\t<Setter Property=\"Background\" Value=\"Transparent\" />\r\n\t\t<Setter Property=\"Margin\" Value=\"-1,0,-12,-1\" />\r\n\t\t<!--  Hide right expand mini button  -->\r\n\t\t<Setter Property=\"Padding\" Value=\"0,0,0,0\" />\r\n\t\t<Setter Property=\"ToolBar.OverflowMode\" Value=\"AsNeeded\" />\r\n\t\t<Setter Property=\"ToolBarTray.IsLocked\" Value=\"True\" />\r\n\t\t<Style.Resources>\r\n\t\t\t<Style x:Key=\"ToolBarMainPanelBorderStyle\" TargetType=\"{x:Type Border}\">\r\n\t\t\t\t<Setter Property=\"CornerRadius\" Value=\"0,0,0,0\" />\r\n\t\t\t</Style>\r\n\t\t</Style.Resources>\r\n\t</Style>\r\n\r\n\t<!--  Separator  -->\r\n\t<Style x:Key=\"MainSeparator\" TargetType=\"Separator\">\r\n\t\t<Setter Property=\"Margin\" Value=\"1,3,1,3\" />\r\n\t\t<Setter Property=\"Focusable\" Value=\"false\" />\r\n\t\t<Setter Property=\"Template\">\r\n\t\t\t<Setter.Value>\r\n\t\t\t\t<ControlTemplate TargetType=\"Separator\">\r\n\t\t\t\t\t<Border\r\n\t\t\t\t\t\tVerticalAlignment=\"Stretch\"\r\n\t\t\t\t\t\tBackground=\"{TemplateBinding Background}\"\r\n\t\t\t\t\t\tBorderBrush=\"{TemplateBinding BorderBrush}\"\r\n\t\t\t\t\t\tBorderThickness=\"{TemplateBinding BorderThickness}\"\r\n\t\t\t\t\t\tSnapsToDevicePixels=\"true\" />\r\n\t\t\t\t</ControlTemplate>\r\n\t\t\t</Setter.Value>\r\n\t\t</Setter>\r\n\t</Style>\r\n\r\n\t<!--  GroupBox  -->\r\n\t<Style x:Key=\"GroupBox_Default\" TargetType=\"GroupBox\">\r\n\t\t<Setter Property=\"Margin\" Value=\"3,0,0,3\" />\r\n\t\t<Setter Property=\"Padding\" Value=\"5,0,5,5\" />\r\n\t\t<Setter Property=\"VerticalAlignment\" Value=\"Top\" />\r\n\t\t<Setter Property=\"Template\">\r\n\t\t\t<Setter.Value>\r\n\t\t\t\t<ControlTemplate TargetType=\"GroupBox\">\r\n\t\t\t\t\t<Grid>\r\n\t\t\t\t\t\t<Border\r\n\t\t\t\t\t\t\tMargin=\"1,14,0,0\"\r\n\t\t\t\t\t\t\tHorizontalAlignment=\"Stretch\"\r\n\t\t\t\t\t\t\tVerticalAlignment=\"Stretch\"\r\n\t\t\t\t\t\t\tBorderBrush=\"White\"\r\n\t\t\t\t\t\t\tBorderThickness=\"1\"\r\n\t\t\t\t\t\t\tCornerRadius=\"5\" />\r\n\t\t\t\t\t\t<Border\r\n\t\t\t\t\t\t\tMargin=\"0,13,1,1\"\r\n\t\t\t\t\t\t\tHorizontalAlignment=\"Stretch\"\r\n\t\t\t\t\t\t\tVerticalAlignment=\"Stretch\"\r\n\t\t\t\t\t\t\tBorderBrush=\"{StaticResource BorderDark}\"\r\n\t\t\t\t\t\t\tBorderThickness=\"1\"\r\n\t\t\t\t\t\t\tCornerRadius=\"5\" />\r\n\t\t\t\t\t\t<StackPanel>\r\n\t\t\t\t\t\t\t<Border\r\n\t\t\t\t\t\t\t\tMargin=\"10,2,10,2\"\r\n\t\t\t\t\t\t\t\tHorizontalAlignment=\"Left\"\r\n\t\t\t\t\t\t\t\tVerticalAlignment=\"Top\"\r\n\t\t\t\t\t\t\t\tBackground=\"{StaticResource BackgroundLight}\">\r\n\t\t\t\t\t\t\t\t<ContentPresenter Margin=\"5,3,5,3\" ContentSource=\"Header\" />\r\n\t\t\t\t\t\t\t</Border>\r\n\t\t\t\t\t\t\t<ContentPresenter Margin=\"{TemplateBinding Padding}\" ContentSource=\"Content\" />\r\n\t\t\t\t\t\t</StackPanel>\r\n\t\t\t\t\t</Grid>\r\n\t\t\t\t</ControlTemplate>\r\n\t\t\t</Setter.Value>\r\n\t\t</Setter>\r\n\t</Style>\r\n\r\n\t<!--\r\n\t\tUsing default style in .NET 4.8 prevents GroupBox control from disposing.\r\n\t\tUse modified style extracted from:\r\n\t\tc:\\Program Files\\Microsoft Visual Studio\\2022\\Community\\DesignTools\\SystemThemes\\Wpf\\aero2.normalcolor.xaml\r\n\t\tRemoved static resource link to \"<BorderGapMaskConverter x:Key=\"BorderGapMaskConverter\"/>\" from style which caused memory leak.\r\n\t-->\r\n\r\n\t<!--  ComboBox Style  -->\r\n\t<Style x:Key=\"ComboBox_Style\" TargetType=\"ComboBox\">\r\n\t\t<Setter Property=\"HorizontalAlignment\" Value=\"Left\" />\r\n\t\t<Setter Property=\"VerticalAlignment\" Value=\"Center\" />\r\n\t\t<Setter Property=\"SnapsToDevicePixels\" Value=\"True\" />\r\n\t\t<Setter Property=\"ScrollViewer.HorizontalScrollBarVisibility\" Value=\"Hidden\" />\r\n\t\t<Setter Property=\"ScrollViewer.VerticalScrollBarVisibility\" Value=\"Auto\" />\r\n\t\t<Setter Property=\"FocusVisualStyle\" Value=\"{StaticResource FocusVisualStyle_Default}\" />\r\n\t\t<Setter Property=\"Margin\" Value=\"3\" />\r\n\t</Style>\r\n\r\n\t<!--  ComboBox ContentPresenter  -->\r\n\t<Style x:Key=\"ComboBoxContentPresenter\" TargetType=\"ContentPresenter\">\r\n\t\t<Setter Property=\"Margin\" Value=\"0,0,5,1\" />\r\n\t\t<Setter Property=\"HorizontalAlignment\" Value=\"Left\" />\r\n\t\t<Setter Property=\"VerticalAlignment\" Value=\"Center\" />\r\n\t\t<Setter Property=\"IsHitTestVisible\" Value=\"False\" />\r\n\t</Style>\r\n\r\n\t<!--  ComboBoxItem  -->\r\n\t<Style x:Key=\"ComboBoxItem_Default\" TargetType=\"ComboBoxItem\">\r\n\t\t<Setter Property=\"Template\">\r\n\t\t\t<Setter.Value>\r\n\t\t\t\t<ControlTemplate TargetType=\"ComboBoxItem\">\r\n\t\t\t\t\t<Border\r\n\t\t\t\t\t\tName=\"ComboBoxItemBorder\"\r\n\t\t\t\t\t\tPadding=\"5\"\r\n\t\t\t\t\t\tCornerRadius=\"5\"\r\n\t\t\t\t\t\tSnapsToDevicePixels=\"True\">\r\n\t\t\t\t\t\t<ContentPresenter />\r\n\t\t\t\t\t</Border>\r\n\t\t\t\t\t<!--  Triggers  -->\r\n\t\t\t\t\t<ControlTemplate.Triggers>\r\n\t\t\t\t\t\t<Trigger Property=\"IsMouseOver\" Value=\"True\">\r\n\t\t\t\t\t\t\t<Setter TargetName=\"ComboBoxItemBorder\" Property=\"Background\" Value=\"{StaticResource BackgroundDarkSelected}\" />\r\n\t\t\t\t\t\t</Trigger>\r\n\t\t\t\t\t</ControlTemplate.Triggers>\r\n\t\t\t\t</ControlTemplate>\r\n\t\t\t</Setter.Value>\r\n\t\t</Setter>\r\n\t</Style>\r\n\r\n\t<!--  ComboBox  -->\r\n\t<Style x:Key=\"ComboBox_Default\" BasedOn=\"{StaticResource ComboBox_Style}\" TargetType=\"ComboBox\">\r\n\t\t<Setter Property=\"Template\">\r\n\t\t\t<Setter.Value>\r\n\t\t\t\t<ControlTemplate TargetType=\"ComboBox\">\r\n\t\t\t\t\t<StackPanel>\r\n\t\t\t\t\t\t<!--  Toggle Button (SelectedItem) and (Arrow)  -->\r\n\t\t\t\t\t\t<ToggleButton\r\n\t\t\t\t\t\t\tName=\"ToggleButton\"\r\n\t\t\t\t\t\t\tClickMode=\"Press\"\r\n\t\t\t\t\t\t\tFocusable=\"false\"\r\n\t\t\t\t\t\t\tIsChecked=\"{Binding Path=IsDropDownOpen, Mode=TwoWay, RelativeSource={RelativeSource TemplatedParent}}\">\r\n\t\t\t\t\t\t\t<ToggleButton.Template>\r\n\t\t\t\t\t\t\t\t<ControlTemplate TargetType=\"ToggleButton\">\r\n\t\t\t\t\t\t\t\t\t<!--  Border  -->\r\n\t\t\t\t\t\t\t\t\t<Border Name=\"DropDownControlBorder\" Style=\"{StaticResource DropDownControlBorder}\">\r\n\t\t\t\t\t\t\t\t\t\t<Grid>\r\n\t\t\t\t\t\t\t\t\t\t\t<Grid.ColumnDefinitions>\r\n\t\t\t\t\t\t\t\t\t\t\t\t<ColumnDefinition />\r\n\t\t\t\t\t\t\t\t\t\t\t\t<ColumnDefinition Width=\"16\" />\r\n\t\t\t\t\t\t\t\t\t\t\t</Grid.ColumnDefinitions>\r\n\t\t\t\t\t\t\t\t\t\t\t<!--  Selected Item  -->\r\n\t\t\t\t\t\t\t\t\t\t\t<ContentPresenter\r\n\t\t\t\t\t\t\t\t\t\t\t\tName=\"ContentSite\"\r\n\t\t\t\t\t\t\t\t\t\t\t\tContent=\"{Binding RelativeSource={RelativeSource Mode=FindAncestor, AncestorType={x:Type ComboBox}}, Path=SelectionBoxItem}\"\r\n\t\t\t\t\t\t\t\t\t\t\t\tContentTemplateSelector=\"{Binding RelativeSource={RelativeSource Mode=FindAncestor, AncestorType={x:Type ComboBox}}, Path=ItemTemplateSelector}\"\r\n\t\t\t\t\t\t\t\t\t\t\t\tStyle=\"{StaticResource ComboBoxContentPresenter}\" />\r\n\t\t\t\t\t\t\t\t\t\t\t<!--  Triangle  -->\r\n\t\t\t\t\t\t\t\t\t\t\t<Path Style=\"{StaticResource DropDownControlTriangle}\" />\r\n\t\t\t\t\t\t\t\t\t\t</Grid>\r\n\t\t\t\t\t\t\t\t\t</Border>\r\n\t\t\t\t\t\t\t\t\t<!--  Triggers  -->\r\n\t\t\t\t\t\t\t\t\t<ControlTemplate.Triggers>\r\n\t\t\t\t\t\t\t\t\t\t<Trigger Property=\"IsMouseOver\" Value=\"True\">\r\n\t\t\t\t\t\t\t\t\t\t\t<Setter TargetName=\"DropDownControlBorder\" Property=\"Background\" Value=\"{StaticResource BackgroundDarkSelected}\" />\r\n\t\t\t\t\t\t\t\t\t\t</Trigger>\r\n\t\t\t\t\t\t\t\t\t</ControlTemplate.Triggers>\r\n\t\t\t\t\t\t\t\t</ControlTemplate>\r\n\t\t\t\t\t\t\t</ToggleButton.Template>\r\n\t\t\t\t\t\t</ToggleButton>\r\n\t\t\t\t\t\t<!--  DropDown popup settings  -->\r\n\t\t\t\t\t\t<Popup Name=\"Popup\" AllowsTransparency=\"True\" IsOpen=\"{TemplateBinding IsDropDownOpen}\">\r\n\t\t\t\t\t\t\t<Grid MinWidth=\"{TemplateBinding ActualWidth}\" MaxHeight=\"{TemplateBinding MaxDropDownHeight}\">\r\n\t\t\t\t\t\t\t\t<Grid.OpacityMask>\r\n\t\t\t\t\t\t\t\t\t<VisualBrush Visual=\"{Binding ElementName=OpacityMaskBorder}\" />\r\n\t\t\t\t\t\t\t\t</Grid.OpacityMask>\r\n\t\t\t\t\t\t\t\t<Border Name=\"OpacityMaskBorder\" Background=\"{StaticResource BackgroundDark}\" CornerRadius=\"5\" />\r\n\t\t\t\t\t\t\t\t<ScrollViewer Margin=\"0\" ClipToBounds=\"True\">\r\n\t\t\t\t\t\t\t\t\t<ItemsPresenter />\r\n\t\t\t\t\t\t\t\t\t<!--<VirtualizingStackPanel IsItemsHost=\"True\" />-->\r\n\t\t\t\t\t\t\t\t</ScrollViewer>\r\n\t\t\t\t\t\t\t</Grid>\r\n\t\t\t\t\t\t</Popup>\r\n\t\t\t\t\t</StackPanel>\r\n\t\t\t\t\t<!--  Triggers  -->\r\n\t\t\t\t\t<ControlTemplate.Triggers>\r\n\t\t\t\t\t\t<Trigger Property=\"IsEnabled\" Value=\"False\">\r\n\t\t\t\t\t\t\t<Setter Property=\"Opacity\" Value=\"0.5\" />\r\n\t\t\t\t\t\t</Trigger>\r\n\t\t\t\t\t</ControlTemplate.Triggers>\r\n\t\t\t\t</ControlTemplate>\r\n\t\t\t</Setter.Value>\r\n\t\t</Setter>\r\n\t</Style>\r\n\r\n\t<!--  ComboBox Static - Text=\"Value\" or Text=\"\"  -->\r\n\t<Style x:Key=\"ComboBox_Static\" BasedOn=\"{StaticResource ComboBox_Style}\" TargetType=\"ComboBox\">\r\n\t\t<Setter Property=\"Template\">\r\n\t\t\t<Setter.Value>\r\n\t\t\t\t<ControlTemplate TargetType=\"ComboBox\">\r\n\t\t\t\t\t<StackPanel>\r\n\t\t\t\t\t\t<!--  Toggle Button (SelectedItem) and (Arrow)  -->\r\n\t\t\t\t\t\t<ToggleButton\r\n\t\t\t\t\t\t\tName=\"ToggleButton\"\r\n\t\t\t\t\t\t\tClickMode=\"Press\"\r\n\t\t\t\t\t\t\tFocusable=\"false\"\r\n\t\t\t\t\t\t\tIsChecked=\"{Binding Path=IsDropDownOpen, Mode=TwoWay, RelativeSource={RelativeSource TemplatedParent}}\">\r\n\t\t\t\t\t\t\t<ToggleButton.Template>\r\n\t\t\t\t\t\t\t\t<ControlTemplate TargetType=\"ToggleButton\">\r\n\t\t\t\t\t\t\t\t\t<!--  Border  -->\r\n\t\t\t\t\t\t\t\t\t<Border Name=\"DropDownControlBorder\" Style=\"{StaticResource DropDownControlBorder}\">\r\n\t\t\t\t\t\t\t\t\t\t<Grid>\r\n\t\t\t\t\t\t\t\t\t\t\t<Grid.ColumnDefinitions>\r\n\t\t\t\t\t\t\t\t\t\t\t\t<ColumnDefinition />\r\n\t\t\t\t\t\t\t\t\t\t\t\t<ColumnDefinition Width=\"16\" />\r\n\t\t\t\t\t\t\t\t\t\t\t</Grid.ColumnDefinitions>\r\n\t\t\t\t\t\t\t\t\t\t\t<!--  Static Selected Item  -->\r\n\t\t\t\t\t\t\t\t\t\t\t<ContentPresenter Name=\"ContentSite\" Content=\"{Binding RelativeSource={RelativeSource Mode=FindAncestor, AncestorType={x:Type ComboBox}}, Path=Text, Mode=OneTime}\" Style=\"{StaticResource ComboBoxContentPresenter}\" />\r\n\t\t\t\t\t\t\t\t\t\t\t<!--  Triangle  -->\r\n\t\t\t\t\t\t\t\t\t\t\t<Path Style=\"{StaticResource DropDownControlTriangle}\" />\r\n\t\t\t\t\t\t\t\t\t\t</Grid>\r\n\t\t\t\t\t\t\t\t\t</Border>\r\n\t\t\t\t\t\t\t\t\t<!--  Triggers  -->\r\n\t\t\t\t\t\t\t\t\t<ControlTemplate.Triggers>\r\n\t\t\t\t\t\t\t\t\t\t<Trigger Property=\"IsMouseOver\" Value=\"True\">\r\n\t\t\t\t\t\t\t\t\t\t\t<Setter TargetName=\"DropDownControlBorder\" Property=\"Background\" Value=\"{StaticResource BackgroundDarkSelected}\" />\r\n\t\t\t\t\t\t\t\t\t\t</Trigger>\r\n\t\t\t\t\t\t\t\t\t\t<!--  Static Trigger - if (Text=\"\") Width=\"26\"  -->\r\n\t\t\t\t\t\t\t\t\t\t<DataTrigger Binding=\"{Binding RelativeSource={RelativeSource Mode=FindAncestor, AncestorType={x:Type ComboBox}}, Path=Text, Mode=OneTime}\" Value=\"\">\r\n\t\t\t\t\t\t\t\t\t\t\t<Setter TargetName=\"ContentSite\" Property=\"Visibility\" Value=\"Collapsed\" />\r\n\t\t\t\t\t\t\t\t\t\t\t<Setter TargetName=\"DropDownControlBorder\" Property=\"Width\" Value=\"26\" />\r\n\t\t\t\t\t\t\t\t\t\t</DataTrigger>\r\n\t\t\t\t\t\t\t\t\t</ControlTemplate.Triggers>\r\n\t\t\t\t\t\t\t\t</ControlTemplate>\r\n\t\t\t\t\t\t\t</ToggleButton.Template>\r\n\t\t\t\t\t\t</ToggleButton>\r\n\t\t\t\t\t\t<!--  DropDown popup settings  -->\r\n\t\t\t\t\t\t<Popup Name=\"Popup\" AllowsTransparency=\"True\" IsOpen=\"{TemplateBinding IsDropDownOpen}\">\r\n\t\t\t\t\t\t\t<Grid MinWidth=\"{TemplateBinding ActualWidth}\" MaxHeight=\"{TemplateBinding MaxDropDownHeight}\">\r\n\t\t\t\t\t\t\t\t<Grid.OpacityMask>\r\n\t\t\t\t\t\t\t\t\t<VisualBrush Visual=\"{Binding ElementName=OpacityMaskBorder}\" />\r\n\t\t\t\t\t\t\t\t</Grid.OpacityMask>\r\n\t\t\t\t\t\t\t\t<Border Name=\"OpacityMaskBorder\" Background=\"{StaticResource BackgroundDark}\" CornerRadius=\"5\" />\r\n\t\t\t\t\t\t\t\t<ScrollViewer Margin=\"0\" ClipToBounds=\"True\">\r\n\t\t\t\t\t\t\t\t\t<ItemsPresenter />\r\n\t\t\t\t\t\t\t\t\t<!--<VirtualizingStackPanel IsItemsHost=\"True\" />-->\r\n\t\t\t\t\t\t\t\t</ScrollViewer>\r\n\t\t\t\t\t\t\t</Grid>\r\n\t\t\t\t\t\t</Popup>\r\n\t\t\t\t\t</StackPanel>\r\n\t\t\t\t\t<!--  Triggers  -->\r\n\t\t\t\t\t<ControlTemplate.Triggers>\r\n\t\t\t\t\t\t<Trigger Property=\"IsEnabled\" Value=\"False\">\r\n\t\t\t\t\t\t\t<Setter Property=\"Opacity\" Value=\"0.5\" />\r\n\t\t\t\t\t\t</Trigger>\r\n\t\t\t\t\t</ControlTemplate.Triggers>\r\n\t\t\t\t</ControlTemplate>\r\n\t\t\t</Setter.Value>\r\n\t\t</Setter>\r\n\t</Style>\r\n\r\n\t<!--  ComboBox Dynamic - Text=\"Value\" or Text=\"\"  -->\r\n\t<Style x:Key=\"ComboBox_Dynamic\" BasedOn=\"{StaticResource ComboBox_Style}\" TargetType=\"ComboBox\">\r\n\t\t<Setter Property=\"Template\">\r\n\t\t\t<Setter.Value>\r\n\t\t\t\t<ControlTemplate TargetType=\"ComboBox\">\r\n\t\t\t\t\t<StackPanel>\r\n\t\t\t\t\t\t<!--  Toggle Button (SelectedItem) and (Arrow)  -->\r\n\t\t\t\t\t\t<ToggleButton\r\n\t\t\t\t\t\t\tName=\"ToggleButton\"\r\n\t\t\t\t\t\t\tClickMode=\"Press\"\r\n\t\t\t\t\t\t\tFocusable=\"false\"\r\n\t\t\t\t\t\t\tIsChecked=\"{Binding Path=IsDropDownOpen, Mode=TwoWay, RelativeSource={RelativeSource TemplatedParent}}\">\r\n\t\t\t\t\t\t\t<ToggleButton.Template>\r\n\t\t\t\t\t\t\t\t<ControlTemplate TargetType=\"ToggleButton\">\r\n\t\t\t\t\t\t\t\t\t<!--  Border  -->\r\n\t\t\t\t\t\t\t\t\t<Border Name=\"DropDownControlBorder\" Style=\"{StaticResource DropDownControlBorder}\">\r\n\t\t\t\t\t\t\t\t\t\t<Grid>\r\n\t\t\t\t\t\t\t\t\t\t\t<Grid.ColumnDefinitions>\r\n\t\t\t\t\t\t\t\t\t\t\t\t<ColumnDefinition />\r\n\t\t\t\t\t\t\t\t\t\t\t\t<ColumnDefinition Width=\"16\" />\r\n\t\t\t\t\t\t\t\t\t\t\t</Grid.ColumnDefinitions>\r\n\t\t\t\t\t\t\t\t\t\t\t<!--  Dynamic Selected Item  -->\r\n\t\t\t\t\t\t\t\t\t\t\t<ContentPresenter Name=\"ContentSite\" Content=\"{Binding RelativeSource={RelativeSource Mode=FindAncestor, AncestorType={x:Type ComboBox}}, Path=Text}\" Style=\"{StaticResource ComboBoxContentPresenter}\" />\r\n\t\t\t\t\t\t\t\t\t\t\t<!--  Triangle  -->\r\n\t\t\t\t\t\t\t\t\t\t\t<Path Style=\"{StaticResource DropDownControlTriangle}\" />\r\n\t\t\t\t\t\t\t\t\t\t</Grid>\r\n\t\t\t\t\t\t\t\t\t</Border>\r\n\t\t\t\t\t\t\t\t\t<!--  Triggers  -->\r\n\t\t\t\t\t\t\t\t\t<ControlTemplate.Triggers>\r\n\t\t\t\t\t\t\t\t\t\t<Trigger Property=\"IsMouseOver\" Value=\"True\">\r\n\t\t\t\t\t\t\t\t\t\t\t<Setter TargetName=\"DropDownControlBorder\" Property=\"Background\" Value=\"{StaticResource BackgroundDarkSelected}\" />\r\n\t\t\t\t\t\t\t\t\t\t</Trigger>\r\n\t\t\t\t\t\t\t\t\t\t<!--  Dynamic Trigger - if (Text=\"\") Width=\"26\"  -->\r\n\t\t\t\t\t\t\t\t\t\t<DataTrigger Binding=\"{Binding RelativeSource={RelativeSource Mode=FindAncestor, AncestorType={x:Type ComboBox}}, Path=Text}\" Value=\"\">\r\n\t\t\t\t\t\t\t\t\t\t\t<Setter TargetName=\"ContentSite\" Property=\"Visibility\" Value=\"Collapsed\" />\r\n\t\t\t\t\t\t\t\t\t\t\t<Setter TargetName=\"DropDownControlBorder\" Property=\"Width\" Value=\"26\" />\r\n\t\t\t\t\t\t\t\t\t\t</DataTrigger>\r\n\t\t\t\t\t\t\t\t\t</ControlTemplate.Triggers>\r\n\t\t\t\t\t\t\t\t</ControlTemplate>\r\n\t\t\t\t\t\t\t</ToggleButton.Template>\r\n\t\t\t\t\t\t</ToggleButton>\r\n\t\t\t\t\t\t<!--  DropDown popup settings  -->\r\n\t\t\t\t\t\t<Popup Name=\"Popup\" AllowsTransparency=\"True\" IsOpen=\"{TemplateBinding IsDropDownOpen}\">\r\n\t\t\t\t\t\t\t<Grid MinWidth=\"{TemplateBinding ActualWidth}\" MaxHeight=\"{TemplateBinding MaxDropDownHeight}\">\r\n\t\t\t\t\t\t\t\t<Grid.OpacityMask>\r\n\t\t\t\t\t\t\t\t\t<VisualBrush Visual=\"{Binding ElementName=OpacityMaskBorder}\" />\r\n\t\t\t\t\t\t\t\t</Grid.OpacityMask>\r\n\t\t\t\t\t\t\t\t<Border Name=\"OpacityMaskBorder\" Background=\"{StaticResource BackgroundDark}\" CornerRadius=\"5\" />\r\n\t\t\t\t\t\t\t\t<ScrollViewer Margin=\"0\" ClipToBounds=\"True\">\r\n\t\t\t\t\t\t\t\t\t<ItemsPresenter />\r\n\t\t\t\t\t\t\t\t\t<!--<VirtualizingStackPanel IsItemsHost=\"True\" />-->\r\n\t\t\t\t\t\t\t\t</ScrollViewer>\r\n\t\t\t\t\t\t\t</Grid>\r\n\t\t\t\t\t\t</Popup>\r\n\t\t\t\t\t</StackPanel>\r\n\t\t\t\t\t<!--  Triggers  -->\r\n\t\t\t\t\t<ControlTemplate.Triggers>\r\n\t\t\t\t\t\t<Trigger Property=\"IsEnabled\" Value=\"False\">\r\n\t\t\t\t\t\t\t<Setter Property=\"Opacity\" Value=\"0.5\" />\r\n\t\t\t\t\t\t</Trigger>\r\n\t\t\t\t\t</ControlTemplate.Triggers>\r\n\t\t\t\t</ControlTemplate>\r\n\t\t\t</Setter.Value>\r\n\t\t</Setter>\r\n\t</Style>\r\n\r\n\t<!--  ListView Default  -->\r\n\t<!--<Style TargetType=\"ListView\">\r\n        <Setter Property=\"Background\" Value=\"{StaticResource Color.Black.4}\"/>\r\n        <Setter Property=\"Foreground\" Value=\"Black\"/>\r\n        <Setter Property=\"Padding\" Value=\"5\"/>\r\n        <Style.Resources>\r\n            <Style TargetType=\"Border\">\r\n                <Setter Property=\"CornerRadius\" Value=\"5\"/>\r\n            </Style>\r\n            <Style TargetType=\"TextBlock\">\r\n                <Setter Property=\"FontFamily\" Value=\"Roboto Mono\"/>\r\n                <Setter Property=\"FontSize\" Value=\"11\"/>\r\n            </Style>\r\n\r\n            <Style TargetType=\"ListViewItem\">\r\n                <Setter Property=\"BorderThickness\" Value=\"0\" />\r\n                <Setter Property=\"Padding\" Value=\"0\"/>\r\n                <Setter Property=\"Background\" Value=\"Transparent\"/>\r\n            </Style>\r\n        </Style.Resources>\r\n    </Style>-->\r\n\r\n\t<!--<Style x:Key=\"DataGridCheckBoxColumn_CellStyle\" TargetType=\"DataGridCell\">\r\n        <Style.Resources>\r\n            <Style BasedOn=\"{StaticResource CheckBoxCell_Default}\" TargetType=\"CheckBox\" />\r\n        </Style.Resources>\r\n    </Style>-->\r\n\r\n\t<!--  Cell Styles  -->\r\n\t<!--<Style x:Key=\"DataGridCheckBoxColumn_Default\" TargetType=\"DataGridCheckBoxColumn\">\r\n        <Setter Property=\"EditingElementStyle\" Value=\"{StaticResource CheckBoxCell_Default}\" />\r\n        <Setter Property=\"ElementStyle\" Value=\"{StaticResource CheckBoxCell_Default}\" />\r\n    </Style>-->\r\n\r\n\t<!--<Style TargetType=\"DataGridTextColumn\">\r\n        <Setter Property=\"EditingElementStyle\" Value=\"{StaticResource TextBoxCell}\" />\r\n        <Setter Property=\"ElementStyle\" Value=\"{StaticResource TextBoxCell}\" />\r\n    </Style>-->\r\n\r\n\t<!--  Style for icons  -->\r\n\t<Style x:Key=\"IconTextStyle\" TargetType=\"TextBlock\">\r\n\t\t<Setter Property=\"FontFamily\" Value=\"Segoe MDL2 Assets\" />\r\n\t\t<Setter Property=\"FontSize\" Value=\"16\" />\r\n\t\t<Setter Property=\"VerticalAlignment\" Value=\"Center\" />\r\n\t\t<Setter Property=\"HorizontalAlignment\" Value=\"Center\" />\r\n\t\t<Setter Property=\"Margin\" Value=\"0,0,5,0\" />\r\n\t</Style>\r\n\r\n\t<Style x:Key=\"IconEmojiTextStyle\" TargetType=\"TextBlock\">\r\n\t\t<Setter Property=\"FontFamily\" Value=\"Segoe UI Emoji\" />\r\n\t\t<Setter Property=\"FontSize\" Value=\"16\" />\r\n\t\t<Setter Property=\"VerticalAlignment\" Value=\"Center\" />\r\n\t\t<Setter Property=\"HorizontalAlignment\" Value=\"Center\" />\r\n\t\t<Setter Property=\"Margin\" Value=\"0,0,5,0\" />\r\n\t</Style>\r\n\r\n\t<!--  Default Styles  -->\r\n\t<Style BasedOn=\"{StaticResource Window_Default}\" TargetType=\"Window\" />\r\n\t<Style BasedOn=\"{StaticResource UserControl_Default}\" TargetType=\"UserControl\" />\r\n\t<Style BasedOn=\"{StaticResource Grid_Default}\" TargetType=\"Grid\" />\r\n\t<Style BasedOn=\"{StaticResource GroupBox_Default}\" TargetType=\"GroupBox\" />\r\n\t<Style BasedOn=\"{StaticResource DataGrid_Default}\" TargetType=\"DataGrid\" />\r\n\t<!--<Style BasedOn=\"{StaticResource DataGridRow_Default}\" TargetType=\"DataGridRow\" />-->\r\n\t<Style BasedOn=\"{StaticResource PasswordBox_Default}\" TargetType=\"PasswordBox\" />\r\n\t<Style BasedOn=\"{StaticResource TextBox_Default}\" TargetType=\"TextBox\" />\r\n\t<Style BasedOn=\"{StaticResource ComboBox_Default}\" TargetType=\"ComboBox\" />\r\n\t<Style BasedOn=\"{StaticResource Button_Default}\" TargetType=\"Button\" />\r\n\t<Style BasedOn=\"{StaticResource CheckBox_Default}\" TargetType=\"CheckBox\" />\r\n\t<Style BasedOn=\"{StaticResource MenuItem_Default}\" TargetType=\"MenuItem\" />\r\n\t<Style BasedOn=\"{StaticResource Label_Default}\" TargetType=\"Label\" />\r\n\t<Style BasedOn=\"{StaticResource ContentControl_Default}\" TargetType=\"ContentControl\" />\r\n\t<Style BasedOn=\"{StaticResource StatusBar_Default}\" TargetType=\"StatusBar\" />\r\n\t<!--<Style BasedOn=\"{StaticResource TickBar_Default}\" TargetType=\"TickBar\" />-->\r\n\t<Style BasedOn=\"{StaticResource Slider_Default}\" TargetType=\"Slider\" />\r\n\t<Style BasedOn=\"{StaticResource ComboBoxItem_Default}\" TargetType=\"ComboBoxItem\" />\r\n\t<Style BasedOn=\"{StaticResource RichTextBox_Default}\" TargetType=\"RichTextBox\" />\r\n\t<Style BasedOn=\"{StaticResource ListBox_Default}\" TargetType=\"ListBox\" />\r\n\t<Style BasedOn=\"{StaticResource ListBoxItem_Default}\" TargetType=\"ListBoxItem\" />\r\n\r\n\t<!--  TabControl_Default style is making control to fail garbage collection (leak memory)  -->\r\n\t<Style BasedOn=\"{StaticResource TabControl_Default}\" TargetType=\"TabControl\" />\r\n\r\n</ResourceDictionary>\r\n"
  },
  {
    "path": "FocusLogger/JocysCom/Controls/Themes/Default_MultiReplace.ps1",
    "content": "<#\r\n.SYNOPSIS\r\n    Replace in multiple files.\r\n.NOTES\r\n    Author:     Evaldas Jocys <evaldas@jocys.com>\r\n    Modified:   2021-04-14\r\n.LINK\r\n    http://www.jocys.com\r\n#>\r\n\r\nusing namespace System;\r\nusing namespace System.IO;\r\nusing namespace System.Text;\r\nusing namespace System.Text.RegularExpressions;\r\n\r\n# ----------------------------------------------------------------------------\r\n# Get current command path.\r\n[string]$current = $MyInvocation.MyCommand.Path;\r\n# Get calling command path.\r\n[string]$calling = @(Get-PSCallStack)[1].InvocationInfo.MyCommand.Path;\r\n# If executed directly then...\r\nif ($calling -ne \"\") {\r\n    $current = $calling;\r\n}\r\n# ----------------------------------------------------------------------------\r\n[FileInfo]$file = New-Object FileInfo($current);\r\n# Set public parameters.\r\n$global:scriptName = $file.Basename;\r\n$global:scriptPath = $file.Directory.FullName;\r\n# Change current directory.\r\n[Console]::WriteLine(\"Path: {0}\", $scriptPath);\r\n[Environment]::CurrentDirectory = $scriptPath;\r\nSet-Location $scriptPath;\r\n# ----------------------------------------------------------------------------\r\nFunction DoReplace\r\n{\r\n\t# Parameters.\r\n\tparam ([string]$path, [string]$filesPattern, [string]$pattern, [string]$replacement);\r\n\tWrite-Output \"`\"$path`\" `\"$filesPattern`\" `\"$pattern`\" `\"$replacement`\"\";\r\n\t# Function.\r\n\t$di = new-Object DirectoryInfo($path);\r\n\t[FileInfo[]]$files = $di.GetFiles($filesPattern, [SearchOption]::AllDirectories);\r\n\t$rx = new-Object Regex($pattern);\r\n\tforeach ($file in $files)\r\n\t{\r\n\t\t[string]$oldContent = [File]::ReadAllText($file.FullName);\r\n\t\t$newContent = $rx.Replace($oldContent, $replacement);\r\n\t\tif ($oldContent -ne $newContent)\r\n\t\t{\r\n\t\t\t$subName = $file.FullName.Substring($di.FullName.Length + 1);\r\n\t\t\tWrite-Output \"  $subName\";\r\n\t\t\t[File]::WriteAllText($file.FullName, $newContent);\r\n\t\t}\r\n\t}\r\n}\r\n# Replace default padding and margin from 4 to 3.\r\nDoReplace \".\" \"*.xaml\" \"(4),([034]),([034]),([034])\" \"3,`$2,`$3,`$4\";\r\nDoReplace \".\" \"*.xaml\" \"([034]),(4),([034]),([034])\" \"`$1,3,`$3,`$4\";\r\nDoReplace \".\" \"*.xaml\" \"([034]),([034]),(4),([034])\" \"`$1,`$2,3,`$4\";\r\nDoReplace \".\" \"*.xaml\" \"([034]),([034]),([034]),(4)\" \"`$1,`$2,`$3,3\";\r\npause;\r\n"
  },
  {
    "path": "FocusLogger/JocysCom/Controls/Themes/Icons.xaml",
    "content": "﻿<?xml version=\"1.0\" encoding=\"utf-8\" ?>\r\n<ResourceDictionary\r\n\tx:Class=\"JocysCom.ClassLibrary.Controls.Themes.Icons\"\r\n\txmlns=\"http://schemas.microsoft.com/winfx/2006/xaml/presentation\"\r\n\txmlns:x=\"http://schemas.microsoft.com/winfx/2006/xaml\"\r\n\tx:ClassModifier=\"public\">\r\n\t<Viewbox\r\n\t\txmlns=\"http://schemas.microsoft.com/winfx/2006/xaml/presentation\"\r\n\t\txmlns:p1=\"http://schemas.microsoft.com/winfx/2006/xaml\"\r\n\t\tp1:Key=\"Icon_Add\"\r\n\t\tp1:Shared=\"False\"\r\n\t\tStretch=\"Uniform\"\r\n\t\tTag=\"SHA256_ACD07674E66A4AC5C0131D45700172CA8F3F592B11254039D1E5C38D4ED78E95\">\r\n\t\t<Canvas Width=\"512\" Height=\"512\">\r\n\t\t\t<Canvas.RenderTransform>\r\n\t\t\t\t<TranslateTransform X=\"0\" Y=\"0\" />\r\n\t\t\t</Canvas.RenderTransform>\r\n\t\t\t<Canvas.Resources>\r\n\t\t\t\t<LinearGradientBrush xmlns:x=\"http://schemas.microsoft.com/winfx/2006/xaml\" x:Key=\"color-1\" MappingMode=\"Absolute\" StartPoint=\"32,32\" EndPoint=\"323,461\">\r\n\t\t\t\t\t<LinearGradientBrush.GradientStops>\r\n\t\t\t\t\t\t<GradientStopCollection>\r\n\t\t\t\t\t\t\t<GradientStop Offset=\"0\" Color=\"#98E957\" />\r\n\t\t\t\t\t\t\t<GradientStop Offset=\"1\" Color=\"#5EA821\" />\r\n\t\t\t\t\t\t</GradientStopCollection>\r\n\t\t\t\t\t</LinearGradientBrush.GradientStops>\r\n\t\t\t\t</LinearGradientBrush>\r\n\t\t\t</Canvas.Resources>\r\n\t\t\t<!--  Unknown tag: metadata  -->\r\n\t\t\t<!--  Unknown tag: sodipodi:namedview  -->\r\n\t\t\t<Canvas>\r\n\t\t\t\t<Path\r\n\t\t\t\t\txmlns:x=\"http://schemas.microsoft.com/winfx/2006/xaml\"\r\n\t\t\t\t\tFill=\"{StaticResource color-1}\"\r\n\t\t\t\t\tStrokeDashArray=\"\"\r\n\t\t\t\t\tStrokeEndLineCap=\"Flat\"\r\n\t\t\t\t\tStrokeLineJoin=\"Miter\"\r\n\t\t\t\t\tStrokeMiterLimit=\"10\"\r\n\t\t\t\t\tStrokeStartLineCap=\"Flat\"\r\n\t\t\t\t\tStrokeThickness=\"1\">\r\n\t\t\t\t\t<Path.Data>\r\n\t\t\t\t\t\t<PathGeometry Figures=\"M192 480l0 -160l-160 0l0 -128l160 0l0 -160l128 0l0 160l160 0l0 128l-160 0l0 160z\" FillRule=\"EvenOdd\" />\r\n\t\t\t\t\t</Path.Data>\r\n\t\t\t\t</Path>\r\n\t\t\t</Canvas>\r\n\t\t</Canvas>\r\n\t</Viewbox>\r\n\t<Viewbox\r\n\t\txmlns=\"http://schemas.microsoft.com/winfx/2006/xaml/presentation\"\r\n\t\txmlns:p1=\"http://schemas.microsoft.com/winfx/2006/xaml\"\r\n\t\tp1:Key=\"Icon_Cancel\"\r\n\t\tp1:Shared=\"False\"\r\n\t\tStretch=\"Uniform\"\r\n\t\tTag=\"SHA256_60288D29D3CF3980F259C3AE429B98A3BA14F5B49DC3BF57AB53090C1545B608\">\r\n\t\t<Canvas Width=\"512\" Height=\"512\">\r\n\t\t\t<Canvas.RenderTransform>\r\n\t\t\t\t<TranslateTransform X=\"0\" Y=\"0\" />\r\n\t\t\t</Canvas.RenderTransform>\r\n\t\t\t<Canvas.Resources>\r\n\t\t\t\t<LinearGradientBrush xmlns:x=\"http://schemas.microsoft.com/winfx/2006/xaml\" x:Key=\"color-1\" MappingMode=\"Absolute\" StartPoint=\"32,32\" EndPoint=\"480,460\">\r\n\t\t\t\t\t<LinearGradientBrush.GradientStops>\r\n\t\t\t\t\t\t<GradientStopCollection>\r\n\t\t\t\t\t\t\t<GradientStop Offset=\"0\" Color=\"#EF5C51\" />\r\n\t\t\t\t\t\t\t<GradientStop Offset=\"1\" Color=\"#C63328\" />\r\n\t\t\t\t\t\t</GradientStopCollection>\r\n\t\t\t\t\t</LinearGradientBrush.GradientStops>\r\n\t\t\t\t</LinearGradientBrush>\r\n\t\t\t\t<RadialGradientBrush xmlns:x=\"http://schemas.microsoft.com/winfx/2006/xaml\" x:Key=\"color-2\" Center=\"256,388\" GradientOrigin=\"256,388\" MappingMode=\"Absolute\" RadiusX=\"346.48136\" RadiusY=\"346.48136\">\r\n\t\t\t\t\t<RadialGradientBrush.GradientStops>\r\n\t\t\t\t\t\t<GradientStopCollection>\r\n\t\t\t\t\t\t\t<GradientStop Offset=\"0\" Color=\"#FFFFFF\" />\r\n\t\t\t\t\t\t\t<GradientStop Offset=\"0.6\" Color=\"#FFFFFF\" />\r\n\t\t\t\t\t\t\t<GradientStop Offset=\"1\" Color=\"#D8E0E8\" />\r\n\t\t\t\t\t\t</GradientStopCollection>\r\n\t\t\t\t\t</RadialGradientBrush.GradientStops>\r\n\t\t\t\t</RadialGradientBrush>\r\n\t\t\t</Canvas.Resources>\r\n\t\t\t<!--  Unknown tag: metadata  -->\r\n\t\t\t<!--  Unknown tag: sodipodi:namedview  -->\r\n\t\t\t<Canvas>\r\n\t\t\t\t<Path\r\n\t\t\t\t\txmlns:x=\"http://schemas.microsoft.com/winfx/2006/xaml\"\r\n\t\t\t\t\tFill=\"{StaticResource color-1}\"\r\n\t\t\t\t\tStrokeDashArray=\"\"\r\n\t\t\t\t\tStrokeEndLineCap=\"Flat\"\r\n\t\t\t\t\tStrokeLineJoin=\"Miter\"\r\n\t\t\t\t\tStrokeMiterLimit=\"10\"\r\n\t\t\t\t\tStrokeStartLineCap=\"Flat\"\r\n\t\t\t\t\tStrokeThickness=\"1\">\r\n\t\t\t\t\t<Path.Data>\r\n\t\t\t\t\t\t<PathGeometry Figures=\"M48 32l416 0c9 0 16 7 16 16l0 416c0 9 -7 16 -16 16l-416 0c-9 0 -16 -7 -16 -16l0 -416c0 -9 7 -16 16 -16z\" FillRule=\"NonZero\" />\r\n\t\t\t\t\t</Path.Data>\r\n\t\t\t\t</Path>\r\n\t\t\t\t<Path\r\n\t\t\t\t\txmlns:x=\"http://schemas.microsoft.com/winfx/2006/xaml\"\r\n\t\t\t\t\tFill=\"{StaticResource color-2}\"\r\n\t\t\t\t\tStrokeDashArray=\"\"\r\n\t\t\t\t\tStrokeEndLineCap=\"Flat\"\r\n\t\t\t\t\tStrokeLineJoin=\"Miter\"\r\n\t\t\t\t\tStrokeMiterLimit=\"10\"\r\n\t\t\t\t\tStrokeStartLineCap=\"Flat\"\r\n\t\t\t\t\tStrokeThickness=\"1\">\r\n\t\t\t\t\t<Path.Data>\r\n\t\t\t\t\t\t<PathGeometry Figures=\"M358 86.5l67.5 67.5l-101.5 102l101.5 102l-67.5 67.5l-102 -101.5l-102 101.5l-67.5 -67.5l101.5 -102l-101.5 -102l67.5 -67.5l102 101.5z\" FillRule=\"EvenOdd\" />\r\n\t\t\t\t\t</Path.Data>\r\n\t\t\t\t</Path>\r\n\t\t\t</Canvas>\r\n\t\t</Canvas>\r\n\t</Viewbox>\r\n\t<Viewbox\r\n\t\txmlns=\"http://schemas.microsoft.com/winfx/2006/xaml/presentation\"\r\n\t\txmlns:p1=\"http://schemas.microsoft.com/winfx/2006/xaml\"\r\n\t\tp1:Key=\"Icon_Cleanup\"\r\n\t\tp1:Shared=\"False\"\r\n\t\tStretch=\"Uniform\"\r\n\t\tTag=\"SHA256_20B62732DDBDCC2F8024EFBABF8730CF57684B68281A098DAC8C04E638F92B2F\">\r\n\t\t<Canvas Width=\"512\" Height=\"512\">\r\n\t\t\t<Canvas.RenderTransform>\r\n\t\t\t\t<TranslateTransform X=\"0\" Y=\"0\" />\r\n\t\t\t</Canvas.RenderTransform>\r\n\t\t\t<Canvas.Resources>\r\n\t\t\t\t<LinearGradientBrush xmlns:x=\"http://schemas.microsoft.com/winfx/2006/xaml\" x:Key=\"color-1\" MappingMode=\"Absolute\" StartPoint=\"32,208\" EndPoint=\"439,465\">\r\n\t\t\t\t\t<LinearGradientBrush.GradientStops>\r\n\t\t\t\t\t\t<GradientStopCollection>\r\n\t\t\t\t\t\t\t<GradientStop Offset=\"0\" Color=\"#FFD75C\" />\r\n\t\t\t\t\t\t\t<GradientStop Offset=\"1\" Color=\"#FFC106\" />\r\n\t\t\t\t\t\t</GradientStopCollection>\r\n\t\t\t\t\t</LinearGradientBrush.GradientStops>\r\n\t\t\t\t</LinearGradientBrush>\r\n\t\t\t\t<LinearGradientBrush xmlns:x=\"http://schemas.microsoft.com/winfx/2006/xaml\" x:Key=\"color-2\" MappingMode=\"Absolute\" StartPoint=\"231,208\" EndPoint=\"288,224\">\r\n\t\t\t\t\t<LinearGradientBrush.GradientStops>\r\n\t\t\t\t\t\t<GradientStopCollection>\r\n\t\t\t\t\t\t\t<GradientStop Offset=\"0\" Color=\"#F8C83A\" />\r\n\t\t\t\t\t\t\t<GradientStop Offset=\"0.90196\" Color=\"#F0BF32\" />\r\n\t\t\t\t\t\t\t<GradientStop Offset=\"1\" Color=\"#E8B52A\" />\r\n\t\t\t\t\t\t</GradientStopCollection>\r\n\t\t\t\t\t</LinearGradientBrush.GradientStops>\r\n\t\t\t\t</LinearGradientBrush>\r\n\t\t\t\t<LinearGradientBrush xmlns:x=\"http://schemas.microsoft.com/winfx/2006/xaml\" x:Key=\"color-3\" MappingMode=\"Absolute\" StartPoint=\"176,240\" EndPoint=\"327,304\">\r\n\t\t\t\t\t<LinearGradientBrush.GradientStops>\r\n\t\t\t\t\t\t<GradientStopCollection>\r\n\t\t\t\t\t\t\t<GradientStop Offset=\"0\" Color=\"#D89953\" />\r\n\t\t\t\t\t\t\t<GradientStop Offset=\"1\" Color=\"#D38E3E\" />\r\n\t\t\t\t\t\t</GradientStopCollection>\r\n\t\t\t\t\t</LinearGradientBrush.GradientStops>\r\n\t\t\t\t</LinearGradientBrush>\r\n\t\t\t\t<LinearGradientBrush xmlns:x=\"http://schemas.microsoft.com/winfx/2006/xaml\" x:Key=\"color-4\" MappingMode=\"Absolute\" StartPoint=\"256,0\" EndPoint=\"352,0\">\r\n\t\t\t\t\t<LinearGradientBrush.GradientStops>\r\n\t\t\t\t\t\t<GradientStopCollection>\r\n\t\t\t\t\t\t\t<GradientStop Offset=\"0\" Color=\"#717F8F\" />\r\n\t\t\t\t\t\t\t<GradientStop Offset=\"1\" Color=\"#51616F\" />\r\n\t\t\t\t\t\t</GradientStopCollection>\r\n\t\t\t\t\t</LinearGradientBrush.GradientStops>\r\n\t\t\t\t</LinearGradientBrush>\r\n\t\t\t\t<LinearGradientBrush xmlns:x=\"http://schemas.microsoft.com/winfx/2006/xaml\" x:Key=\"color-5\" MappingMode=\"Absolute\" StartPoint=\"256,48\" EndPoint=\"352,48\">\r\n\t\t\t\t\t<LinearGradientBrush.GradientStops>\r\n\t\t\t\t\t\t<GradientStopCollection>\r\n\t\t\t\t\t\t\t<GradientStop Offset=\"0\" Color=\"#D08730\" />\r\n\t\t\t\t\t\t\t<GradientStop Offset=\"1\" Color=\"#9C5F24\" />\r\n\t\t\t\t\t\t</GradientStopCollection>\r\n\t\t\t\t\t</LinearGradientBrush.GradientStops>\r\n\t\t\t\t</LinearGradientBrush>\r\n\t\t\t\t<LinearGradientBrush xmlns:x=\"http://schemas.microsoft.com/winfx/2006/xaml\" x:Key=\"color-6\" MappingMode=\"Absolute\" StartPoint=\"48,304\" EndPoint=\"439,402\">\r\n\t\t\t\t\t<LinearGradientBrush.GradientStops>\r\n\t\t\t\t\t\t<GradientStopCollection>\r\n\t\t\t\t\t\t\t<GradientStop Offset=\"0\" Color=\"#E3B633\" />\r\n\t\t\t\t\t\t\t<GradientStop Offset=\"0.90196\" Color=\"#CCA127\" />\r\n\t\t\t\t\t\t\t<GradientStop Offset=\"1\" Color=\"#B58C1B\" />\r\n\t\t\t\t\t\t</GradientStopCollection>\r\n\t\t\t\t\t</LinearGradientBrush.GradientStops>\r\n\t\t\t\t</LinearGradientBrush>\r\n\t\t\t</Canvas.Resources>\r\n\t\t\t<!--  Unknown tag: sodipodi:namedview  -->\r\n\t\t\t<Canvas>\r\n\t\t\t\t<Path\r\n\t\t\t\t\txmlns:x=\"http://schemas.microsoft.com/winfx/2006/xaml\"\r\n\t\t\t\t\tFill=\"{StaticResource color-1}\"\r\n\t\t\t\t\tStrokeDashArray=\"\"\r\n\t\t\t\t\tStrokeEndLineCap=\"Flat\"\r\n\t\t\t\t\tStrokeLineJoin=\"Miter\"\r\n\t\t\t\t\tStrokeMiterLimit=\"10\"\r\n\t\t\t\t\tStrokeStartLineCap=\"Flat\"\r\n\t\t\t\t\tStrokeThickness=\"1\">\r\n\t\t\t\t\t<Path.Data>\r\n\t\t\t\t\t\t<PathGeometry Figures=\"M256 208l96 0c38 19 67.5 55.5 80 96c10 33 15 94.5 -27.5 174c-11.5 21.5 -32.5 34 -56.5 34l-316 0c96 -80 128 -160 144 -208c13.5 -40 42.5 -77 80 -96z\" FillRule=\"EvenOdd\" />\r\n\t\t\t\t\t</Path.Data>\r\n\t\t\t\t</Path>\r\n\t\t\t\t<Path\r\n\t\t\t\t\txmlns:x=\"http://schemas.microsoft.com/winfx/2006/xaml\"\r\n\t\t\t\t\tFill=\"{StaticResource color-2}\"\r\n\t\t\t\t\tStrokeDashArray=\"\"\r\n\t\t\t\t\tStrokeEndLineCap=\"Flat\"\r\n\t\t\t\t\tStrokeLineJoin=\"Miter\"\r\n\t\t\t\t\tStrokeMiterLimit=\"10\"\r\n\t\t\t\t\tStrokeStartLineCap=\"Flat\"\r\n\t\t\t\t\tStrokeThickness=\"1\">\r\n\t\t\t\t\t<Path.Data>\r\n\t\t\t\t\t\t<PathGeometry Figures=\"M352 208c9 4.5 17.5 10 25 16l-73 0l-73 0c8 -6 16 -11.5 25 -16z\" FillRule=\"EvenOdd\" />\r\n\t\t\t\t\t</Path.Data>\r\n\t\t\t\t</Path>\r\n\t\t\t\t<Path\r\n\t\t\t\t\txmlns:x=\"http://schemas.microsoft.com/winfx/2006/xaml\"\r\n\t\t\t\t\tFill=\"{StaticResource color-3}\"\r\n\t\t\t\t\tStrokeDashArray=\"\"\r\n\t\t\t\t\tStrokeEndLineCap=\"Flat\"\r\n\t\t\t\t\tStrokeLineJoin=\"Miter\"\r\n\t\t\t\t\tStrokeMiterLimit=\"10\"\r\n\t\t\t\t\tStrokeStartLineCap=\"Flat\"\r\n\t\t\t\t\tStrokeThickness=\"1\">\r\n\t\t\t\t\t<Path.Data>\r\n\t\t\t\t\t\t<PathGeometry Figures=\"M182 288l244 0c2.5 5 4.5 10.5 6 16l-256 0c2 -5.5 4 -10.5 6 -16z M394.5 240c5 5 9.5 10.5 13.5 16l-207 0c4 -5.5 8 -11 13 -16z\" FillRule=\"EvenOdd\" />\r\n\t\t\t\t\t</Path.Data>\r\n\t\t\t\t</Path>\r\n\t\t\t\t<Path\r\n\t\t\t\t\txmlns:x=\"http://schemas.microsoft.com/winfx/2006/xaml\"\r\n\t\t\t\t\tFill=\"{StaticResource color-4}\"\r\n\t\t\t\t\tStrokeDashArray=\"\"\r\n\t\t\t\t\tStrokeEndLineCap=\"Flat\"\r\n\t\t\t\t\tStrokeLineJoin=\"Miter\"\r\n\t\t\t\t\tStrokeMiterLimit=\"10\"\r\n\t\t\t\t\tStrokeStartLineCap=\"Flat\"\r\n\t\t\t\t\tStrokeThickness=\"1\">\r\n\t\t\t\t\t<Path.Data>\r\n\t\t\t\t\t\t<PathGeometry Figures=\"M256 208l0 -160c0 -26.5 21.5 -48 48 -48c26.5 0 48 21.5 48 48l0 160z\" FillRule=\"EvenOdd\" />\r\n\t\t\t\t\t</Path.Data>\r\n\t\t\t\t</Path>\r\n\t\t\t\t<Path\r\n\t\t\t\t\txmlns:x=\"http://schemas.microsoft.com/winfx/2006/xaml\"\r\n\t\t\t\t\tFill=\"{StaticResource color-5}\"\r\n\t\t\t\t\tStrokeDashArray=\"\"\r\n\t\t\t\t\tStrokeEndLineCap=\"Flat\"\r\n\t\t\t\t\tStrokeLineJoin=\"Miter\"\r\n\t\t\t\t\tStrokeMiterLimit=\"10\"\r\n\t\t\t\t\tStrokeStartLineCap=\"Flat\"\r\n\t\t\t\t\tStrokeThickness=\"1\">\r\n\t\t\t\t\t<Path.Data>\r\n\t\t\t\t\t\t<PathGeometry Figures=\"M256 48l96 0l0 144l-96 0z\" FillRule=\"EvenOdd\" />\r\n\t\t\t\t\t</Path.Data>\r\n\t\t\t\t</Path>\r\n\t\t\t\t<Path\r\n\t\t\t\t\txmlns:x=\"http://schemas.microsoft.com/winfx/2006/xaml\"\r\n\t\t\t\t\tFill=\"{StaticResource color-6}\"\r\n\t\t\t\t\tStrokeDashArray=\"\"\r\n\t\t\t\t\tStrokeEndLineCap=\"Flat\"\r\n\t\t\t\t\tStrokeLineJoin=\"Miter\"\r\n\t\t\t\t\tStrokeMiterLimit=\"10\"\r\n\t\t\t\t\tStrokeStartLineCap=\"Flat\"\r\n\t\t\t\t\tStrokeThickness=\"1\">\r\n\t\t\t\t\t<Path.Data>\r\n\t\t\t\t\t\t<PathGeometry Figures=\"M235.5 487.5c28.5 -19 46 -54 58.5 -78c-6 38.5 -18.5 65 -26.5 77c28 -9.5 66 -65.5 84 -129c-7 57 -18 78 -31.5 106.5c14.5 -6 26.5 -22.5 37 -48c0 26.5 -12.5 63.5 -23 80c32.5 -12.5 53 -117.5 58.5 -152.5c5 30.5 -5.5 108.5 -18.5 130c39.5 -33 60 -138.5 58 -169.5c10 33 15 94.5 -27.5 174c-11.5 21.5 -32.5 34 -56.5 34l-300 0c32 0 104 -73 122 -109.5c-3.5 25.5 -60 89.5 -58 93.5c28.5 -10.5 89.5 -98 124.5 -164.5c-18.5 64 -37.5 102.5 -60.5 132.5c15 -6 31 -26 48 -48c-8 22.5 -33 70 -32 80c32 -16 73.5 -100 89.5 -132c-6 23 -20 66 -31.5 94.5c-5 10.5 -6 17 -14.5 29z\" FillRule=\"EvenOdd\" />\r\n\t\t\t\t\t</Path.Data>\r\n\t\t\t\t</Path>\r\n\t\t\t</Canvas>\r\n\t\t</Canvas>\r\n\t</Viewbox>\r\n\t<Viewbox\r\n\t\txmlns=\"http://schemas.microsoft.com/winfx/2006/xaml/presentation\"\r\n\t\txmlns:p1=\"http://schemas.microsoft.com/winfx/2006/xaml\"\r\n\t\tp1:Key=\"Icon_Delete\"\r\n\t\tp1:Shared=\"False\"\r\n\t\tStretch=\"Uniform\"\r\n\t\tTag=\"SHA256_F4B79BB9FFA1CCD7578374D96A82B1CCAEA2C07B3E4371EA1CBFB476AB64DA73\">\r\n\t\t<Canvas Width=\"512\" Height=\"512\">\r\n\t\t\t<Canvas.RenderTransform>\r\n\t\t\t\t<TranslateTransform X=\"0\" Y=\"0\" />\r\n\t\t\t</Canvas.RenderTransform>\r\n\t\t\t<Canvas.Resources>\r\n\t\t\t\t<LinearGradientBrush xmlns:x=\"http://schemas.microsoft.com/winfx/2006/xaml\" x:Key=\"color-1\" MappingMode=\"Absolute\" StartPoint=\"30,30\" EndPoint=\"447,417\">\r\n\t\t\t\t\t<LinearGradientBrush.GradientStops>\r\n\t\t\t\t\t\t<GradientStopCollection>\r\n\t\t\t\t\t\t\t<GradientStop Offset=\"0\" Color=\"#EF5C51\" />\r\n\t\t\t\t\t\t\t<GradientStop Offset=\"1\" Color=\"#C43126\" />\r\n\t\t\t\t\t\t</GradientStopCollection>\r\n\t\t\t\t\t</LinearGradientBrush.GradientStops>\r\n\t\t\t\t</LinearGradientBrush>\r\n\t\t\t</Canvas.Resources>\r\n\t\t\t<!--  Unknown tag: metadata  -->\r\n\t\t\t<!--  Unknown tag: sodipodi:namedview  -->\r\n\t\t\t<Canvas>\r\n\t\t\t\t<Path\r\n\t\t\t\t\txmlns:x=\"http://schemas.microsoft.com/winfx/2006/xaml\"\r\n\t\t\t\t\tFill=\"{StaticResource color-1}\"\r\n\t\t\t\t\tStrokeDashArray=\"\"\r\n\t\t\t\t\tStrokeEndLineCap=\"Flat\"\r\n\t\t\t\t\tStrokeLineJoin=\"Miter\"\r\n\t\t\t\t\tStrokeMiterLimit=\"10\"\r\n\t\t\t\t\tStrokeStartLineCap=\"Flat\"\r\n\t\t\t\t\tStrokeThickness=\"1\">\r\n\t\t\t\t\t<Path.Data>\r\n\t\t\t\t\t\t<PathGeometry Figures=\"M392 29.5l90.5 90.5l-136 136l136 136l-90.5 90.5l-136 -136l-136 136l-90.5 -90.5l136 -136l-136 -136l90.5 -90.5l136 136z\" FillRule=\"NonZero\" />\r\n\t\t\t\t\t</Path.Data>\r\n\t\t\t\t</Path>\r\n\t\t\t</Canvas>\r\n\t\t</Canvas>\r\n\t</Viewbox>\r\n\t<Viewbox\r\n\t\txmlns=\"http://schemas.microsoft.com/winfx/2006/xaml/presentation\"\r\n\t\txmlns:p1=\"http://schemas.microsoft.com/winfx/2006/xaml\"\r\n\t\tp1:Key=\"Icon_Edit\"\r\n\t\tp1:Shared=\"False\"\r\n\t\tStretch=\"Uniform\"\r\n\t\tTag=\"SHA256_A44F5D52DF97023CE4DAEA35680A5E89A5F93C9B3D709ADA9ADD0B828DC624E4\">\r\n\t\t<Canvas Width=\"512\" Height=\"512\">\r\n\t\t\t<Canvas.RenderTransform>\r\n\t\t\t\t<TranslateTransform X=\"0\" Y=\"0\" />\r\n\t\t\t</Canvas.RenderTransform>\r\n\t\t\t<Canvas.Resources>\r\n\t\t\t\t<LinearGradientBrush xmlns:x=\"http://schemas.microsoft.com/winfx/2006/xaml\" x:Key=\"color-1\" MappingMode=\"Absolute\" StartPoint=\"75,100\" EndPoint=\"166,193\">\r\n\t\t\t\t\t<LinearGradientBrush.GradientStops>\r\n\t\t\t\t\t\t<GradientStopCollection>\r\n\t\t\t\t\t\t\t<GradientStop Offset=\"0\" Color=\"#FFD041\" />\r\n\t\t\t\t\t\t\t<GradientStop Offset=\"0.27059\" Color=\"#FFD861\" />\r\n\t\t\t\t\t\t\t<GradientStop Offset=\"1\" Color=\"#FFC001\" />\r\n\t\t\t\t\t\t</GradientStopCollection>\r\n\t\t\t\t\t</LinearGradientBrush.GradientStops>\r\n\t\t\t\t</LinearGradientBrush>\r\n\t\t\t\t<LinearGradientBrush xmlns:x=\"http://schemas.microsoft.com/winfx/2006/xaml\" x:Key=\"color-2\" MappingMode=\"Absolute\" StartPoint=\"27,360\" EndPoint=\"93,434\">\r\n\t\t\t\t\t<LinearGradientBrush.GradientStops>\r\n\t\t\t\t\t\t<GradientStopCollection>\r\n\t\t\t\t\t\t\t<GradientStop Offset=\"0\" Color=\"#F4E0CC\" />\r\n\t\t\t\t\t\t\t<GradientStop Offset=\"0.52157\" Color=\"#E8C098\" />\r\n\t\t\t\t\t\t\t<GradientStop Offset=\"1\" Color=\"#DCA264\" />\r\n\t\t\t\t\t\t</GradientStopCollection>\r\n\t\t\t\t\t</LinearGradientBrush.GradientStops>\r\n\t\t\t\t</LinearGradientBrush>\r\n\t\t\t\t<LinearGradientBrush xmlns:x=\"http://schemas.microsoft.com/winfx/2006/xaml\" x:Key=\"color-3\" MappingMode=\"Absolute\" StartPoint=\"13,453\" EndPoint=\"42,482\">\r\n\t\t\t\t\t<LinearGradientBrush.GradientStops>\r\n\t\t\t\t\t\t<GradientStopCollection>\r\n\t\t\t\t\t\t\t<GradientStop Offset=\"0\" Color=\"#717F8F\" />\r\n\t\t\t\t\t\t\t<GradientStop Offset=\"1\" Color=\"#36414A\" />\r\n\t\t\t\t\t\t</GradientStopCollection>\r\n\t\t\t\t\t</LinearGradientBrush.GradientStops>\r\n\t\t\t\t</LinearGradientBrush>\r\n\t\t\t\t<LinearGradientBrush xmlns:x=\"http://schemas.microsoft.com/winfx/2006/xaml\" x:Key=\"color-4\" MappingMode=\"Absolute\" StartPoint=\"358,16\" EndPoint=\"470,128\">\r\n\t\t\t\t\t<LinearGradientBrush.GradientStops>\r\n\t\t\t\t\t\t<GradientStopCollection>\r\n\t\t\t\t\t\t\t<GradientStop Offset=\"0\" Color=\"#EF5C51\" />\r\n\t\t\t\t\t\t\t<GradientStop Offset=\"0.34118\" Color=\"#CF3C31\" />\r\n\t\t\t\t\t\t\t<GradientStop Offset=\"1\" Color=\"#AF1C11\" />\r\n\t\t\t\t\t\t</GradientStopCollection>\r\n\t\t\t\t\t</LinearGradientBrush.GradientStops>\r\n\t\t\t\t</LinearGradientBrush>\r\n\t\t\t\t<LinearGradientBrush xmlns:x=\"http://schemas.microsoft.com/winfx/2006/xaml\" x:Key=\"color-5\" MappingMode=\"Absolute\" StartPoint=\"335,77\" EndPoint=\"448,186\">\r\n\t\t\t\t\t<LinearGradientBrush.GradientStops>\r\n\t\t\t\t\t\t<GradientStopCollection>\r\n\t\t\t\t\t\t\t<GradientStop Offset=\"0\" Color=\"#FBFCFC\" />\r\n\t\t\t\t\t\t\t<GradientStop Offset=\"1\" Color=\"#DDE4EB\" />\r\n\t\t\t\t\t\t</GradientStopCollection>\r\n\t\t\t\t\t</LinearGradientBrush.GradientStops>\r\n\t\t\t\t</LinearGradientBrush>\r\n\t\t\t</Canvas.Resources>\r\n\t\t\t<!--  Unknown tag: metadata  -->\r\n\t\t\t<!--  Unknown tag: sodipodi:namedview  -->\r\n\t\t\t<Canvas>\r\n\t\t\t\t<Path\r\n\t\t\t\t\txmlns:x=\"http://schemas.microsoft.com/winfx/2006/xaml\"\r\n\t\t\t\t\tFill=\"{StaticResource color-1}\"\r\n\t\t\t\t\tStrokeDashArray=\"\"\r\n\t\t\t\t\tStrokeEndLineCap=\"Flat\"\r\n\t\t\t\t\tStrokeLineJoin=\"Miter\"\r\n\t\t\t\t\tStrokeMiterLimit=\"10\"\r\n\t\t\t\t\tStrokeStartLineCap=\"Flat\"\r\n\t\t\t\t\tStrokeThickness=\"1\">\r\n\t\t\t\t\t<Path.Data>\r\n\t\t\t\t\t\t<PathGeometry Figures=\"M341 100l84.5 84.5l0 6l-260 260l-60 -30.5l-30.5 -60l260 -260z\" FillRule=\"EvenOdd\" />\r\n\t\t\t\t\t</Path.Data>\r\n\t\t\t\t</Path>\r\n\t\t\t\t<Path\r\n\t\t\t\t\txmlns:x=\"http://schemas.microsoft.com/winfx/2006/xaml\"\r\n\t\t\t\t\tFill=\"{StaticResource color-2}\"\r\n\t\t\t\t\tStrokeDashArray=\"\"\r\n\t\t\t\t\tStrokeEndLineCap=\"Flat\"\r\n\t\t\t\t\tStrokeLineJoin=\"Miter\"\r\n\t\t\t\t\tStrokeMiterLimit=\"10\"\r\n\t\t\t\t\tStrokeStartLineCap=\"Flat\"\r\n\t\t\t\t\tStrokeThickness=\"1\">\r\n\t\t\t\t\t<Path.Data>\r\n\t\t\t\t\t\t<PathGeometry Figures=\"M165.5 450.5l-93 43l-45.5 5l5 -45.5l43 -93z\" FillRule=\"EvenOdd\" />\r\n\t\t\t\t\t</Path.Data>\r\n\t\t\t\t</Path>\r\n\t\t\t\t<Path\r\n\t\t\t\t\txmlns:x=\"http://schemas.microsoft.com/winfx/2006/xaml\"\r\n\t\t\t\t\tFill=\"{StaticResource color-3}\"\r\n\t\t\t\t\tStrokeDashArray=\"\"\r\n\t\t\t\t\tStrokeEndLineCap=\"Flat\"\r\n\t\t\t\t\tStrokeLineJoin=\"Miter\"\r\n\t\t\t\t\tStrokeMiterLimit=\"10\"\r\n\t\t\t\t\tStrokeStartLineCap=\"Flat\"\r\n\t\t\t\t\tStrokeThickness=\"1\">\r\n\t\t\t\t\t<Path.Data>\r\n\t\t\t\t\t\t<PathGeometry Figures=\"M32 453l40.5 40.5l-37.5 17.5c-6 2.5 -12 0.5 -16.5 -4c-4.5 -4.5 -6.5 -10.5 -4 -16.5z\" FillRule=\"EvenOdd\" />\r\n\t\t\t\t\t</Path.Data>\r\n\t\t\t\t</Path>\r\n\t\t\t\t<Path\r\n\t\t\t\t\txmlns:x=\"http://schemas.microsoft.com/winfx/2006/xaml\"\r\n\t\t\t\t\tFill=\"{StaticResource color-4}\"\r\n\t\t\t\t\tStrokeDashArray=\"\"\r\n\t\t\t\t\tStrokeEndLineCap=\"Flat\"\r\n\t\t\t\t\tStrokeLineJoin=\"Miter\"\r\n\t\t\t\t\tStrokeMiterLimit=\"10\"\r\n\t\t\t\t\tStrokeStartLineCap=\"Flat\"\r\n\t\t\t\t\tStrokeThickness=\"1\">\r\n\t\t\t\t\t<Path.Data>\r\n\t\t\t\t\t\t<PathGeometry Figures=\"M437 20.5l68 68c6 6 6 16.5 0 22.5l-56.5 56.5l-90.5 -90.5l56.5 -56.5c6 -6 16.5 -6 22.5 0z\" FillRule=\"EvenOdd\" />\r\n\t\t\t\t\t</Path.Data>\r\n\t\t\t\t</Path>\r\n\t\t\t\t<Path\r\n\t\t\t\t\txmlns:x=\"http://schemas.microsoft.com/winfx/2006/xaml\"\r\n\t\t\t\t\tFill=\"{StaticResource color-5}\"\r\n\t\t\t\t\tStrokeDashArray=\"\"\r\n\t\t\t\t\tStrokeEndLineCap=\"Flat\"\r\n\t\t\t\t\tStrokeLineJoin=\"Miter\"\r\n\t\t\t\t\tStrokeMiterLimit=\"10\"\r\n\t\t\t\t\tStrokeStartLineCap=\"Flat\"\r\n\t\t\t\t\tStrokeThickness=\"1\">\r\n\t\t\t\t\t<Path.Data>\r\n\t\t\t\t\t\t<PathGeometry Figures=\"M358 77l90.5 90.5l-23 23l-90.5 -90.5z\" FillRule=\"EvenOdd\" />\r\n\t\t\t\t\t</Path.Data>\r\n\t\t\t\t</Path>\r\n\t\t\t</Canvas>\r\n\t\t</Canvas>\r\n\t</Viewbox>\r\n\t<Viewbox\r\n\t\txmlns=\"http://schemas.microsoft.com/winfx/2006/xaml/presentation\"\r\n\t\txmlns:p1=\"http://schemas.microsoft.com/winfx/2006/xaml\"\r\n\t\tp1:Key=\"Icon_Error\"\r\n\t\tp1:Shared=\"False\"\r\n\t\tStretch=\"Uniform\"\r\n\t\tTag=\"SHA256_04518F79ED1189F76700A3225670E5F822BC46C64BC1C6C2E5332CFF84F21C99\">\r\n\t\t<Canvas Width=\"512\" Height=\"512\">\r\n\t\t\t<Canvas.RenderTransform>\r\n\t\t\t\t<TranslateTransform X=\"0\" Y=\"0\" />\r\n\t\t\t</Canvas.RenderTransform>\r\n\t\t\t<Canvas.Resources>\r\n\t\t\t\t<LinearGradientBrush xmlns:x=\"http://schemas.microsoft.com/winfx/2006/xaml\" x:Key=\"color-1\" MappingMode=\"Absolute\" StartPoint=\"16,16\" EndPoint=\"451,378\">\r\n\t\t\t\t\t<LinearGradientBrush.GradientStops>\r\n\t\t\t\t\t\t<GradientStopCollection>\r\n\t\t\t\t\t\t\t<GradientStop Offset=\"0\" Color=\"#EF5C51\" />\r\n\t\t\t\t\t\t\t<GradientStop Offset=\"1\" Color=\"#C22F24\" />\r\n\t\t\t\t\t\t</GradientStopCollection>\r\n\t\t\t\t\t</LinearGradientBrush.GradientStops>\r\n\t\t\t\t</LinearGradientBrush>\r\n\t\t\t\t<RadialGradientBrush xmlns:x=\"http://schemas.microsoft.com/winfx/2006/xaml\" x:Key=\"color-2\" Center=\"256,326\" GradientOrigin=\"256,326\" MappingMode=\"Absolute\" RadiusX=\"277.68899\" RadiusY=\"277.68899\">\r\n\t\t\t\t\t<RadialGradientBrush.GradientStops>\r\n\t\t\t\t\t\t<GradientStopCollection>\r\n\t\t\t\t\t\t\t<GradientStop Offset=\"0\" Color=\"#FFFFFF\" />\r\n\t\t\t\t\t\t\t<GradientStop Offset=\"0.34118\" Color=\"#FFFFFF\" />\r\n\t\t\t\t\t\t\t<GradientStop Offset=\"1\" Color=\"#D8E0E8\" />\r\n\t\t\t\t\t\t</GradientStopCollection>\r\n\t\t\t\t\t</RadialGradientBrush.GradientStops>\r\n\t\t\t\t</RadialGradientBrush>\r\n\t\t\t</Canvas.Resources>\r\n\t\t\t<!--  Unknown tag: metadata  -->\r\n\t\t\t<!--  Unknown tag: sodipodi:namedview  -->\r\n\t\t\t<Canvas>\r\n\t\t\t\t<Path\r\n\t\t\t\t\txmlns:x=\"http://schemas.microsoft.com/winfx/2006/xaml\"\r\n\t\t\t\t\tFill=\"{StaticResource color-1}\"\r\n\t\t\t\t\tStrokeDashArray=\"\"\r\n\t\t\t\t\tStrokeEndLineCap=\"Flat\"\r\n\t\t\t\t\tStrokeLineJoin=\"Miter\"\r\n\t\t\t\t\tStrokeMiterLimit=\"10\"\r\n\t\t\t\t\tStrokeStartLineCap=\"Flat\"\r\n\t\t\t\t\tStrokeThickness=\"1\">\r\n\t\t\t\t\t<Path.Data>\r\n\t\t\t\t\t\t<PathGeometry Figures=\"M256 16c132.5 0 240 107.5 240 240c0 132.5 -107.5 240 -240 240c-132.5 0 -240 -107.5 -240 -240c0 -132.5 107.5 -240 240 -240z\" FillRule=\"NonZero\" />\r\n\t\t\t\t\t</Path.Data>\r\n\t\t\t\t</Path>\r\n\t\t\t\t<Path\r\n\t\t\t\t\txmlns:x=\"http://schemas.microsoft.com/winfx/2006/xaml\"\r\n\t\t\t\t\tFill=\"{StaticResource color-2}\"\r\n\t\t\t\t\tStrokeDashArray=\"\"\r\n\t\t\t\t\tStrokeEndLineCap=\"Flat\"\r\n\t\t\t\t\tStrokeLineJoin=\"Miter\"\r\n\t\t\t\t\tStrokeMiterLimit=\"10\"\r\n\t\t\t\t\tStrokeStartLineCap=\"Flat\"\r\n\t\t\t\t\tStrokeThickness=\"1\">\r\n\t\t\t\t\t<Path.Data>\r\n\t\t\t\t\t\t<PathGeometry Figures=\"M165.5 414.5l-68 -68l90.5 -90.5l-90.5 -90.5l68 -68l90.5 90.5l90.5 -90.5l68 68l-90.5 90.5l90.5 90.5l-68 68l-90.5 -90.5z\" FillRule=\"EvenOdd\" />\r\n\t\t\t\t\t</Path.Data>\r\n\t\t\t\t</Path>\r\n\t\t\t</Canvas>\r\n\t\t</Canvas>\r\n\t</Viewbox>\r\n\t<Viewbox\r\n\t\txmlns=\"http://schemas.microsoft.com/winfx/2006/xaml/presentation\"\r\n\t\txmlns:p1=\"http://schemas.microsoft.com/winfx/2006/xaml\"\r\n\t\tp1:Key=\"Icon_Exit\"\r\n\t\tp1:Shared=\"False\"\r\n\t\tStretch=\"Uniform\"\r\n\t\tTag=\"SHA256_6BF141867927FE9DCA09DB6F47F31BA859C8633BEE1C313B8D0D8D592CF3D80C\">\r\n\t\t<Canvas Width=\"512\" Height=\"512\">\r\n\t\t\t<Canvas.RenderTransform>\r\n\t\t\t\t<TranslateTransform X=\"0\" Y=\"0\" />\r\n\t\t\t</Canvas.RenderTransform>\r\n\t\t\t<Canvas.Resources>\r\n\t\t\t\t<LinearGradientBrush xmlns:x=\"http://schemas.microsoft.com/winfx/2006/xaml\" x:Key=\"color-1\" MappingMode=\"Absolute\" StartPoint=\"80,16\" EndPoint=\"369,473\">\r\n\t\t\t\t\t<LinearGradientBrush.GradientStops>\r\n\t\t\t\t\t\t<GradientStopCollection>\r\n\t\t\t\t\t\t\t<GradientStop Offset=\"0\" Color=\"#717F8F\" />\r\n\t\t\t\t\t\t\t<GradientStop Offset=\"1\" Color=\"#5C6B7A\" />\r\n\t\t\t\t\t\t</GradientStopCollection>\r\n\t\t\t\t\t</LinearGradientBrush.GradientStops>\r\n\t\t\t\t</LinearGradientBrush>\r\n\t\t\t\t<LinearGradientBrush xmlns:x=\"http://schemas.microsoft.com/winfx/2006/xaml\" x:Key=\"color-2\" MappingMode=\"Absolute\" StartPoint=\"112,48\" EndPoint=\"228,480\">\r\n\t\t\t\t\t<LinearGradientBrush.GradientStops>\r\n\t\t\t\t\t\t<GradientStopCollection>\r\n\t\t\t\t\t\t\t<GradientStop Offset=\"0\" Color=\"#4D5D6A\" />\r\n\t\t\t\t\t\t\t<GradientStop Offset=\"1\" Color=\"#3B4852\" />\r\n\t\t\t\t\t\t</GradientStopCollection>\r\n\t\t\t\t\t</LinearGradientBrush.GradientStops>\r\n\t\t\t\t</LinearGradientBrush>\r\n\t\t\t\t<LinearGradientBrush xmlns:x=\"http://schemas.microsoft.com/winfx/2006/xaml\" x:Key=\"color-3\" MappingMode=\"Absolute\" StartPoint=\"288,0\" EndPoint=\"304,504\">\r\n\t\t\t\t\t<LinearGradientBrush.GradientStops>\r\n\t\t\t\t\t\t<GradientStopCollection>\r\n\t\t\t\t\t\t\t<GradientStop Offset=\"0\" Color=\"#DCA162\" />\r\n\t\t\t\t\t\t\t<GradientStop Offset=\"1\" Color=\"#D08730\" />\r\n\t\t\t\t\t\t</GradientStopCollection>\r\n\t\t\t\t\t</LinearGradientBrush.GradientStops>\r\n\t\t\t\t</LinearGradientBrush>\r\n\t\t\t\t<LinearGradientBrush xmlns:x=\"http://schemas.microsoft.com/winfx/2006/xaml\" x:Key=\"color-4\" MappingMode=\"Absolute\" StartPoint=\"320,0\" EndPoint=\"400,328\">\r\n\t\t\t\t\t<LinearGradientBrush.GradientStops>\r\n\t\t\t\t\t\t<GradientStopCollection>\r\n\t\t\t\t\t\t\t<GradientStop Offset=\"0\" Color=\"#CB832F\" />\r\n\t\t\t\t\t\t\t<GradientStop Offset=\"1\" Color=\"#B16F29\" />\r\n\t\t\t\t\t\t</GradientStopCollection>\r\n\t\t\t\t\t</LinearGradientBrush.GradientStops>\r\n\t\t\t\t</LinearGradientBrush>\r\n\t\t\t\t<LinearGradientBrush xmlns:x=\"http://schemas.microsoft.com/winfx/2006/xaml\" x:Key=\"color-5\" MappingMode=\"Absolute\" StartPoint=\"0,128\" EndPoint=\"0,366\">\r\n\t\t\t\t\t<LinearGradientBrush.GradientStops>\r\n\t\t\t\t\t\t<GradientStopCollection>\r\n\t\t\t\t\t\t\t<GradientStop Offset=\"0\" Color=\"#77DA26\" />\r\n\t\t\t\t\t\t\t<GradientStop Offset=\"1\" Color=\"#59A31D\" />\r\n\t\t\t\t\t\t</GradientStopCollection>\r\n\t\t\t\t\t</LinearGradientBrush.GradientStops>\r\n\t\t\t\t</LinearGradientBrush>\r\n\t\t\t</Canvas.Resources>\r\n\t\t\t<!--  Unknown tag: metadata  -->\r\n\t\t\t<!--  Unknown tag: sodipodi:namedview  -->\r\n\t\t\t<Canvas>\r\n\t\t\t\t<Path\r\n\t\t\t\t\txmlns:x=\"http://schemas.microsoft.com/winfx/2006/xaml\"\r\n\t\t\t\t\tFill=\"{StaticResource color-1}\"\r\n\t\t\t\t\tStrokeDashArray=\"\"\r\n\t\t\t\t\tStrokeEndLineCap=\"Flat\"\r\n\t\t\t\t\tStrokeLineJoin=\"Miter\"\r\n\t\t\t\t\tStrokeMiterLimit=\"10\"\r\n\t\t\t\t\tStrokeStartLineCap=\"Flat\"\r\n\t\t\t\t\tStrokeThickness=\"1\">\r\n\t\t\t\t\t<Path.Data>\r\n\t\t\t\t\t\t<PathGeometry Figures=\"M80 16l352 0l0 464l-352 0z\" FillRule=\"EvenOdd\" />\r\n\t\t\t\t\t</Path.Data>\r\n\t\t\t\t</Path>\r\n\t\t\t\t<Path\r\n\t\t\t\t\txmlns:x=\"http://schemas.microsoft.com/winfx/2006/xaml\"\r\n\t\t\t\t\tFill=\"{StaticResource color-2}\"\r\n\t\t\t\t\tStrokeDashArray=\"\"\r\n\t\t\t\t\tStrokeEndLineCap=\"Flat\"\r\n\t\t\t\t\tStrokeLineJoin=\"Miter\"\r\n\t\t\t\t\tStrokeMiterLimit=\"10\"\r\n\t\t\t\t\tStrokeStartLineCap=\"Flat\"\r\n\t\t\t\t\tStrokeThickness=\"1\">\r\n\t\t\t\t\t<Path.Data>\r\n\t\t\t\t\t\t<PathGeometry Figures=\"M112 48l288 0l0 432l-288 0z\" FillRule=\"EvenOdd\" />\r\n\t\t\t\t\t</Path.Data>\r\n\t\t\t\t</Path>\r\n\t\t\t\t<Path\r\n\t\t\t\t\txmlns:x=\"http://schemas.microsoft.com/winfx/2006/xaml\"\r\n\t\t\t\t\tFill=\"{StaticResource color-3}\"\r\n\t\t\t\t\tStrokeDashArray=\"\"\r\n\t\t\t\t\tStrokeEndLineCap=\"Flat\"\r\n\t\t\t\t\tStrokeLineJoin=\"Miter\"\r\n\t\t\t\t\tStrokeMiterLimit=\"10\"\r\n\t\t\t\t\tStrokeStartLineCap=\"Flat\"\r\n\t\t\t\t\tStrokeThickness=\"1\">\r\n\t\t\t\t\t<Path.Data>\r\n\t\t\t\t\t\t<PathGeometry Figures=\"M288 0l32 0l80 48l0 432l-80 32l-32 0z\" FillRule=\"EvenOdd\" />\r\n\t\t\t\t\t</Path.Data>\r\n\t\t\t\t</Path>\r\n\t\t\t\t<Path\r\n\t\t\t\t\txmlns:x=\"http://schemas.microsoft.com/winfx/2006/xaml\"\r\n\t\t\t\t\tFill=\"{StaticResource color-4}\"\r\n\t\t\t\t\tStrokeDashArray=\"\"\r\n\t\t\t\t\tStrokeEndLineCap=\"Flat\"\r\n\t\t\t\t\tStrokeLineJoin=\"Miter\"\r\n\t\t\t\t\tStrokeMiterLimit=\"10\"\r\n\t\t\t\t\tStrokeStartLineCap=\"Flat\"\r\n\t\t\t\t\tStrokeThickness=\"1\">\r\n\t\t\t\t\t<Path.Data>\r\n\t\t\t\t\t\t<PathGeometry Figures=\"M320 0l80 48l0 432l-80 32z\" FillRule=\"EvenOdd\" />\r\n\t\t\t\t\t</Path.Data>\r\n\t\t\t\t</Path>\r\n\t\t\t\t<Path\r\n\t\t\t\t\txmlns:x=\"http://schemas.microsoft.com/winfx/2006/xaml\"\r\n\t\t\t\t\tFill=\"#FFD8E0E8\"\r\n\t\t\t\t\tStrokeDashArray=\"\"\r\n\t\t\t\t\tStrokeEndLineCap=\"Flat\"\r\n\t\t\t\t\tStrokeLineJoin=\"Miter\"\r\n\t\t\t\t\tStrokeMiterLimit=\"10\"\r\n\t\t\t\t\tStrokeStartLineCap=\"Flat\"\r\n\t\t\t\t\tStrokeThickness=\"1\">\r\n\t\t\t\t\t<Path.Data>\r\n\t\t\t\t\t\t<PathGeometry Figures=\"M368 272c0 9 -3.5 16 -8 16l-16 0c-4.5 0 -8 -7 -8 -16c0 -9 3.5 -16 8 -16l36 0c2 0 4 3.5 4 8c0 4.5 -2 8 -4 8z\" FillRule=\"EvenOdd\" />\r\n\t\t\t\t\t</Path.Data>\r\n\t\t\t\t</Path>\r\n\t\t\t\t<Path\r\n\t\t\t\t\txmlns:x=\"http://schemas.microsoft.com/winfx/2006/xaml\"\r\n\t\t\t\t\tFill=\"{StaticResource color-5}\"\r\n\t\t\t\t\tStrokeDashArray=\"\"\r\n\t\t\t\t\tStrokeEndLineCap=\"Flat\"\r\n\t\t\t\t\tStrokeLineJoin=\"Miter\"\r\n\t\t\t\t\tStrokeMiterLimit=\"10\"\r\n\t\t\t\t\tStrokeStartLineCap=\"Flat\"\r\n\t\t\t\t\tStrokeThickness=\"1\">\r\n\t\t\t\t\t<Path.Data>\r\n\t\t\t\t\t\t<PathGeometry Figures=\"M0 208l0 96l112 0l0 80l128 -128l-128 -128l0 80z\" FillRule=\"EvenOdd\" />\r\n\t\t\t\t\t</Path.Data>\r\n\t\t\t\t</Path>\r\n\t\t\t</Canvas>\r\n\t\t</Canvas>\r\n\t</Viewbox>\r\n\t<Viewbox\r\n\t\txmlns=\"http://schemas.microsoft.com/winfx/2006/xaml/presentation\"\r\n\t\txmlns:p1=\"http://schemas.microsoft.com/winfx/2006/xaml\"\r\n\t\tp1:Key=\"Icon_Export\"\r\n\t\tp1:Shared=\"False\"\r\n\t\tStretch=\"Uniform\"\r\n\t\tTag=\"SHA256_F9E794CF99BE4B4EB0A2D56CF8E7E62B40529F9C7BBEDBFBD81D2EF59F858F7B\">\r\n\t\t<Canvas Width=\"512\" Height=\"512\">\r\n\t\t\t<Canvas.RenderTransform>\r\n\t\t\t\t<TranslateTransform X=\"0\" Y=\"0\" />\r\n\t\t\t</Canvas.RenderTransform>\r\n\t\t\t<Canvas.Resources>\r\n\t\t\t\t<LinearGradientBrush xmlns:x=\"http://schemas.microsoft.com/winfx/2006/xaml\" x:Key=\"color-1\" MappingMode=\"Absolute\" StartPoint=\"64,336\" EndPoint=\"384,528\">\r\n\t\t\t\t\t<LinearGradientBrush.GradientStops>\r\n\t\t\t\t\t\t<GradientStopCollection>\r\n\t\t\t\t\t\t\t<GradientStop Offset=\"0\" Color=\"#D8E0E8\" />\r\n\t\t\t\t\t\t\t<GradientStop Offset=\"1\" Color=\"#B3C0CD\" />\r\n\t\t\t\t\t\t</GradientStopCollection>\r\n\t\t\t\t\t</LinearGradientBrush.GradientStops>\r\n\t\t\t\t</LinearGradientBrush>\r\n\t\t\t\t<LinearGradientBrush xmlns:x=\"http://schemas.microsoft.com/winfx/2006/xaml\" x:Key=\"color-2\" MappingMode=\"Absolute\" StartPoint=\"64,496\" EndPoint=\"624,688\">\r\n\t\t\t\t\t<LinearGradientBrush.GradientStops>\r\n\t\t\t\t\t\t<GradientStopCollection>\r\n\t\t\t\t\t\t\t<GradientStop Offset=\"0\" Color=\"#D8E0E8\" />\r\n\t\t\t\t\t\t\t<GradientStop Offset=\"1\" Color=\"#91A0AF\" />\r\n\t\t\t\t\t\t</GradientStopCollection>\r\n\t\t\t\t\t</LinearGradientBrush.GradientStops>\r\n\t\t\t\t</LinearGradientBrush>\r\n\t\t\t\t<LinearGradientBrush xmlns:x=\"http://schemas.microsoft.com/winfx/2006/xaml\" x:Key=\"color-3\" MappingMode=\"Absolute\" StartPoint=\"96,0\" EndPoint=\"462,443\">\r\n\t\t\t\t\t<LinearGradientBrush.GradientStops>\r\n\t\t\t\t\t\t<GradientStopCollection>\r\n\t\t\t\t\t\t\t<GradientStop Offset=\"0\" Color=\"#EF5C51\" />\r\n\t\t\t\t\t\t\t<GradientStop Offset=\"1\" Color=\"#AF1C11\" />\r\n\t\t\t\t\t\t</GradientStopCollection>\r\n\t\t\t\t\t</LinearGradientBrush.GradientStops>\r\n\t\t\t\t</LinearGradientBrush>\r\n\t\t\t</Canvas.Resources>\r\n\t\t\t<!--  Unknown tag: metadata  -->\r\n\t\t\t<!--  Unknown tag: sodipodi:namedview  -->\r\n\t\t\t<Canvas>\r\n\t\t\t\t<Path\r\n\t\t\t\t\txmlns:x=\"http://schemas.microsoft.com/winfx/2006/xaml\"\r\n\t\t\t\t\tFill=\"{StaticResource color-1}\"\r\n\t\t\t\t\tStrokeDashArray=\"\"\r\n\t\t\t\t\tStrokeEndLineCap=\"Flat\"\r\n\t\t\t\t\tStrokeLineJoin=\"Miter\"\r\n\t\t\t\t\tStrokeMiterLimit=\"10\"\r\n\t\t\t\t\tStrokeStartLineCap=\"Flat\"\r\n\t\t\t\t\tStrokeThickness=\"1\">\r\n\t\t\t\t\t<Path.Data>\r\n\t\t\t\t\t\t<PathGeometry Figures=\"M80 336l352 0c9 0 16 7 16 16l0 144c0 9 -7 16 -16 16l-352 0c-9 0 -16 -7 -16 -16l0 -144c0 -9 7 -16 16 -16z\" FillRule=\"EvenOdd\" />\r\n\t\t\t\t\t</Path.Data>\r\n\t\t\t\t</Path>\r\n\t\t\t\t<Path\r\n\t\t\t\t\txmlns:x=\"http://schemas.microsoft.com/winfx/2006/xaml\"\r\n\t\t\t\t\tFill=\"{StaticResource color-2}\"\r\n\t\t\t\t\tStrokeDashArray=\"\"\r\n\t\t\t\t\tStrokeEndLineCap=\"Flat\"\r\n\t\t\t\t\tStrokeLineJoin=\"Miter\"\r\n\t\t\t\t\tStrokeMiterLimit=\"10\"\r\n\t\t\t\t\tStrokeStartLineCap=\"Flat\"\r\n\t\t\t\t\tStrokeThickness=\"1\">\r\n\t\t\t\t\t<Path.Data>\r\n\t\t\t\t\t\t<PathGeometry Figures=\"M448 496c0 9 -7 16 -16 16l-352 0c-9 0 -16 -7 -16 -16z\" FillRule=\"EvenOdd\" />\r\n\t\t\t\t\t</Path.Data>\r\n\t\t\t\t</Path>\r\n\t\t\t\t<Path\r\n\t\t\t\t\txmlns:x=\"http://schemas.microsoft.com/winfx/2006/xaml\"\r\n\t\t\t\t\tFill=\"{StaticResource color-3}\"\r\n\t\t\t\t\tStrokeDashArray=\"\"\r\n\t\t\t\t\tStrokeEndLineCap=\"Flat\"\r\n\t\t\t\t\tStrokeLineJoin=\"Miter\"\r\n\t\t\t\t\tStrokeMiterLimit=\"10\"\r\n\t\t\t\t\tStrokeStartLineCap=\"Flat\"\r\n\t\t\t\t\tStrokeThickness=\"1\">\r\n\t\t\t\t\t<Path.Data>\r\n\t\t\t\t\t\t<PathGeometry Figures=\"M192 304l128 0l0 -144l96 0l-160 -160l-160 160l96 0z\" FillRule=\"EvenOdd\" />\r\n\t\t\t\t\t</Path.Data>\r\n\t\t\t\t</Path>\r\n\t\t\t</Canvas>\r\n\t\t</Canvas>\r\n\t</Viewbox>\r\n\t<Viewbox\r\n\t\txmlns=\"http://schemas.microsoft.com/winfx/2006/xaml/presentation\"\r\n\t\txmlns:p1=\"http://schemas.microsoft.com/winfx/2006/xaml\"\r\n\t\tp1:Key=\"Icon_FolderOpen\"\r\n\t\tp1:Shared=\"False\"\r\n\t\tStretch=\"Uniform\"\r\n\t\tTag=\"SHA256_F7B3374D5957C5DE2318B051918B7D3648C81B6F4834466FB10844516C8D3EC3\">\r\n\t\t<Canvas Width=\"512\" Height=\"512\">\r\n\t\t\t<Canvas.RenderTransform>\r\n\t\t\t\t<TranslateTransform X=\"0\" Y=\"0\" />\r\n\t\t\t</Canvas.RenderTransform>\r\n\t\t\t<Canvas.Resources>\r\n\t\t\t\t<LinearGradientBrush xmlns:x=\"http://schemas.microsoft.com/winfx/2006/xaml\" x:Key=\"color-1\" MappingMode=\"Absolute\" StartPoint=\"32,64\" EndPoint=\"32,480\">\r\n\t\t\t\t\t<LinearGradientBrush.GradientStops>\r\n\t\t\t\t\t\t<GradientStopCollection>\r\n\t\t\t\t\t\t\t<GradientStop Offset=\"0\" Color=\"#F0CA62\" />\r\n\t\t\t\t\t\t\t<GradientStop Offset=\"0.47843\" Color=\"#DEAF3D\" />\r\n\t\t\t\t\t\t\t<GradientStop Offset=\"1\" Color=\"#CC9418\" />\r\n\t\t\t\t\t\t</GradientStopCollection>\r\n\t\t\t\t\t</LinearGradientBrush.GradientStops>\r\n\t\t\t\t</LinearGradientBrush>\r\n\t\t\t\t<LinearGradientBrush xmlns:x=\"http://schemas.microsoft.com/winfx/2006/xaml\" x:Key=\"color-2\" MappingMode=\"Absolute\" StartPoint=\"4,480\" EndPoint=\"6,240\">\r\n\t\t\t\t\t<LinearGradientBrush.GradientStops>\r\n\t\t\t\t\t\t<GradientStopCollection>\r\n\t\t\t\t\t\t\t<GradientStop Offset=\"0\" Color=\"#FFD24B\" />\r\n\t\t\t\t\t\t\t<GradientStop Offset=\"0.58823\" Color=\"#FFD863\" />\r\n\t\t\t\t\t\t\t<GradientStop Offset=\"1\" Color=\"#FFDF7C\" />\r\n\t\t\t\t\t\t</GradientStopCollection>\r\n\t\t\t\t\t</LinearGradientBrush.GradientStops>\r\n\t\t\t\t</LinearGradientBrush>\r\n\t\t\t</Canvas.Resources>\r\n\t\t\t<!--  Unknown tag: metadata  -->\r\n\t\t\t<!--  Unknown tag: sodipodi:namedview  -->\r\n\t\t\t<Canvas>\r\n\t\t\t\t<Path\r\n\t\t\t\t\txmlns:x=\"http://schemas.microsoft.com/winfx/2006/xaml\"\r\n\t\t\t\t\tFill=\"{StaticResource color-1}\"\r\n\t\t\t\t\tStrokeDashArray=\"\"\r\n\t\t\t\t\tStrokeEndLineCap=\"Flat\"\r\n\t\t\t\t\tStrokeLineJoin=\"Miter\"\r\n\t\t\t\t\tStrokeMiterLimit=\"10\"\r\n\t\t\t\t\tStrokeStartLineCap=\"Flat\"\r\n\t\t\t\t\tStrokeThickness=\"1\">\r\n\t\t\t\t\t<Path.Data>\r\n\t\t\t\t\t\t<PathGeometry Figures=\"M202.5 78l14 20c6 9 15.5 14 26 14l229.5 0c4.5 0 8 3.5 8 8l0 352c0 4.5 -3.5 8 -8 8l-432 0c-4.5 0 -8 -3.5 -8 -8l0 -400c0 -4.5 3.5 -8 8 -8l136 0c11 0 20 5 26.5 14z\" FillRule=\"EvenOdd\" />\r\n\t\t\t\t\t</Path.Data>\r\n\t\t\t\t</Path>\r\n\t\t\t\t<Path\r\n\t\t\t\t\txmlns:x=\"http://schemas.microsoft.com/winfx/2006/xaml\"\r\n\t\t\t\t\tFill=\"{StaticResource color-2}\"\r\n\t\t\t\t\tStrokeDashArray=\"\"\r\n\t\t\t\t\tStrokeEndLineCap=\"Flat\"\r\n\t\t\t\t\tStrokeLineJoin=\"Miter\"\r\n\t\t\t\t\tStrokeMiterLimit=\"10\"\r\n\t\t\t\t\tStrokeStartLineCap=\"Flat\"\r\n\t\t\t\t\tStrokeThickness=\"1\">\r\n\t\t\t\t\t<Path.Data>\r\n\t\t\t\t\t\t<PathGeometry Figures=\"M172 266l-159.5 0c-5 0 -8.5 2.5 -8 6l27 203c0 3 4 5 8.5 5c144 0 288 0 432 0c4.5 0 8.5 -2 8.5 -5c10.5 -76 20.5 -152.5 30.5 -228.5c0.5 -3.5 -3 -6.5 -8 -6.5l-266.5 0c-11 0 -20 3 -27 8.5c-3.5 3 -7.5 6 -11 9c-7 5.5 -16 8.5 -26.5 8.5z\" FillRule=\"EvenOdd\" />\r\n\t\t\t\t\t</Path.Data>\r\n\t\t\t\t</Path>\r\n\t\t\t</Canvas>\r\n\t\t</Canvas>\r\n\t</Viewbox>\r\n\t<Viewbox\r\n\t\txmlns=\"http://schemas.microsoft.com/winfx/2006/xaml/presentation\"\r\n\t\txmlns:p1=\"http://schemas.microsoft.com/winfx/2006/xaml\"\r\n\t\tp1:Key=\"Icon_Ignore\"\r\n\t\tp1:Shared=\"False\"\r\n\t\tStretch=\"Uniform\"\r\n\t\tTag=\"SHA256_ECD2D51B8F62BA5293F57FEDE6EDC5B71D2C019B363B527C4A11ED082ACA3F2B\">\r\n\t\t<Canvas Width=\"512\" Height=\"512\">\r\n\t\t\t<Canvas.RenderTransform>\r\n\t\t\t\t<TranslateTransform X=\"0\" Y=\"0\" />\r\n\t\t\t</Canvas.RenderTransform>\r\n\t\t\t<Canvas.Resources>\r\n\t\t\t\t<LinearGradientBrush xmlns:x=\"http://schemas.microsoft.com/winfx/2006/xaml\" x:Key=\"color-1\" MappingMode=\"Absolute\" StartPoint=\"0,64\" EndPoint=\"288,336\">\r\n\t\t\t\t\t<LinearGradientBrush.GradientStops>\r\n\t\t\t\t\t\t<GradientStopCollection>\r\n\t\t\t\t\t\t\t<GradientStop Offset=\"0\" Color=\"#F5B673\" />\r\n\t\t\t\t\t\t\t<GradientStop Offset=\"1\" Color=\"#F5A557\" />\r\n\t\t\t\t\t\t</GradientStopCollection>\r\n\t\t\t\t\t</LinearGradientBrush.GradientStops>\r\n\t\t\t\t</LinearGradientBrush>\r\n\t\t\t\t<LinearGradientBrush xmlns:x=\"http://schemas.microsoft.com/winfx/2006/xaml\" x:Key=\"color-2\" MappingMode=\"Absolute\" StartPoint=\"48,96\" EndPoint=\"314,346\">\r\n\t\t\t\t\t<LinearGradientBrush.GradientStops>\r\n\t\t\t\t\t\t<GradientStopCollection>\r\n\t\t\t\t\t\t\t<GradientStop Offset=\"0\" Color=\"#FFFFFF\" />\r\n\t\t\t\t\t\t\t<GradientStop Offset=\"1\" Color=\"#E5EAF0\" />\r\n\t\t\t\t\t\t</GradientStopCollection>\r\n\t\t\t\t\t</LinearGradientBrush.GradientStops>\r\n\t\t\t\t</LinearGradientBrush>\r\n\t\t\t\t<RadialGradientBrush xmlns:x=\"http://schemas.microsoft.com/winfx/2006/xaml\" x:Key=\"color-3\" Center=\"210,224\" GradientOrigin=\"210,224\" MappingMode=\"Absolute\" RadiusX=\"258.83157\" RadiusY=\"258.83157\">\r\n\t\t\t\t\t<RadialGradientBrush.GradientStops>\r\n\t\t\t\t\t\t<GradientStopCollection>\r\n\t\t\t\t\t\t\t<GradientStop Offset=\"0\" Color=\"#B3C0CD\" />\r\n\t\t\t\t\t\t\t<GradientStop Offset=\"0.4\" Color=\"#9CAAB8\" />\r\n\t\t\t\t\t\t\t<GradientStop Offset=\"1\" Color=\"#91A0AF\" />\r\n\t\t\t\t\t\t</GradientStopCollection>\r\n\t\t\t\t\t</RadialGradientBrush.GradientStops>\r\n\t\t\t\t</RadialGradientBrush>\r\n\t\t\t\t<RadialGradientBrush xmlns:x=\"http://schemas.microsoft.com/winfx/2006/xaml\" x:Key=\"color-4\" Center=\"210,224\" GradientOrigin=\"210,224\" MappingMode=\"Absolute\" RadiusX=\"258.8317\" RadiusY=\"258.8317\">\r\n\t\t\t\t\t<RadialGradientBrush.GradientStops>\r\n\t\t\t\t\t\t<GradientStopCollection>\r\n\t\t\t\t\t\t\t<GradientStop Offset=\"0\" Color=\"#36414A\" />\r\n\t\t\t\t\t\t\t<GradientStop Offset=\"1\" Color=\"#788F9B\" />\r\n\t\t\t\t\t\t</GradientStopCollection>\r\n\t\t\t\t\t</RadialGradientBrush.GradientStops>\r\n\t\t\t\t</RadialGradientBrush>\r\n\t\t\t</Canvas.Resources>\r\n\t\t\t<!--  Unknown tag: metadata  -->\r\n\t\t\t<!--  Unknown tag: sodipodi:namedview  -->\r\n\t\t\t<Canvas>\r\n\t\t\t\t<Path\r\n\t\t\t\t\txmlns:x=\"http://schemas.microsoft.com/winfx/2006/xaml\"\r\n\t\t\t\t\tFill=\"{StaticResource color-1}\"\r\n\t\t\t\t\tStrokeDashArray=\"\"\r\n\t\t\t\t\tStrokeEndLineCap=\"Flat\"\r\n\t\t\t\t\tStrokeLineJoin=\"Miter\"\r\n\t\t\t\t\tStrokeMiterLimit=\"10\"\r\n\t\t\t\t\tStrokeStartLineCap=\"Flat\"\r\n\t\t\t\t\tStrokeThickness=\"1\">\r\n\t\t\t\t\t<Path.Data>\r\n\t\t\t\t\t\t<PathGeometry Figures=\"M512 256c-31.5 111 -135 192 -256 192c-121 0 -224.5 -81 -256 -192c31.5 -111 135 -192 256 -192c121 0 224.5 81 256 192z\" FillRule=\"EvenOdd\" />\r\n\t\t\t\t\t</Path.Data>\r\n\t\t\t\t</Path>\r\n\t\t\t\t<Path\r\n\t\t\t\t\txmlns:x=\"http://schemas.microsoft.com/winfx/2006/xaml\"\r\n\t\t\t\t\tFill=\"{StaticResource color-2}\"\r\n\t\t\t\t\tStrokeDashArray=\"\"\r\n\t\t\t\t\tStrokeEndLineCap=\"Flat\"\r\n\t\t\t\t\tStrokeLineJoin=\"Miter\"\r\n\t\t\t\t\tStrokeMiterLimit=\"10\"\r\n\t\t\t\t\tStrokeStartLineCap=\"Flat\"\r\n\t\t\t\t\tStrokeThickness=\"1\">\r\n\t\t\t\t\t<Path.Data>\r\n\t\t\t\t\t\t<PathGeometry Figures=\"M48 256c29 95 114 160 208 160c94 0 179 -65 208 -160c-29 -95 -114 -160 -208 -160c-94 0 -179 65 -208 160z\" FillRule=\"EvenOdd\" />\r\n\t\t\t\t\t</Path.Data>\r\n\t\t\t\t</Path>\r\n\t\t\t\t<Path\r\n\t\t\t\t\txmlns:x=\"http://schemas.microsoft.com/winfx/2006/xaml\"\r\n\t\t\t\t\tFill=\"{StaticResource color-3}\"\r\n\t\t\t\t\tStrokeDashArray=\"\"\r\n\t\t\t\t\tStrokeEndLineCap=\"Flat\"\r\n\t\t\t\t\tStrokeLineJoin=\"Miter\"\r\n\t\t\t\t\tStrokeMiterLimit=\"10\"\r\n\t\t\t\t\tStrokeStartLineCap=\"Flat\"\r\n\t\t\t\t\tStrokeThickness=\"1\">\r\n\t\t\t\t\t<Path.Data>\r\n\t\t\t\t\t\t<PathGeometry Figures=\"M256 112c79.5 0 144 64.5 144 144c0 79.5 -64.5 144 -144 144c-79.5 0 -144 -64.5 -144 -144c0 -79.5 64.5 -144 144 -144z\" FillRule=\"EvenOdd\" />\r\n\t\t\t\t\t</Path.Data>\r\n\t\t\t\t</Path>\r\n\t\t\t\t<Path\r\n\t\t\t\t\txmlns:x=\"http://schemas.microsoft.com/winfx/2006/xaml\"\r\n\t\t\t\t\tFill=\"{StaticResource color-4}\"\r\n\t\t\t\t\tStrokeDashArray=\"\"\r\n\t\t\t\t\tStrokeEndLineCap=\"Flat\"\r\n\t\t\t\t\tStrokeLineJoin=\"Miter\"\r\n\t\t\t\t\tStrokeMiterLimit=\"10\"\r\n\t\t\t\t\tStrokeStartLineCap=\"Flat\"\r\n\t\t\t\t\tStrokeThickness=\"1\">\r\n\t\t\t\t\t<Path.Data>\r\n\t\t\t\t\t\t<PathGeometry Figures=\"M256 112c79.5 0 144 64.5 144 144c0 79.5 -64.5 144 -144 144c-79.5 0 -144 -64.5 -144 -144c0 -79.5 64.5 -144 144 -144z M345.5 189l-156.5 156.5c18.5 14 42 22.5 67 22.5c62 0 112 -50 112 -112c0 -25 -8.5 -48.5 -22.5 -67z M166.5 323l156.5 -156.5c-18.5 -14 -42 -22.5 -67 -22.5c-62 0 -112 50 -112 112c0 25 8.5 48.5 22.5 67z\" FillRule=\"EvenOdd\" />\r\n\t\t\t\t\t</Path.Data>\r\n\t\t\t\t</Path>\r\n\t\t\t</Canvas>\r\n\t\t</Canvas>\r\n\t</Viewbox>\r\n\t<Viewbox\r\n\t\txmlns=\"http://schemas.microsoft.com/winfx/2006/xaml/presentation\"\r\n\t\txmlns:p1=\"http://schemas.microsoft.com/winfx/2006/xaml\"\r\n\t\tp1:Key=\"Icon_Import\"\r\n\t\tp1:Shared=\"False\"\r\n\t\tStretch=\"Uniform\"\r\n\t\tTag=\"SHA256_20DC58A6BA80A8A82C3C292811016A6979994D3D6B0611048E6843F147230417\">\r\n\t\t<Canvas Width=\"512\" Height=\"512\">\r\n\t\t\t<Canvas.RenderTransform>\r\n\t\t\t\t<TranslateTransform X=\"0\" Y=\"0\" />\r\n\t\t\t</Canvas.RenderTransform>\r\n\t\t\t<Canvas.Resources>\r\n\t\t\t\t<LinearGradientBrush xmlns:x=\"http://schemas.microsoft.com/winfx/2006/xaml\" x:Key=\"color-1\" MappingMode=\"Absolute\" StartPoint=\"96,0\" EndPoint=\"422,305\">\r\n\t\t\t\t\t<LinearGradientBrush.GradientStops>\r\n\t\t\t\t\t\t<GradientStopCollection>\r\n\t\t\t\t\t\t\t<GradientStop Offset=\"0\" Color=\"#77DA26\" />\r\n\t\t\t\t\t\t\t<GradientStop Offset=\"1\" Color=\"#59A31D\" />\r\n\t\t\t\t\t\t</GradientStopCollection>\r\n\t\t\t\t\t</LinearGradientBrush.GradientStops>\r\n\t\t\t\t</LinearGradientBrush>\r\n\t\t\t\t<LinearGradientBrush xmlns:x=\"http://schemas.microsoft.com/winfx/2006/xaml\" x:Key=\"color-2\" MappingMode=\"Absolute\" StartPoint=\"64,336\" EndPoint=\"384,528\">\r\n\t\t\t\t\t<LinearGradientBrush.GradientStops>\r\n\t\t\t\t\t\t<GradientStopCollection>\r\n\t\t\t\t\t\t\t<GradientStop Offset=\"0\" Color=\"#D8E0E8\" />\r\n\t\t\t\t\t\t\t<GradientStop Offset=\"1\" Color=\"#B3C0CD\" />\r\n\t\t\t\t\t\t</GradientStopCollection>\r\n\t\t\t\t\t</LinearGradientBrush.GradientStops>\r\n\t\t\t\t</LinearGradientBrush>\r\n\t\t\t\t<LinearGradientBrush xmlns:x=\"http://schemas.microsoft.com/winfx/2006/xaml\" x:Key=\"color-3\" MappingMode=\"Absolute\" StartPoint=\"64,496\" EndPoint=\"624,688\">\r\n\t\t\t\t\t<LinearGradientBrush.GradientStops>\r\n\t\t\t\t\t\t<GradientStopCollection>\r\n\t\t\t\t\t\t\t<GradientStop Offset=\"0\" Color=\"#D8E0E8\" />\r\n\t\t\t\t\t\t\t<GradientStop Offset=\"1\" Color=\"#91A0AF\" />\r\n\t\t\t\t\t\t</GradientStopCollection>\r\n\t\t\t\t\t</LinearGradientBrush.GradientStops>\r\n\t\t\t\t</LinearGradientBrush>\r\n\t\t\t</Canvas.Resources>\r\n\t\t\t<!--  Unknown tag: metadata  -->\r\n\t\t\t<!--  Unknown tag: sodipodi:namedview  -->\r\n\t\t\t<Canvas>\r\n\t\t\t\t<Path\r\n\t\t\t\t\txmlns:x=\"http://schemas.microsoft.com/winfx/2006/xaml\"\r\n\t\t\t\t\tFill=\"{StaticResource color-1}\"\r\n\t\t\t\t\tStrokeDashArray=\"\"\r\n\t\t\t\t\tStrokeEndLineCap=\"Flat\"\r\n\t\t\t\t\tStrokeLineJoin=\"Miter\"\r\n\t\t\t\t\tStrokeMiterLimit=\"10\"\r\n\t\t\t\t\tStrokeStartLineCap=\"Flat\"\r\n\t\t\t\t\tStrokeThickness=\"1\">\r\n\t\t\t\t\t<Path.Data>\r\n\t\t\t\t\t\t<PathGeometry Figures=\"M192 0l128 0l0 144l96 0l-160 160l-160 -160l96 0z\" FillRule=\"EvenOdd\" />\r\n\t\t\t\t\t</Path.Data>\r\n\t\t\t\t</Path>\r\n\t\t\t\t<Path\r\n\t\t\t\t\txmlns:x=\"http://schemas.microsoft.com/winfx/2006/xaml\"\r\n\t\t\t\t\tFill=\"{StaticResource color-2}\"\r\n\t\t\t\t\tStrokeDashArray=\"\"\r\n\t\t\t\t\tStrokeEndLineCap=\"Flat\"\r\n\t\t\t\t\tStrokeLineJoin=\"Miter\"\r\n\t\t\t\t\tStrokeMiterLimit=\"10\"\r\n\t\t\t\t\tStrokeStartLineCap=\"Flat\"\r\n\t\t\t\t\tStrokeThickness=\"1\">\r\n\t\t\t\t\t<Path.Data>\r\n\t\t\t\t\t\t<PathGeometry Figures=\"M80 336l352 0c9 0 16 7 16 16l0 144c0 9 -7 16 -16 16l-352 0c-9 0 -16 -7 -16 -16l0 -144c0 -9 7 -16 16 -16z\" FillRule=\"EvenOdd\" />\r\n\t\t\t\t\t</Path.Data>\r\n\t\t\t\t</Path>\r\n\t\t\t\t<Path\r\n\t\t\t\t\txmlns:x=\"http://schemas.microsoft.com/winfx/2006/xaml\"\r\n\t\t\t\t\tFill=\"{StaticResource color-3}\"\r\n\t\t\t\t\tStrokeDashArray=\"\"\r\n\t\t\t\t\tStrokeEndLineCap=\"Flat\"\r\n\t\t\t\t\tStrokeLineJoin=\"Miter\"\r\n\t\t\t\t\tStrokeMiterLimit=\"10\"\r\n\t\t\t\t\tStrokeStartLineCap=\"Flat\"\r\n\t\t\t\t\tStrokeThickness=\"1\">\r\n\t\t\t\t\t<Path.Data>\r\n\t\t\t\t\t\t<PathGeometry Figures=\"M448 496c0 9 -7 16 -16 16l-352 0c-9 0 -16 -7 -16 -16z\" FillRule=\"EvenOdd\" />\r\n\t\t\t\t\t</Path.Data>\r\n\t\t\t\t</Path>\r\n\t\t\t</Canvas>\r\n\t\t</Canvas>\r\n\t</Viewbox>\r\n\t<Viewbox\r\n\t\txmlns=\"http://schemas.microsoft.com/winfx/2006/xaml/presentation\"\r\n\t\txmlns:p1=\"http://schemas.microsoft.com/winfx/2006/xaml\"\r\n\t\tp1:Key=\"Icon_Information\"\r\n\t\tp1:Shared=\"False\"\r\n\t\tStretch=\"Uniform\"\r\n\t\tTag=\"SHA256_786D3006E008A58AA75D218FB4E9F1140ACE398E2C59E725C7A1C2855FEC063C\">\r\n\t\t<Canvas Width=\"512\" Height=\"512\">\r\n\t\t\t<Canvas.RenderTransform>\r\n\t\t\t\t<TranslateTransform X=\"0\" Y=\"0\" />\r\n\t\t\t</Canvas.RenderTransform>\r\n\t\t\t<Canvas.Resources>\r\n\t\t\t\t<LinearGradientBrush xmlns:x=\"http://schemas.microsoft.com/winfx/2006/xaml\" x:Key=\"color-1\" MappingMode=\"Absolute\" StartPoint=\"16,16\" EndPoint=\"16,496\">\r\n\t\t\t\t\t<LinearGradientBrush.GradientStops>\r\n\t\t\t\t\t\t<GradientStopCollection>\r\n\t\t\t\t\t\t\t<GradientStop Offset=\"0\" Color=\"#4EB6F2\" />\r\n\t\t\t\t\t\t\t<GradientStop Offset=\"1\" Color=\"#129EEE\" />\r\n\t\t\t\t\t\t</GradientStopCollection>\r\n\t\t\t\t\t</LinearGradientBrush.GradientStops>\r\n\t\t\t\t</LinearGradientBrush>\r\n\t\t\t\t<RadialGradientBrush xmlns:x=\"http://schemas.microsoft.com/winfx/2006/xaml\" x:Key=\"color-2\" Center=\"259,374\" GradientOrigin=\"259,374\" MappingMode=\"Absolute\" RadiusX=\"276.24253\" RadiusY=\"276.24253\">\r\n\t\t\t\t\t<RadialGradientBrush.GradientStops>\r\n\t\t\t\t\t\t<GradientStopCollection>\r\n\t\t\t\t\t\t\t<GradientStop Offset=\"0\" Color=\"#FFFFFF\" />\r\n\t\t\t\t\t\t\t<GradientStop Offset=\"0.45882\" Color=\"#FFFFFF\" />\r\n\t\t\t\t\t\t\t<GradientStop Offset=\"1\" Color=\"#D8E0E8\" />\r\n\t\t\t\t\t\t</GradientStopCollection>\r\n\t\t\t\t\t</RadialGradientBrush.GradientStops>\r\n\t\t\t\t</RadialGradientBrush>\r\n\t\t\t</Canvas.Resources>\r\n\t\t\t<!--  Unknown tag: metadata  -->\r\n\t\t\t<!--  Unknown tag: sodipodi:namedview  -->\r\n\t\t\t<Canvas>\r\n\t\t\t\t<Path\r\n\t\t\t\t\txmlns:x=\"http://schemas.microsoft.com/winfx/2006/xaml\"\r\n\t\t\t\t\tFill=\"{StaticResource color-1}\"\r\n\t\t\t\t\tStrokeDashArray=\"\"\r\n\t\t\t\t\tStrokeEndLineCap=\"Flat\"\r\n\t\t\t\t\tStrokeLineJoin=\"Miter\"\r\n\t\t\t\t\tStrokeMiterLimit=\"10\"\r\n\t\t\t\t\tStrokeStartLineCap=\"Flat\"\r\n\t\t\t\t\tStrokeThickness=\"1\">\r\n\t\t\t\t\t<Path.Data>\r\n\t\t\t\t\t\t<PathGeometry Figures=\"M256 16c132.5 0 240 107.5 240 240c0 132.5 -107.5 240 -240 240c-132.5 0 -240 -107.5 -240 -240c0 -132.5 107.5 -240 240 -240z\" FillRule=\"EvenOdd\" />\r\n\t\t\t\t\t</Path.Data>\r\n\t\t\t\t</Path>\r\n\t\t\t\t<Path\r\n\t\t\t\t\txmlns:x=\"http://schemas.microsoft.com/winfx/2006/xaml\"\r\n\t\t\t\t\tFill=\"{StaticResource color-2}\"\r\n\t\t\t\t\tStrokeDashArray=\"\"\r\n\t\t\t\t\tStrokeEndLineCap=\"Flat\"\r\n\t\t\t\t\tStrokeLineJoin=\"Miter\"\r\n\t\t\t\t\tStrokeMiterLimit=\"10\"\r\n\t\t\t\t\tStrokeStartLineCap=\"Flat\"\r\n\t\t\t\t\tStrokeThickness=\"1\">\r\n\t\t\t\t\t<Path.Data>\r\n\t\t\t\t\t\t<PathGeometry Figures=\"M291 411l-69 0l0 -220l69 0z M218 134.5c0 -10 3.5 -18 10.5 -24.5c7 -6 16 -9 27.5 -9c11.5 0 20.5 3 27.5 9c7 6.5 10.5 14.5 10.5 24.5c0 9.5 -3.5 17.5 -10.5 24c-7 6 -16 9.5 -27.5 9.5c-11.5 0 -20.5 -3.5 -27.5 -9.5c-7 -6.5 -10.5 -14.5 -10.5 -24z\" FillRule=\"EvenOdd\" />\r\n\t\t\t\t\t</Path.Data>\r\n\t\t\t\t</Path>\r\n\t\t\t</Canvas>\r\n\t\t</Canvas>\r\n\t</Viewbox>\r\n\t<Viewbox\r\n\t\txmlns=\"http://schemas.microsoft.com/winfx/2006/xaml/presentation\"\r\n\t\tx:Key=\"Icon_InformationGrey\"\r\n\t\tx:Shared=\"False\"\r\n\t\tStretch=\"Uniform\"\r\n\t\tTag=\"SHA256_0DE51757DD8F0CA962BB1460C6B2E6436AB967B301715FFAADA6F0EC4103C57D\">\r\n\t\t<Canvas Width=\"512\" Height=\"512\">\r\n\t\t\t<Canvas.RenderTransform>\r\n\t\t\t\t<TranslateTransform X=\"0\" Y=\"0\" />\r\n\t\t\t</Canvas.RenderTransform>\r\n\t\t\t<Canvas.Resources>\r\n\t\t\t\t<RadialGradientBrush xmlns:x=\"http://schemas.microsoft.com/winfx/2006/xaml\" x:Key=\"_Radial1\" Center=\"0,0\" MappingMode=\"Absolute\" RadiusX=\"1\" RadiusY=\"1\">\r\n\t\t\t\t\t<RadialGradientBrush.GradientStops>\r\n\t\t\t\t\t\t<GradientStopCollection>\r\n\t\t\t\t\t\t\t<GradientStop Offset=\"0\" Color=\"#FFffffff\" />\r\n\t\t\t\t\t\t\t<GradientStop Offset=\"0.46\" Color=\"#FFffffff\" />\r\n\t\t\t\t\t\t\t<GradientStop Offset=\"1\" Color=\"#FFD8E0E8\" />\r\n\t\t\t\t\t\t</GradientStopCollection>\r\n\t\t\t\t\t</RadialGradientBrush.GradientStops>\r\n\t\t\t\t\t<RadialGradientBrush.Transform>\r\n\t\t\t\t\t\t<MatrixTransform Matrix=\"276.243 0 0 276.243 259.075 373.95\" />\r\n\t\t\t\t\t</RadialGradientBrush.Transform>\r\n\t\t\t\t</RadialGradientBrush>\r\n\t\t\t</Canvas.Resources>\r\n\t\t\t<!--  Unknown tag: metadata  -->\r\n\t\t\t<!--  Unknown tag: sodipodi:namedview  -->\r\n\t\t\t<Canvas>\r\n\t\t\t\t<Path\r\n\t\t\t\t\txmlns:x=\"http://schemas.microsoft.com/winfx/2006/xaml\"\r\n\t\t\t\t\tFill=\"#A0A0A0\"\r\n\t\t\t\t\tStrokeLineJoin=\"Round\"\r\n\t\t\t\t\tStrokeMiterLimit=\"2\">\r\n\t\t\t\t\t<Path.Data>\r\n\t\t\t\t\t\t<PathGeometry Figures=\"M256 16C388.5 16 496 123.5 496 256C496 388.5 388.5 496 256 496C123.5 496 16 388.5 16 256C16 123.5 123.5 16 256 16Z\" FillRule=\"EvenOdd\" />\r\n\t\t\t\t\t</Path.Data>\r\n\t\t\t\t</Path>\r\n\t\t\t\t<Path\r\n\t\t\t\t\txmlns:x=\"http://schemas.microsoft.com/winfx/2006/xaml\"\r\n\t\t\t\t\tFill=\"{StaticResource _Radial1}\"\r\n\t\t\t\t\tStrokeLineJoin=\"Round\"\r\n\t\t\t\t\tStrokeMiterLimit=\"2\">\r\n\t\t\t\t\t<Path.Data>\r\n\t\t\t\t\t\t<PathGeometry Figures=\"M291 411L222 411L222 191L291 191L291 411ZM218 134.5C218 124.5 221.5 116.5 228.5 110C235.5 104 244.5 101 256 101C267.5 101 276.5 104 283.5 110C290.5 116.5 294 124.5 294 134.5C294 144 290.5 152 283.5 158.5C276.5 164.5 267.5 168 256 168C244.5 168 235.5 164.5 228.5 158.5C221.5 152 218 144 218 134.5Z\" FillRule=\"EvenOdd\" />\r\n\t\t\t\t\t</Path.Data>\r\n\t\t\t\t</Path>\r\n\t\t\t</Canvas>\r\n\t\t</Canvas>\r\n\t</Viewbox>\r\n\t<Viewbox\r\n\t\txmlns=\"http://schemas.microsoft.com/winfx/2006/xaml/presentation\"\r\n\t\txmlns:p1=\"http://schemas.microsoft.com/winfx/2006/xaml\"\r\n\t\tp1:Key=\"Icon_Maximize\"\r\n\t\tp1:Shared=\"False\"\r\n\t\tStretch=\"Uniform\"\r\n\t\tTag=\"SHA256_61CCD8E6CE9761D2F2497F10DA23783A50EDDE47539EAD93E0CB2831C47CEEDC\">\r\n\t\t<Canvas Width=\"512\" Height=\"512\">\r\n\t\t\t<Canvas.RenderTransform>\r\n\t\t\t\t<TranslateTransform X=\"0\" Y=\"0\" />\r\n\t\t\t</Canvas.RenderTransform>\r\n\t\t\t<Canvas.Resources>\r\n\t\t\t\t<LinearGradientBrush xmlns:x=\"http://schemas.microsoft.com/winfx/2006/xaml\" x:Key=\"color-1\" MappingMode=\"Absolute\" StartPoint=\"32,32\" EndPoint=\"466,480\">\r\n\t\t\t\t\t<LinearGradientBrush.GradientStops>\r\n\t\t\t\t\t\t<GradientStopCollection>\r\n\t\t\t\t\t\t\t<GradientStop Offset=\"0\" Color=\"#4AB4F2\" />\r\n\t\t\t\t\t\t\t<GradientStop Offset=\"0.47843\" Color=\"#129EEE\" />\r\n\t\t\t\t\t\t\t<GradientStop Offset=\"1\" Color=\"#0E7AB7\" />\r\n\t\t\t\t\t\t</GradientStopCollection>\r\n\t\t\t\t\t</LinearGradientBrush.GradientStops>\r\n\t\t\t\t</LinearGradientBrush>\r\n\t\t\t\t<LinearGradientBrush xmlns:x=\"http://schemas.microsoft.com/winfx/2006/xaml\" x:Key=\"color-2\" MappingMode=\"Absolute\" StartPoint=\"64,64\" EndPoint=\"448,438\">\r\n\t\t\t\t\t<LinearGradientBrush.GradientStops>\r\n\t\t\t\t\t\t<GradientStopCollection>\r\n\t\t\t\t\t\t\t<GradientStop Offset=\"0\" Color=\"#EEF8FE\" />\r\n\t\t\t\t\t\t\t<GradientStop Offset=\"1\" Color=\"#C6E7FB\" />\r\n\t\t\t\t\t\t</GradientStopCollection>\r\n\t\t\t\t\t</LinearGradientBrush.GradientStops>\r\n\t\t\t\t</LinearGradientBrush>\r\n\t\t\t\t<LinearGradientBrush xmlns:x=\"http://schemas.microsoft.com/winfx/2006/xaml\" x:Key=\"color-3\" MappingMode=\"Absolute\" StartPoint=\"144,96\" EndPoint=\"288,240\">\r\n\t\t\t\t\t<LinearGradientBrush.GradientStops>\r\n\t\t\t\t\t\t<GradientStopCollection>\r\n\t\t\t\t\t\t\t<GradientStop Offset=\"0\" Color=\"#129EEE\" />\r\n\t\t\t\t\t\t\t<GradientStop Offset=\"1\" Color=\"#0E76B2\" />\r\n\t\t\t\t\t\t</GradientStopCollection>\r\n\t\t\t\t\t</LinearGradientBrush.GradientStops>\r\n\t\t\t\t</LinearGradientBrush>\r\n\t\t\t\t<LinearGradientBrush xmlns:x=\"http://schemas.microsoft.com/winfx/2006/xaml\" x:Key=\"color-4\" MappingMode=\"Absolute\" StartPoint=\"32,320\" EndPoint=\"192,480\">\r\n\t\t\t\t\t<LinearGradientBrush.GradientStops>\r\n\t\t\t\t\t\t<GradientStopCollection>\r\n\t\t\t\t\t\t\t<GradientStop Offset=\"0\" Color=\"#B3C0CD\" />\r\n\t\t\t\t\t\t\t<GradientStop Offset=\"1\" Color=\"#91A0AF\" />\r\n\t\t\t\t\t\t</GradientStopCollection>\r\n\t\t\t\t\t</LinearGradientBrush.GradientStops>\r\n\t\t\t\t</LinearGradientBrush>\r\n\t\t\t</Canvas.Resources>\r\n\t\t\t<!--  Unknown tag: metadata  -->\r\n\t\t\t<!--  Unknown tag: sodipodi:namedview  -->\r\n\t\t\t<Canvas>\r\n\t\t\t\t<Path\r\n\t\t\t\t\txmlns:x=\"http://schemas.microsoft.com/winfx/2006/xaml\"\r\n\t\t\t\t\tFill=\"{StaticResource color-1}\"\r\n\t\t\t\t\tStrokeDashArray=\"\"\r\n\t\t\t\t\tStrokeEndLineCap=\"Flat\"\r\n\t\t\t\t\tStrokeLineJoin=\"Miter\"\r\n\t\t\t\t\tStrokeMiterLimit=\"10\"\r\n\t\t\t\t\tStrokeStartLineCap=\"Flat\"\r\n\t\t\t\t\tStrokeThickness=\"1\">\r\n\t\t\t\t\t<Path.Data>\r\n\t\t\t\t\t\t<PathGeometry Figures=\"M32 32l448 0l0 448l-448 0z\" FillRule=\"EvenOdd\" />\r\n\t\t\t\t\t</Path.Data>\r\n\t\t\t\t</Path>\r\n\t\t\t\t<Path\r\n\t\t\t\t\txmlns:x=\"http://schemas.microsoft.com/winfx/2006/xaml\"\r\n\t\t\t\t\tFill=\"{StaticResource color-2}\"\r\n\t\t\t\t\tStrokeDashArray=\"\"\r\n\t\t\t\t\tStrokeEndLineCap=\"Flat\"\r\n\t\t\t\t\tStrokeLineJoin=\"Miter\"\r\n\t\t\t\t\tStrokeMiterLimit=\"10\"\r\n\t\t\t\t\tStrokeStartLineCap=\"Flat\"\r\n\t\t\t\t\tStrokeThickness=\"1\">\r\n\t\t\t\t\t<Path.Data>\r\n\t\t\t\t\t\t<PathGeometry Figures=\"M64 64l384 0l0 384l-384 0z\" FillRule=\"EvenOdd\" />\r\n\t\t\t\t\t</Path.Data>\r\n\t\t\t\t</Path>\r\n\t\t\t\t<Path\r\n\t\t\t\t\txmlns:x=\"http://schemas.microsoft.com/winfx/2006/xaml\"\r\n\t\t\t\t\tFill=\"{StaticResource color-3}\"\r\n\t\t\t\t\tStrokeDashArray=\"\"\r\n\t\t\t\t\tStrokeEndLineCap=\"Flat\"\r\n\t\t\t\t\tStrokeLineJoin=\"Miter\"\r\n\t\t\t\t\tStrokeMiterLimit=\"10\"\r\n\t\t\t\t\tStrokeStartLineCap=\"Flat\"\r\n\t\t\t\t\tStrokeThickness=\"1\">\r\n\t\t\t\t\t<Path.Data>\r\n\t\t\t\t\t\t<PathGeometry Figures=\"M192 365.5l178.5 -179l45.5 45.5l0 -136l-136 0l45.5 45.5l-179 178.5l-2.5 48z\" FillRule=\"EvenOdd\" />\r\n\t\t\t\t\t</Path.Data>\r\n\t\t\t\t</Path>\r\n\t\t\t\t<Path\r\n\t\t\t\t\txmlns:x=\"http://schemas.microsoft.com/winfx/2006/xaml\"\r\n\t\t\t\t\tFill=\"{StaticResource color-4}\"\r\n\t\t\t\t\tStrokeDashArray=\"\"\r\n\t\t\t\t\tStrokeEndLineCap=\"Flat\"\r\n\t\t\t\t\tStrokeLineJoin=\"Miter\"\r\n\t\t\t\t\tStrokeMiterLimit=\"10\"\r\n\t\t\t\t\tStrokeStartLineCap=\"Flat\"\r\n\t\t\t\t\tStrokeThickness=\"1\">\r\n\t\t\t\t\t<Path.Data>\r\n\t\t\t\t\t\t<PathGeometry Figures=\"M32 320l160 0l0 160l-160 0z\" FillRule=\"EvenOdd\" />\r\n\t\t\t\t\t</Path.Data>\r\n\t\t\t\t</Path>\r\n\t\t\t</Canvas>\r\n\t\t</Canvas>\r\n\t</Viewbox>\r\n\t<Viewbox\r\n\t\txmlns=\"http://schemas.microsoft.com/winfx/2006/xaml/presentation\"\r\n\t\txmlns:p1=\"http://schemas.microsoft.com/winfx/2006/xaml\"\r\n\t\tp1:Key=\"Icon_MediaPlay\"\r\n\t\tp1:Shared=\"False\"\r\n\t\tStretch=\"Uniform\"\r\n\t\tTag=\"SHA256_6350D9BADE111BC4E64CF281A8B253051545623D47C75123226C752A74A7468D\">\r\n\t\t<Canvas Width=\"24\" Height=\"24\">\r\n\t\t\t<Canvas.RenderTransform>\r\n\t\t\t\t<TranslateTransform X=\"0\" Y=\"0\" />\r\n\t\t\t</Canvas.RenderTransform>\r\n\t\t\t<Canvas.Resources />\r\n\t\t\t<!--  Unknown tag: metadata  -->\r\n\t\t\t<!--  Unknown tag: sodipodi:namedview  -->\r\n\t\t\t<Canvas>\r\n\t\t\t\t<Canvas.RenderTransform>\r\n\t\t\t\t\t<MatrixTransform Matrix=\"1.17543 0 0 1.17543 -2.32536 -2.11196\" />\r\n\t\t\t\t</Canvas.RenderTransform>\r\n\t\t\t\t<Canvas>\r\n\t\t\t\t\t<Path\r\n\t\t\t\t\t\txmlns:x=\"http://schemas.microsoft.com/winfx/2006/xaml\"\r\n\t\t\t\t\t\tFill=\"#ffffff\"\r\n\t\t\t\t\t\tStrokeLineJoin=\"Round\"\r\n\t\t\t\t\t\tStrokeMiterLimit=\"2\">\r\n\t\t\t\t\t\t<Path.Data>\r\n\t\t\t\t\t\t\t<PathGeometry Figures=\"M9.581 7.81L17.288 11.681C17.582 11.829 17.582 12.248 17.288 12.396L9.581 16.267C9.315 16.4 9.001 16.207 9.001 15.909L9.001 8.168C9.001 7.87 9.315 7.677 9.581 7.81Z\" FillRule=\"EvenOdd\" />\r\n\t\t\t\t\t\t</Path.Data>\r\n\t\t\t\t\t</Path>\r\n\t\t\t\t</Canvas>\r\n\t\t\t</Canvas>\r\n\t\t</Canvas>\r\n\t</Viewbox>\r\n\t<Viewbox\r\n\t\txmlns=\"http://schemas.microsoft.com/winfx/2006/xaml/presentation\"\r\n\t\txmlns:p1=\"http://schemas.microsoft.com/winfx/2006/xaml\"\r\n\t\tp1:Key=\"Icon_MediaStop\"\r\n\t\tp1:Shared=\"False\"\r\n\t\tStretch=\"Uniform\"\r\n\t\tTag=\"SHA256_F5CACA7A7169E0D43CE3B6D9BDCEE0D8B46FA2C818CDF944D917893D7CC7390D\">\r\n\t\t<Canvas Width=\"24\" Height=\"24\">\r\n\t\t\t<Canvas.RenderTransform>\r\n\t\t\t\t<TranslateTransform X=\"0\" Y=\"0\" />\r\n\t\t\t</Canvas.RenderTransform>\r\n\t\t\t<Canvas.Resources />\r\n\t\t\t<!--  Unknown tag: metadata  -->\r\n\t\t\t<!--  Unknown tag: sodipodi:namedview  -->\r\n\t\t\t<Canvas>\r\n\t\t\t\t<Canvas.RenderTransform>\r\n\t\t\t\t\t<MatrixTransform Matrix=\"1.25 0 0 1.25 -3 -3\" />\r\n\t\t\t\t</Canvas.RenderTransform>\r\n\t\t\t\t<Canvas>\r\n\t\t\t\t\t<Path\r\n\t\t\t\t\t\txmlns:x=\"http://schemas.microsoft.com/winfx/2006/xaml\"\r\n\t\t\t\t\t\tFill=\"#ffffff\"\r\n\t\t\t\t\t\tStrokeLineJoin=\"Round\"\r\n\t\t\t\t\t\tStrokeMiterLimit=\"2\">\r\n\t\t\t\t\t\t<Path.Data>\r\n\t\t\t\t\t\t\t<PathGeometry Figures=\"M15.6 16L8.4 16C8.179 16 8 15.821 8 15.6L8 8.4C8 8.179 8.179 8 8.4 8L15.6 8C15.821 8 16 8.179 16 8.4L16 15.6C16 15.821 15.821 16 15.6 16Z\" FillRule=\"NonZero\" />\r\n\t\t\t\t\t\t</Path.Data>\r\n\t\t\t\t\t</Path>\r\n\t\t\t\t</Canvas>\r\n\t\t\t</Canvas>\r\n\t\t</Canvas>\r\n\t</Viewbox>\r\n\t<Viewbox\r\n\t\txmlns=\"http://schemas.microsoft.com/winfx/2006/xaml/presentation\"\r\n\t\txmlns:p1=\"http://schemas.microsoft.com/winfx/2006/xaml\"\r\n\t\tp1:Key=\"Icon_Minimize\"\r\n\t\tp1:Shared=\"False\"\r\n\t\tStretch=\"Uniform\"\r\n\t\tTag=\"SHA256_F1ADF1DD4793E8F9E60E34B70F8FC4BF72D06B783959F259FF5CF7F4F9811D21\">\r\n\t\t<Canvas Width=\"512\" Height=\"512\">\r\n\t\t\t<Canvas.RenderTransform>\r\n\t\t\t\t<TranslateTransform X=\"0\" Y=\"0\" />\r\n\t\t\t</Canvas.RenderTransform>\r\n\t\t\t<Canvas.Resources>\r\n\t\t\t\t<LinearGradientBrush xmlns:x=\"http://schemas.microsoft.com/winfx/2006/xaml\" x:Key=\"color-1\" MappingMode=\"Absolute\" StartPoint=\"32,32\" EndPoint=\"480,480\">\r\n\t\t\t\t\t<LinearGradientBrush.GradientStops>\r\n\t\t\t\t\t\t<GradientStopCollection>\r\n\t\t\t\t\t\t\t<GradientStop Offset=\"0\" Color=\"#B3C0CD\" />\r\n\t\t\t\t\t\t\t<GradientStop Offset=\"1\" Color=\"#91A0AF\" />\r\n\t\t\t\t\t\t</GradientStopCollection>\r\n\t\t\t\t\t</LinearGradientBrush.GradientStops>\r\n\t\t\t\t</LinearGradientBrush>\r\n\t\t\t\t<LinearGradientBrush xmlns:x=\"http://schemas.microsoft.com/winfx/2006/xaml\" x:Key=\"color-2\" MappingMode=\"Absolute\" StartPoint=\"64,64\" EndPoint=\"448,438\">\r\n\t\t\t\t\t<LinearGradientBrush.GradientStops>\r\n\t\t\t\t\t\t<GradientStopCollection>\r\n\t\t\t\t\t\t\t<GradientStop Offset=\"0\" Color=\"#EEF8FE\" />\r\n\t\t\t\t\t\t\t<GradientStop Offset=\"1\" Color=\"#C6E7FB\" />\r\n\t\t\t\t\t\t</GradientStopCollection>\r\n\t\t\t\t\t</LinearGradientBrush.GradientStops>\r\n\t\t\t\t</LinearGradientBrush>\r\n\t\t\t\t<LinearGradientBrush xmlns:x=\"http://schemas.microsoft.com/winfx/2006/xaml\" x:Key=\"color-3\" MappingMode=\"Absolute\" StartPoint=\"32,320\" EndPoint=\"188,480\">\r\n\t\t\t\t\t<LinearGradientBrush.GradientStops>\r\n\t\t\t\t\t\t<GradientStopCollection>\r\n\t\t\t\t\t\t\t<GradientStop Offset=\"0\" Color=\"#4AB5F2\" />\r\n\t\t\t\t\t\t\t<GradientStop Offset=\"0.54902\" Color=\"#129EEE\" />\r\n\t\t\t\t\t\t\t<GradientStop Offset=\"1\" Color=\"#1089CF\" />\r\n\t\t\t\t\t\t</GradientStopCollection>\r\n\t\t\t\t\t</LinearGradientBrush.GradientStops>\r\n\t\t\t\t</LinearGradientBrush>\r\n\t\t\t\t<LinearGradientBrush xmlns:x=\"http://schemas.microsoft.com/winfx/2006/xaml\" x:Key=\"color-4\" MappingMode=\"Absolute\" StartPoint=\"192,64\" EndPoint=\"328,200\">\r\n\t\t\t\t\t<LinearGradientBrush.GradientStops>\r\n\t\t\t\t\t\t<GradientStopCollection>\r\n\t\t\t\t\t\t\t<GradientStop Offset=\"0\" Color=\"#129EEE\" />\r\n\t\t\t\t\t\t\t<GradientStop Offset=\"1\" Color=\"#0E76B2\" />\r\n\t\t\t\t\t\t</GradientStopCollection>\r\n\t\t\t\t\t</LinearGradientBrush.GradientStops>\r\n\t\t\t\t</LinearGradientBrush>\r\n\t\t\t</Canvas.Resources>\r\n\t\t\t<!--  Unknown tag: metadata  -->\r\n\t\t\t<!--  Unknown tag: sodipodi:namedview  -->\r\n\t\t\t<Canvas>\r\n\t\t\t\t<Path\r\n\t\t\t\t\txmlns:x=\"http://schemas.microsoft.com/winfx/2006/xaml\"\r\n\t\t\t\t\tFill=\"{StaticResource color-1}\"\r\n\t\t\t\t\tStrokeDashArray=\"\"\r\n\t\t\t\t\tStrokeEndLineCap=\"Flat\"\r\n\t\t\t\t\tStrokeLineJoin=\"Miter\"\r\n\t\t\t\t\tStrokeMiterLimit=\"10\"\r\n\t\t\t\t\tStrokeStartLineCap=\"Flat\"\r\n\t\t\t\t\tStrokeThickness=\"1\">\r\n\t\t\t\t\t<Path.Data>\r\n\t\t\t\t\t\t<PathGeometry Figures=\"M32 32l448 0l0 448l-448 0z\" FillRule=\"EvenOdd\" />\r\n\t\t\t\t\t</Path.Data>\r\n\t\t\t\t</Path>\r\n\t\t\t\t<Path\r\n\t\t\t\t\txmlns:x=\"http://schemas.microsoft.com/winfx/2006/xaml\"\r\n\t\t\t\t\tFill=\"{StaticResource color-2}\"\r\n\t\t\t\t\tStrokeDashArray=\"\"\r\n\t\t\t\t\tStrokeEndLineCap=\"Flat\"\r\n\t\t\t\t\tStrokeLineJoin=\"Miter\"\r\n\t\t\t\t\tStrokeMiterLimit=\"10\"\r\n\t\t\t\t\tStrokeStartLineCap=\"Flat\"\r\n\t\t\t\t\tStrokeThickness=\"1\">\r\n\t\t\t\t\t<Path.Data>\r\n\t\t\t\t\t\t<PathGeometry Figures=\"M64 64l384 0l0 384l-384 0z\" FillRule=\"EvenOdd\" />\r\n\t\t\t\t\t</Path.Data>\r\n\t\t\t\t</Path>\r\n\t\t\t\t<Path\r\n\t\t\t\t\txmlns:x=\"http://schemas.microsoft.com/winfx/2006/xaml\"\r\n\t\t\t\t\tFill=\"{StaticResource color-3}\"\r\n\t\t\t\t\tStrokeDashArray=\"\"\r\n\t\t\t\t\tStrokeEndLineCap=\"Flat\"\r\n\t\t\t\t\tStrokeLineJoin=\"Miter\"\r\n\t\t\t\t\tStrokeMiterLimit=\"10\"\r\n\t\t\t\t\tStrokeStartLineCap=\"Flat\"\r\n\t\t\t\t\tStrokeThickness=\"1\">\r\n\t\t\t\t\t<Path.Data>\r\n\t\t\t\t\t\t<PathGeometry Figures=\"M32 320l160 0l0 160l-160 0z\" FillRule=\"EvenOdd\" />\r\n\t\t\t\t\t</Path.Data>\r\n\t\t\t\t</Path>\r\n\t\t\t\t<Path\r\n\t\t\t\t\txmlns:x=\"http://schemas.microsoft.com/winfx/2006/xaml\"\r\n\t\t\t\t\tFill=\"{StaticResource color-4}\"\r\n\t\t\t\t\tStrokeDashArray=\"\"\r\n\t\t\t\t\tStrokeEndLineCap=\"Flat\"\r\n\t\t\t\t\tStrokeLineJoin=\"Miter\"\r\n\t\t\t\t\tStrokeMiterLimit=\"10\"\r\n\t\t\t\t\tStrokeStartLineCap=\"Flat\"\r\n\t\t\t\t\tStrokeThickness=\"1\">\r\n\t\t\t\t\t<Path.Data>\r\n\t\t\t\t\t\t<PathGeometry Figures=\"M448 109.5l-165.5 165l45.5 45.5l-136 0l0 -136l45.5 45.5l165 -165.5l45.5 0z\" FillRule=\"EvenOdd\" />\r\n\t\t\t\t\t</Path.Data>\r\n\t\t\t\t</Path>\r\n\t\t\t</Canvas>\r\n\t\t</Canvas>\r\n\t</Viewbox>\r\n\t<Viewbox\r\n\t\txmlns=\"http://schemas.microsoft.com/winfx/2006/xaml/presentation\"\r\n\t\txmlns:p1=\"http://schemas.microsoft.com/winfx/2006/xaml\"\r\n\t\tp1:Key=\"Icon_OK\"\r\n\t\tp1:Shared=\"False\"\r\n\t\tStretch=\"Uniform\"\r\n\t\tTag=\"SHA256_A3D5DABADBFB954A13CBF81EF88CCF0E588E61D2B9B300F73A439FE918646BF1\">\r\n\t\t<Canvas Width=\"512\" Height=\"512\">\r\n\t\t\t<Canvas.RenderTransform>\r\n\t\t\t\t<TranslateTransform X=\"0\" Y=\"0\" />\r\n\t\t\t</Canvas.RenderTransform>\r\n\t\t\t<Canvas.Resources>\r\n\t\t\t\t<RadialGradientBrush xmlns:x=\"http://schemas.microsoft.com/winfx/2006/xaml\" x:Key=\"color-1\" Center=\"206,327\" GradientOrigin=\"206,327\" MappingMode=\"Absolute\" RadiusX=\"325.60929\" RadiusY=\"325.60929\">\r\n\t\t\t\t\t<RadialGradientBrush.GradientStops>\r\n\t\t\t\t\t\t<GradientStopCollection>\r\n\t\t\t\t\t\t\t<GradientStop Offset=\"0\" Color=\"#FFFFFF\" />\r\n\t\t\t\t\t\t\t<GradientStop Offset=\"0.45882\" Color=\"#FFFFFF\" />\r\n\t\t\t\t\t\t\t<GradientStop Offset=\"1\" Color=\"#D8E0E8\" />\r\n\t\t\t\t\t\t</GradientStopCollection>\r\n\t\t\t\t\t</RadialGradientBrush.GradientStops>\r\n\t\t\t\t</RadialGradientBrush>\r\n\t\t\t</Canvas.Resources>\r\n\t\t\t<!--  Unknown tag: metadata  -->\r\n\t\t\t<!--  Unknown tag: sodipodi:namedview  -->\r\n\t\t\t<Canvas>\r\n\t\t\t\t<Path\r\n\t\t\t\t\txmlns:x=\"http://schemas.microsoft.com/winfx/2006/xaml\"\r\n\t\t\t\t\tFill=\"#FF59A31D\"\r\n\t\t\t\t\tStrokeDashArray=\"\"\r\n\t\t\t\t\tStrokeEndLineCap=\"Flat\"\r\n\t\t\t\t\tStrokeLineJoin=\"Miter\"\r\n\t\t\t\t\tStrokeMiterLimit=\"10\"\r\n\t\t\t\t\tStrokeStartLineCap=\"Flat\"\r\n\t\t\t\t\tStrokeThickness=\"1\">\r\n\t\t\t\t\t<Path.Data>\r\n\t\t\t\t\t\t<PathGeometry Figures=\"M256 16c132.5 0 240 107.5 240 240c0 132.5 -107.5 240 -240 240c-132.5 0 -240 -107.5 -240 -240c0 -132.5 107.5 -240 240 -240z\" FillRule=\"EvenOdd\" />\r\n\t\t\t\t\t</Path.Data>\r\n\t\t\t\t</Path>\r\n\t\t\t\t<Path\r\n\t\t\t\t\txmlns:x=\"http://schemas.microsoft.com/winfx/2006/xaml\"\r\n\t\t\t\t\tFill=\"{StaticResource color-1}\"\r\n\t\t\t\t\tStrokeDashArray=\"\"\r\n\t\t\t\t\tStrokeEndLineCap=\"Flat\"\r\n\t\t\t\t\tStrokeLineJoin=\"Miter\"\r\n\t\t\t\t\tStrokeMiterLimit=\"10\"\r\n\t\t\t\t\tStrokeStartLineCap=\"Flat\"\r\n\t\t\t\t\tStrokeThickness=\"1\">\r\n\t\t\t\t\t<Path.Data>\r\n\t\t\t\t\t\t<PathGeometry Figures=\"M381 97.5l56.5 56.5l-237.5 238l-136 -136l56.5 -56.5l79.5 79z\" FillRule=\"EvenOdd\" />\r\n\t\t\t\t\t</Path.Data>\r\n\t\t\t\t</Path>\r\n\t\t\t</Canvas>\r\n\t\t</Canvas>\r\n\t</Viewbox>\r\n\t<Viewbox\r\n\t\txmlns=\"http://schemas.microsoft.com/winfx/2006/xaml/presentation\"\r\n\t\txmlns:p1=\"http://schemas.microsoft.com/winfx/2006/xaml\"\r\n\t\tp1:Key=\"Icon_Play\"\r\n\t\tp1:Shared=\"False\"\r\n\t\tStretch=\"Uniform\"\r\n\t\tTag=\"SHA256_98E24DAB9344906BD16A3D0C0989EC738DB69222FA95725DF876C5BAA28E8F3E\">\r\n\t\t<Canvas Width=\"512\" Height=\"512\">\r\n\t\t\t<Canvas.RenderTransform>\r\n\t\t\t\t<TranslateTransform X=\"0\" Y=\"0\" />\r\n\t\t\t</Canvas.RenderTransform>\r\n\t\t\t<Canvas.Resources>\r\n\t\t\t\t<LinearGradientBrush xmlns:x=\"http://schemas.microsoft.com/winfx/2006/xaml\" x:Key=\"color-1\" MappingMode=\"Absolute\" StartPoint=\"64,32\" EndPoint=\"427,242\">\r\n\t\t\t\t\t<LinearGradientBrush.GradientStops>\r\n\t\t\t\t\t\t<GradientStopCollection>\r\n\t\t\t\t\t\t\t<GradientStop Offset=\"0\" Color=\"#4EB6F2\" />\r\n\t\t\t\t\t\t\t<GradientStop Offset=\"0.5098\" Color=\"#129EEE\" />\r\n\t\t\t\t\t\t\t<GradientStop Offset=\"1\" Color=\"#0E76B2\" />\r\n\t\t\t\t\t\t</GradientStopCollection>\r\n\t\t\t\t\t</LinearGradientBrush.GradientStops>\r\n\t\t\t\t</LinearGradientBrush>\r\n\t\t\t</Canvas.Resources>\r\n\t\t\t<!--  Unknown tag: metadata  -->\r\n\t\t\t<!--  Unknown tag: sodipodi:namedview  -->\r\n\t\t\t<Canvas>\r\n\t\t\t\t<Path\r\n\t\t\t\t\txmlns:x=\"http://schemas.microsoft.com/winfx/2006/xaml\"\r\n\t\t\t\t\tFill=\"{StaticResource color-1}\"\r\n\t\t\t\t\tStrokeDashArray=\"\"\r\n\t\t\t\t\tStrokeEndLineCap=\"Flat\"\r\n\t\t\t\t\tStrokeLineJoin=\"Miter\"\r\n\t\t\t\t\tStrokeMiterLimit=\"10\"\r\n\t\t\t\t\tStrokeStartLineCap=\"Flat\"\r\n\t\t\t\t\tStrokeThickness=\"1\">\r\n\t\t\t\t\t<Path.Data>\r\n\t\t\t\t\t\t<PathGeometry Figures=\"M88 34l354 208c5 3 8 8 8 14c0 6 -3 11 -8 14l-354 208c-5 3 -11 3 -16 0c-5 -2.5 -8 -8 -8 -13.5l0 -208.5l0 -208.5c0 -5.5 3 -10.5 8 -13.5c5 -3 11 -3 16 0z\" FillRule=\"EvenOdd\" />\r\n\t\t\t\t\t</Path.Data>\r\n\t\t\t\t</Path>\r\n\t\t\t</Canvas>\r\n\t\t</Canvas>\r\n\t</Viewbox>\r\n\t<Viewbox\r\n\t\txmlns=\"http://schemas.microsoft.com/winfx/2006/xaml/presentation\"\r\n\t\tx:Key=\"Icon_ProcessLeft\"\r\n\t\tx:Shared=\"False\"\r\n\t\tStretch=\"Uniform\"\r\n\t\tTag=\"SHA256_E72C9FA22F7EB60AFC3BBA4374882AFBE6F06A2097D0A1EDE7AC0BBB00C090AF\">\r\n\t\t<Canvas Width=\"512\" Height=\"512\">\r\n\t\t\t<Canvas.RenderTransform>\r\n\t\t\t\t<TranslateTransform X=\"0\" Y=\"0\" />\r\n\t\t\t</Canvas.RenderTransform>\r\n\t\t\t<Canvas.Resources>\r\n\t\t\t\t<LinearGradientBrush xmlns:x=\"http://schemas.microsoft.com/winfx/2006/xaml\" x:Key=\"color-1\" MappingMode=\"Absolute\" StartPoint=\"292,59\" EndPoint=\"476,323\">\r\n\t\t\t\t\t<LinearGradientBrush.GradientStops>\r\n\t\t\t\t\t\t<GradientStopCollection>\r\n\t\t\t\t\t\t\t<GradientStop Offset=\"0\" Color=\"#FFC001\" />\r\n\t\t\t\t\t\t\t<GradientStop Offset=\"0.72157\" Color=\"#F3B401\" />\r\n\t\t\t\t\t\t\t<GradientStop Offset=\"1\" Color=\"#E6A801\" />\r\n\t\t\t\t\t\t</GradientStopCollection>\r\n\t\t\t\t\t</LinearGradientBrush.GradientStops>\r\n\t\t\t\t</LinearGradientBrush>\r\n\t\t\t\t<LinearGradientBrush xmlns:x=\"http://schemas.microsoft.com/winfx/2006/xaml\" x:Key=\"color-2\" MappingMode=\"Absolute\" StartPoint=\"18,16\" EndPoint=\"217,303\">\r\n\t\t\t\t\t<LinearGradientBrush.GradientStops>\r\n\t\t\t\t\t\t<GradientStopCollection>\r\n\t\t\t\t\t\t\t<GradientStop Offset=\"0\" Color=\"#6CC723\" />\r\n\t\t\t\t\t\t\t<GradientStop Offset=\"1\" Color=\"#61B11F\" />\r\n\t\t\t\t\t\t</GradientStopCollection>\r\n\t\t\t\t\t</LinearGradientBrush.GradientStops>\r\n\t\t\t\t</LinearGradientBrush>\r\n\t\t\t\t<LinearGradientBrush xmlns:x=\"http://schemas.microsoft.com/winfx/2006/xaml\" x:Key=\"color-3\" MappingMode=\"Absolute\" StartPoint=\"16,231\" EndPoint=\"201,496\">\r\n\t\t\t\t\t<LinearGradientBrush.GradientStops>\r\n\t\t\t\t\t\t<GradientStopCollection>\r\n\t\t\t\t\t\t\t<GradientStop Offset=\"0\" Color=\"#39AEF1\" />\r\n\t\t\t\t\t\t\t<GradientStop Offset=\"1\" Color=\"#24A5EF\" />\r\n\t\t\t\t\t\t</GradientStopCollection>\r\n\t\t\t\t\t</LinearGradientBrush.GradientStops>\r\n\t\t\t\t</LinearGradientBrush>\r\n\t\t\t</Canvas.Resources>\r\n\t\t\t<!--  Unknown tag: metadata  -->\r\n\t\t\t<!--  Unknown tag: sodipodi:namedview  -->\r\n\t\t\t<Canvas>\r\n\t\t\t\t<Path\r\n\t\t\t\t\txmlns:x=\"http://schemas.microsoft.com/winfx/2006/xaml\"\r\n\t\t\t\t\tFill=\"{StaticResource color-1}\"\r\n\t\t\t\t\tStrokeDashArray=\"\"\r\n\t\t\t\t\tStrokeEndLineCap=\"Flat\"\r\n\t\t\t\t\tStrokeLineJoin=\"Miter\"\r\n\t\t\t\t\tStrokeMiterLimit=\"10\"\r\n\t\t\t\t\tStrokeStartLineCap=\"Flat\"\r\n\t\t\t\t\tStrokeThickness=\"1\">\r\n\t\t\t\t\t<Path.Data>\r\n\t\t\t\t\t\t<PathGeometry Figures=\"M348 475l23.5 -88l-79.5 -22l3 -4c43 -16 73 -57 73 -105c0 -34.5 -20 -62 -40.5 -86.5l-7.5 -8.5l-20.5 -77.5l89 -24.5l8.5 3c60 43.5 99 114.5 99 194c0 98 -58.5 182 -142.5 219z\" FillRule=\"EvenOdd\" />\r\n\t\t\t\t\t</Path.Data>\r\n\t\t\t\t</Path>\r\n\t\t\t\t<Path\r\n\t\t\t\t\txmlns:x=\"http://schemas.microsoft.com/winfx/2006/xaml\"\r\n\t\t\t\t\tFill=\"{StaticResource color-2}\"\r\n\t\t\t\t\tStrokeDashArray=\"\"\r\n\t\t\t\t\tStrokeEndLineCap=\"Flat\"\r\n\t\t\t\t\tStrokeLineJoin=\"Miter\"\r\n\t\t\t\t\tStrokeMiterLimit=\"10\"\r\n\t\t\t\t\tStrokeStartLineCap=\"Flat\"\r\n\t\t\t\t\tStrokeThickness=\"1\">\r\n\t\t\t\t\t<Path.Data>\r\n\t\t\t\t\t\t<PathGeometry Figures=\"M17.5 231l0 0c7.5 -74 49.5 -143 118.5 -183c84.5 -48.5 187 -40 261 14l-91.5 25l22 82.5c-35.5 -29 -86 -34.5 -127.5 -10.5c-30 17.5 -49 46.5 -54.5 78.5l-3 10.5l-58 55l-66 -65z\" FillRule=\"EvenOdd\" />\r\n\t\t\t\t\t</Path.Data>\r\n\t\t\t\t</Path>\r\n\t\t\t\t<Path\r\n\t\t\t\t\txmlns:x=\"http://schemas.microsoft.com/winfx/2006/xaml\"\r\n\t\t\t\t\tFill=\"{StaticResource color-3}\"\r\n\t\t\t\t\tStrokeDashArray=\"\"\r\n\t\t\t\t\tStrokeEndLineCap=\"Flat\"\r\n\t\t\t\t\tStrokeLineJoin=\"Miter\"\r\n\t\t\t\t\tStrokeMiterLimit=\"10\"\r\n\t\t\t\t\tStrokeStartLineCap=\"Flat\"\r\n\t\t\t\t\tStrokeThickness=\"1\">\r\n\t\t\t\t\t<Path.Data>\r\n\t\t\t\t\t\t<PathGeometry Figures=\"M295 361l82.5 22.5l-24 91.5c-67.5 30.5 -148.5 29 -217.5 -11c-84.5 -49 -128.5 -142 -118.5 -233l67 66.5l61 -60c-7.5 45 13 91.5 54.5 115.5c30 17.5 65 19 95 8z\" FillRule=\"EvenOdd\" />\r\n\t\t\t\t\t</Path.Data>\r\n\t\t\t\t</Path>\r\n\t\t\t</Canvas>\r\n\t\t</Canvas>\r\n\t</Viewbox>\r\n\t<Viewbox\r\n\t\txmlns=\"http://schemas.microsoft.com/winfx/2006/xaml/presentation\"\r\n\t\txmlns:p1=\"http://schemas.microsoft.com/winfx/2006/xaml\"\r\n\t\tp1:Key=\"Icon_ProcessRight\"\r\n\t\tp1:Shared=\"False\"\r\n\t\tStretch=\"Uniform\"\r\n\t\tTag=\"SHA256_E4A7FD54E74EA79FCD5BC6694CF7B01835E0B7B3B8A9C2854CD237D8CDD5786E\">\r\n\t\t<Canvas Width=\"512\" Height=\"512\">\r\n\t\t\t<Canvas.RenderTransform>\r\n\t\t\t\t<TranslateTransform X=\"0\" Y=\"0\" />\r\n\t\t\t</Canvas.RenderTransform>\r\n\t\t\t<Canvas.Resources>\r\n\t\t\t\t<LinearGradientBrush xmlns:x=\"http://schemas.microsoft.com/winfx/2006/xaml\" x:Key=\"_Linear1\" MappingMode=\"Absolute\" StartPoint=\"0,0\" EndPoint=\"1,0\">\r\n\t\t\t\t\t<LinearGradientBrush.GradientStops>\r\n\t\t\t\t\t\t<GradientStopCollection>\r\n\t\t\t\t\t\t\t<GradientStop Offset=\"0\" Color=\"#FFFFC001\" />\r\n\t\t\t\t\t\t\t<GradientStop Offset=\"0.72\" Color=\"#FFF3B401\" />\r\n\t\t\t\t\t\t\t<GradientStop Offset=\"1\" Color=\"#FFE6A801\" />\r\n\t\t\t\t\t\t</GradientStopCollection>\r\n\t\t\t\t\t</LinearGradientBrush.GradientStops>\r\n\t\t\t\t\t<LinearGradientBrush.Transform>\r\n\t\t\t\t\t\t<MatrixTransform Matrix=\"183.574 263.887 -263.887 183.574 292 59\" />\r\n\t\t\t\t\t</LinearGradientBrush.Transform>\r\n\t\t\t\t</LinearGradientBrush>\r\n\t\t\t\t<LinearGradientBrush xmlns:x=\"http://schemas.microsoft.com/winfx/2006/xaml\" x:Key=\"_Linear2\" MappingMode=\"Absolute\" StartPoint=\"0,0\" EndPoint=\"1,0\">\r\n\t\t\t\t\t<LinearGradientBrush.GradientStops>\r\n\t\t\t\t\t\t<GradientStopCollection>\r\n\t\t\t\t\t\t\t<GradientStop Offset=\"0\" Color=\"#FF6CC723\" />\r\n\t\t\t\t\t\t\t<GradientStop Offset=\"1\" Color=\"#FF61B11F\" />\r\n\t\t\t\t\t\t</GradientStopCollection>\r\n\t\t\t\t\t</LinearGradientBrush.GradientStops>\r\n\t\t\t\t\t<LinearGradientBrush.Transform>\r\n\t\t\t\t\t\t<MatrixTransform Matrix=\"199.713 287.088 -287.088 199.713 17.5 16.1014\" />\r\n\t\t\t\t\t</LinearGradientBrush.Transform>\r\n\t\t\t\t</LinearGradientBrush>\r\n\t\t\t\t<LinearGradientBrush xmlns:x=\"http://schemas.microsoft.com/winfx/2006/xaml\" x:Key=\"_Linear3\" MappingMode=\"Absolute\" StartPoint=\"0,0\" EndPoint=\"1,0\">\r\n\t\t\t\t\t<LinearGradientBrush.GradientStops>\r\n\t\t\t\t\t\t<GradientStopCollection>\r\n\t\t\t\t\t\t\t<GradientStop Offset=\"0\" Color=\"#FF39AEF1\" />\r\n\t\t\t\t\t\t\t<GradientStop Offset=\"1\" Color=\"#FF24A5EF\" />\r\n\t\t\t\t\t\t</GradientStopCollection>\r\n\t\t\t\t\t</LinearGradientBrush.GradientStops>\r\n\t\t\t\t\t<LinearGradientBrush.Transform>\r\n\t\t\t\t\t\t<MatrixTransform Matrix=\"184.442 265.137 -265.137 184.442 16.1002 231\" />\r\n\t\t\t\t\t</LinearGradientBrush.Transform>\r\n\t\t\t\t</LinearGradientBrush>\r\n\t\t\t</Canvas.Resources>\r\n\t\t\t<!--  Unknown tag: metadata  -->\r\n\t\t\t<!--  Unknown tag: sodipodi:namedview  -->\r\n\t\t\t<Canvas>\r\n\t\t\t\t<Canvas.RenderTransform>\r\n\t\t\t\t\t<MatrixTransform Matrix=\"-1 0 0 1 512.1 0\" />\r\n\t\t\t\t</Canvas.RenderTransform>\r\n\t\t\t\t<Path\r\n\t\t\t\t\txmlns:x=\"http://schemas.microsoft.com/winfx/2006/xaml\"\r\n\t\t\t\t\tFill=\"{StaticResource _Linear1}\"\r\n\t\t\t\t\tStrokeLineJoin=\"Round\"\r\n\t\t\t\t\tStrokeMiterLimit=\"2\">\r\n\t\t\t\t\t<Path.Data>\r\n\t\t\t\t\t\t<PathGeometry Figures=\"M348 475L371.5 387L292 365L295 361C338 345 368 304 368 256C368 221.5 348 194 327.5 169.5L320 161L299.5 83.5L388.5 59L397 62C457 105.5 496 176.5 496 256C496 354 437.5 438 353.5 475L348 475Z\" FillRule=\"EvenOdd\" />\r\n\t\t\t\t\t</Path.Data>\r\n\t\t\t\t</Path>\r\n\t\t\t\t<Path\r\n\t\t\t\t\txmlns:x=\"http://schemas.microsoft.com/winfx/2006/xaml\"\r\n\t\t\t\t\tFill=\"{StaticResource _Linear2}\"\r\n\t\t\t\t\tStrokeLineJoin=\"Round\"\r\n\t\t\t\t\tStrokeMiterLimit=\"2\">\r\n\t\t\t\t\t<Path.Data>\r\n\t\t\t\t\t\t<PathGeometry Figures=\"M17.5 231C25 157 67 88 136 48C220.5 -0.5 323 8 397 62L305.5 87L327.5 169.5C292 140.5 241.5 135 200 159C170 176.5 151 205.5 145.5 237.5L142.5 248L84.5 303L18.5 238L17.5 231Z\" FillRule=\"EvenOdd\" />\r\n\t\t\t\t\t</Path.Data>\r\n\t\t\t\t</Path>\r\n\t\t\t\t<Path\r\n\t\t\t\t\txmlns:x=\"http://schemas.microsoft.com/winfx/2006/xaml\"\r\n\t\t\t\t\tFill=\"{StaticResource _Linear3}\"\r\n\t\t\t\t\tStrokeLineJoin=\"Round\"\r\n\t\t\t\t\tStrokeMiterLimit=\"2\">\r\n\t\t\t\t\t<Path.Data>\r\n\t\t\t\t\t\t<PathGeometry Figures=\"M295 361L377.5 383.5L353.5 475C286 505.5 205 504 136 464C51.5 415 7.5 322 17.5 231L84.5 297.5L145.5 237.5C138 282.5 158.5 329 200 353C230 370.5 265 372 295 361Z\" FillRule=\"EvenOdd\" />\r\n\t\t\t\t\t</Path.Data>\r\n\t\t\t\t</Path>\r\n\t\t\t</Canvas>\r\n\t\t</Canvas>\r\n\t</Viewbox>\r\n\t<Viewbox\r\n\t\txmlns=\"http://schemas.microsoft.com/winfx/2006/xaml/presentation\"\r\n\t\txmlns:p1=\"http://schemas.microsoft.com/winfx/2006/xaml\"\r\n\t\tp1:Key=\"Icon_Question\"\r\n\t\tp1:Shared=\"False\"\r\n\t\tStretch=\"Uniform\"\r\n\t\tTag=\"SHA256_8BAB2B1121975E67E9B709E8702C331DAC00E8752132F6ABDE8C1A424BDA3232\">\r\n\t\t<Canvas Width=\"512\" Height=\"512\">\r\n\t\t\t<Canvas.RenderTransform>\r\n\t\t\t\t<TranslateTransform X=\"0\" Y=\"0\" />\r\n\t\t\t</Canvas.RenderTransform>\r\n\t\t\t<Canvas.Resources>\r\n\t\t\t\t<LinearGradientBrush xmlns:x=\"http://schemas.microsoft.com/winfx/2006/xaml\" x:Key=\"color-1\" MappingMode=\"Absolute\" StartPoint=\"16,16\" EndPoint=\"389,486\">\r\n\t\t\t\t\t<LinearGradientBrush.GradientStops>\r\n\t\t\t\t\t\t<GradientStopCollection>\r\n\t\t\t\t\t\t\t<GradientStop Offset=\"0\" Color=\"#8D9BAB\" />\r\n\t\t\t\t\t\t\t<GradientStop Offset=\"1\" Color=\"#717F8F\" />\r\n\t\t\t\t\t\t</GradientStopCollection>\r\n\t\t\t\t\t</LinearGradientBrush.GradientStops>\r\n\t\t\t\t</LinearGradientBrush>\r\n\t\t\t\t<RadialGradientBrush xmlns:x=\"http://schemas.microsoft.com/winfx/2006/xaml\" x:Key=\"color-2\" Center=\"256,320\" GradientOrigin=\"256,320\" MappingMode=\"Absolute\" RadiusX=\"235.45638\" RadiusY=\"235.45638\">\r\n\t\t\t\t\t<RadialGradientBrush.GradientStops>\r\n\t\t\t\t\t\t<GradientStopCollection>\r\n\t\t\t\t\t\t\t<GradientStop Offset=\"0\" Color=\"#FFFFFF\" />\r\n\t\t\t\t\t\t\t<GradientStop Offset=\"0.34118\" Color=\"#FFFFFF\" />\r\n\t\t\t\t\t\t\t<GradientStop Offset=\"1\" Color=\"#D8E0E8\" />\r\n\t\t\t\t\t\t</GradientStopCollection>\r\n\t\t\t\t\t</RadialGradientBrush.GradientStops>\r\n\t\t\t\t</RadialGradientBrush>\r\n\t\t\t</Canvas.Resources>\r\n\t\t\t<!--  Unknown tag: metadata  -->\r\n\t\t\t<!--  Unknown tag: sodipodi:namedview  -->\r\n\t\t\t<Canvas>\r\n\t\t\t\t<Path\r\n\t\t\t\t\txmlns:x=\"http://schemas.microsoft.com/winfx/2006/xaml\"\r\n\t\t\t\t\tFill=\"{StaticResource color-1}\"\r\n\t\t\t\t\tStrokeDashArray=\"\"\r\n\t\t\t\t\tStrokeEndLineCap=\"Flat\"\r\n\t\t\t\t\tStrokeLineJoin=\"Miter\"\r\n\t\t\t\t\tStrokeMiterLimit=\"10\"\r\n\t\t\t\t\tStrokeStartLineCap=\"Flat\"\r\n\t\t\t\t\tStrokeThickness=\"1\">\r\n\t\t\t\t\t<Path.Data>\r\n\t\t\t\t\t\t<PathGeometry Figures=\"M256 16c132.5 0 240 107.5 240 240c0 132.5 -107.5 240 -240 240c-132.5 0 -240 -107.5 -240 -240c0 -132.5 107.5 -240 240 -240z\" FillRule=\"EvenOdd\" />\r\n\t\t\t\t\t</Path.Data>\r\n\t\t\t\t</Path>\r\n\t\t\t\t<Path\r\n\t\t\t\t\txmlns:x=\"http://schemas.microsoft.com/winfx/2006/xaml\"\r\n\t\t\t\t\tFill=\"{StaticResource color-2}\"\r\n\t\t\t\t\tStrokeDashArray=\"\"\r\n\t\t\t\t\tStrokeEndLineCap=\"Flat\"\r\n\t\t\t\t\tStrokeLineJoin=\"Miter\"\r\n\t\t\t\t\tStrokeMiterLimit=\"10\"\r\n\t\t\t\t\tStrokeStartLineCap=\"Flat\"\r\n\t\t\t\t\tStrokeThickness=\"1\">\r\n\t\t\t\t\t<Path.Data>\r\n\t\t\t\t\t\t<PathGeometry Figures=\"M224 311c0 -19.5 2 -35.5 6.5 -46.5c4.5 -11.5 12 -21.5 22 -30c10 -8.5 17.5 -16 22.5 -23c5.5 -7 8 -15 8 -23.5c0 -20.5 -8.5 -30.5 -26.5 -30.5c-8 0 -14.5 3 -19.5 9c-5 6 -8 14 -8 24l-69 0c0.5 -27 9 -48 26 -63.5c16.5 -15 40 -22.5 70.5 -22.5c30.5 0 54 7 70.5 21c16.5 14 25 34 25 59.5c0 11.5 -2.5 21.5 -7 31c-4.5 9 -11.5 18.5 -21.5 28l-23.5 22c-6.5 6.5 -11 13 -13.5 19.5c-3 7 -4 15.5 -4.5 25.5z M215.5 373c0 -10 3.5 -18 11 -24.5c7 -6.5 16 -10 27 -10c11 0 20 3.5 27 10c7.5 6.5 11 14.5 11 24.5c0 10 -3.5 18.5 -11 25c-7 6.5 -16 9.5 -27 9.5c-11 0 -20 -3 -27 -9.5c-7.5 -6.5 -11 -15 -11 -25z\" FillRule=\"EvenOdd\" />\r\n\t\t\t\t\t</Path.Data>\r\n\t\t\t\t</Path>\r\n\t\t\t</Canvas>\r\n\t\t</Canvas>\r\n\t</Viewbox>\r\n\t<Viewbox\r\n\t\txmlns=\"http://schemas.microsoft.com/winfx/2006/xaml/presentation\"\r\n\t\txmlns:p1=\"http://schemas.microsoft.com/winfx/2006/xaml\"\r\n\t\tp1:Key=\"Icon_Record\"\r\n\t\tp1:Shared=\"False\"\r\n\t\tStretch=\"Uniform\"\r\n\t\tTag=\"SHA256_1B73DA5CF3BF7377C6AB53B6714C4E6DCD25D78780CAA0734F92521794FECE49\">\r\n\t\t<Canvas Width=\"512\" Height=\"512\">\r\n\t\t\t<Canvas.RenderTransform>\r\n\t\t\t\t<TranslateTransform X=\"0\" Y=\"0\" />\r\n\t\t\t</Canvas.RenderTransform>\r\n\t\t\t<Canvas.Resources>\r\n\t\t\t\t<LinearGradientBrush xmlns:x=\"http://schemas.microsoft.com/winfx/2006/xaml\" x:Key=\"_Linear1\" MappingMode=\"Absolute\" StartPoint=\"0,0\" EndPoint=\"1,0\">\r\n\t\t\t\t\t<LinearGradientBrush.GradientStops>\r\n\t\t\t\t\t\t<GradientStopCollection>\r\n\t\t\t\t\t\t\t<GradientStop Offset=\"0\" Color=\"#FFEF5C51\" />\r\n\t\t\t\t\t\t\t<GradientStop Offset=\"1\" Color=\"#FFC22F24\" />\r\n\t\t\t\t\t\t</GradientStopCollection>\r\n\t\t\t\t\t</LinearGradientBrush.GradientStops>\r\n\t\t\t\t\t<LinearGradientBrush.Transform>\r\n\t\t\t\t\t\t<MatrixTransform Matrix=\"435.147 362.254 -362.254 435.147 16 16\" />\r\n\t\t\t\t\t</LinearGradientBrush.Transform>\r\n\t\t\t\t</LinearGradientBrush>\r\n\t\t\t</Canvas.Resources>\r\n\t\t\t<!--  Unknown tag: metadata  -->\r\n\t\t\t<!--  Unknown tag: sodipodi:namedview  -->\r\n\t\t\t<Canvas>\r\n\t\t\t\t<Path\r\n\t\t\t\t\txmlns:x=\"http://schemas.microsoft.com/winfx/2006/xaml\"\r\n\t\t\t\t\tFill=\"{StaticResource _Linear1}\"\r\n\t\t\t\t\tStrokeLineJoin=\"Round\"\r\n\t\t\t\t\tStrokeMiterLimit=\"2\">\r\n\t\t\t\t\t<Path.Data>\r\n\t\t\t\t\t\t<PathGeometry Figures=\"M256 16C388.5 16 496 123.5 496 256C496 388.5 388.5 496 256 496C123.5 496 16 388.5 16 256C16 123.5 123.5 16 256 16Z\" FillRule=\"NonZero\" />\r\n\t\t\t\t\t</Path.Data>\r\n\t\t\t\t</Path>\r\n\t\t\t</Canvas>\r\n\t\t</Canvas>\r\n\t</Viewbox>\r\n\t<Viewbox\r\n\t\txmlns=\"http://schemas.microsoft.com/winfx/2006/xaml/presentation\"\r\n\t\txmlns:p1=\"http://schemas.microsoft.com/winfx/2006/xaml\"\r\n\t\tp1:Key=\"Icon_Refresh\"\r\n\t\tp1:Shared=\"False\"\r\n\t\tStretch=\"Uniform\"\r\n\t\tTag=\"SHA256_6A9A2101F331624D84C4A448CCA29EAF9EF36AAE9C2FD7AE997693259298FAD4\">\r\n\t\t<Canvas Width=\"512\" Height=\"512\">\r\n\t\t\t<Canvas.RenderTransform>\r\n\t\t\t\t<TranslateTransform X=\"0\" Y=\"0\" />\r\n\t\t\t</Canvas.RenderTransform>\r\n\t\t\t<Canvas.Resources>\r\n\t\t\t\t<LinearGradientBrush xmlns:x=\"http://schemas.microsoft.com/winfx/2006/xaml\" x:Key=\"color-1\" MappingMode=\"Absolute\" StartPoint=\"0,32\" EndPoint=\"185,373\">\r\n\t\t\t\t\t<LinearGradientBrush.GradientStops>\r\n\t\t\t\t\t\t<GradientStopCollection>\r\n\t\t\t\t\t\t\t<GradientStop Offset=\"0\" Color=\"#4EB6F2\" />\r\n\t\t\t\t\t\t\t<GradientStop Offset=\"1\" Color=\"#129EEE\" />\r\n\t\t\t\t\t\t</GradientStopCollection>\r\n\t\t\t\t\t</LinearGradientBrush.GradientStops>\r\n\t\t\t\t</LinearGradientBrush>\r\n\t\t\t\t<LinearGradientBrush xmlns:x=\"http://schemas.microsoft.com/winfx/2006/xaml\" x:Key=\"color-2\" MappingMode=\"Absolute\" StartPoint=\"208,112\" EndPoint=\"375,420\">\r\n\t\t\t\t\t<LinearGradientBrush.GradientStops>\r\n\t\t\t\t\t\t<GradientStopCollection>\r\n\t\t\t\t\t\t\t<GradientStop Offset=\"0\" Color=\"#48B4F2\" />\r\n\t\t\t\t\t\t\t<GradientStop Offset=\"1\" Color=\"#129EEE\" />\r\n\t\t\t\t\t\t</GradientStopCollection>\r\n\t\t\t\t\t</LinearGradientBrush.GradientStops>\r\n\t\t\t\t</LinearGradientBrush>\r\n\t\t\t</Canvas.Resources>\r\n\t\t\t<!--  Unknown tag: metadata  -->\r\n\t\t\t<!--  Unknown tag: sodipodi:namedview  -->\r\n\t\t\t<Canvas>\r\n\t\t\t\t<Path\r\n\t\t\t\t\txmlns:x=\"http://schemas.microsoft.com/winfx/2006/xaml\"\r\n\t\t\t\t\tFill=\"{StaticResource color-1}\"\r\n\t\t\t\t\tStrokeDashArray=\"\"\r\n\t\t\t\t\tStrokeEndLineCap=\"Flat\"\r\n\t\t\t\t\tStrokeLineJoin=\"Miter\"\r\n\t\t\t\t\tStrokeMiterLimit=\"10\"\r\n\t\t\t\t\tStrokeStartLineCap=\"Flat\"\r\n\t\t\t\t\tStrokeThickness=\"1\">\r\n\t\t\t\t\t<Path.Data>\r\n\t\t\t\t\t\t<PathGeometry Figures=\"M192 32l0 80l-48 0c-79.5 0 -144 64.5 -144 144c0 79.5 64.5 144 144 144l50.5 0l-32 -32l32 -32l-50.5 0c-44 0 -80 -36 -80 -80c0 -44 36 -80 80 -80l48 0l0 80l112 -112z\" FillRule=\"EvenOdd\" />\r\n\t\t\t\t\t</Path.Data>\r\n\t\t\t\t</Path>\r\n\t\t\t\t<Path\r\n\t\t\t\t\txmlns:x=\"http://schemas.microsoft.com/winfx/2006/xaml\"\r\n\t\t\t\t\tFill=\"{StaticResource color-2}\"\r\n\t\t\t\t\tStrokeDashArray=\"\"\r\n\t\t\t\t\tStrokeEndLineCap=\"Flat\"\r\n\t\t\t\t\tStrokeLineJoin=\"Miter\"\r\n\t\t\t\t\tStrokeMiterLimit=\"10\"\r\n\t\t\t\t\tStrokeStartLineCap=\"Flat\"\r\n\t\t\t\t\tStrokeThickness=\"1\">\r\n\t\t\t\t\t<Path.Data>\r\n\t\t\t\t\t\t<PathGeometry Figures=\"M320 480l0 -80l48 0c79.5 0 144 -64.5 144 -144c0 -79.5 -64.5 -144 -144 -144l-50.5 0l32 32l-32 32l50.5 0c44 0 80 36 80 80c0 44 -36 80 -80 80l-48 0l0 -80l-112 112z\" FillRule=\"EvenOdd\" />\r\n\t\t\t\t\t</Path.Data>\r\n\t\t\t\t</Path>\r\n\t\t\t</Canvas>\r\n\t\t</Canvas>\r\n\t</Viewbox>\r\n\t<Viewbox\r\n\t\txmlns=\"http://schemas.microsoft.com/winfx/2006/xaml/presentation\"\r\n\t\txmlns:p1=\"http://schemas.microsoft.com/winfx/2006/xaml\"\r\n\t\tp1:Key=\"Icon_Remove\"\r\n\t\tp1:Shared=\"False\"\r\n\t\tStretch=\"Uniform\"\r\n\t\tTag=\"SHA256_D066D4EBCD0E99127111E10AB471EC2D589F3566717792740F24BEB60C50E2EA\">\r\n\t\t<Canvas Width=\"512\" Height=\"512\">\r\n\t\t\t<Canvas.RenderTransform>\r\n\t\t\t\t<TranslateTransform X=\"0\" Y=\"0\" />\r\n\t\t\t</Canvas.RenderTransform>\r\n\t\t\t<Canvas.Resources>\r\n\t\t\t\t<LinearGradientBrush xmlns:x=\"http://schemas.microsoft.com/winfx/2006/xaml\" x:Key=\"color-1\" MappingMode=\"Absolute\" StartPoint=\"16,192\" EndPoint=\"154,307\">\r\n\t\t\t\t\t<LinearGradientBrush.GradientStops>\r\n\t\t\t\t\t\t<GradientStopCollection>\r\n\t\t\t\t\t\t\t<GradientStop Offset=\"0\" Color=\"#EF5C51\" />\r\n\t\t\t\t\t\t\t<GradientStop Offset=\"1\" Color=\"#DC493E\" />\r\n\t\t\t\t\t\t</GradientStopCollection>\r\n\t\t\t\t\t</LinearGradientBrush.GradientStops>\r\n\t\t\t\t</LinearGradientBrush>\r\n\t\t\t</Canvas.Resources>\r\n\t\t\t<!--  Unknown tag: metadata  -->\r\n\t\t\t<!--  Unknown tag: sodipodi:namedview  -->\r\n\t\t\t<Canvas>\r\n\t\t\t\t<Path\r\n\t\t\t\t\txmlns:x=\"http://schemas.microsoft.com/winfx/2006/xaml\"\r\n\t\t\t\t\tFill=\"{StaticResource color-1}\"\r\n\t\t\t\t\tStrokeDashArray=\"\"\r\n\t\t\t\t\tStrokeEndLineCap=\"Flat\"\r\n\t\t\t\t\tStrokeLineJoin=\"Miter\"\r\n\t\t\t\t\tStrokeMiterLimit=\"10\"\r\n\t\t\t\t\tStrokeStartLineCap=\"Flat\"\r\n\t\t\t\t\tStrokeThickness=\"1\">\r\n\t\t\t\t\t<Path.Data>\r\n\t\t\t\t\t\t<PathGeometry Figures=\"M16 320l0 -128l480 0l0 128z\" FillRule=\"NonZero\" />\r\n\t\t\t\t\t</Path.Data>\r\n\t\t\t\t</Path>\r\n\t\t\t</Canvas>\r\n\t\t</Canvas>\r\n\t</Viewbox>\r\n\t<Viewbox\r\n\t\txmlns=\"http://schemas.microsoft.com/winfx/2006/xaml/presentation\"\r\n\t\txmlns:p1=\"http://schemas.microsoft.com/winfx/2006/xaml\"\r\n\t\tp1:Key=\"Icon_Reset\"\r\n\t\tp1:Shared=\"False\"\r\n\t\tStretch=\"Uniform\"\r\n\t\tTag=\"SHA256_DC63D3C5A3E5ACEA5E848822477E808734B3FD3C0426710184C8BE301FB49B02\">\r\n\t\t<Canvas Width=\"512\" Height=\"512\">\r\n\t\t\t<Canvas.RenderTransform>\r\n\t\t\t\t<TranslateTransform X=\"0\" Y=\"0\" />\r\n\t\t\t</Canvas.RenderTransform>\r\n\t\t\t<Canvas.Resources>\r\n\t\t\t\t<LinearGradientBrush xmlns:x=\"http://schemas.microsoft.com/winfx/2006/xaml\" x:Key=\"color-1\" MappingMode=\"Absolute\" StartPoint=\"464,432\" EndPoint=\"223,204\">\r\n\t\t\t\t\t<LinearGradientBrush.GradientStops>\r\n\t\t\t\t\t\t<GradientStopCollection>\r\n\t\t\t\t\t\t\t<GradientStop Offset=\"0\" Color=\"#FFFFFF\" />\r\n\t\t\t\t\t\t\t<GradientStop Offset=\"1\" Color=\"#B3C0CD\" />\r\n\t\t\t\t\t\t</GradientStopCollection>\r\n\t\t\t\t\t</LinearGradientBrush.GradientStops>\r\n\t\t\t\t</LinearGradientBrush>\r\n\t\t\t\t<LinearGradientBrush xmlns:x=\"http://schemas.microsoft.com/winfx/2006/xaml\" x:Key=\"color-2\" MappingMode=\"Absolute\" StartPoint=\"448,416\" EndPoint=\"248,103\">\r\n\t\t\t\t\t<LinearGradientBrush.GradientStops>\r\n\t\t\t\t\t\t<GradientStopCollection>\r\n\t\t\t\t\t\t\t<GradientStop Offset=\"0\" Color=\"#FFFFFF\" />\r\n\t\t\t\t\t\t\t<GradientStop Offset=\"1\" Color=\"#E3E9EE\" />\r\n\t\t\t\t\t\t</GradientStopCollection>\r\n\t\t\t\t\t</LinearGradientBrush.GradientStops>\r\n\t\t\t\t</LinearGradientBrush>\r\n\t\t\t\t<LinearGradientBrush xmlns:x=\"http://schemas.microsoft.com/winfx/2006/xaml\" x:Key=\"color-3\" MappingMode=\"Absolute\" StartPoint=\"0,32\" EndPoint=\"336,399\">\r\n\t\t\t\t\t<LinearGradientBrush.GradientStops>\r\n\t\t\t\t\t\t<GradientStopCollection>\r\n\t\t\t\t\t\t\t<GradientStop Offset=\"0\" Color=\"#4EB6F2\" />\r\n\t\t\t\t\t\t\t<GradientStop Offset=\"1\" Color=\"#129EEE\" />\r\n\t\t\t\t\t\t</GradientStopCollection>\r\n\t\t\t\t\t</LinearGradientBrush.GradientStops>\r\n\t\t\t\t</LinearGradientBrush>\r\n\t\t\t\t<LinearGradientBrush xmlns:x=\"http://schemas.microsoft.com/winfx/2006/xaml\" x:Key=\"color-4\" MappingMode=\"Absolute\" StartPoint=\"165,141\" EndPoint=\"302,274\">\r\n\t\t\t\t\t<LinearGradientBrush.GradientStops>\r\n\t\t\t\t\t\t<GradientStopCollection>\r\n\t\t\t\t\t\t\t<GradientStop Offset=\"0\" Color=\"#D8E0E8\" />\r\n\t\t\t\t\t\t\t<GradientStop Offset=\"1\" Color=\"#B3C0CD\" />\r\n\t\t\t\t\t\t</GradientStopCollection>\r\n\t\t\t\t\t</LinearGradientBrush.GradientStops>\r\n\t\t\t\t</LinearGradientBrush>\r\n\t\t\t\t<LinearGradientBrush xmlns:x=\"http://schemas.microsoft.com/winfx/2006/xaml\" x:Key=\"color-5\" MappingMode=\"Absolute\" StartPoint=\"165,134\" EndPoint=\"290,288\">\r\n\t\t\t\t\t<LinearGradientBrush.GradientStops>\r\n\t\t\t\t\t\t<GradientStopCollection>\r\n\t\t\t\t\t\t\t<GradientStop Offset=\"0\" Color=\"#ED5A4F\" />\r\n\t\t\t\t\t\t\t<GradientStop Offset=\"1\" Color=\"#E24F44\" />\r\n\t\t\t\t\t\t</GradientStopCollection>\r\n\t\t\t\t\t</LinearGradientBrush.GradientStops>\r\n\t\t\t\t</LinearGradientBrush>\r\n\t\t\t</Canvas.Resources>\r\n\t\t\t<!--  Unknown tag: metadata  -->\r\n\t\t\t<!--  Unknown tag: sodipodi:namedview  -->\r\n\t\t\t<Canvas>\r\n\t\t\t\t<Path\r\n\t\t\t\t\txmlns:x=\"http://schemas.microsoft.com/winfx/2006/xaml\"\r\n\t\t\t\t\tFill=\"{StaticResource color-1}\"\r\n\t\t\t\t\tStrokeDashArray=\"\"\r\n\t\t\t\t\tStrokeEndLineCap=\"Flat\"\r\n\t\t\t\t\tStrokeLineJoin=\"Miter\"\r\n\t\t\t\t\tStrokeMiterLimit=\"10\"\r\n\t\t\t\t\tStrokeStartLineCap=\"Flat\"\r\n\t\t\t\t\tStrokeThickness=\"1\">\r\n\t\t\t\t\t<Path.Data>\r\n\t\t\t\t\t\t<PathGeometry Figures=\"M288 80c97 0 176 79 176 176c0 97 -79 176 -176 176c-97 0 -176 -79 -176 -176c0 -97 79 -176 176 -176z\" FillRule=\"EvenOdd\" />\r\n\t\t\t\t\t</Path.Data>\r\n\t\t\t\t</Path>\r\n\t\t\t\t<Path\r\n\t\t\t\t\txmlns:x=\"http://schemas.microsoft.com/winfx/2006/xaml\"\r\n\t\t\t\t\tFill=\"{StaticResource color-2}\"\r\n\t\t\t\t\tStrokeDashArray=\"\"\r\n\t\t\t\t\tStrokeEndLineCap=\"Flat\"\r\n\t\t\t\t\tStrokeLineJoin=\"Miter\"\r\n\t\t\t\t\tStrokeMiterLimit=\"10\"\r\n\t\t\t\t\tStrokeStartLineCap=\"Flat\"\r\n\t\t\t\t\tStrokeThickness=\"1\">\r\n\t\t\t\t\t<Path.Data>\r\n\t\t\t\t\t\t<PathGeometry Figures=\"M288 96c88.5 0 160 71.5 160 160c0 88.5 -71.5 160 -160 160c-88.5 0 -160 -71.5 -160 -160c0 -88.5 71.5 -160 160 -160z\" FillRule=\"EvenOdd\" />\r\n\t\t\t\t\t</Path.Data>\r\n\t\t\t\t</Path>\r\n\t\t\t\t<Path\r\n\t\t\t\t\txmlns:x=\"http://schemas.microsoft.com/winfx/2006/xaml\"\r\n\t\t\t\t\tFill=\"{StaticResource color-3}\"\r\n\t\t\t\t\tStrokeDashArray=\"\"\r\n\t\t\t\t\tStrokeEndLineCap=\"Flat\"\r\n\t\t\t\t\tStrokeLineJoin=\"Miter\"\r\n\t\t\t\t\tStrokeMiterLimit=\"10\"\r\n\t\t\t\t\tStrokeStartLineCap=\"Flat\"\r\n\t\t\t\t\tStrokeThickness=\"1\">\r\n\t\t\t\t\t<Path.Data>\r\n\t\t\t\t\t\t<PathGeometry Figures=\"M288 32c123.5 0 224 100.5 224 224c0 123.5 -100.5 224 -224 224c-79 0 -148.5 -41 -188 -102.5l34.5 -35c30.5 53.5 88 89.5 153.5 89.5c97 0 176 -79 176 -176c0 -97 -79 -176 -176 -176c-97 0 -176 79 -176 176l64 0l-88 88l-88 -88l64 0c0 -123.5 100.5 -224 224 -224z\" FillRule=\"EvenOdd\" />\r\n\t\t\t\t\t</Path.Data>\r\n\t\t\t\t</Path>\r\n\t\t\t\t<Path\r\n\t\t\t\t\txmlns:x=\"http://schemas.microsoft.com/winfx/2006/xaml\"\r\n\t\t\t\t\tFill=\"{StaticResource color-4}\"\r\n\t\t\t\t\tStrokeDashArray=\"\"\r\n\t\t\t\t\tStrokeEndLineCap=\"Flat\"\r\n\t\t\t\t\tStrokeLineJoin=\"Miter\"\r\n\t\t\t\t\tStrokeMiterLimit=\"10\"\r\n\t\t\t\t\tStrokeStartLineCap=\"Flat\"\r\n\t\t\t\t\tStrokeThickness=\"1\">\r\n\t\t\t\t\t<Path.Data>\r\n\t\t\t\t\t\t<PathGeometry Figures=\"M291.5 232l55 -55.5c3 -3 7.5 -3 11 -0.5c6.5 5 12.5 11.5 18 18c2.5 3.5 2.5 8 -0.5 11l-55.5 55c0 1.5 0.5 2.5 0.5 3.5c0 18 -14.5 32 -32 32c-18 0 -32 -14 -32 -32c0 -1 0 -2 0 -3.5l-88 -88c-3 -3 -3.5 -7.5 -0.5 -11c5.5 -6.5 11.5 -12.5 17.5 -18c3.5 -2.5 8 -2.5 11 0.5l88.5 88.5c2 -0.5 4.5 -0.5 7 0z\" FillRule=\"EvenOdd\" />\r\n\t\t\t\t\t</Path.Data>\r\n\t\t\t\t</Path>\r\n\t\t\t\t<Path\r\n\t\t\t\t\txmlns:x=\"http://schemas.microsoft.com/winfx/2006/xaml\"\r\n\t\t\t\t\tFill=\"{StaticResource color-5}\"\r\n\t\t\t\t\tStrokeDashArray=\"\"\r\n\t\t\t\t\tStrokeEndLineCap=\"Flat\"\r\n\t\t\t\t\tStrokeLineJoin=\"Miter\"\r\n\t\t\t\t\tStrokeMiterLimit=\"10\"\r\n\t\t\t\t\tStrokeStartLineCap=\"Flat\"\r\n\t\t\t\t\tStrokeThickness=\"1\">\r\n\t\t\t\t\t<Path.Data>\r\n\t\t\t\t\t\t<PathGeometry Figures=\"M291.5 224l55 -55c3 -3 7.5 -3.5 11 -1c6.5 5.5 12.5 11.5 18 18.5c2.5 3 2.5 7.5 -0.5 10.5l-55.5 55.5c0 1 0.5 2.5 0.5 3.5c0 17.5 -14.5 32 -32 32c-18 0 -32 -14.5 -32 -32c0 -1 0 -2.5 0 -3.5l-88 -88c-3 -3 -3.5 -8 -0.5 -11c5.5 -6.5 11.5 -12.5 17.5 -18c3.5 -2.5 8 -2.5 11 0.5l88.5 88c2 0 4.5 0 7 0z\" FillRule=\"EvenOdd\" />\r\n\t\t\t\t\t</Path.Data>\r\n\t\t\t\t</Path>\r\n\t\t\t\t<Path\r\n\t\t\t\t\txmlns:x=\"http://schemas.microsoft.com/winfx/2006/xaml\"\r\n\t\t\t\t\tFill=\"#FFD8E0E8\"\r\n\t\t\t\t\tStrokeDashArray=\"\"\r\n\t\t\t\t\tStrokeEndLineCap=\"Flat\"\r\n\t\t\t\t\tStrokeLineJoin=\"Miter\"\r\n\t\t\t\t\tStrokeMiterLimit=\"10\"\r\n\t\t\t\t\tStrokeStartLineCap=\"Flat\"\r\n\t\t\t\t\tStrokeThickness=\"1\">\r\n\t\t\t\t\t<Path.Data>\r\n\t\t\t\t\t\t<PathGeometry Figures=\"M288 240c8.5 0 16 7 16 16c0 9 -7.5 16 -16 16c-9 0 -16 -7 -16 -16c0 -9 7 -16 16 -16z\" FillRule=\"EvenOdd\" />\r\n\t\t\t\t\t</Path.Data>\r\n\t\t\t\t</Path>\r\n\t\t\t</Canvas>\r\n\t\t</Canvas>\r\n\t</Viewbox>\r\n\t<Viewbox\r\n\t\txmlns=\"http://schemas.microsoft.com/winfx/2006/xaml/presentation\"\r\n\t\txmlns:p1=\"http://schemas.microsoft.com/winfx/2006/xaml\"\r\n\t\tp1:Key=\"Icon_Save\"\r\n\t\tp1:Shared=\"False\"\r\n\t\tStretch=\"Uniform\"\r\n\t\tTag=\"SHA256_759E181789D0456B262C6799B891C2F357C883222B4347ED9839C611A0D9CEAB\">\r\n\t\t<Canvas Width=\"512\" Height=\"512\">\r\n\t\t\t<Canvas.RenderTransform>\r\n\t\t\t\t<TranslateTransform X=\"0\" Y=\"0\" />\r\n\t\t\t</Canvas.RenderTransform>\r\n\t\t\t<Canvas.Resources>\r\n\t\t\t\t<LinearGradientBrush xmlns:x=\"http://schemas.microsoft.com/winfx/2006/xaml\" x:Key=\"color-1\" MappingMode=\"Absolute\" StartPoint=\"32,32\" EndPoint=\"430,430\">\r\n\t\t\t\t\t<LinearGradientBrush.GradientStops>\r\n\t\t\t\t\t\t<GradientStopCollection>\r\n\t\t\t\t\t\t\t<GradientStop Offset=\"0\" Color=\"#717F8F\" />\r\n\t\t\t\t\t\t\t<GradientStop Offset=\"1\" Color=\"#51616F\" />\r\n\t\t\t\t\t\t</GradientStopCollection>\r\n\t\t\t\t\t</LinearGradientBrush.GradientStops>\r\n\t\t\t\t</LinearGradientBrush>\r\n\t\t\t\t<LinearGradientBrush xmlns:x=\"http://schemas.microsoft.com/winfx/2006/xaml\" x:Key=\"color-2\" MappingMode=\"Absolute\" StartPoint=\"96,32\" EndPoint=\"186,272\">\r\n\t\t\t\t\t<LinearGradientBrush.GradientStops>\r\n\t\t\t\t\t\t<GradientStopCollection>\r\n\t\t\t\t\t\t\t<GradientStop Offset=\"0\" Color=\"#EAF6FE\" />\r\n\t\t\t\t\t\t\t<GradientStop Offset=\"1\" Color=\"#CBE9FB\" />\r\n\t\t\t\t\t\t</GradientStopCollection>\r\n\t\t\t\t\t</LinearGradientBrush.GradientStops>\r\n\t\t\t\t</LinearGradientBrush>\r\n\t\t\t\t<LinearGradientBrush xmlns:x=\"http://schemas.microsoft.com/winfx/2006/xaml\" x:Key=\"color-3\" MappingMode=\"Absolute\" StartPoint=\"416,272\" EndPoint=\"415,271\">\r\n\t\t\t\t\t<LinearGradientBrush.GradientStops>\r\n\t\t\t\t\t\t<GradientStopCollection>\r\n\t\t\t\t\t\t\t<GradientStop Offset=\"0\" Color=\"#FF7901\" />\r\n\t\t\t\t\t\t\t<GradientStop Offset=\"1\" Color=\"#F77501\" />\r\n\t\t\t\t\t\t</GradientStopCollection>\r\n\t\t\t\t\t</LinearGradientBrush.GradientStops>\r\n\t\t\t\t</LinearGradientBrush>\r\n\t\t\t\t<LinearGradientBrush xmlns:x=\"http://schemas.microsoft.com/winfx/2006/xaml\" x:Key=\"color-4\" MappingMode=\"Absolute\" StartPoint=\"128,320\" EndPoint=\"289,481\">\r\n\t\t\t\t\t<LinearGradientBrush.GradientStops>\r\n\t\t\t\t\t\t<GradientStopCollection>\r\n\t\t\t\t\t\t\t<GradientStop Offset=\"0\" Color=\"#FFFFFF\" />\r\n\t\t\t\t\t\t\t<GradientStop Offset=\"1\" Color=\"#E1E7ED\" />\r\n\t\t\t\t\t\t</GradientStopCollection>\r\n\t\t\t\t\t</LinearGradientBrush.GradientStops>\r\n\t\t\t\t</LinearGradientBrush>\r\n\t\t\t</Canvas.Resources>\r\n\t\t\t<!--  Unknown tag: metadata  -->\r\n\t\t\t<!--  Unknown tag: sodipodi:namedview  -->\r\n\t\t\t<Canvas>\r\n\t\t\t\t<Path\r\n\t\t\t\t\txmlns:x=\"http://schemas.microsoft.com/winfx/2006/xaml\"\r\n\t\t\t\t\tFill=\"{StaticResource color-1}\"\r\n\t\t\t\t\tStrokeDashArray=\"\"\r\n\t\t\t\t\tStrokeEndLineCap=\"Flat\"\r\n\t\t\t\t\tStrokeLineJoin=\"Miter\"\r\n\t\t\t\t\tStrokeMiterLimit=\"10\"\r\n\t\t\t\t\tStrokeStartLineCap=\"Flat\"\r\n\t\t\t\t\tStrokeThickness=\"1\">\r\n\t\t\t\t\t<Path.Data>\r\n\t\t\t\t\t\t<PathGeometry Figures=\"M480 48l0 416c0 9 -7 16 -16 16l-400 0l-32 -32l0 -400c0 -9 7 -16 16 -16l416 0c9 0 16 7 16 16z\" FillRule=\"NonZero\" />\r\n\t\t\t\t\t</Path.Data>\r\n\t\t\t\t</Path>\r\n\t\t\t\t<Path\r\n\t\t\t\t\txmlns:x=\"http://schemas.microsoft.com/winfx/2006/xaml\"\r\n\t\t\t\t\tFill=\"{StaticResource color-2}\"\r\n\t\t\t\t\tStrokeDashArray=\"\"\r\n\t\t\t\t\tStrokeEndLineCap=\"Flat\"\r\n\t\t\t\t\tStrokeLineJoin=\"Miter\"\r\n\t\t\t\t\tStrokeMiterLimit=\"10\"\r\n\t\t\t\t\tStrokeStartLineCap=\"Flat\"\r\n\t\t\t\t\tStrokeThickness=\"1\">\r\n\t\t\t\t\t<Path.Data>\r\n\t\t\t\t\t\t<PathGeometry Figures=\"M96 32l320 0l0 224c0 9 -7 16 -16 16l-288 0c-9 0 -16 -7 -16 -16z\" FillRule=\"NonZero\" />\r\n\t\t\t\t\t</Path.Data>\r\n\t\t\t\t</Path>\r\n\t\t\t\t<Path\r\n\t\t\t\t\txmlns:x=\"http://schemas.microsoft.com/winfx/2006/xaml\"\r\n\t\t\t\t\tFill=\"{StaticResource color-3}\"\r\n\t\t\t\t\tStrokeDashArray=\"\"\r\n\t\t\t\t\tStrokeEndLineCap=\"Flat\"\r\n\t\t\t\t\tStrokeLineJoin=\"Miter\"\r\n\t\t\t\t\tStrokeMiterLimit=\"10\"\r\n\t\t\t\t\tStrokeStartLineCap=\"Flat\"\r\n\t\t\t\t\tStrokeThickness=\"1\">\r\n\t\t\t\t\t<Path.Data>\r\n\t\t\t\t\t\t<PathGeometry Figures=\"M416 208l0 48c0 9 -7 16 -16 16l-288 0c-9 0 -16 -7 -16 -16l0 -48z\" FillRule=\"NonZero\" />\r\n\t\t\t\t\t</Path.Data>\r\n\t\t\t\t</Path>\r\n\t\t\t\t<Path\r\n\t\t\t\t\txmlns:x=\"http://schemas.microsoft.com/winfx/2006/xaml\"\r\n\t\t\t\t\tFill=\"{StaticResource color-4}\"\r\n\t\t\t\t\tStrokeDashArray=\"\"\r\n\t\t\t\t\tStrokeEndLineCap=\"Flat\"\r\n\t\t\t\t\tStrokeLineJoin=\"Miter\"\r\n\t\t\t\t\tStrokeMiterLimit=\"10\"\r\n\t\t\t\t\tStrokeStartLineCap=\"Flat\"\r\n\t\t\t\t\tStrokeThickness=\"1\">\r\n\t\t\t\t\t<Path.Data>\r\n\t\t\t\t\t\t<PathGeometry Figures=\"M384 336l0 144l-256 0l0 -144c0 -9 7 -16 16 -16l224 0c9 0 16 7 16 16z M160 336l0 112l64 0l0 -112z\" FillRule=\"NonZero\" />\r\n\t\t\t\t\t</Path.Data>\r\n\t\t\t\t</Path>\r\n\t\t\t</Canvas>\r\n\t\t</Canvas>\r\n\t</Viewbox>\r\n\t<Viewbox\r\n\t\txmlns=\"http://schemas.microsoft.com/winfx/2006/xaml/presentation\"\r\n\t\txmlns:p1=\"http://schemas.microsoft.com/winfx/2006/xaml\"\r\n\t\tp1:Key=\"Icon_SelectAll\"\r\n\t\tp1:Shared=\"False\"\r\n\t\tStretch=\"Uniform\"\r\n\t\tTag=\"SHA256_D1971AC412FC6A4B653167F504C56333881D26FD6E755B8B5423A54F98113A24\">\r\n\t\t<Canvas\r\n\t\t\tCanvas.Left=\"0\"\r\n\t\t\tCanvas.Top=\"0\"\r\n\t\t\tWidth=\"24\"\r\n\t\t\tHeight=\"24\">\r\n\t\t\t<Canvas.RenderTransform>\r\n\t\t\t\t<TranslateTransform X=\"0\" Y=\"0\" />\r\n\t\t\t</Canvas.RenderTransform>\r\n\t\t\t<Canvas.Resources />\r\n\t\t\t<!--  Unknown tag: metadata  -->\r\n\t\t\t<!--  Unknown tag: sodipodi:namedview  -->\r\n\t\t\t<Canvas>\r\n\t\t\t\t<Path xmlns:x=\"http://schemas.microsoft.com/winfx/2006/xaml\" Fill=\"#FF5D9CEC\">\r\n\t\t\t\t\t<Path.Data>\r\n\t\t\t\t\t\t<PathGeometry Figures=\"M2.9 2h7.2c0.495 0 0.9 0.405 0.9 0.9v7.2c0 0.495 -0.405 0.9 -0.9 0.9H2.9c-0.495 0 -0.9 -0.405 -0.9 -0.9V2.9C2 2.405 2.405 2 2.9 2zM13 2.9v7.2c0 0.495 0.405 0.9 0.9 0.9h7.2c0.495 0 0.9 -0.405 0.9 -0.9V2.9c0 -0.495 -0.405 -0.9 -0.9 -0.9h-7.2C13.405 2 13 2.405 13 2.9zM2 13.9v7.2c0 0.495 0.405 0.9 0.9 0.9h7.2c0.495 0 0.9 -0.405 0.9 -0.9v-7.2c0 -0.495 -0.405 -0.9 -0.9 -0.9H2.9C2.405 13 2 13.405 2 13.9zM13 13.9v7.2c0 0.495 0.405 0.9 0.9 0.9h7.2c0.495 0 0.9 -0.405 0.9 -0.9v-7.2c0 -0.495 -0.405 -0.9 -0.9 -0.9h-7.2C13.405 13 13 13.405 13 13.9z\" FillRule=\"NonZero\" />\r\n\t\t\t\t\t</Path.Data>\r\n\t\t\t\t</Path>\r\n\t\t\t\t<Path xmlns:x=\"http://schemas.microsoft.com/winfx/2006/xaml\" Fill=\"#FF4B89DC\" Opacity=\"0.8\">\r\n\t\t\t\t\t<Path.Data>\r\n\t\t\t\t\t\t<PathGeometry Figures=\"M11 2.9v7.2c0 0.495 -0.405 0.9 -0.9 0.9H5.9818l2.8364 -9H10.1C10.595 2 11 2.405 11 2.9zM21.1 2h-1.2818l-2.8364 9H21.1c0.495 0 0.9 -0.405 0.9 -0.9V2.9C22 2.405 21.595 2 21.1 2zM21.1 13h-1.2818l-2.8364 9H21.1c0.495 0 0.9 -0.405 0.9 -0.9v-7.2C22 13.405 21.595 13 21.1 13zM10.1 13H8.8182l-2.8364 9H10.1c0.495 0 0.9 -0.405 0.9 -0.9v-7.2C11 13.405 10.595 13 10.1 13z\" FillRule=\"NonZero\" />\r\n\t\t\t\t\t</Path.Data>\r\n\t\t\t\t</Path>\r\n\t\t\t</Canvas>\r\n\t\t</Canvas>\r\n\t</Viewbox>\r\n\t<Viewbox\r\n\t\txmlns=\"http://schemas.microsoft.com/winfx/2006/xaml/presentation\"\r\n\t\txmlns:p1=\"http://schemas.microsoft.com/winfx/2006/xaml\"\r\n\t\tp1:Key=\"Icon_SelectInverse\"\r\n\t\tp1:Shared=\"False\"\r\n\t\tStretch=\"Uniform\"\r\n\t\tTag=\"SHA256_5EB87DC9B46C7DA280F1741FFBA82CBC655FC317C038C6F5DADDC51AB24B497B\">\r\n\t\t<Canvas\r\n\t\t\tCanvas.Left=\"0\"\r\n\t\t\tCanvas.Top=\"0\"\r\n\t\t\tWidth=\"24\"\r\n\t\t\tHeight=\"24\">\r\n\t\t\t<Canvas.RenderTransform>\r\n\t\t\t\t<TranslateTransform X=\"0\" Y=\"0\" />\r\n\t\t\t</Canvas.RenderTransform>\r\n\t\t\t<Canvas.Resources />\r\n\t\t\t<!--  Unknown tag: metadata  -->\r\n\t\t\t<!--  Unknown tag: sodipodi:namedview  -->\r\n\t\t\t<Canvas>\r\n\t\t\t\t<Path xmlns:x=\"http://schemas.microsoft.com/winfx/2006/xaml\" Fill=\"#FFE6E9EE\">\r\n\t\t\t\t\t<Path.Data>\r\n\t\t\t\t\t\t<PathGeometry Figures=\"M13 2.9v7.2c0 0.495 0.405 0.9 0.9 0.9h7.2c0.495 0 0.9 -0.405 0.9 -0.9V2.9c0 -0.495 -0.405 -0.9 -0.9 -0.9h-7.2C13.405 2 13 2.405 13 2.9zM2 13.9v7.2c0 0.495 0.405 0.9 0.9 0.9h7.2c0.495 0 0.9 -0.405 0.9 -0.9v-7.2c0 -0.495 -0.405 -0.9 -0.9 -0.9H2.9C2.405 13 2 13.405 2 13.9z\" FillRule=\"NonZero\" />\r\n\t\t\t\t\t</Path.Data>\r\n\t\t\t\t</Path>\r\n\t\t\t\t<Path xmlns:x=\"http://schemas.microsoft.com/winfx/2006/xaml\" Fill=\"#FFCCD0D9\" Opacity=\"0.5\">\r\n\t\t\t\t\t<Path.Data>\r\n\t\t\t\t\t\t<PathGeometry Figures=\"M21.1 2h-1.2818l-2.8364 9H21.1c0.495 0 0.9 -0.405 0.9 -0.9V2.9C22 2.405 21.595 2 21.1 2zM10.1 13H8.8182l-2.8364 9H10.1c0.495 0 0.9 -0.405 0.9 -0.9v-7.2C11 13.405 10.595 13 10.1 13z\" FillRule=\"NonZero\" />\r\n\t\t\t\t\t</Path.Data>\r\n\t\t\t\t</Path>\r\n\t\t\t\t<Path xmlns:x=\"http://schemas.microsoft.com/winfx/2006/xaml\" Fill=\"#FF5D9CEC\">\r\n\t\t\t\t\t<Path.Data>\r\n\t\t\t\t\t\t<PathGeometry Figures=\"M2.9 2h7.2c0.495 0 0.9 0.405 0.9 0.9v7.2c0 0.495 -0.405 0.9 -0.9 0.9H2.9c-0.495 0 -0.9 -0.405 -0.9 -0.9V2.9C2 2.405 2.405 2 2.9 2zM13 13.9v7.2c0 0.495 0.405 0.9 0.9 0.9h7.2c0.495 0 0.9 -0.405 0.9 -0.9v-7.2c0 -0.495 -0.405 -0.9 -0.9 -0.9h-7.2C13.405 13 13 13.405 13 13.9z\" FillRule=\"NonZero\" />\r\n\t\t\t\t\t</Path.Data>\r\n\t\t\t\t</Path>\r\n\t\t\t\t<Path xmlns:x=\"http://schemas.microsoft.com/winfx/2006/xaml\" Fill=\"#FF4B89DC\" Opacity=\"0.8\">\r\n\t\t\t\t\t<Path.Data>\r\n\t\t\t\t\t\t<PathGeometry Figures=\"M11 2.9v7.2c0 0.495 -0.405 0.9 -0.9 0.9H5.9818l2.8364 -9H10.1C10.595 2 11 2.405 11 2.9zM21.1 13h-1.2818l-2.8364 9H21.1c0.495 0 0.9 -0.405 0.9 -0.9v-7.2C22 13.405 21.595 13 21.1 13z\" FillRule=\"NonZero\" />\r\n\t\t\t\t\t</Path.Data>\r\n\t\t\t\t</Path>\r\n\t\t\t</Canvas>\r\n\t\t</Canvas>\r\n\t</Viewbox>\r\n\t<Viewbox\r\n\t\txmlns=\"http://schemas.microsoft.com/winfx/2006/xaml/presentation\"\r\n\t\txmlns:p1=\"http://schemas.microsoft.com/winfx/2006/xaml\"\r\n\t\tp1:Key=\"Icon_SelectNone\"\r\n\t\tp1:Shared=\"False\"\r\n\t\tStretch=\"Uniform\"\r\n\t\tTag=\"SHA256_51348C9FA6E577E44521305523227F16178877EF5E89E8E5A50925FBE1505318\">\r\n\t\t<Canvas\r\n\t\t\tCanvas.Left=\"0\"\r\n\t\t\tCanvas.Top=\"0\"\r\n\t\t\tWidth=\"24\"\r\n\t\t\tHeight=\"24\">\r\n\t\t\t<Canvas.RenderTransform>\r\n\t\t\t\t<TranslateTransform X=\"0\" Y=\"0\" />\r\n\t\t\t</Canvas.RenderTransform>\r\n\t\t\t<Canvas.Resources />\r\n\t\t\t<!--  Unknown tag: metadata  -->\r\n\t\t\t<!--  Unknown tag: sodipodi:namedview  -->\r\n\t\t\t<Canvas>\r\n\t\t\t\t<Path xmlns:x=\"http://schemas.microsoft.com/winfx/2006/xaml\" Fill=\"#FFE6E9EE\">\r\n\t\t\t\t\t<Path.Data>\r\n\t\t\t\t\t\t<PathGeometry Figures=\"M13 2.9v7.2c0 0.495 0.405 0.9 0.9 0.9h7.2c0.495 0 0.9 -0.405 0.9 -0.9V2.9c0 -0.495 -0.405 -0.9 -0.9 -0.9h-7.2C13.405 2 13 2.405 13 2.9zM2 13.9v7.2c0 0.495 0.405 0.9 0.9 0.9h7.2c0.495 0 0.9 -0.405 0.9 -0.9v-7.2c0 -0.495 -0.405 -0.9 -0.9 -0.9H2.9C2.405 13 2 13.405 2 13.9zM2.9 2h7.2c0.495 0 0.9 0.405 0.9 0.9v7.2c0 0.495 -0.405 0.9 -0.9 0.9H2.9c-0.495 0 -0.9 -0.405 -0.9 -0.9V2.9C2 2.405 2.405 2 2.9 2zM13 13.9v7.2c0 0.495 0.405 0.9 0.9 0.9h7.2c0.495 0 0.9 -0.405 0.9 -0.9v-7.2c0 -0.495 -0.405 -0.9 -0.9 -0.9h-7.2C13.405 13 13 13.405 13 13.9z\" FillRule=\"NonZero\" />\r\n\t\t\t\t\t</Path.Data>\r\n\t\t\t\t</Path>\r\n\t\t\t\t<Path xmlns:x=\"http://schemas.microsoft.com/winfx/2006/xaml\" Fill=\"#FFCCD0D9\" Opacity=\"0.5\">\r\n\t\t\t\t\t<Path.Data>\r\n\t\t\t\t\t\t<PathGeometry Figures=\"M21.1 2h-1.2818l-2.8364 9H21.1c0.495 0 0.9 -0.405 0.9 -0.9V2.9C22 2.405 21.595 2 21.1 2zM10.1 13H8.8182l-2.8364 9H10.1c0.495 0 0.9 -0.405 0.9 -0.9v-7.2C11 13.405 10.595 13 10.1 13zM11 2.9v7.2c0 0.495 -0.405 0.9 -0.9 0.9H5.9818l2.8364 -9H10.1C10.595 2 11 2.405 11 2.9zM21.1 13h-1.2818l-2.8364 9H21.1c0.495 0 0.9 -0.405 0.9 -0.9v-7.2C22 13.405 21.595 13 21.1 13z\" FillRule=\"NonZero\" />\r\n\t\t\t\t\t</Path.Data>\r\n\t\t\t\t</Path>\r\n\t\t\t</Canvas>\r\n\t\t</Canvas>\r\n\t</Viewbox>\r\n\t<Viewbox\r\n\t\txmlns=\"http://schemas.microsoft.com/winfx/2006/xaml/presentation\"\r\n\t\txmlns:p1=\"http://schemas.microsoft.com/winfx/2006/xaml\"\r\n\t\tp1:Key=\"Icon_Stop\"\r\n\t\tp1:Shared=\"False\"\r\n\t\tStretch=\"Uniform\"\r\n\t\tTag=\"SHA256_55A9E125F34D9BCA4020558F768CCF19720EE8673E6C117C4A7230F48D2E6351\">\r\n\t\t<Canvas Width=\"512\" Height=\"512\">\r\n\t\t\t<Canvas.RenderTransform>\r\n\t\t\t\t<TranslateTransform X=\"0\" Y=\"0\" />\r\n\t\t\t</Canvas.RenderTransform>\r\n\t\t\t<Canvas.Resources>\r\n\t\t\t\t<LinearGradientBrush xmlns:x=\"http://schemas.microsoft.com/winfx/2006/xaml\" x:Key=\"color-1\" MappingMode=\"Absolute\" StartPoint=\"32,32\" EndPoint=\"496,480\">\r\n\t\t\t\t\t<LinearGradientBrush.GradientStops>\r\n\t\t\t\t\t\t<GradientStopCollection>\r\n\t\t\t\t\t\t\t<GradientStop Offset=\"0\" Color=\"#4EB6F2\" />\r\n\t\t\t\t\t\t\t<GradientStop Offset=\"0.5098\" Color=\"#129EEE\" />\r\n\t\t\t\t\t\t\t<GradientStop Offset=\"1\" Color=\"#0E76B2\" />\r\n\t\t\t\t\t\t</GradientStopCollection>\r\n\t\t\t\t\t</LinearGradientBrush.GradientStops>\r\n\t\t\t\t</LinearGradientBrush>\r\n\t\t\t</Canvas.Resources>\r\n\t\t\t<!--  Unknown tag: metadata  -->\r\n\t\t\t<!--  Unknown tag: sodipodi:namedview  -->\r\n\t\t\t<Canvas>\r\n\t\t\t\t<Path\r\n\t\t\t\t\txmlns:x=\"http://schemas.microsoft.com/winfx/2006/xaml\"\r\n\t\t\t\t\tFill=\"{StaticResource color-1}\"\r\n\t\t\t\t\tStrokeDashArray=\"\"\r\n\t\t\t\t\tStrokeEndLineCap=\"Flat\"\r\n\t\t\t\t\tStrokeLineJoin=\"Miter\"\r\n\t\t\t\t\tStrokeMiterLimit=\"10\"\r\n\t\t\t\t\tStrokeStartLineCap=\"Flat\"\r\n\t\t\t\t\tStrokeThickness=\"1\">\r\n\t\t\t\t\t<Path.Data>\r\n\t\t\t\t\t\t<PathGeometry Figures=\"M48 32l416 0c9 0 16 7 16 16l0 416c0 9 -7 16 -16 16l-416 0c-9 0 -16 -7 -16 -16l0 -416c0 -9 7 -16 16 -16z\" FillRule=\"EvenOdd\" />\r\n\t\t\t\t\t</Path.Data>\r\n\t\t\t\t</Path>\r\n\t\t\t</Canvas>\r\n\t\t</Canvas>\r\n\t</Viewbox>\r\n\t<Viewbox\r\n\t\txmlns=\"http://schemas.microsoft.com/winfx/2006/xaml/presentation\"\r\n\t\txmlns:p1=\"http://schemas.microsoft.com/winfx/2006/xaml\"\r\n\t\tp1:Key=\"Icon_ToggleOff\"\r\n\t\tp1:Shared=\"False\"\r\n\t\tStretch=\"Uniform\"\r\n\t\tTag=\"SHA256_C0E6E916EFBBFF44FBAB9270154D7B1BE7B56A2CD8187147650FF43150C246E9\">\r\n\t\t<Canvas Width=\"512\" Height=\"512\">\r\n\t\t\t<Canvas.RenderTransform>\r\n\t\t\t\t<TranslateTransform X=\"0\" Y=\"0\" />\r\n\t\t\t</Canvas.RenderTransform>\r\n\t\t\t<Canvas.Resources>\r\n\t\t\t\t<LinearGradientBrush xmlns:x=\"http://schemas.microsoft.com/winfx/2006/xaml\" x:Key=\"color-1\" MappingMode=\"Absolute\" StartPoint=\"0,112\" EndPoint=\"0,400\">\r\n\t\t\t\t\t<LinearGradientBrush.GradientStops>\r\n\t\t\t\t\t\t<GradientStopCollection>\r\n\t\t\t\t\t\t\t<GradientStop Offset=\"0\" Color=\"#B3C0CD\" />\r\n\t\t\t\t\t\t\t<GradientStop Offset=\"1\" Color=\"#91A0AF\" />\r\n\t\t\t\t\t\t</GradientStopCollection>\r\n\t\t\t\t\t</LinearGradientBrush.GradientStops>\r\n\t\t\t\t</LinearGradientBrush>\r\n\t\t\t\t<LinearGradientBrush xmlns:x=\"http://schemas.microsoft.com/winfx/2006/xaml\" x:Key=\"color-2\" MappingMode=\"Absolute\" StartPoint=\"32,144\" EndPoint=\"32,368\">\r\n\t\t\t\t\t<LinearGradientBrush.GradientStops>\r\n\t\t\t\t\t\t<GradientStopCollection>\r\n\t\t\t\t\t\t\t<GradientStop Offset=\"0\" Color=\"#ECF7FE\" />\r\n\t\t\t\t\t\t\t<GradientStop Offset=\"1\" Color=\"#CFEBFC\" />\r\n\t\t\t\t\t\t</GradientStopCollection>\r\n\t\t\t\t\t</LinearGradientBrush.GradientStops>\r\n\t\t\t\t</LinearGradientBrush>\r\n\t\t\t\t<LinearGradientBrush xmlns:x=\"http://schemas.microsoft.com/winfx/2006/xaml\" x:Key=\"color-3\" MappingMode=\"Absolute\" StartPoint=\"0,112\" EndPoint=\"151,360\">\r\n\t\t\t\t\t<LinearGradientBrush.GradientStops>\r\n\t\t\t\t\t\t<GradientStopCollection>\r\n\t\t\t\t\t\t\t<GradientStop Offset=\"0\" Color=\"#717F8F\" />\r\n\t\t\t\t\t\t\t<GradientStop Offset=\"1\" Color=\"#51616F\" />\r\n\t\t\t\t\t\t</GradientStopCollection>\r\n\t\t\t\t\t</LinearGradientBrush.GradientStops>\r\n\t\t\t\t</LinearGradientBrush>\r\n\t\t\t</Canvas.Resources>\r\n\t\t\t<!--  Unknown tag: metadata  -->\r\n\t\t\t<!--  Unknown tag: sodipodi:namedview  -->\r\n\t\t\t<Canvas>\r\n\t\t\t\t<Path\r\n\t\t\t\t\txmlns:x=\"http://schemas.microsoft.com/winfx/2006/xaml\"\r\n\t\t\t\t\tFill=\"{StaticResource color-1}\"\r\n\t\t\t\t\tStrokeDashArray=\"\"\r\n\t\t\t\t\tStrokeEndLineCap=\"Flat\"\r\n\t\t\t\t\tStrokeLineJoin=\"Miter\"\r\n\t\t\t\t\tStrokeMiterLimit=\"10\"\r\n\t\t\t\t\tStrokeStartLineCap=\"Flat\"\r\n\t\t\t\t\tStrokeThickness=\"1\">\r\n\t\t\t\t\t<Path.Data>\r\n\t\t\t\t\t\t<PathGeometry Figures=\"M368 112c79.5 0 144 64.5 144 144c0 79.5 -64.5 144 -144 144l-224 0c-79.5 0 -144 -64.5 -144 -144c0 -79.5 64.5 -144 144 -144z\" FillRule=\"EvenOdd\" />\r\n\t\t\t\t\t</Path.Data>\r\n\t\t\t\t</Path>\r\n\t\t\t\t<Path\r\n\t\t\t\t\txmlns:x=\"http://schemas.microsoft.com/winfx/2006/xaml\"\r\n\t\t\t\t\tFill=\"{StaticResource color-2}\"\r\n\t\t\t\t\tStrokeDashArray=\"\"\r\n\t\t\t\t\tStrokeEndLineCap=\"Flat\"\r\n\t\t\t\t\tStrokeLineJoin=\"Miter\"\r\n\t\t\t\t\tStrokeMiterLimit=\"10\"\r\n\t\t\t\t\tStrokeStartLineCap=\"Flat\"\r\n\t\t\t\t\tStrokeThickness=\"1\">\r\n\t\t\t\t\t<Path.Data>\r\n\t\t\t\t\t\t<PathGeometry Figures=\"M368 144l-224 0c-62 -0.5 -112 50.5 -112 112c0 61.5 50 112.5 112 112l224 0c62 0.5 112 -50.5 112 -112c0 -61.5 -50 -112.5 -112 -112z\" FillRule=\"EvenOdd\" />\r\n\t\t\t\t\t</Path.Data>\r\n\t\t\t\t</Path>\r\n\t\t\t\t<Path\r\n\t\t\t\t\txmlns:x=\"http://schemas.microsoft.com/winfx/2006/xaml\"\r\n\t\t\t\t\tFill=\"#FFC5E7FB\"\r\n\t\t\t\t\tStrokeDashArray=\"\"\r\n\t\t\t\t\tStrokeEndLineCap=\"Flat\"\r\n\t\t\t\t\tStrokeLineJoin=\"Miter\"\r\n\t\t\t\t\tStrokeMiterLimit=\"10\"\r\n\t\t\t\t\tStrokeStartLineCap=\"Flat\"\r\n\t\t\t\t\tStrokeThickness=\"1\">\r\n\t\t\t\t\t<Path.Data>\r\n\t\t\t\t\t\t<PathGeometry Figures=\"M368 144l-224 0c-62 -0.5 -112 50.5 -112 112c0 2.5 0 5.5 0.5 8c4 -58 52 -104.5 111.5 -104l224 0c59.5 -0.5 107.5 46 111.5 104c0.5 -2.5 0.5 -5.5 0.5 -8c0 -61.5 -50 -112.5 -112 -112z\" FillRule=\"EvenOdd\" />\r\n\t\t\t\t\t</Path.Data>\r\n\t\t\t\t</Path>\r\n\t\t\t\t<Path\r\n\t\t\t\t\txmlns:x=\"http://schemas.microsoft.com/winfx/2006/xaml\"\r\n\t\t\t\t\tFill=\"{StaticResource color-3}\"\r\n\t\t\t\t\tStrokeDashArray=\"\"\r\n\t\t\t\t\tStrokeEndLineCap=\"Flat\"\r\n\t\t\t\t\tStrokeLineJoin=\"Miter\"\r\n\t\t\t\t\tStrokeMiterLimit=\"10\"\r\n\t\t\t\t\tStrokeStartLineCap=\"Flat\"\r\n\t\t\t\t\tStrokeThickness=\"1\">\r\n\t\t\t\t\t<Path.Data>\r\n\t\t\t\t\t\t<PathGeometry Figures=\"M144 112c79.5 0 144 64.5 144 144c0 79.5 -64.5 144 -144 144c-79.5 0 -144 -64.5 -144 -144c0 -79.5 64.5 -144 144 -144z\" FillRule=\"EvenOdd\" />\r\n\t\t\t\t\t</Path.Data>\r\n\t\t\t\t</Path>\r\n\t\t\t\t<Path\r\n\t\t\t\t\txmlns:x=\"http://schemas.microsoft.com/winfx/2006/xaml\"\r\n\t\t\t\t\tFill=\"#FFD8E0E8\"\r\n\t\t\t\t\tStrokeDashArray=\"\"\r\n\t\t\t\t\tStrokeEndLineCap=\"Flat\"\r\n\t\t\t\t\tStrokeLineJoin=\"Miter\"\r\n\t\t\t\t\tStrokeMiterLimit=\"10\"\r\n\t\t\t\t\tStrokeStartLineCap=\"Flat\"\r\n\t\t\t\t\tStrokeThickness=\"1\">\r\n\t\t\t\t\t<Path.Data>\r\n\t\t\t\t\t\t<PathGeometry Figures=\"M144 175c44.5 0 81 36.5 81 81c0 44.5 -36.5 81 -81 81c-44.5 0 -81 -36.5 -81 -81c0 -44.5 36.5 -81 81 -81z\" FillRule=\"EvenOdd\" />\r\n\t\t\t\t\t</Path.Data>\r\n\t\t\t\t</Path>\r\n\t\t\t</Canvas>\r\n\t\t</Canvas>\r\n\t</Viewbox>\r\n\t<Viewbox\r\n\t\txmlns=\"http://schemas.microsoft.com/winfx/2006/xaml/presentation\"\r\n\t\txmlns:p1=\"http://schemas.microsoft.com/winfx/2006/xaml\"\r\n\t\tp1:Key=\"Icon_ToggleOn\"\r\n\t\tp1:Shared=\"False\"\r\n\t\tStretch=\"Uniform\"\r\n\t\tTag=\"SHA256_11B264CBEF6A05C54582124AE7E1EB497A3F250E11BD6DEE9C4968CDF08A8FC5\">\r\n\t\t<Canvas Width=\"512\" Height=\"512\">\r\n\t\t\t<Canvas.RenderTransform>\r\n\t\t\t\t<TranslateTransform X=\"0\" Y=\"0\" />\r\n\t\t\t</Canvas.RenderTransform>\r\n\t\t\t<Canvas.Resources>\r\n\t\t\t\t<LinearGradientBrush xmlns:x=\"http://schemas.microsoft.com/winfx/2006/xaml\" x:Key=\"color-1\" MappingMode=\"Absolute\" StartPoint=\"0,112\" EndPoint=\"0,400\">\r\n\t\t\t\t\t<LinearGradientBrush.GradientStops>\r\n\t\t\t\t\t\t<GradientStopCollection>\r\n\t\t\t\t\t\t\t<GradientStop Offset=\"0\" Color=\"#B3C0CD\" />\r\n\t\t\t\t\t\t\t<GradientStop Offset=\"1\" Color=\"#91A0AF\" />\r\n\t\t\t\t\t\t</GradientStopCollection>\r\n\t\t\t\t\t</LinearGradientBrush.GradientStops>\r\n\t\t\t\t</LinearGradientBrush>\r\n\t\t\t\t<LinearGradientBrush xmlns:x=\"http://schemas.microsoft.com/winfx/2006/xaml\" x:Key=\"color-2\" MappingMode=\"Absolute\" StartPoint=\"32,144\" EndPoint=\"32,368\">\r\n\t\t\t\t\t<LinearGradientBrush.GradientStops>\r\n\t\t\t\t\t\t<GradientStopCollection>\r\n\t\t\t\t\t\t\t<GradientStop Offset=\"0\" Color=\"#77DA26\" />\r\n\t\t\t\t\t\t\t<GradientStop Offset=\"1\" Color=\"#59A31D\" />\r\n\t\t\t\t\t\t</GradientStopCollection>\r\n\t\t\t\t\t</LinearGradientBrush.GradientStops>\r\n\t\t\t\t</LinearGradientBrush>\r\n\t\t\t\t<LinearGradientBrush xmlns:x=\"http://schemas.microsoft.com/winfx/2006/xaml\" x:Key=\"color-3\" MappingMode=\"Absolute\" StartPoint=\"224,112\" EndPoint=\"375,360\">\r\n\t\t\t\t\t<LinearGradientBrush.GradientStops>\r\n\t\t\t\t\t\t<GradientStopCollection>\r\n\t\t\t\t\t\t\t<GradientStop Offset=\"0\" Color=\"#717F8F\" />\r\n\t\t\t\t\t\t\t<GradientStop Offset=\"1\" Color=\"#51616F\" />\r\n\t\t\t\t\t\t</GradientStopCollection>\r\n\t\t\t\t\t</LinearGradientBrush.GradientStops>\r\n\t\t\t\t</LinearGradientBrush>\r\n\t\t\t</Canvas.Resources>\r\n\t\t\t<!--  Unknown tag: metadata  -->\r\n\t\t\t<!--  Unknown tag: sodipodi:namedview  -->\r\n\t\t\t<Canvas>\r\n\t\t\t\t<Path\r\n\t\t\t\t\txmlns:x=\"http://schemas.microsoft.com/winfx/2006/xaml\"\r\n\t\t\t\t\tFill=\"{StaticResource color-1}\"\r\n\t\t\t\t\tStrokeDashArray=\"\"\r\n\t\t\t\t\tStrokeEndLineCap=\"Flat\"\r\n\t\t\t\t\tStrokeLineJoin=\"Miter\"\r\n\t\t\t\t\tStrokeMiterLimit=\"10\"\r\n\t\t\t\t\tStrokeStartLineCap=\"Flat\"\r\n\t\t\t\t\tStrokeThickness=\"1\">\r\n\t\t\t\t\t<Path.Data>\r\n\t\t\t\t\t\t<PathGeometry Figures=\"M368 112c79.5 0 144 64.5 144 144c0 79.5 -64.5 144 -144 144l-224 0c-79.5 0 -144 -64.5 -144 -144c0 -79.5 64.5 -144 144 -144z\" FillRule=\"EvenOdd\" />\r\n\t\t\t\t\t</Path.Data>\r\n\t\t\t\t</Path>\r\n\t\t\t\t<Path\r\n\t\t\t\t\txmlns:x=\"http://schemas.microsoft.com/winfx/2006/xaml\"\r\n\t\t\t\t\tFill=\"{StaticResource color-2}\"\r\n\t\t\t\t\tStrokeDashArray=\"\"\r\n\t\t\t\t\tStrokeEndLineCap=\"Flat\"\r\n\t\t\t\t\tStrokeLineJoin=\"Miter\"\r\n\t\t\t\t\tStrokeMiterLimit=\"10\"\r\n\t\t\t\t\tStrokeStartLineCap=\"Flat\"\r\n\t\t\t\t\tStrokeThickness=\"1\">\r\n\t\t\t\t\t<Path.Data>\r\n\t\t\t\t\t\t<PathGeometry Figures=\"M368 144l-224 0c-62 -0.5 -112 50.5 -112 112c0 61.5 50 112.5 112 112l0 0l224 0c62 0.5 112 -50.5 112 -112c0 -61.5 -50 -112.5 -112 -112z\" FillRule=\"EvenOdd\" />\r\n\t\t\t\t\t</Path.Data>\r\n\t\t\t\t</Path>\r\n\t\t\t\t<Path\r\n\t\t\t\t\txmlns:x=\"http://schemas.microsoft.com/winfx/2006/xaml\"\r\n\t\t\t\t\tFill=\"#FF59A31D\"\r\n\t\t\t\t\tStrokeDashArray=\"\"\r\n\t\t\t\t\tStrokeEndLineCap=\"Flat\"\r\n\t\t\t\t\tStrokeLineJoin=\"Miter\"\r\n\t\t\t\t\tStrokeMiterLimit=\"10\"\r\n\t\t\t\t\tStrokeStartLineCap=\"Flat\"\r\n\t\t\t\t\tStrokeThickness=\"1\">\r\n\t\t\t\t\t<Path.Data>\r\n\t\t\t\t\t\t<PathGeometry Figures=\"M368 144l0 0l-224 0c-62 -0.5 -112 50.5 -112 112c0 2.5 0 5.5 0.5 8c4 -58 52 -104.5 111.5 -104l224 0l0 0c59.5 -0.5 107.5 46 111.5 104c0.5 -2.5 0.5 -5.5 0.5 -8c0 -61.5 -50 -112.5 -112 -112z\" FillRule=\"EvenOdd\" />\r\n\t\t\t\t\t</Path.Data>\r\n\t\t\t\t</Path>\r\n\t\t\t\t<Path\r\n\t\t\t\t\txmlns:x=\"http://schemas.microsoft.com/winfx/2006/xaml\"\r\n\t\t\t\t\tFill=\"{StaticResource color-3}\"\r\n\t\t\t\t\tStrokeDashArray=\"\"\r\n\t\t\t\t\tStrokeEndLineCap=\"Flat\"\r\n\t\t\t\t\tStrokeLineJoin=\"Miter\"\r\n\t\t\t\t\tStrokeMiterLimit=\"10\"\r\n\t\t\t\t\tStrokeStartLineCap=\"Flat\"\r\n\t\t\t\t\tStrokeThickness=\"1\">\r\n\t\t\t\t\t<Path.Data>\r\n\t\t\t\t\t\t<PathGeometry Figures=\"M368 112c79.5 0 144 64.5 144 144c0 79.5 -64.5 144 -144 144c-79.5 0 -144 -64.5 -144 -144c0 -79.5 64.5 -144 144 -144z\" FillRule=\"EvenOdd\" />\r\n\t\t\t\t\t</Path.Data>\r\n\t\t\t\t</Path>\r\n\t\t\t\t<Path\r\n\t\t\t\t\txmlns:x=\"http://schemas.microsoft.com/winfx/2006/xaml\"\r\n\t\t\t\t\tFill=\"#FFD8E0E8\"\r\n\t\t\t\t\tStrokeDashArray=\"\"\r\n\t\t\t\t\tStrokeEndLineCap=\"Flat\"\r\n\t\t\t\t\tStrokeLineJoin=\"Miter\"\r\n\t\t\t\t\tStrokeMiterLimit=\"10\"\r\n\t\t\t\t\tStrokeStartLineCap=\"Flat\"\r\n\t\t\t\t\tStrokeThickness=\"1\">\r\n\t\t\t\t\t<Path.Data>\r\n\t\t\t\t\t\t<PathGeometry Figures=\"M368 175c44.5 0 81 36.5 81 81c0 44.5 -36.5 81 -81 81c-44.5 0 -81 -36.5 -81 -81c0 -44.5 36.5 -81 81 -81z\" FillRule=\"EvenOdd\" />\r\n\t\t\t\t\t</Path.Data>\r\n\t\t\t\t</Path>\r\n\t\t\t</Canvas>\r\n\t\t</Canvas>\r\n\t</Viewbox>\r\n\t<Viewbox\r\n\t\txmlns=\"http://schemas.microsoft.com/winfx/2006/xaml/presentation\"\r\n\t\txmlns:p1=\"http://schemas.microsoft.com/winfx/2006/xaml\"\r\n\t\tp1:Key=\"Icon_Warning\"\r\n\t\tp1:Shared=\"False\"\r\n\t\tStretch=\"Uniform\"\r\n\t\tTag=\"SHA256_CAC0E9723D380AB62A52C2B59F779388CBA13FB4FFDC04AF7F531FF1B689AB54\">\r\n\t\t<Canvas Width=\"512\" Height=\"512\">\r\n\t\t\t<Canvas.RenderTransform>\r\n\t\t\t\t<TranslateTransform X=\"0\" Y=\"0\" />\r\n\t\t\t</Canvas.RenderTransform>\r\n\t\t\t<Canvas.Resources>\r\n\t\t\t\t<LinearGradientBrush xmlns:x=\"http://schemas.microsoft.com/winfx/2006/xaml\" x:Key=\"color-1\" MappingMode=\"Absolute\" StartPoint=\"0,33\" EndPoint=\"277,428\">\r\n\t\t\t\t\t<LinearGradientBrush.GradientStops>\r\n\t\t\t\t\t\t<GradientStopCollection>\r\n\t\t\t\t\t\t\t<GradientStop Offset=\"0\" Color=\"#FFD041\" />\r\n\t\t\t\t\t\t\t<GradientStop Offset=\"1\" Color=\"#FFC105\" />\r\n\t\t\t\t\t\t</GradientStopCollection>\r\n\t\t\t\t\t</LinearGradientBrush.GradientStops>\r\n\t\t\t\t</LinearGradientBrush>\r\n\t\t\t\t<LinearGradientBrush xmlns:x=\"http://schemas.microsoft.com/winfx/2006/xaml\" x:Key=\"color-2\" MappingMode=\"Absolute\" StartPoint=\"286,432\" EndPoint=\"227,196\">\r\n\t\t\t\t\t<LinearGradientBrush.GradientStops>\r\n\t\t\t\t\t\t<GradientStopCollection>\r\n\t\t\t\t\t\t\t<GradientStop Offset=\"0\" Color=\"#50606E\" />\r\n\t\t\t\t\t\t\t<GradientStop Offset=\"1\" Color=\"#3A454F\" />\r\n\t\t\t\t\t\t</GradientStopCollection>\r\n\t\t\t\t\t</LinearGradientBrush.GradientStops>\r\n\t\t\t\t</LinearGradientBrush>\r\n\t\t\t</Canvas.Resources>\r\n\t\t\t<!--  Unknown tag: metadata  -->\r\n\t\t\t<!--  Unknown tag: sodipodi:namedview  -->\r\n\t\t\t<Canvas>\r\n\t\t\t\t<Path\r\n\t\t\t\t\txmlns:x=\"http://schemas.microsoft.com/winfx/2006/xaml\"\r\n\t\t\t\t\tFill=\"{StaticResource color-1}\"\r\n\t\t\t\t\tStrokeDashArray=\"\"\r\n\t\t\t\t\tStrokeEndLineCap=\"Flat\"\r\n\t\t\t\t\tStrokeLineJoin=\"Miter\"\r\n\t\t\t\t\tStrokeMiterLimit=\"10\"\r\n\t\t\t\t\tStrokeStartLineCap=\"Flat\"\r\n\t\t\t\t\tStrokeThickness=\"1\">\r\n\t\t\t\t\t<Path.Data>\r\n\t\t\t\t\t\t<PathGeometry Figures=\"M270.5 41l119.5 207l119.5 207c3.5 5.5 3.5 11.5 0 17c-3 5.5 -8 8.5 -14.5 8.5l-239 0l-239 0c-6.5 0 -11.5 -3 -14.5 -8.5c-3.5 -5.5 -3.5 -11.5 0 -17l119.5 -207l119.5 -207c3 -5.5 8.5 -8.5 14.5 -8.5c6 0 11.5 3 14.5 8.5z\" FillRule=\"EvenOdd\" />\r\n\t\t\t\t\t</Path.Data>\r\n\t\t\t\t</Path>\r\n\t\t\t\t<Path\r\n\t\t\t\t\txmlns:x=\"http://schemas.microsoft.com/winfx/2006/xaml\"\r\n\t\t\t\t\tFill=\"{StaticResource color-2}\"\r\n\t\t\t\t\tStrokeDashArray=\"\"\r\n\t\t\t\t\tStrokeEndLineCap=\"Flat\"\r\n\t\t\t\t\tStrokeLineJoin=\"Miter\"\r\n\t\t\t\t\tStrokeMiterLimit=\"10\"\r\n\t\t\t\t\tStrokeStartLineCap=\"Flat\"\r\n\t\t\t\t\tStrokeThickness=\"1\">\r\n\t\t\t\t\t<Path.Data>\r\n\t\t\t\t\t\t<PathGeometry Figures=\"M278 355.5l-43.5 0l-7 -159.5l57.5 0z M226 405c0 -8 3 -14.5 8.5 -19.5c6 -5 13 -7.5 21.5 -7.5c8.5 0 15.5 2.5 21.5 7.5c5.5 5 8.5 11.5 8.5 19.5c0 8 -3 14.5 -8.5 19.5c-6 5 -13 7.5 -21.5 7.5c-8.5 0 -15.5 -2.5 -21.5 -7.5c-5.5 -5 -8.5 -11.5 -8.5 -19.5z\" FillRule=\"EvenOdd\" />\r\n\t\t\t\t\t</Path.Data>\r\n\t\t\t\t</Path>\r\n\t\t\t</Canvas>\r\n\t\t</Canvas>\r\n\t</Viewbox>\r\n</ResourceDictionary>\r\n"
  },
  {
    "path": "FocusLogger/JocysCom/Controls/Themes/Icons.xaml.cs",
    "content": "using System.Windows;\r\n\r\nnamespace JocysCom.ClassLibrary.Controls.Themes\r\n{\r\n\tpartial class Icons : ResourceDictionary\r\n\t{\r\n\t\tpublic Icons()\r\n\t\t{\r\n\t\t\tInitializeComponent();\r\n\t\t}\r\n\r\n\t\tpublic static Icons Current => _Current = _Current ?? new Icons();\r\n\t\tprivate static Icons _Current;\r\n\r\n\t\tpublic const string Icon_Add = nameof(Icon_Add);\r\n\t\tpublic const string Icon_Cancel = nameof(Icon_Cancel);\r\n\t\tpublic const string Icon_Cleanup = nameof(Icon_Cleanup);\r\n\t\tpublic const string Icon_Delete = nameof(Icon_Delete);\r\n\t\tpublic const string Icon_Edit = nameof(Icon_Edit);\r\n\t\tpublic const string Icon_Error = nameof(Icon_Error);\r\n\t\tpublic const string Icon_Exit = nameof(Icon_Exit);\r\n\t\tpublic const string Icon_Export = nameof(Icon_Export);\r\n\t\tpublic const string Icon_FolderOpen = nameof(Icon_FolderOpen);\r\n\t\tpublic const string Icon_Ignore = nameof(Icon_Ignore);\r\n\t\tpublic const string Icon_Import = nameof(Icon_Import);\r\n\t\tpublic const string Icon_Information = nameof(Icon_Information);\r\n\t\tpublic const string Icon_InformationGrey = nameof(Icon_InformationGrey);\r\n\t\tpublic const string Icon_Maximize = nameof(Icon_Maximize);\r\n\t\tpublic const string Icon_MediaPlay = nameof(Icon_MediaPlay);\r\n\t\tpublic const string Icon_MediaStop = nameof(Icon_MediaStop);\r\n\t\tpublic const string Icon_Minimize = nameof(Icon_Minimize);\r\n\t\tpublic const string Icon_OK = nameof(Icon_OK);\r\n\t\tpublic const string Icon_Play = nameof(Icon_Play);\r\n\t\tpublic const string Icon_ProcessLeft = nameof(Icon_ProcessLeft);\r\n\t\tpublic const string Icon_ProcessRight = nameof(Icon_ProcessRight);\r\n\t\tpublic const string Icon_Question = nameof(Icon_Question);\r\n\t\tpublic const string Icon_Record = nameof(Icon_Record);\r\n\t\tpublic const string Icon_Refresh = nameof(Icon_Refresh);\r\n\t\tpublic const string Icon_Remove = nameof(Icon_Remove);\r\n\t\tpublic const string Icon_Reset = nameof(Icon_Reset);\r\n\t\tpublic const string Icon_Save = nameof(Icon_Save);\r\n\t\tpublic const string Icon_SelectAll = nameof(Icon_SelectAll);\r\n\t\tpublic const string Icon_SelectInverse = nameof(Icon_SelectInverse);\r\n\t\tpublic const string Icon_SelectNone = nameof(Icon_SelectNone);\r\n\t\tpublic const string Icon_Stop = nameof(Icon_Stop);\r\n\t\tpublic const string Icon_ToggleOff = nameof(Icon_ToggleOff);\r\n\t\tpublic const string Icon_ToggleOn = nameof(Icon_ToggleOn);\r\n\t\tpublic const string Icon_Warning = nameof(Icon_Warning);\r\n\r\n\t}\r\n}\r\n"
  },
  {
    "path": "FocusLogger/JocysCom/Controls/ToolStripBorderlessRenderer.cs",
    "content": "﻿using System.Windows.Forms;\r\n\r\nnamespace JocysCom.ClassLibrary.Controls\r\n{\r\n\t/// <summary>\r\n\t/// Remove white borders from toolstrip.\r\n\t/// How to use:\r\n\t/// toolStrip.Renderer = new ToolStripBorderlessRenderer();\r\n\t/// </summary>\r\n\tpublic class ToolStripBorderlessRenderer : ToolStripSystemRenderer\r\n\t{\r\n\t\tprotected override void OnRenderToolStripBorder(ToolStripRenderEventArgs e)\r\n\t\t{\r\n\t\t\tif (e.ToolStrip.GetType() == typeof(ToolStrip))\r\n\t\t\t{\r\n\t\t\t\t// skip render border\r\n\t\t\t}\r\n\t\t\telse\r\n\t\t\t{\r\n\t\t\t\t// do render border\r\n\t\t\t\tbase.OnRenderToolStripBorder(e);\r\n\t\t\t}\r\n\t\t}\r\n\r\n\t}\r\n}\r\n"
  },
  {
    "path": "FocusLogger/JocysCom/Data/SqlHelper.Types.cs",
    "content": "using System;\r\n#if NETFRAMEWORK\r\nusing System.Data.SqlClient;\r\n#else\r\n#endif\r\n\r\nnamespace JocysCom.ClassLibrary.Data\r\n{\r\n\tpublic partial class SqlHelper\r\n\t{\r\n\t\t/// <summary>Determines whether the given SQL type string supports a size specification (e.g. CHAR, VARCHAR, BINARY).</summary>\r\n\t\tpublic static bool HaveSize(string sqlType)\r\n\t\t{\r\n\t\t\tvar name = sqlType.ToLower();\r\n\t\t\treturn name.Contains(\"char\") || name.Contains(\"binary\");\r\n\t\t}\r\n\r\n\t\t/// <summary>Determines whether the given SQL type string represents a Unicode type (e.g. NCHAR, NVARCHAR).</summary>\r\n\t\tpublic static bool IsUnicode(string sqlType)\r\n\t\t{\r\n\t\t\tvar name = sqlType.ToLower();\r\n\t\t\treturn name.Contains(\"nchar\") || name.Contains(\"nvarchar\");\r\n\t\t}\r\n\r\n\t\t/// <summary>Maps a SQL Server type name to its Visual Basic built-in type alias (e.g. Date, Integer) or returns the CLR type name if no VB alias exists.</summary>\r\n\t\tpublic static string ToSystemTypeVB(string sqlType)\r\n\t\t{\r\n\t\t\tvar t = ToSystemType(sqlType);\r\n\t\t\tif (t == typeof(DateTime)) return \"Date\";\r\n\t\t\tif (t == typeof(Int16)) return \"Short\";\r\n\t\t\tif (t == typeof(Int32)) return \"Integer\";\r\n\t\t\tif (t == typeof(Int64)) return \"Long\";\r\n\t\t\tif (t == typeof(UInt16)) return \"UShort\";\r\n\t\t\tif (t == typeof(UInt32)) return \"UInteger\";\r\n\t\t\tif (t == typeof(UInt64)) return \"ULong\";\r\n\t\t\treturn t.Name;\r\n\t\t}\r\n\r\n\t\t/// <summary>Maps a SQL Server type name to its C# built-in type alias (e.g. int, string) or returns the CLR type name if no alias exists.</summary>\r\n\t\tpublic static string ToSystemTypeCS(string sqlType)\r\n\t\t{\r\n\t\t\tvar t = ToSystemType(sqlType);\r\n\t\t\tvar name = ClassLibrary.Runtime.RuntimeHelper.GetBuiltInTypeNameOrAlias(t);\r\n\t\t\treturn name;\r\n\t\t}\r\n\r\n\t\t/// <summary>Infers the CLR System.Type for the specified SQL Server data type name.</summary>\r\n\t\tpublic static Type ToSystemType(string sqlTypeSring)\r\n\t\t{\r\n\t\t\tvar sqlType = (SqlDataType)Enum.Parse(typeof(SqlDataType), sqlTypeSring, true);\r\n\t\t\tvar t = typeof(String);\r\n\t\t\tswitch (sqlType)\r\n\t\t\t{\r\n\t\t\t\tcase SqlDataType.BigInt:\r\n\t\t\t\t\tt = typeof(Int64);\r\n\t\t\t\t\tbreak;\r\n\t\t\t\tcase SqlDataType.Bit:\r\n\t\t\t\t\tt = typeof(Boolean);\r\n\t\t\t\t\tbreak;\r\n\t\t\t\tcase SqlDataType.Char:\r\n\t\t\t\tcase SqlDataType.VarChar:\r\n\t\t\t\tcase SqlDataType.VarCharMax:\r\n\t\t\t\t\tt = typeof(String);\r\n\t\t\t\t\tbreak;\r\n\t\t\t\tcase SqlDataType.Date:\r\n\t\t\t\tcase SqlDataType.DateTime:\r\n\t\t\t\tcase SqlDataType.DateTime2:\r\n\t\t\t\tcase SqlDataType.SmallDateTime:\r\n\t\t\t\t\tt = typeof(DateTime);\r\n\t\t\t\t\tbreak;\r\n\t\t\t\tcase SqlDataType.DateTimeOffset:\r\n\t\t\t\t\tt = typeof(DateTimeOffset);\r\n\t\t\t\t\tbreak;\r\n\t\t\t\tcase SqlDataType.Time:\r\n\t\t\t\t\tt = typeof(TimeSpan);\r\n\t\t\t\t\tbreak;\r\n\t\t\t\tcase SqlDataType.Decimal:\r\n\t\t\t\tcase SqlDataType.Money:\r\n\t\t\t\tcase SqlDataType.SmallMoney:\r\n\t\t\t\t\tt = typeof(Decimal);\r\n\t\t\t\t\tbreak;\r\n\t\t\t\tcase SqlDataType.Int:\r\n\t\t\t\t\tt = typeof(Int32);\r\n\t\t\t\t\tbreak;\r\n\t\t\t\tcase SqlDataType.NChar:\r\n\t\t\t\tcase SqlDataType.NText:\r\n\t\t\t\tcase SqlDataType.NVarChar:\r\n\t\t\t\tcase SqlDataType.NVarCharMax:\r\n\t\t\t\tcase SqlDataType.Text:\r\n\t\t\t\t\tt = typeof(String);\r\n\t\t\t\t\tbreak;\r\n\t\t\t\tcase SqlDataType.Real:\r\n\t\t\t\tcase SqlDataType.Numeric:\r\n\t\t\t\tcase SqlDataType.Float:\r\n\t\t\t\t\tt = typeof(Double);\r\n\t\t\t\t\tbreak;\r\n\t\t\t\tcase SqlDataType.Timestamp:\r\n\t\t\t\tcase SqlDataType.Binary:\r\n\t\t\t\t\tt = typeof(Byte[]);\r\n\t\t\t\t\tbreak;\r\n\t\t\t\tcase SqlDataType.TinyInt:\r\n\t\t\t\t\tt = typeof(Byte);\r\n\t\t\t\t\tbreak;\r\n\t\t\t\tcase SqlDataType.SmallInt:\r\n\t\t\t\t\tt = typeof(Int16);\r\n\t\t\t\t\tbreak;\r\n\t\t\t\tcase SqlDataType.UniqueIdentifier:\r\n\t\t\t\t\tt = typeof(Guid);\r\n\t\t\t\t\tbreak;\r\n\t\t\t\tcase SqlDataType.UserDefinedDataType:\r\n\t\t\t\tcase SqlDataType.UserDefinedType:\r\n\t\t\t\tcase SqlDataType.Variant:\r\n\t\t\t\tcase SqlDataType.Image:\r\n\t\t\t\t\tt = typeof(Object);\r\n\t\t\t\t\tbreak;\r\n\t\t\t\tdefault:\r\n\t\t\t\t\tt = typeof(String);\r\n\t\t\t\t\tbreak;\r\n\t\t\t}\r\n\t\t\treturn t;\r\n\t\t}\r\n\r\n\t\t/// <summary>Infers the SqlDataType for a given CLR TypeCode.</summary>\r\n\t\t/// <remarks>\r\n\t\t/// Chooses VarBinary or VarChar when min != max to indicate variable-length;\r\n\t\t/// uses NChar/NVarChar instead of Char/VarChar when isUnicode is true.\r\n\t\t/// </remarks>\r\n\t\tpublic static SqlDataType GetSqlDataType(TypeCode code, int min = 0, int max = 0, bool isUnicode = false)\r\n\t\t{\r\n\t\t\tvar v = min != max;\r\n\t\t\tswitch (code)\r\n\t\t\t{\r\n\t\t\t\tcase TypeCode.Empty: return SqlDataType.None;\r\n\t\t\t\tcase TypeCode.Object: return v ? SqlDataType.VarBinary : SqlDataType.Binary;\r\n\t\t\t\tcase TypeCode.DBNull: return SqlDataType.None;\r\n\t\t\t\tcase TypeCode.Boolean: return SqlDataType.Bit;\r\n\t\t\t\tcase TypeCode.Char: return isUnicode ? SqlDataType.NChar : SqlDataType.Char;\r\n\t\t\t\tcase TypeCode.SByte: return SqlDataType.None;\r\n\t\t\t\tcase TypeCode.Byte: return SqlDataType.TinyInt;\r\n\t\t\t\tcase TypeCode.Int16: return SqlDataType.SmallInt;\r\n\t\t\t\tcase TypeCode.UInt16: return SqlDataType.SmallInt;\r\n\t\t\t\tcase TypeCode.Int32: return SqlDataType.Int;\r\n\t\t\t\tcase TypeCode.UInt32: return SqlDataType.Int;\r\n\t\t\t\tcase TypeCode.Int64: return SqlDataType.BigInt;\r\n\t\t\t\tcase TypeCode.UInt64: return SqlDataType.BigInt;\r\n\t\t\t\tcase TypeCode.Single: return SqlDataType.Real;\r\n\t\t\t\tcase TypeCode.Double: return SqlDataType.Float;\r\n\t\t\t\tcase TypeCode.Decimal: return SqlDataType.Money;\r\n\t\t\t\tcase TypeCode.DateTime: return SqlDataType.DateTime;\r\n\t\t\t\tcase TypeCode.String: return v\r\n\t\t\t\t\t? isUnicode ? SqlDataType.NVarChar : SqlDataType.VarChar \r\n\t\t\t\t\t: isUnicode ? SqlDataType.NChar : SqlDataType.Char;\r\n\t\t\t\tdefault: return SqlDataType.None;\r\n\t\t\t}\r\n\t\t}\r\n\r\n\t\t//public static SqlDataType GetSqlDataType(Type giveType)\r\n\t\t//{\r\n\t\t//\t// Allow nullable types to be handled\r\n\t\t//\tgiveType = Nullable.GetUnderlyingType(giveType) ?? giveType;\r\n\t\t//\treturn GetSqlDataType(giveType);\r\n\t\t//}\r\n\r\n\t\t//public static SqlDataType GetSqlDataType<T>()\r\n\t\t//{\r\n\t\t//\treturn GetSqlDataType(typeof(T));\r\n\t\t//}\r\n\r\n\t\t/// <summary>Enumerates SQL Server data types used for mapping between SQL and CLR types.</summary>\r\n\t\tpublic enum SqlDataType\r\n\t\t{\r\n\t\t\tBigInt = 1,\r\n\t\t\tBinary = 2,\r\n\t\t\tBit = 3,\r\n\t\t\tChar = 4,\r\n\t\t\tDate = 36,\r\n\t\t\tDateTime = 6,\r\n\t\t\tDateTime2 = 39,\r\n\t\t\tDateTimeOffset = 38,\r\n\t\t\tDecimal = 7,\r\n\t\t\tFloat = 8,\r\n\t\t\tGeography = 43,\r\n\t\t\tGeometry = 42,\r\n\t\t\tHierarchyId = 41,\r\n\t\t\tImage = 9,\r\n\t\t\tInt = 10,\r\n\t\t\tMoney = 11,\r\n\t\t\tNChar = 12,\r\n\t\t\tNone = 0,\r\n\t\t\tNText = 13,\r\n\t\t\tNumeric = 35,\r\n\t\t\tNVarChar = 14,\r\n\t\t\tNVarCharMax = 15,\r\n\t\t\tReal = 16,\r\n\t\t\tSmallDateTime = 17,\r\n\t\t\tSmallInt = 18,\r\n\t\t\tSmallMoney = 19,\r\n\t\t\tSysName = 34,\r\n\t\t\tText = 20,\r\n\t\t\tTime = 37,\r\n\t\t\tTimestamp = 21,\r\n\t\t\tTinyInt = 22,\r\n\t\t\tUniqueIdentifier = 23,\r\n\t\t\tUserDefinedDataType = 24,\r\n\t\t\tUserDefinedTableType = 40,\r\n\t\t\tUserDefinedType = 25,\r\n\t\t\tVarBinary = 28,\r\n\t\t\tVarBinaryMax = 29,\r\n\t\t\tVarChar = 30,\r\n\t\t\tVarCharMax = 31,\r\n\t\t\tVariant = 32,\r\n\t\t\tXml = 33\r\n\t\t}\r\n\t}\r\n}\r\n"
  },
  {
    "path": "FocusLogger/JocysCom/IO/PathHelper.cs",
    "content": "using System;\r\nusing System.Collections.Generic;\r\nusing System.IO;\r\nusing System.Linq;\r\n\r\nnamespace JocysCom.ClassLibrary.IO\r\n{\r\n\t/// <summary>\r\n\t/// Summary description for PathConvert.\r\n\t/// </summary>\r\n\tpublic static partial class PathHelper\r\n\t{\r\n\t\tconst string nullOrEmpty = \"Input cannot be null or empty\";\r\n\r\n\t\tpublic static void ValidateFileName(string name)\r\n\t\t{\r\n\t\t\tif (name is null)\r\n\t\t\t\tthrow new ArgumentNullException(nameof(name));\r\n\t\t\tvar invalid = name.Intersect(System.IO.Path.GetInvalidFileNameChars());\r\n\t\t\tif (invalid.Any())\r\n\t\t\t\tthrow new ArgumentException(string.Format(\"Invalid file name chars found: {0}\", string.Join(\", \", invalid)), nameof(name));\r\n\t\t}\r\n\r\n\t\t/// <summary>\r\n\t\t/// Method used to protect from\r\n\t\t/// SUPPRESS: CWE-78: Improper Neutralization of Special Elements used in an OS Command ('OS Command Injection')\r\n\t\t/// https://cwe.mitre.org/data/definitions/78.html\r\n\t\t/// </summary>\r\n\t\t/// <param name=\"path\"></param>\r\n\t\tpublic static void ValidatePath(string path)\r\n\t\t{\r\n\t\t\tvar d = System.IO.Path.GetDirectoryName(path);\r\n\t\t\tvar invalid = d.Intersect(System.IO.Path.GetInvalidPathChars());\r\n\t\t\tif (invalid.Any())\r\n\t\t\t\tthrow new ArgumentException(string.Format(\"Invalid path chars found: {0}\", string.Join(\", \", invalid)), nameof(path));\r\n\t\t\tvar name = System.IO.Path.GetFileName(path);\r\n\t\t\tValidateFileName(name);\r\n\t\t}\r\n\r\n\t\t#region Detect Type of Path\r\n\r\n\t\t/// <summary>\r\n\t\t/// Virtual path starts from \"/\" or \"\\\" \r\n\t\t/// </summary>\r\n\t\tpublic static bool IsPathVirtual(string path)\r\n\t\t{\r\n\t\t\tif (string.IsNullOrEmpty(path))\r\n\t\t\t\tthrow new ArgumentException(nullOrEmpty, nameof(path));\r\n\t\t\tvar newPath = path.Trim();\r\n\t\t\treturn\r\n\t\t\t\tnewPath.StartsWith(\"/\") ||\r\n\t\t\t\tnewPath.StartsWith(\"\\\\\");\r\n\t\t}\r\n\r\n\t\t/// <summary>\r\n\t\t/// A physical path is how the OS locates the resource.\r\n\t\t/// </summary>\r\n\t\t/// <param name=\"path\"></param>\r\n\t\t/// <returns></returns>\r\n\t\tpublic static bool IsPathPhysical(string path)\r\n\t\t{\r\n\t\t\tif (string.IsNullOrEmpty(path))\r\n\t\t\t\tthrow new ArgumentException(nullOrEmpty, nameof(path));\r\n\t\t\tvar newPath = path.Trim();\r\n\t\t\treturn\r\n\t\t\t\tnewPath.Contains(':') ||\r\n\t\t\t\tnewPath.Contains(\"\\\\\");\r\n\t\t}\r\n\r\n\t\t#endregion\r\n\r\n\t\t// Get Physical path from any type of path.\r\n\t\tpublic static string GetPathPhysical(string relativeTo, string path)\r\n\t\t{\r\n\t\t\tif (path is null)\r\n\t\t\t\tthrow new ArgumentNullException(nameof(path));\r\n\t\t\tstring physicalPath;\r\n\t\t\t// If path is Physical. \r\n\t\t\tif (IsPathPhysical(path))\r\n\t\t\t{\r\n\t\t\t\t// Return current path\r\n\t\t\t\tphysicalPath = path;\r\n\t\t\t}\r\n\t\t\telse\r\n\t\t\t{\r\n\t\t\t\t// Convert to physical.\r\n\t\t\t\tphysicalPath = relativeTo + \"/\" + path;\r\n\t\t\t\t// Convert \"/\" separator to \"\\\".\r\n\t\t\t\tphysicalPath = physicalPath.Replace(\"/\", \"\\\\\");\r\n\t\t\t\t// Remove double \"\\\\\".\r\n\t\t\t\tphysicalPath = physicalPath.Replace(\"\\\\\\\\\", \"\\\\\");\r\n\t\t\t}\r\n\t\t\t// Return results.\r\n\t\t\treturn physicalPath;\r\n\t\t}\r\n\r\n\t\t// Get Rooted path.\r\n\t\tpublic static string GetPathRooted(string relativeTo, string path)\r\n\t\t{\r\n\t\t\tif (path is null) throw new ArgumentNullException(nameof(path));\r\n\t\t\t//if path already rooted then...\r\n\t\t\tif (System.IO.Path.IsPathRooted(path))\r\n\t\t\t\treturn path;\r\n\t\t\t// Combine path.\r\n#if NETFRAMEWORK\r\n\t\t\tvar combined = System.IO.Path.Combine(relativeTo, path);\r\n#else\r\n\t\t\tvar combined = System.IO.Path.GetFullPath(path, relativeTo);\r\n#endif\r\n\t\t\t// Fix dot notations.\r\n\t\t\tcombined = Path.GetFullPath(combined);\r\n\t\t\treturn combined;\r\n\t\t}\r\n\r\n\t\t/// <summary>\r\n\t\t///    root     parent\r\n\t\t/// ----------- --------\r\n\t\t///             ..\\..\\..\r\n\t\t/// c:\\r1\\r2\\r3\\p1\\p2\\p3\\\\\r\n\t\t/// c:\\r1\\r2\\r3\\a1\\file.ext\r\n\t\t///             ----------- \r\n\t\t///             append\r\n\t\t/// Important: if relative is directory then path must end with '\\' or '/'.\r\n\t\t/// </summary>\r\n\t\t/// <param name=\"relativeTo\"></param>\r\n\t\t/// <param name=\"path\"></param>\r\n\t\t/// <param name=\"addCurrentDir\"></param>\r\n\t\t/// <returns></returns>\r\n\t\tpublic static string GetRelativePath(string relativeTo, string path, bool addCurrentDir = true)\r\n\t\t{\r\n\t\t\tif (string.IsNullOrEmpty(relativeTo))\r\n\t\t\t\tthrow new ArgumentException(nullOrEmpty, nameof(relativeTo));\r\n\t\t\tif (string.IsNullOrEmpty(path))\r\n\t\t\t\tthrow new ArgumentException(nullOrEmpty, nameof(path));\r\n\t\t\tvar containsPrimarySeparator = relativeTo.Contains(Path.DirectorySeparatorChar);\r\n\t\t\trelativeTo = Path.GetFullPath(relativeTo);\r\n\t\t\tpath = Path.GetFullPath(path);\r\n\t\t\tvar relativeToUri = new Uri(relativeTo);\r\n\t\t\tvar pathUri = new Uri(path);\r\n\t\t\tvar relativeUri = relativeToUri.MakeRelativeUri(pathUri).ToString();\r\n\t\t\tvar relativePath = Uri.UnescapeDataString(relativeUri);\r\n\t\t\t// Add current folder prefix if output does not contain separators.\r\n\t\t\tif (addCurrentDir && !relativePath.Contains(Path.DirectorySeparatorChar) && !relativePath.Contains(Path.AltDirectorySeparatorChar))\r\n\t\t\t\trelativePath = \".\" + Path.AltDirectorySeparatorChar + relativePath;\r\n\t\t\t// Make sure that output contains same separator type as input.\r\n\t\t\tif (containsPrimarySeparator)\r\n\t\t\t\trelativePath = relativePath.Replace(Path.AltDirectorySeparatorChar, Path.DirectorySeparatorChar);\r\n\t\t\treturn relativePath;\r\n\t\t}\r\n\r\n\t\t// Get Relative path from any type of path.\r\n\t\tpublic static string GetPathVirtual(string relativeTo, string path)\r\n\t\t{\r\n\t\t\tif (path is null) throw new ArgumentNullException(nameof(path));\r\n\t\t\t// If path is not physical then just return.\r\n\t\t\tif (!IsPathPhysical(path))\r\n\t\t\t\treturn path;\r\n\t\t\t// Convert to virtual path by cropping root part.\r\n\t\t\tvar virtualPath = \"/\" + path.Substring(relativeTo.Length);\r\n\t\t\t// Convert \"\\\" separator to \"/\".\r\n\t\t\tvirtualPath = virtualPath.Replace(\"\\\\\", \"/\");\r\n\t\t\t// Remove double \"//\".\r\n\t\t\tvirtualPath = virtualPath.Replace(\"//\", \"/\");\r\n\t\t\t// Return results.\r\n\t\t\treturn virtualPath;\r\n\t\t}\r\n\r\n\t\t// Get parent folder from path.\r\n\t\tpublic static string GetParentFolderPhysical(string relativeTo, string path)\r\n\t\t{\r\n\t\t\tif (path is null)\r\n\t\t\t\tthrow new ArgumentNullException(nameof(path));\r\n\t\t\t// Get physical path.\r\n\t\t\tvar physicalPath = GetPathPhysical(relativeTo, path);\r\n\t\t\t// Get folder only.\r\n\t\t\tvar folderPath = System.IO.Path.GetDirectoryName(physicalPath);\r\n\t\t\t// Return results.\r\n\t\t\treturn folderPath;\r\n\t\t}\r\n\r\n\t\t// Get parent folder from path.\r\n\t\tpublic static string GetParentFolderVirtual(string relativeTo, string path)\r\n\t\t{\r\n\t\t\tif (path is null)\r\n\t\t\t\tthrow new ArgumentNullException(nameof(path));\r\n\t\t\t// Get physical parent folder path.\r\n\t\t\tvar folderPath = GetParentFolderPhysical(relativeTo, path);\r\n\t\t\t// Convert to virtual path.\r\n\t\t\tvar virtualPath = GetPathVirtual(relativeTo, folderPath);\r\n\t\t\t// Return results.\r\n\t\t\treturn virtualPath;\r\n\t\t}\r\n\r\n\t\tpublic static string GetFileName(string relativeTo, string path)\r\n\t\t{\r\n\t\t\tif (path is null)\r\n\t\t\t\tthrow new ArgumentNullException(nameof(path));\r\n\t\t\t// Make sure that path is physical.\r\n\t\t\tvar physicalPath = GetPathPhysical(relativeTo, path);\r\n\t\t\treturn System.IO.Path.GetFileName(physicalPath);\r\n\t\t}\r\n\r\n\r\n\t\t#region Path Converter\r\n\r\n\t\tstatic object SpecialFoldersLock = new object();\r\n\r\n\t\tstatic Dictionary<string, string> _SpecialFolders;\r\n\r\n\t\tpublic static Dictionary<string, string> SpecialFolders\r\n\t\t{\r\n\t\t\tget\r\n\t\t\t{\r\n\t\t\t\tlock (SpecialFoldersLock)\r\n\t\t\t\t{\r\n\t\t\t\t\tif (_SpecialFolders is null)\r\n\t\t\t\t\t{\r\n\t\t\t\t\t\tvar keys = (System.Environment.SpecialFolder[])Enum.GetValues(typeof(System.Environment.SpecialFolder));\r\n\t\t\t\t\t\tvar items = new List<KeyValuePair<string, string>>();\r\n\t\t\t\t\t\tforeach (var key in keys)\r\n\t\t\t\t\t\t{\r\n\t\t\t\t\t\t\tvar item = new KeyValuePair<string, string>($\"{key}\", System.Environment.GetFolderPath(key));\r\n\t\t\t\t\t\t\t// Make sure all values are not empty and unique.\r\n\t\t\t\t\t\t\tif (!string.IsNullOrEmpty(item.Key) && !string.IsNullOrEmpty(item.Value))\r\n\t\t\t\t\t\t\t\titems.Add(item);\r\n\t\t\t\t\t\t}\r\n\t\t\t\t\t\t_SpecialFolders = new Dictionary<string, string>();\r\n\t\t\t\t\t\t// Order folders descending.\r\n\t\t\t\t\t\tvar list = items.OrderByDescending(x => x.Value).ToArray();\r\n\t\t\t\t\t\tforeach (var listItem in list)\r\n\t\t\t\t\t\t{\r\n\t\t\t\t\t\t\t// If list doesn't contains key then...\r\n\t\t\t\t\t\t\tif (!_SpecialFolders.ContainsKey(listItem.Key))\r\n\t\t\t\t\t\t\t\t_SpecialFolders.Add(listItem.Key, listItem.Value);\r\n\t\t\t\t\t\t}\r\n\t\t\t\t\t}\r\n\t\t\t\t}\r\n\t\t\t\treturn _SpecialFolders;\r\n\t\t\t}\r\n\t\t}\r\n\r\n\t\t#endregion\r\n\r\n\t}\r\n}\r\n"
  },
  {
    "path": "FocusLogger/JocysCom/MakeLinks_Ref.ps1",
    "content": "<#\r\n.SYNOPSIS\r\n    Thin wrapper that calls the original MakeLinks.ps1 from the Core library.\r\n    Place this file in external JocysCom folders instead of duplicating the full script.\r\n#>\r\n$paths = @(\r\n    \"d:\\Projects\\Jocys.com\\ClassLibrary\\Core\\_Resources\\MakeLinks.ps1\",\r\n    \"c:\\Projects\\Jocys.com\\ClassLibrary\\Core\\_Resources\\MakeLinks.ps1\"\r\n);\r\nforeach ($p in $paths) {\r\n    if (Test-Path $p) { & $p; return; }\r\n}\r\nWrite-Host \"ERROR: MakeLinks.ps1 not found in any known location.\" -ForegroundColor Red;\r\n"
  },
  {
    "path": "FocusLogger/JocysCom/Runtime/RuntimeHelper.cs",
    "content": "﻿using System;\r\nusing System.Collections.Concurrent;\r\nusing System.Collections.Generic;\r\nusing System.Data;\r\nusing System.IO;\r\nusing System.Linq;\r\nusing System.Reflection;\r\nusing System.Runtime.InteropServices;\r\nusing System.Runtime.Serialization.Json;\r\nusing System.Text;\r\n\r\nnamespace JocysCom.ClassLibrary.Runtime\r\n{\r\n\tpublic static partial class RuntimeHelper\r\n\t{\r\n\r\n\t\t/*\r\n\t\tpublic static bool IsKnownType(Type type)\r\n\t\t{\r\n\t\t\tif (type is null)\r\n\t\t\t\tthrow new ArgumentNullException(nameof(type));\r\n\t\t\treturn\r\n\t\t\t\ttype == typeof(string)\r\n\t\t\t\t// Note: Every Primitive type (such as int, double, bool, char, etc.) is a ValueType. \r\n\t\t\t\t|| type.IsValueType\r\n\t\t\t\t|| type.IsSerializable;\r\n\t\t}\r\n\t\t*/\r\n\r\n\t\tprivate static readonly HashSet<Type> numericTypes = new HashSet<Type>\r\n\t\t{\r\n\t\t\ttypeof(int),  typeof(double),  typeof(decimal),\r\n\t\t\ttypeof(long), typeof(short),   typeof(sbyte),\r\n\t\t\ttypeof(byte), typeof(ulong),   typeof(ushort),\r\n\t\t\ttypeof(uint), typeof(float),\r\n\t\t};\r\n\r\n\t\tpublic static bool IsNumeric(Type type)\r\n\t\t{\r\n\t\t\treturn numericTypes.Contains(type);\r\n\t\t}\r\n\r\n\t\t/// <summary>Built-in types</summary>\r\n\t\tpublic static readonly Dictionary<Type, string> TypeAliases = new Dictionary<Type, string>\r\n\t\t{\r\n\t\t\t{ typeof(bool), \"bool\" },\r\n\t\t\t{ typeof(byte), \"byte\" },\r\n\t\t\t{ typeof(char), \"char\" },\r\n\t\t\t{ typeof(decimal), \"decimal\" },\r\n\t\t\t{ typeof(double), \"double\" },\r\n\t\t\t{ typeof(float), \"float\" },\r\n\t\t\t{ typeof(int), \"int\" },\r\n\t\t\t{ typeof(long), \"long\" },\r\n\t\t\t{ typeof(object), \"object\" },\r\n\t\t\t{ typeof(sbyte), \"sbyte\" },\r\n\t\t\t{ typeof(short), \"short\" },\r\n\t\t\t{ typeof(string), \"string\" },\r\n\t\t\t{ typeof(uint), \"uint\" },\r\n\t\t\t{ typeof(ulong), \"ulong\" },\r\n\t\t\t{ typeof(ushort), \"ushort\" },\r\n\t\t\t{ typeof(void), \"void\" }\r\n\t\t};\r\n\r\n\t\tpublic static string GetBuiltInTypeNameOrAlias(Type type)\r\n\t\t{\r\n\t\t\tif (type is null)\r\n\t\t\t\tthrow new ArgumentNullException(nameof(type));\r\n\t\t\tvar elementType = type.IsArray\r\n\t\t\t\t? type.GetElementType()\r\n\t\t\t\t: type;\r\n\t\t\t// Lookup alias for type\r\n\t\t\tstring alias;\r\n\t\t\tif (TypeAliases.TryGetValue(elementType, out alias))\r\n\t\t\t\treturn alias + (type.IsArray ? \"[]\" : \"\");\r\n\t\t\t// Note: All Nullable<T> are value types.\r\n\t\t\tif (type.IsValueType)\r\n\t\t\t{\r\n\t\t\t\tvar underType = Nullable.GetUnderlyingType(type);\r\n\t\t\t\tif (underType != null)\r\n\t\t\t\t\treturn GetBuiltInTypeNameOrAlias(underType) + \"?\";\r\n\t\t\t}\r\n\t\t\tif (type.IsGenericType && type.GetGenericTypeDefinition() == typeof(List<>))\r\n\t\t\t{\r\n\t\t\t\tvar itemType = type.GetGenericArguments()[0];\r\n\t\t\t\treturn string.Format(\"List<{0}>\", GetBuiltInTypeNameOrAlias(itemType));\r\n\t\t\t}\r\n\t\t\t// Default to CLR type name\r\n\t\t\treturn type.Name;\r\n\t\t}\r\n\r\n\t\tprivate static Type GetFirstArgumentOfGenericType(Type type)\r\n\t\t{\r\n\t\t\treturn type.GetGenericArguments()[0];\r\n\t\t}\r\n\r\n\t\tpublic static bool IsNullableType(Type type)\r\n\t\t{\r\n\t\t\tif (type is null)\r\n\t\t\t\tthrow new ArgumentNullException(nameof(type));\r\n\t\t\treturn type.IsGenericType\r\n\t\t\t\t? type.GetGenericTypeDefinition() == typeof(Nullable<>)\r\n\t\t\t\t: false;\r\n\t\t}\r\n\r\n\t\tpublic static BindingFlags DefaultBindingFlags = BindingFlags.Instance | BindingFlags.Public | BindingFlags.NonPublic;\r\n\r\n\t\t#region Copy Fields\r\n\r\n\t\t/// <summary>\r\n\t\t/// Get source intersecting fields.\r\n\t\t/// </summary>\r\n\t\tprivate static FieldInfo[] GetItersectingFields(object source, object target)\r\n\t\t{\r\n\t\t\tvar targetNames = target.GetType().GetFields(DefaultBindingFlags).Select(x => x.Name).ToArray();\r\n\t\t\tvar sourceFields = source\r\n\t\t\t\t.GetType()\r\n\t\t\t\t.GetFields(DefaultBindingFlags)\r\n\t\t\t\t.Where(x => targetNames.Contains(x.Name))\r\n\t\t\t\t.ToArray();\r\n\t\t\treturn sourceFields;\r\n\t\t}\r\n\r\n\t\t/// <summary>Cache data for speed.</summary>\r\n\t\t/// <remarks>Cache allows for this class to work 20 times faster.</remarks>\r\n\t\tprivate static ConcurrentDictionary<Type, FieldInfo[]> Fields { get; } = new ConcurrentDictionary<Type, FieldInfo[]>();\r\n\r\n\t\tprivate static FieldInfo[] GetFields(Type t, bool cache = true)\r\n\t\t{\r\n\t\t\tvar items = cache\r\n\t\t\t\t? Fields.GetOrAdd(t, x => t.GetFields(DefaultBindingFlags))\r\n\t\t\t\t: t.GetFields(DefaultBindingFlags);\r\n\t\t\treturn items;\r\n\t\t}\r\n\r\n\r\n\t\t/// <summary>Cache data for speed.</summary>\r\n\t\t/// <remarks>Cache allows for this class to work 20 times faster.</remarks>\r\n\t\tprivate static ConcurrentDictionary<Type, PropertyInfo[]> Properties { get; } = new ConcurrentDictionary<Type, PropertyInfo[]>();\r\n\r\n\t\tprivate static PropertyInfo[] GetProperties(Type t, bool cache = true)\r\n\t\t{\r\n\t\t\tvar items = cache\r\n\t\t\t\t? Properties.GetOrAdd(t, x => t.GetProperties(DefaultBindingFlags))\r\n\t\t\t\t: t.GetProperties(DefaultBindingFlags);\r\n\t\t\treturn items;\r\n\t\t}\r\n\r\n\t\tpublic static void CopyFields(object source, object target, bool onlyNonByRef = false)\r\n\t\t{\r\n\t\t\tif (source is null)\r\n\t\t\t\tthrow new ArgumentNullException(nameof(source));\r\n\t\t\tif (target is null)\r\n\t\t\t\tthrow new ArgumentNullException(nameof(target));\r\n\t\t\t// Get Field Info.\r\n\t\t\tvar sourceFields = GetFields(source.GetType());\r\n\t\t\tvar targetFields = GetFields(target.GetType());\r\n\t\t\tforeach (var sm in sourceFields)\r\n\t\t\t{\r\n\t\t\t\tvar tm = targetFields.FirstOrDefault(x => x.Name == sm.Name);\r\n\t\t\t\tbool useJson;\r\n\t\t\t\tif (!CanCopy(sm.FieldType, tm.FieldType, onlyNonByRef, out useJson))\r\n\t\t\t\t\tcontinue;\r\n\t\t\t\t// Get source value.\r\n\t\t\t\tvar sValue = sm.GetValue(source);\r\n\t\t\t\tif (useJson)\r\n\t\t\t\t\tsValue = Serialize(sValue);\r\n\t\t\t\tvar update = true;\r\n\t\t\t\t// Get target value.\r\n\t\t\t\tvar dValue = tm.GetValue(target);\r\n\t\t\t\tif (useJson)\r\n\t\t\t\t\tdValue = Serialize(dValue);\r\n\t\t\t\t// Update only if values are different.\r\n\t\t\t\tupdate = !Equals(sValue, dValue);\r\n\t\t\t\tif (update)\r\n\t\t\t\t{\r\n\t\t\t\t\tif (useJson)\r\n\t\t\t\t\t\tsValue = Deserialize(sValue as string, tm.FieldType);\r\n\t\t\t\t\ttm.SetValue(target, sValue);\r\n\t\t\t\t}\r\n\t\t\t}\r\n\t\t}\r\n\r\n\t\t#endregion\r\n\r\n\t\t#region Serializer\r\n\r\n\t\t/// <summary>Cache data for speed.</summary>\r\n\t\t/// <remarks>Cache allows for this class to work 20 times faster.</remarks>\r\n\t\tprivate static ConcurrentDictionary<Type, DataContractJsonSerializer> JsonSerializers = new ConcurrentDictionary<Type, DataContractJsonSerializer>();\r\n\r\n\t\tstatic DataContractJsonSerializer GetJsonSerializer(Type type, DataContractJsonSerializerSettings settings = null)\r\n\t\t{\r\n\t\t\tif (type == null)\r\n\t\t\t\treturn null;\r\n\t\t\treturn JsonSerializers.GetOrAdd(type, x => new DataContractJsonSerializer(type, settings));\r\n\t\t}\r\n\r\n\t\tprivate static string Serialize(object o)\r\n\t\t{\r\n\t\t\tif (o is null)\r\n\t\t\t\treturn null;\r\n\t\t\tvar serializer = GetJsonSerializer(o.GetType());\r\n\t\t\tvar ms = new MemoryStream();\r\n\t\t\tlock (serializer) { serializer.WriteObject(ms, o); }\r\n\t\t\tvar json = Encoding.UTF8.GetString(ms.ToArray());\r\n\t\t\tms.Close();\r\n\t\t\treturn json;\r\n\t\t}\r\n\r\n\t\tprivate static object Deserialize(string json, Type type)\r\n\t\t{\r\n\t\t\tif (json is null)\r\n\t\t\t\treturn null;\r\n\t\t\tvar serializer = GetJsonSerializer(type);\r\n\t\t\tvar bytes = Encoding.UTF8.GetBytes(json);\r\n\t\t\tvar ms = new MemoryStream(bytes);\r\n\t\t\tobject o;\r\n\t\t\tlock (serializer) { o = serializer.ReadObject(ms); }\r\n\t\t\tms.Close();\r\n\t\t\treturn o;\r\n\t\t}\r\n\r\n\t\t#endregion\r\n\r\n\t\t#region Copy Properties\r\n\r\n\t\t/// <summary>\r\n\t\t/// Get information about different and intersecting properties.\r\n\t\t/// </summary>\r\n\t\tpublic static string GetPropertyDiffInfo<TSource, TTarget>()\r\n\t\t{\r\n\t\t\tvar sType = typeof(TSource);\r\n\t\t\tvar tType = typeof(TTarget);\r\n\t\t\tvar sProperties = sType.GetProperties(DefaultBindingFlags);\r\n\t\t\tvar tProperties = tType.GetProperties(DefaultBindingFlags);\r\n\t\t\tvar sNames = sProperties.Select(x => x.Name).ToArray();\r\n\t\t\tvar tNames = tProperties.Select(x => x.Name).ToArray();\r\n\t\t\tvar targetOnly = tProperties.Where(x => !sNames.Contains(x.Name)).OrderBy(x => x.Name).ToArray();\r\n\t\t\tvar sourceOnly = sProperties.Where(x => !tNames.Contains(x.Name)).OrderBy(x => x.Name).ToArray();\r\n\t\t\tvar sourceSame = sProperties.Where(x => tNames.Contains(x.Name)).OrderBy(x => x.Name).ToArray();\r\n\t\t\tvar sb = new StringBuilder();\r\n\t\t\tvar targetOnlyLines = targetOnly.Select(x => string.Format(\"{0} {1}\", GetBuiltInTypeNameOrAlias(x.PropertyType), x.Name));\r\n\t\t\tif (targetOnly.Length > 0)\r\n\t\t\t{\r\n\t\t\t\tsb.AppendFormat(\"// ---- Target Only [{0}] - {1}\\r\\n\\t\", targetOnly.Length, tType.Name);\r\n\t\t\t\tsb.Append(string.Join(\"\\r\\n\\t\", targetOnlyLines));\r\n\t\t\t\tsb.AppendLine();\r\n\t\t\t}\r\n\t\t\tvar sourceOnlyLines = sourceOnly.Select(x => string.Format(\"{0} {1}\", GetBuiltInTypeNameOrAlias(x.PropertyType), x.Name));\r\n\t\t\tif (sourceOnly.Length > 0)\r\n\t\t\t{\r\n\t\t\t\tsb.AppendFormat(\"// ---- Source Only [{0}] - {1}\\r\\n\\t\", sourceOnly.Length, sType.Name);\r\n\t\t\t\tsb.Append(string.Join(\"\\r\\n\\t\", sourceOnlyLines));\r\n\t\t\t\tsb.AppendLine();\r\n\t\t\t}\r\n\t\t\tif (sourceSame.Length > 0)\r\n\t\t\t{\r\n\t\t\t\tsb.AppendFormat(\"// ---- Intersects [{0}]:\\r\\n\\t\", sourceSame.Length);\r\n\t\t\t\tvar intesectsLines = sourceSame.Select(s =>\r\n\t\t\t\t\tstring.Format(\"{0} {1}{2}\",\r\n\t\t\t\t\t\tGetBuiltInTypeNameOrAlias(s.PropertyType),\r\n\t\t\t\t\t\ts.Name,\r\n\t\t\t\t\t\t(GetBuiltInTypeNameOrAlias(tProperties.First(t => t.Name == s.Name).PropertyType) == GetBuiltInTypeNameOrAlias(s.PropertyType))\r\n\t\t\t\t\t\t? \"\"\r\n\t\t\t\t\t\t: \" // \" + GetBuiltInTypeNameOrAlias(tProperties.First(t => t.Name == s.Name).PropertyType)\r\n\t\t\t\t\t)\r\n\t\t\t\t);\r\n\t\t\t\tsb.Append(string.Join(\"\\r\\n\\t\", intesectsLines));\r\n\t\t\t\tsb.AppendLine();\r\n\t\t\t}\r\n\t\t\treturn sb.ToString();\r\n\t\t}\r\n\r\n\t\t/// <summary>\r\n\t\t/// Retur true if can copy.\r\n\t\t/// </summary>\r\n\t\tpublic static bool CanCopy(Type source, Type target, bool onlyNonByRef, out bool useJson)\r\n\t\t{\r\n\t\t\tuseJson = false;\r\n\t\t\t// If target property don't exists.\r\n\t\t\tif (target == null)\r\n\t\t\t\treturn false;\r\n\t\t\t// If target property can't be assigned.\r\n\t\t\tif (!source.IsAssignableFrom(source))\r\n\t\t\t\treturn false;\r\n\t\t\t// If only non reference properties can be compied.\r\n\t\t\tif (onlyNonByRef && source.IsByRef)\r\n\t\t\t\treturn false;\r\n\t\t\t// Use JSON to clone referenced values.\r\n\t\t\tuseJson = source.IsByRef;\r\n\t\t\treturn true;\r\n\t\t}\r\n\r\n\t\tpublic static void CopyProperties(object source, object target, bool onlyNonByRef = false)\r\n\t\t{\r\n\t\t\tif (source is null)\r\n\t\t\t\tthrow new ArgumentNullException(nameof(source));\r\n\t\t\tif (target is null)\r\n\t\t\t\tthrow new ArgumentNullException(nameof(target));\r\n\t\t\t// Get type of the destination object.\r\n\t\t\tvar sourceProperties = GetProperties(source.GetType());\r\n\t\t\tvar targetProperties = GetProperties(target.GetType());\r\n\t\t\tforeach (var sm in sourceProperties)\r\n\t\t\t{\r\n\t\t\t\t// Get destination property and skip if not found.\r\n\t\t\t\tvar tm = targetProperties.FirstOrDefault(x => Equals(x.Name, sm.Name));\r\n\t\t\t\tif (!sm.CanRead || !tm.CanWrite)\r\n\t\t\t\t\tcontinue;\r\n\t\t\t\tbool useJson;\r\n\t\t\t\tif (!CanCopy(sm.PropertyType, tm.PropertyType, onlyNonByRef, out useJson))\r\n\t\t\t\t\tcontinue;\r\n\t\t\t\t// Get source value.\r\n\t\t\t\tvar sValue = sm.GetValue(source, null);\r\n\t\t\t\tif (useJson)\r\n\t\t\t\t\tsValue = Serialize(sValue);\r\n\t\t\t\tvar update = true;\r\n\t\t\t\t// If can read target value.\r\n\t\t\t\tif (tm.CanRead)\r\n\t\t\t\t{\r\n\t\t\t\t\t// Get target value.\r\n\t\t\t\t\tvar dValue = tm.GetValue(target, null);\r\n\t\t\t\t\tif (useJson)\r\n\t\t\t\t\t\tdValue = Serialize(dValue);\r\n\t\t\t\t\t// Update only if values are different.\r\n\t\t\t\t\tupdate = !Equals(sValue, dValue);\r\n\t\t\t\t}\r\n\t\t\t\tif (update)\r\n\t\t\t\t{\r\n\t\t\t\t\tif (useJson)\r\n\t\t\t\t\t\tsValue = Deserialize(sValue as string, tm.PropertyType);\r\n\t\t\t\t\ttm.SetValue(target, sValue, null);\r\n\t\t\t\t}\r\n\t\t\t}\r\n\t\t}\r\n\r\n\t\t/// <summary>\r\n\t\t/// Returns true if all properties with the same name are equal.\r\n\t\t/// </summary>\r\n\t\tpublic static bool EqualProperties(object source, object target, bool onlyNonByRef = false)\r\n\t\t{\r\n\t\t\tif (source is null)\r\n\t\t\t\tthrow new ArgumentNullException(nameof(source));\r\n\t\t\tif (target is null)\r\n\t\t\t\tthrow new ArgumentNullException(nameof(target));\r\n\t\t\t// Get type of the destination object.\r\n\t\t\tvar sourceProperties = GetProperties(source.GetType());\r\n\t\t\tvar targetProperties = GetProperties(target.GetType());\r\n\t\t\tforeach (var sm in sourceProperties)\r\n\t\t\t{\r\n\t\t\t\t// Get destination property and skip if not found.\r\n\t\t\t\tvar tm = targetProperties.FirstOrDefault(x => Equals(x.Name, sm.Name));\r\n\t\t\t\tif (!sm.CanRead)\r\n\t\t\t\t\tcontinue;\r\n\t\t\t\tbool useJson;\r\n\t\t\t\tif (!CanCopy(sm.PropertyType, tm.PropertyType, onlyNonByRef, out useJson))\r\n\t\t\t\t\tcontinue;\r\n\t\t\t\t// Get source value.\r\n\t\t\t\tvar sValue = sm.GetValue(source, null);\r\n\t\t\t\tif (useJson)\r\n\t\t\t\t\tsValue = Serialize(sValue);\r\n\t\t\t\tvar update = true;\r\n\t\t\t\t// If can read target value.\r\n\t\t\t\tif (tm.CanRead)\r\n\t\t\t\t{\r\n\t\t\t\t\t// Get target value.\r\n\t\t\t\t\tvar dValue = tm.GetValue(target, null);\r\n\t\t\t\t\tif (useJson)\r\n\t\t\t\t\t\tdValue = Serialize(dValue);\r\n\t\t\t\t\t// Update only if values are different.\r\n\t\t\t\t\tupdate = !Equals(sValue, dValue);\r\n\t\t\t\t}\r\n\t\t\t\t// If update needed then not equal.\r\n\t\t\t\tif (update)\r\n\t\t\t\t\treturn false;\r\n\t\t\t}\r\n\t\t\treturn true;\r\n\t\t}\r\n\r\n\t\t#endregion\r\n\r\n\t\t#region Convert: Object <-> Bytes\r\n\r\n\t\t// Note: Similar as \"Structure <-> Bytes\", but with ability to convert variable strings.\r\n\r\n\t\tpublic static byte[] ObjectToBytes<T>(T o)\r\n\t\t{\r\n\t\t\tusing (var ms = new MemoryStream())\r\n\t\t\t{\r\n\t\t\t\tvar flags = BindingFlags.Instance | BindingFlags.Public;\r\n\t\t\t\tvar props = typeof(T).GetProperties(flags);\r\n\t\t\t\tusing (var writer = new BinaryWriter(ms))\r\n\t\t\t\t{\r\n\t\t\t\t\tforeach (var p in props)\r\n\t\t\t\t\t{\r\n\t\t\t\t\t\tvar value = p.GetValue(o);\r\n\t\t\t\t\t\tswitch (value)\r\n\t\t\t\t\t\t{\r\n\t\t\t\t\t\t\tcase bool v:\r\n\t\t\t\t\t\t\t\twriter.Write(v); break;\r\n\t\t\t\t\t\t\tcase byte v:\r\n\t\t\t\t\t\t\t\twriter.Write(v); break;\r\n\t\t\t\t\t\t\tcase byte[] v:\r\n\t\t\t\t\t\t\t\twriter.Write(v); break;\r\n\t\t\t\t\t\t\tcase char[] v:\r\n\t\t\t\t\t\t\t\twriter.Write(v); break;\r\n\t\t\t\t\t\t\tcase char v:\r\n\t\t\t\t\t\t\t\twriter.Write(v); break;\r\n\t\t\t\t\t\t\tcase decimal v:\r\n\t\t\t\t\t\t\t\twriter.Write(v); break;\r\n\t\t\t\t\t\t\tcase double v:\r\n\t\t\t\t\t\t\t\twriter.Write(v); break;\r\n\t\t\t\t\t\t\tcase float v:\r\n\t\t\t\t\t\t\t\twriter.Write(v); break;\r\n\t\t\t\t\t\t\tcase int v:\r\n\t\t\t\t\t\t\t\twriter.Write(v); break;\r\n\t\t\t\t\t\t\tcase long v:\r\n\t\t\t\t\t\t\t\twriter.Write(v); break;\r\n\t\t\t\t\t\t\tcase sbyte v:\r\n\t\t\t\t\t\t\t\twriter.Write(v); break;\r\n\t\t\t\t\t\t\tcase short v:\r\n\t\t\t\t\t\t\t\twriter.Write(v); break;\r\n\t\t\t\t\t\t\tcase string v:\r\n\t\t\t\t\t\t\t\twriter.Write(v); break;\r\n\t\t\t\t\t\t\tcase uint v:\r\n\t\t\t\t\t\t\t\twriter.Write(v); break;\r\n\t\t\t\t\t\t\tcase ulong v:\r\n\t\t\t\t\t\t\t\twriter.Write(v); break;\r\n\t\t\t\t\t\t\tcase ushort v:\r\n\t\t\t\t\t\t\t\twriter.Write(v); break;\r\n\t\t\t\t\t\t\tdefault:\r\n\t\t\t\t\t\t\t\tbreak;\r\n\t\t\t\t\t\t}\r\n\t\t\t\t\t}\r\n\t\t\t\t\tms.Flush();\r\n\t\t\t\t\tms.Seek(0, SeekOrigin.Begin);\r\n\t\t\t\t\treturn ms.ToArray();\r\n\t\t\t\t}\r\n\t\t\t}\r\n\t\t}\r\n\r\n\t\tpublic static T BytesToObject<T>(byte[] bytes)\r\n\t\t{\r\n\t\t\tusing (var ms = new MemoryStream(bytes))\r\n\t\t\t{\r\n\t\t\t\tvar o = Activator.CreateInstance<T>();\r\n\t\t\t\tvar flags = BindingFlags.Instance | BindingFlags.Public;\r\n\t\t\t\tvar props = typeof(T).GetProperties(flags);\r\n\t\t\t\tusing (var reader = new BinaryReader(ms))\r\n\t\t\t\t{\r\n\t\t\t\t\tforeach (var p in props)\r\n\t\t\t\t\t{\r\n\t\t\t\t\t\tvar typeCode = Type.GetTypeCode(p.PropertyType);\r\n\t\t\t\t\t\tobject v;\r\n\t\t\t\t\t\tswitch (typeCode)\r\n\t\t\t\t\t\t{\r\n\t\t\t\t\t\t\tcase TypeCode.Boolean:\r\n\t\t\t\t\t\t\t\tv = reader.ReadBoolean();\r\n\t\t\t\t\t\t\t\tbreak;\r\n\t\t\t\t\t\t\tcase TypeCode.Char:\r\n\t\t\t\t\t\t\t\tv = reader.ReadChar();\r\n\t\t\t\t\t\t\t\tbreak;\r\n\t\t\t\t\t\t\tcase TypeCode.DBNull:\r\n\t\t\t\t\t\t\t\tv = DBNull.Value;\r\n\t\t\t\t\t\t\t\tbreak;\r\n\t\t\t\t\t\t\tcase TypeCode.DateTime:\r\n\t\t\t\t\t\t\t\tv = new DateTime(reader.ReadInt64());\r\n\t\t\t\t\t\t\t\tbreak;\r\n\t\t\t\t\t\t\tcase TypeCode.Decimal:\r\n\t\t\t\t\t\t\t\tv = reader.ReadDecimal();\r\n\t\t\t\t\t\t\t\tbreak;\r\n\t\t\t\t\t\t\tcase TypeCode.Double:\r\n\t\t\t\t\t\t\t\tv = reader.ReadDouble();\r\n\t\t\t\t\t\t\t\tbreak;\r\n\t\t\t\t\t\t\tcase TypeCode.Empty:\r\n\t\t\t\t\t\t\t\tv = null;\r\n\t\t\t\t\t\t\t\tbreak;\r\n\t\t\t\t\t\t\tcase TypeCode.SByte:\r\n\t\t\t\t\t\t\t\tv = reader.ReadSByte();\r\n\t\t\t\t\t\t\t\tbreak;\r\n\t\t\t\t\t\t\tcase TypeCode.Int16:\r\n\t\t\t\t\t\t\t\tv = reader.ReadInt16();\r\n\t\t\t\t\t\t\t\tbreak;\r\n\t\t\t\t\t\t\tcase TypeCode.Int32:\r\n\t\t\t\t\t\t\t\tv = reader.ReadInt32();\r\n\t\t\t\t\t\t\t\tbreak;\r\n\t\t\t\t\t\t\tcase TypeCode.Int64:\r\n\t\t\t\t\t\t\t\tv = reader.ReadInt64();\r\n\t\t\t\t\t\t\t\tbreak;\r\n\t\t\t\t\t\t\tcase TypeCode.Single:\r\n\t\t\t\t\t\t\t\tv = reader.ReadSingle();\r\n\t\t\t\t\t\t\t\tbreak;\r\n\t\t\t\t\t\t\tcase TypeCode.String:\r\n\t\t\t\t\t\t\t\tv = reader.ReadString();\r\n\t\t\t\t\t\t\t\tbreak;\r\n\t\t\t\t\t\t\tcase TypeCode.Byte:\r\n\t\t\t\t\t\t\t\tv = reader.ReadByte();\r\n\t\t\t\t\t\t\t\tbreak;\r\n\t\t\t\t\t\t\tcase TypeCode.UInt16:\r\n\t\t\t\t\t\t\t\tv = reader.ReadUInt16();\r\n\t\t\t\t\t\t\t\tbreak;\r\n\t\t\t\t\t\t\tcase TypeCode.UInt32:\r\n\t\t\t\t\t\t\t\tv = reader.ReadUInt32();\r\n\t\t\t\t\t\t\t\tbreak;\r\n\t\t\t\t\t\t\tcase TypeCode.UInt64:\r\n\t\t\t\t\t\t\t\tv = reader.ReadUInt64();\r\n\t\t\t\t\t\t\t\tbreak;\r\n\t\t\t\t\t\t\tdefault:\r\n\t\t\t\t\t\t\t\tthrow new Exception(\"Non Serializable Object: \" + p.PropertyType);\r\n\t\t\t\t\t\t}\r\n\t\t\t\t\t\tp.SetValue(o, v);\r\n\t\t\t\t\t}\r\n\t\t\t\t}\r\n\t\t\t\treturn o;\r\n\t\t\t}\r\n\t\t}\r\n\r\n\t\t#endregion\r\n\r\n\t\t#region Convert: Structure <-> Bytes\r\n\r\n\t\t/// <summary>\r\n\t\t/// Convert structure to byte array (unmanaged block of memory).\r\n\t\t/// </summary>\r\n\t\tpublic static byte[] StructureToBytes<T>(T value) where T : struct\r\n\t\t{\r\n\t\t\tvar size = Marshal.SizeOf(value);\r\n\t\t\tvar bytes = new byte[size];\r\n\t\t\tvar handle = default(GCHandle);\r\n\t\t\ttry\r\n\t\t\t{\r\n\t\t\t\thandle = GCHandle.Alloc(bytes, GCHandleType.Pinned);\r\n\t\t\t\tMarshal.StructureToPtr(value, handle.AddrOfPinnedObject(), false);\r\n\t\t\t}\r\n\t\t\tfinally\r\n\t\t\t{\r\n\t\t\t\tif (handle.IsAllocated)\r\n\t\t\t\t\thandle.Free();\r\n\t\t\t}\r\n\t\t\treturn bytes;\r\n\t\t}\r\n\r\n\t\tpublic static T BytesToStructure<T>(byte[] bytes) where T : struct\r\n\t\t{\r\n\t\t\treturn (T)BytesToStructure(bytes, typeof(T));\r\n\t\t}\r\n\r\n\t\t/// <summary>\r\n\t\t/// Convert byte array (unmanaged block of memory) to structure.\r\n\t\t/// </summary>\r\n\t\tpublic static object BytesToStructure(byte[] bytes, Type type)\r\n\t\t{\r\n\t\t\tif (type is null)\r\n\t\t\t\tthrow new ArgumentNullException(nameof(type));\r\n\t\t\tvar value = type.IsValueType ? Activator.CreateInstance(type) : null;\r\n\t\t\tvar handle = default(GCHandle);\r\n\t\t\ttry\r\n\t\t\t{\r\n\t\t\t\thandle = GCHandle.Alloc(bytes, GCHandleType.Pinned);\r\n\t\t\t\tvalue = Marshal.PtrToStructure(handle.AddrOfPinnedObject(), type);\r\n\t\t\t}\r\n\t\t\tfinally\r\n\t\t\t{\r\n\t\t\t\tif (handle.IsAllocated)\r\n\t\t\t\t\thandle.Free();\r\n\t\t\t}\r\n\t\t\treturn value;\r\n\t\t}\r\n\r\n\t\t#endregion\r\n\r\n\t\t#region Try Parse\r\n\r\n\t\t/// <summary>\r\n\t\t/// Tries to convert the specified string representation of a logical value to\r\n\t\t/// its type T equivalent. A return value indicates whether the conversion\r\n\t\t/// succeeded or failed.\r\n\t\t/// </summary>\r\n\t\t/// <param name=\"value\">A string containing the value to try and convert.</param>\r\n\t\t/// <param name=\"t\">target type</param>\r\n\t\t/// <param name=\"result\">If the conversion was successful, the converted value of type T.</param>\r\n\t\t/// <returns>If value was converted successfully, true; otherwise false.</returns>\r\n\t\tpublic static bool TryParse(object value, Type t, out object result)\r\n\t\t{\r\n\t\t\tif (IsNullable(t))\r\n\t\t\t\tt = Nullable.GetUnderlyingType(t) ?? t;\r\n\t\t\t//var converter = System.ComponentModel.TypeDescriptor.GetConverter(typeof(T));\r\n\t\t\t//if (converter.IsValid(value))\r\n\t\t\t//{\r\n\t\t\t//\tresult = (T)converter.ConvertFromString(value);\r\n\t\t\t//\treturn true;\r\n\t\t\t//}\r\n\t\t\tif (t == typeof(string))\r\n\t\t\t{\r\n\t\t\t\tresult = value;\r\n\t\t\t\treturn true;\r\n\t\t\t}\r\n\t\t\tif (t.IsEnum)\r\n\t\t\t{\r\n\t\t\t\tvar retValue = value is null ? false : Enum.IsDefined(t, value?.ToString());\r\n\t\t\t\tresult = retValue ? Enum.Parse(t, value?.ToString()) : default;\r\n\t\t\t\treturn retValue;\r\n\t\t\t}\r\n\t\t\tvar tryParseMethod = t.GetMethod(\"TryParse\",\r\n\t\t\t\tBindingFlags.Static | BindingFlags.Public, null,\r\n\t\t\t\tnew[] { typeof(string), t.MakeByRefType() }, null);\r\n\t\t\tvar parameters = new object[] { value, null };\r\n\t\t\tvar retVal = (bool)tryParseMethod.Invoke(null, parameters);\r\n\t\t\tresult = parameters[1];\r\n\t\t\treturn retVal;\r\n\t\t}\r\n\r\n\r\n\t\t/// <summary>\r\n\t\t/// Tries to convert the specified string representation of a logical value to\r\n\t\t/// its type T equivalent. A return value indicates whether the conversion\r\n\t\t/// succeeded or failed.\r\n\t\t/// </summary>\r\n\t\t/// <typeparam name=\"T\">The type to try and convert to.</typeparam>\r\n\t\t/// <param name=\"value\">A string containing the value to try and convert.</param>\r\n\t\t/// <param name=\"result\">If the conversion was successful, the converted value of type T.</param>\r\n\t\t/// <returns>If value was converted successfully, true; otherwise false.</returns>\r\n\t\tpublic static bool TryParse<T>(string value, out T result)\r\n\t\t{\r\n\t\t\tvar t = typeof(T);\r\n\t\t\tobject o;\r\n\t\t\tvar success = TryParse(value, t, out o);\r\n\t\t\tresult = (T)o;\r\n\t\t\treturn success;\r\n\t\t}\r\n\r\n\t\t/// <summary>\r\n\t\t/// Tries to convert the specified string representation of a logical value to\r\n\t\t/// its type T equivalent. Returns default value if conversion failed.\r\n\t\t/// </summary>\r\n\t\tpublic static T TryParse<T>(string value, T defaultValue = default(T))\r\n\t\t{\r\n\t\t\tvar result = default(T);\r\n\t\t\treturn TryParse(value, out result)\r\n\t\t\t\t? result\r\n\t\t\t\t: defaultValue;\r\n\t\t}\r\n\r\n\t\t/// <summary>\r\n\t\t/// Tries to convert the specified string representation of a logical value to\r\n\t\t/// its type T equivalent. Returns default value if conversion failed.\r\n\t\t/// </summary>\r\n\t\tpublic static bool CanParse<T>(string value)\r\n\t\t{\r\n\t\t\tT result;\r\n\t\t\treturn TryParse(value, out result);\r\n\t\t}\r\n\r\n\t\tpublic static bool IsNullable(Type t)\r\n\t\t{\r\n\t\t\t// Throw exception if type not supplied.\r\n\t\t\tif (t is null)\r\n\t\t\t\tthrow new ArgumentNullException(nameof(t));\r\n\t\t\t// Special Handling - known cases where Exceptions would be thrown\r\n\t\t\telse if (t == typeof(void))\r\n\t\t\t\tthrow new Exception(\"There is no Nullable version of void\");\r\n\t\t\t// If this is not a value type, it is a reference type, so it is automatically nullable.\r\n\t\t\t// (NOTE: All forms of Nullable<T> are value types)\r\n\t\t\tif (!t.IsValueType)\r\n\t\t\t\treturn true;\r\n\t\t\t// Return true if underlying Type exists (this is faster than line above).\r\n\t\t\treturn Nullable.GetUnderlyingType(t) != null;\r\n\t\t}\r\n\r\n\t\t#endregion\r\n\r\n\t\t/// <summary>\r\n\t\t/// Convert year values from 0 to 99 to the years xx00 to yy99 with appropriate century. \r\n\t\t/// </summary>\r\n\t\t/// <param name=\"year\">A two-digit or four-digit integer that represents the year to convert.</param>\r\n\t\t/// <param name=\"twoDigitYearMax\">The last year of a 100-year range that can be represented by a 2-digit year.</param>\r\n\t\t/// <returns>An integer that contains the four-digit representation of year.</returns>\r\n\t\t/// <remarks>\r\n\t\t/// Use C# solution from System.Globalization.Calendar.ToFourDigitYear(System.Int32 year).\r\n\t\t/// For example, if TwoDigitYearMax is set to 2029, the 100-year range is from 1930 to 2029:\r\n\t\t///   a 2-digit value of 30 is interpreted as 1930\r\n\t\t///   a 2-digit value of 29 is interpreted as 2029\r\n\t\t/// </remarks>\r\n\t\tpublic static int ToFourDigitYear(int year, int? twoDigitYearMax = null)\r\n\t\t{\r\n\t\t\t// If year is outside of the range then return.\r\n\t\t\tif (year < 0 && year > 99)\r\n\t\t\t\treturn year;\r\n\t\t\t// Convert year from 2 to 4 digits with the correct century (-50/+50 rule).\r\n\t\t\t// JavaScript: By default, new Date(..) convert values from 0 to 99 to the years 1900 to 1999.\r\n\t\t\t//var twoDigitYearMax = new Date().getFullYear() + 50;\r\n\t\t\tvar fullYearMax = twoDigitYearMax ?? DateTime.Now.Year + 50;\r\n\t\t\tvar yearMax = fullYearMax % 100;\r\n\t\t\tvar century = (fullYearMax - yearMax) / 100;\r\n\t\t\tvar fullYear = (century - (year > yearMax ? 1 : 0)) * 100 + year;\r\n\t\t\treturn fullYear;\r\n\t\t}\r\n\r\n\t}\r\n}\r\n"
  },
  {
    "path": "FocusLogger/JocysCom/Runtime/Serializer.cs",
    "content": "using System;\r\nusing System.Collections.Concurrent;\r\nusing System.Collections.Generic;\r\nusing System.IO;\r\nusing System.Linq;\r\nusing System.Runtime.Serialization;\r\nusing System.Runtime.Serialization.Json;\r\nusing System.Text;\r\nusing System.Xml;\r\nusing System.Xml.Schema;\r\nusing System.Xml.Serialization;\r\n\r\nnamespace JocysCom.ClassLibrary.Runtime\r\n{\r\n\tpublic static class Serializer\r\n\t{\r\n\r\n\t\t#region Helper Functions\r\n\r\n\t\t/// <summary>\r\n\t\t/// Read file content in multiple attempts.\r\n\t\t/// </summary>\r\n\t\t/// <param name=\"path\">The file to open for reading.</param>\r\n\t\t/// <param name=\"attempts\">Number of attempts to read from the file. Default 2 times.</param>\r\n\t\t/// <param name=\"waitTime\">Wait time between attempts. Default 500ms.</param>\r\n\t\t/// <returns>A byte array containing the contents of the file.</returns>\r\n\t\tpublic static byte[] ReadFile(string path, int attempts = 2, int waitTime = 500)\r\n\t\t{\r\n\t\t\twhile (true)\r\n\t\t\t{\r\n\t\t\t\tattempts -= 1;\r\n\t\t\t\ttry\r\n\t\t\t\t{\r\n\t\t\t\t\t// SUPPRESS: CWE-73: External Control of File Name or Path\r\n\t\t\t\t\t// Note: False Positive. File path is not externally controlled by the user.\r\n\t\t\t\t\treturn System.IO.File.ReadAllBytes(path);\r\n\t\t\t\t}\r\n\t\t\t\tcatch (Exception)\r\n\t\t\t\t{\r\n\t\t\t\t\tif (attempts > 0)\r\n\t\t\t\t\t{\r\n\t\t\t\t\t\tnew System.Threading.ManualResetEvent(false).WaitOne(waitTime);\r\n\t\t\t\t\t\tcontinue;\r\n\t\t\t\t\t}\r\n\t\t\t\t\tthrow;\r\n\t\t\t\t}\r\n\t\t\t}\r\n\t\t}\r\n\r\n\t\t/// <summary>\r\n\t\t/// Write file content in multiple attempts.\r\n\t\t/// </summary>\r\n\t\t/// <param name=\"path\">The file to open for writing.</param>\r\n\t\t/// <param name=\"bytes\">The bytes to write to the file.</param>\r\n\t\t/// <param name=\"attempts\">Number of attempts to write into the file. Default 2 times.</param>\r\n\t\t/// <param name=\"waitTime\">Wait time between attempts. Default 500ms.</param>\r\n\t\tpublic static void WriteFile(string path, byte[] bytes, int attempts = 2, int waitTime = 500)\r\n\t\t{\r\n\t\t\twhile (true)\r\n\t\t\t{\r\n\t\t\t\tattempts -= 1;\r\n\t\t\t\ttry\r\n\t\t\t\t{\r\n\t\t\t\t\t// WriteAllBytes will lock file for writing and reading.\r\n\t\t\t\t\t// SUPPRESS: CWE-73: External Control of File Name or Path\r\n\t\t\t\t\t// Note: False Positive. File path is not externally controlled by the user.\r\n\t\t\t\t\tSystem.IO.File.WriteAllBytes(path, bytes);\r\n\t\t\t\t\treturn;\r\n\t\t\t\t}\r\n\t\t\t\tcatch (Exception)\r\n\t\t\t\t{\r\n\t\t\t\t\tif (attempts > 0)\r\n\t\t\t\t\t{\r\n\t\t\t\t\t\tnew System.Threading.ManualResetEvent(false).WaitOne(waitTime);\r\n\t\t\t\t\t\tcontinue;\r\n\t\t\t\t\t}\r\n\t\t\t\t\tthrow;\r\n\t\t\t\t}\r\n\t\t\t}\r\n\t\t}\r\n\r\n\t\t#endregion\r\n\r\n\t\t#region JSON\r\n\r\n#if NETFRAMEWORK\r\n\t\t/// <summary>Cache data for speed.</summary>\r\n\t\t/// <remarks>Cache allows for this class to work 20 times faster.</remarks>\r\n\t\tprivate static ConcurrentDictionary<Type, DataContractJsonSerializer> JsonSerializers = new ConcurrentDictionary<Type, DataContractJsonSerializer>();\r\n\r\n\t\tstatic DataContractJsonSerializer GetJsonSerializer(Type type, DataContractJsonSerializerSettings settings = null)\r\n\t\t{\r\n\t\t\tif (type == null)\r\n\t\t\t\treturn null;\r\n\t\t\treturn JsonSerializers.GetOrAdd(type, x => new DataContractJsonSerializer(type, settings));\r\n\t\t}\r\n#endif\r\n\r\n\t\tpublic static Func<object, Encoding, string> _SerializeToJson;\r\n\t\tpublic static Func<string, Type, Encoding, object> _DeserializeFromJson;\r\n\r\n#if !NETFRAMEWORK\r\n\t\tprivate static System.Text.Json.JsonSerializerOptions GetJsonOptions()\r\n\t\t{\r\n\t\t\tvar options = new System.Text.Json.JsonSerializerOptions();\r\n\t\t\toptions.DefaultIgnoreCondition = System.Text.Json.Serialization.JsonIgnoreCondition.WhenWritingNull;\r\n\t\t\toptions.PropertyNamingPolicy = null;\r\n\t\t\toptions.WriteIndented = true;\r\n\t\t\toptions.MaxDepth = 64;\r\n\t\t\t// Add support for de-serializing enumeration strings.\r\n\t\t\toptions.Converters.Add(new System.Text.Json.Serialization.JsonStringEnumConverter());\r\n\t\t\treturn options;\r\n\t\t}\r\n#endif\r\n\r\n\t\t/// <summary>\r\n\t\t/// Serialize object to JSON string.\r\n\t\t/// </summary>\r\n\t\t/// <param name=\"o\">The object to serialize.</param>\r\n\t\t/// <param name=\"encoding\">JSON string encoding.</param>\r\n\t\t/// <returns>JSON string.</returns>\r\n\t\tpublic static string SerializeToJson(object o, Encoding encoding = null)\r\n\t\t{\r\n\t\t\tif (_SerializeToJson != null)\r\n\t\t\t\treturn _SerializeToJson(o, encoding);\r\n\t\t\tif (o is null)\r\n\t\t\t\treturn null;\r\n#if NETFRAMEWORK\r\n\t\t\tvar serializer = GetJsonSerializer(o.GetType());\r\n\t\t\tvar ms = new MemoryStream();\r\n\t\t\tlock (serializer) { serializer.WriteObject(ms, o); }\r\n\t\t\tif (encoding is null)\r\n\t\t\t\tencoding = Encoding.UTF8;\r\n\t\t\tvar json = encoding.GetString(ms.ToArray());\r\n\t\t\tms.Close();\r\n\t\t\treturn json;\r\n#else\r\n\t\t\tvar options = GetJsonOptions();\r\n\t\t\tvar json = System.Text.Json.JsonSerializer.Serialize(o, o.GetType(), options);\r\n\t\t\treturn json;\r\n#endif\r\n\t\t}\r\n\r\n\t\t/// <summary>\r\n\t\t/// De-serialize object from JSON string.\r\n\t\t/// </summary>\r\n\t\t/// <param name=\"json\">JSON string representing object.</param>\r\n\t\t/// <param name=\"type\">Type of object.</param>\r\n\t\t/// <param name=\"encoding\">JSON string encoding.</param>\r\n\t\t/// <returns>The de-serialized object.</returns>\r\n\t\tpublic static object DeserializeFromJson(string json, Type type, Encoding encoding = null)\r\n\t\t{\r\n\t\t\tif (_DeserializeFromJson != null)\r\n\t\t\t\treturn _DeserializeFromJson(json, type, encoding);\r\n\t\t\tif (json is null)\r\n\t\t\t\treturn null;\r\n#if NETFRAMEWORK\r\n\t\t\tvar serializer = GetJsonSerializer(type);\r\n\t\t\tif (encoding is null)\r\n\t\t\t\tencoding = Encoding.UTF8;\r\n\t\t\tvar bytes = encoding.GetBytes(json);\r\n\t\t\tvar ms = new MemoryStream(bytes);\r\n\t\t\tobject o;\r\n\t\t\tlock (serializer) { o = serializer.ReadObject(ms); }\r\n\t\t\tms.Close();\r\n\t\t\treturn o;\r\n#else\r\n\t\t\tvar options = GetJsonOptions();\r\n\t\t\tvar o = System.Text.Json.JsonSerializer.Deserialize(json, type, options);\r\n\t\t\treturn o;\r\n#endif\r\n\t\t}\r\n\r\n\t\t/// <summary>\r\n\t\t/// De-serialize object from JSON string.\r\n\t\t/// </summary>\r\n\t\t/// <param name=\"json\">JSON string representing object.</param>\r\n\t\t/// <param name=\"encoding\">JSON string encoding.</param>\r\n\t\t/// <returns>The de-serialized object.</returns>\r\n\t\tpublic static T DeserializeFromJson<T>(string json, Encoding encoding = null)\r\n\t\t{\r\n\t\t\treturn (T)DeserializeFromJson(json, typeof(T), encoding);\r\n\t\t}\r\n\r\n\t\t// Created by: https://stackoverflow.com/users/17211/vince-panuccio\r\n\t\t// https://stackoverflow.com/questions/4580397/json-formatter-in-c\r\n\t\tpublic static string FormatJson(string json, string indent = \"\\t\")\r\n\t\t{\r\n\t\t\tvar indentation = 0;\r\n\t\t\tvar quoteCount = 0;\r\n\t\t\tvar escapeCount = 0;\r\n\t\t\tvar result =\r\n\t\t\t\tfrom ch in json ?? string.Empty\r\n\t\t\t\tlet escaped = (ch == '\\\\' ? escapeCount++ : escapeCount > 0 ? escapeCount-- : escapeCount) > 0\r\n\t\t\t\tlet quotes = ch == '\"' && !escaped ? quoteCount++ : quoteCount\r\n\t\t\t\tlet unquoted = quotes % 2 == 0\r\n\t\t\t\tlet colon = ch == ':' && unquoted ? \": \" : null\r\n\t\t\t\tlet nospace = char.IsWhiteSpace(ch) && unquoted ? string.Empty : null\r\n\t\t\t\tlet lineBreak = ch == ',' && unquoted ? ch + Environment.NewLine + string.Concat(Enumerable.Repeat(indent, indentation)) : null\r\n\t\t\t\tlet openChar = (ch == '{' || ch == '[') && unquoted ? ch + Environment.NewLine + string.Concat(Enumerable.Repeat(indent, ++indentation)) : ch.ToString()\r\n\t\t\t\tlet closeChar = (ch == '}' || ch == ']') && unquoted ? Environment.NewLine + string.Concat(Enumerable.Repeat(indent, --indentation)) + ch : ch.ToString()\r\n\t\t\t\tselect colon ?? nospace ?? lineBreak ?? (openChar.Length > 1 ? openChar : closeChar);\r\n\t\t\treturn string.Concat(result);\r\n\t\t}\r\n\r\n\t\t//public static string FormatJson(string json, bool indent = true)\r\n\t\t//{\r\n\t\t//\tusing document = JsonDocument.Parse(json);\r\n\t\t//\tusing var stream = new MemoryStream();\r\n\t\t//\tusing var writer = new Utf8JsonWriter(stream, new JsonWriterOptions() { Indented = indent });\r\n\t\t//\tdocument.WriteTo(writer);\r\n\t\t//\twriter.Flush();\r\n\t\t//\treturn Encoding.UTF8.GetString(stream.ToArray());\r\n\t\t//}\r\n\r\n\t\t#endregion\r\n\r\n\t\t#region XML\r\n\r\n\t\t/// <summary>\r\n\t\t/// Reformat XML document.\r\n\t\t/// </summary>\r\n\t\t/// <param name=\"xml\"></param>\r\n\t\t/// <returns></returns>\r\n\t\tpublic static string XmlFormat(string xml)\r\n\t\t{\r\n\t\t\tvar xd = new XmlDocument();\r\n\t\t\txd.XmlResolver = null;\r\n\t\t\txd.LoadXml(xml);\r\n\t\t\tvar sb = new StringBuilder();\r\n\t\t\tvar xws = new XmlWriterSettings();\r\n\t\t\txws.Indent = true;\r\n\t\t\txws.CheckCharacters = true;\r\n\t\t\t// XmlReader normalizes all newlines and converts '\\r\\n' to '\\n'.\r\n\t\t\t// This requires to save NewLines with  option which\r\n\t\t\t// \"Entitize\" option replace '\\r' with '&#xD;' in text node values.\r\n\t\t\txws.NewLineHandling = NewLineHandling.Entitize;\r\n\t\t\tvar xw = XmlWriter.Create(sb, xws);\r\n\t\t\txd.WriteTo(xw);\r\n\t\t\txw.Close();\r\n\t\t\treturn sb.ToString();\r\n\t\t}\r\n\r\n\t\tstatic object XmlSerializersLock = new object();\r\n\t\tstatic Dictionary<Type, XmlSerializer> XmlSerializers;\r\n\t\tpublic static XmlSerializer GetXmlSerializer(Type type)\r\n\t\t{\r\n\t\t\tlock (XmlSerializersLock)\r\n\t\t\t{\r\n\t\t\t\tif (XmlSerializers is null)\r\n\t\t\t\t\tXmlSerializers = new Dictionary<Type, XmlSerializer>();\r\n\t\t\t\tif (!XmlSerializers.ContainsKey(type))\r\n\t\t\t\t{\r\n\t\t\t\t\tvar extraTypes = new Type[] { typeof(string) };\r\n\t\t\t\t\tXmlSerializers.Add(type, new XmlSerializer(type, extraTypes));\r\n\t\t\t\t}\r\n\t\t\t}\r\n\t\t\treturn XmlSerializers[type];\r\n\t\t}\r\n\r\n\t\t#endregion\r\n\r\n\t\t#region XML: Serialize\r\n\r\n\t\t/// <summary>\r\n\t\t/// Serialize object to XML document.\r\n\t\t/// </summary>\r\n\t\t/// <param name=\"o\">The object to serialize.</param>\r\n\t\t/// <returns>XML document</returns>\r\n\t\tpublic static XmlDocument SerializeToXml(object o)\r\n\t\t{\r\n\t\t\tif (o is null)\r\n\t\t\t\treturn null;\r\n\t\t\tvar serializer = GetXmlSerializer(o.GetType());\r\n\t\t\tvar ms = new MemoryStream();\r\n\t\t\tlock (serializer) { serializer.Serialize(ms, o); }\r\n\t\t\tms.Seek(0, SeekOrigin.Begin);\r\n\t\t\tvar doc = new XmlDocument();\r\n\t\t\tdoc.XmlResolver = null;\r\n\t\t\t// Settings used to protect from:\r\n\t\t\t// SUPPRESS: CWE-611: Improper Restriction of XML External Entity Reference('XXE')\r\n\t\t\t// https://cwe.mitre.org/data/definitions/611.html\r\n\t\t\tvar settings = new XmlReaderSettings();\r\n\t\t\tsettings.DtdProcessing = DtdProcessing.Ignore;\r\n\t\t\tsettings.XmlResolver = null;\r\n\t\t\tsettings.CloseInput = false;\r\n\t\t\tvar reader = XmlReader.Create(ms, settings);\r\n\t\t\tdoc.Load(reader);\r\n\t\t\treader.Dispose();\r\n\t\t\tms.Close();\r\n\t\t\treturn doc;\r\n\t\t}\r\n\r\n\t\tstatic T SeriallizeToXml<T>(object o, Encoding encoding = null, bool omitXmlDeclaration = false, string comment = null, bool indent = true)\r\n\t\t{\r\n\t\t\tif (o is null)\r\n\t\t\t\treturn default(T);\r\n\t\t\t// Create serialization settings.\r\n\t\t\tencoding = encoding ?? Encoding.UTF8;\r\n\t\t\tvar settings = new XmlWriterSettings();\r\n\t\t\tsettings.OmitXmlDeclaration = omitXmlDeclaration;\r\n\t\t\tsettings.Encoding = encoding;\r\n\t\t\tsettings.Indent = indent;\r\n\t\t\t// XmlReader normalizes all newlines and converts '\\r\\n' to '\\n'.\r\n\t\t\t// This requires to save NewLines with  option which\r\n\t\t\t// \"Entitize\" option replace '\\r' with '&#xD;' in text node values.\r\n\t\t\tsettings.NewLineHandling = NewLineHandling.Entitize;\r\n\t\t\t// Serialize.\r\n\t\t\tvar serializer = GetXmlSerializer(o.GetType());\r\n\t\t\t// Serialize in memory first, so file will be locked for shorter times.\r\n\t\t\tvar ms = new MemoryStream();\r\n\t\t\tvar xw = XmlWriter.Create(ms, settings);\r\n\t\t\ttry\r\n\t\t\t{\r\n\t\t\t\tlock (serializer)\r\n\t\t\t\t{\r\n\t\t\t\t\tif (!string.IsNullOrEmpty(comment))\r\n\t\t\t\t\t{\r\n\t\t\t\t\t\txw.WriteStartDocument();\r\n\t\t\t\t\t\txw.WriteComment(comment);\r\n\t\t\t\t\t}\r\n\t\t\t\t\tif (omitXmlDeclaration)\r\n\t\t\t\t\t{\r\n\t\t\t\t\t\t//Create our own namespaces for the output\r\n\t\t\t\t\t\tvar ns = new XmlSerializerNamespaces();\r\n\t\t\t\t\t\t//Add an empty namespace and empty value\r\n\t\t\t\t\t\tns.Add(\"\", \"\");\r\n\t\t\t\t\t\tserializer.Serialize(xw, o, ns);\r\n\t\t\t\t\t}\r\n\t\t\t\t\telse\r\n\t\t\t\t\t{\r\n\t\t\t\t\t\tserializer.Serialize(xw, o);\r\n\t\t\t\t\t}\r\n\t\t\t\t\tif (!string.IsNullOrEmpty(comment))\r\n\t\t\t\t\t{\r\n\t\t\t\t\t\txw.WriteEndDocument();\r\n\t\t\t\t\t}\r\n\t\t\t\t\t// Make sure that all data flushed into memory stream.\r\n\t\t\t\t\txw.Flush();\r\n\t\t\t\t}\r\n\t\t\t}\r\n\t\t\tcatch (Exception)\r\n\t\t\t{\r\n\t\t\t\tthrow;\r\n\t\t\t}\r\n\t\t\tfinally\r\n\t\t\t{\r\n\t\t\t\t// This will close underlying MemoryStream too.\r\n\t\t\t\txw.Close();\r\n\t\t\t}\r\n\t\t\t// ToArray will return all bytes from memory stream despite it being closed.\r\n\t\t\t// Bytes will start with Byte Order Mark(BOM) and are ready to write into file.\r\n\t\t\tvar xmlBytes = ms.ToArray();\r\n\t\t\t// If string must be returned then...\r\n\t\t\tif (typeof(T) == typeof(string))\r\n\t\t\t{\r\n\t\t\t\t// Use StreamReader to remove Byte Order Mark(BOM).\r\n\t\t\t\tvar ms2 = new MemoryStream(xmlBytes);\r\n\t\t\t\tvar sr = new StreamReader(ms2, true);\r\n\t\t\t\tvar xmlString = sr.ReadToEnd();\r\n\t\t\t\t// This will close underlying MemoryStream too.\r\n\t\t\t\tsr.Close();\r\n\t\t\t\treturn (T)(object)xmlString;\r\n\t\t\t}\r\n\t\t\telse\r\n\t\t\t{\r\n\t\t\t\treturn (T)(object)xmlBytes;\r\n\t\t\t}\r\n\t\t}\r\n\r\n\t\t/// <summary>\r\n\t\t/// Serialize object to XML string.\r\n\t\t/// </summary>\r\n\t\t/// <param name=\"o\">The object to serialize.</param>\r\n\t\t/// <param name=\"encoding\">The encoding to use (default is UTF8).</param>\r\n\t\t/// <param name=\"omitXmlDeclaration\">Whether to omit the XML declaration.</param>\r\n\t\t/// <param name=\"comment\">Optional comment to include in the XML.</param>\r\n\t\t/// <param name=\"indent\">Whether to indent the XML output.</param>\r\n\t\t/// <returns>XML string.</returns>\r\n\t\tpublic static string SerializeToXmlString(object o, Encoding encoding = null, bool omitXmlDeclaration = false, string comment = null, bool indent = true)\r\n\t\t{\r\n\t\t\treturn SeriallizeToXml<string>(o, encoding, omitXmlDeclaration, comment, indent);\r\n\t\t}\r\n\r\n\t\t/// <summary>\r\n\t\t/// Serialize object to XML file.\r\n\t\t/// </summary>\r\n\t\t/// <param name=\"o\">The object to serialize.</param>\r\n\t\t/// <param name=\"path\">The file name to write to.</param>\r\n\t\t/// <param name=\"encoding\">The encoding to use (default is UTF8).</param>\r\n\t\t/// <param name=\"omitXmlDeclaration\">Whether to omit the XML declaration.</param>\r\n\t\t/// <param name=\"comment\">Optional comment to include in the XML.</param>\r\n\t\t/// <param name=\"attempts\">Number of attempts to write the file.</param>\r\n\t\t/// <param name=\"waitTime\">Wait time between attempts in milliseconds.</param>\r\n\t\tpublic static void SerializeToXmlFile(object o, string path, Encoding encoding = null, bool omitXmlDeclaration = false, string comment = null, int attempts = 2, int waitTime = 500)\r\n\t\t{\r\n\t\t\tvar bytes = (o is null)\r\n\t\t\t\t? new byte[0]\r\n\t\t\t\t: SeriallizeToXml<byte[]>(o, encoding, omitXmlDeclaration, comment);\r\n\t\t\t// Write serialized data into file.\r\n\t\t\tWriteFile(path, bytes, attempts, waitTime);\r\n\t\t}\r\n\r\n\t\t/// <summary>\r\n\t\t/// Serialize object to XML bytes with Byte Order Mark (BOM).\r\n\t\t/// </summary>\r\n\t\t/// <param name=\"o\">The object to serialize.</param>\r\n\t\t/// <param name=\"encoding\">The encoding to use (default is UTF8).</param>\r\n\t\t/// <param name=\"omitXmlDeclaration\">Whether to omit the XML declaration.</param>\r\n\t\t/// <param name=\"comment\">Optional comment to include in the XML.</param>\r\n\t\t/// <returns>XML bytes with Byte Order Mark (BOM).</returns>\r\n\t\tpublic static byte[] SerializeToXmlBytes(object o, Encoding encoding = null, bool omitXmlDeclaration = false, string comment = null)\r\n\t\t{\r\n\t\t\tvar bytes = (o is null)\r\n\t\t\t\t? new byte[0]\r\n\t\t\t\t: SeriallizeToXml<byte[]>(o, encoding, omitXmlDeclaration, comment);\r\n\t\t\treturn bytes;\r\n\t\t}\r\n\r\n\t\t#endregion\r\n\r\n\t\t#region XML: De-serialize\r\n\r\n\t\t/// <summary>\r\n\t\t/// De-serialize System.Collections.Generic.List to XML document.\r\n\t\t/// </summary>\r\n\t\t/// <param name=\"doc\">XML document representing object.</param>\r\n\t\t/// <param name=\"type\">Type of object.</param>\r\n\t\t/// <returns>XML document</returns>\r\n\t\tpublic static object DeserializeFromXml(XmlDocument doc, Type type)\r\n\t\t{\r\n\t\t\tif (doc is null)\r\n\t\t\t\treturn null;\r\n\t\t\treturn DeserializeFromXmlString(doc.OuterXml, type);\r\n\t\t}\r\n\r\n\t\t/// <summary>\r\n\t\t/// De-serialize object from XML bytes. XML bytes can contain Byte Order Mark (BOM).\r\n\t\t/// </summary>\r\n\t\t/// <param name=\"bytes\">XML bytes representing object.</param>\r\n\t\t/// <param name=\"type\">Type of object.</param>\r\n\t\t/// <param name=\"encoding\">Encoding to use (default is UTF8) if Byte Order Mark (BOM) is missing.</param>\r\n\t\t/// <returns>Object.</returns>\r\n\t\tpublic static object DeserializeFromXmlBytes(byte[] bytes, Type type, Encoding encoding = null)\r\n\t\t{\r\n\t\t\tvar ms = new MemoryStream(bytes);\r\n\t\t\t// Use stream reader (inherits from TextReader) to avoid encoding errors.\r\n\t\t\t// Use specified encoding if Byte Order Mark (BOM) is missing.\r\n\t\t\tvar sr = new StreamReader(ms, encoding ?? Encoding.UTF8, true);\r\n\t\t\t// Settings used to protect from\r\n\t\t\t// SUPPRESS: CWE-611: Improper Restriction of XML External Entity Reference('XXE')\r\n\t\t\t// https://cwe.mitre.org/data/definitions/611.html\r\n\t\t\tvar settings = new XmlReaderSettings();\r\n\t\t\tsettings.DtdProcessing = DtdProcessing.Ignore;\r\n\t\t\tsettings.XmlResolver = null;\r\n\t\t\tsettings.IgnoreWhitespace = true;\r\n\t\t\t// Stream 'ms' and 'sr' will be disposed by the reader.\r\n\t\t\tusing (var reader = XmlReader.Create(sr, settings))\r\n\t\t\t{\r\n\t\t\t\tobject o;\r\n\t\t\t\tvar serializer = GetXmlSerializer(type);\r\n\t\t\t\tlock (serializer) { o = serializer.Deserialize(reader); }\r\n\t\t\t\treturn o;\r\n\t\t\t}\r\n\t\t}\r\n\r\n\t\t// Example how to add the missing namespaces.\r\n\t\t// \r\n\t\t// Create a new NameTable\r\n\t\t//var nameTable = new NameTable();\r\n\t\t// Create a new NamespaceManager\r\n\t\t//var nsMgr = new XmlNamespaceManager(nameTable);\r\n\t\t// Add namespaces used in the XML\r\n\t\t//nsMgr.AddNamespace(\"xlink\", \"urn:http://namespaceurl.com\");\r\n\t\t// Create the XmlParserContext using the previous declared XmlNamespaceManager\r\n\t\t//var inputContext = new XmlParserContext(null, nsMgr, null, XmlSpace.None);\r\n\r\n\t\t/// <summary>\r\n\t\t/// De-serialize object from XML string. XML string must not contain Byte Order Mark (BOM).\r\n\t\t/// </summary>\r\n\t\t/// <param name=\"xml\">XML string representing object.</param>\r\n\t\t/// <param name=\"type\">Type of object.</param>\r\n\t\t/// <param name=\"inputContext\">You can use inputContext to add missing namespaces.</param>\r\n\t\t/// <returns>Object.</returns>\r\n\t\tpublic static object DeserializeFromXmlString(string xml, Type type, XmlParserContext inputContext = null, bool ignoreNamespaces = false)\r\n\t\t{\r\n\t\t\t// Note: If you are getting de-serialization error in XML document(1,1) then there is a chance that\r\n\t\t\t// you are trying to de-serialize string which contains Byte Order Mark (BOM) which must not be there.\r\n\t\t\t// Probably you used \"var xml = System.Text.Encoding.GetString(bytes)\" directly on file content.\r\n\t\t\t// You should use \"StreamReader\" on file content, because this method will strip BOM properly\r\n\t\t\t// when converting bytes to string.\r\n\t\t\t// Settings used to protect from\r\n\t\t\t// SUPPRESS: CWE-611: Improper Restriction of XML External Entity Reference('XXE')\r\n\t\t\t// https://cwe.mitre.org/data/definitions/611.html\r\n\t\t\tvar settings = new XmlReaderSettings();\r\n\t\t\tsettings.DtdProcessing = DtdProcessing.Ignore;\r\n\t\t\tsettings.XmlResolver = null;\r\n\t\t\tobject o;\r\n\t\t\tvar serializer = GetXmlSerializer(type);\r\n\t\t\t// Stream 'sr' will be disposed by the reader.\r\n\t\t\tusing (var sr = new StringReader(xml))\r\n\t\t\t{\r\n\t\t\t\tif (ignoreNamespaces)\r\n\t\t\t\t{\r\n\t\t\t\t\tusing (var tr = new XmlTextReader(sr))\r\n\t\t\t\t\t{\r\n\t\t\t\t\t\t// Ignore namespaces.\r\n\t\t\t\t\t\ttr.Namespaces = false;\r\n\t\t\t\t\t\tusing (var reader = XmlReader.Create(tr, settings))\r\n\t\t\t\t\t\t\tlock (serializer) { o = serializer.Deserialize(reader); }\r\n\t\t\t\t\t}\r\n\t\t\t\t\treturn o;\r\n\t\t\t\t}\r\n\t\t\t\tusing (var reader = XmlReader.Create(sr, settings, inputContext))\r\n\t\t\t\t\tlock (serializer) { o = serializer.Deserialize(reader); }\r\n\t\t\t\treturn o;\r\n\t\t\t}\r\n\t\t}\r\n\r\n\t\t/// <summary>\r\n\t\t/// De-serialize object from XML file.\r\n\t\t/// </summary>\r\n\t\t/// <param name=\"filename\">The file name to read from.</param>\r\n\t\t/// <param name=\"type\">Type of object.</param>\r\n\t\t/// <param name=\"encoding\">Encoding to use (default is UTF8) if file Byte Order Mark (BOM) is missing.</param>\r\n\t\t/// <returns>Object.</returns>\r\n\t\tpublic static object DeserializeFromXmlFile(string filename, Type type, Encoding encoding = null, int attempts = 1, int waitTime = 500)\r\n\t\t{\r\n\t\t\t// Read full file content first, so file will be locked for shorter period of time.\r\n\t\t\tvar bytes = ReadFile(filename, attempts, waitTime);\r\n\t\t\tif (bytes is null || bytes.Length == 0)\r\n\t\t\t\treturn null;\r\n\t\t\treturn DeserializeFromXmlBytes(bytes, type, encoding);\r\n\t\t}\r\n\r\n\t\t/// <summary>\r\n\t\t/// De-serialize object from XML Document.\r\n\t\t/// </summary>\r\n\t\t/// <param name=\"doc\">XML document representing object.</param>\r\n\t\t/// <returns>XML document</returns>\r\n\t\tpublic static T DeserializeFromXml<T>(XmlDocument doc)\r\n\t\t{\r\n\t\t\treturn (T)DeserializeFromXml(doc, typeof(T));\r\n\t\t}\r\n\r\n\t\t/// <summary>\r\n\t\t/// De-serialize object from XML string.\r\n\t\t/// </summary>\r\n\t\t/// <param name=\"xml\">XML string representing object.</param>\r\n\t\t/// <returns>Object.</returns>\r\n\t\tpublic static T DeserializeFromXmlString<T>(string xml)\r\n\t\t{\r\n\t\t\treturn (T)DeserializeFromXmlString(xml, typeof(T));\r\n\t\t}\r\n\r\n\t\t/// <summary>\r\n\t\t/// De-serialize object from XML file.\r\n\t\t/// </summary>\r\n\t\t/// <param name=\"filename\">The file name to read from.</param>\r\n\t\t/// <param name=\"encoding\">Specified encoding will be used if file Byte Order Mark (BOM) is missing.</param>\r\n\t\t/// <returns>Object.</returns>\r\n\t\tpublic static T DeserializeFromXmlFile<T>(string filename, Encoding encoding = null, int attempts = 1, int waitTime = 500)\r\n\t\t{\r\n\t\t\treturn (T)DeserializeFromXmlFile(filename, typeof(T), encoding, attempts, waitTime);\r\n\t\t}\r\n\r\n\t\t/// <summary>\r\n\t\t/// De-serialize object from XML bytes. XML bytes can contain Byte Order Mark (BOM).\r\n\t\t/// </summary>\r\n\t\t/// <param name=\"bytes\">XML bytes representing object.</param>\r\n\t\t/// <param name=\"encoding\">The encoding to use (default is UTF8) if Byte Order Mark (BOM) is missing.</param>\r\n\t\t/// <returns>Object.</returns>\r\n\t\tpublic static T DeserializeFromXmlBytes<T>(byte[] bytes, Encoding encoding = null)\r\n\t\t{\r\n\t\t\treturn (T)DeserializeFromXmlBytes(bytes, typeof(T), encoding);\r\n\t\t}\r\n\r\n\t\t#endregion\r\n\r\n\t\t#region XSD: Serialize\r\n\r\n\t\t/// <summary>\r\n\t\t/// Serialize object schema to XSD file.\r\n\t\t/// </summary>\r\n\t\t/// <param name=\"o\">The object to serialize.</param>\r\n\t\t/// <param name=\"path\">The file name to write to.</param>\r\n\t\t/// <param name=\"encoding\">The encoding to use (default is UTF8).</param>\r\n\t\tpublic static void SerializeToXsdFile(object o, string path, Encoding encoding = null, bool omitXmlDeclaration = false, int attempts = 2, int waitTime = 500)\r\n\t\t{\r\n\t\t\tif (o is null)\r\n\t\t\t{\r\n\t\t\t\tWriteFile(path, new byte[0], attempts, waitTime);\r\n\t\t\t\treturn;\r\n\t\t\t}\r\n\t\t\tencoding = encoding ?? Encoding.UTF8;\r\n\t\t\t// Create serialization settings.\r\n\t\t\tvar settings = new XmlWriterSettings();\r\n\t\t\tsettings.OmitXmlDeclaration = omitXmlDeclaration;\r\n\t\t\tsettings.Encoding = encoding;\r\n\t\t\tsettings.Indent = true;\r\n\t\t\t// XmlReader normalizes all newlines and converts '\\r\\n' to '\\n'.\r\n\t\t\t// This requires to save NewLines with  option which\r\n\t\t\t// \"Entitize\" option replace '\\r' with '&#xD;' in text node values.\r\n\t\t\tsettings.NewLineHandling = NewLineHandling.Entitize;\r\n\t\t\t// Serialize in memory first, so file will be locked for shorter times.\r\n\t\t\tvar ms = new MemoryStream();\r\n\t\t\tvar xw = XmlWriter.Create(ms, settings);\r\n\t\t\tvar serializer = GetXmlSerializer(o.GetType());\r\n\t\t\ttry\r\n\t\t\t{\r\n\t\t\t\tvar exporter = new XsdDataContractExporter();\r\n\t\t\t\tif (exporter.CanExport(o.GetType()))\r\n\t\t\t\t{\r\n\t\t\t\t\texporter.Export(o.GetType());\r\n\t\t\t\t\t//Console.WriteLine(\"number of schemas: {0}\", exporter.Schemas.Count);\r\n\t\t\t\t\tXmlSchemaSet schemas = exporter.Schemas;\r\n\t\t\t\t\tXmlQualifiedName XmlNameValue = exporter.GetRootElementName(o.GetType());\r\n\t\t\t\t\tstring nameSpace = XmlNameValue.Namespace;\r\n\t\t\t\t\tforeach (XmlSchema schema in schemas.Schemas(nameSpace))\r\n\t\t\t\t\t{\r\n\t\t\t\t\t\tschema.Write(xw);\r\n\t\t\t\t\t}\r\n\t\t\t\t}\r\n\t\t\t}\r\n\t\t\tcatch (Exception)\r\n\t\t\t{\r\n\t\t\t\txw.Close();\r\n\t\t\t\t// CA2202: Do not dispose objects multiple times\r\n\t\t\t\t//ms.Close();\r\n\t\t\t\tthrow;\r\n\t\t\t}\r\n\t\t\txw.Flush();\r\n\t\t\tbyte[] bytes = ms.ToArray();\r\n\t\t\txw.Close();\r\n\t\t\t// CA2202: Do not dispose objects multiple times\r\n\t\t\t//ms.Close();\r\n\t\t\t// Write serialized data into file.\r\n\t\t\tWriteFile(path, bytes, attempts, waitTime);\r\n\t\t}\r\n\r\n\t\t#endregion\r\n\r\n\t}\r\n\r\n}\r\n"
  },
  {
    "path": "FocusLogger/JocysCom/Text/Helper.cs",
    "content": "using System;\r\nusing System.Collections.Generic;\r\nusing System.IO;\r\nusing System.Linq;\r\nusing System.Text;\r\nusing System.Text.RegularExpressions;\r\n\r\nnamespace JocysCom.ClassLibrary.Text\r\n{\r\n\r\n\tpublic static class Helper\r\n\t{\r\n\t\tprivate static readonly Regex tagRx = new Regex(\"{((?<prefix>[\\\\w]+)[.])?(?<property>[\\\\w]+)(:(?<format>[^{}]+))?}\", RegexOptions.IgnoreCase);\r\n\r\n\t\t/// <summary>\r\n\t\t/// Replace {TypeName.PropertyName[:format]} or {customPrefix.propertyName[:format]} pattern with the property value of the object.\r\n\t\t/// </summary>\r\n\t\t/// <remarks>\r\n\t\t/// Example 1: Supply current date. Use {customPrefix.propertyName[:format]}.\r\n\t\t///\tvar template = \"file_{date.Now:yyyyMMdd}.txt\";\r\n\t\t/// var fileName = JocysCom.ClassLibrary.Text.Helper.Replace(template, DateTime.Now, true, \"date\");\r\n\t\t///\r\n\t\t/// Example 2: Supply profile object. Use {TypeName.PropertyName[:format]}.\r\n\t\t///\tvar template = \"Profile full name: {Profile.first_name} {Profile.last_name}\";\r\n\t\t/// var fileName = JocysCom.ClassLibrary.Text.Helper.Replace(template, profile);\r\n\t\t/// </remarks>\r\n\t\t/// <param name=\"s\">String template</param>\r\n\t\t/// <param name=\"o\">Object values.</param>\r\n\t\t/// <param name=\"usePrefix\">If true then type name or custom prefix, separated by dot, will be used.</param>\r\n\t\t/// <param name=\"customPrefix\">Custom prefix string.</param>\r\n\t\tpublic static string Replace<T>(string s, T o, bool usePrefix = true, string customPrefix = null)\r\n\t\t{\r\n\t\t\tif (string.IsNullOrEmpty(s))\r\n\t\t\t\treturn s;\r\n\t\t\tif (o == null)\r\n\t\t\t\treturn s;\r\n\t\t\tvar t = typeof(T);\r\n\t\t\tvar properties = t.GetProperties();\r\n\t\t\tvar prefix = string.IsNullOrEmpty(customPrefix) ? t.Name : customPrefix;\r\n\t\t\tvar matches = tagRx.Matches(s);\r\n\t\t\tforeach (var p in properties)\r\n\t\t\t{\r\n\t\t\t\tforeach (Match m in matches)\r\n\t\t\t\t{\r\n\t\t\t\t\tif (usePrefix && string.Compare(prefix, m.Groups[\"prefix\"].Value, true) != 0)\r\n\t\t\t\t\t\tcontinue;\r\n\t\t\t\t\tif (string.Compare(p.Name, m.Groups[\"property\"].Value, true) != 0)\r\n\t\t\t\t\t\tcontinue;\r\n\t\t\t\t\tvar format = m.Groups[\"format\"].Value;\r\n\t\t\t\t\tvar value = p.GetValue(o, null);\r\n\t\t\t\t\tvar text = string.IsNullOrEmpty(format)\r\n\t\t\t\t\t\t? string.Format(\"{0}\", value)\r\n\t\t\t\t\t\t: string.Format(\"{0:\" + format + \"}\", value);\r\n\t\t\t\t\ts = Replace(s, m.Value, text, StringComparison.OrdinalIgnoreCase);\r\n\t\t\t\t}\r\n\t\t\t}\r\n\t\t\treturn s;\r\n\t\t}\r\n\r\n\t\t/// <summary>\r\n\t\t/// Replace {TypeName.PropertyName[:format]} or {customPrefix.propertyName[:format]} pattern with the property value of the object.\r\n\t\t/// </summary>\r\n\t\t/// <remarks>\r\n\t\t/// Example 1: Supply current date. Use {customPrefix.propertyName[:format]}.\r\n\t\t///\tvar template = \"file_{date.Now:yyyyMMdd}.txt\";\r\n\t\t/// var fileName = JocysCom.ClassLibrary.Text.Helper.Replace(template, DateTime.Now, true, \"date\");\r\n\t\t///\r\n\t\t/// Example 2: Supply profile object. Use {TypeName.PropertyName[:format]}.\r\n\t\t///\tvar template = \"Profile full name: {Profile.first_name} {Profile.last_name}\";\r\n\t\t/// var fileName = JocysCom.ClassLibrary.Text.Helper.Replace(template, profile);\r\n\t\t/// </remarks>\r\n\t\t/// <param name=\"s\">String template</param>\r\n\t\t/// <param name=\"o\">Object values.</param>\r\n\t\tpublic static string ReplaceDictionary(string s, Dictionary<string, object> o, bool usePrefix = true, string customPrefix = null)\r\n\t\t{\r\n\t\t\tif (string.IsNullOrEmpty(s))\r\n\t\t\t\treturn s;\r\n\t\t\tif (o == null)\r\n\t\t\t\treturn s;\r\n\t\t\tvar prefix = customPrefix;\r\n\t\t\tvar matches = tagRx.Matches(s);\r\n\t\t\tforeach (var key in o.Keys)\r\n\t\t\t{\r\n\t\t\t\tforeach (Match m in matches)\r\n\t\t\t\t{\r\n\t\t\t\t\tif (usePrefix && string.Compare(prefix, m.Groups[\"prefix\"].Value, true) != 0)\r\n\t\t\t\t\t\tcontinue;\r\n\t\t\t\t\tif (string.Compare(key, m.Groups[\"property\"].Value, true) != 0)\r\n\t\t\t\t\t\tcontinue;\r\n\t\t\t\t\tvar format = m.Groups[\"format\"].Value;\r\n\t\t\t\t\tvar value = o[key];\r\n\t\t\t\t\tvar text = string.IsNullOrEmpty(format)\r\n\t\t\t\t\t\t? string.Format(\"{0}\", value)\r\n\t\t\t\t\t\t: string.Format(\"{0:\" + format + \"}\", value);\r\n\t\t\t\t\ts = Replace(s, m.Value, text, StringComparison.OrdinalIgnoreCase);\r\n\t\t\t\t}\r\n\t\t\t}\r\n\t\t\treturn s;\r\n\t\t}\r\n\r\n\t\tpublic static List<string> GetReplaceMacros<T>(bool usePrefix = true, string customPrefix = null)\r\n\t\t{\r\n\t\t\tvar list = new List<string>();\r\n\t\t\tvar t = typeof(T);\r\n\t\t\tvar properties = t.GetProperties();\r\n\t\t\tvar prefix = string.IsNullOrEmpty(customPrefix) ? t.Name : customPrefix;\r\n\t\t\tforeach (var p in properties)\r\n\t\t\t{\r\n\t\t\t\tvar macro = usePrefix\r\n\t\t\t\t\t? prefix + \".\" + p.Name\r\n\t\t\t\t\t: p.Name;\r\n\t\t\t\tlist.Add(macro);\r\n\t\t\t}\r\n\t\t\treturn list;\r\n\t\t}\r\n\r\n\t\t/// <summary>\r\n\t\t/// Used to parametrize path. For example:\r\n\t\t/// Convert \"C:\\Program Files\\JocysCom\\Focus Logger\" to\r\n\t\t/// \"C:\\Program Files\\{Company}\\{Product}\"\r\n\t\t/// </summary>\r\n\t\tpublic static string Replace<T>(T o, string s, bool usePrefix = true, string customPrefix = null)\r\n\t\t{\r\n\t\t\tif (string.IsNullOrEmpty(s))\r\n\t\t\t\treturn s;\r\n\t\t\tif (o == null)\r\n\t\t\t\treturn s;\r\n\t\t\tvar t = typeof(T);\r\n\t\t\tvar properties = t.GetProperties();\r\n\t\t\tvar prefix = string.IsNullOrEmpty(customPrefix) ? t.Name : customPrefix;\r\n\t\t\tvar replacement = new List<(string Param, string Value)>();\r\n\t\t\tforeach (var p in properties)\r\n\t\t\t{\r\n\t\t\t\tvar value = $\"{p.GetValue(o, null)}\";\r\n\t\t\t\tif (string.IsNullOrEmpty(value))\r\n\t\t\t\t\tcontinue;\r\n\t\t\t\tvar param = \"{\";\r\n\t\t\t\tif (usePrefix && !string.IsNullOrEmpty(prefix))\r\n\t\t\t\t\tparam += prefix;\r\n\t\t\t\tparam += p.Name + \"}\";\r\n\t\t\t\treplacement.Add((param, value));\r\n\t\t\t}\r\n\t\t\treplacement = replacement\r\n\t\t\t\t.OrderByDescending(x => x.Value.Length)\r\n\t\t\t\t.ThenBy(x => x.Param.Length)\r\n\t\t\t\t.ToList();\r\n\t\t\tforeach (var item in replacement)\r\n\t\t\t\ts = Replace(s, item.Value, item.Param, StringComparison.OrdinalIgnoreCase);\r\n\t\t\treturn s;\r\n\t\t}\r\n\r\n\t\t/// <summary>Case insensitive replace.</summary>\r\n\t\tpublic static string Replace(string s, string oldValue, string newValue, StringComparison comparison)\r\n\t\t{\r\n\t\t\tif (string.IsNullOrEmpty(s))\r\n\t\t\t\treturn s;\r\n\t\t\tif (string.IsNullOrEmpty(oldValue))\r\n\t\t\t\tthrow new ArgumentNullException(nameof(oldValue));\r\n\t\t\tvar sb = new StringBuilder();\r\n\t\t\tvar previousIndex = 0;\r\n\t\t\tvar index = s.IndexOf(oldValue, comparison);\r\n\t\t\twhile (index != -1)\r\n\t\t\t{\r\n\t\t\t\tsb.Append(s.Substring(previousIndex, index - previousIndex));\r\n\t\t\t\tsb.Append(newValue);\r\n\t\t\t\tindex += oldValue.Length;\r\n\t\t\t\tpreviousIndex = index;\r\n\t\t\t\tindex = s.IndexOf(oldValue, index, comparison);\r\n\t\t\t}\r\n\t\t\tsb.Append(s.Substring(previousIndex));\r\n\t\t\treturn sb.ToString();\r\n\t\t}\r\n\r\n\t\t/// <summary>\r\n\t\t/// Reports the zero-based index of the first occurrence of the specified value\r\n\t\t/// in input. The search starts at a specified byte position.\r\n\t\t/// </summary>\r\n\t\t/// <param name=\"input\">input bytes.</param>\r\n\t\t/// <param name=\"value\">The bytes to seek.</param>\r\n\t\t/// <param name=\"startIndex\">The search starting position.</param>\r\n\t\t/// <returns>\r\n\t\t/// The zero-based index position of value if that byte value is found, or -1 if it is not.\r\n\t\t/// </returns>\r\n\t\t/// <remarks>This is very fast search.</remarks>\r\n\t\tpublic static int IndexOf(byte[] input, byte[] value, int startIndex = 0)\r\n\t\t{\r\n\t\t\tif (input is null)\r\n\t\t\t\tthrow new ArgumentNullException(nameof(input));\r\n\t\t\tif (value is null)\r\n\t\t\t\tthrow new ArgumentNullException(nameof(value));\r\n\t\t\tvar endIndex = input.Length - value.Length;\r\n\t\t\tint v;\r\n\t\t\tfor (var i = startIndex; i <= endIndex; i++)\r\n\t\t\t{\r\n\t\t\t\t// Check sequence against pattern.\r\n\t\t\t\tfor (v = 0; v < value.Length; v++)\r\n\t\t\t\t{\r\n\t\t\t\t\t// Break if byte doesn't match.\r\n\t\t\t\t\tif (input[i + v] != value[v])\r\n\t\t\t\t\t\tbreak;\r\n\t\t\t\t\t// If last byte matched then return.\r\n\t\t\t\t\telse if (v + 1 == value.Length)\r\n\t\t\t\t\t\treturn i;\r\n\t\t\t\t}\r\n\t\t\t}\r\n\t\t\treturn -1;\r\n\t\t}\r\n\r\n\t\t/// <summary>\r\n\t\t/// Get value from text [name]:\\s*[value].\r\n\t\t/// </summary>\r\n\t\t/// <param name=\"name\">Prefix name.</param>\r\n\t\t/// <param name=\"s\">String to get value from.</param>\r\n\t\t/// <param name=\"defaultValue\">Override default value.</param>\r\n\t\tpublic static string GetValue(string name, string s, string defaultValue = \"\")\r\n\t\t{\r\n\t\t\tvar pattern = string.Format(@\"{0}:\\s*(?<Value>[^\\s]+)\", name);\r\n\t\t\tvar rx = new Regex(pattern, RegexOptions.IgnoreCase);\r\n\t\t\tvar m = rx.Match(s ?? \"\");\r\n\t\t\tif (!m.Success)\r\n\t\t\t\treturn defaultValue;\r\n\t\t\tvar v = m.Groups[\"Value\"].Value;\r\n\t\t\treturn v;\r\n\t\t}\r\n\r\n\t\t/// <summary>\r\n\t\t/// Convert string value to an escaped C# string literal.\r\n\t\t/// </summary>\r\n\t\tpublic static string ToLiteral(string input)\r\n\t\t{\r\n\t\t\treturn FormatLiteral(input, quote: true);\r\n\t\t}\r\n\r\n\t\t/// <summary>Return a C# string literal (optionally quoted) that represents <paramref name=\"value\"/>.</summary>\r\n\t\tprivate static string FormatLiteral(string value, bool quote)\r\n\t\t{\r\n\t\t\tif (value is null) return \"null\";\r\n\r\n\t\t\tvar sb = new StringBuilder(value.Length + 2);\r\n\t\t\tif (quote) sb.Append('\"');\r\n\r\n\t\t\tfor (int i = 0; i < value.Length; i++)\r\n\t\t\t{\r\n\t\t\t\tchar c = value[i];\r\n\t\t\t\tswitch (c)\r\n\t\t\t\t{\r\n\t\t\t\t\tcase '\\\"': sb.Append(\"\\\\\\\"\"); break; // quote\r\n\t\t\t\t\tcase '\\\\': sb.Append(\"\\\\\\\\\"); break; // backslash\r\n\t\t\t\t\tcase '\\0': sb.Append(\"\\\\0\"); break;\r\n\t\t\t\t\tcase '\\a': sb.Append(\"\\\\a\"); break;\r\n\t\t\t\t\tcase '\\b': sb.Append(\"\\\\b\"); break;\r\n\t\t\t\t\tcase '\\f': sb.Append(\"\\\\f\"); break;\r\n\t\t\t\t\tcase '\\n': sb.Append(\"\\\\n\"); break;\r\n\t\t\t\t\tcase '\\r': sb.Append(\"\\\\r\"); break;\r\n\t\t\t\t\tcase '\\t': sb.Append(\"\\\\t\"); break;\r\n\t\t\t\t\tcase '\\v': sb.Append(\"\\\\v\"); break;\r\n\r\n\t\t\t\t\tdefault:\r\n\t\t\t\t\t\t// Line/para separators & NEL must be escaped in C# source, and any control chars.\r\n\t\t\t\t\t\tif (char.IsControl(c) || c == '\\u0085' || c == '\\u2028' || c == '\\u2029')\r\n\t\t\t\t\t\t{\r\n\t\t\t\t\t\t\tsb.Append(\"\\\\u\").Append(((int)c).ToString(\"x4\"));\r\n\t\t\t\t\t\t}\r\n\t\t\t\t\t\telse if (char.IsSurrogate(c))\r\n\t\t\t\t\t\t{\r\n\t\t\t\t\t\t\t// If it's a valid surrogate pair, keep it as-is (e.g., emoji).\r\n\t\t\t\t\t\t\tif (char.IsHighSurrogate(c) && i + 1 < value.Length && char.IsLowSurrogate(value[i + 1]))\r\n\t\t\t\t\t\t\t{\r\n\t\t\t\t\t\t\t\tsb.Append(c).Append(value[++i]);\r\n\t\t\t\t\t\t\t}\r\n\t\t\t\t\t\t\telse\r\n\t\t\t\t\t\t\t{\r\n\t\t\t\t\t\t\t\t// Unpaired surrogate — escape it.\r\n\t\t\t\t\t\t\t\tsb.Append(\"\\\\u\").Append(((int)c).ToString(\"x4\"));\r\n\t\t\t\t\t\t\t}\r\n\t\t\t\t\t\t}\r\n\t\t\t\t\t\telse\r\n\t\t\t\t\t\t{\r\n\t\t\t\t\t\t\tsb.Append(c);\r\n\t\t\t\t\t\t}\r\n\t\t\t\t\t\tbreak;\r\n\t\t\t\t}\r\n\t\t\t}\r\n\t\t\tif (quote)\r\n\t\t\t\tsb.Append('\"');\r\n\t\t\treturn sb.ToString();\r\n\t\t}\r\n\r\n\t\t#region Word Wrap\r\n\r\n\t\t// http://www.codeproject.com/Articles/51488/Implementing-Word-Wrap-in-C\r\n\r\n\r\n\t\t/// <summary>\r\n\t\t/// Word wraps the given text to fit within the specified width.\r\n\t\t/// </summary>\r\n\t\t/// <param name=\"text\">Text to be word wrapped</param>\r\n\t\t/// <param name=\"width\">Width, in characters, to which the text\r\n\t\t/// should be word wrapped</param>\r\n\t\t/// <returns>The modified text</returns>\r\n\t\tpublic static string WrapText(string text, int width, bool useSpaces = false)\r\n\t\t{\r\n\t\t\tif (text is null)\r\n\t\t\t\tthrow new ArgumentNullException(nameof(text));\r\n\t\t\t// Lucidity check\r\n\t\t\tif (width < 1)\r\n\t\t\t\treturn text;\r\n\t\t\tif (!useSpaces)\r\n\t\t\t{\r\n\t\t\t\tvar sb2 = new StringBuilder(string.Empty);\r\n\t\t\t\tvar i = 0;\r\n\t\t\t\tforeach (var c in text)\r\n\t\t\t\t{\r\n\t\t\t\t\tif (i > 0 && i % width == 0)\r\n\t\t\t\t\t\tsb2.Append(Environment.NewLine);\r\n\t\t\t\t\tsb2.Append(c);\r\n\t\t\t\t\ti++;\r\n\t\t\t\t}\r\n\t\t\t\treturn sb2.ToString().Trim('\\r', '\\n');\r\n\t\t\t}\r\n\t\t\tint pos, next;\r\n\t\t\tvar sb = new StringBuilder();\r\n\t\t\t// Parse each line of text\r\n\t\t\tfor (pos = 0; pos < text.Length; pos = next)\r\n\t\t\t{\r\n\t\t\t\t// Find end of line\r\n\t\t\t\tvar eol = text.IndexOf(Environment.NewLine, pos);\r\n\t\t\t\tif (eol == -1)\r\n\t\t\t\t\tnext = eol = text.Length;\r\n\t\t\t\telse\r\n\t\t\t\t\tnext = eol + Environment.NewLine.Length;\r\n\t\t\t\t// Copy this line of text, breaking into smaller lines as needed\r\n\t\t\t\tif (eol > pos)\r\n\t\t\t\t{\r\n\t\t\t\t\tdo\r\n\t\t\t\t\t{\r\n\t\t\t\t\t\tvar len = eol - pos;\r\n\t\t\t\t\t\tif (len > width)\r\n\t\t\t\t\t\t\tlen = BreakLine(text, pos, width);\r\n\t\t\t\t\t\tsb.Append(text, pos, len);\r\n\t\t\t\t\t\tsb.Append(Environment.NewLine);\r\n\t\t\t\t\t\t// Trim white space following break\r\n\t\t\t\t\t\tpos += len;\r\n\t\t\t\t\t\twhile (pos < eol && char.IsWhiteSpace(text[pos]))\r\n\t\t\t\t\t\t\tpos++;\r\n\t\t\t\t\t} while (eol > pos);\r\n\t\t\t\t}\r\n\t\t\t\telse sb.Append(Environment.NewLine); // Empty line\r\n\t\t\t}\r\n\t\t\treturn sb.ToString();\r\n\t\t}\r\n\r\n\t\t/// <summary>\r\n\t\t/// Locates position to break the given line so as to avoid\r\n\t\t/// breaking words.\r\n\t\t/// </summary>\r\n\t\t/// <param name=\"text\">String that contains line of text</param>\r\n\t\t/// <param name=\"pos\">Index where line of text starts</param>\r\n\t\t/// <param name=\"max\">Maximum line length</param>\r\n\t\t/// <returns>The modified line length</returns>\r\n\t\tprivate static int BreakLine(string text, int pos, int max)\r\n\t\t{\r\n\t\t\t// Find last white space in line\r\n\t\t\tvar i = max;\r\n\t\t\twhile (i >= 0 && !char.IsWhiteSpace(text[pos + i]))\r\n\t\t\t\ti--;\r\n\t\t\t// If no white space found, break at maximum length\r\n\t\t\tif (i < 0)\r\n\t\t\t\treturn max;\r\n\t\t\t// Find start of white space\r\n\t\t\twhile (i >= 0 && char.IsWhiteSpace(text[pos + i]))\r\n\t\t\t\ti--;\r\n\t\t\t// Return length of text before white space\r\n\t\t\treturn i + 1;\r\n\t\t}\r\n\r\n\t\t#endregion\r\n\r\n\t\t/// <summary>\r\n\t\t/// Add or remove ident.\r\n\t\t/// </summary>\r\n\t\t/// <param name=\"s\">String to ident.</param>\r\n\t\t/// <param name=\"tabs\">Positive - add ident, negative - remove ident.</param>\r\n\t\t/// <param name=\"ident\">Ident character</param>\r\n\t\tpublic static string IdentText(string s, int tabs = 1, string ident = \"\\t\")\r\n\t\t{\r\n\t\t\tif (tabs == 0)\r\n\t\t\t\treturn s;\r\n\t\t\tif (string.IsNullOrEmpty(s))\r\n\t\t\t\treturn s;\r\n\t\t\tvar sb = new StringBuilder();\r\n\t\t\tvar tr = new StringReader(s);\r\n\t\t\tvar prefix = string.Concat(Enumerable.Repeat(ident, tabs));\r\n\t\t\tstring line;\r\n\t\t\tvar lines = s.Split(new string[] { \"\\r\\n\", \"\\r\", \"\\n\" }, StringSplitOptions.None);\r\n\t\t\tfor (int i = 0; i < lines.Length; i++)\r\n\t\t\t{\r\n\t\t\t\tline = lines[i];\r\n\t\t\t\tif (line != \"\")\r\n\t\t\t\t{\r\n\t\t\t\t\t// If add idents then...\r\n\t\t\t\t\tif (tabs > 0)\r\n\t\t\t\t\t\tsb.Append(prefix);\r\n\t\t\t\t\t// If remove idents then...\r\n\t\t\t\t\telse if (tabs < 0)\r\n\t\t\t\t\t{\r\n\t\t\t\t\t\tvar count = 0;\r\n\t\t\t\t\t\t// Count how much idents could be removed\r\n\t\t\t\t\t\twhile (line.Substring(count * ident.Length, ident.Length) == ident && count < tabs)\r\n\t\t\t\t\t\t\tcount++;\r\n\t\t\t\t\t\tline = line.Substring(count * ident.Length);\r\n\t\t\t\t\t}\r\n\t\t\t\t}\r\n\t\t\t\tif (i < lines.Length - 1)\r\n\t\t\t\t\tsb.AppendLine(line);\r\n\t\t\t\telse\r\n\t\t\t\t\tsb.Append(line);\r\n\t\t\t}\r\n\t\t\ttr.Dispose();\r\n\t\t\treturn sb.ToString();\r\n\t\t}\r\n\r\n\t\tpublic static string RemoveIdent(string s)\r\n\t\t{\r\n\t\t\ts = s.Trim('\\n', '\\r', ' ', '\\t').Replace(\"\\r\\n\", \"\\n\");\r\n\t\t\tvar lines = s.Split('\\n');\r\n\t\t\tvar checkLines = lines\r\n\t\t\t\t// Ignore first trimmed line.\r\n\t\t\t\t.Where((x, i) => i > 0 && !string.IsNullOrWhiteSpace(x)).ToArray();\r\n\t\t\tif (checkLines.Length == 0)\r\n\t\t\t\treturn s;\r\n\t\t\tvar minIndent = checkLines.Min(x => x.Length - x.TrimStart(' ', '\\t').Length);\r\n\t\t\tfor (var i = 0; i < lines.Length; i++)\r\n\t\t\t{\r\n\t\t\t\tif (lines[i].Length > minIndent)\r\n\t\t\t\t\t// Don't trim first line.\r\n\t\t\t\t\tlines[i] = lines[i].Substring(i == 0 ? 0 : minIndent);\r\n\t\t\t\telse if (string.IsNullOrWhiteSpace(lines[i]))\r\n\t\t\t\t\tlines[i] = \"\";\r\n\t\t\t}\r\n\t\t\treturn string.Join(Environment.NewLine, lines);\r\n\t\t}\r\n\r\n\t\tpublic static string BytesToStringBlock(string s, bool addIndex, bool addHex, bool addText)\r\n\t\t{\r\n\t\t\tvar bytes = Encoding.ASCII.GetBytes(s);\r\n\t\t\treturn BytesToStringBlock(bytes, addIndex, addHex, addText);\r\n\t\t}\r\n\r\n\t\tpublic static string BytesToStringBlock(byte[] bytes, bool addIndex, bool addHex, bool addText, int offset = 0, int size = -1, int? maxDisplayLines = null)\r\n\t\t{\r\n\t\t\tif (bytes is null)\r\n\t\t\t\tthrow new ArgumentNullException(nameof(bytes));\r\n\t\t\tvar builder = new StringBuilder();\r\n\t\t\tvar hx = new StringBuilder();\r\n\t\t\tvar ch = new StringBuilder();\r\n\t\t\tvar lineIndex = 0;\r\n\t\t\tvar lines = new List<string>();\r\n\t\t\tvar length = size == -1 ? bytes.Length : size;\r\n\t\t\tfor (var i = 1; i <= length; i++)\r\n\t\t\t{\r\n\t\t\t\tvar modulus = i % 16;\r\n\t\t\t\thx.Append(bytes[i - 1 + offset].ToString(\"X2\")).Append(\" \");\r\n\t\t\t\tvar c = (char)bytes[i - 1 + offset];\r\n\t\t\t\tif (char.IsControl(c))\r\n\t\t\t\t\tch.Append(\".\");\r\n\t\t\t\telse\r\n\t\t\t\t\tch.Append(c);\r\n\t\t\t\t// If line ended.\r\n\t\t\t\tif ((modulus == 0 && i > 1) || (i == length))\r\n\t\t\t\t{\r\n\t\t\t\t\tif (addIndex)\r\n\t\t\t\t\t{\r\n\t\t\t\t\t\tbuilder.Append((lineIndex * 16).ToString(\"X8\"));\r\n\t\t\t\t\t\tif (addHex || addText)\r\n\t\t\t\t\t\t\tbuilder.Append(\": \");\r\n\t\t\t\t\t}\r\n\t\t\t\t\tif (addHex)\r\n\t\t\t\t\t{\r\n\t\t\t\t\t\tif (hx.Length < 50) hx.Append(' ', 50 - hx.Length);\r\n\t\t\t\t\t\tbuilder.Append(hx.ToString());\r\n\t\t\t\t\t\tif (addText)\r\n\t\t\t\t\t\t\tbuilder.Append(\" | \");\r\n\t\t\t\t\t}\r\n\t\t\t\t\tif (addText)\r\n\t\t\t\t\t{\r\n\t\t\t\t\t\tbuilder.Append(ch.ToString());\r\n\t\t\t\t\t\tif (!maxDisplayLines.HasValue || lines.Count < maxDisplayLines.Value)\r\n\t\t\t\t\t\t\tlines.Add(builder.ToString());\r\n\t\t\t\t\t\tbuilder.Clear();\r\n\t\t\t\t\t}\r\n\t\t\t\t\thx.Clear();\r\n\t\t\t\t\tch.Clear();\r\n\t\t\t\t\tlineIndex++;\r\n\t\t\t\t}\r\n\t\t\t}\r\n\t\t\tif (lineIndex > lines.Count)\r\n\t\t\t\tlines[lines.Count - 1] = string.Format(\"... {0} more lines.\", lineIndex - lines.Count + 1);\r\n\t\t\treturn string.Join(Environment.NewLine, lines);\r\n\t\t}\r\n\r\n\t\tpublic static string CropLines(string s, int maxLines = 8)\r\n\t\t{\r\n\t\t\tif (string.IsNullOrEmpty(s))\r\n\t\t\t\treturn s;\r\n\t\t\tvar lines = s.Split(new string[] { Environment.NewLine }, StringSplitOptions.None);\r\n\t\t\tif (lines.Length <= maxLines)\r\n\t\t\t\treturn s;\r\n\t\t\tvar sb = new StringBuilder();\r\n\t\t\tfor (var i = 0; i < maxLines - 1; i++)\r\n\t\t\t\tsb.AppendLine(lines[i]);\r\n\t\t\tsb.AppendFormat(\"... {0} more lines.\", lines.Length - maxLines + 1);\r\n\t\t\treturn sb.ToString();\r\n\t\t}\r\n\r\n\t\t/// <summary>\r\n\t\t/// Convert .NFO file content to Unicode so they can be displayed properly between 'pre' tags on web.\r\n\t\t/// </summary>\r\n\t\tpublic static string IBM437ToUTF8(byte[] bytes)\r\n\t\t{\r\n\t\t\tvar enc = Encoding.GetEncoding(\"IBM437\");\r\n\t\t\treturn enc.GetString(bytes);\r\n\t\t}\r\n\r\n\t\t/// <summary>\r\n\t\t/// Convert .NFO file content to Unicode so they can be displayed properly between 'pre' tags on web.\r\n\t\t/// </summary>\r\n\t\tpublic static string IBM437ToUTF8(string text)\r\n\t\t{\r\n\t\t\tvar bytes = Encoding.ASCII.GetBytes(text);\r\n\t\t\tvar enc = Encoding.GetEncoding(\"IBM437\");\r\n\t\t\treturn enc.GetString(bytes);\r\n\t\t}\r\n\r\n\t\t#region TimeSpan\r\n\r\n\t\t/// <summary>\r\n\t\t/// Convert timespan to string.\r\n\t\t/// </summary>\r\n\t\t/// <param name=\"ts\">TimeSpan value to convert.</param>\r\n\t\t/// <param name=\"includeMilliseconds\">include milliseconds.</param>\r\n\t\t/// <param name=\"useWords\">Use words instead of ':' and '.' separator.</param>\r\n\t\t/// <param name=\"useShortWords\">Use short words. Applied when useWords = true.</param>\r\n\t\t/// <param name=\"precision\">Precision. Applied when useWords = true.</param>\r\n\t\t/// <returns></returns>\r\n\t\tpublic static string TimeSpanToString(TimeSpan ts, bool includeMilliseconds = false, bool useWords = false, bool useShortWords = false, int? precision = null)\r\n\t\t{\r\n\t\t\tvar s = \"\";\r\n\t\t\tif (useWords)\r\n\t\t\t{\r\n\t\t\t\tvar list = new List<string>();\r\n\t\t\t\tif (ts.Days != 0)\r\n\t\t\t\t{\r\n\t\t\t\t\ts = string.Format(\"{0} {1}\", ts.Days, ts.Days == 1 ? \"day\" : \"days\");\r\n\t\t\t\t\tlist.Add(s);\r\n\t\t\t\t}\r\n\t\t\t\tif (ts.Hours != 0 && (!precision.HasValue || list.Count < precision.Value))\r\n\t\t\t\t{\r\n\t\t\t\t\ts = string.Format(\"{0} {1}\", ts.Hours, ts.Hours == 1 ? \"hour\" : \"hours\");\r\n\t\t\t\t\tlist.Add(s);\r\n\t\t\t\t}\r\n\t\t\t\tif (ts.Minutes != 0 && (!precision.HasValue || list.Count < precision.Value))\r\n\t\t\t\t{\r\n\t\t\t\t\ts = string.Format(\"{0} {1}\", ts.Minutes, useShortWords ? \"min\" : (ts.Minutes == 1 ? \"minute\" : \"minutes\"));\r\n\t\t\t\t\tlist.Add(s);\r\n\t\t\t\t}\r\n\t\t\t\t// Force to show seconds if milliseconds will not be visible.\r\n\t\t\t\tif (!precision.HasValue || list.Count < precision.Value)\r\n\t\t\t\t{\r\n\t\t\t\t\ts = string.Format(\"{0} {1}\", ts.Seconds, useShortWords ? \"sec\" : (ts.Seconds == 1 ? \"second\" : \"seconds\"));\r\n\t\t\t\t\tlist.Add(s);\r\n\t\t\t\t}\r\n\t\t\t\tvar showMilliseconds = includeMilliseconds && (ts.Milliseconds != 0 || list.Count == 0);\r\n\t\t\t\tif (showMilliseconds && (!precision.HasValue || list.Count < precision.Value))\r\n\t\t\t\t{\r\n\t\t\t\t\ts = string.Format(\"{0} {1}\", ts.Milliseconds, useShortWords ? \"ms\" : (ts.Milliseconds == 1 ? \"millisecond\" : \"milliseconds\"));\r\n\t\t\t\t\tlist.Add(s);\r\n\t\t\t\t}\r\n\t\t\t\ts = string.Join(\" \", list);\r\n\t\t\t}\r\n\t\t\telse\r\n\t\t\t{\r\n\t\t\t\tif (ts.Days != 0)\r\n\t\t\t\t\ts += ts.Days.ToString(\"0\") + \".\";\r\n\t\t\t\tif (s.Length != 0 || ts.Hours > 0)\r\n\t\t\t\t\ts += ts.Days.ToString(\"00\") + \":\";\r\n\t\t\t\tif (s.Length != 0 || ts.Minutes > 0)\r\n\t\t\t\t\ts += ts.Minutes.ToString(\"00\") + \":\";\r\n\t\t\t\t// Seconds will be always included.\r\n\t\t\t\ts += ts.Seconds.ToString(\"00\");\r\n\t\t\t\tif (includeMilliseconds)\r\n\t\t\t\t\ts += \".\" + ts.Milliseconds.ToString(\"000\");\r\n\t\t\t}\r\n\t\t\treturn s;\r\n\t\t}\r\n\r\n\t\t/// <summary>Time Span Standard regular expression.</summary>\r\n\t\t/// <remarks>\r\n\t\t/// Minutes are mandatory with required colon from left or right.\r\n\t\t/// Pattern: [-][[dd.]HH:](:mm|mm:)[:ss[.fffffff]]\r\n\t\t/// </remarks>\r\n\t\tpublic const string TimeSpanStandard =\r\n\t\t\t@\"(?:(?<ne>-))?\" +\r\n\t\t\t@\"(?:(?:(?<dd>0*[0-9]+)[.])?(?:(?<HH>0*[2][0-3]|0*[1][0-9]|0*[0-9])[:]))?\" +\r\n\t\t\t@\"(?<mm>(?<=:)0*[0-5]?[0-9]|0*[5-9]?[0-9](?=[:]))\" +\r\n\t\t\t@\"(?:[:](?<ss>0*[0-5]?[0-9](?:[.][0-9]{0,7})?))?\";\r\n\r\n\t\t/// <summary>\r\n\t\t/// Convert JSON TimeSpan format...\r\n\t\t///\t\tFrom Standard: [-][d.]HH:mm[:ss.fffffff]\r\n\t\t///\t\tTo   ISO8601:  P(n)Y(n)M(n)DT(n)H(n)M(n)S\r\n\t\t/// </summary>\r\n\t\tpublic static string ConvertTimeSpanStandardToISO8601(string jsonString)\r\n\t\t{\r\n\t\t\tvar spanRx = new Regex(\"\\\"\" + TimeSpanStandard + \"\\\"\");\r\n\t\t\tvar me = new MatchEvaluator((Match m) =>\r\n\t\t\t{\r\n\t\t\t\tvar standard = m.Value.Trim('\"');\r\n\t\t\t\tvar span = TimeSpan.Parse(standard);\r\n\t\t\t\tvar iso8601 = System.Xml.XmlConvert.ToString(span);\r\n\t\t\t\treturn string.Format(@\"\"\"{0}\"\"\", iso8601);\r\n\t\t\t});\r\n\t\t\treturn spanRx.Replace(jsonString, me);\r\n\t\t}\r\n\r\n\t\t/// <summary>Time Span ISO8601 regular expression</summary>\r\n\t\tpublic const string TimeSpanISO8601 =\r\n\t\t\t@\"(?<V>(P(?=\\d+[YMWD])?(\\d+Y)?(\\d+M)?(\\d+W)?(\\d+D)?)(T(?=\\d+[HMS])(\\d+H)?(\\d+M)?(\\d+S)?))\";\r\n\r\n\t\t/// <summary>\r\n\t\t/// Convert JSON TimeSpan format...\r\n\t\t///\t\tFrom ISO8601:  P(n)Y(n)M(n)DT(n)H(n)M(n)S\r\n\t\t///\t\tTo   Standard: [-][d.]HH:mm[:ss.fffffff]\r\n\t\t/// </summary>\r\n\t\tpublic static string ConvertTimeSpanISO8601ToStandard(string jsonString)\r\n\t\t{\r\n\t\t\tvar spanRx = new Regex(\"\\\"\" + TimeSpanISO8601 + \"\\\"\");\r\n\t\t\tvar me = new MatchEvaluator((Match m) =>\r\n\t\t\t{\r\n\t\t\t\tvar iso8601 = m.Groups[\"V\"].Value.Trim('\"');\r\n\t\t\t\tvar span = System.Xml.XmlConvert.ToTimeSpan(iso8601);\r\n\t\t\t\tvar standard = span.ToString();\r\n\t\t\t\treturn string.Format(@\"\"\"{0}\"\"\", standard);\r\n\t\t\t});\r\n\t\t\treturn spanRx.Replace(jsonString, me);\r\n\t\t}\r\n\r\n\t\t#endregion\r\n\r\n\t}\r\n}\r\n"
  },
  {
    "path": "FocusLogger/JocysCom.FocusLogger.csproj",
    "content": "﻿<Project Sdk=\"Microsoft.NET.Sdk\">\r\n\r\n  <PropertyGroup>\r\n    <OutputType>WinExe</OutputType>\r\n    <TargetFramework>net8.0-windows</TargetFramework>\r\n    <UseWPF>true</UseWPF>\r\n\t<UseWindowsForms>true</UseWindowsForms>\r\n\t<Authors>Jocys.com</Authors>\r\n\t<Company>Jocys.com</Company>\r\n\t<Product>Focus Logger</Product>\r\n\t<Description>Find out which process or program is taking the window focus. In game, mouse and keyboard could temporarily stop responding if another program takes the focus. This tool could help diagnose which program is stealing the focus.</Description>\r\n\t<ApplicationIcon>App.ico</ApplicationIcon>\r\n\t<Version>1.2.6</Version>\r\n\t<RepositoryUrl>https://github.com/JocysCom/FocusLogger</RepositoryUrl>\r\n\t<PackageProjectUrl>https://www.jocys.com</PackageProjectUrl>\r\n\t<Copyright>Copyright © Jocys.com 2026</Copyright>\r\n\t<PackageLicenseExpression>GNU General Public License v3.0</PackageLicenseExpression>\r\n  </PropertyGroup>\r\n\r\n  <PropertyGroup Condition=\"'$(Configuration)|$(Platform)'=='Debug|AnyCPU'\">\r\n    <DebugType>embedded</DebugType>\r\n    <DebugSymbols>true</DebugSymbols>\r\n  </PropertyGroup>\r\n\r\n  <PropertyGroup Condition=\"'$(Configuration)|$(Platform)'=='Release|AnyCPU'\">\r\n    <DebugType>embedded</DebugType>\r\n  </PropertyGroup>\r\n\r\n  <ItemGroup>\r\n    <None Remove=\"App.ico\" />\r\n    <None Remove=\"Resources\\BuildDate.txt\" />\r\n    <None Remove=\"Resources\\AiAnalysisPrompt.md\" />\r\n  </ItemGroup>\r\n\r\n  <ItemGroup>\r\n    <Content Include=\"App.ico\" />\r\n  </ItemGroup>\r\n\r\n  <ItemGroup>\r\n    <EmbeddedResource Include=\"Resources\\BuildDate.txt\" />\r\n    <EmbeddedResource Include=\"Resources\\AiAnalysisPrompt.md\" />\r\n  </ItemGroup>\r\n\r\n  <Target Name=\"PreBuild\" BeforeTargets=\"PreBuildEvent\">\r\n    <Exec Command=\"PowerShell.exe -NoProfile -Command &quot;if(-not (Test-Path -Path '$(ProjectDir)Resources')) { New-Item -ItemType Directory -Force -Path '$(ProjectDir)Resources' }&quot;&#xD;&#xA;PowerShell.exe -NoProfile -Command &quot;(Get-Date).ToString('o') | Out-File -Force '$(ProjectDir)Resources\\BuildDate.txt'&quot;&#xD;&#xA;\" />\r\n  </Target>\r\n\r\n</Project>\r\n"
  },
  {
    "path": "FocusLogger/MainWindow.xaml",
    "content": "﻿<Window\r\n\tx:Class=\"JocysCom.FocusLogger.MainWindow\"\r\n\txmlns=\"http://schemas.microsoft.com/winfx/2006/xaml/presentation\"\r\n\txmlns:x=\"http://schemas.microsoft.com/winfx/2006/xaml\"\r\n\txmlns:JcControls=\"clr-namespace:JocysCom.ClassLibrary.Controls\"\r\n\txmlns:controls=\"clr-namespace:JocysCom.FocusLogger.Controls\"\r\n\txmlns:d=\"http://schemas.microsoft.com/expression/blend/2008\"\r\n\txmlns:mc=\"http://schemas.openxmlformats.org/markup-compatibility/2006\"\r\n\tAutomationProperties.AutomationId=\"MainWindow\"\r\n\tTitle=\"MainWindow\"\r\n\tWidth=\"920\"\r\n\tHeight=\"480\"\r\n\tBackground=\"{DynamicResource {x:Static SystemColors.ControlBrushKey}}\"\r\n\tClosing=\"Window_Closing\"\r\n\tmc:Ignorable=\"d\">\r\n\t<Grid>\r\n\t\t<Grid.RowDefinitions>\r\n\t\t\t<RowDefinition Height=\"Auto\" />\r\n\t\t\t<RowDefinition Height=\"*\" />\r\n\t\t</Grid.RowDefinitions>\r\n\t\t<JcControls:InfoControl x:Name=\"InfoPanel\" Grid.Row=\"0\" RightIconContent=\"{StaticResource Icon_windows}\" />\r\n\t\t<controls:DataListControl x:Name=\"ConnectionsPanel\" Grid.Row=\"1\" />\r\n\t</Grid>\r\n</Window>\r\n"
  },
  {
    "path": "FocusLogger/MainWindow.xaml.cs",
    "content": "﻿using JocysCom.ClassLibrary.Controls;\nusing System.Reflection;\nusing System.Windows;\n\nnamespace JocysCom.FocusLogger\n{\n\tpublic partial class MainWindow : Window\n\t{\n\t\tpublic MainWindow()\n\t\t{\n\t\t\tControlsHelper.InitInvokeContext();\n\t\t\tInitializeComponent();\n\t\t\tLoadHelpAndInfo();\n\t\t}\n\n\t\tvoid LoadHelpAndInfo()\n\t\t{\n\t\t\tvar assembly = Assembly.GetExecutingAssembly();\n\t\t\tvar ai = new ClassLibrary.Configuration.AssemblyInfo();\n\t\t\tTitle = ai.GetTitle(true, false, true, false, false);\n\t\t}\n\n\t\tpublic InfoControl HMan;\n\n\t\tpublic static bool IsClosing;\n\n\t\tprivate void Window_Closing(object sender, System.ComponentModel.CancelEventArgs e)\n\t\t{\n\t\t\tIsClosing = true;\n\t\t}\n\t}\n\n}\n"
  },
  {
    "path": "FocusLogger/Resources/AiAnalysisPrompt.md",
    "content": "I have a Focus Logger CSV file that records which Windows process or program takes window focus.\nEach row contains: Date, PID, Process Name, Active, Mouse, Keyboard, Caret, Window Title, Window Class, and Path.\n\nThe \"Window Class\" column contains the Win32 window class name (e.g., \"Shell_TrayWnd\" for the taskbar, \"tooltips_class32\" for tooltip popups, \"MSCTFIME UI\" for text input framework, \"Chrome_WidgetWin_1\" for Chromium-based browsers). This helps identify the type of window that took focus.\n\nPlease analyse the attached CSV file and:\n1. Identify which processes are stealing focus unexpectedly (e.g., briefly appearing then disappearing).\n2. Use the Window Class to determine what type of window stole focus (notification, tooltip, taskbar, input method, dialog, etc.) and explain what it means.\n3. Flag any unusual patterns such as rapid focus switching, background processes taking foreground, or processes that shouldn't normally grab focus.\n4. If a game or fullscreen application is present, highlight any process that interrupted it.\n5. Summarise the most frequent focus-stealing offenders with timestamps.\n6. Suggest possible causes and fixes for each offender.\n"
  },
  {
    "path": "FocusLogger/Resources/Icons/Convert_SVG_to_XAML.ps1",
    "content": "<#\r\n.SYNOPSIS\r\n    Convert Folder with SVG image files into XAML Resource file.\r\n.NOTES\r\n    Author:     Evaldas Jocys <evaldas@jocys.com>\r\n    Modified:   2021-11-06\r\n.LINK\r\n    http://www.jocys.com\r\n\r\n.REMARKS\r\n\r\n    Requires Installation of InkScape from https://inkscape.org/release/\r\n\r\n\tHow to include icons resource into App.xaml file:\r\n\r\n\t\t<Application\r\n\t\t\tx:Class=\"JocysCom.SomeApp\"\r\n\t\t\txmlns=\"http://schemas.microsoft.com/winfx/2006/xaml/presentation\"\r\n\t\t\txmlns:x=\"http://schemas.microsoft.com/winfx/2006/xaml\"\r\n\t\t\tStartupUri=\"MainWindow.xaml\">\r\n\t\t\t<Application.Resources>\r\n\t\t\t\t<ResourceDictionary>\r\n\t\t\t\t\t<ResourceDictionary.MergedDictionaries>\r\n\t\t\t\t\t\t<ResourceDictionary Source=\"Resources/Icons/Icons_Default.xaml\" />\r\n\t\t\t\t\t</ResourceDictionary.MergedDictionaries>\r\n\t\t\t\t</ResourceDictionary>\r\n\t\t\t</Application.Resources>\r\n\t\t</Application>\r\n\t\r\n\tHow to display image inside the XAML with style:\r\n\t\r\n\t\t<ContentControl\tx:Name=\"MyIcon\" Width=\"24\" Height=\"24\" Content=\"{StaticResource Icon_IconFileName}\" />\t\r\n\r\n\tHow to set image to Content control from code behind:\r\n\r\n\t\tMyIcon.Content = Icons_Default.Current[Icons_Default.Icon_IconFileName];\r\n\t\t\r\n#>\r\nusing namespace System;\r\nusing namespace System.IO;\r\nusing namespace System.Linq;\r\nusing namespace System.Xml.Linq;\r\nusing namespace System.Text.RegularExpressions;\r\nusing namespace System.Collections.Generic;\r\n\r\n[Reflection.Assembly]::LoadWithPartialName(\"System.Xml.Linq\") | Out-Null;\r\n\r\nClear-Host;\r\n\r\n# ----------------------------------------------------------------------------\r\n# Get current command path.\r\n[string]$current = $MyInvocation.MyCommand.Path;\r\n# Get calling command path.\r\n[string]$calling = @(Get-PSCallStack)[1].InvocationInfo.MyCommand.Path;\r\n# If executed directly then...\r\nif ($calling -ne \"\") {\r\n    $current = $calling;\r\n}\r\n# ----------------------------------------------------------------------------\r\n[FileInfo]$file = New-Object FileInfo($current);\r\n# Set public parameters.\r\n$global:scriptName = $file.Basename;\r\n$global:scriptPath = $file.Directory.FullName;\r\n# Change current directory.\r\n[Console]::WriteLine(\"Script Path: {0}\", $scriptPath);\r\n[Environment]::CurrentDirectory = $scriptPath;\r\nSet-Location $scriptPath;\r\n# ----------------------------------------------------------------------------\r\n[DirectoryInfo]$root = New-Object DirectoryInfo($scriptPath);\r\n# ----------------------------------------------------------------------------\r\nfunction RemoveAttributes\r\n{\r\n    param([XElement]$Node,[string]$Name);\r\n    #----------------------------------------------------------\r\n    foreach ($attr in $Node.Attributes())\r\n    {\r\n        if ($attr.Name -eq $Name)\r\n        {\r\n            $attr.Remove();\r\n        }\r\n    }\r\n    foreach ($child in $Node.Descendants())\r\n    {\r\n        RemoveAttributes -Node $child -Name $Name;\r\n    }\r\n}\r\n# ----------------------------------------------------------------------------\r\nfunction FindParentFile\r\n{\r\n    [OutputType([FileInfo[]])] param([string]$pattern);\r\n    #----------------------------------------------------------\r\n    [DirectoryInfo]$di = new-Object DirectoryInfo $scriptPath;\r\n    do\r\n    {\r\n        $files = $di.GetFiles($pattern);\r\n        # Return if project files were found.\r\n        if ($files.Count -gt 0)\r\n        {\r\n            return $files;\r\n        }\r\n        # Continue to parent.\r\n        $di = $di.Parent;\r\n    } while($null -ne $di);\r\n    return $null;\r\n}\r\n# ----------------------------------------------------------------------------\r\nfunction GetProjectValue\r\n{\r\n    [OutputType([string])] param([string]$path, [string]$name);\r\n    #----------------------------------------------------------\r\n    [string]$content = [File]::ReadAllText($path);\r\n\t[Regex]$rx = New-Object Regex(\"(?<p><$name>)(?<v>[^<]*)(?<s><\\/$name>)\");\r\n\t$match = $rx.Match($content);\r\n\tif ($match.Success -eq $true) {\r\n\t\treturn $match.Groups[\"v\"].Value;\r\n\t}\r\n\treturn $null;\r\n}\r\n# ----------------------------------------------------------------------------\r\nfunction FindExistingFile\r\n{\r\n    [OutputType([string])] param([string]$path);\r\n    #----------------------------------------------------------\r\n    [FileInfo]$fi = $null;\r\n    # Paths to look for executable.\r\n    $ps = @(\r\n        $path,\r\n        \"${env:ProgramFiles}\\$path\",\r\n        \"${env:ProgramFiles(x86)}\\$path\",\r\n        \"D:\\Program Files\\$path\",\r\n        \"D:\\Program Files (x86)\\$dfe\"\r\n    );\r\n    foreach ($p in $ps) {\r\n        # Fix dot notations.\r\n        $fullPath = [Path]::GetFullPath($p);\r\n        #Write-Host \"Check... $fullPath\";\r\n        if ([File]::Exists($fullPath)) {\r\n            $fi = new-Object FileInfo $fullPath;\r\n            break;\r\n        }\r\n    }\r\n    return $fi;\r\n}\r\n# ----------------------------------------------------------------------------\r\nfunction SHA256CheckSum\r\n{\r\n    param($filePath);\r\n    #----------------------------------------------------------\r\n    $SHA256 = [System.Security.Cryptography.SHA256Managed]::Create();\r\n    $fileStream = [System.IO.File]::OpenRead($filePath);\r\n    $bytes = $SHA256.ComputeHash($fileStream);\r\n    $hash = ($bytes|ForEach-Object ToString X2) -join '';\r\n    $fileStream.Dispose();\r\n    $SHA256.Dispose();\r\n    return $hash;\r\n}\r\n# ----------------------------------------------------------------------------\r\nfunction FindProjectFile\r\n{\r\n    [FileInfo[]]$list = FindParentFile \"*.*proj\";\r\n    if ($list -ne $null -and $list.Count -gt 0){\r\n        # Order by date descendign to most recent file.\r\n        $list = [Enumerable]::OrderByDescending($list, [Func[object,object]]{ param($x) $x.LastWriteTime });\r\n        $list = [Enumerable]::ToArray($list);\r\n        return $list[0];\r\n    }\r\n    return $null;\r\n}\r\n# ----------------------------------------------------------------------------\r\n# Show menu\r\n# ----------------------------------------------------------------------------\r\nfunction ShowOptionsMenu\r\n{\r\n    param($items);\r\n    #----------------------------------------------------------\r\n    # Get local configurations.\r\n\t$keys = \"1234567890ABCDEFGHIJKLMNOPQRSTUVWXYZ\";\r\n    Write-Host \"Options:\";\r\n    Write-Host;\r\n    for ($i = 0; $i -lt $items.Count; $i++)\r\n    {\r\n        $item = $items[$i];\r\n        Write-Host \"    $($keys[$i]) - $($item)\";\r\n    }\r\n    Write-Host;\r\n    $m = Read-Host -Prompt \"Type option and press ENTER to continue\";\r\n    $m = $m.ToUpper();\r\n    $keyIndex = $keys.IndexOf($m);\r\n    # If wrong choice then...\r\n    if ($keyIndex -eq -1)\r\n    {\r\n        return $null;\r\n    }\r\n    return $items[$keyIndex];\r\n}\r\n# ----------------------------------------------------------------------------\r\n\r\nWrite-Host;\r\n\r\n#------------------------------\r\n# Inkscape program location, which will be used for conversion from SVG format to XAML format.\r\n#------------------------------\r\n\r\n$inkscape = FindExistingFile \"Inkscape\\bin\\inkscape.exe\";\r\nif ($null -eq $inkscape) {\r\n    Write-Host \"Inkscape program not found!\";\r\n    Write-Host \"Download from https://inkscape.org/release/\";\r\n    return;\r\n}\r\nWrite-Host \"Inkscape:  $($inkscape.FullName)\";\r\n\r\n#------------------------------\r\n# Get Project file.\r\n#------------------------------\r\n\r\n[FileInfo]$project = FindProjectFile;\r\nif ($null -eq $project) {\r\n    Write-Host \"Project file not found.\";\r\n    return;\r\n}\r\nWrite-Host \"Project:   $($project.FullName)\";\r\n\r\n#------------------------------\r\n# Get Default namespace.\r\n#------------------------------\r\n\r\n# Get from project file.\r\n$defaultNamespace = GetProjectValue $project.FullName \"RootNamespace\";\r\n# If default namespace not found.\r\nif (\"\" -eq \"$defaultNamespace\") {\r\n    # Visual studio use Project file name as default assembly and root namespace.\r\n    $defaultNamespace = $project.BaseName;\r\n}\r\n# If namespace not found then...\r\nif (\"\" -eq \"$defaultNamespace\") {\r\n\tWrite-Host \"Please provide default namespace\";\r\n    $defaultNamespace = Host-Read;\r\n}\r\n#Write-Host \"Default Namespace: $defaultNamespace\";\r\n\r\n# Get Relative namespace.\r\n$relativeNamespace = $scriptPath.Substring($project.Directory.FullName.Length).Replace(\"\\\", \".\");\r\n#Write-Host \"Relative Namespace: $relativeNamespace\";\r\n$namespace = $defaultNamespace + $relativeNamespace;\r\n\r\nWrite-Host;\r\nWrite-Host \"Namespace: $namespace\";\r\n\r\n#------------------------------\r\n# Get Class Name\r\n#------------------------------\r\n\r\n# Get forders with *.svg files inside.\r\n$dirs = $file.Directory.GetDirectories();\r\n$dirNames = new-Object List[string];\r\nforeach ($dir in $dirs) {\r\n    $filePattern = \"*.svg\";\r\n    # If folder contains images then...\r\n    $dirFiles = $dir.GetFiles($filePattern);\r\n    if ($dirFiles.Length -gt 0){\r\n        $dirNames.Add($dir.Name);\r\n    }\r\n    #Write-Host \"Source: $($dir.Name), $filePattern Files: $($dirFiles.Length)\";\r\n}\r\n\r\n[string]$className = $null;\r\n\r\nif ($dirNames.Count -eq 1){\r\n    $className = $dirNames[0];\r\n} elseif ($dirNames.Count -gt 1){\r\n    $className = ShowOptionsMenu $dirNames;\r\n}\r\n\r\nif ($null -eq $className){\r\n    Write-Host \"Folder with images not found!\";\r\n    return;\r\n}\r\n\r\n$sourceDir = New-Object DirectoryInfo($root.FullName + \"\\\" + $className);\r\n\r\nWrite-Host \"Class:     $className\";\r\nWrite-Host;\r\nWrite-Host \"Source:    $($sourceDir.Name)\\\";\r\nWrite-Host \"Target:    $className.xaml + $className.xaml.cs\";\r\nWrite-Host;\r\npause;\r\n\r\n#------------------------------\r\n# Generate images.\r\n#------------------------------\r\n\r\nWrite-Host;\r\n\r\n#Write-Host \"Done. Press any key to continue...\";\r\n#$null = $Host.UI.RawUI.ReadKey(\"NoEcho,IncludeKeyDown\");\r\n\r\n# Get files.\r\n$files = $sourceDir.GetFiles(\"*.svg\");\r\n# If no SVG images found then skip.\r\nif ($files.Length -eq 0){\r\n    continue;\r\n}\r\n\r\n# Create regular expressions for key and names generation.\r\n$RxAllExceptNumbersAndLetters = New-Object Regex(\"[^a-zA-Z0-9]\", [RegexOptions]::IgnoreCase);\r\n$UsRx = New-Object Regex(\"_+\");\r\n# Crate output file name.\r\n$fileName = $RxAllExceptNumbersAndLetters.Replace($sourceDir.Name, \"_\");\r\n$fileName = $UsRx.Replace($fileName, \"_\");\r\n$fileName = \"$className.xaml\";\r\n$fileNameCs = \"$className.xaml.cs\";\r\nif ($files.Length -eq 1){\r\n    Write-Host \"Convert  $($files.Length) image:\";\r\n}else{\r\n    Write-Host \"Convert $($files.Length) images:\";\r\n}\r\n# Start <ResourceName>.xaml file.\r\n$xNs = \"http://schemas.microsoft.com/winfx/2006/xaml\";\r\nif ([File]::Exists($fileName) -ne $true)\r\n{\r\n    [File]::WriteAllText($fileName, \"<ResourceDictionary xmlns=`\"http://schemas.microsoft.com/winfx/2006/xaml/presentation`\" xmlns:x=`\"$xNs`\"\");\r\n    [File]::AppendAllText($fileName,\"`r`nx:Class=`\"$($namespace).$($className)`\"\");\r\n    [File]::AppendAllText($fileName,\"`r`nx:ClassModifier=`\"public`\"\");\r\n    [File]::AppendAllText($fileName,'>');\r\n    [File]::AppendAllText($fileName,\"`r`n`r`n</ResourceDictionary>\");\r\n}\r\n[XDocument]$xaml = [XDocument]::Load($fileName); \r\n# Create list from existing nodes.\r\n$nodes = $xaml.Root.Nodes();\r\n#$nodes = $xaml.Root.Elements(\"Viewbox\").ToArray();\r\n#$nodes = $xaml.DocumentElement.SelectNodes(\"/*[local-name() = 'ResourceDictionary']/*[local-name() = 'Viewbox']\");\r\n\r\n$nodeList = new-Object System.Collections.Generic.Dictionary[string`,object];\r\n[XElement]$node = $null;\r\nforeach ($node in $nodes)\r\n{\r\n    $nodeKey = $node.Attribute([XName]::Get(\"Key\", $xNs)).Value;\r\n    $nodeList.Add($nodeKey, $node);\r\n    #Write-Host \"Key $nodeKey\";\r\n}\r\n# Cleanup old nodes.\r\n$xaml.Root.RemoveNodes();\r\n\r\n# Start <ResourceName>.xaml.cs file.\r\n[File]::WriteAllText($fileNameCs, \"using System.Windows;`r`n\");\r\n[File]::AppendAllText($fileNameCs, \"`r`n\");\r\n[File]::AppendAllText($fileNameCs, \"namespace $namespace`r`n\");\r\n[File]::AppendAllText($fileNameCs, \"{`r`n\");\r\n[File]::AppendAllText($fileNameCs, \"`tpartial class $className : ResourceDictionary`r`n\");\r\n[File]::AppendAllText($fileNameCs, \"`t{`r`n\");\r\n[File]::AppendAllText($fileNameCs, \"`t`tpublic $className()`r`n\");\r\n[File]::AppendAllText($fileNameCs, \"`t`t{`r`n\");\r\n[File]::AppendAllText($fileNameCs, \"`t`t`tInitializeComponent();`r`n\");\r\n[File]::AppendAllText($fileNameCs, \"`t`t}`r`n\");\r\n[File]::AppendAllText($fileNameCs, \"`r`n\");\r\n[File]::AppendAllText($fileNameCs, \"`t`tpublic static $className Current => _Current = _Current ?? new $className();`r`n\");\r\n[File]::AppendAllText($fileNameCs, \"`t`tprivate static $className _Current;`r`n\");\r\n[File]::AppendAllText($fileNameCs, \"`r`n\");\r\n\r\nWrite-Host;\r\n\r\n# Process files.\r\nfor ($f = 0; $f -lt $files.Length; $f++) {\r\n    $file = $files[$f];\r\n    $fileHash = SHA256CheckSum -filePath $file.FullName;\r\n    $fileHashNodeName = \"Tag\";\r\n    $nodeXml = $null;\r\n    # Create unique key.\r\n    $key = \"Icon_$($file.BaseName)\";\r\n    # Get existing node or create new.\r\n\t$action = \"Insert:\";\r\n    if ($nodeList.ContainsKey($key))\r\n    {\r\n        $action = \"Update:\";\r\n        [XElement]$oldNode = $nodeList[$key];\r\n        # Get hash of current node.\r\n        $oldHash = $oldNode.Attribute([XName]::Get($fileHashNodeName)).Value;\r\n        if ($oldHash -eq \"SHA256_$fileHash\")\r\n        {\r\n            $nodeXml = $oldNode;\r\n            $action = \"Keep:  \";\r\n        }\r\n    }\r\n    $isNew = ($null -eq $nodeXml);\r\n    if ($isNew)\r\n    {\r\n        $nodeXml = Get-Content \"$($file.FullName)\" | & $inkscape --pipe --export-type=xaml | Out-String;\r\n    }\r\n    # Show file name.\r\n    Write-Host \"`t$action $($sourceDir.Name)\\$($file.Name)\";\r\n    # Remove name attributes.\r\n    [XDocument]$node = [XDocument]::Parse($nodeXml);\r\n    # Remove \"Name\" attributes.\r\n    RemoveAttributes -Node $node.Root -Name \"Name\";\r\n\r\n    RemoveAttributes -Node $node.Root -Name \"Key\";\r\n    RemoveAttributes -Node $node.Root -Name \"Shared\";\r\n\r\n    # Add image XML to XAML document.\r\n    $xaml.Root.Add($node.Root);\r\n    # Remove old attributes.\r\n    if ($isNew)\r\n    {\r\n        # Get node which was just added.\r\n        $ln = $xaml.Root.LastNode;\r\n        # Give node unique name.\r\n        $ln.SetAttributeValue([XName]::Get(\"Key\", $xNs), $key);\r\n        # Make sure that image copy is made when it is used.\r\n        $ln.SetAttributeValue([XName]::Get(\"Shared\", $xNs), \"False\");\r\n        # Set file hash.\r\n        $ln.SetAttributeValue([XName]::Get($fileHashNodeName), \"SHA256_$fileHash\");\r\n    }\r\n    # Write unique name to code file.\r\n    [File]::AppendAllText($fileNameCs, \"`t`tpublic const string $key = nameof($key);`r`n\");\r\n}\r\n\r\n# Save XAML file.\r\n$xaml.Save($fileName);\r\n# End <ResourceName>.xaml.cs file.\r\n[File]::AppendAllText($fileNameCs, \"`r`n\");\r\n[File]::AppendAllText($fileNameCs, \"`t}`r`n\");\r\n[File]::AppendAllText($fileNameCs, \"}`r`n\");\r\n\r\nWrite-Host;\r\nWrite-Host \"Done. Press any key to continue...\";\r\n$null = $Host.UI.RawUI.ReadKey(\"NoEcho,IncludeKeyDown\");\r\n"
  },
  {
    "path": "FocusLogger/Resources/Icons/IconExperience.License.txt",
    "content": "License Agreement (G-Collection v2.0)\r\n\r\nThis is a legal agreement between you, the purchaser, and INCORS GmbH (\"INCORS\"). By downloading any icons from this website, you agree to be bound by the terms of this agreement.\r\n\r\nAll ownership and copyright of the licensed icons remain the property of INCORS. INCORS grants to you a nonexclusive, non-sublicensable right to use the icons royalty-free according to the license terms for any commercial or personal design and software projects. You may also display the icon in related documentation, help files, and advertising materials.\r\n\r\nYou may modify the icons in shape, color, and/or file format and use the modified icons royalty-free according to the license terms for any commercial or personal design and software projects. You may also display the modified icons in related documentation, help files, and advertising materials.\r\n\r\nYou may not lease, license or sub-license the icons, or a subset of the icons, or any modified icons to any third party unless they are incorporated into your own software or design products, which do not compete directly with the IconExperience icon collections.\r\n\r\nAll icon files are provided \"As is\" without warranties of merchantability and fitness for a particular purpose. You agree to hold INCORS harmless for any result that may occur during the course of using the licensed icons.\r\n\r\nThis License Agreement shall be governed and construed in accordance with the laws of Germany. No choice of law rules of any jurisdiction will apply. If any provision of this License Agreement is held to be unenforceable, this License Agreement will remain in effect with the provision omitted.\r\n"
  },
  {
    "path": "FocusLogger/Resources/Icons/Icons_Default.xaml",
    "content": "<?xml version=\"1.0\" encoding=\"utf-8\" ?>\r\n<ResourceDictionary\r\n\tx:Class=\"JocysCom.FocusLogger.Resources.Icons.Icons_Default\"\r\n\txmlns=\"http://schemas.microsoft.com/winfx/2006/xaml/presentation\"\r\n\txmlns:x=\"http://schemas.microsoft.com/winfx/2006/xaml\"\r\n\tx:ClassModifier=\"public\">\r\n\t<Viewbox\r\n\t\txmlns=\"http://schemas.microsoft.com/winfx/2006/xaml/presentation\"\r\n\t\tx:Key=\"Icon_keyboard\"\r\n\t\tx:Shared=\"False\"\r\n\t\tStretch=\"Uniform\"\r\n\t\tTag=\"SHA256_91BCF142307D3C205E8DBAE2973107BCAEDF49CB9E90EE4AD3F40AAB1CCC6C8E\">\r\n\t\t<Canvas Width=\"512\" Height=\"512\">\r\n\t\t\t<Canvas.RenderTransform>\r\n\t\t\t\t<TranslateTransform X=\"0\" Y=\"0\" />\r\n\t\t\t</Canvas.RenderTransform>\r\n\t\t\t<Canvas.Resources>\r\n\t\t\t\t<LinearGradientBrush xmlns:x=\"http://schemas.microsoft.com/winfx/2006/xaml\" x:Key=\"color-1\" MappingMode=\"Absolute\" StartPoint=\"64,0\" EndPoint=\"64,224\">\r\n\t\t\t\t\t<LinearGradientBrush.GradientStops>\r\n\t\t\t\t\t\t<GradientStopCollection>\r\n\t\t\t\t\t\t\t<GradientStop Offset=\"0\" Color=\"#8F9EAD\" />\r\n\t\t\t\t\t\t\t<GradientStop Offset=\"1\" Color=\"#717F8F\" />\r\n\t\t\t\t\t\t</GradientStopCollection>\r\n\t\t\t\t\t</LinearGradientBrush.GradientStops>\r\n\t\t\t\t</LinearGradientBrush>\r\n\t\t\t\t<LinearGradientBrush xmlns:x=\"http://schemas.microsoft.com/winfx/2006/xaml\" x:Key=\"color-2\" MappingMode=\"Absolute\" StartPoint=\"0,208\" EndPoint=\"380,448\">\r\n\t\t\t\t\t<LinearGradientBrush.GradientStops>\r\n\t\t\t\t\t\t<GradientStopCollection>\r\n\t\t\t\t\t\t\t<GradientStop Offset=\"0\" Color=\"#48B4F2\" />\r\n\t\t\t\t\t\t\t<GradientStop Offset=\"1\" Color=\"#23A5EF\" />\r\n\t\t\t\t\t\t</GradientStopCollection>\r\n\t\t\t\t\t</LinearGradientBrush.GradientStops>\r\n\t\t\t\t</LinearGradientBrush>\r\n\t\t\t\t<LinearGradientBrush xmlns:x=\"http://schemas.microsoft.com/winfx/2006/xaml\" x:Key=\"color-3\" MappingMode=\"Absolute\" StartPoint=\"32,240\" EndPoint=\"39,264\">\r\n\t\t\t\t\t<LinearGradientBrush.GradientStops>\r\n\t\t\t\t\t\t<GradientStopCollection>\r\n\t\t\t\t\t\t\t<GradientStop Offset=\"0\" Color=\"#FFFFFF\" />\r\n\t\t\t\t\t\t\t<GradientStop Offset=\"1\" Color=\"#CFEBFC\" />\r\n\t\t\t\t\t\t</GradientStopCollection>\r\n\t\t\t\t\t</LinearGradientBrush.GradientStops>\r\n\t\t\t\t</LinearGradientBrush>\r\n\t\t\t\t<LinearGradientBrush xmlns:x=\"http://schemas.microsoft.com/winfx/2006/xaml\" x:Key=\"color-4\" MappingMode=\"Absolute\" StartPoint=\"96,240\" EndPoint=\"107,262\">\r\n\t\t\t\t\t<LinearGradientBrush.GradientStops>\r\n\t\t\t\t\t\t<GradientStopCollection>\r\n\t\t\t\t\t\t\t<GradientStop Offset=\"0\" Color=\"#FFFFFF\" />\r\n\t\t\t\t\t\t\t<GradientStop Offset=\"1\" Color=\"#C9E9FB\" />\r\n\t\t\t\t\t\t</GradientStopCollection>\r\n\t\t\t\t\t</LinearGradientBrush.GradientStops>\r\n\t\t\t\t</LinearGradientBrush>\r\n\t\t\t\t<LinearGradientBrush xmlns:x=\"http://schemas.microsoft.com/winfx/2006/xaml\" x:Key=\"color-5\" MappingMode=\"Absolute\" StartPoint=\"144,240\" EndPoint=\"155,262\">\r\n\t\t\t\t\t<LinearGradientBrush.GradientStops>\r\n\t\t\t\t\t\t<GradientStopCollection>\r\n\t\t\t\t\t\t\t<GradientStop Offset=\"0\" Color=\"#FFFFFF\" />\r\n\t\t\t\t\t\t\t<GradientStop Offset=\"1\" Color=\"#C9E9FB\" />\r\n\t\t\t\t\t\t</GradientStopCollection>\r\n\t\t\t\t\t</LinearGradientBrush.GradientStops>\r\n\t\t\t\t</LinearGradientBrush>\r\n\t\t\t\t<LinearGradientBrush xmlns:x=\"http://schemas.microsoft.com/winfx/2006/xaml\" x:Key=\"color-6\" MappingMode=\"Absolute\" StartPoint=\"192,240\" EndPoint=\"203,262\">\r\n\t\t\t\t\t<LinearGradientBrush.GradientStops>\r\n\t\t\t\t\t\t<GradientStopCollection>\r\n\t\t\t\t\t\t\t<GradientStop Offset=\"0\" Color=\"#FFFFFF\" />\r\n\t\t\t\t\t\t\t<GradientStop Offset=\"1\" Color=\"#C9E9FB\" />\r\n\t\t\t\t\t\t</GradientStopCollection>\r\n\t\t\t\t\t</LinearGradientBrush.GradientStops>\r\n\t\t\t\t</LinearGradientBrush>\r\n\t\t\t\t<LinearGradientBrush xmlns:x=\"http://schemas.microsoft.com/winfx/2006/xaml\" x:Key=\"color-7\" MappingMode=\"Absolute\" StartPoint=\"240,240\" EndPoint=\"251,262\">\r\n\t\t\t\t\t<LinearGradientBrush.GradientStops>\r\n\t\t\t\t\t\t<GradientStopCollection>\r\n\t\t\t\t\t\t\t<GradientStop Offset=\"0\" Color=\"#FFFFFF\" />\r\n\t\t\t\t\t\t\t<GradientStop Offset=\"1\" Color=\"#C9E9FB\" />\r\n\t\t\t\t\t\t</GradientStopCollection>\r\n\t\t\t\t\t</LinearGradientBrush.GradientStops>\r\n\t\t\t\t</LinearGradientBrush>\r\n\t\t\t\t<LinearGradientBrush xmlns:x=\"http://schemas.microsoft.com/winfx/2006/xaml\" x:Key=\"color-8\" MappingMode=\"Absolute\" StartPoint=\"288,240\" EndPoint=\"299,262\">\r\n\t\t\t\t\t<LinearGradientBrush.GradientStops>\r\n\t\t\t\t\t\t<GradientStopCollection>\r\n\t\t\t\t\t\t\t<GradientStop Offset=\"0\" Color=\"#FFFFFF\" />\r\n\t\t\t\t\t\t\t<GradientStop Offset=\"1\" Color=\"#C9E9FB\" />\r\n\t\t\t\t\t\t</GradientStopCollection>\r\n\t\t\t\t\t</LinearGradientBrush.GradientStops>\r\n\t\t\t\t</LinearGradientBrush>\r\n\t\t\t\t<LinearGradientBrush xmlns:x=\"http://schemas.microsoft.com/winfx/2006/xaml\" x:Key=\"color-9\" MappingMode=\"Absolute\" StartPoint=\"336,240\" EndPoint=\"347,262\">\r\n\t\t\t\t\t<LinearGradientBrush.GradientStops>\r\n\t\t\t\t\t\t<GradientStopCollection>\r\n\t\t\t\t\t\t\t<GradientStop Offset=\"0\" Color=\"#FFFFFF\" />\r\n\t\t\t\t\t\t\t<GradientStop Offset=\"1\" Color=\"#C9E9FB\" />\r\n\t\t\t\t\t\t</GradientStopCollection>\r\n\t\t\t\t\t</LinearGradientBrush.GradientStops>\r\n\t\t\t\t</LinearGradientBrush>\r\n\t\t\t\t<LinearGradientBrush xmlns:x=\"http://schemas.microsoft.com/winfx/2006/xaml\" x:Key=\"color-10\" MappingMode=\"Absolute\" StartPoint=\"384,240\" EndPoint=\"395,262\">\r\n\t\t\t\t\t<LinearGradientBrush.GradientStops>\r\n\t\t\t\t\t\t<GradientStopCollection>\r\n\t\t\t\t\t\t\t<GradientStop Offset=\"0\" Color=\"#FFFFFF\" />\r\n\t\t\t\t\t\t\t<GradientStop Offset=\"1\" Color=\"#C9E9FB\" />\r\n\t\t\t\t\t\t</GradientStopCollection>\r\n\t\t\t\t\t</LinearGradientBrush.GradientStops>\r\n\t\t\t\t</LinearGradientBrush>\r\n\t\t\t\t<LinearGradientBrush xmlns:x=\"http://schemas.microsoft.com/winfx/2006/xaml\" x:Key=\"color-11\" MappingMode=\"Absolute\" StartPoint=\"432,240\" EndPoint=\"439,264\">\r\n\t\t\t\t\t<LinearGradientBrush.GradientStops>\r\n\t\t\t\t\t\t<GradientStopCollection>\r\n\t\t\t\t\t\t\t<GradientStop Offset=\"0\" Color=\"#FFFFFF\" />\r\n\t\t\t\t\t\t\t<GradientStop Offset=\"1\" Color=\"#CFEBFC\" />\r\n\t\t\t\t\t\t</GradientStopCollection>\r\n\t\t\t\t\t</LinearGradientBrush.GradientStops>\r\n\t\t\t\t</LinearGradientBrush>\r\n\t\t\t\t<LinearGradientBrush xmlns:x=\"http://schemas.microsoft.com/winfx/2006/xaml\" x:Key=\"color-12\" MappingMode=\"Absolute\" StartPoint=\"32,288\" EndPoint=\"35,320\">\r\n\t\t\t\t\t<LinearGradientBrush.GradientStops>\r\n\t\t\t\t\t\t<GradientStopCollection>\r\n\t\t\t\t\t\t\t<GradientStop Offset=\"0\" Color=\"#FCFEFF\" />\r\n\t\t\t\t\t\t\t<GradientStop Offset=\"1\" Color=\"#DAF0FD\" />\r\n\t\t\t\t\t\t</GradientStopCollection>\r\n\t\t\t\t\t</LinearGradientBrush.GradientStops>\r\n\t\t\t\t</LinearGradientBrush>\r\n\t\t\t\t<LinearGradientBrush xmlns:x=\"http://schemas.microsoft.com/winfx/2006/xaml\" x:Key=\"color-13\" MappingMode=\"Absolute\" StartPoint=\"126,288\" EndPoint=\"137,310\">\r\n\t\t\t\t\t<LinearGradientBrush.GradientStops>\r\n\t\t\t\t\t\t<GradientStopCollection>\r\n\t\t\t\t\t\t\t<GradientStop Offset=\"0\" Color=\"#FFFFFF\" />\r\n\t\t\t\t\t\t\t<GradientStop Offset=\"1\" Color=\"#C9E9FB\" />\r\n\t\t\t\t\t\t</GradientStopCollection>\r\n\t\t\t\t\t</LinearGradientBrush.GradientStops>\r\n\t\t\t\t</LinearGradientBrush>\r\n\t\t\t\t<LinearGradientBrush xmlns:x=\"http://schemas.microsoft.com/winfx/2006/xaml\" x:Key=\"color-14\" MappingMode=\"Absolute\" StartPoint=\"174,288\" EndPoint=\"185,310\">\r\n\t\t\t\t\t<LinearGradientBrush.GradientStops>\r\n\t\t\t\t\t\t<GradientStopCollection>\r\n\t\t\t\t\t\t\t<GradientStop Offset=\"0\" Color=\"#FFFFFF\" />\r\n\t\t\t\t\t\t\t<GradientStop Offset=\"1\" Color=\"#C9E9FB\" />\r\n\t\t\t\t\t\t</GradientStopCollection>\r\n\t\t\t\t\t</LinearGradientBrush.GradientStops>\r\n\t\t\t\t</LinearGradientBrush>\r\n\t\t\t\t<LinearGradientBrush xmlns:x=\"http://schemas.microsoft.com/winfx/2006/xaml\" x:Key=\"color-15\" MappingMode=\"Absolute\" StartPoint=\"222,288\" EndPoint=\"233,310\">\r\n\t\t\t\t\t<LinearGradientBrush.GradientStops>\r\n\t\t\t\t\t\t<GradientStopCollection>\r\n\t\t\t\t\t\t\t<GradientStop Offset=\"0\" Color=\"#FFFFFF\" />\r\n\t\t\t\t\t\t\t<GradientStop Offset=\"1\" Color=\"#C9E9FB\" />\r\n\t\t\t\t\t\t</GradientStopCollection>\r\n\t\t\t\t\t</LinearGradientBrush.GradientStops>\r\n\t\t\t\t</LinearGradientBrush>\r\n\t\t\t\t<LinearGradientBrush xmlns:x=\"http://schemas.microsoft.com/winfx/2006/xaml\" x:Key=\"color-16\" MappingMode=\"Absolute\" StartPoint=\"270,288\" EndPoint=\"281,310\">\r\n\t\t\t\t\t<LinearGradientBrush.GradientStops>\r\n\t\t\t\t\t\t<GradientStopCollection>\r\n\t\t\t\t\t\t\t<GradientStop Offset=\"0\" Color=\"#FFFFFF\" />\r\n\t\t\t\t\t\t\t<GradientStop Offset=\"1\" Color=\"#C9E9FB\" />\r\n\t\t\t\t\t\t</GradientStopCollection>\r\n\t\t\t\t\t</LinearGradientBrush.GradientStops>\r\n\t\t\t\t</LinearGradientBrush>\r\n\t\t\t\t<LinearGradientBrush xmlns:x=\"http://schemas.microsoft.com/winfx/2006/xaml\" x:Key=\"color-17\" MappingMode=\"Absolute\" StartPoint=\"318,288\" EndPoint=\"329,310\">\r\n\t\t\t\t\t<LinearGradientBrush.GradientStops>\r\n\t\t\t\t\t\t<GradientStopCollection>\r\n\t\t\t\t\t\t\t<GradientStop Offset=\"0\" Color=\"#FFFFFF\" />\r\n\t\t\t\t\t\t\t<GradientStop Offset=\"1\" Color=\"#C9E9FB\" />\r\n\t\t\t\t\t\t</GradientStopCollection>\r\n\t\t\t\t\t</LinearGradientBrush.GradientStops>\r\n\t\t\t\t</LinearGradientBrush>\r\n\t\t\t\t<LinearGradientBrush xmlns:x=\"http://schemas.microsoft.com/winfx/2006/xaml\" x:Key=\"color-18\" MappingMode=\"Absolute\" StartPoint=\"366,288\" EndPoint=\"377,310\">\r\n\t\t\t\t\t<LinearGradientBrush.GradientStops>\r\n\t\t\t\t\t\t<GradientStopCollection>\r\n\t\t\t\t\t\t\t<GradientStop Offset=\"0\" Color=\"#FFFFFF\" />\r\n\t\t\t\t\t\t\t<GradientStop Offset=\"1\" Color=\"#C9E9FB\" />\r\n\t\t\t\t\t\t</GradientStopCollection>\r\n\t\t\t\t\t</LinearGradientBrush.GradientStops>\r\n\t\t\t\t</LinearGradientBrush>\r\n\t\t\t\t<LinearGradientBrush xmlns:x=\"http://schemas.microsoft.com/winfx/2006/xaml\" x:Key=\"color-19\" MappingMode=\"Absolute\" StartPoint=\"414,288\" EndPoint=\"450,362\">\r\n\t\t\t\t\t<LinearGradientBrush.GradientStops>\r\n\t\t\t\t\t\t<GradientStopCollection>\r\n\t\t\t\t\t\t\t<GradientStop Offset=\"0\" Color=\"#FFFFFF\" />\r\n\t\t\t\t\t\t\t<GradientStop Offset=\"1\" Color=\"#CFEBFC\" />\r\n\t\t\t\t\t\t</GradientStopCollection>\r\n\t\t\t\t\t</LinearGradientBrush.GradientStops>\r\n\t\t\t\t</LinearGradientBrush>\r\n\t\t\t\t<LinearGradientBrush xmlns:x=\"http://schemas.microsoft.com/winfx/2006/xaml\" x:Key=\"color-20\" MappingMode=\"Absolute\" StartPoint=\"144,336\" EndPoint=\"155,358\">\r\n\t\t\t\t\t<LinearGradientBrush.GradientStops>\r\n\t\t\t\t\t\t<GradientStopCollection>\r\n\t\t\t\t\t\t\t<GradientStop Offset=\"0\" Color=\"#FFFFFF\" />\r\n\t\t\t\t\t\t\t<GradientStop Offset=\"1\" Color=\"#C9E9FB\" />\r\n\t\t\t\t\t\t</GradientStopCollection>\r\n\t\t\t\t\t</LinearGradientBrush.GradientStops>\r\n\t\t\t\t</LinearGradientBrush>\r\n\t\t\t\t<LinearGradientBrush xmlns:x=\"http://schemas.microsoft.com/winfx/2006/xaml\" x:Key=\"color-21\" MappingMode=\"Absolute\" StartPoint=\"32,336\" EndPoint=\"43,358\">\r\n\t\t\t\t\t<LinearGradientBrush.GradientStops>\r\n\t\t\t\t\t\t<GradientStopCollection>\r\n\t\t\t\t\t\t\t<GradientStop Offset=\"0\" Color=\"#FFFFFF\" />\r\n\t\t\t\t\t\t\t<GradientStop Offset=\"1\" Color=\"#D4EDFC\" />\r\n\t\t\t\t\t\t</GradientStopCollection>\r\n\t\t\t\t\t</LinearGradientBrush.GradientStops>\r\n\t\t\t\t</LinearGradientBrush>\r\n\t\t\t\t<LinearGradientBrush xmlns:x=\"http://schemas.microsoft.com/winfx/2006/xaml\" x:Key=\"color-22\" MappingMode=\"Absolute\" StartPoint=\"192,336\" EndPoint=\"203,358\">\r\n\t\t\t\t\t<LinearGradientBrush.GradientStops>\r\n\t\t\t\t\t\t<GradientStopCollection>\r\n\t\t\t\t\t\t\t<GradientStop Offset=\"0\" Color=\"#FFFFFF\" />\r\n\t\t\t\t\t\t\t<GradientStop Offset=\"1\" Color=\"#C9E9FB\" />\r\n\t\t\t\t\t\t</GradientStopCollection>\r\n\t\t\t\t\t</LinearGradientBrush.GradientStops>\r\n\t\t\t\t</LinearGradientBrush>\r\n\t\t\t\t<LinearGradientBrush xmlns:x=\"http://schemas.microsoft.com/winfx/2006/xaml\" x:Key=\"color-23\" MappingMode=\"Absolute\" StartPoint=\"240,336\" EndPoint=\"251,358\">\r\n\t\t\t\t\t<LinearGradientBrush.GradientStops>\r\n\t\t\t\t\t\t<GradientStopCollection>\r\n\t\t\t\t\t\t\t<GradientStop Offset=\"0\" Color=\"#FFFFFF\" />\r\n\t\t\t\t\t\t\t<GradientStop Offset=\"1\" Color=\"#C9E9FB\" />\r\n\t\t\t\t\t\t</GradientStopCollection>\r\n\t\t\t\t\t</LinearGradientBrush.GradientStops>\r\n\t\t\t\t</LinearGradientBrush>\r\n\t\t\t\t<LinearGradientBrush xmlns:x=\"http://schemas.microsoft.com/winfx/2006/xaml\" x:Key=\"color-24\" MappingMode=\"Absolute\" StartPoint=\"288,336\" EndPoint=\"299,358\">\r\n\t\t\t\t\t<LinearGradientBrush.GradientStops>\r\n\t\t\t\t\t\t<GradientStopCollection>\r\n\t\t\t\t\t\t\t<GradientStop Offset=\"0\" Color=\"#FFFFFF\" />\r\n\t\t\t\t\t\t\t<GradientStop Offset=\"1\" Color=\"#C9E9FB\" />\r\n\t\t\t\t\t\t</GradientStopCollection>\r\n\t\t\t\t\t</LinearGradientBrush.GradientStops>\r\n\t\t\t\t</LinearGradientBrush>\r\n\t\t\t\t<LinearGradientBrush xmlns:x=\"http://schemas.microsoft.com/winfx/2006/xaml\" x:Key=\"color-25\" MappingMode=\"Absolute\" StartPoint=\"336,336\" EndPoint=\"347,358\">\r\n\t\t\t\t\t<LinearGradientBrush.GradientStops>\r\n\t\t\t\t\t\t<GradientStopCollection>\r\n\t\t\t\t\t\t\t<GradientStop Offset=\"0\" Color=\"#FFFFFF\" />\r\n\t\t\t\t\t\t\t<GradientStop Offset=\"1\" Color=\"#C9E9FB\" />\r\n\t\t\t\t\t\t</GradientStopCollection>\r\n\t\t\t\t\t</LinearGradientBrush.GradientStops>\r\n\t\t\t\t</LinearGradientBrush>\r\n\t\t\t\t<LinearGradientBrush xmlns:x=\"http://schemas.microsoft.com/winfx/2006/xaml\" x:Key=\"color-26\" MappingMode=\"Absolute\" StartPoint=\"384,336\" EndPoint=\"395,358\">\r\n\t\t\t\t\t<LinearGradientBrush.GradientStops>\r\n\t\t\t\t\t\t<GradientStopCollection>\r\n\t\t\t\t\t\t\t<GradientStop Offset=\"0\" Color=\"#FFFFFF\" />\r\n\t\t\t\t\t\t\t<GradientStop Offset=\"1\" Color=\"#C9E9FB\" />\r\n\t\t\t\t\t\t</GradientStopCollection>\r\n\t\t\t\t\t</LinearGradientBrush.GradientStops>\r\n\t\t\t\t</LinearGradientBrush>\r\n\t\t\t\t<LinearGradientBrush xmlns:x=\"http://schemas.microsoft.com/winfx/2006/xaml\" x:Key=\"color-27\" MappingMode=\"Absolute\" StartPoint=\"32,384\" EndPoint=\"35,416\">\r\n\t\t\t\t\t<LinearGradientBrush.GradientStops>\r\n\t\t\t\t\t\t<GradientStopCollection>\r\n\t\t\t\t\t\t\t<GradientStop Offset=\"0\" Color=\"#FCFEFF\" />\r\n\t\t\t\t\t\t\t<GradientStop Offset=\"1\" Color=\"#D8EFFC\" />\r\n\t\t\t\t\t\t</GradientStopCollection>\r\n\t\t\t\t\t</LinearGradientBrush.GradientStops>\r\n\t\t\t\t</LinearGradientBrush>\r\n\t\t\t\t<LinearGradientBrush xmlns:x=\"http://schemas.microsoft.com/winfx/2006/xaml\" x:Key=\"color-28\" MappingMode=\"Absolute\" StartPoint=\"112,384\" EndPoint=\"123,406\">\r\n\t\t\t\t\t<LinearGradientBrush.GradientStops>\r\n\t\t\t\t\t\t<GradientStopCollection>\r\n\t\t\t\t\t\t\t<GradientStop Offset=\"0\" Color=\"#FFFFFF\" />\r\n\t\t\t\t\t\t\t<GradientStop Offset=\"1\" Color=\"#C9E9FB\" />\r\n\t\t\t\t\t\t</GradientStopCollection>\r\n\t\t\t\t\t</LinearGradientBrush.GradientStops>\r\n\t\t\t\t</LinearGradientBrush>\r\n\t\t\t\t<LinearGradientBrush xmlns:x=\"http://schemas.microsoft.com/winfx/2006/xaml\" x:Key=\"color-29\" MappingMode=\"Absolute\" StartPoint=\"160,384\" EndPoint=\"164,416\">\r\n\t\t\t\t\t<LinearGradientBrush.GradientStops>\r\n\t\t\t\t\t\t<GradientStopCollection>\r\n\t\t\t\t\t\t\t<GradientStop Offset=\"0\" Color=\"#FBFDFF\" />\r\n\t\t\t\t\t\t\t<GradientStop Offset=\"1\" Color=\"#DEF1FD\" />\r\n\t\t\t\t\t\t</GradientStopCollection>\r\n\t\t\t\t\t</LinearGradientBrush.GradientStops>\r\n\t\t\t\t</LinearGradientBrush>\r\n\t\t\t\t<LinearGradientBrush xmlns:x=\"http://schemas.microsoft.com/winfx/2006/xaml\" x:Key=\"color-30\" MappingMode=\"Absolute\" StartPoint=\"352,384\" EndPoint=\"363,406\">\r\n\t\t\t\t\t<LinearGradientBrush.GradientStops>\r\n\t\t\t\t\t\t<GradientStopCollection>\r\n\t\t\t\t\t\t\t<GradientStop Offset=\"0\" Color=\"#FFFFFF\" />\r\n\t\t\t\t\t\t\t<GradientStop Offset=\"1\" Color=\"#C9E9FB\" />\r\n\t\t\t\t\t\t</GradientStopCollection>\r\n\t\t\t\t\t</LinearGradientBrush.GradientStops>\r\n\t\t\t\t</LinearGradientBrush>\r\n\t\t\t\t<LinearGradientBrush xmlns:x=\"http://schemas.microsoft.com/winfx/2006/xaml\" x:Key=\"color-31\" MappingMode=\"Absolute\" StartPoint=\"400,384\" EndPoint=\"403,416\">\r\n\t\t\t\t\t<LinearGradientBrush.GradientStops>\r\n\t\t\t\t\t\t<GradientStopCollection>\r\n\t\t\t\t\t\t\t<GradientStop Offset=\"0\" Color=\"#FCFEFF\" />\r\n\t\t\t\t\t\t\t<GradientStop Offset=\"1\" Color=\"#DAF0FD\" />\r\n\t\t\t\t\t\t</GradientStopCollection>\r\n\t\t\t\t\t</LinearGradientBrush.GradientStops>\r\n\t\t\t\t</LinearGradientBrush>\r\n\t\t\t\t<LinearGradientBrush xmlns:x=\"http://schemas.microsoft.com/winfx/2006/xaml\" x:Key=\"color-32\" MappingMode=\"Absolute\" StartPoint=\"96,336\" EndPoint=\"107,358\">\r\n\t\t\t\t\t<LinearGradientBrush.GradientStops>\r\n\t\t\t\t\t\t<GradientStopCollection>\r\n\t\t\t\t\t\t\t<GradientStop Offset=\"0\" Color=\"#FFFFFF\" />\r\n\t\t\t\t\t\t\t<GradientStop Offset=\"1\" Color=\"#C9E9FB\" />\r\n\t\t\t\t\t\t</GradientStopCollection>\r\n\t\t\t\t\t</LinearGradientBrush.GradientStops>\r\n\t\t\t\t</LinearGradientBrush>\r\n\t\t\t</Canvas.Resources>\r\n\t\t\t<!--  Unknown tag: metadata  -->\r\n\t\t\t<!--  Unknown tag: sodipodi:namedview  -->\r\n\t\t\t<Canvas>\r\n\t\t\t\t<Path\r\n\t\t\t\t\txmlns:x=\"http://schemas.microsoft.com/winfx/2006/xaml\"\r\n\t\t\t\t\tFill=\"{StaticResource color-1}\"\r\n\t\t\t\t\tStrokeDashArray=\"\"\r\n\t\t\t\t\tStrokeEndLineCap=\"Flat\"\r\n\t\t\t\t\tStrokeLineJoin=\"Miter\"\r\n\t\t\t\t\tStrokeMiterLimit=\"10\"\r\n\t\t\t\t\tStrokeStartLineCap=\"Flat\"\r\n\t\t\t\t\tStrokeThickness=\"1\">\r\n\t\t\t\t\t<Path.Data>\r\n\t\t\t\t\t\t<PathGeometry Figures=\"M64 224l0 -32c0 -53 43 -96 96 -96l80 0c35.5 0 64 -28.5 64 -64l0 -32l32 0l0 32c0 53 -43 96 -96 96l-80 0c-35.5 0 -64 28.5 -64 64l0 32z\" FillRule=\"NonZero\" />\r\n\t\t\t\t\t</Path.Data>\r\n\t\t\t\t</Path>\r\n\t\t\t\t<Path\r\n\t\t\t\t\txmlns:x=\"http://schemas.microsoft.com/winfx/2006/xaml\"\r\n\t\t\t\t\tFill=\"{StaticResource color-2}\"\r\n\t\t\t\t\tStrokeDashArray=\"\"\r\n\t\t\t\t\tStrokeEndLineCap=\"Flat\"\r\n\t\t\t\t\tStrokeLineJoin=\"Miter\"\r\n\t\t\t\t\tStrokeMiterLimit=\"10\"\r\n\t\t\t\t\tStrokeStartLineCap=\"Flat\"\r\n\t\t\t\t\tStrokeThickness=\"1\">\r\n\t\t\t\t\t<Path.Data>\r\n\t\t\t\t\t\t<PathGeometry Figures=\"M16 208l480 0c9 0 16 7 16 16l0 208c0 9 -7 16 -16 16l-480 0c-9 0 -16 -7 -16 -16l0 -208c0 -9 7 -16 16 -16z\" FillRule=\"EvenOdd\" />\r\n\t\t\t\t\t</Path.Data>\r\n\t\t\t\t</Path>\r\n\t\t\t\t<Path\r\n\t\t\t\t\txmlns:x=\"http://schemas.microsoft.com/winfx/2006/xaml\"\r\n\t\t\t\t\tFill=\"{StaticResource color-3}\"\r\n\t\t\t\t\tStrokeDashArray=\"\"\r\n\t\t\t\t\tStrokeEndLineCap=\"Flat\"\r\n\t\t\t\t\tStrokeLineJoin=\"Miter\"\r\n\t\t\t\t\tStrokeMiterLimit=\"10\"\r\n\t\t\t\t\tStrokeStartLineCap=\"Flat\"\r\n\t\t\t\t\tStrokeThickness=\"1\">\r\n\t\t\t\t\t<Path.Data>\r\n\t\t\t\t\t\t<PathGeometry Figures=\"M40 240l32 0c4.5 0 8 3.5 8 8l0 16c0 4.5 -3.5 8 -8 8l-32 0c-4.5 0 -8 -3.5 -8 -8l0 -16c0 -4.5 3.5 -8 8 -8z\" FillRule=\"EvenOdd\" />\r\n\t\t\t\t\t</Path.Data>\r\n\t\t\t\t</Path>\r\n\t\t\t\t<Path\r\n\t\t\t\t\txmlns:x=\"http://schemas.microsoft.com/winfx/2006/xaml\"\r\n\t\t\t\t\tFill=\"{StaticResource color-4}\"\r\n\t\t\t\t\tStrokeDashArray=\"\"\r\n\t\t\t\t\tStrokeEndLineCap=\"Flat\"\r\n\t\t\t\t\tStrokeLineJoin=\"Miter\"\r\n\t\t\t\t\tStrokeMiterLimit=\"10\"\r\n\t\t\t\t\tStrokeStartLineCap=\"Flat\"\r\n\t\t\t\t\tStrokeThickness=\"1\">\r\n\t\t\t\t\t<Path.Data>\r\n\t\t\t\t\t\t<PathGeometry Figures=\"M104 240l16 0c4.5 0 8 3.5 8 8l0 16c0 4.5 -3.5 8 -8 8l-16 0c-4.5 0 -8 -3.5 -8 -8l0 -16c0 -4.5 3.5 -8 8 -8z\" FillRule=\"EvenOdd\" />\r\n\t\t\t\t\t</Path.Data>\r\n\t\t\t\t</Path>\r\n\t\t\t\t<Path\r\n\t\t\t\t\txmlns:x=\"http://schemas.microsoft.com/winfx/2006/xaml\"\r\n\t\t\t\t\tFill=\"{StaticResource color-5}\"\r\n\t\t\t\t\tStrokeDashArray=\"\"\r\n\t\t\t\t\tStrokeEndLineCap=\"Flat\"\r\n\t\t\t\t\tStrokeLineJoin=\"Miter\"\r\n\t\t\t\t\tStrokeMiterLimit=\"10\"\r\n\t\t\t\t\tStrokeStartLineCap=\"Flat\"\r\n\t\t\t\t\tStrokeThickness=\"1\">\r\n\t\t\t\t\t<Path.Data>\r\n\t\t\t\t\t\t<PathGeometry Figures=\"M152 240l16 0c4.5 0 8 3.5 8 8l0 16c0 4.5 -3.5 8 -8 8l-16 0c-4.5 0 -8 -3.5 -8 -8l0 -16c0 -4.5 3.5 -8 8 -8z\" FillRule=\"EvenOdd\" />\r\n\t\t\t\t\t</Path.Data>\r\n\t\t\t\t</Path>\r\n\t\t\t\t<Path\r\n\t\t\t\t\txmlns:x=\"http://schemas.microsoft.com/winfx/2006/xaml\"\r\n\t\t\t\t\tFill=\"{StaticResource color-6}\"\r\n\t\t\t\t\tStrokeDashArray=\"\"\r\n\t\t\t\t\tStrokeEndLineCap=\"Flat\"\r\n\t\t\t\t\tStrokeLineJoin=\"Miter\"\r\n\t\t\t\t\tStrokeMiterLimit=\"10\"\r\n\t\t\t\t\tStrokeStartLineCap=\"Flat\"\r\n\t\t\t\t\tStrokeThickness=\"1\">\r\n\t\t\t\t\t<Path.Data>\r\n\t\t\t\t\t\t<PathGeometry Figures=\"M200 240l16 0c4.5 0 8 3.5 8 8l0 16c0 4.5 -3.5 8 -8 8l-16 0c-4.5 0 -8 -3.5 -8 -8l0 -16c0 -4.5 3.5 -8 8 -8z\" FillRule=\"EvenOdd\" />\r\n\t\t\t\t\t</Path.Data>\r\n\t\t\t\t</Path>\r\n\t\t\t\t<Path\r\n\t\t\t\t\txmlns:x=\"http://schemas.microsoft.com/winfx/2006/xaml\"\r\n\t\t\t\t\tFill=\"{StaticResource color-7}\"\r\n\t\t\t\t\tStrokeDashArray=\"\"\r\n\t\t\t\t\tStrokeEndLineCap=\"Flat\"\r\n\t\t\t\t\tStrokeLineJoin=\"Miter\"\r\n\t\t\t\t\tStrokeMiterLimit=\"10\"\r\n\t\t\t\t\tStrokeStartLineCap=\"Flat\"\r\n\t\t\t\t\tStrokeThickness=\"1\">\r\n\t\t\t\t\t<Path.Data>\r\n\t\t\t\t\t\t<PathGeometry Figures=\"M248 240l16 0c4.5 0 8 3.5 8 8l0 16c0 4.5 -3.5 8 -8 8l-16 0c-4.5 0 -8 -3.5 -8 -8l0 -16c0 -4.5 3.5 -8 8 -8z\" FillRule=\"EvenOdd\" />\r\n\t\t\t\t\t</Path.Data>\r\n\t\t\t\t</Path>\r\n\t\t\t\t<Path\r\n\t\t\t\t\txmlns:x=\"http://schemas.microsoft.com/winfx/2006/xaml\"\r\n\t\t\t\t\tFill=\"{StaticResource color-8}\"\r\n\t\t\t\t\tStrokeDashArray=\"\"\r\n\t\t\t\t\tStrokeEndLineCap=\"Flat\"\r\n\t\t\t\t\tStrokeLineJoin=\"Miter\"\r\n\t\t\t\t\tStrokeMiterLimit=\"10\"\r\n\t\t\t\t\tStrokeStartLineCap=\"Flat\"\r\n\t\t\t\t\tStrokeThickness=\"1\">\r\n\t\t\t\t\t<Path.Data>\r\n\t\t\t\t\t\t<PathGeometry Figures=\"M296 240l16 0c4.5 0 8 3.5 8 8l0 16c0 4.5 -3.5 8 -8 8l-16 0c-4.5 0 -8 -3.5 -8 -8l0 -16c0 -4.5 3.5 -8 8 -8z\" FillRule=\"EvenOdd\" />\r\n\t\t\t\t\t</Path.Data>\r\n\t\t\t\t</Path>\r\n\t\t\t\t<Path\r\n\t\t\t\t\txmlns:x=\"http://schemas.microsoft.com/winfx/2006/xaml\"\r\n\t\t\t\t\tFill=\"{StaticResource color-9}\"\r\n\t\t\t\t\tStrokeDashArray=\"\"\r\n\t\t\t\t\tStrokeEndLineCap=\"Flat\"\r\n\t\t\t\t\tStrokeLineJoin=\"Miter\"\r\n\t\t\t\t\tStrokeMiterLimit=\"10\"\r\n\t\t\t\t\tStrokeStartLineCap=\"Flat\"\r\n\t\t\t\t\tStrokeThickness=\"1\">\r\n\t\t\t\t\t<Path.Data>\r\n\t\t\t\t\t\t<PathGeometry Figures=\"M344 240l16 0c4.5 0 8 3.5 8 8l0 16c0 4.5 -3.5 8 -8 8l-16 0c-4.5 0 -8 -3.5 -8 -8l0 -16c0 -4.5 3.5 -8 8 -8z\" FillRule=\"EvenOdd\" />\r\n\t\t\t\t\t</Path.Data>\r\n\t\t\t\t</Path>\r\n\t\t\t\t<Path\r\n\t\t\t\t\txmlns:x=\"http://schemas.microsoft.com/winfx/2006/xaml\"\r\n\t\t\t\t\tFill=\"{StaticResource color-10}\"\r\n\t\t\t\t\tStrokeDashArray=\"\"\r\n\t\t\t\t\tStrokeEndLineCap=\"Flat\"\r\n\t\t\t\t\tStrokeLineJoin=\"Miter\"\r\n\t\t\t\t\tStrokeMiterLimit=\"10\"\r\n\t\t\t\t\tStrokeStartLineCap=\"Flat\"\r\n\t\t\t\t\tStrokeThickness=\"1\">\r\n\t\t\t\t\t<Path.Data>\r\n\t\t\t\t\t\t<PathGeometry Figures=\"M392 240l16 0c4.5 0 8 3.5 8 8l0 16c0 4.5 -3.5 8 -8 8l-16 0c-4.5 0 -8 -3.5 -8 -8l0 -16c0 -4.5 3.5 -8 8 -8z\" FillRule=\"EvenOdd\" />\r\n\t\t\t\t\t</Path.Data>\r\n\t\t\t\t</Path>\r\n\t\t\t\t<Path\r\n\t\t\t\t\txmlns:x=\"http://schemas.microsoft.com/winfx/2006/xaml\"\r\n\t\t\t\t\tFill=\"{StaticResource color-11}\"\r\n\t\t\t\t\tStrokeDashArray=\"\"\r\n\t\t\t\t\tStrokeEndLineCap=\"Flat\"\r\n\t\t\t\t\tStrokeLineJoin=\"Miter\"\r\n\t\t\t\t\tStrokeMiterLimit=\"10\"\r\n\t\t\t\t\tStrokeStartLineCap=\"Flat\"\r\n\t\t\t\t\tStrokeThickness=\"1\">\r\n\t\t\t\t\t<Path.Data>\r\n\t\t\t\t\t\t<PathGeometry Figures=\"M440 240l32 0c4.5 0 8 3.5 8 8l0 16c0 4.5 -3.5 8 -8 8l-32 0c-4.5 0 -8 -3.5 -8 -8l0 -16c0 -4.5 3.5 -8 8 -8z\" FillRule=\"EvenOdd\" />\r\n\t\t\t\t\t</Path.Data>\r\n\t\t\t\t</Path>\r\n\t\t\t\t<Path\r\n\t\t\t\t\txmlns:x=\"http://schemas.microsoft.com/winfx/2006/xaml\"\r\n\t\t\t\t\tFill=\"{StaticResource color-12}\"\r\n\t\t\t\t\tStrokeDashArray=\"\"\r\n\t\t\t\t\tStrokeEndLineCap=\"Flat\"\r\n\t\t\t\t\tStrokeLineJoin=\"Miter\"\r\n\t\t\t\t\tStrokeMiterLimit=\"10\"\r\n\t\t\t\t\tStrokeStartLineCap=\"Flat\"\r\n\t\t\t\t\tStrokeThickness=\"1\">\r\n\t\t\t\t\t<Path.Data>\r\n\t\t\t\t\t\t<PathGeometry Figures=\"M40 288l40 0l21.5 0c4.5 0 8 3.5 8 8l0 16c0 4.5 -3.5 8 -8 8l-61.5 0c-4.5 0 -8 -3.5 -8 -8l0 -16c0 -4.5 3.5 -8 8 -8z\" FillRule=\"EvenOdd\" />\r\n\t\t\t\t\t</Path.Data>\r\n\t\t\t\t</Path>\r\n\t\t\t\t<Path\r\n\t\t\t\t\txmlns:x=\"http://schemas.microsoft.com/winfx/2006/xaml\"\r\n\t\t\t\t\tFill=\"{StaticResource color-13}\"\r\n\t\t\t\t\tStrokeDashArray=\"\"\r\n\t\t\t\t\tStrokeEndLineCap=\"Flat\"\r\n\t\t\t\t\tStrokeLineJoin=\"Miter\"\r\n\t\t\t\t\tStrokeMiterLimit=\"10\"\r\n\t\t\t\t\tStrokeStartLineCap=\"Flat\"\r\n\t\t\t\t\tStrokeThickness=\"1\">\r\n\t\t\t\t\t<Path.Data>\r\n\t\t\t\t\t\t<PathGeometry Figures=\"M133.5 288l16 0c4.5 0 8 3.5 8 8l0 16c0 4.5 -3.5 8 -8 8l-16 0c-4.5 0 -8 -3.5 -8 -8l0 -16c0 -4.5 3.5 -8 8 -8z\" FillRule=\"EvenOdd\" />\r\n\t\t\t\t\t</Path.Data>\r\n\t\t\t\t</Path>\r\n\t\t\t\t<Path\r\n\t\t\t\t\txmlns:x=\"http://schemas.microsoft.com/winfx/2006/xaml\"\r\n\t\t\t\t\tFill=\"{StaticResource color-14}\"\r\n\t\t\t\t\tStrokeDashArray=\"\"\r\n\t\t\t\t\tStrokeEndLineCap=\"Flat\"\r\n\t\t\t\t\tStrokeLineJoin=\"Miter\"\r\n\t\t\t\t\tStrokeMiterLimit=\"10\"\r\n\t\t\t\t\tStrokeStartLineCap=\"Flat\"\r\n\t\t\t\t\tStrokeThickness=\"1\">\r\n\t\t\t\t\t<Path.Data>\r\n\t\t\t\t\t\t<PathGeometry Figures=\"M181.5 288l16 0c4.5 0 8 3.5 8 8l0 16c0 4.5 -3.5 8 -8 8l-16 0c-4.5 0 -8 -3.5 -8 -8l0 -16c0 -4.5 3.5 -8 8 -8z\" FillRule=\"EvenOdd\" />\r\n\t\t\t\t\t</Path.Data>\r\n\t\t\t\t</Path>\r\n\t\t\t\t<Path\r\n\t\t\t\t\txmlns:x=\"http://schemas.microsoft.com/winfx/2006/xaml\"\r\n\t\t\t\t\tFill=\"{StaticResource color-15}\"\r\n\t\t\t\t\tStrokeDashArray=\"\"\r\n\t\t\t\t\tStrokeEndLineCap=\"Flat\"\r\n\t\t\t\t\tStrokeLineJoin=\"Miter\"\r\n\t\t\t\t\tStrokeMiterLimit=\"10\"\r\n\t\t\t\t\tStrokeStartLineCap=\"Flat\"\r\n\t\t\t\t\tStrokeThickness=\"1\">\r\n\t\t\t\t\t<Path.Data>\r\n\t\t\t\t\t\t<PathGeometry Figures=\"M229.5 288l16 0c4.5 0 8 3.5 8 8l0 16c0 4.5 -3.5 8 -8 8l-16 0c-4.5 0 -8 -3.5 -8 -8l0 -16c0 -4.5 3.5 -8 8 -8z\" FillRule=\"EvenOdd\" />\r\n\t\t\t\t\t</Path.Data>\r\n\t\t\t\t</Path>\r\n\t\t\t\t<Path\r\n\t\t\t\t\txmlns:x=\"http://schemas.microsoft.com/winfx/2006/xaml\"\r\n\t\t\t\t\tFill=\"{StaticResource color-16}\"\r\n\t\t\t\t\tStrokeDashArray=\"\"\r\n\t\t\t\t\tStrokeEndLineCap=\"Flat\"\r\n\t\t\t\t\tStrokeLineJoin=\"Miter\"\r\n\t\t\t\t\tStrokeMiterLimit=\"10\"\r\n\t\t\t\t\tStrokeStartLineCap=\"Flat\"\r\n\t\t\t\t\tStrokeThickness=\"1\">\r\n\t\t\t\t\t<Path.Data>\r\n\t\t\t\t\t\t<PathGeometry Figures=\"M277.5 288l16 0c4.5 0 8 3.5 8 8l0 16c0 4.5 -3.5 8 -8 8l-16 0c-4.5 0 -8 -3.5 -8 -8l0 -16c0 -4.5 3.5 -8 8 -8z\" FillRule=\"EvenOdd\" />\r\n\t\t\t\t\t</Path.Data>\r\n\t\t\t\t</Path>\r\n\t\t\t\t<Path\r\n\t\t\t\t\txmlns:x=\"http://schemas.microsoft.com/winfx/2006/xaml\"\r\n\t\t\t\t\tFill=\"{StaticResource color-17}\"\r\n\t\t\t\t\tStrokeDashArray=\"\"\r\n\t\t\t\t\tStrokeEndLineCap=\"Flat\"\r\n\t\t\t\t\tStrokeLineJoin=\"Miter\"\r\n\t\t\t\t\tStrokeMiterLimit=\"10\"\r\n\t\t\t\t\tStrokeStartLineCap=\"Flat\"\r\n\t\t\t\t\tStrokeThickness=\"1\">\r\n\t\t\t\t\t<Path.Data>\r\n\t\t\t\t\t\t<PathGeometry Figures=\"M325.5 288l16 0c4.5 0 8 3.5 8 8l0 16c0 4.5 -3.5 8 -8 8l-16 0c-4.5 0 -8 -3.5 -8 -8l0 -16c0 -4.5 3.5 -8 8 -8z\" FillRule=\"EvenOdd\" />\r\n\t\t\t\t\t</Path.Data>\r\n\t\t\t\t</Path>\r\n\t\t\t\t<Path\r\n\t\t\t\t\txmlns:x=\"http://schemas.microsoft.com/winfx/2006/xaml\"\r\n\t\t\t\t\tFill=\"{StaticResource color-18}\"\r\n\t\t\t\t\tStrokeDashArray=\"\"\r\n\t\t\t\t\tStrokeEndLineCap=\"Flat\"\r\n\t\t\t\t\tStrokeLineJoin=\"Miter\"\r\n\t\t\t\t\tStrokeMiterLimit=\"10\"\r\n\t\t\t\t\tStrokeStartLineCap=\"Flat\"\r\n\t\t\t\t\tStrokeThickness=\"1\">\r\n\t\t\t\t\t<Path.Data>\r\n\t\t\t\t\t\t<PathGeometry Figures=\"M373.5 288l16 0c4.5 0 8 3.5 8 8l0 16c0 4.5 -3.5 8 -8 8l-16 0c-4.5 0 -8 -3.5 -8 -8l0 -16c0 -4.5 3.5 -8 8 -8z\" FillRule=\"EvenOdd\" />\r\n\t\t\t\t\t</Path.Data>\r\n\t\t\t\t</Path>\r\n\t\t\t\t<Path\r\n\t\t\t\t\txmlns:x=\"http://schemas.microsoft.com/winfx/2006/xaml\"\r\n\t\t\t\t\tFill=\"{StaticResource color-19}\"\r\n\t\t\t\t\tStrokeDashArray=\"\"\r\n\t\t\t\t\tStrokeEndLineCap=\"Flat\"\r\n\t\t\t\t\tStrokeLineJoin=\"Miter\"\r\n\t\t\t\t\tStrokeMiterLimit=\"10\"\r\n\t\t\t\t\tStrokeStartLineCap=\"Flat\"\r\n\t\t\t\t\tStrokeThickness=\"1\">\r\n\t\t\t\t\t<Path.Data>\r\n\t\t\t\t\t\t<PathGeometry Figures=\"M421.5 288l50.5 0c4.5 0 8 3.5 8 8l0 64c0 4.5 -3.5 8 -8 8l-32 0c-4.5 0 -8 -3.5 -8 -8l0 -32c0 -4.5 -3.5 -8 -8 -8l-2.5 0c-4.5 0 -8 -3.5 -8 -8l0 -16c0 -4.5 3.5 -8 8 -8z\" FillRule=\"EvenOdd\" />\r\n\t\t\t\t\t</Path.Data>\r\n\t\t\t\t</Path>\r\n\t\t\t\t<Path\r\n\t\t\t\t\txmlns:x=\"http://schemas.microsoft.com/winfx/2006/xaml\"\r\n\t\t\t\t\tFill=\"{StaticResource color-20}\"\r\n\t\t\t\t\tStrokeDashArray=\"\"\r\n\t\t\t\t\tStrokeEndLineCap=\"Flat\"\r\n\t\t\t\t\tStrokeLineJoin=\"Miter\"\r\n\t\t\t\t\tStrokeMiterLimit=\"10\"\r\n\t\t\t\t\tStrokeStartLineCap=\"Flat\"\r\n\t\t\t\t\tStrokeThickness=\"1\">\r\n\t\t\t\t\t<Path.Data>\r\n\t\t\t\t\t\t<PathGeometry Figures=\"M152 336l16 0c4.5 0 8 3.5 8 8l0 16c0 4.5 -3.5 8 -8 8l-16 0c-4.5 0 -8 -3.5 -8 -8l0 -16c0 -4.5 3.5 -8 8 -8z\" FillRule=\"EvenOdd\" />\r\n\t\t\t\t\t</Path.Data>\r\n\t\t\t\t</Path>\r\n\t\t\t\t<Path\r\n\t\t\t\t\txmlns:x=\"http://schemas.microsoft.com/winfx/2006/xaml\"\r\n\t\t\t\t\tFill=\"{StaticResource color-21}\"\r\n\t\t\t\t\tStrokeDashArray=\"\"\r\n\t\t\t\t\tStrokeEndLineCap=\"Flat\"\r\n\t\t\t\t\tStrokeLineJoin=\"Miter\"\r\n\t\t\t\t\tStrokeMiterLimit=\"10\"\r\n\t\t\t\t\tStrokeStartLineCap=\"Flat\"\r\n\t\t\t\t\tStrokeThickness=\"1\">\r\n\t\t\t\t\t<Path.Data>\r\n\t\t\t\t\t\t<PathGeometry Figures=\"M40 336l8 0l24 0c4.5 0 8 3.5 8 8l0 16c0 4.5 -3.5 8 -8 8l-32 0c-4.5 0 -8 -3.5 -8 -8l0 -16c0 -4.5 3.5 -8 8 -8z\" FillRule=\"EvenOdd\" />\r\n\t\t\t\t\t</Path.Data>\r\n\t\t\t\t</Path>\r\n\t\t\t\t<Path\r\n\t\t\t\t\txmlns:x=\"http://schemas.microsoft.com/winfx/2006/xaml\"\r\n\t\t\t\t\tFill=\"{StaticResource color-22}\"\r\n\t\t\t\t\tStrokeDashArray=\"\"\r\n\t\t\t\t\tStrokeEndLineCap=\"Flat\"\r\n\t\t\t\t\tStrokeLineJoin=\"Miter\"\r\n\t\t\t\t\tStrokeMiterLimit=\"10\"\r\n\t\t\t\t\tStrokeStartLineCap=\"Flat\"\r\n\t\t\t\t\tStrokeThickness=\"1\">\r\n\t\t\t\t\t<Path.Data>\r\n\t\t\t\t\t\t<PathGeometry Figures=\"M200 336l16 0c4.5 0 8 3.5 8 8l0 16c0 4.5 -3.5 8 -8 8l-16 0c-4.5 0 -8 -3.5 -8 -8l0 -16c0 -4.5 3.5 -8 8 -8z\" FillRule=\"EvenOdd\" />\r\n\t\t\t\t\t</Path.Data>\r\n\t\t\t\t</Path>\r\n\t\t\t\t<Path\r\n\t\t\t\t\txmlns:x=\"http://schemas.microsoft.com/winfx/2006/xaml\"\r\n\t\t\t\t\tFill=\"{StaticResource color-23}\"\r\n\t\t\t\t\tStrokeDashArray=\"\"\r\n\t\t\t\t\tStrokeEndLineCap=\"Flat\"\r\n\t\t\t\t\tStrokeLineJoin=\"Miter\"\r\n\t\t\t\t\tStrokeMiterLimit=\"10\"\r\n\t\t\t\t\tStrokeStartLineCap=\"Flat\"\r\n\t\t\t\t\tStrokeThickness=\"1\">\r\n\t\t\t\t\t<Path.Data>\r\n\t\t\t\t\t\t<PathGeometry Figures=\"M248 336l16 0c4.5 0 8 3.5 8 8l0 16c0 4.5 -3.5 8 -8 8l-16 0c-4.5 0 -8 -3.5 -8 -8l0 -16c0 -4.5 3.5 -8 8 -8z\" FillRule=\"EvenOdd\" />\r\n\t\t\t\t\t</Path.Data>\r\n\t\t\t\t</Path>\r\n\t\t\t\t<Path\r\n\t\t\t\t\txmlns:x=\"http://schemas.microsoft.com/winfx/2006/xaml\"\r\n\t\t\t\t\tFill=\"{StaticResource color-24}\"\r\n\t\t\t\t\tStrokeDashArray=\"\"\r\n\t\t\t\t\tStrokeEndLineCap=\"Flat\"\r\n\t\t\t\t\tStrokeLineJoin=\"Miter\"\r\n\t\t\t\t\tStrokeMiterLimit=\"10\"\r\n\t\t\t\t\tStrokeStartLineCap=\"Flat\"\r\n\t\t\t\t\tStrokeThickness=\"1\">\r\n\t\t\t\t\t<Path.Data>\r\n\t\t\t\t\t\t<PathGeometry Figures=\"M296 336l16 0c4.5 0 8 3.5 8 8l0 16c0 4.5 -3.5 8 -8 8l-16 0c-4.5 0 -8 -3.5 -8 -8l0 -16c0 -4.5 3.5 -8 8 -8z\" FillRule=\"EvenOdd\" />\r\n\t\t\t\t\t</Path.Data>\r\n\t\t\t\t</Path>\r\n\t\t\t\t<Path\r\n\t\t\t\t\txmlns:x=\"http://schemas.microsoft.com/winfx/2006/xaml\"\r\n\t\t\t\t\tFill=\"{StaticResource color-25}\"\r\n\t\t\t\t\tStrokeDashArray=\"\"\r\n\t\t\t\t\tStrokeEndLineCap=\"Flat\"\r\n\t\t\t\t\tStrokeLineJoin=\"Miter\"\r\n\t\t\t\t\tStrokeMiterLimit=\"10\"\r\n\t\t\t\t\tStrokeStartLineCap=\"Flat\"\r\n\t\t\t\t\tStrokeThickness=\"1\">\r\n\t\t\t\t\t<Path.Data>\r\n\t\t\t\t\t\t<PathGeometry Figures=\"M344 336l16 0c4.5 0 8 3.5 8 8l0 16c0 4.5 -3.5 8 -8 8l-16 0c-4.5 0 -8 -3.5 -8 -8l0 -16c0 -4.5 3.5 -8 8 -8z\" FillRule=\"EvenOdd\" />\r\n\t\t\t\t\t</Path.Data>\r\n\t\t\t\t</Path>\r\n\t\t\t\t<Path\r\n\t\t\t\t\txmlns:x=\"http://schemas.microsoft.com/winfx/2006/xaml\"\r\n\t\t\t\t\tFill=\"{StaticResource color-26}\"\r\n\t\t\t\t\tStrokeDashArray=\"\"\r\n\t\t\t\t\tStrokeEndLineCap=\"Flat\"\r\n\t\t\t\t\tStrokeLineJoin=\"Miter\"\r\n\t\t\t\t\tStrokeMiterLimit=\"10\"\r\n\t\t\t\t\tStrokeStartLineCap=\"Flat\"\r\n\t\t\t\t\tStrokeThickness=\"1\">\r\n\t\t\t\t\t<Path.Data>\r\n\t\t\t\t\t\t<PathGeometry Figures=\"M392 336l16 0c4.5 0 8 3.5 8 8l0 16c0 4.5 -3.5 8 -8 8l-16 0c-4.5 0 -8 -3.5 -8 -8l0 -16c0 -4.5 3.5 -8 8 -8z\" FillRule=\"EvenOdd\" />\r\n\t\t\t\t\t</Path.Data>\r\n\t\t\t\t</Path>\r\n\t\t\t\t<Path\r\n\t\t\t\t\txmlns:x=\"http://schemas.microsoft.com/winfx/2006/xaml\"\r\n\t\t\t\t\tFill=\"{StaticResource color-27}\"\r\n\t\t\t\t\tStrokeDashArray=\"\"\r\n\t\t\t\t\tStrokeEndLineCap=\"Flat\"\r\n\t\t\t\t\tStrokeLineJoin=\"Miter\"\r\n\t\t\t\t\tStrokeMiterLimit=\"10\"\r\n\t\t\t\t\tStrokeStartLineCap=\"Flat\"\r\n\t\t\t\t\tStrokeThickness=\"1\">\r\n\t\t\t\t\t<Path.Data>\r\n\t\t\t\t\t\t<PathGeometry Figures=\"M40 384l48 0c4.5 0 8 3.5 8 8l0 16c0 4.5 -3.5 8 -8 8l-48 0c-4.5 0 -8 -3.5 -8 -8l0 -16c0 -4.5 3.5 -8 8 -8z\" FillRule=\"EvenOdd\" />\r\n\t\t\t\t\t</Path.Data>\r\n\t\t\t\t</Path>\r\n\t\t\t\t<Path\r\n\t\t\t\t\txmlns:x=\"http://schemas.microsoft.com/winfx/2006/xaml\"\r\n\t\t\t\t\tFill=\"{StaticResource color-28}\"\r\n\t\t\t\t\tStrokeDashArray=\"\"\r\n\t\t\t\t\tStrokeEndLineCap=\"Flat\"\r\n\t\t\t\t\tStrokeLineJoin=\"Miter\"\r\n\t\t\t\t\tStrokeMiterLimit=\"10\"\r\n\t\t\t\t\tStrokeStartLineCap=\"Flat\"\r\n\t\t\t\t\tStrokeThickness=\"1\">\r\n\t\t\t\t\t<Path.Data>\r\n\t\t\t\t\t\t<PathGeometry Figures=\"M120 384l16 0c4.5 0 8 3.5 8 8l0 16c0 4.5 -3.5 8 -8 8l-16 0c-4.5 0 -8 -3.5 -8 -8l0 -16c0 -4.5 3.5 -8 8 -8z\" FillRule=\"EvenOdd\" />\r\n\t\t\t\t\t</Path.Data>\r\n\t\t\t\t</Path>\r\n\t\t\t\t<Path\r\n\t\t\t\t\txmlns:x=\"http://schemas.microsoft.com/winfx/2006/xaml\"\r\n\t\t\t\t\tFill=\"{StaticResource color-29}\"\r\n\t\t\t\t\tStrokeDashArray=\"\"\r\n\t\t\t\t\tStrokeEndLineCap=\"Flat\"\r\n\t\t\t\t\tStrokeLineJoin=\"Miter\"\r\n\t\t\t\t\tStrokeMiterLimit=\"10\"\r\n\t\t\t\t\tStrokeStartLineCap=\"Flat\"\r\n\t\t\t\t\tStrokeThickness=\"1\">\r\n\t\t\t\t\t<Path.Data>\r\n\t\t\t\t\t\t<PathGeometry Figures=\"M168 384l160 0c4.5 0 8 3.5 8 8l0 16c0 4.5 -3.5 8 -8 8l-160 0c-4.5 0 -8 -3.5 -8 -8l0 -16c0 -4.5 3.5 -8 8 -8z\" FillRule=\"EvenOdd\" />\r\n\t\t\t\t\t</Path.Data>\r\n\t\t\t\t</Path>\r\n\t\t\t\t<Path\r\n\t\t\t\t\txmlns:x=\"http://schemas.microsoft.com/winfx/2006/xaml\"\r\n\t\t\t\t\tFill=\"{StaticResource color-30}\"\r\n\t\t\t\t\tStrokeDashArray=\"\"\r\n\t\t\t\t\tStrokeEndLineCap=\"Flat\"\r\n\t\t\t\t\tStrokeLineJoin=\"Miter\"\r\n\t\t\t\t\tStrokeMiterLimit=\"10\"\r\n\t\t\t\t\tStrokeStartLineCap=\"Flat\"\r\n\t\t\t\t\tStrokeThickness=\"1\">\r\n\t\t\t\t\t<Path.Data>\r\n\t\t\t\t\t\t<PathGeometry Figures=\"M360 384l16 0c4.5 0 8 3.5 8 8l0 16c0 4.5 -3.5 8 -8 8l-16 0c-4.5 0 -8 -3.5 -8 -8l0 -16c0 -4.5 3.5 -8 8 -8z\" FillRule=\"EvenOdd\" />\r\n\t\t\t\t\t</Path.Data>\r\n\t\t\t\t</Path>\r\n\t\t\t\t<Path\r\n\t\t\t\t\txmlns:x=\"http://schemas.microsoft.com/winfx/2006/xaml\"\r\n\t\t\t\t\tFill=\"{StaticResource color-31}\"\r\n\t\t\t\t\tStrokeDashArray=\"\"\r\n\t\t\t\t\tStrokeEndLineCap=\"Flat\"\r\n\t\t\t\t\tStrokeLineJoin=\"Miter\"\r\n\t\t\t\t\tStrokeMiterLimit=\"10\"\r\n\t\t\t\t\tStrokeStartLineCap=\"Flat\"\r\n\t\t\t\t\tStrokeThickness=\"1\">\r\n\t\t\t\t\t<Path.Data>\r\n\t\t\t\t\t\t<PathGeometry Figures=\"M408 384l64 0c4.5 0 8 3.5 8 8l0 16c0 4.5 -3.5 8 -8 8l-64 0c-4.5 0 -8 -3.5 -8 -8l0 -16c0 -4.5 3.5 -8 8 -8z\" FillRule=\"EvenOdd\" />\r\n\t\t\t\t\t</Path.Data>\r\n\t\t\t\t</Path>\r\n\t\t\t\t<Path\r\n\t\t\t\t\txmlns:x=\"http://schemas.microsoft.com/winfx/2006/xaml\"\r\n\t\t\t\t\tFill=\"{StaticResource color-32}\"\r\n\t\t\t\t\tStrokeDashArray=\"\"\r\n\t\t\t\t\tStrokeEndLineCap=\"Flat\"\r\n\t\t\t\t\tStrokeLineJoin=\"Miter\"\r\n\t\t\t\t\tStrokeMiterLimit=\"10\"\r\n\t\t\t\t\tStrokeStartLineCap=\"Flat\"\r\n\t\t\t\t\tStrokeThickness=\"1\">\r\n\t\t\t\t\t<Path.Data>\r\n\t\t\t\t\t\t<PathGeometry Figures=\"M104 336l16 0c4.5 0 8 3.5 8 8l0 16c0 4.5 -3.5 8 -8 8l-16 0c-4.5 0 -8 -3.5 -8 -8l0 -16c0 -4.5 3.5 -8 8 -8z\" FillRule=\"EvenOdd\" />\r\n\t\t\t\t\t</Path.Data>\r\n\t\t\t\t</Path>\r\n\t\t\t</Canvas>\r\n\t\t</Canvas>\r\n\t</Viewbox>\r\n\t<Viewbox\r\n\t\txmlns=\"http://schemas.microsoft.com/winfx/2006/xaml/presentation\"\r\n\t\tx:Key=\"Icon_list\"\r\n\t\tx:Shared=\"False\"\r\n\t\tStretch=\"Uniform\"\r\n\t\tTag=\"SHA256_8B823E196B93DC4E630F26D3CDA6E44DAB232C3FB47DE1FD1F85AAFB85FDD38C\">\r\n\t\t<Canvas\r\n\t\t\tCanvas.Left=\"0\"\r\n\t\t\tCanvas.Top=\"0\"\r\n\t\t\tWidth=\"24\"\r\n\t\t\tHeight=\"24\">\r\n\t\t\t<Canvas.RenderTransform>\r\n\t\t\t\t<TranslateTransform X=\"0\" Y=\"0\" />\r\n\t\t\t</Canvas.RenderTransform>\r\n\t\t\t<Canvas.Resources />\r\n\t\t\t<!--  Unknown tag: metadata  -->\r\n\t\t\t<!--  Unknown tag: sodipodi:namedview  -->\r\n\t\t\t<Canvas>\r\n\t\t\t\t<Path xmlns:x=\"http://schemas.microsoft.com/winfx/2006/xaml\" Fill=\"#FF73A3E3\">\r\n\t\t\t\t\t<Path.Data>\r\n\t\t\t\t\t\t<PathGeometry Figures=\"M21 4V2.2c0 -0.66 -0.54 -1.2 -1.2 -1.2H4.2c-0.66 0 -1.2 0.54 -1.2 1.2v1.8H21z\" FillRule=\"NonZero\" />\r\n\t\t\t\t\t</Path.Data>\r\n\t\t\t\t</Path>\r\n\t\t\t\t<Path xmlns:x=\"http://schemas.microsoft.com/winfx/2006/xaml\" Fill=\"#FFDBE7F8\">\r\n\t\t\t\t\t<Path.Data>\r\n\t\t\t\t\t\t<PathGeometry Figures=\"M3 4v17.8c0 0.66 0.54 1.2 1.2 1.2h15.6c0.66 0 1.2 -0.54 1.2 -1.2V4\" FillRule=\"NonZero\" />\r\n\t\t\t\t\t</Path.Data>\r\n\t\t\t\t</Path>\r\n\t\t\t\t<Path xmlns:x=\"http://schemas.microsoft.com/winfx/2006/xaml\" Fill=\"#FF0A1D37\" Opacity=\"0.06\">\r\n\t\t\t\t\t<Path.Data>\r\n\t\t\t\t\t\t<PathGeometry Figures=\"M19.8 1h-3.7738l-4.5377 22H19.8c0.66 0 1.2 -0.54 1.2 -1.2V2.2C21 1.54 20.46 1 19.8 1z\" FillRule=\"NonZero\" />\r\n\t\t\t\t\t</Path.Data>\r\n\t\t\t\t</Path>\r\n\t\t\t\t<Path\r\n\t\t\t\t\txmlns:x=\"http://schemas.microsoft.com/winfx/2006/xaml\"\r\n\t\t\t\t\tOpacity=\"0.7\"\r\n\t\t\t\t\tStroke=\"#FF225FB0\"\r\n\t\t\t\t\tStrokeEndLineCap=\"Round\"\r\n\t\t\t\t\tStrokeMiterLimit=\"10\"\r\n\t\t\t\t\tStrokeStartLineCap=\"Round\"\r\n\t\t\t\t\tStrokeThickness=\"1\">\r\n\t\t\t\t\t<Path.Data>\r\n\t\t\t\t\t\t<PathGeometry Figures=\"M6.5 11.5h11M6.5 9.5h11M6.5 7.5h11M6.5 17.5h11M6.5 19.5h11M6.5 15.5h11M6.5 13.5h11\" FillRule=\"NonZero\" />\r\n\t\t\t\t\t</Path.Data>\r\n\t\t\t\t</Path>\r\n\t\t\t</Canvas>\r\n\t\t</Canvas>\r\n\t</Viewbox>\r\n\t<Viewbox\r\n\t\txmlns=\"http://schemas.microsoft.com/winfx/2006/xaml/presentation\"\r\n\t\tx:Key=\"Icon_mouse2\"\r\n\t\tx:Shared=\"False\"\r\n\t\tStretch=\"Uniform\"\r\n\t\tTag=\"SHA256_099D1183E0516B6C747032E04A1AEEDCEB5C027F64F7BC6FF3BB5FD115E6F329\">\r\n\t\t<Canvas Width=\"512\" Height=\"512\">\r\n\t\t\t<Canvas.RenderTransform>\r\n\t\t\t\t<TranslateTransform X=\"0\" Y=\"0\" />\r\n\t\t\t</Canvas.RenderTransform>\r\n\t\t\t<Canvas.Resources>\r\n\t\t\t\t<RadialGradientBrush xmlns:x=\"http://schemas.microsoft.com/winfx/2006/xaml\" x:Key=\"color-1\" Center=\"126,246\" GradientOrigin=\"126,246\" MappingMode=\"Absolute\" RadiusX=\"418.24558\" RadiusY=\"418.24558\">\r\n\t\t\t\t\t<RadialGradientBrush.GradientStops>\r\n\t\t\t\t\t\t<GradientStopCollection>\r\n\t\t\t\t\t\t\t<GradientStop Offset=\"0\" Color=\"#B3C0CD\" />\r\n\t\t\t\t\t\t\t<GradientStop Offset=\"0.43137\" Color=\"#9AA8B6\" />\r\n\t\t\t\t\t\t\t<GradientStop Offset=\"1\" Color=\"#717F8F\" />\r\n\t\t\t\t\t\t</GradientStopCollection>\r\n\t\t\t\t\t</RadialGradientBrush.GradientStops>\r\n\t\t\t\t</RadialGradientBrush>\r\n\t\t\t\t<LinearGradientBrush xmlns:x=\"http://schemas.microsoft.com/winfx/2006/xaml\" x:Key=\"color-2\" MappingMode=\"Absolute\" StartPoint=\"80,16\" EndPoint=\"464,448\">\r\n\t\t\t\t\t<LinearGradientBrush.GradientStops>\r\n\t\t\t\t\t\t<GradientStopCollection>\r\n\t\t\t\t\t\t\t<GradientStop Offset=\"0\" Color=\"#FFFFFF\" />\r\n\t\t\t\t\t\t\t<GradientStop Offset=\"1\" Color=\"#C5E7FB\" />\r\n\t\t\t\t\t\t</GradientStopCollection>\r\n\t\t\t\t\t</LinearGradientBrush.GradientStops>\r\n\t\t\t\t</LinearGradientBrush>\r\n\t\t\t\t<LinearGradientBrush xmlns:x=\"http://schemas.microsoft.com/winfx/2006/xaml\" x:Key=\"color-3\" MappingMode=\"Absolute\" StartPoint=\"272,16\" EndPoint=\"656,448\">\r\n\t\t\t\t\t<LinearGradientBrush.GradientStops>\r\n\t\t\t\t\t\t<GradientStopCollection>\r\n\t\t\t\t\t\t\t<GradientStop Offset=\"0\" Color=\"#FFFFFF\" />\r\n\t\t\t\t\t\t\t<GradientStop Offset=\"1\" Color=\"#C5E7FB\" />\r\n\t\t\t\t\t\t</GradientStopCollection>\r\n\t\t\t\t\t</LinearGradientBrush.GradientStops>\r\n\t\t\t\t</LinearGradientBrush>\r\n\t\t\t\t<LinearGradientBrush xmlns:x=\"http://schemas.microsoft.com/winfx/2006/xaml\" x:Key=\"color-4\" MappingMode=\"Absolute\" StartPoint=\"224,48\" EndPoint=\"320,48\">\r\n\t\t\t\t\t<LinearGradientBrush.GradientStops>\r\n\t\t\t\t\t\t<GradientStopCollection>\r\n\t\t\t\t\t\t\t<GradientStop Offset=\"0\" Color=\"#FFFFFF\" />\r\n\t\t\t\t\t\t\t<GradientStop Offset=\"0.50196\" Color=\"#D8E0E8\" />\r\n\t\t\t\t\t\t\t<GradientStop Offset=\"1\" Color=\"#91A0AF\" />\r\n\t\t\t\t\t\t</GradientStopCollection>\r\n\t\t\t\t\t</LinearGradientBrush.GradientStops>\r\n\t\t\t\t</LinearGradientBrush>\r\n\t\t\t</Canvas.Resources>\r\n\t\t\t<!--  Unknown tag: metadata  -->\r\n\t\t\t<!--  Unknown tag: sodipodi:namedview  -->\r\n\t\t\t<Canvas>\r\n\t\t\t\t<Path\r\n\t\t\t\t\txmlns:x=\"http://schemas.microsoft.com/winfx/2006/xaml\"\r\n\t\t\t\t\tFill=\"{StaticResource color-1}\"\r\n\t\t\t\t\tStrokeDashArray=\"\"\r\n\t\t\t\t\tStrokeEndLineCap=\"Flat\"\r\n\t\t\t\t\tStrokeLineJoin=\"Miter\"\r\n\t\t\t\t\tStrokeMiterLimit=\"10\"\r\n\t\t\t\t\tStrokeStartLineCap=\"Flat\"\r\n\t\t\t\t\tStrokeThickness=\"1\">\r\n\t\t\t\t\t<Path.Data>\r\n\t\t\t\t\t\t<PathGeometry Figures=\"M448 96l0 224c0 106 -86 192 -192 192c-106 0 -192 -86 -192 -192l0 -224c0 -53 43 -96 96 -96c64 0 128 0 192 0c53 0 96 43 96 96z\" FillRule=\"EvenOdd\" />\r\n\t\t\t\t\t</Path.Data>\r\n\t\t\t\t</Path>\r\n\t\t\t\t<Path\r\n\t\t\t\t\txmlns:x=\"http://schemas.microsoft.com/winfx/2006/xaml\"\r\n\t\t\t\t\tFill=\"{StaticResource color-2}\"\r\n\t\t\t\t\tStrokeDashArray=\"\"\r\n\t\t\t\t\tStrokeEndLineCap=\"Flat\"\r\n\t\t\t\t\tStrokeLineJoin=\"Miter\"\r\n\t\t\t\t\tStrokeMiterLimit=\"10\"\r\n\t\t\t\t\tStrokeStartLineCap=\"Flat\"\r\n\t\t\t\t\tStrokeThickness=\"1\">\r\n\t\t\t\t\t<Path.Data>\r\n\t\t\t\t\t\t<PathGeometry Figures=\"M208 272l32 0l0 -256l-80 0c-44 0 -80 36 -80 80l0 48c0 70.5 57.5 128 128 128z\" FillRule=\"EvenOdd\" />\r\n\t\t\t\t\t</Path.Data>\r\n\t\t\t\t</Path>\r\n\t\t\t\t<Path\r\n\t\t\t\t\txmlns:x=\"http://schemas.microsoft.com/winfx/2006/xaml\"\r\n\t\t\t\t\tFill=\"{StaticResource color-3}\"\r\n\t\t\t\t\tStrokeDashArray=\"\"\r\n\t\t\t\t\tStrokeEndLineCap=\"Flat\"\r\n\t\t\t\t\tStrokeLineJoin=\"Miter\"\r\n\t\t\t\t\tStrokeMiterLimit=\"10\"\r\n\t\t\t\t\tStrokeStartLineCap=\"Flat\"\r\n\t\t\t\t\tStrokeThickness=\"1\">\r\n\t\t\t\t\t<Path.Data>\r\n\t\t\t\t\t\t<PathGeometry Figures=\"M272 272l32 0c70.5 0 128 -57.5 128 -128l0 -48c0 -44 -36 -80 -80 -80l-80 0z\" FillRule=\"EvenOdd\" />\r\n\t\t\t\t\t</Path.Data>\r\n\t\t\t\t</Path>\r\n\t\t\t\t<Path\r\n\t\t\t\t\txmlns:x=\"http://schemas.microsoft.com/winfx/2006/xaml\"\r\n\t\t\t\t\tFill=\"#FF91A0AF\"\r\n\t\t\t\t\tStrokeDashArray=\"\"\r\n\t\t\t\t\tStrokeEndLineCap=\"Flat\"\r\n\t\t\t\t\tStrokeLineJoin=\"Miter\"\r\n\t\t\t\t\tStrokeMiterLimit=\"10\"\r\n\t\t\t\t\tStrokeStartLineCap=\"Flat\"\r\n\t\t\t\t\tStrokeThickness=\"1\">\r\n\t\t\t\t\t<Path.Data>\r\n\t\t\t\t\t\t<PathGeometry Figures=\"M256 32c26.5 0 48 21.5 48 48l0 48c0 26.5 -21.5 48 -48 48c-26.5 0 -48 -21.5 -48 -48l0 -48c0 -26.5 21.5 -48 48 -48z\" FillRule=\"EvenOdd\" />\r\n\t\t\t\t\t</Path.Data>\r\n\t\t\t\t</Path>\r\n\t\t\t\t<Path\r\n\t\t\t\t\txmlns:x=\"http://schemas.microsoft.com/winfx/2006/xaml\"\r\n\t\t\t\t\tFill=\"{StaticResource color-4}\"\r\n\t\t\t\t\tStrokeDashArray=\"\"\r\n\t\t\t\t\tStrokeEndLineCap=\"Flat\"\r\n\t\t\t\t\tStrokeLineJoin=\"Miter\"\r\n\t\t\t\t\tStrokeMiterLimit=\"10\"\r\n\t\t\t\t\tStrokeStartLineCap=\"Flat\"\r\n\t\t\t\t\tStrokeThickness=\"1\">\r\n\t\t\t\t\t<Path.Data>\r\n\t\t\t\t\t\t<PathGeometry Figures=\"M256 48c17.5 0 32 14.5 32 32l0 48c0 17.5 -14.5 32 -32 32c-17.5 0 -32 -14.5 -32 -32l0 -48c0 -17.5 14.5 -32 32 -32z\" FillRule=\"EvenOdd\" />\r\n\t\t\t\t\t</Path.Data>\r\n\t\t\t\t</Path>\r\n\t\t\t</Canvas>\r\n\t\t</Canvas>\r\n\t</Viewbox>\r\n\t<Viewbox\r\n\t\txmlns=\"http://schemas.microsoft.com/winfx/2006/xaml/presentation\"\r\n\t\tx:Key=\"Icon_text_field\"\r\n\t\tx:Shared=\"False\"\r\n\t\tStretch=\"Uniform\"\r\n\t\tTag=\"SHA256_8C2AEC487B725125E32C4A7322E72B12B82E8F45981B645E00A3F0AF8FA16F14\">\r\n\t\t<Canvas Width=\"512\" Height=\"512\">\r\n\t\t\t<Canvas.RenderTransform>\r\n\t\t\t\t<TranslateTransform X=\"0\" Y=\"0\" />\r\n\t\t\t</Canvas.RenderTransform>\r\n\t\t\t<Canvas.Resources>\r\n\t\t\t\t<LinearGradientBrush xmlns:x=\"http://schemas.microsoft.com/winfx/2006/xaml\" x:Key=\"_Linear1\" MappingMode=\"Absolute\" StartPoint=\"0,0\" EndPoint=\"1,0\">\r\n\t\t\t\t\t<LinearGradientBrush.GradientStops>\r\n\t\t\t\t\t\t<GradientStopCollection>\r\n\t\t\t\t\t\t\t<GradientStop Offset=\"0\" Color=\"#FFF4F6F8\" />\r\n\t\t\t\t\t\t\t<GradientStop Offset=\"1\" Color=\"#FFE7ECF1\" />\r\n\t\t\t\t\t\t</GradientStopCollection>\r\n\t\t\t\t\t</LinearGradientBrush.GradientStops>\r\n\t\t\t\t\t<LinearGradientBrush.Transform>\r\n\t\t\t\t\t\t<MatrixTransform Matrix=\"103.226 160 -160 103.226 0 176\" />\r\n\t\t\t\t\t</LinearGradientBrush.Transform>\r\n\t\t\t\t</LinearGradientBrush>\r\n\t\t\t\t<LinearGradientBrush xmlns:x=\"http://schemas.microsoft.com/winfx/2006/xaml\" x:Key=\"_Linear2\" MappingMode=\"Absolute\" StartPoint=\"0,0\" EndPoint=\"1,0\">\r\n\t\t\t\t\t<LinearGradientBrush.GradientStops>\r\n\t\t\t\t\t\t<GradientStopCollection>\r\n\t\t\t\t\t\t\t<GradientStop Offset=\"0\" Color=\"#FF129EEE\" />\r\n\t\t\t\t\t\t\t<GradientStop Offset=\"1\" Color=\"#FF1086CA\" />\r\n\t\t\t\t\t\t</GradientStopCollection>\r\n\t\t\t\t\t</LinearGradientBrush.GradientStops>\r\n\t\t\t\t\t<LinearGradientBrush.Transform>\r\n\t\t\t\t\t\t<MatrixTransform Matrix=\"81.0499 89.7495 -89.7495 81.0499 32 208\" />\r\n\t\t\t\t\t</LinearGradientBrush.Transform>\r\n\t\t\t\t</LinearGradientBrush>\r\n\t\t\t</Canvas.Resources>\r\n\t\t\t<!--  Unknown tag: metadata  -->\r\n\t\t\t<!--  Unknown tag: sodipodi:namedview  -->\r\n\t\t\t<Canvas>\r\n\t\t\t\t<Canvas>\r\n\t\t\t\t\t<Canvas.RenderTransform>\r\n\t\t\t\t\t\t<MatrixTransform Matrix=\"1 0 0 1.27679 0 -39.8571\" />\r\n\t\t\t\t\t</Canvas.RenderTransform>\r\n\t\t\t\t\t<Path\r\n\t\t\t\t\t\txmlns:x=\"http://schemas.microsoft.com/winfx/2006/xaml\"\r\n\t\t\t\t\t\tFill=\"{StaticResource _Linear1}\"\r\n\t\t\t\t\t\tStrokeLineJoin=\"Round\"\r\n\t\t\t\t\t\tStrokeMiterLimit=\"2\">\r\n\t\t\t\t\t\t<Path.Data>\r\n\t\t\t\t\t\t\t<PathGeometry Figures=\"M8 176L504 176C508.5 176 512 179.5 512 184L512 328C512 332.5 508.5 336 504 336L8 336C3.5 336 0 332.5 0 328L0 184C0 179.5 3.5 176 8 176Z\" FillRule=\"EvenOdd\" />\r\n\t\t\t\t\t\t</Path.Data>\r\n\t\t\t\t\t</Path>\r\n\t\t\t\t</Canvas>\r\n\t\t\t\t<Canvas>\r\n\t\t\t\t\t<Canvas.RenderTransform>\r\n\t\t\t\t\t\t<MatrixTransform Matrix=\"1.27778 0 0 1.27679 0 -39.8571\" />\r\n\t\t\t\t\t</Canvas.RenderTransform>\r\n\t\t\t\t\t<Path\r\n\t\t\t\t\t\txmlns:x=\"http://schemas.microsoft.com/winfx/2006/xaml\"\r\n\t\t\t\t\t\tFill=\"{StaticResource _Linear2}\"\r\n\t\t\t\t\t\tStrokeLineJoin=\"Round\"\r\n\t\t\t\t\t\tStrokeMiterLimit=\"2\">\r\n\t\t\t\t\t\t<Path.Data>\r\n\t\t\t\t\t\t\t<PathGeometry Figures=\"M92.5 285L61.5 285L56 302.5L32 302.5L66.5 209.5L87.5 209.5L122 302.5L97.5 302.5L92.5 285ZM67 268L87 268L77 235.5L67 268ZM133.5 302.5L133.5 209.5L167 209.5C179 209.5 188 211.5 194.5 216C200.5 220.5 203.5 226.5 203.5 235C203.5 240 202.5 244 200.5 247.5C198 251 195 253.5 190.5 255C195.5 256.5 199 259 201.5 262.5C204.5 266 205.5 270 205.5 275C205.5 284 203 291 197 295.5C191 300.5 182.5 302.5 171 302.5L133.5 302.5ZM156 263L156 285.5L170.5 285.5C174.5 285.5 178 284.5 180 282.5C182 280.5 183 278 183 275C183 267 179.5 263 171.5 263L156 263ZM156 248L167.5 248C172.5 248 176 247 178 245.5C180 243.5 181 241 181 237.5C181 234 180 231 178 229.5C175.5 227.5 172 226.5 167 226.5L156 226.5L156 248ZM298 271C298 277.5 296 283.5 293 288.5C289.5 293.5 285 297 279.5 300C273.5 302.5 267 304 259.5 304C247.5 304 237.5 300 230.5 292C223.5 284 220 272.5 220 258L220 253.5C220 244.5 221.5 236.5 225 229.5C228 222.5 232.5 217.5 238.5 213.5C244.5 210 251.5 208 259.5 208C270.5 208 279.5 211 286.5 217C293.5 223 297.5 231 298.5 241.5L276 241.5C275.5 236 274.5 232 271.5 229.5C269 227 265 225.5 259.5 225.5C253.5 225.5 249.5 227.5 247 232C244.5 236 243 243 243 252L243 258.5C243 268.5 244 275.5 246.5 280C249 284.5 253.5 286.5 259.5 286.5C264.5 286.5 268.5 285 271.5 282.5C274 280.5 275.5 276.5 276 271L298 271Z\" FillRule=\"NonZero\" />\r\n\t\t\t\t\t\t</Path.Data>\r\n\t\t\t\t\t</Path>\r\n\t\t\t\t</Canvas>\r\n\t\t\t\t<Canvas>\r\n\t\t\t\t\t<Canvas.RenderTransform>\r\n\t\t\t\t\t\t<MatrixTransform Matrix=\"1.27778 0 0 1.27679 0 -39.8571\" />\r\n\t\t\t\t\t</Canvas.RenderTransform>\r\n\t\t\t\t\t<Path\r\n\t\t\t\t\t\txmlns:x=\"http://schemas.microsoft.com/winfx/2006/xaml\"\r\n\t\t\t\t\t\tFill=\"#717F8F\"\r\n\t\t\t\t\t\tStrokeLineJoin=\"Round\"\r\n\t\t\t\t\t\tStrokeMiterLimit=\"2\">\r\n\t\t\t\t\t\t<Path.Data>\r\n\t\t\t\t\t\t\t<PathGeometry Figures=\"M304 144L336 144C339 144 341.5 145 344 146C346.5 145 349 144 352 144L384 144L384 160L352 160L352 352L384 352L384 368L352 368C349 368 346.5 367 344 366C341.5 367 339 368 336 368L304 368L304 352L336 352L336 160L304 160L304 144Z\" FillRule=\"NonZero\" />\r\n\t\t\t\t\t\t</Path.Data>\r\n\t\t\t\t\t</Path>\r\n\t\t\t\t</Canvas>\r\n\t\t\t</Canvas>\r\n\t\t</Canvas>\r\n\t</Viewbox>\r\n\t<Viewbox\r\n\t\txmlns=\"http://schemas.microsoft.com/winfx/2006/xaml/presentation\"\r\n\t\tx:Key=\"Icon_window\"\r\n\t\tx:Shared=\"False\"\r\n\t\tStretch=\"Uniform\"\r\n\t\tTag=\"SHA256_9CDA8C27F026B2F92515E387247A03029C5175F79B9D93C0982A626078879160\">\r\n\t\t<Canvas Width=\"512\" Height=\"512\">\r\n\t\t\t<Canvas.RenderTransform>\r\n\t\t\t\t<TranslateTransform X=\"0\" Y=\"0\" />\r\n\t\t\t</Canvas.RenderTransform>\r\n\t\t\t<Canvas.Resources>\r\n\t\t\t\t<LinearGradientBrush xmlns:x=\"http://schemas.microsoft.com/winfx/2006/xaml\" x:Key=\"color-1\" MappingMode=\"Absolute\" StartPoint=\"0,64\" EndPoint=\"284,331\">\r\n\t\t\t\t\t<LinearGradientBrush.GradientStops>\r\n\t\t\t\t\t\t<GradientStopCollection>\r\n\t\t\t\t\t\t\t<GradientStop Offset=\"0\" Color=\"#40B0F1\" />\r\n\t\t\t\t\t\t\t<GradientStop Offset=\"1\" Color=\"#129EEE\" />\r\n\t\t\t\t\t\t</GradientStopCollection>\r\n\t\t\t\t\t</LinearGradientBrush.GradientStops>\r\n\t\t\t\t</LinearGradientBrush>\r\n\t\t\t\t<LinearGradientBrush xmlns:x=\"http://schemas.microsoft.com/winfx/2006/xaml\" x:Key=\"color-2\" MappingMode=\"Absolute\" StartPoint=\"0,128\" EndPoint=\"154,464\">\r\n\t\t\t\t\t<LinearGradientBrush.GradientStops>\r\n\t\t\t\t\t\t<GradientStopCollection>\r\n\t\t\t\t\t\t\t<GradientStop Offset=\"0\" Color=\"#E1F2FD\" />\r\n\t\t\t\t\t\t\t<GradientStop Offset=\"1\" Color=\"#CCEAFB\" />\r\n\t\t\t\t\t\t</GradientStopCollection>\r\n\t\t\t\t\t</LinearGradientBrush.GradientStops>\r\n\t\t\t\t</LinearGradientBrush>\r\n\t\t\t</Canvas.Resources>\r\n\t\t\t<!--  Unknown tag: metadata  -->\r\n\t\t\t<!--  Unknown tag: sodipodi:namedview  -->\r\n\t\t\t<Canvas>\r\n\t\t\t\t<Path\r\n\t\t\t\t\txmlns:x=\"http://schemas.microsoft.com/winfx/2006/xaml\"\r\n\t\t\t\t\tFill=\"{StaticResource color-1}\"\r\n\t\t\t\t\tStrokeDashArray=\"\"\r\n\t\t\t\t\tStrokeEndLineCap=\"Flat\"\r\n\t\t\t\t\tStrokeLineJoin=\"Miter\"\r\n\t\t\t\t\tStrokeMiterLimit=\"10\"\r\n\t\t\t\t\tStrokeStartLineCap=\"Flat\"\r\n\t\t\t\t\tStrokeThickness=\"1\">\r\n\t\t\t\t\t<Path.Data>\r\n\t\t\t\t\t\t<PathGeometry Figures=\"M512 80l0 384l-512 0l0 -384c0 -9 7 -16 16 -16l480 0c9 0 16 7 16 16z\" FillRule=\"NonZero\" />\r\n\t\t\t\t\t</Path.Data>\r\n\t\t\t\t</Path>\r\n\t\t\t\t<Path\r\n\t\t\t\t\txmlns:x=\"http://schemas.microsoft.com/winfx/2006/xaml\"\r\n\t\t\t\t\tFill=\"{StaticResource color-2}\"\r\n\t\t\t\t\tStrokeDashArray=\"\"\r\n\t\t\t\t\tStrokeEndLineCap=\"Flat\"\r\n\t\t\t\t\tStrokeLineJoin=\"Miter\"\r\n\t\t\t\t\tStrokeMiterLimit=\"10\"\r\n\t\t\t\t\tStrokeStartLineCap=\"Flat\"\r\n\t\t\t\t\tStrokeThickness=\"1\">\r\n\t\t\t\t\t<Path.Data>\r\n\t\t\t\t\t\t<PathGeometry Figures=\"M0 128l512 0l0 336l-512 0z\" FillRule=\"NonZero\" />\r\n\t\t\t\t\t</Path.Data>\r\n\t\t\t\t</Path>\r\n\t\t\t\t<Path\r\n\t\t\t\t\txmlns:x=\"http://schemas.microsoft.com/winfx/2006/xaml\"\r\n\t\t\t\t\tFill=\"#FFFFFFFF\"\r\n\t\t\t\t\tStrokeDashArray=\"\"\r\n\t\t\t\t\tStrokeEndLineCap=\"Flat\"\r\n\t\t\t\t\tStrokeLineJoin=\"Miter\"\r\n\t\t\t\t\tStrokeMiterLimit=\"10\"\r\n\t\t\t\t\tStrokeStartLineCap=\"Flat\"\r\n\t\t\t\t\tStrokeThickness=\"1\">\r\n\t\t\t\t\t<Path.Data>\r\n\t\t\t\t\t\t<PathGeometry Figures=\"M464 80c9 0 16 7 16 16c0 9 -7 16 -16 16c-9 0 -16 -7 -16 -16c0 -9 7 -16 16 -16z M416 80c9 0 16 7 16 16c0 9 -7 16 -16 16c-9 0 -16 -7 -16 -16c0 -9 7 -16 16 -16z\" FillRule=\"NonZero\" />\r\n\t\t\t\t\t</Path.Data>\r\n\t\t\t\t</Path>\r\n\t\t\t</Canvas>\r\n\t\t</Canvas>\r\n\t</Viewbox>\r\n\t<Viewbox\r\n\t\txmlns=\"http://schemas.microsoft.com/winfx/2006/xaml/presentation\"\r\n\t\tx:Key=\"Icon_windows\"\r\n\t\tx:Shared=\"False\"\r\n\t\tStretch=\"Uniform\"\r\n\t\tTag=\"SHA256_41F06478F2878276067D39045ECC1E58151A3DBA1A72F6CCE7A8B77F8C99898E\">\r\n\t\t<Canvas Width=\"512\" Height=\"512\">\r\n\t\t\t<Canvas.RenderTransform>\r\n\t\t\t\t<TranslateTransform X=\"0\" Y=\"0\" />\r\n\t\t\t</Canvas.RenderTransform>\r\n\t\t\t<Canvas.Resources>\r\n\t\t\t\t<LinearGradientBrush xmlns:x=\"http://schemas.microsoft.com/winfx/2006/xaml\" x:Key=\"color-1\" MappingMode=\"Absolute\" StartPoint=\"64,32\" EndPoint=\"321,274\">\r\n\t\t\t\t\t<LinearGradientBrush.GradientStops>\r\n\t\t\t\t\t\t<GradientStopCollection>\r\n\t\t\t\t\t\t\t<GradientStop Offset=\"0\" Color=\"#AAB8C5\" />\r\n\t\t\t\t\t\t\t<GradientStop Offset=\"1\" Color=\"#91A0AF\" />\r\n\t\t\t\t\t\t</GradientStopCollection>\r\n\t\t\t\t\t</LinearGradientBrush.GradientStops>\r\n\t\t\t\t</LinearGradientBrush>\r\n\t\t\t\t<LinearGradientBrush xmlns:x=\"http://schemas.microsoft.com/winfx/2006/xaml\" x:Key=\"color-2\" MappingMode=\"Absolute\" StartPoint=\"64,96\" EndPoint=\"271,400\">\r\n\t\t\t\t\t<LinearGradientBrush.GradientStops>\r\n\t\t\t\t\t\t<GradientStopCollection>\r\n\t\t\t\t\t\t\t<GradientStop Offset=\"0\" Color=\"#E6EBF0\" />\r\n\t\t\t\t\t\t\t<GradientStop Offset=\"1\" Color=\"#DDE4EB\" />\r\n\t\t\t\t\t\t</GradientStopCollection>\r\n\t\t\t\t\t</LinearGradientBrush.GradientStops>\r\n\t\t\t\t</LinearGradientBrush>\r\n\t\t\t\t<LinearGradientBrush xmlns:x=\"http://schemas.microsoft.com/winfx/2006/xaml\" x:Key=\"color-3\" MappingMode=\"Absolute\" StartPoint=\"64,144\" EndPoint=\"238,400\">\r\n\t\t\t\t\t<LinearGradientBrush.GradientStops>\r\n\t\t\t\t\t\t<GradientStopCollection>\r\n\t\t\t\t\t\t\t<GradientStop Offset=\"0\" Color=\"#D2DAE1\" />\r\n\t\t\t\t\t\t\t<GradientStop Offset=\"1\" Color=\"#C3CED8\" />\r\n\t\t\t\t\t\t</GradientStopCollection>\r\n\t\t\t\t\t</LinearGradientBrush.GradientStops>\r\n\t\t\t\t</LinearGradientBrush>\r\n\t\t\t\t<LinearGradientBrush xmlns:x=\"http://schemas.microsoft.com/winfx/2006/xaml\" x:Key=\"color-4\" MappingMode=\"Absolute\" StartPoint=\"0,112\" EndPoint=\"257,354\">\r\n\t\t\t\t\t<LinearGradientBrush.GradientStops>\r\n\t\t\t\t\t\t<GradientStopCollection>\r\n\t\t\t\t\t\t\t<GradientStop Offset=\"0\" Color=\"#3EB0F1\" />\r\n\t\t\t\t\t\t\t<GradientStop Offset=\"1\" Color=\"#129EEE\" />\r\n\t\t\t\t\t\t</GradientStopCollection>\r\n\t\t\t\t\t</LinearGradientBrush.GradientStops>\r\n\t\t\t\t</LinearGradientBrush>\r\n\t\t\t\t<LinearGradientBrush xmlns:x=\"http://schemas.microsoft.com/winfx/2006/xaml\" x:Key=\"color-5\" MappingMode=\"Absolute\" StartPoint=\"0,176\" EndPoint=\"139,480\">\r\n\t\t\t\t\t<LinearGradientBrush.GradientStops>\r\n\t\t\t\t\t\t<GradientStopCollection>\r\n\t\t\t\t\t\t\t<GradientStop Offset=\"0\" Color=\"#E0F2FD\" />\r\n\t\t\t\t\t\t\t<GradientStop Offset=\"1\" Color=\"#CBEAFB\" />\r\n\t\t\t\t\t\t</GradientStopCollection>\r\n\t\t\t\t\t</LinearGradientBrush.GradientStops>\r\n\t\t\t\t</LinearGradientBrush>\r\n\t\t\t</Canvas.Resources>\r\n\t\t\t<!--  Unknown tag: metadata  -->\r\n\t\t\t<!--  Unknown tag: sodipodi:namedview  -->\r\n\t\t\t<Canvas>\r\n\t\t\t\t<Path\r\n\t\t\t\t\txmlns:x=\"http://schemas.microsoft.com/winfx/2006/xaml\"\r\n\t\t\t\t\tFill=\"{StaticResource color-1}\"\r\n\t\t\t\t\tStrokeDashArray=\"\"\r\n\t\t\t\t\tStrokeEndLineCap=\"Flat\"\r\n\t\t\t\t\tStrokeLineJoin=\"Miter\"\r\n\t\t\t\t\tStrokeMiterLimit=\"10\"\r\n\t\t\t\t\tStrokeStartLineCap=\"Flat\"\r\n\t\t\t\t\tStrokeThickness=\"1\">\r\n\t\t\t\t\t<Path.Data>\r\n\t\t\t\t\t\t<PathGeometry Figures=\"M512 48l0 352l-448 0l0 -352c0 -9 7 -16 16 -16l416 0c9 0 16 7 16 16z\" FillRule=\"NonZero\" />\r\n\t\t\t\t\t</Path.Data>\r\n\t\t\t\t</Path>\r\n\t\t\t\t<Path\r\n\t\t\t\t\txmlns:x=\"http://schemas.microsoft.com/winfx/2006/xaml\"\r\n\t\t\t\t\tFill=\"{StaticResource color-2}\"\r\n\t\t\t\t\tStrokeDashArray=\"\"\r\n\t\t\t\t\tStrokeEndLineCap=\"Flat\"\r\n\t\t\t\t\tStrokeLineJoin=\"Miter\"\r\n\t\t\t\t\tStrokeMiterLimit=\"10\"\r\n\t\t\t\t\tStrokeStartLineCap=\"Flat\"\r\n\t\t\t\t\tStrokeThickness=\"1\">\r\n\t\t\t\t\t<Path.Data>\r\n\t\t\t\t\t\t<PathGeometry Figures=\"M64 96l448 0l0 304l-448 0z\" FillRule=\"NonZero\" />\r\n\t\t\t\t\t</Path.Data>\r\n\t\t\t\t</Path>\r\n\t\t\t\t<Path\r\n\t\t\t\t\txmlns:x=\"http://schemas.microsoft.com/winfx/2006/xaml\"\r\n\t\t\t\t\tFill=\"{StaticResource color-3}\"\r\n\t\t\t\t\tStrokeDashArray=\"\"\r\n\t\t\t\t\tStrokeEndLineCap=\"Flat\"\r\n\t\t\t\t\tStrokeLineJoin=\"Miter\"\r\n\t\t\t\t\tStrokeMiterLimit=\"10\"\r\n\t\t\t\t\tStrokeStartLineCap=\"Flat\"\r\n\t\t\t\t\tStrokeThickness=\"1\">\r\n\t\t\t\t\t<Path.Data>\r\n\t\t\t\t\t\t<PathGeometry Figures=\"M464 400l-400 0l0 -256l384 0c9 0 16 7 16 16z\" FillRule=\"NonZero\" />\r\n\t\t\t\t\t</Path.Data>\r\n\t\t\t\t</Path>\r\n\t\t\t\t<Path\r\n\t\t\t\t\txmlns:x=\"http://schemas.microsoft.com/winfx/2006/xaml\"\r\n\t\t\t\t\tFill=\"#FFFFFFFF\"\r\n\t\t\t\t\tStrokeDashArray=\"\"\r\n\t\t\t\t\tStrokeEndLineCap=\"Flat\"\r\n\t\t\t\t\tStrokeLineJoin=\"Miter\"\r\n\t\t\t\t\tStrokeMiterLimit=\"10\"\r\n\t\t\t\t\tStrokeStartLineCap=\"Flat\"\r\n\t\t\t\t\tStrokeThickness=\"1\">\r\n\t\t\t\t\t<Path.Data>\r\n\t\t\t\t\t\t<PathGeometry Figures=\"M464 48c9 0 16 7 16 16c0 9 -7 16 -16 16c-9 0 -16 -7 -16 -16c0 -9 7 -16 16 -16z M416 48c9 0 16 7 16 16c0 9 -7 16 -16 16c-9 0 -16 -7 -16 -16c0 -9 7 -16 16 -16z\" FillRule=\"NonZero\" />\r\n\t\t\t\t\t</Path.Data>\r\n\t\t\t\t</Path>\r\n\t\t\t\t<Path\r\n\t\t\t\t\txmlns:x=\"http://schemas.microsoft.com/winfx/2006/xaml\"\r\n\t\t\t\t\tFill=\"{StaticResource color-4}\"\r\n\t\t\t\t\tStrokeDashArray=\"\"\r\n\t\t\t\t\tStrokeEndLineCap=\"Flat\"\r\n\t\t\t\t\tStrokeLineJoin=\"Miter\"\r\n\t\t\t\t\tStrokeMiterLimit=\"10\"\r\n\t\t\t\t\tStrokeStartLineCap=\"Flat\"\r\n\t\t\t\t\tStrokeThickness=\"1\">\r\n\t\t\t\t\t<Path.Data>\r\n\t\t\t\t\t\t<PathGeometry Figures=\"M448 128l0 352l-448 0l0 -352c0 -9 7 -16 16 -16l416 0c9 0 16 7 16 16z\" FillRule=\"NonZero\" />\r\n\t\t\t\t\t</Path.Data>\r\n\t\t\t\t</Path>\r\n\t\t\t\t<Path\r\n\t\t\t\t\txmlns:x=\"http://schemas.microsoft.com/winfx/2006/xaml\"\r\n\t\t\t\t\tFill=\"{StaticResource color-5}\"\r\n\t\t\t\t\tStrokeDashArray=\"\"\r\n\t\t\t\t\tStrokeEndLineCap=\"Flat\"\r\n\t\t\t\t\tStrokeLineJoin=\"Miter\"\r\n\t\t\t\t\tStrokeMiterLimit=\"10\"\r\n\t\t\t\t\tStrokeStartLineCap=\"Flat\"\r\n\t\t\t\t\tStrokeThickness=\"1\">\r\n\t\t\t\t\t<Path.Data>\r\n\t\t\t\t\t\t<PathGeometry Figures=\"M0 176l448 0l0 304l-448 0z\" FillRule=\"NonZero\" />\r\n\t\t\t\t\t</Path.Data>\r\n\t\t\t\t</Path>\r\n\t\t\t\t<Path\r\n\t\t\t\t\txmlns:x=\"http://schemas.microsoft.com/winfx/2006/xaml\"\r\n\t\t\t\t\tFill=\"#FFFFFFFF\"\r\n\t\t\t\t\tStrokeDashArray=\"\"\r\n\t\t\t\t\tStrokeEndLineCap=\"Flat\"\r\n\t\t\t\t\tStrokeLineJoin=\"Miter\"\r\n\t\t\t\t\tStrokeMiterLimit=\"10\"\r\n\t\t\t\t\tStrokeStartLineCap=\"Flat\"\r\n\t\t\t\t\tStrokeThickness=\"1\">\r\n\t\t\t\t\t<Path.Data>\r\n\t\t\t\t\t\t<PathGeometry Figures=\"M400 128c9 0 16 7 16 16c0 9 -7 16 -16 16c-9 0 -16 -7 -16 -16c0 -9 7 -16 16 -16z M352 128c9 0 16 7 16 16c0 9 -7 16 -16 16c-9 0 -16 -7 -16 -16c0 -9 7 -16 16 -16z\" FillRule=\"NonZero\" />\r\n\t\t\t\t\t</Path.Data>\r\n\t\t\t\t</Path>\r\n\t\t\t</Canvas>\r\n\t\t</Canvas>\r\n\t</Viewbox>\r\n</ResourceDictionary>"
  },
  {
    "path": "FocusLogger/Resources/Icons/Icons_Default.xaml.cs",
    "content": "using System.Windows;\r\n\r\nnamespace JocysCom.FocusLogger.Resources.Icons\r\n{\r\n\tpartial class Icons_Default : ResourceDictionary\r\n\t{\r\n\t\tpublic Icons_Default()\r\n\t\t{\r\n\t\t\tInitializeComponent();\r\n\t\t}\r\n\r\n\t\tpublic static Icons_Default Current => _Current = _Current ?? new Icons_Default();\r\n\t\tprivate static Icons_Default _Current;\r\n\r\n\t\tpublic const string Icon_keyboard = nameof(Icon_keyboard);\r\n\t\tpublic const string Icon_list = nameof(Icon_list);\r\n\t\tpublic const string Icon_mouse2 = nameof(Icon_mouse2);\r\n\t\tpublic const string Icon_text_field = nameof(Icon_text_field);\r\n\t\tpublic const string Icon_window = nameof(Icon_window);\r\n\t\tpublic const string Icon_windows = nameof(Icon_windows);\r\n\r\n\t}\r\n}\r\n"
  },
  {
    "path": "FocusLogger.Tests/CsvExportTests.cs",
    "content": "using JocysCom.FocusLogger;\nusing JocysCom.FocusLogger.Controls;\nusing Microsoft.VisualStudio.TestTools.UnitTesting;\nusing System;\nusing System.IO;\nusing System.Linq;\nusing System.Text;\n\nnamespace JocysCom.FocusLogger.Tests\n{\n\t[TestClass]\n\tpublic class CsvExportTests\n\t{\n\t\t[TestMethod]\n\t\tpublic void CsvEscape_PlainText_ReturnsUnchanged()\n\t\t{\n\t\t\tAssert.AreEqual(\"notepad\", DataListControl.CsvEscape(\"notepad\"));\n\t\t}\n\n\t\t[TestMethod]\n\t\tpublic void CsvEscape_TextWithComma_WrapsInQuotes()\n\t\t{\n\t\t\tAssert.AreEqual(\"\\\"hello,world\\\"\", DataListControl.CsvEscape(\"hello,world\"));\n\t\t}\n\n\t\t[TestMethod]\n\t\tpublic void CsvEscape_TextWithQuotes_EscapesQuotes()\n\t\t{\n\t\t\tAssert.AreEqual(\"\\\"say \\\"\\\"hi\\\"\\\"\\\"\", DataListControl.CsvEscape(\"say \\\"hi\\\"\"));\n\t\t}\n\n\t\t[TestMethod]\n\t\tpublic void CsvEscape_TextWithNewline_WrapsInQuotes()\n\t\t{\n\t\t\tAssert.AreEqual(\"\\\"line1\\nline2\\\"\", DataListControl.CsvEscape(\"line1\\nline2\"));\n\t\t}\n\n\t\t[TestMethod]\n\t\tpublic void CsvEscape_NullOrEmpty_ReturnsEmpty()\n\t\t{\n\t\t\tAssert.AreEqual(\"\", DataListControl.CsvEscape(null));\n\t\t\tAssert.AreEqual(\"\", DataListControl.CsvEscape(\"\"));\n\t\t}\n\n\t\t[TestMethod]\n\t\tpublic void BuildCsvContent_WithItems_ProducesValidCsv()\n\t\t{\n\t\t\tvar items = new[]\n\t\t\t{\n\t\t\t\tnew DataItem\n\t\t\t\t{\n\t\t\t\t\tDate = new DateTime(2026, 4, 4, 10, 30, 0, 123),\n\t\t\t\t\tProcessId = 1234,\n\t\t\t\t\tProcessName = \"notepad\",\n\t\t\t\t\tIsActive = true,\n\t\t\t\t\tHasMouse = false,\n\t\t\t\t\tHasKeyboard = true,\n\t\t\t\t\tHasCaret = true,\n\t\t\t\t\tWindowTitle = \"Untitled - Notepad\",\n\t\t\t\t\tWindowClassName = \"Notepad\",\n\t\t\t\t\tProcessPath = @\"C:\\Windows\\notepad.exe\",\n\t\t\t\t},\n\t\t\t\tnew DataItem\n\t\t\t\t{\n\t\t\t\t\tDate = new DateTime(2026, 4, 4, 10, 30, 1, 456),\n\t\t\t\t\tProcessId = 5678,\n\t\t\t\t\tProcessName = \"explorer\",\n\t\t\t\t\tIsActive = false,\n\t\t\t\t\tHasMouse = true,\n\t\t\t\t\tHasKeyboard = false,\n\t\t\t\t\tHasCaret = false,\n\t\t\t\t\tWindowTitle = \"File, \\\"Explorer\\\"\",\n\t\t\t\t\tWindowClassName = \"CabinetWClass\",\n\t\t\t\t\tProcessPath = @\"C:\\Windows\\explorer.exe\",\n\t\t\t\t},\n\t\t\t};\n\t\t\tvar csv = DataListControl.BuildCsvContent(items);\n\t\t\tvar lines = csv.Split(new[] { Environment.NewLine }, StringSplitOptions.None);\n\t\t\t// Header + 2 data rows + trailing empty line.\n\t\t\tAssert.AreEqual(\"Date,PID,Process Name,Active,Mouse,Keyboard,Caret,Window Title,Window Class,Path\", lines[0]);\n\t\t\tAssert.AreEqual(4, lines.Length);\n\t\t\t// Verify comma/quote in WindowTitle is escaped.\n\t\t\tAssert.IsTrue(lines[2].Contains(\"\\\"File, \\\"\\\"Explorer\\\"\\\"\\\"\"));\n\t\t}\n\n\t\t[TestMethod]\n\t\tpublic void BuildCsvContent_Empty_ReturnsHeaderOnly()\n\t\t{\n\t\t\tvar csv = DataListControl.BuildCsvContent(Array.Empty<DataItem>());\n\t\t\tvar lines = csv.Split(new[] { Environment.NewLine }, StringSplitOptions.RemoveEmptyEntries);\n\t\t\tAssert.AreEqual(1, lines.Length);\n\t\t\tAssert.AreEqual(\"Date,PID,Process Name,Active,Mouse,Keyboard,Caret,Window Title,Window Class,Path\", lines[0]);\n\t\t}\n\t}\n}\n"
  },
  {
    "path": "FocusLogger.Tests/JocysCom.FocusLogger.Tests.csproj",
    "content": "<Project Sdk=\"Microsoft.NET.Sdk\">\n\n  <PropertyGroup>\n    <TargetFramework>net8.0-windows</TargetFramework>\n    <UseWPF>true</UseWPF>\n    <IsPackable>false</IsPackable>\n    <IsTestProject>true</IsTestProject>\n  </PropertyGroup>\n\n  <ItemGroup>\n    <PackageReference Include=\"Microsoft.NET.Test.Sdk\" Version=\"17.*\" />\n    <PackageReference Include=\"MSTest.TestAdapter\" Version=\"3.*\" />\n    <PackageReference Include=\"MSTest.TestFramework\" Version=\"3.*\" />\n  </ItemGroup>\n\n  <ItemGroup>\n    <ProjectReference Include=\"..\\FocusLogger\\JocysCom.FocusLogger.csproj\" />\n  </ItemGroup>\n\n</Project>\n"
  },
  {
    "path": "FocusLogger.Tests/UIAutomationTests.cs",
    "content": "using Microsoft.VisualStudio.TestTools.UnitTesting;\nusing System;\nusing System.Diagnostics;\nusing System.IO;\nusing System.Threading;\nusing System.Windows.Automation;\n\nnamespace JocysCom.FocusLogger.Tests\n{\n\t[TestClass]\n\tpublic class UIAutomationTests\n\t{\n\t\tprivate static string GetAppPath()\n\t\t{\n\t\t\tvar dir = AppDomain.CurrentDomain.BaseDirectory;\n\t\t\t// Navigate from test bin to the main project bin.\n\t\t\tvar appDir = Path.GetFullPath(Path.Combine(dir, \"..\", \"..\", \"..\", \"..\", \"FocusLogger\", \"bin\", \"Debug\", \"net8.0-windows\"));\n\t\t\treturn Path.Combine(appDir, \"JocysCom.FocusLogger.exe\");\n\t\t}\n\n\t\tprivate static AutomationElement FindDescendant(AutomationElement parent, string automationId, int timeoutMs = 5000)\n\t\t{\n\t\t\tvar condition = new PropertyCondition(AutomationElement.AutomationIdProperty, automationId);\n\t\t\tvar sw = Stopwatch.StartNew();\n\t\t\twhile (sw.ElapsedMilliseconds < timeoutMs)\n\t\t\t{\n\t\t\t\tvar element = parent.FindFirst(TreeScope.Descendants, condition);\n\t\t\t\tif (element != null)\n\t\t\t\t\treturn element;\n\t\t\t\tThread.Sleep(200);\n\t\t\t}\n\t\t\treturn null;\n\t\t}\n\n\t\t[TestMethod]\n\t\tpublic void App_LaunchAndDetectNotepadFocus()\n\t\t{\n\t\t\tvar appPath = GetAppPath();\n\t\t\tif (!File.Exists(appPath))\n\t\t\t\tAssert.Inconclusive($\"App not built at: {appPath}. Build the main project first.\");\n\n\t\t\tProcess appProcess = null;\n\t\t\tProcess notepadProcess = null;\n\t\t\ttry\n\t\t\t{\n\t\t\t\t// Launch FocusLogger.\n\t\t\t\tappProcess = Process.Start(new ProcessStartInfo(appPath) { UseShellExecute = true });\n\t\t\t\tThread.Sleep(2000);\n\n\t\t\t\t// Find the main window via UI Automation.\n\t\t\t\tvar mainWindow = FindMainWindow(appProcess.Id);\n\t\t\t\tAssert.IsNotNull(mainWindow, \"FocusLogger main window not found.\");\n\n\t\t\t\t// Find the DataGrid.\n\t\t\t\tvar dataGrid = FindDescendant(mainWindow, \"MainDataGrid\");\n\t\t\t\tAssert.IsNotNull(dataGrid, \"MainDataGrid not found.\");\n\n\t\t\t\t// Launch Notepad to trigger a focus change.\n\t\t\t\tnotepadProcess = Process.Start(\"notepad.exe\");\n\t\t\t\tThread.Sleep(2000);\n\n\t\t\t\t// Bring FocusLogger back to check the grid.\n\t\t\t\tnotepadProcess.Kill();\n\t\t\t\tnotepadProcess.WaitForExit();\n\t\t\t\tnotepadProcess = null;\n\t\t\t\tThread.Sleep(1000);\n\n\t\t\t\t// Verify the DataGrid has rows (focus events were logged).\n\t\t\t\tvar gridPattern = dataGrid.GetCurrentPattern(GridPattern.Pattern) as GridPattern;\n\t\t\t\tAssert.IsNotNull(gridPattern, \"DataGrid does not support GridPattern.\");\n\t\t\t\tAssert.IsTrue(gridPattern.Current.RowCount > 0, \"DataGrid should have logged focus change events.\");\n\n\t\t\t\t// Verify Clear button works.\n\t\t\t\tvar clearButton = FindDescendant(mainWindow, \"ClearButton\");\n\t\t\t\tAssert.IsNotNull(clearButton, \"ClearButton not found.\");\n\t\t\t\tvar invokePattern = clearButton.GetCurrentPattern(InvokePattern.Pattern) as InvokePattern;\n\t\t\t\tinvokePattern.Invoke();\n\t\t\t\tThread.Sleep(500);\n\t\t\t\tAssert.AreEqual(0, gridPattern.Current.RowCount, \"DataGrid should be empty after Clear.\");\n\t\t\t}\n\t\t\tfinally\n\t\t\t{\n\t\t\t\tnotepadProcess?.Kill();\n\t\t\t\tif (appProcess != null && !appProcess.HasExited)\n\t\t\t\t{\n\t\t\t\t\tappProcess.Kill();\n\t\t\t\t\tappProcess.WaitForExit();\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\n\t\tprivate static AutomationElement FindMainWindow(int processId, int timeoutMs = 10000)\n\t\t{\n\t\t\tvar desktop = AutomationElement.RootElement;\n\t\t\tvar sw = Stopwatch.StartNew();\n\t\t\twhile (sw.ElapsedMilliseconds < timeoutMs)\n\t\t\t{\n\t\t\t\tvar condition = new AndCondition(\n\t\t\t\t\tnew PropertyCondition(AutomationElement.ProcessIdProperty, processId),\n\t\t\t\t\tnew PropertyCondition(AutomationElement.ControlTypeProperty, ControlType.Window)\n\t\t\t\t);\n\t\t\t\tvar window = desktop.FindFirst(TreeScope.Children, condition);\n\t\t\t\tif (window != null)\n\t\t\t\t\treturn window;\n\t\t\t\tThread.Sleep(300);\n\t\t\t}\n\t\t\treturn null;\n\t\t}\n\t}\n}\n"
  },
  {
    "path": "JocysCom.FocusLogger.slnx",
    "content": "<Solution>\r\n  <Project Path=\"FocusLogger/JocysCom.FocusLogger.csproj\" />\r\n  <Project Path=\"FocusLogger.Tests/JocysCom.FocusLogger.Tests.csproj\" />\r\n</Solution>\r\n"
  },
  {
    "path": "LICENSE",
    "content": "                    GNU GENERAL PUBLIC LICENSE\n                       Version 3, 29 June 2007\n\n Copyright (C) 2007 Free Software Foundation, Inc. <https://fsf.org/>\n Everyone is permitted to copy and distribute verbatim copies\n of this license document, but changing it is not allowed.\n\n                            Preamble\n\n  The GNU General Public License is a free, copyleft license for\nsoftware and other kinds of works.\n\n  The licenses for most software and other practical works are designed\nto take away your freedom to share and change the works.  By contrast,\nthe GNU General Public License is intended to guarantee your freedom to\nshare and change all versions of a program--to make sure it remains free\nsoftware for all its users.  We, the Free Software Foundation, use the\nGNU General Public License for most of our software; it applies also to\nany other work released this way by its authors.  You can apply it to\nyour programs, too.\n\n  When we speak of free software, we are referring to freedom, not\nprice.  Our General Public Licenses are designed to make sure that you\nhave the freedom to distribute copies of free software (and charge for\nthem if you wish), that you receive source code or can get it if you\nwant it, that you can change the software or use pieces of it in new\nfree programs, and that you know you can do these things.\n\n  To protect your rights, we need to prevent others from denying you\nthese rights or asking you to surrender the rights.  Therefore, you have\ncertain responsibilities if you distribute copies of the software, or if\nyou modify it: responsibilities to respect the freedom of others.\n\n  For example, if you distribute copies of such a program, whether\ngratis or for a fee, you must pass on to the recipients the same\nfreedoms that you received.  You must make sure that they, too, receive\nor can get the source code.  And you must show them these terms so they\nknow their rights.\n\n  Developers that use the GNU GPL protect your rights with two steps:\n(1) assert copyright on the software, and (2) offer you this License\ngiving you legal permission to copy, distribute and/or modify it.\n\n  For the developers' and authors' protection, the GPL clearly explains\nthat there is no warranty for this free software.  For both users' and\nauthors' sake, the GPL requires that modified versions be marked as\nchanged, so that their problems will not be attributed erroneously to\nauthors of previous versions.\n\n  Some devices are designed to deny users access to install or run\nmodified versions of the software inside them, although the manufacturer\ncan do so.  This is fundamentally incompatible with the aim of\nprotecting users' freedom to change the software.  The systematic\npattern of such abuse occurs in the area of products for individuals to\nuse, which is precisely where it is most unacceptable.  Therefore, we\nhave designed this version of the GPL to prohibit the practice for those\nproducts.  If such problems arise substantially in other domains, we\nstand ready to extend this provision to those domains in future versions\nof the GPL, as needed to protect the freedom of users.\n\n  Finally, every program is threatened constantly by software patents.\nStates should not allow patents to restrict development and use of\nsoftware on general-purpose computers, but in those that do, we wish to\navoid the special danger that patents applied to a free program could\nmake it effectively proprietary.  To prevent this, the GPL assures that\npatents cannot be used to render the program non-free.\n\n  The precise terms and conditions for copying, distribution and\nmodification follow.\n\n                       TERMS AND CONDITIONS\n\n  0. Definitions.\n\n  \"This License\" refers to version 3 of the GNU General Public License.\n\n  \"Copyright\" also means copyright-like laws that apply to other kinds of\nworks, such as semiconductor masks.\n\n  \"The Program\" refers to any copyrightable work licensed under this\nLicense.  Each licensee is addressed as \"you\".  \"Licensees\" and\n\"recipients\" may be individuals or organizations.\n\n  To \"modify\" a work means to copy from or adapt all or part of the work\nin a fashion requiring copyright permission, other than the making of an\nexact copy.  The resulting work is called a \"modified version\" of the\nearlier work or a work \"based on\" the earlier work.\n\n  A \"covered work\" means either the unmodified Program or a work based\non the Program.\n\n  To \"propagate\" a work means to do anything with it that, without\npermission, would make you directly or secondarily liable for\ninfringement under applicable copyright law, except executing it on a\ncomputer or modifying a private copy.  Propagation includes copying,\ndistribution (with or without modification), making available to the\npublic, and in some countries other activities as well.\n\n  To \"convey\" a work means any kind of propagation that enables other\nparties to make or receive copies.  Mere interaction with a user through\na computer network, with no transfer of a copy, is not conveying.\n\n  An interactive user interface displays \"Appropriate Legal Notices\"\nto the extent that it includes a convenient and prominently visible\nfeature that (1) displays an appropriate copyright notice, and (2)\ntells the user that there is no warranty for the work (except to the\nextent that warranties are provided), that licensees may convey the\nwork under this License, and how to view a copy of this License.  If\nthe interface presents a list of user commands or options, such as a\nmenu, a prominent item in the list meets this criterion.\n\n  1. Source Code.\n\n  The \"source code\" for a work means the preferred form of the work\nfor making modifications to it.  \"Object code\" means any non-source\nform of a work.\n\n  A \"Standard Interface\" means an interface that either is an official\nstandard defined by a recognized standards body, or, in the case of\ninterfaces specified for a particular programming language, one that\nis widely used among developers working in that language.\n\n  The \"System Libraries\" of an executable work include anything, other\nthan the work as a whole, that (a) is included in the normal form of\npackaging a Major Component, but which is not part of that Major\nComponent, and (b) serves only to enable use of the work with that\nMajor Component, or to implement a Standard Interface for which an\nimplementation is available to the public in source code form.  A\n\"Major Component\", in this context, means a major essential component\n(kernel, window system, and so on) of the specific operating system\n(if any) on which the executable work runs, or a compiler used to\nproduce the work, or an object code interpreter used to run it.\n\n  The \"Corresponding Source\" for a work in object code form means all\nthe source code needed to generate, install, and (for an executable\nwork) run the object code and to modify the work, including scripts to\ncontrol those activities.  However, it does not include the work's\nSystem Libraries, or general-purpose tools or generally available free\nprograms which are used unmodified in performing those activities but\nwhich are not part of the work.  For example, Corresponding Source\nincludes interface definition files associated with source files for\nthe work, and the source code for shared libraries and dynamically\nlinked subprograms that the work is specifically designed to require,\nsuch as by intimate data communication or control flow between those\nsubprograms and other parts of the work.\n\n  The Corresponding Source need not include anything that users\ncan regenerate automatically from other parts of the Corresponding\nSource.\n\n  The Corresponding Source for a work in source code form is that\nsame work.\n\n  2. Basic Permissions.\n\n  All rights granted under this License are granted for the term of\ncopyright on the Program, and are irrevocable provided the stated\nconditions are met.  This License explicitly affirms your unlimited\npermission to run the unmodified Program.  The output from running a\ncovered work is covered by this License only if the output, given its\ncontent, constitutes a covered work.  This License acknowledges your\nrights of fair use or other equivalent, as provided by copyright law.\n\n  You may make, run and propagate covered works that you do not\nconvey, without conditions so long as your license otherwise remains\nin force.  You may convey covered works to others for the sole purpose\nof having them make modifications exclusively for you, or provide you\nwith facilities for running those works, provided that you comply with\nthe terms of this License in conveying all material for which you do\nnot control copyright.  Those thus making or running the covered works\nfor you must do so exclusively on your behalf, under your direction\nand control, on terms that prohibit them from making any copies of\nyour copyrighted material outside their relationship with you.\n\n  Conveying under any other circumstances is permitted solely under\nthe conditions stated below.  Sublicensing is not allowed; section 10\nmakes it unnecessary.\n\n  3. Protecting Users' Legal Rights From Anti-Circumvention Law.\n\n  No covered work shall be deemed part of an effective technological\nmeasure under any applicable law fulfilling obligations under article\n11 of the WIPO copyright treaty adopted on 20 December 1996, or\nsimilar laws prohibiting or restricting circumvention of such\nmeasures.\n\n  When you convey a covered work, you waive any legal power to forbid\ncircumvention of technological measures to the extent such circumvention\nis effected by exercising rights under this License with respect to\nthe covered work, and you disclaim any intention to limit operation or\nmodification of the work as a means of enforcing, against the work's\nusers, your or third parties' legal rights to forbid circumvention of\ntechnological measures.\n\n  4. Conveying Verbatim Copies.\n\n  You may convey verbatim copies of the Program's source code as you\nreceive it, in any medium, provided that you conspicuously and\nappropriately publish on each copy an appropriate copyright notice;\nkeep intact all notices stating that this License and any\nnon-permissive terms added in accord with section 7 apply to the code;\nkeep intact all notices of the absence of any warranty; and give all\nrecipients a copy of this License along with the Program.\n\n  You may charge any price or no price for each copy that you convey,\nand you may offer support or warranty protection for a fee.\n\n  5. Conveying Modified Source Versions.\n\n  You may convey a work based on the Program, or the modifications to\nproduce it from the Program, in the form of source code under the\nterms of section 4, provided that you also meet all of these conditions:\n\n    a) The work must carry prominent notices stating that you modified\n    it, and giving a relevant date.\n\n    b) The work must carry prominent notices stating that it is\n    released under this License and any conditions added under section\n    7.  This requirement modifies the requirement in section 4 to\n    \"keep intact all notices\".\n\n    c) You must license the entire work, as a whole, under this\n    License to anyone who comes into possession of a copy.  This\n    License will therefore apply, along with any applicable section 7\n    additional terms, to the whole of the work, and all its parts,\n    regardless of how they are packaged.  This License gives no\n    permission to license the work in any other way, but it does not\n    invalidate such permission if you have separately received it.\n\n    d) If the work has interactive user interfaces, each must display\n    Appropriate Legal Notices; however, if the Program has interactive\n    interfaces that do not display Appropriate Legal Notices, your\n    work need not make them do so.\n\n  A compilation of a covered work with other separate and independent\nworks, which are not by their nature extensions of the covered work,\nand which are not combined with it such as to form a larger program,\nin or on a volume of a storage or distribution medium, is called an\n\"aggregate\" if the compilation and its resulting copyright are not\nused to limit the access or legal rights of the compilation's users\nbeyond what the individual works permit.  Inclusion of a covered work\nin an aggregate does not cause this License to apply to the other\nparts of the aggregate.\n\n  6. Conveying Non-Source Forms.\n\n  You may convey a covered work in object code form under the terms\nof sections 4 and 5, provided that you also convey the\nmachine-readable Corresponding Source under the terms of this License,\nin one of these ways:\n\n    a) Convey the object code in, or embodied in, a physical product\n    (including a physical distribution medium), accompanied by the\n    Corresponding Source fixed on a durable physical medium\n    customarily used for software interchange.\n\n    b) Convey the object code in, or embodied in, a physical product\n    (including a physical distribution medium), accompanied by a\n    written offer, valid for at least three years and valid for as\n    long as you offer spare parts or customer support for that product\n    model, to give anyone who possesses the object code either (1) a\n    copy of the Corresponding Source for all the software in the\n    product that is covered by this License, on a durable physical\n    medium customarily used for software interchange, for a price no\n    more than your reasonable cost of physically performing this\n    conveying of source, or (2) access to copy the\n    Corresponding Source from a network server at no charge.\n\n    c) Convey individual copies of the object code with a copy of the\n    written offer to provide the Corresponding Source.  This\n    alternative is allowed only occasionally and noncommercially, and\n    only if you received the object code with such an offer, in accord\n    with subsection 6b.\n\n    d) Convey the object code by offering access from a designated\n    place (gratis or for a charge), and offer equivalent access to the\n    Corresponding Source in the same way through the same place at no\n    further charge.  You need not require recipients to copy the\n    Corresponding Source along with the object code.  If the place to\n    copy the object code is a network server, the Corresponding Source\n    may be on a different server (operated by you or a third party)\n    that supports equivalent copying facilities, provided you maintain\n    clear directions next to the object code saying where to find the\n    Corresponding Source.  Regardless of what server hosts the\n    Corresponding Source, you remain obligated to ensure that it is\n    available for as long as needed to satisfy these requirements.\n\n    e) Convey the object code using peer-to-peer transmission, provided\n    you inform other peers where the object code and Corresponding\n    Source of the work are being offered to the general public at no\n    charge under subsection 6d.\n\n  A separable portion of the object code, whose source code is excluded\nfrom the Corresponding Source as a System Library, need not be\nincluded in conveying the object code work.\n\n  A \"User Product\" is either (1) a \"consumer product\", which means any\ntangible personal property which is normally used for personal, family,\nor household purposes, or (2) anything designed or sold for incorporation\ninto a dwelling.  In determining whether a product is a consumer product,\ndoubtful cases shall be resolved in favor of coverage.  For a particular\nproduct received by a particular user, \"normally used\" refers to a\ntypical or common use of that class of product, regardless of the status\nof the particular user or of the way in which the particular user\nactually uses, or expects or is expected to use, the product.  A product\nis a consumer product regardless of whether the product has substantial\ncommercial, industrial or non-consumer uses, unless such uses represent\nthe only significant mode of use of the product.\n\n  \"Installation Information\" for a User Product means any methods,\nprocedures, authorization keys, or other information required to install\nand execute modified versions of a covered work in that User Product from\na modified version of its Corresponding Source.  The information must\nsuffice to ensure that the continued functioning of the modified object\ncode is in no case prevented or interfered with solely because\nmodification has been made.\n\n  If you convey an object code work under this section in, or with, or\nspecifically for use in, a User Product, and the conveying occurs as\npart of a transaction in which the right of possession and use of the\nUser Product is transferred to the recipient in perpetuity or for a\nfixed term (regardless of how the transaction is characterized), the\nCorresponding Source conveyed under this section must be accompanied\nby the Installation Information.  But this requirement does not apply\nif neither you nor any third party retains the ability to install\nmodified object code on the User Product (for example, the work has\nbeen installed in ROM).\n\n  The requirement to provide Installation Information does not include a\nrequirement to continue to provide support service, warranty, or updates\nfor a work that has been modified or installed by the recipient, or for\nthe User Product in which it has been modified or installed.  Access to a\nnetwork may be denied when the modification itself materially and\nadversely affects the operation of the network or violates the rules and\nprotocols for communication across the network.\n\n  Corresponding Source conveyed, and Installation Information provided,\nin accord with this section must be in a format that is publicly\ndocumented (and with an implementation available to the public in\nsource code form), and must require no special password or key for\nunpacking, reading or copying.\n\n  7. Additional Terms.\n\n  \"Additional permissions\" are terms that supplement the terms of this\nLicense by making exceptions from one or more of its conditions.\nAdditional permissions that are applicable to the entire Program shall\nbe treated as though they were included in this License, to the extent\nthat they are valid under applicable law.  If additional permissions\napply only to part of the Program, that part may be used separately\nunder those permissions, but the entire Program remains governed by\nthis License without regard to the additional permissions.\n\n  When you convey a copy of a covered work, you may at your option\nremove any additional permissions from that copy, or from any part of\nit.  (Additional permissions may be written to require their own\nremoval in certain cases when you modify the work.)  You may place\nadditional permissions on material, added by you to a covered work,\nfor which you have or can give appropriate copyright permission.\n\n  Notwithstanding any other provision of this License, for material you\nadd to a covered work, you may (if authorized by the copyright holders of\nthat material) supplement the terms of this License with terms:\n\n    a) Disclaiming warranty or limiting liability differently from the\n    terms of sections 15 and 16 of this License; or\n\n    b) Requiring preservation of specified reasonable legal notices or\n    author attributions in that material or in the Appropriate Legal\n    Notices displayed by works containing it; or\n\n    c) Prohibiting misrepresentation of the origin of that material, or\n    requiring that modified versions of such material be marked in\n    reasonable ways as different from the original version; or\n\n    d) Limiting the use for publicity purposes of names of licensors or\n    authors of the material; or\n\n    e) Declining to grant rights under trademark law for use of some\n    trade names, trademarks, or service marks; or\n\n    f) Requiring indemnification of licensors and authors of that\n    material by anyone who conveys the material (or modified versions of\n    it) with contractual assumptions of liability to the recipient, for\n    any liability that these contractual assumptions directly impose on\n    those licensors and authors.\n\n  All other non-permissive additional terms are considered \"further\nrestrictions\" within the meaning of section 10.  If the Program as you\nreceived it, or any part of it, contains a notice stating that it is\ngoverned by this License along with a term that is a further\nrestriction, you may remove that term.  If a license document contains\na further restriction but permits relicensing or conveying under this\nLicense, you may add to a covered work material governed by the terms\nof that license document, provided that the further restriction does\nnot survive such relicensing or conveying.\n\n  If you add terms to a covered work in accord with this section, you\nmust place, in the relevant source files, a statement of the\nadditional terms that apply to those files, or a notice indicating\nwhere to find the applicable terms.\n\n  Additional terms, permissive or non-permissive, may be stated in the\nform of a separately written license, or stated as exceptions;\nthe above requirements apply either way.\n\n  8. Termination.\n\n  You may not propagate or modify a covered work except as expressly\nprovided under this License.  Any attempt otherwise to propagate or\nmodify it is void, and will automatically terminate your rights under\nthis License (including any patent licenses granted under the third\nparagraph of section 11).\n\n  However, if you cease all violation of this License, then your\nlicense from a particular copyright holder is reinstated (a)\nprovisionally, unless and until the copyright holder explicitly and\nfinally terminates your license, and (b) permanently, if the copyright\nholder fails to notify you of the violation by some reasonable means\nprior to 60 days after the cessation.\n\n  Moreover, your license from a particular copyright holder is\nreinstated permanently if the copyright holder notifies you of the\nviolation by some reasonable means, this is the first time you have\nreceived notice of violation of this License (for any work) from that\ncopyright holder, and you cure the violation prior to 30 days after\nyour receipt of the notice.\n\n  Termination of your rights under this section does not terminate the\nlicenses of parties who have received copies or rights from you under\nthis License.  If your rights have been terminated and not permanently\nreinstated, you do not qualify to receive new licenses for the same\nmaterial under section 10.\n\n  9. Acceptance Not Required for Having Copies.\n\n  You are not required to accept this License in order to receive or\nrun a copy of the Program.  Ancillary propagation of a covered work\noccurring solely as a consequence of using peer-to-peer transmission\nto receive a copy likewise does not require acceptance.  However,\nnothing other than this License grants you permission to propagate or\nmodify any covered work.  These actions infringe copyright if you do\nnot accept this License.  Therefore, by modifying or propagating a\ncovered work, you indicate your acceptance of this License to do so.\n\n  10. Automatic Licensing of Downstream Recipients.\n\n  Each time you convey a covered work, the recipient automatically\nreceives a license from the original licensors, to run, modify and\npropagate that work, subject to this License.  You are not responsible\nfor enforcing compliance by third parties with this License.\n\n  An \"entity transaction\" is a transaction transferring control of an\norganization, or substantially all assets of one, or subdividing an\norganization, or merging organizations.  If propagation of a covered\nwork results from an entity transaction, each party to that\ntransaction who receives a copy of the work also receives whatever\nlicenses to the work the party's predecessor in interest had or could\ngive under the previous paragraph, plus a right to possession of the\nCorresponding Source of the work from the predecessor in interest, if\nthe predecessor has it or can get it with reasonable efforts.\n\n  You may not impose any further restrictions on the exercise of the\nrights granted or affirmed under this License.  For example, you may\nnot impose a license fee, royalty, or other charge for exercise of\nrights granted under this License, and you may not initiate litigation\n(including a cross-claim or counterclaim in a lawsuit) alleging that\nany patent claim is infringed by making, using, selling, offering for\nsale, or importing the Program or any portion of it.\n\n  11. Patents.\n\n  A \"contributor\" is a copyright holder who authorizes use under this\nLicense of the Program or a work on which the Program is based.  The\nwork thus licensed is called the contributor's \"contributor version\".\n\n  A contributor's \"essential patent claims\" are all patent claims\nowned or controlled by the contributor, whether already acquired or\nhereafter acquired, that would be infringed by some manner, permitted\nby this License, of making, using, or selling its contributor version,\nbut do not include claims that would be infringed only as a\nconsequence of further modification of the contributor version.  For\npurposes of this definition, \"control\" includes the right to grant\npatent sublicenses in a manner consistent with the requirements of\nthis License.\n\n  Each contributor grants you a non-exclusive, worldwide, royalty-free\npatent license under the contributor's essential patent claims, to\nmake, use, sell, offer for sale, import and otherwise run, modify and\npropagate the contents of its contributor version.\n\n  In the following three paragraphs, a \"patent license\" is any express\nagreement or commitment, however denominated, not to enforce a patent\n(such as an express permission to practice a patent or covenant not to\nsue for patent infringement).  To \"grant\" such a patent license to a\nparty means to make such an agreement or commitment not to enforce a\npatent against the party.\n\n  If you convey a covered work, knowingly relying on a patent license,\nand the Corresponding Source of the work is not available for anyone\nto copy, free of charge and under the terms of this License, through a\npublicly available network server or other readily accessible means,\nthen you must either (1) cause the Corresponding Source to be so\navailable, or (2) arrange to deprive yourself of the benefit of the\npatent license for this particular work, or (3) arrange, in a manner\nconsistent with the requirements of this License, to extend the patent\nlicense to downstream recipients.  \"Knowingly relying\" means you have\nactual knowledge that, but for the patent license, your conveying the\ncovered work in a country, or your recipient's use of the covered work\nin a country, would infringe one or more identifiable patents in that\ncountry that you have reason to believe are valid.\n\n  If, pursuant to or in connection with a single transaction or\narrangement, you convey, or propagate by procuring conveyance of, a\ncovered work, and grant a patent license to some of the parties\nreceiving the covered work authorizing them to use, propagate, modify\nor convey a specific copy of the covered work, then the patent license\nyou grant is automatically extended to all recipients of the covered\nwork and works based on it.\n\n  A patent license is \"discriminatory\" if it does not include within\nthe scope of its coverage, prohibits the exercise of, or is\nconditioned on the non-exercise of one or more of the rights that are\nspecifically granted under this License.  You may not convey a covered\nwork if you are a party to an arrangement with a third party that is\nin the business of distributing software, under which you make payment\nto the third party based on the extent of your activity of conveying\nthe work, and under which the third party grants, to any of the\nparties who would receive the covered work from you, a discriminatory\npatent license (a) in connection with copies of the covered work\nconveyed by you (or copies made from those copies), or (b) primarily\nfor and in connection with specific products or compilations that\ncontain the covered work, unless you entered into that arrangement,\nor that patent license was granted, prior to 28 March 2007.\n\n  Nothing in this License shall be construed as excluding or limiting\nany implied license or other defenses to infringement that may\notherwise be available to you under applicable patent law.\n\n  12. No Surrender of Others' Freedom.\n\n  If conditions are imposed on you (whether by court order, agreement or\notherwise) that contradict the conditions of this License, they do not\nexcuse you from the conditions of this License.  If you cannot convey a\ncovered work so as to satisfy simultaneously your obligations under this\nLicense and any other pertinent obligations, then as a consequence you may\nnot convey it at all.  For example, if you agree to terms that obligate you\nto collect a royalty for further conveying from those to whom you convey\nthe Program, the only way you could satisfy both those terms and this\nLicense would be to refrain entirely from conveying the Program.\n\n  13. Use with the GNU Affero General Public License.\n\n  Notwithstanding any other provision of this License, you have\npermission to link or combine any covered work with a work licensed\nunder version 3 of the GNU Affero General Public License into a single\ncombined work, and to convey the resulting work.  The terms of this\nLicense will continue to apply to the part which is the covered work,\nbut the special requirements of the GNU Affero General Public License,\nsection 13, concerning interaction through a network will apply to the\ncombination as such.\n\n  14. Revised Versions of this License.\n\n  The Free Software Foundation may publish revised and/or new versions of\nthe GNU General Public License from time to time.  Such new versions will\nbe similar in spirit to the present version, but may differ in detail to\naddress new problems or concerns.\n\n  Each version is given a distinguishing version number.  If the\nProgram specifies that a certain numbered version of the GNU General\nPublic License \"or any later version\" applies to it, you have the\noption of following the terms and conditions either of that numbered\nversion or of any later version published by the Free Software\nFoundation.  If the Program does not specify a version number of the\nGNU General Public License, you may choose any version ever published\nby the Free Software Foundation.\n\n  If the Program specifies that a proxy can decide which future\nversions of the GNU General Public License can be used, that proxy's\npublic statement of acceptance of a version permanently authorizes you\nto choose that version for the Program.\n\n  Later license versions may give you additional or different\npermissions.  However, no additional obligations are imposed on any\nauthor or copyright holder as a result of your choosing to follow a\nlater version.\n\n  15. Disclaimer of Warranty.\n\n  THERE IS NO WARRANTY FOR THE PROGRAM, TO THE EXTENT PERMITTED BY\nAPPLICABLE LAW.  EXCEPT WHEN OTHERWISE STATED IN WRITING THE COPYRIGHT\nHOLDERS AND/OR OTHER PARTIES PROVIDE THE PROGRAM \"AS IS\" WITHOUT WARRANTY\nOF ANY KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING, BUT NOT LIMITED TO,\nTHE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR\nPURPOSE.  THE ENTIRE RISK AS TO THE QUALITY AND PERFORMANCE OF THE PROGRAM\nIS WITH YOU.  SHOULD THE PROGRAM PROVE DEFECTIVE, YOU ASSUME THE COST OF\nALL NECESSARY SERVICING, REPAIR OR CORRECTION.\n\n  16. Limitation of Liability.\n\n  IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN WRITING\nWILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MODIFIES AND/OR CONVEYS\nTHE PROGRAM AS PERMITTED ABOVE, BE LIABLE TO YOU FOR DAMAGES, INCLUDING ANY\nGENERAL, SPECIAL, INCIDENTAL OR CONSEQUENTIAL DAMAGES ARISING OUT OF THE\nUSE OR INABILITY TO USE THE PROGRAM (INCLUDING BUT NOT LIMITED TO LOSS OF\nDATA OR DATA BEING RENDERED INACCURATE OR LOSSES SUSTAINED BY YOU OR THIRD\nPARTIES OR A FAILURE OF THE PROGRAM TO OPERATE WITH ANY OTHER PROGRAMS),\nEVEN IF SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE POSSIBILITY OF\nSUCH DAMAGES.\n\n  17. Interpretation of Sections 15 and 16.\n\n  If the disclaimer of warranty and limitation of liability provided\nabove cannot be given local legal effect according to their terms,\nreviewing courts shall apply local law that most closely approximates\nan absolute waiver of all civil liability in connection with the\nProgram, unless a warranty or assumption of liability accompanies a\ncopy of the Program in return for a fee.\n\n                     END OF TERMS AND CONDITIONS\n\n            How to Apply These Terms to Your New Programs\n\n  If you develop a new program, and you want it to be of the greatest\npossible use to the public, the best way to achieve this is to make it\nfree software which everyone can redistribute and change under these terms.\n\n  To do so, attach the following notices to the program.  It is safest\nto attach them to the start of each source file to most effectively\nstate the exclusion of warranty; and each file should have at least\nthe \"copyright\" line and a pointer to where the full notice is found.\n\n    <one line to give the program's name and a brief idea of what it does.>\n    Copyright (C) <year>  <name of author>\n\n    This program is free software: you can redistribute it and/or modify\n    it under the terms of the GNU General Public License as published by\n    the Free Software Foundation, either version 3 of the License, or\n    (at your option) any later version.\n\n    This program is distributed in the hope that it will be useful,\n    but WITHOUT ANY WARRANTY; without even the implied warranty of\n    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\n    GNU General Public License for more details.\n\n    You should have received a copy of the GNU General Public License\n    along with this program.  If not, see <https://www.gnu.org/licenses/>.\n\nAlso add information on how to contact you by electronic and paper mail.\n\n  If the program does terminal interaction, make it output a short\nnotice like this when it starts in an interactive mode:\n\n    <program>  Copyright (C) <year>  <name of author>\n    This program comes with ABSOLUTELY NO WARRANTY; for details type `show w'.\n    This is free software, and you are welcome to redistribute it\n    under certain conditions; type `show c' for details.\n\nThe hypothetical commands `show w' and `show c' should show the appropriate\nparts of the General Public License.  Of course, your program's commands\nmight be different; for a GUI interface, you would use an \"about box\".\n\n  You should also get your employer (if you work as a programmer) or school,\nif any, to sign a \"copyright disclaimer\" for the program, if necessary.\nFor more information on this, and how to apply and follow the GNU GPL, see\n<https://www.gnu.org/licenses/>.\n\n  The GNU General Public License does not permit incorporating your program\ninto proprietary programs.  If your program is a subroutine library, you\nmay consider it more useful to permit linking proprietary applications with\nthe library.  If this is what you want to do, use the GNU Lesser General\nPublic License instead of this License.  But first, please read\n<https://www.gnu.org/licenses/why-not-lgpl.html>.\n"
  },
  {
    "path": "README.md",
    "content": "# Jocys.com Focus Logger\n\nFind out which process or program is taking the window focus.\n\nIn game, mouse and keyboard could start temporary stop responding if other program takes the focus. This tools could help to find out which program steals the focus.\n\n# Download\n\nDigitally Signed Application v1.2.6 (2026-04-04)\n\n[Download - JocysCom.FocusLogger.zip](https://github.com/JocysCom/FocusLogger/releases/download/1.2.6/JocysCom.FocusLogger.zip)\n\n# System Requirements\n\n- Microsoft Windows 10 or newer.\n\n  https://github.com/dotnet/core/blob/main/release-notes/8.0/supported-os.md\n- Microsoft .NET 8.0 (included with Windows Updates).\n\n  https://dotnet.microsoft.com/download/dotnet\n\n## Screenshots\n\n<img alt=\"Main From\" src=\"Documents/Images/JocysCom.FocusLogger.png\" width=\"700\" height=\"480\">\n\n"
  },
  {
    "path": "Resources/ZipFiles.ps1",
    "content": "param (\r\n    [Parameter(Mandatory = $true, Position = 0)]\r\n    [string] $sourceDir,\r\n\r\n    [Parameter(Mandatory = $true, Position = 1)]\r\n    [string] $destFile,\r\n    \r\n    # Optional. The search string to match against the names of files.\r\n    [Parameter(Mandatory = $false, Position = 2)]\r\n    [string] $searchPattern,\r\n\r\n    # Optional. The pattern to exclude files from the zip.\r\n    [Parameter(Mandatory = $false, Position = 3)]\r\n    [string] $excludePattern,\r\n\r\n    # Optional. Use shell zipper if this parameter is set to true.\r\n    [Parameter(Mandatory = $false, Position = 4)]\r\n    [bool] $UseShellToZipFiles = $false,\r\n\r\n    # Optional. Use comment for console.\r\n    [Parameter(Mandatory = $false, Position = 5)]\r\n    [string] $LogPrefix = \"\",\r\n\r\n    # Optional. Ignore empty folders when creating the zip file.\r\n    [Parameter(Mandatory = $false, Position = 6)]\r\n    $IgnoreEmptyFolders = $false\r\n)\r\n\r\nif (!(Test-Path -Path $sourceDir)) {\r\n    return\r\n}\r\n\r\nAdd-Type -Assembly \"System.IO.Compression.FileSystem\"\r\n\r\nfunction Get-FileChecksum {\r\n    param (\r\n        [string] $filePath\r\n    )\r\n    $checksum = $null\r\n    if (Test-Path -Path $filePath -PathType Leaf) {\r\n        $hashAlgorithm = [System.Security.Cryptography.SHA256]::Create()\r\n        try {\r\n            $stream = [System.IO.File]::OpenRead($filePath)\r\n            $hashBytes = $hashAlgorithm.ComputeHash($stream)\r\n            $stream.Close()\r\n            $checksum = -join ($hashBytes | ForEach-Object { $_.ToString(\"x2\") })\r\n        }\r\n        finally {\r\n            $hashAlgorithm.Dispose()\r\n            if ($stream) {\r\n                $stream.Dispose()\r\n            }\r\n        }\r\n    } else {\r\n        Write-Host \"File does not exist: $filePath\"\r\n    }\r\n    return $checksum\r\n}\r\n\r\nfunction Get-FileChecksums {\r\n    param (\r\n        [string] $directory,\r\n        [string] $searchPattern = \"*\",\r\n        [string] $excludePattern = \"\"\r\n    )\r\n    $checksums = @{}\r\n    $files = Get-ChildItem -Path $directory -Recurse -File -Filter $searchPattern\r\n    \r\n    # Apply exclude pattern if specified\r\n    if (![string]::IsNullOrEmpty($excludePattern)) {\r\n        $files = $files | Where-Object { $_.Name -notlike $excludePattern }\r\n    }\r\n    \r\n    $files | ForEach-Object {\r\n        $checksum = Get-FileChecksum -filePath $_.FullName\r\n        if ($checksum) {\r\n            [string]$key = $_.FullName.Replace($directory, \"\").TrimStart(\"\\\")\r\n            $checksums[$key] = $checksum\r\n        }\r\n    }\r\n    return $checksums\r\n}\r\n\r\nfunction CheckAndZipFiles {\r\n\r\n    # Get file checksums...\r\n    $sourceChecksums = Get-FileChecksums -directory $sourceDir -searchPattern $searchPattern -excludePattern $excludePattern\r\n\r\n    $tempDir = $null\r\n    $destChecksums = @{}\r\n    if (Test-Path -Path $destFile) {\r\n        $tempDir = [System.IO.Path]::Combine([System.IO.Path]::GetTempPath(), [System.IO.Path]::GetRandomFileName())\r\n        [IO.Compression.ZipFile]::ExtractToDirectory($destFile, $tempDir)\r\n        $destChecksums = Get-FileChecksums -directory $tempDir -searchPattern $searchPattern -excludePattern $excludePattern\r\n    }\r\n\r\n    $checksumsChanged = $false\r\n\r\n    # 1. Compare files by checksums\r\n    $allFileKeys = ($sourceChecksums.Keys + $destChecksums.Keys) | Sort-Object -Unique\r\n    foreach ($key in $allFileKeys) {\r\n        if (-not $sourceChecksums.ContainsKey($key)) {\r\n            Write-Host \"Zip-only file: $key\"\r\n            $checksumsChanged = $true\r\n            break\r\n        }\r\n        if (-not $destChecksums.ContainsKey($key)) {\r\n            Write-Host \"New file on disk: $key\"\r\n            $checksumsChanged = $true\r\n            break\r\n        }\r\n        if ($sourceChecksums[$key] -ne $destChecksums[$key]) {\r\n            Write-Host \"File changed: $key\"\r\n            $checksumsChanged = $true\r\n            break\r\n        }\r\n    }\r\n\r\n    # 2. Compare directories: any directory in zip but missing on disk (or vice versa) triggers a rewrite.\r\n    # Skip this check if IgnoreEmptyFolders is true\r\n    if ($tempDir -and -not $IgnoreEmptyFolders) {\r\n        $sourceDirs = Get-ChildItem -Path $sourceDir -Recurse -Directory | \r\n                      ForEach-Object { $_.FullName.Replace($sourceDir, \"\").TrimStart(\"\\\") }\r\n        $destDirs   = Get-ChildItem -Path $tempDir  -Recurse -Directory |\r\n                      ForEach-Object { $_.FullName.Replace($tempDir, \"\").TrimStart(\"\\\") }\r\n\r\n        $allDirKeys = ($sourceDirs + $destDirs) | Sort-Object -Unique\r\n\r\n        foreach ($dirKey in $allDirKeys) {\r\n            if (-not $sourceDirs.Contains($dirKey)) {\r\n                Write-Host \"Zip-only directory: $dirKey\"\r\n                $checksumsChanged = $true\r\n                break\r\n            }\r\n            if (-not $destDirs.Contains($dirKey)) {\r\n                Write-Host \"New directory on disk: $dirKey\"\r\n                $checksumsChanged = $true\r\n                break\r\n            }\r\n        }\r\n\r\n        # Clean up extracted folder\r\n        Remove-Item -Path $tempDir -Recurse -Force\r\n    }\r\n\r\n    # 3. Rezip if needed\r\n    if ($checksumsChanged) {\r\n        Write-Host \"$($logPrefix)Source and destination checksums (or folders) do not match. Updating destination file...\"\r\n        if (Test-Path -Path $destFile) {\r\n            Remove-Item -Path $destFile -Force\r\n        }\r\n        if ($UseShellToZipFiles) {\r\n            Compress-ZipFileUsingShell -sourceDir $sourceDir -destFile $destFile -searchPattern $searchPattern -excludePattern $excludePattern -ignoreEmptyFolders $IgnoreEmptyFolders\r\n        } else {\r\n            Compress-ZipFileUsingCSharp -sourceDir $sourceDir -destFile $destFile -searchPattern $searchPattern -excludePattern $excludePattern -ignoreEmptyFolders $IgnoreEmptyFolders\r\n        }\r\n    } else {\r\n        Write-Host \"$($logPrefix)Source and destination checksums match. No update needed.\"\r\n    }\r\n}\r\n\r\nfunction Compress-ZipFileUsingCSharp {\r\n    param (\r\n        [string] $sourceDir,\r\n        [string] $destFile,\r\n        [string] $searchPattern,\r\n        [string] $excludePattern,\r\n        $ignoreEmptyFolders = $false\r\n    )\r\n    # Create a temporary directory\r\n    $tempSourceDir = New-Item -ItemType Directory -Path ([System.IO.Path]::Combine([System.IO.Path]::GetTempPath(), [System.IO.Path]::GetRandomFileName()))\r\n    \r\n    $files = Get-ChildItem -Path $sourceDir -Recurse -File\r\n    \r\n    # Apply search pattern if specified\r\n    if (![string]::IsNullOrEmpty($searchPattern)) {\r\n        $files = $files | Where-Object { $_.Name -like $searchPattern }\r\n    }\r\n    \r\n    # Apply exclude pattern if specified\r\n    if (![string]::IsNullOrEmpty($excludePattern)) {\r\n        $files = $files | Where-Object { $_.FullName -notmatch \"\\\\Temp\\\\|\\\\Temp$\" }\r\n    }\r\n    \r\n    foreach ($file in $files) {\r\n        $relativePath = $file.FullName.Replace($sourceDir, \"\").TrimStart(\"\\\")\r\n        $targetPath = Join-Path -Path $tempSourceDir -ChildPath $relativePath\r\n        \r\n        # Ensure the directory structure exists\r\n        $targetDir = [System.IO.Path]::GetDirectoryName($targetPath)\r\n        if (!(Test-Path $targetDir)) {\r\n            New-Item -ItemType Directory -Path $targetDir -Force | Out-Null\r\n        }\r\n        \r\n        # Copy the file\r\n        Copy-Item -Path $file.FullName -Destination $targetPath -Force\r\n    }\r\n    \r\n    # If not ignoring empty folders, copy the directory structure as well\r\n    if (-not $ignoreEmptyFolders) {\r\n        $directories = Get-ChildItem -Path $sourceDir -Recurse -Directory\r\n        foreach ($dir in $directories) {\r\n            $relativePath = $dir.FullName.Replace($sourceDir, \"\").TrimStart(\"\\\")\r\n            $targetPath = Join-Path -Path $tempSourceDir -ChildPath $relativePath\r\n            \r\n            if (!(Test-Path $targetPath)) {\r\n                New-Item -ItemType Directory -Path $targetPath -Force | Out-Null\r\n            }\r\n        }\r\n    }\r\n    \r\n    [IO.Compression.ZipFile]::CreateFromDirectory($tempSourceDir.FullName, $destFile)\r\n    Remove-Item -Path $tempSourceDir -Recurse -Force\r\n}\r\n\r\nfunction Compress-ZipFileUsingShell {\r\n    param (\r\n        [string] $sourceDir,\r\n        [string] $destFile,\r\n        [string] $searchPattern,\r\n        [string] $excludePattern,\r\n        $ignoreEmptyFolders = $false\r\n    )\r\n    \r\n    # Ensure the destination directory exists\r\n    $destDir = [System.IO.Path]::GetDirectoryName($destFile)\r\n    if (-not (Test-Path $destDir)) {\r\n        New-Item -ItemType Directory -Path $destDir | Out-Null\r\n    }\r\n\r\n    # Create an empty zip if it doesn't exist\r\n    if (-not (Test-Path $destFile)) {\r\n        $null = Set-Content -Path $destFile -Value (\"PK\" + [char]5 + [char]6 + (\"$([char]0)\" * 18))\r\n    }\r\n\r\n    # Use Shell Application to manipulate the zip file\r\n    $shellApplication = New-Object -ComObject Shell.Application\r\n    $zipPackage = $shellApplication.NameSpace($destFile)\r\n\r\n    if (-not $zipPackage) {\r\n        Write-Error \"$($logPrefix)Failed to create a zip package COM object for the destination file. Check the path and permissions.\"\r\n        return\r\n    }\r\n\r\n    # Get files first\r\n    $files = Get-ChildItem -Path $sourceDir -Recurse -File\r\n    \r\n    # Apply search pattern if specified\r\n    if (![string]::IsNullOrEmpty($searchPattern)) {\r\n        $files = $files | Where-Object { $_.Name -like $searchPattern }\r\n    }\r\n    \r\n    # Apply exclude pattern if specified\r\n    if (![string]::IsNullOrEmpty($excludePattern)) {\r\n        $files = $files | Where-Object { $_.Name -notlike $excludePattern }\r\n    }\r\n\r\n    # Process files\r\n    foreach ($file in $files) {\r\n        $path = $file.FullName\r\n        $zipPackage.CopyHere($path)\r\n        \r\n        $maxRetries = 4\r\n        $retryCount = 0\r\n        Do {\r\n            Start-Sleep -Seconds 2\r\n            $retryCount++\r\n            if ($retryCount -gt $maxRetries) {\r\n                Write-Host \"$($logPrefix)Max retries reached. Moving to next file...\"\r\n                break\r\n            }\r\n        } While (($shellApplication.NameSpace($destFile).Items() | Where-Object { $_.Path -eq $path }).Count -eq 0)\r\n    }\r\n\r\n    # Process directories if not ignoring empty folders\r\n    if (-not $ignoreEmptyFolders) {\r\n        $directories = Get-ChildItem -Path $sourceDir -Recurse -Directory\r\n        \r\n        # Filter directories to only include empty ones (since non-empty ones will be created when adding files)\r\n        $emptyDirectories = $directories | Where-Object {\r\n            (Get-ChildItem -Path $_.FullName -File -Recurse).Count -eq 0\r\n        }\r\n        \r\n        foreach ($dir in $emptyDirectories) {\r\n            $path = $dir.FullName\r\n            $zipPackage.CopyHere($path)\r\n            \r\n            $maxRetries = 4\r\n            $retryCount = 0\r\n            Do {\r\n                Start-Sleep -Seconds 2\r\n                $retryCount++\r\n                if ($retryCount -gt $maxRetries) {\r\n                    Write-Host \"$($logPrefix)Max retries reached. Moving to next directory...\"\r\n                    break\r\n                }\r\n            } While (($shellApplication.NameSpace($destFile).Items() | Where-Object { $_.Path -eq $path }).Count -eq 0)\r\n        }\r\n    }\r\n\r\n    # Release COM objects\r\n    [System.Runtime.InteropServices.Marshal]::ReleaseComObject($zipPackage) | Out-Null\r\n    [System.Runtime.InteropServices.Marshal]::ReleaseComObject($shellApplication) | Out-Null\r\n    [GC]::Collect()\r\n    [GC]::WaitForPendingFinalizers()\r\n}\r\n\r\n$destName = [System.IO.Path]::GetFileName($destFile)\r\n$logPrefix = \"$($destName): $($LogPrefix)\"\r\n\r\n#==============================================================\r\n# Ensure that only one instance of this script can run.\r\n# Other instances wait for the previous one to complete.\r\n#--------------------------------------------------------------\r\n# Use the full script name with path as the lock name.\r\n$scriptName = $MyInvocation.MyCommand.Name\r\n$mutexName = \"Global\\$scriptName\"\r\n$mutexCreated = $false\r\n$mutex = New-Object System.Threading.Mutex($true, $mutexName, [ref] $mutexCreated)\r\nif (-not $mutexCreated) {\r\n    # Set timeout (e.g., 5 minutes = 300,000 milliseconds)\r\n    $timeout = 300000\r\n    Write-Host \"$($logPrefix)Another instance is running. Waiting...\"\r\n    $waitResult = $mutex.WaitOne($timeout)\r\n}\r\ntry {\r\n    # Main script logic goes here...\r\n    CheckAndZipFiles\r\n}\r\nfinally {\r\n    # Release the mutex so that other instances can proceed.\r\n    $mutex.ReleaseMutex()\r\n    $mutex.Dispose()\r\n}\r\n#==============================================================\r\n"
  },
  {
    "path": "SECURITY.md",
    "content": "# Security Policy\n\n## Reporting a Vulnerability\n\nPlease report (suspected) security vulnerabilities to\n**[support@jocys.com](mailto:support@jocys.com)**\n"
  },
  {
    "path": "Settings.XamlStyler",
    "content": "{\r\n  \"AttributesTolerance\": 3,\r\n  \"KeepFirstAttributeOnSameLine\": false,\r\n  \"MaxAttributeCharactersPerLine\": 0,\r\n  \"MaxAttributesPerLine\": 1,\r\n  \"NewlineExemptionElements\": \"RadialGradientBrush, GradientStop, LinearGradientBrush, ScaleTransform, SkewTransform, RotateTransform, TranslateTransform, Trigger, Condition, Setter\",\r\n  \"SeparateByGroups\": false,\r\n  \"AttributeIndentation\": 0,\r\n  \"AttributeIndentationStyle\": 1,\r\n  \"RemoveDesignTimeReferences\": false,\r\n  \"IgnoreDesignTimeReferencePrefix\": false,\r\n  \"EnableAttributeReordering\": true,\r\n  \"AttributeOrderingRuleGroups\": [\r\n    \"x:Class\",\r\n    \"xmlns, xmlns:x\",\r\n    \"xmlns:*\",\r\n    \"x:Key, Key, x:Name, Name, x:Uid, Uid, Title\",\r\n    \"Grid.Row, Grid.RowSpan, Grid.Column, Grid.ColumnSpan, Canvas.Left, Canvas.Top, Canvas.Right, Canvas.Bottom\",\r\n    \"Width, Height, MinWidth, MinHeight, MaxWidth, MaxHeight\",\r\n    \"Margin, Padding, HorizontalAlignment, VerticalAlignment, HorizontalContentAlignment, VerticalContentAlignment, Panel.ZIndex\",\r\n    \"*:*, *\",\r\n    \"PageSource, PageIndex, Offset, Color, TargetName, Property, Value, StartPoint, EndPoint\",\r\n    \"mc:Ignorable, d:IsDataSource, d:LayoutOverrides, d:IsStaticText\",\r\n    \"Storyboard.*, From, To, Duration\"\r\n  ],\r\n  \"FirstLineAttributes\": \"\",\r\n  \"OrderAttributesByName\": true,\r\n  \"PutEndingBracketOnNewLine\": false,\r\n  \"RemoveEndingTagOfEmptyElement\": true,\r\n  \"SpaceBeforeClosingSlash\": true,\r\n  \"RootElementLineBreakRule\": 0,\r\n  \"ReorderVSM\": 2,\r\n  \"ReorderGridChildren\": false,\r\n  \"ReorderCanvasChildren\": false,\r\n  \"ReorderSetters\": 0,\r\n  \"FormatMarkupExtension\": true,\r\n  \"NoNewLineMarkupExtensions\": \"x:Bind, Binding\",\r\n  \"ThicknessSeparator\": 2,\r\n  \"ThicknessAttributes\": \"Margin, Padding, BorderThickness, ThumbnailClipMargin\",\r\n  \"FormatOnSave\": true,\r\n  \"CommentPadding\": 2,\r\n  \"IndentWithTabs\": true,\r\n  \"IndentSize\": 1\r\n}\r\n"
  },
  {
    "path": "Solution_Cleanup.ps1",
    "content": "<#\r\n.SYNOPSIS\r\n\t\tRemoves temporary bin and obj folders.\r\n\t\tKill and clear IIS Express configuration.\r\n\t\tRemoves temporary and user specific solution files.\r\n.NOTES\r\n    Author:     Evaldas Jocys <evaldas@jocys.com>\r\n    Modified:   2023-06-06\r\n.LINK\r\n    http://www.jocys.com\r\n#>\r\nusing namespace System;\r\nusing namespace System.IO;\r\n\r\n# ----------------------------------------------------------------------------\r\n# Run as administrator.\r\nIf (-NOT ([Security.Principal.WindowsPrincipal][Security.Principal.WindowsIdentity]::GetCurrent()).IsInRole([Security.Principal.WindowsBuiltInRole] \"Administrator\")) {   \r\n\t# Pass arguments: script path, original user profile path and local app data path.\r\n\t$argumentList = \"& '\" + $MyInvocation.MyCommand.Path + \"' '$($env:USERNAME)' '$($env:USERPROFILE)' '$($env:LOCALAPPDATA)'\";\r\n\tStart-Process PowerShell.exe -Verb Runas -ArgumentList $argumentList\r\n\treturn;\r\n}\t\r\n\r\n# Add original user profile path and optionally admin user profile path to process.\r\n$userNames = @( $args[0]);\r\nif ($args[0] -ne $env:USERNAME) { $userNames += $env:USERNAME };\r\n\r\n# Add original user profile path and optionally admin user profile path to process.\r\n$userProfilePaths = @( $args[1]);\r\nif ($args[1] -ne $env:USERPROFILE) { $userProfilePaths += $env:USERPROFILE };\r\n\r\n# Add original user app data path and optionally admin user app data path to process.\r\n$localAppDataPaths = @( $args[2]);\r\nif ($args[2] -ne $env:LOCALAPPDATA) { $localAppDataPaths += $env:LOCALAPPDATA };\r\n\r\n# ----------------------------------------------------------------------------\r\n# Get current command path.\r\n[string]$current = $MyInvocation.MyCommand.Path;\r\n# Get calling command path.\r\n[string]$calling = @(Get-PSCallStack)[1].InvocationInfo.MyCommand.Path;\r\n# If executed directly then...\r\nif ($calling -ne \"\") {\r\n\t$current = $calling;\r\n}\r\n# ----------------------------------------------------------------------------\r\n[FileInfo]$file = New-Object FileInfo($current);\r\n# Set public parameters.\r\n$global:scriptName = $file.Basename;\r\n$global:scriptPath = $file.Directory.FullName;\r\n# Change current directory.\r\nWrite-Host \"Script Path:    $scriptPath\";\r\n[Environment]::CurrentDirectory = $scriptPath;\r\nSet-Location $scriptPath;\r\n# ----------------------------------------------------------------------------\r\n# Shot which profiles will be affected.\r\nforeach ($p in $userProfilePaths) {\r\n\tWrite-host \"User Profile:   $p\";\r\n}\r\nforeach ($d in $localAppDataPaths) {\r\n\tWrite-host \"Local App Data: $d\";\r\n}\r\n# ----------------------------------------------------------------------------\r\nFunction KillProcess {\r\n\tparam($pattern);\r\n\t# -------------------------\r\n\t# Function.\r\n\t$procs = Get-Process;\r\n\tforeach ($proc in $procs) {\r\n\t\tif ($proc.Path) {\r\n\t\t\t$item = Get-Item $proc.Path;\r\n\t\t\tif ($item.Name -eq $pattern) {\r\n\t\t\t\tWrite-Output \"  Stopping process: $($item.Name)\";\r\n\t\t\t\tStop-Process $proc;\r\n\t\t\t}\r\n\t\t}\r\n\t}\t\r\n}\r\n# ----------------------------------------------------------------------------\r\nFunction RemoveDirectories {\r\n\tparam ($pattern, $mustBeInProject)\r\n\t# -------------------------\r\n\t# Function.\r\n\t$items = Get-ChildItem $wdir -Filter $pattern -Recurse -Force | Where-Object { $_ -is [DirectoryInfo] };\r\n\tforeach ($item in $items) {\r\n\t\tif ($mustBeInProject) {\r\n\t\t\t# Get parent folder.\r\n\t\t\t[DirectoryInfo] $parent = $item.Parent;\r\n\t\t\t$projects = $parent.GetFiles(\"*.*proj\", [SearchOption]::TopDirectoryOnly);\r\n\t\t\t# If parent folder do not contain *.*proj file then...\r\n\t\t\tif ($projects.length -eq 0) {\r\n\t\t\t\t# Ignore node_modules.\r\n\t\t\t\tif ($item.FullName -like \"*\\node_modules\\*\") {\r\n\t\t\t\t\tcontinue;\r\n\t\t\t\t}\r\n\t\t\t\tWrite-Output \"  Skip:   $($item.FullName)\";\r\n\t\t\t\t$global:skipCount += 1;\r\n\t\t\t\tcontinue;\r\n\t\t\t}\r\n\t\t\tWrite-Output \"  Remove: $($item.FullName)\";\r\n\t\t\t$global:removeCount += 1;\r\n\t\t\tRemove-Item -LiteralPath $item.FullName -Force -Recurse\r\n\t\t}\r\n\t}\r\n}\r\n# ----------------------------------------------------------------------------\r\nfunction RemoveSubFoldersAndFiles {\r\n\tparam($path, $onlyDirs);\r\n\t# -------------------------\r\n\t# Function.\r\n\t$dirs = Get-Item $path -ErrorAction SilentlyContinue;\r\n\tforeach ($dir in $dirs) {\r\n\t\tWrite-Output \"  $($dir.FullName)\";\r\n\t\t$items = Get-ChildItem -LiteralPath $dir.FullName -Force;\r\n\t\tif ($onlyDirs -eq $true) {\r\n\t\t\t$items = $items | Where-Object { $_ -is [DirectoryInfo] };\r\n\t\t}\r\n\t\tforeach ($item in $items) {\r\n\t\t\tWrite-Output \"  - $($item.Name)\";\r\n\t\t\tRemove-Item -LiteralPath $item.FullName -Force -Recurse;\r\n\t\t}\r\n\t}\r\n}\r\n# ----------------------------------------------------------------------------\r\nFunction RemoveFiles {\r\n\tparam($pattern);\r\n\t# -------------------------\r\n\t# Function.\r\n\t$items = Get-ChildItem $wdir -Filter $pattern -Recurse -Force | Where-Object { $_ -is [FileInfo] };\r\n\tforeach ($item in $items) {\r\n\t\tWrite-Output $item.FullName;\r\n\t\tRemove-Item -LiteralPath $item.FullName -Force;\r\n\t}\r\n}\r\n# ----------------------------------------------------------------------------\r\nfunction ClearBuilds {\r\n\t$global:removeCount = 0;\r\n\t$global:skipCount = 0;\r\n\tWrite-Host \"Clear Build Folders\";\r\n\t# Remove 'obj' folders first, because it can contain 'bin' inside.\r\n\tRemoveDirectories \"obj\" $true;\r\n\tRemoveDirectories \"bin\" $true;\r\n\t#Write-Output \"Skipped: $global:skipCount, Removed: $global:removeCount\";\r\n}\r\n# ----------------------------------------------------------------------------\r\n# Kill tasks which could lock files in the project folders.\r\nfunction KillDeveloperTasks {\r\n\t# TaskKill\r\n\t#   /IM <name>  Name of the process to be terminated.\r\n\t#   /T          Terminates the specified process and any child processes.\r\n\t#   /F          Specifies to forcefully terminate the process(es).\r\n\t#\r\n\t# Kill Microsoft Build Engine. \r\n\t& TaskKill.exe @(\"/F\", \"/T\", \"/IM\", \"MsBuild.exe\");\r\n\t# Kill Microsoft Visual Studio Team Foundation Server End Task.\r\n\t& TaskKill.exe @(\"/F\", \"/T\", \"/IM\", \"EndTask.exe\");\r\n\t# Kill IIS Worker.\r\n\t& TaskKill.exe @(\"/F\", \"/T\", \"/IM\", \"w3wp.exe\");\r\n\t# Kill Node.js JavaScript runtime environment.\r\n\t& TaskKill.exe @(\"/F\", \"/T\", \"/IM\", \"node.exe\");\r\n\t# Kill Web View Host.\r\n\t& TaskKill.exe @(\"/F\", \"/T\", \"/IM\", \"WebViewHost.exe\");\r\n\t# Kill ChromeDriver. Ued for UI testing.\r\n\t& TaskKill.exe @(\"/F\", \"/T\", \"/IM\", \"ChromeDriver.exe\");\r\n\t# Kill IIS Express.\r\n\t& TaskKill.exe @(\"/F\", \"/T\", \"/IM\", \"iisexpress.exe\");\r\n\t# Kill IIS Express Tray Icon.\r\n\t& TaskKill.exe @(\"/F\", \"/T\", \"/IM\", \"iisexpresstray.exe\");\r\n}\r\n# ----------------------------------------------------------------------------\r\nfunction ClearCache {\r\n\tWrite-Host \"Clear IIS Express configuration and remove temp files\";\r\n\tStart-Sleep -Seconds 2.0;\r\n\tforeach ($p in $userProfilePaths) {\r\n\t\tRemoveSubFoldersAndFiles \"$p\\Documents\\My Web Sites\" $true;\r\n\t}\r\n\tWrite-Host \"Remove temp directories\";\r\n\tRemoveDirectories \".vs\"; # Visual Studio\r\n\tRemoveDirectories \".vscode\"; # Visual Studio Code\r\n\tWrite-Host \"Remove temp files\";\r\n\tRemoveFiles \"*.dbmdl\";\r\n\tRemoveFiles \"*.user\";\r\n\tRemoveFiles \"*.suo\";\r\n\tRemoveFiles \"tsconfig.tsbuildinfo\";\r\n}\r\n# ----------------------------------------------------------------------------\r\nfunction ResetPermissions {\r\n\tparam([string]$path);\r\n\t# -------------------------\r\n\t# Give read write permissions to local users.\r\n\t$di = new-Object System.IO.DirectoryInfo($path);\r\n\tWrite-Host \"Reset Permissions on $($di.FullName)\";\r\n\tif ($di.Exists -eq $false) {\r\n\t\tWrite-Host \"Folder not found!\";\r\n\t\treturn;\r\n\t}\r\n\t# Take ownership.\r\n\t& takeown.exe @(\"/F\", $path);\r\n\t# Return ownership to TrustedInstaller.\r\n\t& icacls.exe @($path, \"/setowner\", \"`\"NT Service\\TrustedInstaller`\"\", \"/Q\");\r\n\t# Replace ACL with default inherited acls for all matching files.\r\n\t& icacls.exe @($path, \"/reset\", \"/T\", \"/C\", \"/Q\");\r\n\t# Add modify (M) & write (W) permission.\r\n\t# Inherit: This folder and files (OI), This folder and subfolders (CI).\r\n\t#& icacls.exe @($path, \"/grant\", \"`\"Users`\":(OI)(CI)MW\");\r\n}\r\n# ----------------------------------------------------------------------------\r\nfunction ClearCacheVS {\r\n\t# Fix Visual Studio \"Windows Form Designer: Could not load file or assembly\" designer error by \r\n\t# clearing temporary compiled assemblies inside dynamically created folders by Visual Studio.\r\n\t# Visual studio must be closed for this batch script to succeed.\r\n\t#\r\n\tWrite-Host \"Clear Visual Studio Cache\";\r\n\tforeach ($p in $userProfilePaths) {\r\n\t\tfor ($i = 12; $i -le 20; $i++) {\r\n\t\t\t$vsPaths = @(\r\n\t\t\t\t\"$p\\AppData\\Local\\Microsoft\\VisualStudio\\$($i).*\\ProjectAssemblies\",\r\n\t\t\t\t\"$p\\AppData\\Local\\Microsoft\\VisualStudio\\$($i).*\\ItemTemplatesCache_{00000000-0000-0000-0000-000000000000}\",\r\n\t\t\t\t\"$p\\AppData\\Local\\Microsoft\\VisualStudio\\$($i).*\\ProjectTemplatesCache_{00000000-0000-0000-0000-000000000000}\"\r\n\t\t\t);\r\n\t\t\tforeach ($vsPath in $vsPaths) {\r\n\t\t\t\t$paExpanded = Get-Item $vsPath -ErrorAction SilentlyContinue;\r\n\t\t\t\tif ($paExpanded.Length -ne 0) {\r\n\t\t\t\t\tRemoveSubFoldersAndFiles $vsPath;\r\n\t\t\t\t}\r\n\t\t\t}\r\n\t\t}\r\n\t}\t\r\n\treturn;\r\n\tWrite-Host \"Clear IIS Express Cache\";\r\n\tforeach ($p in $localAppDataPaths) {\r\n\t\tRemoveSubFoldersAndFiles \"$p\\Temp\\iisexpress\";\r\n\t\tRemoveSubFoldersAndFiles \"$p\\Temp\\Temporary ASP.NET Files\";\r\n\t}\r\n\tWrite-Host \"Clear Xamarin Cache\";\r\n\tforeach ($p in $localAppDataPaths) {\r\n\t\tRemoveSubFoldersAndFiles \"$p\\Temp\\Xamarin\";\r\n\t\tRemoveSubFoldersAndFiles \"$p\\Xamarin\\iOS\\Provisioning\";\r\n\t}\r\n\tWrite-Host \"Clear .NET Framework Cache\";\r\n\t$netVersions = @(\"v2.0.50727\", \"v4.0.30319\");\r\n\tforeach ($v in $netVersions) {\r\n\t\tRemoveSubFoldersAndFiles \"$($env:SystemRoot)\\Microsoft.NET\\Framework\\$v\\Temporary ASP.NET Files\";\r\n\t\tRemoveSubFoldersAndFiles \"$($env:SystemRoot)\\Microsoft.NET\\Framework64\\$v\\Temporary ASP.NET Files\";\r\n\t}\r\n\t#\r\n\t# Solution Explorer, right-click Solution\r\n\t#\tProperties -> Common Properties -> Debug Source Files -> clean \"Do not look for these source files\" box.\r\n\t#\r\n\t# Tools -> Options -> Projects and Solutions -> Build and Run\r\n\t# Set \"On Run, when build or deployment errors occur:\" Prompt to Launch\r\n\t#\r\n\t# .EditorConfig file.\r\n\t# \"charset=utf-8\" option can trigger \"The source file is different from when the module was built.\" warning when debugging.\r\n}\r\n# ----------------------------------------------------------------------------\r\nfunction ShowMainMenu {\r\n\t$m = \"\";\r\n\tdo {\r\n\t\t$namesAffected = [String]::Join(\", \", $userNames);\r\n\t\t# Clear screen.\r\n\t\tClear-Host;\r\n\t\tWrite-Host;\r\n\t\tWrite-Host \"User profiles will be affected: $namesAffected\";\r\n\t\tWrite-Host \"Please close Visual Studio before starting cleanup.\";\r\n\t\tWrite-Host \"The 'Clear' option will kill all developer tasks.\";\r\n\t\tWrite-Host;\r\n\t\tWrite-Host \"    1 - Clear Project builds\";\r\n\t\tWrite-Host \"    2 - Clear IIS and temp files\";\r\n\t\tWrite-Host \"    3 - Clear Visual Studio cache\";\r\n\t\tWrite-Host;\r\n\t\tWrite-Host \"    0 - Clear all\";\r\n\t\tWrite-Host;\r\n\t\tWrite-Host \"    R - Reset Permissions\";\r\n\t\tWrite-Host \"    K - Kill Developer Tasks\";\r\n\t\tWrite-Host;\r\n\t\t$m = Read-Host -Prompt \"Type option and press ENTER to continue\";\r\n\t\tWrite-Host;\r\n\t\t# Options:\r\n\t\tIF (\"$m\" -eq \"0\" -or \"$m\" -eq \"1\" -or \"$m\" -eq \"2\" -or \"$m\" -eq \"3\" ) { KillDeveloperTasks; };\r\n\t\tIF (\"$m\" -eq \"0\" -or \"$m\" -eq \"1\") { ClearBuilds; };\r\n\t\tIF (\"$m\" -eq \"0\" -or \"$m\" -eq \"2\") { ClearCache; };\r\n\t\tIF (\"$m\" -eq \"0\" -or \"$m\" -eq \"3\") { ClearCacheVS; };\r\n\t\tIF (\"$m\" -eq \"R\") { ResetPermissions \"$scriptPath\"; };\r\n\t\tIF (\"$m\" -eq \"K\") { KillDeveloperTasks; Start-Sleep -Seconds 2.0; };\r\n\t\t# If option was choosen.\r\n\t\tIF (\"$m\" -ne \"\") {\r\n\t\t\tpause;\r\n\t\t}\r\n\t} until (\"$m\" -eq \"\");\r\n\treturn $m;\r\n}\r\n# ----------------------------------------------------------------------------\r\n# Execute.\r\n# ----------------------------------------------------------------------------\r\nShowMainMenu;"
  }
]