1 /** 2 * HexChat Theme Manager 3 * 4 * Copyright (C) 2012 Patrick Griffs 5 * Copyright (C) 2012 Berke Viktor 6 * 7 * This program is free software; you can redistribute it and/or modify 8 * it under the terms of the GNU General Public License as published by 9 * the Free Software Foundation; either version 2 of the License, or 10 * (at your option) any later version. 11 * 12 * This program is distributed in the hope that it will be useful, 13 * but WITHOUT ANY WARRANTY; without even the implied warranty of 14 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 15 * GNU General Public License for more details. 16 * 17 * You should have received a copy of the GNU General Public License 18 * along with this program; if not, write to the Free Software 19 * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA 20 */ 21 22 using System; 23 using System.Collections.Generic; 24 using System.Collections; 25 using System.ComponentModel; 26 using System.Data; 27 using System.Drawing; 28 using System.IO; 29 /* using System.IO.Compression; */ 30 using System.IO.Packaging; 31 using System.Linq; 32 using System.Text; 33 using System.Windows.Forms; 34 using System.Net; 35 36 namespace thememan 37 { 38 public partial class HTM : Form 39 { 40 public string hexchatdir; 41 public string themedir; 42 43 OpenFileDialog importDialog; 44 IsPortable(out string directory)45 static bool IsPortable(out string directory) 46 { 47 System.Reflection.Assembly asm = System.Reflection.Assembly.GetExecutingAssembly(); 48 directory = asm != null ? asm.Location : string.Empty; 49 if (string.IsNullOrEmpty(directory)) 50 { 51 directory = string.Empty; 52 return File.Exists("portable-mode"); 53 } 54 directory = Path.GetDirectoryName(directory); 55 return File.Exists(Path.Combine(directory, "portable-mode")); 56 } 57 HTM()58 public HTM () 59 { 60 InitializeComponent (); 61 string portableDir; 62 63 if (RunningOnWindows() && IsPortable(out portableDir)) 64 { 65 hexchatdir = Path.Combine(portableDir, "config\\"); 66 67 if (!Directory.Exists(hexchatdir)) 68 { 69 MessageBox.Show("HexChat installation not found!\nCheck your .\\config folder", "Error", MessageBoxButtons.OK, MessageBoxIcon.Error); 70 Environment.Exit(1); 71 } 72 } 73 else 74 { 75 /* Environment.SpecialFolder.ApplicationData 76 * Windows: %APPDATA% 77 * Unix: ~/.config 78 * Windows is case-insensitive so 'hexchat' should be fine for both 79 */ 80 hexchatdir = Path.Combine(Environment.GetFolderPath(Environment.SpecialFolder.ApplicationData), "hexchat"); 81 82 if (!Directory.Exists(hexchatdir)) 83 { 84 if (RunningOnWindows()) 85 { 86 MessageBox.Show("HexChat installation not found!\nCheck your %APPDATA%\\HexChat folder", "Error", MessageBoxButtons.OK, MessageBoxIcon.Error); 87 } 88 else 89 { 90 MessageBox.Show("HexChat installation not found!\nCheck your ~/.config/hexchat folder", "Error", MessageBoxButtons.OK, MessageBoxIcon.Error); 91 } 92 93 Environment.Exit(1); 94 } 95 } 96 97 themedir = Path.Combine(hexchatdir, "themes"); 98 ListThemes(); 99 100 String[] arguments = Environment.GetCommandLineArgs(); 101 if (arguments.Length > 1) 102 { 103 FileInfo fi = new FileInfo(arguments[1]); 104 attemptImport(fi); 105 } 106 } 107 RunningOnWindows()108 private bool RunningOnWindows() 109 { 110 if (Environment.OSVersion.ToString().ToLower().Contains("windows")) 111 { 112 return true; 113 } 114 else 115 { 116 return false; 117 } 118 } 119 ListThemes()120 private void ListThemes() 121 { 122 themelist.Items.Clear(); 123 124 if (Directory.Exists(themedir)) 125 { 126 foreach (string theme in Directory.GetDirectories(themedir)) 127 { 128 themelist.Items.Add(theme.Remove(0, themedir.Length + 1)); 129 } 130 } 131 else 132 { 133 Directory.CreateDirectory(themedir); 134 } 135 136 if (themelist.Items.Count == 0) 137 { 138 applybutton.Enabled = false; 139 deleteButton.Enabled = false; 140 } 141 else 142 { 143 themelist.SetSelected(0, true); 144 } 145 } 146 ShowColors(List<List<string>> themecolors)147 private void ShowColors(List<List<string>> themecolors) 148 { 149 List<Control> labels = this.Controls.OfType<Label>().Cast<Control>().OrderBy(label => label.Name).ToList(); 150 for (byte themecolor = 0; themecolor < themecolors.Count; themecolor++) 151 { 152 byte rval = Convert.ToByte(int.Parse(themecolors[themecolor][0].ToString(), System.Globalization.NumberStyles.HexNumber) / 257); 153 byte gval = Convert.ToByte(int.Parse(themecolors[themecolor][1].ToString(), System.Globalization.NumberStyles.HexNumber) / 257); 154 byte bval = Convert.ToByte(int.Parse(themecolors[themecolor][2].ToString(), System.Globalization.NumberStyles.HexNumber) / 257); 155 156 if (themecolor <= 15) 157 labels[themecolor].BackColor = Color.FromArgb(rval, gval, bval); 158 else if (themecolor == 16) 159 themecolorfgmarked.ForeColor = Color.FromArgb(rval, gval, bval); 160 else if (themecolor == 17) 161 themecolorfgmarked.BackColor = Color.FromArgb(rval, gval, bval); 162 else if (themecolor == 18) 163 themecolorfg.ForeColor = Color.FromArgb(rval, gval, bval); 164 else if (themecolor == 19) 165 { 166 themecolortextbg.BackColor = Color.FromArgb(rval, gval, bval); 167 themecolorfg.BackColor = themecolortextbg.BackColor; 168 } 169 } 170 } 171 ReadTheme(string theme)172 private List<List<string>> ReadTheme(string theme) 173 { 174 List<List<string>> themecolors = new List<List<string>>(); 175 foreach (string line in File.ReadLines(Path.Combine(themedir, theme, "colors.conf"))) 176 { 177 List<string> colors = new List<string>(); 178 List<string> colorlist = new List<string>(); 179 string[] possiblecolors = { "color_256", "color_257", "color_258", "color_259" }; 180 181 for (byte num = 16; num <=31; num++) 182 colorlist.Add("color_" + num); 183 colorlist.AddRange(possiblecolors); 184 185 string[] config = line.Split(new char[] { ' ' }); 186 if(colorlist.Contains(config[0]) == true) 187 { 188 colors.Add(config[2]); 189 colors.Add(config[3]); 190 colors.Add(config[4]); 191 themecolors.Add(colors); 192 } 193 } 194 return themecolors; 195 } 196 applybutton_Click_1(object sender, EventArgs e)197 private void applybutton_Click_1(object sender, EventArgs e) 198 { 199 DialogResult result = MessageBox.Show("HexChat must be closed and this will overwrite your current theme!\n\nDo you wish to continue?", "Warning", MessageBoxButtons.OKCancel, MessageBoxIcon.Warning); 200 if (result == DialogResult.OK) 201 { 202 File.Copy(Path.Combine(themedir, themelist.SelectedItem.ToString(), "colors.conf"), Path.Combine(hexchatdir, "colors.conf"), true); 203 if (File.Exists(Path.Combine(themedir, themelist.SelectedItem.ToString(), "pevents.conf"))) 204 { 205 File.Copy(Path.Combine(themedir, themelist.SelectedItem.ToString(), "pevents.conf"), Path.Combine(hexchatdir, "pevents.conf"), true); 206 } 207 else if (File.Exists(Path.Combine(hexchatdir, "pevents.conf"))) 208 { 209 File.Delete(Path.Combine(hexchatdir, "pevents.conf")); 210 } 211 } 212 } 213 theme_selected(object sender, EventArgs e)214 private void theme_selected(object sender, EventArgs e) 215 { 216 if (themelist.SelectedItem != null) 217 { 218 ShowColors(ReadTheme(themelist.SelectedItem.ToString())); 219 applybutton.Enabled = true; 220 deleteButton.Enabled = true; 221 } 222 } 223 importbutton_Click_1(object sender, EventArgs e)224 private void importbutton_Click_1(object sender, EventArgs e) 225 { 226 importDialog = new OpenFileDialog(); 227 importDialog.Filter = "HexChat Theme Files|*.hct"; 228 importDialog.FilterIndex = 1; 229 importDialog.FileOk += new CancelEventHandler(importdialog_FileOk); 230 importDialog.ShowDialog(); 231 } 232 importdialog_FileOk(object sender, System.ComponentModel.CancelEventArgs e)233 private void importdialog_FileOk(object sender, System.ComponentModel.CancelEventArgs e) 234 { 235 FileInfo fi = new FileInfo(importDialog.FileName); 236 attemptImport(fi); 237 } 238 attemptImport(FileInfo fi)239 private void attemptImport(FileInfo fi) 240 { 241 string themeName = fi.Name.Remove(fi.Name.Length - fi.Extension.Length); 242 int result = extractTheme(fi); 243 ListThemes(); 244 /* although a check is added to ListThemes(), this would still fail if the theme file was invalid or the theme is already installed */ 245 switch (result) 246 { 247 case 0: 248 themelist.SetSelected(themelist.FindStringExact(themeName), true); 249 /* required for command line invoking */ 250 ShowColors(ReadTheme(themeName)); 251 break; 252 case 1: 253 MessageBox.Show("This theme is already installed!", "Warning", MessageBoxButtons.OK, MessageBoxIcon.Warning); 254 themelist.SetSelected(themelist.FindStringExact(themeName), true); 255 /* required for command line invoking */ 256 ShowColors(ReadTheme(themeName)); 257 break; 258 case 2: 259 MessageBox.Show("Invalid theme file!", "Error", MessageBoxButtons.OK, MessageBoxIcon.Error); 260 break; 261 } 262 } 263 /* gzip solution, not good enough coz we need multiple files 264 * 265 public string extractTheme(FileInfo fi) 266 { 267 using (FileStream inFile = fi.OpenRead()) 268 { 269 string themeName = fi.Name.Remove(fi.Name.Length - fi.Extension.Length); 270 string destFolder = xchatdir + themedir + themeName; 271 272 if (!Directory.Exists(destFolder)) 273 { 274 Directory.CreateDirectory(destFolder); 275 } 276 277 using (FileStream outFile = File.Create(destFolder + "\\colors.conf")) 278 { 279 using (GZipStream Decompress = new GZipStream(inFile, CompressionMode.Decompress)) 280 { 281 Decompress.CopyTo(outFile); 282 } 283 } 284 return themeName; 285 } 286 } 287 */ 288 289 /* using System.IO.Package: 290 * http://weblogs.asp.net/jgalloway/archive/2007/10/25/creating-zip-archives-in-net-without-an-external-library-like-sharpziplib.aspx 291 * [Content_Types].xml must be present for every zip file 292 */ 293 294 private const long BUFFER_SIZE = 4096; 295 extractTheme(FileInfo zipFile)296 private int extractTheme(FileInfo zipFile) 297 { 298 string themeName = zipFile.Name.Remove(zipFile.Name.Length - zipFile.Extension.Length); 299 string destFolder = Path.Combine(themedir, themeName); 300 301 try 302 { 303 using (Package zip = Package.Open(zipFile.FullName, FileMode.Open, FileAccess.Read)) 304 { 305 PackagePartCollection parts = zip.GetParts(); 306 307 if (Directory.Exists(destFolder)) 308 { 309 return 1; 310 } 311 else 312 { 313 Directory.CreateDirectory(destFolder); 314 } 315 316 foreach (PackagePart part in parts) 317 { 318 /* not sure what's this good for */ 319 /* String archive = part.Uri.ToString().Replace(@"/", @"\"); */ 320 String destFile = destFolder + part.Uri.ToString(); 321 322 using (FileStream outFileStream = new FileStream(destFile, FileMode.CreateNew, FileAccess.ReadWrite)) 323 { 324 using (Stream inStream = part.GetStream()) 325 { 326 long bufferSize = inStream.Length < BUFFER_SIZE ? inStream.Length : BUFFER_SIZE; 327 byte[] buffer = new byte[bufferSize]; 328 int bytesRead = 0; 329 long bytesWritten = 0; 330 331 while ((bytesRead = inStream.Read(buffer, 0, buffer.Length)) != 0) 332 { 333 outFileStream.Write(buffer, 0, bytesRead); 334 bytesWritten += bufferSize; 335 } 336 } 337 } 338 339 340 } 341 } 342 } 343 catch (System.IO.FileFormatException) 344 { 345 return 2; 346 } 347 348 if (IsDirectoryEmpty(destFolder)) 349 { 350 Directory.Delete(destFolder); 351 return 2; 352 } 353 else 354 { 355 return 0; 356 } 357 } 358 IsDirectoryEmpty(string path)359 public bool IsDirectoryEmpty(string path) 360 { 361 return !Directory.EnumerateFileSystemEntries(path).Any(); 362 } 363 deleteButton_Click(object sender, EventArgs e)364 private void deleteButton_Click(object sender, EventArgs e) 365 { 366 DialogResult result = MessageBox.Show("Are you sure you want to delete this theme from the theme repo?\n\nYour currently applied theme won't be affected.", "Warning", MessageBoxButtons.OKCancel, MessageBoxIcon.Warning); 367 if (result == DialogResult.OK) 368 { 369 Directory.Delete(Path.Combine(themedir, themelist.SelectedItem.ToString()), true); 370 ListThemes(); 371 if (themelist.Items.Count == 0) 372 { 373 deleteButton.Enabled = false; 374 } 375 } 376 } 377 378 379 380 } 381 } 382