Compare commits
14 Commits
20251007-v
...
bug/ssh_fo
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
7305bc4441 | ||
|
|
2c47c857ec | ||
|
|
19f1770eda | ||
|
|
40e878a517 | ||
|
|
d09e9b10a0 | ||
|
|
0d727338f0 | ||
|
|
e9838960fa | ||
|
|
74611e9db6 | ||
|
|
f7dc0918eb | ||
|
|
26d9e3c2ff | ||
|
|
53c534aa93 | ||
|
|
dfd02e7b9a | ||
|
|
65eac21471 | ||
|
|
5f776a3525 |
24
.github/CONTRIBUTING.md
vendored
Normal file
@@ -0,0 +1,24 @@
|
||||
**Hello, and welcome to the mRemoteNG project!**
|
||||
|
||||
Open source software is best when shared with others. This also applies to the work that goes into the software. Your ideas and passion are what make this software great!
|
||||
There are many ways that you can help improve mRemoteNG, even if you don't know how to program:
|
||||
|
||||
- add documentation or "how-to" articles on the Wiki
|
||||
- answer support questions on the forum
|
||||
- add or improve a translation
|
||||
- submit a pull request for a bug or feature ticket
|
||||
|
||||
The GitHub Issue tracker is our preferred channel for bug reports and feature requests.
|
||||
For questions or general discussion, please use [our public Gitter chat](https://gitter.im/mRemoteNG/PublicChat) or the [forum](http://forum.mremoteng.org).
|
||||
|
||||
If you find a security vulnerability, do **NOT** open an issue. Email security@mremoteng.org instead.
|
||||
|
||||
You'll find all information on our GitHub workflow in our [Wiki](https://github.com/mRemoteNG/mRemoteNG/wiki), or you can use these links:
|
||||
|
||||
- [Branches](https://github.com/mRemoteNG/mRemoteNG/wiki/Branches)
|
||||
- [Bug Repots](https://github.com/mRemoteNG/mRemoteNG/wiki/Bug-Reports)
|
||||
- [Feature Requests](https://github.com/mRemoteNG/mRemoteNG/wiki/Feature-Requests)
|
||||
- [Pull Requests](https://github.com/mRemoteNG/mRemoteNG/wiki/Pull-Requests)
|
||||
|
||||
Want to help but don't know where to start? Check out the issues that we've labeled with [`Help Wanted`](https://github.com/mRemoteNG/mRemoteNG/issues?q=is%3Aissue+is%3Aopen+label%3A%22Help+Wanted%22) or [`ready`](https://github.com/mRemoteNG/mRemoteNG/issues?utf8=%E2%9C%93&q=is%3Aissue%20is%3Aopen%20label%3A%22ready%22). These will vary in difficulty, but should be possible for new contributors.
|
||||
Even if you don't know C# you can heck out the issues that we've labeled with [`Documentation`](https://github.com/mRemoteNG/mRemoteNG/labels/Documentation).
|
||||
30
.github/ISSUE_TEMPLATE.md
vendored
Normal file
@@ -0,0 +1,30 @@
|
||||
<!--- Provide a general summary of the issue in the Title above -->
|
||||
|
||||
## Expected Behavior
|
||||
<!--- If you're describing a bug, tell us what should happen -->
|
||||
<!--- If you're suggesting a change/improvement, tell us how it should work -->
|
||||
|
||||
## Current Behavior
|
||||
<!--- If describing a bug, tell us what happens instead of the expected behavior -->
|
||||
<!--- If suggesting a change/improvement, explain the difference from current behavior -->
|
||||
|
||||
## Possible Solution
|
||||
<!--- Not obligatory, but suggest a fix/reason for the bug, -->
|
||||
<!--- or ideas how to implement the addition or change -->
|
||||
|
||||
## Steps to Reproduce (for bugs)
|
||||
<!--- Provide an unambiguous set of steps to reproduce -->
|
||||
<!--- this bug. Include code to reproduce, if relevant -->
|
||||
1.
|
||||
2.
|
||||
3.
|
||||
4.
|
||||
|
||||
## Context
|
||||
<!--- How has this issue affected you? What are you trying to accomplish? -->
|
||||
<!--- Providing context helps us come up with a solution that is most useful in the real world -->
|
||||
|
||||
## Your Environment
|
||||
<!--- Include as many relevant details about the environment you experienced the bug in -->
|
||||
* Version used:
|
||||
* Operating System and version (e.g. Windows 10 1709 x64):
|
||||
32
.github/PULL_REQUEST_TEMPLATE.md
vendored
Normal file
@@ -0,0 +1,32 @@
|
||||
<!--- Provide a general summary of your changes in the Title above -->
|
||||
|
||||
## Description
|
||||
<!--- Describe your changes in detail -->
|
||||
|
||||
## Motivation and Context
|
||||
<!--- Why is this change required? What problem does it solve? -->
|
||||
<!--- If it fixes an open issue, please link to the issue here. -->
|
||||
|
||||
## How Has This Been Tested?
|
||||
<!--- Please describe in detail how you tested your changes. -->
|
||||
<!--- Include details of your testing environment, and the tests you ran to -->
|
||||
<!--- see how your change affects other areas of the code, etc. -->
|
||||
|
||||
## Screenshots (if appropriate):
|
||||
|
||||
## Types of changes
|
||||
<!--- What types of changes does your code introduce? Put an `x` in all the boxes that apply: -->
|
||||
- [ ] Bug fix (non-breaking change which fixes an issue)
|
||||
- [ ] New feature (non-breaking change which adds functionality)
|
||||
- [ ] Breaking change (fix or feature that would cause existing functionality to change)
|
||||
- [ ] Updated translation
|
||||
|
||||
## Checklist:
|
||||
<!--- Go over all the following points, and put an `x` in all the boxes that apply. -->
|
||||
<!--- If you're unsure about any of these, don't hesitate to ask. We're here to help! -->
|
||||
- [ ] I have read the **CONTRIBUTING** document.
|
||||
- [ ] My code follows the code style of this project.
|
||||
- [ ] This pull request does not target the master branch.
|
||||
- [ ] I have updated the changelog file accordingly.
|
||||
- [ ] My change requires a change to the documentation.
|
||||
- [ ] I have updated the documentation accordingly.
|
||||
BIN
.github/icon_ReSharper.png
vendored
Normal file
|
After Width: | Height: | Size: 3.1 KiB |
157
.github/workflows/Build_mR-NB.yml
vendored
@@ -1,157 +0,0 @@
|
||||
name: Build_and_Release_mR-NB
|
||||
on:
|
||||
push:
|
||||
branches:
|
||||
- v1.78.2-dev
|
||||
|
||||
workflow_dispatch:
|
||||
inputs:
|
||||
release_flag:
|
||||
description: 'Run NB release'
|
||||
required: false
|
||||
default: 'true'
|
||||
|
||||
permissions:
|
||||
contents: write
|
||||
|
||||
jobs:
|
||||
NB-Build-and-Release:
|
||||
strategy:
|
||||
matrix:
|
||||
include:
|
||||
- runner: windows-latest
|
||||
platform: x64
|
||||
arch: x64
|
||||
- runner: windows-11-arm
|
||||
platform: ARM64
|
||||
arch: arm64
|
||||
runs-on: ${{ matrix.runner }}
|
||||
# Only run if:
|
||||
# - manual dispatch, OR
|
||||
# - push event AND commit message contains "NB release"
|
||||
if: >
|
||||
github.event_name == 'workflow_dispatch' ||
|
||||
(github.event_name == 'push' && contains(github.event.head_commit.message, 'NB release'))
|
||||
|
||||
steps:
|
||||
- name: (01) Checkout Repository
|
||||
uses: actions/checkout@v5
|
||||
|
||||
- name: (02) Setup MSBuild
|
||||
uses: microsoft/setup-msbuild@v2
|
||||
with:
|
||||
vs-version: '17.14.12'
|
||||
|
||||
- name: (03) Install and run dotnet-t4 to transform T4 templates
|
||||
shell: pwsh
|
||||
run: |
|
||||
dotnet tool install --global dotnet-t4
|
||||
# Refresh PATH to include global tools
|
||||
$env:PATH += ";$env:USERPROFILE\.dotnet\tools"
|
||||
$ttFile = "$env:GITHUB_WORKSPACE\mRemoteNG\Properties\AssemblyInfo.tt"
|
||||
# VS Enterprise 2022 assemblies
|
||||
$vsPath = "C:\Program Files\Microsoft Visual Studio\2022\Enterprise\Common7\IDE\PublicAssemblies"
|
||||
Write-Host "Transforming T4 template"
|
||||
t4 $ttFile -P platformType=${{ matrix.platform }} -r:"$vsPath\EnvDTE.dll" -r:"$vsPath\Microsoft.VisualStudio.Interop.dll"
|
||||
env:
|
||||
PLATFORM: '${{ matrix.platform }}'
|
||||
|
||||
- name: (04) Cache NuGet Packages
|
||||
uses: actions/cache@v4
|
||||
with:
|
||||
path: ~/.nuget/packages
|
||||
key: ${{ runner.os }}-${{ matrix.arch }}-nuget-${{ hashFiles('**/*.csproj') }}
|
||||
restore-keys: |
|
||||
${{ runner.os }}-${{ matrix.arch }}-nuget-
|
||||
|
||||
- name: (05) Restore NuGet Packages
|
||||
shell: pwsh
|
||||
run: dotnet restore
|
||||
|
||||
- name: (06) Build Release
|
||||
shell: pwsh
|
||||
run: |
|
||||
msbuild "$Env:GITHUB_WORKSPACE\mRemoteNG.sln" -p:Configuration="Release" -p:Platform=${{ matrix.platform }} /verbosity:minimal
|
||||
|
||||
- name: (07) Release Information
|
||||
id: version
|
||||
shell: pwsh
|
||||
run: |
|
||||
$assemblyInfoPath = "${{ github.workspace }}\mRemoteNG\Properties\AssemblyInfo.cs"
|
||||
$line = Get-Content $assemblyInfoPath | Where-Object { $_ -match 'AssemblyVersion\("(.+?)"\)' }
|
||||
if ($line -match 'AssemblyVersion\("(?<ver>\d+\.\d+\.\d+)\.(?<build>\d+)"\)') {
|
||||
$version = $matches['ver']
|
||||
$build = $matches['build']
|
||||
} else {
|
||||
throw "Could not extract version and build number"
|
||||
}
|
||||
$date = Get-Date -Format "yyyyMMdd"
|
||||
$zipName = "mRemoteNG-$date-v$version-NB-$build-${{ matrix.arch }}.zip"
|
||||
$tag = "$date-v$version-NB-($build)"
|
||||
$message = git log -1 --pretty=%B
|
||||
echo "message=$message" >> $env:GITHUB_OUTPUT
|
||||
echo "zipname=$zipName" >> $env:GITHUB_OUTPUT
|
||||
echo "version=$version" >> $env:GITHUB_OUTPUT
|
||||
echo "build=$build" >> $env:GITHUB_OUTPUT
|
||||
echo "tag=$tag" >> $env:GITHUB_OUTPUT
|
||||
$version = "${{ steps.version.outputs.version }}"
|
||||
|
||||
- name: (08) Extract Changelog Section
|
||||
id: changelog
|
||||
shell: pwsh
|
||||
run: |
|
||||
$changelogPath = "$env:GITHUB_WORKSPACE\CHANGELOG.md"
|
||||
$lines = Get-Content $changelogPath
|
||||
|
||||
$startIndex = -1
|
||||
for ($i = 0; $i -lt $lines.Count; $i++) {
|
||||
if ($lines[$i] -match '^## \[') {
|
||||
$startIndex = $i
|
||||
break
|
||||
}
|
||||
}
|
||||
|
||||
if ($startIndex -eq -1) {
|
||||
throw "No version header found in CHANGELOG.md"
|
||||
}
|
||||
|
||||
$section = @()
|
||||
for ($i = $startIndex + 1; $i -lt $lines.Count; $i++) {
|
||||
if ($lines[$i] -match '^## ') {
|
||||
break
|
||||
}
|
||||
$section += $lines[$i]
|
||||
}
|
||||
|
||||
$joined = $section -join "`n"
|
||||
echo "log<<EOF" >> $env:GITHUB_OUTPUT
|
||||
echo $joined >> $env:GITHUB_OUTPUT
|
||||
echo "EOF" >> $env:GITHUB_OUTPUT
|
||||
|
||||
echo "log=$escaped"
|
||||
|
||||
- name: (09) Create Zip File
|
||||
shell: pwsh
|
||||
run: |
|
||||
$sourceDir = "$Env:GITHUB_WORKSPACE\mRemoteNG\bin\${{ matrix.platform }}\Release"
|
||||
Compress-Archive -Path "$sourceDir\*" -DestinationPath ${{ steps.version.outputs.zipname }}
|
||||
echo "File: ${{ steps.version.outputs.zipname }}"
|
||||
|
||||
- name: (10) Create release
|
||||
id: create_release
|
||||
uses: softprops/action-gh-release@aec2ec56f94eb8180ceec724245f64ef008b89f5 # v2
|
||||
env:
|
||||
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} # This token is provided by Actions, you do not need to create your own token
|
||||
with:
|
||||
tag_name: ${{ steps.version.outputs.tag }}
|
||||
name: "mRemoteNG ${{ steps.version.outputs.version }} NB ${{ steps.version.outputs.build }}"
|
||||
files: ${{ steps.version.outputs.zipname }}
|
||||
body: |
|
||||
Changes in this Release:
|
||||
${{ steps.changelog.outputs.log }}
|
||||
|
||||
Last Commit Message:
|
||||
${{ steps.version.outputs.message }}
|
||||
|
||||
draft: false
|
||||
prerelease: true
|
||||
126
.github/workflows/add_PR_2_chlog.yml
vendored
@@ -1,126 +0,0 @@
|
||||
name: Update Changelog After Renovate PR Merge
|
||||
|
||||
on:
|
||||
# 1) Auto on pushes to your repo’s default branch (merge commits included)
|
||||
push:
|
||||
branches:
|
||||
- v1.78.2-dev
|
||||
|
||||
# 2) Manual trigger
|
||||
workflow_dispatch:
|
||||
inputs:
|
||||
dryRun:
|
||||
description: 'Run without committing changes'
|
||||
required: false
|
||||
default: 'true'
|
||||
|
||||
jobs:
|
||||
update-changelog:
|
||||
runs-on: ubuntu-latest
|
||||
|
||||
# Only proceed if…
|
||||
# - manual dispatch
|
||||
# - OR a push to default branch
|
||||
if: |
|
||||
github.event_name == 'workflow_dispatch' ||
|
||||
github.event_name == 'push'
|
||||
|
||||
steps:
|
||||
- name: Checkout code
|
||||
uses: actions/checkout@v5
|
||||
|
||||
- name: Check for Renovate dependency update
|
||||
id: check-renovate
|
||||
shell: bash
|
||||
run: |
|
||||
if [[ "${{ github.event_name }}" == "workflow_dispatch" ]]; then
|
||||
echo "isRenovate=true" >> $GITHUB_OUTPUT
|
||||
exit 0
|
||||
fi
|
||||
msg="$(git log -1 --pretty=%B)"
|
||||
if echo "$msg" | grep -q 'chore(deps): update dependency'; then
|
||||
echo "isRenovate=true" >> $GITHUB_OUTPUT
|
||||
else
|
||||
echo "isRenovate=false" >> $GITHUB_OUTPUT
|
||||
fi
|
||||
|
||||
- name: Abort if not a Renovate PR
|
||||
if: steps.check-renovate.outputs.isRenovate == 'false'
|
||||
run: |
|
||||
echo "ℹ️ Last commit is not a Renovate dependency update—skipping."
|
||||
|
||||
- name: Parse Renovate PR info
|
||||
if: steps.check-renovate.outputs.isRenovate == 'true'
|
||||
id: extract
|
||||
shell: pwsh
|
||||
run: |
|
||||
# 1) Determine dryRun
|
||||
$dryRun = '${{ github.event.inputs.dryRun }}'
|
||||
if (-not $dryRun) { $dryRun = 'false' }
|
||||
Write-Host "🔍 dryRun = $dryRun"
|
||||
|
||||
# 2) Read full commit message
|
||||
$fullMsg = git log -1 --pretty=%B
|
||||
Write-Host "📝 Commit message:"; Write-Host $fullMsg
|
||||
|
||||
# 3) Extract PR number
|
||||
if ($fullMsg -match 'Merge pull request #(\d+)') {
|
||||
$prNumber = $matches[1]
|
||||
} else {
|
||||
throw "❌ Could not locate PR number in merge commit"
|
||||
}
|
||||
|
||||
# 4) Extract dependency name & version
|
||||
if ($fullMsg -match 'chore\(deps\): update dependency ([\w\.\-]+) to ([\d\.]+)') {
|
||||
$depName = $matches[1]
|
||||
$depVersion = $matches[2]
|
||||
} else {
|
||||
throw "❌ Could not parse dependency name/version"
|
||||
}
|
||||
|
||||
# 5) Export outputs
|
||||
echo "pr=$prNumber" >> $env:GITHUB_OUTPUT
|
||||
echo "depName=$depName" >> $env:GITHUB_OUTPUT
|
||||
echo "depVersion=$depVersion" >> $env:GITHUB_OUTPUT
|
||||
echo "dryRun=$dryRun" >> $env:GITHUB_OUTPUT
|
||||
|
||||
- name: Update CHANGELOG.md
|
||||
if: steps.check-renovate.outputs.isRenovate == 'true'
|
||||
shell: pwsh
|
||||
run: |
|
||||
$path = "$env:GITHUB_WORKSPACE/CHANGELOG.md"
|
||||
if (-not (Test-Path $path)) { throw "❌ CHANGELOG.md not found" }
|
||||
$lines = Get-Content $path
|
||||
$pr = '${{ steps.extract.outputs.pr }}'
|
||||
$dep = '${{ steps.extract.outputs.depName }}'
|
||||
$ver = '${{ steps.extract.outputs.depVersion }}'
|
||||
$entry = "- #$pr: update dependency $dep to $ver"
|
||||
|
||||
# Find latest version header: ## [x.y.z]
|
||||
$vIndex = $lines.FindIndex({ $_ -match '^## \[\d+\.\d+\.\d+\]' })
|
||||
if ($vIndex -lt 0) { throw "❌ No version header found" }
|
||||
|
||||
# Locate or create "### Dependency update"
|
||||
$depIndex = -1
|
||||
for ($i = $vIndex + 1; $i -lt $lines.Count; $i++) {
|
||||
if ($lines[$i] -match '^### Dependency update') { $depIndex = $i; break }
|
||||
if ($lines[$i] -match '^## ') { break }
|
||||
}
|
||||
if ($depIndex -eq -1) {
|
||||
$lines.Insert($vIndex + 1, '### Dependency update')
|
||||
$depIndex = $vIndex + 1
|
||||
}
|
||||
|
||||
# Insert the changelog entry
|
||||
$lines.Insert($depIndex + 1, $entry)
|
||||
Set-Content -Path $path -Value $lines
|
||||
Write-Host "✅ Inserted in CHANGELOG.md:"; Write-Host " $entry"
|
||||
|
||||
- name: Commit & push
|
||||
if: steps.check-renovate.outputs.isRenovate == 'true' && steps.extract.outputs.dryRun != 'true'
|
||||
run: |
|
||||
git config user.name "github-actions"
|
||||
git config user.email "github-actions@github.com"
|
||||
git add CHANGELOG.md
|
||||
git commit -m "docs: update changelog for #${{ steps.extract.outputs.pr }}"
|
||||
git push
|
||||
49
.github/workflows/post_2_Reddit.yml
vendored
@@ -1,49 +0,0 @@
|
||||
name: Post to Reddit via PowerShell
|
||||
|
||||
on:
|
||||
workflow_dispatch:
|
||||
|
||||
jobs:
|
||||
post:
|
||||
runs-on: windows-latest
|
||||
steps:
|
||||
- name: Authenticate and post to Reddit
|
||||
shell: pwsh
|
||||
env:
|
||||
CLIENT_ID: ${{ secrets.REDDIT_CLIENT_ID }}
|
||||
CLIENT_SECRET: ${{ secrets.REDDIT_CLIENT_SECRET }}
|
||||
USERNAME: ${{ secrets.REDDIT_USERNAME }}
|
||||
PASSWORD: ${{ secrets.REDDIT_PASSWORD }}
|
||||
SUBREDDIT: ${{ secrets.REDDIT_SUBREDDIT }}
|
||||
run: |
|
||||
# Step 1: Get access token
|
||||
$authHeaders = @{
|
||||
Authorization = "Basic " + [Convert]::ToBase64String([Text.Encoding]::ASCII.GetBytes("$env:CLIENT_ID:$env:CLIENT_SECRET"))
|
||||
"User-Agent" = "github-to-reddit-pwsh/0.1"
|
||||
}
|
||||
|
||||
$authBody = @{
|
||||
grant_type = "password"
|
||||
username = $env:USERNAME
|
||||
password = $env:PASSWORD
|
||||
}
|
||||
|
||||
$authResponse = Invoke-RestMethod -Uri "https://www.reddit.com/api/v1/access_token" -Method Post -Headers $authHeaders -Body $authBody
|
||||
$token = $authResponse.access_token
|
||||
|
||||
# Step 2: Post to subreddit
|
||||
$postHeaders = @{
|
||||
Authorization = "bearer $token"
|
||||
"User-Agent" = "github-to-reddit-pwsh/0.1"
|
||||
}
|
||||
|
||||
$postBody = @{
|
||||
sr = $env:SUBREDDIT
|
||||
title = "Hello from GitHub Actions (PowerShell)"
|
||||
kind = "self"
|
||||
text = "This post was made using PowerShell in GitHub Actions."
|
||||
}
|
||||
|
||||
$postResponse = Invoke-RestMethod -Uri "https://oauth.reddit.com/api/submit" -Method Post -Headers $postHeaders -Body $postBody
|
||||
Write-Host "Posted: $($postResponse.json.url)"
|
||||
|
||||
6
.gitignore
vendored
@@ -283,6 +283,6 @@ Installer Projects/Installer/Fragments/HelpFilesFragment.wxs
|
||||
InstallerProjects/Installer/Fragments/FilesFragment.wxs
|
||||
InstallerProjects/Installer/Resources/License.rtf
|
||||
|
||||
# mRemoteNG
|
||||
**/mRemoteNG/Properties/AssemblyInfo.tt
|
||||
**/mRemoteNG/Properties/AssemblyInfo.cs
|
||||
# gh-pages info
|
||||
runlocal.bat
|
||||
/_site
|
||||
|
||||
@@ -1,14 +0,0 @@
|
||||
# .readthedocs.yaml
|
||||
# Read the Docs configuration file
|
||||
# See https://docs.readthedocs.io/en/stable/config-file/v2.html for details
|
||||
|
||||
# Required
|
||||
version: 2
|
||||
|
||||
# Build documentation in the docs/ directory with Sphinx
|
||||
sphinx:
|
||||
configuration: mRemoteNGDocumentation/conf.py
|
||||
|
||||
# Optionally build your docs in additional formats such as PDF
|
||||
formats:
|
||||
- pdf
|
||||
@@ -1,11 +0,0 @@
|
||||
## Add-ons library by 3rd party
|
||||
This is a list of add-ons, plugins and extentions what could be used with mRemoteNG, if you wish to add yours to this list - just drop me a line: <a href="mailto:support@mremoteng.org">support@mremoteng.org</a>
|
||||
<br>
|
||||
|
||||
| <b>Date added</b> | <b>Author</b> | <b>Type</b> | <b>Name</b> | <b>Description</b> | <b>Repository</b> |
|
||||
| :---------------------------------------------------------:|:-------------------------------------------------:| :---------: |-------------|--------------------|:-----------------:|
|
||||
| 02-08-2022 | <a href="https://github.com/JustBeta"><img align="left" src="https://avatars.githubusercontent.com/u/25150896?v=4" alt="JustBeta" width="30px"/>JustBeta</a> | script | Export-MobaXterm2mRemoteNG | Conversion of MobaXterm's ini file to mRemoteNG format. | [GITHUB Repository](https://github.com/JustBeta/Export-MobaXtern2mRemoteNG/tree/main) |
|
||||
|
||||
<br>
|
||||
|
||||
For a detailed usage examples and documentation please reach out authors.
|
||||
122
CHANGELOG.md
@@ -2,121 +2,8 @@
|
||||
All notable changes to this project will be documented in this file.
|
||||
|
||||
The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/), and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html).
|
||||
## [1.78.2]
|
||||
### Fixed
|
||||
- #2855: Fix missing Username field for HTTP and HTTPS protocols
|
||||
- #2852: Fix XML External Entity (XXE) vulnerability in XML deserialization
|
||||
- #2851: Fix path traversal vulnerability in file operations
|
||||
- #2850: Fix password dialog appearing behind splash screen on startup
|
||||
- #2842: Fix element placement in options window
|
||||
- #2715: Disable WinForms analyzers and suppress WFO1000 build errors for ObjectListView
|
||||
- #2712: VNCEvent_Disconnected send the ProtocolBase based object reference
|
||||
- #2668: fix ssh quickconnect exception
|
||||
- #2611: correct registry path
|
||||
- #2496: use pwfile instead of cleartext password for putty connections
|
||||
- #2734: fix native build for Windows-x64
|
||||
|
||||
### Added
|
||||
- #2728 Add support for building mRemoteNG on Windows ARM64
|
||||
- #2723: Read keyboardhook, gatewayaccesstoken and gatewaycredentialssource from RDP File
|
||||
- #2690: தமிழ் (ta) Translation update
|
||||
- #2643: Registry Settings: enhancements and new settings implementation
|
||||
- #2591: add Clickstudios Passwordstate API connector
|
||||
|
||||
### Updated
|
||||
- #2854: Refactor settings dialog to be opened in dockable panel (for consistency)
|
||||
- #2597: Remember the opened connection file on relaunch
|
||||
- #2502: Updated Polish translation
|
||||
|
||||
### Dependency update
|
||||
|
||||
## [1.77.3.1784]
|
||||
### Fixed
|
||||
- #2362: Fix use of sql database
|
||||
- #2356: Improve speed for the display of the options page
|
||||
- #2352: SSH.NET Update
|
||||
- #2346: Modify "auto reconnect" to have the ability to really auto-reconnect
|
||||
- #2340: Set the default theme setting
|
||||
- #2339: Add 2 missing settings
|
||||
- #2261: Implement Show/Hide file menu in view menu
|
||||
- #2244: Save RCG and RestrictedAdmin fields correctly in connections file
|
||||
- #2195: Fix crafted XML File Code Execution vulnerability
|
||||
- #304: use pwfile instead of cleartext password for puttyng
|
||||
|
||||
### Added
|
||||
- #2285: Support extraction of SSH private keys from external cred prov
|
||||
- #2268: Postregsql database support
|
||||
|
||||
### Updated
|
||||
- #2295: Updates hyperlink style to make links more visible to end users
|
||||
- #2337: Set language.resx to auto generate the designer class
|
||||
|
||||
## [1.77.3]
|
||||
### Added
|
||||
- #1736: Update of SSH.NET to 2020.0.2 to allow File Transfer again
|
||||
- #2138: Improve compatibility with Remote Desktop Connection Manager v2.83
|
||||
- #2123: Thycotic Secret Server - Added 2FA OTP support
|
||||
### Changed
|
||||
- #1546: Enable resize without reconnect for RDP Version Rdc9 or higher
|
||||
|
||||
## [1.77.2]
|
||||
### Added
|
||||
- #2086: Replace WebClient with async HttpClient for updater.
|
||||
- #1850: Minify config xml
|
||||
- #1770: Added missing RDP performance settings
|
||||
- #1516: added API to access credential vault (Thycotic Secret Server) by specifying SSAPI:ID as username
|
||||
- #1476: Configurable backups. Can now edit/set backup frequency, backup path, and max number of backup files.
|
||||
- #1427: Fix RDP local desktop scale not taking effect on remote
|
||||
- #1332: Added option to hide menu strip container
|
||||
- #870: Added option to push inheritance settings to child nodes recursively
|
||||
- #545: Option to minimize to system tray on closing
|
||||
- #503: SSH Execute a single command after login
|
||||
- #420: SSH tunneling implemented
|
||||
- #327: Added Alternative Shell for RDP settings
|
||||
- #319: Override quick connect username when using user@domain
|
||||
- #283: Support for native PowerShell remoting as new protocol
|
||||
- #xxx: Add external connector to retrieve ip address from Amazon EC2 Instance IDs
|
||||
### Changed
|
||||
- #2102: Extended the field RenderingEngine from 10 chars to 16
|
||||
- #2022: Replaced CefSharp with WebView2
|
||||
- #2014: Revised icons
|
||||
- #2013: Removed components check
|
||||
- #2011: Removed screenshot manager
|
||||
- #2010: Redesigned menus
|
||||
- #2005: Removed in-app documentation
|
||||
- #1777: Cleaned up VisualStudio project structure
|
||||
- #1767: Turned about window into a simple popup form
|
||||
- #1690: Replaced GeckoFX (Firefox) with CefSharp (Chromium)
|
||||
- #1325: Language resource files cleanup
|
||||
- #xxxx: Secret Server connector via new field "API User ID" instead of SSAPI: prefix
|
||||
### Fixed
|
||||
- #2125: Fixed string parsing logic for Quick Connect toolbar.
|
||||
- #2122: Fix to avoid throwing exception incase if not able decrypt connections and ask to open another one or create a new.
|
||||
- #2117: Fix of broken Links due migration to .NET 6 and branch renaming
|
||||
- #2098: Fix failed BinaryFileTest
|
||||
- #2097: Fix failed tests related to mRemoteNGTests.UI.Window.ConfigWindowTests
|
||||
- #2096: Corrected encryption code of LegacyRijndaelCryptographyProvider
|
||||
- #2089: Fixed the exception thrown by menu buttons "Documentation" and "Website"
|
||||
- #2087: Fixed application crash, when the update file is launched from the application
|
||||
- #2079: Fixed theme files not being copied to output directory
|
||||
- #2012: Updated PuTTYNG to v0.76
|
||||
- #1884: Allow setting Port when using MSSQL
|
||||
- #1783: Added missing inheritance properties to SQL scripts
|
||||
- #1773: Connection issue with MySql - Missing fields in
|
||||
- #1756: Cannot type any character on MultiSSH toolbar
|
||||
- #1720: Show configuration file name in title of password prompt form
|
||||
- #1713: Sound redirection does not work if Clipboard redirection is set to No
|
||||
- #1632: 1.77.1 breaks RDP drive and sound redirection
|
||||
- #1610: Menu bar changes to English when canceling options form
|
||||
- #1595: Unhandled exception when trying to browse through non existent multi ssh history with keyboard key strokes
|
||||
- #1589: Update SQL tables instead of rewriting them
|
||||
- #1465: REGRESSION: Smart Cards redirection to Remote Desktop not working
|
||||
- #1363: Don't show "Disk Usage" button in installer
|
||||
- #1337: Unhandled exception after closing mRemoteNG
|
||||
- #359: Making a VNC connection to an unreachable host causes the application to not respond for 20-30 seconds
|
||||
- #618: Do not break the Windows Clipboard Chain when exiting.
|
||||
|
||||
## [1.77.1] - 2019-09-02
|
||||
## [Unreleased]
|
||||
### Added
|
||||
- #1512: Added option to close panel from right click menu
|
||||
- #1434: Revised sort button in connection tree to be able to sort in both orders
|
||||
@@ -124,20 +11,13 @@ The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/),
|
||||
- #1385: Added option to start mRemoteNG minimized
|
||||
- #826: Allow selecting RDP version to use when connecting
|
||||
### Changed
|
||||
- #1544: Improved Polish translations
|
||||
- #1518: Inheritance is no longer automatically enabled when importing nodes from Active Directory
|
||||
- #1468: Improved mRemoteNG startup time
|
||||
- #1443: Chinese (simplified) translation improvements
|
||||
- #1437: Norwegian translation improvements
|
||||
- #1378: Hyperlinks embedded within mRemoteNG now open in the system default browser
|
||||
- #1239: Increased default key derivation function (KDF) iterations from 1000 to 10000
|
||||
- #718: Moved port property from 'protocol' to 'connection' section
|
||||
- Moved most RDP enums outside of the RDP protocol class. Scripts which reference these enums will need to be updated.
|
||||
- Removed the "Automatically get session info" from the advanced options screen since it is no longer used.
|
||||
### Fixed
|
||||
- #1505: About screen now better follows theme colors
|
||||
- #1493: Updated database setup scripts for MSSQL and MySQL
|
||||
- #1470: The "Favorite" setting is now properly saved in the local connection settings file (not saved in database)
|
||||
- #1447: Exception occurs when resetting layout
|
||||
- #1439: Searching in hosts tree loses first keystroke
|
||||
- #1428: Fixed a rare error when checking for FIPS
|
||||
|
||||
@@ -278,3 +278,62 @@ PROGRAMS), EVEN IF SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE
|
||||
POSSIBILITY OF SUCH DAMAGES.
|
||||
|
||||
END OF TERMS AND CONDITIONS
|
||||
|
||||
How to Apply These Terms to Your New Programs
|
||||
|
||||
If you develop a new program, and you want it to be of the greatest
|
||||
possible use to the public, the best way to achieve this is to make it
|
||||
free software which everyone can redistribute and change under these terms.
|
||||
|
||||
To do so, attach the following notices to the program. It is safest
|
||||
to attach them to the start of each source file to most effectively
|
||||
convey the exclusion of warranty; and each file should have at least
|
||||
the "copyright" line and a pointer to where the full notice is found.
|
||||
|
||||
<one line to give the program's name and a brief idea of what it does.>
|
||||
Copyright (C) <year> <name of author>
|
||||
|
||||
This program is free software; you can redistribute it and/or modify
|
||||
it under the terms of the GNU General Public License as published by
|
||||
the Free Software Foundation; either version 2 of the License, or
|
||||
(at your option) any later version.
|
||||
|
||||
This program is distributed in the hope that it will be useful,
|
||||
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
GNU General Public License for more details.
|
||||
|
||||
You should have received a copy of the GNU General Public License along
|
||||
with this program; if not, write to the Free Software Foundation, Inc.,
|
||||
51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
|
||||
|
||||
Also add information on how to contact you by electronic and paper mail.
|
||||
|
||||
If the program is interactive, make it output a short notice like this
|
||||
when it starts in an interactive mode:
|
||||
|
||||
Gnomovision version 69, Copyright (C) year name of author
|
||||
Gnomovision comes with ABSOLUTELY NO WARRANTY; for details type `show w'.
|
||||
This is free software, and you are welcome to redistribute it
|
||||
under certain conditions; type `show c' for details.
|
||||
|
||||
The hypothetical commands `show w' and `show c' should show the appropriate
|
||||
parts of the General Public License. Of course, the commands you use may
|
||||
be called something other than `show w' and `show c'; they could even be
|
||||
mouse-clicks or menu items--whatever suits your program.
|
||||
|
||||
You should also get your employer (if you work as a programmer) or your
|
||||
school, if any, to sign a "copyright disclaimer" for the program, if
|
||||
necessary. Here is a sample; alter the names:
|
||||
|
||||
Yoyodyne, Inc., hereby disclaims all copyright interest in the program
|
||||
`Gnomovision' (which makes passes at compilers) written by James Hacker.
|
||||
|
||||
<signature of Ty Coon>, 1 April 1989
|
||||
Ty Coon, President of Vice
|
||||
|
||||
This General Public License does not permit incorporating your program into
|
||||
proprietary programs. If your program is a subroutine library, you may
|
||||
consider it more useful to permit linking proprietary applications with the
|
||||
library. If this is what you want to do, use the GNU Lesser General
|
||||
Public License instead of this License.
|
||||
207
CREDITS.md
@@ -1,39 +1,37 @@
|
||||
# Contributors
|
||||
|
||||
## Current mRemoteNG dev team
|
||||
David Sparer (github.com/sparerd)
|
||||
Sean Kaim (github.com/kmscode)
|
||||
Faryan Rezagholi (github.com/farosch)
|
||||
|
||||
[David Sparer](http://github.com/sparerd)
|
||||
[Sean Kaim](http://github.com/kmscode)
|
||||
[Faryan Rezagholi](http://github.com/farosch)
|
||||
[Bennett Blodinger](http://github.com/benwa)
|
||||
Thanks for the awesome new website!
|
||||
Bennett Blodinger (github.com/benwa)
|
||||
|
||||
[Joe Cefoli](http://github.com/jcefoli)
|
||||
[countchappy](http://github.com/countchappy)
|
||||
Joe Cefoli (github.com/jcefoli)
|
||||
countchappy (github.com/countchappy)
|
||||
Tony Lambert
|
||||
[Julien Roncaglia](http://github.com/vbfox)
|
||||
[peterchenadded](http://github.com/peterchenadded)
|
||||
[Brandon Wulf](http://github.com/mrwulf)
|
||||
[Pedro Rodrigues](http://github.com/pedro2555)
|
||||
[dekelMP](http://github.com/dekelMP)
|
||||
[Bruce](http://github.com/brucetp)
|
||||
[Camilo Alvarez](http://github.com/jotatsu)
|
||||
[DamianBis](http://github.com/DamianBis)
|
||||
[pfjason](http://github.com/pfjason)
|
||||
[sirLoaf](http://github.com/sirLoaf)
|
||||
[Fyers](http://github.com/Fyers)
|
||||
[Vladimir Semenov](http://github.com/sli-pro)
|
||||
[Stephan](http://github.com/st-schuler)
|
||||
[Aleksey Reytsman](http://github.com/areytsman)
|
||||
[Cristian Abelleira](http://github.com/CrAbelleira)
|
||||
[MitchellBot](http://github.com/MitchellBot)
|
||||
[Filippo Ferrazini](http://github.com/Filippo125)
|
||||
Julien Roncaglia (github.com/vbfox)
|
||||
github.com/peterchenadded
|
||||
Brandon Wulf (github.com/mrwulf)
|
||||
Pedro Rodrigues (github.com/pedro2555)
|
||||
github.com/dekelMP
|
||||
Bruce (github.com/brucetp)
|
||||
Camilo Alvarez (github.com/jotatsu)
|
||||
github.com/DamianBis
|
||||
github.com/pfjason
|
||||
github.com/sirLoaf
|
||||
github.com/Fyers
|
||||
Vladimir Semenov (github.com/sli-pro)
|
||||
Stephan (github.com/st-schuler)
|
||||
Aleksey Reytsman (github.com/areytsman)
|
||||
Cristian Abelleira (github.com/CrAbelleira)
|
||||
github.com/MitchellBot
|
||||
|
||||
## Past Contributors
|
||||
|
||||
Felix Deimel - mRemote original developer
|
||||
Riley McArdle - mRemoteNG original developer
|
||||
|
||||
[Hayato Iriumi](http://github.com/hiriumi)
|
||||
Hayato Iriumi (github.com/hiriumi)
|
||||
Jason Barbier
|
||||
Wiktor Beryt
|
||||
Lionel Caignec
|
||||
@@ -43,85 +41,102 @@ Tom Hiller
|
||||
Apisitt Rattana
|
||||
Andreas Rehm
|
||||
David Vidmar
|
||||
[Brandhor](http://github.com/Brandhor)
|
||||
[Dimitrij](http://github.com/Kvarkas)
|
||||
github.com/Brandhor
|
||||
Dimitrij (github.com/Kvarkas)
|
||||
|
||||
## Translators
|
||||
|
||||
Eugenio "Ryo567" Martínez
|
||||
Mathieu Pape
|
||||
Emanuel Silva
|
||||
Robert Siwiec
|
||||
Hayato Iriumi
|
||||
[Sebastien Thieury](http://github.com/SebThieu)
|
||||
Sebastien Thieury (github.com/SebThieu)
|
||||
Riza Emet
|
||||
[Lukas Plachy](http://github.com/rheingold)
|
||||
Lukas Plachy (github.com/rheingold)
|
||||
Gyuha Shin
|
||||
[Stefan](http://github.com/polluks)
|
||||
[emazv72](http://github.com/emazv72)
|
||||
[Vladimir Semenov](http://github.com/sli-pro)
|
||||
[Marco Sousa](http://github.com/marcomsousa)
|
||||
[wwj402](http://github.com/wwj402)
|
||||
[Fyers](http://github.com/Fyers)
|
||||
[pablomh](http://github.com/pablomh)
|
||||
[Damian Szczepanik](http://github.com/damianszczepanik)
|
||||
[Mant1kor](http://github.com/Mant1kor)
|
||||
Stefan (github.com/polluks)
|
||||
github.com/emazv72
|
||||
Vladimir Semenov (github.com/sli-pro)
|
||||
Marco Sousa (github.com/marcomsousa)
|
||||
github.com/wwj402
|
||||
github.com/Fyers
|
||||
github.com/pablomh
|
||||
|
||||
# Included Source Code
|
||||
|
||||
**[Command Line Arguments Parser](http://www.codeproject.com/KB/recipes/command_line.aspx)**
|
||||
## Included Source Code
|
||||
Command Line Arguments Parser
|
||||
Copyright © 2002 Richard Lopes
|
||||
MIT License
|
||||
|
||||
**[FilteredPropertyGrid](http://www.codeproject.com/KB/cs/FilteredPropertyGrid.aspx)**
|
||||
Copyright © 2006 Azuria
|
||||
|
||||
**[InputBox](http://www.csharp-examples.net/inputbox/)**
|
||||
Copyright © 2016 Jan Slama
|
||||
|
||||
**[IP TextBox](http://www.codeproject.com/Articles/11576/IP-TextBox)**
|
||||
Copyright © 2005 mawnkay
|
||||
|
||||
**[PortableSettingsProvider](https://github.com/crdx/PortableSettingsProvider)**
|
||||
Copyright © 2014 crdx
|
||||
|
||||
**[ADTree](http://www.codeproject.com/KB/selection/ADPickerCtrl.aspx)**
|
||||
Copyright © 2004 Marc Merritt © 2008 Felix Deimel
|
||||
|
||||
# Included Components
|
||||
|
||||
**[CefSharp](https://github.com/cefsharp/CefSharp)**
|
||||
Copyright © The CefSharp Authors
|
||||
MIT License
|
||||
|
||||
**[DockPanel Suite](https://github.com/dockpanelsuite/dockpanelsuite)**
|
||||
Copyright © 2018 @roken and @lextm (formerly Weifen Luo)
|
||||
MIT License
|
||||
|
||||
**[log4net](http://logging.apache.org/log4net/)**
|
||||
Copyright © 2001-2015 The Apache Software Foundation
|
||||
Apache License Version 2.0
|
||||
|
||||
**[PuTTY](http://www.chiark.greenend.org.uk/~sgtatham/putty/)**
|
||||
Copyright © 1997-2017 Simon Tatham
|
||||
MIT License
|
||||
|
||||
**[Silk Icon Set](http://www.famfamfam.com/)**
|
||||
Copyright © 2005-2008 FAMFAMFAM
|
||||
Creative Commons Attribution 2.5 License
|
||||
|
||||
**[SSH.NET](https://github.com/sshnet/SSH.NET)**
|
||||
Copyright © 2016
|
||||
MIT License
|
||||
|
||||
**[VncSharp](https://github.com/humphd/VncSharp) (Archived)**
|
||||
Copyright © 2004-2009 David Humphrey
|
||||
GNU General Public License (GPL) Version 2
|
||||
|
||||
**[ObjectListView](https://sourceforge.net/projects/objectlistview/)**
|
||||
Copyright © 2006-2016 Phillip Piper
|
||||
GNU General Public License (GPL) Version 3
|
||||
|
||||
**[ConsoleControl](https://github.com/dwmkerr/consolecontrol)**
|
||||
Copyright © 2015 Dave Kerr
|
||||
MIT License
|
||||
http://www.codeproject.com/KB/recipes/command_line.aspx
|
||||
|
||||
FilteredPropertyGrid
|
||||
Copyright © 2006 Azuria
|
||||
http://www.codeproject.com/KB/cs/FilteredPropertyGrid.aspx
|
||||
|
||||
InputBox
|
||||
Copyright © 2016 Jan Slama
|
||||
http://www.csharp-examples.net/inputbox/
|
||||
|
||||
IP TextBox
|
||||
Copyright © 2005 mawnkay
|
||||
http://www.codeproject.com/Articles/11576/IP-TextBox
|
||||
|
||||
PortableSettingsProvider
|
||||
Copyright © 2014 crdx
|
||||
https://github.com/crdx/PortableSettingsProvider
|
||||
|
||||
|
||||
## Included Components
|
||||
ADTree
|
||||
Copyright © 2004 Marc Merritt
|
||||
Copyright © 2008 Felix Deimel
|
||||
http://www.codeproject.com/KB/selection/ADPickerCtrl.aspx
|
||||
|
||||
DockPanel Suite
|
||||
Copyright © 2018 @roken and @lextm (formerly Weifen Luo)
|
||||
MIT License
|
||||
https://github.com/dockpanelsuite/dockpanelsuite
|
||||
|
||||
GeckoFX
|
||||
Copyright © 2016 Tom Hindle
|
||||
Mozilla Public License
|
||||
https://bitbucket.org/geckofx/
|
||||
|
||||
log4net
|
||||
Copyright © 2001-2015 The Apache Software Foundation
|
||||
Apache License Version 2.0
|
||||
http://logging.apache.org/log4net/
|
||||
|
||||
Magic Library
|
||||
Copyright © 2002-2003 Crownwood Consulting, Ltd.
|
||||
Freely redistributable with attribution
|
||||
http://www.dotnetmagic.com/magic_download.html
|
||||
|
||||
PuTTY
|
||||
Copyright © 1997-2017 Simon Tatham
|
||||
MIT License
|
||||
http://www.chiark.greenend.org.uk/~sgtatham/putty/
|
||||
|
||||
Silk Icon Set
|
||||
Copyright © 2005-2008 FAMFAMFAM
|
||||
Creative Commons Attribution 2.5 License
|
||||
http://www.famfamfam.com/
|
||||
|
||||
SSH.NET
|
||||
Copyright © 2016
|
||||
MIT License
|
||||
https://github.com/sshnet/SSH.NET
|
||||
|
||||
VncSharp
|
||||
Copyright © 2004-2009 David Humphrey
|
||||
GNU General Public License (GPL) Version 2
|
||||
https://github.com/humphd/VncSharp
|
||||
|
||||
ObjectListView
|
||||
Copyright © 2006-2016 Phillip Piper
|
||||
GNU General Public License (GPL) Version 3
|
||||
https://sourceforge.net/projects/objectlistview/
|
||||
|
||||
Markdig
|
||||
Copyright © 2016-2019 Alexandre Mutel
|
||||
BSD 2-Clause "Simplified"
|
||||
https://github.com/lunet-io/markdig
|
||||
|
||||
@@ -1,104 +0,0 @@
|
||||
<Project>
|
||||
<PropertyGroup>
|
||||
<ManagePackageVersionsCentrally>true</ManagePackageVersionsCentrally>
|
||||
<CentralPackageTransitivePinningEnabled>true</CentralPackageTransitivePinningEnabled>
|
||||
<NoWarn>$(NoWarn);NU1507</NoWarn>
|
||||
</PropertyGroup>
|
||||
<ItemGroup>
|
||||
<PackageVersion Include="AWSSDK.Core" Version="4.0.0.32" />
|
||||
<PackageVersion Include="AWSSDK.EC2" Version="4.0.40.4" />
|
||||
<PackageVersion Include="BouncyCastle.Cryptography" Version="2.6.2" />
|
||||
<PackageVersion Include="Castle.Core" Version="5.2.1" />
|
||||
<PackageVersion Include="ConsoleControl" Version="1.3.0" />
|
||||
<PackageVersion Include="ConsoleControlAPI" Version="1.3.0" />
|
||||
<PackageVersion Include="coverlet.collector" Version="6.0.4" />
|
||||
<PackageVersion Include="Cucumber.Messages" Version="30.0.0" />
|
||||
<PackageVersion Include="DockPanelSuite" Version="3.1.1" />
|
||||
<PackageVersion Include="DockPanelSuite.ThemeVS2015" Version="3.1.1" />
|
||||
<PackageVersion Include="envdte" Version="17.14.40260" />
|
||||
<PackageVersion Include="Gherkin" Version="35.1.0" />
|
||||
<PackageVersion Include="Google.Protobuf" Version="3.32.1" />
|
||||
<PackageVersion Include="LiteDB" Version="5.0.21" />
|
||||
<PackageVersion Include="log4net" Version="3.2.0" />
|
||||
<PackageVersion Include="Microsoft.Data.SqlClient" Version="6.1.1" />
|
||||
<PackageVersion Include="Microsoft.Data.SqlClient.SNI" Version="6.0.2" />
|
||||
<PackageVersion Include="Microsoft.Data.SqlClient.SNI.runtime" Version="6.0.2" />
|
||||
<PackageVersion Include="Microsoft.Extensions.DependencyModel" Version="9.0.9" />
|
||||
<PackageVersion Include="Microsoft.Extensions.Configuration.UserSecrets" Version="9.0.9" />
|
||||
<PackageVersion Include="Microsoft.NET.Test.Sdk" Version="18.0.0" />
|
||||
<PackageVersion Include="Microsoft.NETCore.Platforms" Version="7.0.4" />
|
||||
<PackageVersion Include="Microsoft.NETCore.Targets" Version="5.0.0" />
|
||||
<PackageVersion Include="Microsoft.VisualStudio.TextTemplating.VSHost" Version="17.14.40265" />
|
||||
<PackageVersion Include="Microsoft.Web.WebView2" Version="1.0.3537.50" />
|
||||
<PackageVersion Include="Microsoft.Xaml.Behaviors.Wpf" Version="1.1.135" />
|
||||
<PackageVersion Include="Microsoft-WindowsAPICodePack-Shell" Version="1.1.5" />
|
||||
<PackageVersion Include="MySql.Data" Version="9.4.0" />
|
||||
<PackageVersion Include="NETStandard.Library" Version="2.0.3" />
|
||||
<PackageVersion Include="Newtonsoft.Json" Version="13.0.4" />
|
||||
<PackageVersion Include="Newtonsoft.Json.Schema" Version="4.0.1" />
|
||||
<PackageVersion Include="NSubstitute" Version="5.3.0" />
|
||||
<PackageVersion Include="NUnit" Version="4.4.0" />
|
||||
<PackageVersion Include="NUnit.Console" Version="3.20.1" />
|
||||
<PackageVersion Include="NUnit.ConsoleRunner" Version="3.20.1" />
|
||||
<PackageVersion Include="NUnit.Extension.TeamCityEventListener" Version="1.0.10" />
|
||||
<PackageVersion Include="NUnit.Runners" Version="3.12.0" />
|
||||
<PackageVersion Include="NUnit3TestAdapter" Version="5.2.0" />
|
||||
<PackageVersion Include="OpenCover" Version="4.7.1221" />
|
||||
<PackageVersion Include="Renci.SshNet.Async" Version="1.4.0" />
|
||||
<PackageVersion Include="ReportGenerator" Version="5.4.17" />
|
||||
<PackageVersion Include="runtime.debian.8-x64.runtime.native.System.Security.Cryptography.OpenSsl" Version="4.3.3" />
|
||||
<PackageVersion Include="runtime.fedora.23-x64.runtime.native.System.Security.Cryptography.OpenSsl" Version="4.3.3" />
|
||||
<PackageVersion Include="runtime.fedora.24-x64.runtime.native.System.Security.Cryptography.OpenSsl" Version="4.3.3" />
|
||||
<PackageVersion Include="runtime.native.System" Version="4.3.1" />
|
||||
<PackageVersion Include="runtime.native.System.IO.Compression" Version="4.3.2" />
|
||||
<PackageVersion Include="runtime.native.System.Net.Http" Version="4.3.1" />
|
||||
<PackageVersion Include="runtime.native.System.Security.Cryptography.OpenSsl" Version="4.3.3" />
|
||||
<PackageVersion Include="runtime.opensuse.42.1-x64.runtime.native.System.Security.Cryptography.OpenSsl" Version="4.3.3" />
|
||||
<PackageVersion Include="runtime.ubuntu.14.04-x64.runtime.native.System.Security.Cryptography.OpenSsl" Version="4.3.3" />
|
||||
<PackageVersion Include="runtime.ubuntu.16.10-x64.runtime.native.System.Security.Cryptography.OpenSsl" Version="4.3.3" />
|
||||
<PackageVersion Include="SSH.NET" Version="2025.0.0" />
|
||||
<PackageVersion Include="System.Buffers" Version="4.6.1" />
|
||||
<PackageVersion Include="System.Configuration.ConfigurationManager" Version="9.0.9" />
|
||||
<PackageVersion Include="System.Collections.Immutable" Version="9.0.9" />
|
||||
<PackageVersion Include="System.Console" Version="4.3.1" />
|
||||
<PackageVersion Include="System.Data.Common" Version="4.3.0" />
|
||||
<PackageVersion Include="System.Diagnostics.DiagnosticSource" Version="9.0.9" />
|
||||
<PackageVersion Include="System.Drawing.Common" Version="9.0.9" />
|
||||
<PackageVersion Include="System.Diagnostics.EventLog" Version="9.0.9" />
|
||||
<PackageVersion Include="System.DirectoryServices" Version="9.0.9" />
|
||||
<PackageVersion Include="System.Dynamic.Runtime" Version="4.3.0" />
|
||||
<PackageVersion Include="System.IO.Pipelines" Version="9.0.9" />
|
||||
<PackageVersion Include="System.Formats.Asn1" Version="9.0.9" />
|
||||
<PackageVersion Include="System.Management" Version="9.0.9" />
|
||||
<PackageVersion Include="System.Memory" Version="4.6.3" />
|
||||
<PackageVersion Include="System.Net.Http" Version="4.3.4" />
|
||||
<PackageVersion Include="System.Net.Primitives" Version="4.3.1" />
|
||||
<PackageVersion Include="System.Net.Sockets" Version="4.3.0" />
|
||||
<PackageVersion Include="System.Reflection.Emit" Version="4.7.0" />
|
||||
<PackageVersion Include="System.Reflection.Emit.ILGeneration" Version="4.7.0" />
|
||||
<PackageVersion Include="System.Reflection.Emit.Lightweight" Version="4.7.0" />
|
||||
<PackageVersion Include="System.Reflection.Metadata" Version="9.0.9" />
|
||||
<PackageVersion Include="System.Reflection.TypeExtensions" Version="4.7.0" />
|
||||
<PackageVersion Include="System.Resources.ResourceManager" Version="4.3.0" />
|
||||
<PackageVersion Include="System.Runtime" Version="4.3.1" />
|
||||
<PackageVersion Include="System.Runtime.CompilerServices.Unsafe" Version="6.1.2" />
|
||||
<PackageVersion Include="System.Runtime.Extensions" Version="4.3.1" />
|
||||
<PackageVersion Include="System.Security.AccessControl" Version="6.0.1" />
|
||||
<PackageVersion Include="System.Security.Cryptography.Algorithms" Version="4.3.1" />
|
||||
<PackageVersion Include="System.Security.Cryptography.Cng" Version="5.0.0" />
|
||||
<PackageVersion Include="System.Security.Cryptography.OpenSsl" Version="5.0.0" />
|
||||
<PackageVersion Include="System.Security.Cryptography.ProtectedData" Version="9.0.9" />
|
||||
<PackageVersion Include="System.Security.Cryptography.X509Certificates" Version="4.3.2" />
|
||||
<PackageVersion Include="System.Security.Permissions" Version="9.0.9" />
|
||||
<PackageVersion Include="System.Security.Principal.Windows" Version="5.0.0" />
|
||||
<PackageVersion Include="System.Text.Encoding.CodePages" Version="9.0.9" />
|
||||
<PackageVersion Include="System.Text.Json" Version="9.0.9" />
|
||||
<PackageVersion Include="System.Text.RegularExpressions" Version="4.3.1" />
|
||||
<PackageVersion Include="System.Threading.Tasks.Extensions" Version="4.6.3" />
|
||||
<PackageVersion Include="System.ValueTuple" Version="4.6.1" />
|
||||
<PackageVersion Include="System.Windows.Extensions" Version="9.0.9" />
|
||||
<PackageVersion Include="System.Xml.ReaderWriter" Version="4.3.1" />
|
||||
<PackageVersion Include="VncSharpCore" Version="1.2.1" />
|
||||
<PackageVersion Include="ZstdSharp.Port" Version="0.8.6" />
|
||||
</ItemGroup>
|
||||
</Project>
|
||||
180
ExternalConnectors/AWS/AWSConnectionForm.Designer.cs
generated
@@ -1,180 +0,0 @@
|
||||
namespace ExternalConnectors.AWS
|
||||
{
|
||||
partial class AWSConnectionForm
|
||||
{
|
||||
/// <summary>
|
||||
/// Required designer variable.
|
||||
/// </summary>
|
||||
private System.ComponentModel.IContainer components = null;
|
||||
|
||||
/// <summary>
|
||||
/// Clean up any resources being used.
|
||||
/// </summary>
|
||||
/// <param name="disposing">true if managed resources should be disposed; otherwise, false.</param>
|
||||
protected override void Dispose(bool disposing)
|
||||
{
|
||||
if (disposing && (components != null))
|
||||
{
|
||||
components.Dispose();
|
||||
}
|
||||
base.Dispose(disposing);
|
||||
}
|
||||
|
||||
#region Windows Form Designer generated code
|
||||
|
||||
/// <summary>
|
||||
/// Required method for Designer support - do not modify
|
||||
/// the contents of this method with the code editor.
|
||||
/// </summary>
|
||||
private void InitializeComponent()
|
||||
{
|
||||
this.tbAccesKeyID = new System.Windows.Forms.TextBox();
|
||||
this.tbAccesKey = new System.Windows.Forms.TextBox();
|
||||
this.btnOK = new System.Windows.Forms.Button();
|
||||
this.btnCancel = new System.Windows.Forms.Button();
|
||||
this.tableLayoutPanel1 = new System.Windows.Forms.TableLayoutPanel();
|
||||
this.label1 = new System.Windows.Forms.Label();
|
||||
this.label2 = new System.Windows.Forms.Label();
|
||||
this.tableLayoutPanel2 = new System.Windows.Forms.TableLayoutPanel();
|
||||
this.tableLayoutPanel1.SuspendLayout();
|
||||
this.tableLayoutPanel2.SuspendLayout();
|
||||
this.SuspendLayout();
|
||||
//
|
||||
// tbAccesKeyID
|
||||
//
|
||||
this.tbAccesKeyID.Dock = System.Windows.Forms.DockStyle.Fill;
|
||||
this.tbAccesKeyID.Location = new System.Drawing.Point(152, 4);
|
||||
this.tbAccesKeyID.Margin = new System.Windows.Forms.Padding(4, 4, 4, 4);
|
||||
this.tbAccesKeyID.Name = "tbAccesKeyID";
|
||||
this.tbAccesKeyID.Size = new System.Drawing.Size(490, 23);
|
||||
this.tbAccesKeyID.TabIndex = 0;
|
||||
//
|
||||
// tbAccesKey
|
||||
//
|
||||
this.tbAccesKey.Dock = System.Windows.Forms.DockStyle.Fill;
|
||||
this.tbAccesKey.Location = new System.Drawing.Point(152, 43);
|
||||
this.tbAccesKey.Margin = new System.Windows.Forms.Padding(4, 4, 4, 4);
|
||||
this.tbAccesKey.Name = "tbAccesKey";
|
||||
this.tbAccesKey.Size = new System.Drawing.Size(490, 23);
|
||||
this.tbAccesKey.TabIndex = 2;
|
||||
//
|
||||
// btnOK
|
||||
//
|
||||
this.btnOK.Anchor = System.Windows.Forms.AnchorStyles.Right;
|
||||
this.btnOK.DialogResult = System.Windows.Forms.DialogResult.OK;
|
||||
this.btnOK.Location = new System.Drawing.Point(219, 12);
|
||||
this.btnOK.Margin = new System.Windows.Forms.Padding(4, 4, 4, 4);
|
||||
this.btnOK.Name = "btnOK";
|
||||
this.btnOK.Size = new System.Drawing.Size(88, 26);
|
||||
this.btnOK.TabIndex = 10;
|
||||
this.btnOK.Text = "OK";
|
||||
this.btnOK.UseVisualStyleBackColor = true;
|
||||
//
|
||||
// btnCancel
|
||||
//
|
||||
this.btnCancel.Anchor = System.Windows.Forms.AnchorStyles.Left;
|
||||
this.btnCancel.DialogResult = System.Windows.Forms.DialogResult.Cancel;
|
||||
this.btnCancel.Location = new System.Drawing.Point(338, 12);
|
||||
this.btnCancel.Margin = new System.Windows.Forms.Padding(4, 4, 4, 4);
|
||||
this.btnCancel.Name = "btnCancel";
|
||||
this.btnCancel.Size = new System.Drawing.Size(88, 26);
|
||||
this.btnCancel.TabIndex = 11;
|
||||
this.btnCancel.Text = "Cancel";
|
||||
this.btnCancel.UseVisualStyleBackColor = true;
|
||||
//
|
||||
// tableLayoutPanel1
|
||||
//
|
||||
this.tableLayoutPanel1.ColumnCount = 2;
|
||||
this.tableLayoutPanel1.ColumnStyles.Add(new System.Windows.Forms.ColumnStyle(System.Windows.Forms.SizeType.Percent, 22.92994F));
|
||||
this.tableLayoutPanel1.ColumnStyles.Add(new System.Windows.Forms.ColumnStyle(System.Windows.Forms.SizeType.Percent, 77.07006F));
|
||||
this.tableLayoutPanel1.Controls.Add(this.label1, 0, 0);
|
||||
this.tableLayoutPanel1.Controls.Add(this.label2, 0, 1);
|
||||
this.tableLayoutPanel1.Controls.Add(this.tbAccesKeyID, 1, 0);
|
||||
this.tableLayoutPanel1.Controls.Add(this.tbAccesKey, 1, 1);
|
||||
this.tableLayoutPanel1.Dock = System.Windows.Forms.DockStyle.Top;
|
||||
this.tableLayoutPanel1.Location = new System.Drawing.Point(0, 0);
|
||||
this.tableLayoutPanel1.Margin = new System.Windows.Forms.Padding(4, 4, 4, 4);
|
||||
this.tableLayoutPanel1.Name = "tableLayoutPanel1";
|
||||
this.tableLayoutPanel1.RowCount = 3;
|
||||
this.tableLayoutPanel1.RowStyles.Add(new System.Windows.Forms.RowStyle(System.Windows.Forms.SizeType.Percent, 50F));
|
||||
this.tableLayoutPanel1.RowStyles.Add(new System.Windows.Forms.RowStyle(System.Windows.Forms.SizeType.Percent, 50F));
|
||||
this.tableLayoutPanel1.RowStyles.Add(new System.Windows.Forms.RowStyle(System.Windows.Forms.SizeType.Absolute, 23F));
|
||||
this.tableLayoutPanel1.RowStyles.Add(new System.Windows.Forms.RowStyle(System.Windows.Forms.SizeType.Absolute, 23F));
|
||||
this.tableLayoutPanel1.RowStyles.Add(new System.Windows.Forms.RowStyle(System.Windows.Forms.SizeType.Absolute, 23F));
|
||||
this.tableLayoutPanel1.Size = new System.Drawing.Size(646, 102);
|
||||
this.tableLayoutPanel1.TabIndex = 12;
|
||||
//
|
||||
// label1
|
||||
//
|
||||
this.label1.AutoSize = true;
|
||||
this.label1.Dock = System.Windows.Forms.DockStyle.Fill;
|
||||
this.label1.Location = new System.Drawing.Point(4, 0);
|
||||
this.label1.Margin = new System.Windows.Forms.Padding(4, 0, 4, 0);
|
||||
this.label1.Name = "label1";
|
||||
this.label1.Size = new System.Drawing.Size(140, 39);
|
||||
this.label1.TabIndex = 2;
|
||||
this.label1.Text = "Access Key ID";
|
||||
this.label1.TextAlign = System.Drawing.ContentAlignment.MiddleLeft;
|
||||
//
|
||||
// label2
|
||||
//
|
||||
this.label2.AutoSize = true;
|
||||
this.label2.Dock = System.Windows.Forms.DockStyle.Fill;
|
||||
this.label2.Location = new System.Drawing.Point(4, 39);
|
||||
this.label2.Margin = new System.Windows.Forms.Padding(4, 0, 4, 0);
|
||||
this.label2.Name = "label2";
|
||||
this.label2.Size = new System.Drawing.Size(140, 39);
|
||||
this.label2.TabIndex = 4;
|
||||
this.label2.Text = "Access Key";
|
||||
this.label2.TextAlign = System.Drawing.ContentAlignment.MiddleLeft;
|
||||
//
|
||||
// tableLayoutPanel2
|
||||
//
|
||||
this.tableLayoutPanel2.ColumnCount = 5;
|
||||
this.tableLayoutPanel2.ColumnStyles.Add(new System.Windows.Forms.ColumnStyle(System.Windows.Forms.SizeType.Absolute, 93F));
|
||||
this.tableLayoutPanel2.ColumnStyles.Add(new System.Windows.Forms.ColumnStyle(System.Windows.Forms.SizeType.Percent, 50F));
|
||||
this.tableLayoutPanel2.ColumnStyles.Add(new System.Windows.Forms.ColumnStyle(System.Windows.Forms.SizeType.Absolute, 23F));
|
||||
this.tableLayoutPanel2.ColumnStyles.Add(new System.Windows.Forms.ColumnStyle(System.Windows.Forms.SizeType.Percent, 50F));
|
||||
this.tableLayoutPanel2.ColumnStyles.Add(new System.Windows.Forms.ColumnStyle(System.Windows.Forms.SizeType.Absolute, 93F));
|
||||
this.tableLayoutPanel2.Controls.Add(this.btnOK, 1, 0);
|
||||
this.tableLayoutPanel2.Controls.Add(this.btnCancel, 3, 0);
|
||||
this.tableLayoutPanel2.Dock = System.Windows.Forms.DockStyle.Bottom;
|
||||
this.tableLayoutPanel2.Location = new System.Drawing.Point(0, 112);
|
||||
this.tableLayoutPanel2.Margin = new System.Windows.Forms.Padding(4, 4, 4, 4);
|
||||
this.tableLayoutPanel2.Name = "tableLayoutPanel2";
|
||||
this.tableLayoutPanel2.RowCount = 1;
|
||||
this.tableLayoutPanel2.RowStyles.Add(new System.Windows.Forms.RowStyle(System.Windows.Forms.SizeType.Percent, 100F));
|
||||
this.tableLayoutPanel2.Size = new System.Drawing.Size(646, 50);
|
||||
this.tableLayoutPanel2.TabIndex = 13;
|
||||
//
|
||||
// AWSConnectionForm
|
||||
//
|
||||
this.AcceptButton = this.btnOK;
|
||||
this.AutoScaleDimensions = new System.Drawing.SizeF(7F, 15F);
|
||||
this.AutoScaleMode = System.Windows.Forms.AutoScaleMode.Font;
|
||||
this.ClientSize = new System.Drawing.Size(646, 162);
|
||||
this.Controls.Add(this.tableLayoutPanel2);
|
||||
this.Controls.Add(this.tableLayoutPanel1);
|
||||
this.Margin = new System.Windows.Forms.Padding(4, 4, 4, 4);
|
||||
this.Name = "AWSConnectionForm";
|
||||
this.Text = "AWS EC2 API Login Data";
|
||||
this.Activated += new System.EventHandler(this.AWSConnectionForm_Activated);
|
||||
this.tableLayoutPanel1.ResumeLayout(false);
|
||||
this.tableLayoutPanel1.PerformLayout();
|
||||
this.tableLayoutPanel2.ResumeLayout(false);
|
||||
this.ResumeLayout(false);
|
||||
|
||||
}
|
||||
|
||||
#endregion
|
||||
|
||||
public System.Windows.Forms.TextBox tbAccesKeyID;
|
||||
public System.Windows.Forms.TextBox tbAccesKey;
|
||||
private System.Windows.Forms.Button btnOK;
|
||||
private System.Windows.Forms.Button btnCancel;
|
||||
private System.Windows.Forms.TableLayoutPanel tableLayoutPanel1;
|
||||
private System.Windows.Forms.Label label2;
|
||||
private System.Windows.Forms.Label label1;
|
||||
private System.Windows.Forms.TableLayoutPanel tableLayoutPanel2;
|
||||
}
|
||||
}
|
||||
@@ -1,16 +0,0 @@
|
||||
namespace ExternalConnectors.AWS
|
||||
{
|
||||
public partial class AWSConnectionForm : Form
|
||||
{
|
||||
public AWSConnectionForm()
|
||||
{
|
||||
InitializeComponent();
|
||||
|
||||
}
|
||||
|
||||
private void AWSConnectionForm_Activated(object sender, EventArgs e)
|
||||
{
|
||||
tbAccesKeyID.Focus();
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,60 +0,0 @@
|
||||
<root>
|
||||
<xsd:schema id="root" xmlns="" xmlns:xsd="http://www.w3.org/2001/XMLSchema" xmlns:msdata="urn:schemas-microsoft-com:xml-msdata">
|
||||
<xsd:import namespace="http://www.w3.org/XML/1998/namespace" />
|
||||
<xsd:element name="root" msdata:IsDataSet="true">
|
||||
<xsd:complexType>
|
||||
<xsd:choice maxOccurs="unbounded">
|
||||
<xsd:element name="metadata">
|
||||
<xsd:complexType>
|
||||
<xsd:sequence>
|
||||
<xsd:element name="value" type="xsd:string" minOccurs="0" />
|
||||
</xsd:sequence>
|
||||
<xsd:attribute name="name" use="required" type="xsd:string" />
|
||||
<xsd:attribute name="type" type="xsd:string" />
|
||||
<xsd:attribute name="mimetype" type="xsd:string" />
|
||||
<xsd:attribute ref="xml:space" />
|
||||
</xsd:complexType>
|
||||
</xsd:element>
|
||||
<xsd:element name="assembly">
|
||||
<xsd:complexType>
|
||||
<xsd:attribute name="alias" type="xsd:string" />
|
||||
<xsd:attribute name="name" type="xsd:string" />
|
||||
</xsd:complexType>
|
||||
</xsd:element>
|
||||
<xsd:element name="data">
|
||||
<xsd:complexType>
|
||||
<xsd:sequence>
|
||||
<xsd:element name="value" type="xsd:string" minOccurs="0" msdata:Ordinal="1" />
|
||||
<xsd:element name="comment" type="xsd:string" minOccurs="0" msdata:Ordinal="2" />
|
||||
</xsd:sequence>
|
||||
<xsd:attribute name="name" type="xsd:string" use="required" msdata:Ordinal="1" />
|
||||
<xsd:attribute name="type" type="xsd:string" msdata:Ordinal="3" />
|
||||
<xsd:attribute name="mimetype" type="xsd:string" msdata:Ordinal="4" />
|
||||
<xsd:attribute ref="xml:space" />
|
||||
</xsd:complexType>
|
||||
</xsd:element>
|
||||
<xsd:element name="resheader">
|
||||
<xsd:complexType>
|
||||
<xsd:sequence>
|
||||
<xsd:element name="value" type="xsd:string" minOccurs="0" msdata:Ordinal="1" />
|
||||
</xsd:sequence>
|
||||
<xsd:attribute name="name" type="xsd:string" use="required" />
|
||||
</xsd:complexType>
|
||||
</xsd:element>
|
||||
</xsd:choice>
|
||||
</xsd:complexType>
|
||||
</xsd:element>
|
||||
</xsd:schema>
|
||||
<resheader name="resmimetype">
|
||||
<value>text/microsoft-resx</value>
|
||||
</resheader>
|
||||
<resheader name="version">
|
||||
<value>2.0</value>
|
||||
</resheader>
|
||||
<resheader name="reader">
|
||||
<value>System.Resources.ResXResourceReader, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089</value>
|
||||
</resheader>
|
||||
<resheader name="writer">
|
||||
<value>System.Resources.ResXResourceWriter, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089</value>
|
||||
</resheader>
|
||||
</root>
|
||||
@@ -1,119 +0,0 @@
|
||||
using Amazon;
|
||||
using Amazon.EC2;
|
||||
using Amazon.EC2.Model;
|
||||
using Microsoft.Win32;
|
||||
|
||||
namespace ExternalConnectors.AWS
|
||||
{
|
||||
public class EC2FetchDataService
|
||||
{
|
||||
private static DateTime lastFetch;
|
||||
private static List<InstanceInfo>? lastData;
|
||||
|
||||
// input must be in format "AWSAPI:instanceid" where instanceid is the ec2 instance id, e.g. i-066f750a76c97583d
|
||||
public static async Task<string> GetEC2InstanceDataAsync(string input, string region)
|
||||
{
|
||||
// get secret id
|
||||
if (!input.StartsWith("AWSAPI:"))
|
||||
throw new Exception("calling this function requires AWSAPI: input");
|
||||
string InstanceID = input[7..];
|
||||
|
||||
// init connection credentials, display popup if necessary
|
||||
AWSConnectionData.Init();
|
||||
var alldata = await GetEC2IPDataAsync(region);
|
||||
var found = alldata.Where(x => x.InstanceId == InstanceID).SingleOrDefault();
|
||||
return (found == null) ? "" : found.PublicIP;
|
||||
}
|
||||
|
||||
private static async Task<List<InstanceInfo>> GetEC2IPDataAsync(string region)
|
||||
{
|
||||
// caching
|
||||
TimeSpan timeSpan = DateTime.Now - lastFetch;
|
||||
if (timeSpan.TotalMinutes < 1 && lastData != null)
|
||||
return lastData;
|
||||
|
||||
//AWSConfigs.AWSRegion = AWSConnectionData.region;
|
||||
AWSConfigs.AWSRegion = region;
|
||||
string awsAccessKeyId = AWSConnectionData.awsKeyID;
|
||||
string awsSecretAccessKey = AWSConnectionData.awsKey;
|
||||
|
||||
var _client = new AmazonEC2Client(awsAccessKeyId, awsSecretAccessKey, RegionEndpoint.EUCentral1);
|
||||
bool done = false;
|
||||
|
||||
List<InstanceInfo> instanceList = new();
|
||||
var request = new DescribeInstancesRequest();
|
||||
while (!done)
|
||||
{
|
||||
DescribeInstancesResponse response = await _client.DescribeInstancesAsync(request);
|
||||
|
||||
foreach (var reservation in response.Reservations)
|
||||
{
|
||||
foreach (var instance in reservation.Instances)
|
||||
{
|
||||
string vmname = "";
|
||||
foreach (var tag in instance.Tags)
|
||||
{
|
||||
if (tag.Key == "Name")
|
||||
{
|
||||
vmname = tag.Value;
|
||||
}
|
||||
}
|
||||
InstanceInfo inf = new(instance, vmname);
|
||||
instanceList.Add(inf);
|
||||
}
|
||||
}
|
||||
|
||||
request.NextToken = response.NextToken;
|
||||
|
||||
if (response.NextToken == null)
|
||||
{
|
||||
done = true;
|
||||
}
|
||||
}
|
||||
|
||||
lastData = instanceList.OrderBy(x => x.Name).ToList();
|
||||
lastFetch = DateTime.Now;
|
||||
return lastData;
|
||||
}
|
||||
|
||||
|
||||
public static class AWSConnectionData
|
||||
{
|
||||
private static readonly RegistryKey key = Registry.CurrentUser.CreateSubKey(@"SOFTWARE\mRemoteAWSInterface");
|
||||
|
||||
public static string awsKeyID = "";
|
||||
public static string awsKey = "";
|
||||
//public static string _region = "eu-central-1";
|
||||
|
||||
public static void Init()
|
||||
{
|
||||
if (awsKey != "")
|
||||
return;
|
||||
// display gui and ask for data
|
||||
AWSConnectionForm f = new();
|
||||
f.tbAccesKeyID.Text = "" + key.GetValue("KeyID");
|
||||
f.tbAccesKey.Text = "" + key.GetValue("Key");
|
||||
//f.tbRegion.Text = "" + key.GetValue("Region");
|
||||
//if (f.tbRegion.Text == null || f.tbRegion.Text.Length < 2)
|
||||
// f.tbRegion.Text = region;
|
||||
_ = f.ShowDialog();
|
||||
|
||||
if (f.DialogResult != DialogResult.OK)
|
||||
return;
|
||||
|
||||
// store values to memory
|
||||
awsKeyID = f.tbAccesKeyID.Text;
|
||||
awsKey = f.tbAccesKey.Text;
|
||||
//region = f.tbRegion.Text;
|
||||
|
||||
|
||||
// write values to registry
|
||||
key.SetValue("KeyID", awsKeyID);
|
||||
key.SetValue("Key", awsKey);
|
||||
//key.SetValue("Region", region);
|
||||
key.Close();
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
@@ -1,34 +0,0 @@
|
||||
using Amazon.EC2.Model;
|
||||
using System;
|
||||
|
||||
namespace ExternalConnectors.AWS
|
||||
{
|
||||
public class InstanceInfo
|
||||
{
|
||||
public string InstanceId { get; }
|
||||
public string Name { get; }
|
||||
public string Status { get; }
|
||||
public string PublicIP { get; }
|
||||
public string PrivateIP { get; }
|
||||
public InstanceInfo(Instance instance, string name)
|
||||
{
|
||||
InstanceId = instance.InstanceId;
|
||||
Name = name;
|
||||
|
||||
switch(instance.State.Code)
|
||||
{
|
||||
case 0: Status = "Pending"; break;
|
||||
case 16: Status = "Running"; break;
|
||||
case 32: Status = "Shutdown"; break;
|
||||
case 48: Status = "Terminated"; break;
|
||||
case 64: Status = "Stopping"; break;
|
||||
case 80: Status = "Stopped"; break;
|
||||
default: Status = "Unknown"; break;
|
||||
};
|
||||
|
||||
PublicIP = instance.PublicIpAddress ?? "";
|
||||
PrivateIP = instance.PrivateIpAddress ?? "";
|
||||
|
||||
}
|
||||
}
|
||||
}
|
||||
|
Before Width: | Height: | Size: 1.4 KiB |
241
ExternalConnectors/CPS/CPSConnectionForm.Designer.cs
generated
@@ -1,241 +0,0 @@
|
||||
namespace ExternalConnectors.CPS
|
||||
{
|
||||
partial class CPSConnectionForm
|
||||
{
|
||||
/// <summary>
|
||||
/// Required designer variable.
|
||||
/// </summary>
|
||||
private System.ComponentModel.IContainer components = null;
|
||||
|
||||
/// <summary>
|
||||
/// Clean up any resources being used.
|
||||
/// </summary>
|
||||
/// <param name="disposing">true if managed resources should be disposed; otherwise, false.</param>
|
||||
protected override void Dispose(bool disposing)
|
||||
{
|
||||
if (disposing && (components != null))
|
||||
{
|
||||
components.Dispose();
|
||||
}
|
||||
base.Dispose(disposing);
|
||||
}
|
||||
|
||||
#region Windows Form Designer generated code
|
||||
|
||||
/// <summary>
|
||||
/// Required method for Designer support - do not modify
|
||||
/// the contents of this method with the code editor.
|
||||
/// </summary>
|
||||
private void InitializeComponent()
|
||||
{
|
||||
System.ComponentModel.ComponentResourceManager resources = new System.ComponentModel.ComponentResourceManager(typeof(CPSConnectionForm));
|
||||
tbServerURL = new TextBox();
|
||||
label3 = new Label();
|
||||
tbAPIKey = new TextBox();
|
||||
btnOK = new Button();
|
||||
btnCancel = new Button();
|
||||
tableLayoutPanel1 = new TableLayoutPanel();
|
||||
label1 = new Label();
|
||||
label6 = new Label();
|
||||
tbOTP = new TextBox();
|
||||
cbUseSSO = new CheckBox();
|
||||
tableLayoutPanel2 = new TableLayoutPanel();
|
||||
label4 = new Label();
|
||||
tableLayoutPanel1.SuspendLayout();
|
||||
tableLayoutPanel2.SuspendLayout();
|
||||
SuspendLayout();
|
||||
//
|
||||
// tbServerURL
|
||||
//
|
||||
tbServerURL.Dock = DockStyle.Fill;
|
||||
tbServerURL.Location = new Point(298, 5);
|
||||
tbServerURL.Margin = new Padding(5);
|
||||
tbServerURL.Name = "tbServerURL";
|
||||
tbServerURL.Size = new Size(611, 27);
|
||||
tbServerURL.TabIndex = 0;
|
||||
//
|
||||
// label3
|
||||
//
|
||||
label3.AutoSize = true;
|
||||
label3.Dock = DockStyle.Fill;
|
||||
label3.Location = new Point(5, 84);
|
||||
label3.Margin = new Padding(5, 0, 5, 0);
|
||||
label3.Name = "label3";
|
||||
label3.Size = new Size(283, 42);
|
||||
label3.TabIndex = 5;
|
||||
label3.Text = "API Key";
|
||||
label3.TextAlign = ContentAlignment.MiddleLeft;
|
||||
//
|
||||
// tbAPIKey
|
||||
//
|
||||
tbAPIKey.Dock = DockStyle.Fill;
|
||||
tbAPIKey.Location = new Point(298, 89);
|
||||
tbAPIKey.Margin = new Padding(5);
|
||||
tbAPIKey.Name = "tbAPIKey";
|
||||
tbAPIKey.Size = new Size(611, 27);
|
||||
tbAPIKey.TabIndex = 4;
|
||||
tbAPIKey.UseSystemPasswordChar = true;
|
||||
//
|
||||
// btnOK
|
||||
//
|
||||
btnOK.Anchor = AnchorStyles.Right;
|
||||
btnOK.DialogResult = DialogResult.OK;
|
||||
btnOK.Location = new Point(337, 16);
|
||||
btnOK.Margin = new Padding(5);
|
||||
btnOK.Name = "btnOK";
|
||||
btnOK.Size = new Size(101, 35);
|
||||
btnOK.TabIndex = 6;
|
||||
btnOK.Text = "OK";
|
||||
btnOK.UseVisualStyleBackColor = true;
|
||||
//
|
||||
// btnCancel
|
||||
//
|
||||
btnCancel.Anchor = AnchorStyles.Left;
|
||||
btnCancel.DialogResult = DialogResult.Cancel;
|
||||
btnCancel.Location = new Point(474, 16);
|
||||
btnCancel.Margin = new Padding(5);
|
||||
btnCancel.Name = "btnCancel";
|
||||
btnCancel.Size = new Size(101, 35);
|
||||
btnCancel.TabIndex = 11;
|
||||
btnCancel.Text = "Cancel";
|
||||
btnCancel.UseVisualStyleBackColor = true;
|
||||
//
|
||||
// tableLayoutPanel1
|
||||
//
|
||||
tableLayoutPanel1.ColumnCount = 2;
|
||||
tableLayoutPanel1.ColumnStyles.Add(new ColumnStyle(SizeType.Percent, 32.06997F));
|
||||
tableLayoutPanel1.ColumnStyles.Add(new ColumnStyle(SizeType.Percent, 67.93003F));
|
||||
tableLayoutPanel1.Controls.Add(label1, 0, 0);
|
||||
tableLayoutPanel1.Controls.Add(label3, 0, 2);
|
||||
tableLayoutPanel1.Controls.Add(tbServerURL, 1, 0);
|
||||
tableLayoutPanel1.Controls.Add(tbAPIKey, 1, 2);
|
||||
tableLayoutPanel1.Controls.Add(label6, 0, 3);
|
||||
tableLayoutPanel1.Controls.Add(tbOTP, 1, 3);
|
||||
tableLayoutPanel1.Controls.Add(cbUseSSO, 0, 1);
|
||||
tableLayoutPanel1.Dock = DockStyle.Top;
|
||||
tableLayoutPanel1.Location = new Point(0, 0);
|
||||
tableLayoutPanel1.Margin = new Padding(5);
|
||||
tableLayoutPanel1.Name = "tableLayoutPanel1";
|
||||
tableLayoutPanel1.RowCount = 5;
|
||||
tableLayoutPanel1.RowStyles.Add(new RowStyle(SizeType.Percent, 20F));
|
||||
tableLayoutPanel1.RowStyles.Add(new RowStyle(SizeType.Percent, 20F));
|
||||
tableLayoutPanel1.RowStyles.Add(new RowStyle(SizeType.Percent, 20F));
|
||||
tableLayoutPanel1.RowStyles.Add(new RowStyle(SizeType.Percent, 20F));
|
||||
tableLayoutPanel1.RowStyles.Add(new RowStyle(SizeType.Percent, 20F));
|
||||
tableLayoutPanel1.Size = new Size(914, 212);
|
||||
tableLayoutPanel1.TabIndex = 12;
|
||||
//
|
||||
// label1
|
||||
//
|
||||
label1.AutoSize = true;
|
||||
label1.Dock = DockStyle.Fill;
|
||||
label1.Location = new Point(5, 0);
|
||||
label1.Margin = new Padding(5, 0, 5, 0);
|
||||
label1.Name = "label1";
|
||||
label1.Size = new Size(283, 42);
|
||||
label1.TabIndex = 2;
|
||||
label1.Text = "Passwordstate URL";
|
||||
label1.TextAlign = ContentAlignment.MiddleLeft;
|
||||
//
|
||||
// label6
|
||||
//
|
||||
label6.AutoSize = true;
|
||||
label6.Dock = DockStyle.Fill;
|
||||
label6.Location = new Point(3, 126);
|
||||
label6.Name = "label6";
|
||||
label6.Size = new Size(287, 42);
|
||||
label6.TabIndex = 15;
|
||||
label6.Text = "2FA OTP (Optional)";
|
||||
//
|
||||
// tbOTP
|
||||
//
|
||||
tbOTP.Dock = DockStyle.Fill;
|
||||
tbOTP.Location = new Point(298, 131);
|
||||
tbOTP.Margin = new Padding(5);
|
||||
tbOTP.Name = "tbOTP";
|
||||
tbOTP.Size = new Size(611, 27);
|
||||
tbOTP.TabIndex = 5;
|
||||
//
|
||||
// cbUseSSO
|
||||
//
|
||||
cbUseSSO.Anchor = AnchorStyles.Left;
|
||||
cbUseSSO.AutoSize = true;
|
||||
cbUseSSO.Location = new Point(5, 53);
|
||||
cbUseSSO.Margin = new Padding(5, 5, 5, 0);
|
||||
cbUseSSO.Name = "cbUseSSO";
|
||||
cbUseSSO.Size = new Size(157, 24);
|
||||
cbUseSSO.TabIndex = 14;
|
||||
cbUseSSO.Text = "Use SSO / WinAuth";
|
||||
cbUseSSO.UseVisualStyleBackColor = true;
|
||||
cbUseSSO.CheckedChanged += cbUseSSO_CheckedChanged;
|
||||
//
|
||||
// tableLayoutPanel2
|
||||
//
|
||||
tableLayoutPanel2.ColumnCount = 5;
|
||||
tableLayoutPanel2.ColumnStyles.Add(new ColumnStyle(SizeType.Absolute, 106F));
|
||||
tableLayoutPanel2.ColumnStyles.Add(new ColumnStyle(SizeType.Percent, 50F));
|
||||
tableLayoutPanel2.ColumnStyles.Add(new ColumnStyle(SizeType.Absolute, 26F));
|
||||
tableLayoutPanel2.ColumnStyles.Add(new ColumnStyle(SizeType.Percent, 50F));
|
||||
tableLayoutPanel2.ColumnStyles.Add(new ColumnStyle(SizeType.Absolute, 107F));
|
||||
tableLayoutPanel2.Controls.Add(btnOK, 1, 0);
|
||||
tableLayoutPanel2.Controls.Add(btnCancel, 3, 0);
|
||||
tableLayoutPanel2.Dock = DockStyle.Bottom;
|
||||
tableLayoutPanel2.Location = new Point(0, 300);
|
||||
tableLayoutPanel2.Margin = new Padding(5);
|
||||
tableLayoutPanel2.Name = "tableLayoutPanel2";
|
||||
tableLayoutPanel2.RowCount = 1;
|
||||
tableLayoutPanel2.RowStyles.Add(new RowStyle(SizeType.Percent, 100F));
|
||||
tableLayoutPanel2.Size = new Size(914, 67);
|
||||
tableLayoutPanel2.TabIndex = 13;
|
||||
//
|
||||
// label4
|
||||
//
|
||||
label4.AutoSize = true;
|
||||
label4.Dock = DockStyle.Fill;
|
||||
label4.Location = new Point(0, 212);
|
||||
label4.Margin = new Padding(5, 0, 5, 0);
|
||||
label4.Name = "label4";
|
||||
label4.Size = new Size(345, 20);
|
||||
label4.TabIndex = 14;
|
||||
label4.Text = "URL is the base URL, like https://pass.domain.local/";
|
||||
label4.TextAlign = ContentAlignment.MiddleLeft;
|
||||
//
|
||||
// CPSConnectionForm
|
||||
//
|
||||
AcceptButton = btnOK;
|
||||
AutoScaleDimensions = new SizeF(8F, 20F);
|
||||
AutoScaleMode = AutoScaleMode.Font;
|
||||
ClientSize = new Size(914, 367);
|
||||
Controls.Add(label4);
|
||||
Controls.Add(tableLayoutPanel2);
|
||||
Controls.Add(tableLayoutPanel1);
|
||||
Icon = (Icon)resources.GetObject("$this.Icon");
|
||||
Margin = new Padding(5);
|
||||
Name = "CPSConnectionForm";
|
||||
Text = "Passwordstate API Login Data";
|
||||
Activated += CPSConnectionForm_Activated;
|
||||
tableLayoutPanel1.ResumeLayout(false);
|
||||
tableLayoutPanel1.PerformLayout();
|
||||
tableLayoutPanel2.ResumeLayout(false);
|
||||
ResumeLayout(false);
|
||||
PerformLayout();
|
||||
}
|
||||
|
||||
#endregion
|
||||
private System.Windows.Forms.Label label3;
|
||||
|
||||
public System.Windows.Forms.TextBox tbServerURL;
|
||||
//public System.Windows.Forms.TextBox tbUsername;
|
||||
public System.Windows.Forms.TextBox tbAPIKey;
|
||||
private System.Windows.Forms.Button btnOK;
|
||||
private System.Windows.Forms.Button btnCancel;
|
||||
private System.Windows.Forms.TableLayoutPanel tableLayoutPanel1;
|
||||
private System.Windows.Forms.Label label2;
|
||||
private System.Windows.Forms.Label label1;
|
||||
private System.Windows.Forms.TableLayoutPanel tableLayoutPanel2;
|
||||
public System.Windows.Forms.CheckBox cbUseSSO;
|
||||
private System.Windows.Forms.Label label4;
|
||||
private Label label6;
|
||||
public TextBox tbOTP;
|
||||
}
|
||||
}
|
||||
@@ -1,41 +0,0 @@
|
||||
namespace ExternalConnectors.CPS
|
||||
{
|
||||
public partial class CPSConnectionForm : Form
|
||||
{
|
||||
public CPSConnectionForm()
|
||||
{
|
||||
InitializeComponent();
|
||||
}
|
||||
|
||||
private void CPSConnectionForm_Activated(object sender, EventArgs e)
|
||||
{
|
||||
SetVisibility();
|
||||
if (cbUseSSO.Checked)
|
||||
btnOK.Focus();
|
||||
else
|
||||
{
|
||||
if (tbAPIKey.Text.Length == 0)
|
||||
tbAPIKey.Focus();
|
||||
else
|
||||
tbOTP.Focus();
|
||||
}
|
||||
|
||||
tbAPIKey.Focus();
|
||||
if (!string.IsNullOrEmpty(tbAPIKey.Text) || cbUseSSO.Checked == true)
|
||||
tbOTP.Focus();
|
||||
|
||||
|
||||
}
|
||||
|
||||
private void cbUseSSO_CheckedChanged(object sender, EventArgs e)
|
||||
{
|
||||
SetVisibility();
|
||||
}
|
||||
private void SetVisibility()
|
||||
{
|
||||
bool ch = cbUseSSO.Checked;
|
||||
tbAPIKey.Enabled = !ch;
|
||||
//tbUsername.Enabled = !ch;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,295 +0,0 @@
|
||||
using Microsoft.Win32;
|
||||
using Org.BouncyCastle.Crypto;
|
||||
using Org.BouncyCastle.Crypto.Parameters;
|
||||
using Org.BouncyCastle.OpenSsl;
|
||||
using Org.BouncyCastle.Security;
|
||||
using System.Security.Cryptography;
|
||||
using System.Text.Json;
|
||||
using System.Text.Json.Nodes;
|
||||
|
||||
namespace ExternalConnectors.CPS;
|
||||
|
||||
public class PasswordstateInterface
|
||||
{
|
||||
private static class CPSConnectionData
|
||||
{
|
||||
public static string ssUsername = "";
|
||||
public static string ssPassword = "";
|
||||
public static string ssUrl = "";
|
||||
public static string ssOTP = "";
|
||||
public static DateTime ssOTPTimeStampExpiration;
|
||||
|
||||
public static bool ssSSO = false;
|
||||
public static bool initdone = false;
|
||||
|
||||
//token
|
||||
//public static string ssTokenBearer = "";
|
||||
//public static DateTime ssTokenExpiresOn = DateTime.UtcNow;
|
||||
//public static string ssTokenRefresh = "";
|
||||
|
||||
public static void Init()
|
||||
{
|
||||
// 2024-05-04 passwordstate currently does not support auth tokens, so we need to re-enter otp codes frequently
|
||||
if (!string.IsNullOrEmpty(ssOTP) && DateTime.Now > ssOTPTimeStampExpiration)
|
||||
{
|
||||
ssOTP = "";
|
||||
initdone = false;
|
||||
}
|
||||
|
||||
if (initdone == true)
|
||||
return;
|
||||
|
||||
RegistryKey key = Registry.CurrentUser.CreateSubKey(@"SOFTWARE\mRemoteCPSInterface");
|
||||
try
|
||||
{
|
||||
// display gui and ask for data
|
||||
CPSConnectionForm f = new CPSConnectionForm();
|
||||
//string? un = key.GetValue("Username") as string;
|
||||
//f.tbUsername.Text = un ?? "";
|
||||
f.tbAPIKey.Text = CPSConnectionData.ssPassword; // in OTP refresh cases, this value might already be filled
|
||||
|
||||
string? url = key.GetValue("URL") as string;
|
||||
if (url == null || !url.Contains("://"))
|
||||
url = "https://cred.domain.local/SecretServer";
|
||||
f.tbServerURL.Text = url;
|
||||
|
||||
var b = key.GetValue("SSO");
|
||||
if (b == null || (string)b != "True")
|
||||
ssSSO = false;
|
||||
else
|
||||
ssSSO = true;
|
||||
f.cbUseSSO.Checked = ssSSO;
|
||||
|
||||
// show dialog
|
||||
while (true)
|
||||
{
|
||||
_ = f.ShowDialog();
|
||||
|
||||
if (f.DialogResult != DialogResult.OK)
|
||||
return;
|
||||
|
||||
// store values to memory
|
||||
//ssUsername = f.tbUsername.Text;
|
||||
ssPassword = f.tbAPIKey.Text;
|
||||
ssUrl = f.tbServerURL.Text;
|
||||
ssSSO = f.cbUseSSO.Checked;
|
||||
ssOTP = f.tbOTP.Text;
|
||||
ssOTPTimeStampExpiration = DateTime.Now.AddSeconds(30);
|
||||
// check connection first
|
||||
try
|
||||
{
|
||||
if (TestCredentials() == true)
|
||||
{
|
||||
initdone = true;
|
||||
break;
|
||||
}
|
||||
}
|
||||
catch (Exception)
|
||||
{
|
||||
MessageBox.Show("Test Credentials failed - please check your credentials");
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
// write values to registry
|
||||
//key.SetValue("Username", ssUsername);
|
||||
key.SetValue("URL", ssUrl);
|
||||
key.SetValue("SSO", ssSSO);
|
||||
}
|
||||
catch (Exception)
|
||||
{
|
||||
throw;
|
||||
}
|
||||
finally
|
||||
{
|
||||
key.Close();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private static bool TestCredentials()
|
||||
{
|
||||
return ConnectionTest();
|
||||
}
|
||||
private static bool ConnectionTest()
|
||||
{
|
||||
if (CPSConnectionData.ssSSO)
|
||||
{
|
||||
string url = $"{CPSConnectionData.ssUrl}/winapi/passwordlists/";
|
||||
|
||||
using HttpClient client = new HttpClient(new HttpClientHandler() { UseDefaultCredentials = true });
|
||||
client.DefaultRequestHeaders.Accept.Clear();
|
||||
client.DefaultRequestHeaders.Add("User-Agent", "mRemote");
|
||||
client.DefaultRequestHeaders.Add("OTP", CPSConnectionData.ssOTP);
|
||||
|
||||
var json = client.GetStringAsync(url).Result;
|
||||
JsonNode? data = JsonSerializer.Deserialize<JsonNode>(json);
|
||||
if (data == null)
|
||||
return false;
|
||||
return true;
|
||||
}
|
||||
else
|
||||
{
|
||||
string url = $"{CPSConnectionData.ssUrl}/api/passwordlists/";
|
||||
using HttpClient client = new HttpClient();
|
||||
client.DefaultRequestHeaders.Accept.Clear();
|
||||
client.DefaultRequestHeaders.Add("User-Agent", "mRemote");
|
||||
client.DefaultRequestHeaders.Add("APIKey", CPSConnectionData.ssPassword);
|
||||
client.DefaultRequestHeaders.Add("OTP", CPSConnectionData.ssOTP);
|
||||
|
||||
var json = client.GetStringAsync(url).Result;
|
||||
JsonNode? data = JsonSerializer.Deserialize<JsonNode>(json);
|
||||
if (data == null)
|
||||
return false;
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
private static JsonNode? FetchDataWinAuth(int secretID)
|
||||
{
|
||||
string url = $"{CPSConnectionData.ssUrl}/winapi/passwords/{secretID}";
|
||||
|
||||
using HttpClient client = new HttpClient(new HttpClientHandler() { UseDefaultCredentials = true });
|
||||
client.DefaultRequestHeaders.Accept.Clear();
|
||||
client.DefaultRequestHeaders.Add("User-Agent", "mRemote");
|
||||
client.DefaultRequestHeaders.Add("OTP", CPSConnectionData.ssOTP);
|
||||
|
||||
var json = client.GetStringAsync(url).Result;
|
||||
JsonNode? data = JsonSerializer.Deserialize<JsonNode>(json);
|
||||
if (data == null)
|
||||
return null;
|
||||
JsonNode? element = data[0];
|
||||
return element;
|
||||
}
|
||||
private static JsonNode? FetchDataAPIKeyAuth(int secretID)
|
||||
{
|
||||
string url = $"{CPSConnectionData.ssUrl}/api/passwords/{secretID}";
|
||||
|
||||
using HttpClient client = new HttpClient();
|
||||
client.DefaultRequestHeaders.Accept.Clear();
|
||||
client.DefaultRequestHeaders.Add("User-Agent", "mRemote");
|
||||
client.DefaultRequestHeaders.Add("APIKey", CPSConnectionData.ssPassword);
|
||||
client.DefaultRequestHeaders.Add("OTP", CPSConnectionData.ssOTP);
|
||||
|
||||
var json = client.GetStringAsync(url).Result;
|
||||
JsonNode? data = JsonSerializer.Deserialize<JsonNode>(json);
|
||||
if (data == null)
|
||||
return null;
|
||||
JsonNode? element = data[0];
|
||||
return element;
|
||||
}
|
||||
|
||||
private static void FetchSecret(int secretID, out string secretUsername, out string secretPassword, out string secretDomain, out string privatekey)
|
||||
{
|
||||
// clear return variables
|
||||
secretDomain = "";
|
||||
secretUsername = "";
|
||||
secretPassword = "";
|
||||
privatekey = "";
|
||||
string privatekeypassphrase = "";
|
||||
JsonNode? element = null;
|
||||
|
||||
if (CPSConnectionData.ssSSO)
|
||||
element = FetchDataWinAuth(secretID);
|
||||
else
|
||||
element = FetchDataAPIKeyAuth(secretID);
|
||||
|
||||
if (element == null)
|
||||
return;
|
||||
|
||||
var dom = element["Domain"];
|
||||
if (dom != null) secretDomain = dom.ToString();
|
||||
|
||||
var user = element["UserName"];
|
||||
if (user != null) secretUsername = user.ToString();
|
||||
|
||||
var pw = element["Password"];
|
||||
if (pw != null) secretPassword = pw.ToString();
|
||||
|
||||
var privkey = element["GenericField1"];
|
||||
if (privkey != null) privatekey = privkey.ToString();
|
||||
|
||||
var phrase = element["GenericField3"];
|
||||
if (phrase != null) privatekeypassphrase = phrase.ToString();
|
||||
|
||||
// need to decode the private key?
|
||||
if (!string.IsNullOrEmpty(privatekeypassphrase))
|
||||
{
|
||||
try
|
||||
{
|
||||
var key = DecodePrivateKey(privatekey, privatekeypassphrase);
|
||||
privatekey = key;
|
||||
}
|
||||
catch(Exception)
|
||||
{
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
// conversion to putty format necessary?
|
||||
if (!string.IsNullOrEmpty(privatekey) && !privatekey.StartsWith("PuTTY-User-Key-File-2"))
|
||||
{
|
||||
try
|
||||
{
|
||||
RSACryptoServiceProvider key = ImportPrivateKey(privatekey);
|
||||
privatekey = PuttyKeyFileGenerator.ToPuttyPrivateKey(key);
|
||||
}
|
||||
catch (Exception)
|
||||
{
|
||||
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#region PUTTY KEY HANDLING
|
||||
// decode rsa private key with encryption password
|
||||
private static string DecodePrivateKey(string encryptedPrivateKey, string password)
|
||||
{
|
||||
TextReader textReader = new StringReader(encryptedPrivateKey);
|
||||
PemReader pemReader = new PemReader(textReader, new PasswordFinder(password));
|
||||
|
||||
AsymmetricCipherKeyPair keyPair = (AsymmetricCipherKeyPair)pemReader.ReadObject();
|
||||
|
||||
TextWriter textWriter = new StringWriter();
|
||||
var pemWriter = new PemWriter(textWriter);
|
||||
pemWriter.WriteObject(keyPair.Private);
|
||||
pemWriter.Writer.Flush();
|
||||
|
||||
return ""+textWriter.ToString();
|
||||
}
|
||||
private class PasswordFinder(string password) : IPasswordFinder
|
||||
{
|
||||
private string password = password;
|
||||
|
||||
public char[] GetPassword()
|
||||
{
|
||||
return password.ToCharArray();
|
||||
}
|
||||
}
|
||||
|
||||
// read private key pem string to rsacryptoserviceprovider
|
||||
public static RSACryptoServiceProvider ImportPrivateKey(string pem)
|
||||
{
|
||||
PemReader pr = new PemReader(new StringReader(pem));
|
||||
AsymmetricCipherKeyPair KeyPair = (AsymmetricCipherKeyPair)pr.ReadObject();
|
||||
RSAParameters rsaParams = DotNetUtilities.ToRSAParameters((RsaPrivateCrtKeyParameters)KeyPair.Private);
|
||||
RSACryptoServiceProvider rsa = new RSACryptoServiceProvider();
|
||||
rsa.ImportParameters(rsaParams);
|
||||
return rsa;
|
||||
}
|
||||
#endregion
|
||||
|
||||
|
||||
// input: must be the secret id to fetch
|
||||
public static void FetchSecretFromServer(string secretID, out string username, out string password, out string domain, out string privatekey)
|
||||
{
|
||||
// get secret id
|
||||
int sid = Int32.Parse(secretID);
|
||||
|
||||
// init connection credentials, display popup if necessary
|
||||
CPSConnectionData.Init();
|
||||
|
||||
// get the secret
|
||||
FetchSecret(sid, out username, out password, out domain, out privatekey);
|
||||
}
|
||||
}
|
||||
|
Before Width: | Height: | Size: 32 KiB |
281
ExternalConnectors/DSS/SSConnectionForm.Designer.cs
generated
@@ -1,281 +0,0 @@
|
||||
namespace ExternalConnectors.DSS
|
||||
{
|
||||
partial class SSConnectionForm
|
||||
{
|
||||
/// <summary>
|
||||
/// Required designer variable.
|
||||
/// </summary>
|
||||
private System.ComponentModel.IContainer components = null;
|
||||
|
||||
/// <summary>
|
||||
/// Clean up any resources being used.
|
||||
/// </summary>
|
||||
/// <param name="disposing">true if managed resources should be disposed; otherwise, false.</param>
|
||||
protected override void Dispose(bool disposing)
|
||||
{
|
||||
if (disposing && (components != null))
|
||||
{
|
||||
components.Dispose();
|
||||
}
|
||||
base.Dispose(disposing);
|
||||
}
|
||||
|
||||
#region Windows Form Designer generated code
|
||||
|
||||
/// <summary>
|
||||
/// Required method for Designer support - do not modify
|
||||
/// the contents of this method with the code editor.
|
||||
/// </summary>
|
||||
private void InitializeComponent()
|
||||
{
|
||||
System.ComponentModel.ComponentResourceManager resources = new System.ComponentModel.ComponentResourceManager(typeof(SSConnectionForm));
|
||||
this.tbSSURL = new System.Windows.Forms.TextBox();
|
||||
this.tbUsername = new System.Windows.Forms.TextBox();
|
||||
this.label3 = new System.Windows.Forms.Label();
|
||||
this.tbPassword = new System.Windows.Forms.TextBox();
|
||||
this.btnOK = new System.Windows.Forms.Button();
|
||||
this.btnCancel = new System.Windows.Forms.Button();
|
||||
this.tableLayoutPanel1 = new System.Windows.Forms.TableLayoutPanel();
|
||||
this.label5 = new System.Windows.Forms.Label();
|
||||
this.label1 = new System.Windows.Forms.Label();
|
||||
this.label2 = new System.Windows.Forms.Label();
|
||||
this.cbUseSSO = new System.Windows.Forms.CheckBox();
|
||||
this.label6 = new System.Windows.Forms.Label();
|
||||
this.tbOTP = new System.Windows.Forms.TextBox();
|
||||
this.tableLayoutPanel2 = new System.Windows.Forms.TableLayoutPanel();
|
||||
this.label4 = new System.Windows.Forms.Label();
|
||||
this.tableLayoutPanel1.SuspendLayout();
|
||||
this.tableLayoutPanel2.SuspendLayout();
|
||||
this.SuspendLayout();
|
||||
//
|
||||
// tbSSURL
|
||||
//
|
||||
this.tbSSURL.Dock = System.Windows.Forms.DockStyle.Fill;
|
||||
this.tbSSURL.Location = new System.Drawing.Point(298, 5);
|
||||
this.tbSSURL.Margin = new System.Windows.Forms.Padding(5, 5, 5, 5);
|
||||
this.tbSSURL.Name = "tbSSURL";
|
||||
this.tbSSURL.Size = new System.Drawing.Size(611, 27);
|
||||
this.tbSSURL.TabIndex = 0;
|
||||
//
|
||||
// tbUsername
|
||||
//
|
||||
this.tbUsername.Dock = System.Windows.Forms.DockStyle.Fill;
|
||||
this.tbUsername.Location = new System.Drawing.Point(298, 47);
|
||||
this.tbUsername.Margin = new System.Windows.Forms.Padding(5, 5, 5, 5);
|
||||
this.tbUsername.Name = "tbUsername";
|
||||
this.tbUsername.Size = new System.Drawing.Size(611, 27);
|
||||
this.tbUsername.TabIndex = 2;
|
||||
//
|
||||
// label3
|
||||
//
|
||||
this.label3.AutoSize = true;
|
||||
this.label3.Dock = System.Windows.Forms.DockStyle.Fill;
|
||||
this.label3.Location = new System.Drawing.Point(5, 84);
|
||||
this.label3.Margin = new System.Windows.Forms.Padding(5, 0, 5, 0);
|
||||
this.label3.Name = "label3";
|
||||
this.label3.Size = new System.Drawing.Size(283, 42);
|
||||
this.label3.TabIndex = 5;
|
||||
this.label3.Text = "Password";
|
||||
this.label3.TextAlign = System.Drawing.ContentAlignment.MiddleLeft;
|
||||
//
|
||||
// tbPassword
|
||||
//
|
||||
this.tbPassword.Dock = System.Windows.Forms.DockStyle.Fill;
|
||||
this.tbPassword.Location = new System.Drawing.Point(298, 89);
|
||||
this.tbPassword.Margin = new System.Windows.Forms.Padding(5, 5, 5, 5);
|
||||
this.tbPassword.Name = "tbPassword";
|
||||
this.tbPassword.Size = new System.Drawing.Size(611, 27);
|
||||
this.tbPassword.TabIndex = 4;
|
||||
this.tbPassword.UseSystemPasswordChar = true;
|
||||
//
|
||||
// btnOK
|
||||
//
|
||||
this.btnOK.Anchor = System.Windows.Forms.AnchorStyles.Right;
|
||||
this.btnOK.DialogResult = System.Windows.Forms.DialogResult.OK;
|
||||
this.btnOK.Location = new System.Drawing.Point(337, 16);
|
||||
this.btnOK.Margin = new System.Windows.Forms.Padding(5, 5, 5, 5);
|
||||
this.btnOK.Name = "btnOK";
|
||||
this.btnOK.Size = new System.Drawing.Size(101, 35);
|
||||
this.btnOK.TabIndex = 6;
|
||||
this.btnOK.Text = "OK";
|
||||
this.btnOK.UseVisualStyleBackColor = true;
|
||||
//
|
||||
// btnCancel
|
||||
//
|
||||
this.btnCancel.Anchor = System.Windows.Forms.AnchorStyles.Left;
|
||||
this.btnCancel.DialogResult = System.Windows.Forms.DialogResult.Cancel;
|
||||
this.btnCancel.Location = new System.Drawing.Point(474, 16);
|
||||
this.btnCancel.Margin = new System.Windows.Forms.Padding(5, 5, 5, 5);
|
||||
this.btnCancel.Name = "btnCancel";
|
||||
this.btnCancel.Size = new System.Drawing.Size(101, 35);
|
||||
this.btnCancel.TabIndex = 11;
|
||||
this.btnCancel.Text = "Cancel";
|
||||
this.btnCancel.UseVisualStyleBackColor = true;
|
||||
//
|
||||
// tableLayoutPanel1
|
||||
//
|
||||
this.tableLayoutPanel1.ColumnCount = 2;
|
||||
this.tableLayoutPanel1.ColumnStyles.Add(new System.Windows.Forms.ColumnStyle(System.Windows.Forms.SizeType.Percent, 32.06997F));
|
||||
this.tableLayoutPanel1.ColumnStyles.Add(new System.Windows.Forms.ColumnStyle(System.Windows.Forms.SizeType.Percent, 67.93003F));
|
||||
this.tableLayoutPanel1.Controls.Add(this.label5, 1, 4);
|
||||
this.tableLayoutPanel1.Controls.Add(this.label1, 0, 0);
|
||||
this.tableLayoutPanel1.Controls.Add(this.label2, 0, 1);
|
||||
this.tableLayoutPanel1.Controls.Add(this.label3, 0, 2);
|
||||
this.tableLayoutPanel1.Controls.Add(this.tbSSURL, 1, 0);
|
||||
this.tableLayoutPanel1.Controls.Add(this.cbUseSSO, 0, 4);
|
||||
this.tableLayoutPanel1.Controls.Add(this.tbUsername, 1, 1);
|
||||
this.tableLayoutPanel1.Controls.Add(this.tbPassword, 1, 2);
|
||||
this.tableLayoutPanel1.Controls.Add(this.label6, 0, 3);
|
||||
this.tableLayoutPanel1.Controls.Add(this.tbOTP, 1, 3);
|
||||
this.tableLayoutPanel1.Dock = System.Windows.Forms.DockStyle.Top;
|
||||
this.tableLayoutPanel1.Location = new System.Drawing.Point(0, 0);
|
||||
this.tableLayoutPanel1.Margin = new System.Windows.Forms.Padding(5, 5, 5, 5);
|
||||
this.tableLayoutPanel1.Name = "tableLayoutPanel1";
|
||||
this.tableLayoutPanel1.RowCount = 5;
|
||||
this.tableLayoutPanel1.RowStyles.Add(new System.Windows.Forms.RowStyle(System.Windows.Forms.SizeType.Percent, 20F));
|
||||
this.tableLayoutPanel1.RowStyles.Add(new System.Windows.Forms.RowStyle(System.Windows.Forms.SizeType.Percent, 20F));
|
||||
this.tableLayoutPanel1.RowStyles.Add(new System.Windows.Forms.RowStyle(System.Windows.Forms.SizeType.Percent, 20F));
|
||||
this.tableLayoutPanel1.RowStyles.Add(new System.Windows.Forms.RowStyle(System.Windows.Forms.SizeType.Percent, 20F));
|
||||
this.tableLayoutPanel1.RowStyles.Add(new System.Windows.Forms.RowStyle(System.Windows.Forms.SizeType.Percent, 20F));
|
||||
this.tableLayoutPanel1.Size = new System.Drawing.Size(914, 212);
|
||||
this.tableLayoutPanel1.TabIndex = 12;
|
||||
//
|
||||
// label5
|
||||
//
|
||||
this.label5.AutoSize = true;
|
||||
this.label5.Dock = System.Windows.Forms.DockStyle.Fill;
|
||||
this.label5.Location = new System.Drawing.Point(298, 168);
|
||||
this.label5.Margin = new System.Windows.Forms.Padding(5, 0, 5, 0);
|
||||
this.label5.Name = "label5";
|
||||
this.label5.Size = new System.Drawing.Size(611, 44);
|
||||
this.label5.TabIndex = 15;
|
||||
this.label5.Text = "For SSO to work, additional IIS configuration is required!";
|
||||
this.label5.TextAlign = System.Drawing.ContentAlignment.MiddleLeft;
|
||||
//
|
||||
// label1
|
||||
//
|
||||
this.label1.AutoSize = true;
|
||||
this.label1.Dock = System.Windows.Forms.DockStyle.Fill;
|
||||
this.label1.Location = new System.Drawing.Point(5, 0);
|
||||
this.label1.Margin = new System.Windows.Forms.Padding(5, 0, 5, 0);
|
||||
this.label1.Name = "label1";
|
||||
this.label1.Size = new System.Drawing.Size(283, 42);
|
||||
this.label1.TabIndex = 2;
|
||||
this.label1.Text = "Secret Server URL";
|
||||
this.label1.TextAlign = System.Drawing.ContentAlignment.MiddleLeft;
|
||||
//
|
||||
// label2
|
||||
//
|
||||
this.label2.AutoSize = true;
|
||||
this.label2.Dock = System.Windows.Forms.DockStyle.Fill;
|
||||
this.label2.Location = new System.Drawing.Point(5, 42);
|
||||
this.label2.Margin = new System.Windows.Forms.Padding(5, 0, 5, 0);
|
||||
this.label2.Name = "label2";
|
||||
this.label2.Size = new System.Drawing.Size(283, 42);
|
||||
this.label2.TabIndex = 4;
|
||||
this.label2.Text = "DOMAIN\\Username";
|
||||
this.label2.TextAlign = System.Drawing.ContentAlignment.MiddleLeft;
|
||||
//
|
||||
// cbUseSSO
|
||||
//
|
||||
this.cbUseSSO.AutoSize = true;
|
||||
this.cbUseSSO.Location = new System.Drawing.Point(5, 173);
|
||||
this.cbUseSSO.Margin = new System.Windows.Forms.Padding(5, 5, 5, 0);
|
||||
this.cbUseSSO.Name = "cbUseSSO";
|
||||
this.cbUseSSO.Size = new System.Drawing.Size(86, 24);
|
||||
this.cbUseSSO.TabIndex = 14;
|
||||
this.cbUseSSO.Text = "Use SSO";
|
||||
this.cbUseSSO.UseVisualStyleBackColor = true;
|
||||
this.cbUseSSO.CheckedChanged += new System.EventHandler(this.cbUseSSO_CheckedChanged);
|
||||
//
|
||||
// label6
|
||||
//
|
||||
this.label6.AutoSize = true;
|
||||
this.label6.Dock = System.Windows.Forms.DockStyle.Fill;
|
||||
this.label6.Location = new System.Drawing.Point(3, 126);
|
||||
this.label6.Name = "label6";
|
||||
this.label6.Size = new System.Drawing.Size(287, 42);
|
||||
this.label6.TabIndex = 15;
|
||||
this.label6.Text = "2FA OTP (Optional)";
|
||||
//
|
||||
// tbOTP
|
||||
//
|
||||
this.tbOTP.Dock = System.Windows.Forms.DockStyle.Fill;
|
||||
this.tbOTP.Location = new System.Drawing.Point(296, 130);
|
||||
this.tbOTP.Margin = new System.Windows.Forms.Padding(3, 4, 3, 4);
|
||||
this.tbOTP.Name = "tbOTP";
|
||||
this.tbOTP.Size = new System.Drawing.Size(615, 27);
|
||||
this.tbOTP.TabIndex = 5;
|
||||
//
|
||||
// tableLayoutPanel2
|
||||
//
|
||||
this.tableLayoutPanel2.ColumnCount = 5;
|
||||
this.tableLayoutPanel2.ColumnStyles.Add(new System.Windows.Forms.ColumnStyle(System.Windows.Forms.SizeType.Absolute, 106F));
|
||||
this.tableLayoutPanel2.ColumnStyles.Add(new System.Windows.Forms.ColumnStyle(System.Windows.Forms.SizeType.Percent, 50F));
|
||||
this.tableLayoutPanel2.ColumnStyles.Add(new System.Windows.Forms.ColumnStyle(System.Windows.Forms.SizeType.Absolute, 26F));
|
||||
this.tableLayoutPanel2.ColumnStyles.Add(new System.Windows.Forms.ColumnStyle(System.Windows.Forms.SizeType.Percent, 50F));
|
||||
this.tableLayoutPanel2.ColumnStyles.Add(new System.Windows.Forms.ColumnStyle(System.Windows.Forms.SizeType.Absolute, 107F));
|
||||
this.tableLayoutPanel2.Controls.Add(this.btnOK, 1, 0);
|
||||
this.tableLayoutPanel2.Controls.Add(this.btnCancel, 3, 0);
|
||||
this.tableLayoutPanel2.Dock = System.Windows.Forms.DockStyle.Bottom;
|
||||
this.tableLayoutPanel2.Location = new System.Drawing.Point(0, 300);
|
||||
this.tableLayoutPanel2.Margin = new System.Windows.Forms.Padding(5, 5, 5, 5);
|
||||
this.tableLayoutPanel2.Name = "tableLayoutPanel2";
|
||||
this.tableLayoutPanel2.RowCount = 1;
|
||||
this.tableLayoutPanel2.RowStyles.Add(new System.Windows.Forms.RowStyle(System.Windows.Forms.SizeType.Percent, 100F));
|
||||
this.tableLayoutPanel2.Size = new System.Drawing.Size(914, 67);
|
||||
this.tableLayoutPanel2.TabIndex = 13;
|
||||
//
|
||||
// label4
|
||||
//
|
||||
this.label4.AutoSize = true;
|
||||
this.label4.Dock = System.Windows.Forms.DockStyle.Fill;
|
||||
this.label4.Location = new System.Drawing.Point(0, 212);
|
||||
this.label4.Margin = new System.Windows.Forms.Padding(5, 0, 5, 0);
|
||||
this.label4.Name = "label4";
|
||||
this.label4.Size = new System.Drawing.Size(427, 20);
|
||||
this.label4.TabIndex = 14;
|
||||
this.label4.Text = "URL is the base URL, like https://cred.domain.local/SecretServer";
|
||||
this.label4.TextAlign = System.Drawing.ContentAlignment.MiddleLeft;
|
||||
//
|
||||
// SSConnectionForm
|
||||
//
|
||||
this.AcceptButton = this.btnOK;
|
||||
this.AutoScaleDimensions = new System.Drawing.SizeF(8F, 20F);
|
||||
this.AutoScaleMode = System.Windows.Forms.AutoScaleMode.Font;
|
||||
this.ClientSize = new System.Drawing.Size(914, 367);
|
||||
this.Controls.Add(this.label4);
|
||||
this.Controls.Add(this.tableLayoutPanel2);
|
||||
this.Controls.Add(this.tableLayoutPanel1);
|
||||
this.Icon = ((System.Drawing.Icon)(resources.GetObject("$this.Icon")));
|
||||
this.Margin = new System.Windows.Forms.Padding(5, 5, 5, 5);
|
||||
this.Name = "SSConnectionForm";
|
||||
this.Text = "Secret Server API Login Data";
|
||||
this.Activated += new System.EventHandler(this.SSConnectionForm_Activated);
|
||||
this.tableLayoutPanel1.ResumeLayout(false);
|
||||
this.tableLayoutPanel1.PerformLayout();
|
||||
this.tableLayoutPanel2.ResumeLayout(false);
|
||||
this.ResumeLayout(false);
|
||||
this.PerformLayout();
|
||||
|
||||
}
|
||||
|
||||
#endregion
|
||||
private System.Windows.Forms.Label label3;
|
||||
|
||||
public System.Windows.Forms.TextBox tbSSURL;
|
||||
public System.Windows.Forms.TextBox tbUsername;
|
||||
public System.Windows.Forms.TextBox tbPassword;
|
||||
private System.Windows.Forms.Button btnOK;
|
||||
private System.Windows.Forms.Button btnCancel;
|
||||
private System.Windows.Forms.TableLayoutPanel tableLayoutPanel1;
|
||||
private System.Windows.Forms.Label label2;
|
||||
private System.Windows.Forms.Label label1;
|
||||
private System.Windows.Forms.TableLayoutPanel tableLayoutPanel2;
|
||||
public System.Windows.Forms.CheckBox cbUseSSO;
|
||||
private System.Windows.Forms.Label label4;
|
||||
private Label label5;
|
||||
private Label label6;
|
||||
public TextBox tbOTP;
|
||||
}
|
||||
}
|
||||
@@ -1,35 +0,0 @@
|
||||
namespace ExternalConnectors.DSS
|
||||
{
|
||||
public partial class SSConnectionForm : Form
|
||||
{
|
||||
public SSConnectionForm()
|
||||
{
|
||||
InitializeComponent();
|
||||
}
|
||||
|
||||
private void SSConnectionForm_Activated(object sender, EventArgs e)
|
||||
{
|
||||
SetVisibility();
|
||||
if (cbUseSSO.Checked)
|
||||
btnOK.Focus();
|
||||
else
|
||||
{
|
||||
if (tbPassword.Text.Length == 0)
|
||||
tbPassword.Focus();
|
||||
else
|
||||
tbOTP.Focus();
|
||||
}
|
||||
}
|
||||
|
||||
private void cbUseSSO_CheckedChanged(object sender, EventArgs e)
|
||||
{
|
||||
SetVisibility();
|
||||
}
|
||||
private void SetVisibility()
|
||||
{
|
||||
bool ch = cbUseSSO.Checked;
|
||||
tbPassword.Enabled = !ch;
|
||||
tbUsername.Enabled = !ch;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,615 +0,0 @@
|
||||
<root>
|
||||
<xsd:schema id="root" xmlns="" xmlns:xsd="http://www.w3.org/2001/XMLSchema" xmlns:msdata="urn:schemas-microsoft-com:xml-msdata">
|
||||
<xsd:import namespace="http://www.w3.org/XML/1998/namespace" />
|
||||
<xsd:element name="root" msdata:IsDataSet="true">
|
||||
<xsd:complexType>
|
||||
<xsd:choice maxOccurs="unbounded">
|
||||
<xsd:element name="metadata">
|
||||
<xsd:complexType>
|
||||
<xsd:sequence>
|
||||
<xsd:element name="value" type="xsd:string" minOccurs="0" />
|
||||
</xsd:sequence>
|
||||
<xsd:attribute name="name" use="required" type="xsd:string" />
|
||||
<xsd:attribute name="type" type="xsd:string" />
|
||||
<xsd:attribute name="mimetype" type="xsd:string" />
|
||||
<xsd:attribute ref="xml:space" />
|
||||
</xsd:complexType>
|
||||
</xsd:element>
|
||||
<xsd:element name="assembly">
|
||||
<xsd:complexType>
|
||||
<xsd:attribute name="alias" type="xsd:string" />
|
||||
<xsd:attribute name="name" type="xsd:string" />
|
||||
</xsd:complexType>
|
||||
</xsd:element>
|
||||
<xsd:element name="data">
|
||||
<xsd:complexType>
|
||||
<xsd:sequence>
|
||||
<xsd:element name="value" type="xsd:string" minOccurs="0" msdata:Ordinal="1" />
|
||||
<xsd:element name="comment" type="xsd:string" minOccurs="0" msdata:Ordinal="2" />
|
||||
</xsd:sequence>
|
||||
<xsd:attribute name="name" type="xsd:string" use="required" msdata:Ordinal="1" />
|
||||
<xsd:attribute name="type" type="xsd:string" msdata:Ordinal="3" />
|
||||
<xsd:attribute name="mimetype" type="xsd:string" msdata:Ordinal="4" />
|
||||
<xsd:attribute ref="xml:space" />
|
||||
</xsd:complexType>
|
||||
</xsd:element>
|
||||
<xsd:element name="resheader">
|
||||
<xsd:complexType>
|
||||
<xsd:sequence>
|
||||
<xsd:element name="value" type="xsd:string" minOccurs="0" msdata:Ordinal="1" />
|
||||
</xsd:sequence>
|
||||
<xsd:attribute name="name" type="xsd:string" use="required" />
|
||||
</xsd:complexType>
|
||||
</xsd:element>
|
||||
</xsd:choice>
|
||||
</xsd:complexType>
|
||||
</xsd:element>
|
||||
</xsd:schema>
|
||||
<resheader name="resmimetype">
|
||||
<value>text/microsoft-resx</value>
|
||||
</resheader>
|
||||
<resheader name="version">
|
||||
<value>2.0</value>
|
||||
</resheader>
|
||||
<resheader name="reader">
|
||||
<value>System.Resources.ResXResourceReader, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089</value>
|
||||
</resheader>
|
||||
<resheader name="writer">
|
||||
<value>System.Resources.ResXResourceWriter, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089</value>
|
||||
</resheader>
|
||||
<assembly alias="System.Drawing" name="System.Drawing, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a" />
|
||||
<data name="$this.Icon" type="System.Drawing.Icon, System.Drawing" mimetype="application/x-microsoft.net.object.bytearray.base64">
|
||||
<value>
|
||||
AAABAAQAQEAAAAEAIAAoQAAARgAAACAgAAABACAAKBAAAG5AAAAYGAAAAQAgACgJAACWUAAAEBAAAAEA
|
||||
IAAoBAAAvlkAACgAAABAAAAAgAAAAAEAIAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAD9/f3e/f397vz9
|
||||
/e79/f3u7ezr7p6Xju5XTEHuNScV7i8hCe4rHwjuKx8I7isfCO4rHwjuKx8I7isfCO4rHwjuKx8I7isf
|
||||
CO4rHwjuKx8I7isfCO4rHwjuKx8I7isfCO4rHwjuKx8I7isfCO4rHwjuKx8I7isfCO4rHwjuKx8I7isf
|
||||
CO4rHwjuKx8I7isfCO4rHwjuKx8I7isfCO4rHwjuKx8I7isfCO4rHwjuKx8I7isfCO4rHwjuKx8I7isf
|
||||
CO4rHwjuKx8I7isfCO4rHwjuKx8I7isfCO4rHAjuKx8I7jYoFu5XTUHumJGO7u3r6+79/f3u/f397v39
|
||||
/e79/f3e/f399P//////////u7Wy/0M1KP8jEwD/KRgA/ywdBv8tHwj/LiAJ/y4gCf8uIAn/LiAJ/y4g
|
||||
Cf8uIAn/LiAJ/y4gCf8uIAn/LiAJ/y4gCf8uIAn/LiAJ/y4gCf8uIAn/LiAJ/y4gCf8uIAn/LiAJ/y4g
|
||||
Cf8uIAn/LiAJ/y4gCf8uIAn/LiAJ/y4gCf8uIAn/LiAJ/y4gCf8uIAn/LiAJ/y4gCf8uIAn/LiAJ/y4g
|
||||
Cf8uIAn/LiAJ/y4gCf8uIAn/LiAJ/y4gCf8uIAn/LiAJ/y4gCf8uIAn/LiAJ/y4gCf8sHAT/KRcA/yAR
|
||||
AP9DNSX/ubOt/////////////f399Pv8/e7/////m5aK/xoMAP8lFAD/LSAI/y0gCv8uIAr/LR8J/y0f
|
||||
Cf8tHwn/LR8J/y0fCf8tHwn/LR8J/y0fCf8tHwn/LR8J/y0fCf8tHwn/LR8J/y0fCf8tHwn/LR8J/y0f
|
||||
Cf8tHwn/LR8J/y0fCf8tHwn/LR8J/y0fCf8tHwn/LR8J/y0fCf8tHwn/LR8J/y0fCf8tHwn/LR8J/y0f
|
||||
Cf8tHwn/LR8J/y0fCf8tHwn/LR8J/y0fCf8tHwn/LR8J/y0fCf8tHwn/LR8J/y0fCf8tHwn/LR8J/y0f
|
||||
Cv8uIAr/LB4J/ysfCf8uIQj/JBYA/xoLAP+elYz///////z9/O79/f3uvLWw/xsNAP8rHAX/LiAK/y0f
|
||||
Cf8tHwj/LR8I/y0fCf8tHwj/LR8I/y0fCP8tHwj/LR8I/y0fCP8tHwj/LR8I/y0fCP8tHwj/LR8I/y0f
|
||||
CP8tHwj/LR8I/y0fCP8tHwj/LR8I/y0fCP8tHwj/LR8I/y0fCP8tHwj/LR8I/y0fCP8tHwj/LR8I/y0f
|
||||
CP8tHwj/LR8I/y0fCP8tHwj/LR8I/y0fCP8tHwj/LR8I/y0fCP8tHwj/LR8I/y0fCP8tHwj/LR8I/y0f
|
||||
CP8tHwj/LR8I/y0fCP8tHwj/LR8I/y0fCf8tHwn/LR8J/y4gCv8pGgP/GgsA/7y3sP/9/f3u6unp7kM0
|
||||
I/8nFwH/LiEL/yweCP8tHwn/LR8J/y0fCf8tHwn/LR8J/y0fCf8tHwn/LR8J/y0fCf8tHwn/LR8J/y0f
|
||||
Cf8tHwn/LR8J/y0fCf8tHwn/LR8J/y0fCf8tHwn/LR8J/y0fCf8tHwn/LR8J/y0fCf8tHwn/LR8J/y0f
|
||||
Cf8tHwn/LR8J/y0fCf8tHwn/LR8J/y0fCf8tHwn/LR8J/y0fCf8tHwn/LR8J/y0fCf8tHwn/LR8J/y0f
|
||||
Cf8tHwn/LR8J/y0fCf8tHwn/LR8J/y0fCf8tHwn/LR8J/y0fCf8tHwn/LR8J/y0fCf8uIAr/LR8J/yIU
|
||||
AP9DNSL/6+nq7pqUju4fEQD/LiAI/y0fCf8tHwn/LR8J/y0fCf8tHwn/LR8J/y0fCf8tHwn/LR8J/y0f
|
||||
Cf8tHwn/LR8J/y0fCf8tHwn/LR8J/y0fCf8tHwn/LR8J/y0fCf8tHwn/LR8J/y0fCf8tHwn/LR8J/y0f
|
||||
Cf8tHwn/LR8J/y0fCf8tHwn/LR8J/y0fCf8tHwn/LR8J/y0fCf8tHwn/LR8J/y0fCf8tHwn/LR8J/y0f
|
||||
Cf8tHwn/LR8J/y0fCf8tHwn/LR8J/y0fCf8tHwn/LR8J/y0fCf8tHwn/LR8J/y0fCf8tHwn/LR8J/y0f
|
||||
Cf8tHwn/LR8J/y0fCf8uIAf/HxAA/5qTju5YTT7uJRYA/ywgCf8tHwn/LR8J/y0fCf8tHwn/LR8J/y0f
|
||||
Cf8tHwn/LR8J/y0fCf8tHwn/LR8J/y0fCf8tHwn/LR8J/y0fCf8tHwn/LR8J/y0fCf8tHwn/LR8J/y0f
|
||||
Cf8tHwn/LR8J/y0fCf8tHwn/LR8J/y0fCf8tHwn/LR8J/y0fCf8tHwn/LR8J/y0fCf8tHwn/LR8J/y0f
|
||||
Cf8tHwn/LR8J/y0fCf8tHwn/LR8J/y0fCf8tHwn/LR8J/y0fCf8tHwn/LR8J/y0fCf8tHwn/LR8J/y0f
|
||||
Cf8tHwn/LR8J/y0fCf8tHwn/LR8J/y0fCf8tHwn/Kx8I/ygXAP9YTkDuOCkV7iweBv8sHwn/LR8J/y0f
|
||||
Cf8tHwn/LR8J/y0fCf8tHwn/LR8J/y0fCf8tHwn/LR8J/y0fCf8tHwn/LR8J/y0fCf8tHwn/LR8J/y0f
|
||||
Cf8tHwn/LR8J/y0fCf8tHwn/LR8J/y0fCf8tHwn/LR8J/y0fCf8tHwn/LR8J/y0fCf8tHwn/LR8J/y0f
|
||||
Cf8tHwn/LR8J/y0fCf8tHwn/LR8J/y0fCf8tHwn/LR8J/y0fCf8tHwn/LR8J/y0fCf8tHwn/LR8J/y0f
|
||||
Cf8tHwn/LR8J/y0fCf8tHwn/LR8J/y0fCf8tHwn/LR8J/y0fCf8tHwn/LR8J/y0fCf8sHQX/NykW7ioc
|
||||
CO4uIAn/LR8J/y0fCf8tHwn/LR8J/y0fCf8tHwn/LR8J/y0fCf8tHwn/LR8J/y0fCf8tHwn/LR8J/y0f
|
||||
Cf8tHwn/LR8J/y0fCf8tHwn/LR8J/y0fCf8tHwn/LR8J/y0fCf8tHwn/LR8J/y0fCf8tHwn/LR8J/y0f
|
||||
Cf8tHwn/LR8J/y0fCf8tHwn/LR8J/y0fCf8tHwn/LR8J/y0fCf8tHwn/LR8J/y0fCf8tHwn/LR8J/y0f
|
||||
Cf8tHwn/LR8J/y0fCf8tHwn/LR8J/y0fCf8tHwn/LR8J/y0fCf8tHwn/LR8J/y0fCf8tHwn/LR8J/y0f
|
||||
Cf8uIAn/Lh8I/yocCO4qHAjuLyEK/y0fCf8tHwn/LR8J/y0fCf8tHwn/LR8J/y0fCf8tHwn/LR8J/y0f
|
||||
Cf8tHwn/LR8J/y0fCf8tHwn/LR8J/y0fCf8tHwn/LR8J/y0fCf8tHwn/LR8J/y0fCf8tHwn/LR8J/y0f
|
||||
Cf8tHwn/LR8J/y0fCf8tHwn/LR8J/y0fCf8tHwn/LR8J/y0fCf8tHwn/LR8J/y0fCf8tHwn/LR8J/y0f
|
||||
Cf8tHwn/LR8J/y0fCf8tHwn/LR8J/y0fCf8tHwn/LR8J/y0fCf8tHwn/LR8J/y0fCf8tHwn/LR8J/y0f
|
||||
Cf8tHwn/LR8J/y0fCf8tHwn/LR8K/y4gCf8rHAjuKx8I7i4gCP8tHwj/LR8I/y0fCP8tHwj/LR8I/y0f
|
||||
CP8tHwj/LR8I/y0fCP8tHwj/LR8I/y0fCP8tHwj/LR8I/y0fCP8tHwj/LR8J/y0fCP8tHwn/LR8J/y0f
|
||||
CP8tHwn/LR8J/y0fCP8tHwn/LR8J/y0fCf8tHwn/LR8J/y0fCf8tHwn/LR8J/y0fCf8tHwn/LR8J/y0f
|
||||
CP8tHwj/LR8J/y0fCf8tHwj/LR8J/y0fCf8tHwn/LR8J/y0fCP8tHwj/LR8I/y0fCP8tHwj/LR8I/y0f
|
||||
CP8tHwj/LR8I/y0fCP8tHwj/LR8I/y0fCP8tHwj/LR8I/y0fCP8uIAj/Kx8I7isfCO4uIAn/LR8J/y0f
|
||||
Cf8tHwn/LR8J/y0fCf8tHwn/LR8J/y0fCf8tHwn/LR8J/y0fCf8tHwn/LR8J/y0fCf8tHwn/LR8J/y0f
|
||||
Cf8tHwn/LR8I/y8fCP8uHwj/LR8G/yweCP8uHgn/LR4H/yweB/8rHgf/Kx4H/yweCP8sHgj/Kx4I/yse
|
||||
CP8sHgj/LB4I/yweCP8sHgn/LB4J/yoeCP8qHgj/LB4I/y4fCP8sIQj/LB8H/y0eCf8tHwn/LR8J/y0f
|
||||
Cf8tHwn/LR8J/y0fCf8tHwn/LR8J/y0fCf8tHwn/LR8J/y0fCf8tHwn/LR8J/y0fCf8tHwn/LiAJ/ysf
|
||||
CO4rHwjuLiAJ/y0fCf8tHwn/LR8J/y0fCf8tHwn/LR8J/y0fCf8tHwn/LR8J/y0fCf8tHwn/LR8J/y0f
|
||||
Cf8tHwn/LR8J/y0fCf8tIAn/LiAK/y0WAf8sFgH/LRsG/ywbBv8uGwX/MR0B/y8cAv8uGwX/LRkG/ysZ
|
||||
Bf8qGQX/KhkF/ygaBP8oGgX/JxoF/ycaBf8pGgX/KRkG/ykZBv8nGQT/JxkF/ygZCv8jFAb/IBUG/ywe
|
||||
Cf8tHwf/LR8J/y0fCf8tHwn/LR8J/y0fCf8tHwn/LR8J/y0fCf8tHwn/LR8J/y0fCf8tHwn/LR8J/y0f
|
||||
Cf8tHwn/LR8J/y4gCf8rHwjuKx8I7i4gCf8tHwn/LR8J/y0fCf8tHwn/LR8J/y0fCf8tHwn/LR8J/y0f
|
||||
Cf8tHwn/LR8J/y0fCf8tHwn/LR8J/y0fCf8tHwn/Lh0I/y0XA/84Qyf/P31P/0aSXP9KkVv/UpJT/2OV
|
||||
Sv93mUf/jJ9A/6CjPP+vpjv/rac4/6ilN/+ppDn/qaQ4/62jOP+uozj/sKI2/7KiNv+yojX/s6Az/7Wi
|
||||
NP+0oDP/mIUs/1pIFv8lFQb/Kh4I/y4gCf8sHgn/LR8J/y0fCf8tHwn/LR8J/y0fCf8tHwn/LR8J/y0f
|
||||
Cf8tHwn/LR8J/y0fCf8tHwn/LR8J/y0fCf8uIAn/Kx8I7isfCO4uIAn/LR8J/y0fCf8tHwn/LR8J/y0f
|
||||
Cf8tHwn/LR8J/y0fCf8tHwn/LR8J/y0fCf8tHwn/LR8J/y0fCf8tHwn/LR4I/ygWA/83b0X/SsOE/0zC
|
||||
f/9QvHf/Ubp0/1e4b/9YtGz/WrJl/2KwZP96tWD/lL9X/7fNUf/b2E//3NhP/9nWTP/X00v/29NL/9vS
|
||||
Sv/d0Ur/39FJ/9/RSP/j0Ej/5tBD/+3VRP/r0EX/jHYl/yMUBv8sHwb/LB4I/y0fCf8tHwn/LR8J/y0f
|
||||
Cf8tHwn/LR8J/y0fCf8tHwn/LR8J/y0fCf8tHwn/LR8J/y0fCf8tHwn/LiAJ/ysfCO4rHwjuLiAJ/y0f
|
||||
Cf8tHwn/LR8J/y0fCf8tHwn/LR8J/y0fCf8tHwn/LR8J/y0fCf8tHwn/LR8J/y0fCf8tHwn/LR8J/ysV
|
||||
AP8zWTb/ScmH/0e8ef9Ltnb/TrRx/1Cyb/9VsGr/Wa1m/1mrYf9dqV3/XqRc/1yfWf9ooVL/g6tQ/7XD
|
||||
Tf/R0Ur/ys1H/87LR//Oykb/0MlG/9PKRf/Uy0X/18pF/9jJQf/byEH/4MtD/+7VRv9rWxz/HxMC/y8h
|
||||
Cf8tHwn/LR8J/y0fCf8tHwn/LR8J/y0fCf8tHwn/LR8J/y0fCf8tHwn/LR8J/y0fCf8tHwn/LR8J/y4g
|
||||
Cf8rHwjuKx8I7i4gCf8tHwn/LR8J/y0fCf8tHwn/LR8J/y0fCf8tHwn/LR8J/y0fCf8tHwn/LR8J/y0f
|
||||
Cf8tHwn/Kx8I/y0cBv8vHQj/QKFs/0fEgP9IuHX/TLZ0/0+0cP9UsW3/Vq9o/1isZP9cqWD/X6hc/2Ok
|
||||
Wf9loVX/aKBT/2iZUP9um0j/obZJ/87RTP/SzUz/yspI/8zLSP/Qykj/0stH/9XJR//VyUT/2clE/9fH
|
||||
RP/izkn/vao2/ywdBv8rHAn/LR8J/y0fCf8tHwn/LR8J/y0fCf8tHwn/LR8J/y0fCf8tHwn/LR8J/y0f
|
||||
Cf8tHwn/LR8J/y0fCf8uIAn/Kx8I7isfCO4uIAj/LR8I/y0fCP8tHwj/LR8I/y0fCP8tHwj/LR8I/y0f
|
||||
CP8tHwj/LR8I/y0fCP8tHwj/LR8I/y0fCP8tGwX/MCoV/0azfP9Fvnv/Sbh2/0y2c/9PtG//VbFs/1av
|
||||
Z/9YrGT/XKlg/2KnXP9kpVr/ZaJX/2mhU/9unkz/bZtK/2qVR/+asUj/zdJM/8nOSf/JzUv/zcxJ/8/L
|
||||
R//Rykf/0cpE/9XKRP/WykX/3c5G/9DBQv86Kw//KRoH/y0fCP8tHwj/LR8I/y0fCP8tHwj/LR8I/y0f
|
||||
CP8tHwj/LR8I/y0fCP8tHwj/LR8I/y0fCP8tHwj/LiAI/ysfCO4rHwjuLiAJ/y0fCf8tHwn/LR8J/y0f
|
||||
Cf8tHwn/LR8J/y0fCf8tHwn/LR8J/y0fCf8tHwn/LR8J/y0fCf8tHwr/LRoE/ywsGP9Dt3//R7x6/0q3
|
||||
dv9OtHL/ULNu/1Wwa/9Xrmb/Watj/12pXv9hplv/ZaRY/2eiVP9qoFH/bp5M/3OcSf9ymUb/b5BC/6m5
|
||||
SP/L0k7/yM5M/8vNS//LzEn/z8xJ/87LR//Rykf/08tH/9nNSP/RxkT/PS8R/ygZBv8tHwn/LR8J/y0f
|
||||
Cf8tHwn/LR8J/y0fCf8tHwn/LR8J/y0fCf8tHwn/LR8J/y0fCf8tHwn/LR8J/y4gCf8rHwjuKx8I7i4g
|
||||
CP8tHwj/LR8I/y0fCP8tHwj/LR8I/y0fCP8tHwj/LR8I/y0fCP8tHwj/LR8I/y0fCP8tHwj/LB8K/y4a
|
||||
A/8uLRj/RLZ9/0e8ev9LtnX/ULNy/1Gybf9VsGr/Wa5l/1mrYv9dqVz/YKZa/2WkVv9oolL/aqBQ/2+e
|
||||
S/9zm0n/dJpE/3SUQ/+Dmj7/wMxM/8fPTP/IzUz/yc5L/8zNS//LzEr/z8tK/8/LSP/UzUj/0MZG/z0u
|
||||
EP8oGgX/LR8I/y0fCP8tHwj/LR8I/y0fCP8tHwj/LR8I/y0fCP8tHwj/LR8I/y0fCP8tHwj/LR8I/y0f
|
||||
CP8uIAj/Kx8I7isfCO4uIAn/LR8J/y0fCf8tHwn/LR8J/y0fCf8tHwn/LR8J/y0fCf8tHwn/LR8J/y0f
|
||||
Cf8tHwn/LR8J/ywfCv8uGgP/Li0Y/0W2fP9Iu3n/TLZ0/1Czcf9Rsm3/VbBq/1muZf9aq2L/Xqlc/2Gm
|
||||
Wv9lpFb/aKJS/2ugUP9wnkv/c5tI/3WaQ/95l0H/do07/6m6Rf/F007/xM5N/8XOTP/IzUz/yM5L/8zN
|
||||
S//MzEn/0c9J/83HR/89LhD/KBoG/y0fCf8tHwn/LR8J/y0fCf8tHwn/LR8J/y0fCf8tHwn/LR8J/y0f
|
||||
Cf8tHwn/LR8J/y0fCf8tHwn/LiAJ/ysfCO4rHwjuLiAJ/y0fCf8tHwn/LR8J/y0fCf8tHwn/LR8J/y0f
|
||||
Cf8tHwn/LR8J/y0fCf8tHwn/LR8J/y0fCf8tHwr/LRsE/zAsGP9HtHr/S7p3/061cv9Qs2//U7Fr/1Wv
|
||||
af9arGP/XKlh/2GnW/9lpFn/ZaNV/2igUf9unk7/cpxK/3WbRv91mEH/e5Y//3mPOP+YrEH/wtVT/7/O
|
||||
Uf/CzlD/xc1Q/8TOTP/IzUz/yc5L/87QTP/HyEn/PS8P/ykZBv8tHwn/LR8J/y0fCf8tHwn/LR8J/y0f
|
||||
Cf8tHwn/LR8J/y0fCf8tHwn/LR8J/y0fCf8tHwn/LR8J/y4gCf8rHwjuKx8I7i4gCf8tHwn/LR8J/y0f
|
||||
Cf8tHwn/LR8J/y0fCf8tHwn/LR8J/y0fCf8tHwn/LR8J/y0fCf8tHwn/LR8K/y0bBP8wLBj/R7R6/0u6
|
||||
d/9OtXL/ULNv/1Oxa/9Vr2n/Wqxj/1ypYf9hp1v/ZaRZ/2WjVf9ooFH/bp5O/3KcSv91m0b/dZhB/3yW
|
||||
P/97kDj/j6I9/7/VV/+90VH/wNBR/8PPUf/Bz0z/xM5M/8XOS//K0Uz/xclJ/z0vD/8pGQb/LR8J/y0f
|
||||
Cf8tHwn/LR8J/y0fCf8tHwn/LR8J/y0fCf8tHwn/LR8J/y0fCf8tHwn/LR8J/y0fCf8uIAn/Kx8I7isf
|
||||
CO4uIAn/LR8J/y0fCf8tHwn/LR8J/y0fCf8tHwn/LR8J/y0fCf8tHwn/LR8J/y0fCf8tHwn/LR8J/y0f
|
||||
Cf8tGwT/MCwa/0qzef9MuXb/TrRx/1Kybv9Tr2n/V61k/12qYP9fqF3/YKZZ/2SkVv9qoVL/bKBP/26d
|
||||
TP9ym0b/dJpE/3iWQf98lD7/fY43/42gPf+111v/t9RU/7nSU/+/z1L/vtBQ/8HPUP/Cz03/xtFN/8HJ
|
||||
TP88MA//KhsF/y0fCf8tHwn/LR8J/y0fCf8tHwn/LR8J/y0fCf8tHwn/LR8J/y0fCf8tHwn/LR8J/y0f
|
||||
Cf8tHwn/LiAJ/ysfCO4rHwjuLiAJ/y0fCf8tHwn/LR8J/y0fCf8tHwn/LR8J/y0fCf8tHwn/LR8J/y0f
|
||||
Cf8tHwn/LR8J/y0fCf8tHwn/LRsE/zAsGv9Ks3n/TLl2/060cf9Ssm7/U69p/1etZP9dqmD/X6hd/2Cm
|
||||
Wf9kpFb/aqFS/2ygT/9unUz/cptG/3SaRP94lkH/fJQ+/36NNv+LpkP/qNxh/6zYWv+v1Ff/ttFV/7zS
|
||||
Uf/A0VL/wNFP/8TUT//Ayk3/PDAP/yobBP8tHwn/LR8J/y0fCf8tHwn/LR8J/y0fCf8tHwn/LR8J/y0f
|
||||
Cf8tHwn/LR8J/y0fCf8tHwn/LR8J/y4gCf8rHwjuKx8I7i4gCf8tHwn/LR8J/y0fCf8tHwn/LR8J/y0f
|
||||
Cf8tHwn/LR8J/y0fCf8tHwn/LR8J/y0fCf8tHwn/LR8J/y4cA/8wLBn/SrN2/024dP9Ps3D/VLFs/1eu
|
||||
Z/9Zq2T/XKlf/2KnXf9jpVj/ZaNV/2qgUf9unk7/cJ1K/3KbRf92mUP/e5U+/3mUPP9/jDP/ibRM/5zi
|
||||
aP+g2mD/pdhe/6zVW/+01Ff/utNU/7vSUv/D1VH/vctN/zswEf8pGgb/LR8J/y0fCf8tHwn/LR8J/y0f
|
||||
Cf8tHwn/LR8J/y0fCf8tHwn/LR8J/y0fCf8tHwn/LR8J/y0fCf8uIAn/Kx8I7isfCO4uIAj/LR8I/y0f
|
||||
CP8tHwj/LR8I/y0fCP8tHwj/LR8I/y0fCP8tHwj/LR8I/y0fCP8tHwj/LR8I/y0fCP8uHQL/MCwY/0qz
|
||||
df9NuHP/T7Nv/1Wwa/9arWb/Wqtj/1qpX/9jplz/ZKRY/2WiVf9qoFH/b55N/3CdSf9ym0T/d5hC/3yW
|
||||
Pf98kjn/g441/4nRZP+N4mv/ld5n/5rcZf+g22D/p9hd/63XWf+y1Fb/utZU/7jMTv86MBH/KBoG/y0f
|
||||
CP8tHwj/LR8I/y0fCP8tHwj/LR8I/y0fCP8tHwj/LR8I/y0fCP8tHwj/LR8I/y0fCP8tHwj/LiAI/ysf
|
||||
CO4rHwjuLiAJ/y0fCf8tHwn/LR8J/y0fCf8tHwn/LR8J/y0fCf8tHwn/LR8J/y0fCf8tHwn/LR8J/y0f
|
||||
Cf8tHwn/Lh0D/zAsF/9LsnT/Trdz/1Gybv9WsGr/WK1l/1uqYv9dqV3/YqZb/2WkVv9nolT/aqBQ/2+e
|
||||
TP9ynEn/c5pD/3aXQf97lT3/gokz/3uvT/986nr/g+Rw/4nibf+O32v/lN5n/5rcY/+h22D/ptdd/6/Y
|
||||
Wv+xzlX/Oi8R/ygZBv8tHwn/LR8J/y0fCf8tHwn/LR8J/y0fCf8tHwn/LR8J/y0fCf8tHwn/LR8J/y0f
|
||||
Cf8tHwn/LR8J/y4gCf8rHwjuKx8I7i4gCf8tHwn/LR8J/y0fCf8tHwn/LR8J/y0fCf8tHwn/LR8J/y0f
|
||||
Cf8tHwn/LR8J/y0fCf8tHwn/LR8J/y4cBf8wLRb/TbFz/1C2cv9TsWz/V7Bo/1etZP9cqmH/Yahb/2Gl
|
||||
Wv9mo1T/aKFS/2ugTv9wnUr/dJpI/3WZQ/98kz3/gYo3/3WsUP9s7ID/but8/3fneP995XT/geJy/4fh
|
||||
bf+O32r/lN5m/5rbZP+j3GL/pdJd/zkvEv8pFwX/LR8J/y0fCf8tHwn/LR8J/y0fCf8tHwn/LR8J/y0f
|
||||
Cf8tHwn/LR8J/y0fCf8tHwn/LR8J/y0fCf8uIAn/Kx8I7isfCO4uIAn/LR8J/y0fCf8tHwn/LR8J/y0f
|
||||
Cf8tHwn/LR8J/y0fCf8tHwn/LR8J/y0fCf8tHwn/LR8J/y0fCf8tHAX/MC0W/02xc/9QtnL/U7Fs/1ev
|
||||
aP9YrWT/XKpg/1+oW/9hpVr/ZqNV/2mhUv9sn03/cJxJ/3WVRP95kT3/eJZA/2TAYf9Y8Yj/XPOI/2fs
|
||||
gv9r637/cel7/3bneP995XT/g+Nw/4nibP+P32r/l+Bo/5jXYf85MBL/KhgG/y0fCf8tHwn/LR8J/y0f
|
||||
Cf8tHwn/LR8J/y0fCf8tHwn/LR8J/y0fCf8tHwn/LR8J/y0fCf8tHwn/LiAJ/ysfCO4rHwjuLiAJ/y0f
|
||||
Cf8tHwn/LR8J/y0fCf8tHwn/LR8J/y0fCf8tHwn/LR8J/y0fCf8tHwn/LR8J/y0fCf8tHwn/KhsE/zAs
|
||||
FP9NsXH/UbVv/1awav9arWf/Wqxi/12oXv9jo1v/Y6FU/2mcUP9tm0r/bptI/2+cSv9rp1P/YMNo/1Do
|
||||
hf9G/ZX/SfaO/1PyjP9b8Ir/X+6F/2btgv9s637/cul6/3fmeP995XT/g+Nw/4vlbv+O22n/NjAU/yca
|
||||
BP8uHwn/LR8J/y0fCf8tHwn/LR8J/y0fCf8tHwn/LR8J/y0fCf8tHwn/LR8J/y0fCf8tHwn/LR8J/y4g
|
||||
Cf8rHwjuKx8I7i4gCf8tHwn/LR8J/y0fCf8tHwn/LR8J/y0fCf8tHwn/LR8J/y0fCf8tHwn/LR8J/y0f
|
||||
Cf8tHwn/LR8J/yobBP8wLBT/TbJy/1G1b/9WsGr/Wq1n/1uoX/9dp13/YKlf/2CvXv9gt2X/XcBq/1XQ
|
||||
dv9P4YP/SPCO/0T3lP9J85L/R/SR/0n1kf9L9ZD/TfSP/1Tzi/9b8Yj/Yu+E/2ftgP9r637/cel6/3jn
|
||||
dv+A6XT/geBs/zgxFf8pGwX/LR8J/y0fCf8tHwn/LR8J/y0fCf8tHwn/LR8J/y0fCf8tHwn/LR8J/y0f
|
||||
Cf8tHwn/LR8J/y0fCf8uIAn/Kx8I7isfCO4uIAn/LR8J/y0fCf8tHwn/LR8J/y0fCf8tHwn/LR8J/y0f
|
||||
Cf8tHwn/LR8J/y0fCf8tHwn/LR8J/y8fCf8rHAL/LisU/02ucv9Vs23/VLBp/1ipYP9XuGr/TtOA/0fg
|
||||
if9H5Ir/RuqO/0Xukf9G7pL/Ru2Q/0Ttjf9J7I7/R+6N/0nwj/9L8ZD/S/KR/0nzkP9I85D/TPSN/1X0
|
||||
if9d8If/Ye6D/2fsf/9s6nz/cux7/3Xic/81MRb/KxkE/ywfCf8tHwn/LR8J/y0fCf8tHwn/LR8J/y0f
|
||||
Cf8tHwn/LR8J/y0fCf8tHwn/LR8J/y0fCf8tHwn/LiAJ/ysfCO4rHwjuLiAJ/y0fCf8tHwn/LR8J/y0f
|
||||
Cf8tHwn/LR8J/y0fCf8tHwn/LR8J/y0fCf8tHwn/LR8J/y0fCf8sHwn/Lh0D/y4gCP9PnWP/Vbdx/1eq
|
||||
ZP9TwXP/RuSM/0fmjv9F4ov/Q+SJ/0bliv9F5or/RuaM/0bojP9G6or/R+uM/0jtjP9J7o3/SO+O/0nw
|
||||
j/9I8o//RvKP/0f0j/9K947/S/WN/1Tyif9b8YT/YO6D/2n2hP9kz27/KyEM/y4dBv8tHwn/LR8J/y0f
|
||||
Cf8tHwn/LR8J/y0fCf8tHwn/LR8J/y0fCf8tHwn/LR8J/y0fCf8tHwn/LR8J/y4gCf8rHwjuKx8I7i4g
|
||||
Cf8tHwn/LR8J/y0fCf8tHwn/LR8J/y0fCf8tHwn/LR8J/y0fCf8tHwn/LR8J/y0fCf8tHwn/LB8J/y4e
|
||||
B/8sFgH/PGU2/1i9dP9Utm3/RN6J/0Leif9J3on/SN+K/0Xgif9H4ov/R+KL/0fjjP9I5Yz/R+eL/0jo
|
||||
jP9I6o3/SeuO/0jsjf9J7Y7/SO+O/0jwjv9J8Y//S/OQ/0r0kf9M9ZD/T/SO/1Pziv9h/5X/SIJE/yoP
|
||||
Af8sHwf/LR8K/y0fCf8tHwn/LR8J/y0fCf8tHwn/LR8J/y0fCf8tHwn/LR8J/y0fCf8tHwn/LR8J/y0f
|
||||
Cf8uIAn/Kx8I7isfCO4uIAj/LR8I/y0fCP8tHwj/LR8I/y0fCP8tHwj/LR8I/y0fCP8tHwj/LR8I/y0f
|
||||
CP8tHwj/LR8I/y0fCP8tHwn/LBwH/ywdCf9IfEf/Tsx8/0Ddif9F14X/RduH/0bdiP9E3on/Rd+K/0fl
|
||||
jf9I6JD/SOqQ/0jsj/9J7ZD/SO6R/0nvkv9H8JD/SPKR/0n0kf9K84//SPCP/0fxkP9J8pH/SvWS/0r3
|
||||
lP9M/pn/RKxh/y0YBv8sHAf/LR8J/y0fCP8tHwj/LR8I/y0fCP8tHwj/LR8I/y0fCP8tHwj/LR8I/y0f
|
||||
CP8tHwj/LR8I/y0fCP8tHwj/LiAI/ysfCO4rHwjuLiAJ/y0fCf8tHwn/LR8J/y0fCf8tHwn/LR8J/y0f
|
||||
Cf8tHwn/LR8J/y0fCf8tHwn/LR8J/y0fCf8tHwn/LR8J/y0fCf8sGgX/KhUE/0GsbP9E4Y7/QteG/0TZ
|
||||
h/9F2oj/RdyI/0Teiv9Ez3//RMp7/0XNe/9Eznr/RM97/0TQff9F0H3/Q9J7/0PTfP9E0n3/SNyC/0zq
|
||||
iv9M6Yr/TeqL/0zsiv9P8Iz/S7Vp/ywWB/8sGgT/LiAJ/y0fCf8tHwn/LR8J/y0fCf8tHwn/LR8J/y0f
|
||||
Cf8tHwn/LR8J/y0fCf8tHwn/LR8J/y0fCf8tHwn/LR8J/y4gCf8rHwjuKx8I7i4gCf8tHwn/LR8J/y0f
|
||||
Cf8tHwn/LR8J/y0fCf8tHwn/LR8J/y0fCf8tHwn/LR8J/y0fCf8tHwn/LR8J/y0fCf8sHwn/Lh8G/ywR
|
||||
BP87m2b/ReCR/0PUiP9E1oj/RtiJ/0bZh/9D5o//Nlk1/ywZB/8vJg3/LyYM/y8mDP8vJQ3/LyUN/ywm
|
||||
DP8sJgz/KhgH/z9sO/9c2Hr/Vslx/1fJcf9YyXL/XdF3/0yRUv8pEwT/LyEJ/y0fCf8tHwn/LR8J/y0f
|
||||
Cf8tHwn/LR8J/y0fCf8tHwn/LR8J/y0fCf8tHwn/LR8J/y0fCf8tHwn/LR8J/y0fCf8uIAn/Kx8I7isf
|
||||
CO4uIAn/LR8J/y0fCf8tHwn/LR8J/y0fCf8tHwn/LR8J/y0fCf8tHwn/LR8J/y0fCf8tHwn/LR8J/y0f
|
||||
Cf8tHwn/LB8J/y0eBv8sEwX/PJ1n/0Tej/9D0of/Q9SG/0TWiP9F2Ib/QuWO/zVPLv8sDgD/MBsI/y8b
|
||||
B/8vGwb/LhsE/y4bBP8sGgb/KxoG/ywKAP87Zjr/U+CD/1HQe/9R0Hv/UtB7/1Pcg/9En17/KxMD/y4g
|
||||
Cf8tHwn/LR8J/y0fCf8tHwn/LR8J/y0fCf8tHwn/LR8J/y0fCf8tHwn/LR8J/y0fCf8tHwn/LR8J/y0f
|
||||
Cf8tHwn/LiAJ/ysfCO4rHwjuLiAJ/y0fCf8tHwn/LR8J/y0fCf8tHwn/LR8J/y0fCf8tHwn/LR8J/y0f
|
||||
Cf8tHwn/LR8J/y0fCf8tHwn/LR8J/y4fCf8sHwj/LhUD/0CbZP9F2o7/RM+H/0TQhv9F0of/Q9WG/0bk
|
||||
kf8yVzL/KxEA/y4gCf8tHwn/LR8J/y0fCf8tHwn/LR8J/y0fCf8tDgD/O3ZF/0zslP9I2ob/SdqG/0na
|
||||
hv9L5o7/QaNm/ywVAv8uHgf/LR8J/y0fCf8tHwn/LR8J/y0fCf8tHwn/LR8J/y0fCf8tHwn/LR8J/y0f
|
||||
Cf8tHwn/LR8J/y0fCf8tHwn/LR8J/y4gCf8rHwjuKx8I7i4gCf8tHwn/LR8J/y0fCf8tHwn/LR8J/y0f
|
||||
Cf8tHwn/LR8J/y0fCf8tHwn/LR8J/y0fCf8tHwn/LR8J/y0fCf8sHwj/LiAJ/ywUAf89jlf/Q9qO/0LN
|
||||
hf9Dz4T/RNCG/0LRg/9H4I//O4dQ/yoOAP8tIAj/LR8J/y0fCf8tHwn/LR8J/y0fCP8tHgj/LBMC/z+n
|
||||
ZP9H6pH/RN+K/0Tfiv9E34r/RuyT/z2bXv8uEwH/LR8H/y0fCf8tHwn/LR8J/y0fCf8tHwn/LR8J/y0f
|
||||
Cf8tHwn/LR8J/y0fCf8tHwn/LR8J/y0fCf8tHwn/LR8J/y0fCf8uIAn/Kx8I7isfCO4uIAn/LR8J/y0f
|
||||
Cf8tHwn/LR8J/y0fCf8tHwn/LR8J/y0fCf8tHwn/LR8J/y0fCf8tHwn/LR8J/y0fCf8tHwn/LB8J/y0f
|
||||
Cf8rDwD/NXNF/0PZjP9DyYX/RcuF/0XNhv9EzYT/RdSJ/0TFfv8uKRH/LBYC/ywfCP8tHwn/LR8J/ywf
|
||||
Cf8sHwj/KBAA/zZAIf9H34n/R+GL/0Xdif9F3Yn/Rd2I/0nvlv86gU3/Kg4A/y0eCf8tHwn/LR8J/y0f
|
||||
Cf8tHwn/LR8J/y0fCf8tHwn/LR8J/y0fCf8tHwn/LR8J/y0fCf8tHwn/LR8J/y0fCf8tHwn/LiAJ/ysf
|
||||
CO4rHwjuLiAI/y0fCP8tHwj/LR8I/y0fCP8tHwj/LR8I/y0fCP8tHwj/LR8I/y0fCP8tHwj/LR8I/y0f
|
||||
CP8tHwj/LR8I/y0fCf8rHwj/LRMB/zZRLv9H0ov/QsmE/0LKhP9Ey4X/Q8uE/0XMhP9H3I7/Pppj/ysa
|
||||
Bv8sEQD/Lh0I/y0fB/8tGgX/Kg4A/y0lD/9AuXH/R+qQ/0Lci/9D34n/Rd6J/0PdiP9C65H/M1wx/y8U
|
||||
Av8uIAf/LR8J/y0fCP8tHwj/LR8I/y0fCP8tHwj/LR8I/y0fCP8tHwj/LR8I/y0fCP8tHwj/LR8I/y0f
|
||||
CP8tHwj/LR8I/y4gCP8rHwjuKx8I7i4gCf8tHwn/LR8J/y0fCf8tHwn/LR8J/y0fCf8tHwn/LR8J/y0f
|
||||
Cf8tHwn/LR8J/y0fCf8tHwn/LR8J/y0fCf8tHwn/LB4I/y4bBv8uJQ//QLV4/0LNh/9Bx4P/Q8iE/0PJ
|
||||
g/9Ey4P/RMuG/0Xajv9EpGn/MEoo/ysfC/8uFgr/LiUN/zNULf8+vHf/SOWQ/0TYh/9F3Ij/Rt6J/0fe
|
||||
i/9E44z/Rc9//y0qDv8vGQX/LSAH/y0fCf8tHwn/LR8J/y0fCf8tHwn/LR8J/y0fCf8tHwn/LR8J/y0f
|
||||
Cf8tHwn/LR8J/y0fCf8tHwn/LR8J/y0fCf8uIAn/Kx8I7isfCO4uIAj/LR8I/y0fCP8tHwj/LR8I/y0f
|
||||
CP8tHwj/LR8I/y0fCP8tHwj/LR8I/y0fCP8tHwj/LR8I/y0fCP8tHwj/LR8I/y0fCf8tHwn/KxEA/zt2
|
||||
Sv9F047/QcOC/0PFg/9Cx4L/RMmD/0PKgv9EzIP/RdaL/0fUif8/t3P/Pqpv/0C+dv9H3I//Rt6O/0HW
|
||||
hv9E1oj/RdiI/0Tah/9G3In/R+6U/zuET/8sDwD/LR8J/y0fCf8tHwj/LR8I/y0fCP8tHwj/LR8I/y0f
|
||||
CP8tHwj/LR8I/y0fCP8tHwj/LR8I/y0fCP8tHwj/LR8I/y0fCP8tHwj/LiAI/ysfCO4rHwjuLiAJ/y0f
|
||||
Cf8tHwn/LR8J/y0fCf8tHwn/LR8J/y0fCf8tHwn/LR8J/y0fCf8tHwn/LR8J/y0fCf8tHwn/LR8J/y0f
|
||||
Cf8tHwn/LR8J/y0aBP8wKhH/QLV4/0HKhv9BxIP/QsWB/0LHgv9CyIL/QsmD/0XKg/9FzoT/Q9KH/0LY
|
||||
iP9G14r/Q9CH/0XRhf9F0oX/RNSI/0LXiP9C14X/ReGM/0XOgv8uKxT/KxgF/y0fCf8tHwn/LR8J/y0f
|
||||
Cf8tHwn/LR8J/y0fCf8tHwn/LR8J/y0fCf8tHwn/LR8J/y0fCf8tHwn/LR8J/y0fCf8tHwn/LR8J/y4g
|
||||
Cf8rHwjuKx8I7i4gCf8tHwn/LR8J/y0fCf8tHwn/LR8J/y0fCf8tHwn/LR8J/y0fCf8tHwn/LR8J/y0f
|
||||
Cf8tHwn/LR8J/y0fCf8tHwn/LR8J/y0fCf8tHwf/LRYA/zRTL/9Dy4v/QcWG/0HCgf9CxIL/QsSC/0PE
|
||||
g/9CxoP/Q8iC/0TKhP9Dy4T/RsyF/0PNhf9Cz4b/RNCH/0XSiP9H0oj/QtiJ/0jikP8yWjX/KxAA/ywg
|
||||
B/8tHwn/LR8J/y0fCf8tHwn/LR8J/y0fCf8tHwn/LR8J/y0fCf8tHwn/LR8J/y0fCf8tHwn/LR8J/y0f
|
||||
Cf8tHwn/LR8J/y0fCf8uIAn/Kx8I7isfCO4uIAn/LR8J/y0fCf8tHwn/LR8J/y0fCf8tHwn/LR8J/y0f
|
||||
Cf8tHwn/LR8J/y0fCf8tHwn/LR8J/y0fCf8tHwn/LR8J/y0fCf8tHwn/LSAH/y8gBv8vEgD/OWlC/0PL
|
||||
if8+xYP/P8KA/0DCgf9Bw4L/QsaC/0LHgf9DyIL/QsqC/0PLg/9CzIT/Q82F/0TOhf9Fz4X/RdaK/0fg
|
||||
lf85dkf/KhEA/yseB/8uHwj/LR8J/y0fCf8tHwn/LR8J/y0fCf8tHwn/LR8J/y0fCf8tHwn/LR8J/y0f
|
||||
Cf8tHwn/LR8J/y0fCf8tHwn/LR8J/y0fCf8tHwn/LiAJ/ysfCO4rHwjuLiAJ/y0fCf8tHwn/LR8J/y0f
|
||||
Cf8tHwn/LR8J/y0fCf8tHwn/LR8J/y0fCf8tHwn/LR8J/y0fCf8tHwn/LR8J/y0fCf8tHwn/LR8J/y0f
|
||||
Cf8vHwn/Kh4I/y8WAP82Yzj/P7+C/0LLiP9CwoL/QMGC/0HFhP9BxIH/Q8WD/0PGhP9Ex4X/RMmE/0bK
|
||||
hf9DzYT/QtyN/0nTiP86aj3/LRIA/y4eCP8sIAr/Lh8J/y0fCf8tHwn/LR8J/y0fCf8tHwn/LR8J/y0f
|
||||
Cf8tHwn/LR8J/y0fCf8tHwn/LR8J/y0fCf8tHwn/LR8J/y0fCf8tHwn/LR8J/y4gCf8rHwjuKx8I7i4g
|
||||
Cf8tHwn/LR8J/y0fCf8tHwn/LR8J/y0fCf8tHwn/LR8J/y0fCf8tHwn/LR8J/y0fCf8tHwn/LR8J/y0f
|
||||
Cf8tHwn/LR8J/y0fCf8tHwn/LR8J/y0fCf8rIAj/KhQA/zU/IP9Bl2X/RseI/0LLiv8/x4b/QMaD/0DF
|
||||
g/9CxoT/Q8mG/0LOh/9G1o3/RtSK/0WjaP8yQiD/LBEA/y8eCP8sIAn/LR8I/y0fCP8tHwn/LR8J/y0f
|
||||
Cf8tHwn/LR8J/y0fCf8tHwn/LR8J/y0fCf8tHwn/LR8J/y0fCf8tHwn/LR8J/y0fCf8tHwn/LR8J/y0f
|
||||
Cf8uIAn/Kx8I7isfCO4uIAn/LR8J/y0fCf8tHwn/LR8J/y0fCf8tHwn/LR8J/y0fCf8tHwn/LR8J/y0f
|
||||
Cf8tHwn/LR8J/y0fCf8tHwn/LR8J/y0fCf8tHwn/LR8J/y0fCf8sHwn/LR4I/y4gCv8sFQD/KxoE/zRJ
|
||||
Kf9Cg1P/Q6tv/0O6ev9EwoL/QsOC/0a+fv9FsHD/QoZS/zZJJ/8sGgT/KxMB/y4fCf8wIAj/Lh8J/y0f
|
||||
Cf8tHwn/LR8J/y0fCf8tHwn/LR8J/y0fCf8tHwn/LR8J/y0fCf8tHwn/LR8J/y0fCf8tHwn/LR8J/y0f
|
||||
Cf8tHwn/LR8J/y0fCf8tHwn/LiAJ/ysfCO4rHwjuLiAI/y0fCP8tHwj/LR8I/y0fCP8tHwj/LR8I/y0f
|
||||
CP8tHwj/LR8I/y0fCP8tHwj/LR8I/y0fCP8tHwj/LR8I/y0fCP8tHwj/LR8I/y0fCP8tHwj/LR8J/y0f
|
||||
Cf8tHwj/LR8J/y0dCP8rEwL/LBQA/zEjCf8yLxP/Njce/zQ3H/8yMBT/MiMJ/y8UAP8rEgD/LB0H/y4g
|
||||
Cf8tHwn/LB8J/y0fCf8tHwj/LR8I/y0fCP8tHwj/LR8I/y0fCP8tHwj/LR8I/y0fCP8tHwj/LR8I/y0f
|
||||
CP8tHwj/LR8I/y0fCP8tHwj/LR8I/y0fCP8tHwj/LR8I/y4gCP8rHwjuKx8I7i4gCf8tHwn/LR8J/y0f
|
||||
Cf8tHwn/LR8J/y0fCf8tHwn/LR8J/y0fCf8tHwn/LR8J/y0fCf8tHwn/LR8J/y0fCf8tHwn/LR8J/y0f
|
||||
Cf8tHwn/LR8J/y0fCf8tHwn/LR8J/y0fCf8tHwn/LiAI/yseB/8rHQj/LBoF/ysXAv8sGAP/KxsF/ygb
|
||||
Bv8wHgf/LR8J/y4fCv8uIAr/LR8J/y0fCf8tHwn/LR8J/y0fCf8tHwn/LR8J/y0fCf8tHwn/LR8J/y0f
|
||||
Cf8tHwn/LR8J/y0fCf8tHwn/LR8J/y0fCf8tHwn/LR8J/y0fCf8tHwn/LR8J/y0fCf8uIAn/Kx8I7isf
|
||||
CO4uIAj/LR8J/y0fCf8tHwn/LR8J/y0fCf8tHwn/LR8J/y0fCf8tHwn/LR8J/y0fCf8tHwn/LR8J/y0f
|
||||
Cf8tHwn/LR8J/y0fCf8tHwn/LR8J/y0fCf8tHwn/LR8J/y0fCf8tHwn/LR8J/y0fCf8tHwn/LR8I/y0f
|
||||
Cf8tHwn/LR8J/y0fCf8tHwn/LB8J/y0fCf8tHwj/LR8I/y0fCf8tHwn/LR8J/y0fCf8tHwn/LR8J/y0f
|
||||
Cf8tHwn/LR8J/y0fCf8tHwn/LR8J/y0fCf8tHwn/LR8J/y0fCf8tHwn/LR8J/y0fCf8tHwn/LR8J/y0f
|
||||
Cf8tHwn/LiAJ/ysfCO4rHwjuLyEJ/y0fCf8tHwn/LR8J/y0fCf8tHwn/LR8J/y0fCf8tHwn/LR8J/y0f
|
||||
Cf8tHwn/LR8J/y0fCf8tHwn/LR8J/y0fCf8tHwn/LR8J/y0fCf8tHwn/LR8J/y0fCf8tHwn/LR8J/y0f
|
||||
Cf8tHwn/LR8J/y0fCf8tHwn/LR8J/y0fCf8tHwn/LR8J/y0fCf8tHwn/LR8J/y0fCf8tHwn/LR8J/y0f
|
||||
Cf8tHwn/LR8J/y0fCf8tHwn/LR8J/y0fCf8tHwn/LR8J/y0fCf8tHwn/LR8J/y0fCf8tHwn/LR8J/y0f
|
||||
Cf8tHwn/LR8J/y0fCf8tHwn/LR8J/y4gCf8rHwjuLh8J7i8hCP8tIAj/LR8J/y0fCf8tHwn/LR8J/y0f
|
||||
Cf8tHwn/LR8J/y0fCf8tHwn/LR8J/y0fCf8tHwn/LR8J/y0fCf8tHwn/LR8J/y0fCf8tHwn/LR8J/y0f
|
||||
Cf8tHwn/LR8J/y0fCf8tHwn/LR8J/y0fCf8tHwn/LR8J/y0fCf8tHwn/LR8J/y0fCf8tHwn/LR8J/y0f
|
||||
Cf8tHwn/LR8J/y0fCf8tHwn/LR8J/y0fCf8tHwn/LR8J/y0fCf8tHwn/LR8J/y0fCf8tHwn/LR8J/y0f
|
||||
Cf8tHwn/LR8J/y0fCf8tHwn/LR8J/y0fCf8tHwn/LR8J/y0fCf8tHwj/LyEJ7jgqFu4sHgX/LR8H/y0f
|
||||
Cf8tHwn/LR8J/y0fCf8tHwn/LR8J/y0fCf8tHwn/LR8J/y0fCf8tHwn/LR8J/y0fCf8tHwn/LR8J/y0f
|
||||
Cf8tHwn/LR8J/y0fCf8tHwn/LR8J/y0fCf8tHwn/LR8J/y0fCf8tHwn/LR8J/y0fCf8tHwn/LR8J/y0f
|
||||
Cf8tHwn/LR8J/y0fCf8tHwn/LR8J/y0fCf8tHwn/LR8J/y0fCf8tHwn/LR8J/y0fCf8tHwn/LR8J/y0f
|
||||
Cf8tHwn/LR8J/y0fCf8tHwn/LR8J/y0fCf8tHwn/LR8J/y0fCf8tHwn/LR8J/y0fCf8uIAn/LB4G/zUn
|
||||
FO5YTT7uJhUA/y4gCv8tHwn/LR8J/y0fCf8tHwn/LR8J/y0fCf8tHwn/LR8J/y0fCf8tHwn/LR8J/y0f
|
||||
Cf8tHwn/LR8J/y0fCf8tHwn/LR8J/y0fCf8tHwn/LR8J/y0fCf8tHwn/LR8J/y0fCf8tHwn/LR8J/y0f
|
||||
Cf8tHwn/LR8J/y0fCf8tHwn/LR8J/y0fCf8tHwn/LR8J/y0fCf8tHwn/LR8J/y0fCf8tHwn/LR8J/y0f
|
||||
Cf8tHwn/LR8J/y0fCf8tHwn/LR8J/y0fCf8tHwn/LR8J/y0fCf8tHwn/LR8J/y0fCf8tHwn/LR8J/y0f
|
||||
Cf8tHwn/LSAL/yoZAP9XTD7um5SO7iMRAP8vIAn/LR8J/y0fCf8tHwj/LR8I/y0fCP8tHwj/LR8I/y0f
|
||||
CP8tHwj/LR8I/y0fCP8tHwj/LR8I/y0fCP8tHwj/LR8I/y0fCP8tHwj/LR8I/y0fCP8tHwj/LR8I/y0f
|
||||
CP8tHwj/LR8I/y0fCP8tHwj/LR8I/y0fCP8tHwj/LR8I/y0fCP8tHwj/LR8I/y0fCP8tHwj/LR8I/y0f
|
||||
CP8tHwj/LR8I/y0fCP8tHwj/LR8I/y0fCP8tHwj/LR8I/y0fCP8tHwj/LR8I/y0fCP8tHwj/LR8I/y0f
|
||||
CP8tHwj/LR8I/y0fCP8tHwn/LR8I/ywfBv8iEgD/npeO7urq6e5ENSX/JBQA/y0hCP8tIAf/LR8J/y0f
|
||||
Cf8tHwn/LR8J/y0fCf8tHwn/LR8J/y0fCf8tHwn/LR8J/y0fCf8tHwn/LR8J/y0fCf8tHwn/LR8J/y0f
|
||||
Cf8tHwn/LR8J/y0fCf8tHwn/LR8J/y0fCf8tHwn/LR8J/y0fCf8tHwn/LR8J/y0fCf8tHwn/LR8J/y0f
|
||||
Cf8tHwn/LR8J/y0fCf8tHwn/LR8J/y0fCf8tHwn/LR8J/y0fCf8tHwn/LR8J/y0fCf8tHwn/LR8J/y0f
|
||||
Cf8tHwn/LR8J/y0fCf8tHwn/LR8J/y0fCf8tHwn/Kx0H/y4gCv8lFQD/QzYm/+vr6e79/f3uvLax/xoM
|
||||
AP8pGwL/LiEI/y0fCf8tHwj/LR8J/y0fCf8tHwj/LR8I/y0fCP8tHwj/LR8I/y0fCP8tHwj/LR8I/y0f
|
||||
CP8tHwj/LR8I/y0fCP8tHwj/LR8I/y0fCP8tHwj/LR8I/y0fCP8tHwj/LR8I/y0fCP8tHwj/LR8I/y0f
|
||||
CP8tHwj/LR8I/y0fCP8tHwj/LR8I/y0fCP8tHwj/LR8I/y0fCP8tHwj/LR8I/y0fCP8tHwj/LR8I/y0f
|
||||
CP8tHwj/LR8I/y0fCP8tHwj/LR8I/y0fCP8tHwj/LR8J/y0fCf8tHwn/LR8J/y4gCf8rHQX/Gw0A/723
|
||||
tP/9/f3u+vv87v////+ak4n/GQsA/yQUAP8uHwn/LR8J/y0fCP8uIAn/LR8J/y0fCf8tHwn/LR8J/y0f
|
||||
Cf8tHwn/LR8J/y0fCf8tHwn/LR8J/y0fCf8tHwn/LR8J/y0fCf8tHwn/LR8J/y0fCf8tHwn/LR8J/y0f
|
||||
Cf8tHwn/LR8J/y0fCf8tHwn/LR8J/y0fCf8tHwn/LR8J/y0fCf8tHwn/LR8J/y0fCf8tHwn/LR8J/y0f
|
||||
Cf8tHwn/LR8J/y0fCf8tHwn/LR8J/y0fCf8tHwn/LR8J/y0fCf8tHwn/LR8J/y0eCf8sHgj/LB8J/y4g
|
||||
B/8mFwD/Gw0A/5qViv//////+/z87v39/fT//////////7q1sP9DNCb/IxEA/ycWAP8rHgX/LiAH/y8h
|
||||
Cf8uIAn/LiAJ/y4gCf8uIAn/LiAJ/y4gCf8uIAn/LiAJ/y4gCf8uIAn/LiAJ/y4gCf8uIAn/LiAJ/y4g
|
||||
Cf8uIAn/LiAJ/y4gCf8uIAn/LiAJ/y4gCf8uIAn/LiAJ/y4gCf8uIAn/LiAJ/y4gCf8uIAn/LiAJ/y4g
|
||||
Cf8uIAn/LiAJ/y4gCf8uIAn/LiAJ/y4gCf8uIAn/LiAJ/y4gCf8uIAn/LiAJ/y4gCf8uIAn/LiAJ/zAh
|
||||
Cv8uHwj/LB0G/yQWAP8fEQD/QzQm/7q0r/////////////39/fT9/f3e/f397vz9/e79/f3u6+rp7puU
|
||||
ju5YTj/uOCkW7i4fCu4rHwjuKx8I7isfCO4rHwjuKx8I7isfCO4rHwjuKx8I7isfCO4rHwjuKx8I7isf
|
||||
CO4rHwjuKx8I7isfCO4rHwjuKx8I7isfCO4rHwjuKx8I7isfCO4rHwjuKx8I7isfCO4rHwjuKx8I7isf
|
||||
CO4rHwjuKx8I7isfCO4rHwjuKx8I7isfCO4rHwjuKx8I7isfCO4rHwjuKx8I7isfCO4rHwjuKx8I7isf
|
||||
CO4rHwjuKx8I7isfCO4rHwjuKyAI7jcpFu5YTj7umpOO7uvr6u79/f3u/Pz97v39/e79/f3eKAAAACAA
|
||||
AABAAAAAAQAgAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAP39/dzt6+vtdGte7TQmFu0rHQjtLR8I7S0f
|
||||
CO0tHwjtLR8I7S0fCO0tHwjtLR8I7S0fCO0tHwjtLR8I7S0fCO0tHwjtLR8I7S0fCO0tHwjtLR8I7S0f
|
||||
CO0tHwjtLR8I7S0fCO0tHwjtLR8I7SsdBu00Jhbtc2le7e3r6e39/f3c6ubl81ZKOP8dDgD/LB4G/y4g
|
||||
Cf8uIAn/LiAJ/y4gCf8uIAn/LiAJ/y4gCf8uIAn/LiAJ/y4gCf8uIAn/LiAJ/y4gCf8uIAn/LiAJ/y4g
|
||||
Cf8uIAn/LiAJ/y4gCf8uIAn/LiAJ/y4gCf8uIAn/LyAK/ywdBf8dDgD/VUk4/+rn5vN0a13tHg4A/y4g
|
||||
Cf8tHwn/LR8J/y0fCf8tHwn/LR8J/y0fCf8tHwn/LR8J/y0fCf8tHwn/LR8J/y0fCf8tHwn/LR8J/y0f
|
||||
Cf8tHwn/LR8J/y0fCf8tHwn/LR8J/y0fCf8tHwn/LR8J/y0fCf8tHwn/LR8J/y4gCf8dDQD/dWtd7TQm
|
||||
Eu0rHQb/LR8J/y0fCf8tHwn/LR8J/y0fCf8tHwn/LR8J/y0fCf8tHwn/LR8J/y0fCf8tHwn/LR8J/y0f
|
||||
Cf8tHwn/LR8J/y0fCf8tHwn/LR8J/y0fCf8tHwn/LR8J/y0fCf8tHwn/LR8J/y0fCf8tHwn/LR8J/ywd
|
||||
Bf80JhLtKx0G7S8gCf8tHwn/LR8J/y0fCf8tHwn/LR8J/y0fCf8tHwn/LR8J/y0fCf8tHwn/LR8J/y0f
|
||||
Cf8tHwn/LR8J/y0fCf8tHwn/LR8J/y0fCf8tHwn/LR8J/y0fCf8tHwn/LR8J/y0fCf8tHwn/LR8J/y0f
|
||||
Cf8tHwn/LiAJ/ysdBu0tHwjtLiAJ/y0fCf8tHwn/LR8J/y0fCf8tHwn/LR8J/y0fCf8tHwj/LBcC/ysT
|
||||
AP8pFAH/JhMC/yISA/8hEQP/IBID/yASA/8gEgT/HxIE/x8RBP8jFgb/LB4I/y0fCf8tHwn/LR8J/y0f
|
||||
Cf8tHwn/LR8J/y0fCf8uIAn/LR8I7S0fCO0uIAn/LR8J/y0fCf8tHwn/LR8J/y0fCf8tHwn/LR4I/ywY
|
||||
BP81QiP/Olw1/0RbLP9WXSb/bGMj/3RnIP9vZCD/bmIg/3FiIP9yYh7/dWMf/1ZFFv8lFwb/LB4I/y0f
|
||||
Cf8tHwn/LR8J/y0fCf8tHwn/LR8J/y4gCf8tHwjtLR8I7S4gCf8tHwn/LR8J/y0fCf8tHwn/LR8J/y0f
|
||||
Cf8rFgL/N2c//0vDgf9SwHr/W7pv/2a2Zf9+t13/p8RT/9bYT//g20z/3tZL/+LWSv/p10f/69RF/39r
|
||||
Iv8iFQT/LR8J/y0fCf8tHwn/LR8J/y0fCf8tHwn/LiAJ/y0fCO0tHwjtLiAJ/y0fCf8tHwn/LR8J/y0f
|
||||
Cf8tHwn/LBwG/y4lD/9Esnb/Sr55/1Kybv9XrWf/Xahe/1+iWP9imlH/faJJ/8DHSv/Pz0r/z8tI/9PK
|
||||
Rf/f0Uf/z75A/zQmC/8pGwj/LR8J/y0fCf8tHwn/LR8J/y0fCf8uIAn/LR8I7S0fCO0uIAn/LR8J/y0f
|
||||
Cf8tHwn/LR8J/y0fCf8sGgX/Ly0W/0S0ef9NuHb/U7Fs/1itZP9ep1z/ZqNW/2yfTv9slUb/hqFE/8fP
|
||||
TP/Lzkv/zcxJ/9TOSf/Ow0T/PS4P/ygaB/8tHwn/LR8J/y0fCf8tHwn/LR8J/y4gCf8tHwjtLR8I7S4g
|
||||
Cf8tHwn/LR8J/y0fCf8tHwn/LR8J/ywbBf8wLBX/RrF1/0+4dP9UsGv/Wqxj/2CmWv9molT/b55N/3WZ
|
||||
RP93kTz/r8BI/8fST//Hzkz/zdFM/8bERv87LQ7/KRoH/y0fCf8tHwn/LR8J/y0fCf8tHwn/LiAJ/y0f
|
||||
CO0tHwjtLiAJ/y0fCf8tHwn/LR8J/y0fCf8tHwn/LBsF/zAsFf9Jr3T/ULdz/1WvaP9dqmD/YqVZ/2mg
|
||||
Uv9wnEv/dplD/3mOOf+iuEn/wNZV/8HPT//H0k7/v8ZJ/zstDv8pGgf/LR8J/y0fCf8tHwn/LR8J/y0f
|
||||
Cf8uIAn/LR8I7S0fCO0uIAn/LR8J/y0fCf8tHwn/LR8J/y0fCf8sGwX/MCwV/0mvc/9RtnH/Vq1m/16p
|
||||
Xv9jpFf/bKBQ/3CcSP93mEH/fIw2/5bCU/+q3F//tdNW/8HWUv+7yEv/Oi4O/ykaB/8tHwn/LR8J/y0f
|
||||
Cf8tHwn/LR8J/y4gCf8tHwjtLR8I7S4gCf8tHwn/LR8J/y0fCf8tHwn/LR8J/ywbBf8wLRT/S65w/1O1
|
||||
bv9ZrGT/YKdc/2ajVv9sn0//cpxG/3uPOf+AmT7/hd5t/5Hhav+e22L/rdtc/6/KUf85LQ//KRoH/y0f
|
||||
Cf8tHwn/LR8J/y0fCf8tHwn/LiAJ/y0fCO0tHwjtLiAJ/y0fCf8tHwn/LR8J/y0fCf8tHwn/LBsG/zAs
|
||||
E/9NrW7/VrRt/1qrYf9ipFj/aZ5R/3GXR/94kj//dKFI/2bddv9t74H/eeZ2/4Xib/+U4mr/mdFe/zgt
|
||||
EP8qGQb/LR8J/y0fCf8tHwn/LR8J/y0fCf8uIAn/LR8I7S0fCO0uIAn/LR8J/y0fCf8tHwn/LR8J/y0f
|
||||
Cf8sGwb/LiwU/06ubf9ZsGn/Xahf/2CqXv9jr13/YLtj/1jRdP9K8o7/SvqT/1byi/9j7oP/b+p8/3zq
|
||||
d/+D22r/NjAS/yoaBv8tHwn/LR8J/y0fCf8tHwn/LR8J/y4gCf8tHwjtLR8I7S4gCf8tHwn/LR8J/y0f
|
||||
Cf8tHwn/LR8J/ywcBv8vJw7/Ualp/1e3bP9N0n//R+GJ/0bojf9E75H/RPKR/0jwj/9J8ZD/R/OR/0z1
|
||||
jv9W8on/ZfWF/2vhd/8yKg//KxoG/y0fCf8tHwn/LR8J/y0fCf8tHwn/LiAJ/y0fCO0tHwjtLiAJ/y0f
|
||||
Cf8tHwn/LR8J/y0fCf8tHwn/LR8I/ysXAv9CaTr/TNSC/0PijP9F34n/RueP/0n0lv9J95b/SvqY/0n9
|
||||
mP9J+5b/R/OR/0j4lP9Q/pb/RI5N/ysTAv8tHwn/LR8J/y0fCf8tHwn/LR8J/y0fCf8uIAn/LR8I7S0f
|
||||
CO0uIAn/LR8J/y0fCf8tHwn/LR8J/y0fCf8tHwn/LRwG/ywbB/9Aunf/RN6M/0bijf9At3D/OXJC/zp7
|
||||
Rf86fEb/NnhD/z+SUv9S3H//U+KD/068bf8sGgj/LRsH/y0fCf8tHwn/LR8J/y0fCf8tHwn/LR8J/y4g
|
||||
Cf8tHwjtLR8I7S4gCf8tHwn/LR8J/y0fCf8tHwn/LR8J/y0fCf8tHQf/LRwI/0CydP9E2oz/RuWR/zqO
|
||||
Vv8qAAD/LA8A/ywOAP8pAgD/Mzkb/0/bhP9Q24T/SLRs/y0dCP8tHQf/LR8J/y0fCf8tHwn/LR8J/y0f
|
||||
Cf8tHwn/LiAJ/y0fCO0tHwjtLiAJ/y0fCf8tHwn/LR8J/y0fCf8tHwn/LR8J/y0eCP8tGQT/PqZp/0TW
|
||||
jP9F1or/Qbh0/ywbBv8sFQL/LBwG/ykGAP86eEX/Ru6U/0Xpkf9At3H/LRkE/y0eB/8tHwn/LR8J/y0f
|
||||
Cf8tHwn/LR8J/y0fCf8uIAn/LR8I7S0fCO0uIAn/LR8J/y0fCf8tHwn/LR8J/y0fCf8tHwn/LR8J/ysR
|
||||
AP86fk//RNeP/0TJg/9G14z/PI1Y/y4pEP8tHQn/M1ky/0TWhv9F4Yz/R/CV/zqOU/8sEAD/LR8I/y0f
|
||||
Cf8tHwn/LR8J/y0fCf8tHwn/LR8J/y4gCf8tHwjtLR8I7S4gCf8tHwn/LR8J/y0fCf8tHwn/LR8J/y0f
|
||||
Cf8tHwn/LBcD/zE5HP9CxoX/QsqF/0PIgv9F1ov/QsR+/0C7d/9F2oz/RNmK/0Xdiv9G34z/MT0d/ywW
|
||||
A/8tHwn/LR8J/y0fCf8tHwn/LR8J/y0fCf8tHwn/LiAJ/y0fCO0tHwjtLiAJ/y0fCf8tHwn/LR8J/y0f
|
||||
Cf8tHwn/LR8J/y0fCf8tHwn/LBQA/zdmPf9C0Iz/QcmG/0LFgv9DzIX/RNGI/0POhv9F14v/R+WU/zdv
|
||||
Qv8qEQD/LR8J/y0fCf8tHwn/LR8J/y0fCf8tHwn/LR8J/y0fCf8uIAn/LR8I7S0fCO0uIAn/LR8J/y0f
|
||||
Cf8tHwn/LR8J/y0fCf8tHwn/LR8J/y0fCf8tHgj/LBUB/zVeNv9Ct3v/Q8+M/0LRjP9E1I7/RteO/0TE
|
||||
fv84ZDr/LRQB/y0eCP8tHwn/LR8J/y0fCf8tHwn/LR8J/y0fCf8tHwn/LR8J/y4gCf8tHwjtLR8I7S4g
|
||||
Cf8tHwn/LR8J/y0fCf8tHwn/LR8J/y0fCf8tHwn/LR8J/y0fCf8tHgj/LBMA/y8qEf84Xjb/O3lM/zt6
|
||||
Tf86YDf/MCoR/ysSAP8tHgj/LR8J/y0fCf8tHwn/LR8J/y0fCf8tHwn/LR8J/y0fCf8tHwn/LiAJ/y0f
|
||||
CO0tHwjtLiAJ/y0fCf8tHwn/LR8J/y0fCf8tHwn/LR8J/y0fCf8tHwn/LR8J/y0fCf8tHwn/LBoF/ysT
|
||||
AP8sEQD/KxEA/ysSAP8sGgX/LSAJ/y0fCf8tHwn/LR8J/y0fCf8tHwn/LR8J/y0fCf8tHwn/LR8J/y0f
|
||||
Cf8uIAn/LR8I7SweBu0vIAn/LR8J/y0fCf8tHwn/LR8J/y0fCf8tHwn/LR8J/y0fCf8tHwn/LR8J/y0f
|
||||
Cf8tHwn/LR8J/y0fCf8tHwn/LR8J/y0fCf8tHwn/LR8J/y0fCf8tHwn/LR8J/y0fCf8tHwn/LR8J/y0f
|
||||
Cf8tHwn/LR8J/y4gCf8rHQbtNCYT7SwdBv8tHwn/LR8J/y0fCf8tHwn/LR8J/y0fCf8tHwn/LR8J/y0f
|
||||
Cf8tHwn/LR8J/y0fCf8tHwn/LR8J/y0fCf8tHwn/LR8J/y0fCf8tHwn/LR8J/y0fCf8tHwn/LR8J/y0f
|
||||
Cf8tHwn/LR8J/y0fCf8tHwn/LB4G/zQmEu11a17tHQ4A/y4gCf8tHwn/LR8J/y0fCf8tHwn/LR8J/y0f
|
||||
Cf8tHwn/LR8J/y0fCf8tHwn/LR8J/y0fCf8tHwn/LR8J/y0fCf8tHwn/LR8J/y0fCf8tHwn/LR8J/y0f
|
||||
Cf8tHwn/LR8J/y0fCf8tHwn/LR8J/y0gCf8dDgD/dmxe7ern5vNVSTf/Hg4A/ywdBv8uIAn/LiAJ/y4g
|
||||
Cf8uIAn/LiAJ/y4gCf8uIAn/LiAJ/y4gCf8uIAn/LiAJ/y4gCf8uIAn/LiAJ/y4gCf8uIAn/LiAJ/y4g
|
||||
Cf8uIAn/LiAJ/y4gCf8uIAn/LiAJ/y4gCf8rHQX/Hg4A/1ZKOP/q5+fz/f393Ozr6e10aV7tNSYU7Swe
|
||||
CO0tHwjtLR8I7S0fCO0tHwjtLR8I7S0fCO0tHwjtLR8I7S0fCO0tHwjtLR8I7S0fCO0tHwjtLR8I7S0f
|
||||
CO0tHwjtLR8I7S0fCO0tHwjtLR8I7S0fCO0tHwjtLB4I7TQmFO1zaV7t7evp7f39/dwoAAAAGAAAADAA
|
||||
AAABACAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA/f393JSPhO00JhbtKx0G7S0fCO0tHwjtLR8I7S0f
|
||||
CO0tHwjtLR8I7S0fCO0tHwjtLR8I7S0fCO0tHwjtLR8I7S0fCO0tHwjtLR8I7S0fCO0rHQbtMyUW7ZSO
|
||||
g+39/f3ckouC8yESAP8qHAT/LyAJ/y4gCf8uIAn/LiAJ/y4gCf8uIAn/LiAJ/y4gCf8uIAn/LiAJ/y4g
|
||||
Cf8uIAn/LiAJ/y4gCf8uIAn/LiAJ/y4gCf8vIAn/KhwE/yERAP+Si4LzMyUR7SocBP8tHwn/LR8J/y0f
|
||||
Cf8tHwn/LR8J/y0fCf8tHwn/LR8J/y0fCf8tHwn/LR8J/y0fCf8tHwn/LR8J/y0fCf8tHwn/LR8J/y0f
|
||||
Cf8tHwn/LR8J/yocBP8zJRLtKx0F7S4gCf8tHwn/LR8J/y0fCf8tHwn/LR8J/y0eCP8sGgX/KxoG/yka
|
||||
B/8oGgf/KBoH/ygaB/8nGgf/KBoH/yweCP8tHwn/LR8J/y0fCf8tHwn/LR8J/y4gCf8rHQXtLR8I7S4g
|
||||
Cf8tHwn/LR8J/y0fCf8tHwn/LRwH/y0aBf8wLRP/Ni8Q/0ExDv9FMw3/PjAN/z4vDv8/MA3/PS4N/ycZ
|
||||
B/8qHAj/LR8J/y0fCf8tHwn/LR8J/y4gCf8tHwjtLR8I7S4gCf8tHwn/LR8J/y0fCf8tHQf/LB8K/z6E
|
||||
VP9NsnL/Wapi/3GoVv+bskv/x8VG/87FRP/PwkL/2cZB/6GMLf8tHwj/Kx0I/y0fCf8tHwn/LR8J/y4g
|
||||
Cf8tHwjtLR8I7S4gCf8tHwn/LR8J/y0fCf8sFgH/MkYn/0nIhP9Rt3L/WK9m/16nXf9jnlP/iKlK/8nO
|
||||
S//T0Er/2M9H/+jYSv9XSRf/IxUF/y0fCf8tHwn/LR8J/y4gCf8tHwjtLR8I7S4gCf8tHwn/LR8J/y0f
|
||||
Cf8sFQH/M0os/0vBfv9Tsm3/Wqxi/2OlWP9sn07/bJJD/5uuRP/M0k3/y81L/9nWTP9cTxv/IhQF/y0f
|
||||
Cf8tHwn/LR8J/y4gCf8tHwjtLR8I7S4gCf8tHwn/LR8J/y0fCf8rFQH/NEkq/02+e/9UsGr/Xalg/2Wj
|
||||
Vv9unkz/dJRB/4ufPv+/01P/xM9O/8/XT/9YThr/IxQE/y0fCf8tHwn/LR8J/y4gCf8tHwjtLR8I7S4g
|
||||
Cf8tHwn/LR8J/y0fCf8rFgH/NUkp/068ef9Xrmf/X6dc/2iiVP9wnUn/eI86/4ilRP+k3mL/tNVX/8ba
|
||||
U/9WTxv/JBQE/y0fCf8tHwn/LR8J/y4gCf8tHwjtLR8I7S4gCf8tHwn/LR8J/y0fCf8rFgH/Nkgo/1K7
|
||||
dv9ZrWT/YqRZ/2ycTf93kj//epU+/3bVa/+D53T/lN5n/63hYv9RTx//JRMD/y0fCf8tHwn/LR8J/y4g
|
||||
Cf8tHwjtLR8I7S4gCf8tHwn/LR8J/y0fCf8qFQL/NUop/1a6c/9cqWH/Yatd/2SwW/9gv2b/UeeE/1L4
|
||||
kP9k7oL/dOh5/4vvdf9JViX/JxIC/y0fCf8tHwn/LR8J/y4gCf8tHwjtLR8I7S4gCf8tHwn/LR8J/y0f
|
||||
Cf8rFwP/NT4e/1a5cP9N0H3/R+GI/0fwkv9F+5n/SPuY/0j7lv9J95H/VfaM/2f6iP88SyH/KhQC/y0f
|
||||
Cf8tHwn/LR8J/y4gCf8tHwjtLR8I7S4gCf8tHwn/LR8J/y0fCf8tHgj/LBUC/z6ETv9E6JL/Rd+L/0LG
|
||||
ef9DwHP/QsR1/0LHdP9L4oX/T/aS/0KaV/8rFgT/LR4I/y0fCf8tHwn/LR8J/y4gCf8tHwjtLR8I7S4g
|
||||
Cf8tHwn/LR8J/y0fCf8tHwn/Kw8A/zVgOv9F4pL/Rd+N/zJGJP8tEAD/LRoF/ywaBv9Lum3/VOeL/zle
|
||||
M/8qDwD/LR8J/y0fCf8tHwn/LR8J/y4gCf8tHwjtLR8I7S4gCf8tHwn/LR8J/y0fCf8tHwn/KxMA/zVX
|
||||
Mf9F2I7/RtuO/zl3R/8qBQD/KQQA/zJDIv9F4Iv/RvCW/zVfNf8rEQD/LR8J/y0fCf8tHwn/LR8J/y4g
|
||||
Cf8tHwjtLR8I7S4gCf8tHwn/LR8J/y0fCf8tHwn/LBoF/zAtE/9Cv3//RNCJ/0TNhf86g1D/N29C/0LF
|
||||
fP9G6JH/RNeF/y8wFP8sGQT/LR8J/y0fCf8tHwn/LR8J/y4gCf8tHwjtLR8I7S4gCf8tHwn/LR8J/y0f
|
||||
Cf8tHwn/LR8J/ywTAP82Yzv/QtKN/0PPif9F2I3/Rd2Q/0XekP9H5ZP/N2w//ysRAP8tHwn/LR8J/y0f
|
||||
Cf8tHwn/LR8J/y4gCf8tHwjtLR8I7S4gCf8tHwn/LR8J/y0fCf8tHwn/LR8J/y0eCP8sFQH/NVs1/0Cu
|
||||
dP9CxoT/RMqG/0O3dv83YTj/LBQB/y0eCP8tHwn/LR8J/y0fCf8tHwn/LR8J/y4gCf8tHwjtLR8I7S4g
|
||||
Cf8tHwn/LR8J/y0fCf8tHwn/LR8J/y0fCf8tHgj/LBMA/y4iC/8zPB7/Mj0e/y8hCv8rEgD/LR4I/y0f
|
||||
Cf8tHwn/LR8J/y0fCf8tHwn/LR8J/y4gCf8tHwjtKx0F7S4gCf8tHwn/LR8J/y0fCf8tHwn/LR8J/y0f
|
||||
Cf8tHwn/LR8J/ywcB/8sFwP/LBcD/ywcB/8tHwn/LR8J/y0fCf8tHwn/LR8J/y0fCf8tHwn/LR8J/y4g
|
||||
Cf8rHQbtNCUS7SocBP8tHwn/LR8J/y0fCf8tHwn/LR8J/y0fCf8tHwn/LR8J/y0fCf8tHwn/LR8J/y0f
|
||||
Cf8tHwn/LR8J/y0fCf8tHwn/LR8J/y0fCf8tHwn/LR8J/yocBP80JhLtkouD8yERAP8qHAT/LyAJ/y4g
|
||||
Cf8uIAn/LiAJ/y4gCf8uIAn/LiAJ/y4gCf8uIAn/LiAJ/y4gCf8uIAn/LiAJ/y4gCf8uIAn/LiAJ/y4g
|
||||
Cf8vIAn/KhwE/yERAP+VjITz/f393JSOhO00JhbtKx0G7S0fCO0tHwjtLR8I7S0fCO0tHwjtLR8I7S0f
|
||||
CO0tHwjtLR8I7S0fCO0tHwjtLR8I7S0fCO0tHwjtLR8I7S0fCO0rHQbtMyUW7ZSOg+39/f3cKAAAABAA
|
||||
AAAgAAAAAQAgAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAL65stw/MR/tKBkF7S0fCO0tHwjtLR8I7S0f
|
||||
CO0tHwjtLR8I7S0fCO0tHwjtLR8I7S0fCO0oGQXtPzEf7b65stw8MR3zJxgA/y8gCf8uIAn/LiAJ/y4g
|
||||
Cf8uHwn/Lh8J/y4fCf8uHwn/LiAJ/y4gCf8uIAn/LyAJ/ycYAP88MR3zKBoE7S8gCf8tHwn/LSAJ/ywa
|
||||
Bf8rFAH/LBcD/ysXBP8kFQT/IxUF/yATBP8oGgf/LiAJ/y0fCf8vIQn/KBkE7S0fCO0uIAn/LR8J/ywa
|
||||
Bf8wMxj/QYNR/1OFSP94iTr/npg0/6aYM/+hji7/RDQP/ycaB/8tHwn/LiAJ/y0fCO0tHwjtLiAJ/y0f
|
||||
Cf8rEQD/OW9F/1LNhP9dtGj/aahY/5u2TP/Z2U3/7+ZP/4V2KP8eEAT/LR8J/y4gCf8tHwjtLR8I7S4g
|
||||
Cf8tHwn/KxIA/ztvRf9Tv3f/Xqhd/2ufT/91kj7/tMNK/9fgU/+AeSr/IBAD/y0fCf8uIAn/LR8I7S0f
|
||||
CO0uIAn/LR8J/ysTAP89bUL/Vrxz/2OjWP9ylkX/fZQ7/5XTX/+952D/d3ot/yEQA/8tHwn/LiAJ/y0f
|
||||
CO0tHwjtLiAJ/y0fCf8qEwD/P29C/1y6bv9iqlv/Z7Fa/1zad/9l8oX/ifZ6/2OGPP8lDgH/LR8J/y4g
|
||||
Cf8tHwjtLR8I7S4gCf8tHwn/KhQB/zxULf9P0oD/SOSL/0b1lf9F/Jn/SPaS/1j7kP9DbTb/KRAB/y0f
|
||||
Cf8uIAn/LR8I7S0fCO0uIAn/LR8J/y0cB/8uIwz/QcuC/0HCef8zRSL/MD0e/0SmYP9M0n7/LSMN/ywc
|
||||
Bv8tHwn/LiAJ/y0fCO0tHwjtLiAJ/y0fCf8tHQj/LRsG/0Cxc/9Ez4b/MDca/yweCf9Cxnr/Qsd6/y0b
|
||||
Bv8tHQj/LR8J/y4gCf8tHwjtLR8I7S4gCf8tHwn/LR8J/ywUAf81XTf/RNiR/0TTiv9E1In/R+2Y/zZn
|
||||
Ov8rEgH/LR8J/y0fCf8uIAn/LR8I7S0fCO0uIAn/LR8J/y0fCf8tHgj/LBcC/zRWMv8+nWb/P6Jo/zZb
|
||||
NP8sFgL/LR4I/y0fCf8tHwn/LiAJ/y0fCO0oGgTtLyEJ/y0fCf8tHwn/LR8J/y0fCP8sFAH/LBYC/ywW
|
||||
Av8rEwD/LR4I/y0fCf8tHwn/LR8J/y8hCf8oGQTtPTEd8ycYAP8vIAn/LiAJ/y4gCf8uIAn/LiAJ/y4f
|
||||
CP8uHwn/LiAJ/y4gCf8uIAn/LiAJ/y8gCf8nGAD/PTEd87u5stw/MR/tKBkF7S0fCO0tHwjtLR8I7S0f
|
||||
CO0tHwjtLR8I7S0fCO0tHwjtLR8I7S0fCO0oGQXtPzEf7b65stwAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
|
||||
AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
|
||||
AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
|
||||
AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
|
||||
AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
|
||||
AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
|
||||
AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
|
||||
AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
|
||||
AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
|
||||
AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
|
||||
AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
|
||||
AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
|
||||
AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
|
||||
AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
|
||||
AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
|
||||
AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
|
||||
AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
|
||||
AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
|
||||
AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
|
||||
AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
|
||||
AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
|
||||
AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
|
||||
AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
|
||||
AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
|
||||
AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
|
||||
AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
|
||||
AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
|
||||
AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
|
||||
AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
|
||||
AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
|
||||
AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
|
||||
AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
|
||||
AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
|
||||
AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
|
||||
AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
|
||||
AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
|
||||
AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
|
||||
AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
|
||||
AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
|
||||
AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
|
||||
AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
|
||||
AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
|
||||
AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
|
||||
AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
|
||||
AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
|
||||
AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
|
||||
AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
|
||||
AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
|
||||
AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
|
||||
AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
|
||||
AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
|
||||
AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
|
||||
AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
|
||||
AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
|
||||
AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
|
||||
AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
|
||||
AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
|
||||
AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
|
||||
AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
|
||||
AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
|
||||
AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
|
||||
AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
|
||||
AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
|
||||
AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
|
||||
AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
|
||||
AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
|
||||
AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
|
||||
AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
|
||||
AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
|
||||
AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
|
||||
AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
|
||||
AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
|
||||
AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
|
||||
AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
|
||||
AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
|
||||
AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
|
||||
AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
|
||||
AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
|
||||
AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
|
||||
AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
|
||||
AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
|
||||
AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
|
||||
AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
|
||||
AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
|
||||
AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
|
||||
AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
|
||||
AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
|
||||
AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
|
||||
AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
|
||||
AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
|
||||
AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
|
||||
AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
|
||||
AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
|
||||
AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
|
||||
AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
|
||||
AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
|
||||
AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
|
||||
AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
|
||||
AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
|
||||
AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
|
||||
AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
|
||||
AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
|
||||
AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
|
||||
AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
|
||||
AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
|
||||
AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
|
||||
AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
|
||||
AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
|
||||
AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
|
||||
AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
|
||||
AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
|
||||
AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
|
||||
AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
|
||||
AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
|
||||
AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
|
||||
AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
|
||||
AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
|
||||
AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
|
||||
AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
|
||||
AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
|
||||
AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
|
||||
AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
|
||||
AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
|
||||
AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
|
||||
AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
|
||||
AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
|
||||
AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
|
||||
AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
|
||||
AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
|
||||
AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
|
||||
AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
|
||||
AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
|
||||
AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
|
||||
AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
|
||||
AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
|
||||
AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
|
||||
AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
|
||||
AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
|
||||
AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
|
||||
AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
|
||||
AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
|
||||
AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
|
||||
AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
|
||||
AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
|
||||
AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
|
||||
AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
|
||||
AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
|
||||
AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
|
||||
AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
|
||||
AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
|
||||
</value>
|
||||
</data>
|
||||
</root>
|
||||
@@ -1,377 +0,0 @@
|
||||
//----------------------
|
||||
// <auto-generated>
|
||||
// Generated using the NSwag toolchain v13.14.8.0 (NJsonSchema v10.5.2.0 (Newtonsoft.Json v11.0.0.0)) (http://NSwag.org)
|
||||
// </auto-generated>
|
||||
//----------------------
|
||||
|
||||
#pragma warning disable 108 // Disable "CS0108 '{derivedDto}.ToJson()' hides inherited member '{dtoBase}.ToJson()'. Use the new keyword if hiding was intended."
|
||||
#pragma warning disable 114 // Disable "CS0114 '{derivedDto}.RaisePropertyChanged(String)' hides inherited member 'dtoBase.RaisePropertyChanged(String)'. To make the current member override that implementation, add the override keyword. Otherwise add the new keyword."
|
||||
#pragma warning disable 472 // Disable "CS0472 The result of the expression is always 'false' since a value of type 'Int32' is never equal to 'null' of type 'Int32?'
|
||||
#pragma warning disable 1573 // Disable "CS1573 Parameter '...' has no matching param tag in the XML comment for ...
|
||||
#pragma warning disable 1591 // Disable "CS1591 Missing XML comment for publicly visible type or member ..."
|
||||
#pragma warning disable 8073 // Disable "CS8073 The result of the expression is always 'false' since a value of type 'T' is never equal to 'null' of type 'T?'"
|
||||
#pragma warning disable 3016 // Disable "CS3016 Arrays as attribute arguments is not CLS-compliant"
|
||||
|
||||
namespace SecretServerAuthentication.DSS
|
||||
{
|
||||
using System = global::System;
|
||||
|
||||
[System.CodeDom.Compiler.GeneratedCode("NSwag", "13.14.8.0 (NJsonSchema v10.5.2.0 (Newtonsoft.Json v11.0.0.0))")]
|
||||
public partial class OAuth2ServiceClient
|
||||
{
|
||||
private string _baseUrl = "";
|
||||
private System.Net.Http.HttpClient _httpClient;
|
||||
private System.Lazy<Newtonsoft.Json.JsonSerializerSettings> _settings;
|
||||
|
||||
public OAuth2ServiceClient(string baseUrl, System.Net.Http.HttpClient httpClient)
|
||||
{
|
||||
BaseUrl = baseUrl;
|
||||
_httpClient = httpClient;
|
||||
_settings = new System.Lazy<Newtonsoft.Json.JsonSerializerSettings>(CreateSerializerSettings);
|
||||
}
|
||||
|
||||
private Newtonsoft.Json.JsonSerializerSettings CreateSerializerSettings()
|
||||
{
|
||||
var settings = new Newtonsoft.Json.JsonSerializerSettings();
|
||||
UpdateJsonSerializerSettings(settings);
|
||||
return settings;
|
||||
}
|
||||
|
||||
public string BaseUrl
|
||||
{
|
||||
get { return _baseUrl; }
|
||||
set { _baseUrl = value; }
|
||||
}
|
||||
|
||||
protected Newtonsoft.Json.JsonSerializerSettings JsonSerializerSettings { get { return _settings.Value; } }
|
||||
|
||||
partial void UpdateJsonSerializerSettings(Newtonsoft.Json.JsonSerializerSettings settings);
|
||||
|
||||
|
||||
partial void PrepareRequest(System.Net.Http.HttpClient client, System.Net.Http.HttpRequestMessage request, string url);
|
||||
partial void PrepareRequest(System.Net.Http.HttpClient client, System.Net.Http.HttpRequestMessage request, System.Text.StringBuilder urlBuilder);
|
||||
partial void ProcessResponse(System.Net.Http.HttpClient client, System.Net.Http.HttpResponseMessage response);
|
||||
/// <summary>Retrieve or Refresh Access Token</summary>
|
||||
/// <param name="grant_type">Authentication grant type. Use 'password' when authenticating, and 'refresh_token' when refreshing a token.</param>
|
||||
/// <param name="username">Secret Server authentication username. Required when authenticating.</param>
|
||||
/// <param name="password">Secret Server authentication password. Required when authenticating.</param>
|
||||
/// <param name="refresh_token">The refresh token. Required when refreshing a token.</param>
|
||||
/// <returns>Successful retrieval of an access token</returns>
|
||||
/// <exception cref="ApiException">A server side error occurred.</exception>
|
||||
public System.Threading.Tasks.Task<TokenResponse> AuthorizeAsync(Grant_type grant_type, string username, string password, string refresh_token, string OTP) => AuthorizeAsync(grant_type, username, password, refresh_token, System.Threading.CancellationToken.None, OTP);
|
||||
|
||||
/// <param name="cancellationToken">A cancellation token that can be used by other objects or threads to receive notice of cancellation.</param>
|
||||
/// <summary>Retrieve or Refresh Access Token</summary>
|
||||
/// <param name="grant_type">Authentication grant type. Use 'password' when authenticating, and 'refresh_token' when refreshing a token.</param>
|
||||
/// <param name="username">Secret Server authentication username. Required when authenticating.</param>
|
||||
/// <param name="password">Secret Server authentication password. Required when authenticating.</param>
|
||||
/// <param name="refresh_token">The refresh token. Required when refreshing a token.</param>
|
||||
/// <returns>Successful retrieval of an access token</returns>
|
||||
/// <exception cref="ApiException">A server side error occurred.</exception>
|
||||
public async System.Threading.Tasks.Task<TokenResponse> AuthorizeAsync(Grant_type grant_type, string username, string password, string refresh_token, System.Threading.CancellationToken cancellationToken, string OTP)
|
||||
{
|
||||
var urlBuilder_ = new System.Text.StringBuilder();
|
||||
urlBuilder_.Append(BaseUrl != null ? BaseUrl.TrimEnd('/') : "").Append("/oauth2/token");
|
||||
|
||||
var client_ = _httpClient;
|
||||
var disposeClient_ = false;
|
||||
try
|
||||
{
|
||||
using (var request_ = new System.Net.Http.HttpRequestMessage())
|
||||
{
|
||||
var keyValues_ = new System.Collections.Generic.List<System.Collections.Generic.KeyValuePair<string, string>>();
|
||||
if (grant_type == null)
|
||||
throw new System.ArgumentNullException("grant_type");
|
||||
else
|
||||
keyValues_.Add(new System.Collections.Generic.KeyValuePair<string, string>("grant_type", ConvertToString(grant_type, System.Globalization.CultureInfo.InvariantCulture)));
|
||||
if (username != null)
|
||||
keyValues_.Add(new System.Collections.Generic.KeyValuePair<string, string>("username", ConvertToString(username, System.Globalization.CultureInfo.InvariantCulture)));
|
||||
if (password != null)
|
||||
keyValues_.Add(new System.Collections.Generic.KeyValuePair<string, string>("password", ConvertToString(password, System.Globalization.CultureInfo.InvariantCulture)));
|
||||
if (refresh_token != null)
|
||||
keyValues_.Add(new System.Collections.Generic.KeyValuePair<string, string>("refresh_token", ConvertToString(refresh_token, System.Globalization.CultureInfo.InvariantCulture)));
|
||||
if (OTP != null)
|
||||
request_.Headers.Add("OTP", ConvertToString(OTP, System.Globalization.CultureInfo.InvariantCulture));
|
||||
|
||||
request_.Content = new System.Net.Http.FormUrlEncodedContent(keyValues_);
|
||||
request_.Method = new System.Net.Http.HttpMethod("POST");
|
||||
request_.Headers.Accept.Add(System.Net.Http.Headers.MediaTypeWithQualityHeaderValue.Parse("application/json"));
|
||||
|
||||
PrepareRequest(client_, request_, urlBuilder_);
|
||||
|
||||
var url_ = urlBuilder_.ToString();
|
||||
request_.RequestUri = new System.Uri(url_, System.UriKind.RelativeOrAbsolute);
|
||||
|
||||
PrepareRequest(client_, request_, url_);
|
||||
|
||||
var response_ = await client_.SendAsync(request_, System.Net.Http.HttpCompletionOption.ResponseHeadersRead, cancellationToken).ConfigureAwait(false);
|
||||
var disposeResponse_ = true;
|
||||
try
|
||||
{
|
||||
var headers_ = System.Linq.Enumerable.ToDictionary(response_.Headers, h_ => h_.Key, h_ => h_.Value);
|
||||
if (response_.Content != null && response_.Content.Headers != null)
|
||||
{
|
||||
foreach (var item_ in response_.Content.Headers)
|
||||
headers_[item_.Key] = item_.Value;
|
||||
}
|
||||
|
||||
ProcessResponse(client_, response_);
|
||||
|
||||
var status_ = (int)response_.StatusCode;
|
||||
if (status_ == 200)
|
||||
{
|
||||
var objectResponse_ = await ReadObjectResponseAsync<TokenResponse>(response_, headers_, cancellationToken).ConfigureAwait(false);
|
||||
if (objectResponse_.Object == null)
|
||||
{
|
||||
throw new ApiException("Response was null which was not expected.", status_, objectResponse_.Text, headers_, null);
|
||||
}
|
||||
|
||||
return objectResponse_.Object;
|
||||
}
|
||||
else
|
||||
if (status_ == 400)
|
||||
{
|
||||
var objectResponse_ = await ReadObjectResponseAsync<TokenErrorResponse>(response_, headers_, cancellationToken).ConfigureAwait(false);
|
||||
if (objectResponse_.Object == null)
|
||||
{
|
||||
throw new ApiException("Response was null which was not expected.", status_, objectResponse_.Text, headers_, null);
|
||||
}
|
||||
throw new ApiException<TokenErrorResponse>("An error occurred", status_, objectResponse_.Text, headers_, objectResponse_.Object, null);
|
||||
}
|
||||
else
|
||||
{
|
||||
var responseData_ = response_.Content == null ? null : await response_.Content.ReadAsStringAsync().ConfigureAwait(false);
|
||||
throw new ApiException("The HTTP status code of the response was not expected (" + status_ + ").", status_, responseData_, headers_, null);
|
||||
}
|
||||
}
|
||||
finally
|
||||
{
|
||||
if (disposeResponse_)
|
||||
response_.Dispose();
|
||||
}
|
||||
}
|
||||
}
|
||||
finally
|
||||
{
|
||||
if (disposeClient_)
|
||||
client_.Dispose();
|
||||
}
|
||||
}
|
||||
|
||||
protected struct ObjectResponseResult<T>
|
||||
{
|
||||
public ObjectResponseResult(T responseObject, string responseText)
|
||||
{
|
||||
this.Object = responseObject;
|
||||
this.Text = responseText;
|
||||
}
|
||||
|
||||
public T Object { get; }
|
||||
|
||||
public string Text { get; }
|
||||
}
|
||||
|
||||
public bool ReadResponseAsString { get; set; }
|
||||
|
||||
protected virtual async System.Threading.Tasks.Task<ObjectResponseResult<T>> ReadObjectResponseAsync<T>(System.Net.Http.HttpResponseMessage response, System.Collections.Generic.IReadOnlyDictionary<string, System.Collections.Generic.IEnumerable<string>> headers, System.Threading.CancellationToken cancellationToken)
|
||||
{
|
||||
if (response == null || response.Content == null)
|
||||
{
|
||||
return new ObjectResponseResult<T>(default(T), string.Empty);
|
||||
}
|
||||
|
||||
if (ReadResponseAsString)
|
||||
{
|
||||
var responseText = await response.Content.ReadAsStringAsync().ConfigureAwait(false);
|
||||
try
|
||||
{
|
||||
var typedBody = Newtonsoft.Json.JsonConvert.DeserializeObject<T>(responseText, JsonSerializerSettings);
|
||||
return new ObjectResponseResult<T>(typedBody, responseText);
|
||||
}
|
||||
catch (Newtonsoft.Json.JsonException exception)
|
||||
{
|
||||
var message = "Could not deserialize the response body string as " + typeof(T).FullName + ".";
|
||||
throw new ApiException(message, (int)response.StatusCode, responseText, headers, exception);
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
try
|
||||
{
|
||||
using (var responseStream = await response.Content.ReadAsStreamAsync().ConfigureAwait(false))
|
||||
using (var streamReader = new System.IO.StreamReader(responseStream))
|
||||
using (var jsonTextReader = new Newtonsoft.Json.JsonTextReader(streamReader))
|
||||
{
|
||||
var serializer = Newtonsoft.Json.JsonSerializer.Create(JsonSerializerSettings);
|
||||
var typedBody = serializer.Deserialize<T>(jsonTextReader);
|
||||
return new ObjectResponseResult<T>(typedBody, string.Empty);
|
||||
}
|
||||
}
|
||||
catch (Newtonsoft.Json.JsonException exception)
|
||||
{
|
||||
var message = "Could not deserialize the response body stream as " + typeof(T).FullName + ".";
|
||||
throw new ApiException(message, (int)response.StatusCode, string.Empty, headers, exception);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private string ConvertToString(object value, System.Globalization.CultureInfo cultureInfo)
|
||||
{
|
||||
if (value == null)
|
||||
{
|
||||
return "";
|
||||
}
|
||||
|
||||
if (value is System.Enum)
|
||||
{
|
||||
var name = System.Enum.GetName(value.GetType(), value);
|
||||
if (name != null)
|
||||
{
|
||||
var field = System.Reflection.IntrospectionExtensions.GetTypeInfo(value.GetType()).GetDeclaredField(name);
|
||||
if (field != null)
|
||||
{
|
||||
var attribute = System.Reflection.CustomAttributeExtensions.GetCustomAttribute(field, typeof(System.Runtime.Serialization.EnumMemberAttribute))
|
||||
as System.Runtime.Serialization.EnumMemberAttribute;
|
||||
if (attribute != null)
|
||||
{
|
||||
return attribute.Value != null ? attribute.Value : name;
|
||||
}
|
||||
}
|
||||
|
||||
var converted = System.Convert.ToString(System.Convert.ChangeType(value, System.Enum.GetUnderlyingType(value.GetType()), cultureInfo));
|
||||
return converted == null ? string.Empty : converted;
|
||||
}
|
||||
}
|
||||
else if (value is bool)
|
||||
{
|
||||
return System.Convert.ToString((bool)value, cultureInfo).ToLowerInvariant();
|
||||
}
|
||||
else if (value is byte[])
|
||||
{
|
||||
return System.Convert.ToBase64String((byte[])value);
|
||||
}
|
||||
else if (value.GetType().IsArray)
|
||||
{
|
||||
var array = System.Linq.Enumerable.OfType<object>((System.Array)value);
|
||||
return string.Join(",", System.Linq.Enumerable.Select(array, o => ConvertToString(o, cultureInfo)));
|
||||
}
|
||||
|
||||
var result = System.Convert.ToString(value, cultureInfo);
|
||||
return result == null ? "" : result;
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>API access token response</summary>
|
||||
[System.CodeDom.Compiler.GeneratedCode("NJsonSchema", "10.5.2.0 (Newtonsoft.Json v11.0.0.0)")]
|
||||
public partial class TokenResponse
|
||||
{
|
||||
/// <summary>Authentication token</summary>
|
||||
[Newtonsoft.Json.JsonProperty("access_token", Required = Newtonsoft.Json.Required.Always)]
|
||||
[System.ComponentModel.DataAnnotations.Required(AllowEmptyStrings = true)]
|
||||
public string Access_token { get; set; }
|
||||
|
||||
/// <summary>Authentication token type</summary>
|
||||
[Newtonsoft.Json.JsonProperty("token_type", Required = Newtonsoft.Json.Required.Always)]
|
||||
[System.ComponentModel.DataAnnotations.Required(AllowEmptyStrings = true)]
|
||||
[Newtonsoft.Json.JsonConverter(typeof(Newtonsoft.Json.Converters.StringEnumConverter))]
|
||||
public TokenResponseToken_type Token_type { get; set; }
|
||||
|
||||
|
||||
private string _Expires_in;
|
||||
/// <summary>Authentication token expiration time, in seconds</summary>
|
||||
[Newtonsoft.Json.JsonProperty("expires_in", Required = Newtonsoft.Json.Required.Always)]
|
||||
[System.ComponentModel.DataAnnotations.Required(AllowEmptyStrings = true)]
|
||||
// public string Expires_in { get; set; }
|
||||
|
||||
public string Expires_in
|
||||
{
|
||||
get { return _Expires_in; }
|
||||
set
|
||||
{
|
||||
_Expires_in = value;
|
||||
Expires_on = DateTime.UtcNow.AddSeconds(Double.Parse(value) - 60);
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>Authentication token expiration time in UTC</summary>
|
||||
public DateTime Expires_on { get; set; }
|
||||
|
||||
/// <summary>Refresh token. This is only provided when the server is set to allow refresh tokens for web services and when the session timeout duration is not set to Unlimited.</summary>
|
||||
[Newtonsoft.Json.JsonProperty("refresh_token", Required = Newtonsoft.Json.Required.Default, NullValueHandling = Newtonsoft.Json.NullValueHandling.Ignore)]
|
||||
public string Refresh_token { get; set; }
|
||||
|
||||
|
||||
}
|
||||
|
||||
/// <summary>API access token error response</summary>
|
||||
[System.CodeDom.Compiler.GeneratedCode("NJsonSchema", "10.5.2.0 (Newtonsoft.Json v11.0.0.0)")]
|
||||
public partial class TokenErrorResponse
|
||||
{
|
||||
/// <summary>Authentication token</summary>
|
||||
[Newtonsoft.Json.JsonProperty("message", Required = Newtonsoft.Json.Required.Always)]
|
||||
[System.ComponentModel.DataAnnotations.Required(AllowEmptyStrings = true)]
|
||||
public string Message { get; set; }
|
||||
|
||||
|
||||
}
|
||||
|
||||
/// <summary>Authentication grant type. Use 'password' when authenticating, and 'refresh_token' when refreshing a token.</summary>
|
||||
[System.CodeDom.Compiler.GeneratedCode("NJsonSchema", "10.5.2.0 (Newtonsoft.Json v11.0.0.0)")]
|
||||
public enum Grant_type
|
||||
{
|
||||
[System.Runtime.Serialization.EnumMember(Value = @"password")]
|
||||
Password = 0,
|
||||
|
||||
[System.Runtime.Serialization.EnumMember(Value = @"refresh_token")]
|
||||
Refresh_token = 1,
|
||||
|
||||
}
|
||||
|
||||
[System.CodeDom.Compiler.GeneratedCode("NJsonSchema", "10.5.2.0 (Newtonsoft.Json v11.0.0.0)")]
|
||||
public enum TokenResponseToken_type
|
||||
{
|
||||
[System.Runtime.Serialization.EnumMember(Value = @"bearer")]
|
||||
Bearer = 0,
|
||||
|
||||
}
|
||||
|
||||
[System.CodeDom.Compiler.GeneratedCode("NSwag", "13.14.8.0 (NJsonSchema v10.5.2.0 (Newtonsoft.Json v11.0.0.0))")]
|
||||
public partial class ApiException : System.Exception
|
||||
{
|
||||
public int StatusCode { get; private set; }
|
||||
|
||||
public string Response { get; private set; }
|
||||
|
||||
public System.Collections.Generic.IReadOnlyDictionary<string, System.Collections.Generic.IEnumerable<string>> Headers { get; private set; }
|
||||
|
||||
public ApiException(string message, int statusCode, string response, System.Collections.Generic.IReadOnlyDictionary<string, System.Collections.Generic.IEnumerable<string>> headers, System.Exception innerException)
|
||||
: base(message + "\n\nStatus: " + statusCode + "\nResponse: \n" + ((response == null) ? "(null)" : response.Substring(0, response.Length >= 512 ? 512 : response.Length)), innerException)
|
||||
{
|
||||
StatusCode = statusCode;
|
||||
Response = response;
|
||||
Headers = headers;
|
||||
}
|
||||
|
||||
public override string ToString() => string.Format("HTTP Response: \n\n{0}\n\n{1}", Response, base.ToString());
|
||||
}
|
||||
|
||||
[System.CodeDom.Compiler.GeneratedCode("NSwag", "13.14.8.0 (NJsonSchema v10.5.2.0 (Newtonsoft.Json v11.0.0.0))")]
|
||||
public partial class ApiException<TResult> : ApiException
|
||||
{
|
||||
public TResult Result { get; private set; }
|
||||
|
||||
public ApiException(string message, int statusCode, string response, System.Collections.Generic.IReadOnlyDictionary<string, System.Collections.Generic.IEnumerable<string>> headers, TResult result, System.Exception innerException)
|
||||
: base(message, statusCode, response, headers, innerException)
|
||||
{
|
||||
Result = result;
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
#pragma warning restore 1591
|
||||
#pragma warning restore 1573
|
||||
#pragma warning restore 472
|
||||
#pragma warning restore 114
|
||||
#pragma warning restore 108
|
||||
#pragma warning restore 3016
|
||||
@@ -1,335 +0,0 @@
|
||||
using Microsoft.Win32;
|
||||
using Org.BouncyCastle.Crypto;
|
||||
using Org.BouncyCastle.Crypto.Parameters;
|
||||
using Org.BouncyCastle.OpenSsl;
|
||||
using Org.BouncyCastle.Security;
|
||||
using SecretServerAuthentication.DSS;
|
||||
using SecretServerRestClient.DSS;
|
||||
using System.Security.Cryptography;
|
||||
|
||||
namespace ExternalConnectors.DSS;
|
||||
|
||||
public class SecretServerInterface
|
||||
{
|
||||
private static class SSConnectionData
|
||||
{
|
||||
public static string ssUsername = "";
|
||||
public static string ssPassword = "";
|
||||
public static string ssUrl = "";
|
||||
public static string ssOTP = "";
|
||||
public static bool ssSSO = false;
|
||||
public static bool initdone = false;
|
||||
|
||||
//token
|
||||
public static string ssTokenBearer = "";
|
||||
public static DateTime ssTokenExpiresOn = DateTime.UtcNow;
|
||||
public static string ssTokenRefresh = "";
|
||||
|
||||
public static void Init()
|
||||
{
|
||||
if (initdone == true)
|
||||
return;
|
||||
|
||||
RegistryKey key = Registry.CurrentUser.CreateSubKey(@"SOFTWARE\mRemoteSSInterface");
|
||||
try
|
||||
{
|
||||
// display gui and ask for data
|
||||
SSConnectionForm f = new();
|
||||
string? un = key.GetValue("Username") as string;
|
||||
f.tbUsername.Text = un ?? "";
|
||||
f.tbPassword.Text = SSConnectionData.ssPassword; // in OTP refresh cases, this value might already be filled
|
||||
|
||||
string? url = key.GetValue("URL") as string;
|
||||
if (url == null || !url.Contains("://"))
|
||||
url = "https://cred.domain.local/SecretServer";
|
||||
f.tbSSURL.Text = url;
|
||||
|
||||
var b = key.GetValue("SSO");
|
||||
if (b == null || (string)b != "True")
|
||||
ssSSO = false;
|
||||
else
|
||||
ssSSO = true;
|
||||
f.cbUseSSO.Checked = ssSSO;
|
||||
|
||||
// show dialog
|
||||
while (true)
|
||||
{
|
||||
_ = f.ShowDialog();
|
||||
|
||||
if (f.DialogResult != DialogResult.OK)
|
||||
return;
|
||||
|
||||
// store values to memory
|
||||
ssUsername = f.tbUsername.Text;
|
||||
ssPassword = f.tbPassword.Text;
|
||||
ssUrl = f.tbSSURL.Text;
|
||||
ssSSO = f.cbUseSSO.Checked;
|
||||
ssOTP = f.tbOTP.Text;
|
||||
// check connection first
|
||||
try
|
||||
{
|
||||
if (TestCredentials() == true)
|
||||
{
|
||||
initdone = true;
|
||||
break;
|
||||
}
|
||||
}
|
||||
catch (Exception)
|
||||
{
|
||||
MessageBox.Show("Test Credentials failed - please check your credentials");
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
// write values to registry
|
||||
key.SetValue("Username", ssUsername);
|
||||
key.SetValue("URL", ssUrl);
|
||||
key.SetValue("SSO", ssSSO);
|
||||
}
|
||||
catch (Exception)
|
||||
{
|
||||
throw;
|
||||
}
|
||||
finally
|
||||
{
|
||||
key.Close();
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
private static bool TestCredentials()
|
||||
{
|
||||
if (SSConnectionData.ssSSO)
|
||||
{
|
||||
// checking creds doesn't really make sense here, as we can't modify them anyway if something is wrong
|
||||
return true;
|
||||
}
|
||||
else
|
||||
{
|
||||
|
||||
if (!String.IsNullOrEmpty(GetToken()))
|
||||
{
|
||||
return true;
|
||||
}
|
||||
else
|
||||
{
|
||||
return false;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private static SecretsServiceClient ConstructSecretsServiceClient()
|
||||
{
|
||||
string baseURL = SSConnectionData.ssUrl;
|
||||
if (SSConnectionData.ssSSO)
|
||||
{
|
||||
// REQUIRES IIS CONFIG! https://docs.thycotic.com/ss/11.0.0/api-scripting/webservice-iwa-powershell
|
||||
var handler = new HttpClientHandler() { UseDefaultCredentials = true };
|
||||
var httpClient = new HttpClient(handler);
|
||||
{
|
||||
// Call REST API:
|
||||
return new SecretsServiceClient($"{baseURL}/winauthwebservices/api", httpClient);
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
var httpClient = new HttpClient();
|
||||
{
|
||||
|
||||
var token = GetToken();
|
||||
// Set credentials (token):
|
||||
httpClient.DefaultRequestHeaders.Authorization = new System.Net.Http.Headers.AuthenticationHeaderValue("Bearer", token);
|
||||
|
||||
// Call REST API:
|
||||
return new SecretsServiceClient($"{baseURL}/api", httpClient);
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
private static void FetchSecret(int secretID, out string secretUsername, out string secretPassword, out string secretDomain, out string privatekey)
|
||||
{
|
||||
var client = ConstructSecretsServiceClient();
|
||||
SecretModel secret = client.GetSecretAsync(false, true, secretID, null).Result;
|
||||
|
||||
// clear return variables
|
||||
secretDomain = "";
|
||||
secretUsername = "";
|
||||
secretPassword = "";
|
||||
privatekey = "";
|
||||
string privatekeypassphrase = "";
|
||||
|
||||
// parse data and extract what we need
|
||||
foreach (var item in secret.Items)
|
||||
{
|
||||
if (item.FieldName.ToLower().Equals("domain"))
|
||||
secretDomain = item.ItemValue;
|
||||
else if (item.FieldName.ToLower().Equals("username"))
|
||||
secretUsername = item.ItemValue;
|
||||
else if (item.FieldName.ToLower().Equals("password"))
|
||||
secretPassword = item.ItemValue;
|
||||
else if (item.FieldName.ToLower().Equals("private key"))
|
||||
{
|
||||
client.ReadResponseNoJSONConvert = true;
|
||||
privatekey = client.GetFieldAsync(false, false, secretID, "private-key").Result;
|
||||
client.ReadResponseNoJSONConvert = false;
|
||||
}
|
||||
else if (item.FieldName.ToLower().Equals("private key passphrase"))
|
||||
privatekeypassphrase = item.ItemValue;
|
||||
}
|
||||
|
||||
// need to decode the private key?
|
||||
if (!string.IsNullOrEmpty(privatekeypassphrase))
|
||||
{
|
||||
try
|
||||
{
|
||||
var key = DecodePrivateKey(privatekey, privatekeypassphrase);
|
||||
privatekey = key;
|
||||
}
|
||||
catch(Exception)
|
||||
{
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
// conversion to putty format necessary?
|
||||
if (!string.IsNullOrEmpty(privatekey) && !privatekey.StartsWith("PuTTY-User-Key-File-2"))
|
||||
{
|
||||
try
|
||||
{
|
||||
RSACryptoServiceProvider key = ImportPrivateKey(privatekey);
|
||||
privatekey = PuttyKeyFileGenerator.ToPuttyPrivateKey(key);
|
||||
}
|
||||
catch (Exception)
|
||||
{
|
||||
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#region PUTTY KEY HANDLING
|
||||
// decode rsa private key with encryption password
|
||||
private static string DecodePrivateKey(string encryptedPrivateKey, string password)
|
||||
{
|
||||
TextReader textReader = new StringReader(encryptedPrivateKey);
|
||||
PemReader pemReader = new(textReader, new PasswordFinder(password));
|
||||
|
||||
AsymmetricCipherKeyPair keyPair = (AsymmetricCipherKeyPair)pemReader.ReadObject();
|
||||
|
||||
TextWriter textWriter = new StringWriter();
|
||||
var pemWriter = new PemWriter(textWriter);
|
||||
pemWriter.WriteObject(keyPair.Private);
|
||||
pemWriter.Writer.Flush();
|
||||
|
||||
return ""+textWriter.ToString();
|
||||
}
|
||||
private class PasswordFinder(string password) : IPasswordFinder
|
||||
{
|
||||
private string password = password;
|
||||
|
||||
public char[] GetPassword()
|
||||
{
|
||||
return password.ToCharArray();
|
||||
}
|
||||
}
|
||||
|
||||
// read private key pem string to rsacryptoserviceprovider
|
||||
public static RSACryptoServiceProvider ImportPrivateKey(string pem)
|
||||
{
|
||||
PemReader pr = new(new StringReader(pem));
|
||||
AsymmetricCipherKeyPair KeyPair = (AsymmetricCipherKeyPair)pr.ReadObject();
|
||||
RSAParameters rsaParams = DotNetUtilities.ToRSAParameters((RsaPrivateCrtKeyParameters)KeyPair.Private);
|
||||
RSACryptoServiceProvider rsa = new();
|
||||
rsa.ImportParameters(rsaParams);
|
||||
return rsa;
|
||||
}
|
||||
#endregion
|
||||
|
||||
|
||||
#region TOKEN
|
||||
private static string GetToken()
|
||||
{
|
||||
// if there is no token, fetch a fresh one
|
||||
if (String.IsNullOrEmpty(SSConnectionData.ssTokenBearer))
|
||||
{
|
||||
return GetTokenFresh();
|
||||
}
|
||||
// if there is a token, check if it is valid
|
||||
if (SSConnectionData.ssTokenExpiresOn >= DateTime.UtcNow)
|
||||
{
|
||||
return SSConnectionData.ssTokenBearer;
|
||||
}
|
||||
else
|
||||
{
|
||||
// try using refresh token
|
||||
using (var httpClient = new HttpClient())
|
||||
{
|
||||
var tokenClient = new OAuth2ServiceClient(SSConnectionData.ssUrl, httpClient);
|
||||
TokenResponse token = new();
|
||||
try
|
||||
{
|
||||
token = tokenClient.AuthorizeAsync(Grant_type.Refresh_token, null, null, SSConnectionData.ssTokenRefresh, null).Result;
|
||||
var tokenResult = token.Access_token;
|
||||
|
||||
SSConnectionData.ssTokenBearer = tokenResult;
|
||||
SSConnectionData.ssTokenRefresh = token.Refresh_token;
|
||||
SSConnectionData.ssTokenExpiresOn = token.Expires_on;
|
||||
return tokenResult;
|
||||
}
|
||||
catch (Exception)
|
||||
{
|
||||
// refresh token failed. clean memory and start fresh
|
||||
SSConnectionData.ssTokenBearer = "";
|
||||
SSConnectionData.ssTokenRefresh = "";
|
||||
SSConnectionData.ssTokenExpiresOn = DateTime.Now;
|
||||
// if OTP is required we need to ask user for a new OTP
|
||||
if (!String.IsNullOrEmpty(SSConnectionData.ssOTP))
|
||||
{
|
||||
SSConnectionData.initdone = false;
|
||||
// the call below executes a connection test, which fetches a valid token
|
||||
SSConnectionData.Init();
|
||||
// we now have a fresh token in memory. return it to caller
|
||||
return SSConnectionData.ssTokenBearer;
|
||||
}
|
||||
else
|
||||
{
|
||||
// no user interaction required. get a fresh token and return it to caller
|
||||
return GetTokenFresh();
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
static string GetTokenFresh()
|
||||
{
|
||||
using (var httpClient = new HttpClient())
|
||||
{
|
||||
// Authenticate:
|
||||
var tokenClient = new OAuth2ServiceClient(SSConnectionData.ssUrl, httpClient);
|
||||
// call below will throw an exception if the creds are invalid
|
||||
var token = tokenClient.AuthorizeAsync(Grant_type.Password, SSConnectionData.ssUsername, SSConnectionData.ssPassword, null, SSConnectionData.ssOTP).Result;
|
||||
// here we can be sure the creds are ok - return success state
|
||||
var tokenResult = token.Access_token;
|
||||
|
||||
SSConnectionData.ssTokenBearer = tokenResult;
|
||||
SSConnectionData.ssTokenRefresh = token.Refresh_token;
|
||||
SSConnectionData.ssTokenExpiresOn = token.Expires_on;
|
||||
return tokenResult;
|
||||
}
|
||||
}
|
||||
#endregion
|
||||
|
||||
|
||||
// input must be the secret id to fetch
|
||||
public static void FetchSecretFromServer(string input, out string username, out string password, out string domain, out string privatekey)
|
||||
{
|
||||
// get secret id
|
||||
int secretID = Int32.Parse(input);
|
||||
|
||||
// init connection credentials, display popup if necessary
|
||||
SSConnectionData.Init();
|
||||
|
||||
// get the secret
|
||||
FetchSecret(secretID, out username, out password, out domain, out privatekey);
|
||||
}
|
||||
}
|
||||
@@ -1,29 +0,0 @@
|
||||
<Project Sdk="Microsoft.NET.Sdk">
|
||||
<PropertyGroup>
|
||||
<TargetFramework>net9.0-windows10.0.26100.0</TargetFramework>
|
||||
<ImplicitUsings>enable</ImplicitUsings>
|
||||
<Nullable>enable</Nullable>
|
||||
<OutputType>Library</OutputType>
|
||||
<UseWindowsForms>True</UseWindowsForms>
|
||||
<Platforms>x64;arm64</Platforms>
|
||||
<Configurations>Debug;Release;Debug Portable;Release Portable;Deploy to github</Configurations>
|
||||
<SupportedOSPlatformVersion>10.0.26100.0</SupportedOSPlatformVersion>
|
||||
</PropertyGroup>
|
||||
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Release Portable|x64'">
|
||||
<Optimize>True</Optimize>
|
||||
</PropertyGroup>
|
||||
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Release Portable|arm64'">
|
||||
<Optimize>True</Optimize>
|
||||
</PropertyGroup>
|
||||
<ItemGroup>
|
||||
<PackageReference Include="AWSSDK.Core" />
|
||||
<PackageReference Include="AWSSDK.EC2" />
|
||||
<PackageReference Include="BouncyCastle.Cryptography" />
|
||||
<PackageReference Include="Newtonsoft.Json" />
|
||||
</ItemGroup>
|
||||
<ItemGroup>
|
||||
<Compile Update="AWS\AWSConnectionForm.cs" />
|
||||
<Compile Update="CPS\CPSConnectionForm.cs" />
|
||||
<Compile Update="DSS\SSConnectionForm.cs" />
|
||||
</ItemGroup>
|
||||
</Project>
|
||||
@@ -1,111 +0,0 @@
|
||||
using System.Security.Cryptography;
|
||||
using System.Text;
|
||||
using System.Text.RegularExpressions;
|
||||
|
||||
namespace ExternalConnectors;
|
||||
|
||||
public class PuttyKeyFileGenerator
|
||||
{
|
||||
private const int prefixSize = 4;
|
||||
private const int paddedPrefixSize = prefixSize + 1;
|
||||
private const int lineLength = 64;
|
||||
private const string keyType = "ssh-rsa";
|
||||
private const string encryptionType = "none";
|
||||
|
||||
public static string ToPuttyPrivateKey(RSACryptoServiceProvider cryptoServiceProvider, string Comment = "imported-openssh-key")
|
||||
{
|
||||
var publicParameters = cryptoServiceProvider.ExportParameters(false);
|
||||
byte[] publicBuffer = new byte[3 + keyType.Length + GetPrefixSize(publicParameters.Exponent) + publicParameters.Exponent!.Length + GetPrefixSize(publicParameters.Modulus) + publicParameters.Modulus!.Length + 1];
|
||||
|
||||
using (var bw = new BinaryWriter(new MemoryStream(publicBuffer)))
|
||||
{
|
||||
bw.Write(new byte[] { 0x00, 0x00, 0x00 });
|
||||
bw.Write(Encoding.ASCII.GetBytes(keyType));
|
||||
PutPrefixed(bw, publicParameters.Exponent, CheckIsNeddPadding(publicParameters.Exponent));
|
||||
PutPrefixed(bw, publicParameters.Modulus, CheckIsNeddPadding(publicParameters.Modulus));
|
||||
}
|
||||
var publicBlob = System.Convert.ToBase64String(publicBuffer);
|
||||
|
||||
var privateParameters = cryptoServiceProvider.ExportParameters(true);
|
||||
|
||||
byte[] privateBuffer = new byte[paddedPrefixSize + privateParameters.D!.Length + paddedPrefixSize + privateParameters.P!.Length + paddedPrefixSize + privateParameters.Q!.Length + paddedPrefixSize + privateParameters.InverseQ!.Length];
|
||||
|
||||
using (var bw = new BinaryWriter(new MemoryStream(privateBuffer)))
|
||||
{
|
||||
PutPrefixed(bw, privateParameters.D, true);
|
||||
PutPrefixed(bw, privateParameters.P, true);
|
||||
PutPrefixed(bw, privateParameters.Q, true);
|
||||
PutPrefixed(bw, privateParameters.InverseQ, true);
|
||||
}
|
||||
var privateBlob = System.Convert.ToBase64String(privateBuffer);
|
||||
|
||||
HMACSHA1 hmacSha1 = new(SHA1.Create().ComputeHash(Encoding.ASCII.GetBytes("putty-private-key-file-mac-key")));
|
||||
byte[] bytesToHash = new byte[prefixSize + keyType.Length + prefixSize + encryptionType.Length + prefixSize + Comment.Length + prefixSize + publicBuffer.Length + prefixSize + privateBuffer.Length];
|
||||
|
||||
using (var bw = new BinaryWriter(new MemoryStream(bytesToHash)))
|
||||
{
|
||||
PutPrefixed(bw, Encoding.ASCII.GetBytes(keyType));
|
||||
PutPrefixed(bw, Encoding.ASCII.GetBytes(encryptionType));
|
||||
PutPrefixed(bw, Encoding.ASCII.GetBytes(Comment));
|
||||
PutPrefixed(bw, publicBuffer);
|
||||
PutPrefixed(bw, privateBuffer);
|
||||
}
|
||||
|
||||
var hash = string.Join("", hmacSha1.ComputeHash(bytesToHash).Select(x => $"{x:x2}"));
|
||||
|
||||
var sb = new StringBuilder();
|
||||
sb.AppendLine("PuTTY-User-Key-File-2: " + keyType);
|
||||
sb.AppendLine("Encryption: " + encryptionType);
|
||||
sb.AppendLine("Comment: " + Comment);
|
||||
|
||||
var publicLines = SpliceText(publicBlob, lineLength);
|
||||
sb.AppendLine("Public-Lines: " + publicLines.Length);
|
||||
foreach (var line in publicLines)
|
||||
{
|
||||
sb.AppendLine(line);
|
||||
}
|
||||
|
||||
var privateLines = SpliceText(privateBlob, lineLength);
|
||||
sb.AppendLine("Private-Lines: " + privateLines.Length);
|
||||
foreach (var line in privateLines)
|
||||
{
|
||||
sb.AppendLine(line);
|
||||
}
|
||||
|
||||
sb.AppendLine("Private-MAC: " + hash);
|
||||
|
||||
return sb.ToString();
|
||||
}
|
||||
|
||||
private static void PutPrefixed(BinaryWriter bw, byte[] bytes, bool addLeadingNull = false)
|
||||
{
|
||||
bw.Write(BitConverter.GetBytes(bytes.Length + (addLeadingNull ? 1 : 0)).Reverse().ToArray());
|
||||
if (addLeadingNull)
|
||||
bw.Write(new byte[] { 0x00 });
|
||||
bw.Write(bytes);
|
||||
}
|
||||
|
||||
private static string[] SpliceText(string text, int lineLength)
|
||||
{
|
||||
return Regex.Matches(text, ".{1," + lineLength + "}").Cast<Match>().Select(m => m.Value).ToArray();
|
||||
}
|
||||
|
||||
private static int GetPrefixSize(byte[]? bytes)
|
||||
{
|
||||
if (bytes is null)
|
||||
return 0;
|
||||
|
||||
return CheckIsNeddPadding(bytes) ? paddedPrefixSize : prefixSize;
|
||||
}
|
||||
|
||||
private static bool CheckIsNeddPadding(byte[] bytes)
|
||||
{
|
||||
if (bytes is null || bytes.Length == 0)
|
||||
return false;
|
||||
|
||||
// 128 == 10000000
|
||||
// This means that the number of bits can be divided by 8.
|
||||
// According to the algorithm in putty, you need to add a padding.
|
||||
return bytes[0] >= 128;
|
||||
}
|
||||
}
|
||||
56
InstallerProjects/CustomActions/CustomActions.csproj
Normal file
@@ -0,0 +1,56 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<Project ToolsVersion="4.0" DefaultTargets="Build" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
|
||||
<PropertyGroup>
|
||||
<Configuration Condition=" '$(Configuration)' == '' ">Debug</Configuration>
|
||||
<Platform Condition=" '$(Platform)' == '' ">x86</Platform>
|
||||
<ProductVersion>8.0.30703</ProductVersion>
|
||||
<SchemaVersion>2.0</SchemaVersion>
|
||||
<ProjectGuid>{5423D985-CB48-4344-B47F-E8C6D60C8B04}</ProjectGuid>
|
||||
<OutputType>Library</OutputType>
|
||||
<AppDesignerFolder>Properties</AppDesignerFolder>
|
||||
<RootNamespace>CustomActions</RootNamespace>
|
||||
<AssemblyName>CustomActions</AssemblyName>
|
||||
<TargetFrameworkVersion>v2.0</TargetFrameworkVersion>
|
||||
<FileAlignment>512</FileAlignment>
|
||||
<WixCATargetsPath Condition=" '$(WixCATargetsPath)' == '' ">$(MSBuildExtensionsPath)\Microsoft\WiX\v3.x\Wix.CA.targets</WixCATargetsPath>
|
||||
<TargetFrameworkProfile />
|
||||
</PropertyGroup>
|
||||
<PropertyGroup Condition=" '$(Configuration)|$(Platform)' == 'Debug|x86' ">
|
||||
<DebugSymbols>true</DebugSymbols>
|
||||
<DebugType>full</DebugType>
|
||||
<Optimize>false</Optimize>
|
||||
<OutputPath>bin\Debug\</OutputPath>
|
||||
<DefineConstants>DEBUG;TRACE</DefineConstants>
|
||||
<ErrorReport>prompt</ErrorReport>
|
||||
<WarningLevel>4</WarningLevel>
|
||||
</PropertyGroup>
|
||||
<PropertyGroup Condition=" '$(Configuration)|$(Platform)' == 'Release|x86' ">
|
||||
<DebugType>pdbonly</DebugType>
|
||||
<Optimize>true</Optimize>
|
||||
<OutputPath>bin\Release\</OutputPath>
|
||||
<DefineConstants>TRACE</DefineConstants>
|
||||
<ErrorReport>prompt</ErrorReport>
|
||||
<WarningLevel>4</WarningLevel>
|
||||
</PropertyGroup>
|
||||
<PropertyGroup>
|
||||
<StartupObject>
|
||||
</StartupObject>
|
||||
</PropertyGroup>
|
||||
<ItemGroup>
|
||||
<Reference Include="System" />
|
||||
<Reference Include="System.Management" />
|
||||
<Reference Include="Microsoft.Deployment.WindowsInstaller">
|
||||
<Private>True</Private>
|
||||
</Reference>
|
||||
</ItemGroup>
|
||||
<ItemGroup>
|
||||
<Compile Include="CustomActions.cs" />
|
||||
<Compile Include="KbInstalledChecker.cs" />
|
||||
<Compile Include="Properties\AssemblyInfo.cs" />
|
||||
<Compile Include="InstalledWindowsUpdateChecker.cs" />
|
||||
<Compile Include="UninstallNsisVersions.cs" />
|
||||
<Content Include="CustomAction.config" />
|
||||
</ItemGroup>
|
||||
<Import Project="$(MSBuildToolsPath)\Microsoft.CSharp.targets" />
|
||||
<Import Project="$(WixCATargetsPath)" />
|
||||
</Project>
|
||||
@@ -1,4 +1,5 @@
|
||||
using System.Reflection;
|
||||
using System.Runtime.CompilerServices;
|
||||
using System.Runtime.InteropServices;
|
||||
|
||||
// General Information about an assembly is controlled through the following
|
||||
@@ -14,7 +14,7 @@ xmlns:wix="http://schemas.microsoft.com/wix/2006/wi">
|
||||
<xsl:key name="service-search" match="wix:Component[contains(wix:File/@Source, 'manifest')]" use="@Id" />
|
||||
<xsl:key name="service-search" match="wix:Component[contains(wix:File/@Source, '.application')]" use="@Id" />
|
||||
<xsl:key name="service-search" match="wix:Component[wix:File/@Source = '$(var.HarvestPath)\mRemoteNG.exe']" use="@Id" />
|
||||
<xsl:key name="service-search" match="wix:Component[wix:File/@Source = '$(var.HarvestPath)\PuttyNG.exe']" use="@Id" />
|
||||
<xsl:key name="service-search" match="wix:Component[wix:File/@Source = '$(var.HarvestPath)\PuTTYNG.exe']" use="@Id" />
|
||||
<xsl:template match="wix:Component[key('service-search', @Id)]" />
|
||||
<xsl:template match="wix:ComponentRef[key('service-search', @Id)]" />
|
||||
</xsl:stylesheet>
|
||||
@@ -0,0 +1,9 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<Wix xmlns="http://schemas.microsoft.com/wix/2006/wi">
|
||||
<?include $(sys.CURRENTDIR)Includes\Config.wxi?>
|
||||
<Fragment>
|
||||
<Component Id="C.MainExe" Directory="INSTALLDIR" Guid="*">
|
||||
<File Id="MainExeFile" Source="$(var.SolutionDir)mRemoteV1\bin\$(var.Configuration)\mRemoteNG.exe" />
|
||||
</Component>
|
||||
</Fragment>
|
||||
</Wix>
|
||||
@@ -0,0 +1,22 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<Wix xmlns="http://schemas.microsoft.com/wix/2006/wi">
|
||||
<Fragment>
|
||||
<ComponentGroup Id="CG.ProjectInfoFiles" Directory="INSTALLDIR">
|
||||
<Component Id="C.Changelog" Guid="*">
|
||||
<File Id="ChangelogFile" Name="Changelog.txt" Source="$(var.SolutionDir)CHANGELOG.md" KeyPath="yes" />
|
||||
</Component>
|
||||
<Component Id="C.Credits" Guid="*">
|
||||
<File Id="CreditsFile" Name="Credits.txt" Source="$(var.SolutionDir)CREDITS.md" KeyPath="yes" />
|
||||
</Component>
|
||||
<Component Id="C.License" Guid="*">
|
||||
<File Id="LicenseFile" Name="License.txt" Source="$(var.SolutionDir)COPYING.TXT" KeyPath="yes" />
|
||||
</Component>
|
||||
<Component Id="C.Readme" Guid="*">
|
||||
<File Id="ReadmeFile" Name="Readme.txt" Source="$(var.SolutionDir)README.md" KeyPath="yes" />
|
||||
</Component>
|
||||
<Component Id="C.TileManifest" Guid="*">
|
||||
<File Id="TileManifest" Name="mRemoteNG.VisualElementsManifest.xml" Source="$(var.SolutionDir)mRemoteV1\bin\Release\mRemoteNG.VisualElementsManifest.xml" KeyPath="yes" />
|
||||
</Component>
|
||||
</ComponentGroup>
|
||||
</Fragment>
|
||||
</Wix>
|
||||
10
InstallerProjects/Installer/Fragments/PuTTYNGFragment.wxs
Normal file
@@ -0,0 +1,10 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<Wix xmlns="http://schemas.microsoft.com/wix/2006/wi">
|
||||
<Fragment>
|
||||
<DirectoryRef Id="INSTALLDIR">
|
||||
<Component Id="C.PuttyNGFile" Guid="*">
|
||||
<File Id="PuttyNGFile" Name="PuTTYNG.exe" Source="$(var.HarvestPath)\PuTTYNG.exe" KeyPath="yes" />
|
||||
</Component>
|
||||
</DirectoryRef>
|
||||
</Fragment>
|
||||
</Wix>
|
||||
@@ -2,7 +2,7 @@
|
||||
<Wix xmlns="http://schemas.microsoft.com/wix/2006/wi">
|
||||
<Fragment>
|
||||
<DirectoryRef Id="TARGETDIR">
|
||||
<Component Id="C.RegistryEntries" Guid="888643e4-4c52-45d3-801a-0d0d71c737fe">
|
||||
<Component Id="C.RegistryEntries" Guid="*">
|
||||
<RegistryKey Root="HKLM" Key="Software\mRemoteNG">
|
||||
<RegistryValue Type="string" Name="InstallDir" Value="[INSTALLDIR]" KeyPath="yes" />
|
||||
</RegistryKey>
|
||||
@@ -1,7 +1,7 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<Include>
|
||||
<?define ProductName = "mRemoteNG" ?>
|
||||
<?define Company = "Profi-Kom Ltd." ?>
|
||||
<?define Company = "Next Generation Software" ?>
|
||||
<?define UpgradeCode = "dd678a54-ca75-4791-8dfe-d818095684f8" ?>
|
||||
<?define ProductCode = "*" ?>
|
||||
<?define ProductVersion = "!(bind.FileVersion.MainExeFile)" ?>
|
||||
@@ -26,5 +26,9 @@
|
||||
<?define ProductNameWithPlatform = "$(var.ProductName) (64 bit)" ?>
|
||||
<?define Win64 = "yes" ?>
|
||||
<?define PlatformProgramFilesFolder = "ProgramFiles64Folder" ?>
|
||||
<?else ?>
|
||||
<?define ProductNameWithPlatform = "$(var.ProductName)" ?>
|
||||
<?define Win64 = "no" ?>
|
||||
<?define PlatformProgramFilesFolder = "ProgramFilesFolder" ?>
|
||||
<?endif ?>
|
||||
</Include>
|
||||
138
InstallerProjects/Installer/Installer.wixproj
Normal file
@@ -0,0 +1,138 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<Project ToolsVersion="4.0" DefaultTargets="Build" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
|
||||
<PropertyGroup>
|
||||
<Configuration Condition=" '$(Configuration)' == '' ">Debug</Configuration>
|
||||
<Platform Condition=" '$(Platform)' == '' ">x86</Platform>
|
||||
<ProductVersion>3.10</ProductVersion>
|
||||
<ProjectGuid>f0168b9f-6815-40df-ba53-46cee7683b68</ProjectGuid>
|
||||
<SchemaVersion>2.0</SchemaVersion>
|
||||
<OutputName>mRemoteNG-Installer</OutputName>
|
||||
<OutputType>Package</OutputType>
|
||||
<WixTargetsPath Condition=" '$(WixTargetsPath)' == '' AND '$(MSBuildExtensionsPath32)' != '' ">$(MSBuildExtensionsPath32)\Microsoft\WiX\v3.x\Wix.targets</WixTargetsPath>
|
||||
<WixTargetsPath Condition=" '$(WixTargetsPath)' == '' ">$(MSBuildExtensionsPath)\Microsoft\WiX\v3.x\Wix.targets</WixTargetsPath>
|
||||
<OutputPath>bin\$(Configuration)\</OutputPath>
|
||||
<IntermediateOutputPath>obj\$(Configuration)\</IntermediateOutputPath>
|
||||
</PropertyGroup>
|
||||
<PropertyGroup Condition=" '$(Configuration)' == 'Debug' ">
|
||||
<DefineConstants>HarvestPath=..\mRemoteV1\bin\Debug;</DefineConstants>
|
||||
</PropertyGroup>
|
||||
<PropertyGroup Condition=" '$(Configuration)' == 'Release' ">
|
||||
<DefineConstants>HarvestPath=..\mRemoteV1\bin\Release;</DefineConstants>
|
||||
</PropertyGroup>
|
||||
<PropertyGroup Condition=" '$(Configuration)' == 'Debug Portable' ">
|
||||
<DefineConstants>HarvestPath=..\mRemoteV1\bin\Debug Portable;</DefineConstants>
|
||||
</PropertyGroup>
|
||||
<PropertyGroup Condition=" '$(Configuration)' == 'Release Portable' ">
|
||||
<DefineConstants>HarvestPath=..\mRemoteV1\bin\Release Portable;</DefineConstants>
|
||||
</PropertyGroup>
|
||||
<ItemGroup>
|
||||
<Compile Include="CustomActions\CheckForInstalledWindowsUpdates.wxs" />
|
||||
<Compile Include="CustomActions\SaveInstallLocation.wxs" />
|
||||
<Compile Include="CustomActions\UninstallLegacyVersions.wxs" />
|
||||
<Compile Include="Fragments\FilesFragment.wxs" />
|
||||
<Compile Include="Fragments\DirectoriesFragment.wxs" />
|
||||
<Compile Include="Fragments\MainExeFragment.wxs" />
|
||||
<Compile Include="Fragments\MiscTextFilesFragment.wxs" />
|
||||
<Compile Include="Fragments\PuTTYNGFragment.wxs" />
|
||||
<Compile Include="Fragments\RegistryEntriesFragment.wxs" />
|
||||
<Compile Include="Fragments\ShortcutFragment.wxs" />
|
||||
<Compile Include="mRemoteNGV1.wxs" />
|
||||
</ItemGroup>
|
||||
<ItemGroup>
|
||||
<Content Include="Filters\Harvest_Filter.xslt" />
|
||||
<Content Include="Includes\Config.wxi" />
|
||||
<Content Include="Resources\AppIcon.ico" />
|
||||
<Content Include="Resources\Installer_Header.png" />
|
||||
<Content Include="Resources\Installer_Side.png" />
|
||||
</ItemGroup>
|
||||
<ItemGroup>
|
||||
<Folder Include="CustomActions" />
|
||||
<Folder Include="CustomDialogs" />
|
||||
<Folder Include="Fragments" />
|
||||
<Folder Include="Includes" />
|
||||
<Folder Include="Localizations" />
|
||||
<Folder Include="Filters" />
|
||||
<Folder Include="bin" />
|
||||
<Folder Include="Resources" />
|
||||
</ItemGroup>
|
||||
<ItemGroup>
|
||||
<EmbeddedResource Include="Localizations\en-US.wxl" />
|
||||
</ItemGroup>
|
||||
<ItemGroup>
|
||||
<WixExtension Include="WixUtilExtension">
|
||||
<HintPath>$(WixExtDir)\WixUtilExtension.dll</HintPath>
|
||||
<Name>WixUtilExtension</Name>
|
||||
</WixExtension>
|
||||
<WixExtension Include="WixUIExtension">
|
||||
<HintPath>$(WixExtDir)\WixUIExtension.dll</HintPath>
|
||||
<Name>WixUIExtension</Name>
|
||||
</WixExtension>
|
||||
<WixExtension Include="WixNetFxExtension">
|
||||
<HintPath>$(WixExtDir)\WixNetFxExtension.dll</HintPath>
|
||||
<Name>WixNetFxExtension</Name>
|
||||
</WixExtension>
|
||||
</ItemGroup>
|
||||
<ItemGroup>
|
||||
<ProjectReference Include="..\..\mRemoteV1\mRemoteV1.csproj">
|
||||
<Name>mRemoteV1</Name>
|
||||
<Project>{4934a491-40bc-4e5b-9166-ea1169a220f6}</Project>
|
||||
<Private>True</Private>
|
||||
<DoNotHarvest>
|
||||
</DoNotHarvest>
|
||||
<RefProjectOutputGroups>Binaries;Content;Satellites</RefProjectOutputGroups>
|
||||
<RefTargetDir>APPLICATIONROOTDIRECTORY</RefTargetDir>
|
||||
</ProjectReference>
|
||||
</ItemGroup>
|
||||
<Import Project="$(WixTargetsPath)" />
|
||||
<!--
|
||||
To modify your build process, add your task inside one of the targets below and uncomment it.
|
||||
Other similar extension points exist, see Wix.targets.
|
||||
-->
|
||||
<Target Name="BeforeBuild">
|
||||
</Target>
|
||||
<Target Name="AfterBuild">
|
||||
</Target>
|
||||
<PropertyGroup Condition=" '$(Configuration)|$(Platform)' == 'Release|x86' ">
|
||||
<LinkerAdditionalOptions>
|
||||
</LinkerAdditionalOptions>
|
||||
<DefineConstants>HarvestPath=$(SolutionDir)mRemoteV1\bin\Release;HelpFilesHarvestPath=$(SolutionDir)mRemoteV1\Resources\Help</DefineConstants>
|
||||
<Cultures>en-US</Cultures>
|
||||
<SuppressPdbOutput>True</SuppressPdbOutput>
|
||||
</PropertyGroup>
|
||||
<PropertyGroup Condition=" '$(Configuration)|$(Platform)' == 'Debug|x86' ">
|
||||
<Cultures>en-US</Cultures>
|
||||
<DefineConstants>HarvestPath=$(SolutionDir)mRemoteV1\bin\Debug;HelpFilesHarvestPath=$(SolutionDir)mRemoteV1\Resources\Help</DefineConstants>
|
||||
</PropertyGroup>
|
||||
<PropertyGroup Condition=" '$(Configuration)|$(Platform)' == 'Debug Portable|x86' ">
|
||||
<DefineConstants>HarvestPath=$(SolutionDir)mRemoteV1\bin\Debug Portable;HelpFilesHarvestPath=$(SolutionDir)mRemoteV1\Resources\Help</DefineConstants>
|
||||
</PropertyGroup>
|
||||
<PropertyGroup Condition=" '$(Configuration)|$(Platform)' == 'Release Portable|x86' ">
|
||||
<DefineConstants>HarvestPath=$(SolutionDir)mRemoteV1\bin\Release Portable;HelpFilesHarvestPath=$(SolutionDir)mRemoteV1\Resources\Help</DefineConstants>
|
||||
</PropertyGroup>
|
||||
<PropertyGroup>
|
||||
<PreBuildEvent>REM Clean the TargetDir
|
||||
rmdir /S /Q "$(TargetDir)"
|
||||
|
||||
echo $(ConfigurationName) > buildenv.tmp
|
||||
|
||||
REM Harvest bin directory of the mRemoteV1 project
|
||||
"$(WIX)bin\heat.exe" dir "$(SolutionDir)mRemoteV1\bin\$(Configuration)" -ag -nologo -dr INSTALLDIR -var var.HarvestPath -srd -cg MandatoryComponents -template fragment -out "$(ProjectDir)Fragments\FilesFragment.wxs" -t "$(ProjectDir)Filters\Harvest_Filter.xslt"
|
||||
|
||||
REM Convert the license file "COPYING.TXT" to "License.rtf" to be shown in the installer GUI
|
||||
"$(ProjectDir)Resources\Pandoc\pandoc.exe" -s -t rtf -o "$(ProjectDir)\Resources\License.rtf" "$(SolutionDir)COPYING.TXT"</PreBuildEvent>
|
||||
</PropertyGroup>
|
||||
<PropertyGroup>
|
||||
<PostBuildEvent>:: When passing paths to powershell scripts, check if the path ends with a backslash "\"
|
||||
:: If it does, then the backslash may be interpreted as an escape character. Add another backslash to cancel the first one.
|
||||
|
||||
powershell -noprofile -command "sleep 2"
|
||||
|
||||
set /p buildenv=<buildenv.tmp
|
||||
|
||||
:: Manual builds, set the cert password and uncomment below.
|
||||
:: IF "%25APPVEYOR_BUILD_FOLDER"=="" ( set cert_pwd= )
|
||||
|
||||
:: Call the post build powershell script
|
||||
powershell.exe -ExecutionPolicy Bypass -File "$(SolutionDir)Tools\postbuild_installer.ps1" -SolutionDir "$(SolutionDir)\" -TargetDir "%25cd%25" -TargetFileName "mRemoteNG.exe" -ConfigurationName "%25buildenv%25" -CertificatePath "$(CertPath)" -CertificatePassword "$(CertPassword)" -ExcludeFromSigning "PuTTYNG.exe"</PostBuildEvent>
|
||||
</PropertyGroup>
|
||||
</Project>
|
||||
@@ -19,7 +19,7 @@
|
||||
<!-- Features and install options -->
|
||||
<String Id="Feature_Complete">Complete</String>
|
||||
<String Id="Feature_DesktopShortcut">Desktop Shortcut</String>
|
||||
<String Id="Feature_StartMenuShortcut">Start Menu Shortcut</String>
|
||||
<String Id="Feature_StartMenuShortcut">Start menu shortcut</String>
|
||||
|
||||
<!-- GUI Page Text -->
|
||||
<String Id="FinishPage_LaunchMremoteNow" Overridable="yes">Launch mRemoteNG now</String>
|
||||
|
Before Width: | Height: | Size: 58 KiB After Width: | Height: | Size: 58 KiB |
|
Before Width: | Height: | Size: 2.3 KiB After Width: | Height: | Size: 2.3 KiB |
|
Before Width: | Height: | Size: 2.8 KiB After Width: | Height: | Size: 2.8 KiB |
@@ -47,5 +47,36 @@
|
||||
{\pard \ql \f0 \sa180 \li360 \fi0 \f1 NO WARRANTY\par}
|
||||
{\pard \ql \f0 \sa180 \li360 \fi-360 11.\tx360\tab BECAUSE THE PROGRAM IS LICENSED FREE OF CHARGE, THERE IS NO WARRANTY FOR THE PROGRAM, TO THE EXTENT PERMITTED BY APPLICABLE LAW. EXCEPT WHEN OTHERWISE STATED IN WRITING THE COPYRIGHT HOLDERS AND/OR OTHER PARTIES PROVIDE THE PROGRAM "AS IS" WITHOUT WARRANTY OF ANY KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. THE ENTIRE RISK AS TO THE QUALITY AND PERFORMANCE OF THE PROGRAM IS WITH YOU. SHOULD THE PROGRAM PROVE DEFECTIVE, YOU ASSUME THE COST OF ALL NECESSARY SERVICING, REPAIR OR CORRECTION.\par}
|
||||
{\pard \ql \f0 \sa180 \li360 \fi-360 12.\tx360\tab IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN WRITING WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MAY MODIFY AND/OR REDISTRIBUTE THE PROGRAM AS PERMITTED ABOVE, BE LIABLE TO YOU FOR DAMAGES, INCLUDING ANY GENERAL, SPECIAL, INCIDENTAL OR CONSEQUENTIAL DAMAGES ARISING OUT OF THE USE OR INABILITY TO USE THE PROGRAM (INCLUDING BUT NOT LIMITED TO LOSS OF DATA OR DATA BEING RENDERED INACCURATE OR LOSSES SUSTAINED BY YOU OR THIRD PARTIES OR A FAILURE OF THE PROGRAM TO OPERATE WITH ANY OTHER PROGRAMS), EVEN IF SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE POSSIBILITY OF SUCH DAMAGES.\par}
|
||||
{\pard \ql \f0 \sa180 \li360 \fi0 \f1 END OF TERMS AND CONDITIONS\sa180\par}
|
||||
{\pard \ql \f0 \sa180 \li360 \fi0 \f1 END OF TERMS AND CONDITIONS\line
|
||||
\line
|
||||
How to Apply These Terms to Your New Programs\sa180\par}
|
||||
{\pard \ql \f0 \sa180 \li0 \fi0 If you develop a new program, and you want it to be of the greatest possible use to the public, the best way to achieve this is to make it free software which everyone can redistribute and change under these terms.\par}
|
||||
{\pard \ql \f0 \sa180 \li0 \fi0 To do so, attach the following notices to the program. It is safest to attach them to the start of each source file to most effectively convey the exclusion of warranty; and each file should have at least the "copyright" line and a pointer to where the full notice is found.\par}
|
||||
{\pard \ql \f0 \sa180 \li0 \fi0 \f1 <one line to give the program's name and a brief idea of what it does.>\line
|
||||
Copyright (C) <year> <name of author>\line
|
||||
\line
|
||||
This program is free software; you can redistribute it and/or modify\line
|
||||
it under the terms of the GNU General Public License as published by\line
|
||||
the Free Software Foundation; either version 2 of the License, or\line
|
||||
(at your option) any later version.\line
|
||||
\line
|
||||
This program is distributed in the hope that it will be useful,\line
|
||||
but WITHOUT ANY WARRANTY; without even the implied warranty of\line
|
||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the\line
|
||||
GNU General Public License for more details.\line
|
||||
\line
|
||||
You should have received a copy of the GNU General Public License along\line
|
||||
with this program; if not, write to the Free Software Foundation, Inc.,\line
|
||||
51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.\par}
|
||||
{\pard \ql \f0 \sa180 \li0 \fi0 Also add information on how to contact you by electronic and paper mail.\par}
|
||||
{\pard \ql \f0 \sa180 \li0 \fi0 If the program is interactive, make it output a short notice like this when it starts in an interactive mode:\par}
|
||||
{\pard \ql \f0 \sa180 \li0 \fi0 \f1 Gnomovision version 69, Copyright (C) year name of author\line
|
||||
Gnomovision comes with ABSOLUTELY NO WARRANTY; for details type `show w'.\line
|
||||
This is free software, and you are welcome to redistribute it\line
|
||||
under certain conditions; type `show c' for details.\par}
|
||||
{\pard \ql \f0 \sa180 \li0 \fi0 The hypothetical commands {\f1 show w' and}show c' should show the appropriate parts of the General Public License. Of course, the commands you use may be called something other than {\f1 show w' and}show c'; they could even be mouse-clicks or menu items--whatever suits your program.\par}
|
||||
{\pard \ql \f0 \sa180 \li0 \fi0 You should also get your employer (if you work as a programmer) or your school, if any, to sign a "copyright disclaimer" for the program, if necessary. Here is a sample; alter the names:\par}
|
||||
{\pard \ql \f0 \sa180 \li0 \fi0 Yoyodyne, Inc., hereby disclaims all copyright interest in the program `Gnomovision' (which makes passes at compilers) written by James Hacker.\par}
|
||||
{\pard \ql \f0 \sa180 \li0 \fi0 , 1 April 1989 Ty Coon, President of Vice\par}
|
||||
{\pard \ql \f0 \sa180 \li0 \fi0 This General Public License does not permit incorporating your program into proprietary programs. If your program is a subroutine library, you may consider it more useful to permit linking proprietary applications with the library. If this is what you want to do, use the GNU Lesser General Public License instead of this License.\par}
|
||||
}
|
||||
85
InstallerProjects/Installer/Resources/Pandoc/COPYING.rtf
Normal file
@@ -0,0 +1,85 @@
|
||||
{\rtf1\ansi\deff0{\fonttbl{\f0 \fswiss Helvetica;}{\f1 Courier;}}
|
||||
{\colortbl;\red255\green0\blue0;\red0\green0\blue255;}
|
||||
\widowctrl\hyphauto
|
||||
|
||||
{\pard \ql \f0 \sa180 \li0 \fi0 \b \fs36 GNU GENERAL PUBLIC LICENSE\par}
|
||||
{\pard \ql \f0 \sa180 \li0 \fi0 Version 2, June 1991\par}
|
||||
{\pard \ql \f0 \sa180 \li0 \fi0 Copyright (C) 1989, 1991 Free Software Foundation, Inc.\line 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA\par}
|
||||
{\pard \ql \f0 \sa180 \li0 \fi0 Everyone is permitted to copy and distribute verbatim copies of this license document, but changing it is not allowed.\par}
|
||||
{\pard \ql \f0 \sa180 \li0 \fi0 \b \fs32 Preamble\par}
|
||||
{\pard \ql \f0 \sa180 \li0 \fi0 The licenses for most software are designed to take away your freedom to share and change it. By contrast, the GNU General Public License is intended to guarantee your freedom to share and change free software\u8211-to make sure the software is free for all its users. This General Public License applies to most of the Free Software Foundation\u8217's software and to any other program whose authors commit to using it. (Some other Free Software Foundation software is covered by the GNU Library General Public License instead.) You can apply it to your programs, too.\par}
|
||||
{\pard \ql \f0 \sa180 \li0 \fi0 When we speak of free software, we are referring to freedom, not price. Our General Public Licenses are designed to make sure that you have the freedom to distribute copies of free software (and charge for this service if you wish), that you receive source code or can get it if you want it, that you can change the software or use pieces of it in new free programs; and that you know you can do these things.\par}
|
||||
{\pard \ql \f0 \sa180 \li0 \fi0 To protect your rights, we need to make restrictions that forbid anyone to deny you these rights or to ask you to surrender the rights. These restrictions translate to certain responsibilities for you if you distribute copies of the software, or if you modify it.\par}
|
||||
{\pard \ql \f0 \sa180 \li0 \fi0 For example, if you distribute copies of such a program, whether gratis or for a fee, you must give the recipients all the rights that you have. You must make sure that they, too, receive or can get the source code. And you must show them these terms so they know their rights.\par}
|
||||
{\pard \ql \f0 \sa180 \li0 \fi0 We protect your rights with two steps: (1) copyright the software, and (2) offer you this license which gives you legal permission to copy, distribute and/or modify the software.\par}
|
||||
{\pard \ql \f0 \sa180 \li0 \fi0 Also, for each author\u8217's protection and ours, we want to make certain that everyone understands that there is no warranty for this free software. If the software is modified by someone else and passed on, we want its recipients to know that what they have is not the original, so that any problems introduced by others will not reflect on the original authors\u8217' reputations.\par}
|
||||
{\pard \ql \f0 \sa180 \li0 \fi0 Finally, any free program is threatened constantly by software patents. We wish to avoid the danger that redistributors of a free program will individually obtain patent licenses, in effect making the program proprietary. To prevent this, we have made it clear that any patent must be licensed for everyone\u8217's free use or not licensed at all.\par}
|
||||
{\pard \ql \f0 \sa180 \li0 \fi0 The precise terms and conditions for copying, distribution and modification follow.\par}
|
||||
{\pard \ql \f0 \sa180 \li0 \fi0 \b \fs32 GNU GENERAL PUBLIC LICENSE\par}
|
||||
{\pard \ql \f0 \sa180 \li0 \fi0 TERMS AND CONDITIONS FOR COPYING, DISTRIBUTION AND MODIFICATION\par}
|
||||
{\pard \ql \f0 \sa180 \li360 \fi-360 0.\tx360\tab This License applies to any program or other work which contains a notice placed by the copyright holder saying it may be distributed under the terms of this General Public License. The \u8220"Program\u8221", below, refers to any such program or work, and a \u8220"work based on the Program\u8221" means either the Program or any derivative work under copyright law: that is to say, a work containing the Program or a portion of it, either verbatim or with modifications and/or translated into another language. (Hereinafter, translation is included without limitation in the term \u8220"modification\u8221".) Each licensee is addressed as \u8220"you\u8221".\par}
|
||||
{\pard \ql \f0 \sa180 \li360 \fi0 Activities other than copying, distribution and modification are not covered by this License; they are outside its scope. The act of running the Program is not restricted, and the output from the Program is covered only if its contents constitute a work based on the Program (independent of having been made by running the Program). Whether that is true depends on what the Program does.\par}
|
||||
{\pard \ql \f0 \sa180 \li360 \fi-360 1.\tx360\tab You may copy and distribute verbatim copies of the Program\u8217's source code as you receive it, in any medium, provided that you conspicuously and appropriately publish on each copy an appropriate copyright notice and disclaimer of warranty; keep intact all the notices that refer to this License and to the absence of any warranty; and give any other recipients of the Program a copy of this License along with the Program.\par}
|
||||
{\pard \ql \f0 \sa180 \li360 \fi0 You may charge a fee for the physical act of transferring a copy, and you may at your option offer warranty protection in exchange for a fee.\par}
|
||||
{\pard \ql \f0 \sa180 \li360 \fi-360 2.\tx360\tab You may modify your copy or copies of the Program or any portion of it, thus forming a work based on the Program, and copy and distribute such modifications or work under the terms of Section 1 above, provided that you also meet all of these conditions:\par}
|
||||
{\pard \ql \f0 \sa180 \li720 \fi-360 a)\tx360\tab You must cause the modified files to carry prominent notices stating that you changed the files and the date of any change.\par}
|
||||
{\pard \ql \f0 \sa180 \li720 \fi-360 b)\tx360\tab You must cause any work that you distribute or publish, that in whole or in part contains or is derived from the Program or any part thereof, to be licensed as a whole at no charge to all third parties under the terms of this License.\par}
|
||||
{\pard \ql \f0 \sa180 \li720 \fi-360 c)\tx360\tab If the modified program normally reads commands interactively when run, you must cause it, when started running for such interactive use in the most ordinary way, to print or display an announcement including an appropriate copyright notice and a notice that there is no warranty (or else, saying that you provide a warranty) and that users may redistribute the program under these conditions, and telling the user how to view a copy of this License. (Exception: if the Program itself is interactive but does not normally print such an announcement, your work based on the Program is not required to print an announcement.)\sa180\par}
|
||||
{\pard \ql \f0 \sa180 \li360 \fi0 These requirements apply to the modified work as a whole. If identifiable sections of that work are not derived from the Program, and can be reasonably considered independent and separate works in themselves, then this License, and its terms, do not apply to those sections when you distribute them as separate works. But when you distribute the same sections as part of a whole which is a work based on the Program, the distribution of the whole must be on the terms of this License, whose permissions for other licensees extend to the entire whole, and thus to each and every part regardless of who wrote it.\par}
|
||||
{\pard \ql \f0 \sa180 \li360 \fi0 Thus, it is not the intent of this section to claim rights or contest your rights to work written entirely by you; rather, the intent is to exercise the right to control the distribution of derivative or collective works based on the Program.\par}
|
||||
{\pard \ql \f0 \sa180 \li360 \fi0 In addition, mere aggregation of another work not based on the Program with the Program (or with a work based on the Program) on a volume of a storage or distribution medium does not bring the other work under the scope of this License.\par}
|
||||
{\pard \ql \f0 \sa180 \li360 \fi-360 3.\tx360\tab You may copy and distribute the Program (or a work based on it, under Section 2) in object code or executable form under the terms of Sections 1 and 2 above provided that you also do one of the following:\par}
|
||||
{\pard \ql \f0 \sa180 \li720 \fi-360 a)\tx360\tab Accompany it with the complete corresponding machine-readable source code, which must be distributed under the terms of Sections 1 and 2 above on a medium customarily used for software interchange; or,\par}
|
||||
{\pard \ql \f0 \sa180 \li720 \fi-360 b)\tx360\tab Accompany it with a written offer, valid for at least three years, to give any third party, for a charge no more than your cost of physically performing source distribution, a complete machine-readable copy of the corresponding source code, to be distributed under the terms of Sections 1 and 2 above on a medium customarily used for software interchange; or,\par}
|
||||
{\pard \ql \f0 \sa180 \li720 \fi-360 c)\tx360\tab Accompany it with the information you received as to the offer to distribute corresponding source code. (This alternative is allowed only for noncommercial distribution and only if you received the program in object code or executable form with such an offer, in accord with Subsection b above.)\sa180\par}
|
||||
{\pard \ql \f0 \sa180 \li360 \fi0 The source code for a work means the preferred form of the work for making modifications to it. For an executable work, complete source code means all the source code for all modules it contains, plus any associated interface definition files, plus the scripts used to control compilation and installation of the executable. However, as a special exception, the source code distributed need not include anything that is normally distributed (in either source or binary form) with the major components (compiler, kernel, and so on) of the operating system on which the executable runs, unless that component itself accompanies the executable.\par}
|
||||
{\pard \ql \f0 \sa180 \li360 \fi0 If distribution of executable or object code is made by offering access to copy from a designated place, then offering equivalent access to copy the source code from the same place counts as distribution of the source code, even though third parties are not compelled to copy the source along with the object code.\par}
|
||||
{\pard \ql \f0 \sa180 \li360 \fi-360 4.\tx360\tab You may not copy, modify, sublicense, or distribute the Program except as expressly provided under this License. Any attempt otherwise to copy, modify, sublicense or distribute the Program is void, and will automatically terminate your rights under this License. However, parties who have received copies, or rights, from you under this License will not have their licenses terminated so long as such parties remain in full compliance.\par}
|
||||
{\pard \ql \f0 \sa180 \li360 \fi-360 5.\tx360\tab You are not required to accept this License, since you have not signed it. However, nothing else grants you permission to modify or distribute the Program or its derivative works. These actions are prohibited by law if you do not accept this License. Therefore, by modifying or distributing the Program (or any work based on the Program), you indicate your acceptance of this License to do so, and all its terms and conditions for copying, distributing or modifying the Program or works based on it.\par}
|
||||
{\pard \ql \f0 \sa180 \li360 \fi-360 6.\tx360\tab Each time you redistribute the Program (or any work based on the Program), the recipient automatically receives a license from the original licensor to copy, distribute or modify the Program subject to these terms and conditions. You may not impose any further restrictions on the recipients\u8217' exercise of the rights granted herein. You are not responsible for enforcing compliance by third parties to this License.\par}
|
||||
{\pard \ql \f0 \sa180 \li360 \fi-360 7.\tx360\tab If, as a consequence of a court judgment or allegation of patent infringement or for any other reason (not limited to patent issues), conditions are imposed on you (whether by court order, agreement or otherwise) that contradict the conditions of this License, they do not excuse you from the conditions of this License. If you cannot distribute so as to satisfy simultaneously your obligations under this License and any other pertinent obligations, then as a consequence you may not distribute the Program at all. For example, if a patent license would not permit royalty-free redistribution of the Program by all those who receive copies directly or indirectly through you, then the only way you could satisfy both it and this License would be to refrain entirely from distribution of the Program.\par}
|
||||
{\pard \ql \f0 \sa180 \li360 \fi0 If any portion of this section is held invalid or unenforceable under any particular circumstance, the balance of the section is intended to apply and the section as a whole is intended to apply in other circumstances.\par}
|
||||
{\pard \ql \f0 \sa180 \li360 \fi0 It is not the purpose of this section to induce you to infringe any patents or other property right claims or to contest validity of any such claims; this section has the sole purpose of protecting the integrity of the free software distribution system, which is implemented by public license practices. Many people have made generous contributions to the wide range of software distributed through that system in reliance on consistent application of that system; it is up to the author/donor to decide if he or she is willing to distribute software through any other system and a licensee cannot impose that choice.\par}
|
||||
{\pard \ql \f0 \sa180 \li360 \fi0 This section is intended to make thoroughly clear what is believed to be a consequence of the rest of this License.\par}
|
||||
{\pard \ql \f0 \sa180 \li360 \fi-360 8.\tx360\tab If the distribution and/or use of the Program is restricted in certain countries either by patents or by copyrighted interfaces, the original copyright holder who places the Program under this License may add an explicit geographical distribution limitation excluding those countries, so that distribution is permitted only in or among countries not thus excluded. In such case, this License incorporates the limitation as if written in the body of this License.\par}
|
||||
{\pard \ql \f0 \sa180 \li360 \fi-360 9.\tx360\tab The Free Software Foundation may publish revised and/or new versions of the General Public License from time to time. Such new versions will be similar in spirit to the present version, but may differ in detail to address new problems or concerns.\par}
|
||||
{\pard \ql \f0 \sa180 \li360 \fi0 Each version is given a distinguishing version number. If the Program specifies a version number of this License which applies to it and \u8220"any later version\u8221", you have the option of following the terms and conditions either of that version or of any later version published by the Free Software Foundation. If the Program does not specify a version number of this License, you may choose any version ever published by the Free Software Foundation.\par}
|
||||
{\pard \ql \f0 \sa180 \li360 \fi-360 10.\tx360\tab If you wish to incorporate parts of the Program into other free programs whose distribution conditions are different, write to the author to ask for permission. For software which is copyrighted by the Free Software Foundation, write to the Free Software Foundation; we sometimes make exceptions for this. Our decision will be guided by the two goals of preserving the free status of all derivatives of our free software and of promoting the sharing and reuse of software generally.\sa180\par}
|
||||
{\pard \ql \f0 \sa180 \li0 \fi0 NO WARRANTY\par}
|
||||
{\pard \ql \f0 \sa180 \li360 \fi-360 11.\tx360\tab BECAUSE THE PROGRAM IS LICENSED FREE OF CHARGE, THERE IS NO WARRANTY FOR THE PROGRAM, TO THE EXTENT PERMITTED BY APPLICABLE LAW. EXCEPT WHEN OTHERWISE STATED IN WRITING THE COPYRIGHT HOLDERS AND/OR OTHER PARTIES PROVIDE THE PROGRAM \u8220"AS IS\u8221" WITHOUT WARRANTY OF ANY KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. THE ENTIRE RISK AS TO THE QUALITY AND PERFORMANCE OF THE PROGRAM IS WITH YOU. SHOULD THE PROGRAM PROVE DEFECTIVE, YOU ASSUME THE COST OF ALL NECESSARY SERVICING, REPAIR OR CORRECTION.\par}
|
||||
{\pard \ql \f0 \sa180 \li360 \fi-360 12.\tx360\tab IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN WRITING WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MAY MODIFY AND/OR REDISTRIBUTE THE PROGRAM AS PERMITTED ABOVE, BE LIABLE TO YOU FOR DAMAGES, INCLUDING ANY GENERAL, SPECIAL, INCIDENTAL OR CONSEQUENTIAL DAMAGES ARISING OUT OF THE USE OR INABILITY TO USE THE PROGRAM (INCLUDING BUT NOT LIMITED TO LOSS OF DATA OR DATA BEING RENDERED INACCURATE OR LOSSES SUSTAINED BY YOU OR THIRD PARTIES OR A FAILURE OF THE PROGRAM TO OPERATE WITH ANY OTHER PROGRAMS), EVEN IF SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE POSSIBILITY OF SUCH DAMAGES.\sa180\par}
|
||||
{\pard \ql \f0 \sa180 \li0 \fi0 END OF TERMS AND CONDITIONS\par}
|
||||
{\pard \ql \f0 \sa180 \li0 \fi0 \b \fs32 How to Apply These Terms to Your New Programs\par}
|
||||
{\pard \ql \f0 \sa180 \li0 \fi0 If you develop a new program, and you want it to be of the greatest possible use to the public, the best way to achieve this is to make it free software which everyone can redistribute and change under these terms.\par}
|
||||
{\pard \ql \f0 \sa180 \li0 \fi0 To do so, attach the following notices to the program. It is safest to attach them to the start of each source file to most effectively convey the exclusion of warranty; and each file should have at least the \u8220"copyright\u8221" line and a pointer to where the full notice is found.\par}
|
||||
{\pard \ql \f0 \sa180 \li0 \fi0 \f1 <one line to give the program's name and a brief idea of what it does.>\line
|
||||
Copyright (C) <year> <name of author>\line
|
||||
\line
|
||||
This program is free software; you can redistribute it and/or modify\line
|
||||
it under the terms of the GNU General Public License as published by\line
|
||||
the Free Software Foundation; either version 2 of the License, or\line
|
||||
(at your option) any later version.\line
|
||||
\line
|
||||
This program is distributed in the hope that it will be useful,\line
|
||||
but WITHOUT ANY WARRANTY; without even the implied warranty of\line
|
||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the\line
|
||||
GNU General Public License for more details.\line
|
||||
\line
|
||||
You should have received a copy of the GNU General Public License\line
|
||||
along with this program; if not, write to the Free Software\line
|
||||
Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA\par}
|
||||
{\pard \ql \f0 \sa180 \li0 \fi0 Also add information on how to contact you by electronic and paper mail.\par}
|
||||
{\pard \ql \f0 \sa180 \li0 \fi0 If the program is interactive, make it output a short notice like this when it starts in an interactive mode:\par}
|
||||
{\pard \ql \f0 \sa180 \li0 \fi0 \f1 Gnomovision version 69, Copyright (C) year name of author\line
|
||||
Gnomovision comes with ABSOLUTELY NO WARRANTY; for details type `show w'.\line
|
||||
This is free software, and you are welcome to redistribute it\line
|
||||
under certain conditions; type `show c' for details.\par}
|
||||
{\pard \ql \f0 \sa180 \li0 \fi0 The hypothetical commands {\f1 show w' and}show c\u8217' should show the appropriate parts of the General Public License. Of course, the commands you use may be called something other than {\f1 show w' and}show c\u8217'; they could even be mouse-clicks or menu items\u8211-whatever suits your program.\par}
|
||||
{\pard \ql \f0 \sa180 \li0 \fi0 You should also get your employer (if you work as a programmer) or your school, if any, to sign a \u8220"copyright disclaimer\u8221" for the program, if necessary. Here is a sample; alter the names:\par}
|
||||
{\pard \ql \f0 \sa180 \li0 \fi0 \f1 Yoyodyne, Inc., hereby disclaims all copyright interest in the program\line
|
||||
`Gnomovision' (which makes passes at compilers) written by James Hacker.\line
|
||||
\line
|
||||
<signature of Ty Coon>, 1 April 1989\line
|
||||
Ty Coon, President of Vice\par}
|
||||
{\pard \ql \f0 \sa180 \li0 \fi0 This General Public License does not permit incorporating your program into proprietary programs. If your program is a subroutine library, you may consider it more useful to permit linking proprietary applications with the library. If this is what you want to do, use the GNU Library General Public License instead of this License.\par}
|
||||
}
|
||||
108
InstallerProjects/Installer/Resources/Pandoc/COPYRIGHT.txt
Normal file
@@ -0,0 +1,108 @@
|
||||
Pandoc
|
||||
Copyright (C) 2006-2016 John MacFarlane <jgm at berkeley dot edu>
|
||||
|
||||
This code is released under the [GPL], version 2 or later:
|
||||
|
||||
This program is free software; you can redistribute it and/or modify
|
||||
it under the terms of the GNU General Public License as published by
|
||||
the Free Software Foundation; either version 2 of the License, or
|
||||
(at your option) any later version.
|
||||
|
||||
This program is distributed in the hope that it will be useful,
|
||||
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
GNU General Public License for more details.
|
||||
|
||||
You should have received a copy of the GNU General Public License
|
||||
along with this program; if not, write to the Free Software
|
||||
Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
|
||||
|
||||
The GNU General Public License is available in the file COPYING in
|
||||
the source distribution. On Debian systems, the complete text of the
|
||||
GPL can be found in `/usr/share/common-licenses/GPL`.
|
||||
|
||||
[GPL]: http://www.gnu.org/copyleft/gpl.html
|
||||
|
||||
Pandoc's complete source code is available from the [Pandoc home page].
|
||||
|
||||
[Pandoc home page]: http://pandoc.org
|
||||
|
||||
Pandoc includes some code from other authors. The copyright and license
|
||||
statements for these sources are included below. All are GPL-compatible
|
||||
licenses.
|
||||
|
||||
----------------------------------------------------------------------
|
||||
src/Text/Pandoc/Writers/Texinfo.hs
|
||||
Copyright (C) 2008-2015 John MacFarlane and Peter Wang
|
||||
|
||||
Released under the GNU General Public License version 2 or later.
|
||||
|
||||
----------------------------------------------------------------------
|
||||
src/Text/Pandoc/Writers/OpenDocument.hs
|
||||
Copyright (C) 2008-2015 Andrea Rossato and John MacFarlane
|
||||
|
||||
Released under the GNU General Public License version 2 or later.
|
||||
|
||||
----------------------------------------------------------------------
|
||||
src/Text/Pandoc/Writers/Org.hs
|
||||
Copyright (C) 2010-2015 Puneeth Chaganti and John MacFarlane
|
||||
|
||||
Released under the GNU General Public License version 2 or later.
|
||||
|
||||
----------------------------------------------------------------------
|
||||
src/Text/Pandoc/Readers/Textile.hs
|
||||
Copyright (C) 2010-2015 Paul Rivier and John MacFarlane
|
||||
|
||||
Released under the GNU General Public License version 2 or later.
|
||||
|
||||
----------------------------------------------------------------------
|
||||
src/Text/Pandoc/Readers/Org.hs
|
||||
tests/Tests/Readers/Org.hs
|
||||
Copyright (C) 2014-2015 Albert Krewinkel
|
||||
|
||||
Released under the GNU General Public License version 2 or later.
|
||||
|
||||
----------------------------------------------------------------------
|
||||
data/LaTeXMathML.js
|
||||
Adapted by Jeff Knisely and Douglas Woodall from
|
||||
ASCIIMathML.js v. 1.4.7
|
||||
Copyright (C) 2005 Peter Jipsen
|
||||
|
||||
Released under the GNU General Public License version 2 or later.
|
||||
|
||||
----------------------------------------------------------------------
|
||||
data/MathMLinHTML.js
|
||||
Copyright (C) 2004 Peter Jipsen http://www.chapman.edu/~jipsen
|
||||
|
||||
Released under the GNU General Public License version 2 or later.
|
||||
|
||||
------------------------------------------------------------------------
|
||||
The dzslides template contains javascript and CSS from Paul Rouget's
|
||||
dzslides template.
|
||||
http://github.com/paulrouget/dzslides
|
||||
|
||||
Released under the Do What the Fuck You Want To Public License.
|
||||
|
||||
------------------------------------------------------------------------
|
||||
Pandoc embeds a lua interpreter (via hslua).
|
||||
|
||||
Copyright © 1994–2015 Lua.org, PUC-Rio.
|
||||
|
||||
Permission is hereby granted, free of charge, to any person obtaining
|
||||
a copy of this software and associated documentation files (the
|
||||
"Software"), to deal in the Software without restriction, including
|
||||
without limitation the rights to use, copy, modify, merge, publish,
|
||||
distribute, sublicense, and/or sell copies of the Software, and to
|
||||
permit persons to whom the Software is furnished to do so, subject to
|
||||
the following conditions:
|
||||
|
||||
The above copyright notice and this permission notice shall be
|
||||
included in all copies or substantial portions of the Software.
|
||||
|
||||
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
|
||||
EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
|
||||
MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
|
||||
NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
|
||||
LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
|
||||
OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
|
||||
WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
||||
BIN
InstallerProjects/Installer/Resources/Pandoc/pandoc.exe
Normal file
@@ -1,7 +1,7 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<Wix xmlns="http://schemas.microsoft.com/wix/2006/wi" xmlns:netfx="http://schemas.microsoft.com/wix/NetFxExtension">
|
||||
<?include $(sys.CURRENTDIR)Includes\Config.wxi?>
|
||||
<Product Name="$(var.ProductNameWithPlatform)" Manufacturer="Profi-Kom Ltd."
|
||||
<Product Name="$(var.ProductNameWithPlatform)" Manufacturer="Next Generation Software"
|
||||
Version="$(var.ProductVersion)"
|
||||
Id="$(var.ProductCode)"
|
||||
UpgradeCode="$(var.UpgradeCode)"
|
||||
@@ -9,10 +9,10 @@
|
||||
<Package InstallerVersion="200" Compressed="yes" InstallScope="perMachine" />
|
||||
<MajorUpgrade DowngradeErrorMessage="!(loc.Upgrade_NewerVersionInstalled)" Schedule="afterInstallExecute" />
|
||||
<MediaTemplate EmbedCab="yes" />
|
||||
<Binary Id="CustomActions.CA.dll" SourceFile="$(var.SolutionDir)mRemoteNGInstaller\CustomActions\bin\x64\$(var.Configuration)\CustomActions.CA.dll" />
|
||||
<Binary Id="CustomActions.CA.dll" SourceFile="$(var.SolutionDir)InstallerProjects\CustomActions\bin\$(var.Configuration)\CustomActions.CA.dll" />
|
||||
<Property Id="MsiLogging" Value="v" />
|
||||
<Property Id="ARPPRODUCTICON" Value="AppIcon.ico" />
|
||||
<Property Id="ARPHELPLINK" Value="http://mremoteng.org" />
|
||||
<Property Id="ARPHELPLINK" Value="http://www.mremoteng.org" />
|
||||
<SetProperty Id="ARPINSTALLLOCATION" Value="[INSTALLDIR]" After="CostFinalize" />
|
||||
<Property Id="MAINEXE" Value="$(var.ExeProcessName)" />
|
||||
<Property Id="INSTALLDIR">
|
||||
@@ -70,24 +70,32 @@
|
||||
<Condition Message="!(loc.Install_RDP80Requirement)">
|
||||
<![CDATA[Installed OR (IGNOREPREREQUISITES = 1) OR (VersionNT >= 602 OR VersionNT64 >= 602) OR ((VersionNT = 601 OR VersionNT64 = 601) AND (MINIMUM_RDP_VERSION_INSTALLED = 1))]]>
|
||||
</Condition>
|
||||
|
||||
<Feature Id="F.MinimalFeature" Title="$(var.ProductName)" Absent="disallow" ConfigurableDirectory="INSTALLDIR" Display="expand" Level="1">
|
||||
<ComponentGroupRef Id="MandatoryComponents" Primary="yes" />
|
||||
<ComponentRef Id="C.MainExe" Primary="yes" />
|
||||
<ComponentGroupRef Id="CG.ProjectInfoFiles" Primary="yes" />
|
||||
<ComponentRef Id="C.RegistryEntries" Primary="yes" />
|
||||
</Feature>
|
||||
|
||||
<Feature Id="F.DesktopShortcut" Title="!(loc.Feature_DesktopShortcut)" Absent="allow" AllowAdvertise="no" Level="1">
|
||||
<ComponentRef Id="C.DesktopShortcut" Primary="yes" />
|
||||
</Feature>
|
||||
|
||||
<Feature Id="F.ApplicationStartMenuShortcut" Title="!(loc.Feature_StartMenuShortcut)" Absent="allow" AllowAdvertise="no" Level="1">
|
||||
<ComponentRef Id="C.ApplicationStartMenuShortcut" Primary="yes" />
|
||||
</Feature>
|
||||
|
||||
|
||||
|
||||
<Feature Id="F.Complete" Title="!(loc.Feature_Complete)" Display="expand" AllowAdvertise="no" Level="1">
|
||||
<Feature Id="F.MinimalFeature" Title="$(var.ProductName)" Absent="disallow" ConfigurableDirectory="INSTALLDIR" Level="1">
|
||||
<ComponentGroupRef Id="MandatoryComponents" Primary="yes" />
|
||||
<ComponentRef Id="C.MainExe" Primary="yes" />
|
||||
<ComponentGroupRef Id="CG.ProjectInfoFiles" Primary="yes" />
|
||||
<ComponentRef Id="C.RegistryEntries" Primary="yes" />
|
||||
</Feature>
|
||||
|
||||
<Feature Id="F.PuttyNG" Title="PuTTYNG" Absent="allow" AllowAdvertise="no" Level="1">
|
||||
<ComponentRef Id="C.PuttyNGFile" Primary="yes" />
|
||||
</Feature>
|
||||
|
||||
<Feature Id="F.DesktopShortcut" Title="!(loc.Feature_DesktopShortcut)" Absent="allow" AllowAdvertise="no" Level="1">
|
||||
<ComponentRef Id="C.DesktopShortcut" Primary="yes" />
|
||||
</Feature>
|
||||
|
||||
<Feature Id="F.ApplicationStartMenuShortcut" Title="!(loc.Feature_StartMenuShortcut)" Absent="allow" AllowAdvertise="no" Level="1">
|
||||
<ComponentRef Id="C.ApplicationStartMenuShortcut" Primary="yes" />
|
||||
</Feature>
|
||||
</Feature>
|
||||
|
||||
<UI>
|
||||
<UIRef Id="My_WixUI_FeatureTree"/>
|
||||
<UIRef Id="WixUI_FeatureTree"/>
|
||||
<UIRef Id="WixUI_ErrorProgressText" />
|
||||
<Publish Dialog="ExitDialog"
|
||||
Control="Finish"
|
||||
63
Jenkinsfile
vendored
Normal file
@@ -0,0 +1,63 @@
|
||||
#!groovy
|
||||
node('windows') {
|
||||
def jobDir = pwd()
|
||||
def solutionFilePath = "\"${jobDir}\\mRemoteV1.sln\""
|
||||
def msBuild = "C:\\Program Files (x86)\\Microsoft Visual Studio\\2017\\Community\\MSBuild\\15.0\\Bin\\msbuild.exe"
|
||||
def nunitConsolePath = "${jobDir}\\packages\\NUnit.ConsoleRunner.3.7.0\\tools\\nunit3-console.exe"
|
||||
def openCoverPath = "${jobDir}\\packages\\OpenCover.4.6.519\\tools\\OpenCover.Console.exe"
|
||||
def reportGeneratorPath = "${jobDir}\\packages\\ReportGenerator.3.0.2\\tools\\ReportGenerator.exe"
|
||||
def testResultFilePrefix = "TestResult"
|
||||
def testResultFileNormal = "${testResultFilePrefix}_UnitTests_normal.xml"
|
||||
def testResultFilePortable = "${testResultFilePrefix}_UnitTests_portable.xml"
|
||||
def testResultFileAcceptance = "${testResultFilePrefix}_AcceptanceTests.xml"
|
||||
def coverageReport = "code_coverage_report.xml"
|
||||
def codeCoverageHtml = "CodeCoverageReport.html"
|
||||
|
||||
stage ('Checkout Branch') {
|
||||
checkout scm
|
||||
bat "del /Q \"${jobDir}\\${testResultFilePrefix}*.xml\""
|
||||
}
|
||||
|
||||
stage ('Restore NuGet Packages') {
|
||||
def nugetPath = "C:\\nuget.exe"
|
||||
bat "${nugetPath} restore ${solutionFilePath}"
|
||||
}
|
||||
|
||||
stage ('Build mRemoteNG (Normal)') {
|
||||
bat "\"${msBuild}\" /nologo /p:Platform=x86 \"${jobDir}\\mRemoteV1.sln\""
|
||||
}
|
||||
|
||||
stage ('Build mRemoteNG (Portable)') {
|
||||
bat "\"${msBuild}\" /nologo /p:Configuration=\"Debug Portable\";Platform=x86 \"${jobDir}\\mRemoteV1.sln\""
|
||||
}
|
||||
|
||||
stage ('Run Unit Tests (Normal, w/coverage)') {
|
||||
try {
|
||||
bat "\"${openCoverPath}\" -register:user -target:\"${nunitConsolePath}\" -targetargs:\"\"${jobDir}\\mRemoteNGTests\\bin\\debug\\mRemoteNGTests.dll\" --result=${testResultFileNormal} --x86\" -output:\"${coverageReport}\""
|
||||
}
|
||||
catch (ex) {
|
||||
nunit testResultsPattern: "${testResultFilePrefix}*.xml"
|
||||
throw ex
|
||||
}
|
||||
}
|
||||
|
||||
stage ('Run Unit Tests (Portable)') {
|
||||
try {
|
||||
bat "\"${nunitConsolePath}\" \"${jobDir}\\mRemoteNGTests\\bin\\debug portable\\mRemoteNGTests.dll\" --result=${testResultFilePortable} --x86"
|
||||
}
|
||||
catch (ex) {
|
||||
nunit testResultsPattern: "${testResultFilePrefix}*.xml"
|
||||
throw ex
|
||||
}
|
||||
}
|
||||
|
||||
stage ('Run Acceptance Tests') {
|
||||
try {
|
||||
bat "\"${nunitConsolePath}\" \"${jobDir}\\mRemoteNG.Specs\\bin\\debug\\mRemoteNG.Specs.dll\" --result=${testResultFileAcceptance} --x86"
|
||||
}
|
||||
catch (ex) {
|
||||
nunit testResultsPattern: "${testResultFilePrefix}*.xml"
|
||||
throw ex
|
||||
}
|
||||
}
|
||||
}
|
||||
71
Jenkinsfile_publish.groovy
Normal file
@@ -0,0 +1,71 @@
|
||||
node('windows') {
|
||||
def jobDir = pwd()
|
||||
def solutionFilePath = "\"${jobDir}\\mRemoteV1.sln\""
|
||||
def msBuild = "C:\\Program Files (x86)\\Microsoft Visual Studio\\2017\\Community\\MSBuild\\15.0\\Bin\\msbuild.exe"
|
||||
def nunitConsolePath = "${jobDir}\\packages\\NUnit.ConsoleRunner.3.7.0\\tools\\nunit3-console.exe"
|
||||
def openCoverPath = "${jobDir}\\packages\\OpenCover.4.6.519\\tools\\OpenCover.Console.exe"
|
||||
def testResultFilePrefix = "TestResult"
|
||||
def testResultFileNormal = "${testResultFilePrefix}_UnitTests_normal.xml"
|
||||
def testResultFilePortable = "${testResultFilePrefix}_UnitTests_portable.xml"
|
||||
def coverageReport = "code_coverage_report.xml"
|
||||
|
||||
|
||||
stage ('Clean output dir') {
|
||||
bat script: "rmdir /S /Q \"${jobDir}\\Release\" 2>nul", returnStatus: true
|
||||
}
|
||||
|
||||
stage ('Checkout Branch') {
|
||||
checkout([
|
||||
$class: 'GitSCM',
|
||||
branches: [[name: '*/${TargetBranch}']],
|
||||
doGenerateSubmoduleConfigurations: false,
|
||||
extensions: [],
|
||||
submoduleCfg: [],
|
||||
userRemoteConfigs: [[
|
||||
credentialsId: '9c3fbff4-5b90-402f-a298-00e607fcec87',
|
||||
url: 'https://github.com/mRemoteNG/mRemoteNG.git'
|
||||
]]
|
||||
])
|
||||
}
|
||||
|
||||
stage ('Restore NuGet Packages') {
|
||||
def nugetPath = "C:\\nuget.exe"
|
||||
bat "${nugetPath} restore ${solutionFilePath}"
|
||||
}
|
||||
|
||||
withCredentials([file(credentialsId: '9b674d57-6792-48e3-984a-4d1bab2abb64', variable: 'CODE_SIGNING_CERT')]) {
|
||||
withCredentials([usernamePassword(credentialsId: '05b7449b-05c0-490f-8661-236242526e62', passwordVariable: 'MRNG_CERT_PASSWORD', usernameVariable: 'NO_USERNAME')]) {
|
||||
stage ('Build mRemoteNG (Normal - MSI)') {
|
||||
bat "\"${msBuild}\" /nologo /t:Clean,Build /p:Configuration=\"Release Installer\" /p:Platform=x86 /p:CertPath=\"${env.CODE_SIGNING_CERT}\" /p:CertPassword=${env.MRNG_CERT_PASSWORD} \"${jobDir}\\mRemoteV1.sln\""
|
||||
archiveArtifacts artifacts: "Release\\*.msi", caseSensitive: false, onlyIfSuccessful: true, fingerprint: true
|
||||
}
|
||||
|
||||
stage ('Build mRemoteNG (Portable)') {
|
||||
bat "\"${msBuild}\" /nologo /t:Clean,Build /p:Configuration=\"Release Portable\" /p:Platform=x86 /p:CertPath=\"${env.CODE_SIGNING_CERT}\" /p:CertPassword=${env.MRNG_CERT_PASSWORD} \"${jobDir}\\mRemoteV1.sln\""
|
||||
archiveArtifacts artifacts: "Release\\*.zip", caseSensitive: false, onlyIfSuccessful: true, fingerprint: true
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
stage ('Run Unit Tests (Normal - MSI)') {
|
||||
bat "\"${nunitConsolePath}\" \"${jobDir}\\mRemoteNGTests\\bin\\release\\mRemoteNGTests.dll\" --result=${testResultFileNormal} --x86"
|
||||
}
|
||||
|
||||
stage ('Run Unit Tests (Portable)') {
|
||||
bat "\"${nunitConsolePath}\" \"${jobDir}\\mRemoteNGTests\\bin\\release portable\\mRemoteNGTests.dll\" --result=${testResultFilePortable} --x86"
|
||||
}
|
||||
|
||||
stage ('Generate UpdateCheck Files') {
|
||||
bat "powershell -ExecutionPolicy Bypass -File \"${jobDir}\\Tools\\create_upg_chk_files.ps1\" -TagName \"${env.TagName}\" -UpdateChannel \"${env.UpdateChannel}\""
|
||||
archiveArtifacts artifacts: "Release\\*.txt", caseSensitive: false, onlyIfSuccessful: true
|
||||
}
|
||||
|
||||
stage ('Publish to GitHub') {
|
||||
withCredentials([string(credentialsId: '5443a369-dbe8-42d3-b4e8-04d0b4e9039a', variable: 'GH_AUTH_TOKEN')]) {
|
||||
def releaseFolder = "${jobDir}\\Release"
|
||||
// because batch files suck at handling newline characters, we have to convert to base64 in groovy and back to text in powershell
|
||||
def base64Description = env.ReleaseDescription.bytes.encodeBase64().toString()
|
||||
bat "powershell -ExecutionPolicy Bypass -File \"${jobDir}\\Tools\\publish_to_github.ps1\" -Owner \"mRemoteNG\" -Repository \"mRemoteNG\" -ReleaseTitle \"${env.ReleaseTitle}\" -TagName \"${env.TagName}\" -TargetCommitish \"${env.TargetBranch}\" -Description \"${base64Description}\" -IsDraft ${env.IsDraft} -IsPrerelease ${env.IsPreRelease} -ReleaseFolderPath \"${releaseFolder}\" -AuthToken \"${env.GH_AUTH_TOKEN}\" -DescriptionIsBase64Encoded"
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,15 +0,0 @@
|
||||
using System.Reflection;
|
||||
|
||||
// Version information for an assembly consists of the following four values:
|
||||
//
|
||||
// Major Version
|
||||
// Minor Version
|
||||
// Build Number
|
||||
// Revision
|
||||
//
|
||||
// You can specify all the values or you can default the Revision and Build Numbers
|
||||
// by using the '*' as shown below:
|
||||
[assembly: AssemblyVersion("2.9.3.*")]
|
||||
[assembly: AssemblyFileVersion("2.9.3")]
|
||||
[assembly: AssemblyInformationalVersion("2.9.3")]
|
||||
[assembly: System.CLSCompliant(true)]
|
||||
@@ -1,520 +0,0 @@
|
||||
/*
|
||||
* CellEditKeyEngine - A engine that allows the behaviour of arbitrary keys to be configured
|
||||
*
|
||||
* Author: Phillip Piper
|
||||
* Date: 3-March-2011 10:53 pm
|
||||
*
|
||||
* Change log:
|
||||
* v2.8
|
||||
* 2014-05-30 JPP - When a row is disabled, skip over it when looking for another cell to edit
|
||||
* v2.5
|
||||
* 2012-04-14 JPP - Fixed bug where, on a OLV with only a single editable column, tabbing
|
||||
* to change rows would edit the cell above rather than the cell below
|
||||
* the cell being edited.
|
||||
* 2.5
|
||||
* 2011-03-03 JPP - First version
|
||||
*
|
||||
* Copyright (C) 2011-2014 Phillip Piper
|
||||
*
|
||||
* This program is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*
|
||||
* If you wish to use this code in a closed source application, please contact phillip.piper@gmail.com.
|
||||
*/
|
||||
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Text;
|
||||
using System.Windows.Forms;
|
||||
using BrightIdeasSoftware;
|
||||
|
||||
namespace BrightIdeasSoftware {
|
||||
/// <summary>
|
||||
/// Indicates the behavior of a key when a cell "on the edge" is being edited.
|
||||
/// and the normal behavior of that key would exceed the edge. For example,
|
||||
/// for a key that normally moves one column to the left, the "edge" would be
|
||||
/// the left most column, since the normal action of the key cannot be taken
|
||||
/// (since there are no more columns to the left).
|
||||
/// </summary>
|
||||
public enum CellEditAtEdgeBehaviour {
|
||||
/// <summary>
|
||||
/// The key press will be ignored
|
||||
/// </summary>
|
||||
Ignore,
|
||||
|
||||
/// <summary>
|
||||
/// The key press will result in the cell editing wrapping to the
|
||||
/// cell on the opposite edge.
|
||||
/// </summary>
|
||||
Wrap,
|
||||
|
||||
/// <summary>
|
||||
/// The key press will wrap, but the column will be changed to the
|
||||
/// appropiate adjacent column. This only makes sense for keys where
|
||||
/// the normal action is ChangeRow.
|
||||
/// </summary>
|
||||
ChangeColumn,
|
||||
|
||||
/// <summary>
|
||||
/// The key press will wrap, but the row will be changed to the
|
||||
/// appropiate adjacent row. This only makes sense for keys where
|
||||
/// the normal action is ChangeColumn.
|
||||
/// </summary>
|
||||
ChangeRow,
|
||||
|
||||
/// <summary>
|
||||
/// The key will result in the current edit operation being ended.
|
||||
/// </summary>
|
||||
EndEdit
|
||||
};
|
||||
|
||||
/// <summary>
|
||||
/// Indicates the normal behaviour of a key when used during a cell edit
|
||||
/// operation.
|
||||
/// </summary>
|
||||
public enum CellEditCharacterBehaviour {
|
||||
/// <summary>
|
||||
/// The key press will be ignored
|
||||
/// </summary>
|
||||
Ignore,
|
||||
|
||||
/// <summary>
|
||||
/// The key press will end the current edit and begin an edit
|
||||
/// operation on the next editable cell to the left.
|
||||
/// </summary>
|
||||
ChangeColumnLeft,
|
||||
|
||||
/// <summary>
|
||||
/// The key press will end the current edit and begin an edit
|
||||
/// operation on the next editable cell to the right.
|
||||
/// </summary>
|
||||
ChangeColumnRight,
|
||||
|
||||
/// <summary>
|
||||
/// The key press will end the current edit and begin an edit
|
||||
/// operation on the row above.
|
||||
/// </summary>
|
||||
ChangeRowUp,
|
||||
|
||||
/// <summary>
|
||||
/// The key press will end the current edit and begin an edit
|
||||
/// operation on the row below
|
||||
/// </summary>
|
||||
ChangeRowDown,
|
||||
|
||||
/// <summary>
|
||||
/// The key press will cancel the current edit
|
||||
/// </summary>
|
||||
CancelEdit,
|
||||
|
||||
/// <summary>
|
||||
/// The key press will finish the current edit operation
|
||||
/// </summary>
|
||||
EndEdit,
|
||||
|
||||
/// <summary>
|
||||
/// Custom verb that can be used for specialized actions.
|
||||
/// </summary>
|
||||
CustomVerb1,
|
||||
|
||||
/// <summary>
|
||||
/// Custom verb that can be used for specialized actions.
|
||||
/// </summary>
|
||||
CustomVerb2,
|
||||
|
||||
/// <summary>
|
||||
/// Custom verb that can be used for specialized actions.
|
||||
/// </summary>
|
||||
CustomVerb3,
|
||||
|
||||
/// <summary>
|
||||
/// Custom verb that can be used for specialized actions.
|
||||
/// </summary>
|
||||
CustomVerb4,
|
||||
|
||||
/// <summary>
|
||||
/// Custom verb that can be used for specialized actions.
|
||||
/// </summary>
|
||||
CustomVerb5,
|
||||
|
||||
/// <summary>
|
||||
/// Custom verb that can be used for specialized actions.
|
||||
/// </summary>
|
||||
CustomVerb6,
|
||||
|
||||
/// <summary>
|
||||
/// Custom verb that can be used for specialized actions.
|
||||
/// </summary>
|
||||
CustomVerb7,
|
||||
|
||||
/// <summary>
|
||||
/// Custom verb that can be used for specialized actions.
|
||||
/// </summary>
|
||||
CustomVerb8,
|
||||
|
||||
/// <summary>
|
||||
/// Custom verb that can be used for specialized actions.
|
||||
/// </summary>
|
||||
CustomVerb9,
|
||||
|
||||
/// <summary>
|
||||
/// Custom verb that can be used for specialized actions.
|
||||
/// </summary>
|
||||
CustomVerb10,
|
||||
};
|
||||
|
||||
/// <summary>
|
||||
/// Instances of this class handle key presses during a cell edit operation.
|
||||
/// </summary>
|
||||
public class CellEditKeyEngine {
|
||||
|
||||
#region Public interface
|
||||
|
||||
/// <summary>
|
||||
/// Sets the behaviour of a given key
|
||||
/// </summary>
|
||||
/// <param name="key"></param>
|
||||
/// <param name="normalBehaviour"></param>
|
||||
/// <param name="atEdgeBehaviour"></param>
|
||||
public virtual void SetKeyBehaviour(Keys key, CellEditCharacterBehaviour normalBehaviour, CellEditAtEdgeBehaviour atEdgeBehaviour) {
|
||||
this.CellEditKeyMap[key] = normalBehaviour;
|
||||
this.CellEditKeyAtEdgeBehaviourMap[key] = atEdgeBehaviour;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Handle a key press
|
||||
/// </summary>
|
||||
/// <param name="olv"></param>
|
||||
/// <param name="keyData"></param>
|
||||
/// <returns>True if the key was completely handled.</returns>
|
||||
public virtual bool HandleKey(ObjectListView olv, Keys keyData) {
|
||||
if (olv == null) throw new ArgumentNullException("olv");
|
||||
|
||||
CellEditCharacterBehaviour behaviour;
|
||||
if (!CellEditKeyMap.TryGetValue(keyData, out behaviour))
|
||||
return false;
|
||||
|
||||
this.ListView = olv;
|
||||
|
||||
switch (behaviour) {
|
||||
case CellEditCharacterBehaviour.Ignore:
|
||||
break;
|
||||
case CellEditCharacterBehaviour.CancelEdit:
|
||||
this.HandleCancelEdit();
|
||||
break;
|
||||
case CellEditCharacterBehaviour.EndEdit:
|
||||
this.HandleEndEdit();
|
||||
break;
|
||||
case CellEditCharacterBehaviour.ChangeColumnLeft:
|
||||
case CellEditCharacterBehaviour.ChangeColumnRight:
|
||||
this.HandleColumnChange(keyData, behaviour);
|
||||
break;
|
||||
case CellEditCharacterBehaviour.ChangeRowDown:
|
||||
case CellEditCharacterBehaviour.ChangeRowUp:
|
||||
this.HandleRowChange(keyData, behaviour);
|
||||
break;
|
||||
default:
|
||||
return this.HandleCustomVerb(keyData, behaviour);
|
||||
};
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
#endregion
|
||||
|
||||
#region Implementation properties
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets the ObjectListView on which the current key is being handled.
|
||||
/// This cannot be null.
|
||||
/// </summary>
|
||||
protected ObjectListView ListView {
|
||||
get { return listView; }
|
||||
set { listView = value; }
|
||||
}
|
||||
private ObjectListView listView;
|
||||
|
||||
/// <summary>
|
||||
/// Gets the row of the cell that is currently being edited
|
||||
/// </summary>
|
||||
protected OLVListItem ItemBeingEdited {
|
||||
get {
|
||||
return (this.ListView == null || this.ListView.CellEditEventArgs == null) ? null : this.ListView.CellEditEventArgs.ListViewItem;
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets the index of the column of the cell that is being edited
|
||||
/// </summary>
|
||||
protected int SubItemIndexBeingEdited {
|
||||
get {
|
||||
return (this.ListView == null || this.ListView.CellEditEventArgs == null) ? -1 : this.ListView.CellEditEventArgs.SubItemIndex;
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets the map that remembers the normal behaviour of keys
|
||||
/// </summary>
|
||||
protected IDictionary<Keys, CellEditCharacterBehaviour> CellEditKeyMap {
|
||||
get {
|
||||
if (cellEditKeyMap == null)
|
||||
this.InitializeCellEditKeyMaps();
|
||||
return cellEditKeyMap;
|
||||
}
|
||||
set {
|
||||
cellEditKeyMap = value;
|
||||
}
|
||||
}
|
||||
private IDictionary<Keys, CellEditCharacterBehaviour> cellEditKeyMap;
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets the map that remembers the desired behaviour of keys
|
||||
/// on edge cases.
|
||||
/// </summary>
|
||||
protected IDictionary<Keys, CellEditAtEdgeBehaviour> CellEditKeyAtEdgeBehaviourMap {
|
||||
get {
|
||||
if (cellEditKeyAtEdgeBehaviourMap == null)
|
||||
this.InitializeCellEditKeyMaps();
|
||||
return cellEditKeyAtEdgeBehaviourMap;
|
||||
}
|
||||
set {
|
||||
cellEditKeyAtEdgeBehaviourMap = value;
|
||||
}
|
||||
}
|
||||
private IDictionary<Keys, CellEditAtEdgeBehaviour> cellEditKeyAtEdgeBehaviourMap;
|
||||
|
||||
#endregion
|
||||
|
||||
#region Initialization
|
||||
|
||||
/// <summary>
|
||||
/// Setup the default key mapping
|
||||
/// </summary>
|
||||
protected virtual void InitializeCellEditKeyMaps() {
|
||||
this.cellEditKeyMap = new Dictionary<Keys, CellEditCharacterBehaviour>();
|
||||
this.cellEditKeyMap[Keys.Escape] = CellEditCharacterBehaviour.CancelEdit;
|
||||
this.cellEditKeyMap[Keys.Return] = CellEditCharacterBehaviour.EndEdit;
|
||||
this.cellEditKeyMap[Keys.Enter] = CellEditCharacterBehaviour.EndEdit;
|
||||
this.cellEditKeyMap[Keys.Tab] = CellEditCharacterBehaviour.ChangeColumnRight;
|
||||
this.cellEditKeyMap[Keys.Tab | Keys.Shift] = CellEditCharacterBehaviour.ChangeColumnLeft;
|
||||
this.cellEditKeyMap[Keys.Left | Keys.Alt] = CellEditCharacterBehaviour.ChangeColumnLeft;
|
||||
this.cellEditKeyMap[Keys.Right | Keys.Alt] = CellEditCharacterBehaviour.ChangeColumnRight;
|
||||
this.cellEditKeyMap[Keys.Up | Keys.Alt] = CellEditCharacterBehaviour.ChangeRowUp;
|
||||
this.cellEditKeyMap[Keys.Down | Keys.Alt] = CellEditCharacterBehaviour.ChangeRowDown;
|
||||
|
||||
this.cellEditKeyAtEdgeBehaviourMap = new Dictionary<Keys, CellEditAtEdgeBehaviour>();
|
||||
this.cellEditKeyAtEdgeBehaviourMap[Keys.Tab] = CellEditAtEdgeBehaviour.Wrap;
|
||||
this.cellEditKeyAtEdgeBehaviourMap[Keys.Tab | Keys.Shift] = CellEditAtEdgeBehaviour.Wrap;
|
||||
this.cellEditKeyAtEdgeBehaviourMap[Keys.Left | Keys.Alt] = CellEditAtEdgeBehaviour.Wrap;
|
||||
this.cellEditKeyAtEdgeBehaviourMap[Keys.Right | Keys.Alt] = CellEditAtEdgeBehaviour.Wrap;
|
||||
this.cellEditKeyAtEdgeBehaviourMap[Keys.Up | Keys.Alt] = CellEditAtEdgeBehaviour.ChangeColumn;
|
||||
this.cellEditKeyAtEdgeBehaviourMap[Keys.Down | Keys.Alt] = CellEditAtEdgeBehaviour.ChangeColumn;
|
||||
}
|
||||
|
||||
#endregion
|
||||
|
||||
#region Command handling
|
||||
|
||||
/// <summary>
|
||||
/// Handle the end edit command
|
||||
/// </summary>
|
||||
protected virtual void HandleEndEdit() {
|
||||
this.ListView.PossibleFinishCellEditing();
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Handle the cancel edit command
|
||||
/// </summary>
|
||||
protected virtual void HandleCancelEdit() {
|
||||
this.ListView.CancelCellEdit();
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Placeholder that subclasses can override to handle any custom verbs
|
||||
/// </summary>
|
||||
/// <param name="keyData"></param>
|
||||
/// <param name="behaviour"></param>
|
||||
/// <returns></returns>
|
||||
protected virtual bool HandleCustomVerb(Keys keyData, CellEditCharacterBehaviour behaviour) {
|
||||
return false;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Handle a change row command
|
||||
/// </summary>
|
||||
/// <param name="keyData"></param>
|
||||
/// <param name="behaviour"></param>
|
||||
protected virtual void HandleRowChange(Keys keyData, CellEditCharacterBehaviour behaviour) {
|
||||
// If we couldn't finish editing the current cell, don't try to move it
|
||||
if (!this.ListView.PossibleFinishCellEditing())
|
||||
return;
|
||||
|
||||
OLVListItem olvi = this.ItemBeingEdited;
|
||||
int subItemIndex = this.SubItemIndexBeingEdited;
|
||||
bool isGoingUp = behaviour == CellEditCharacterBehaviour.ChangeRowUp;
|
||||
|
||||
// Try to find a row above (or below) the currently edited cell
|
||||
// If we find one, start editing it and we're done.
|
||||
OLVListItem adjacentOlvi = this.GetAdjacentItemOrNull(olvi, isGoingUp);
|
||||
if (adjacentOlvi != null) {
|
||||
this.StartCellEditIfDifferent(adjacentOlvi, subItemIndex);
|
||||
return;
|
||||
}
|
||||
|
||||
// There is no adjacent row in the direction we want, so we must be on an edge.
|
||||
CellEditAtEdgeBehaviour atEdgeBehaviour;
|
||||
if (!this.CellEditKeyAtEdgeBehaviourMap.TryGetValue(keyData, out atEdgeBehaviour))
|
||||
atEdgeBehaviour = CellEditAtEdgeBehaviour.Wrap;
|
||||
switch (atEdgeBehaviour) {
|
||||
case CellEditAtEdgeBehaviour.Ignore:
|
||||
break;
|
||||
case CellEditAtEdgeBehaviour.EndEdit:
|
||||
this.ListView.PossibleFinishCellEditing();
|
||||
break;
|
||||
case CellEditAtEdgeBehaviour.Wrap:
|
||||
adjacentOlvi = this.GetAdjacentItemOrNull(null, isGoingUp);
|
||||
this.StartCellEditIfDifferent(adjacentOlvi, subItemIndex);
|
||||
break;
|
||||
case CellEditAtEdgeBehaviour.ChangeColumn:
|
||||
// Figure out the next editable column
|
||||
List<OLVColumn> editableColumnsInDisplayOrder = this.EditableColumnsInDisplayOrder;
|
||||
int displayIndex = Math.Max(0, editableColumnsInDisplayOrder.IndexOf(this.ListView.GetColumn(subItemIndex)));
|
||||
if (isGoingUp)
|
||||
displayIndex = (editableColumnsInDisplayOrder.Count + displayIndex - 1) % editableColumnsInDisplayOrder.Count;
|
||||
else
|
||||
displayIndex = (displayIndex + 1) % editableColumnsInDisplayOrder.Count;
|
||||
subItemIndex = editableColumnsInDisplayOrder[displayIndex].Index;
|
||||
|
||||
// Wrap to the next row and start the cell edit
|
||||
adjacentOlvi = this.GetAdjacentItemOrNull(null, isGoingUp);
|
||||
this.StartCellEditIfDifferent(adjacentOlvi, subItemIndex);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Handle a change column command
|
||||
/// </summary>
|
||||
/// <param name="keyData"></param>
|
||||
/// <param name="behaviour"></param>
|
||||
protected virtual void HandleColumnChange(Keys keyData, CellEditCharacterBehaviour behaviour)
|
||||
{
|
||||
// If we couldn't finish editing the current cell, don't try to move it
|
||||
if (!this.ListView.PossibleFinishCellEditing())
|
||||
return;
|
||||
|
||||
// Changing columns only works in details mode
|
||||
if (this.ListView.View != View.Details)
|
||||
return;
|
||||
|
||||
List<OLVColumn> editableColumns = this.EditableColumnsInDisplayOrder;
|
||||
OLVListItem olvi = this.ItemBeingEdited;
|
||||
int displayIndex = Math.Max(0,
|
||||
editableColumns.IndexOf(this.ListView.GetColumn(this.SubItemIndexBeingEdited)));
|
||||
bool isGoingLeft = behaviour == CellEditCharacterBehaviour.ChangeColumnLeft;
|
||||
|
||||
// Are we trying to continue past one of the edges?
|
||||
if ((isGoingLeft && displayIndex == 0) ||
|
||||
(!isGoingLeft && displayIndex == editableColumns.Count - 1))
|
||||
{
|
||||
// Yes, so figure out our at edge behaviour
|
||||
CellEditAtEdgeBehaviour atEdgeBehaviour;
|
||||
if (!this.CellEditKeyAtEdgeBehaviourMap.TryGetValue(keyData, out atEdgeBehaviour))
|
||||
atEdgeBehaviour = CellEditAtEdgeBehaviour.Wrap;
|
||||
switch (atEdgeBehaviour)
|
||||
{
|
||||
case CellEditAtEdgeBehaviour.Ignore:
|
||||
return;
|
||||
case CellEditAtEdgeBehaviour.EndEdit:
|
||||
this.HandleEndEdit();
|
||||
return;
|
||||
case CellEditAtEdgeBehaviour.ChangeRow:
|
||||
case CellEditAtEdgeBehaviour.Wrap:
|
||||
if (atEdgeBehaviour == CellEditAtEdgeBehaviour.ChangeRow)
|
||||
olvi = GetAdjacentItem(olvi, isGoingLeft && displayIndex == 0);
|
||||
if (isGoingLeft)
|
||||
displayIndex = editableColumns.Count - 1;
|
||||
else
|
||||
displayIndex = 0;
|
||||
break;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
if (isGoingLeft)
|
||||
displayIndex -= 1;
|
||||
else
|
||||
displayIndex += 1;
|
||||
}
|
||||
|
||||
int subItemIndex = editableColumns[displayIndex].Index;
|
||||
this.StartCellEditIfDifferent(olvi, subItemIndex);
|
||||
}
|
||||
|
||||
#endregion
|
||||
|
||||
#region Utilities
|
||||
|
||||
/// <summary>
|
||||
/// Start editing the indicated cell if that cell is not already being edited
|
||||
/// </summary>
|
||||
/// <param name="olvi">The row to edit</param>
|
||||
/// <param name="subItemIndex">The cell within that row to edit</param>
|
||||
protected void StartCellEditIfDifferent(OLVListItem olvi, int subItemIndex) {
|
||||
if (this.ItemBeingEdited == olvi && this.SubItemIndexBeingEdited == subItemIndex)
|
||||
return;
|
||||
|
||||
this.ListView.EnsureVisible(olvi.Index);
|
||||
this.ListView.StartCellEdit(olvi, subItemIndex);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets the adjacent item to the given item in the given direction.
|
||||
/// If that item is disabled, continue in that direction until an enabled item is found.
|
||||
/// </summary>
|
||||
/// <param name="olvi">The row whose neighbour is sought</param>
|
||||
/// <param name="up">The direction of the adjacentness</param>
|
||||
/// <returns>An OLVListView adjacent to the given item, or null if there are no more enabled items in that direction.</returns>
|
||||
protected OLVListItem GetAdjacentItemOrNull(OLVListItem olvi, bool up) {
|
||||
OLVListItem item = up ? this.ListView.GetPreviousItem(olvi) : this.ListView.GetNextItem(olvi);
|
||||
while (item != null && !item.Enabled)
|
||||
item = up ? this.ListView.GetPreviousItem(item) : this.ListView.GetNextItem(item);
|
||||
return item;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets the adjacent item to the given item in the given direction, wrapping if needed.
|
||||
/// </summary>
|
||||
/// <param name="olvi">The row whose neighbour is sought</param>
|
||||
/// <param name="up">The direction of the adjacentness</param>
|
||||
/// <returns>An OLVListView adjacent to the given item, or null if there are no more items in that direction.</returns>
|
||||
protected OLVListItem GetAdjacentItem(OLVListItem olvi, bool up) {
|
||||
return this.GetAdjacentItemOrNull(olvi, up) ?? this.GetAdjacentItemOrNull(null, up);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets a collection of columns that are editable in the order they are shown to the user
|
||||
/// </summary>
|
||||
protected List<OLVColumn> EditableColumnsInDisplayOrder {
|
||||
get {
|
||||
List<OLVColumn> editableColumnsInDisplayOrder = new List<OLVColumn>();
|
||||
foreach (OLVColumn x in this.ListView.ColumnsInDisplayOrder)
|
||||
if (x.IsEditable)
|
||||
editableColumnsInDisplayOrder.Add(x);
|
||||
return editableColumnsInDisplayOrder;
|
||||
}
|
||||
}
|
||||
|
||||
#endregion
|
||||
}
|
||||
}
|
||||
@@ -1,284 +0,0 @@
|
||||
/*
|
||||
* CellEditors - Several slightly modified controls that are used as celleditors within ObjectListView.
|
||||
*
|
||||
* Author: Phillip Piper
|
||||
* Date: 20/10/2008 5:15 PM
|
||||
*
|
||||
* Change log:
|
||||
* v2.6
|
||||
* 2012-08-02 JPP - Make most editors public so they can be reused/subclassed
|
||||
* v2.3
|
||||
* 2009-08-13 JPP - Standardized code formatting
|
||||
* v2.2.1
|
||||
* 2008-01-18 JPP - Added special handling for enums
|
||||
* 2008-01-16 JPP - Added EditorRegistry
|
||||
* v2.0.1
|
||||
* 2008-10-20 JPP - Separated from ObjectListView.cs
|
||||
*
|
||||
* Copyright (C) 2006-2014 Phillip Piper
|
||||
*
|
||||
* This program is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*
|
||||
* If you wish to use this code in a closed source application, please contact phillip.piper@gmail.com.
|
||||
*/
|
||||
|
||||
using System;
|
||||
using System.Collections;
|
||||
using System.Collections.Generic;
|
||||
using System.ComponentModel;
|
||||
using System.Drawing;
|
||||
using System.Reflection;
|
||||
using System.Windows.Forms;
|
||||
|
||||
namespace BrightIdeasSoftware
|
||||
{
|
||||
/// <summary>
|
||||
/// These items allow combo boxes to remember a value and its description.
|
||||
/// </summary>
|
||||
/// <remarks>
|
||||
///
|
||||
/// </remarks>
|
||||
/// <param name="key"></param>
|
||||
/// <param name="description"></param>
|
||||
public class ComboBoxItem(Object key, String description)
|
||||
{
|
||||
private readonly String description = description;
|
||||
|
||||
/// <summary>
|
||||
///
|
||||
/// </summary>
|
||||
public Object Key {
|
||||
get { return key; }
|
||||
}
|
||||
private readonly Object key = key;
|
||||
|
||||
/// <summary>
|
||||
/// Returns a string that represents the current object.
|
||||
/// </summary>
|
||||
/// <returns>
|
||||
/// A string that represents the current object.
|
||||
/// </returns>
|
||||
/// <filterpriority>2</filterpriority>
|
||||
public override string ToString() {
|
||||
return this.description;
|
||||
}
|
||||
}
|
||||
|
||||
//-----------------------------------------------------------------------
|
||||
// Cell editors
|
||||
// These classes are simple cell editors that make it easier to get and set
|
||||
// the value that the control is showing.
|
||||
// In many cases, you can intercept the CellEditStarting event to
|
||||
// change the characteristics of the editor. For example, changing
|
||||
// the acceptable range for a numeric editor or changing the strings
|
||||
// that respresent true and false values for a boolean editor.
|
||||
|
||||
/// <summary>
|
||||
/// This editor shows and auto completes values from the given listview column.
|
||||
/// </summary>
|
||||
[ToolboxItem(false)]
|
||||
public class AutoCompleteCellEditor : ComboBox
|
||||
{
|
||||
/// <summary>
|
||||
/// Create an AutoCompleteCellEditor
|
||||
/// </summary>
|
||||
/// <param name="lv"></param>
|
||||
/// <param name="column"></param>
|
||||
public AutoCompleteCellEditor(ObjectListView lv, OLVColumn column) {
|
||||
this.DropDownStyle = ComboBoxStyle.DropDown;
|
||||
|
||||
Dictionary<String, bool> alreadySeen = new Dictionary<string, bool>();
|
||||
for (int i = 0; i < Math.Min(lv.GetItemCount(), 1000); i++) {
|
||||
String str = column.GetStringValue(lv.GetModelObject(i));
|
||||
if (!alreadySeen.ContainsKey(str)) {
|
||||
this.Items.Add(str);
|
||||
alreadySeen[str] = true;
|
||||
}
|
||||
}
|
||||
|
||||
this.Sorted = true;
|
||||
this.AutoCompleteSource = AutoCompleteSource.ListItems;
|
||||
this.AutoCompleteMode = AutoCompleteMode.Append;
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// This combo box is specialised to allow editing of an enum.
|
||||
/// </summary>
|
||||
[ToolboxItem(false)]
|
||||
public class EnumCellEditor : ComboBox
|
||||
{
|
||||
/// <summary>
|
||||
///
|
||||
/// </summary>
|
||||
/// <param name="type"></param>
|
||||
public EnumCellEditor(Type type) {
|
||||
this.DropDownStyle = ComboBoxStyle.DropDownList;
|
||||
this.ValueMember = "Key";
|
||||
|
||||
ArrayList values = new ArrayList();
|
||||
foreach (object value in Enum.GetValues(type))
|
||||
values.Add(new ComboBoxItem(value, Enum.GetName(type, value)));
|
||||
|
||||
this.DataSource = values;
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// This editor simply shows and edits integer values.
|
||||
/// </summary>
|
||||
[ToolboxItem(false)]
|
||||
public class IntUpDown : NumericUpDown
|
||||
{
|
||||
/// <summary>
|
||||
///
|
||||
/// </summary>
|
||||
public IntUpDown() {
|
||||
this.DecimalPlaces = 0;
|
||||
this.Minimum = -9999999;
|
||||
this.Maximum = 9999999;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets the value shown by this editor
|
||||
/// </summary>
|
||||
new public int Value {
|
||||
get { return Decimal.ToInt32(base.Value); }
|
||||
set { base.Value = new Decimal(value); }
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// This editor simply shows and edits unsigned integer values.
|
||||
/// </summary>
|
||||
/// <remarks>This class can't be made public because unsigned int is not a
|
||||
/// CLS-compliant type. If you want to use, just copy the code to this class
|
||||
/// into your project and use it from there.</remarks>
|
||||
[ToolboxItem(false)]
|
||||
internal class UintUpDown : NumericUpDown
|
||||
{
|
||||
public UintUpDown() {
|
||||
this.DecimalPlaces = 0;
|
||||
this.Minimum = 0;
|
||||
this.Maximum = 9999999;
|
||||
}
|
||||
|
||||
new public uint Value {
|
||||
get { return Decimal.ToUInt32(base.Value); }
|
||||
set { base.Value = new Decimal(value); }
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// This editor simply shows and edits boolean values.
|
||||
/// </summary>
|
||||
[ToolboxItem(false)]
|
||||
public class BooleanCellEditor : ComboBox
|
||||
{
|
||||
/// <summary>
|
||||
///
|
||||
/// </summary>
|
||||
public BooleanCellEditor() {
|
||||
this.DropDownStyle = ComboBoxStyle.DropDownList;
|
||||
this.ValueMember = "Key";
|
||||
|
||||
ArrayList values = new ArrayList();
|
||||
values.Add(new ComboBoxItem(false, "False"));
|
||||
values.Add(new ComboBoxItem(true, "True"));
|
||||
|
||||
this.DataSource = values;
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// This editor simply shows and edits boolean values using a checkbox
|
||||
/// </summary>
|
||||
[ToolboxItem(false)]
|
||||
public class BooleanCellEditor2 : CheckBox
|
||||
{
|
||||
/// <summary>
|
||||
/// Gets or sets the value shown by this editor
|
||||
/// </summary>
|
||||
public bool? Value {
|
||||
get {
|
||||
switch (this.CheckState) {
|
||||
case CheckState.Checked: return true;
|
||||
case CheckState.Indeterminate: return null;
|
||||
case CheckState.Unchecked:
|
||||
default: return false;
|
||||
}
|
||||
}
|
||||
set {
|
||||
if (value.HasValue)
|
||||
this.CheckState = value.Value ? CheckState.Checked : CheckState.Unchecked;
|
||||
else
|
||||
this.CheckState = CheckState.Indeterminate;
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets how the checkbox will be aligned
|
||||
/// </summary>
|
||||
public new HorizontalAlignment TextAlign {
|
||||
get {
|
||||
switch (this.CheckAlign) {
|
||||
case ContentAlignment.MiddleRight: return HorizontalAlignment.Right;
|
||||
case ContentAlignment.MiddleCenter: return HorizontalAlignment.Center;
|
||||
case ContentAlignment.MiddleLeft:
|
||||
default: return HorizontalAlignment.Left;
|
||||
}
|
||||
}
|
||||
set {
|
||||
switch (value) {
|
||||
case HorizontalAlignment.Left:
|
||||
this.CheckAlign = ContentAlignment.MiddleLeft;
|
||||
break;
|
||||
case HorizontalAlignment.Center:
|
||||
this.CheckAlign = ContentAlignment.MiddleCenter;
|
||||
break;
|
||||
case HorizontalAlignment.Right:
|
||||
this.CheckAlign = ContentAlignment.MiddleRight;
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// This editor simply shows and edits floating point values.
|
||||
/// </summary>
|
||||
/// <remarks>You can intercept the CellEditStarting event if you want
|
||||
/// to change the characteristics of the editor. For example, by increasing
|
||||
/// the number of decimal places.</remarks>
|
||||
[ToolboxItem(false)]
|
||||
public class FloatCellEditor : NumericUpDown
|
||||
{
|
||||
/// <summary>
|
||||
///
|
||||
/// </summary>
|
||||
public FloatCellEditor() {
|
||||
this.DecimalPlaces = 2;
|
||||
this.Minimum = -9999999;
|
||||
this.Maximum = 9999999;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets the value shown by this editor
|
||||
/// </summary>
|
||||
new public double Value {
|
||||
get { return Convert.ToDouble(base.Value); }
|
||||
set { base.Value = Convert.ToDecimal(value); }
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,213 +0,0 @@
|
||||
/*
|
||||
* EditorRegistry - A registry mapping types to cell editors.
|
||||
*
|
||||
* Author: Phillip Piper
|
||||
* Date: 6-March-2011 7:53 am
|
||||
*
|
||||
* Change log:
|
||||
* 2011-03-31 JPP - Use OLVColumn.DataType if the value to be edited is null
|
||||
* 2011-03-06 JPP - Separated from CellEditors.cs
|
||||
*
|
||||
* Copyright (C) 2011-2014 Phillip Piper
|
||||
*
|
||||
* This program is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*
|
||||
* If you wish to use this code in a closed source application, please contact phillip.piper@gmail.com.
|
||||
*/
|
||||
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Text;
|
||||
using System.Windows.Forms;
|
||||
using System.Reflection;
|
||||
|
||||
namespace BrightIdeasSoftware {
|
||||
|
||||
/// <summary>
|
||||
/// A delegate that creates an editor for the given value
|
||||
/// </summary>
|
||||
/// <param name="model">The model from which that value came</param>
|
||||
/// <param name="column">The column for which the editor is being created</param>
|
||||
/// <param name="value">A representative value of the type to be edited. This value may not be the exact
|
||||
/// value for the column/model combination. It could be simply representative of
|
||||
/// the appropriate type of value.</param>
|
||||
/// <returns>A control which can edit the given value</returns>
|
||||
public delegate Control EditorCreatorDelegate(Object model, OLVColumn column, Object value);
|
||||
|
||||
/// <summary>
|
||||
/// An editor registry gives a way to decide what cell editor should be used to edit
|
||||
/// the value of a cell. Programmers can register non-standard types and the control that
|
||||
/// should be used to edit instances of that type.
|
||||
/// </summary>
|
||||
/// <remarks>
|
||||
/// <para>All ObjectListViews share the same editor registry.</para>
|
||||
/// </remarks>
|
||||
public class EditorRegistry {
|
||||
#region Initializing
|
||||
|
||||
/// <summary>
|
||||
/// Create an EditorRegistry
|
||||
/// </summary>
|
||||
public EditorRegistry() {
|
||||
this.InitializeStandardTypes();
|
||||
}
|
||||
|
||||
private void InitializeStandardTypes() {
|
||||
this.Register(typeof(Boolean), typeof(BooleanCellEditor));
|
||||
this.Register(typeof(Int16), typeof(IntUpDown));
|
||||
this.Register(typeof(Int32), typeof(IntUpDown));
|
||||
this.Register(typeof(Int64), typeof(IntUpDown));
|
||||
this.Register(typeof(UInt16), typeof(UintUpDown));
|
||||
this.Register(typeof(UInt32), typeof(UintUpDown));
|
||||
this.Register(typeof(UInt64), typeof(UintUpDown));
|
||||
this.Register(typeof(Single), typeof(FloatCellEditor));
|
||||
this.Register(typeof(Double), typeof(FloatCellEditor));
|
||||
this.Register(typeof(DateTime), delegate(Object model, OLVColumn column, Object value) {
|
||||
DateTimePicker c = new DateTimePicker();
|
||||
c.Format = DateTimePickerFormat.Short;
|
||||
return c;
|
||||
});
|
||||
this.Register(typeof(Boolean), delegate(Object model, OLVColumn column, Object value) {
|
||||
CheckBox c = new BooleanCellEditor2();
|
||||
c.ThreeState = column.TriStateCheckBoxes;
|
||||
return c;
|
||||
});
|
||||
}
|
||||
|
||||
#endregion
|
||||
|
||||
#region Registering
|
||||
|
||||
/// <summary>
|
||||
/// Register that values of 'type' should be edited by instances of 'controlType'.
|
||||
/// </summary>
|
||||
/// <param name="type">The type of value to be edited</param>
|
||||
/// <param name="controlType">The type of the Control that will edit values of 'type'</param>
|
||||
/// <example>
|
||||
/// ObjectListView.EditorRegistry.Register(typeof(Color), typeof(MySpecialColorEditor));
|
||||
/// </example>
|
||||
public void Register(Type type, Type controlType) {
|
||||
this.Register(type, delegate(Object model, OLVColumn column, Object value) {
|
||||
return controlType.InvokeMember("", BindingFlags.CreateInstance, null, null, null) as Control;
|
||||
});
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Register the given delegate so that it is called to create editors
|
||||
/// for values of the given type
|
||||
/// </summary>
|
||||
/// <param name="type">The type of value to be edited</param>
|
||||
/// <param name="creator">The delegate that will create a control that can edit values of 'type'</param>
|
||||
/// <example>
|
||||
/// ObjectListView.EditorRegistry.Register(typeof(Color), CreateColorEditor);
|
||||
/// ...
|
||||
/// public Control CreateColorEditor(Object model, OLVColumn column, Object value)
|
||||
/// {
|
||||
/// return new MySpecialColorEditor();
|
||||
/// }
|
||||
/// </example>
|
||||
public void Register(Type type, EditorCreatorDelegate creator) {
|
||||
this.creatorMap[type] = creator;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Register a delegate that will be called to create an editor for values
|
||||
/// that have not been handled.
|
||||
/// </summary>
|
||||
/// <param name="creator">The delegate that will create a editor for all other types</param>
|
||||
public void RegisterDefault(EditorCreatorDelegate creator) {
|
||||
this.defaultCreator = creator;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Register a delegate that will be given a chance to create a control
|
||||
/// before any other option is considered.
|
||||
/// </summary>
|
||||
/// <param name="creator">The delegate that will create a control</param>
|
||||
public void RegisterFirstChance(EditorCreatorDelegate creator) {
|
||||
this.firstChanceCreator = creator;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Remove the registered handler for the given type
|
||||
/// </summary>
|
||||
/// <remarks>Does nothing if the given type doesn't exist</remarks>
|
||||
/// <param name="type">The type whose registration is to be removed</param>
|
||||
public void Unregister(Type type) {
|
||||
if (this.creatorMap.ContainsKey(type))
|
||||
this.creatorMap.Remove(type);
|
||||
}
|
||||
|
||||
#endregion
|
||||
|
||||
#region Accessing
|
||||
|
||||
/// <summary>
|
||||
/// Create and return an editor that is appropriate for the given value.
|
||||
/// Return null if no appropriate editor can be found.
|
||||
/// </summary>
|
||||
/// <param name="model">The model involved</param>
|
||||
/// <param name="column">The column to be edited</param>
|
||||
/// <param name="value">The value to be edited. This value may not be the exact
|
||||
/// value for the column/model combination. It could be simply representative of
|
||||
/// the appropriate type of value.</param>
|
||||
/// <returns>A Control that can edit the given type of values</returns>
|
||||
public Control GetEditor(Object model, OLVColumn column, Object value) {
|
||||
Control editor;
|
||||
|
||||
// Give the first chance delegate a chance to decide
|
||||
if (this.firstChanceCreator != null) {
|
||||
editor = this.firstChanceCreator(model, column, value);
|
||||
if (editor != null)
|
||||
return editor;
|
||||
}
|
||||
|
||||
// Try to find a creator based on the type of the value (or the column)
|
||||
Type type = value == null ? column.DataType : value.GetType();
|
||||
if (type != null && this.creatorMap.ContainsKey(type)) {
|
||||
editor = this.creatorMap[type](model, column, value);
|
||||
if (editor != null)
|
||||
return editor;
|
||||
}
|
||||
|
||||
// Enums without other processing get a special editor
|
||||
if (value != null && value.GetType().IsEnum)
|
||||
return this.CreateEnumEditor(value.GetType());
|
||||
|
||||
// Give any default creator a final chance
|
||||
if (this.defaultCreator != null)
|
||||
return this.defaultCreator(model, column, value);
|
||||
|
||||
return null;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Create and return an editor that will edit values of the given type
|
||||
/// </summary>
|
||||
/// <param name="type">A enum type</param>
|
||||
protected Control CreateEnumEditor(Type type) {
|
||||
return new EnumCellEditor(type);
|
||||
}
|
||||
|
||||
#endregion
|
||||
|
||||
#region Private variables
|
||||
|
||||
private EditorCreatorDelegate firstChanceCreator;
|
||||
private EditorCreatorDelegate defaultCreator;
|
||||
private Dictionary<Type, EditorCreatorDelegate> creatorMap = new Dictionary<Type, EditorCreatorDelegate>();
|
||||
|
||||
#endregion
|
||||
}
|
||||
}
|
||||
@@ -1,46 +0,0 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<Dictionary>
|
||||
<Words>
|
||||
<Recognized>
|
||||
<Word>br</Word>
|
||||
<Word>Canceled</Word>
|
||||
<Word>Center</Word>
|
||||
<Word>Color</Word>
|
||||
<Word>Colors</Word>
|
||||
<Word>f</Word>
|
||||
<Word>fmt</Word>
|
||||
<Word>g</Word>
|
||||
<Word>gdi</Word>
|
||||
<Word>hti</Word>
|
||||
<Word>i</Word>
|
||||
<Word>lightbox</Word>
|
||||
<Word>lv</Word>
|
||||
<Word>lvi</Word>
|
||||
<Word>lvsi</Word>
|
||||
<Word>m</Word>
|
||||
<Word>multi</Word>
|
||||
<Word>Munger</Word>
|
||||
<Word>n</Word>
|
||||
<Word>olv</Word>
|
||||
<Word>olvi</Word>
|
||||
<Word>p</Word>
|
||||
<Word>parms</Word>
|
||||
<Word>r</Word>
|
||||
<Word>Renderer</Word>
|
||||
<Word>s</Word>
|
||||
<Word>SubItem</Word>
|
||||
<Word>Unapply</Word>
|
||||
<Word>Unpause</Word>
|
||||
<Word>x</Word>
|
||||
<Word>y</Word>
|
||||
</Recognized>
|
||||
<Deprecated>
|
||||
<Term PreferredAlternate="EnterpriseServices">ComPlus</Term>
|
||||
</Deprecated>
|
||||
</Words>
|
||||
<Acronyms>
|
||||
<CasingExceptions>
|
||||
<Acronym>OLV</Acronym>
|
||||
</CasingExceptions>
|
||||
</Acronyms>
|
||||
</Dictionary>
|
||||
@@ -1,236 +0,0 @@
|
||||
/*
|
||||
* DataListView - A data-bindable listview
|
||||
*
|
||||
* Author: Phillip Piper
|
||||
* Date: 27/09/2008 9:15 AM
|
||||
*
|
||||
* Change log:
|
||||
* 2015-02-02 JPP - Made Unfreezing more efficient by removing a redundant BuildList() call
|
||||
* v2.6
|
||||
* 2011-02-27 JPP - Moved most of the logic to DataSourceAdapter (where it
|
||||
* can be used by FastDataListView too)
|
||||
* v2.3
|
||||
* 2009-01-18 JPP - Boolean columns are now handled as checkboxes
|
||||
* - Auto-generated columns would fail if the data source was
|
||||
* reseated, even to the same data source
|
||||
* v2.0.1
|
||||
* 2009-01-07 JPP - Made all public and protected methods virtual
|
||||
* 2008-10-03 JPP - Separated from ObjectListView.cs
|
||||
*
|
||||
* Copyright (C) 2006-2015 Phillip Piper
|
||||
*
|
||||
* This program is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*
|
||||
* If you wish to use this code in a closed source application, please contact phillip.piper@gmail.com.
|
||||
*/
|
||||
|
||||
using System;
|
||||
using System.Collections;
|
||||
using System.ComponentModel;
|
||||
using System.Data;
|
||||
using System.Diagnostics;
|
||||
using System.Drawing.Design;
|
||||
using System.Windows.Forms;
|
||||
|
||||
namespace BrightIdeasSoftware
|
||||
{
|
||||
|
||||
/// <summary>
|
||||
/// A DataListView is a ListView that can be bound to a datasource (which would normally be a DataTable or DataView).
|
||||
/// </summary>
|
||||
/// <remarks>
|
||||
/// <para>This listview keeps itself in sync with its source datatable by listening for change events.</para>
|
||||
/// <para>The DataListView will automatically create columns to show all of the data source's columns/properties, if there is not already
|
||||
/// a column showing that property. This allows you to define one or two columns in the designer and then have the others generated automatically.
|
||||
/// If you don't want any column to be auto generated, set <see cref="AutoGenerateColumns"/> to false.
|
||||
/// These generated columns will be only the simplest view of the world, and would look more interesting with a few delegates installed.</para>
|
||||
/// <para>This listview will also automatically generate missing aspect getters to fetch the values from the data view.</para>
|
||||
/// <para>Changing data sources is possible, but error prone. Before changing data sources, the programmer is responsible for modifying/resetting
|
||||
/// the column collection to be valid for the new data source.</para>
|
||||
/// <para>Internally, a CurrencyManager controls keeping the data source in-sync with other users of the data source (as per normal .NET
|
||||
/// behavior). This means that the model objects in the DataListView are DataRowView objects. If you write your own AspectGetters/Setters,
|
||||
/// they will be given DataRowView objects.</para>
|
||||
/// </remarks>
|
||||
public class DataListView : ObjectListView
|
||||
{
|
||||
#region Life and death
|
||||
|
||||
/// <summary>
|
||||
/// Make a DataListView
|
||||
/// </summary>
|
||||
public DataListView()
|
||||
{
|
||||
this.Adapter = new DataSourceAdapter(this);
|
||||
}
|
||||
|
||||
protected override void Dispose(bool disposing) {
|
||||
this.Adapter.Dispose();
|
||||
base.Dispose(disposing);
|
||||
}
|
||||
|
||||
#endregion
|
||||
|
||||
#region Public Properties
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets whether or not columns will be automatically generated to show the
|
||||
/// columns when the DataSource is set.
|
||||
/// </summary>
|
||||
/// <remarks>This must be set before the DataSource is set. It has no effect afterwards.</remarks>
|
||||
[Category("Data"),
|
||||
Description("Should the control automatically generate columns from the DataSource"),
|
||||
DefaultValue(true)]
|
||||
public bool AutoGenerateColumns {
|
||||
get { return this.Adapter.AutoGenerateColumns; }
|
||||
set { this.Adapter.AutoGenerateColumns = value; }
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Get or set the DataSource that will be displayed in this list view.
|
||||
/// </summary>
|
||||
/// <remarks>The DataSource should implement either <see cref="IList"/>, <see cref="IBindingList"/>,
|
||||
/// or <see cref="IListSource"/>. Some common examples are the following types of objects:
|
||||
/// <list type="unordered">
|
||||
/// <item><description><see cref="DataView"/></description></item>
|
||||
/// <item><description><see cref="DataTable"/></description></item>
|
||||
/// <item><description><see cref="DataSet"/></description></item>
|
||||
/// <item><description><see cref="DataViewManager"/></description></item>
|
||||
/// <item><description><see cref="BindingSource"/></description></item>
|
||||
/// </list>
|
||||
/// <para>When binding to a list container (i.e. one that implements the
|
||||
/// <see cref="IListSource"/> interface, such as <see cref="DataSet"/>)
|
||||
/// you must also set the <see cref="DataMember"/> property in order
|
||||
/// to identify which particular list you would like to display. You
|
||||
/// may also set the <see cref="DataMember"/> property even when
|
||||
/// DataSource refers to a list, since <see cref="DataMember"/> can
|
||||
/// also be used to navigate relations between lists.</para>
|
||||
/// <para>When a DataSource is set, the control will create OLVColumns to show any
|
||||
/// data source columns that are not already shown.</para>
|
||||
/// <para>If the DataSource is changed, you will have to remove any previously
|
||||
/// created columns, since they will be configured for the previous DataSource.
|
||||
/// <see cref="ObjectListView.Reset()"/>.</para>
|
||||
/// </remarks>
|
||||
[Category("Data"),
|
||||
TypeConverter("System.Windows.Forms.Design.DataSourceConverter, System.Design")]
|
||||
public virtual Object DataSource
|
||||
{
|
||||
get { return this.Adapter.DataSource; }
|
||||
set { this.Adapter.DataSource = value; }
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets the name of the list or table in the data source for which the DataListView is displaying data.
|
||||
/// </summary>
|
||||
/// <remarks>If the data source is not a DataSet or DataViewManager, this property has no effect</remarks>
|
||||
[Category("Data"),
|
||||
Editor("System.Windows.Forms.Design.DataMemberListEditor, System.Design", typeof(UITypeEditor)),
|
||||
DefaultValue("")]
|
||||
public virtual string DataMember
|
||||
{
|
||||
get { return this.Adapter.DataMember; }
|
||||
set { this.Adapter.DataMember = value; }
|
||||
}
|
||||
|
||||
#endregion
|
||||
|
||||
#region Implementation properties
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets the DataSourceAdaptor that does the bulk of the work needed
|
||||
/// for data binding.
|
||||
/// </summary>
|
||||
/// <remarks>
|
||||
/// Adaptors cannot be shared between controls. Each DataListView needs its own adapter.
|
||||
/// </remarks>
|
||||
protected DataSourceAdapter Adapter {
|
||||
get {
|
||||
Debug.Assert(adapter != null, "Data adapter should not be null");
|
||||
return adapter;
|
||||
}
|
||||
set { adapter = value; }
|
||||
}
|
||||
private DataSourceAdapter adapter;
|
||||
|
||||
#endregion
|
||||
|
||||
#region Object manipulations
|
||||
|
||||
/// <summary>
|
||||
/// Add the given collection of model objects to this control.
|
||||
/// </summary>
|
||||
/// <param name="modelObjects">A collection of model objects</param>
|
||||
/// <remarks>This is a no-op for data lists, since the data
|
||||
/// is controlled by the DataSource. Manipulate the data source
|
||||
/// rather than this view of the data source.</remarks>
|
||||
public override void AddObjects(ICollection modelObjects)
|
||||
{
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Insert the given collection of objects before the given position
|
||||
/// </summary>
|
||||
/// <param name="index">Where to insert the objects</param>
|
||||
/// <param name="modelObjects">The objects to be inserted</param>
|
||||
/// <remarks>This is a no-op for data lists, since the data
|
||||
/// is controlled by the DataSource. Manipulate the data source
|
||||
/// rather than this view of the data source.</remarks>
|
||||
public override void InsertObjects(int index, ICollection modelObjects) {
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Remove the given collection of model objects from this control.
|
||||
/// </summary>
|
||||
/// <remarks>This is a no-op for data lists, since the data
|
||||
/// is controlled by the DataSource. Manipulate the data source
|
||||
/// rather than this view of the data source.</remarks>
|
||||
public override void RemoveObjects(ICollection modelObjects)
|
||||
{
|
||||
}
|
||||
|
||||
#endregion
|
||||
|
||||
#region Event Handlers
|
||||
|
||||
/// <summary>
|
||||
/// Change the Unfreeze behaviour
|
||||
/// </summary>
|
||||
protected override void DoUnfreeze() {
|
||||
|
||||
// Copied from base method, but we don't need to BuildList() since we know that our
|
||||
// data adaptor is going to do that immediately after this method exits.
|
||||
this.EndUpdate();
|
||||
this.ResizeFreeSpaceFillingColumns();
|
||||
// this.BuildList();
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Handles parent binding context changes
|
||||
/// </summary>
|
||||
/// <param name="e">Unused EventArgs.</param>
|
||||
protected override void OnParentBindingContextChanged(EventArgs e)
|
||||
{
|
||||
base.OnParentBindingContextChanged(e);
|
||||
|
||||
// BindingContext is an ambient property - by default it simply picks
|
||||
// up the parent control's context (unless something has explicitly
|
||||
// given us our own). So we must respond to changes in our parent's
|
||||
// binding context in the same way we would changes to our own
|
||||
// binding context.
|
||||
|
||||
// THINK: Do we need to forward this to the adapter?
|
||||
}
|
||||
|
||||
#endregion
|
||||
}
|
||||
}
|
||||
@@ -1,240 +0,0 @@
|
||||
/*
|
||||
* DataTreeListView - A data bindable TreeListView
|
||||
*
|
||||
* Author: Phillip Piper
|
||||
* Date: 05/05/2012 3:26 PM
|
||||
*
|
||||
* Change log:
|
||||
|
||||
* 2012-05-05 JPP Initial version
|
||||
*
|
||||
* TO DO:
|
||||
|
||||
*
|
||||
* Copyright (C) 2012 Phillip Piper
|
||||
*
|
||||
* This program is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*
|
||||
* If you wish to use this code in a closed source application, please contact phillip.piper@gmail.com.
|
||||
*/
|
||||
|
||||
using System;
|
||||
using System.Collections;
|
||||
using System.ComponentModel;
|
||||
using System.Data;
|
||||
using System.Diagnostics;
|
||||
using System.Drawing.Design;
|
||||
using System.Windows.Forms;
|
||||
|
||||
namespace BrightIdeasSoftware
|
||||
{
|
||||
/// <summary>
|
||||
/// A DataTreeListView is a TreeListView that calculates its hierarchy based on
|
||||
/// information in the data source.
|
||||
/// </summary>
|
||||
/// <remarks>
|
||||
/// <para>Like a <see cref="DataListView"/>, a DataTreeListView sources all its information
|
||||
/// from a combination of <see cref="DataSource"/> and <see cref="DataMember"/>.
|
||||
/// <see cref="DataSource"/> can be a DataTable, DataSet,
|
||||
/// or anything that implements <see cref="IList"/>.
|
||||
/// </para>
|
||||
/// <para>
|
||||
/// To function properly, the DataTreeListView requires:
|
||||
/// <list type="bullet">
|
||||
/// <item>the table to have a column which holds a unique for the row. The name of this column must be set in <see cref="KeyAspectName"/>.</item>
|
||||
/// <item>the table to have a column which holds id of the hierarchical parent of the row. The name of this column must be set in <see cref="ParentKeyAspectName"/>.</item>
|
||||
/// <item>a value which identifies which rows are the roots of the tree (<see cref="RootKeyValue"/>).</item>
|
||||
/// </list>
|
||||
/// The hierarchy structure is determined finding all the rows where the parent key is equal to <see cref="RootKeyValue"/>. These rows
|
||||
/// become the root objects of the hierarchy.
|
||||
/// </para>
|
||||
/// <para>Like a TreeListView, the hierarchy must not contain cycles. Bad things will happen if the data is cyclic.</para>
|
||||
/// </remarks>
|
||||
public partial class DataTreeListView : TreeListView
|
||||
{
|
||||
#region Public Properties
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets whether or not columns will be automatically generated to show the
|
||||
/// columns when the DataSource is set.
|
||||
/// </summary>
|
||||
/// <remarks>This must be set before the DataSource is set. It has no effect afterwards.</remarks>
|
||||
[Category("Data"),
|
||||
Description("Should the control automatically generate columns from the DataSource"),
|
||||
DefaultValue(true)]
|
||||
public bool AutoGenerateColumns
|
||||
{
|
||||
get { return this.Adapter.AutoGenerateColumns; }
|
||||
set { this.Adapter.AutoGenerateColumns = value; }
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Get or set the DataSource that will be displayed in this list view.
|
||||
/// </summary>
|
||||
/// <remarks>The DataSource should implement either <see cref="IList"/>, <see cref="IBindingList"/>,
|
||||
/// or <see cref="IListSource"/>. Some common examples are the following types of objects:
|
||||
/// <list type="unordered">
|
||||
/// <item><description><see cref="DataView"/></description></item>
|
||||
/// <item><description><see cref="DataTable"/></description></item>
|
||||
/// <item><description><see cref="DataSet"/></description></item>
|
||||
/// <item><description><see cref="DataViewManager"/></description></item>
|
||||
/// <item><description><see cref="BindingSource"/></description></item>
|
||||
/// </list>
|
||||
/// <para>When binding to a list container (i.e. one that implements the
|
||||
/// <see cref="IListSource"/> interface, such as <see cref="DataSet"/>)
|
||||
/// you must also set the <see cref="DataMember"/> property in order
|
||||
/// to identify which particular list you would like to display. You
|
||||
/// may also set the <see cref="DataMember"/> property even when
|
||||
/// DataSource refers to a list, since <see cref="DataMember"/> can
|
||||
/// also be used to navigate relations between lists.</para>
|
||||
/// </remarks>
|
||||
[Category("Data"),
|
||||
TypeConverter("System.Windows.Forms.Design.DataSourceConverter, System.Design")]
|
||||
public virtual Object DataSource {
|
||||
get { return this.Adapter.DataSource; }
|
||||
set { this.Adapter.DataSource = value; }
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets the name of the list or table in the data source for which the DataListView is displaying data.
|
||||
/// </summary>
|
||||
/// <remarks>If the data source is not a DataSet or DataViewManager, this property has no effect</remarks>
|
||||
[Category("Data"),
|
||||
Editor("System.Windows.Forms.Design.DataMemberListEditor, System.Design", typeof(UITypeEditor)),
|
||||
DefaultValue("")]
|
||||
public virtual string DataMember {
|
||||
get { return this.Adapter.DataMember; }
|
||||
set { this.Adapter.DataMember = value; }
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets the name of the property/column that uniquely identifies each row.
|
||||
/// </summary>
|
||||
/// <remarks>
|
||||
/// <para>
|
||||
/// The value contained by this column must be unique across all rows
|
||||
/// in the data source. Odd and unpredictable things will happen if two
|
||||
/// rows have the same id.
|
||||
/// </para>
|
||||
/// <para>Null cannot be a valid key value.</para>
|
||||
/// </remarks>
|
||||
[Category("Data"),
|
||||
Description("The name of the property/column that holds the key of a row"),
|
||||
DefaultValue(null)]
|
||||
public virtual string KeyAspectName {
|
||||
get { return this.Adapter.KeyAspectName; }
|
||||
set { this.Adapter.KeyAspectName = value; }
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets the name of the property/column that contains the key of
|
||||
/// the parent of a row.
|
||||
/// </summary>
|
||||
/// <remarks>
|
||||
/// <para>
|
||||
/// The test condition for deciding if one row is the parent of another is functionally
|
||||
/// equivilent to this:
|
||||
/// <code>
|
||||
/// Object.Equals(candidateParentRow[this.KeyAspectName], row[this.ParentKeyAspectName])
|
||||
/// </code>
|
||||
/// </para>
|
||||
/// <para>Unlike key value, parent keys can be null but a null parent key can only be used
|
||||
/// to identify root objects.</para>
|
||||
/// </remarks>
|
||||
[Category("Data"),
|
||||
Description("The name of the property/column that holds the key of the parent of a row"),
|
||||
DefaultValue(null)]
|
||||
public virtual string ParentKeyAspectName {
|
||||
get { return this.Adapter.ParentKeyAspectName; }
|
||||
set { this.Adapter.ParentKeyAspectName = value; }
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets the value that identifies a row as a root object.
|
||||
/// When the ParentKey of a row equals the RootKeyValue, that row will
|
||||
/// be treated as root of the TreeListView.
|
||||
/// </summary>
|
||||
/// <remarks>
|
||||
/// <para>
|
||||
/// The test condition for deciding a root object is functionally
|
||||
/// equivilent to this:
|
||||
/// <code>
|
||||
/// Object.Equals(candidateRow[this.ParentKeyAspectName], this.RootKeyValue)
|
||||
/// </code>
|
||||
/// </para>
|
||||
/// <para>The RootKeyValue can be null. Actually, it can be any value that can
|
||||
/// be compared for equality against a basic type.</para>
|
||||
/// <para>If this is set to the wrong value (i.e. to a value that no row
|
||||
/// has in the parent id column), the list will be empty.</para>
|
||||
/// </remarks>
|
||||
[Browsable(false),
|
||||
DesignerSerializationVisibility(DesignerSerializationVisibility.Hidden)]
|
||||
public virtual object RootKeyValue {
|
||||
get { return this.Adapter.RootKeyValue; }
|
||||
set { this.Adapter.RootKeyValue = value; }
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets the value that identifies a row as a root object.
|
||||
/// <see cref="RootKeyValue"/>. The RootKeyValue can be of any type,
|
||||
/// but the IDE cannot sensibly represent a value of any type,
|
||||
/// so this is a typed wrapper around that property.
|
||||
/// </summary>
|
||||
/// <remarks>
|
||||
/// If you want the root value to be something other than a string,
|
||||
/// you will have set it yourself.
|
||||
/// </remarks>
|
||||
[Category("Data"),
|
||||
Description("The parent id value that identifies a row as a root object"),
|
||||
DefaultValue(null)]
|
||||
public virtual string RootKeyValueString {
|
||||
get { return Convert.ToString(this.Adapter.RootKeyValue); }
|
||||
set { this.Adapter.RootKeyValue = value; }
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets whether or not the key columns (id and parent id) should
|
||||
/// be shown to the user.
|
||||
/// </summary>
|
||||
/// <remarks>This must be set before the DataSource is set. It has no effect
|
||||
/// afterwards.</remarks>
|
||||
[Category("Data"),
|
||||
Description("Should the keys columns (id and parent id) be shown to the user?"),
|
||||
DefaultValue(true)]
|
||||
public virtual bool ShowKeyColumns {
|
||||
get { return this.Adapter.ShowKeyColumns; }
|
||||
set { this.Adapter.ShowKeyColumns = value; }
|
||||
}
|
||||
|
||||
#endregion
|
||||
|
||||
#region Implementation properties
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets the DataSourceAdaptor that does the bulk of the work needed
|
||||
/// for data binding.
|
||||
/// </summary>
|
||||
protected TreeDataSourceAdapter Adapter {
|
||||
get {
|
||||
if (this.adapter == null)
|
||||
this.adapter = new TreeDataSourceAdapter(this);
|
||||
return adapter;
|
||||
}
|
||||
set { adapter = value; }
|
||||
}
|
||||
private TreeDataSourceAdapter adapter;
|
||||
|
||||
#endregion
|
||||
}
|
||||
}
|
||||
@@ -1,219 +0,0 @@
|
||||
/*
|
||||
* DragSource.cs - Add drag source functionality to an ObjectListView
|
||||
*
|
||||
* Author: Phillip Piper
|
||||
* Date: 2009-03-17 5:15 PM
|
||||
*
|
||||
* Change log:
|
||||
* 2011-03-29 JPP - Separate OLVDataObject.cs
|
||||
* v2.3
|
||||
* 2009-07-06 JPP - Make sure Link is acceptable as an drop effect by default
|
||||
* (since MS didn't make it part of the 'All' value)
|
||||
* v2.2
|
||||
* 2009-04-15 JPP - Separated DragSource.cs into DropSink.cs
|
||||
* 2009-03-17 JPP - Initial version
|
||||
*
|
||||
* Copyright (C) 2009-2014 Phillip Piper
|
||||
*
|
||||
* This program is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*
|
||||
* If you wish to use this code in a closed source application, please contact phillip.piper@gmail.com.
|
||||
*/
|
||||
|
||||
using System;
|
||||
using System.Collections;
|
||||
using System.Collections.Generic;
|
||||
using System.Text;
|
||||
using System.Windows.Forms;
|
||||
using System.Drawing;
|
||||
using System.Drawing.Drawing2D;
|
||||
|
||||
namespace BrightIdeasSoftware
|
||||
{
|
||||
/// <summary>
|
||||
/// An IDragSource controls how drag out from the ObjectListView will behave
|
||||
/// </summary>
|
||||
public interface IDragSource
|
||||
{
|
||||
/// <summary>
|
||||
/// A drag operation is beginning. Return the data object that will be used
|
||||
/// for data transfer. Return null to prevent the drag from starting. The data
|
||||
/// object will normally include all the selected objects.
|
||||
/// </summary>
|
||||
/// <remarks>
|
||||
/// The returned object is later passed to the GetAllowedEffect() and EndDrag()
|
||||
/// methods.
|
||||
/// </remarks>
|
||||
/// <param name="olv">What ObjectListView is being dragged from.</param>
|
||||
/// <param name="button">Which mouse button is down?</param>
|
||||
/// <param name="item">What item was directly dragged by the user? There may be more than just this
|
||||
/// item selected.</param>
|
||||
/// <returns>The data object that will be used for data transfer. This will often be a subclass
|
||||
/// of DataObject, but does not need to be.</returns>
|
||||
Object StartDrag(ObjectListView olv, MouseButtons button, OLVListItem item);
|
||||
|
||||
/// <summary>
|
||||
/// What operations are possible for this drag? This controls the icon shown during the drag
|
||||
/// </summary>
|
||||
/// <param name="dragObject">The data object returned by StartDrag()</param>
|
||||
/// <returns>A combination of DragDropEffects flags</returns>
|
||||
DragDropEffects GetAllowedEffects(Object dragObject);
|
||||
|
||||
/// <summary>
|
||||
/// The drag operation is complete. Do whatever is necessary to complete the action.
|
||||
/// </summary>
|
||||
/// <param name="dragObject">The data object returned by StartDrag()</param>
|
||||
/// <param name="effect">The value returned from GetAllowedEffects()</param>
|
||||
void EndDrag(Object dragObject, DragDropEffects effect);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// A do-nothing implementation of IDragSource that can be safely subclassed.
|
||||
/// </summary>
|
||||
public class AbstractDragSource : IDragSource
|
||||
{
|
||||
#region IDragSource Members
|
||||
|
||||
/// <summary>
|
||||
/// See IDragSource documentation
|
||||
/// </summary>
|
||||
/// <param name="olv"></param>
|
||||
/// <param name="button"></param>
|
||||
/// <param name="item"></param>
|
||||
/// <returns></returns>
|
||||
public virtual Object StartDrag(ObjectListView olv, MouseButtons button, OLVListItem item) {
|
||||
return null;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// See IDragSource documentation
|
||||
/// </summary>
|
||||
/// <param name="data"></param>
|
||||
/// <returns></returns>
|
||||
public virtual DragDropEffects GetAllowedEffects(Object data) {
|
||||
return DragDropEffects.None;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// See IDragSource documentation
|
||||
/// </summary>
|
||||
/// <param name="dragObject"></param>
|
||||
/// <param name="effect"></param>
|
||||
public virtual void EndDrag(Object dragObject, DragDropEffects effect) {
|
||||
}
|
||||
|
||||
#endregion
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// A reasonable implementation of IDragSource that provides normal
|
||||
/// drag source functionality. It creates a data object that supports
|
||||
/// inter-application dragging of text and HTML representation of
|
||||
/// the dragged rows. It can optionally force a refresh of all dragged
|
||||
/// rows when the drag is complete.
|
||||
/// </summary>
|
||||
/// <remarks>Subclasses can override GetDataObject() to add new
|
||||
/// data formats to the data transfer object.</remarks>
|
||||
public class SimpleDragSource : IDragSource
|
||||
{
|
||||
#region Constructors
|
||||
|
||||
/// <summary>
|
||||
/// Construct a SimpleDragSource
|
||||
/// </summary>
|
||||
public SimpleDragSource() {
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Construct a SimpleDragSource that refreshes the dragged rows when
|
||||
/// the drag is complete
|
||||
/// </summary>
|
||||
/// <param name="refreshAfterDrop"></param>
|
||||
public SimpleDragSource(bool refreshAfterDrop) {
|
||||
this.RefreshAfterDrop = refreshAfterDrop;
|
||||
}
|
||||
|
||||
#endregion
|
||||
|
||||
#region Public properties
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets whether the dragged rows should be refreshed when the
|
||||
/// drag operation is complete.
|
||||
/// </summary>
|
||||
public bool RefreshAfterDrop {
|
||||
get { return refreshAfterDrop; }
|
||||
set { refreshAfterDrop = value; }
|
||||
}
|
||||
private bool refreshAfterDrop;
|
||||
|
||||
#endregion
|
||||
|
||||
#region IDragSource Members
|
||||
|
||||
/// <summary>
|
||||
/// Create a DataObject when the user does a left mouse drag operation.
|
||||
/// See IDragSource for further information.
|
||||
/// </summary>
|
||||
/// <param name="olv"></param>
|
||||
/// <param name="button"></param>
|
||||
/// <param name="item"></param>
|
||||
/// <returns></returns>
|
||||
public virtual Object StartDrag(ObjectListView olv, MouseButtons button, OLVListItem item) {
|
||||
// We only drag on left mouse
|
||||
if (button != MouseButtons.Left)
|
||||
return null;
|
||||
|
||||
return this.CreateDataObject(olv);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Which operations are allowed in the operation? By default, all operations are supported.
|
||||
/// </summary>
|
||||
/// <param name="data"></param>
|
||||
/// <returns>All opertions are supported</returns>
|
||||
public virtual DragDropEffects GetAllowedEffects(Object data) {
|
||||
return DragDropEffects.All | DragDropEffects.Link; // why didn't MS include 'Link' in 'All'??
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// The drag operation is finished. Refreshe the dragged rows if so configured.
|
||||
/// </summary>
|
||||
/// <param name="dragObject"></param>
|
||||
/// <param name="effect"></param>
|
||||
public virtual void EndDrag(Object dragObject, DragDropEffects effect) {
|
||||
OLVDataObject data = dragObject as OLVDataObject;
|
||||
if (data == null)
|
||||
return;
|
||||
|
||||
if (this.RefreshAfterDrop)
|
||||
data.ListView.RefreshObjects(data.ModelObjects);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Create a data object that will be used to as the data object
|
||||
/// for the drag operation.
|
||||
/// </summary>
|
||||
/// <remarks>
|
||||
/// Subclasses can override this method add new formats to the data object.
|
||||
/// </remarks>
|
||||
/// <param name="olv">The ObjectListView that is the source of the drag</param>
|
||||
/// <returns>A data object for the drag</returns>
|
||||
protected virtual object CreateDataObject(ObjectListView olv) {
|
||||
return new OLVDataObject(olv);
|
||||
}
|
||||
|
||||
#endregion
|
||||
}
|
||||
}
|
||||
@@ -1,185 +0,0 @@
|
||||
/*
|
||||
* OLVDataObject.cs - An OLE DataObject that knows how to convert rows of an OLV to text and HTML
|
||||
*
|
||||
* Author: Phillip Piper
|
||||
* Date: 2011-03-29 3:34PM
|
||||
*
|
||||
* Change log:
|
||||
* v2.8
|
||||
* 2014-05-02 JPP - When the listview is completely empty, don't try to set CSV text in the clipboard.
|
||||
* v2.6
|
||||
* 2012-08-08 JPP - Changed to use OLVExporter.
|
||||
* - Added CSV to formats exported to Clipboard
|
||||
* v2.4
|
||||
* 2011-03-29 JPP - Initial version
|
||||
*
|
||||
* Copyright (C) 2011-2014 Phillip Piper
|
||||
*
|
||||
* This program is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*
|
||||
* If you wish to use this code in a closed source application, please contact phillip.piper@gmail.com.
|
||||
*/
|
||||
|
||||
using System;
|
||||
using System.Collections;
|
||||
using System.Windows.Forms;
|
||||
|
||||
namespace BrightIdeasSoftware {
|
||||
|
||||
/// <summary>
|
||||
/// A data transfer object that knows how to transform a list of model
|
||||
/// objects into a text and HTML representation.
|
||||
/// </summary>
|
||||
public class OLVDataObject : DataObject {
|
||||
#region Life and death
|
||||
|
||||
/// <summary>
|
||||
/// Create a data object from the selected objects in the given ObjectListView
|
||||
/// </summary>
|
||||
/// <param name="olv">The source of the data object</param>
|
||||
public OLVDataObject(ObjectListView olv)
|
||||
: this(olv, olv.SelectedObjects) {
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Create a data object which operates on the given model objects
|
||||
/// in the given ObjectListView
|
||||
/// </summary>
|
||||
/// <param name="olv">The source of the data object</param>
|
||||
/// <param name="modelObjects">The model objects to be put into the data object</param>
|
||||
public OLVDataObject(ObjectListView olv, IList modelObjects) {
|
||||
this.objectListView = olv;
|
||||
this.modelObjects = modelObjects;
|
||||
this.includeHiddenColumns = olv.IncludeHiddenColumnsInDataTransfer;
|
||||
this.includeColumnHeaders = olv.IncludeColumnHeadersInCopy;
|
||||
this.CreateTextFormats();
|
||||
}
|
||||
|
||||
#endregion
|
||||
|
||||
#region Properties
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets whether hidden columns will also be included in the text
|
||||
/// and HTML representation. If this is false, only visible columns will
|
||||
/// be included.
|
||||
/// </summary>
|
||||
public bool IncludeHiddenColumns {
|
||||
get { return includeHiddenColumns; }
|
||||
}
|
||||
private readonly bool includeHiddenColumns;
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets whether column headers will also be included in the text
|
||||
/// and HTML representation.
|
||||
/// </summary>
|
||||
public bool IncludeColumnHeaders {
|
||||
get { return includeColumnHeaders; }
|
||||
}
|
||||
private readonly bool includeColumnHeaders;
|
||||
|
||||
/// <summary>
|
||||
/// Gets the ObjectListView that is being used as the source of the data
|
||||
/// </summary>
|
||||
public ObjectListView ListView {
|
||||
get { return objectListView; }
|
||||
}
|
||||
private readonly ObjectListView objectListView;
|
||||
|
||||
/// <summary>
|
||||
/// Gets the model objects that are to be placed in the data object
|
||||
/// </summary>
|
||||
public IList ModelObjects {
|
||||
get { return modelObjects; }
|
||||
}
|
||||
private readonly IList modelObjects;
|
||||
|
||||
#endregion
|
||||
|
||||
/// <summary>
|
||||
/// Put a text and HTML representation of our model objects
|
||||
/// into the data object.
|
||||
/// </summary>
|
||||
public void CreateTextFormats() {
|
||||
|
||||
OLVExporter exporter = this.CreateExporter();
|
||||
|
||||
// Put both the text and html versions onto the clipboard.
|
||||
// For some reason, SetText() with UnicodeText doesn't set the basic CF_TEXT format,
|
||||
// but using SetData() does.
|
||||
//this.SetText(sbText.ToString(), TextDataFormat.UnicodeText);
|
||||
this.SetData(exporter.ExportTo(OLVExporter.ExportFormat.TabSeparated));
|
||||
string exportTo = exporter.ExportTo(OLVExporter.ExportFormat.CSV);
|
||||
if (!String.IsNullOrEmpty(exportTo))
|
||||
this.SetText(exportTo, TextDataFormat.CommaSeparatedValue);
|
||||
this.SetText(ConvertToHtmlFragment(exporter.ExportTo(OLVExporter.ExportFormat.HTML)), TextDataFormat.Html);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Create an exporter for the data contained in this object
|
||||
/// </summary>
|
||||
/// <returns></returns>
|
||||
protected OLVExporter CreateExporter() {
|
||||
OLVExporter exporter = new OLVExporter(this.ListView);
|
||||
exporter.IncludeColumnHeaders = this.IncludeColumnHeaders;
|
||||
exporter.IncludeHiddenColumns = this.IncludeHiddenColumns;
|
||||
exporter.ModelObjects = this.ModelObjects;
|
||||
return exporter;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Make a HTML representation of our model objects
|
||||
/// </summary>
|
||||
[Obsolete("Use OLVExporter directly instead", false)]
|
||||
public string CreateHtml() {
|
||||
OLVExporter exporter = this.CreateExporter();
|
||||
return exporter.ExportTo(OLVExporter.ExportFormat.HTML);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Convert the fragment of HTML into the Clipboards HTML format.
|
||||
/// </summary>
|
||||
/// <remarks>The HTML format is found here http://msdn2.microsoft.com/en-us/library/aa767917.aspx
|
||||
/// </remarks>
|
||||
/// <param name="fragment">The HTML to put onto the clipboard. It must be valid HTML!</param>
|
||||
/// <returns>A string that can be put onto the clipboard and will be recognized as HTML</returns>
|
||||
private string ConvertToHtmlFragment(string fragment) {
|
||||
// Minimal implementation of HTML clipboard format
|
||||
const string SOURCE = "http://www.codeproject.com/Articles/16009/A-Much-Easier-to-Use-ListView";
|
||||
|
||||
const String MARKER_BLOCK =
|
||||
"Version:1.0\r\n" +
|
||||
"StartHTML:{0,8}\r\n" +
|
||||
"EndHTML:{1,8}\r\n" +
|
||||
"StartFragment:{2,8}\r\n" +
|
||||
"EndFragment:{3,8}\r\n" +
|
||||
"StartSelection:{2,8}\r\n" +
|
||||
"EndSelection:{3,8}\r\n" +
|
||||
"SourceURL:{4}\r\n" +
|
||||
"{5}";
|
||||
|
||||
int prefixLength = String.Format(MARKER_BLOCK, 0, 0, 0, 0, SOURCE, "").Length;
|
||||
|
||||
const String DEFAULT_HTML_BODY =
|
||||
"<!DOCTYPE HTML PUBLIC \"-//W3C//DTD HTML 4.0 Transitional//EN\">" +
|
||||
"<HTML><HEAD></HEAD><BODY><!--StartFragment-->{0}<!--EndFragment--></BODY></HTML>";
|
||||
|
||||
string html = String.Format(DEFAULT_HTML_BODY, fragment);
|
||||
int startFragment = prefixLength + html.IndexOf(fragment, StringComparison.Ordinal);
|
||||
int endFragment = startFragment + fragment.Length;
|
||||
|
||||
return String.Format(MARKER_BLOCK, prefixLength, prefixLength + html.Length, startFragment, endFragment, SOURCE, html);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,165 +0,0 @@
|
||||
/*
|
||||
* FastDataListView - A data bindable listview that has the speed of a virtual list
|
||||
*
|
||||
* Author: Phillip Piper
|
||||
* Date: 22/09/2010 8:11 AM
|
||||
*
|
||||
* Change log:
|
||||
* 2015-02-02 JPP - Made Unfreezing more efficient by removing a redundant BuildList() call
|
||||
* v2.6
|
||||
* 2010-09-22 JPP - Initial version
|
||||
*
|
||||
* Copyright (C) 2006-2015 Phillip Piper
|
||||
*
|
||||
* This program is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*
|
||||
* If you wish to use this code in a closed source application, please contact phillip.piper@gmail.com.
|
||||
*/
|
||||
|
||||
using System;
|
||||
using System.Collections;
|
||||
using System.Collections.Generic;
|
||||
using System.Data;
|
||||
using System.ComponentModel;
|
||||
using System.Windows.Forms;
|
||||
using System.Drawing.Design;
|
||||
|
||||
namespace BrightIdeasSoftware
|
||||
{
|
||||
/// <summary>
|
||||
/// A FastDataListView virtualizes the display of data from a DataSource. It operates on
|
||||
/// DataSets and DataTables in the same way as a DataListView, but does so much more efficiently.
|
||||
/// </summary>
|
||||
/// <remarks>
|
||||
/// <para>
|
||||
/// A FastDataListView still has to load all its data from the DataSource. If you have SQL statement
|
||||
/// that returns 1 million rows, all 1 million rows will still need to read from the database.
|
||||
/// However, once the rows are loaded, the FastDataListView will only build rows as they are displayed.
|
||||
/// </para>
|
||||
/// </remarks>
|
||||
public class FastDataListView : FastObjectListView
|
||||
{
|
||||
protected override void Dispose(bool disposing)
|
||||
{
|
||||
if (this.adapter != null) {
|
||||
this.adapter.Dispose();
|
||||
this.adapter = null;
|
||||
}
|
||||
|
||||
base.Dispose(disposing);
|
||||
}
|
||||
|
||||
#region Public Properties
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets whether or not columns will be automatically generated to show the
|
||||
/// columns when the DataSource is set.
|
||||
/// </summary>
|
||||
/// <remarks>This must be set before the DataSource is set. It has no effect afterwards.</remarks>
|
||||
[Category("Data"),
|
||||
Description("Should the control automatically generate columns from the DataSource"),
|
||||
DefaultValue(true)]
|
||||
public bool AutoGenerateColumns
|
||||
{
|
||||
get { return this.Adapter.AutoGenerateColumns; }
|
||||
set { this.Adapter.AutoGenerateColumns = value; }
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Get or set the VirtualListDataSource that will be displayed in this list view.
|
||||
/// </summary>
|
||||
/// <remarks>The VirtualListDataSource should implement either <see cref="IList"/>, <see cref="IBindingList"/>,
|
||||
/// or <see cref="IListSource"/>. Some common examples are the following types of objects:
|
||||
/// <list type="unordered">
|
||||
/// <item><description><see cref="DataView"/></description></item>
|
||||
/// <item><description><see cref="DataTable"/></description></item>
|
||||
/// <item><description><see cref="DataSet"/></description></item>
|
||||
/// <item><description><see cref="DataViewManager"/></description></item>
|
||||
/// <item><description><see cref="BindingSource"/></description></item>
|
||||
/// </list>
|
||||
/// <para>When binding to a list container (i.e. one that implements the
|
||||
/// <see cref="IListSource"/> interface, such as <see cref="DataSet"/>)
|
||||
/// you must also set the <see cref="DataMember"/> property in order
|
||||
/// to identify which particular list you would like to display. You
|
||||
/// may also set the <see cref="DataMember"/> property even when
|
||||
/// VirtualListDataSource refers to a list, since <see cref="DataMember"/> can
|
||||
/// also be used to navigate relations between lists.</para>
|
||||
/// </remarks>
|
||||
[Category("Data"),
|
||||
TypeConverter("System.Windows.Forms.Design.DataSourceConverter, System.Design")]
|
||||
public virtual Object DataSource {
|
||||
get { return this.Adapter.DataSource; }
|
||||
set { this.Adapter.DataSource = value; }
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets the name of the list or table in the data source for which the DataListView is displaying data.
|
||||
/// </summary>
|
||||
/// <remarks>If the data source is not a DataSet or DataViewManager, this property has no effect</remarks>
|
||||
[Category("Data"),
|
||||
Editor("System.Windows.Forms.Design.DataMemberListEditor, System.Design", typeof(UITypeEditor)),
|
||||
DefaultValue("")]
|
||||
public virtual string DataMember {
|
||||
get { return this.Adapter.DataMember; }
|
||||
set { this.Adapter.DataMember = value; }
|
||||
}
|
||||
|
||||
#endregion
|
||||
|
||||
#region Implementation properties
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets the DataSourceAdaptor that does the bulk of the work needed
|
||||
/// for data binding.
|
||||
/// </summary>
|
||||
protected DataSourceAdapter Adapter {
|
||||
get {
|
||||
if (adapter == null)
|
||||
adapter = this.CreateDataSourceAdapter();
|
||||
return adapter;
|
||||
}
|
||||
set { adapter = value; }
|
||||
}
|
||||
private DataSourceAdapter adapter;
|
||||
|
||||
#endregion
|
||||
|
||||
#region Implementation
|
||||
|
||||
/// <summary>
|
||||
/// Create the DataSourceAdapter that this control will use.
|
||||
/// </summary>
|
||||
/// <returns>A DataSourceAdapter configured for this list</returns>
|
||||
/// <remarks>Subclasses should override this to create their
|
||||
/// own specialized adapters</remarks>
|
||||
protected virtual DataSourceAdapter CreateDataSourceAdapter() {
|
||||
return new DataSourceAdapter(this);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Change the Unfreeze behaviour
|
||||
/// </summary>
|
||||
protected override void DoUnfreeze()
|
||||
{
|
||||
|
||||
// Copied from base method, but we don't need to BuildList() since we know that our
|
||||
// data adaptor is going to do that immediately after this method exits.
|
||||
this.EndUpdate();
|
||||
this.ResizeFreeSpaceFillingColumns();
|
||||
// this.BuildList();
|
||||
}
|
||||
|
||||
#endregion
|
||||
}
|
||||
}
|
||||
@@ -1,419 +0,0 @@
|
||||
/*
|
||||
* FastObjectListView - A listview that behaves like an ObjectListView but has the speed of a virtual list
|
||||
*
|
||||
* Author: Phillip Piper
|
||||
* Date: 27/09/2008 9:15 AM
|
||||
*
|
||||
* Change log:
|
||||
* 2014-10-15 JPP - Fire Filter event when applying filters
|
||||
* v2.8
|
||||
* 2012-06-11 JPP - Added more efficient version of FilteredObjects
|
||||
* v2.5.1
|
||||
* 2011-04-25 JPP - Fixed problem with removing objects from filtered or sorted list
|
||||
* v2.4
|
||||
* 2010-04-05 JPP - Added filtering
|
||||
* v2.3
|
||||
* 2009-08-27 JPP - Added GroupingStrategy
|
||||
* - Added optimized Objects property
|
||||
* v2.2.1
|
||||
* 2009-01-07 JPP - Made all public and protected methods virtual
|
||||
* 2008-09-27 JPP - Separated from ObjectListView.cs
|
||||
*
|
||||
* Copyright (C) 2006-2014 Phillip Piper
|
||||
*
|
||||
* This program is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*
|
||||
* If you wish to use this code in a closed source application, please contact phillip.piper@gmail.com.
|
||||
*/
|
||||
|
||||
using System;
|
||||
using System.Collections;
|
||||
using System.Collections.Generic;
|
||||
using System.ComponentModel;
|
||||
using System.Windows.Forms;
|
||||
|
||||
namespace BrightIdeasSoftware
|
||||
{
|
||||
/// <summary>
|
||||
/// A FastObjectListView trades function for speed.
|
||||
/// </summary>
|
||||
/// <remarks>
|
||||
/// <para>On my mid-range laptop, this view builds a list of 10,000 objects in 0.1 seconds,
|
||||
/// as opposed to a normal ObjectListView which takes 10-15 seconds. Lists of up to 50,000 items should be
|
||||
/// able to be handled with sub-second response times even on low end machines.</para>
|
||||
/// <para>
|
||||
/// A FastObjectListView is implemented as a virtual list with many of the virtual modes limits (e.g. no sorting)
|
||||
/// fixed through coding. There are some functions that simply cannot be provided. Specifically, a FastObjectListView cannot:
|
||||
/// <list type="bullet">
|
||||
/// <item><description>use Tile view</description></item>
|
||||
/// <item><description>show groups on XP</description></item>
|
||||
/// </list>
|
||||
/// </para>
|
||||
/// </remarks>
|
||||
public class FastObjectListView : VirtualObjectListView
|
||||
{
|
||||
/// <summary>
|
||||
/// Make a FastObjectListView
|
||||
/// </summary>
|
||||
public FastObjectListView() {
|
||||
this.VirtualListDataSource = new FastObjectListDataSource(this);
|
||||
this.GroupingStrategy = new FastListGroupingStrategy();
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets the collection of objects that survive any filtering that may be in place.
|
||||
/// </summary>
|
||||
[Browsable(false),
|
||||
DesignerSerializationVisibility(DesignerSerializationVisibility.Hidden)]
|
||||
public override IEnumerable FilteredObjects {
|
||||
get {
|
||||
// This is much faster than the base method
|
||||
return ((FastObjectListDataSource)this.VirtualListDataSource).FilteredObjectList;
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Get/set the collection of objects that this list will show
|
||||
/// </summary>
|
||||
/// <remarks>
|
||||
/// <para>
|
||||
/// The contents of the control will be updated immediately after setting this property.
|
||||
/// </para>
|
||||
/// <para>This method preserves selection, if possible. Use SetObjects() if
|
||||
/// you do not want to preserve the selection. Preserving selection is the slowest part of this
|
||||
/// code and performance is O(n) where n is the number of selected rows.</para>
|
||||
/// <para>This method is not thread safe.</para>
|
||||
/// </remarks>
|
||||
[Browsable(false),
|
||||
DesignerSerializationVisibility(DesignerSerializationVisibility.Hidden)]
|
||||
public override IEnumerable Objects {
|
||||
get {
|
||||
// This is much faster than the base method
|
||||
return ((FastObjectListDataSource)this.VirtualListDataSource).ObjectList;
|
||||
}
|
||||
set { base.Objects = value; }
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Move the given collection of objects to the given index.
|
||||
/// </summary>
|
||||
/// <remarks>This operation only makes sense on non-grouped ObjectListViews.</remarks>
|
||||
/// <param name="index"></param>
|
||||
/// <param name="modelObjects"></param>
|
||||
public override void MoveObjects(int index, ICollection modelObjects) {
|
||||
if (this.InvokeRequired) {
|
||||
this.Invoke((MethodInvoker)delegate() { this.MoveObjects(index, modelObjects); });
|
||||
return;
|
||||
}
|
||||
|
||||
// If any object that is going to be moved is before the point where the insertion
|
||||
// will occur, then we have to reduce the location of our insertion point
|
||||
int displacedObjectCount = 0;
|
||||
foreach (object modelObject in modelObjects) {
|
||||
int i = this.IndexOf(modelObject);
|
||||
if (i >= 0 && i <= index)
|
||||
displacedObjectCount++;
|
||||
}
|
||||
index -= displacedObjectCount;
|
||||
|
||||
this.BeginUpdate();
|
||||
try {
|
||||
this.RemoveObjects(modelObjects);
|
||||
this.InsertObjects(index, modelObjects);
|
||||
}
|
||||
finally {
|
||||
this.EndUpdate();
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Remove any sorting and revert to the given order of the model objects
|
||||
/// </summary>
|
||||
/// <remarks>To be really honest, Unsort() doesn't work on FastObjectListViews since
|
||||
/// the original ordering of model objects is lost when Sort() is called. So this method
|
||||
/// effectively just turns off sorting.</remarks>
|
||||
public override void Unsort() {
|
||||
this.ShowGroups = false;
|
||||
this.PrimarySortColumn = null;
|
||||
this.PrimarySortOrder = SortOrder.None;
|
||||
this.SetObjects(this.Objects);
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Provide a data source for a FastObjectListView
|
||||
/// </summary>
|
||||
/// <remarks>
|
||||
/// This class isn't intended to be used directly, but it is left as a public
|
||||
/// class just in case someone wants to subclass it.
|
||||
/// </remarks>
|
||||
/// <remarks>
|
||||
/// Create a FastObjectListDataSource
|
||||
/// </remarks>
|
||||
/// <param name="listView"></param>
|
||||
public class FastObjectListDataSource(FastObjectListView listView) : AbstractVirtualListDataSource(listView)
|
||||
{
|
||||
|
||||
#region IVirtualListDataSource Members
|
||||
|
||||
/// <summary>
|
||||
/// Get n'th object
|
||||
/// </summary>
|
||||
/// <param name="n"></param>
|
||||
/// <returns></returns>
|
||||
public override object GetNthObject(int n) {
|
||||
if (n >= 0 && n < this.filteredObjectList.Count)
|
||||
return this.filteredObjectList[n];
|
||||
|
||||
return null;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// How many items are in the data source
|
||||
/// </summary>
|
||||
/// <returns></returns>
|
||||
public override int GetObjectCount() {
|
||||
return this.filteredObjectList.Count;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Get the index of the given model
|
||||
/// </summary>
|
||||
/// <param name="model"></param>
|
||||
/// <returns></returns>
|
||||
public override int GetObjectIndex(object model) {
|
||||
int index;
|
||||
|
||||
if (model != null && this.objectsToIndexMap.TryGetValue(model, out index))
|
||||
return index;
|
||||
|
||||
return -1;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
///
|
||||
/// </summary>
|
||||
/// <param name="text"></param>
|
||||
/// <param name="first"></param>
|
||||
/// <param name="last"></param>
|
||||
/// <param name="column"></param>
|
||||
/// <returns></returns>
|
||||
public override int SearchText(string text, int first, int last, OLVColumn column) {
|
||||
if (first <= last) {
|
||||
for (int i = first; i <= last; i++) {
|
||||
string data = column.GetStringValue(this.listView.GetNthItemInDisplayOrder(i).RowObject);
|
||||
if (data.StartsWith(text, StringComparison.CurrentCultureIgnoreCase))
|
||||
return i;
|
||||
}
|
||||
} else {
|
||||
for (int i = first; i >= last; i--) {
|
||||
string data = column.GetStringValue(this.listView.GetNthItemInDisplayOrder(i).RowObject);
|
||||
if (data.StartsWith(text, StringComparison.CurrentCultureIgnoreCase))
|
||||
return i;
|
||||
}
|
||||
}
|
||||
|
||||
return -1;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
///
|
||||
/// </summary>
|
||||
/// <param name="column"></param>
|
||||
/// <param name="sortOrder"></param>
|
||||
public override void Sort(OLVColumn column, SortOrder sortOrder) {
|
||||
if (sortOrder != SortOrder.None) {
|
||||
ModelObjectComparer comparer = new ModelObjectComparer(column, sortOrder, this.listView.SecondarySortColumn, this.listView.SecondarySortOrder);
|
||||
this.fullObjectList.Sort(comparer);
|
||||
this.filteredObjectList.Sort(comparer);
|
||||
}
|
||||
this.RebuildIndexMap();
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
///
|
||||
/// </summary>
|
||||
/// <param name="modelObjects"></param>
|
||||
public override void AddObjects(ICollection modelObjects) {
|
||||
foreach (object modelObject in modelObjects) {
|
||||
if (modelObject != null)
|
||||
this.fullObjectList.Add(modelObject);
|
||||
}
|
||||
this.FilterObjects();
|
||||
this.RebuildIndexMap();
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
///
|
||||
/// </summary>
|
||||
/// <param name="index"></param>
|
||||
/// <param name="modelObjects"></param>
|
||||
public override void InsertObjects(int index, ICollection modelObjects) {
|
||||
this.fullObjectList.InsertRange(index, modelObjects);
|
||||
this.FilterObjects();
|
||||
this.RebuildIndexMap();
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Remove the given collection of models from this source.
|
||||
/// </summary>
|
||||
/// <param name="modelObjects"></param>
|
||||
public override void RemoveObjects(ICollection modelObjects) {
|
||||
|
||||
// We have to unselect any object that is about to be deleted
|
||||
List<int> indicesToRemove = new List<int>();
|
||||
foreach (object modelObject in modelObjects) {
|
||||
int i = this.GetObjectIndex(modelObject);
|
||||
if (i >= 0)
|
||||
indicesToRemove.Add(i);
|
||||
}
|
||||
|
||||
// Sort the indices from highest to lowest so that we
|
||||
// remove latter ones before earlier ones. In this way, the
|
||||
// indices of the rows doesn't change after the deletes.
|
||||
indicesToRemove.Sort();
|
||||
indicesToRemove.Reverse();
|
||||
|
||||
foreach (int i in indicesToRemove)
|
||||
this.listView.SelectedIndices.Remove(i);
|
||||
|
||||
// Remove the objects from the unfiltered list
|
||||
foreach (object modelObject in modelObjects)
|
||||
this.fullObjectList.Remove(modelObject);
|
||||
|
||||
this.FilterObjects();
|
||||
this.RebuildIndexMap();
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
///
|
||||
/// </summary>
|
||||
/// <param name="collection"></param>
|
||||
public override void SetObjects(IEnumerable collection) {
|
||||
ArrayList newObjects = ObjectListView.EnumerableToArray(collection, true);
|
||||
|
||||
this.fullObjectList = newObjects;
|
||||
this.FilterObjects();
|
||||
this.RebuildIndexMap();
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Update/replace the nth object with the given object
|
||||
/// </summary>
|
||||
/// <param name="index"></param>
|
||||
/// <param name="modelObject"></param>
|
||||
public override void UpdateObject(int index, object modelObject) {
|
||||
if (index < 0 || index >= this.filteredObjectList.Count)
|
||||
return;
|
||||
|
||||
int i = this.fullObjectList.IndexOf(this.filteredObjectList[index]);
|
||||
if (i < 0)
|
||||
return;
|
||||
|
||||
if (ReferenceEquals(this.fullObjectList[i], modelObject))
|
||||
return;
|
||||
|
||||
this.fullObjectList[i] = modelObject;
|
||||
this.filteredObjectList[index] = modelObject;
|
||||
this.objectsToIndexMap[modelObject] = index;
|
||||
}
|
||||
|
||||
private ArrayList fullObjectList = new ArrayList();
|
||||
private ArrayList filteredObjectList = new ArrayList();
|
||||
private IModelFilter modelFilter;
|
||||
private IListFilter listFilter;
|
||||
|
||||
#endregion
|
||||
|
||||
#region IFilterableDataSource Members
|
||||
|
||||
/// <summary>
|
||||
/// Apply the given filters to this data source. One or both may be null.
|
||||
/// </summary>
|
||||
/// <param name="iModelFilter"></param>
|
||||
/// <param name="iListFilter"></param>
|
||||
public override void ApplyFilters(IModelFilter iModelFilter, IListFilter iListFilter) {
|
||||
this.modelFilter = iModelFilter;
|
||||
this.listFilter = iListFilter;
|
||||
this.SetObjects(this.fullObjectList);
|
||||
}
|
||||
|
||||
#endregion
|
||||
|
||||
#region Implementation
|
||||
|
||||
/// <summary>
|
||||
/// Gets the full list of objects being used for this fast list.
|
||||
/// This list is unfiltered.
|
||||
/// </summary>
|
||||
public ArrayList ObjectList {
|
||||
get { return fullObjectList; }
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets the list of objects from ObjectList which survive any installed filters.
|
||||
/// </summary>
|
||||
public ArrayList FilteredObjectList {
|
||||
get { return filteredObjectList; }
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Rebuild the map that remembers which model object is displayed at which line
|
||||
/// </summary>
|
||||
protected void RebuildIndexMap() {
|
||||
this.objectsToIndexMap.Clear();
|
||||
for (int i = 0; i < this.filteredObjectList.Count; i++)
|
||||
this.objectsToIndexMap[this.filteredObjectList[i]] = i;
|
||||
}
|
||||
readonly Dictionary<Object, int> objectsToIndexMap = new Dictionary<Object, int>();
|
||||
|
||||
/// <summary>
|
||||
/// Build our filtered list from our full list.
|
||||
/// </summary>
|
||||
protected void FilterObjects() {
|
||||
|
||||
// If this list isn't filtered, we don't need to do anything else
|
||||
if (!this.listView.UseFiltering) {
|
||||
this.filteredObjectList = new ArrayList(this.fullObjectList);
|
||||
return;
|
||||
}
|
||||
|
||||
// Tell the world to filter the objects. If they do so, don't do anything else
|
||||
// ReSharper disable PossibleMultipleEnumeration
|
||||
FilterEventArgs args = new FilterEventArgs(this.fullObjectList);
|
||||
this.listView.OnFilter(args);
|
||||
if (args.FilteredObjects != null) {
|
||||
this.filteredObjectList = ObjectListView.EnumerableToArray(args.FilteredObjects, false);
|
||||
return;
|
||||
}
|
||||
|
||||
IEnumerable objects = (this.listFilter == null) ?
|
||||
this.fullObjectList : this.listFilter.Filter(this.fullObjectList);
|
||||
|
||||
// Apply the object filter if there is one
|
||||
if (this.modelFilter == null) {
|
||||
this.filteredObjectList = ObjectListView.EnumerableToArray(objects, false);
|
||||
} else {
|
||||
this.filteredObjectList = new ArrayList();
|
||||
foreach (object model in objects) {
|
||||
if (this.modelFilter.Filter(model))
|
||||
this.filteredObjectList.Add(model);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#endregion
|
||||
}
|
||||
|
||||
}
|
||||
@@ -1,125 +0,0 @@
|
||||
/*
|
||||
* Cluster - Implements a simple cluster
|
||||
*
|
||||
* Author: Phillip Piper
|
||||
* Date: 3-March-2011 10:53 pm
|
||||
*
|
||||
* Change log:
|
||||
* 2011-03-03 JPP - First version
|
||||
*
|
||||
* Copyright (C) 2011-2014 Phillip Piper
|
||||
*
|
||||
* This program is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*
|
||||
* If you wish to use this code in a closed source application, please contact phillip.piper@gmail.com.
|
||||
*/
|
||||
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Text;
|
||||
|
||||
namespace BrightIdeasSoftware {
|
||||
|
||||
/// <summary>
|
||||
/// Concrete implementation of the ICluster interface.
|
||||
/// </summary>
|
||||
public class Cluster : ICluster {
|
||||
|
||||
#region Life and death
|
||||
|
||||
/// <summary>
|
||||
/// Create a cluster
|
||||
/// </summary>
|
||||
/// <param name="key">The key for the cluster</param>
|
||||
public Cluster(object key) {
|
||||
this.Count = 1;
|
||||
this.ClusterKey = key;
|
||||
}
|
||||
|
||||
#endregion
|
||||
|
||||
#region Public overrides
|
||||
|
||||
/// <summary>
|
||||
/// Return a string representation of this cluster
|
||||
/// </summary>
|
||||
/// <returns></returns>
|
||||
public override string ToString() {
|
||||
return this.DisplayLabel ?? "[empty]";
|
||||
}
|
||||
|
||||
#endregion
|
||||
|
||||
#region Implementation of ICluster
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets how many items belong to this cluster
|
||||
/// </summary>
|
||||
public int Count {
|
||||
get { return count; }
|
||||
set { count = value; }
|
||||
}
|
||||
private int count;
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets the label that will be shown to the user to represent
|
||||
/// this cluster
|
||||
/// </summary>
|
||||
public string DisplayLabel {
|
||||
get { return displayLabel; }
|
||||
set { displayLabel = value; }
|
||||
}
|
||||
private string displayLabel;
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets the actual data object that all members of this cluster
|
||||
/// have commonly returned.
|
||||
/// </summary>
|
||||
public object ClusterKey {
|
||||
get { return clusterKey; }
|
||||
set { clusterKey = value; }
|
||||
}
|
||||
private object clusterKey;
|
||||
|
||||
#endregion
|
||||
|
||||
#region Implementation of IComparable
|
||||
|
||||
/// <summary>
|
||||
/// Return an indication of the ordering between this object and the given one
|
||||
/// </summary>
|
||||
/// <param name="other"></param>
|
||||
/// <returns></returns>
|
||||
public int CompareTo(object other) {
|
||||
if (other == null || other == System.DBNull.Value)
|
||||
return 1;
|
||||
|
||||
ICluster otherCluster = other as ICluster;
|
||||
if (otherCluster == null)
|
||||
return 1;
|
||||
|
||||
string keyAsString = this.ClusterKey as string;
|
||||
if (keyAsString != null)
|
||||
return String.Compare(keyAsString, otherCluster.ClusterKey as string, StringComparison.CurrentCultureIgnoreCase);
|
||||
|
||||
IComparable keyAsComparable = this.ClusterKey as IComparable;
|
||||
if (keyAsComparable != null)
|
||||
return keyAsComparable.CompareTo(otherCluster.ClusterKey);
|
||||
|
||||
return -1;
|
||||
}
|
||||
|
||||
#endregion
|
||||
}
|
||||
}
|
||||
@@ -1,189 +0,0 @@
|
||||
/*
|
||||
* ClusteringStrategy - Implements a simple clustering strategy
|
||||
*
|
||||
* Author: Phillip Piper
|
||||
* Date: 3-March-2011 10:53 pm
|
||||
*
|
||||
* Change log:
|
||||
* 2011-03-03 JPP - First version
|
||||
*
|
||||
* Copyright (C) 2011-2014 Phillip Piper
|
||||
*
|
||||
* This program is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*
|
||||
* If you wish to use this code in a closed source application, please contact phillip.piper@gmail.com.
|
||||
*/
|
||||
|
||||
using System;
|
||||
using System.Collections;
|
||||
using System.Text;
|
||||
|
||||
namespace BrightIdeasSoftware {
|
||||
|
||||
/// <summary>
|
||||
/// This class provides a useful base implemention of a clustering
|
||||
/// strategy where the clusters are grouped around the value of a given column.
|
||||
/// </summary>
|
||||
public class ClusteringStrategy : IClusteringStrategy {
|
||||
|
||||
#region Static properties
|
||||
|
||||
/// <summary>
|
||||
/// This field is the text that will be shown to the user when a cluster
|
||||
/// key is null. It is exposed so it can be localized.
|
||||
/// </summary>
|
||||
static public string NULL_LABEL = "[null]";
|
||||
|
||||
/// <summary>
|
||||
/// This field is the text that will be shown to the user when a cluster
|
||||
/// key is empty (i.e. a string of zero length). It is exposed so it can be localized.
|
||||
/// </summary>
|
||||
static public string EMPTY_LABEL = "[empty]";
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets the format that will be used by default for clusters that only
|
||||
/// contain 1 item. The format string must accept two placeholders:
|
||||
/// - {0} is the cluster key converted to a string
|
||||
/// - {1} is the number of items in the cluster (always 1 in this case)
|
||||
/// </summary>
|
||||
static public string DefaultDisplayLabelFormatSingular {
|
||||
get { return defaultDisplayLabelFormatSingular; }
|
||||
set { defaultDisplayLabelFormatSingular = value; }
|
||||
}
|
||||
static private string defaultDisplayLabelFormatSingular = "{0} ({1} item)";
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets the format that will be used by default for clusters that
|
||||
/// contain 0 or two or more items. The format string must accept two placeholders:
|
||||
/// - {0} is the cluster key converted to a string
|
||||
/// - {1} is the number of items in the cluster
|
||||
/// </summary>
|
||||
static public string DefaultDisplayLabelFormatPlural {
|
||||
get { return defaultDisplayLabelFormatPural; }
|
||||
set { defaultDisplayLabelFormatPural = value; }
|
||||
}
|
||||
static private string defaultDisplayLabelFormatPural = "{0} ({1} items)";
|
||||
|
||||
#endregion
|
||||
|
||||
#region Life and death
|
||||
|
||||
/// <summary>
|
||||
/// Create a clustering strategy
|
||||
/// </summary>
|
||||
public ClusteringStrategy() {
|
||||
this.DisplayLabelFormatSingular = DefaultDisplayLabelFormatSingular;
|
||||
this.DisplayLabelFormatPlural = DefaultDisplayLabelFormatPlural;
|
||||
}
|
||||
|
||||
#endregion
|
||||
|
||||
#region Public properties
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets the column upon which this strategy is operating
|
||||
/// </summary>
|
||||
public OLVColumn Column {
|
||||
get { return column; }
|
||||
set { column = value; }
|
||||
}
|
||||
private OLVColumn column;
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets the format that will be used when the cluster
|
||||
/// contains only 1 item. The format string must accept two placeholders:
|
||||
/// - {0} is the cluster key converted to a string
|
||||
/// - {1} is the number of items in the cluster (always 1 in this case)
|
||||
/// </summary>
|
||||
/// <remarks>If this is not set, the value from
|
||||
/// ClusteringStrategy.DefaultDisplayLabelFormatSingular will be used</remarks>
|
||||
public string DisplayLabelFormatSingular {
|
||||
get { return displayLabelFormatSingular; }
|
||||
set { displayLabelFormatSingular = value; }
|
||||
}
|
||||
private string displayLabelFormatSingular;
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets the format that will be used when the cluster
|
||||
/// contains 0 or two or more items. The format string must accept two placeholders:
|
||||
/// - {0} is the cluster key converted to a string
|
||||
/// - {1} is the number of items in the cluster
|
||||
/// </summary>
|
||||
/// <remarks>If this is not set, the value from
|
||||
/// ClusteringStrategy.DefaultDisplayLabelFormatPlural will be used</remarks>
|
||||
public string DisplayLabelFormatPlural {
|
||||
get { return displayLabelFormatPural; }
|
||||
set { displayLabelFormatPural = value; }
|
||||
}
|
||||
private string displayLabelFormatPural;
|
||||
|
||||
#endregion
|
||||
|
||||
#region ICluster implementation
|
||||
|
||||
/// <summary>
|
||||
/// Get the cluster key by which the given model will be partitioned by this strategy
|
||||
/// </summary>
|
||||
/// <param name="model"></param>
|
||||
/// <returns></returns>
|
||||
virtual public object GetClusterKey(object model) {
|
||||
return this.Column.GetValue(model);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Create a cluster to hold the given cluster key
|
||||
/// </summary>
|
||||
/// <param name="clusterKey"></param>
|
||||
/// <returns></returns>
|
||||
virtual public ICluster CreateCluster(object clusterKey) {
|
||||
return new Cluster(clusterKey);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets the display label that the given cluster should use
|
||||
/// </summary>
|
||||
/// <param name="cluster"></param>
|
||||
/// <returns></returns>
|
||||
virtual public string GetClusterDisplayLabel(ICluster cluster) {
|
||||
string s = this.Column.ValueToString(cluster.ClusterKey) ?? NULL_LABEL;
|
||||
if (String.IsNullOrEmpty(s))
|
||||
s = EMPTY_LABEL;
|
||||
return this.ApplyDisplayFormat(cluster, s);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Create a filter that will include only model objects that
|
||||
/// match one or more of the given values.
|
||||
/// </summary>
|
||||
/// <param name="valuesChosenForFiltering"></param>
|
||||
/// <returns></returns>
|
||||
virtual public IModelFilter CreateFilter(IList valuesChosenForFiltering) {
|
||||
return new OneOfFilter(this.GetClusterKey, valuesChosenForFiltering);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Create a label that combines the string representation of the cluster
|
||||
/// key with a format string that holds an "X [N items in cluster]" type layout.
|
||||
/// </summary>
|
||||
/// <param name="cluster"></param>
|
||||
/// <param name="s"></param>
|
||||
/// <returns></returns>
|
||||
virtual protected string ApplyDisplayFormat(ICluster cluster, string s) {
|
||||
string format = (cluster.Count == 1) ? this.DisplayLabelFormatSingular : this.DisplayLabelFormatPlural;
|
||||
return String.IsNullOrEmpty(format) ? s : String.Format(format, s, cluster.Count);
|
||||
}
|
||||
|
||||
#endregion
|
||||
}
|
||||
}
|
||||
@@ -1,70 +0,0 @@
|
||||
/*
|
||||
* ClusteringStrategy - Implements a simple clustering strategy
|
||||
*
|
||||
* Author: Phillip Piper
|
||||
* Date: 1-April-2011 8:12am
|
||||
*
|
||||
* Change log:
|
||||
* 2011-04-01 JPP - First version
|
||||
*
|
||||
* Copyright (C) 2011-2014 Phillip Piper
|
||||
*
|
||||
* This program is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*
|
||||
* If you wish to use this code in a closed source application, please contact phillip.piper@gmail.com.
|
||||
*/
|
||||
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Text;
|
||||
|
||||
namespace BrightIdeasSoftware {
|
||||
|
||||
/// <summary>
|
||||
/// This class calculates clusters from the groups that the column uses.
|
||||
/// </summary>
|
||||
/// <remarks>
|
||||
/// <para>
|
||||
/// This is the default strategy for all non-date, filterable columns.
|
||||
/// </para>
|
||||
/// <para>
|
||||
/// This class does not strictly mimic the groups created by the given column.
|
||||
/// In particular, if the programmer changes the default grouping technique
|
||||
/// by listening for grouping events, this class will not mimic that behaviour.
|
||||
/// </para>
|
||||
/// </remarks>
|
||||
public class ClustersFromGroupsStrategy : ClusteringStrategy {
|
||||
|
||||
/// <summary>
|
||||
/// Get the cluster key by which the given model will be partitioned by this strategy
|
||||
/// </summary>
|
||||
/// <param name="model"></param>
|
||||
/// <returns></returns>
|
||||
public override object GetClusterKey(object model) {
|
||||
return this.Column.GetGroupKey(model);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets the display label that the given cluster should use
|
||||
/// </summary>
|
||||
/// <param name="cluster"></param>
|
||||
/// <returns></returns>
|
||||
public override string GetClusterDisplayLabel(ICluster cluster) {
|
||||
string s = this.Column.ConvertGroupKeyToTitle(cluster.ClusterKey);
|
||||
if (String.IsNullOrEmpty(s))
|
||||
s = EMPTY_LABEL;
|
||||
return this.ApplyDisplayFormat(cluster, s);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,187 +0,0 @@
|
||||
/*
|
||||
* DateTimeClusteringStrategy - A strategy to cluster objects by a date time
|
||||
*
|
||||
* Author: Phillip Piper
|
||||
* Date: 30-March-2011 9:40am
|
||||
*
|
||||
* Change log:
|
||||
* 2011-03-30 JPP - First version
|
||||
*
|
||||
* Copyright (C) 2011-2014 Phillip Piper
|
||||
*
|
||||
* This program is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*
|
||||
* If you wish to use this code in a closed source application, please contact phillip.piper@gmail.com.
|
||||
*/
|
||||
|
||||
using System;
|
||||
using System.Globalization;
|
||||
|
||||
namespace BrightIdeasSoftware {
|
||||
|
||||
/// <summary>
|
||||
/// This enum is used to indicate various portions of a datetime
|
||||
/// </summary>
|
||||
[Flags]
|
||||
public enum DateTimePortion {
|
||||
/// <summary>
|
||||
/// Year
|
||||
/// </summary>
|
||||
Year = 0x01,
|
||||
|
||||
/// <summary>
|
||||
/// Month
|
||||
/// </summary>
|
||||
Month = 0x02,
|
||||
|
||||
/// <summary>
|
||||
/// Day of the month
|
||||
/// </summary>
|
||||
Day = 0x04,
|
||||
|
||||
/// <summary>
|
||||
/// Hour
|
||||
/// </summary>
|
||||
Hour = 0x08,
|
||||
|
||||
/// <summary>
|
||||
/// Minute
|
||||
/// </summary>
|
||||
Minute = 0x10,
|
||||
|
||||
/// <summary>
|
||||
/// Second
|
||||
/// </summary>
|
||||
Second = 0x20
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// This class implements a strategy where the model objects are clustered
|
||||
/// according to some portion of the datetime value in the configured column.
|
||||
/// </summary>
|
||||
/// <remarks>To create a strategy that grouped people who were born in
|
||||
/// the same month, you would create a strategy that extracted just
|
||||
/// the month, and formatted it to show just the month's name. Like this:
|
||||
/// </remarks>
|
||||
/// <example>
|
||||
/// someColumn.ClusteringStrategy = new DateTimeClusteringStrategy(DateTimePortion.Month, "MMMM");
|
||||
/// </example>
|
||||
public class DateTimeClusteringStrategy : ClusteringStrategy {
|
||||
#region Life and death
|
||||
|
||||
/// <summary>
|
||||
/// Create a strategy that clusters by month/year
|
||||
/// </summary>
|
||||
public DateTimeClusteringStrategy()
|
||||
: this(DateTimePortion.Year | DateTimePortion.Month, "MMMM yyyy") {
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Create a strategy that clusters around the given parts
|
||||
/// </summary>
|
||||
/// <param name="portions"></param>
|
||||
/// <param name="format"></param>
|
||||
public DateTimeClusteringStrategy(DateTimePortion portions, string format) {
|
||||
this.Portions = portions;
|
||||
this.Format = format;
|
||||
}
|
||||
|
||||
#endregion
|
||||
|
||||
#region Properties
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets the format string will will be used to create a user-presentable
|
||||
/// version of the cluster key.
|
||||
/// </summary>
|
||||
/// <remarks>The format should use the date/time format strings, as documented
|
||||
/// in the Windows SDK. Both standard formats and custom format will work.</remarks>
|
||||
/// <example>"D" - long date pattern</example>
|
||||
/// <example>"MMMM, yyyy" - "January, 1999"</example>
|
||||
public string Format {
|
||||
get { return format; }
|
||||
set { format = value; }
|
||||
}
|
||||
private string format;
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets the parts of the DateTime that will be extracted when
|
||||
/// determining the clustering key for an object.
|
||||
/// </summary>
|
||||
public DateTimePortion Portions {
|
||||
get { return portions; }
|
||||
set { portions = value; }
|
||||
}
|
||||
private DateTimePortion portions = DateTimePortion.Year | DateTimePortion.Month;
|
||||
|
||||
#endregion
|
||||
|
||||
#region IClusterStrategy implementation
|
||||
|
||||
/// <summary>
|
||||
/// Get the cluster key by which the given model will be partitioned by this strategy
|
||||
/// </summary>
|
||||
/// <param name="model"></param>
|
||||
/// <returns></returns>
|
||||
public override object GetClusterKey(object model) {
|
||||
// Get the data attribute we want from the given model
|
||||
// Make sure the returned value is a DateTime
|
||||
DateTime? dateTime = this.Column.GetValue(model) as DateTime?;
|
||||
if (!dateTime.HasValue)
|
||||
return null;
|
||||
|
||||
// Extract the parts of the datetime that we are intereted in.
|
||||
// Even if we aren't interested in a particular portion, we still have to give it a reasonable default
|
||||
// otherwise we won't be able to build a DateTime object for it
|
||||
int year = ((this.Portions & DateTimePortion.Year) == DateTimePortion.Year) ? dateTime.Value.Year : 1;
|
||||
int month = ((this.Portions & DateTimePortion.Month) == DateTimePortion.Month) ? dateTime.Value.Month : 1;
|
||||
int day = ((this.Portions & DateTimePortion.Day) == DateTimePortion.Day) ? dateTime.Value.Day : 1;
|
||||
int hour = ((this.Portions & DateTimePortion.Hour) == DateTimePortion.Hour) ? dateTime.Value.Hour : 0;
|
||||
int minute = ((this.Portions & DateTimePortion.Minute) == DateTimePortion.Minute) ? dateTime.Value.Minute : 0;
|
||||
int second = ((this.Portions & DateTimePortion.Second) == DateTimePortion.Second) ? dateTime.Value.Second : 0;
|
||||
|
||||
return new DateTime(year, month, day, hour, minute, second);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets the display label that the given cluster should use
|
||||
/// </summary>
|
||||
/// <param name="cluster"></param>
|
||||
/// <returns></returns>
|
||||
public override string GetClusterDisplayLabel(ICluster cluster) {
|
||||
DateTime? dateTime = cluster.ClusterKey as DateTime?;
|
||||
|
||||
return this.ApplyDisplayFormat(cluster, dateTime.HasValue ? this.DateToString(dateTime.Value) : NULL_LABEL);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Convert the given date into a user presentable string
|
||||
/// </summary>
|
||||
/// <param name="dateTime"></param>
|
||||
/// <returns></returns>
|
||||
protected virtual string DateToString(DateTime dateTime) {
|
||||
if (String.IsNullOrEmpty(this.Format))
|
||||
return dateTime.ToString(CultureInfo.CurrentUICulture);
|
||||
|
||||
try {
|
||||
return dateTime.ToString(this.Format);
|
||||
}
|
||||
catch (FormatException) {
|
||||
return String.Format("Bad format string '{0}' for value '{1}'", this.Format, dateTime);
|
||||
}
|
||||
}
|
||||
|
||||
#endregion
|
||||
}
|
||||
}
|
||||
@@ -1,369 +0,0 @@
|
||||
/*
|
||||
* FilterMenuBuilder - Responsible for creating a Filter menu
|
||||
*
|
||||
* Author: Phillip Piper
|
||||
* Date: 4-March-2011 11:59 pm
|
||||
*
|
||||
* Change log:
|
||||
* 2012-05-20 JPP - Allow the same model object to be in multiple clusters
|
||||
* Useful for xor'ed flag fields, and multi-value strings
|
||||
* (e.g. hobbies that are stored as comma separated values).
|
||||
* v2.5.1
|
||||
* 2012-04-14 JPP - Fixed rare bug with clustering an empty list (SF #3445118)
|
||||
* v2.5
|
||||
* 2011-04-12 JPP - Added some images to menu
|
||||
* 2011-03-04 JPP - First version
|
||||
*
|
||||
* Copyright (C) 2011-2014 Phillip Piper
|
||||
*
|
||||
* This program is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*
|
||||
* If you wish to use this code in a closed source application, please contact phillip.piper@gmail.com.
|
||||
*/
|
||||
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Text;
|
||||
using System.Windows.Forms;
|
||||
using System.Collections;
|
||||
using System.Drawing;
|
||||
|
||||
namespace BrightIdeasSoftware {
|
||||
|
||||
/// <summary>
|
||||
/// Instances of this class know how to build a Filter menu.
|
||||
/// It is responsible for clustering the values in the target column,
|
||||
/// build a menu that shows those clusters, and then constructing
|
||||
/// a filter that will enact the users choices.
|
||||
/// </summary>
|
||||
/// <remarks>
|
||||
/// Almost all of the methods in this class are declared as "virtual protected"
|
||||
/// so that subclasses can provide alternative behaviours.
|
||||
/// </remarks>
|
||||
public class FilterMenuBuilder {
|
||||
|
||||
#region Static properties
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets the string that labels the Apply button.
|
||||
/// Exposed so it can be localized.
|
||||
/// </summary>
|
||||
static public string APPLY_LABEL = "Apply";
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets the string that labels the Clear All menu item.
|
||||
/// Exposed so it can be localized.
|
||||
/// </summary>
|
||||
static public string CLEAR_ALL_FILTERS_LABEL = "Clear All Filters";
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets the string that labels the Filtering menu as a whole..
|
||||
/// Exposed so it can be localized.
|
||||
/// </summary>
|
||||
static public string FILTERING_LABEL = "Filtering";
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets the string that represents Select All values.
|
||||
/// If this is set to null or empty, no Select All option will be included.
|
||||
/// Exposed so it can be localized.
|
||||
/// </summary>
|
||||
static public string SELECT_ALL_LABEL = "Select All";
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets the image that will be placed next to the Clear Filtering menu item
|
||||
/// </summary>
|
||||
static public Bitmap ClearFilteringImage = BrightIdeasSoftware.Properties.Resources.ClearFiltering;
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets the image that will be placed next to all "Apply" menu items on the filtering menu
|
||||
/// </summary>
|
||||
static public Bitmap FilteringImage = BrightIdeasSoftware.Properties.Resources.Filtering;
|
||||
|
||||
#endregion
|
||||
|
||||
#region Public properties
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets whether null should be considered as a valid data value.
|
||||
/// If this is true (the default), then a cluster will null as a key will be allow.
|
||||
/// If this is false, object that return a cluster key of null will ignored.
|
||||
/// </summary>
|
||||
public bool TreatNullAsDataValue {
|
||||
get { return treatNullAsDataValue; }
|
||||
set { treatNullAsDataValue = value; }
|
||||
}
|
||||
private bool treatNullAsDataValue = true;
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets the maximum number of objects that the clustering strategy
|
||||
/// will consider. This should be large enough to collect all unique clusters,
|
||||
/// but small enough to finish in a reasonable time.
|
||||
/// </summary>
|
||||
/// <remarks>The default value is 10,000. This should be perfectly
|
||||
/// acceptable for almost all lists.</remarks>
|
||||
public int MaxObjectsToConsider {
|
||||
get { return maxObjectsToConsider; }
|
||||
set { maxObjectsToConsider = value; }
|
||||
}
|
||||
private int maxObjectsToConsider = 10000;
|
||||
|
||||
#endregion
|
||||
|
||||
/// <summary>
|
||||
/// Create a Filter menu on the given tool tip for the given column in the given ObjectListView.
|
||||
/// </summary>
|
||||
/// <remarks>This is the main entry point into this class.</remarks>
|
||||
/// <param name="strip"></param>
|
||||
/// <param name="listView"></param>
|
||||
/// <param name="column"></param>
|
||||
/// <returns>The strip that should be shown to the user</returns>
|
||||
virtual public ToolStripDropDown MakeFilterMenu(ToolStripDropDown strip, ObjectListView listView, OLVColumn column) {
|
||||
if (strip == null) throw new ArgumentNullException("strip");
|
||||
if (listView == null) throw new ArgumentNullException("listView");
|
||||
if (column == null) throw new ArgumentNullException("column");
|
||||
|
||||
if (!column.UseFiltering || column.ClusteringStrategy == null || listView.Objects == null)
|
||||
return strip;
|
||||
|
||||
List<ICluster> clusters = this.Cluster(column.ClusteringStrategy, listView, column);
|
||||
if (clusters.Count > 0) {
|
||||
this.SortClusters(column.ClusteringStrategy, clusters);
|
||||
strip.Items.Add(this.CreateFilteringMenuItem(column, clusters));
|
||||
}
|
||||
|
||||
return strip;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Create a collection of clusters that should be presented to the user
|
||||
/// </summary>
|
||||
/// <param name="strategy"></param>
|
||||
/// <param name="listView"></param>
|
||||
/// <param name="column"></param>
|
||||
/// <returns></returns>
|
||||
virtual protected List<ICluster> Cluster(IClusteringStrategy strategy, ObjectListView listView, OLVColumn column) {
|
||||
// Build a map that correlates cluster key to clusters
|
||||
NullableDictionary<object, ICluster> map = new NullableDictionary<object, ICluster>();
|
||||
int count = 0;
|
||||
foreach (object model in listView.ObjectsForClustering) {
|
||||
this.ClusterOneModel(strategy, map, model);
|
||||
|
||||
if (count++ > this.MaxObjectsToConsider)
|
||||
break;
|
||||
}
|
||||
|
||||
// Now that we know exactly how many items are in each cluster, create a label for it
|
||||
foreach (ICluster cluster in map.Values)
|
||||
cluster.DisplayLabel = strategy.GetClusterDisplayLabel(cluster);
|
||||
|
||||
return new List<ICluster>(map.Values);
|
||||
}
|
||||
|
||||
private void ClusterOneModel(IClusteringStrategy strategy, NullableDictionary<object, ICluster> map, object model) {
|
||||
object clusterKey = strategy.GetClusterKey(model);
|
||||
|
||||
// If the returned value is an IEnumerable, that means the given model can belong to more than one cluster
|
||||
IEnumerable keyEnumerable = clusterKey as IEnumerable;
|
||||
if (clusterKey is string || keyEnumerable == null)
|
||||
keyEnumerable = new object[] {clusterKey};
|
||||
|
||||
// Deal with nulls and DBNulls
|
||||
ArrayList nullCorrected = new ArrayList();
|
||||
foreach (object key in keyEnumerable) {
|
||||
if (key == null || key == System.DBNull.Value) {
|
||||
if (this.TreatNullAsDataValue)
|
||||
nullCorrected.Add(null);
|
||||
} else nullCorrected.Add(key);
|
||||
}
|
||||
|
||||
// Group by key
|
||||
foreach (object key in nullCorrected) {
|
||||
if (map.ContainsKey(key))
|
||||
map[key].Count += 1;
|
||||
else
|
||||
map[key] = strategy.CreateCluster(key);
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Order the given list of clusters in the manner in which they should be presented to the user.
|
||||
/// </summary>
|
||||
/// <param name="strategy"></param>
|
||||
/// <param name="clusters"></param>
|
||||
virtual protected void SortClusters(IClusteringStrategy strategy, List<ICluster> clusters) {
|
||||
clusters.Sort();
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Do the work of making a menu that shows the clusters to the users
|
||||
/// </summary>
|
||||
/// <param name="column"></param>
|
||||
/// <param name="clusters"></param>
|
||||
/// <returns></returns>
|
||||
virtual protected ToolStripMenuItem CreateFilteringMenuItem(OLVColumn column, List<ICluster> clusters) {
|
||||
ToolStripCheckedListBox checkedList = new ToolStripCheckedListBox();
|
||||
checkedList.Tag = column;
|
||||
foreach (ICluster cluster in clusters)
|
||||
checkedList.AddItem(cluster, column.ValuesChosenForFiltering.Contains(cluster.ClusterKey));
|
||||
if (!String.IsNullOrEmpty(SELECT_ALL_LABEL)) {
|
||||
int checkedCount = checkedList.CheckedItems.Count;
|
||||
if (checkedCount == 0)
|
||||
checkedList.AddItem(SELECT_ALL_LABEL, CheckState.Unchecked);
|
||||
else
|
||||
checkedList.AddItem(SELECT_ALL_LABEL, checkedCount == clusters.Count ? CheckState.Checked : CheckState.Indeterminate);
|
||||
}
|
||||
checkedList.ItemCheck += new ItemCheckEventHandler(HandleItemCheckedWrapped);
|
||||
|
||||
ToolStripMenuItem clearAll = new ToolStripMenuItem(CLEAR_ALL_FILTERS_LABEL, ClearFilteringImage, delegate(object sender, EventArgs args) {
|
||||
this.ClearAllFilters(column);
|
||||
});
|
||||
ToolStripMenuItem apply = new ToolStripMenuItem(APPLY_LABEL, FilteringImage, delegate(object sender, EventArgs args) {
|
||||
this.EnactFilter(checkedList, column);
|
||||
});
|
||||
ToolStripMenuItem subMenu = new ToolStripMenuItem(FILTERING_LABEL, null, new ToolStripItem[] {
|
||||
clearAll, new ToolStripSeparator(), checkedList, apply });
|
||||
return subMenu;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Wrap a protected section around the real HandleItemChecked method, so that if
|
||||
/// that method tries to change a "checkedness" of an item, we don't get a recursive
|
||||
/// stack error. Effectively, this ensure that HandleItemChecked is only called
|
||||
/// in response to a user action.
|
||||
/// </summary>
|
||||
/// <param name="sender"></param>
|
||||
/// <param name="e"></param>
|
||||
private void HandleItemCheckedWrapped(object sender, ItemCheckEventArgs e) {
|
||||
if (alreadyInHandleItemChecked)
|
||||
return;
|
||||
|
||||
try {
|
||||
alreadyInHandleItemChecked = true;
|
||||
this.HandleItemChecked(sender, e);
|
||||
}
|
||||
finally {
|
||||
alreadyInHandleItemChecked = false;
|
||||
}
|
||||
}
|
||||
bool alreadyInHandleItemChecked = false;
|
||||
|
||||
/// <summary>
|
||||
/// Handle a user-generated ItemCheck event
|
||||
/// </summary>
|
||||
/// <param name="sender"></param>
|
||||
/// <param name="e"></param>
|
||||
virtual protected void HandleItemChecked(object sender, ItemCheckEventArgs e) {
|
||||
|
||||
ToolStripCheckedListBox checkedList = sender as ToolStripCheckedListBox;
|
||||
if (checkedList == null) return;
|
||||
OLVColumn column = checkedList.Tag as OLVColumn;
|
||||
if (column == null) return;
|
||||
ObjectListView listView = column.ListView as ObjectListView;
|
||||
if (listView == null) return;
|
||||
|
||||
// Deal with the "Select All" item if there is one
|
||||
int selectAllIndex = checkedList.Items.IndexOf(SELECT_ALL_LABEL);
|
||||
if (selectAllIndex >= 0)
|
||||
HandleSelectAllItem(e, checkedList, selectAllIndex);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Handle any checking/unchecking of the Select All option, and keep
|
||||
/// its checkedness in sync with everything else that is checked.
|
||||
/// </summary>
|
||||
/// <param name="e"></param>
|
||||
/// <param name="checkedList"></param>
|
||||
/// <param name="selectAllIndex"></param>
|
||||
virtual protected void HandleSelectAllItem(ItemCheckEventArgs e, ToolStripCheckedListBox checkedList, int selectAllIndex) {
|
||||
// Did they check/uncheck the "Select All"?
|
||||
if (e.Index == selectAllIndex) {
|
||||
if (e.NewValue == CheckState.Checked)
|
||||
checkedList.CheckAll();
|
||||
if (e.NewValue == CheckState.Unchecked)
|
||||
checkedList.UncheckAll();
|
||||
return;
|
||||
}
|
||||
|
||||
// OK. The user didn't check/uncheck SelectAll. Now we have to update it's
|
||||
// checkedness to reflect the state of everything else
|
||||
// If all clusters are checked, we check the Select All.
|
||||
// If no clusters are checked, the uncheck the Select All.
|
||||
// For everything else, Select All is set to indeterminate.
|
||||
|
||||
// How many items are currenty checked?
|
||||
int count = checkedList.CheckedItems.Count;
|
||||
|
||||
// First complication.
|
||||
// The value of the Select All itself doesn't count
|
||||
if (checkedList.GetItemCheckState(selectAllIndex) != CheckState.Unchecked)
|
||||
count -= 1;
|
||||
|
||||
// Another complication.
|
||||
// CheckedItems does not yet know about the item the user has just
|
||||
// clicked, so we have to adjust the count of checked items to what
|
||||
// it is going to be
|
||||
if (e.NewValue != e.CurrentValue) {
|
||||
if (e.NewValue == CheckState.Checked)
|
||||
count += 1;
|
||||
else
|
||||
count -= 1;
|
||||
}
|
||||
|
||||
// Update the state of the Select All item
|
||||
if (count == 0)
|
||||
checkedList.SetItemState(selectAllIndex, CheckState.Unchecked);
|
||||
else if (count == checkedList.Items.Count - 1)
|
||||
checkedList.SetItemState(selectAllIndex, CheckState.Checked);
|
||||
else
|
||||
checkedList.SetItemState(selectAllIndex, CheckState.Indeterminate);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Clear all the filters that are applied to the given column
|
||||
/// </summary>
|
||||
/// <param name="column">The column from which filters are to be removed</param>
|
||||
virtual protected void ClearAllFilters(OLVColumn column) {
|
||||
|
||||
ObjectListView olv = column.ListView as ObjectListView;
|
||||
if (olv == null || olv.IsDisposed)
|
||||
return;
|
||||
|
||||
olv.ResetColumnFiltering();
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Apply the selected values from the given list as a filter on the given column
|
||||
/// </summary>
|
||||
/// <param name="checkedList">A list in which the checked items should be used as filters</param>
|
||||
/// <param name="column">The column for which a filter should be generated</param>
|
||||
virtual protected void EnactFilter(ToolStripCheckedListBox checkedList, OLVColumn column) {
|
||||
|
||||
ObjectListView olv = column.ListView as ObjectListView;
|
||||
if (olv == null || olv.IsDisposed)
|
||||
return;
|
||||
|
||||
// Collect all the checked values
|
||||
ArrayList chosenValues = new ArrayList();
|
||||
foreach (object x in checkedList.CheckedItems) {
|
||||
ICluster cluster = x as ICluster;
|
||||
if (cluster != null) {
|
||||
chosenValues.Add(cluster.ClusterKey);
|
||||
}
|
||||
}
|
||||
column.ValuesChosenForFiltering = chosenValues;
|
||||
|
||||
olv.UpdateColumnFiltering();
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,481 +0,0 @@
|
||||
/*
|
||||
* Filters - Filtering on ObjectListViews
|
||||
*
|
||||
* Author: Phillip Piper
|
||||
* Date: 03/03/2010 17:00
|
||||
*
|
||||
* Change log:
|
||||
* 2011-03-01 JPP Added CompositeAllFilter, CompositeAnyFilter and OneOfFilter
|
||||
* v2.4.1
|
||||
* 2010-06-23 JPP Extended TextMatchFilter to handle regular expressions and string prefix matching.
|
||||
* v2.4
|
||||
* 2010-03-03 JPP Initial version
|
||||
*
|
||||
* TO DO:
|
||||
*
|
||||
* Copyright (C) 2010-2014 Phillip Piper
|
||||
*
|
||||
* This program is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*
|
||||
* If you wish to use this code in a closed source application, please contact phillip.piper@gmail.com.
|
||||
*/
|
||||
|
||||
using System;
|
||||
using System.Collections;
|
||||
using System.Collections.Generic;
|
||||
using System.Data;
|
||||
using System.Reflection;
|
||||
using System.Drawing;
|
||||
|
||||
namespace BrightIdeasSoftware
|
||||
{
|
||||
/// <summary>
|
||||
/// Interface for model-by-model filtering
|
||||
/// </summary>
|
||||
public interface IModelFilter
|
||||
{
|
||||
/// <summary>
|
||||
/// Should the given model be included when this filter is installed
|
||||
/// </summary>
|
||||
/// <param name="modelObject">The model object to consider</param>
|
||||
/// <returns>Returns true if the model will be included by the filter</returns>
|
||||
bool Filter(object modelObject);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Interface for whole list filtering
|
||||
/// </summary>
|
||||
public interface IListFilter
|
||||
{
|
||||
/// <summary>
|
||||
/// Return a subset of the given list of model objects as the new
|
||||
/// contents of the ObjectListView
|
||||
/// </summary>
|
||||
/// <param name="modelObjects">The collection of model objects that the list will possibly display</param>
|
||||
/// <returns>The filtered collection that holds the model objects that will be displayed.</returns>
|
||||
IEnumerable Filter(IEnumerable modelObjects);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Base class for model-by-model filters
|
||||
/// </summary>
|
||||
public class AbstractModelFilter : IModelFilter
|
||||
{
|
||||
/// <summary>
|
||||
/// Should the given model be included when this filter is installed
|
||||
/// </summary>
|
||||
/// <param name="modelObject">The model object to consider</param>
|
||||
/// <returns>Returns true if the model will be included by the filter</returns>
|
||||
virtual public bool Filter(object modelObject) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// This filter calls a given Predicate to decide if a model object should be included
|
||||
/// </summary>
|
||||
public class ModelFilter : IModelFilter
|
||||
{
|
||||
/// <summary>
|
||||
/// Create a filter based on the given predicate
|
||||
/// </summary>
|
||||
/// <param name="predicate">The function that will filter objects</param>
|
||||
public ModelFilter(Predicate<object> predicate) {
|
||||
this.Predicate = predicate;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets the predicate used to filter model objects
|
||||
/// </summary>
|
||||
protected Predicate<object> Predicate {
|
||||
get { return predicate; }
|
||||
set { predicate = value; }
|
||||
}
|
||||
private Predicate<object> predicate;
|
||||
|
||||
/// <summary>
|
||||
/// Should the given model object be included?
|
||||
/// </summary>
|
||||
/// <param name="modelObject"></param>
|
||||
/// <returns></returns>
|
||||
virtual public bool Filter(object modelObject) {
|
||||
return this.Predicate == null ? true : this.Predicate(modelObject);
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// A CompositeFilter joins several other filters together.
|
||||
/// If there are no filters, all model objects are included
|
||||
/// </summary>
|
||||
abstract public class CompositeFilter : IModelFilter {
|
||||
|
||||
/// <summary>
|
||||
/// Create an empty filter
|
||||
/// </summary>
|
||||
public CompositeFilter() {
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Create a composite filter from the given list of filters
|
||||
/// </summary>
|
||||
/// <param name="filters">A list of filters</param>
|
||||
public CompositeFilter(IEnumerable<IModelFilter> filters) {
|
||||
foreach (IModelFilter filter in filters) {
|
||||
if (filter != null)
|
||||
Filters.Add(filter);
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets the filters used by this composite
|
||||
/// </summary>
|
||||
public IList<IModelFilter> Filters {
|
||||
get { return filters; }
|
||||
set { filters = value; }
|
||||
}
|
||||
private IList<IModelFilter> filters = new List<IModelFilter>();
|
||||
|
||||
/// <summary>
|
||||
/// Get the sub filters that are text match filters
|
||||
/// </summary>
|
||||
public IEnumerable<TextMatchFilter> TextFilters {
|
||||
get {
|
||||
foreach (IModelFilter filter in this.Filters) {
|
||||
TextMatchFilter textFilter = filter as TextMatchFilter;
|
||||
if (textFilter != null)
|
||||
yield return textFilter;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Decide whether or not the given model should be included by the filter
|
||||
/// </summary>
|
||||
/// <param name="modelObject"></param>
|
||||
/// <returns>True if the object is included by the filter</returns>
|
||||
virtual public bool Filter(object modelObject) {
|
||||
if (this.Filters == null || this.Filters.Count == 0)
|
||||
return true;
|
||||
|
||||
return this.FilterObject(modelObject);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Decide whether or not the given model should be included by the filter
|
||||
/// </summary>
|
||||
/// <remarks>Filters is guaranteed to be non-empty when this method is called</remarks>
|
||||
/// <param name="modelObject">The model object under consideration</param>
|
||||
/// <returns>True if the object is included by the filter</returns>
|
||||
abstract public bool FilterObject(object modelObject);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// A CompositeAllFilter joins several other filters together.
|
||||
/// A model object must satisfy all filters to be included.
|
||||
/// If there are no filters, all model objects are included
|
||||
/// </summary>
|
||||
/// <remarks>
|
||||
/// Create a filter
|
||||
/// </remarks>
|
||||
/// <param name="filters"></param>
|
||||
public class CompositeAllFilter(List<IModelFilter> filters) : CompositeFilter(filters) {
|
||||
|
||||
/// <summary>
|
||||
/// Decide whether or not the given model should be included by the filter
|
||||
/// </summary>
|
||||
/// <remarks>Filters is guaranteed to be non-empty when this method is called</remarks>
|
||||
/// <param name="modelObject">The model object under consideration</param>
|
||||
/// <returns>True if the object is included by the filter</returns>
|
||||
override public bool FilterObject(object modelObject) {
|
||||
foreach (IModelFilter filter in this.Filters)
|
||||
if (!filter.Filter(modelObject))
|
||||
return false;
|
||||
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// A CompositeAllFilter joins several other filters together.
|
||||
/// A model object must only satisfy one of the filters to be included.
|
||||
/// If there are no filters, all model objects are included
|
||||
/// </summary>
|
||||
/// <remarks>
|
||||
/// Create a filter from the given filters
|
||||
/// </remarks>
|
||||
/// <param name="filters"></param>
|
||||
public class CompositeAnyFilter(List<IModelFilter> filters) : CompositeFilter(filters) {
|
||||
|
||||
/// <summary>
|
||||
/// Decide whether or not the given model should be included by the filter
|
||||
/// </summary>
|
||||
/// <remarks>Filters is guaranteed to be non-empty when this method is called</remarks>
|
||||
/// <param name="modelObject">The model object under consideration</param>
|
||||
/// <returns>True if the object is included by the filter</returns>
|
||||
override public bool FilterObject(object modelObject) {
|
||||
foreach (IModelFilter filter in this.Filters)
|
||||
if (filter.Filter(modelObject))
|
||||
return true;
|
||||
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Instances of this class extract a value from the model object
|
||||
/// and compare that value to a list of fixed values. The model
|
||||
/// object is included if the extracted value is in the list
|
||||
/// </summary>
|
||||
/// <remarks>If there is no delegate installed or there are
|
||||
/// no values to match, no model objects will be matched</remarks>
|
||||
public class OneOfFilter : IModelFilter {
|
||||
|
||||
/// <summary>
|
||||
/// Create a filter that will use the given delegate to extract values
|
||||
/// </summary>
|
||||
/// <param name="valueGetter"></param>
|
||||
public OneOfFilter(AspectGetterDelegate valueGetter) :
|
||||
this(valueGetter, new ArrayList()) {
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Create a filter that will extract values using the given delegate
|
||||
/// and compare them to the values in the given list.
|
||||
/// </summary>
|
||||
/// <param name="valueGetter"></param>
|
||||
/// <param name="possibleValues"></param>
|
||||
public OneOfFilter(AspectGetterDelegate valueGetter, ICollection possibleValues) {
|
||||
this.ValueGetter = valueGetter;
|
||||
this.PossibleValues = new ArrayList(possibleValues);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets the delegate that will be used to extract values
|
||||
/// from model objects
|
||||
/// </summary>
|
||||
virtual public AspectGetterDelegate ValueGetter {
|
||||
get { return valueGetter; }
|
||||
set { valueGetter = value; }
|
||||
}
|
||||
private AspectGetterDelegate valueGetter;
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets the list of values that the value extracted from
|
||||
/// the model object must match in order to be included.
|
||||
/// </summary>
|
||||
virtual public IList PossibleValues {
|
||||
get { return possibleValues; }
|
||||
set { possibleValues = value; }
|
||||
}
|
||||
private IList possibleValues;
|
||||
|
||||
/// <summary>
|
||||
/// Should the given model object be included?
|
||||
/// </summary>
|
||||
/// <param name="modelObject"></param>
|
||||
/// <returns></returns>
|
||||
public virtual bool Filter(object modelObject) {
|
||||
if (this.ValueGetter == null || this.PossibleValues == null || this.PossibleValues.Count == 0)
|
||||
return false;
|
||||
|
||||
object result = this.ValueGetter(modelObject);
|
||||
IEnumerable enumerable = result as IEnumerable;
|
||||
if (result is string || enumerable == null)
|
||||
return this.DoesValueMatch(result);
|
||||
|
||||
foreach (object x in enumerable) {
|
||||
if (this.DoesValueMatch(x))
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Decides if the given property is a match for the values in the PossibleValues collection
|
||||
/// </summary>
|
||||
/// <param name="result"></param>
|
||||
/// <returns></returns>
|
||||
protected virtual bool DoesValueMatch(object result) {
|
||||
return this.PossibleValues.Contains(result);
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Instances of this class match a property of a model objects against
|
||||
/// a list of bit flags. The property should be an xor-ed collection
|
||||
/// of bits flags.
|
||||
/// </summary>
|
||||
/// <remarks>Both the property compared and the list of possible values
|
||||
/// must be convertible to ulongs.</remarks>
|
||||
public class FlagBitSetFilter : OneOfFilter {
|
||||
|
||||
/// <summary>
|
||||
/// Create an instance
|
||||
/// </summary>
|
||||
/// <param name="valueGetter"></param>
|
||||
/// <param name="possibleValues"></param>
|
||||
public FlagBitSetFilter(AspectGetterDelegate valueGetter, ICollection possibleValues) : base(valueGetter, possibleValues) {
|
||||
this.ConvertPossibleValues();
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets the collection of values that will be matched.
|
||||
/// These must be ulongs (or convertible to ulongs).
|
||||
/// </summary>
|
||||
public override IList PossibleValues {
|
||||
get { return base.PossibleValues; }
|
||||
set {
|
||||
base.PossibleValues = value;
|
||||
this.ConvertPossibleValues();
|
||||
}
|
||||
}
|
||||
|
||||
private void ConvertPossibleValues() {
|
||||
this.possibleValuesAsUlongs = new List<UInt64>();
|
||||
foreach (object x in this.PossibleValues)
|
||||
this.possibleValuesAsUlongs.Add(Convert.ToUInt64(x));
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Decides if the given property is a match for the values in the PossibleValues collection
|
||||
/// </summary>
|
||||
/// <param name="result"></param>
|
||||
/// <returns></returns>
|
||||
protected override bool DoesValueMatch(object result) {
|
||||
try {
|
||||
UInt64 value = Convert.ToUInt64(result);
|
||||
foreach (ulong flag in this.possibleValuesAsUlongs) {
|
||||
if ((value & flag) == flag)
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
catch (InvalidCastException) {
|
||||
return false;
|
||||
}
|
||||
catch (FormatException) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
private List<UInt64> possibleValuesAsUlongs = new List<UInt64>();
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Base class for whole list filters
|
||||
/// </summary>
|
||||
public class AbstractListFilter : IListFilter
|
||||
{
|
||||
/// <summary>
|
||||
/// Return a subset of the given list of model objects as the new
|
||||
/// contents of the ObjectListView
|
||||
/// </summary>
|
||||
/// <param name="modelObjects">The collection of model objects that the list will possibly display</param>
|
||||
/// <returns>The filtered collection that holds the model objects that will be displayed.</returns>
|
||||
virtual public IEnumerable Filter(IEnumerable modelObjects) {
|
||||
return modelObjects;
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Instance of this class implement delegate based whole list filtering
|
||||
/// </summary>
|
||||
public class ListFilter : AbstractListFilter
|
||||
{
|
||||
/// <summary>
|
||||
/// A delegate that filters on a whole list
|
||||
/// </summary>
|
||||
/// <param name="rowObjects"></param>
|
||||
/// <returns></returns>
|
||||
public delegate IEnumerable ListFilterDelegate(IEnumerable rowObjects);
|
||||
|
||||
/// <summary>
|
||||
/// Create a ListFilter
|
||||
/// </summary>
|
||||
/// <param name="function"></param>
|
||||
public ListFilter(ListFilterDelegate function) {
|
||||
this.Function = function;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets the delegate that will filter the list
|
||||
/// </summary>
|
||||
public ListFilterDelegate Function {
|
||||
get { return function; }
|
||||
set { function = value; }
|
||||
}
|
||||
private ListFilterDelegate function;
|
||||
|
||||
/// <summary>
|
||||
/// Do the actual work of filtering
|
||||
/// </summary>
|
||||
/// <param name="modelObjects"></param>
|
||||
/// <returns></returns>
|
||||
public override IEnumerable Filter(IEnumerable modelObjects) {
|
||||
if (this.Function == null)
|
||||
return modelObjects;
|
||||
|
||||
return this.Function(modelObjects);
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Filter the list so only the last N entries are displayed
|
||||
/// </summary>
|
||||
public class TailFilter : AbstractListFilter
|
||||
{
|
||||
/// <summary>
|
||||
/// Create a no-op tail filter
|
||||
/// </summary>
|
||||
public TailFilter() {
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Create a filter that includes on the last N model objects
|
||||
/// </summary>
|
||||
/// <param name="numberOfObjects"></param>
|
||||
public TailFilter(int numberOfObjects) {
|
||||
this.Count = numberOfObjects;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets the number of model objects that will be
|
||||
/// returned from the tail of the list
|
||||
/// </summary>
|
||||
public int Count {
|
||||
get { return count; }
|
||||
set { count = value; }
|
||||
}
|
||||
private int count;
|
||||
|
||||
/// <summary>
|
||||
/// Return the last N subset of the model objects
|
||||
/// </summary>
|
||||
/// <param name="modelObjects"></param>
|
||||
/// <returns></returns>
|
||||
public override IEnumerable Filter(IEnumerable modelObjects) {
|
||||
if (this.Count <= 0)
|
||||
return modelObjects;
|
||||
|
||||
ArrayList list = ObjectListView.EnumerableToArray(modelObjects, false);
|
||||
|
||||
if (this.Count > list.Count)
|
||||
return list;
|
||||
|
||||
object[] tail = new object[this.Count];
|
||||
list.CopyTo(list.Count - this.Count, tail, 0, this.Count);
|
||||
return new ArrayList(tail);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,160 +0,0 @@
|
||||
/*
|
||||
* FlagClusteringStrategy - Implements a clustering strategy for a field which is a single integer
|
||||
* containing an XOR'ed collection of bit flags
|
||||
*
|
||||
* Author: Phillip Piper
|
||||
* Date: 23-March-2012 8:33 am
|
||||
*
|
||||
* Change log:
|
||||
* 2012-03-23 JPP - First version
|
||||
*
|
||||
* Copyright (C) 2012 Phillip Piper
|
||||
*
|
||||
* This program is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*
|
||||
* If you wish to use this code in a closed source application, please contact phillip.piper@gmail.com.
|
||||
*/
|
||||
|
||||
using System;
|
||||
using System.Collections;
|
||||
using System.Collections.Generic;
|
||||
using System.Globalization;
|
||||
|
||||
namespace BrightIdeasSoftware {
|
||||
|
||||
/// <summary>
|
||||
/// Instances of this class cluster model objects on the basis of a
|
||||
/// property that holds an xor-ed collection of bit flags.
|
||||
/// </summary>
|
||||
public class FlagClusteringStrategy : ClusteringStrategy
|
||||
{
|
||||
#region Life and death
|
||||
|
||||
/// <summary>
|
||||
/// Create a clustering strategy that operates on the flags of the given enum
|
||||
/// </summary>
|
||||
/// <param name="enumType"></param>
|
||||
public FlagClusteringStrategy(Type enumType) {
|
||||
if (enumType == null) throw new ArgumentNullException("enumType");
|
||||
if (!enumType.IsEnum) throw new ArgumentException("Type must be enum", "enumType");
|
||||
if (enumType.GetCustomAttributes(typeof(FlagsAttribute), false) == null) throw new ArgumentException("Type must have [Flags] attribute", "enumType");
|
||||
|
||||
List<long> flags = new List<long>();
|
||||
foreach (object x in Enum.GetValues(enumType))
|
||||
flags.Add(Convert.ToInt64(x));
|
||||
|
||||
List<string> flagLabels = new List<string>();
|
||||
foreach (string x in Enum.GetNames(enumType))
|
||||
flagLabels.Add(x);
|
||||
|
||||
this.SetValues(flags.ToArray(), flagLabels.ToArray());
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Create a clustering strategy around the given collections of flags and their display labels.
|
||||
/// There must be the same number of elements in both collections.
|
||||
/// </summary>
|
||||
/// <param name="values">The list of flags. </param>
|
||||
/// <param name="labels"></param>
|
||||
public FlagClusteringStrategy(long[] values, string[] labels) {
|
||||
this.SetValues(values, labels);
|
||||
}
|
||||
|
||||
#endregion
|
||||
|
||||
#region Implementation
|
||||
|
||||
/// <summary>
|
||||
/// Gets the value that will be xor-ed to test for the presence of a particular value.
|
||||
/// </summary>
|
||||
public long[] Values {
|
||||
get { return this.values; }
|
||||
private set { this.values = value; }
|
||||
}
|
||||
private long[] values;
|
||||
|
||||
/// <summary>
|
||||
/// Gets the labels that will be used when the corresponding Value is XOR present in the data.
|
||||
/// </summary>
|
||||
public string[] Labels {
|
||||
get { return this.labels; }
|
||||
private set { this.labels = value; }
|
||||
}
|
||||
private string[] labels;
|
||||
|
||||
private void SetValues(long[] flags, string[] flagLabels) {
|
||||
if (flags == null || flags.Length == 0) throw new ArgumentNullException("flags");
|
||||
if (flagLabels == null || flagLabels.Length == 0) throw new ArgumentNullException("flagLabels");
|
||||
if (flags.Length != flagLabels.Length) throw new ArgumentException("values and labels must have the same number of entries", "flags");
|
||||
|
||||
this.Values = flags;
|
||||
this.Labels = flagLabels;
|
||||
}
|
||||
|
||||
#endregion
|
||||
|
||||
#region Implementation of IClusteringStrategy
|
||||
|
||||
/// <summary>
|
||||
/// Get the cluster key by which the given model will be partitioned by this strategy
|
||||
/// </summary>
|
||||
/// <param name="model"></param>
|
||||
/// <returns></returns>
|
||||
public override object GetClusterKey(object model) {
|
||||
List<long> flags = new List<long>();
|
||||
try {
|
||||
long modelValue = Convert.ToInt64(this.Column.GetValue(model));
|
||||
foreach (long x in this.Values) {
|
||||
if ((x & modelValue) == x)
|
||||
flags.Add(x);
|
||||
}
|
||||
return flags;
|
||||
}
|
||||
catch (InvalidCastException ex) {
|
||||
System.Diagnostics.Debug.Write(ex);
|
||||
return flags;
|
||||
}
|
||||
catch (FormatException ex) {
|
||||
System.Diagnostics.Debug.Write(ex);
|
||||
return flags;
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets the display label that the given cluster should use
|
||||
/// </summary>
|
||||
/// <param name="cluster"></param>
|
||||
/// <returns></returns>
|
||||
public override string GetClusterDisplayLabel(ICluster cluster) {
|
||||
long clusterKeyAsUlong = Convert.ToInt64(cluster.ClusterKey);
|
||||
for (int i = 0; i < this.Values.Length; i++ ) {
|
||||
if (clusterKeyAsUlong == this.Values[i])
|
||||
return this.ApplyDisplayFormat(cluster, this.Labels[i]);
|
||||
}
|
||||
return this.ApplyDisplayFormat(cluster, clusterKeyAsUlong.ToString(CultureInfo.CurrentUICulture));
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Create a filter that will include only model objects that
|
||||
/// match one or more of the given values.
|
||||
/// </summary>
|
||||
/// <param name="valuesChosenForFiltering"></param>
|
||||
/// <returns></returns>
|
||||
public override IModelFilter CreateFilter(IList valuesChosenForFiltering) {
|
||||
return new FlagBitSetFilter(this.GetClusterKey, valuesChosenForFiltering);
|
||||
}
|
||||
|
||||
#endregion
|
||||
}
|
||||
}
|
||||
@@ -1,56 +0,0 @@
|
||||
/*
|
||||
* ICluster - A cluster is a group of objects that can be included or excluded as a whole
|
||||
*
|
||||
* Author: Phillip Piper
|
||||
* Date: 4-March-2011 11:59 pm
|
||||
*
|
||||
* Change log:
|
||||
* 2011-03-04 JPP - First version
|
||||
*
|
||||
* Copyright (C) 2011-2014 Phillip Piper
|
||||
*
|
||||
* This program is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*
|
||||
* If you wish to use this code in a closed source application, please contact phillip.piper@gmail.com.
|
||||
*/
|
||||
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Text;
|
||||
|
||||
namespace BrightIdeasSoftware {
|
||||
|
||||
/// <summary>
|
||||
/// A cluster is a like collection of objects that can be usefully filtered
|
||||
/// as whole using the filtering UI provided by the ObjectListView.
|
||||
/// </summary>
|
||||
public interface ICluster : IComparable {
|
||||
/// <summary>
|
||||
/// Gets or sets how many items belong to this cluster
|
||||
/// </summary>
|
||||
int Count { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets the label that will be shown to the user to represent
|
||||
/// this cluster
|
||||
/// </summary>
|
||||
string DisplayLabel { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets the actual data object that all members of this cluster
|
||||
/// have commonly returned.
|
||||
/// </summary>
|
||||
object ClusterKey { get; set; }
|
||||
}
|
||||
}
|
||||
@@ -1,80 +0,0 @@
|
||||
/*
|
||||
* IClusterStrategy - Encapsulates the ability to create a list of clusters from an ObjectListView
|
||||
*
|
||||
* Author: Phillip Piper
|
||||
* Date: 4-March-2011 11:59 pm
|
||||
*
|
||||
* Change log:
|
||||
* 2012-05-23 JPP - Added CreateFilter() method to interface to allow the strategy
|
||||
* to control the actual model filter that is created.
|
||||
* v2.5
|
||||
* 2011-03-04 JPP - First version
|
||||
*
|
||||
* Copyright (C) 2011-2014 Phillip Piper
|
||||
*
|
||||
* This program is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*
|
||||
* If you wish to use this code in a closed source application, please contact phillip.piper@gmail.com.
|
||||
*/
|
||||
|
||||
using System;
|
||||
using System.Collections;
|
||||
using System.Collections.Generic;
|
||||
using System.Text;
|
||||
|
||||
namespace BrightIdeasSoftware{
|
||||
|
||||
/// <summary>
|
||||
/// Implementation of this interface control the selecting of cluster keys
|
||||
/// and how those clusters will be presented to the user
|
||||
/// </summary>
|
||||
public interface IClusteringStrategy {
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets the column upon which this strategy will operate
|
||||
/// </summary>
|
||||
OLVColumn Column { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Get the cluster key by which the given model will be partitioned by this strategy
|
||||
/// </summary>
|
||||
/// <remarks>If the returned value is an IEnumerable, the given model is considered
|
||||
/// to belong to multiple clusters</remarks>
|
||||
/// <param name="model"></param>
|
||||
/// <returns></returns>
|
||||
object GetClusterKey(object model);
|
||||
|
||||
/// <summary>
|
||||
/// Create a cluster to hold the given cluster key
|
||||
/// </summary>
|
||||
/// <param name="clusterKey"></param>
|
||||
/// <returns></returns>
|
||||
ICluster CreateCluster(object clusterKey);
|
||||
|
||||
/// <summary>
|
||||
/// Gets the display label that the given cluster should use
|
||||
/// </summary>
|
||||
/// <param name="cluster"></param>
|
||||
/// <returns></returns>
|
||||
string GetClusterDisplayLabel(ICluster cluster);
|
||||
|
||||
/// <summary>
|
||||
/// Create a filter that will include only model objects that
|
||||
/// match one or more of the given values.
|
||||
/// </summary>
|
||||
/// <param name="valuesChosenForFiltering"></param>
|
||||
/// <returns></returns>
|
||||
IModelFilter CreateFilter(IList valuesChosenForFiltering);
|
||||
}
|
||||
}
|
||||
@@ -1,629 +0,0 @@
|
||||
/*
|
||||
* TextMatchFilter - Text based filtering on ObjectListViews
|
||||
*
|
||||
* Author: Phillip Piper
|
||||
* Date: 31/05/2011 7:45am
|
||||
*
|
||||
* Change log:
|
||||
* v2.6
|
||||
* 2012-10-13 JPP Allow filtering to consider additional columns
|
||||
* v2.5.1
|
||||
* 2011-06-22 JPP Handle searching for empty strings
|
||||
* v2.5.0
|
||||
* 2011-05-31 JPP Initial version
|
||||
*
|
||||
* TO DO:
|
||||
*
|
||||
* Copyright (C) 2011-2014 Phillip Piper
|
||||
*
|
||||
* This program is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*
|
||||
* If you wish to use this code in a closed source application, please contact phillip.piper@gmail.com.
|
||||
*/
|
||||
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Text;
|
||||
using System.Text.RegularExpressions;
|
||||
using System.Drawing;
|
||||
|
||||
namespace BrightIdeasSoftware {
|
||||
|
||||
/// <summary>
|
||||
/// Instances of this class include only those rows of the listview
|
||||
/// that match one or more given strings.
|
||||
/// </summary>
|
||||
/// <remarks>This class can match strings by prefix, regex, or simple containment.
|
||||
/// There are factory methods for each of these matching strategies.</remarks>
|
||||
public class TextMatchFilter : AbstractModelFilter {
|
||||
|
||||
#region Life and death
|
||||
|
||||
/// <summary>
|
||||
/// Create a text filter that will include rows where any cell matches
|
||||
/// any of the given regex expressions.
|
||||
/// </summary>
|
||||
/// <param name="olv"></param>
|
||||
/// <param name="texts"></param>
|
||||
/// <returns></returns>
|
||||
/// <remarks>Any string that is not a valid regex expression will be ignored.</remarks>
|
||||
public static TextMatchFilter Regex(ObjectListView olv, params string[] texts) {
|
||||
TextMatchFilter filter = new TextMatchFilter(olv);
|
||||
filter.RegexStrings = texts;
|
||||
return filter;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Create a text filter that includes rows where any cell begins with one of the given strings
|
||||
/// </summary>
|
||||
/// <param name="olv"></param>
|
||||
/// <param name="texts"></param>
|
||||
/// <returns></returns>
|
||||
public static TextMatchFilter Prefix(ObjectListView olv, params string[] texts) {
|
||||
TextMatchFilter filter = new TextMatchFilter(olv);
|
||||
filter.PrefixStrings = texts;
|
||||
return filter;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Create a text filter that includes rows where any cell contains any of the given strings.
|
||||
/// </summary>
|
||||
/// <param name="olv"></param>
|
||||
/// <param name="texts"></param>
|
||||
/// <returns></returns>
|
||||
public static TextMatchFilter Contains(ObjectListView olv, params string[] texts) {
|
||||
TextMatchFilter filter = new TextMatchFilter(olv);
|
||||
filter.ContainsStrings = texts;
|
||||
return filter;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Create a TextFilter
|
||||
/// </summary>
|
||||
/// <param name="olv"></param>
|
||||
public TextMatchFilter(ObjectListView olv) {
|
||||
this.ListView = olv;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Create a TextFilter that finds the given string
|
||||
/// </summary>
|
||||
/// <param name="olv"></param>
|
||||
/// <param name="text"></param>
|
||||
public TextMatchFilter(ObjectListView olv, string text) {
|
||||
this.ListView = olv;
|
||||
this.ContainsStrings = new string[] { text };
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Create a TextFilter that finds the given string using the given comparison
|
||||
/// </summary>
|
||||
/// <param name="olv"></param>
|
||||
/// <param name="text"></param>
|
||||
/// <param name="comparison"></param>
|
||||
public TextMatchFilter(ObjectListView olv, string text, StringComparison comparison) {
|
||||
this.ListView = olv;
|
||||
this.ContainsStrings = new string[] { text };
|
||||
this.StringComparison = comparison;
|
||||
}
|
||||
|
||||
#endregion
|
||||
|
||||
#region Public properties
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets which columns will be used for the comparisons? If this is null, all columns will be used
|
||||
/// </summary>
|
||||
public OLVColumn[] Columns {
|
||||
get { return columns; }
|
||||
set { columns = value; }
|
||||
}
|
||||
private OLVColumn[] columns;
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets additional columns which will be used in the comparison. These will be used
|
||||
/// in addition to either the Columns property or to all columns taken from the control.
|
||||
/// </summary>
|
||||
public OLVColumn[] AdditionalColumns {
|
||||
get { return additionalColumns; }
|
||||
set { additionalColumns = value; }
|
||||
}
|
||||
private OLVColumn[] additionalColumns;
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets the collection of strings that will be used for
|
||||
/// contains matching. Setting this replaces all previous texts
|
||||
/// of any kind.
|
||||
/// </summary>
|
||||
public IEnumerable<string> ContainsStrings {
|
||||
get {
|
||||
foreach (TextMatchingStrategy component in this.MatchingStrategies)
|
||||
yield return component.Text;
|
||||
}
|
||||
set {
|
||||
this.MatchingStrategies = new List<TextMatchingStrategy>();
|
||||
if (value != null) {
|
||||
foreach (string text in value)
|
||||
this.MatchingStrategies.Add(new TextContainsMatchingStrategy(this, text));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets whether or not this filter has any search criteria
|
||||
/// </summary>
|
||||
public bool HasComponents {
|
||||
get {
|
||||
return this.MatchingStrategies.Count > 0;
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets or set the ObjectListView upon which this filter will work
|
||||
/// </summary>
|
||||
/// <remarks>
|
||||
/// You cannot really rebase a filter after it is created, so do not change this value.
|
||||
/// It is included so that it can be set in an object initializer.
|
||||
/// </remarks>
|
||||
public ObjectListView ListView {
|
||||
get { return listView; }
|
||||
set { listView = value; }
|
||||
}
|
||||
private ObjectListView listView;
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets the collection of strings that will be used for
|
||||
/// prefix matching. Setting this replaces all previous texts
|
||||
/// of any kind.
|
||||
/// </summary>
|
||||
public IEnumerable<string> PrefixStrings {
|
||||
get {
|
||||
foreach (TextMatchingStrategy component in this.MatchingStrategies)
|
||||
yield return component.Text;
|
||||
}
|
||||
set {
|
||||
this.MatchingStrategies = new List<TextMatchingStrategy>();
|
||||
if (value != null) {
|
||||
foreach (string text in value)
|
||||
this.MatchingStrategies.Add(new TextBeginsMatchingStrategy(this, text));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets the options that will be used when compiling the regular expression.
|
||||
/// </summary>
|
||||
/// <remarks>
|
||||
/// This is only used when doing Regex matching (obviously).
|
||||
/// If this is not set specifically, the appropriate options are chosen to match the
|
||||
/// StringComparison setting (culture invariant, case sensitive).
|
||||
/// </remarks>
|
||||
public RegexOptions RegexOptions {
|
||||
get {
|
||||
if (!regexOptions.HasValue) {
|
||||
switch (this.StringComparison) {
|
||||
case StringComparison.CurrentCulture:
|
||||
regexOptions = RegexOptions.None;
|
||||
break;
|
||||
case StringComparison.CurrentCultureIgnoreCase:
|
||||
regexOptions = RegexOptions.IgnoreCase;
|
||||
break;
|
||||
case StringComparison.Ordinal:
|
||||
case StringComparison.InvariantCulture:
|
||||
regexOptions = RegexOptions.CultureInvariant;
|
||||
break;
|
||||
case StringComparison.OrdinalIgnoreCase:
|
||||
case StringComparison.InvariantCultureIgnoreCase:
|
||||
regexOptions = RegexOptions.CultureInvariant | RegexOptions.IgnoreCase;
|
||||
break;
|
||||
default:
|
||||
regexOptions = RegexOptions.None;
|
||||
break;
|
||||
}
|
||||
}
|
||||
return regexOptions.Value;
|
||||
}
|
||||
set {
|
||||
regexOptions = value;
|
||||
}
|
||||
}
|
||||
private RegexOptions? regexOptions;
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets the collection of strings that will be used for
|
||||
/// regex pattern matching. Setting this replaces all previous texts
|
||||
/// of any kind.
|
||||
/// </summary>
|
||||
public IEnumerable<string> RegexStrings {
|
||||
get {
|
||||
foreach (TextMatchingStrategy component in this.MatchingStrategies)
|
||||
yield return component.Text;
|
||||
}
|
||||
set {
|
||||
this.MatchingStrategies = new List<TextMatchingStrategy>();
|
||||
if (value != null) {
|
||||
foreach (string text in value)
|
||||
this.MatchingStrategies.Add(new TextRegexMatchingStrategy(this, text));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets how the filter will match text
|
||||
/// </summary>
|
||||
public StringComparison StringComparison {
|
||||
get { return this.stringComparison; }
|
||||
set { this.stringComparison = value; }
|
||||
}
|
||||
private StringComparison stringComparison = StringComparison.InvariantCultureIgnoreCase;
|
||||
|
||||
#endregion
|
||||
|
||||
#region Implementation
|
||||
|
||||
/// <summary>
|
||||
/// Loop over the columns that are being considering by the filter
|
||||
/// </summary>
|
||||
/// <returns></returns>
|
||||
protected virtual IEnumerable<OLVColumn> IterateColumns() {
|
||||
if (this.Columns == null) {
|
||||
foreach (OLVColumn column in this.ListView.Columns)
|
||||
yield return column;
|
||||
} else {
|
||||
foreach (OLVColumn column in this.Columns)
|
||||
yield return column;
|
||||
}
|
||||
if (this.AdditionalColumns != null) {
|
||||
foreach (OLVColumn column in this.AdditionalColumns)
|
||||
yield return column;
|
||||
}
|
||||
}
|
||||
|
||||
#endregion
|
||||
|
||||
#region Public interface
|
||||
|
||||
/// <summary>
|
||||
/// Do the actual work of filtering
|
||||
/// </summary>
|
||||
/// <param name="modelObject"></param>
|
||||
/// <returns></returns>
|
||||
public override bool Filter(object modelObject) {
|
||||
if (this.ListView == null || !this.HasComponents)
|
||||
return true;
|
||||
|
||||
foreach (OLVColumn column in this.IterateColumns()) {
|
||||
if (column.IsVisible && column.Searchable) {
|
||||
string[] cellTexts = column.GetSearchValues(modelObject);
|
||||
if (cellTexts != null && cellTexts.Length > 0) {
|
||||
foreach (TextMatchingStrategy filter in this.MatchingStrategies) {
|
||||
if (String.IsNullOrEmpty(filter.Text))
|
||||
return true;
|
||||
foreach (string cellText in cellTexts) {
|
||||
if (filter.MatchesText(cellText))
|
||||
return true;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Find all the ways in which this filter matches the given string.
|
||||
/// </summary>
|
||||
/// <remarks>This is used by the renderer to decide which bits of
|
||||
/// the string should be highlighted</remarks>
|
||||
/// <param name="cellText"></param>
|
||||
/// <returns>A list of character ranges indicating the matched substrings</returns>
|
||||
public IEnumerable<CharacterRange> FindAllMatchedRanges(string cellText) {
|
||||
List<CharacterRange> ranges = new List<CharacterRange>();
|
||||
|
||||
foreach (TextMatchingStrategy filter in this.MatchingStrategies) {
|
||||
if (!String.IsNullOrEmpty(filter.Text))
|
||||
ranges.AddRange(filter.FindAllMatchedRanges(cellText));
|
||||
}
|
||||
|
||||
return ranges;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Is the given column one of the columns being used by this filter?
|
||||
/// </summary>
|
||||
/// <param name="column"></param>
|
||||
/// <returns></returns>
|
||||
public bool IsIncluded(OLVColumn column) {
|
||||
if (this.Columns == null) {
|
||||
return column.ListView == this.ListView;
|
||||
}
|
||||
|
||||
foreach (OLVColumn x in this.Columns) {
|
||||
if (x == column)
|
||||
return true;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
#endregion
|
||||
|
||||
#region Implementation members
|
||||
|
||||
private List<TextMatchingStrategy> MatchingStrategies = new List<TextMatchingStrategy>();
|
||||
|
||||
#endregion
|
||||
|
||||
#region Components
|
||||
|
||||
/// <summary>
|
||||
/// Base class for the various types of string matching that TextMatchFilter provides
|
||||
/// </summary>
|
||||
abstract protected class TextMatchingStrategy {
|
||||
|
||||
/// <summary>
|
||||
/// Gets how the filter will match text
|
||||
/// </summary>
|
||||
public StringComparison StringComparison {
|
||||
get { return this.TextFilter.StringComparison; }
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets the text filter to which this component belongs
|
||||
/// </summary>
|
||||
public TextMatchFilter TextFilter {
|
||||
get { return textFilter; }
|
||||
set { textFilter = value; }
|
||||
}
|
||||
private TextMatchFilter textFilter;
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets the text that will be matched
|
||||
/// </summary>
|
||||
public string Text {
|
||||
get { return this.text; }
|
||||
set { this.text = value; }
|
||||
}
|
||||
private string text;
|
||||
|
||||
/// <summary>
|
||||
/// Find all the ways in which this filter matches the given string.
|
||||
/// </summary>
|
||||
/// <remarks>
|
||||
/// <para>
|
||||
/// This is used by the renderer to decide which bits of
|
||||
/// the string should be highlighted.
|
||||
/// </para>
|
||||
/// <para>this.Text will not be null or empty when this is called.</para>
|
||||
/// </remarks>
|
||||
/// <param name="cellText">The text of the cell we want to search</param>
|
||||
/// <returns>A list of character ranges indicating the matched substrings</returns>
|
||||
abstract public IEnumerable<CharacterRange> FindAllMatchedRanges(string cellText);
|
||||
|
||||
/// <summary>
|
||||
/// Does the given text match the filter
|
||||
/// </summary>
|
||||
/// <remarks>
|
||||
/// <para>this.Text will not be null or empty when this is called.</para>
|
||||
/// </remarks>
|
||||
/// <param name="cellText">The text of the cell we want to search</param>
|
||||
/// <returns>Return true if the given cellText matches our strategy</returns>
|
||||
abstract public bool MatchesText(string cellText);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// This component provides text contains matching strategy.
|
||||
/// </summary>
|
||||
protected class TextContainsMatchingStrategy : TextMatchingStrategy {
|
||||
|
||||
/// <summary>
|
||||
/// Create a text contains strategy
|
||||
/// </summary>
|
||||
/// <param name="filter"></param>
|
||||
/// <param name="text"></param>
|
||||
public TextContainsMatchingStrategy(TextMatchFilter filter, string text) {
|
||||
this.TextFilter = filter;
|
||||
this.Text = text;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Does the given text match the filter
|
||||
/// </summary>
|
||||
/// <remarks>
|
||||
/// <para>this.Text will not be null or empty when this is called.</para>
|
||||
/// </remarks>
|
||||
/// <param name="cellText">The text of the cell we want to search</param>
|
||||
/// <returns>Return true if the given cellText matches our strategy</returns>
|
||||
override public bool MatchesText(string cellText) {
|
||||
return cellText.IndexOf(this.Text, this.StringComparison) != -1;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Find all the ways in which this filter matches the given string.
|
||||
/// </summary>
|
||||
/// <remarks>
|
||||
/// <para>
|
||||
/// This is used by the renderer to decide which bits of
|
||||
/// the string should be highlighted.
|
||||
/// </para>
|
||||
/// <para>this.Text will not be null or empty when this is called.</para>
|
||||
/// </remarks>
|
||||
/// <param name="cellText">The text of the cell we want to search</param>
|
||||
/// <returns>A list of character ranges indicating the matched substrings</returns>
|
||||
override public IEnumerable<CharacterRange> FindAllMatchedRanges(string cellText) {
|
||||
List<CharacterRange> ranges = new List<CharacterRange>();
|
||||
|
||||
int matchIndex = cellText.IndexOf(this.Text, this.StringComparison);
|
||||
while (matchIndex != -1) {
|
||||
ranges.Add(new CharacterRange(matchIndex, this.Text.Length));
|
||||
matchIndex = cellText.IndexOf(this.Text, matchIndex + this.Text.Length, this.StringComparison);
|
||||
}
|
||||
|
||||
return ranges;
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// This component provides text begins with matching strategy.
|
||||
/// </summary>
|
||||
protected class TextBeginsMatchingStrategy : TextMatchingStrategy {
|
||||
|
||||
/// <summary>
|
||||
/// Create a text begins strategy
|
||||
/// </summary>
|
||||
/// <param name="filter"></param>
|
||||
/// <param name="text"></param>
|
||||
public TextBeginsMatchingStrategy(TextMatchFilter filter, string text) {
|
||||
this.TextFilter = filter;
|
||||
this.Text = text;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Does the given text match the filter
|
||||
/// </summary>
|
||||
/// <remarks>
|
||||
/// <para>this.Text will not be null or empty when this is called.</para>
|
||||
/// </remarks>
|
||||
/// <param name="cellText">The text of the cell we want to search</param>
|
||||
/// <returns>Return true if the given cellText matches our strategy</returns>
|
||||
override public bool MatchesText(string cellText) {
|
||||
return cellText.StartsWith(this.Text, this.StringComparison);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Find all the ways in which this filter matches the given string.
|
||||
/// </summary>
|
||||
/// <remarks>
|
||||
/// <para>
|
||||
/// This is used by the renderer to decide which bits of
|
||||
/// the string should be highlighted.
|
||||
/// </para>
|
||||
/// <para>this.Text will not be null or empty when this is called.</para>
|
||||
/// </remarks>
|
||||
/// <param name="cellText">The text of the cell we want to search</param>
|
||||
/// <returns>A list of character ranges indicating the matched substrings</returns>
|
||||
override public IEnumerable<CharacterRange> FindAllMatchedRanges(string cellText) {
|
||||
List<CharacterRange> ranges = new List<CharacterRange>();
|
||||
|
||||
if (cellText.StartsWith(this.Text, this.StringComparison))
|
||||
ranges.Add(new CharacterRange(0, this.Text.Length));
|
||||
|
||||
return ranges;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// This component provides regex matching strategy.
|
||||
/// </summary>
|
||||
protected class TextRegexMatchingStrategy : TextMatchingStrategy {
|
||||
|
||||
/// <summary>
|
||||
/// Creates a regex strategy
|
||||
/// </summary>
|
||||
/// <param name="filter"></param>
|
||||
/// <param name="text"></param>
|
||||
public TextRegexMatchingStrategy(TextMatchFilter filter, string text) {
|
||||
this.TextFilter = filter;
|
||||
this.Text = text;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets the options that will be used when compiling the regular expression.
|
||||
/// </summary>
|
||||
public RegexOptions RegexOptions {
|
||||
get {
|
||||
return this.TextFilter.RegexOptions;
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets a compilex regular expression, based on our current Text and RegexOptions.
|
||||
/// </summary>
|
||||
/// <remarks>
|
||||
/// If Text fails to compile as a regular expression, this will return a Regex object
|
||||
/// that will match all strings.
|
||||
/// </remarks>
|
||||
protected Regex Regex {
|
||||
get {
|
||||
if (this.regex == null) {
|
||||
try {
|
||||
this.regex = new Regex(this.Text, this.RegexOptions);
|
||||
}
|
||||
catch (ArgumentException) {
|
||||
this.regex = TextRegexMatchingStrategy.InvalidRegexMarker;
|
||||
}
|
||||
}
|
||||
return this.regex;
|
||||
}
|
||||
set {
|
||||
this.regex = value;
|
||||
}
|
||||
}
|
||||
private Regex regex;
|
||||
|
||||
/// <summary>
|
||||
/// Gets whether or not our current regular expression is a valid regex
|
||||
/// </summary>
|
||||
protected bool IsRegexInvalid {
|
||||
get {
|
||||
return this.Regex == TextRegexMatchingStrategy.InvalidRegexMarker;
|
||||
}
|
||||
}
|
||||
static private Regex InvalidRegexMarker = new Regex(".*");
|
||||
|
||||
/// <summary>
|
||||
/// Does the given text match the filter
|
||||
/// </summary>
|
||||
/// <remarks>
|
||||
/// <para>this.Text will not be null or empty when this is called.</para>
|
||||
/// </remarks>
|
||||
/// <param name="cellText">The text of the cell we want to search</param>
|
||||
/// <returns>Return true if the given cellText matches our strategy</returns>
|
||||
public override bool MatchesText(string cellText) {
|
||||
if (this.IsRegexInvalid)
|
||||
return true;
|
||||
return this.Regex.Match(cellText).Success;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Find all the ways in which this filter matches the given string.
|
||||
/// </summary>
|
||||
/// <remarks>
|
||||
/// <para>
|
||||
/// This is used by the renderer to decide which bits of
|
||||
/// the string should be highlighted.
|
||||
/// </para>
|
||||
/// <para>this.Text will not be null or empty when this is called.</para>
|
||||
/// </remarks>
|
||||
/// <param name="cellText">The text of the cell we want to search</param>
|
||||
/// <returns>A list of character ranges indicating the matched substrings</returns>
|
||||
override public IEnumerable<CharacterRange> FindAllMatchedRanges(string cellText) {
|
||||
List<CharacterRange> ranges = new List<CharacterRange>();
|
||||
|
||||
if (!this.IsRegexInvalid) {
|
||||
foreach (Match match in this.Regex.Matches(cellText)) {
|
||||
if (match.Length > 0)
|
||||
ranges.Add(new CharacterRange(match.Index, match.Length));
|
||||
}
|
||||
}
|
||||
|
||||
return ranges;
|
||||
}
|
||||
}
|
||||
|
||||
#endregion
|
||||
}
|
||||
}
|
||||
@@ -1,335 +0,0 @@
|
||||
/*
|
||||
* Attributes - Attributes that can be attached to properties of models to allow columns to be
|
||||
* built from them directly
|
||||
*
|
||||
* Author: Phillip Piper
|
||||
* Date: 15/08/2009 22:01
|
||||
*
|
||||
* Change log:
|
||||
* v2.6
|
||||
* 2012-08-16 JPP - Added [OLVChildren] and [OLVIgnore]
|
||||
* - OLV attributes can now only be set on properties
|
||||
* v2.4
|
||||
* 2010-04-14 JPP - Allow Name property to be set
|
||||
*
|
||||
* v2.3
|
||||
* 2009-08-15 JPP - Initial version
|
||||
*
|
||||
* To do:
|
||||
*
|
||||
* Copyright (C) 2009-2014 Phillip Piper
|
||||
*
|
||||
* This program is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*
|
||||
* If you wish to use this code in a closed source application, please contact phillip.piper@gmail.com.
|
||||
*/
|
||||
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Windows.Forms;
|
||||
|
||||
namespace BrightIdeasSoftware
|
||||
{
|
||||
/// <summary>
|
||||
/// This attribute is used to mark a property of a model
|
||||
/// class that should be noticed by Generator class.
|
||||
/// </summary>
|
||||
/// <remarks>
|
||||
/// All the attributes of this class match their equivilent properties on OLVColumn.
|
||||
/// </remarks>
|
||||
[AttributeUsage(AttributeTargets.Property)]
|
||||
public class OLVColumnAttribute : Attribute
|
||||
{
|
||||
#region Constructor
|
||||
|
||||
// There are several property where we actually want nullable value (bool?, int?),
|
||||
// but it seems attribute properties can't be nullable types.
|
||||
// So we explicitly track if those properties have been set.
|
||||
|
||||
/// <summary>
|
||||
/// Create a new OLVColumnAttribute
|
||||
/// </summary>
|
||||
public OLVColumnAttribute() {
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Create a new OLVColumnAttribute with the given title
|
||||
/// </summary>
|
||||
/// <param name="title">The title of the column</param>
|
||||
public OLVColumnAttribute(string title) {
|
||||
this.Title = title;
|
||||
}
|
||||
|
||||
#endregion
|
||||
|
||||
#region Public properties
|
||||
|
||||
/// <summary>
|
||||
///
|
||||
/// </summary>
|
||||
public string AspectToStringFormat {
|
||||
get { return aspectToStringFormat; }
|
||||
set { aspectToStringFormat = value; }
|
||||
}
|
||||
private string aspectToStringFormat;
|
||||
|
||||
/// <summary>
|
||||
///
|
||||
/// </summary>
|
||||
public bool CheckBoxes {
|
||||
get { return checkBoxes; }
|
||||
set {
|
||||
checkBoxes = value;
|
||||
this.IsCheckBoxesSet = true;
|
||||
}
|
||||
}
|
||||
private bool checkBoxes;
|
||||
internal bool IsCheckBoxesSet = false;
|
||||
|
||||
/// <summary>
|
||||
///
|
||||
/// </summary>
|
||||
public int DisplayIndex {
|
||||
get { return displayIndex; }
|
||||
set { displayIndex = value; }
|
||||
}
|
||||
private int displayIndex = -1;
|
||||
|
||||
/// <summary>
|
||||
///
|
||||
/// </summary>
|
||||
public bool FillsFreeSpace {
|
||||
get { return fillsFreeSpace; }
|
||||
set { fillsFreeSpace = value; }
|
||||
}
|
||||
private bool fillsFreeSpace;
|
||||
|
||||
/// <summary>
|
||||
///
|
||||
/// </summary>
|
||||
public int FreeSpaceProportion {
|
||||
get { return freeSpaceProportion; }
|
||||
set {
|
||||
freeSpaceProportion = value;
|
||||
IsFreeSpaceProportionSet = true;
|
||||
}
|
||||
}
|
||||
private int freeSpaceProportion;
|
||||
internal bool IsFreeSpaceProportionSet = false;
|
||||
|
||||
/// <summary>
|
||||
/// An array of IComparables that mark the cutoff points for values when
|
||||
/// grouping on this column.
|
||||
/// </summary>
|
||||
public object[] GroupCutoffs {
|
||||
get { return groupCutoffs; }
|
||||
set { groupCutoffs = value; }
|
||||
}
|
||||
private object[] groupCutoffs;
|
||||
|
||||
/// <summary>
|
||||
///
|
||||
/// </summary>
|
||||
public string[] GroupDescriptions {
|
||||
get { return groupDescriptions; }
|
||||
set { groupDescriptions = value; }
|
||||
}
|
||||
private string[] groupDescriptions;
|
||||
|
||||
/// <summary>
|
||||
///
|
||||
/// </summary>
|
||||
public string GroupWithItemCountFormat {
|
||||
get { return groupWithItemCountFormat; }
|
||||
set { groupWithItemCountFormat = value; }
|
||||
}
|
||||
private string groupWithItemCountFormat;
|
||||
|
||||
/// <summary>
|
||||
///
|
||||
/// </summary>
|
||||
public string GroupWithItemCountSingularFormat {
|
||||
get { return groupWithItemCountSingularFormat; }
|
||||
set { groupWithItemCountSingularFormat = value; }
|
||||
}
|
||||
private string groupWithItemCountSingularFormat;
|
||||
|
||||
/// <summary>
|
||||
///
|
||||
/// </summary>
|
||||
public bool Hyperlink {
|
||||
get { return hyperlink; }
|
||||
set { hyperlink = value; }
|
||||
}
|
||||
private bool hyperlink;
|
||||
|
||||
/// <summary>
|
||||
///
|
||||
/// </summary>
|
||||
public string ImageAspectName {
|
||||
get { return imageAspectName; }
|
||||
set { imageAspectName = value; }
|
||||
}
|
||||
private string imageAspectName;
|
||||
|
||||
/// <summary>
|
||||
///
|
||||
/// </summary>
|
||||
public bool IsEditable {
|
||||
get { return isEditable; }
|
||||
set {
|
||||
isEditable = value;
|
||||
this.IsEditableSet = true;
|
||||
}
|
||||
}
|
||||
private bool isEditable = true;
|
||||
internal bool IsEditableSet = false;
|
||||
|
||||
/// <summary>
|
||||
///
|
||||
/// </summary>
|
||||
public bool IsVisible {
|
||||
get { return isVisible; }
|
||||
set { isVisible = value; }
|
||||
}
|
||||
private bool isVisible = true;
|
||||
|
||||
/// <summary>
|
||||
///
|
||||
/// </summary>
|
||||
public bool IsTileViewColumn {
|
||||
get { return isTileViewColumn; }
|
||||
set { isTileViewColumn = value; }
|
||||
}
|
||||
private bool isTileViewColumn;
|
||||
|
||||
/// <summary>
|
||||
///
|
||||
/// </summary>
|
||||
public int MaximumWidth {
|
||||
get { return maximumWidth; }
|
||||
set { maximumWidth = value; }
|
||||
}
|
||||
private int maximumWidth = -1;
|
||||
|
||||
/// <summary>
|
||||
///
|
||||
/// </summary>
|
||||
public int MinimumWidth {
|
||||
get { return minimumWidth; }
|
||||
set { minimumWidth = value; }
|
||||
}
|
||||
private int minimumWidth = -1;
|
||||
|
||||
/// <summary>
|
||||
///
|
||||
/// </summary>
|
||||
public String Name {
|
||||
get { return name; }
|
||||
set { name = value; }
|
||||
}
|
||||
private String name;
|
||||
|
||||
/// <summary>
|
||||
///
|
||||
/// </summary>
|
||||
public HorizontalAlignment TextAlign {
|
||||
get { return this.textAlign; }
|
||||
set {
|
||||
this.textAlign = value;
|
||||
IsTextAlignSet = true;
|
||||
}
|
||||
}
|
||||
private HorizontalAlignment textAlign = HorizontalAlignment.Left;
|
||||
internal bool IsTextAlignSet = false;
|
||||
|
||||
/// <summary>
|
||||
///
|
||||
/// </summary>
|
||||
public String Tag {
|
||||
get { return tag; }
|
||||
set { tag = value; }
|
||||
}
|
||||
private String tag;
|
||||
|
||||
/// <summary>
|
||||
///
|
||||
/// </summary>
|
||||
public String Title {
|
||||
get { return title; }
|
||||
set { title = value; }
|
||||
}
|
||||
private String title;
|
||||
|
||||
/// <summary>
|
||||
///
|
||||
/// </summary>
|
||||
public String ToolTipText {
|
||||
get { return toolTipText; }
|
||||
set { toolTipText = value; }
|
||||
}
|
||||
private String toolTipText;
|
||||
|
||||
/// <summary>
|
||||
///
|
||||
/// </summary>
|
||||
public bool TriStateCheckBoxes {
|
||||
get { return triStateCheckBoxes; }
|
||||
set {
|
||||
triStateCheckBoxes = value;
|
||||
this.IsTriStateCheckBoxesSet = true;
|
||||
}
|
||||
}
|
||||
private bool triStateCheckBoxes;
|
||||
internal bool IsTriStateCheckBoxesSet = false;
|
||||
|
||||
/// <summary>
|
||||
///
|
||||
/// </summary>
|
||||
public bool UseInitialLetterForGroup {
|
||||
get { return useInitialLetterForGroup; }
|
||||
set { useInitialLetterForGroup = value; }
|
||||
}
|
||||
private bool useInitialLetterForGroup;
|
||||
|
||||
/// <summary>
|
||||
///
|
||||
/// </summary>
|
||||
public int Width {
|
||||
get { return width; }
|
||||
set { width = value; }
|
||||
}
|
||||
private int width = 150;
|
||||
|
||||
#endregion
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Properties marked with [OLVChildren] will be used as the children source in a TreeListView.
|
||||
/// </summary>
|
||||
[AttributeUsage(AttributeTargets.Property)]
|
||||
public class OLVChildrenAttribute : Attribute
|
||||
{
|
||||
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Properties marked with [OLVIgnore] will not have columns generated for them.
|
||||
/// </summary>
|
||||
[AttributeUsage(AttributeTargets.Property)]
|
||||
public class OLVIgnoreAttribute : Attribute
|
||||
{
|
||||
|
||||
}
|
||||
}
|
||||
@@ -1,315 +0,0 @@
|
||||
/*
|
||||
* Comparers - Various Comparer classes used within ObjectListView
|
||||
*
|
||||
* Author: Phillip Piper
|
||||
* Date: 25/11/2008 17:15
|
||||
*
|
||||
* Change log:
|
||||
* v2.8.1
|
||||
* 2014-12-03 JPP - Added StringComparer
|
||||
* v2.3
|
||||
* 2009-08-24 JPP - Added OLVGroupComparer
|
||||
* 2009-06-01 JPP - ModelObjectComparer would crash if secondary sort column was null.
|
||||
* 2008-12-20 JPP - Fixed bug with group comparisons when a group key was null (SF#2445761)
|
||||
* 2008-11-25 JPP Initial version
|
||||
*
|
||||
* TO DO:
|
||||
*
|
||||
* Copyright (C) 2006-2014 Phillip Piper
|
||||
*
|
||||
* This program is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*
|
||||
* If you wish to use this code in a closed source application, please contact phillip.piper@gmail.com.
|
||||
*/
|
||||
|
||||
using System;
|
||||
using System.Collections;
|
||||
using System.Collections.Generic;
|
||||
using System.Windows.Forms;
|
||||
|
||||
namespace BrightIdeasSoftware
|
||||
{
|
||||
/// <summary>
|
||||
/// ColumnComparer is the workhorse for all comparison between two values of a particular column.
|
||||
/// If the column has a specific comparer, use that to compare the values. Otherwise, do
|
||||
/// a case insensitive string compare of the string representations of the values.
|
||||
/// </summary>
|
||||
/// <remarks><para>This class inherits from both IComparer and its generic counterpart
|
||||
/// so that it can be used on untyped and typed collections.</para>
|
||||
/// <para>This is used by normal (non-virtual) ObjectListViews. Virtual lists use
|
||||
/// ModelObjectComparer</para>
|
||||
/// </remarks>
|
||||
/// <remarks>
|
||||
/// Create a ColumnComparer that will order the rows in a list view according
|
||||
/// to the values in a given column
|
||||
/// </remarks>
|
||||
/// <param name="col">The column whose values will be compared</param>
|
||||
/// <param name="order">The ordering for column values</param>
|
||||
public class ColumnComparer(OLVColumn col, SortOrder order) : IComparer, IComparer<OLVListItem>
|
||||
{
|
||||
/// <summary>
|
||||
/// Gets or sets the method that will be used to compare two strings.
|
||||
/// The default is to compare on the current culture, case-insensitive
|
||||
/// </summary>
|
||||
public static StringCompareDelegate StringComparer
|
||||
{
|
||||
get { return stringComparer; }
|
||||
set { stringComparer = value; }
|
||||
}
|
||||
private static StringCompareDelegate stringComparer;
|
||||
|
||||
/// <summary>
|
||||
/// Create a ColumnComparer that will order the rows in a list view according
|
||||
/// to the values in a given column, and by a secondary column if the primary
|
||||
/// column is equal.
|
||||
/// </summary>
|
||||
/// <param name="col">The column whose values will be compared</param>
|
||||
/// <param name="order">The ordering for column values</param>
|
||||
/// <param name="col2">The column whose values will be compared for secondary sorting</param>
|
||||
/// <param name="order2">The ordering for secondary column values</param>
|
||||
public ColumnComparer(OLVColumn col, SortOrder order, OLVColumn col2, SortOrder order2)
|
||||
: this(col, order)
|
||||
{
|
||||
// There is no point in secondary sorting on the same column
|
||||
if (col != col2)
|
||||
this.secondComparer = new ColumnComparer(col2, order2);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Compare two rows
|
||||
/// </summary>
|
||||
/// <param name="x">row1</param>
|
||||
/// <param name="y">row2</param>
|
||||
/// <returns>An ordering indication: -1, 0, 1</returns>
|
||||
public int Compare(object x, object y)
|
||||
{
|
||||
return this.Compare((OLVListItem)x, (OLVListItem)y);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Compare two rows
|
||||
/// </summary>
|
||||
/// <param name="x">row1</param>
|
||||
/// <param name="y">row2</param>
|
||||
/// <returns>An ordering indication: -1, 0, 1</returns>
|
||||
public int Compare(OLVListItem x, OLVListItem y)
|
||||
{
|
||||
if (this.sortOrder == SortOrder.None)
|
||||
return 0;
|
||||
|
||||
int result = 0;
|
||||
object x1 = this.column.GetValue(x.RowObject);
|
||||
object y1 = this.column.GetValue(y.RowObject);
|
||||
|
||||
// Handle nulls. Null values come last
|
||||
bool xIsNull = (x1 == null || x1 == System.DBNull.Value);
|
||||
bool yIsNull = (y1 == null || y1 == System.DBNull.Value);
|
||||
if (xIsNull || yIsNull) {
|
||||
if (xIsNull && yIsNull)
|
||||
result = 0;
|
||||
else
|
||||
result = (xIsNull ? -1 : 1);
|
||||
} else {
|
||||
result = this.CompareValues(x1, y1);
|
||||
}
|
||||
|
||||
if (this.sortOrder == SortOrder.Descending)
|
||||
result = 0 - result;
|
||||
|
||||
// If the result was equality, use the secondary comparer to resolve it
|
||||
if (result == 0 && this.secondComparer != null)
|
||||
result = this.secondComparer.Compare(x, y);
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Compare the actual values to be used for sorting
|
||||
/// </summary>
|
||||
/// <param name="x">The aspect extracted from the first row</param>
|
||||
/// <param name="y">The aspect extracted from the second row</param>
|
||||
/// <returns>An ordering indication: -1, 0, 1</returns>
|
||||
public int CompareValues(object x, object y)
|
||||
{
|
||||
// Force case insensitive compares on strings
|
||||
String xAsString = x as String;
|
||||
if (xAsString != null)
|
||||
return CompareStrings(xAsString, y as String);
|
||||
|
||||
IComparable comparable = x as IComparable;
|
||||
return comparable != null ? comparable.CompareTo(y) : 0;
|
||||
}
|
||||
|
||||
private static int CompareStrings(string x, string y)
|
||||
{
|
||||
if (StringComparer == null)
|
||||
return String.Compare(x, y, StringComparison.CurrentCultureIgnoreCase);
|
||||
else
|
||||
return StringComparer(x, y);
|
||||
}
|
||||
|
||||
private OLVColumn column = col;
|
||||
private SortOrder sortOrder = order;
|
||||
private ColumnComparer secondComparer;
|
||||
}
|
||||
|
||||
|
||||
/// <summary>
|
||||
/// This comparer sort list view groups. OLVGroups have a "SortValue" property,
|
||||
/// which is used if present. Otherwise, the titles of the groups will be compared.
|
||||
/// </summary>
|
||||
/// <remarks>
|
||||
/// Create a group comparer
|
||||
/// </remarks>
|
||||
/// <param name="order">The ordering for column values</param>
|
||||
public class OLVGroupComparer(SortOrder order) : IComparer<OLVGroup>
|
||||
{
|
||||
|
||||
/// <summary>
|
||||
/// Compare the two groups. OLVGroups have a "SortValue" property,
|
||||
/// which is used if present. Otherwise, the titles of the groups will be compared.
|
||||
/// </summary>
|
||||
/// <param name="x">group1</param>
|
||||
/// <param name="y">group2</param>
|
||||
/// <returns>An ordering indication: -1, 0, 1</returns>
|
||||
public int Compare(OLVGroup x, OLVGroup y) {
|
||||
// If we can compare the sort values, do that.
|
||||
// Otherwise do a case insensitive compare on the group header.
|
||||
int result;
|
||||
if (x.SortValue != null && y.SortValue != null)
|
||||
result = x.SortValue.CompareTo(y.SortValue);
|
||||
else
|
||||
result = String.Compare(x.Header, y.Header, StringComparison.CurrentCultureIgnoreCase);
|
||||
|
||||
if (this.sortOrder == SortOrder.Descending)
|
||||
result = 0 - result;
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
private SortOrder sortOrder = order;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// This comparer can be used to sort a collection of model objects by a given column
|
||||
/// </summary>
|
||||
/// <remarks>
|
||||
/// <para>This is used by virtual ObjectListViews. Non-virtual lists use
|
||||
/// ColumnComparer</para>
|
||||
/// </remarks>
|
||||
/// <remarks>
|
||||
/// Create a model object comparer
|
||||
/// </remarks>
|
||||
/// <param name="col"></param>
|
||||
/// <param name="order"></param>
|
||||
public class ModelObjectComparer(OLVColumn col, SortOrder order) : IComparer, IComparer<object>
|
||||
{
|
||||
/// <summary>
|
||||
/// Gets or sets the method that will be used to compare two strings.
|
||||
/// The default is to compare on the current culture, case-insensitive
|
||||
/// </summary>
|
||||
public static StringCompareDelegate StringComparer
|
||||
{
|
||||
get { return stringComparer; }
|
||||
set { stringComparer = value; }
|
||||
}
|
||||
private static StringCompareDelegate stringComparer;
|
||||
|
||||
/// <summary>
|
||||
/// Create a model object comparer with a secondary sorting column
|
||||
/// </summary>
|
||||
/// <param name="col"></param>
|
||||
/// <param name="order"></param>
|
||||
/// <param name="col2"></param>
|
||||
/// <param name="order2"></param>
|
||||
public ModelObjectComparer(OLVColumn col, SortOrder order, OLVColumn col2, SortOrder order2)
|
||||
: this(col, order)
|
||||
{
|
||||
// There is no point in secondary sorting on the same column
|
||||
if (col != col2 && col2 != null && order2 != SortOrder.None)
|
||||
this.secondComparer = new ModelObjectComparer(col2, order2);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Compare the two model objects
|
||||
/// </summary>
|
||||
/// <param name="x"></param>
|
||||
/// <param name="y"></param>
|
||||
/// <returns></returns>
|
||||
public int Compare(object x, object y)
|
||||
{
|
||||
int result = 0;
|
||||
object x1 = this.column.GetValue(x);
|
||||
object y1 = this.column.GetValue(y);
|
||||
|
||||
if (this.sortOrder == SortOrder.None)
|
||||
return 0;
|
||||
|
||||
// Handle nulls. Null values come last
|
||||
bool xIsNull = (x1 == null || x1 == System.DBNull.Value);
|
||||
bool yIsNull = (y1 == null || y1 == System.DBNull.Value);
|
||||
if (xIsNull || yIsNull) {
|
||||
if (xIsNull && yIsNull)
|
||||
result = 0;
|
||||
else
|
||||
result = (xIsNull ? -1 : 1);
|
||||
} else {
|
||||
result = this.CompareValues(x1, y1);
|
||||
}
|
||||
|
||||
if (this.sortOrder == SortOrder.Descending)
|
||||
result = 0 - result;
|
||||
|
||||
// If the result was equality, use the secondary comparer to resolve it
|
||||
if (result == 0 && this.secondComparer != null)
|
||||
result = this.secondComparer.Compare(x, y);
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Compare the actual values
|
||||
/// </summary>
|
||||
/// <param name="x"></param>
|
||||
/// <param name="y"></param>
|
||||
/// <returns></returns>
|
||||
public int CompareValues(object x, object y)
|
||||
{
|
||||
// Force case insensitive compares on strings
|
||||
String xStr = x as String;
|
||||
if (xStr != null)
|
||||
return CompareStrings(xStr, y as String);
|
||||
|
||||
IComparable comparable = x as IComparable;
|
||||
return comparable != null ? comparable.CompareTo(y) : 0;
|
||||
}
|
||||
|
||||
private static int CompareStrings(string x, string y)
|
||||
{
|
||||
if (StringComparer == null)
|
||||
return String.Compare(x, y, StringComparison.CurrentCultureIgnoreCase);
|
||||
else
|
||||
return StringComparer(x, y);
|
||||
}
|
||||
|
||||
private OLVColumn column = col;
|
||||
private SortOrder sortOrder = order;
|
||||
private ModelObjectComparer secondComparer;
|
||||
|
||||
#region IComparer<object> Members
|
||||
|
||||
#endregion
|
||||
}
|
||||
|
||||
}
|
||||
@@ -1,630 +0,0 @@
|
||||
/*
|
||||
* DataSourceAdapter - A helper class that translates DataSource events for an ObjectListView
|
||||
*
|
||||
* Author: Phillip Piper
|
||||
* Date: 20/09/2010 7:42 AM
|
||||
*
|
||||
* Change log:
|
||||
* v2.9
|
||||
* 2015-10-31 JPP - Put back sanity check on upper limit of source items
|
||||
* 2015-02-02 JPP - Made CreateColumnsFromSource() only rebuild columns when new ones were added
|
||||
* v2.8.1
|
||||
* 2014-11-23 JPP - Honour initial CurrencyManager.Position when setting DataSource.
|
||||
* 2014-10-27 JPP - Fix issue where SelectedObject was not sync'ed with CurrencyManager.Position (SF #129)
|
||||
* v2.6
|
||||
* 2012-08-16 JPP - Unify common column creation functionality with Generator when possible
|
||||
*
|
||||
* 2010-09-20 JPP - Initial version
|
||||
*
|
||||
* Copyright (C) 2010-2014 Phillip Piper
|
||||
*
|
||||
* This program is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*
|
||||
* If you wish to use this code in a closed source application, please contact phillip.piper@gmail.com.
|
||||
*/
|
||||
|
||||
using System;
|
||||
using System.ComponentModel;
|
||||
using System.Data;
|
||||
using System.Windows.Forms;
|
||||
using System.Diagnostics;
|
||||
|
||||
namespace BrightIdeasSoftware
|
||||
{
|
||||
/// <summary>
|
||||
/// A helper class that translates DataSource events for an ObjectListView
|
||||
/// </summary>
|
||||
public class DataSourceAdapter : IDisposable
|
||||
{
|
||||
#region Life and death
|
||||
|
||||
/// <summary>
|
||||
/// Make a DataSourceAdapter
|
||||
/// </summary>
|
||||
public DataSourceAdapter(ObjectListView olv) {
|
||||
if (olv == null) throw new ArgumentNullException("olv");
|
||||
|
||||
this.ListView = olv;
|
||||
// ReSharper disable once DoNotCallOverridableMethodsInConstructor
|
||||
this.BindListView(this.ListView);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Finalize this object
|
||||
/// </summary>
|
||||
~DataSourceAdapter() {
|
||||
this.Dispose(false);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Release all the resources used by this instance
|
||||
/// </summary>
|
||||
public void Dispose() {
|
||||
this.Dispose(true);
|
||||
GC.SuppressFinalize(this);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Release all the resources used by this instance
|
||||
/// </summary>
|
||||
public virtual void Dispose(bool fromUser) {
|
||||
this.UnbindListView(this.ListView);
|
||||
this.UnbindDataSource();
|
||||
}
|
||||
|
||||
#endregion
|
||||
|
||||
#region Public Properties
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets whether or not columns will be automatically generated to show the
|
||||
/// columns when the DataSource is set.
|
||||
/// </summary>
|
||||
/// <remarks>This must be set before the DataSource is set. It has no effect afterwards.</remarks>
|
||||
public bool AutoGenerateColumns {
|
||||
get { return this.autoGenerateColumns; }
|
||||
set { this.autoGenerateColumns = value; }
|
||||
}
|
||||
private bool autoGenerateColumns = true;
|
||||
|
||||
/// <summary>
|
||||
/// Get or set the DataSource that will be displayed in this list view.
|
||||
/// </summary>
|
||||
public virtual Object DataSource {
|
||||
get { return dataSource; }
|
||||
set {
|
||||
dataSource = value;
|
||||
this.RebindDataSource(true);
|
||||
}
|
||||
}
|
||||
private Object dataSource;
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets the name of the list or table in the data source for which the DataListView is displaying data.
|
||||
/// </summary>
|
||||
/// <remarks>If the data source is not a DataSet or DataViewManager, this property has no effect</remarks>
|
||||
public virtual string DataMember {
|
||||
get { return dataMember; }
|
||||
set {
|
||||
if (dataMember != value) {
|
||||
dataMember = value;
|
||||
RebindDataSource();
|
||||
}
|
||||
}
|
||||
}
|
||||
private string dataMember = "";
|
||||
|
||||
/// <summary>
|
||||
/// Gets the ObjectListView upon which this adaptor will operate
|
||||
/// </summary>
|
||||
public ObjectListView ListView {
|
||||
get { return listView; }
|
||||
internal set { listView = value; }
|
||||
}
|
||||
private ObjectListView listView;
|
||||
|
||||
#endregion
|
||||
|
||||
#region Implementation properties
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets the currency manager which is handling our binding context
|
||||
/// </summary>
|
||||
protected CurrencyManager CurrencyManager {
|
||||
get { return currencyManager; }
|
||||
set { currencyManager = value; }
|
||||
}
|
||||
private CurrencyManager currencyManager;
|
||||
|
||||
#endregion
|
||||
|
||||
#region Binding and unbinding
|
||||
|
||||
/// <summary>
|
||||
///
|
||||
/// </summary>
|
||||
/// <param name="olv"></param>
|
||||
protected virtual void BindListView(ObjectListView olv) {
|
||||
if (olv == null)
|
||||
return;
|
||||
|
||||
olv.Freezing += new EventHandler<FreezeEventArgs>(HandleListViewFreezing);
|
||||
olv.SelectionChanged += new EventHandler(HandleListViewSelectionChanged);
|
||||
olv.BindingContextChanged += new EventHandler(HandleListViewBindingContextChanged);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
///
|
||||
/// </summary>
|
||||
/// <param name="olv"></param>
|
||||
protected virtual void UnbindListView(ObjectListView olv) {
|
||||
if (olv == null)
|
||||
return;
|
||||
|
||||
olv.Freezing -= new EventHandler<FreezeEventArgs>(HandleListViewFreezing);
|
||||
olv.SelectionChanged -= new EventHandler(HandleListViewSelectionChanged);
|
||||
olv.BindingContextChanged -= new EventHandler(HandleListViewBindingContextChanged);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
///
|
||||
/// </summary>
|
||||
protected virtual void BindDataSource() {
|
||||
if (this.CurrencyManager == null)
|
||||
return;
|
||||
|
||||
this.CurrencyManager.MetaDataChanged += new EventHandler(HandleCurrencyManagerMetaDataChanged);
|
||||
this.CurrencyManager.PositionChanged += new EventHandler(HandleCurrencyManagerPositionChanged);
|
||||
this.CurrencyManager.ListChanged += new ListChangedEventHandler(CurrencyManagerListChanged);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
///
|
||||
/// </summary>
|
||||
protected virtual void UnbindDataSource() {
|
||||
if (this.CurrencyManager == null)
|
||||
return;
|
||||
|
||||
this.CurrencyManager.MetaDataChanged -= new EventHandler(HandleCurrencyManagerMetaDataChanged);
|
||||
this.CurrencyManager.PositionChanged -= new EventHandler(HandleCurrencyManagerPositionChanged);
|
||||
this.CurrencyManager.ListChanged -= new ListChangedEventHandler(CurrencyManagerListChanged);
|
||||
}
|
||||
|
||||
#endregion
|
||||
|
||||
#region Initialization
|
||||
|
||||
/// <summary>
|
||||
/// Our data source has changed. Figure out how to handle the new source
|
||||
/// </summary>
|
||||
protected virtual void RebindDataSource() {
|
||||
RebindDataSource(false);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Our data source has changed. Figure out how to handle the new source
|
||||
/// </summary>
|
||||
protected virtual void RebindDataSource(bool forceDataInitialization) {
|
||||
|
||||
CurrencyManager tempCurrencyManager = null;
|
||||
if (this.ListView != null && this.ListView.BindingContext != null && this.DataSource != null) {
|
||||
tempCurrencyManager = this.ListView.BindingContext[this.DataSource, this.DataMember] as CurrencyManager;
|
||||
}
|
||||
|
||||
// Has our currency manager changed?
|
||||
if (this.CurrencyManager != tempCurrencyManager) {
|
||||
this.UnbindDataSource();
|
||||
this.CurrencyManager = tempCurrencyManager;
|
||||
this.BindDataSource();
|
||||
|
||||
// Our currency manager has changed so we have to initialize a new data source
|
||||
forceDataInitialization = true;
|
||||
}
|
||||
|
||||
if (forceDataInitialization)
|
||||
InitializeDataSource();
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// The data source for this control has changed. Reconfigure the control for the new source
|
||||
/// </summary>
|
||||
protected virtual void InitializeDataSource() {
|
||||
if (this.ListView.Frozen || this.CurrencyManager == null)
|
||||
return;
|
||||
|
||||
this.CreateColumnsFromSource();
|
||||
this.CreateMissingAspectGettersAndPutters();
|
||||
this.SetListContents();
|
||||
this.ListView.AutoSizeColumns();
|
||||
|
||||
// Fake a position change event so that the control matches any initial Position
|
||||
this.HandleCurrencyManagerPositionChanged(null, null);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Take the contents of the currently bound list and put them into the control
|
||||
/// </summary>
|
||||
protected virtual void SetListContents() {
|
||||
this.ListView.Objects = this.CurrencyManager.List;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Create columns for the listview based on what properties are available in the data source
|
||||
/// </summary>
|
||||
/// <remarks>
|
||||
/// <para>This method will create columns if there is not already a column displaying that property.</para>
|
||||
/// </remarks>
|
||||
protected virtual void CreateColumnsFromSource() {
|
||||
if (this.CurrencyManager == null)
|
||||
return;
|
||||
|
||||
// Don't generate any columns in design mode. If we do, the user will see them,
|
||||
// but the Designer won't know about them and won't persist them, which is very confusing
|
||||
if (this.ListView.IsDesignMode)
|
||||
return;
|
||||
|
||||
// Don't create columns if we've been told not to
|
||||
if (!this.AutoGenerateColumns)
|
||||
return;
|
||||
|
||||
// Use a Generator to create columns
|
||||
Generator generator = Generator.Instance as Generator ?? new Generator();
|
||||
|
||||
PropertyDescriptorCollection properties = this.CurrencyManager.GetItemProperties();
|
||||
if (properties.Count == 0)
|
||||
return;
|
||||
|
||||
bool wereColumnsAdded = false;
|
||||
foreach (PropertyDescriptor property in properties) {
|
||||
|
||||
if (!this.ShouldCreateColumn(property))
|
||||
continue;
|
||||
|
||||
// Create a column
|
||||
OLVColumn column = generator.MakeColumnFromPropertyDescriptor(property);
|
||||
this.ConfigureColumn(column, property);
|
||||
|
||||
// Add it to our list
|
||||
this.ListView.AllColumns.Add(column);
|
||||
wereColumnsAdded = true;
|
||||
}
|
||||
|
||||
if (wereColumnsAdded)
|
||||
generator.PostCreateColumns(this.ListView);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Decide if a new column should be added to the control to display
|
||||
/// the given property
|
||||
/// </summary>
|
||||
/// <param name="property"></param>
|
||||
/// <returns></returns>
|
||||
protected virtual bool ShouldCreateColumn(PropertyDescriptor property) {
|
||||
|
||||
// Is there a column that already shows this property? If so, we don't show it again
|
||||
if (this.ListView.AllColumns.Exists(delegate(OLVColumn x) { return x.AspectName == property.Name; }))
|
||||
return false;
|
||||
|
||||
// Relationships to other tables turn up as IBindibleLists. Don't make columns to show them.
|
||||
// CHECK: Is this always true? What other things could be here? Constraints? Triggers?
|
||||
if (property.PropertyType == typeof(IBindingList))
|
||||
return false;
|
||||
|
||||
// Ignore anything marked with [OLVIgnore]
|
||||
return property.Attributes[typeof(OLVIgnoreAttribute)] == null;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Configure the given column to show the given property.
|
||||
/// The title and aspect name of the column are already filled in.
|
||||
/// </summary>
|
||||
/// <param name="column"></param>
|
||||
/// <param name="property"></param>
|
||||
protected virtual void ConfigureColumn(OLVColumn column, PropertyDescriptor property) {
|
||||
|
||||
column.LastDisplayIndex = this.ListView.AllColumns.Count;
|
||||
|
||||
// If our column is a BLOB, it could be an image, so assign a renderer to draw it.
|
||||
// CONSIDER: Is this a common enough case to warrant this code?
|
||||
if (property.PropertyType == typeof(System.Byte[]))
|
||||
column.Renderer = new ImageRenderer();
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Generate aspect getters and putters for any columns that are missing them (and for which we have
|
||||
/// enough information to actually generate a getter)
|
||||
/// </summary>
|
||||
protected virtual void CreateMissingAspectGettersAndPutters() {
|
||||
foreach (OLVColumn x in this.ListView.AllColumns) {
|
||||
OLVColumn column = x; // stack based variable accessible from closures
|
||||
if (column.AspectGetter == null && !String.IsNullOrEmpty(column.AspectName)) {
|
||||
column.AspectGetter = delegate(object row) {
|
||||
// In most cases, rows will be DataRowView objects
|
||||
DataRowView drv = row as DataRowView;
|
||||
if (drv == null)
|
||||
return column.GetAspectByName(row);
|
||||
return (drv.Row.RowState == DataRowState.Detached) ? null : drv[column.AspectName];
|
||||
};
|
||||
}
|
||||
if (column.IsEditable && column.AspectPutter == null && !String.IsNullOrEmpty(column.AspectName)) {
|
||||
column.AspectPutter = delegate(object row, object newValue) {
|
||||
// In most cases, rows will be DataRowView objects
|
||||
DataRowView drv = row as DataRowView;
|
||||
if (drv == null)
|
||||
column.PutAspectByName(row, newValue);
|
||||
else {
|
||||
if (drv.Row.RowState != DataRowState.Detached)
|
||||
drv[column.AspectName] = newValue;
|
||||
}
|
||||
};
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#endregion
|
||||
|
||||
#region Event Handlers
|
||||
|
||||
/// <summary>
|
||||
/// CurrencyManager ListChanged event handler.
|
||||
/// Deals with fine-grained changes to list items.
|
||||
/// </summary>
|
||||
/// <remarks>
|
||||
/// It's actually difficult to deal with these changes in a fine-grained manner.
|
||||
/// If our listview is grouped, then any change may make a new group appear or
|
||||
/// an old group disappear. It is rarely enough to simply update the affected row.
|
||||
/// </remarks>
|
||||
/// <param name="sender"></param>
|
||||
/// <param name="e"></param>
|
||||
protected virtual void CurrencyManagerListChanged(object sender, ListChangedEventArgs e) {
|
||||
Debug.Assert(sender == this.CurrencyManager);
|
||||
|
||||
// Ignore changes make while frozen, since we will do a complete rebuild when we unfreeze
|
||||
if (this.ListView.Frozen)
|
||||
return;
|
||||
|
||||
//System.Diagnostics.Debug.WriteLine(e.ListChangedType);
|
||||
Stopwatch sw = Stopwatch.StartNew();
|
||||
switch (e.ListChangedType) {
|
||||
|
||||
case ListChangedType.Reset:
|
||||
this.HandleListChangedReset(e);
|
||||
break;
|
||||
|
||||
case ListChangedType.ItemChanged:
|
||||
this.HandleListChangedItemChanged(e);
|
||||
break;
|
||||
|
||||
case ListChangedType.ItemAdded:
|
||||
this.HandleListChangedItemAdded(e);
|
||||
break;
|
||||
|
||||
// An item has gone away.
|
||||
case ListChangedType.ItemDeleted:
|
||||
this.HandleListChangedItemDeleted(e);
|
||||
break;
|
||||
|
||||
// An item has changed its index.
|
||||
case ListChangedType.ItemMoved:
|
||||
this.HandleListChangedItemMoved(e);
|
||||
break;
|
||||
|
||||
// Something has changed in the metadata.
|
||||
// CHECK: When are these events actually fired?
|
||||
case ListChangedType.PropertyDescriptorAdded:
|
||||
case ListChangedType.PropertyDescriptorChanged:
|
||||
case ListChangedType.PropertyDescriptorDeleted:
|
||||
this.HandleListChangedMetadataChanged(e);
|
||||
break;
|
||||
}
|
||||
sw.Stop();
|
||||
System.Diagnostics.Debug.WriteLine(String.Format("PERF - Processing {0} event on {1} rows took {2}ms", e.ListChangedType, this.ListView.GetItemCount(), sw.ElapsedMilliseconds));
|
||||
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Handle PropertyDescriptor* events
|
||||
/// </summary>
|
||||
/// <param name="e"></param>
|
||||
protected virtual void HandleListChangedMetadataChanged(ListChangedEventArgs e) {
|
||||
this.InitializeDataSource();
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Handle ItemMoved event
|
||||
/// </summary>
|
||||
/// <param name="e"></param>
|
||||
protected virtual void HandleListChangedItemMoved(ListChangedEventArgs e) {
|
||||
// When is this actually triggered?
|
||||
this.InitializeDataSource();
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Handle the ItemDeleted event
|
||||
/// </summary>
|
||||
/// <param name="e"></param>
|
||||
protected virtual void HandleListChangedItemDeleted(ListChangedEventArgs e) {
|
||||
this.InitializeDataSource();
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Handle an ItemAdded event.
|
||||
/// </summary>
|
||||
/// <param name="e"></param>
|
||||
protected virtual void HandleListChangedItemAdded(ListChangedEventArgs e) {
|
||||
// We get this event twice if certain grid controls are used to add a new row to a
|
||||
// datatable: once when the editing of a new row begins, and once again when that
|
||||
// editing commits. (If the user cancels the creation of the new row, we never see
|
||||
// the second creation.) We detect this by seeing if this is a view on a row in a
|
||||
// DataTable, and if it is, testing to see if it's a new row under creation.
|
||||
|
||||
Object newRow = this.CurrencyManager.List[e.NewIndex];
|
||||
DataRowView drv = newRow as DataRowView;
|
||||
if (drv == null || !drv.IsNew) {
|
||||
// Either we're not dealing with a view on a data table, or this is the commit
|
||||
// notification. Either way, this is the final notification, so we want to
|
||||
// handle the new row now!
|
||||
this.InitializeDataSource();
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Handle the Reset event
|
||||
/// </summary>
|
||||
/// <param name="e"></param>
|
||||
protected virtual void HandleListChangedReset(ListChangedEventArgs e) {
|
||||
// The whole list has changed utterly, so reload it.
|
||||
this.InitializeDataSource();
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Handle ItemChanged event. This is triggered when a single item
|
||||
/// has changed, so just refresh that one item.
|
||||
/// </summary>
|
||||
/// <param name="e"></param>
|
||||
/// <remarks>Even in this simple case, we should probably rebuild the list.
|
||||
/// For example, the change could put the item into its own new group.</remarks>
|
||||
protected virtual void HandleListChangedItemChanged(ListChangedEventArgs e) {
|
||||
// A single item has changed, so just refresh that.
|
||||
//System.Diagnostics.Debug.WriteLine(String.Format("HandleListChangedItemChanged: {0}, {1}", e.NewIndex, e.PropertyDescriptor.Name));
|
||||
|
||||
Object changedRow = this.CurrencyManager.List[e.NewIndex];
|
||||
this.ListView.RefreshObject(changedRow);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// The CurrencyManager calls this if the data source looks
|
||||
/// different. We just reload everything.
|
||||
/// </summary>
|
||||
/// <param name="sender"></param>
|
||||
/// <param name="e"></param>
|
||||
/// <remarks>
|
||||
/// CHECK: Do we need this if we are handle ListChanged metadata events?
|
||||
/// </remarks>
|
||||
protected virtual void HandleCurrencyManagerMetaDataChanged(object sender, EventArgs e) {
|
||||
this.InitializeDataSource();
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Called by the CurrencyManager when the currently selected item
|
||||
/// changes. We update the ListView selection so that we stay in sync
|
||||
/// with any other controls bound to the same source.
|
||||
/// </summary>
|
||||
/// <param name="sender"></param>
|
||||
/// <param name="e"></param>
|
||||
protected virtual void HandleCurrencyManagerPositionChanged(object sender, EventArgs e) {
|
||||
int index = this.CurrencyManager.Position;
|
||||
|
||||
// Make sure the index is sane (-1 pops up from time to time)
|
||||
if (index < 0 || index >= this.ListView.GetItemCount())
|
||||
return;
|
||||
|
||||
// Avoid recursion. If we are currently changing the index, don't
|
||||
// start the process again.
|
||||
if (this.isChangingIndex)
|
||||
return;
|
||||
|
||||
try {
|
||||
this.isChangingIndex = true;
|
||||
this.ChangePosition(index);
|
||||
}
|
||||
finally {
|
||||
this.isChangingIndex = false;
|
||||
}
|
||||
}
|
||||
private bool isChangingIndex = false;
|
||||
|
||||
/// <summary>
|
||||
/// Change the control's position (which is it's currently selected row)
|
||||
/// to the nth row in the dataset
|
||||
/// </summary>
|
||||
/// <param name="index">The index of the row to be selected</param>
|
||||
protected virtual void ChangePosition(int index) {
|
||||
// We can't use the index directly, since our listview may be sorted
|
||||
// Only assign if not null
|
||||
if (this.ListView.SelectedObject != null)
|
||||
{
|
||||
this.ListView.SelectedObject = this.CurrencyManager.List[index];
|
||||
}
|
||||
|
||||
// THINK: Do we always want to bring it into view?
|
||||
if (this.ListView.SelectedIndices.Count > 0)
|
||||
this.ListView.EnsureVisible(this.ListView.SelectedIndices[0]);
|
||||
}
|
||||
|
||||
#endregion
|
||||
|
||||
#region ObjectListView event handlers
|
||||
|
||||
/// <summary>
|
||||
/// Handle the selection changing in our ListView.
|
||||
/// We need to tell our currency manager about the new position.
|
||||
/// </summary>
|
||||
/// <param name="sender"></param>
|
||||
/// <param name="e"></param>
|
||||
protected virtual void HandleListViewSelectionChanged(object sender, EventArgs e) {
|
||||
// Prevent recursion
|
||||
if (this.isChangingIndex)
|
||||
return;
|
||||
|
||||
// Sanity
|
||||
if (this.CurrencyManager == null)
|
||||
return;
|
||||
|
||||
// If only one item is selected, tell the currency manager which item is selected.
|
||||
// CurrencyManager can't handle multiple selection so there's nothing we can do
|
||||
// if more than one row is selected.
|
||||
if (this.ListView.SelectedIndices.Count != 1)
|
||||
return;
|
||||
|
||||
try {
|
||||
this.isChangingIndex = true;
|
||||
|
||||
// We can't use the selectedIndex directly, since our listview may be sorted and/or filtered
|
||||
// So we have to find the index of the selected object within the original list.
|
||||
this.CurrencyManager.Position = this.CurrencyManager.List.IndexOf(this.ListView.SelectedObject);
|
||||
} finally {
|
||||
this.isChangingIndex = false;
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Handle the frozenness of our ListView changing.
|
||||
/// </summary>
|
||||
/// <param name="sender"></param>
|
||||
/// <param name="e"></param>
|
||||
protected virtual void HandleListViewFreezing(object sender, FreezeEventArgs e) {
|
||||
if (!alreadyFreezing && e.FreezeLevel == 0) {
|
||||
try {
|
||||
alreadyFreezing = true;
|
||||
this.RebindDataSource(true);
|
||||
} finally {
|
||||
alreadyFreezing = false;
|
||||
}
|
||||
}
|
||||
}
|
||||
private bool alreadyFreezing = false;
|
||||
|
||||
/// <summary>
|
||||
/// Handle a change to the BindingContext of our ListView.
|
||||
/// </summary>
|
||||
/// <param name="sender"></param>
|
||||
/// <param name="e"></param>
|
||||
protected virtual void HandleListViewBindingContextChanged(object sender, EventArgs e) {
|
||||
this.RebindDataSource(false);
|
||||
}
|
||||
|
||||
#endregion
|
||||
}
|
||||
}
|
||||
@@ -1,168 +0,0 @@
|
||||
/*
|
||||
* Delegates - All delegate definitions used in ObjectListView
|
||||
*
|
||||
* Author: Phillip Piper
|
||||
* Date: 31-March-2011 5:53 pm
|
||||
*
|
||||
* Change log:
|
||||
* v2.10
|
||||
* 2015-12-30 JPP - Added CellRendererGetterDelegate
|
||||
* v2.?
|
||||
* 2011-03-31 JPP - Split into its own file
|
||||
*
|
||||
* Copyright (C) 2011-2015 Phillip Piper
|
||||
*
|
||||
* This program is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*
|
||||
* If you wish to use this code in a closed source application, please contact phillip.piper@gmail.com.
|
||||
*/
|
||||
|
||||
using System;
|
||||
using System.Windows.Forms;
|
||||
using System.Drawing;
|
||||
|
||||
namespace BrightIdeasSoftware {
|
||||
|
||||
#region Delegate declarations
|
||||
|
||||
/// <summary>
|
||||
/// These delegates are used to extract an aspect from a row object
|
||||
/// </summary>
|
||||
public delegate Object AspectGetterDelegate(Object rowObject);
|
||||
|
||||
/// <summary>
|
||||
/// These delegates are used to put a changed value back into a model object
|
||||
/// </summary>
|
||||
public delegate void AspectPutterDelegate(Object rowObject, Object newValue);
|
||||
|
||||
/// <summary>
|
||||
/// These delegates can be used to convert an aspect value to a display string,
|
||||
/// instead of using the default ToString()
|
||||
/// </summary>
|
||||
public delegate string AspectToStringConverterDelegate(Object value);
|
||||
|
||||
/// <summary>
|
||||
/// These delegates are used to get the tooltip for a cell
|
||||
/// </summary>
|
||||
public delegate String CellToolTipGetterDelegate(OLVColumn column, Object modelObject);
|
||||
|
||||
/// <summary>
|
||||
/// These delegates are used to the state of the checkbox for a row object.
|
||||
/// </summary>
|
||||
/// <remarks><para>
|
||||
/// For reasons known only to someone in Microsoft, we can only set
|
||||
/// a boolean on the ListViewItem to indicate it's "checked-ness", but when
|
||||
/// we receive update events, we have to use a tristate CheckState. So we can
|
||||
/// be told about an indeterminate state, but we can't set it ourselves.
|
||||
/// </para>
|
||||
/// <para>As of version 2.0, we can now return indeterminate state.</para>
|
||||
/// </remarks>
|
||||
public delegate CheckState CheckStateGetterDelegate(Object rowObject);
|
||||
|
||||
/// <summary>
|
||||
/// These delegates are used to get the state of the checkbox for a row object.
|
||||
/// </summary>
|
||||
/// <param name="rowObject"></param>
|
||||
/// <returns></returns>
|
||||
public delegate bool BooleanCheckStateGetterDelegate(Object rowObject);
|
||||
|
||||
/// <summary>
|
||||
/// These delegates are used to put a changed check state back into a model object
|
||||
/// </summary>
|
||||
public delegate CheckState CheckStatePutterDelegate(Object rowObject, CheckState newValue);
|
||||
|
||||
/// <summary>
|
||||
/// These delegates are used to put a changed check state back into a model object
|
||||
/// </summary>
|
||||
/// <param name="rowObject"></param>
|
||||
/// <param name="newValue"></param>
|
||||
/// <returns></returns>
|
||||
public delegate bool BooleanCheckStatePutterDelegate(Object rowObject, bool newValue);
|
||||
|
||||
/// <summary>
|
||||
/// These delegates are used to get the renderer for a particular cell
|
||||
/// </summary>
|
||||
public delegate IRenderer CellRendererGetterDelegate(Object rowObject, OLVColumn column);
|
||||
|
||||
/// <summary>
|
||||
/// The callbacks for RightColumnClick events
|
||||
/// </summary>
|
||||
public delegate void ColumnRightClickEventHandler(object sender, ColumnClickEventArgs e);
|
||||
|
||||
/// <summary>
|
||||
/// This delegate will be used to own draw header column.
|
||||
/// </summary>
|
||||
public delegate bool HeaderDrawingDelegate(Graphics g, Rectangle r, int columnIndex, OLVColumn column, bool isPressed, HeaderStateStyle stateStyle);
|
||||
|
||||
/// <summary>
|
||||
/// This delegate is called when a group has been created but not yet made
|
||||
/// into a real ListViewGroup. The user can take this opportunity to fill
|
||||
/// in lots of other details about the group.
|
||||
/// </summary>
|
||||
public delegate void GroupFormatterDelegate(OLVGroup group, GroupingParameters parms);
|
||||
|
||||
/// <summary>
|
||||
/// These delegates are used to retrieve the object that is the key of the group to which the given row belongs.
|
||||
/// </summary>
|
||||
public delegate Object GroupKeyGetterDelegate(Object rowObject);
|
||||
|
||||
/// <summary>
|
||||
/// These delegates are used to convert a group key into a title for the group
|
||||
/// </summary>
|
||||
public delegate string GroupKeyToTitleConverterDelegate(Object groupKey);
|
||||
|
||||
/// <summary>
|
||||
/// These delegates are used to get the tooltip for a column header
|
||||
/// </summary>
|
||||
public delegate String HeaderToolTipGetterDelegate(OLVColumn column);
|
||||
|
||||
/// <summary>
|
||||
/// These delegates are used to fetch the image selector that should be used
|
||||
/// to choose an image for this column.
|
||||
/// </summary>
|
||||
public delegate Object ImageGetterDelegate(Object rowObject);
|
||||
|
||||
/// <summary>
|
||||
/// These delegates are used to draw a cell
|
||||
/// </summary>
|
||||
public delegate bool RenderDelegate(EventArgs e, Graphics g, Rectangle r, Object rowObject);
|
||||
|
||||
/// <summary>
|
||||
/// These delegates are used to fetch a row object for virtual lists
|
||||
/// </summary>
|
||||
public delegate Object RowGetterDelegate(int rowIndex);
|
||||
|
||||
/// <summary>
|
||||
/// These delegates are used to format a listviewitem before it is added to the control.
|
||||
/// </summary>
|
||||
public delegate void RowFormatterDelegate(OLVListItem olvItem);
|
||||
|
||||
/// <summary>
|
||||
/// These delegates can be used to return the array of texts that should be searched for text filtering
|
||||
/// </summary>
|
||||
public delegate string[] SearchValueGetterDelegate(Object value);
|
||||
|
||||
/// <summary>
|
||||
/// These delegates are used to sort the listview in some custom fashion
|
||||
/// </summary>
|
||||
public delegate void SortDelegate(OLVColumn column, SortOrder sortOrder);
|
||||
|
||||
/// <summary>
|
||||
/// These delegates are used to order two strings.
|
||||
/// x cannot be null. y can be null.
|
||||
/// </summary>
|
||||
public delegate int StringCompareDelegate(string x, string y);
|
||||
|
||||
#endregion
|
||||
}
|
||||
@@ -1,407 +0,0 @@
|
||||
///*
|
||||
// * DragSource.cs - Add drag source functionality to an ObjectListView
|
||||
// *
|
||||
// * UNFINISHED
|
||||
// *
|
||||
// * Author: Phillip Piper
|
||||
// * Date: 2009-03-17 5:15 PM
|
||||
// *
|
||||
// * Change log:
|
||||
// * v2.3
|
||||
// * 2009-07-06 JPP - Make sure Link is acceptable as an drop effect by default
|
||||
// * (since MS didn't make it part of the 'All' value)
|
||||
// * v2.2
|
||||
// * 2009-04-15 JPP - Separated DragSource.cs into DropSink.cs
|
||||
// * 2009-03-17 JPP - Initial version
|
||||
// *
|
||||
// * Copyright (C) 2009 Phillip Piper
|
||||
// *
|
||||
// * This program is free software: you can redistribute it and/or modify
|
||||
// * it under the terms of the GNU General Public License as published by
|
||||
// * the Free Software Foundation, either version 3 of the License, or
|
||||
// * (at your option) any later version.
|
||||
// *
|
||||
// * This program is distributed in the hope that it will be useful,
|
||||
// * but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
// * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
// * GNU General Public License for more details.
|
||||
// *
|
||||
// * You should have received a copy of the GNU General Public License
|
||||
// * along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
// *
|
||||
// * If you wish to use this code in a closed source application, please contact phillip_piper@bigfoot.com.
|
||||
// */
|
||||
|
||||
//using System;
|
||||
//using System.Collections;
|
||||
//using System.Collections.Generic;
|
||||
//using System.Text;
|
||||
//using System.Windows.Forms;
|
||||
//using System.Drawing;
|
||||
//using System.Drawing.Drawing2D;
|
||||
|
||||
//namespace BrightIdeasSoftware
|
||||
//{
|
||||
// /// <summary>
|
||||
// /// An IDragSource controls how drag out from the ObjectListView will behave
|
||||
// /// </summary>
|
||||
// public interface IDragSource
|
||||
// {
|
||||
// /// <summary>
|
||||
// /// A drag operation is beginning. Return the data object that will be used
|
||||
// /// for data transfer. Return null to prevent the drag from starting.
|
||||
// /// </summary>
|
||||
// /// <remarks>
|
||||
// /// The returned object is later passed to the GetAllowedEffect() and EndDrag()
|
||||
// /// methods.
|
||||
// /// </remarks>
|
||||
// /// <param name="olv">What ObjectListView is being dragged from.</param>
|
||||
// /// <param name="button">Which mouse button is down?</param>
|
||||
// /// <param name="item">What item was directly dragged by the user? There may be more than just this
|
||||
// /// item selected.</param>
|
||||
// /// <returns>The data object that will be used for data transfer. This will often be a subclass
|
||||
// /// of DataObject, but does not need to be.</returns>
|
||||
// Object StartDrag(ObjectListView olv, MouseButtons button, OLVListItem item);
|
||||
|
||||
// /// <summary>
|
||||
// /// What operations are possible for this drag? This controls the icon shown during the drag
|
||||
// /// </summary>
|
||||
// /// <param name="dragObject">The data object returned by StartDrag()</param>
|
||||
// /// <returns>A combination of DragDropEffects flags</returns>
|
||||
// DragDropEffects GetAllowedEffects(Object dragObject);
|
||||
|
||||
// /// <summary>
|
||||
// /// The drag operation is complete. Do whatever is necessary to complete the action.
|
||||
// /// </summary>
|
||||
// /// <param name="dragObject">The data object returned by StartDrag()</param>
|
||||
// /// <param name="effect">The value returned from GetAllowedEffects()</param>
|
||||
// void EndDrag(Object dragObject, DragDropEffects effect);
|
||||
// }
|
||||
|
||||
// /// <summary>
|
||||
// /// A do-nothing implementation of IDragSource that can be safely subclassed.
|
||||
// /// </summary>
|
||||
// public class AbstractDragSource : IDragSource
|
||||
// {
|
||||
// #region IDragSource Members
|
||||
|
||||
// /// <summary>
|
||||
// /// See IDragSource documentation
|
||||
// /// </summary>
|
||||
// /// <param name="olv"></param>
|
||||
// /// <param name="button"></param>
|
||||
// /// <param name="item"></param>
|
||||
// /// <returns></returns>
|
||||
// public virtual Object StartDrag(ObjectListView olv, MouseButtons button, OLVListItem item) {
|
||||
// return null;
|
||||
// }
|
||||
|
||||
// /// <summary>
|
||||
// /// See IDragSource documentation
|
||||
// /// </summary>
|
||||
// /// <param name="data"></param>
|
||||
// /// <returns></returns>
|
||||
// public virtual DragDropEffects GetAllowedEffects(Object data) {
|
||||
// return DragDropEffects.None;
|
||||
// }
|
||||
|
||||
// /// <summary>
|
||||
// /// See IDragSource documentation
|
||||
// /// </summary>
|
||||
// /// <param name="dragObject"></param>
|
||||
// /// <param name="effect"></param>
|
||||
// public virtual void EndDrag(Object dragObject, DragDropEffects effect) {
|
||||
// }
|
||||
|
||||
// #endregion
|
||||
// }
|
||||
|
||||
// /// <summary>
|
||||
// /// A reasonable implementation of IDragSource that provides normal
|
||||
// /// drag source functionality. It creates a data object that supports
|
||||
// /// inter-application dragging of text and HTML representation of
|
||||
// /// the dragged rows. It can optionally force a refresh of all dragged
|
||||
// /// rows when the drag is complete.
|
||||
// /// </summary>
|
||||
// /// <remarks>Subclasses can override GetDataObject() to add new
|
||||
// /// data formats to the data transfer object.</remarks>
|
||||
// public class SimpleDragSource : IDragSource
|
||||
// {
|
||||
// #region Constructors
|
||||
|
||||
// /// <summary>
|
||||
// /// Construct a SimpleDragSource
|
||||
// /// </summary>
|
||||
// public SimpleDragSource() {
|
||||
// }
|
||||
|
||||
// /// <summary>
|
||||
// /// Construct a SimpleDragSource that refreshes the dragged rows when
|
||||
// /// the drag is complete
|
||||
// /// </summary>
|
||||
// /// <param name="refreshAfterDrop"></param>
|
||||
// public SimpleDragSource(bool refreshAfterDrop) {
|
||||
// this.RefreshAfterDrop = refreshAfterDrop;
|
||||
// }
|
||||
|
||||
// #endregion
|
||||
|
||||
// #region Public properties
|
||||
|
||||
// /// <summary>
|
||||
// /// Gets or sets whether the dragged rows should be refreshed when the
|
||||
// /// drag operation is complete.
|
||||
// /// </summary>
|
||||
// public bool RefreshAfterDrop {
|
||||
// get { return refreshAfterDrop; }
|
||||
// set { refreshAfterDrop = value; }
|
||||
// }
|
||||
// private bool refreshAfterDrop;
|
||||
|
||||
// #endregion
|
||||
|
||||
// #region IDragSource Members
|
||||
|
||||
// /// <summary>
|
||||
// /// Create a DataObject when the user does a left mouse drag operation.
|
||||
// /// See IDragSource for further information.
|
||||
// /// </summary>
|
||||
// /// <param name="olv"></param>
|
||||
// /// <param name="button"></param>
|
||||
// /// <param name="item"></param>
|
||||
// /// <returns></returns>
|
||||
// public virtual Object StartDrag(ObjectListView olv, MouseButtons button, OLVListItem item) {
|
||||
// // We only drag on left mouse
|
||||
// if (button != MouseButtons.Left)
|
||||
// return null;
|
||||
|
||||
// return this.CreateDataObject(olv);
|
||||
// }
|
||||
|
||||
// /// <summary>
|
||||
// /// Which operations are allowed in the operation? By default, all operations are supported.
|
||||
// /// </summary>
|
||||
// /// <param name="data"></param>
|
||||
// /// <returns>All opertions are supported</returns>
|
||||
// public virtual DragDropEffects GetAllowedEffects(Object data) {
|
||||
// return DragDropEffects.All | DragDropEffects.Link; // why didn't MS include 'Link' in 'All'??
|
||||
// }
|
||||
|
||||
// /// <summary>
|
||||
// /// The drag operation is finished. Refreshe the dragged rows if so configured.
|
||||
// /// </summary>
|
||||
// /// <param name="dragObject"></param>
|
||||
// /// <param name="effect"></param>
|
||||
// public virtual void EndDrag(Object dragObject, DragDropEffects effect) {
|
||||
// OLVDataObject data = dragObject as OLVDataObject;
|
||||
// if (data == null)
|
||||
// return;
|
||||
|
||||
// if (this.RefreshAfterDrop)
|
||||
// data.ListView.RefreshObjects(data.ModelObjects);
|
||||
// }
|
||||
|
||||
// /// <summary>
|
||||
// /// Create a data object that will be used to as the data object
|
||||
// /// for the drag operation.
|
||||
// /// </summary>
|
||||
// /// <remarks>
|
||||
// /// Subclasses can override this method add new formats to the data object.
|
||||
// /// </remarks>
|
||||
// /// <param name="olv">The ObjectListView that is the source of the drag</param>
|
||||
// /// <returns>A data object for the drag</returns>
|
||||
// protected virtual object CreateDataObject(ObjectListView olv) {
|
||||
// OLVDataObject data = new OLVDataObject(olv);
|
||||
// data.CreateTextFormats();
|
||||
// return data;
|
||||
// }
|
||||
|
||||
// #endregion
|
||||
// }
|
||||
|
||||
// /// <summary>
|
||||
// /// A data transfer object that knows how to transform a list of model
|
||||
// /// objects into a text and HTML representation.
|
||||
// /// </summary>
|
||||
// public class OLVDataObject : DataObject
|
||||
// {
|
||||
// #region Life and death
|
||||
|
||||
// /// <summary>
|
||||
// /// Create a data object from the selected objects in the given ObjectListView
|
||||
// /// </summary>
|
||||
// /// <param name="olv">The source of the data object</param>
|
||||
// public OLVDataObject(ObjectListView olv) : this(olv, olv.SelectedObjects) {
|
||||
// }
|
||||
|
||||
// /// <summary>
|
||||
// /// Create a data object which operates on the given model objects
|
||||
// /// in the given ObjectListView
|
||||
// /// </summary>
|
||||
// /// <param name="olv">The source of the data object</param>
|
||||
// /// <param name="modelObjects">The model objects to be put into the data object</param>
|
||||
// public OLVDataObject(ObjectListView olv, IList modelObjects) {
|
||||
// this.objectListView = olv;
|
||||
// this.modelObjects = modelObjects;
|
||||
// this.includeHiddenColumns = olv.IncludeHiddenColumnsInDataTransfer;
|
||||
// this.includeColumnHeaders = olv.IncludeColumnHeadersInCopy;
|
||||
// }
|
||||
|
||||
// #endregion
|
||||
|
||||
// #region Properties
|
||||
|
||||
// /// <summary>
|
||||
// /// Gets or sets whether hidden columns will also be included in the text
|
||||
// /// and HTML representation. If this is false, only visible columns will
|
||||
// /// be included.
|
||||
// /// </summary>
|
||||
// public bool IncludeHiddenColumns {
|
||||
// get { return includeHiddenColumns; }
|
||||
// }
|
||||
// private bool includeHiddenColumns;
|
||||
|
||||
// /// <summary>
|
||||
// /// Gets or sets whether column headers will also be included in the text
|
||||
// /// and HTML representation.
|
||||
// /// </summary>
|
||||
// public bool IncludeColumnHeaders
|
||||
// {
|
||||
// get { return includeColumnHeaders; }
|
||||
// }
|
||||
// private bool includeColumnHeaders;
|
||||
|
||||
// /// <summary>
|
||||
// /// Gets the ObjectListView that is being used as the source of the data
|
||||
// /// </summary>
|
||||
// public ObjectListView ListView {
|
||||
// get { return objectListView; }
|
||||
// }
|
||||
// private ObjectListView objectListView;
|
||||
|
||||
// /// <summary>
|
||||
// /// Gets the model objects that are to be placed in the data object
|
||||
// /// </summary>
|
||||
// public IList ModelObjects {
|
||||
// get { return modelObjects; }
|
||||
// }
|
||||
// private IList modelObjects = new ArrayList();
|
||||
|
||||
// #endregion
|
||||
|
||||
// /// <summary>
|
||||
// /// Put a text and HTML representation of our model objects
|
||||
// /// into the data object.
|
||||
// /// </summary>
|
||||
// public void CreateTextFormats() {
|
||||
// IList<OLVColumn> columns = this.IncludeHiddenColumns ? this.ListView.AllColumns : this.ListView.ColumnsInDisplayOrder;
|
||||
|
||||
// // Build text and html versions of the selection
|
||||
// StringBuilder sbText = new StringBuilder();
|
||||
// StringBuilder sbHtml = new StringBuilder("<table>");
|
||||
|
||||
// // Include column headers
|
||||
// if (includeColumnHeaders)
|
||||
// {
|
||||
// sbHtml.Append("<tr><td>");
|
||||
// foreach (OLVColumn col in columns)
|
||||
// {
|
||||
// if (col != columns[0])
|
||||
// {
|
||||
// sbText.Append("\t");
|
||||
// sbHtml.Append("</td><td>");
|
||||
// }
|
||||
// string strValue = col.Text;
|
||||
// sbText.Append(strValue);
|
||||
// sbHtml.Append(strValue); //TODO: Should encode the string value
|
||||
// }
|
||||
// sbText.AppendLine();
|
||||
// sbHtml.AppendLine("</td></tr>");
|
||||
// }
|
||||
|
||||
// foreach (object modelObject in this.ModelObjects)
|
||||
// {
|
||||
// sbHtml.Append("<tr><td>");
|
||||
// foreach (OLVColumn col in columns) {
|
||||
// if (col != columns[0]) {
|
||||
// sbText.Append("\t");
|
||||
// sbHtml.Append("</td><td>");
|
||||
// }
|
||||
// string strValue = col.GetStringValue(modelObject);
|
||||
// sbText.Append(strValue);
|
||||
// sbHtml.Append(strValue); //TODO: Should encode the string value
|
||||
// }
|
||||
// sbText.AppendLine();
|
||||
// sbHtml.AppendLine("</td></tr>");
|
||||
// }
|
||||
// sbHtml.AppendLine("</table>");
|
||||
|
||||
// // Put both the text and html versions onto the clipboard.
|
||||
// // For some reason, SetText() with UnicodeText doesn't set the basic CF_TEXT format,
|
||||
// // but using SetData() does.
|
||||
// //this.SetText(sbText.ToString(), TextDataFormat.UnicodeText);
|
||||
// this.SetData(sbText.ToString());
|
||||
// this.SetText(ConvertToHtmlFragment(sbHtml.ToString()), TextDataFormat.Html);
|
||||
// }
|
||||
|
||||
// /// <summary>
|
||||
// /// Make a HTML representation of our model objects
|
||||
// /// </summary>
|
||||
// public string CreateHtml() {
|
||||
// IList<OLVColumn> columns = this.ListView.ColumnsInDisplayOrder;
|
||||
|
||||
// // Build html version of the selection
|
||||
// StringBuilder sbHtml = new StringBuilder("<table>");
|
||||
|
||||
// foreach (object modelObject in this.ModelObjects) {
|
||||
// sbHtml.Append("<tr><td>");
|
||||
// foreach (OLVColumn col in columns) {
|
||||
// if (col != columns[0]) {
|
||||
// sbHtml.Append("</td><td>");
|
||||
// }
|
||||
// string strValue = col.GetStringValue(modelObject);
|
||||
// sbHtml.Append(strValue); //TODO: Should encode the string value
|
||||
// }
|
||||
// sbHtml.AppendLine("</td></tr>");
|
||||
// }
|
||||
// sbHtml.AppendLine("</table>");
|
||||
|
||||
// return sbHtml.ToString();
|
||||
// }
|
||||
|
||||
// /// <summary>
|
||||
// /// Convert the fragment of HTML into the Clipboards HTML format.
|
||||
// /// </summary>
|
||||
// /// <remarks>The HTML format is found here http://msdn2.microsoft.com/en-us/library/aa767917.aspx
|
||||
// /// </remarks>
|
||||
// /// <param name="fragment">The HTML to put onto the clipboard. It must be valid HTML!</param>
|
||||
// /// <returns>A string that can be put onto the clipboard and will be recognized as HTML</returns>
|
||||
// private string ConvertToHtmlFragment(string fragment) {
|
||||
// // Minimal implementation of HTML clipboard format
|
||||
// string source = "http://www.codeproject.com/KB/list/ObjectListView.aspx";
|
||||
|
||||
// const String MARKER_BLOCK =
|
||||
// "Version:1.0\r\n" +
|
||||
// "StartHTML:{0,8}\r\n" +
|
||||
// "EndHTML:{1,8}\r\n" +
|
||||
// "StartFragment:{2,8}\r\n" +
|
||||
// "EndFragment:{3,8}\r\n" +
|
||||
// "StartSelection:{2,8}\r\n" +
|
||||
// "EndSelection:{3,8}\r\n" +
|
||||
// "SourceURL:{4}\r\n" +
|
||||
// "{5}";
|
||||
|
||||
// int prefixLength = String.Format(MARKER_BLOCK, 0, 0, 0, 0, source, "").Length;
|
||||
|
||||
// const String DEFAULT_HTML_BODY =
|
||||
// "<!DOCTYPE HTML PUBLIC \"-//W3C//DTD HTML 4.0 Transitional//EN\">" +
|
||||
// "<HTML><HEAD></HEAD><BODY><!--StartFragment-->{0}<!--EndFragment--></BODY></HTML>";
|
||||
|
||||
// string html = String.Format(DEFAULT_HTML_BODY, fragment);
|
||||
// int startFragment = prefixLength + html.IndexOf(fragment);
|
||||
// int endFragment = startFragment + fragment.Length;
|
||||
|
||||
// return String.Format(MARKER_BLOCK, prefixLength, prefixLength + html.Length, startFragment, endFragment, source, html);
|
||||
// }
|
||||
// }
|
||||
//}
|
||||
@@ -1,104 +0,0 @@
|
||||
/*
|
||||
* Enums - All enum definitions used in ObjectListView
|
||||
*
|
||||
* Author: Phillip Piper
|
||||
* Date: 31-March-2011 5:53 pm
|
||||
*
|
||||
* Change log:
|
||||
* 2011-03-31 JPP - Split into its own file
|
||||
*
|
||||
* Copyright (C) 2011-2014 Phillip Piper
|
||||
*
|
||||
* This program is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*
|
||||
* If you wish to use this code in a closed source application, please contact phillip.piper@gmail.com.
|
||||
*/
|
||||
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Text;
|
||||
|
||||
namespace BrightIdeasSoftware {
|
||||
|
||||
public partial class ObjectListView {
|
||||
/// <summary>
|
||||
/// How does a user indicate that they want to edit cells?
|
||||
/// </summary>
|
||||
public enum CellEditActivateMode {
|
||||
/// <summary>
|
||||
/// This list cannot be edited. F2 does nothing.
|
||||
/// </summary>
|
||||
None = 0,
|
||||
|
||||
/// <summary>
|
||||
/// A single click on a <strong>subitem</strong> will edit the value. Single clicking the primary column,
|
||||
/// selects the row just like normal. The user must press F2 to edit the primary column.
|
||||
/// </summary>
|
||||
SingleClick = 1,
|
||||
|
||||
/// <summary>
|
||||
/// Double clicking a subitem or the primary column will edit that cell.
|
||||
/// F2 will edit the primary column.
|
||||
/// </summary>
|
||||
DoubleClick = 2,
|
||||
|
||||
/// <summary>
|
||||
/// Pressing F2 is the only way to edit the cells. Once the primary column is being edited,
|
||||
/// the other cells in the row can be edited by pressing Tab.
|
||||
/// </summary>
|
||||
F2Only = 3,
|
||||
|
||||
/// <summary>
|
||||
/// A single click on a <strong>any</strong> cell will edit the value, even the primary column.
|
||||
/// </summary>
|
||||
SingleClickAlways = 4,
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// These values specify how column selection will be presented to the user
|
||||
/// </summary>
|
||||
public enum ColumnSelectBehaviour {
|
||||
/// <summary>
|
||||
/// No column selection will be presented
|
||||
/// </summary>
|
||||
None,
|
||||
|
||||
/// <summary>
|
||||
/// The columns will be show in the main menu
|
||||
/// </summary>
|
||||
InlineMenu,
|
||||
|
||||
/// <summary>
|
||||
/// The columns will be shown in a submenu
|
||||
/// </summary>
|
||||
Submenu,
|
||||
|
||||
/// <summary>
|
||||
/// A model dialog will be presented to allow the user to choose columns
|
||||
/// </summary>
|
||||
ModelDialog,
|
||||
|
||||
/*
|
||||
* NonModelDialog is just a little bit tricky since the OLV can change views while the dialog is showing
|
||||
* So, just comment this out for the time being.
|
||||
|
||||
/// <summary>
|
||||
/// A non-model dialog will be presented to allow the user to choose columns
|
||||
/// </summary>
|
||||
NonModelDialog
|
||||
*
|
||||
*/
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,175 +0,0 @@
|
||||
/*
|
||||
* GroupingParameters - All the data that is used to create groups in an ObjectListView
|
||||
*
|
||||
* Author: Phillip Piper
|
||||
* Date: 31-March-2011 5:53 pm
|
||||
*
|
||||
* Change log:
|
||||
* 2011-03-31 JPP - Split into its own file
|
||||
*
|
||||
* Copyright (C) 2011-2014 Phillip Piper
|
||||
*
|
||||
* This program is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*
|
||||
* If you wish to use this code in a closed source application, please contact phillip.piper@gmail.com.
|
||||
*/
|
||||
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Text;
|
||||
using System.Windows.Forms;
|
||||
|
||||
namespace BrightIdeasSoftware {
|
||||
|
||||
/// <summary>
|
||||
/// This class contains all the settings used when groups are created
|
||||
/// </summary>
|
||||
public class GroupingParameters {
|
||||
/// <summary>
|
||||
/// Create a GroupingParameters
|
||||
/// </summary>
|
||||
/// <param name="olv"></param>
|
||||
/// <param name="groupByColumn"></param>
|
||||
/// <param name="groupByOrder"></param>
|
||||
/// <param name="column"></param>
|
||||
/// <param name="order"></param>
|
||||
/// <param name="secondaryColumn"></param>
|
||||
/// <param name="secondaryOrder"></param>
|
||||
/// <param name="titleFormat"></param>
|
||||
/// <param name="titleSingularFormat"></param>
|
||||
/// <param name="sortItemsByPrimaryColumn"></param>
|
||||
public GroupingParameters(ObjectListView olv, OLVColumn groupByColumn, SortOrder groupByOrder,
|
||||
OLVColumn column, SortOrder order, OLVColumn secondaryColumn, SortOrder secondaryOrder,
|
||||
string titleFormat, string titleSingularFormat, bool sortItemsByPrimaryColumn) {
|
||||
this.ListView = olv;
|
||||
this.GroupByColumn = groupByColumn;
|
||||
this.GroupByOrder = groupByOrder;
|
||||
this.PrimarySort = column;
|
||||
this.PrimarySortOrder = order;
|
||||
this.SecondarySort = secondaryColumn;
|
||||
this.SecondarySortOrder = secondaryOrder;
|
||||
this.SortItemsByPrimaryColumn = sortItemsByPrimaryColumn;
|
||||
this.TitleFormat = titleFormat;
|
||||
this.TitleSingularFormat = titleSingularFormat;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets the ObjectListView being grouped
|
||||
/// </summary>
|
||||
public ObjectListView ListView {
|
||||
get { return this.listView; }
|
||||
set { this.listView = value; }
|
||||
}
|
||||
private ObjectListView listView;
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets the column used to create groups
|
||||
/// </summary>
|
||||
public OLVColumn GroupByColumn {
|
||||
get { return this.groupByColumn; }
|
||||
set { this.groupByColumn = value; }
|
||||
}
|
||||
private OLVColumn groupByColumn;
|
||||
|
||||
/// <summary>
|
||||
/// In what order will the groups themselves be sorted?
|
||||
/// </summary>
|
||||
public SortOrder GroupByOrder {
|
||||
get { return this.groupByOrder; }
|
||||
set { this.groupByOrder = value; }
|
||||
}
|
||||
private SortOrder groupByOrder;
|
||||
|
||||
/// <summary>
|
||||
/// If this is set, this comparer will be used to order the groups
|
||||
/// </summary>
|
||||
public IComparer<OLVGroup> GroupComparer {
|
||||
get { return this.groupComparer; }
|
||||
set { this.groupComparer = value; }
|
||||
}
|
||||
private IComparer<OLVGroup> groupComparer;
|
||||
|
||||
/// <summary>
|
||||
/// If this is set, this comparer will be used to order items within each group
|
||||
/// </summary>
|
||||
public IComparer<OLVListItem> ItemComparer {
|
||||
get { return this.itemComparer; }
|
||||
set { this.itemComparer = value; }
|
||||
}
|
||||
private IComparer<OLVListItem> itemComparer;
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets the column that will be the primary sort
|
||||
/// </summary>
|
||||
public OLVColumn PrimarySort {
|
||||
get { return this.primarySort; }
|
||||
set { this.primarySort = value; }
|
||||
}
|
||||
private OLVColumn primarySort;
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets the ordering for the primary sort
|
||||
/// </summary>
|
||||
public SortOrder PrimarySortOrder {
|
||||
get { return this.primarySortOrder; }
|
||||
set { this.primarySortOrder = value; }
|
||||
}
|
||||
private SortOrder primarySortOrder;
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets the column used for secondary sorting
|
||||
/// </summary>
|
||||
public OLVColumn SecondarySort {
|
||||
get { return this.secondarySort; }
|
||||
set { this.secondarySort = value; }
|
||||
}
|
||||
private OLVColumn secondarySort;
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets the ordering for the secondary sort
|
||||
/// </summary>
|
||||
public SortOrder SecondarySortOrder {
|
||||
get { return this.secondarySortOrder; }
|
||||
set { this.secondarySortOrder = value; }
|
||||
}
|
||||
private SortOrder secondarySortOrder;
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets the title format used for groups with zero or more than one element
|
||||
/// </summary>
|
||||
public string TitleFormat {
|
||||
get { return this.titleFormat; }
|
||||
set { this.titleFormat = value; }
|
||||
}
|
||||
private string titleFormat;
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets the title format used for groups with only one element
|
||||
/// </summary>
|
||||
public string TitleSingularFormat {
|
||||
get { return this.titleSingularFormat; }
|
||||
set { this.titleSingularFormat = value; }
|
||||
}
|
||||
private string titleSingularFormat;
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets whether the items should be sorted by the primary column
|
||||
/// </summary>
|
||||
public bool SortItemsByPrimaryColumn {
|
||||
get { return this.sortItemsByPrimaryColumn; }
|
||||
set { this.sortItemsByPrimaryColumn = value; }
|
||||
}
|
||||
private bool sortItemsByPrimaryColumn;
|
||||
}
|
||||
}
|
||||
@@ -1,747 +0,0 @@
|
||||
/*
|
||||
* Groups - Enhancements to the normal ListViewGroup
|
||||
*
|
||||
* Author: Phillip Piper
|
||||
* Date: 22/08/2009 6:03PM
|
||||
*
|
||||
* Change log:
|
||||
* v2.3
|
||||
* 2009-09-09 JPP - Added Collapsed and Collapsible properties
|
||||
* 2009-09-01 JPP - Cleaned up code, added more docs
|
||||
* - Works under VS2005 again
|
||||
* 2009-08-22 JPP - Initial version
|
||||
*
|
||||
* To do:
|
||||
* - Implement subseting
|
||||
* - Implement footer items
|
||||
*
|
||||
* Copyright (C) 2009-2014 Phillip Piper
|
||||
*
|
||||
* This program is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*
|
||||
* If you wish to use this code in a closed source application, please contact phillip.piper@gmail.com.
|
||||
*/
|
||||
|
||||
using System;
|
||||
using System.Collections;
|
||||
using System.Collections.Generic;
|
||||
using System.Reflection;
|
||||
using System.Windows.Forms;
|
||||
using System.Runtime.InteropServices;
|
||||
|
||||
namespace BrightIdeasSoftware
|
||||
{
|
||||
/// <summary>
|
||||
/// These values indicate what is the state of the group. These values
|
||||
/// are taken directly from the SDK and many are not used by ObjectListView.
|
||||
/// </summary>
|
||||
[Flags]
|
||||
public enum GroupState
|
||||
{
|
||||
/// <summary>
|
||||
/// Normal
|
||||
/// </summary>
|
||||
LVGS_NORMAL = 0x0,
|
||||
|
||||
/// <summary>
|
||||
/// Collapsed
|
||||
/// </summary>
|
||||
LVGS_COLLAPSED = 0x1,
|
||||
|
||||
/// <summary>
|
||||
/// Hidden
|
||||
/// </summary>
|
||||
LVGS_HIDDEN = 0x2,
|
||||
|
||||
/// <summary>
|
||||
/// NoHeader
|
||||
/// </summary>
|
||||
LVGS_NOHEADER = 0x4,
|
||||
|
||||
/// <summary>
|
||||
/// Can be collapsed
|
||||
/// </summary>
|
||||
LVGS_COLLAPSIBLE = 0x8,
|
||||
|
||||
/// <summary>
|
||||
/// Has focus
|
||||
/// </summary>
|
||||
LVGS_FOCUSED = 0x10,
|
||||
|
||||
/// <summary>
|
||||
/// Is Selected
|
||||
/// </summary>
|
||||
LVGS_SELECTED = 0x20,
|
||||
|
||||
/// <summary>
|
||||
/// Is subsetted
|
||||
/// </summary>
|
||||
LVGS_SUBSETED = 0x40,
|
||||
|
||||
/// <summary>
|
||||
/// Subset link has focus
|
||||
/// </summary>
|
||||
LVGS_SUBSETLINKFOCUSED = 0x80,
|
||||
|
||||
/// <summary>
|
||||
/// All styles
|
||||
/// </summary>
|
||||
LVGS_ALL = 0xFFFF
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// This mask indicates which members of a LVGROUP have valid data. These values
|
||||
/// are taken directly from the SDK and many are not used by ObjectListView.
|
||||
/// </summary>
|
||||
[Flags]
|
||||
public enum GroupMask
|
||||
{
|
||||
/// <summary>
|
||||
/// No mask
|
||||
/// </summary>
|
||||
LVGF_NONE = 0,
|
||||
|
||||
/// <summary>
|
||||
/// Group has header
|
||||
/// </summary>
|
||||
LVGF_HEADER = 1,
|
||||
|
||||
/// <summary>
|
||||
/// Group has footer
|
||||
/// </summary>
|
||||
LVGF_FOOTER = 2,
|
||||
|
||||
/// <summary>
|
||||
/// Group has state
|
||||
/// </summary>
|
||||
LVGF_STATE = 4,
|
||||
|
||||
/// <summary>
|
||||
///
|
||||
/// </summary>
|
||||
LVGF_ALIGN = 8,
|
||||
|
||||
/// <summary>
|
||||
///
|
||||
/// </summary>
|
||||
LVGF_GROUPID = 0x10,
|
||||
|
||||
/// <summary>
|
||||
/// pszSubtitle is valid
|
||||
/// </summary>
|
||||
LVGF_SUBTITLE = 0x00100,
|
||||
|
||||
/// <summary>
|
||||
/// pszTask is valid
|
||||
/// </summary>
|
||||
LVGF_TASK = 0x00200,
|
||||
|
||||
/// <summary>
|
||||
/// pszDescriptionTop is valid
|
||||
/// </summary>
|
||||
LVGF_DESCRIPTIONTOP = 0x00400,
|
||||
|
||||
/// <summary>
|
||||
/// pszDescriptionBottom is valid
|
||||
/// </summary>
|
||||
LVGF_DESCRIPTIONBOTTOM = 0x00800,
|
||||
|
||||
/// <summary>
|
||||
/// iTitleImage is valid
|
||||
/// </summary>
|
||||
LVGF_TITLEIMAGE = 0x01000,
|
||||
|
||||
/// <summary>
|
||||
/// iExtendedImage is valid
|
||||
/// </summary>
|
||||
LVGF_EXTENDEDIMAGE = 0x02000,
|
||||
|
||||
/// <summary>
|
||||
/// iFirstItem and cItems are valid
|
||||
/// </summary>
|
||||
LVGF_ITEMS = 0x04000,
|
||||
|
||||
/// <summary>
|
||||
/// pszSubsetTitle is valid
|
||||
/// </summary>
|
||||
LVGF_SUBSET = 0x08000,
|
||||
|
||||
/// <summary>
|
||||
/// readonly, cItems holds count of items in visible subset, iFirstItem is valid
|
||||
/// </summary>
|
||||
LVGF_SUBSETITEMS = 0x10000
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// This mask indicates which members of a GROUPMETRICS structure are valid
|
||||
/// </summary>
|
||||
[Flags]
|
||||
public enum GroupMetricsMask
|
||||
{
|
||||
/// <summary>
|
||||
///
|
||||
/// </summary>
|
||||
LVGMF_NONE = 0,
|
||||
|
||||
/// <summary>
|
||||
///
|
||||
/// </summary>
|
||||
LVGMF_BORDERSIZE = 1,
|
||||
|
||||
/// <summary>
|
||||
///
|
||||
/// </summary>
|
||||
LVGMF_BORDERCOLOR = 2,
|
||||
|
||||
/// <summary>
|
||||
///
|
||||
/// </summary>
|
||||
LVGMF_TEXTCOLOR = 4
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Instances of this class enhance the capabilities of a normal ListViewGroup,
|
||||
/// enabling the functionality that was released in v6 of the common controls.
|
||||
/// </summary>
|
||||
/// <remarks>
|
||||
/// <para>
|
||||
/// In this implementation (2009-09), these objects are essentially passive.
|
||||
/// Setting properties does not automatically change the associated group in
|
||||
/// the listview. Collapsed and Collapsible are two exceptions to this and
|
||||
/// give immediate results.
|
||||
/// </para>
|
||||
/// <para>
|
||||
/// This really should be a subclass of ListViewGroup, but that class is
|
||||
/// sealed (why is that?). So this class provides the same interface as a
|
||||
/// ListViewGroup, plus many other new properties.
|
||||
/// </para>
|
||||
/// </remarks>
|
||||
public class OLVGroup
|
||||
{
|
||||
#region Creation
|
||||
|
||||
/// <summary>
|
||||
/// Create an OLVGroup
|
||||
/// </summary>
|
||||
public OLVGroup() : this("Default group header") {
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Create a group with the given title
|
||||
/// </summary>
|
||||
/// <param name="header">Title of the group</param>
|
||||
public OLVGroup(string header) {
|
||||
this.Header = header;
|
||||
this.Id = OLVGroup.nextId++;
|
||||
this.TitleImage = -1;
|
||||
this.ExtendedImage = -1;
|
||||
}
|
||||
private static int nextId;
|
||||
|
||||
#endregion
|
||||
|
||||
#region Public properties
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets the bottom description of the group
|
||||
/// </summary>
|
||||
/// <remarks>
|
||||
/// Descriptions only appear when group is centered and there is a title image
|
||||
/// </remarks>
|
||||
public string BottomDescription {
|
||||
get { return this.bottomDescription; }
|
||||
set { this.bottomDescription = value; }
|
||||
}
|
||||
private string bottomDescription;
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets whether or not this group is collapsed
|
||||
/// </summary>
|
||||
public bool Collapsed {
|
||||
get { return this.GetOneState(GroupState.LVGS_COLLAPSED); }
|
||||
set { this.SetOneState(value, GroupState.LVGS_COLLAPSED); }
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets whether or not this group can be collapsed
|
||||
/// </summary>
|
||||
public bool Collapsible {
|
||||
get { return this.GetOneState(GroupState.LVGS_COLLAPSIBLE); }
|
||||
set { this.SetOneState(value, GroupState.LVGS_COLLAPSIBLE); }
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets some representation of the contents of this group
|
||||
/// </summary>
|
||||
/// <remarks>This is user defined (like Tag)</remarks>
|
||||
public IList Contents {
|
||||
get { return this.contents; }
|
||||
set { this.contents = value; }
|
||||
}
|
||||
private IList contents;
|
||||
|
||||
/// <summary>
|
||||
/// Gets whether this group has been created.
|
||||
/// </summary>
|
||||
public bool Created {
|
||||
get { return this.ListView != null; }
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets the int or string that will select the extended image to be shown against the title
|
||||
/// </summary>
|
||||
public object ExtendedImage {
|
||||
get { return this.extendedImage; }
|
||||
set { this.extendedImage = value; }
|
||||
}
|
||||
private object extendedImage;
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets the footer of the group
|
||||
/// </summary>
|
||||
public string Footer {
|
||||
get { return this.footer; }
|
||||
set { this.footer = value; }
|
||||
}
|
||||
private string footer;
|
||||
|
||||
/// <summary>
|
||||
/// Gets the internal id of our associated ListViewGroup.
|
||||
/// </summary>
|
||||
public int GroupId {
|
||||
get {
|
||||
if (this.ListViewGroup == null)
|
||||
return this.Id;
|
||||
|
||||
// Use reflection to get around the access control on the ID property
|
||||
if (OLVGroup.groupIdPropInfo == null) {
|
||||
OLVGroup.groupIdPropInfo = typeof(ListViewGroup).GetProperty("ID",
|
||||
BindingFlags.NonPublic | BindingFlags.Instance);
|
||||
System.Diagnostics.Debug.Assert(OLVGroup.groupIdPropInfo != null);
|
||||
}
|
||||
|
||||
int? groupId = OLVGroup.groupIdPropInfo.GetValue(this.ListViewGroup, null) as int?;
|
||||
return groupId.HasValue ? groupId.Value : -1;
|
||||
}
|
||||
}
|
||||
private static PropertyInfo groupIdPropInfo;
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets the header of the group
|
||||
/// </summary>
|
||||
public string Header {
|
||||
get { return this.header; }
|
||||
set { this.header = value; }
|
||||
}
|
||||
private string header;
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets the horizontal alignment of the group header
|
||||
/// </summary>
|
||||
public HorizontalAlignment HeaderAlignment {
|
||||
get { return this.headerAlignment; }
|
||||
set { this.headerAlignment = value; }
|
||||
}
|
||||
private HorizontalAlignment headerAlignment;
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets the internally created id of the group
|
||||
/// </summary>
|
||||
public int Id {
|
||||
get { return this.id; }
|
||||
set { this.id = value; }
|
||||
}
|
||||
private int id;
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets ListViewItems that are members of this group
|
||||
/// </summary>
|
||||
/// <remarks>Listener of the BeforeCreatingGroups event can populate this collection.
|
||||
/// It is only used on non-virtual lists.</remarks>
|
||||
public IList<OLVListItem> Items {
|
||||
get { return this.items; }
|
||||
set { this.items = value; }
|
||||
}
|
||||
private IList<OLVListItem> items = new List<OLVListItem>();
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets the key that was used to partition objects into this group
|
||||
/// </summary>
|
||||
/// <remarks>This is user defined (like Tag)</remarks>
|
||||
public object Key {
|
||||
get { return this.key; }
|
||||
set { this.key = value; }
|
||||
}
|
||||
private object key;
|
||||
|
||||
/// <summary>
|
||||
/// Gets the ObjectListView that this group belongs to
|
||||
/// </summary>
|
||||
/// <remarks>If this is null, the group has not yet been created.</remarks>
|
||||
public ObjectListView ListView {
|
||||
get { return this.listView; }
|
||||
protected set { this.listView = value; }
|
||||
}
|
||||
private ObjectListView listView;
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets the name of the group
|
||||
/// </summary>
|
||||
/// <remarks>As of 2009-09-01, this property is not used.</remarks>
|
||||
public string Name {
|
||||
get { return this.name; }
|
||||
set { this.name = value; }
|
||||
}
|
||||
private string name;
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets whether this group is focused
|
||||
/// </summary>
|
||||
public bool Focused
|
||||
{
|
||||
get { return this.GetOneState(GroupState.LVGS_FOCUSED); }
|
||||
set { this.SetOneState(value, GroupState.LVGS_FOCUSED); }
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets whether this group is selected
|
||||
/// </summary>
|
||||
public bool Selected
|
||||
{
|
||||
get { return this.GetOneState(GroupState.LVGS_SELECTED); }
|
||||
set { this.SetOneState(value, GroupState.LVGS_SELECTED); }
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets the text that will show that this group is subsetted
|
||||
/// </summary>
|
||||
/// <remarks>
|
||||
/// As of WinSDK v7.0, subsetting of group is officially unimplemented.
|
||||
/// We can get around this using undocumented interfaces and may do so.
|
||||
/// </remarks>
|
||||
public string SubsetTitle {
|
||||
get { return this.subsetTitle; }
|
||||
set { this.subsetTitle = value; }
|
||||
}
|
||||
private string subsetTitle;
|
||||
|
||||
/// <summary>
|
||||
/// Gets or set the subtitleof the task
|
||||
/// </summary>
|
||||
public string Subtitle {
|
||||
get { return this.subtitle; }
|
||||
set { this.subtitle = value; }
|
||||
}
|
||||
private string subtitle;
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets the value by which this group will be sorted.
|
||||
/// </summary>
|
||||
public IComparable SortValue {
|
||||
get { return this.sortValue; }
|
||||
set { this.sortValue = value; }
|
||||
}
|
||||
private IComparable sortValue;
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets the state of the group
|
||||
/// </summary>
|
||||
public GroupState State {
|
||||
get { return this.state; }
|
||||
set { this.state = value; }
|
||||
}
|
||||
private GroupState state;
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets which bits of State are valid
|
||||
/// </summary>
|
||||
public GroupState StateMask {
|
||||
get { return this.stateMask; }
|
||||
set { this.stateMask = value; }
|
||||
}
|
||||
private GroupState stateMask;
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets whether this group is showing only a subset of its elements
|
||||
/// </summary>
|
||||
/// <remarks>
|
||||
/// As of WinSDK v7.0, this property officially does nothing.
|
||||
/// </remarks>
|
||||
public bool Subseted {
|
||||
get { return this.GetOneState(GroupState.LVGS_SUBSETED); }
|
||||
set { this.SetOneState(value, GroupState.LVGS_SUBSETED); }
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets the user-defined data attached to this group
|
||||
/// </summary>
|
||||
public object Tag {
|
||||
get { return this.tag; }
|
||||
set { this.tag = value; }
|
||||
}
|
||||
private object tag;
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets the task of this group
|
||||
/// </summary>
|
||||
/// <remarks>This task is the clickable text that appears on the right margin
|
||||
/// of the group header.</remarks>
|
||||
public string Task {
|
||||
get { return this.task; }
|
||||
set { this.task = value; }
|
||||
}
|
||||
private string task;
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets the int or string that will select the image to be shown against the title
|
||||
/// </summary>
|
||||
public object TitleImage {
|
||||
get { return this.titleImage; }
|
||||
set { this.titleImage = value; }
|
||||
}
|
||||
private object titleImage;
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets the top description of the group
|
||||
/// </summary>
|
||||
/// <remarks>
|
||||
/// Descriptions only appear when group is centered and there is a title image
|
||||
/// </remarks>
|
||||
public string TopDescription {
|
||||
get { return this.topDescription; }
|
||||
set { this.topDescription = value; }
|
||||
}
|
||||
private string topDescription;
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets the number of items that are within this group.
|
||||
/// </summary>
|
||||
/// <remarks>This should only be used for virtual groups.</remarks>
|
||||
public int VirtualItemCount {
|
||||
get { return this.virtualItemCount; }
|
||||
set { this.virtualItemCount = value; }
|
||||
}
|
||||
private int virtualItemCount;
|
||||
|
||||
#endregion
|
||||
|
||||
#region Protected properties
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets the ListViewGroup that is shadowed by this group.
|
||||
/// </summary>
|
||||
/// <remarks>For virtual groups, this will always be null.</remarks>
|
||||
protected ListViewGroup ListViewGroup {
|
||||
get { return this.listViewGroup; }
|
||||
set { this.listViewGroup = value; }
|
||||
}
|
||||
private ListViewGroup listViewGroup;
|
||||
#endregion
|
||||
|
||||
#region Calculations/Conversions
|
||||
|
||||
/// <summary>
|
||||
/// Calculate the index into the group image list of the given image selector
|
||||
/// </summary>
|
||||
/// <param name="imageSelector"></param>
|
||||
/// <returns></returns>
|
||||
public int GetImageIndex(object imageSelector) {
|
||||
if (imageSelector == null || this.ListView == null || this.ListView.GroupImageList == null)
|
||||
return -1;
|
||||
|
||||
if (imageSelector is Int32)
|
||||
return (int)imageSelector;
|
||||
|
||||
String imageSelectorAsString = imageSelector as String;
|
||||
if (imageSelectorAsString != null)
|
||||
return this.ListView.GroupImageList.Images.IndexOfKey(imageSelectorAsString);
|
||||
|
||||
return -1;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Convert this object to a string representation
|
||||
/// </summary>
|
||||
/// <returns></returns>
|
||||
public override string ToString() {
|
||||
return this.Header;
|
||||
}
|
||||
|
||||
#endregion
|
||||
|
||||
#region Commands
|
||||
|
||||
/// <summary>
|
||||
/// Insert a native group into the underlying Windows control,
|
||||
/// *without* using a ListViewGroup
|
||||
/// </summary>
|
||||
/// <param name="olv"></param>
|
||||
/// <remarks>This is used when creating virtual groups</remarks>
|
||||
public void InsertGroupNewStyle(ObjectListView olv) {
|
||||
this.ListView = olv;
|
||||
NativeMethods.InsertGroup(olv, this.AsNativeGroup(true));
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Insert a native group into the underlying control via a ListViewGroup
|
||||
/// </summary>
|
||||
/// <param name="olv"></param>
|
||||
public void InsertGroupOldStyle(ObjectListView olv) {
|
||||
this.ListView = olv;
|
||||
|
||||
// Create/update the associated ListViewGroup
|
||||
if (this.ListViewGroup == null)
|
||||
this.ListViewGroup = new ListViewGroup();
|
||||
this.ListViewGroup.Header = this.Header;
|
||||
this.ListViewGroup.HeaderAlignment = this.HeaderAlignment;
|
||||
this.ListViewGroup.Name = this.Name;
|
||||
|
||||
// Remember which OLVGroup created the ListViewGroup
|
||||
this.ListViewGroup.Tag = this;
|
||||
|
||||
// Add the group to the control
|
||||
olv.Groups.Add(this.ListViewGroup);
|
||||
|
||||
// Add any extra information
|
||||
NativeMethods.SetGroupInfo(olv, this.GroupId, this.AsNativeGroup(false));
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Change the members of the group to match the current contents of Items,
|
||||
/// using a ListViewGroup
|
||||
/// </summary>
|
||||
public void SetItemsOldStyle() {
|
||||
List<OLVListItem> list = this.Items as List<OLVListItem>;
|
||||
if (list == null) {
|
||||
foreach (OLVListItem item in this.Items) {
|
||||
this.ListViewGroup.Items.Add(item);
|
||||
}
|
||||
} else {
|
||||
this.ListViewGroup.Items.AddRange(list.ToArray());
|
||||
}
|
||||
}
|
||||
|
||||
#endregion
|
||||
|
||||
#region Implementation
|
||||
|
||||
/// <summary>
|
||||
/// Create a native LVGROUP structure that matches this group
|
||||
/// </summary>
|
||||
internal NativeMethods.LVGROUP2 AsNativeGroup(bool withId) {
|
||||
|
||||
NativeMethods.LVGROUP2 group = new NativeMethods.LVGROUP2();
|
||||
group.cbSize = (uint)Marshal.SizeOf(typeof(NativeMethods.LVGROUP2));
|
||||
group.mask = (uint)(GroupMask.LVGF_HEADER ^ GroupMask.LVGF_ALIGN ^ GroupMask.LVGF_STATE);
|
||||
group.pszHeader = this.Header;
|
||||
group.uAlign = (uint)this.HeaderAlignment;
|
||||
group.stateMask = (uint)this.StateMask;
|
||||
group.state = (uint)this.State;
|
||||
|
||||
if (withId) {
|
||||
group.iGroupId = this.GroupId;
|
||||
group.mask ^= (uint)GroupMask.LVGF_GROUPID;
|
||||
}
|
||||
|
||||
if (!String.IsNullOrEmpty(this.Footer)) {
|
||||
group.pszFooter = this.Footer;
|
||||
group.mask ^= (uint)GroupMask.LVGF_FOOTER;
|
||||
}
|
||||
|
||||
if (!String.IsNullOrEmpty(this.Subtitle)) {
|
||||
group.pszSubtitle = this.Subtitle;
|
||||
group.mask ^= (uint)GroupMask.LVGF_SUBTITLE;
|
||||
}
|
||||
|
||||
if (!String.IsNullOrEmpty(this.Task)) {
|
||||
group.pszTask = this.Task;
|
||||
group.mask ^= (uint)GroupMask.LVGF_TASK;
|
||||
}
|
||||
|
||||
if (!String.IsNullOrEmpty(this.TopDescription)) {
|
||||
group.pszDescriptionTop = this.TopDescription;
|
||||
group.mask ^= (uint)GroupMask.LVGF_DESCRIPTIONTOP;
|
||||
}
|
||||
|
||||
if (!String.IsNullOrEmpty(this.BottomDescription)) {
|
||||
group.pszDescriptionBottom = this.BottomDescription;
|
||||
group.mask ^= (uint)GroupMask.LVGF_DESCRIPTIONBOTTOM;
|
||||
}
|
||||
|
||||
int imageIndex = this.GetImageIndex(this.TitleImage);
|
||||
if (imageIndex >= 0) {
|
||||
group.iTitleImage = imageIndex;
|
||||
group.mask ^= (uint)GroupMask.LVGF_TITLEIMAGE;
|
||||
}
|
||||
|
||||
imageIndex = this.GetImageIndex(this.ExtendedImage);
|
||||
if (imageIndex >= 0) {
|
||||
group.iExtendedImage = imageIndex;
|
||||
group.mask ^= (uint)GroupMask.LVGF_EXTENDEDIMAGE;
|
||||
}
|
||||
|
||||
if (!String.IsNullOrEmpty(this.SubsetTitle)) {
|
||||
group.pszSubsetTitle = this.SubsetTitle;
|
||||
group.mask ^= (uint)GroupMask.LVGF_SUBSET;
|
||||
}
|
||||
|
||||
if (this.VirtualItemCount > 0) {
|
||||
group.cItems = this.VirtualItemCount;
|
||||
group.mask ^= (uint)GroupMask.LVGF_ITEMS;
|
||||
}
|
||||
|
||||
return group;
|
||||
}
|
||||
|
||||
private bool GetOneState(GroupState mask) {
|
||||
if (this.Created)
|
||||
this.State = this.GetState();
|
||||
return (this.State & mask) == mask;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Get the current state of this group from the underlying control
|
||||
/// </summary>
|
||||
protected GroupState GetState() {
|
||||
return NativeMethods.GetGroupState(this.ListView, this.GroupId, GroupState.LVGS_ALL);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Get the current state of this group from the underlying control
|
||||
/// </summary>
|
||||
protected int SetState(GroupState newState, GroupState mask) {
|
||||
NativeMethods.LVGROUP2 group = new NativeMethods.LVGROUP2();
|
||||
group.cbSize = ((uint)Marshal.SizeOf(typeof(NativeMethods.LVGROUP2)));
|
||||
group.mask = (uint)GroupMask.LVGF_STATE;
|
||||
group.state = (uint)newState;
|
||||
group.stateMask = (uint)mask;
|
||||
return NativeMethods.SetGroupInfo(this.ListView, this.GroupId, group);
|
||||
}
|
||||
|
||||
private void SetOneState(bool value, GroupState mask)
|
||||
{
|
||||
this.StateMask ^= mask;
|
||||
if (value)
|
||||
this.State ^= mask;
|
||||
else
|
||||
this.State &= ~mask;
|
||||
|
||||
if (this.Created)
|
||||
this.SetState(this.State, mask);
|
||||
}
|
||||
|
||||
#endregion
|
||||
|
||||
}
|
||||
}
|
||||
@@ -1,559 +0,0 @@
|
||||
/*
|
||||
* Munger - An Interface pattern on getting and setting values from object through Reflection
|
||||
*
|
||||
* Author: Phillip Piper
|
||||
* Date: 28/11/2008 17:15
|
||||
*
|
||||
* Change log:
|
||||
* v2.5.1
|
||||
* 2012-05-01 JPP - Added IgnoreMissingAspects property
|
||||
* v2.5
|
||||
* 2011-05-20 JPP - Accessing through an indexer when the target had both a integer and
|
||||
* a string indexer didn't work reliably.
|
||||
* v2.4.1
|
||||
* 2010-08-10 JPP - Refactored into Munger/SimpleMunger. 3x faster!
|
||||
* v2.3
|
||||
* 2009-02-15 JPP - Made Munger a public class
|
||||
* 2009-01-20 JPP - Made the Munger capable of handling indexed access.
|
||||
* Incidentally, this removed the ugliness that the last change introduced.
|
||||
* 2009-01-18 JPP - Handle target objects from a DataListView (normally DataRowViews)
|
||||
* v2.0
|
||||
* 2008-11-28 JPP Initial version
|
||||
*
|
||||
* TO DO:
|
||||
*
|
||||
* Copyright (C) 2006-2014 Phillip Piper
|
||||
*
|
||||
* This program is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*
|
||||
* If you wish to use this code in a closed source application, please contact phillip.piper@gmail.com.
|
||||
*/
|
||||
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Reflection;
|
||||
|
||||
namespace BrightIdeasSoftware
|
||||
{
|
||||
/// <summary>
|
||||
/// An instance of Munger gets a value from or puts a value into a target object. The property
|
||||
/// to be peeked (or poked) is determined from a string. The peeking or poking is done using reflection.
|
||||
/// </summary>
|
||||
/// <remarks>
|
||||
/// Name of the aspect to be peeked can be a field, property or parameterless method. The name of an
|
||||
/// aspect to poke can be a field, writable property or single parameter method.
|
||||
/// <para>
|
||||
/// Aspect names can be dotted to chain a series of references.
|
||||
/// </para>
|
||||
/// <example>Order.Customer.HomeAddress.State</example>
|
||||
/// </remarks>
|
||||
public class Munger
|
||||
{
|
||||
#region Life and death
|
||||
|
||||
/// <summary>
|
||||
/// Create a do nothing Munger
|
||||
/// </summary>
|
||||
public Munger()
|
||||
{
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Create a Munger that works on the given aspect name
|
||||
/// </summary>
|
||||
/// <param name="aspectName">The name of the </param>
|
||||
public Munger(String aspectName)
|
||||
{
|
||||
this.AspectName = aspectName;
|
||||
}
|
||||
|
||||
#endregion
|
||||
|
||||
#region Static utility methods
|
||||
|
||||
/// <summary>
|
||||
/// A helper method to put the given value into the given aspect of the given object.
|
||||
/// </summary>
|
||||
/// <remarks>This method catches and silently ignores any errors that occur
|
||||
/// while modifying the target object</remarks>
|
||||
/// <param name="target">The object to be modified</param>
|
||||
/// <param name="propertyName">The name of the property/field to be modified</param>
|
||||
/// <param name="value">The value to be assigned</param>
|
||||
/// <returns>Did the modification work?</returns>
|
||||
public static bool PutProperty(object target, string propertyName, object value) {
|
||||
try {
|
||||
Munger munger = new Munger(propertyName);
|
||||
return munger.PutValue(target, value);
|
||||
}
|
||||
catch (MungerException) {
|
||||
// Not a lot we can do about this. Something went wrong in the bowels
|
||||
// of the property. Let's take the ostrich approach and just ignore it :-)
|
||||
|
||||
// Normally, we would never just silently ignore an exception.
|
||||
// However, in this case, this is a utility method that explicitly
|
||||
// contracts to catch and ignore errors. If this is not acceptible,
|
||||
// the programmer should not use this method.
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets whether Mungers will silently ignore missing aspect errors.
|
||||
/// </summary>
|
||||
/// <remarks>
|
||||
/// <para>
|
||||
/// By default, if a Munger is asked to fetch a field/property/method
|
||||
/// that does not exist from a model, it returns an error message, since that
|
||||
/// condition is normally a programming error. There are some use cases where
|
||||
/// this is not an error, and the munger should simply keep quiet.
|
||||
/// </para>
|
||||
/// <para>By default this is true during release builds.</para>
|
||||
/// </remarks>
|
||||
public static bool IgnoreMissingAspects {
|
||||
get { return ignoreMissingAspects; }
|
||||
set { ignoreMissingAspects = value; }
|
||||
}
|
||||
private static bool ignoreMissingAspects
|
||||
#if !DEBUG
|
||||
= true
|
||||
#endif
|
||||
;
|
||||
|
||||
#endregion
|
||||
|
||||
#region Public properties
|
||||
|
||||
/// <summary>
|
||||
/// The name of the aspect that is to be peeked or poked.
|
||||
/// </summary>
|
||||
/// <remarks>
|
||||
/// <para>
|
||||
/// This name can be a field, property or parameter-less method.
|
||||
/// </para>
|
||||
/// <para>
|
||||
/// The name can be dotted, which chains references. If any link in the chain returns
|
||||
/// null, the entire chain is considered to return null.
|
||||
/// </para>
|
||||
/// </remarks>
|
||||
/// <example>"DateOfBirth"</example>
|
||||
/// <example>"Owner.HomeAddress.Postcode"</example>
|
||||
public string AspectName
|
||||
{
|
||||
get { return aspectName; }
|
||||
set {
|
||||
aspectName = value;
|
||||
|
||||
// Clear any cache
|
||||
aspectParts = null;
|
||||
}
|
||||
}
|
||||
private string aspectName;
|
||||
|
||||
#endregion
|
||||
|
||||
|
||||
#region Public interface
|
||||
|
||||
/// <summary>
|
||||
/// Extract the value indicated by our AspectName from the given target.
|
||||
/// </summary>
|
||||
/// <remarks>If the aspect name is null or empty, this will return null.</remarks>
|
||||
/// <param name="target">The object that will be peeked</param>
|
||||
/// <returns>The value read from the target</returns>
|
||||
public Object GetValue(Object target) {
|
||||
if (this.Parts.Count == 0)
|
||||
return null;
|
||||
|
||||
try {
|
||||
return this.EvaluateParts(target, this.Parts);
|
||||
} catch (MungerException ex) {
|
||||
if (Munger.IgnoreMissingAspects)
|
||||
return null;
|
||||
|
||||
return String.Format("'{0}' is not a parameter-less method, property or field of type '{1}'",
|
||||
ex.Munger.AspectName, ex.Target.GetType());
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Extract the value indicated by our AspectName from the given target, raising exceptions
|
||||
/// if the munger fails.
|
||||
/// </summary>
|
||||
/// <remarks>If the aspect name is null or empty, this will return null.</remarks>
|
||||
/// <param name="target">The object that will be peeked</param>
|
||||
/// <returns>The value read from the target</returns>
|
||||
public Object GetValueEx(Object target) {
|
||||
if (this.Parts.Count == 0)
|
||||
return null;
|
||||
|
||||
return this.EvaluateParts(target, this.Parts);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Poke the given value into the given target indicated by our AspectName.
|
||||
/// </summary>
|
||||
/// <remarks>
|
||||
/// <para>
|
||||
/// If the AspectName is a dotted path, all the selectors bar the last
|
||||
/// are used to find the object that should be updated, and the last
|
||||
/// selector is used as the property to update on that object.
|
||||
/// </para>
|
||||
/// <para>
|
||||
/// So, if 'target' is a Person and the AspectName is "HomeAddress.Postcode",
|
||||
/// this method will first fetch "HomeAddress" property, and then try to set the
|
||||
/// "Postcode" property on the home address object.
|
||||
/// </para>
|
||||
/// </remarks>
|
||||
/// <param name="target">The object that will be poked</param>
|
||||
/// <param name="value">The value that will be poked into the target</param>
|
||||
/// <returns>bool indicating whether the put worked</returns>
|
||||
public bool PutValue(Object target, Object value)
|
||||
{
|
||||
if (this.Parts.Count == 0)
|
||||
return false;
|
||||
|
||||
SimpleMunger lastPart = this.Parts[this.Parts.Count - 1];
|
||||
|
||||
if (this.Parts.Count > 1) {
|
||||
List<SimpleMunger> parts = new List<SimpleMunger>(this.Parts);
|
||||
parts.RemoveAt(parts.Count - 1);
|
||||
try {
|
||||
target = this.EvaluateParts(target, parts);
|
||||
} catch (MungerException ex) {
|
||||
this.ReportPutValueException(ex);
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
if (target != null) {
|
||||
try {
|
||||
return lastPart.PutValue(target, value);
|
||||
} catch (MungerException ex) {
|
||||
this.ReportPutValueException(ex);
|
||||
}
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
#endregion
|
||||
|
||||
#region Implementation
|
||||
|
||||
/// <summary>
|
||||
/// Gets the list of SimpleMungers that match our AspectName
|
||||
/// </summary>
|
||||
private IList<SimpleMunger> Parts {
|
||||
get {
|
||||
if (aspectParts == null)
|
||||
aspectParts = BuildParts(this.AspectName);
|
||||
return aspectParts;
|
||||
}
|
||||
}
|
||||
private IList<SimpleMunger> aspectParts;
|
||||
|
||||
/// <summary>
|
||||
/// Convert a possibly dotted AspectName into a list of SimpleMungers
|
||||
/// </summary>
|
||||
/// <param name="aspect"></param>
|
||||
/// <returns></returns>
|
||||
private IList<SimpleMunger> BuildParts(string aspect) {
|
||||
List<SimpleMunger> parts = new List<SimpleMunger>();
|
||||
if (!String.IsNullOrEmpty(aspect)) {
|
||||
foreach (string part in aspect.Split('.')) {
|
||||
parts.Add(new SimpleMunger(part.Trim()));
|
||||
}
|
||||
}
|
||||
return parts;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Evaluate the given chain of SimpleMungers against an initial target.
|
||||
/// </summary>
|
||||
/// <param name="target"></param>
|
||||
/// <param name="parts"></param>
|
||||
/// <returns></returns>
|
||||
private object EvaluateParts(object target, IList<SimpleMunger> parts) {
|
||||
foreach (SimpleMunger part in parts) {
|
||||
if (target == null)
|
||||
break;
|
||||
target = part.GetValue(target);
|
||||
}
|
||||
return target;
|
||||
}
|
||||
|
||||
private void ReportPutValueException(MungerException ex) {
|
||||
//TODO: How should we report this error?
|
||||
System.Diagnostics.Debug.WriteLine("PutValue failed");
|
||||
System.Diagnostics.Debug.WriteLine(String.Format("- Culprit aspect: {0}", ex.Munger.AspectName));
|
||||
System.Diagnostics.Debug.WriteLine(String.Format("- Target: {0} of type {1}", ex.Target, ex.Target.GetType()));
|
||||
System.Diagnostics.Debug.WriteLine(String.Format("- Inner exception: {0}", ex.InnerException));
|
||||
}
|
||||
|
||||
#endregion
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// A SimpleMunger deals with a single property/field/method on its target.
|
||||
/// </summary>
|
||||
/// <remarks>
|
||||
/// Munger uses a chain of these resolve a dotted aspect name.
|
||||
/// </remarks>
|
||||
/// <remarks>
|
||||
/// Create a SimpleMunger
|
||||
/// </remarks>
|
||||
/// <param name="aspectName"></param>
|
||||
public class SimpleMunger(String aspectName)
|
||||
{
|
||||
|
||||
#region Life and death
|
||||
|
||||
#endregion
|
||||
|
||||
#region Public properties
|
||||
|
||||
/// <summary>
|
||||
/// The name of the aspect that is to be peeked or poked.
|
||||
/// </summary>
|
||||
/// <remarks>
|
||||
/// <para>
|
||||
/// This name can be a field, property or method.
|
||||
/// When using a method to get a value, the method must be parameter-less.
|
||||
/// When using a method to set a value, the method must accept 1 parameter.
|
||||
/// </para>
|
||||
/// <para>
|
||||
/// It cannot be a dotted name.
|
||||
/// </para>
|
||||
/// </remarks>
|
||||
public string AspectName {
|
||||
get { return aspectName; }
|
||||
}
|
||||
private readonly string aspectName = aspectName;
|
||||
|
||||
#endregion
|
||||
|
||||
#region Public interface
|
||||
|
||||
/// <summary>
|
||||
/// Get a value from the given target
|
||||
/// </summary>
|
||||
/// <param name="target"></param>
|
||||
/// <returns></returns>
|
||||
public Object GetValue(Object target) {
|
||||
if (target == null)
|
||||
return null;
|
||||
|
||||
this.ResolveName(target, this.AspectName, 0);
|
||||
|
||||
try {
|
||||
if (this.resolvedPropertyInfo != null)
|
||||
return this.resolvedPropertyInfo.GetValue(target, null);
|
||||
|
||||
if (this.resolvedMethodInfo != null)
|
||||
return this.resolvedMethodInfo.Invoke(target, null);
|
||||
|
||||
if (this.resolvedFieldInfo != null)
|
||||
return this.resolvedFieldInfo.GetValue(target);
|
||||
|
||||
// If that didn't work, try to use the indexer property.
|
||||
// This covers things like dictionaries and DataRows.
|
||||
if (this.indexerPropertyInfo != null)
|
||||
return this.indexerPropertyInfo.GetValue(target, new object[] { this.AspectName });
|
||||
} catch (Exception ex) {
|
||||
// Lots of things can do wrong in these invocations
|
||||
throw new MungerException(this, target, ex);
|
||||
}
|
||||
|
||||
// If we get to here, we couldn't find a match for the aspect
|
||||
throw new MungerException(this, target, new MissingMethodException());
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Poke the given value into the given target indicated by our AspectName.
|
||||
/// </summary>
|
||||
/// <param name="target">The object that will be poked</param>
|
||||
/// <param name="value">The value that will be poked into the target</param>
|
||||
/// <returns>bool indicating if the put worked</returns>
|
||||
public bool PutValue(object target, object value) {
|
||||
if (target == null)
|
||||
return false;
|
||||
|
||||
this.ResolveName(target, this.AspectName, 1);
|
||||
|
||||
try {
|
||||
if (this.resolvedPropertyInfo != null) {
|
||||
this.resolvedPropertyInfo.SetValue(target, value, null);
|
||||
return true;
|
||||
}
|
||||
|
||||
if (this.resolvedMethodInfo != null) {
|
||||
this.resolvedMethodInfo.Invoke(target, new object[] { value });
|
||||
return true;
|
||||
}
|
||||
|
||||
if (this.resolvedFieldInfo != null) {
|
||||
this.resolvedFieldInfo.SetValue(target, value);
|
||||
return true;
|
||||
}
|
||||
|
||||
// If that didn't work, try to use the indexer property.
|
||||
// This covers things like dictionaries and DataRows.
|
||||
if (this.indexerPropertyInfo != null) {
|
||||
this.indexerPropertyInfo.SetValue(target, value, new object[] { this.AspectName });
|
||||
return true;
|
||||
}
|
||||
} catch (Exception ex) {
|
||||
// Lots of things can do wrong in these invocations
|
||||
throw new MungerException(this, target, ex);
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
#endregion
|
||||
|
||||
#region Implementation
|
||||
|
||||
private void ResolveName(object target, string name, int numberMethodParameters) {
|
||||
|
||||
if (cachedTargetType == target.GetType() && cachedName == name && cachedNumberParameters == numberMethodParameters)
|
||||
return;
|
||||
|
||||
cachedTargetType = target.GetType();
|
||||
cachedName = name;
|
||||
cachedNumberParameters = numberMethodParameters;
|
||||
|
||||
resolvedFieldInfo = null;
|
||||
resolvedPropertyInfo = null;
|
||||
resolvedMethodInfo = null;
|
||||
indexerPropertyInfo = null;
|
||||
|
||||
const BindingFlags flags = BindingFlags.Public | BindingFlags.Instance /*| BindingFlags.NonPublic*/;
|
||||
|
||||
foreach (PropertyInfo pinfo in target.GetType().GetProperties(flags)) {
|
||||
if (pinfo.Name == name) {
|
||||
resolvedPropertyInfo = pinfo;
|
||||
return;
|
||||
}
|
||||
|
||||
// See if we can find an string indexer property while we are here.
|
||||
// We also need to allow for old style <object> keyed collections.
|
||||
if (indexerPropertyInfo == null && pinfo.Name == "Item") {
|
||||
ParameterInfo[] par = pinfo.GetGetMethod().GetParameters();
|
||||
if (par.Length > 0) {
|
||||
Type parameterType = par[0].ParameterType;
|
||||
if (parameterType == typeof(string) || parameterType == typeof(object))
|
||||
indexerPropertyInfo = pinfo;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
foreach (FieldInfo info in target.GetType().GetFields(flags)) {
|
||||
if (info.Name == name) {
|
||||
resolvedFieldInfo = info;
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
foreach (MethodInfo info in target.GetType().GetMethods(flags)) {
|
||||
if (info.Name == name && info.GetParameters().Length == numberMethodParameters) {
|
||||
resolvedMethodInfo = info;
|
||||
return;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private Type cachedTargetType;
|
||||
private string cachedName;
|
||||
private int cachedNumberParameters;
|
||||
|
||||
private FieldInfo resolvedFieldInfo;
|
||||
private PropertyInfo resolvedPropertyInfo;
|
||||
private MethodInfo resolvedMethodInfo;
|
||||
private PropertyInfo indexerPropertyInfo;
|
||||
|
||||
#endregion
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// These exceptions are raised when a munger finds something it cannot process
|
||||
/// </summary>
|
||||
/// <remarks>
|
||||
/// Create a MungerException
|
||||
/// </remarks>
|
||||
/// <param name="munger"></param>
|
||||
/// <param name="target"></param>
|
||||
/// <param name="ex"></param>
|
||||
public class MungerException(SimpleMunger munger, object target, Exception ex) : ApplicationException("Munger failed", ex)
|
||||
{
|
||||
|
||||
/// <summary>
|
||||
/// Get the munger that raised the exception
|
||||
/// </summary>
|
||||
public SimpleMunger Munger {
|
||||
get { return munger; }
|
||||
}
|
||||
private readonly SimpleMunger munger = munger;
|
||||
|
||||
/// <summary>
|
||||
/// Gets the target that threw the exception
|
||||
/// </summary>
|
||||
public object Target {
|
||||
get { return target; }
|
||||
}
|
||||
private readonly object target = target;
|
||||
}
|
||||
|
||||
/*
|
||||
* We don't currently need this
|
||||
* 2010-08-06
|
||||
*
|
||||
|
||||
internal class SimpleBinder : Binder
|
||||
{
|
||||
public override FieldInfo BindToField(BindingFlags bindingAttr, FieldInfo[] match, object value, System.Globalization.CultureInfo culture) {
|
||||
//return Type.DefaultBinder.BindToField(
|
||||
throw new NotImplementedException();
|
||||
}
|
||||
|
||||
public override object ChangeType(object value, Type type, System.Globalization.CultureInfo culture) {
|
||||
throw new NotImplementedException();
|
||||
}
|
||||
|
||||
public override MethodBase BindToMethod(BindingFlags bindingAttr, MethodBase[] match, ref object[] args, ParameterModifier[] modifiers, System.Globalization.CultureInfo culture, string[] names, out object state) {
|
||||
throw new NotImplementedException();
|
||||
}
|
||||
|
||||
public override void ReorderArgumentArray(ref object[] args, object state) {
|
||||
throw new NotImplementedException();
|
||||
}
|
||||
|
||||
public override MethodBase SelectMethod(BindingFlags bindingAttr, MethodBase[] match, Type[] types, ParameterModifier[] modifiers) {
|
||||
throw new NotImplementedException();
|
||||
}
|
||||
|
||||
public override PropertyInfo SelectProperty(BindingFlags bindingAttr, PropertyInfo[] match, Type returnType, Type[] indexes, ParameterModifier[] modifiers) {
|
||||
if (match == null)
|
||||
throw new ArgumentNullException("match");
|
||||
|
||||
if (match.Length == 0)
|
||||
return null;
|
||||
|
||||
return match[0];
|
||||
}
|
||||
}
|
||||
*/
|
||||
|
||||
}
|
||||
@@ -1,87 +0,0 @@
|
||||
/*
|
||||
* NullableDictionary - A simple Dictionary that can handle null as a key
|
||||
*
|
||||
* Author: Phillip Piper
|
||||
* Date: 31-March-2011 5:53 pm
|
||||
*
|
||||
* Change log:
|
||||
* 2011-03-31 JPP - Split into its own file
|
||||
*
|
||||
* Copyright (C) 2011-2014 Phillip Piper
|
||||
*
|
||||
* This program is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*
|
||||
* If you wish to use this code in a closed source application, please contact phillip.piper@gmail.com.
|
||||
*/
|
||||
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Text;
|
||||
using System.Collections;
|
||||
|
||||
namespace BrightIdeasSoftware {
|
||||
|
||||
/// <summary>
|
||||
/// A simple-minded implementation of a Dictionary that can handle null as a key.
|
||||
/// </summary>
|
||||
/// <typeparam name="TKey">The type of the dictionary key</typeparam>
|
||||
/// <typeparam name="TValue">The type of the values to be stored</typeparam>
|
||||
/// <remarks>This is not a full implementation and is only meant to handle
|
||||
/// collecting groups by their keys, since groups can have null as a key value.</remarks>
|
||||
internal class NullableDictionary<TKey, TValue> : Dictionary<TKey, TValue> {
|
||||
private bool hasNullKey;
|
||||
private TValue nullValue;
|
||||
|
||||
new public TValue this[TKey key] {
|
||||
get {
|
||||
if (key != null)
|
||||
return base[key];
|
||||
|
||||
if (this.hasNullKey)
|
||||
return this.nullValue;
|
||||
|
||||
throw new KeyNotFoundException();
|
||||
}
|
||||
set {
|
||||
if (key == null) {
|
||||
this.hasNullKey = true;
|
||||
this.nullValue = value;
|
||||
} else
|
||||
base[key] = value;
|
||||
}
|
||||
}
|
||||
|
||||
new public bool ContainsKey(TKey key) {
|
||||
return key == null ? this.hasNullKey : base.ContainsKey(key);
|
||||
}
|
||||
|
||||
new public IList Keys {
|
||||
get {
|
||||
ArrayList list = new ArrayList(base.Keys);
|
||||
if (this.hasNullKey)
|
||||
list.Add(null);
|
||||
return list;
|
||||
}
|
||||
}
|
||||
|
||||
new public IList<TValue> Values {
|
||||
get {
|
||||
List<TValue> list = new List<TValue>(base.Values);
|
||||
if (this.hasNullKey)
|
||||
list.Add(this.nullValue);
|
||||
return list;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,321 +0,0 @@
|
||||
/*
|
||||
* OLVListItem - A row in an ObjectListView
|
||||
*
|
||||
* Author: Phillip Piper
|
||||
* Date: 31-March-2011 5:53 pm
|
||||
*
|
||||
* Change log:
|
||||
* 2015-08-22 JPP - Added OLVListItem.SelectedBackColor and SelectedForeColor
|
||||
* 2015-06-09 JPP - Added HasAnyHyperlinks property
|
||||
* v2.8
|
||||
* 2014-09-27 JPP - Remove faulty caching of CheckState
|
||||
* 2014-05-06 JPP - Added OLVListItem.Enabled flag
|
||||
* vOld
|
||||
* 2011-03-31 JPP - Split into its own file
|
||||
*
|
||||
* Copyright (C) 2011-2015 Phillip Piper
|
||||
*
|
||||
* This program is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*
|
||||
* If you wish to use this code in a closed source application, please contact phillip.piper@gmail.com.
|
||||
*/
|
||||
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Diagnostics;
|
||||
using System.Text;
|
||||
using System.Windows.Forms;
|
||||
using System.Drawing;
|
||||
|
||||
namespace BrightIdeasSoftware {
|
||||
|
||||
/// <summary>
|
||||
/// OLVListItems are specialized ListViewItems that know which row object they came from,
|
||||
/// and the row index at which they are displayed, even when in group view mode. They
|
||||
/// also know the image they should draw against themselves
|
||||
/// </summary>
|
||||
public class OLVListItem : ListViewItem {
|
||||
#region Constructors
|
||||
|
||||
/// <summary>
|
||||
/// Create a OLVListItem for the given row object
|
||||
/// </summary>
|
||||
public OLVListItem(object rowObject) {
|
||||
this.rowObject = rowObject;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Create a OLVListItem for the given row object, represented by the given string and image
|
||||
/// </summary>
|
||||
public OLVListItem(object rowObject, string text, Object image)
|
||||
: base(text, -1) {
|
||||
this.rowObject = rowObject;
|
||||
this.imageSelector = image;
|
||||
}
|
||||
|
||||
#endregion.
|
||||
|
||||
#region Properties
|
||||
|
||||
/// <summary>
|
||||
/// Gets the bounding rectangle of the item, including all subitems
|
||||
/// </summary>
|
||||
new public Rectangle Bounds {
|
||||
get {
|
||||
try {
|
||||
return base.Bounds;
|
||||
}
|
||||
catch (System.ArgumentException) {
|
||||
// If the item is part of a collapsed group, Bounds will throw an exception
|
||||
return Rectangle.Empty;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets how many pixels will be left blank around each cell of this item
|
||||
/// </summary>
|
||||
/// <remarks>This setting only takes effect when the control is owner drawn.</remarks>
|
||||
public Rectangle? CellPadding {
|
||||
get { return this.cellPadding; }
|
||||
set { this.cellPadding = value; }
|
||||
}
|
||||
private Rectangle? cellPadding;
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets how the cells of this item will be vertically aligned
|
||||
/// </summary>
|
||||
/// <remarks>This setting only takes effect when the control is owner drawn.</remarks>
|
||||
public StringAlignment? CellVerticalAlignment {
|
||||
get { return this.cellVerticalAlignment; }
|
||||
set { this.cellVerticalAlignment = value; }
|
||||
}
|
||||
private StringAlignment? cellVerticalAlignment;
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets the checkedness of this item.
|
||||
/// </summary>
|
||||
/// <remarks>
|
||||
/// Virtual lists don't handle checkboxes well, so we have to intercept attempts to change them
|
||||
/// through the items, and change them into something that will work.
|
||||
/// Unfortunately, this won't work if this property is set through the base class, since
|
||||
/// the property is not declared as virtual.
|
||||
/// </remarks>
|
||||
new public bool Checked {
|
||||
get {
|
||||
return base.Checked;
|
||||
}
|
||||
set {
|
||||
if (this.Checked != value) {
|
||||
if (value)
|
||||
((ObjectListView)this.ListView).CheckObject(this.RowObject);
|
||||
else
|
||||
((ObjectListView)this.ListView).UncheckObject(this.RowObject);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Enable tri-state checkbox.
|
||||
/// </summary>
|
||||
/// <remarks>.NET's Checked property was not built to handle tri-state checkboxes,
|
||||
/// and will return True for both Checked and Indeterminate states.</remarks>
|
||||
public CheckState CheckState {
|
||||
get {
|
||||
switch (this.StateImageIndex) {
|
||||
case 0:
|
||||
return System.Windows.Forms.CheckState.Unchecked;
|
||||
case 1:
|
||||
return System.Windows.Forms.CheckState.Checked;
|
||||
case 2:
|
||||
return System.Windows.Forms.CheckState.Indeterminate;
|
||||
default:
|
||||
return System.Windows.Forms.CheckState.Unchecked;
|
||||
}
|
||||
}
|
||||
set {
|
||||
switch (value) {
|
||||
case System.Windows.Forms.CheckState.Unchecked:
|
||||
this.StateImageIndex = 0;
|
||||
break;
|
||||
case System.Windows.Forms.CheckState.Checked:
|
||||
this.StateImageIndex = 1;
|
||||
break;
|
||||
case System.Windows.Forms.CheckState.Indeterminate:
|
||||
this.StateImageIndex = 2;
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets if this item has any decorations set for it.
|
||||
/// </summary>
|
||||
public bool HasDecoration {
|
||||
get {
|
||||
return this.decorations != null && this.decorations.Count > 0;
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets the decoration that will be drawn over this item
|
||||
/// </summary>
|
||||
/// <remarks>Setting this replaces all other decorations</remarks>
|
||||
public IDecoration Decoration {
|
||||
get {
|
||||
if (this.HasDecoration)
|
||||
return this.Decorations[0];
|
||||
else
|
||||
return null;
|
||||
}
|
||||
set {
|
||||
this.Decorations.Clear();
|
||||
if (value != null)
|
||||
this.Decorations.Add(value);
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets the collection of decorations that will be drawn over this item
|
||||
/// </summary>
|
||||
public IList<IDecoration> Decorations {
|
||||
get {
|
||||
if (this.decorations == null)
|
||||
this.decorations = new List<IDecoration>();
|
||||
return this.decorations;
|
||||
}
|
||||
}
|
||||
private IList<IDecoration> decorations;
|
||||
|
||||
/// <summary>
|
||||
/// Gets whether or not this row can be selected and activated
|
||||
/// </summary>
|
||||
public bool Enabled
|
||||
{
|
||||
get { return this.enabled; }
|
||||
internal set { this.enabled = value; }
|
||||
}
|
||||
private bool enabled;
|
||||
|
||||
/// <summary>
|
||||
/// Gets whether any cell on this item is showing a hyperlink
|
||||
/// </summary>
|
||||
public bool HasAnyHyperlinks {
|
||||
get {
|
||||
foreach (OLVListSubItem subItem in this.SubItems) {
|
||||
if (!String.IsNullOrEmpty(subItem.Url))
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Get or set the image that should be shown against this item
|
||||
/// </summary>
|
||||
/// <remarks><para>This can be an Image, a string or an int. A string or an int will
|
||||
/// be used as an index into the small image list.</para></remarks>
|
||||
public Object ImageSelector {
|
||||
get { return imageSelector; }
|
||||
set {
|
||||
imageSelector = value;
|
||||
if (value is Int32)
|
||||
this.ImageIndex = (Int32)value;
|
||||
else if (value is String)
|
||||
this.ImageKey = (String)value;
|
||||
else
|
||||
this.ImageIndex = -1;
|
||||
}
|
||||
}
|
||||
private Object imageSelector;
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets the the model object that is source of the data for this list item.
|
||||
/// </summary>
|
||||
public object RowObject {
|
||||
get { return rowObject; }
|
||||
set { rowObject = value; }
|
||||
}
|
||||
private object rowObject;
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets the color that will be used for this row's background when it is selected and
|
||||
/// the control is focused.
|
||||
/// </summary>
|
||||
/// <remarks>
|
||||
/// <para>To work reliably, this property must be set during a FormatRow event.</para>
|
||||
/// <para>
|
||||
/// If this is not set, the normal selection BackColor will be used.
|
||||
/// </para>
|
||||
/// </remarks>
|
||||
public Color? SelectedBackColor {
|
||||
get { return this.selectedBackColor; }
|
||||
set { this.selectedBackColor = value; }
|
||||
}
|
||||
private Color? selectedBackColor;
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets the color that will be used for this row's foreground when it is selected and
|
||||
/// the control is focused.
|
||||
/// </summary>
|
||||
/// <remarks>
|
||||
/// <para>To work reliably, this property must be set during a FormatRow event.</para>
|
||||
/// <para>
|
||||
/// If this is not set, the normal selection ForeColor will be used.
|
||||
/// </para>
|
||||
/// </remarks>
|
||||
public Color? SelectedForeColor
|
||||
{
|
||||
get { return this.selectedForeColor; }
|
||||
set { this.selectedForeColor = value; }
|
||||
}
|
||||
private Color? selectedForeColor;
|
||||
|
||||
#endregion
|
||||
|
||||
#region Accessing
|
||||
|
||||
/// <summary>
|
||||
/// Return the sub item at the given index
|
||||
/// </summary>
|
||||
/// <param name="index">Index of the subitem to be returned</param>
|
||||
/// <returns>An OLVListSubItem</returns>
|
||||
public virtual OLVListSubItem GetSubItem(int index) {
|
||||
if (index >= 0 && index < this.SubItems.Count)
|
||||
return (OLVListSubItem)this.SubItems[index];
|
||||
|
||||
return null;
|
||||
}
|
||||
|
||||
|
||||
/// <summary>
|
||||
/// Return bounds of the given subitem
|
||||
/// </summary>
|
||||
/// <remarks>This correctly calculates the bounds even for column 0.</remarks>
|
||||
public virtual Rectangle GetSubItemBounds(int subItemIndex) {
|
||||
if (subItemIndex == 0) {
|
||||
Rectangle r = this.Bounds;
|
||||
Point sides = NativeMethods.GetScrolledColumnSides(this.ListView, subItemIndex);
|
||||
r.X = sides.X + 1;
|
||||
r.Width = sides.Y - sides.X;
|
||||
return r;
|
||||
}
|
||||
|
||||
OLVListSubItem subItem = this.GetSubItem(subItemIndex);
|
||||
return subItem == null ? new Rectangle() : subItem.Bounds;
|
||||
}
|
||||
|
||||
#endregion
|
||||
}
|
||||
}
|
||||
@@ -1,173 +0,0 @@
|
||||
/*
|
||||
* OLVListSubItem - A single cell in an ObjectListView
|
||||
*
|
||||
* Author: Phillip Piper
|
||||
* Date: 31-March-2011 5:53 pm
|
||||
*
|
||||
* Change log:
|
||||
* 2011-03-31 JPP - Split into its own file
|
||||
*
|
||||
* Copyright (C) 2011-2014 Phillip Piper
|
||||
*
|
||||
* This program is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*
|
||||
* If you wish to use this code in a closed source application, please contact phillip.piper@gmail.com.
|
||||
*/
|
||||
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Drawing;
|
||||
using System.Text;
|
||||
using System.Windows.Forms;
|
||||
using System.ComponentModel;
|
||||
|
||||
namespace BrightIdeasSoftware {
|
||||
|
||||
/// <summary>
|
||||
/// A ListViewSubItem that knows which image should be drawn against it.
|
||||
/// </summary>
|
||||
[Browsable(false)]
|
||||
public class OLVListSubItem : ListViewItem.ListViewSubItem {
|
||||
#region Constructors
|
||||
|
||||
/// <summary>
|
||||
/// Create a OLVListSubItem
|
||||
/// </summary>
|
||||
public OLVListSubItem() {
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Create a OLVListSubItem that shows the given string and image
|
||||
/// </summary>
|
||||
public OLVListSubItem(object modelValue, string text, Object image) {
|
||||
this.ModelValue = modelValue;
|
||||
this.Text = text;
|
||||
this.ImageSelector = image;
|
||||
}
|
||||
|
||||
#endregion
|
||||
|
||||
#region Properties
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets how many pixels will be left blank around this cell
|
||||
/// </summary>
|
||||
/// <remarks>This setting only takes effect when the control is owner drawn.</remarks>
|
||||
public Rectangle? CellPadding {
|
||||
get { return this.cellPadding; }
|
||||
set { this.cellPadding = value; }
|
||||
}
|
||||
private Rectangle? cellPadding;
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets how this cell will be vertically aligned
|
||||
/// </summary>
|
||||
/// <remarks>This setting only takes effect when the control is owner drawn.</remarks>
|
||||
public StringAlignment? CellVerticalAlignment {
|
||||
get { return this.cellVerticalAlignment; }
|
||||
set { this.cellVerticalAlignment = value; }
|
||||
}
|
||||
private StringAlignment? cellVerticalAlignment;
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets the model value is being displayed by this subitem.
|
||||
/// </summary>
|
||||
public object ModelValue
|
||||
{
|
||||
get { return modelValue; }
|
||||
private set { modelValue = value; }
|
||||
}
|
||||
private object modelValue;
|
||||
|
||||
/// <summary>
|
||||
/// Gets if this subitem has any decorations set for it.
|
||||
/// </summary>
|
||||
public bool HasDecoration {
|
||||
get {
|
||||
return this.decorations != null && this.decorations.Count > 0;
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets the decoration that will be drawn over this item
|
||||
/// </summary>
|
||||
/// <remarks>Setting this replaces all other decorations</remarks>
|
||||
public IDecoration Decoration {
|
||||
get {
|
||||
return this.HasDecoration ? this.Decorations[0] : null;
|
||||
}
|
||||
set {
|
||||
this.Decorations.Clear();
|
||||
if (value != null)
|
||||
this.Decorations.Add(value);
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets the collection of decorations that will be drawn over this item
|
||||
/// </summary>
|
||||
public IList<IDecoration> Decorations {
|
||||
get {
|
||||
if (this.decorations == null)
|
||||
this.decorations = new List<IDecoration>();
|
||||
return this.decorations;
|
||||
}
|
||||
}
|
||||
private IList<IDecoration> decorations;
|
||||
|
||||
/// <summary>
|
||||
/// Get or set the image that should be shown against this item
|
||||
/// </summary>
|
||||
/// <remarks><para>This can be an Image, a string or an int. A string or an int will
|
||||
/// be used as an index into the small image list.</para></remarks>
|
||||
public Object ImageSelector {
|
||||
get { return imageSelector; }
|
||||
set { imageSelector = value; }
|
||||
}
|
||||
private Object imageSelector;
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets the url that should be invoked when this subitem is clicked
|
||||
/// </summary>
|
||||
public string Url
|
||||
{
|
||||
get { return this.url; }
|
||||
set { this.url = value; }
|
||||
}
|
||||
private string url;
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets whether this cell is selected
|
||||
/// </summary>
|
||||
public bool Selected
|
||||
{
|
||||
get { return this.selected; }
|
||||
set { this.selected = value; }
|
||||
}
|
||||
private bool selected;
|
||||
|
||||
#endregion
|
||||
|
||||
#region Implementation Properties
|
||||
|
||||
/// <summary>
|
||||
/// Return the state of the animatation of the image on this subitem.
|
||||
/// Null means there is either no image, or it is not an animation
|
||||
/// </summary>
|
||||
internal ImageRenderer.AnimationState AnimationState;
|
||||
|
||||
#endregion
|
||||
}
|
||||
|
||||
}
|
||||