#!/usr/bin/env python3 import sys from pluslib import ChicagoPlus import logging from pathlib import Path from PIL import Image, ImageFont, ImageDraw, ImageEnhance, ImageOps from pprint import pprint import struct import subprocess import os from shutil import copyfile import textwrap import numpy as np import array import argparse import time import gi gi.require_version("Gtk", "3.0") from gi.repository import Gtk, GdkPixbuf, GLib running_folder = os.path.dirname(os.path.abspath(__file__)) share_dir = running_folder libexec_dir = running_folder work_dir = running_folder if not os.path.exists(work_dir): os.makedirs(work_dir) c95_packaged_cursor_path=str(Path.home())+"/.icons/Chicago95_Cursor_Black" c95_packaged_theme_path=str(Path.home())+"/.themes/Chicago95" c95_packaged_icons_path=str(Path.home())+"/.icons/Chicago95" #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)) print("[MakePreview] Generating Preview Image") 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("[MakePreview] Preview Generated") def return_preview(self): self.preview_window.save(work_dir+"/preview.png", "PNG") return(work_dir+"/preview.png") def return_preview_double(self): self.preview_window.save(work_dir+"/preview_double.png", "PNG") return(work_dir+"/preview_double.png") def delete_preview(self): os.remove(work_dir+"/preview.png") def delete_preview_double(self): os.remove(work_dir+"/preview_double.png") def set_fonts(self): print("[MakePreview] 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("[MakePreview] 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(share_dir+"/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("[MakePreview] 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("[MakePreview] 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(share_dir+"/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(share_dir+"/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(share_dir+"/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("[MakePreview] 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("[MakePreview] 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 = share_dir+"/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(work_dir+"/tmp_"+icon_filename,"wb") f.write(icon_file) f.close() icon = work_dir+"/tmp_"+icon_filename else: icon = share_dir+"/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(share_dir+"/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=c95_packaged_cursor_path, chicago95_theme_path=c95_packaged_theme_path, chicago95_icons_path=c95_packaged_icons_path, 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(libexec_dir+"/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(share_dir+"/assets/check.png") nocheckmark = GdkPixbuf.Pixbuf.new_from_file(share_dir+"/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(share_dir+"/assets/blank-check.png") self.icon_preview = self.builder.get_object("icon_preview") self.icon_preview.set_from_file(share_dir+"/assets/blank-check.png") self.sound_preview = self.builder.get_object("sound_preview") self.sound_preview.set_from_file(share_dir+"/assets/blank-check.png") self.cursor_preview = self.builder.get_object("cursor_preview") self.cursor_preview.set_from_file(share_dir+"/assets/blank-check.png") # Populate cursor preview self.cursor_store = self.builder.get_object("cursor_list") for current_cursor in pointers: print("[previews] Generating cursor preview for: {} (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(work_dir+"/"+cur_filename+".cur","wb") f.write(icon['ico_file']) f.close() convert_args.append("-delay") convert_args.append("{}x60".format(rate)) convert_args.append(work_dir+"/"+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(work_dir+"/"+cur_filename+".cur","wb") f.write(icon['ico_file']) f.close() convert_args.append("-delay") convert_args.append("{}x60".format(rate)) convert_args.append(work_dir+"/"+cur_filename+".cur") convert_args.append("-loop") convert_args.append("0") convert_args.append(work_dir+"/"+current_cursor+".gif") # pprint(convert_args) try: subprocess.check_call(convert_args) except: copyfile(share_dir+"/assets/blank-check.png", work_dir+"/"+current_cursor+".gif") else: cursor_file_config = self.theme.extract_cur(filename) icon_file = cursor_file_config['icon'][0]['ico_file'] f = open(work_dir+"/"+current_cursor+".cur","wb") f.write(icon_file) f.close() try: subprocess.check_call(['convert', work_dir+"/"+current_cursor+".cur", work_dir+"/"+current_cursor+".gif"]) except: copyfile(share_dir+"/assets/blank-check.png", work_dir+"/"+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 print("[previews] Getting 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: print("[previews] Generating icon preview for: {} (name: {})".format(icon, icons[icon])) 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(work_dir+"/"+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 print("[previews] Getting 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: print("[previews] Generating sound preview for: {} (name: {})".format(sound, sounds[sound])) 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(work_dir+"/"+cursor+".gif") self.cursor_text_path.set_text(self.theme_config['cursors'][cursor]['path']) else: self.cursor_preview.set_from_file(share_dir+"/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(share_dir+"/assets/blank-check.png") else: self.icon_preview.set_from_file(share_dir+"/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(share_dir+"/assets/blank-check.png") else: self.icon_preview.set_from_file(share_dir+"/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(work_dir+"/"+icon+".png") self.icon_text_path.set_text(self.theme_config['icons'][icon]['path']) else: self.icon_preview.set_from_file(share_dir+"/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 or sound file corrupt.") 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=c95_packaged_cursor_path) arg_parser.add_argument('--themefolder', help="Chicago95 theme folder to convert to new theme", default=c95_packaged_theme_path) arg_parser.add_argument('--iconsfolder', help="Chicago95 icons folder to convert to new theme", default=c95_packaged_icons_path) 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()