Repository navigation
Expand file tree
/
Copy pathSettings.cs
More file actions
154 lines (143 loc) · 6.87 KB
/
Copy pathSettings.cs
File metadata and controls
154 lines (143 loc) · 6.87 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
// Persisted preferences: %APPDATA%\RSPaster\settings.ini
//
// Deliberately plain key=value rather than JSON. There is no in-box JSON
// parser that does not pull in another assembly reference, and a hand-rolled
// one for seven scalar fields is risk with no return. This file is also
// something a person can read and edit.
//
// What is NOT stored: the contents of the text box, ever. It routinely holds
// passwords and console commands, so it never touches disk - not as a setting,
// not as a recent-items list, not as a crash backup.
//
// C# 5 only (in-box csc).
using System;
using System.Collections.Generic;
using System.Globalization;
using System.IO;
using System.Text;
namespace RSPaster
{
public class Settings
{
public string Theme = "classic";
public int StartDelaySeconds = 3;
public int KeyDelayMs = 15;
public bool LineDelayEnabled;
public int LineDelaySeconds = 15;
public bool EnterAtEnd;
public bool ClearAfter;
public bool AlwaysOnTop = true;
public bool UnicodeMode;
public bool HideText;
// Phrase LABELS and count only. The phrase contents are secrets and
// are never written anywhere, which is the panel's core promise.
public bool PhrasesVisible;
public List<string> PhraseLabels = new List<string>();
public int WindowWidth;
public int WindowHeight;
public static string Dir
{
get
{
return Path.Combine(
Environment.GetFolderPath(Environment.SpecialFolder.ApplicationData),
"RSPaster");
}
}
public static string FilePath { get { return Path.Combine(Dir, "settings.ini"); } }
public static Settings Load()
{
Settings s = new Settings();
Dictionary<string, string> kv = new Dictionary<string, string>(StringComparer.OrdinalIgnoreCase);
try
{
if (!File.Exists(FilePath)) return s;
foreach (string raw in File.ReadAllLines(FilePath))
{
string line = raw.Trim();
if (line.Length == 0 || line[0] == '#') continue;
int eq = line.IndexOf('=');
if (eq <= 0) continue;
kv[line.Substring(0, eq).Trim()] = line.Substring(eq + 1).Trim();
}
}
catch (IOException) { return s; }
catch (UnauthorizedAccessException) { return s; }
s.Theme = Str(kv, "theme", s.Theme);
s.StartDelaySeconds = Int(kv, "startDelaySeconds", s.StartDelaySeconds, 0, 60);
s.KeyDelayMs = Int(kv, "keyDelayMs", s.KeyDelayMs, 0, 500);
s.LineDelayEnabled = Bool(kv, "lineDelayEnabled", s.LineDelayEnabled);
s.LineDelaySeconds = Int(kv, "lineDelaySeconds", s.LineDelaySeconds, 0, 600);
s.EnterAtEnd = Bool(kv, "enterAtEnd", s.EnterAtEnd);
s.ClearAfter = Bool(kv, "clearAfter", s.ClearAfter);
s.AlwaysOnTop = Bool(kv, "alwaysOnTop", s.AlwaysOnTop);
s.UnicodeMode = Bool(kv, "unicodeMode", s.UnicodeMode);
s.HideText = Bool(kv, "hideText", s.HideText);
s.PhrasesVisible = Bool(kv, "phrasesVisible", s.PhrasesVisible);
int phraseCount = Int(kv, "phraseCount", 0, 0, 6);
for (int i = 1; i <= phraseCount; i++)
s.PhraseLabels.Add(Str(kv, "phraseLabel" + i.ToString(CultureInfo.InvariantCulture), "Phrase " + i));
s.WindowWidth = Int(kv, "windowWidth", 0, 0, 10000);
s.WindowHeight = Int(kv, "windowHeight", 0, 0, 10000);
return s;
}
public void Save()
{
try
{
Directory.CreateDirectory(Dir);
StringBuilder sb = new StringBuilder();
sb.AppendLine("# RSPaster settings. The pasted text is never stored here.");
Write(sb, "theme", Theme);
Write(sb, "startDelaySeconds", StartDelaySeconds.ToString(CultureInfo.InvariantCulture));
Write(sb, "keyDelayMs", KeyDelayMs.ToString(CultureInfo.InvariantCulture));
Write(sb, "lineDelayEnabled", LineDelayEnabled ? "true" : "false");
Write(sb, "lineDelaySeconds", LineDelaySeconds.ToString(CultureInfo.InvariantCulture));
Write(sb, "enterAtEnd", EnterAtEnd ? "true" : "false");
Write(sb, "clearAfter", ClearAfter ? "true" : "false");
Write(sb, "alwaysOnTop", AlwaysOnTop ? "true" : "false");
Write(sb, "unicodeMode", UnicodeMode ? "true" : "false");
Write(sb, "hideText", HideText ? "true" : "false");
Write(sb, "phrasesVisible", PhrasesVisible ? "true" : "false");
Write(sb, "phraseCount", PhraseLabels.Count.ToString(CultureInfo.InvariantCulture));
for (int i = 0; i < PhraseLabels.Count; i++)
Write(sb, "phraseLabel" + (i + 1).ToString(CultureInfo.InvariantCulture), PhraseLabels[i]);
Write(sb, "windowWidth", WindowWidth.ToString(CultureInfo.InvariantCulture));
Write(sb, "windowHeight", WindowHeight.ToString(CultureInfo.InvariantCulture));
File.WriteAllText(FilePath, sb.ToString());
}
catch (IOException) { /* a settings file we cannot write is not worth a dialog */ }
catch (UnauthorizedAccessException) { }
}
static void Write(StringBuilder sb, string key, string value)
{
// A value carrying a line break would forge the line after it, and
// the next load would parse the forgery as a setting. Phrase labels
// are the only user-controlled value here and a single-line TextBox
// makes it hard to do by accident, but the guard costs one line.
sb.Append(key).Append('=')
.AppendLine(value.Replace('\r', ' ').Replace('\n', ' ').Trim());
}
static string Str(Dictionary<string, string> kv, string key, string fallback)
{
string v;
return kv.TryGetValue(key, out v) && v.Length > 0 ? v : fallback;
}
static int Int(Dictionary<string, string> kv, string key, int fallback, int min, int max)
{
string v;
int n;
if (!kv.TryGetValue(key, out v)) return fallback;
if (!int.TryParse(v, NumberStyles.Integer, CultureInfo.InvariantCulture, out n)) return fallback;
if (n < min) return min;
if (n > max) return max;
return n;
}
static bool Bool(Dictionary<string, string> kv, string key, bool fallback)
{
string v;
if (!kv.TryGetValue(key, out v)) return fallback;
return v.Equals("true", StringComparison.OrdinalIgnoreCase) || v == "1";
}
}
}