[openbox] theme question
Mikael Magnusson
mangosoft at comhem.se
Wed Jan 21 01:17:49 EST 2004
On Tue, 20 Jan 2004, Jonathan Daugherty wrote:
> # there is a pretty good theme builder written by katanalynx in python+gtk2
> # but it uses a bit outdated theme format, just some minor changes i think.
> # maybe you could hack a bit on that if you want?
>
> I've looked all over (and even emailed katanalynx, to no end) and
> cannot find this program -- its name or its location. Can you give
> me any advice / pointers?
>
> Thanks!
heh, i cant find it anywhere either, but i still have it installed.. maybe
someone else has a source archive, but here are the files it needs i
think, the .py files go in /usr/(local/)?lib/stylebox and the other one in
/usr/(local/)?/bin
--
Mikael Magnusson
-------------- next part --------------
# StyleBox: a style/theme editor for Openbox 2.
# Copyright (C) 2003 Ava Arachne Jarvis <ajar at katanalynx.dyndns.org>
#
# This program is free software; you can redistribute it and/or modify
# it under the terms of the GNU General Public License as published by
# the Free Software Foundation; either version 2 of the License, or
# (at your option) any later version.
#
# This program is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
# GNU General Public License for more details.
#
# You should have received a copy of the GNU General Public License
# along with this program; if not, write to the Free Software
# Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
from Xlib import Xatom
from Xlib.display import Display
from Xlib.rdb import ResourceDB
import gtk, gobject, pango
import sys, os, os.path, signal
from Theme import *
import string
# Convenience GUI widget classes.
class StyleOptionMenu(gtk.OptionMenu):
"""
An OptionMenu with default callbacks.
Instance Variables:
items : list * Any python object that can be str'd.
Supporting __eq__ is probably not a bad
idea either.
callback A function that takes an argument of the
list item type. Needed so the callback
reference doesn't disappear.
"""
def __init__(self, items, selected_item = None, callback = None, rwidget = None):
"""
Parameters:
items : list * - See instance variables: items.
selected_item : * - An item of the same type as those in items,
indicating which should be initially selected
by the option menu.
callback See instance variables: callback.
rwidget : ResourceWidget - so that we can add the connection ID of the
callback.
"""
gtk.OptionMenu.__init__(self)
self.items = items
menu = gtk.Menu()
for i in items: menu.append(gtk.MenuItem(str(i)))
self.set_menu(menu)
if selected_item: self.set_history(items.index(selected_item))
if callback:
self.callback = callback
if rwidget:
rwidget.connectWidget(self, "changed", self.doCallback)
else:
self.connect("changed", self.doCallback)
def doCallback(self, w):
self.callback(self.items[self.get_history()])
def showItem(self, item):
self.set_history(self.items.index(item))
class StyleTable(gtk.Table):
"""
A Table layout widget that takes care of keeping track of its
own current row/column to simplify child packing.
Instance Variables:
current_row : int - current row for next widget placement
current_col : int - current column for next widget placement
max_cols : int - maximum number of columns
"""
def __init__(self, maxcols = 4):
gtk.Table.__init__(self)
self.set_border_width(5)
self.set_row_spacings(5)
self.set_col_spacings(5)
self.set_homogeneous(1)
self.current_row = 0
self.current_col = 0
self.max_cols = maxcols
def nextRow(self, advance=1):
"""Advance to the next row even if the current row isn't full."""
self.current_col = 0
self.current_row += advance
def pack(self, widget, colspan=1, rowspan=1):
"""
Packs widget in the current row and current column.
Parameters:
widget : gtk.Widget - widget to pack
colspan : int - number of columns widget should span
rowspan : int - number of rows widget should span
"""
oldcol = self.current_col
self.current_col += colspan
self.attach(widget, oldcol, self.current_col,
self.current_row, self.current_row + rowspan, yoptions = gtk.FILL)
if self.current_col >= self.max_cols: self.nextRow(rowspan)
def packLabeled(self, *lwidgets):
"""
Packs a list of LabeledResourceWidgets. If the last item in
the argument list is an integer, it is taken as the column span
of the main widget of each LabeledResourceWidget.
"""
wcolspan = 1
if type(lwidgets[-1]) == int:
wcolspan = lwidgets[-1]
lwidgets = lwidgets[:-1]
for w in lwidgets: self.pack(w.label); self.pack(w.widget, wcolspan)
# Classes for working with an undo/redo stack.
class UndoStack:
class Item:
def __init__(self, type, old_value, new_value):
self.type = type
self.old_value = old_value
self.new_value = new_value
def __repr__(self):
return '<%s: %s -> %s>' % (self.type,self.old_value,self.new_value)
def __init__(self):
self.undo_stack = []
self.redo_stack = []
def add(self, type, old_value, new_value):
self.undo_stack.append(UndoStack.Item(type, old_value, new_value))
if self.redo_stack: self.redo_stack = []
def undo(self):
if self.undo_stack:
item = self.undo_stack.pop()
self.redo_stack.append(item)
return item
def redo(self):
if self.redo_stack:
item = self.redo_stack.pop()
self.undo_stack.append(item)
return item
def __repr__(self):
s = '<' + `self.undo_stack` + ' '
self.redo_stack.reverse()
s += `self.redo_stack` + '>'
self.redo_stack.reverse()
return s
# Classes for mucking around with Resources.
class ResourceWidget:
"""
An abstract class that provides a few basic methods for widgets
that deal with theme resources.
Instance Variables:
resname : str - name of the resource this widget handles
stylebox : StyleBox - the stylebox main window (for calling
dialogs and accessing global theme variables
and methods).
callbacks : list - list of pairs: (widget, callbackid), for blocking signals
when widgets are reset.
"""
def __init__(self, resname, stylebox):
self.resname = resname
self.stylebox = stylebox
self.callbacks = []
self.stylebox.widgets.append(self)
def getValue(self):
"""
Returns the value sitting behind the resource. This value can
have individual fields modified and the like.
"""
return self.stylebox.theme[self.resname].value
def setValue(self, val):
"""
Sets the entire value sitting behind the resource to a new value.
"""
if self.stylebox.theme[self.resname].value != val:
self.stylebox.theme[self.resname].value = val
self.stylebox.setChanged()
def connectWidget(self, widget, signal, callback):
self.callbacks.append((widget, widget.connect(signal, callback)))
def reset(self):
"""Resets to show the values of the current theme in stylebox."""
for w, cid in self.callbacks: w.handler_block(cid)
self.resetComponents()
for w, cid in self.callbacks: w.handler_unblock(cid)
def resetComponents(self):
"""
Used by reset() to reset the appropriate widgets. Don't worry
about blocking signals here, which is done by reset(). Also, don't
worry about any ResourceWidget children you might have, because
the reset() call will be made on *all* ResourceWidgets of a StyleBox
window.
"""
pass
def undo(self, item):
"""Processes an undo request, where item is an UndoItem."""
self.processUndo(item.type, item.old_value)
self.reset()
def redo(self, item):
"""Processes an redo request, where item is an UndoItem."""
self.processUndo(item.type, item.new_value)
self.reset()
def processUndo(self, type, value):
"""
Given an undo type and a value, set the appropriate value
on the resource this widget models. This should be overridden
by child classes.
"""
self.setValue(value)
class LabeledResourceWidget(ResourceWidget):
"""
A Resource widget with a single label and a widget.
Instance Variables:
label : gtk.Label - label of this widget
widget : gtk.Widget - any kind of widget
"""
def __init__(self, resname, stylebox, label = None):
ResourceWidget.__init__(self, resname, stylebox)
if not label:
i = resname.rfind('.')
if i < 0: label = resname
else: label = resname[i+1:]
self.label = gtk.Label(label)
self.widget = None
def defaultPack(self):
"""
Returns a gtk.Widget with a default packing of label and widget.
"""
hbox = gtk.HBox()
hbox.pack_start(self.label, 0, 0, 5)
hbox.pack_start(self.widget, 0, 0, 5)
return hbox
def setSensitive(self, sensitive):
self.label.set_sensitive(sensitive)
self.widget.set_sensitive(sensitive)
class StringResourceWidget(LabeledResourceWidget):
"""
Handles any string-type resource.
"""
def __init__(self, resname, stylebox, label = None):
LabeledResourceWidget.__init__(self, resname, stylebox, label)
self.widget = gtk.Entry()
self.connectWidget(self.widget, "changed",
lambda w: self.setValue(w.get_text()))
self.reset()
def resetComponents(self):
self.widget.set_text(self.getValue())
class JustifyResourceWidget(LabeledResourceWidget):
"""
Handles a justify resource.
"""
def __init__(self, resname, stylebox):
LabeledResourceWidget.__init__(self, resname, stylebox)
self.widget = StyleOptionMenu(JustifyResource.VALUES,
self.getValue(), self.setValue, self)
def resetComponents(self):
self.widget.showItem(self.getValue())
class FontResourceWidget(LabeledResourceWidget):
"""
Handles font-type resources. Multiple resources are handled
at the top level (xft.font, xft.flags, xft.size, etc).
"""
FDG = gtk.FontSelectionDialog('Select Font')
def getFont(cls, prev_font = None, title = 'Select Font'):
"""
Queries the user for a font.
Parameters:
prev_font : str - if given, sets the fdg font to this.
title : str - sets the title of the dialog.
Returns:
the font name if the user has selected one, None otherwise.
"""
cls.FDG.set_title(title)
font = None
if prev_font: cls.FDG.set_font_name(prev_font)
if cls.FDG.run() == gtk.RESPONSE_OK:
font = cls.FDG.get_font_name()
cls.FDG.hide()
return font
getFont = classmethod(getFont)
def __init__(self, resname, stylebox):
LabeledResourceWidget.__init__(self, resname, stylebox, 'font')
self.entry = gtk.Entry()
self.entry.set_editable(0)
self.button = gtk.Button('...')
self.connectWidget(self.button, "clicked", lambda w: self.setFont())
self.shadow = gtk.CheckButton('Shadow')
self.connectWidget(self.shadow, "toggled",
lambda w: self.setShadow(w.get_active()))
self.shadow_offset = IntegerResourceWidget(resname+'.shadow.offset', stylebox)
self.shadow_tint = IntegerResourceWidget(resname+'.shadow.tint', stylebox,
min = -100, max = 100, inc_page = 20)
self.reset()
def setShadow(self, shadow):
font = self.getValue()
if font.shadow != shadow:
font.shadow = shadow
self.stylebox.setChanged()
self.setShadowSensitives()
def setShadowSensitives(self):
shadow = self.getValue().shadow
self.shadow_offset.setSensitive(shadow)
self.shadow_tint.setSensitive(shadow)
def setFont(self):
fontname = FontResourceWidget.getFont(self.entry.get_text(), self.resname)
if fontname:
self.entry.set_text(fontname)
pfd = pango.FontDescription(fontname)
font = self.getValue()
font.family = pfd.get_family()
font.size = pfd.get_size() / 1024 # PANGO_SCALE
font.bold = pfd.get_weight() >= pango.WEIGHT_BOLD
font.italic = pfd.get_style() == pango.STYLE_ITALIC
self.stylebox.setChanged()
def resetComponents(self):
font = self.getValue()
self.entry.set_text('%s %s %d' % \
(font.family, font.bold and 'bold' or '', font.size))
self.shadow.set_active(font.shadow)
self.setShadowSensitives()
def defaultPack(self):
table = StyleTable(5)
table.pack(self.label)
hbox = gtk.HBox()
hbox.pack_start(self.entry)
hbox.pack_start(self.button, 0, 0)
table.pack(hbox, 4)
table.pack(self.shadow)
table.packLabeled(self.shadow_offset, self.shadow_tint)
return table
class ColorResourceWidget(LabeledResourceWidget):
"""
Handles a color resource.
"""
COLOR_DIALOG = gtk.ColorSelectionDialog('Select Color')
COLOR_DIALOG.colorsel.set_has_palette(1)
def getColor(cls, prev_color = None, title = 'Select Color'):
"""
Queries the user for a color.
Parameters:
prev_color : gtk.gdk.Color - if given, sets the cdg color to this.
title : str - sets the title of the dialog.
Returns:
a gtk.gdk.Color indicating the selection of the user, or None
if the user hasn't selected anything.
"""
cls.COLOR_DIALOG.set_title(title)
if prev_color: cls.COLOR_DIALOG.colorsel.set_current_color(prev_color)
color = None
if cls.COLOR_DIALOG.run() == gtk.RESPONSE_OK:
color = cls.COLOR_DIALOG.colorsel.get_current_color()
cls.COLOR_DIALOG.hide()
return color
getColor = classmethod(getColor)
def __init__(self, resname, stylebox, label = None):
LabeledResourceWidget.__init__(self, resname, stylebox, label)
self.widget = gtk.Button()
self.widget.set_size_request(24, 20)
self.connectWidget(self.widget, "clicked", lambda w: self.setColor())
self.reset()
def resetComponents(self):
self.setBackground(self.getGdkColor())
def setBackground(self, gdk_color):
for state in [gtk.STATE_ACTIVE, gtk.STATE_NORMAL,
gtk.STATE_PRELIGHT, gtk.STATE_SELECTED]:
self.widget.modify_bg(state, gdk_color)
def getGdkColor(self):
return gtk.gdk.color_parse(str(self.getValue()))
def setColor(self):
gdkcolor = ColorResourceWidget.getColor(self.getGdkColor(), self.resname)
if gdkcolor:
self.setBackground(gdkcolor)
color = self.getValue()
color.red = round((gdkcolor.red / 65535.0) * 255)
color.green = round((gdkcolor.green / 65535.0) * 255)
color.blue = round((gdkcolor.blue / 65535.0) * 255)
self.stylebox.setChanged()
class TextureWidget(StyleTable, ResourceWidget):
"""
Handles a texture resource and two colors. Even though this class
handles multiple resources, it doesn't handle multiple ones at its
own level -- colors are passed off to ColorResourceWidgets.
Instance Variables:
reliefs : gtk.OptionMenu - available texture reliefs.
border : gtk.CheckBox - active iff reliefs is set to "flat".
gradient : gtk.CheckBox - whether texture is gradient or solid.
types : gtk.OptionMenu - available gradient types. active iff
gradient is active.
interlaced : gtk.CheckBox
bevels : gtk.OptionMenu
color, colorTo, borderColor : ColorResourceWidget
row : int - keeps track of current row. Each row
can have up to two widgets.
"""
def __init__(self, basename, stylebox, parentrelative = 1):
"""
Parameters of note:
parentrelative : bool - if false, no parentrelative stuff is
shown in this widget.
"""
StyleTable.__init__(self, 4)
ResourceWidget.__init__(self, basename, stylebox)
if parentrelative:
self.parentrelative = self.createCheckBox('parentrelative',
self.setParentRelative, 4)
self.reliefs = self.createOption(Texture.RELIEFS, self.setRelief, 2)
self.border = self.createCheckBox('Border', self.setBorder, 2)
self.setReliefSensitives()
self.gradient = self.createCheckBox('Gradient', self.setGradient, 2)
self.types = self.createOption(Texture.TYPES, self.setType, 2)
self.setGradientSensitives()
self.interlaced = self.createCheckBox('Interlaced', self.setInterlaced, 2)
self.bevels = self.createOption(Texture.BEVELS, self.setBevel, 2)
self.color = ColorResourceWidget(basename + '.color', stylebox)
self.colorTo = ColorResourceWidget(basename + '.colorTo', stylebox)
self.borderColor = ColorResourceWidget(basename + '.borderColor', stylebox)
self.packLabeled(self.color, self.colorTo)
self.packLabeled(self.borderColor)
self.reset()
def resetComponents(self):
for opmenu in [self.reliefs, self.types, self.bevels]:
opmenu.set_history(0)
for cbox in [self.border, self.gradient, self.interlaced]:
cbox.set_active(0)
texture = self.getValue()
self.reliefs.set_history(texture.relief)
self.border.set_active(texture.border)
self.setReliefSensitives()
self.setBorderSensitives()
self.gradient.set_active(texture.gradient)
self.types.set_history(texture.type)
self.setGradientSensitives()
self.bevels.set_history(texture.bevel)
self.interlaced.set_active(texture.interlaced)
if hasattr(self, 'parentrelative'):
self.parentrelative.set_active(texture.parentrelative)
self.setParentRelativeSensitives()
# Signal methods
def setParentRelative(self, w):
self.getValue().parentrelative = w.get_active()
self.setParentRelativeSensitives()
self.stylebox.setChanged()
def setParentRelativeSensitives(self):
for w in [self.reliefs, self.border, self.gradient, self.types,
self.interlaced, self.bevels,
self.color.label, self.color.widget,
self.colorTo.label, self.colorTo.widget]:
w.set_sensitive(not self.parentrelative.get_active())
# ParentRelative covers multiple widgets which may have "sensitive"
# relations with each other, so if we activate them all, we'll need
# to check the rest to make sure we didn't screw up any other
# relationships.
if not self.parentrelative.get_active():
self.setReliefSensitives()
self.setGradientSensitives()
def setRelief(self, w):
self.getValue().relief = w.get_history()
self.setReliefSensitives()
self.stylebox.setChanged()
def setReliefSensitives(self):
self.border.set_sensitive(
Texture.RELIEFS[self.getValue().relief] == 'flat')
def setBorder(self, w):
self.getValue().border = w.get_active()
self.setBorderSensitives()
self.stylebox.setChanged()
def setBorderSensitives(self):
self.borderColor.setSensitive(self.getValue().border)
def setGradient(self, w):
self.getValue().gradient = w.get_active()
self.setGradientSensitives()
self.stylebox.setChanged()
def setType(self, w):
self.getValue().type = w.get_history()
self.stylebox.setChanged()
def setGradientSensitives(self):
self.types.set_sensitive(self.getValue().gradient)
def setInterlaced(self, w):
self.getValue().interlaced = w.get_active()
self.stylebox.setChanged()
def setBevel(self, w):
self.getValue().bevel = w.get_history()
self.stylebox.setChanged()
# Widget creation methods
def createOption(self, items, callback, numcols = 2):
"""
Parameters:
items : str list - items to be put into the option menu's menu
callback - a gtk callback for the "changed" signal.
left : int - left attach of widget
right : int - right attach of widget
Returns:
an option menu
"""
opmenu = StyleOptionMenu(items)
self.connectWidget(opmenu, "changed", callback)
self.pack(opmenu, numcols)
return opmenu
def createCheckBox(self, name, callback, numcols):
"""
Parameters:
name : str - label of this check box.
callback - a gtk callback for the "toggled" signal
left : int - left attach of widget
right : int - right attach of widget
Returns:
a check button.
"""
cbutton = gtk.CheckButton(name)
self.connectWidget(cbutton, "toggled", callback)
self.pack(cbutton, numcols)
return cbutton
class ButtonTextureWidget(TextureWidget):
"""
Handles a texture resource with an additional picColor.
"""
def __init__(self, basename, stylebox, parentrelative=1):
TextureWidget.__init__(self, basename, stylebox, parentrelative)
self.picColor = ColorResourceWidget(basename + '.picColor', stylebox)
self.packLabeled(self.picColor)
class TextTextureWidget(TextureWidget):
"""
Handles a texture resource with an additional textColor.
"""
def __init__(self, basename, stylebox, parentrelative=1):
TextureWidget.__init__(self, basename, stylebox, parentrelative)
self.textColor = ColorResourceWidget(basename + '.textColor', stylebox)
self.packLabeled(self.textColor)
class FontTextTextureWidget(TextTextureWidget):
"""
Handles a texture resource with a textColor and font settings.
"""
def __init__(self, basename, stylebox, parentrelative=1):
TextTextureWidget.__init__(self, basename, stylebox, parentrelative)
self.nextRow()
self.font = FontResourceWidget(basename + '.xft', stylebox)
self.pack(self.font.defaultPack(), 4, 2)
self.justify = JustifyResourceWidget(basename+'.justify', stylebox)
self.packLabeled(self.justify)
class MaskResourceWidget(LabeledResourceWidget):
BUTTON_DIR = os.path.expanduser('~/.openbox/buttons')
FILE_SELECTOR = gtk.FileSelection('Select XBM Mask')
FILE_SELECTOR.set_filename(BUTTON_DIR + '/')
FILE_SELECTOR.set_select_multiple(0)
FILE_SELECTOR.hide_fileop_buttons()
def getFilename(cls, title = 'Select XBM Mask'):
cls.FILE_SELECTOR.set_title(title)
cls.FILE_SELECTOR.complete('*.xbm')
filename = None
if cls.FILE_SELECTOR.run() == gtk.RESPONSE_OK:
filename = cls.FILE_SELECTOR.get_filename()
if cls.BUTTON_DIR == os.path.dirname(filename):
filename = os.path.basename(filename)
cls.FILE_SELECTOR.hide()
return filename
getFilename = classmethod(getFilename)
def __init__(self, resname, stylebox, label = None):
LabeledResourceWidget.__init__(self, resname, stylebox, label)
self.widget = gtk.HBox()
self.entry = gtk.Entry()
self.connectWidget(self.entry, "changed",
lambda w: self.setValue(w.get_text()))
self.widget.pack_start(self.entry)
button = gtk.Button('...')
self.connectWidget(button, "clicked", self.setMaskFile)
self.widget.pack_start(button, 0, 0)
self.reset()
def resetComponents(self):
self.entry.set_text(self.getValue())
def setMaskFile(self, w):
filename = MaskResourceWidget.getFilename(self.resname)
if filename:
self.setValue(filename)
self.entry.set_text(filename)
self.stylebox.setChanged()
class BulletMenuResourceWidget(LabeledResourceWidget):
def __init__(self, resname, stylebox):
LabeledResourceWidget.__init__(self, resname, stylebox, 'type')
self.widget = StyleOptionMenu(BulletResource.VALUES,
self.getValue(), self.setValue, self)
def resetComponents(self):
self.widget.showItem(self.getValue())
class BulletPositionResourceWidget(LabeledResourceWidget):
def __init__(self, resname, stylebox):
LabeledResourceWidget.__init__(self, resname, stylebox, 'position')
self.widget = StyleOptionMenu(BulletPositionResource.VALUES,
self.getValue(), self.setValue, self)
def resetComponents(self):
self.widget.showItem(self.getValue())
class IntegerResourceWidget(LabeledResourceWidget):
def __init__(self, resname, stylebox, label = None,
min = 1, max = 20, inc_step = 1, inc_page = 5):
LabeledResourceWidget.__init__(self, resname, stylebox, label)
self.widget = gtk.SpinButton()
self.widget.set_numeric(1)
self.widget.set_range(min, max)
self.widget.set_increments(inc_step, inc_page)
self.connectWidget(self.widget, "value_changed",
lambda w: self.setValue(w.get_value_as_int()))
self.reset()
def resetComponents(self):
self.widget.set_value(self.getValue())
class StyleBox(gtk.Window):
"""
Main window of the StyleBox application. Currently, it can only handle
one theme at a time.
Instance Variables:
theme : Theme - current theme being edited.
notebook : gtk.NoteBook - keeps track of separate pages.
tree : gtk.TreeStore - tree for hierarchy of notebook pages
itemfactory : gtk.ItemFactory - factory for menu items. must keep
a reference to.
root : Xlib.Display - current X display; needed to get information
about currently running openbox
checkpoint : int - current checkpoint number
"""
OPENBOX_RC = os.environ['HOME'] + '/.openbox/rc'
FRONTIS = 'Stylebox version 0.3, Copyright (C) 2003 Ava Arachne Jarvis.\n' + \
'Stylebox comes with ABSOLUTELY NO WARRANTY; for details see LICENSE.' +\
'This is free software, and you are welcome to redistribute it' + \
'under certain conditions; see LICENSE for details.'
def __init__(self):
gtk.Window.__init__(self)
self.connect("destroy", self.quit)
self.changed = 0
self.theme = None
self.display = Display()
self.widgets = []
vbox = gtk.VBox()
self.add(vbox)
vbox.pack_start(self.createMenuBar(), 0, 0)
hbox = gtk.HBox()
vbox.pack_start(hbox)
vbox2 = gtk.VBox()
vbox2.pack_start(self.createNotebook())
vbox2.pack_start(self.createButtonArea(), 0, 0)
hbox.pack_start(self.createTreeView())
hbox.pack_start(vbox2)
self.statusbar = gtk.Statusbar()
vbox.pack_start(self.statusbar, 0, 0)
self.getCurrentTheme()
self.createPanels()
self.createDialogs()
def setTitle(self):
self.set_title('Stylebox: ' + (self.theme.filename or '*new*') + \
(self.changed and ' [changed]' or ''))
def setChanged(self):
if not self.changed:
self.changed = 1
self.setTitle()
def getCurrentTheme(self):
rc_rdb = ResourceDB(StyleBox.OPENBOX_RC)
filename = rc_rdb.get('session.styleFile', 'session.styleFile')
self.openTheme(filename)
self.setStatus('Openbox loaded current style')
def setCurrentTheme(self, filename):
rc_rdb = ResourceDB(StyleBox.OPENBOX_RC)
rc_rdb.insert('session.styleFile', filename)
rdb_file = open(StyleBox.OPENBOX_RC, 'w')
rdb_file.write(rc_rdb.output())
rdb_file.close()
self.reconfigureOpenbox()
self.setStatus('Openbox loaded ' + filename)
# Signal methods
def quit(self, *rest):
if self.changed:
if not self.askUser("Theme changed and not saved. Still quit?"):
return
gtk.main_quit()
def showPageButtonPress(self, view, event):
path = view.get_path_at_pos(event.x, event.y)[0]
self.notebook.set_current_page(
self.tree.get_value(self.tree.get_iter(path), 0))
selection = view.get_selection()
selection.select_path(path)
def showPageKeyRelease(self, view, event):
selection = view.get_selection()
model, iter = selection.get_selected()
keyname = gtk.gdk.keyval_name(event.keyval)
if keyname in ['Down', 'Up']:
self.notebook.set_current_page(model.get_value(iter, 0))
elif keyname == 'Left':
def collapse_parent():
parent_iter = model.iter_parent(iter)
if parent_iter:
path = model.get_path(parent_iter)
view.collapse_row(path)
selection.select_path(path)
if model.iter_has_child(iter):
path = model.get_path(iter)
if view.row_expanded(path):
view.collapse_row(path)
else:
collapse_parent()
else:
collapse_parent()
elif keyname == 'Right':
path = model.get_path(iter)
view.expand_row(path, 0)
def openTheme(self, filename = None):
self.theme = Theme(filename)
for w in self.widgets: w.reset()
# set changed after resetting widgets, because they
# might falsely indicate change while reading new values
self.changed = 0
self.setTitle()
self.setStatus('Opened ' + (filename or '*new*'))
def chooseTheme(self, *rest):
if self.changed:
if not self.askUser("Theme changes and not saved. Still open?"):
return
filename = self.getFilename('Select Theme')
if filename:
self.openTheme(filename)
self.setCurrentTheme(filename)
def saveTheme(self, filename = None):
if filename:
self.theme.save(filename)
self.setStatus('Saved as ' + filename)
self.changed = 0
self.setTitle()
self.setCurrentTheme(filename)
else:
if not self.theme.filename:
self.saveThemeAs()
else:
self.theme.save()
self.changed = 0
self.setTitle()
self.setStatus('Saved.')
self.reconfigureOpenbox()
def saveThemeAs(self, *rest):
filename = self.getFilename('Save Theme As...')
if not filename: return
if os.path.exists(filename):
if not self.askUser('%s already exists. Overwrite?' % filename):
return
self.saveTheme(filename)
def setStatus(self, message):
self.statusbar.push(self.statusbar.get_context_id('Main Window'), message)
def reconfigureOpenbox(self, *rest):
root = self.display.screen().root
blackbox_pid_atom = self.display.display.get_atom('_BLACKBOX_PID')
prop = root.get_property(blackbox_pid_atom, Xatom.CARDINAL, 0, 1)
if prop:
ob_pid = int(prop.value.tolist()[0])
os.kill(ob_pid, signal.SIGHUP)
self.setStatus('Openbox reconfigured.')
# Dialogs
def getFilename(self, title):
self.fdg.set_title(title)
filename = None
if self.fdg.run() == gtk.RESPONSE_OK:
filename = self.fdg.get_filename()
self.fdg.hide()
return filename
def askUser(self, message):
ask_dialog = gtk.MessageDialog(self, gtk.DIALOG_MODAL,
gtk.MESSAGE_WARNING, gtk.BUTTONS_YES_NO, message)
response = ask_dialog.run()
ask_dialog.destroy()
return (response == gtk.RESPONSE_YES)
def showAbout(self, *rest):
md = gtk.MessageDialog(self, buttons=gtk.BUTTONS_OK,
message_format=StyleBox.FRONTIS)
md.set_title('About Stylebox')
md.run()
md.destroy()
# Widget-creating methods
def createMenuBar(self):
menu_items = (
('/_File', None, None, 0, '<Branch>'),
('/File/_New', '<control>N', lambda *w: self.openTheme(), 0,
'<StockItem>', gtk.STOCK_NEW),
('/File/_Open', '<control>O', self.chooseTheme, 0,
'<StockItem>', gtk.STOCK_OPEN),
('/File/_Save', '<control>S', lambda *w: self.saveTheme(), 0,
'<StockItem>', gtk.STOCK_SAVE),
('/File/Save _As...', '<control>A', self.saveThemeAs, 0,
'<StockItem>', gtk.STOCK_SAVE_AS),
('/File/sep1', None, None, 0, '<Separator>'),
('/File/_Quit', '<control>Q', self.quit, 0, '<StockItem>', gtk.STOCK_QUIT),
#('/_Edit', None, None, 0, '<Branch>'),
#('/Edit/_Revert', '<control>R', None, 0, '<StockItem>',gtk.STOCK_REVERT_TO_SAVED),
('/Help', None, None, 0, '<LastBranch>'),
('/Help/About', None, self.showAbout, 0, '')
)
accelgroup = gtk.AccelGroup()
self.add_accel_group(accelgroup)
self.item_factory = gtk.ItemFactory(gtk.MenuBar, '<main>', accelgroup)
self.item_factory.create_items(menu_items)
return self.item_factory.get_widget('<main>')
def createButtonArea(self):
quit_button = gtk.Button(gtk.STOCK_QUIT)
quit_button.connect("clicked", self.quit)
save_button = gtk.Button(gtk.STOCK_SAVE)
save_button.connect("clicked", lambda *w: self.saveTheme())
saveas_button = gtk.Button(gtk.STOCK_SAVE_AS)
saveas_button.connect("clicked", lambda *w: self.saveThemeAs())
buttons = [save_button, saveas_button, quit_button]
buttonarea = gtk.HButtonBox()
buttonarea.set_layout(gtk.BUTTONBOX_END)
for b in buttons:
b.set_use_stock(1)
buttonarea.pack_start(b)
return buttonarea
def createTreeView(self):
self.tree = gtk.TreeStore(gobject.TYPE_INT, gobject.TYPE_STRING)
treeview = gtk.TreeView(self.tree)
treeview.set_headers_visible(0)
treeview.append_column(
gtk.TreeViewColumn('', gtk.CellRendererText(), text=1))
treeview.connect("button_press_event", self.showPageButtonPress)
treeview.connect("key_release_event", self.showPageKeyRelease)
scrolled = gtk.ScrolledWindow()
scrolled.set_policy(gtk.POLICY_AUTOMATIC, gtk.POLICY_AUTOMATIC)
scrolled.add_with_viewport(treeview)
scrolled.set_size_request(175, 200)
return scrolled
def createNotebook(self):
self.notebook = gtk.Notebook()
self.notebook.set_show_tabs(0)
self.notebook.set_show_border(0)
return self.notebook
def createDialogs(self):
self.fdg = gtk.FileSelection('Select File')
# Panel-creating functions
def createPanel(self, name, parent = None):
"""
Creates a panel in the main window's notebook.
Parameters:
name : str - name of the panel
parent : gtk.TreeIter - parent node of this panel
Returns:
a Panel and an iter into the main window's tree.
"""
panel = gtk.Frame()
panel.set_shadow_type(gtk.SHADOW_NONE)
self.notebook.append_page_menu(panel, gtk.Label(name), gtk.Label(name))
iter = self.tree.insert_before(parent, None)
self.tree.set_value(iter, 0, self.page)
self.tree.set_value(iter, 1, name)
self.page += 1
return panel, iter
def createPanels(self):
self.page = 0
self.createInfoPanel()
self.createToolbarPanel()
self.createMenuPanel()
self.createWindowPanel()
self.createMiscPanel()
del self.page
def createInfoPanel(self):
panel, iter = self.createPanel("style")
table = StyleTable(4)
panel.add(table)
for s in ['name', 'author', 'date', 'credits', 'comments']:
table.packLabeled(StringResourceWidget('style.' + s, self), 3)
def createToolbarPanel(self):
panel, toolbar_iter = self.createPanel("toolbar")
t = TextureWidget('toolbar', self, parentrelative=0)
t.nextRow()
t.pack(FontResourceWidget('toolbar.xft', self).defaultPack(), 4, 2)
t.packLabeled(JustifyResourceWidget('toolbar.justify',self))
panel.add(t)
for s in ['label', 'windowLabel', 'clock']:
panel, iter = self.createPanel(s, toolbar_iter)
panel.add(TextTextureWidget('toolbar.'+s, self))
panel, button_iter = self.createPanel('button', toolbar_iter)
t = ButtonTextureWidget('toolbar.button', self)
t.nextRow()
t.packLabeled(MaskResourceWidget('toolbar.button.left.mask', self, 'left'), 3)
t.packLabeled(MaskResourceWidget('toolbar.button.right.mask',self,'right'), 3)
panel.add(t)
panel, iter = self.createPanel('pressed', button_iter)
panel.add(TextureWidget('toolbar.button.pressed', self))
def createMenuPanel(self):
panel, menu_iter = self.createPanel('menu')
table = StyleTable()
table.pack(gtk.Label('Menu Masks'), 4)
table.packLabeled(MaskResourceWidget('menu.arrow.mask', self, 'arrow'), 3)
table.packLabeled(MaskResourceWidget('menu.selected.mask', self, 'selected'), 3)
panel.add(table)
panel, iter = self.createPanel('title', menu_iter)
panel.add(FontTextTextureWidget('menu.title', self, parentrelative=0))
panel, iter = self.createPanel('frame', menu_iter)
t = TextureWidget('menu.frame', self, parentrelative=0)
textColor = ColorResourceWidget('menu.frame.textColor', self)
disableColor = ColorResourceWidget('menu.frame.disableColor', self)
t.packLabeled(textColor, disableColor)
t.nextRow()
t.pack(FontResourceWidget('menu.frame.xft', self).defaultPack(), 4, 2)
t.packLabeled(JustifyResourceWidget('menu.frame.justify', self))
panel.add(t)
panel, iter = self.createPanel('hilite', menu_iter)
t = TextTextureWidget('menu.hilite', self)
panel.add(t)
panel, iter = self.createPanel('bullet', menu_iter)
table = StyleTable(2)
table.packLabeled(BulletMenuResourceWidget('menu.bullet', self),
BulletPositionResourceWidget('menu.bullet.position', self))
panel.add(table)
def createWindowPanel(self):
panel, window_iter = self.createPanel('window')
vbox = gtk.VBox()
vbox.pack_start(
FontResourceWidget('window.xft', self).defaultPack(), 0, 0)
vbox.pack_start(
JustifyResourceWidget('window.justify', self).defaultPack(), 0, 0)
panel.add(vbox)
focus_list = ['focus', 'unfocus']
for texture,wclass,parentrelative in [ ('title', TextureWidget, 0),
('label', TextTextureWidget, 1),
('button', ButtonTextureWidget, 1) ]:
t_panel, t_iter = self.createPanel(texture, window_iter)
for focus in focus_list:
f_panel, f_iter = self.createPanel(focus, t_iter)
f_panel.add(wclass('window.%s.%s' % (texture,focus), self, parentrelative))
button_iter = self.tree.iter_parent(f_iter)
button_panel = \
self.notebook.get_nth_page(self.tree.get_value(button_iter, 0))
table = StyleTable()
table.pack(gtk.Label('Button Masks:'), 4)
for b in ['close', 'max', 'stick', 'icon']:
table.packLabeled(MaskResourceWidget('window.button.%s.mask' % b, self, b), 3)
button_panel.add(table)
pressed, pressed_iter = self.createPanel('pressed', button_iter)
vbox = gtk.VBox()
vbox.pack_start(gtk.Label('(Compatability with Blackbox)'), 0, 0, 10)
vbox.pack_start(TextureWidget('window.button.pressed', self, parentrelative=0))
pressed.add(vbox)
for focus in focus_list:
f_panel, f_iter = self.createPanel(focus, pressed_iter)
f_panel.add(TextureWidget('window.button.pressed.'+focus,self,parentrelative=0))
for texture,wclass, parentrelative in [ ('handle', TextureWidget, 0),
('grip', TextureWidget, 1) ]:
t_panel, t_iter = self.createPanel(texture, window_iter)
for focus in focus_list:
f_panel, f_iter = self.createPanel(focus, t_iter)
f_panel.add(wclass('window.%s.%s' % (texture,focus), self, parentrelative))
frame_panel, frame_iter = self.createPanel('frame', window_iter)
table = StyleTable()
for focus in focus_list:
table.packLabeled(ColorResourceWidget('window.frame.%sColor' % focus, self))
frame_panel.add(table)
def createMiscPanel(self):
panel, misc_iter = self.createPanel('Miscellaneous')
table = StyleTable(4)
for w in ['borderWidth', 'bevelWidth', 'handleWidth', 'frameWidth']:
table.packLabeled(IntegerResourceWidget(w, self))
table.packLabeled(ColorResourceWidget('borderColor', self))
table.nextRow()
table.packLabeled(StringResourceWidget('rootCommand', self), 3)
panel.add(table)
# vim: set fdm=expr:
-------------- next part --------------
# StyleBox: a style/theme editor for Openbox 2.
# Copyright (C) 2003 Ava Arachne Jarvis <ajar at katanalynx.dyndns.org>
#
# This program is free software; you can redistribute it and/or modify
# it under the terms of the GNU General Public License as published by
# the Free Software Foundation; either version 2 of the License, or
# (at your option) any later version.
#
# This program is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
# GNU General Public License for more details.
#
# You should have received a copy of the GNU General Public License
# along with this program; if not, write to the Free Software
# Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
import string, os, copy, sys
# Basic data classes.
# Only responsible for knowledge of somewhat weird details
# of the data type they represent.
class Color(object):
"""
Represents an 8-bit color in RGB space.
A Color with a set name will ignore its red/green/blue components.
Setting red/green/blue on a Color will reset its name to None.
Instance Variables:
name : str - a color name from rgb.txt.
red : int - red component, from 0 to 255 inclusive.
green : int - green component, from 0 to 255 inclusive.
blue : int - blue component, from 0 to 255 inclusive.
"""
def parse(s):
"""
Parameters:
s : str - in rgb:hex/hex/hex, #hexhexhex, or name format.
Returns:
Color object.
"""
color = None
if s[0] == '#':
clen = (len(s) - 1) / 3
start = 1
end = clen + 1
color = [None, None, None]
for i in range(3):
color[i] = s[start:end]
start = end
end += clen
elif s[0:4] == 'rgb:':
color = s[4:].split('/')
else:
return Color(name = s)
for i in range(3):
clen = len(color[i])
r = ''
for j in range(clen):
r += 'F'
r = int(r, 16)
color[i] = int((int(color[i], 16) / float(r)) * 255)
return Color(color[0], color[1], color[2])
parse = staticmethod(parse)
def __init__(self, red = 0, green = 0, blue = 0, name = None):
"""
Parameters:
red, green, blue : int - between 0 and 255.
name : str - if given, red/green/blue are ignored.
"""
self.name = name
if not name:
self.__red = red
self.__green = green
self.__blue = blue
# Property methods
def setRed(self, red):
if red < 0 or red > 255:
raise ValueError("red must be between 0 and 255!")
self.__red = red
self.name = None
def setGreen(self, green):
if green < 0 or green > 255:
raise ValueError("green must be between 0 and 255!")
self.__green = green
self.name = None
def setBlue(self, blue):
if blue < 0 or blue > 255:
raise ValueError("blue must be between 0 and 255!")
self.__blue = blue
self.name = None
red = property(lambda c: c.__red, setRed)
green = property(lambda c: c.__green, setGreen)
blue = property(lambda c: c.__blue, setBlue)
def __repr__(self):
if self.name:
return '<Color: name = %s>' % self.name
else:
return '<Color: red = %s, green = %s, blue = %s>' % \
(`self.red`, `self.green`, `self.blue`)
def __str__(self):
if self.name:
return self.name
else:
str = '#'
for c in [self.red, self.green, self.blue]:
str += '%02x' % c
return str
def __eq__(self, other):
if isinstance(other, Color):
if other.name:
return self.name and (self.name == other.name)
elif self.name:
return 0
else:
return self.red == other.red and \
self.blue == other.blue and \
self.green == other.green
return 0
class Texture(object):
"""
Represents an Openbox texture (as in the texture line, no colors).
Class variables:
RELIEFS : str list - Available reliefs.
TYPES : str list - Available gradient types.
BEVELS : str list - Available bevel render types.
Instance variables:
parentrelative : bool - if true, all other attributes are ignored.
relief : int - index into RELIEFS.
border : bool - ignored for any relief type except 'flat'.
gradient : bool - if true, this is a gradient texture;
false, a solid texture.
type : int - index into TYPES; only acknowledged if gradient is true.
interlaced : bool - true if this texture is interlaced.
bevel : int - index into BEVELS.
"""
RELIEFS = ['flat', 'raised', 'sunken']
TYPES = ['horizontal', 'vertical', 'diagonal', 'crossdiagonal',
'pipecross', 'elliptic', 'rectangle', 'pyramid']
BEVELS = ['bevel1', 'bevel2']
# keyword dictionaries for parsing
def makeListDict(list):
dict = {}
for i in range(len(list)): dict[list[i]] = i
return dict
dictRELIEFS = makeListDict(RELIEFS)
dictTYPES = makeListDict(TYPES)
dictBEVELS = makeListDict(BEVELS)
del makeListDict
def parse(s):
words = s.split()
t = Texture()
for word in words:
word = word.lower()
if word in Texture.RELIEFS:
t.relief = Texture.dictRELIEFS[word]
elif word == 'flatborder':
t.relief = Texture.dictRELIEFS['flat']
t.border = 1
elif word in Texture.TYPES:
t.type = Texture.dictTYPES[word]
elif word in Texture.BEVELS:
t.bevel = Texture.dictBEVELS[word]
elif word == 'border': t.border = 1
elif word == 'interlaced': t.interlaced = 1
elif word == 'gradient': t.gradient = 1
elif word == 'solid': t.gradient = 0
elif word == 'parentrelative': t.parentrelative = 1
return t
parse = staticmethod(parse)
def __init__(self):
self.parentrelative = 0
self.relief = 0
self.border = 0
self.gradient = 0
self.type = 0
self.interlaced = 0
self.bevel = 0
def __str__(self):
if self.parentrelative: return 'parentrelative'
s = Texture.RELIEFS[self.relief]
if s == 'flat' and self.border:
s += ' border'
if self.gradient:
s += ' gradient ' + Texture.TYPES[self.type]
else:
s += ' solid'
if self.interlaced: s += ' interlaced'
s += ' ' + Texture.BEVELS[self.bevel]
return s
class XftFont(object):
def __init__(self, family = None, size = 7, bold = 0, italic = 0, shadow = 0):
self.family = family
self.size = size
self.bold = bold
self.italic = italic
self.shadow = shadow
def parseFlags(self, s):
bold, italic, shadow = 0, 0, 0
for word in s.split():
if word == 'bold': bold = 1
elif word == 'normal':
bold = 0
italic = 0
elif word == 'shadow': shadow = 1
elif word == 'italic': italic = 1
self.bold = bold
self.italic = italic
self.shadow = shadow
def strFlags(self):
s = ''
if self.bold and self.italic: s = 'bold italic'
elif self.bold: s = 'bold'
elif self.italic: s = 'italic'
else: s = 'normal'
if self.shadow: s += ' shadow'
return s
# Resource classes.
# Responsible for reading attributes from an
# X resource database, and for returning strings suitable for
# recreating the X resource database file.
from Xlib.rdb import ResourceDB
class Resource:
"""
Represents an X resource line.
Instance Variables:
name : str - name of the resource
value : * - value of the resource. Must implement __str__.
"""
def __init__(self, name, value = ''):
"""
Parameters:
name : str - name of the resource
value : * - default value of the resource
"""
self.name = name
self.value = value
def parse(self, s):
"""
Parses a string gotten from an X resource database. This
should probably be overridden by child classes (they don't
have to override read()).
"""
return s
def read(self, rdb):
"""
Retrives a value (if name is in the database) and sets value
to it via parse().
Parameters:
rdb : ResourceDB - X resource database object
"""
try:
value = rdb.get(self.name, self.name)
except:
return
if value != None:
try:
self.value = self.parse(value)
except:
etype, evalue, traceback = sys.exc_info()
print "Parse error occurred for resource %s:" % self.name
print etype, ':', evalue
def __str__(self):
return '%s: %s\n' % (self.name, self.value)
class IntegerResource(Resource):
def __init__(self, name, value = 0): Resource.__init__(self, name, value)
def parse(self, s): return string.atoi(s)
class ColorResource(Resource):
def __init__(self, name, value = None):
Resource.__init__(self, name, value or Color(0, 0, 0))
def parse(self, s): return Color.parse(s)
class TextureResource(Resource):
def __init__(self, name, value = None):
Resource.__init__(self, name, value or Texture())
def parse(self, s): return Texture.parse(s)
class XftFontResource(Resource):
def __init__(self, name, value = None):
Resource.__init__(self, name, value or XftFont())
def read(self, rdb):
vfont = rdb.get(self.name + '.font', self.name + '.font')
vflags = rdb.get(self.name + '.flags', self.name + '.flags')
vsize = rdb.get(self.name + '.size', self.name + '.size')
# vfont and vflags can be '' and still valid
if vfont != None: self.value.family = vfont
if vflags != None: self.value.parseFlags(vflags)
if vsize: self.value.size = string.atoi(vsize)
def __str__(self):
s = '%s.font: %s\n' % (self.name, self.value.family)
s += '%s.size: %d\n' % (self.name, self.value.size)
s += '%s.flags: %s\n' % (self.name, self.value.strFlags())
return s
class JustifyResource(Resource):
VALUES = ['left', 'center', 'right']
def __init__(self, name, value = 'center'):
Resource.__init__(self, name, value)
def parse(self, s):
s = s.lower()
if s not in JustifyResource.VALUES: s = 'center'
return s
class BulletResource(Resource):
VALUES = ['empty', 'triangle', 'square', 'diamond']
def __init__(self, name, value = 'triangle'):
Resource.__init__(self, name, value)
def parse(self, s):
s = s.lower()
if s not in BulletResource.VALUES: s = 'triangle'
return s
class BulletPositionResource(Resource):
VALUES = ['left', 'right']
def __init__(self, name, value = 'left'):
Resource.__init__(self, name, value)
def parse(self, s):
s = s.lower()
if s not in BulletPositionResource.VALUES: s = 'left'
return s
class Theme(object):
"""
An Openbox theme. Can be directly indexed via *full* resource names,
e.g. theme['toolbar.xft.font'].
Instance Variables:
resources : dict(str -> Resource) - resources that make up the theme
filename : str - name of the theme file
"""
def __init__(self, filename = None):
self.resources = {}
self.filename = None
self.groups = []
if filename: self.read(filename)
else: self.__initializeComponents()
def addGroup(self, description = ''):
self.groups.append({ 'desc': description, 'resources': [] })
def addToGroup(self, resname):
self.groups[-1]['resources'].append(resname)
def read(self, filename):
"""
Reads theme resources from a file. Completely obliterates any
previous resource objects in resources.
"""
self.__initializeComponents()
self.filename = filename
rdb = ResourceDB(self.filename)
for k,r in self.resources.items(): r.read(rdb)
# Initialize new resources from any old compatible ones
compat_list = []
for focus in ['focus', 'unfocus']:
old_base = 'window.button.pressed'
new_base = '%s.%s' % (old_base, focus)
compat_list.append((new_base, old_base))
for suffix in ['.color', '.colorTo']:
compat_list.append((new_base+suffix, old_base+suffix))
for r_new, r_old in compat_list:
if not rdb.get(r_new, r_new):
self[r_new].value = copy.deepcopy(self[r_old].value)
def save(self, filename = None):
"""
Saves theme resources to a file.
Parameters:
filename : str - if given, saves to filename and uses that
as the theme file for this object.
"""
if filename:
self.filename = filename
f = open(self.filename, 'w')
for group in self.groups:
if group['desc']:
f.write('!! %s\n' % group['desc'])
for r in group['resources']:
f.write(str(self[r]))
f.write('\n')
f.close()
def __initializeComponents(self):
self.addGroup('Theme information')
for s in map(lambda s: 'style.'+s, ['name','author','date','credits','comments']):
self.addResource(s)
self.addGroup('Toolbar settings')
self.addTexture('toolbar')
self.addFont('toolbar')
self.addGroup()
for s in map(lambda s: 'toolbar.'+s, ['button','button.pressed']):
self.addButtonTexture(s)
for s in map(lambda s: 'toolbar.'+s, ['label','clock','windowLabel']):
self.addGroup()
self.addTextTexture(s)
self.addGroup('Menu settings')
self.addGroup()
self.addFontTextTexture('menu.title')
self.addGroup()
self.addFontTextTexture('menu.frame')
self.addResource('menu.frame.disableColor', ColorResource)
self.addGroup()
self.addTextTexture('menu.hilite')
self.addGroup()
self.addResource('menu.bullet', BulletResource)
self.addResource('menu.bullet.position', BulletPositionResource)
self.addGroup('General window settings')
self.addTexture('window.button.pressed')
self.addFont('window')
for s in 'focus unfocus'.split():
self.addGroup('%sed window settings' % s)
self.addResource('window.frame.%sColor' % s, ColorResource)
for t in map(lambda x: 'window.%s.%s' % (x, s), ['title', 'handle', 'grip']):
self.addGroup()
self.addTexture(t)
self.addGroup()
self.addTextTexture('window.label.%s' % s)
self.addGroup()
self.addButtonTexture('window.button.%s' % s)
self.addGroup()
self.addTexture('window.button.pressed.%s' % s)
self.addGroup('Global width settings')
for s in 'borderWidth bevelWidth handleWidth frameWidth'.split():
self.addResourceWithDefault(s, 1, IntegerResource)
self.addGroup('Miscellaneous settings')
self.addResource('borderColor', ColorResource)
self.addResource('rootCommand')
self.addGroup('Bitmap button masks')
self.addGroup()
for s in 'close max stick icon'.split():
self.addResource('window.button.%s.mask' % s)
self.addGroup()
for s in 'arrow selected'.split():
self.addResource('menu.%s.mask' % s)
self.addGroup()
for s in 'left right'.split():
self.addResource('toolbar.button.%s.mask' % s)
# Mapping methods
def __getitem__(self, key):
return self.resources[key]
def __setitem__(self, key, val):
self.resources[key] = val
def __delitem__(self, key):
del self.resources[key]
def __contains__(self, key):
return key in self.resources
# Methods to work with adding resources in groups.
def addResource(self, name, rclass = Resource):
"""
Adds to resources a single resource object.
Parameters:
name : str - name of the resource
rclass : class - (Resource-derived) class of the resource
"""
self[name] = rclass(name)
self.addToGroup(name)
def addResourceWithDefault(self, name, value, rclass = Resource):
"""
Like addResource(), but with a default value.
"""
self[name] = rclass(name, value)
self.addToGroup(name)
def addTexture(self, name):
"""
Adds a texture and three color resources, using basename name.
"""
self.addResource(name, TextureResource)
self.addResource('%s.color' % name, ColorResource)
self.addResource('%s.colorTo' % name, ColorResource)
self.addResource('%s.borderColor' % name, ColorResource)
def addButtonTexture(self, name):
"""
Adds all things a "Texture" needs, plus a picColor.
"""
self.addTexture(name)
self.addResource('%s.picColor' % name, ColorResource)
def addTextTexture(self, name):
"""
Adds all things a "Texture" needs, plus a textColor.
"""
self.addTexture(name)
self.addResource('%s.textColor' % name, ColorResource)
def addFont(self, name):
"""
Adds font-specific resources (which include justify).
"""
self.addResource('%s.justify' % name, JustifyResource)
self.addResourceWithDefault('%s.font' % name, 'fixed')
self.addResource('%s.xft' % name, XftFontResource)
self.addResourceWithDefault('%s.xft.shadow.offset' % name, 1, IntegerResource)
self.addResourceWithDefault('%s.xft.shadow.tint' % name, 25, IntegerResource)
def addFontTextTexture(self, name):
"""
Combines font-specific resources with a TextTexture.
"""
self.addTextTexture(name)
self.addFont(name)
# vim: set fdm=expr:
-------------- next part --------------
#!/usr/bin/python
#
# StyleBox: a style/theme editor for Openbox 2.
# Copyright (C) 2003 Ava Arachne Jarvis <ajar at katanalynx.dyndns.org>
#
# This program is free software; you can redistribute it and/or modify
# it under the terms of the GNU General Public License as published by
# the Free Software Foundation; either version 2 of the License, or
# (at your option) any later version.
#
# This program is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
# GNU General Public License for more details.
#
# You should have received a copy of the GNU General Public License
# along with this program; if not, write to the Free Software
# Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
import sys
sys.path.append('/usr/local/lib/stylebox')
import pygtk
pygtk.require('2.0')
import gtk
from StyleBox import StyleBox
print StyleBox.FRONTIS
s = StyleBox()
s.show_all()
gtk.main()
More information about the openbox
mailing list