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