From bd6b3041d7d9cbb52eeb86ed5fed3b03fb7832d2 Mon Sep 17 00:00:00 2001 From: Grassmunk Date: Fri, 29 May 2020 17:24:59 -0700 Subject: [PATCH] Added new Chicago95 Plus! GUI that looks and acts like the real thing! --- Plus/PlusGUI.py | 1271 ++++++++++++++++++++++++ Plus/assets/X.png | Bin 0 -> 230 bytes Plus/assets/blank-check.png | Bin 0 -> 1187 bytes Plus/assets/check.png | Bin 0 -> 316 bytes Plus/assets/my_computer~.png | Bin 0 -> 1188 bytes Plus/assets/my_documents~.png | Bin 0 -> 1132 bytes Plus/assets/network_neighborhood~.png | Bin 0 -> 471 bytes Plus/assets/recycle_bin_empty~.png | Bin 0 -> 1196 bytes Plus/original.png | Bin 0 -> 15822 bytes Plus/plus.glade | 1311 +++++++++++++++++++++++++ 10 files changed, 2582 insertions(+) create mode 100755 Plus/PlusGUI.py create mode 100644 Plus/assets/X.png create mode 100644 Plus/assets/blank-check.png create mode 100755 Plus/assets/check.png create mode 100755 Plus/assets/my_computer~.png create mode 100755 Plus/assets/my_documents~.png create mode 100755 Plus/assets/network_neighborhood~.png create mode 100644 Plus/assets/recycle_bin_empty~.png create mode 100644 Plus/original.png create mode 100644 Plus/plus.glade diff --git a/Plus/PlusGUI.py b/Plus/PlusGUI.py new file mode 100755 index 0000000..2acca84 --- /dev/null +++ b/Plus/PlusGUI.py @@ -0,0 +1,1271 @@ +#!/usr/bin/env python3 + +from pluslib import ChicagoPlus +import logging +import sys +from pathlib import Path +from PIL import Image, ImageFont, ImageDraw, ImageEnhance, ImageOps +from pprint import pprint +import struct +import subprocess +import os +import shutil +import textwrap +import numpy as np +import array +import argparse + +import gi +gi.require_version("Gtk", "3.0") +from gi.repository import Gtk, GdkPixbuf, GLib + +#theme = sys.argv[1].split("/")[7] +print("Font List...", end=' ', flush=True) +fonts_output = subprocess.check_output(['convert', '-list', 'font']) +fonts = fonts_output.decode().split('\n') +installed_fonts = {} +for index, font in enumerate(fonts): + if "Font:" in font: + installed_fonts[fonts[index].split(":")[1].strip()] = { + fonts[index+1].split(":")[0].strip() : fonts[index+1].split(":")[1].strip(), + fonts[index+2].split(":")[0].strip() : fonts[index+2].split(":")[1].strip(), + fonts[index+3].split(":")[0].strip() : fonts[index+3].split(":")[1].strip(), + fonts[index+4].split(":")[0].strip() : fonts[index+4].split(":")[1].strip(), + fonts[index+5].split(":")[0].strip() : fonts[index+5].split(":")[1].strip() + } +print("OK", end='\n', flush=True) +sys.stdout.flush() + + +pointers = { + #windows theme # Name + "arrow" : "Normal select", + "help" : "Help select", + "appstarting" : "Working in background", + "wait" : "Busy", + "crosshair" : "Precision select", + "ibeam" : "Text Select", + "nwpen" : "Handwriting", + "no" : "NO", + "sizens" : "Vertical resize", + "sizewe" : "Horizontal resize", + "sizenwse" : "Diagonal resize 1", + "sizenesw" : "Diagonal resize 2", + "sizeall" : "Move", + "uparrow" : "Alternate select" + } + +icons = { + # Windows Themes : #Name + "my_computer" : "My Computer icon", + "network_neighborhood" : "Network Neighborhood icon", + "recycle_bin_full" : "Recycle Bin full icon", + "recycle_bin_empty" : "Recycle Bin empty icon", + "my_documents" : "My Documents icon" + } + +sounds = { + 'SystemAsterisk' : 'Asterisk', + 'Close' : 'Close program', + 'SystemHand' : 'Critical stop', + '.Default' : 'Default beep', + 'SystemDefault' : 'Default sound', + 'EmptyRecycleBin' : 'Empty Recylcle Bin', + 'SystemExclamation' : 'Exclamation', + 'SystemExit' : 'Exit Windows', + 'Maximize' : 'Maximize', + 'MenuCommand' : 'Menu command', + 'MenuPopup' : 'Menu popup', + 'Minimize' : 'Minimize', + 'Open' : 'Open program', + 'AppGPFault' : 'Program error', + 'SystemQuestion' : 'Question', + 'RestoreDown' : 'Restore down', + 'RestoreUp' : 'Restore up', + 'RingIn' : 'Ring in', + 'RingOut' : 'Ring out', + 'SystemStart' : 'Start Windows' +} + + + +class MakePreview: + def __init__(self, theme_object): + #print("Reading theme file... {}".format(theme_file)) + self.plus = theme_object + self.plus.parse_theme() + self.set_metrics() + self.set_fonts() + self.get_icons() + self.get_wallpaper() + self.plus.print_theme_config() + + self.colors = { + "buttonface": "#C0C0C0", + "buttontext": "#000000", + "inactivetitle": "#808080", + "inactivetitletext": "#DFDFDF", + "inactiveborder": "#C0C0C0", + "buttondkshadow": "#000000", + "buttonshadow": "#808080", + "buttonhilight": "#FFFFFF", + "buttonlight": "#DFDFDF", + "activetitle": "#000080", + "titletext": "#FFFFFF", + "activeborder": "#C0C0C0", + "menutext": "#000000", + "menu": "#C0C0C0", + "hilighttext": "#FFFFFF", + "hilight": "#000080", + "window": "#FFFFFE", + "windowtext": "#000000", + "scrollbar": "#FFFFFF", + "background": "#000000", + "foreground": "#000000" + } + + for i in self.colors: + + try: + self.colors[i] = (self.plus.theme_config['colors'][i]['color']) + except KeyError: + continue + + self.colors['foreground'] = self.black_or_white(self.colors['background']) + self.theme_name = self.plus.theme_config['theme_name'] + self.buttons() + self.preview_gen() +# pprint(self.colors) + print("Preview Generated") + + def return_preview(self): + self.preview_window.save("preview.png", "PNG") + return("preview.png") + + def return_preview_double(self): + self.preview_window.save("preview_double.png", "PNG") + return("preview_double.png") + + def delete_preview(self): + os.remove("preview.png") + + def delete_preview_double(self): + os.remove("preview_double.png") + + def set_fonts(self): + print("Fonts...", end=' ') + self.button_height = self.iCaptionHeight - 4 + self.button_width = self.button_height + 2 + self.lfMessageFont = self.get_font(self.msgfont) + self.lfcaptionfont = self.get_font(self.caption_font) + self.lfMenuFont = self.get_font(self.menufont) + self.arial = self.get_font("Nimbus Sans L") + self.arial_bold = self.get_font("Nimbus-Sans-L-Bold") + self.lfFont = self.get_font(self.iconfont) + + self.cursor_font = self.get_font("Nimbus-Sans-L-Bold") + self.cursor_pt = 12 + print("OK", end='\n', flush=True) + + def buttons(self): + print("Buttons...", end=' ') + buttons = ['minimize','maximize','close'] + button_width = self.button_width + button_height = self.button_height + colors = self.colors + + for button in buttons: + + img = Image.new('RGB', (button_width, button_height), color = colors['buttondkshadow']) + draw = ImageDraw.Draw(img) + draw.line([(0,0),(button_width-2, 0), (0,0),(0, button_height-2)],fill=colors['buttonhilight'], width=1) + + draw.line([(button_width-2,button_height-2),(button_height, 1)],fill=colors['buttonshadow'], width=1) + draw.line([(1,button_height-2),(button_width-2,button_height-2)],fill=colors['buttonshadow'], width=1) + + draw.line([(1,1),(button_width-3, 1), (1,1),(1, button_height-3)],fill=colors['buttonlight'], width=1) + draw.rectangle(((2, 2), (button_width-3, button_height-3)), fill=colors['buttonface'], width=1) + if button_height < 16: + if button == "minimize": + draw.line([(4,9),(10, 9)],fill=colors['buttontext'], width=2) + self.min_button = img + elif button == 'maximize': + draw.line([(3,10),(11, 10)],fill=colors['buttontext'], width=1) + draw.line([(3,2),(11, 2)],fill=colors['buttontext'], width=2) + draw.line([(3,2),(3, 10)],fill=colors['buttontext'], width=1) + draw.line([(11,2),(11, 10)],fill=colors['buttontext'], width=1) + self.max_button = img + elif button == 'close': + img.putpixel( (4, 3), tuple(int(colors['buttontext'].lstrip('#')[i:i+2], 16) for i in (0, 2, 4)) ) + img.putpixel( (5, 3), tuple(int(colors['buttontext'].lstrip('#')[i:i+2], 16) for i in (0, 2, 4)) ) + img.putpixel( (5, 4), tuple(int(colors['buttontext'].lstrip('#')[i:i+2], 16) for i in (0, 2, 4)) ) + img.putpixel( (6, 4), tuple(int(colors['buttontext'].lstrip('#')[i:i+2], 16) for i in (0, 2, 4)) ) + img.putpixel( (6, 5), tuple(int(colors['buttontext'].lstrip('#')[i:i+2], 16) for i in (0, 2, 4)) ) + img.putpixel( (7, 5), tuple(int(colors['buttontext'].lstrip('#')[i:i+2], 16) for i in (0, 2, 4)) ) + img.putpixel( (7, 6), tuple(int(colors['buttontext'].lstrip('#')[i:i+2], 16) for i in (0, 2, 4)) ) + img.putpixel( (8, 6), tuple(int(colors['buttontext'].lstrip('#')[i:i+2], 16) for i in (0, 2, 4)) ) + img.putpixel( (8, 7), tuple(int(colors['buttontext'].lstrip('#')[i:i+2], 16) for i in (0, 2, 4)) ) + img.putpixel( (9, 7), tuple(int(colors['buttontext'].lstrip('#')[i:i+2], 16) for i in (0, 2, 4)) ) + img.putpixel( (9, 8), tuple(int(colors['buttontext'].lstrip('#')[i:i+2], 16) for i in (0, 2, 4)) ) + img.putpixel( (10, 8), tuple(int(colors['buttontext'].lstrip('#')[i:i+2], 16) for i in (0, 2, 4)) ) + img.putpixel( (10, 9), tuple(int(colors['buttontext'].lstrip('#')[i:i+2], 16) for i in (0, 2, 4)) ) + img.putpixel( (11, 9), tuple(int(colors['buttontext'].lstrip('#')[i:i+2], 16) for i in (0, 2, 4)) ) + img.putpixel( (4, 9), tuple(int(colors['buttontext'].lstrip('#')[i:i+2], 16) for i in (0, 2, 4)) ) + img.putpixel( (5, 9), tuple(int(colors['buttontext'].lstrip('#')[i:i+2], 16) for i in (0, 2, 4)) ) + img.putpixel( (5, 8), tuple(int(colors['buttontext'].lstrip('#')[i:i+2], 16) for i in (0, 2, 4)) ) + img.putpixel( (6, 8), tuple(int(colors['buttontext'].lstrip('#')[i:i+2], 16) for i in (0, 2, 4)) ) + img.putpixel( (6, 7), tuple(int(colors['buttontext'].lstrip('#')[i:i+2], 16) for i in (0, 2, 4)) ) + img.putpixel( (7, 7), tuple(int(colors['buttontext'].lstrip('#')[i:i+2], 16) for i in (0, 2, 4)) ) + img.putpixel( (7, 6), tuple(int(colors['buttontext'].lstrip('#')[i:i+2], 16) for i in (0, 2, 4)) ) + img.putpixel( (8, 6), tuple(int(colors['buttontext'].lstrip('#')[i:i+2], 16) for i in (0, 2, 4)) ) + img.putpixel( (8, 5), tuple(int(colors['buttontext'].lstrip('#')[i:i+2], 16) for i in (0, 2, 4)) ) + img.putpixel( (9, 5), tuple(int(colors['buttontext'].lstrip('#')[i:i+2], 16) for i in (0, 2, 4)) ) + img.putpixel( (9, 4), tuple(int(colors['buttontext'].lstrip('#')[i:i+2], 16) for i in (0, 2, 4)) ) + img.putpixel( (10, 4), tuple(int(colors['buttontext'].lstrip('#')[i:i+2], 16) for i in (0, 2, 4)) ) + img.putpixel( (10, 3), tuple(int(colors['buttontext'].lstrip('#')[i:i+2], 16) for i in (0, 2, 4)) ) + img.putpixel( (11, 3), tuple(int(colors['buttontext'].lstrip('#')[i:i+2], 16) for i in (0, 2, 4)) ) + self.close_button = img + + else: + if button == "minimize": + draw.line([(5, button_height - 6),(button_width - 8,button_height - 6)],fill=colors['buttontext'], width=2) + self.min_button = img + elif button == 'maximize': + draw.line([(4,button_height-5),(button_width - 6 , button_height - 5)],fill=colors['buttontext'], width=1) + draw.line([(4,4),(button_width - 6, 4)],fill=colors['buttontext'], width=2) + draw.line([(4,4),(4, button_height-5)],fill=colors['buttontext'], width=1) + draw.line([(button_width - 6, 4),(button_width - 6, button_height-5)],fill=colors['buttontext'], width=1) + self.max_button = img + elif button == 'close': + X = Image.open(r"assets/X.png").convert('RGBA') + pixels = X.load() + rgb = struct.unpack('BBB',bytes.fromhex(colors['buttontext'].lstrip('#'))) + for i in range(X.size[0]): + for j in range(X.size[1]): + if pixels[i,j] == (0, 0, 0, 255): + pixels[i,j] = rgb + x_w, x_h = X.size + offset = ((button_width - x_w) // 2, (button_height - x_h) // 2) + img.paste(X, offset, mask=X) + self.close_button = img + + print("OK", end='\n', flush=True) + + def set_metrics(self): + + print("Metrics...", end=' ', flush=True) + try: + self.iCaptionHeight = self.plus.theme_config['nonclientmetrics']['iCaptionHeight'] + self.iMenuHeight = self.plus.theme_config['nonclientmetrics']['iMenuHeight'] + self.iScrollWidth = self.plus.theme_config['nonclientmetrics']['iScrollWidth'] + self.msgfont = self.plus.theme_config['nonclientmetrics']['lfMessageFont']['lfFaceName[32]'] + self.msgfont_pt = abs(self.plus.theme_config['nonclientmetrics']['lfMessageFont']['lfHeight']) + self.caption_font = self.plus.theme_config['nonclientmetrics']['lfcaptionfont']['lfFaceName[32]'] + self.caption_pt = abs(self.plus.theme_config['nonclientmetrics']['lfcaptionfont']['lfHeight']) + self.menufont = self.plus.theme_config['nonclientmetrics']['lfMenuFont']['lfFaceName[32]'] + self.menufont_pt = abs(self.plus.theme_config['nonclientmetrics']['lfMenuFont']['lfHeight']) + except: + + self.iCaptionHeight = 18 + self.iMenuHeight = 18 + self.iMenuWidth = 18 + self.iScrollHeight = 13 + self.iScrollWidth = 13 + self.msgfont = "Microsoft Sans Serif" + self.caption_font = "MS-Reference-Sans-Serif-Bold" + self.menufont = "Microsoft Sans Serif" + self.menufont_pt = 10 + self.caption_pt = 10 + self.msgfont_pt = 10 + + try: + self.iconfont = self.plus.theme_config['iconmetrics']['lfFont']['lfFaceName[32]'] + self.iconfont_pt = abs(self.plus.theme_config['iconmetrics']['lfFont']['lfHeight']) + except: + self.iconfont = "Microsoft Sans Serif" + self.iconfont_pt = 10 + if self.iconfont_pt == 8: + self.iconfont_pt = 10 + + if self.msgfont_pt == 8: + self.msgfont_pt = 10 + + if self.caption_pt == 8: + self.caption_pt = 10 + + if self.menufont_pt == 8: + self.menufont_pt = 10 + print("OK", end='\n', flush=True) + + def get_icons(self): + print("Icons...", end=' ', flush=True) + if self.plus.theme_config['icons']: + if self.plus.theme_config['icons']['my_computer']: + self.my_computer = self.make_icons(self.plus,'my_computer') + else: + self.my_computer = Image.open("assets/my_computer~.png").convert('RGBA') + + if self.plus.theme_config['icons']['network_neighborhood']: + self.network_neighborhood= self.make_icons(self.plus,'network_neighborhood') + else: + self.network_neighborhood = Image.open("assets/network_neighborhood~.png").convert('RGBA') + + if self.plus.theme_config['icons']['recycle_bin_empty']: + self.recycle_bin_empty = self.make_icons(self.plus,'recycle_bin_empty') + else: + self.recycle_bin_empty = Image.open("assets/recycle_bin_empty~.png").convert('RGBA') + + if self.plus.theme_config['icons']['my_documents']: + self.my_documents = self.make_icons(self.plus,'my_documents') + else: + self.my_documents = False + print("OK", end='\n', flush=True) + + def drawlines(self, drawcontext, xy, outline=None, width=0): + (x1, y1), (x2, y2) = xy + points = (x1, y1), (x2, y1), (x2, y2), (x1, y2), (x1, y1) + drawcontext.line(points, fill=outline, width=width) + + def black_or_white(self, background_color): + + # Counting the perceptive luminance - human eye favors green color... + R, G, B = tuple(int(background_color.lstrip("#")[i:i+2], 16) for i in (0, 2, 4)) + luminance = ( (0.299 * R) + (0.587 * G) + (0.114 * B))/255; + if (luminance > 0.5): + d = "#000000" # bright colors - black font + else: + d = "#FFFFFF" # dark colors - white font + + return d + + def get_font(self, fontname): + + if self.plus.theme_config['fonts']: + if fontname in self.plus.theme_config['fonts']: + return self.plus.theme_config['fonts'][fontname]['path'] + + + if fontname in ["MS Sans Serif", "MS Serif", "‚l‚r ‚oƒSƒVƒbƒN"]: + fontname = "Microsoft Sans Serif" + + if fontname in installed_fonts: + #print("Found installed font {}: {}".format(fontname,installed_fonts[fontname]['glyphs'])) + return installed_fonts[fontname]['glyphs'] + + elif fontname.replace(' ','-') in installed_fonts: + return installed_fonts[fontname.replace(' ','-')]['glyphs'] + + else: + for i in installed_fonts: + if fontname.lower() == i.lower(): + #print("Found installed font {}: {}".format(fontname,installed_fonts[i]['glyphs'])) + return installed_fonts[i]['glyphs'] + elif fontname.lower() in installed_fonts[i]['family'].lower(): + #print("Found installed font {}: {}".format(fontname,installed_fonts[i]['glyphs'])) + return installed_fonts[i]['glyphs'] + elif fontname.replace(' ','-').lower() in i.lower(): + #print("Found installed font {}: {}".format(fontname,installed_fonts[i]['glyphs'])) + return installed_fonts[i]['glyphs'] + + print("Font: {} NOT FOUND".format(fontname)) + + return self.get_font("Noto-Sans-Regular") + + + def preview_gen(self): + print("Preview...", end=' ', flush=True) + colors = self.colors + + tmp = Image.new('RGB', (500, 500), color = colors['activetitle']) + tmp_draw = ImageDraw.Draw(tmp) + + #This generates the Message Box title you see in the preview windoow + msgboxtitle = Image.new('RGB', (173-6, self.iCaptionHeight), color = colors['activetitle']) + draw = ImageDraw.Draw(msgboxtitle) + myFont = ImageFont.truetype(self.lfcaptionfont, self.caption_pt) + w, h = draw.textsize("Message Box", font=myFont) + font_y_offset = myFont.getoffset("Message Box")[1] + draw.fontmode = "1" + draw.text((2,int(((self.iCaptionHeight-h)-font_y_offset)/2)), "Message Box", fill=colors['titletext'], font=myFont) + + #This generates the Inactive Window title in the previre + inactive_window = Image.new('RGB', (202, self.iCaptionHeight+118+3), color = colors['buttondkshadow']) + inactivetitle = Image.new('RGB', (inactive_window.size[0]-8, self.iCaptionHeight), color = colors['inactivetitle']) + draw = ImageDraw.Draw(inactivetitle) + myFont = ImageFont.truetype(self.lfcaptionfont, self.caption_pt) + w, h = draw.textsize("Inactive Window", font=myFont) + font_y_offset = myFont.getoffset("Inactive Window")[1] + draw.fontmode = "1" + draw.text((2,int((self.iCaptionHeight-h)-font_y_offset)/2), "Inactive Window", fill=colors['inactivetitletext'], font=myFont) + + #Now we create the Active window title in multiple steps, starting with the title + activetitle = Image.new('RGB', (217, self.iCaptionHeight), color = colors['activetitle']) + draw = ImageDraw.Draw(activetitle) + myFont = ImageFont.truetype(self.lfcaptionfont, self.caption_pt) + font_y_offset = myFont.getoffset("Active Window")[1] + w, h = draw.textsize("Active Window", font=myFont) + draw.fontmode = "1" + draw.text((2,int((self.iCaptionHeight-h)-font_y_offset)/2), "Active Window", fill=colors['titletext'], font=myFont) + + #Next we draw the menubar + menufont = ImageFont.truetype(self.lfMenuFont, self.menufont_pt) + #normal_w, normal_h = tmp_draw.textsize("Normal", font=menufont) + menubar = Image.new('RGB', (activetitle.size[0], self.iMenuHeight), color = colors['menu']) + draw = ImageDraw.Draw(menubar) + #Normal menu item + normal_w, normal_h = draw.textsize("Normal", font=menufont) + font_y_offset = menufont.getoffset("Normal")[1] + draw.fontmode = "1" + draw.text((6,int(((menubar.size[1]-normal_h)-font_y_offset)/2)), "Normal", fill=colors['menutext'], font=menufont) + #Disabled menu item + disabled_w, disabled_h = draw.textsize("Disabled", font=menufont) + draw.text((normal_w+14,(int(((menubar.size[1]-normal_h)-font_y_offset)/2))+1), "Disabled", fill=colors['buttonhilight'], font=menufont) + draw.text((normal_w+13,int(((menubar.size[1]-normal_h)-font_y_offset)/2)), "Disabled", fill=colors['buttonshadow'], font=menufont) + #Selected menu item + selected_w, selected_h = draw.textsize("Selected", font=menufont) + #myFont.fontmode = "1" + #draw.text((6,int(menubar.size[1]-normal_h)/2), "Selected", fill=colors['hilighttext'], font=menufont) + selected = Image.new('RGB', (selected_w+12,menubar.size[1]), color = colors['hilight']) + selected_draw = ImageDraw.Draw(selected) + selected_draw.fontmode = "1" + font_y_offset = menufont.getoffset("Selected")[1] + selected_draw.text((int((selected.size[0]-selected_w)/2),int(((menubar.size[1]-normal_h)-font_y_offset)/2)), "Selected", fill=colors['hilighttext'], font=menufont) + menubar.paste(selected, (normal_w+13+disabled_w+6,int((menubar.size[1]-selected.size[1])/2))) + + #Now we need to create the scrollbar, first up arrow + uparrow = Image.new('RGB', (self.iScrollWidth, self.iScrollWidth), color = colors['buttonface']) + draw = ImageDraw.Draw(uparrow) + draw.line([(0,0),(uparrow.size[0]-2, 0), (0,0), (0,uparrow.size[1]-2)],fill=colors['buttonlight'], width=1) + draw.line([(1,1),(uparrow.size[0]-3, 1), (1,1), (1,uparrow.size[1]-3)],fill=colors['buttonhilight'], width=1) + draw.line([(uparrow.size[0]-1,uparrow.size[1]-1),(uparrow.size[0]-1, 0), (uparrow.size[0]-1,uparrow.size[1]-1), (0,uparrow.size[1]-1)],fill=colors['buttondkshadow'], width=1) + draw.line([(uparrow.size[0]-2,uparrow.size[1]-2),(uparrow.size[0]-2, 1), (uparrow.size[0]-2,uparrow.size[1]-2), (1,uparrow.size[1]-2)],fill=colors['buttonshadow'], width=1) + draw.polygon([(4,7), (8,7), (6,5)], fill = colors['buttontext']) + + #Next down arrow + downarrow = Image.new('RGB', (self.iScrollWidth, self.iScrollWidth), color = colors['buttonface']) + draw = ImageDraw.Draw(downarrow) + draw.line([(0,0),(downarrow.size[0]-2, 0), (0,0), (0,downarrow.size[1]-2)],fill=colors['buttonlight'], width=1) + draw.line([(1,1),(downarrow.size[0]-3, 1), (1,1), (1,downarrow.size[1]-3)],fill=colors['buttonhilight'], width=1) + draw.line([(downarrow.size[0]-1,downarrow.size[1]-1),(downarrow.size[0]-1, 0), (downarrow.size[0]-1,downarrow.size[1]-1), (0,downarrow.size[1]-1)],fill=colors['buttondkshadow'], width=1) + draw.line([(downarrow.size[0]-2,downarrow.size[1]-2),(downarrow.size[0]-2, 1), (downarrow.size[0]-2,downarrow.size[1]-2), (1,downarrow.size[1]-2)],fill=colors['buttonshadow'], width=1) + draw.polygon([(4,5), (8,5), (6,7)], fill = colors['buttontext']) + + #With all the pieces done we can now create the scroll area + window = Image.new('RGB', (217, 72), color = colors['window']) + draw = ImageDraw.Draw(window) + draw.line([(0,0),(window.size[0]-2, 0), (0,0), (0,window.size[1]-2)],fill=colors['buttonshadow'], width=1) + draw.line([(1,1),(window.size[0]-3, 1), (1,1), (1,window.size[1]-3)],fill=colors['buttondkshadow'], width=1) + draw.line([(window.size[0]-1,window.size[1]-1),(window.size[0]-1, 0), (window.size[0]-1,window.size[1]-1), (0,window.size[1]-1)],fill=colors['buttonhilight'], width=1) + draw.line([(window.size[0]-2,window.size[1]-2),(window.size[0]-2, 1), (window.size[0]-2,window.size[1]-2), (1,window.size[1]-2)],fill=colors['buttonlight'], width=1) + window_font = ImageFont.truetype(self.arial_bold, 14) + draw.fontmode = "1" + font_y_offset = window_font.getoffset("Window Text")[1] + draw.text((4,7-font_y_offset), "Window Text", fill=colors['windowtext'], font=window_font) + window.paste(uparrow, (window.size[0]-2-uparrow.size[0],2)) + draw.rectangle(((window.size[0]-2-uparrow.size[0],2+uparrow.size[1]),(window.size[0]-3, window.size[1]-3)), fill=colors['scrollbar']) + window.paste(downarrow, (window.size[0]-2-downarrow.size[0],window.size[1]-2-downarrow.size[1])) + + #And with that we have all the peices to make the active window, first we make the window + activewindow = Image.new('RGB', (225, 4+self.iCaptionHeight+1+self.iMenuHeight+1+window.size[1]+4), color = colors['buttonface']) + draw = ImageDraw.Draw(activewindow) + draw.line([(0,0),(activewindow.size[0]-2, 0), (0,0), (0,activewindow.size[1]-2)],fill=colors['buttonlight'], width=1) + draw.line([(1,1),(activewindow.size[0]-3, 1), (1,1), (1,activewindow.size[1]-3)],fill=colors['buttonhilight'], width=1) + draw.line([(activewindow.size[0]-1,activewindow.size[1]-1),(activewindow.size[0]-1, 0), (activewindow.size[0]-1,activewindow.size[1]-1), (0,activewindow.size[1]-1)],fill=colors['buttondkshadow'], width=1) + draw.line([(activewindow.size[0]-2,activewindow.size[1]-2),(activewindow.size[0]-2, 1), (activewindow.size[0]-2,activewindow.size[1]-2), (1,activewindow.size[1]-2)],fill=colors['buttonshadow'], width=1) + draw.rectangle(((2, 2), (activewindow.size[0]-3, activewindow.size[1]-3)), fill=colors['activeborder'], width=1) + draw.rectangle(((3, 3), (activewindow.size[0]-4, activewindow.size[1]-4)), fill=colors['buttonface'], width=1) + #Then we put all the pieces inside + activewindow.paste(activetitle, (4,4)) + activewindow.paste(self.close_button, (activewindow.size[0]-self.close_button.size[0]-6, 6)) + activewindow.paste(self.max_button, (activewindow.size[0]-self.close_button.size[0]-6-2-self.max_button.size[0], 6)) + activewindow.paste(self.min_button, (activewindow.size[0]-self.close_button.size[0]-6-2-self.max_button.size[0]-self.min_button.size[0], 6)) + activewindow.paste(menubar, (4, 4+activetitle.size[1]+1)) + activewindow.paste(window, (4, 4+activetitle.size[1]+1+menubar.size[1]+1)) + + # Next we create the message window, first making the 'ok' button + button = Image.new('RGB', (66, 22), color = colors['buttondkshadow']) + draw = ImageDraw.Draw(button) + draw.line([(0,0),(64, 0), (0,0), (0,20)],fill=colors['buttonhilight'], width=1) + draw.line([(button.size[0]-2,1),(button.size[0]-2,button.size[1]-2 )],fill=colors['buttonshadow'], width=1) + draw.line([(1,button.size[1]-2),(button.size[0]-2,button.size[1]-2)],fill=colors['buttonshadow'], width=1) + draw.line([(1,1),(button.size[0]-3, 1), (1,1),(1, button.size[1]-3)],fill=colors['buttonlight'], width=1) + draw.rectangle(((2, 2), (button.size[0]-3, button.size[1]-3)), fill=colors['buttonface'], width=1) + myFont = ImageFont.truetype(self.arial_bold, self.msgfont_pt) + draw.fontmode = "1" + font_y_offset = myFont.getoffset("OK")[1] + w, h = draw.textsize("OK", font=myFont) + draw.text(((66-w)/2,((22-h)-font_y_offset)/2), "OK", fill=colors['buttontext'], font=myFont) + #button.save("button.png", "PNG") + + # Then we create the window itself + myFont = ImageFont.truetype(self.arial, self.msgfont_pt) + msg_text_w, msg_text_h = draw.textsize("Message Text", font=myFont) + message_box = Image.new('RGB', (173, self.iCaptionHeight+8+msg_text_h+3+button.size[1]+6), color = colors['buttondkshadow']) + draw = ImageDraw.Draw(message_box) + draw.line([(0,0),(171, 0), (0,0), (0,message_box.size[1]-2)],fill=colors['buttonlight'], width=1) + draw.line([(message_box.size[0]-2,1),(message_box.size[0]-2,message_box.size[1]-2 )],fill=colors['buttonshadow'], width=1) + draw.line([(1,message_box.size[1]-2),(message_box.size[0]-2,message_box.size[1]-2)],fill=colors['buttonshadow'], width=1) + draw.line([(1,1),(message_box.size[0]-3, 1), (1,1),(1, message_box.size[1]-3)],fill=colors['buttonhilight'], width=1) + draw.rectangle(((2, 2), (message_box.size[0]-3, message_box.size[1]-3)), fill=colors['activeborder'], width=1) + draw.rectangle(((3, 3), (message_box.size[0]-4, message_box.size[1]-4)), fill=colors['buttonface'], width=1) + draw.fontmode = "1" + font_y_offset = myFont.getoffset("Message Text")[1] + draw.text((7,self.iCaptionHeight+8-font_y_offset), "Message Text", fill=colors['windowtext'], font=myFont) + # We then put all the peices together + message_box.paste(msgboxtitle, (3,3)) + message_box.paste(self.close_button, (173-self.close_button.size[0]-5, 5)) + message_box.paste(button, (int((173-button.size[0])/2), self.iCaptionHeight+8+msg_text_h+3)) + + # We do the same for the inactive window, first creating them window + draw = ImageDraw.Draw(inactive_window) + draw.line([(0,0),(inactive_window.size[0]-2, 0), (0,0), (0,inactive_window.size[1]-2)],fill=colors['buttonlight'], width=1) + draw.line([(inactive_window.size[0]-2,1),(inactive_window.size[0]-2,inactive_window.size[1]-2 )],fill=colors['buttonshadow'], width=1) + draw.line([(1,inactive_window.size[1]-2),(inactive_window.size[0]-2,inactive_window.size[1]-2)],fill=colors['buttonshadow'], width=1) + draw.line([(1,1),(inactive_window.size[0]-3, 1), (1,1),(1, inactive_window.size[1]-3)],fill=colors['buttonhilight'], width=1) + draw.rectangle(((2, 2), (inactive_window.size[0]-3, inactive_window.size[1]-3)), fill=colors['activeborder'], width=1) + draw.rectangle(((3, 3), (inactive_window.size[0]-4, inactive_window.size[1]-4)), fill=colors['buttonface'], width=1) + #Then putting all the pieces inside + inactive_window.paste(inactivetitle, (4,4)) + inactive_window.paste(self.close_button, (inactive_window.size[0]-self.close_button.size[0]-6, 6)) + inactive_window.paste(self.max_button, (inactive_window.size[0]-self.close_button.size[0]-6-2-self.max_button.size[0], 6)) + inactive_window.paste(self.min_button, (inactive_window.size[0]-self.close_button.size[0]-6-2-self.max_button.size[0]-self.min_button.size[0], 6)) + + height = 39 + (self.iCaptionHeight * 2) + self.iMenuHeight + message_box.size[1] + width = 4 + activewindow.size[0] + + # Now that we've created all the windows we need, next is to put them all together as one + demowindows = Image.new('RGBA', (width, height), color = (0, 0, 0, 0)) + demowindows.paste(inactive_window) + demowindows.paste(activewindow, (4,5+self.iCaptionHeight)) + demowindows.paste(message_box, (12,39 + (self.iCaptionHeight * 2) + self.iMenuHeight)) + + ######## + # ~~~~~~~~~~~~~~~ + ######## + + # With the windows done we move on to the icons + # First we make the text under each icon + # My Computer + iconfont = ImageFont.truetype(self.lfFont, self.iconfont_pt) + size = iconfont.getsize("My Computer") + font_y_offset = iconfont.getoffset("My Computer")[1] + squaresize = (size[0] + 6, size[1] + 6- font_y_offset) + my_computer_text = Image.new('RGBA', squaresize, colors['background']) + iconfont_draw = ImageDraw.Draw(my_computer_text) + iconfont_draw.fontmode = "1" + iconfont_draw.text((3,3-font_y_offset), "My Computer", fill=colors['foreground'], font=iconfont) + # Network Neighborhood + size = iconfont.getsize("Network Neighborhood") + font_y_offset = iconfont.getoffset("Network Neighborhood")[1] + squaresize = (size[0] + 6, size[1] + 6 - font_y_offset) + network_neighborhood_text = Image.new('RGBA', squaresize, colors['background']) + iconfont_draw = ImageDraw.Draw(network_neighborhood_text) + iconfont_draw.fontmode = "1" + iconfont_draw.text((3,3-font_y_offset), "Network Neighborhood", fill=colors['foreground'],font=iconfont) + # Recycle Bin + size = iconfont.getsize("Recycle Bin") + font_y_offset = iconfont.getoffset("Recycle Bin")[1] + squaresize = (size[0] + 6, size[1] + 6- font_y_offset) + recycle_bin_empty_text = Image.new('RGBA', squaresize, colors['background']) + iconfont_draw = ImageDraw.Draw(recycle_bin_empty_text) + iconfont_draw.fontmode = "1" + iconfont_draw.text((3,3-font_y_offset), "Recycle Bin", fill=colors['foreground'], font=iconfont) + + # My Documents + # Not all themes have one + if self.my_documents: + size = iconfont.getsize("My Documents") + font_y_offset = iconfont.getoffset("My Documents")[1] + squaresize = (size[0] + 6, size[1] + 6- font_y_offset) + my_documents_text = Image.new('RGBA', squaresize, colors['background']) + iconfont_draw = ImageDraw.Draw(my_documents_text) + iconfont_draw.fontmode = "1" + iconfont_draw.text((3,3-font_y_offset), "My Documents", fill=colors['foreground'], font=iconfont) + + # Now we create a canvas to put all the icons and their text on + total_height = ( self.my_computer.size[1] + 4 + my_computer_text.size[1] + 16 + + self.network_neighborhood.size[1] + 4 + network_neighborhood_text.size[1] + 16 + + self.recycle_bin_empty.size[1] + 4 + recycle_bin_empty_text.size[1] ) + + if self.my_documents: + total_height += 16 + self.my_documents.size[1] + 4 + my_documents_text.size[1] + + icons = Image.new('RGBA', (network_neighborhood_text.size[0], total_height), color = (0, 0, 0, 0)) + # The center is determined by the longest text + icon_centerline = int((network_neighborhood_text.size[0]-self.network_neighborhood.size[0])/2) + # Then we start dropping in Icons/Text + icons.paste(self.my_computer, (icon_centerline,0), mask=self.my_computer) + icons.paste(my_computer_text, (int((icons.size[0]-my_computer_text.size[0])/2),self.my_computer.size[1]+4)) + + nn_height = self.my_computer.size[1] + 4 + my_computer_text.size[1] + 16 + nn_text_height = self.network_neighborhood.size[1] + nn_height + 4 + icons.paste(self.network_neighborhood, (icon_centerline,nn_height), mask=self.network_neighborhood) + icons.paste(network_neighborhood_text, (0, nn_text_height)) + + rb_height = nn_height + self.network_neighborhood.size[1] + 4 + network_neighborhood_text.size[1] + 16 + rb_text_height = self.recycle_bin_empty.size[1] + rb_height + 4 + icons.paste(self.recycle_bin_empty, (icon_centerline,rb_height), mask=self.recycle_bin_empty) + icons.paste(recycle_bin_empty_text, (int((network_neighborhood_text.size[0]-recycle_bin_empty_text.size[0])/2),rb_text_height)) + + if self.my_documents: + md_height = rb_height + self.recycle_bin_empty.size[1] + 4 + recycle_bin_empty_text.size[1] + 16 + md_text_height = self.my_documents.size[1] + md_height + 4 + icons.paste(self.my_documents, (icon_centerline,md_height), mask=self.my_documents) + icons.paste(my_documents_text, (int((network_neighborhood_text.size[0]-my_documents_text.size[0])/2),md_text_height)) + + #Finally we create the preview window and set it to the theme background color + theme = Image.new('RGB', (392, 332), color = colors['background']) + #Then we add the wallpaper is we need to + if self.wallpaper: + #; 0: The image is centered if TileWallpaper=0 or tiled if TileWallpaper=1 + #; 2: The image is stretched to fill the screen + #; 6: The image is resized to fit the screen while maintaining the aspect + # ratio. (Windows 7 and later) + #; 10: The image is resized and cropped to fill the screen while maintaining + # the aspect ratio. (Windows 7 and later) + + if self.tile: + tilew, tileh = self.wallpaper.resize((int(self.wallpaper.size[0]/2.6),int(self.wallpaper.size[1]/2.3))).size + twidth = int(theme.size[0]/tilew) + 1 + theight = int(theme.size[1]/tileh) + 1 + startw = 0 + starth = 0 + for i in range(0,theight): + for j in range(0,twidth): + theme.paste(self.wallpaper.resize((int(self.wallpaper.size[0]/2.6),int(self.wallpaper.size[1]/2.3))), + (startw,starth)) + startw = startw + self.wallpaper.resize((int(self.wallpaper.size[0]/2.6),int(self.wallpaper.size[1]/2.3))).size[0] + starth = starth + self.wallpaper.resize((int(self.wallpaper.size[0]/2.6),int(self.wallpaper.size[1]/2.3))).size[1] + startw = 0 + elif self.style == 2: + theme.paste(self.wallpaper.resize((392,332))) + else: + centered_size = self.wallpaper.resize((int(self.wallpaper.size[0]/2.6),int(self.wallpaper.size[1]/2.3))).size + theme.paste(self.wallpaper.resize((int(self.wallpaper.size[0]/2.6),int(self.wallpaper.size[1]/2.3))), + (int((theme.size[0]-centered_size[0])/2),int((theme.size[1]-centered_size[1])/2))) + + + theme.paste(icons, (1,35), mask=icons) + theme.paste(demowindows, (theme.size[0]- demowindows.size[0] - 2, theme.size[1]- demowindows.size[1]-7), mask=demowindows) + #theme.save(self.theme_name+".png", "PNG") + + double = theme.resize((theme.size[0]*2,theme.size[1]*2)) + #double.save(self.theme_name+"_double.png", "PNG") + self.preview_window = theme + self.preview_window_double = double + + print("OK", end='\n', flush=True) + + def get_wallpaper(self): + print("Wallpaper...", end=' ', flush=True) + # WallpaperStyle=2 + #; 0: The image is centered if TileWallpaper=0 or tiled if TileWallpaper=1 + #; 2: The image is stretched to fill the screen + #; 6: The image is resized to fit the screen while maintaining the aspect + # ratio. (Windows 7 and later) + #; 10: The image is resized and cropped to fill the screen while maintaining + # the aspect ratio. (Windows 7 and later) + self.wallpaper = False + self.tile = False + if (self.plus.theme_config['wallpaper'] and self.plus.theme_config['wallpaper']['theme_wallpaper'] and + 'path' in self.plus.theme_config['wallpaper']['theme_wallpaper'] and self.plus.theme_config['wallpaper']['theme_wallpaper']['path']): + + #print(plus.theme_config['wallpaper']['theme_wallpaper']['path']) + #print(os.path.splitext(plus.theme_config['wallpaper']['theme_wallpaper']['path'])[1]) + if os.path.splitext(self.plus.theme_config['wallpaper']['theme_wallpaper']['path'])[1].lower() in [".html", ".htm"]: + w = 1024 + h = 768 + if "800" in self.plus.theme_config['wallpaper']['theme_wallpaper']['path'] or "800" in self.plus.theme_name: + w = 800 + h = 600 + + args = ['wkhtmltoimage', '--quiet','--height', str(h), '--width', str(w), self.plus.theme_config['wallpaper']['theme_wallpaper']['path'],'temp_html.png'] + + subprocess.check_call(args) + self.wallpaper = Image.open("temp_html.png").convert('RGBA') + os.remove("temp_html.png") + else: + try: + self.wallpaper = Image.open(self.plus.theme_config['wallpaper']['theme_wallpaper']['path']).convert('RGBA') + except OSError: + args = ['convert', '-resize', '32x32', self.plus.theme_config['wallpaper']['theme_wallpaper']['path'], "temp_bmp.png"] + subprocess.check_call(args) + self.wallpaper = Image.open("temp_bmp.png").convert('RGBA') + os.remove("temp_bmp.png") + + if self.plus.theme_config['wallpaper']['theme_wallpaper']['tilewallpaper']: + self.tile = True + + self.style = self.plus.theme_config['wallpaper']['theme_wallpaper']['wallpaperstyle'] + + print("OK", end='\n', flush=True) + + def make_icons(self, plus, ico_name): + + icon = "assets/" + ico_name+"~"+".png" + + if plus.theme_config['icons'][ico_name]['type'] in ['dll', 'icl']: + index = plus.theme_config['icons'][ico_name]['index'] + icon_files = plus.extract_icons_from_dll(plus.theme_config['icons'][ico_name]['path']) + if icon_files: + icon_filename, icon_file = plus.get_icons_size_dll(icon_files, index) + + if icon_filename: + f = open("tmp/tmp_"+icon_filename,"wb") + f.write(icon_file) + f.close() + icon = "tmp/tmp_"+icon_filename + else: + icon = "assets/" + ico_name+"~"+".png" + else: + icon_files = plus.extract_ico(plus.theme_config['icons'][ico_name]['path']) + + + + if icon_files == 'bmp': + icon = plus.theme_config['icons'][ico_name]['path'] + elif icon_files: + + for i in icon_files: + if i['Width'] == 32: + icon = "{}[{}]".format(plus.theme_config['icons'][ico_name]['path'], i['ID']) + break + else: + icon = "{}[{}]".format(plus.theme_config['icons'][ico_name]['path'], i['ID']) + + args = ['convert', '-resize', '32x32', icon, ico_name+".png"] + try: + subprocess.check_call(args) + icon_image = Image.open(ico_name+".png").convert('RGBA') + os.remove(ico_name+".png") + except subprocess.CalledProcessError: + icon_image = Image.open("assets/" + ico_name+"~.png").convert('RGBA') + + return icon_image + + + + + +class plusGTK: + def __init__(self, themefile=False, colors=32, overlap=1, + squaresize=20, installdir=os.getcwd(), + chicago95_cursor_path=str(Path.home())+"/.icons/Chicago95_Cursor_Black", + chicago95_theme_path=str(Path.home())+"/.themes/Chicago95", + chicago95_icons_path=str(Path.home())+"/.icons/Chicago95", + loglevel=logging.WARNING, + logfile='plus.log'): + + + self.themefile=themefile + self.colors=colors + self.overlap=overlap + self.squaresize=squaresize + self.installdir=installdir + self.chicago95_cursor_path=chicago95_cursor_path + self.chicago95_theme_path=chicago95_theme_path + self.chicago95_icons_path=chicago95_icons_path + self.loglevel=loglevel + self.logfile=logfile + + # Define signal mappings for builder + self.handlers = { + "cancel" : self.cancel, + "onDestroy": Gtk.main_quit, + "theme_chosen": self.open_file, + "install_screensaver" : self.set_install_screensaver, + "install_sounds" : self.set_install_sounds, + "install_cursors" : self.set_install_cursors, + "install_wallpaper" : self.set_install_wallpaper, + "install_icons" : self.set_install_icons, + "install_colors" : self.set_install_colors, + "install_fonts" : self.set_install_fonts, + "change" : self.open_file, + "warning_ok": self.warning_ok, + "screen_saver_preview" : self.screen_saver_preview, + "other_previews" : self.other_previews, + "show_cursor": self.show_cursor, + "show_icon": self.show_icon, + "show_sound": self.show_sound, + "play_sound": self.play_sound, + "install" : self.install_accept, + "install_ok": self.install_ok, + "preview_closed" : self.preview_closed + } + + + # GTK Initialization + self.builder = builder = Gtk.Builder() + self.builder.add_from_file("plus.glade") + self.window = self.builder.get_object("Main") + self.preview = self.builder.get_object("preview") + self.builder.connect_signals(self.handlers) + self.file_chooser = self.builder.get_object("open_theme") + self.filefilter = self.builder.get_object("Microsoft Theme File") + self.file_chooser.add_filter(self.filefilter) + self.install_cursors = True + self.install_icons = True + self.install_wallpaper = True + self.install_sounds = True + self.install_colors = True + self.install_fonts = True + self.install_screensaver = True + self.theme_selected = False + if self.themefile: + self.theme = ChicagoPlus( + themefile=self.themefile, + loglevel=self.loglevel, + colors=self.colors, + overlap=self.overlap, + squaresize=self.squaresize, + installdir=self.installdir, + chicago95_cursor_path=self.chicago95_cursor_path, + chicago95_theme_path=self.chicago95_theme_path, + chicago95_icons_path=self.chicago95_icons_path, + logfile=self.logfile) + self.preview_image = MakePreview(self.theme) + self.theme_config = self.theme.theme_config + theme_name = self.builder.get_object("theme_name") + preview_theme_name = self.builder.get_object("Preview Theme Name") + theme_name.set_text(self.preview_image.theme_name) + preview_theme_name.set_text("Preview of '{}'".format(self.preview_image.theme_name)) + preview = self.preview_image.return_preview() + self.preview.set_from_file(preview) + self.theme_selected = True + self.preview_image.delete_preview() + self.window.show_all() + + def cancel(self, button): + Gtk.main_quit() + + def open_file(self, button): + #image_file = "demo2.png" + ############################################################# + #self.preview.set_from_file(image_file) + ############################################################# + #print("Image rendered") + self.themefile = self.file_chooser.get_filename() + #print(theme_file) + self.theme = ChicagoPlus( + themefile=self.themefile, + loglevel=self.loglevel, + colors=self.colors, + overlap=self.overlap, + squaresize=self.squaresize, + installdir=self.installdir, + chicago95_cursor_path=self.chicago95_cursor_path, + chicago95_theme_path=self.chicago95_theme_path, + chicago95_icons_path=self.chicago95_icons_path, + logfile=self.logfile) + self.preview_image = MakePreview(self.theme) + self.theme.parse_theme() + self.theme_config = self.theme.theme_config + theme_name = self.builder.get_object("theme_name") + preview_theme_name = self.builder.get_object("Preview Theme Name") + theme_name.set_text(self.preview_image.theme_name) + preview_theme_name.set_text("Preview of '{}'".format(self.preview_image.theme_name)) + preview = self.preview_image.return_preview() + self.preview.set_from_file(preview) + self.theme_selected = True + self.preview_image.delete_preview() + + def set_install_screensaver(self, toggle): + if self.install_screensaver: + self.install_screensaver = False + else: + self.install_screensaver = True + + def set_install_cursors(self, toggle): + if self.install_cursors: + self.install_cursors = False + else: + self.install_cursors = True + + def set_install_sounds(self, toggle): + if self.install_sounds: + self.install_sounds = False + else: + self.install_sounds = True + + def set_install_wallpaper(self, toggle): + if self.install_wallpaper: + self.install_wallpaper = False + else: + self.install_wallpaper = True + + def set_install_icons(self, toggle): + if self.install_icons: + self.install_icons = False + else: + self.install_icons = True + + def set_install_colors(self, toggle): + if self.install_colors: + self.install_colors = False + else: + self.install_colors = True + + def set_install_fonts(self, toggle): + if self.install_fonts: + self.install_fonts = False + else: + self.install_fonts = True + + + def screen_saver_preview(self, button): + + if not self.theme_config['screensaver']: + self.warning_msg("Screen Saver Preview", "{} does not include a screensaver".format(self.theme_config['theme_name'])) + return + + try: + wine = subprocess.check_output(["which", "wine"]).strip() + except subprocess.CalledProcessError: + self.warning_msg("Screen Saver Preview", "Wine is not installed and is required to preview screensavers.") + return + + args = [wine, self.theme_config['screensaver'], '/s'] + subprocess.check_call(args) + + def in_store(self, store, name): + for row in store: + if row[1] == name: + return True + return False + + def in_store_location(self, store, name): + for x in range(len(store)): + if store[x][1] == name: + return x + return 0 + + + def other_previews(self, button): + checkmark = GdkPixbuf.Pixbuf.new_from_file("assets/check.png") + nocheckmark = GdkPixbuf.Pixbuf.new_from_file("assets/blank-check.png") + self.previews_window = self.builder.get_object("Preview Window") + + self.cursor_preview = self.builder.get_object("cursor_preview") + self.cursor_preview.set_from_file("assets/blank-check.png") + + self.icon_preview = self.builder.get_object("icon_preview") + self.icon_preview.set_from_file("assets/blank-check.png") + + self.sound_preview = self.builder.get_object("sound_preview") + self.sound_preview.set_from_file("assets/blank-check.png") + + + self.cursor_preview = self.builder.get_object("cursor_preview") + self.cursor_preview.set_from_file("assets/blank-check.png") + + # Populate cursor preview + self.cursor_store = self.builder.get_object("cursor_list") + for current_cursor in pointers: + print("Current cursor: {} (name: {})".format(current_cursor, pointers[current_cursor])) + if (current_cursor not in self.theme_config['cursors'] or not + self.theme_config['cursors'][current_cursor] or not + self.theme_config['cursors'][current_cursor]['path']): + if not self.in_store(self.cursor_store, pointers[current_cursor]): + self.cursor_store.append([nocheckmark,pointers[current_cursor]]) + else: + loc = self.in_store_location(self.cursor_store, pointers[current_cursor]) + self.cursor_store[loc][0] = nocheckmark + + else: + filename = self.theme_config['cursors'][current_cursor]['path'] + cursor_filename = self.theme_config['cursors'][current_cursor]['filename'] + cursor_type = self.theme_config['cursors'][current_cursor]['type'] + convert_args = ['convert', '-dispose', 'Background'] + if cursor_type == 'ani': + ani_file_config = self.theme.extract_ani(filename) + # convert -delay X image1 -delay Y image2 -delay Z image3 -loop 0 animation.gif + + if ani_file_config['seq']: + for sequence in ani_file_config['seq']: + if ani_file_config['rate']: + rate = ani_file_config['rate'][sequence] + 5 + else: + rate = ani_file_config['anih']['iDispRate'] + 5 + + for icon in ani_file_config['icon']: + if icon['index'] == sequence: + cur_filename = current_cursor+"_"+str(sequence) + f = open("tmp/"+cur_filename+".cur","wb") + f.write(icon['ico_file']) + f.close() + convert_args.append("-delay") + convert_args.append("{}x60".format(rate)) + convert_args.append("tmp/"+cur_filename+".cur") + else: + for icon in ani_file_config['icon']: + rate = ani_file_config['anih']['iDispRate'] + 5 + cur_filename = current_cursor+"_"+str(icon['index']) + f = open("tmp/"+cur_filename+".cur","wb") + f.write(icon['ico_file']) + f.close() + convert_args.append("-delay") + convert_args.append("{}x60".format(rate)) + convert_args.append("tmp/"+cur_filename+".cur") + convert_args.append("-loop") + convert_args.append("0") + convert_args.append("tmp/"+current_cursor+".gif") +# pprint(convert_args) + subprocess.check_call(convert_args) + else: + cursor_file_config = self.theme.extract_cur(filename) + icon_file = cursor_file_config['icon'][0]['ico_file'] + f = open("tmp/"+current_cursor+".cur","wb") + f.write(icon_file) + f.close() + subprocess.check_call(['convert', "tmp/"+current_cursor+".cur", "tmp/"+current_cursor+".gif"]) + + if not self.in_store(self.cursor_store, pointers[current_cursor]): + self.cursor_store.append([checkmark,pointers[current_cursor]]) + else: + loc = self.in_store_location(self.cursor_store, pointers[current_cursor]) + self.cursor_store[loc][0] = checkmark + + # Populate Icons + self.icon_store = self.builder.get_object("icon_list") + #pprint(self.theme_config) + print(self.theme_config['wallpaper'] and self.theme_config['wallpaper']['theme_wallpaper']) + # Wallpaper + if self.theme_config['wallpaper'] and self.theme_config['wallpaper']['theme_wallpaper']: + if not self.in_store(self.icon_store, "Wallpaper bitmap"): + self.icon_store.append([checkmark,"Wallpaper bitmap"]) + else: + loc = self.in_store_location(self.icon_store, "Wallpaper bitmap") + self.icon_store[loc][0] = checkmark + else: + if not self.in_store(self.icon_store, "Wallpaper bitmap"): + self.icon_store.append([nocheckmark,"Wallpaper bitmap"]) + else: + loc = self.in_store_location(self.icon_store, "Wallpaper bitmap") + self.icon_store[loc][0] = nocheckmark + # Icons + for icon in icons: + if not self.theme_config['icons'][icon]: + if not self.in_store(self.icon_store, icons[icon]): + self.icon_store.append([nocheckmark,icons[icon]]) + else: + loc = self.in_store_location(self.icon_store, icons[icon]) + self.icon_store[loc][0] = nocheckmark + else: + icon_image = self.preview_image.make_icons(self.theme, icon) + icon_image.save("tmp/"+icon+".png", "PNG") + if not self.in_store(self.icon_store, icons[icon]): + self.icon_store.append([checkmark,icons[icon]]) + else: + loc = self.in_store_location(self.icon_store, icons[icon]) + self.icon_store[loc][0] = checkmark + #screensaver + if self.theme_config['screensaver']: + if not self.in_store(self.icon_store, "Screen saver"): + self.icon_store.append([checkmark,"Screen saver"]) + else: + loc = self.in_store_location(self.icon_store, "Screen saver") + self.icon_store[loc][0] = checkmark + else: + if not self.in_store(self.icon_store, "Screen saver"): + self.icon_store.append([nocheckmark,"Screen saver"]) + else: + loc = self.in_store_location(self.icon_store, "Screen saver") + self.icon_store[loc][0] = nocheckmark + + # Sounds + self.sound_store = self.builder.get_object("sound_list") + for sound in sounds: + if sound not in self.theme_config['sounds']: + if not self.in_store(self.sound_store, sounds[sound]): + self.sound_store.append([nocheckmark,sounds[sound]]) + else: + loc = self.in_store_location(self.sound_store, sounds[sound]) + self.sound_store[loc][0] = nocheckmark + else: + if not self.in_store(self.sound_store, sounds[sound]): + self.sound_store.append([checkmark,sounds[sound]]) + else: + loc = self.in_store_location(self.sound_store, sounds[sound]) + self.sound_store[loc][0] = checkmark + self.sound_path = False + for sound in sounds: + if sound in self.theme_config['sounds']: + self.sound_path = self.theme_config['sounds'][sound] + break + + + + self.previews_window.show_all() + + def show_cursor(self,widget, row, col): + + self.cursor_text_path = self.builder.get_object("cursor_path") + model = widget.get_model() + for cursor in pointers: + if pointers[cursor] == model[row][1]: + if cursor in self.theme_config['cursors'] and self.theme_config['cursors'][cursor] is not False: + self.cursor_preview.set_from_file("tmp/"+cursor+".gif") + self.cursor_text_path.set_text(self.theme_config['cursors'][cursor]['path']) + else: + self.cursor_preview.set_from_file("assets/blank-check.png") + self.cursor_text_path.set_text("") + break + + def show_icon(self,widget, row, col): + + self.icon_text_path = self.builder.get_object("icon_path") + model = widget.get_model() + + if model[row][1] == "Wallpaper bitmap": + if self.theme_config['wallpaper'] and self.theme_config['wallpaper']['theme_wallpaper']: + self.icon_text_path.set_text(self.theme_config['wallpaper']['theme_wallpaper']['path']) + self.icon_preview.set_from_file("assets/blank-check.png") + else: + self.icon_preview.set_from_file("assets/blank-check.png") + self.icon_text_path.set_text("") + return + if model[row][1] == "Screen saver": + if self.theme_config['screensaver'] : + self.icon_text_path.set_text(self.theme_config['screensaver']) + self.icon_preview.set_from_file("assets/blank-check.png") + else: + self.icon_preview.set_from_file("assets/blank-check.png") + self.icon_text_path.set_text("") + return + for icon in icons: + print(icon, icons[icon], model[row][1]) + if icons[icon] == model[row][1]: + if self.theme_config['icons'][icon] is not False: + self.icon_preview.set_from_file("tmp/"+icon+".png") + self.icon_text_path.set_text(self.theme_config['icons'][icon]['path']) + else: + self.icon_preview.set_from_file("assets/blank-check.png") + self.icon_text_path.set_text("") + break + + def show_sound(self,widget, row, col): + + self.sound_text_path = self.builder.get_object("sound_path") + model = widget.get_model() + for sound in sounds: + if sounds[sound] == model[row][1]: + if sound in self.theme_config['sounds'] and self.theme_config['sounds'][sound]: + self.sound_path = self.theme_config['sounds'][sound] + self.sound_text_path.set_text(self.sound_path) + else: + self.sound_path = False + self.sound_text_path.set_text("") + + + def play_sound(self, button): + if self.sound_path: + print("Playing {} with aplay".format(self.sound_path)) + try: + subprocess.check_call(['aplay', self.sound_path]) + except: + self.warning_msg("Error", "aplay required to play sound previews") + + + def preview_closed(self, *args): + self.previews_window.hide() + return True + + def warning_msg(self, title="Error", message="Error"): + self.warning_popup = self.builder.get_object("Warning") + warning_dialogue = self.builder.get_object("warning_label") + self.warning_popup.set_title(title) + warning_dialogue.set_text(message) + self.warning_popup.show_all() + + def warning_ok(self, *args): + self.warning_popup.hide() + return True + + def install_accept(self, button): + self.install_theme() + return + + def install_ok(self, button): + if not self.theme_selected: + return False + self.install_theme() + Gtk.main_quit() + + def install_theme(self): + if not self.theme_selected: + return False + + print("Installing theme with the following options:") + options = "Theme File: {}\nInkscape Options:\n\tColors: {} Overlap: {} Squaresize: {}\nInstall Directory: {}\nPaths:\n\tCursor Path: {}\n\tTheme Path: {}\n\tIcons Path: {}".format( + self.theme_config['theme_file'], self.colors, self.overlap, self.squaresize, + self.installdir, self.chicago95_cursor_path, self.chicago95_theme_path, + self.chicago95_icons_path) + + checks = "Install (checkbox) Options:\n\tCursors: {}\n\tIcons: {}\n\tWallpaper: {}\n\tSounds: {}\n\tColors: {}\n\tFonts: {}\n\tScreensaver: {}".format( + self.install_cursors, self.install_icons, self.install_wallpaper, + self.install_sounds, self.install_colors, self.install_fonts, self.install_screensaver) + print(options) + print(checks) + self.theme.go(cursors=self.install_cursors, icons=self.install_icons, wallpaper=self.install_wallpaper, + sounds=self.install_sounds, colors=self.install_colors, fonts=self.install_fonts, screensaver=self.install_screensaver) + + + + +def main(): + + + desc = '''Chicago95 Plus!''' + arg_parser = argparse.ArgumentParser(description=desc, + usage='%(prog)s [options] [MS Theme File]', + epilog="Part of the Chicago95 theme project", + formatter_class=argparse.ArgumentDefaultsHelpFormatter) + arg_parser.add_argument('-d', '--debug', help="Print lots of debugging statements", action="store_const", dest="loglevel", const=logging.DEBUG, default=logging.WARNING) + arg_parser.add_argument('-v', '--verbose', help="Be verbose", action="store_const", dest="loglevel", const=logging.INFO) + arg_parser.add_argument('-c', '--colors', help='How many colors before skipping Inkscape fix/merge for SVGs. Set to 1 to speed up conversion. WARNING: This may result in transparent icons!', default=32, type=int) + arg_parser.add_argument('-o', '--overlap', help='Pixel overlap for SVG icons', default=1, type=int) + arg_parser.add_argument('-s', '--squaresize', help='Square size for SVG icons', default=20, type=int) + arg_parser.add_argument('--cursorfolder', help="Chicago95 cursor folder to convert to new theme", default=str(Path.home())+"/.icons/Chicago95_Cursor_Black") + arg_parser.add_argument('--themefolder', help="Chicago95 theme folder to convert to new theme", default=str(Path.home())+"/.themes/Chicago95") + arg_parser.add_argument('--iconsfolder', help="Chicago95 icons folder to convert to new theme", default=str(Path.home())+"/.icons/Chicago95") + arg_parser.add_argument("--installdir", help="Folder to create new theme in, default is current working directory", default=os.getcwd()) + arg_parser.add_argument("--logfile", help="Filename for debug logging", default="chicago95_plus.log") + arg_parser.add_argument("theme_file", help="Microsoft Windows 95/98/ME .theme file", nargs="?",default=False) + + + args = arg_parser.parse_args() + + plus = plusGTK(themefile=args.theme_file, + loglevel=args.loglevel, + colors=args.colors, + overlap=args.overlap, + squaresize=args.squaresize, + installdir=args.installdir, + chicago95_cursor_path=args.cursorfolder, + chicago95_theme_path=args.themefolder, + chicago95_icons_path=args.iconsfolder, + logfile=args.logfile) + + + #plus = plusGTK() + Gtk.main() + +main() + + + + + + + + + + + + + + + + + + diff --git a/Plus/assets/X.png b/Plus/assets/X.png new file mode 100644 index 0000000000000000000000000000000000000000..cc0eb1e66ee3d9b4087a23cd6e825bc168f79636 GIT binary patch literal 230 zcmeAS@N?(olHy`uVBq!ia0vp^oFL4>1|%O$WD@{VY)RhkE)4%caKYZ?lR?r29+AZi z40_5S%viD1z6>bHUgGKN%Kn5!PE^D+Z9Dr`ppayVYeb22er|4RUI~M9QEFmIYKlU6 zW=V#EyQgnJcq5-UP?4Xfi(`ny<=zPgIT;jqj$HD4puqPugYT;2odtCwOTsqnZ+UE? z)xga2+G$77Y~~CRP5#$w%-C&`BR^$Xsb`2t*5_W*IM? zaB^>EX>4U6ba`-PAZ2)IW&i+q+U=KJlI$i7h2L34mVhLL#Bwk{RoOw7p96M#x@RWI zy>;(Xs!bWNkcE6cpl8DP>(3efz(plRBJcK0f+RqH+yen=+&EoJQN$A5f?16G+0zqCr?-npV z3-luLqZdA_fA>C#&-Jp)CoS`(5jx)vkbW=zmiTF6xijL|0h33luOiRcea>-yT}>H^ z=4(a8@K9&H1IGiB%YH4R7;Z)1b}1+xYixkiV~Yn$qe2X=XP`u#Doq-@ZPbur;=zWN zxv^}Ui*ey5Cnpq|Y*M6)gJ0tjL}2DS7uxfdJzpco%$?xMgkXeM{;`CA8~m9P=4?wM zihgT_ym+l#q_N1Y8eRZG^Wvsk;77Ue$EWp!r3!+&Wp;dEjmH_{Q2JO~vN;R<6z#by zC|JJ&KqAboNQMLiVkS|_6ph)4prZp&MdmDt4-g<#?nsi82+kv8Y#GdrcZ{|#D`#G2 zoJ|0slEuKLNCm8v6zj*5V+~amO{$vJ)PokSS#rvnbGEz=xoTp`)Uuhm6{{|uTs^zF zd+}Ph2+lw)xmfX1O066!CaQ>4;a_1n_>dzVdgQ|nKgv-XmQPDfTW;38l~y}<={P2O z?$*7RUI&GNlVYTyBM%!s%BT}-ZOTkjXP!2FmRWCV7u7H2C#cayjW?;~XE!xigV`el zt>Z)&GZ5oMAa08Q7Bnwr(J3WfaV4JJ&X`a~Cd6qhR-8e0UA)b^WN-Uv1xk-htkM-htkM-htkM z{s)03{&>JYh2d{b!JT`dh>J!500ARuLqkwWLqi~Na&Km7Y-Iodc$}S(Jxat-6otQx z|Du&Yg-nsccECcXvKbYEpb+B-MypJc!NDYkB*Wki+=(t`3PErMu0X-s^P+`?X?$=w zAD4U2dGA5XPQtXD-GMZ#3digDm;RN#@Wvc%Hdtae2+O?Zx+gTA&sQv?FCF96Joo3B zkK!_vPE}c$7Zq6h;_jr%4Xaqc?R$sf2eBQcStNcEZ%1il*z9^~G7f({&23iV?Bd+? zRc#!OImN|hz?cyU6&-1&&JkVqqz(lE_mqUvgjl_aW-4w(rJNiNM17HN(~!4S=ebH5 z$YUfglW<2WPm_oF-|w}LvwG9ooaR9h)U6p+YdD1WM_Bb??HQJ@8?qLEzpY`WThILS zjb9A0Ps92E)*llk;)0000ENklYH7zLvM0RRFJ009)jz>)v}002ovPDHLkV1gl( BAKCx_ literal 0 HcmV?d00001 diff --git a/Plus/assets/check.png b/Plus/assets/check.png new file mode 100755 index 0000000000000000000000000000000000000000..733cfb82e057cf6caaae0e3486d46b3faba44b72 GIT binary patch literal 316 zcmeAS@N?(olHy`uVBq!ia0vp^0wB!60wlNoGJgf6SkfJR9T^xl_H+M9WCijSl0AZa z85pY67#JE_7#My5g&JNkFq9fFFuY1&V6d9Oz#v{QXIG#NP=YDR+uenMVO6iP5s=4O z;1OBOz`!jG!i)^F=12eq*-JcqUD+S7@G&rnaq4bo0t)GSx;TbtoKH@WIP}22=U_{` zROrEapUe{Be}|V!COY**@VW`59x-5y<=Ot3jh}(x>RC=^p7k}GfLc{cTq8h@qL4iJ_IDsjh*!m4U&J>s{PwxN# literal 0 HcmV?d00001 diff --git a/Plus/assets/my_computer~.png b/Plus/assets/my_computer~.png new file mode 100755 index 0000000000000000000000000000000000000000..1cde196c2145d9f9b85f9e3e0764ebdad3bd3c05 GIT binary patch literal 1188 zcmZuwdq~xH6hB(p%_W63?ZLKK-^d;-stM7QMnyH4Y!w-YprRsO5t%i@u#+W757?+bvM2(-oqL1udiRCz_j}HdbH1O?`JD5s zEh-G9ri@MjfK>b{SZw#$$POgixv{dc2VejcEiB15*GIRHUfw((S*j``%uGax=$umk z0bl?EKmcc1ai3H{1ek#c5W)Rlkb(*b0}~JeI(sQDEgs&BD6` zafu)se!O_zizD6t^1LjtG$v<4&EM{Xd| zei_K(i51Cuz~Csh+{lcu7g9#C&LNQ189NOKvHdZUpbSD_;T}YR1&D2I#RIER00WAy zwn>Qt0TPyBFt4QaN=ta@KfA{cx&Q6ikv*lK4_&-}&!=?p`}9G7*IQF%bGGcMxnS zs|`(k@q5y>N3Ux?`flxx|61}YGD>Epc1#TvG#{-`Y{27nX*jT8#Ifo>l0p~qpQhft zzJ7Ywkuj^nqv`R3+@Gi9PF%n8Oj%~*D_1^g#G$O$89R$Vx%KhE#(R0;?BT11%_}b- zI(Y8O;m|dqdU8?I;7i+1R91ED-yZ(u&XV4`9e1 zqRJwch(vaRRY?SyEhbgF-vU&%+4&UpUc9mj2_uu~Wv`@y!d3@)MHQ|~fZ z)0<}o{zF=G|58ngffM)?=OEcR+x9Lc-ek|B#Dc^Q37q!v8cb*}yMaXeWFU(tRwU~I zgQMhf<8Oq$kTQyO4uP!Blz(9$wYf_*;Kw1t) z78b8-?_L|rY9BYaykbV>wZ_>QIS=PWrFHI)hO{a3mv_546`Rs7P5OP~^!4WI*tDLj zlcAv~cV&1&?unZ8PvM2PdTzFC{o-dV{&4c>vQrn!)=pfNR(t4R)45>Qw_^>Vp8QRD z^;JWLZyPgWOHN>Q{nJ;Evr4DVTv~S1H-6<{wC?$X1G&-PWp`o~*&ib>&h@WM-+8zu zyx`pqee(WlaC~-Y-ms^$O2%g1&1vo~su;kb=)Tzc10&Br+!f7_Y+pCHbaz&5$92`t zK`TSyz^J;8?AwR$6{mM@+(A|NZ*~l?x_>+pDA-sWeqEM-wx*pdpr8bqw2O1p$(yD``w<5&wD!_eVrP+JhW?a0M7iHd#m4fpHY6LskJ)p N9WE+}wB(gk{Q=8l3B3RS literal 0 HcmV?d00001 diff --git a/Plus/assets/network_neighborhood~.png b/Plus/assets/network_neighborhood~.png new file mode 100755 index 0000000000000000000000000000000000000000..82d21b72e4de27682c79f5eec658728127487155 GIT binary patch literal 471 zcmV;|0Vw{7P)P|8N<)W%hVa6Uifh&(dX?osyBt>oDl4ZeYf9JDT7CDFSO@_#TyqDY*H~hVnB|%~0KG5DMm57V zwd{ax2Yl|Dt#!3Dq0`F^z)uqN=wD`ig=fIf8FxP5-VW~VZt3K}Yui4MAK>m8s4`)k z3$)afV4LdMPPn>KvS$9MBwA}bc{W^qL9QCLpD>lEb)$70D-t=86f*!zA1EbT_a`+R zkVdt4&V3SHNG6i9eUR{Ea^+<+bpKQx3C&dI{}=)9f4gNa?QJa%yXH~46M!1C+ZuPS z2>@}4kDAaCm=ZWeHciYxgX3GndWikLlbb>$LJ^+E8>e zWkv`cwn9-%)aD<$S>P!W#i3OCkBTkSgDi8}7+B&Ue3E)EURi~Nk;(M3S5iS?tAo6v ziq(f+t?pQMBFvl_%rgU@BCWYks3yh0aeR_>8O1t>KvrksYd}cskBJ0j5CS{yK@?bk*q*I;U^NL~ zp!j$0g_Kl)z;cYtsi-`4{Hq1BIX^QpW>i1#4%O!@y4m0RdRgk}$dN}Y-mhujv29}0 zzUJQYJ)xY6+s)s!13TX=5S4>`Bqnp~3}J^#mdms$@_ zsogs6`t)dZWAXZ$l3YmNd0>CWOZiJy-?{kVx^UBto=~(SBQe{(^?j^ z`{L+^P`@<6FL@Gjoo#o^Pismmw&&S5*^vFudDLfxMNo~E)1UDxOLUYMQfJaeK>XP z$<~7Lf7QS8_~X3l)TJpY@61SBoA>jppFBt#Kl@R8;f1^19Qal5=k`phd#!Hsr<1-s yoVsH}=Lg}w%!303Cj#fwz8&5XoY}bUw||GVF6-}mZ`qxTG4HW>ex!YFRnvb|SuS7z literal 0 HcmV?d00001 diff --git a/Plus/original.png b/Plus/original.png new file mode 100644 index 0000000000000000000000000000000000000000..f0efc95eb6d8bb5eb82e103f99e73d5b0aa05c5e GIT binary patch literal 15822 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 E0dL*~eE + + + + + False + Chicago95 Plus + center + preferences-desktop + center + + + + + + + True + False + vertical + + + True + False + 10 + 10 + 10 + + + True + False + 2 + 5 + vertical + 5 + + + True + False + + + True + False + Theme: + + + False + True + 0 + + + + + theme_name + True + True + No Theme Selected + + + True + True + 20 + 1 + + + + + True + False + + + + + False + True + 2 + + + + + False + True + 0 + + + + + 392 + 332 + True + False + 0 + 0 + none + + + True + False + 0 + 0 + 0 + 0 + + + True + False + original.png + + + + + + + + + + + False + False + 1 + + + + + False + False + 0 + + + + + True + False + True + vertical + + + True + False + 0.10000000149011612 + none + + + True + False + 5 + 5 + 5 + vertical + 5 + + + Sc_reen Saver + True + True + True + True + + + + False + True + 0 + + + + + _Pointers, Sounds, etc... + True + True + True + True + + + + True + True + 1 + + + + + + + True + False + Previews + + + + + + False + True + 0 + + + + + True + False + 0.10000000149011612 + + + True + False + 10 + vertical + 5 + + + True + False + Click OK or Apply to apply the selected settings to Windows 95. + True + 25 + 0 + 0.5 + + + False + True + 0 + + + + + Screen sa_ver + True + True + False + Chicago 95 does not support custom screensavers at this time. To see the screen saver use the preview button. Once this theme is installed the screensaver will be saved in the screensaver folder. + True + True + True + + + + False + True + 1 + + + + + Sound _events + True + True + False + True + True + True + + + + False + True + 2 + + + + + _Mouse pointers + True + True + False + True + True + True + + + + + False + True + 3 + + + + + Desktop _wallpaper + True + True + False + True + True + True + + + + False + True + 4 + + + + + _Icons + True + True + False + True + True + True + + + + False + True + 5 + + + + + _Colors + True + True + False + True + True + True + + + + False + True + 6 + + + + + _Font names and styles + True + True + False + True + True + True + + + + False + True + 7 + + + + + _Font and window sizes + True + True + False + This will only change the font, window size changing is not supported at this time. + True + True + True + + + False + True + 8 + + + + + + + True + False + Settings + center + True + 0.10999999940395355 + 0.10000000149011612 + + + + + + + + + True + True + 1 + + + + + False + False + 1 + + + + + True + True + 0 + + + + + True + False + 10 + 10 + 5 + 12 + + + Preview Theme Name + True + False + Preview of 'Selected Theme Name' + end + + + True + True + 0 + + + + + True + False + 3 + True + + + OK + 80 + True + True + True + 2 + 2 + + + + False + True + 0 + + + + + Cancel + True + True + True + + + + False + True + 1 + + + + + _Apply + True + True + True + True + + + + False + True + 2 + + + + + False + True + 1 + + + + + False + False + 5 + 1 + + + + + + + + *.theme + *.Theme + + + + Warning + False + True + Warning + False + True + center + True + application-x-executable + True + False + + + + + + + + + True + False + 4 + 4 + 4 + 4 + vertical + + + True + False + 4 + 4 + 4 + 4 + 11 + + + True + False + gtk-dialog-warning + 6 + + + False + True + 0 + + + + + True + False + This is the generic warning message you would receive if you don't have something installed to view the preview. + True + 54 + 54 + + + False + True + 1 + + + + + False + True + 0 + + + + + Ok + 80 + True + True + True + center + center + 2 + 2 + + + + False + False + 2 + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + False + Preview + True + preferences-desktop + True + + + + + + + True + False + vertical + + + True + True + True + + + True + False + 10 + 10 + 10 + 10 + vertical + 3 + + + True + False + start + Mouse pointer type: + + + False + True + 0 + + + + + True + True + never + always + in + False + False + True + + + 295 + 150 + True + False + + + True + True + cursor_list + False + False + False + True + + + + + + + True + 4 + column1 + + + + 0 + + + + + + + True + column + True + + + + 1 + + + + + + + + + + + False + True + 1 + + + + + True + False + start + 8 + File Name: + + + False + True + 2 + + + + + True + True + False + 48 + Cursor file location + + + False + True + 3 + + + + + True + False + start + 8 + Preview: + + + False + True + 4 + + + + + True + False + vertical + + + 75 + 65 + True + False + start + start + 0 + 0 + in + + + -1 + True + False + gtk-missing-image + + + + + + + + + False + True + 0 + + + + + False + True + 5 + + + + + + + pointers + True + False + Pointers + + + False + + + + + True + False + 10 + 10 + 10 + 10 + vertical + 3 + + + True + False + start + Sound event: + + + False + True + 0 + + + + + True + True + never + always + in + False + False + True + + + 295 + 150 + True + False + + + True + True + sound_list + False + False + 1 + False + True + + + + + + + True + 4 + column1 + + + + 0 + + + + + + + True + column + True + + + + 1 + + + + + + + + + + + False + True + 1 + + + + + True + False + start + 8 + File Name: + + + False + True + 2 + + + + + True + True + False + 48 + + + False + True + 3 + + + + + True + False + start + 8 + Icon: + + + False + True + 4 + + + + + True + False + start + 4 + + + 75 + 65 + True + False + 0 + 0 + none + + + True + False + gtk-missing-image + + + + + + + + + False + True + 0 + + + + + True + True + True + start + start + 2 + top + True + + + + True + False + 2 + 2 + 2 + 2 + gtk-media-play + + + + + False + True + 1 + + + + + False + True + 5 + + + + + 1 + + + + + True + False + Sounds + + + 1 + False + + + + + True + False + 10 + 10 + 10 + 10 + vertical + 3 + + + True + False + start + Visual element: + + + False + True + 0 + + + + + True + True + never + always + in + False + False + True + + + 295 + 150 + True + False + + + True + True + icon_list + False + False + 1 + False + True + + + + + + + True + 4 + column1 + + + + 0 + + + + + + + True + column + True + + + + 1 + + + + + + + + + + + False + True + 1 + + + + + True + False + start + 8 + File Name: + + + False + True + 2 + + + + + True + True + False + 48 + + + False + True + 3 + + + + + True + False + start + 8 + Picture: + + + False + True + 4 + + + + + True + False + vertical + + + 75 + 65 + True + False + start + start + 0 + 0 + in + + + -1 + True + False + gtk-missing-image + + + + + + + + + False + True + 0 + + + + + False + True + 5 + + + + + 2 + + + + + True + False + Visuals + + + 2 + False + + + + + False + True + 0 + + + + + True + False + end + center + 10 + 10 + 5 + 10 + 12 + True + + + -1 + True + False + 3 + True + + + Close + 80 + True + True + True + + + + False + True + 0 + + + + + Cancel + True + False + True + True + + + False + True + 1 + + + + + True + True + 0 + + + + + True + True + 1 + + + + + +