; mxflat/mxflat.jl
;;; START

#| theme.jl for mxflat (v0.7.3) sawfish theme

   Copyright (C) 2002 mx & ta

   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., 675 Mass Ave, Cambridge, MA 02139, USA.

   authors: mx (email) & ta (dev.null@gmx.net)
|#

(require 'gradient)
; need hash tables for icon cache
(require 'rep.data.tables)
(require 'rep.io.timers)
(require 'sawfish.wm.util.recolor-image)

; required when gaoling is turned off (for debugging)
;(require 'rep.mmsystem)
;(require 'sawfish.wm.custom)
;(require 'sawfish.wm.colors)
;(require 'sawfish.wm.images)
;(require 'sawfish.wm.fonts)

;   ---------
;;; REDRAWING
;   ---------

;
;; redrawing:internal vars
;

; we need to define theme here
; so we can reach them from our defcustom clauses

; cache for window icons
(defvar icon-cache (make-weak-table eq-hash eq))

; holds the shadow side for the current selected option
(defvar shadow-side ())

; for now empty, see below
(define (recolor-all))

;
;; redrawing:functions
;

(define (get-symbol #!rest strings)
	(intern (mapconcat identity strings ""))
)
(define (check-symbol #!rest strings)
	(let
		(
			(name (intern (mapconcat identity strings "")))
		)
		(if (boundp name)
			(symbol-value name)
			nil
		)
	)
)

(define (refresh-one-frame w)
	(if (eq (window-get w 'current-frame-style) 'mxflat)
		(refresh-window w)
	)
)

(define (refresh-all-frames)
	(map-windows refresh-window)
)

(define (rebuild-all-frames)
		(rebuild-frames-with-style 'mxflat)
)

; same as rebuild but this also deletes the icon cache
(define (recreate-all-frames)
	(reframe-windows-with-style 'mxflat)
)

; after the light source direction is updated we have to destroy the cache
; with the correct shadow sides, it will be recreated on next access
(define (after-set-shadow-light-source)
	(setq shadow-side ())
	(recreate-all-frames)
)

(define update-interval nil)
(define update-timer 
	(make-timer (lambda ()
		(refresh-all-frames)
		(if update-interval
			(set-timer update-timer update-interval)
		)
	))
)

(define (after-set-title-periodic-update)
	(if (check-symbol "mxflat:title-periodic-update")
		(set-timer update-timer update-interval)
		(delete-timer update-timer)
	)
)

(define (after-set-title-update-interval)
	(setq update-interval (check-symbol "mxflat:title-update-interval"))
	(after-set-title-periodic-update)
)

(define (recreate-all-frames-clear-cache)
	; reset icon cache
	(setq icon-cache (make-weak-table eq-hash eq))
	(recreate-all-frames)
)

(define (recreate-all-frames-recolor-all)
	(recolor-all)
	(recreate-all-frames)
)

;   -------------
;;; CONFIGURATION
;   -------------

;
;; configuration:keymaps
;

; we need to define our default keymaps here,
; so we can reach them from our defcustom clauses

(defvar sticky-button-keymap
	(bind-keys (make-keymap)
		"Button1-Off" 'toggle-window-sticky
	)
)
(defvar previous-button-keymap
	(bind-keys (make-keymap)
		"Button6-Click" 'pack-window-left
		"Button3-Off" '(command-sequence `((send-to-workspace ,(1+ (car (workspace-limits))))
											(activate-workspace ,(1+ (car (workspace-limits))))))
		"Button2-Click" 'popup-workspace-list
		"Button1-Off" 'send-to-previous-workspace
	)
)
(defvar next-button-keymap
	(bind-keys (make-keymap)
		"Button6-Click" 'pack-window-right
		"Button3-Off" '(command-sequence `((send-to-workspace ,(1+ (cdr (workspace-limits))))
											(activate-workspace ,(1+ (cdr (workspace-limits))))))
		"Button2-Click" 'popup-workspace-list
		"Button1-Off" 'send-to-next-workspace
	)
)

(defvar configure-button-keymap
	(bind-keys (make-keymap)
		"Button1-Off" 'customize
		"Button3-Off" '(call-command
			(lambda ()
				(if mxflat:custom-title
					(setq mxflat:custom-title nil)
					(setq mxflat:custom-title t)
				)
				;(reframe-windows-with-style 'mxflat)
				(map-windows refresh-window)
			))
	)
)
(defvar always-on-top-button-keymap
	(bind-keys (make-keymap)
		"Button1-Off" '(call-command
			(lambda ()
				(if (> (window-get (current-event-window) 'depth) 0)
					(window-put (current-event-window) 'depth 0)
					(window-put (current-event-window) 'depth mxflat:always-on-top-depth)
				)
				(reframe-window (current-event-window))
			))
	)
)
(defvar lock-button-keymap
	(bind-keys (make-keymap)
		"Button1-Off" '(call-command
			(lambda ()
				(if (window-get (current-event-window) 'fixed-position)
					(window-put (current-event-window) 'fixed-position nil)
					(window-put (current-event-window) 'fixed-position t)
				)
				(reframe-window (current-event-window))
			))
	)
)


;
;; configuration:groups
;

; there are just too many options
; so we put them into several groups/tabs
(defgroup mxflat:group
	"mxflat"
	:group appearance
)
(defgroup mxflat:title-group
	"title"
	:group (appearance mxflat:group)
)
(defgroup mxflat:title-basic-group
	"basic"
	:group (appearance mxflat:group mxflat:title-group)
)
(defgroup mxflat:title-gradient-group
	"gradient"
	:group (appearance mxflat:group mxflat:title-group)
)
(defgroup mxflat:title-colors-group
	"colors"
	:group (appearance mxflat:group mxflat:title-group)
)
(defgroup mxflat:buttons-group
	"buttons"
	:group (appearance mxflat:group)
)
(defgroup mxflat:buttons-basic-group
	"basic"
	:group (appearance mxflat:group mxflat:buttons-group)
)
(defgroup mxflat:buttons-positions1-group
	"positions 1"
	:group (appearance mxflat:group mxflat:buttons-group)
)
(defgroup mxflat:buttons-positions2-group
	"positions 2"
	:group (appearance mxflat:group mxflat:buttons-group)
)
(defgroup mxflat:buttons-keymaps1-group
	"keymaps 1"
	:group (appearance mxflat:group mxflat:buttons-group)
)
(defgroup mxflat:buttons-keymaps2-group
	"keymaps 2"
	:group (appearance mxflat:group mxflat:buttons-group)
)
(defgroup mxflat:button-colors-group
	"colors"
	:group (appearance mxflat:group mxflat:buttons-group)
)
(defgroup mxflat:border-group
	"borders"
	:group (appearance mxflat:group)
)
(defgroup mxflat:border1-group
	"basic"
	:group (appearance mxflat:group mxflat:border-group)
)
(defgroup mxflat:border-extended-group
	"extended"
	:group (appearance mxflat:group mxflat:border-group)
)
(defgroup mxflat:border-colors-group
	"colors"
	:group (appearance mxflat:group mxflat:border-group)
)

;
;; configuration:title-basic
;

(defcustom mxflat:decoration-mode
	'normal
	"decoration mode"
	:tooltip "set the mode 'when to draw a titlebar and window buttons'. the decoration mode 'all' means that all windows, even transients/dialogs, will get a titlebar and buttons. none means no window will get any :-). usually transients only have a contents-border. this option is for reimplementing a sawfish feature that is gone in the latest version."
	:group (appearance mxflat:group mxflat:title-group mxflat:title-basic-group)
	:type symbol
	:options (normal all none)
	:after-set recreate-all-frames
)

(defcustom mxflat:title-height
	16
	"titlebar height (9 - 1000 pixel)"
	:group (appearance mxflat:group mxflat:title-group mxflat:title-basic-group)
	:type (number 9 1000)
	:after-set rebuild-all-frames
)

(defcustom mxflat:custom-title
	nil
	"customize title string"
	:tooltip "see the tooltip for the 'title string' option below for more information. (NOTE: i hope the redrawing problems with this option are solved. if not, write an email please!)"
	:group (appearance mxflat:group mxflat:title-group mxflat:title-basic-group)
	:type boolean
	:after-set refresh-all-frames
)

(defcustom mxflat:title-string
	"$fullname |$widthx$height-($x-pos,$y-pos)@$current-ws/$last-ws-$(DISPLAY)"
	"title string"
	:tooltip "the string you insert here will be your new title string. variables always start with a dollar-sign $ and are replaced with their actual values. to get the value of an environment variable use $(variable), eg $(DISPLAY). to get the value of an rep/sawfish function use $[fuction], eg $[current-time-string]. the following vars are built-in: $name, $fullname, $width, $width-content, $height, $height-content, $depth, $x-pos, $y-pos, $current-ws and $last-ws. (NOTE: i hope the redrawing problems with this option are solved. if not, write an email please!"
	:group (appearance mxflat:group mxflat:title-group mxflat:title-basic-group)
	:type string
	:depends mxflat:custom-title
	:after-set refresh-all-frames
)

(defcustom mxflat:title-periodic-update
	nil
	"update title string periodically"
	:tooltip "this is useful if your title string changes without user interaction (eg because it displays the current time or something like that)."
	:group (appearance mxflat:group mxflat:title-group mxflat:title-basic-group)
	:type boolean
	:depends mxflat:custom-title
	:after-set after-set-title-periodic-update
)

(defcustom mxflat:title-update-interval
	1000
	"title string update interval (1 - 1000000 s)"
	:tooltip "this is useful if your title string changes without user interaction (eg because it displays the current time or something like that)."
	:group (appearance mxflat:group mxflat:title-group mxflat:title-basic-group)
	:type (number 1 1000000)
	:depends mxflat:custom-title
	:after-set after-set-title-update-interval
)

(defcustom mxflat:title-font
	default-font
	"title string font"
	:tooltip "you should really choose a better font. verdana bold 8 pt looks nice."
	:group (appearance mxflat:group mxflat:title-group mxflat:title-basic-group)
	:type font
	:after-set rebuild-all-frames
)

(defcustom mxflat:customize-title-position
	nil
	"customize title position"
	:tooltip "by default the title string is centered horizontally and vertically."
	:group (appearance mxflat:group mxflat:title-group mxflat:title-basic-group)
	:type boolean
	:after-set rebuild-all-frames
)

(defcustom mxflat:title-x-justify
	'center
	"title string horizontal alignment"
	:tooltip "'real-center' means to take the whole titlebar into account while centering the string. when selecting 'center' only the distance between the buttons is taken into account."
	:group (appearance mxflat:group mxflat:title-group mxflat:title-basic-group)
	:type symbol
	:depends mxflat:customize-title-position
	:options (left center real-center right)
	:after-set rebuild-all-frames
)

(defcustom mxflat:title-x-adjustment
	0
	"title string horizontal align adjustment (-10000 - 10000 pixel)"
	:tooltip "the horizontal position of the title string can only be adjusted if the horizontal alignment is not 'center'. the range also depends on the setting of the option above. it the title string is already aligned left, you cannot adjust it to be even further left because there are button in the way."
	:group (appearance mxflat:group mxflat:title-group mxflat:title-basic-group)
	:type (number -10000 10000)
	:depends mxflat:customize-title-position
	:after-set rebuild-all-frames
)

(defcustom mxflat:title-y-justify
	'center
	"title string vertical alignment"
	:group (appearance mxflat:group mxflat:title-group mxflat:title-basic-group)
	:type symbol
	:depends mxflat:customize-title-position
	:options (top center bottom)
	:after-set rebuild-all-frames
)

(defcustom mxflat:title-y-adjustment
	0
	"title string vertical align adjustment (-10000 - 10000 pixel)"
	:group (appearance mxflat:group mxflat:title-group mxflat:title-basic-group)
	:type (number -10000 10000)
	:depends mxflat:customize-title-position
	:after-set rebuild-all-frames
)

;
;; configuration:title-gradient
;

(defcustom mxflat:title-gradient
	nil
	"gradient titlebar background (experimental)"
	:tooltip "when activating the gradient background the normal titlebar background colors will be ignored. besides the button background colors will be ignored. (NOTE: this can be very cpu intensive if you set the quality to a low value, ie high quality.)"
	:group (appearance mxflat:group mxflat:title-group mxflat:title-gradient-group)
	:type boolean
	:after-set recreate-all-frames
)

(defcustom mxflat:gradient-direction
	'horizontal
	"gradient mode"
	:tooltip "with gradient mode vertical or diagonal only the first or last (reversed) 2 colors in the list are used."
	:group (appearance mxflat:group mxflat:title-group mxflat:title-gradient-group)
	:options (none horizontal vertical diagonal)
	:depends mxflat:title-gradient
	:type symbol
	:after-set recreate-all-frames
)

(defcustom mxflat:gradient-scale
	3
	"gradient quality (1 best - 20 worst)"
	:tooltip "the value here is the factor the calculated gradient image will be scaled down. this increases speed but decreases quality."
	:group (appearance mxflat:group mxflat:title-group mxflat:title-gradient-group)
	:depends mxflat:title-gradient
	:type (number 1 20)
	:after-set recreate-all-frames
)

(defcustom mxflat:titlebar-gradient-color-mode
	'rainbow
	"gradient color mode"
	:tooltip "the custom mode will respect the colors set in the list below."
	:group (appearance mxflat:group mxflat:title-group mxflat:title-gradient-group)
	:options (rainbow jamaica rgb custom)
	:depends mxflat:title-gradient
	:type symbol
	:after-set recreate-all-frames
)

(defcustom mxflat:titlebar-gradient-bgcolors
	nil
	nil
	:tooltip "with gradient mode vertical or diagonal only the first or last (reversed) 2 colors in the list are used. besides the gradient color mode must be set to custom to activate this list."
	:group (appearance mxflat:group mxflat:title-group mxflat:title-gradient-group)
	:widget-flags (expand-horizontally)
	:depends mxflat:title-gradient
	:type* `(list color "titlebar background colors")
	:after-set recreate-all-frames
)

(defcustom mxflat:titlebar-gradient-bgcolors-reverse
	nil
	"reverse colors"
	:group (appearance mxflat:group mxflat:title-group mxflat:title-gradient-group)
	:depends mxflat:title-gradient
	:type boolean
	:after-set recreate-all-frames
)

(defcustom mxflat:color-hue
	0
	"unfocused/highlighted titlebar colors hue (0 - 360)"
	:tooltip "this value changes the hue of a color, by moving (0 - 360) degress around the color cycle...180 for example inverts the color"
	:group (appearance mxflat:group mxflat:title-group mxflat:title-gradient-group)
	:depends mxflat:title-gradient
	:type (number 0 360)
	:after-set recreate-all-frames
)

(defcustom mxflat:color-saturation
	50
	"unfocused/highlighted titlebar colors saturation (0 - 100% of the focused color)"
	:tooltip "the saturation of a color is a measurement of the color intensity"
	:group (appearance mxflat:group mxflat:title-group mxflat:title-gradient-group)
	:depends mxflat:title-gradient
	:type (number 0 100)
	:after-set recreate-all-frames
)

(defcustom mxflat:color-brightness
	90
	"unfocused/highlighted titlebar colors brightness (0 - 100% of the focused color)"
	:group (appearance mxflat:group mxflat:title-group mxflat:title-gradient-group)
	:depends mxflat:title-gradient
	:type (number 0 100)
	:after-set recreate-all-frames
)

;
;; configuration:title-colors
;

(defcustom mxflat:titlebar-bgcolor-focused
	(get-color "#fffbde")
	"  focused titlebar color"
	:tooltip "this is one of the 4 base colors. you can set these 4 colors and keep the extended colors deactivated. all other color options of the theme will then depend on these 4 colors."
	:group (appearance mxflat:group mxflat:title-group mxflat:title-colors-group)
	:type color
	:after-set recreate-all-frames
)

(defcustom mxflat:titlebar-bgcolor-unfocused
	(get-color "#cedfef")
	"unfocused titlebar color"
	:tooltip "this is one of the 4 base colors. you can set these 4 colors and keep the extended colors deactivated. all other color options of the theme will then depend on these 4 colors."
	:group (appearance mxflat:group mxflat:title-group mxflat:title-colors-group)
	:type color
	:after-set recreate-all-frames
)

(defcustom mxflat:text-color-focused
	(get-color "#000000")
	"  focused title string color"
	:tooltip "this is one of the 4 base colors. you can set these 4 colors and keep the extended colors deactivated. all other color options of the theme will then depend on these 4 colors."
	:group (appearance mxflat:group mxflat:title-group mxflat:title-colors-group)
	:type color
	:after-set recreate-all-frames-recolor-all
)

(defcustom mxflat:text-color-unfocused
	(get-color "#838383")
	"unfocused title string color"
	:tooltip "this is one of the 4 base colors. you can set these 4 colors and keep the extended colors deactivated. all other color options of the theme will then depend on these 4 colors."
	:group (appearance mxflat:group mxflat:title-group mxflat:title-colors-group)
	:type color
	:after-set recreate-all-frames-recolor-all
)

(defcustom mxflat:customize-extended-text-colors
	nil
	"customize extended title string colors"
	:tooltip "you can customize additional colors of the title string. if you deactivate this, these colors will be set to the focused title string color."
	:group (appearance mxflat:group mxflat:title-group mxflat:title-colors-group)
	:type boolean
	:after-set recreate-all-frames
)

(defcustom mxflat:text-color-highlighted
	(get-color "#000000")
	"  focused highlighted title string color"
	:group (appearance mxflat:group mxflat:title-group mxflat:title-colors-group)
	:type color
	:depends mxflat:customize-extended-text-colors
	:after-set recreate-all-frames
)

(defcustom mxflat:text-color-inactive-highlighted
	(get-color "#000000")
	"unfocused highlighted title string color"
	:group (appearance mxflat:group mxflat:title-group mxflat:title-colors-group)
	:type color
	:depends mxflat:customize-extended-text-colors
	:after-set recreate-all-frames
)

(defcustom mxflat:text-color-clicked
	(get-color "#000000")
	"  focused clicked title string color"
	:group (appearance mxflat:group mxflat:title-group mxflat:title-colors-group)
	:type color
	:depends mxflat:customize-extended-text-colors
	:after-set recreate-all-frames
)

(defcustom mxflat:text-color-inactive-clicked
	(get-color "#000000")
	"unfocused clicked title string color"
	:group (appearance mxflat:group mxflat:title-group mxflat:title-colors-group)
	:type color
	:depends mxflat:customize-extended-text-colors
	:after-set recreate-all-frames
)


;; configuration:buttons-basic
;

(defcustom mxflat:button-icon-set
	'default-9
	"button icon set (experimental)"
	:tooltip "the number at the end is the width/height of the button icons in the icon set. (NOTE: YOU NEED TO RESTART SAWFISH AFTER CHANGING THE BUTTON ICON SET!)"
	:group (appearance mxflat:group mxflat:buttons-group mxflat:buttons-basic-group)
	:options (default-9 default-12 square-6 square-9 square-12 square-15 square-18 ball-9 ball-12)
	:type symbol
	:after-set recreate-all-frames-clear-cache
)

(defcustom mxflat:customize-button-size
	nil
	"customize button size"
	:tooltip "by default the button size follows the size of the titlebar, but by activating this you can set it to something different."
	:group (appearance mxflat:group mxflat:buttons-group mxflat:buttons-basic-group)
	:type boolean
	:after-set rebuild-all-frames
)

(defcustom mxflat:button-size
	14
	"buttons size (1 - 1000 pixel)"
	:tooltip "a buttons size that is bigger than your current titlebar height is ignored."
	:group (appearance mxflat:group mxflat:buttons-group mxflat:buttons-basic-group)
	:depends mxflat:customize-button-size
	:type (number 1 1000)
	:after-set rebuild-all-frames
)

(defcustom mxflat:button-y-alignment
	'center
	"button vertical alignment"
	:tooltip "if a button is smaller than the titlebar you might want to set its vertical alignment."
	:group (appearance mxflat:group mxflat:buttons-group mxflat:buttons-basic-group)
	:depends mxflat:customize-button-size
	:options (top center bottom)
	:type symbol
	:after-set rebuild-all-frames
)

(defcustom mxflat:scale-buttons
	nil
	"scale button icons (experimental)"
	:tooltip "in case you don't like the default size of 9 pixel you might activate button-scaling. YOU NEED TO RESTART SAWFISH AFTER CHANGING THE BUTTON SCALE!"
	:group (appearance mxflat:group mxflat:buttons-group mxflat:buttons-basic-group)
	:type boolean
	:after-set recreate-all-frames-clear-cache
)

(defcustom mxflat:button-scale
	9
	"button icon scale; changes here need a restart of sawfish (0 - 1000 pixel)"
	:tooltip "in case you don't like the default size of 9 pixel you might activate button-scaling. (NOTE: YOU NEED TO RESTART SAWFISH AFTER CHANGING THE BUTTON SCALE!)"
	:group (appearance mxflat:group mxflat:buttons-group mxflat:buttons-basic-group)
	:depends mxflat:scale-buttons
	:type (number 0 1000)
	:after-set recreate-all-frames-clear-cache
)

(defcustom mxflat:menu-button-icon
	nil
	"display window icon on menu button"
	:tooltip "many people like having the window-icon displayed on the menu-button. this icon is defined by the application itself and is also shown in your pager and task-list."
	:group (appearance mxflat:group mxflat:buttons-group mxflat:buttons-basic-group)
	:type boolean
	:after-set recreate-all-frames
)

(defcustom mxflat:menu-button-scale
	12
	"menu icon scale (0 - 1000 pixel)"
	:group (appearance mxflat:group mxflat:buttons-group mxflat:buttons-basic-group)
	:depends mxflat:menu-button-icon
	:type (number 0 1000)
	:after-set recreate-all-frames-clear-cache
)

;
;; configuration:buttons-position1
;

(defcustom mxflat:button-distance
	3
	"button distance (0 - 1000 pixel)"
	:group (appearance mxflat:group mxflat:buttons-group mxflat:buttons-positions1-group)
	:type (number 0  1000)
	:after-set rebuild-all-frames
)

(defcustom mxflat:button-left-offset
	1
	"buttons left offset (0 - 1000 pixel)"
	:group (appearance mxflat:group mxflat:buttons-group mxflat:buttons-positions1-group)
	:type (number 0  1000)
	:after-set rebuild-all-frames
)

(defcustom mxflat:button-right-offset
	1
	"buttons right offset (0 - 1000 pixel)"
	:group (appearance mxflat:group mxflat:buttons-group mxflat:buttons-positions1-group)
	:type (number 0  1000)
	:after-set rebuild-all-frames
)

(defcustom mxflat:customize-buttons
	nil
	"customize button position (-1 -2 -3 .. title string .. +3 +2 +1)"
	:tooltip "enabling this will allow you to set the positon of each button in the titlebar manually. a negative/positiv value means the button will be drawn on left/right side of the titlebar."
	:group (appearance mxflat:group mxflat:buttons-group mxflat:buttons-positions1-group)
	:type boolean
	:after-set recreate-all-frames
)

(defcustom mxflat:button-position-model
	'title-height
	"button position model"
	:tooltip "'titel-height' means calculate position as multiple of title height. when selecting 'pixel' you can adjust the button position pixelwise, but keep in mind that several buttons might slide on top of each other. 'none' means no buttons at all."
	:group (appearance mxflat:group mxflat:buttons-group mxflat:buttons-positions1-group)
	:options (title-height pixel none)
	:depends mxflat:customize-buttons
	:type symbol
	:after-set rebuild-all-frames
)

;
;; configuration:buttons-position2
;

(defcustom mxflat:menu-button-position
	-1
	"menu button position"
	:group (appearance mxflat:group mxflat:buttons-group mxflat:buttons-positions2-group)
	:depends mxflat:customize-buttons
	:type (number -10000 10000)
	:after-set rebuild-all-frames
)

(defcustom mxflat:shade-button-position
	-2
	"shade button position"
	:group (appearance mxflat:group mxflat:buttons-group mxflat:buttons-positions2-group)
	:depends mxflat:customize-buttons
	:type (number -10000 10000)
	:after-set rebuild-all-frames
)

(defcustom mxflat:iconify-button-position
	+3
	"iconify button position"
	:group (appearance mxflat:group mxflat:buttons-group mxflat:buttons-positions2-group)
	:depends mxflat:customize-buttons
	:type (number -10000 10000)
	:after-set rebuild-all-frames
)

(defcustom mxflat:maximize-button-position
	+2
	"maximize button position"
	:group (appearance mxflat:group mxflat:buttons-group mxflat:buttons-positions2-group)
	:depends mxflat:customize-buttons
	:type (number -10000 10000)
	:after-set rebuild-all-frames
)

(defcustom mxflat:close-button-position
	+1
	"close button position"
	:group (appearance mxflat:group mxflat:buttons-group mxflat:buttons-positions2-group)
	:depends mxflat:customize-buttons
	:type (number -10000 10000)
	:after-set rebuild-all-frames
)

(defcustom mxflat:sticky-button-position
	-3
	"sticky button position"
	:group (appearance mxflat:group mxflat:buttons-group mxflat:buttons-positions2-group)
	:depends mxflat:customize-buttons
	:type (number -10000 10000)
	:after-set rebuild-all-frames
)

(defcustom mxflat:previous-button-position
	-5
	"previous workspace button position"
	:group (appearance mxflat:group mxflat:buttons-group mxflat:buttons-positions2-group)
	:depends mxflat:customize-buttons
	:type (number -10000 10000)
	:after-set rebuild-all-frames
)

(defcustom mxflat:next-button-position
	-6
	"next workspace button position"
	:group (appearance mxflat:group mxflat:buttons-group mxflat:buttons-positions2-group)
	:depends mxflat:customize-buttons
	:type (number -10000 10000)
	:after-set rebuild-all-frames
)

(defcustom mxflat:configure-button-position
	0
	"configure button position"
	:group (appearance mxflat:group mxflat:buttons-group mxflat:buttons-positions2-group)
	:depends mxflat:customize-buttons
	:type (number -10000 10000)
	:after-set rebuild-all-frames
)

(defcustom mxflat:info-button-position
	0
	"info button position"
	:group (appearance mxflat:group mxflat:buttons-group mxflat:buttons-positions2-group)
	:depends mxflat:customize-buttons
	:type (number -10000 10000)
	:after-set rebuild-all-frames
)

(defcustom mxflat:always-on-top-button-position
	0
	"always-on-top button position"
	:group (appearance mxflat:group mxflat:buttons-group mxflat:buttons-positions2-group)
	:depends mxflat:customize-buttons
	:type (number -10000 10000)
	:after-set rebuild-all-frames
)

(defcustom mxflat:lock-button-position
	0
	"lock button position"
	:group (appearance mxflat:group mxflat:buttons-group mxflat:buttons-positions2-group)
	:depends mxflat:customize-buttons
	:type (number -10000 10000)
	:after-set rebuild-all-frames
)

;
;; configuration:buttons-keymaps1
;

(defcustom mxflat:customize-sticky-keymap
	nil
	"customize sticky button keymap"
	:tooltip "by default a button will work as it's name suggests. button1: toggle sticky state of window"
	:group (appearance mxflat:group mxflat:buttons-group mxflat:buttons-keymaps1-group)
	:type boolean
	:after-set recreate-all-frames
)

(defcustom mxflat:sticky-button-keymap
	nil
	nil
	:group (appearance mxflat:group mxflat:buttons-group mxflat:buttons-keymaps1-group)
	:widget-flags (expand-horizontally)
	:depends mxflat:customize-sticky-keymap
    :type keymap
)

(defcustom mxflat:customize-previous-keymap
	nil
	"customize previous button keymap"
	:tooltip "by default a button will work as it's name suggests. button1: send window to previous workspace - button2 workspace-list - button3 send window to first workspace - button6 pack window left"
	:group (appearance mxflat:group mxflat:buttons-group mxflat:buttons-keymaps1-group)
	:type boolean
	:after-set recreate-all-frames
)

(defcustom mxflat:previous-button-keymap
	nil
	nil
	:group (appearance mxflat:group mxflat:buttons-group mxflat:buttons-keymaps1-group)
	:widget-flags (expand-horizontally)
	:depends mxflat:customize-previous-keymap
    :type keymap
)

(defcustom mxflat:customize-next-keymap
	nil
	"customize next button keymap"
	:tooltip "by default a button will work as it's name suggests. button1: send window to next workspace - button2 workspace-list - button3 send window to last workspace - button6 pack window right"
	:group (appearance mxflat:group mxflat:buttons-group mxflat:buttons-keymaps1-group)
	:type boolean
	:after-set recreate-all-frames
)

(defcustom mxflat:next-button-keymap
	nil
	nil
	:tooltip "button1: send window to next workspace - button2 workspace-list - button3 send window to last workspace - button6 pack window right"
	:group (appearance mxflat:group mxflat:buttons-group mxflat:buttons-keymaps1-group)
	:widget-flags (expand-horizontally)
	:depends mxflat:customize-next-keymap
    :type keymap
)

;
;; configuration:buttons-keymaps2
;

(defcustom mxflat:customize-configure-keymap
	nil
	"customize configure button keymap"
	:tooltip "by default a button will work as it's name suggests. button1: start sawfish-ui - button3: toggle custom title string"
	:group (appearance mxflat:group mxflat:buttons-group mxflat:buttons-keymaps2-group)
	:type boolean
	:after-set recreate-all-frames
)

(defcustom mxflat:configure-button-keymap
	nil
	nil
	:tooltip "button1: start sawfish-ui - button3: toggle custom title string"
	:group (appearance mxflat:group mxflat:buttons-group mxflat:buttons-keymaps2-group)
	:widget-flags (expand-horizontally)
	:depends mxflat:customize-configure-keymap
    :type keymap
)

(defcustom mxflat:customize-info-keymap
	nil
	"customize info button keymap"
	:tooltip "by default a button will work as it's name suggests. button1: window list - button3 window snooper (not included by default)"
	:group (appearance mxflat:group mxflat:buttons-group mxflat:buttons-keymaps2-group)
	:type boolean
	:after-set recreate-all-frames
)

(defcustom mxflat:info-button-keymap
	nil
	nil
	:group (appearance mxflat:group mxflat:buttons-group mxflat:buttons-keymaps2-group)
	:widget-flags (expand-horizontally)
	:depends mxflat:customize-info-keymap
    :type keymap
)

(defcustom mxflat:always-on-top-depth
	1
	"always on top depth (-1000 - 1000)"
	:tooltip "a window will have this depth after you have pressed the always-on-top button. 0 is the normal window level. every positive value means above other windows, a negative value means below. keept it at a positive value in order to have the always-on-top button do it's job."
	:group (appearance mxflat:group mxflat:buttons-group mxflat:buttons-keymaps2-group)
	:type (number -1000 1000)
	:after-set rebuild-all-frames
)

(defcustom mxflat:customize-always-on-top-keymap
	nil
	"customize always-on-top button keymap"
	:tooltip "by default a button will work as it's name suggests. button1: sets window depth to the adjusted value"
	:group (appearance mxflat:group mxflat:buttons-group mxflat:buttons-keymaps2-group)
	:type boolean
	:after-set recreate-all-frames
)

(defcustom mxflat:always-on-top-button-keymap
	nil
	nil
	:group (appearance mxflat:group mxflat:buttons-group mxflat:buttons-keymaps2-group)
	:depends mxflat:customize-always-on-top-keymap
	:widget-flags (expand-horizontally)
    :type keymap
)

(defcustom mxflat:customize-lock-keymap
	nil
	"customize look button keymap"
	:tooltip "by default a button will work as it's name suggests. button1: lock window position"
	:group (appearance mxflat:group mxflat:buttons-group mxflat:buttons-keymaps2-group)
	:type boolean
	:after-set recreate-all-frames
)

(defcustom mxflat:lock-button-keymap
	nil
	nil
	:group (appearance mxflat:group mxflat:buttons-group mxflat:buttons-keymaps2-group)
	:depends mxflat:customize-lock-keymap
	:widget-flags (expand-horizontally)
    :type keymap
)

;
;; configuration:button-colors
;

(defcustom mxflat:custom-button-colors
	nil
	"customize button-colors"
	:tooltip "when you active this you will be able to set custom button colors. by default the button colors depend on the titlebar colors."
	:group (appearance mxflat:group mxflat:buttons-group mxflat:button-colors-group)
	:type boolean
	:after-set recreate-all-frames-recolor-all
)

(defcustom mxflat:button-bgcolor-focused
	(get-color "#fffbde")
	"  focused button background color"
	:group (appearance mxflat:group mxflat:buttons-group mxflat:button-colors-group)
	:depends mxflat:custom-button-colors
	:type color
	:after-set recreate-all-frames
)

(defcustom mxflat:button-bgcolor-unfocused
	(get-color "#cedfef")
	"unfocused button background color"
	:group (appearance mxflat:group mxflat:buttons-group mxflat:button-colors-group)
	:depends mxflat:custom-button-colors
	:type color
	:after-set recreate-all-frames
)

(defcustom mxflat:button-bgcolor-highlighted
	(get-color "#def3ff")
	"  focused highlighted button background color"
	:group (appearance mxflat:group mxflat:buttons-group mxflat:button-colors-group)
	:depends mxflat:custom-button-colors
	:type color
	:after-set recreate-all-frames
)

(defcustom mxflat:button-bgcolor-inactive-highlighted
	(get-color "#fffbde")
	"unfocused highlighted button background color"
	:group (appearance mxflat:group mxflat:buttons-group mxflat:button-colors-group)
	:depends mxflat:custom-button-colors
	:type color
	:after-set recreate-all-frames
)

(defcustom mxflat:button-bgcolor-clicked
	(get-color "#cecece")
	"  focused clicked button background color"
	:group (appearance mxflat:group mxflat:buttons-group mxflat:button-colors-group)
	:depends mxflat:custom-button-colors
	:type color
	:after-set recreate-all-frames
)

(defcustom mxflat:button-bgcolor-inactive-clicked
	(get-color "#cecece")
	"unfocused clicked button background color"
	:group (appearance mxflat:group mxflat:buttons-group mxflat:button-colors-group)
	:depends mxflat:custom-button-colors
	:type color
	:after-set recreate-all-frames
)

(defcustom mxflat:button-fgcolor-focused
	(get-color "#000000")
	"  focused button foreground color"
	:group (appearance mxflat:group mxflat:buttons-group mxflat:button-colors-group)
	:depends mxflat:custom-button-colors
	:type color
	:after-set recreate-all-frames-recolor-all
)

(defcustom mxflat:button-fgcolor-inactive
	(get-color "#838383")
	"unfocused button foreground color"
	:group (appearance mxflat:group mxflat:buttons-group mxflat:button-colors-group)
	:depends mxflat:custom-button-colors
	:type color
	:after-set recreate-all-frames-recolor-all
)

(defcustom mxflat:button-fgcolor-highlighted
	(get-color "#000000")
	"  focused highlighted button foreground color"
	:group (appearance mxflat:group mxflat:buttons-group mxflat:button-colors-group)
	:depends mxflat:custom-button-colors
	:type color
	:after-set recreate-all-frames-recolor-all
)

(defcustom mxflat:button-fgcolor-clicked
	(get-color "#000000")
	"  focused clicked button foreground color"
	:group (appearance mxflat:group mxflat:buttons-group mxflat:button-colors-group)
	:depends mxflat:custom-button-colors
	:type color
	:after-set recreate-all-frames-recolor-all
)

;
;; configuration:border-basic
;

(defcustom mxflat:border-width-unmaximized
	1
	"border width (0 - 1000 pixel)"
	:tooltip "set the width border around the window."
	:group (appearance mxflat:group mxflat:border-group mxflat:border1-group)
	:type (number 0 1000)
	:after-set rebuild-all-frames
)

(defcustom mxflat:border-width-maximized
	1
	"border width for maximized windows (0 - 1000 pixel)"
	:tooltip "when you like having a big border around your windows you might have noticed that sawfish still draws that big border when the window is maximized. if you don't want that, set the border width for maximized windows smaller than the border width for unmaximized windows. (NOTE: this feature is still buggy as sawfish does not resize the windows correctly yet, so the best might be to set it to the normal border width.)"
	:group (appearance mxflat:group mxflat:border-group mxflat:border1-group)
	:type (number 0 1000)
	:after-set rebuild-all-frames
)

(defcustom mxflat:custom-border-focused
	nil
	"customize border for focused windows (experimental)"
	:tooltip "(NOTE: this works but it is VERY cpu intensive and might slow down your computer noticably. so turn it on at your own risk.)"
	:group (appearance mxflat:group mxflat:border-group mxflat:border1-group)
	:type boolean
	:after-set rebuild-all-frames
)

(defcustom mxflat:border-width-focused
	1
	"border width for focused windows (0 - 1000 pixel)"
	:tooltip "(NOTE: this works but it is VERY cpu intensive and might slow down your computer noticably. so turn it on at your own risk.)"
	:group (appearance mxflat:group mxflat:border-group mxflat:border1-group)
	:type (number 0 1000)
	:depends mxflat:custom-border-focused
	:after-set rebuild-all-frames
)

(defcustom mxflat:border-width-titlebar-contents
	1
	"title-contents border width (0 - 1000 pixel)"
	:tooltip "sets the widths of the line between titlebar and contents window"
	:group (appearance mxflat:group mxflat:border-group mxflat:border1-group)
	:type (number 0 1000)
	:after-set rebuild-all-frames
)

(defcustom mxflat:border-offset
	-1
	"border offset (-1000 - 1000 pixel)"
	:tooltip "adjust the point where the border actually starts. many gtk2 apps draw a small black border around their contents window and this is just ugly when sawfish draws another border around the window. with a border offset of -1 the sawfish window border will overlay the gtk2 border exactly."
	:group (appearance mxflat:group mxflat:border-group mxflat:border1-group)
	:type (number -1000 1000)
	:after-set rebuild-all-frames
)

(defcustom mxflat:keep-borders-when-shaded
	nil
	"keep window borders and corners at their place when shading a window"
	:tooltip "when shading a window borders and corners will stay where they were and not merge with the titlebar."
	:group (appearance mxflat:group mxflat:border-group mxflat:border1-group)
	:type boolean
	:after-set recreate-all-frames
)

;
;; configuration:border-extended
;

(defcustom mxflat:show-corners
	t
	"show window corners"
	:tooltip " the corners are used to resize a window in two directions at the same time. usually the width and length of window corners are determined by the border width but you can adjust them below."
	:group (appearance mxflat:group mxflat:border-group mxflat:border-extended-group)
	:type boolean
	:after-set recreate-all-frames
)

(defcustom mxflat:corner-length-adjustment
	20
	"corners length adjustement (-1 - 100 pixel)"
	:tooltip "increasing the corner length will grow the window corners along the window border (i.e. make them longer). with a border width of 1 pixel it is hard to grab the 1 pixel corner to be able to resize the window in two directions; thus, make it bigger."
	:group (appearance mxflat:group mxflat:border-group mxflat:border-extended-group)
	:type (number -1 100)
	:depends mxflat:show-corners
	:after-set rebuild-all-frames
)

(defcustom mxflat:corner-direction
	'outside
	"corner direction"
	:"inside or outside the window border"
	:group (appearance mxflat:group mxflat:border-group mxflat:border-extended-group)
	:type symbol
	:options (outside inside)
	:depends mxflat:show-corners
	:after-set recreate-all-frames
)

(defcustom mxflat:corner-width-adjustment
	1
	"corners width adjustement (0 - 100 pixel)"
	:tooltip "this option makes the window corners broader. with a border width of 1 pixel it is hard to grab this 1 pixel corner to be able to resize the window in two directions; thus, make it bigger."
	:group (appearance mxflat:group mxflat:border-group mxflat:border-extended-group)
	:type (number 0 100)
	:depends mxflat:show-corners
	:after-set rebuild-all-frames
)

(defcustom mxflat:corner-width-adjustment-maximized
	0
	"corners width adjustement for maximized windows (0 - 100 pixel)"
	:group (appearance mxflat:group mxflat:border-group mxflat:border-extended-group)
	:type (number 0 100)
	:depends mxflat:show-corners
	:after-set rebuild-all-frames
)

(defcustom mxflat:fake-shadow
	nil
	"fake shadow (experimental)"
	:tooltip "draw a small additional border around your windows emulating a solid shadow effect. use the options below to adjust its width and direction. (NOTE: this is VERY cpu intensive and might slow down your computer noticably. so turn it on at your own risk.)"
	:group (appearance mxflat:group mxflat:border-group mxflat:border-extended-group)
	:type boolean
	:after-set recreate-all-frames
)

(defcustom mxflat:fake-shadow-focused-only
	nil
	"fake shadow only on focused windows"
	:group (appearance mxflat:group mxflat:border-group mxflat:border-extended-group)
	:type boolean
	:depends mxflat:fake-shadow
	:after-set recreate-all-frames
)

(defcustom mxflat:shadow-light-source
	'north-west
	"fake shadow light source"
	:tooltip "set the direction of the fake shadow. usually only 2 or 3 borders of your window have a fake shadow. this depends on where the light comes from."
	:group (appearance mxflat:group mxflat:border-group mxflat:border-extended-group)
	:type symbol
	:options (north east south west north-east north-west south-east south-west)
	:depends mxflat:fake-shadow
	:after-set after-set-shadow-light-source
)

(defcustom mxflat:shadow-light-distance
	3
	"fake shadow light distance (0 - 100)"
	:tooltip "this option has only an effect on the north, east, south and west light sources. the further away the light source is, the smaller will the two 'side' shadows be."
	:group (appearance mxflat:group mxflat:border-group mxflat:border-extended-group)
	:type (number 1 100)
	:depends mxflat:fake-shadow
	:after-set rebuild-all-frames
)

(defcustom mxflat:shadow-width
	4
	"fake shadow width (0 - 100 pixel)"
	:group (appearance mxflat:group mxflat:border-group mxflat:border-extended-group)
	:type (number 0 100)
	:depends mxflat:fake-shadow
	:after-set rebuild-all-frames
)

(defcustom mxflat:shadow-color
	(get-color "#838383")
	"shadow color"
	:group (appearance mxflat:group mxflat:border-group mxflat:border-extended-group)
	:type color
	:depends mxflat:fake-shadow
	:after-set rebuild-all-frames
)

;
;; configuration:border-colors
;

(defcustom mxflat:customize-border-colors
	nil
	"customize border colors"
	:tooltip "you can customize your border colors here. if you deactivate this, these colors will be set to the focused title string color."
	:group (appearance mxflat:group mxflat:border-group mxflat:border-colors-group)
	:type boolean
	:after-set rebuild-all-frames
)

(defcustom mxflat:border-color-focused
	(get-color "#000000")
	"  focused border color"
	:group (appearance mxflat:group mxflat:border-group mxflat:border-colors-group)
	:type color
	:depends mxflat:customize-border-colors
	:after-set rebuild-all-frames
)

(defcustom mxflat:border-color-unfocused
	(get-color "#000000")
	"unfocused border color"
	:group (appearance mxflat:group mxflat:border-group mxflat:border-colors-group)
	:type color
	:depends mxflat:customize-border-colors
	:after-set rebuild-all-frames
)

(defcustom mxflat:border-color-highlighted
	(get-color "#000000")
	"  focused highlighted border color"
	:group (appearance mxflat:group mxflat:border-group mxflat:border-colors-group)
	:type color
	:depends mxflat:customize-border-colors
	:after-set rebuild-all-frames
)

(defcustom mxflat:border-color-inactive-highlighted
	(get-color "#000000")
	"unfocused highlighted border color"
	:group (appearance mxflat:group mxflat:border-group mxflat:border-colors-group)
	:type color
	:depends mxflat:customize-border-colors
	:after-set rebuild-all-frames
)

(defcustom mxflat:border-color-clicked
	(get-color "#000000")
	"  focused clicked border color"
	:group (appearance mxflat:group mxflat:border-group mxflat:border-colors-group)
	:type color
	:depends mxflat:customize-border-colors
	:after-set rebuild-all-frames
)

(defcustom mxflat:border-color-inactive-clicked
	(get-color "#000000")
	"unfocused clicked border color"
	:group (appearance mxflat:group mxflat:border-group mxflat:border-colors-group)
	:type color
	:depends mxflat:customize-border-colors
	:after-set rebuild-all-frames
)

;   ----
;;; MISC
;   ----

(define (get-last-workspace)
	(aref
		(nth 2 (get-x-property 'root '_NET_NUMBER_OF_DESKTOPS))
		0
	)
)

(define (get-first-workspace) 1)

(defmacro window-in-workspace-p (w space)
	`(memq ,space (window-get ,w 'workspaces))
)

(define (window-maximized-p w)
	(window-get w 'unmaximized-geometry)
)

(define (window-maximized-horizontally-p w)
	(window-get w 'maximized-horizontally)
)

(define (window-maximized-vertically-p w)
	(window-get w 'maximized-vertically)
)

; constant accuracy factor since
; we ???are not allowed to  use floats in themes???
; / seems to be gaoled
(defvar afactor 10000000000000)
(defvar afactor360 (* 360 afactor))

;
;; misc:color manipulation
;

; returns a list containing hue saturation and value of color
(define (rgb2hsv color)
	(let*
		(
			; get rgb ints for the color
			(rgbchannels (color-rgb color))

			; get red, green and blue ints from rgbchannels
			(red   (nth 0 rgbchannels))
			(green (nth 1 rgbchannels))
			(blue  (nth 2 rgbchannels))

			; channnel with the biggest int value
			(value (apply max (list red green blue)))
			; delta
			(max-min (- value (apply min (list red green blue))))

			; saturation: 1 - min / max  =  (max - min) / max  =  delta / max
			; saturation * afactor - acutally the saturation
			; is a float between 0 and 1 but as it seems
			; we cannot use them in themes
			(saturation
				(if (= max-min 0)
					0
					(/
						(* max-min afactor)
						value
					)
				)
			)
			; value between 0 and 360 degree * afactor
			; 0 = red, 120 = green, 240 = blue
			(hue
				(if (= max-min 0)
					0
					(/
						(*
							60
							afactor
							(cond
								(
									(= value red)
									(- green blue)
								)
								(
									(= value green)
									(+ (* 2 max-min) (- blue red))
								)
								(
									; (= value blue)
									t
									(+ (* 4 max-min) (- red green))
								)
							)
						)
						max-min
					)
				)
			)
		)
		(list (if (< hue 0) (+ hue afactor360) hue) saturation value)
	)
)

; translates a rgb-color from the ints of the list, which contains hue, saturation and value
(define (hsv2rgb hsv)
	(let*
		(
			(hue (nth 0 hsv))
			(saturation (nth 1 hsv))
			(value (nth 2 hsv))
			
		)

		; value zero -> color black
		(cond
			((= value 0)
				(get-color-rgb 0 0 0)
			)
			((= saturation 0)
		
				; in this case the color is grey and
				; thus its rgb value is:
				(get-color-rgb value value value)
			)
			(t
				; else we need to do some calculations -
				; the hue and saturation are aleady
				; multiplied with afactor
				(let*
					(
						; we need it often, so we define
						; it once here
						(afactor2 (* afactor afactor))
						; kind of an 'compensation delta'
						(f
							(if
								(or
									(= hue 0)
									(= hue afactor360)
								)
								0
								(/ (mod hue (* 60 afactor)) 60)
							)
						)

							; p is the lowest color int
							(p
							(/
								(*  value
									(- afactor saturation)
								)
								afactor
							)
						)
						; tt is a medium color int
						(q
							(/
								(*
									value
									(-
										afactor2
										(* saturation f)
									)
								)
								afactor2
							)
						)
						; tt is a medium color int
						(tt
							(/
								(*
									value
									(-
										afactor2
										(*
											saturation
											(- afactor f)
										)
									)
								)
								afactor2
							)
						)

						; area on the color circle (0 .. 5)
						(circle-part
							(if (>= hue afactor360)
								0
								(/
									(/ hue afactor)
									60
								)
							)
						)
					)
					; arrange the right vars for the current
					; color circle part
					(case circle-part
						((0) (get-color-rgb value  	tt 		p))
						((1) (get-color-rgb q 		value 	p))
						((2) (get-color-rgb p 		value 	tt))
						((3) (get-color-rgb p 		q 		value))
						((4) (get-color-rgb tt 		p 		value))
						((5) (get-color-rgb value 	p 		p))
					)
				)
			)
		)
	)
)

; adjusts the hue, brightness and saturation of color
(define (adjust-color color)
	(let*
		(
			; hue saturation value list
			(hsv (rgb2hsv color))
			; add used configured adjustment to
			; the hue of the color
			(a-hue
				(+
					(nth 0 hsv)
					(* mxflat:color-hue afactor)
				)
			)
			; adjust saturation with
			; user configured factor
			(a-saturation
				(/
					(*
						(nth 1 hsv)
						mxflat:color-saturation
					)
					100
				)
			)
			; adjust value/brightness with
			; user configured factor
			(a-value
				(/
					(*
						(nth 2 hsv)
						mxflat:color-brightness
					)
					100
				)
			)
		)
		(hsv2rgb
			(list
				; adjust the hue if necessary
				; (ie it is >360 degree)
				(if (> a-hue afactor360)
					(- a-hue afactor360)
					a-hue
				)
				a-saturation
				a-value
			)
		)
	)
)

;
;; misc:hook-ins
;

; when the window title or some other wm_hints attribute changes, redraw the window
(call-after-property-changed (list 'WM_NAME)
	(lambda (w)
		(if (eq mxflat:title-x-justify 'real-center)
			(rebuild-frame w)
			(refresh-one-frame w)
		)
	)
)

; when we set a window sticky we have to redraw it,
; the sticky button has changed
(call-after-state-changed '(sticky)	reframe-window)

; when the window is sent to another workspace
; redraw it to update the buttons and titlebar if necessary
(add-hook 'remove-from-workspace-hook
	(lambda (w)
		(reframe-window w)
	)
)

; not necessary anymore?
;(add-hook 'after-resize-hook refresh-one-frame)

(add-hook 'focus-in-hook
	(lambda (w)
		; if 'custom borders for focused windows' -> redraw
		(if (or
				mxflat:custom-border-focused
				(and
					mxflat:fake-shadow
					mxflat:fake-shadow-focused-only
				)
			)
			(reframe-window w)
		)
	)
)

(add-hook 'focus-out-hook
	(lambda (w)
		; if 'custom borders for focused windows' -> redraw
		(if (or
				mxflat:custom-border-focused
				(and
					mxflat:fake-shadow
					mxflat:fake-shadow-focused-only
				)
			)
			(reframe-window w)
		)
	)
)

;
;; misc:functions
;

(define (get-border-offset)
	; we use the negative value to make the defcustom option
	; work more like one would expect, ie a positive value
	; means the border will have a bigger distance to the
	; contents window
	(- mxflat:border-offset)
)

(define (get-button-size)
	(if
		(and
			mxflat:customize-button-size
			(< mxflat:button-size mxflat:title-height)
		)
		mxflat:button-size
		mxflat:title-height
	)
)

;   -----------
;;; FAKE SHADOW
;   -----------

; this list defines which window sides have shadows
; for which direction
(defvar shadow-sides
	`(
		(north . (bottom left right))
		(east . (top bottom left))
		(west . (top bottom right))
		(south . (top left right))
		(north-east . (bottom left))
		(north-west . (bottom right))
		(south-west . (top right))
		(south-east . (top left))
	)
)

; selects the right shadow-side from the shadow-sides list
(define (get-shadow-side)
	(mapc
		(lambda (ss)
			(if (eq (car ss) mxflat:shadow-light-source)
				(setq shadow-side (cdr ss))
			)
		)
		shadow-sides
	)
)

(define (get-shadow-color) mxflat:shadow-color)

(define (get-shadow-width w side)
	(if (member side shadow-side)
		(get-neg-border-width w side)
		mxflat:shadow-width
	)
)

(define (get-shadow-offset w side)
	; the shadow is only not visible on all window edges
	(if (not (window-maximized-p w))
		(if
			(or
				(and
					(eq mxflat:shadow-light-source 'north)
					(not (eq side 'bottom))
				)
				(and
					(eq mxflat:shadow-light-source 'south)
					(not (eq side 'top))
				)
				(and
					(eq mxflat:shadow-light-source 'east)
					(not (eq side 'left))
				)
				(and
					(eq mxflat:shadow-light-source 'west)
					(not (eq side 'right))
				)
			)
			(/ mxflat:shadow-width mxflat:shadow-light-distance)
			(if (member side shadow-side)
				mxflat:shadow-width
				0
			)
		)
		0
	)
)

;   --------
;;; TITLEBAR
;   --------

;
;; titlebar:colors
;

(define (get-text-colors)
	(if mxflat:customize-extended-text-colors
		`(
   			(inactive				. ,mxflat:text-color-unfocused)
			(focused				. ,mxflat:text-color-focused)
			(highlighted			. ,mxflat:text-color-highlighted)
			(inactive-highlighted	. ,mxflat:text-color-inactive-highlighted)
			(clicked				. ,mxflat:text-color-clicked)
			(inactive-clicked		. ,mxflat:text-color-inactive-clicked)
		)
		`(
   			(inactive				. ,mxflat:text-color-unfocused)
			(focused				. ,mxflat:text-color-focused)
			(highlighted			. ,mxflat:text-color-focused)
			(inactive-highlighted	. ,mxflat:text-color-focused)
			(clicked				. ,mxflat:text-color-focused)
			(inactive-clicked		. ,mxflat:text-color-focused)
		)
	)
)

(define (titlebar-gradient-colors-helper)
	(case mxflat:titlebar-gradient-color-mode
		((custom)
			(if (> (length mxflat:titlebar-gradient-bgcolors) 0)
				mxflat:titlebar-gradient-bgcolors
				(list (get-color "#ffffffffffff"))
			)
		)
		((rainbow)
			(list
				(get-color "#ffff0000153b")
				(get-color "#ffff9b700000")
				(get-color "#fffff92d0000")
				(get-color "#3661ffff0000")
				(get-color "#00004c0dffff")
				(get-color "#b7ad0000ffff")
			)
		)
		((jamaica)
			(list
				(get-color "#7899f1340000")
				(get-color "#ffffffff0000")
				(get-color "#ffff37030000")
			)
		)
		((rgb)
			(list
				(get-color "#ffff00000000")
				(get-color "#0000ffff0000")
				(get-color "#00000000ffff")
			)
		)
	)
)
	
(define (titlebar-gradient-colors)
	(if mxflat:titlebar-gradient-bgcolors-reverse
		(reverse (titlebar-gradient-colors-helper))
		(titlebar-gradient-colors-helper)
	)
)

; changes the brightness and saturation for the
; titlebar gradient color with index i
(define (adjust-titlebar-bgcolor i)
	; get rgb ints for the color with index i
	(adjust-color (nth i (titlebar-gradient-colors)))
)

(define (get-titlebar-bgcolors #!optional side)
	(cond
		((and mxflat:title-gradient side)
			(let*
				(
					(index
						(if (eq side 'right)
							(1- (length (titlebar-gradient-colors)))
							0
						)
					)
				)
				`(
					(inactive	. ,(adjust-titlebar-bgcolor index))
					(focused	. ,(nth index (titlebar-gradient-colors)))
				)
			)
		)
		(t
			`(
				(inactive	. ,mxflat:titlebar-bgcolor-unfocused)
				(focused	. ,mxflat:titlebar-bgcolor-focused)
			)
		)
	)
)

;
;; titlebar:bg rendering
;

(define (num-titlebar-parts)
	(if
		(or
			(= (length (titlebar-gradient-colors)) 1)
			(eq mxflat:gradient-direction 'none)
		)
		(length (titlebar-gradient-colors))
		(1- (length (titlebar-gradient-colors)))
   	)
)

; render frame-part background
; this function takes the background img and uses
; many single 2-color gradients to fill it
(define (render-bg img state)
	(let*
		(
			; width and height of the titlebar part we have to fill
			(width (car (image-dimensions img)))
			(height (cdr (image-dimensions img)))

			; 2-color gradient width
			(part-width (/ width (num-titlebar-parts)))
			(rest-width (modulo width (num-titlebar-parts)))

			; the offset in the img that we've already reached
			; with our 2-color gradients
			(offset 0)

			; get the width of the 2-color gradient with index i
			(titlebar-parts-width-helper (lambda (i)
				(if (= i (1- (num-titlebar-parts)))
					(+ part-width rest-width)
					part-width
				)
			))

			; check the 2-color gradient width if it is > 0
			(titlebar-parts-width (lambda (i)
				(let
					(
						(val (titlebar-parts-width-helper i))
					)
					(if (< val 1)
						1
						val
					)
				)
			))

			; 2-color gradient image
			(img2 ())
		)

		(case mxflat:gradient-direction
			((horizontal none)
				(do
					((i 0 (1+ i)))
					((= i (num-titlebar-parts)))

					; create the 2-color gradient image and fill
					; it with a background color
					(setq img2
						(make-sized-image
							(titlebar-parts-width i)
							height
							(if state
								(nth i (titlebar-gradient-colors))
								(adjust-titlebar-bgcolor i)
							)
						)
					)

					; fill the img2 with the 2-color gradient
					; in necessary
					(if (not (eq mxflat:gradient-direction 'none))
						(apply
							draw-horizontal-gradient
							img2
							(if state
								(list
									(nth i (titlebar-gradient-colors))
									(if (or
											(= (length  (titlebar-gradient-colors)) 1)
											(eq mxflat:gradient-direction 'none)
										)
										(nth i (titlebar-gradient-colors))
										(nth (1+ i) (titlebar-gradient-colors))
									)
								)
								(list
									(adjust-titlebar-bgcolor i)
									(if (or
										(= (length (titlebar-gradient-colors)) 1)
											(eq mxflat:gradient-direction 'none)
										)
										(adjust-titlebar-bgcolor i)
										(adjust-titlebar-bgcolor (1+ i))
									)
								)
							)
						)
					)

					; paste the current 2-color gradient into
					; the titlebar background image
					(composite-images img img2 offset 0)
					(setq offset (+ offset (titlebar-parts-width i)))
				)
			)
			((vertical diagonal)
				(apply
					(if (eq mxflat:gradient-direction 'vertical)
						draw-vertical-gradient
						draw-diagonal-gradient
					)
					img
					(if state
						(list
							(nth 0 (titlebar-gradient-colors))
							(if (= (length  (titlebar-gradient-colors)) 1)
								(nth 0 (titlebar-gradient-colors))
								(nth 1 (titlebar-gradient-colors))
							)
						)
						(list
							(adjust-titlebar-bgcolor 0)
							(if (= (length (titlebar-gradient-colors)) 1)
								(adjust-titlebar-bgcolor 0)
								(adjust-titlebar-bgcolor 1)
							)
						)
					)
				)
			)
		)
		img
	)
)

;
;; titlebar:string
;

(define (get-title-font) mxflat:title-font)

; replace vars in the title string with their actual values
; and return the  new title string
(define (get-title-string w)
	(if mxflat:custom-title
		(let*
			(
				(title mxflat:title-string)
				(update-title (lambda (match-str replace-str)
						(while (string-match match-str title)
							(setq title
								(concat
									(expand-last-match "\\1")
									(if (numberp replace-str)
										(number->string replace-str)
										replace-str
									)
									(expand-last-match "\\3")
								)
							)
						)
					))
				)
				; some built-in vars
				(update-title "(.*)(\\$name)(.*)" (window-name w))
				(update-title "(.*)(\\$fullname)(.*)" (window-full-name w))
				(update-title "(.*)(\\$width)(.*)" (car (window-frame-dimensions w)))
				(update-title "(.*)(\\$height)(.*)" (cdr (window-frame-dimensions w)))
				(update-title "(.*)(\\$width-content)(.*)" (car (window-dimensions w)))
				(update-title "(.*)(\\$height-content)(.*)" (cdr (window-dimensions w)))
				(update-title "(.*)(\\$x-pos)(.*)" (car (window-position w)))
				(update-title "(.*)(\\$y-pos)(.*)" (cdr (window-position w)))
				(update-title "(.*)(\\$id)(.*)" (window-id w))
				(update-title "(.*)(\\$depth)(.*)" (window-get w 'depth))
				(update-title "(.*)(\\$gid)(.*)" (window-group-id w))
				(update-title "(.*)(\\$last-ws)(.*)" (get-last-workspace))
				(update-title "(.*)(\\$current-ws)(.*)"
						(let
							(
								(workspaces (window-get w 'workspaces))
							)
							(if workspaces
								(1+ (nth 0 workspaces))
								"*"
							)
						)
				)
				; replace environment vars
				(while (string-match "(.*)\\$\\((.+?)\\)(.*)" title)
					(let
						(
							(part1 (expand-last-match "\\1"))
							(part2 (expand-last-match "\\3"))
							(envvar (getenv (expand-last-match "\\2")))
						)
						(if (not envvar)
							(setq envvar (concat "$?(" (expand-last-match "\\2") ")"))
						)
						(setq title (concat part1 envvar part2))
					)
				)
				; replace rep functions
				; XXX: no arguments to the functions possible yet,
				; does someone need this?
				(while (string-match "(.*)\\$\\[(.+?)\\](.*)" title)
					(let
						(
							(part1 (expand-last-match "\\1"))
							(part2 (expand-last-match "\\3"))
							(symb (intern (expand-last-match "\\2")))
							(func ())
							(value (concat "$?[" (expand-last-match "\\2") "]"))
						)
						(if (boundp symb)
							(setq func (symbol-value symb))
						)
						(if (functionp func)
							(setq value (funcall func))
						)
						(setq title (concat part1 value part2))
					)
				)
				title
			)
			(window-name w)
		)
)

;
;; titlebar:position
;

; calculate the leftmost x position of an object on the titlebar
; this function is also used for button positions on the titlebar
(define (calculate-position pos)
	(if
		(and
			mxflat:customize-buttons
			(eq mxflat:button-position-model 'pixel)
		)
		; if the button-position-model is pixel we dont
		; need to calculate anything but just return the
		; argument value
		pos
		; actually calculate the positon
		(*
			(1- pos)
			(+
				(get-button-size)
				mxflat:button-distance
			)
		)
	)
)

(define (get-title-height)
	(+
		mxflat:title-height
		mxflat:border-width-titlebar-contents
	)
)

; not really the neg value since we add the border offset
; but thats the easiest place for doing so
(define (get-neg-title-height)
	(-
		(get-border-offset)
		(get-title-height)
	)
)

(define (get-titlebar-height w)
	(+
		(get-title-height)
		(get-border-width w 'top)
	)
)

; not really the neg value since we add the border offset
; but thats the easiest place for doing so
(define (get-neg-titlebar-height w)
	(-
		(get-border-offset)
		(get-titlebar-height w)
	)
)

	; get the 1+ position of the leftmost or rightmost button
(define (get-last-button-position left-right)
	(let
		((pos
			(if mxflat:customize-buttons
				(abs (apply
					(if (eq left-right 'left)
						min
						max
					)
					(list mxflat:menu-button-position mxflat:shade-button-position
						mxflat:sticky-button-position mxflat:previous-button-position
						mxflat:next-button-position mxflat:configure-button-position
						mxflat:info-button-position mxflat:iconify-button-position
						mxflat:maximize-button-position mxflat:close-button-position
						mxflat:always-on-top-button-position mxflat:lock-button-position
					)
				))
				(if (eq left-right 'left)
					3
					2
				)
			)
		))
		(if
			(or
				(eq mxflat:button-position-model 'title-height)
				(not mxflat:customize-buttons)
			)
			(calculate-position (1+ pos))
			(if (eq mxflat:button-position-model 'pixel)
				(+
					pos
					mxflat:title-height
				)
				1
			)
		)
	)
)

; by default the title is vertically centered and pixelwise
; adjusted through mxflat:title-y-adjustment
(define (get-title-y-alignment)
	(if mxflat:customize-title-position
		(case mxflat:title-y-justify
			((top)
				; sanity check on the value
				; else the title string would not be visible
				(if (> mxflat:title-y-adjustment 0)
					mxflat:title-y-adjustment
					0
				)
			)
			
			; we have to calculate it manually to be able to take
			; the y align adjustment into account
			((center)
				(+
					mxflat:title-y-adjustment
					(/
						(-
							mxflat:title-height
							(font-height mxflat:title-font)
						)
						2
					)
				)
			)
			; bottom
			(t
				; sanity check on the value
				; else the title string would not be visible
				(if (< mxflat:title-y-adjustment 0)
					mxflat:title-y-adjustment
					-1
				)
			)
		)
		'center
	)
)

(define (get-title-x-alignment w)
	(if mxflat:customize-title-position
		(case mxflat:title-x-justify
			((left)
				; sanity check on the value
				; else the title string would not be visible
				(if (< mxflat:title-x-adjustment 0)
					0
					mxflat:title-x-adjustment
				)
			)
			((center)
				'center
			)
			((right)
				; sanity check on the value
				; else the title string would not be visible
				(if (> mxflat:title-x-adjustment -1)
					-1
					mxflat:title-x-adjustment
				)
			)
			; 'real-center' means to take the whole titlebar into account
			; while centering the title string
			((real-center)
				(let
					(
						(real-title-offset
							(/
								(-
									(car (window-dimensions w))
									(text-width (get-title-string w) (get-title-font))
								)
								2
							)
						)
						(title-offset
							(-
								(get-last-button-position 'left)
								mxflat:title-x-adjustment
							)
						)
					)
					(if (> title-offset real-title-offset)
						0
						(- real-title-offset title-offset)
					)
				)
			)
		)
		'center
	)
)

(define (left-buttons-end-offset)
	(get-last-button-position 'left)
)
(define (right-buttons-end-offset)
	(get-last-button-position 'right)
)

;
;; titlebar:creation
;

(define (titlebar-center backgroun backgroundd)
	`(
		(
			(class			. title)
			(,backgroun		. ,backgroundd)
			(render-scale	. ,mxflat:gradient-scale)
			(foreground		. ,get-text-colors)
			(font			. ,get-title-font)
			(text			. ,get-title-string)
			(x-justify		. ,get-title-x-alignment)
			(y-justify		. ,get-title-y-alignment)
			(left-edge		.
				,(lambda ()
					(+
						(left-buttons-end-offset)
						(get-border-offset)
					)
				)
			)
			(right-edge		.
				,(lambda ()
					(+
						(right-buttons-end-offset)
						(get-border-offset)
					)
				)
			)
			(top-edge		. ,get-neg-title-height)
			(height			. ,(lambda () mxflat:title-height))
		)
	)
)

(define (titlebar-left-right backgroun backgroundd edg1 edgee1 edg2 edgee2)
	`(
		; left part of the titlebar
		(
			(class			. title)
			(,backgroun		. ,backgroundd)
			(render-scale	. ,mxflat:gradient-scale)
			(,edg1			. ,edgee1)
			(,edg2			. ,edgee2)
			(top-edge		. ,get-neg-title-height)
			(height			. ,(lambda () mxflat:title-height))
		)
	)
)

(define (titlebar)
	(append
		(if
			(and
				(or
					(eq mxflat:gradient-direction 'vertical)
					(eq mxflat:gradient-direction 'diagonal)
				)
				mxflat:title-gradient
			)
			(append
				(titlebar-left-right
					'renderer render-bg
					'left-edge get-border-offset
					'width left-buttons-end-offset
				)
				(titlebar-left-right
					'renderer render-bg
					'width right-buttons-end-offset
					'right-edge get-border-offset
				)
			)
			(append
				(titlebar-left-right
					'background	(lambda () (get-titlebar-bgcolors 'left))
					'left-edge get-border-offset
					'width left-buttons-end-offset
				)
				(titlebar-left-right
					'background	(lambda () (get-titlebar-bgcolors 'right))
					'width right-buttons-end-offset
					'right-edge get-border-offset
				)
			)
		)
		(if mxflat:title-gradient
			(titlebar-center 'renderer render-bg)
			(titlebar-center 'background get-titlebar-bgcolors)
		)
	)
)

;   -------
;;; BORDERS
;   -------

;
;; borders:colors
;

(define (get-border-color)
	(if mxflat:customize-border-colors
		`(
			(inactive				. ,mxflat:border-color-unfocused)
			(focused				. ,mxflat:border-color-focused)
			(highlighted			. ,mxflat:border-color-highlighted)
			(inactive-highlighted	. ,mxflat:border-color-inactive-highlighted)
			(clicked				. ,mxflat:border-color-clicked)
			(inactive-clicked		. ,mxflat:border-color-inactive-clicked)
		)
		`(
			(inactive				. ,mxflat:text-color-unfocused)
			(focused				. ,mxflat:text-color-focused)
		)
	)
)

(define (get-border-color-titlebar-contents)
	(if mxflat:customize-border-colors
		`(
			(inactive . ,mxflat:border-color-unfocused)
			(focused . ,mxflat:border-color-focused)
		)
		`(
			(inactive . ,mxflat:text-color-unfocused)
			(focused . ,mxflat:text-color-focused)
		)
	)
)

;
;; borders:width
;

(define (get-border-width w #!optional side)
	(cond
		(
			(or
				(and
					(window-maximized-vertically-p w)
					(or
						(eq side 'top)
						(eq side 'bottom)
					)
				)
				(and
					(window-maximized-horizontally-p w)
					(or
						(eq side 'left)
						(eq side 'right)
					)
				)
			)
			mxflat:border-width-maximized
		)
		(
			(and
				mxflat:custom-border-focused
				(eq w (input-focus))
			)
			mxflat:border-width-focused
		)
		(
			t
			mxflat:border-width-unmaximized
		)
	)
)

(define (get-neg-border-width w #!optional side)
	(-
		(get-border-offset)
		(get-border-width w side)
	)
)

;
;; borders:creation
;

(define (create-border-frame-parts color offset widthh #!optional mode border-list)
	(let*
		(
			(get-pos (lambda (w side)
				(-
					(offset w side)
					(widthh w side)
				)
			))
			; bottom edge of the frame
			(bottom-edgee1 'bottom-edge)
			(bottom-edgee2 'bottom-edge)
			(bottom-edg1 (lambda (w) (offset w 'bottom)))
			(bottom-edg2 (lambda (w) (get-pos w 'bottom)))
			(top-edg1 (lambda (w)
				(-
					(offset w 'top)
					(get-title-height)
				)
			))
			(top-edg2 (lambda (w)
				(-
					(offset w 'top)
					(+
						(get-title-height)
						(widthh w 'top)
					)
				)
			))
			(left-edg (lambda (w) (get-pos w 'left)))
			(right-edg (lambda (w) (get-pos w 'right)))
			(right-borderr (if border-list 'shadow 'right-border))
			(left-borderr (if border-list 'shadow 'left-border))
			(top-borderr (if border-list 'shadow 'top-border))
			(bottom-borderr (if border-list 'shadow 'bottom-border))
		)
		(case mode
			('transient
				(setq top-edg1 (lambda (w) (offset w 'top)))
				(setq top-edg2 (lambda (w) (get-pos w 'top)))
			)
			('shaded
				(setq bottom-edgee2 'top-edge)
				(setq bottom-edg2 (lambda (w) (- (offset w 'bottom) mxflat:border-width-titlebar-contents)))
				; when the window is shaded we rather define the borders by
				; top-edge and height instead of using top- and bottom-edge
				(setq bottom-edgee1 'height)
				(setq bottom-edg1 (lambda (w) (get-titlebar-height w)))
			)
		)
		(append
			; draw the right border
			(if
				(or
					(not border-list)
					(member 'right border-list)
				)
				`((
					(class		. ,right-borderr)
					(background	. ,color)
					(width		. ,(lambda (w) (widthh w 'right)))
					(right-edge	. ,right-edg)
					(top-edge	. ,top-edg1)
					(,bottom-edgee1		. ,bottom-edg1)
				))
			)

			; draw the left border
			(if
				(or
					(not border-list)
					(member 'left border-list)
				)
				`((
					(class		. ,left-borderr)
					(background	. ,color)
					(width		. ,(lambda (w) (widthh w 'left)))
					(left-edge	. ,left-edg)
					(top-edge	. ,top-edg1)
					(,bottom-edgee1		. ,bottom-edg1)
				))
			)

			; draw the top border of the title frame
			(if
				(or
					(not border-list)
					(member 'top border-list)
				)
				`((
					(class		. ,top-borderr)
					(background	. ,color)
					(height		. ,(lambda (w) (widthh w 'top)))
					(left-edge	. ,left-edg)
					(right-edge	. ,right-edg)
					(top-edge	. ,top-edg2)
				))
			)

			; draw the bottom border
			(if
				(or
					(not border-list)
					(member 'bottom border-list)
				)
				`((
					(class		. ,bottom-borderr)
					(background	. ,color)
					(height		. ,(lambda (w) (widthh w 'bottom)))
					(left-edge	. ,left-edg)
					(right-edge	. ,right-edg)
					(,bottom-edgee2		. ,bottom-edg2)
				))
			)
		)
	)
)

; this function appends the window border (frame) and its shadow
(define (create-borders w #!optional mode)
	(if (= (length shadow-side) 0)
		(get-shadow-side)
	)
	(append
		; create normal window border
		(create-border-frame-parts
			get-border-color
			get-border-offset
			get-border-width
			mode
		)
		; create fake shadow if necessary
		(if
			(and
				mxflat:fake-shadow
				(or
					(not mxflat:fake-shadow-focused-only)
					(eq w (input-focus))
				)
			)
			(create-border-frame-parts
				get-shadow-color
				get-shadow-width
				get-shadow-offset
				mode
				shadow-side
			)
		)
	)
)

; create the (small) line between titlebar and contents window
(define (create-titlebar-contents-border)
	`(
		(
			(class			. middle-border)
			(background		. ,get-border-color-titlebar-contents)
			(height			. ,(lambda () mxflat:border-width-titlebar-contents))
			(top-edge		. ,(lambda ()
				(-
					(get-border-offset)
					mxflat:border-width-titlebar-contents
				)
			))
			(left-edge		. ,get-border-offset)
			(right-edge		. ,get-border-offset)
		)
	)
)

; return a top border (for shaded-transient)
(define (create-top-border)
	`(
		(
			(class			. top-border)
			(background		. ,get-border-color)
			(height			. ,(lambda (w) (get-border-width w 'top)))
			(top-edge		. ,(lambda (w) (get-neg-border-width w 'top)))
			(left-edge		. 0)
			(right-edge		. 0)
		)
	)
)

;   -------
;;; BUTTONS
;   -------


; create, scale and cache an image
(define (get-image img)
	(or
		(table-ref icon-cache img)
		(let
			(
				(image
					(if mxflat:scale-buttons
						(scale-image
							(make-image img)
							mxflat:button-scale mxflat:button-scale
						)
						(make-image img)
					)
				)
			)
			(table-set icon-cache img image)
			image
		)
	)
)

;
;; buttons:colors
;

(define (get-button-bgcolor #!optional edge)
	(cond
		(mxflat:title-gradient
			(let*
				(
					(index
						(if (eq edge 'right-edge)
							(1- (length (titlebar-gradient-colors)))
							0
						)
					)
				)
				`(
					(inactive				. ,(adjust-titlebar-bgcolor index))
					(focused				. ,(nth index (titlebar-gradient-colors)))
					(highlighted			. ,(adjust-titlebar-bgcolor index))
					(inactive-highlighted	. ,(nth index (titlebar-gradient-colors)))
					(clicked				. ,(adjust-titlebar-bgcolor index))
					(inactive-clicked		. ,(nth index (titlebar-gradient-colors)))
				)
			)
		)
		(mxflat:custom-button-colors
			`(
				(inactive				. ,mxflat:button-bgcolor-unfocused)
				(focused				. ,mxflat:button-bgcolor-focused)
				(highlighted			. ,mxflat:button-bgcolor-highlighted)
				(inactive-highlighted	. ,mxflat:button-bgcolor-inactive-highlighted)
				(clicked				. ,mxflat:button-bgcolor-clicked)
				(inactive-clicked		. ,mxflat:button-bgcolor-inactive-clicked)
			)
		)
		(t
			`(
				(inactive				. ,mxflat:titlebar-bgcolor-unfocused)
				(focused				. ,mxflat:titlebar-bgcolor-focused)
				(highlighted			. ,mxflat:titlebar-bgcolor-unfocused)
				(inactive-highlighted	. ,mxflat:titlebar-bgcolor-focused)
				(clicked				. ,mxflat:text-color-unfocused)
				(inactive-clicked		. ,mxflat:text-color-unfocused)
			)
		)
	)
)


;
;; buttons:icons
;

; return a list of 4 different image objects
; as needed for the button fg images/icons
(define (get-images img)
	(let
		(
			(image (get-image (concat "button-icons/" (symbol-name mxflat:button-icon-set) "/" img)))
		)
		(list
			image
			(copy-image image)
			(copy-image image)
			(copy-image image)
		)
	)
)

(defvar get-menu-button1 (get-images "button-menu.png"))
(define (get-menu-button w)
	(if mxflat:menu-button-icon
		(or
			(table-ref icon-cache w)
			(or
				(let
					(
						(icon (window-icon-image w))
					)
					(when icon
						(let
							(
								(scaled
									(scale-image icon mxflat:menu-button-scale
										mxflat:menu-button-scale)
								)
							)
							(table-set icon-cache w scaled)
							scaled
						)
					)
				)
				get-menu-button1
			)
		)
		get-menu-button1
	)
)

(defvar get-shade-button1 (get-images "button-shade1.png"))
(defvar get-shade-button2 (get-images "button-shade2.png"))
(define (get-shade-button w)
	(if (window-get w 'shaded)
		get-shade-button2
		get-shade-button1
	)
)

(defvar get-sticky-button1 (get-images "button-sticky1.png"))
(defvar get-sticky-button2 (get-images "button-sticky2.png"))
(define (get-sticky-button w)
	(if (window-get w 'sticky)
		get-sticky-button2
		get-sticky-button1
	)
)

(defvar get-iconify-button (get-images "button-minimize.png"))

(defvar get-maximize-button1 (get-images "button-maximize1.png"))
(defvar get-maximize-button2 (get-images "button-maximize2.png"))
(define (get-maximize-button w)
	(if (window-maximized-p w)
		get-maximize-button2
		get-maximize-button1
	)
)

(defvar get-close-button (get-images "button-close.png"))

(defvar get-previous-button1 (get-images "button-previous1.png"))
(defvar get-previous-button2 (get-images "button-previous2.png"))
(define (get-previous-button w)
	(if
		(or
			(window-in-workspace-p w (- (get-first-workspace) 1))
			(window-get w 'sticky)
		)
		get-previous-button2
		get-previous-button1
	)
)

(defvar get-next-button1 (get-images "button-next1.png"))
(defvar get-next-button2 (get-images "button-next2.png"))
(define (get-next-button w)
	(if
		(or
			(window-in-workspace-p w (- (get-last-workspace) 1))
			(window-get w 'sticky)
		)
		get-next-button2
		get-next-button1
	)
)

(defvar get-configure-button (get-images "button-configure.png"))

(defvar get-info-button (get-images "button-info.png"))

(defvar get-always-on-top-button1 (get-images "button-a1.png"))
(defvar get-always-on-top-button2 (get-images "button-a2.png"))
(define (get-always-on-top-button w)
	(if (> (window-get w 'depth) 0)
		get-always-on-top-button2
		get-always-on-top-button1
	)
)

(defvar get-lock-button1 (get-images "button-lock1.png"))
(defvar get-lock-button2 (get-images "button-lock2.png"))
(define (get-lock-button w)
	(if (window-get w 'fixed-position)
		get-lock-button2
		get-lock-button1
	)
)

;
;; buttons:fg recolor
;

; this function is stolen from the crux sawfish theme
; (the theme is included in the original sawfish package)
(setq recolor-all (lambda ()
	(let*
		(
			(recolorer (lambda (color1 color2)
				(make-image-recolorer
					(if mxflat:custom-button-colors
						color1
						color2
					)
					#:zero-channel blue-channel
					#:index-channel red-channel
				)
			))
			
			; unfocused
			(recolorer1
				(recolorer
					mxflat:button-fgcolor-inactive
					mxflat:text-color-unfocused
				)
			)
			; focused
			(recolorer2
				(recolorer
					mxflat:button-fgcolor-focused
					mxflat:text-color-focused
				)
			)
			; highlighted
			(recolorer3
				(recolorer
					mxflat:button-fgcolor-highlighted
					mxflat:text-color-unfocused
				)
			)
			; clicked
			(recolorer4
				(recolorer
					mxflat:button-fgcolor-clicked
					mxflat:text-color-focused
				)
			)
		)
    	(mapc
			(lambda (x)
				(apply recolorer1 x)
				(apply recolorer2 (cdr x))
				(if (> (length x) 2)
					(apply recolorer3 (nthcdr 2 x))
				)
				(if (> (length x) 3)
					(apply recolorer4 (nthcdr 3 x))
				)
			)
			; list of all button fg icon lists that we have to recolor
			(list get-menu-button1 get-shade-button1 get-shade-button2
				get-sticky-button1 get-sticky-button2 get-iconify-button
				get-maximize-button1 get-maximize-button2 get-close-button
				get-previous-button1 get-previous-button2 get-next-button1
				get-next-button2 get-configure-button get-info-button
				get-always-on-top-button1 get-always-on-top-button2
				get-lock-button1 get-lock-button2
			)
		)
	)
))

;
;; buttons:define classes
;

; missing button frame part classes
; all other classes are defined by sawfish by default -
; it are always 2 classes, one with the default key binding
; and one with the custom (user defined) one
(define-frame-class 'configure-button-custom	'((keymap . mxflat:configure-button-keymap)))
(define-frame-class 'configure-button			'((keymap . configure-button-keymap)))
(define-frame-class 'info-button-custom			'((keymap . mxflat:info-button-keymap)))
(define-frame-class 'info-button				'((keymap . info-button-keymap)))
(define-frame-class 'next-button-custom			'((keymap . mxflat:next-button-keymap)))
(define-frame-class 'next-button				'((keymap . next-button-keymap)))
(define-frame-class 'previous-button-custom		'((keymap . mxflat:previous-button-keymap)))
(define-frame-class 'previous-button			'((keymap . previous-button-keymap)))
(define-frame-class 'sticky-button-custom		'((keymap . mxflat:sticky-button-keymap)))
(define-frame-class 'sticky-button				'((keymap . sticky-button-keymap)))
(define-frame-class 'always-on-top-button-custom '((keymap . mxflat:always-on-top-button-keymap)))
(define-frame-class 'always-on-top-button		'((keymap . always-on-top-button-keymap)))
(define-frame-class 'lock-button-custom			'((keymap . mxflat:lock-button-keymap)))
(define-frame-class 'lock-button				'((keymap . lock-button-keymap)))

;
;; buttons:creation
;

; this function actually creates a button frame part and returns it
(define (create-button-frame-part classs fg edge x-pos y-pos backgroun backgroundd)
	; button frame part definition
	`(
		(class		. ,classs)
		(,backgroun	. ,backgroundd)
		(render-scale . ,mxflat:gradient-scale)
		(foreground	. ,fg)
		(x-justify	. center)
		(y-justify	. center)
		(,edge		. ,x-pos)
		(top-edge	. ,y-pos)
		(height		. ,get-button-size)
		(width		. ,get-button-size)
	)
)

; gather all necessary arguments and then call create-button-frame-part
; to actually create the button
(define (create-button button-type #!optional x-pos-default)
	(let
		(
			; raw position, creatue-button-frame-part does some more
			; calculations on it
			(x-pos-raw
				(if mxflat:customize-buttons
					(check-symbol "mxflat:" button-type "-button-position")
					(if x-pos-default x-pos-default 0)
				)
			)
		)
		
		; position = 0 means the button is not displayed
		; so we can save some time be not doing all these evaluations
		(if (not (= x-pos-raw 0))
			(let*
				(
					; if the position parameter (x-pos-raw) for the current button is
					; positive it starts on the right side
					(edge (if (> x-pos-raw 0) 'right-edge 'left-edge))

					; default button background mode (ie static color)
					(backgroun 'background)
					(backgroundd (get-button-bgcolor edge))
					
					; button x offset from the left/right titlebar edge
					(x-pos (lambda ()
						(+
							(-
								(calculate-position (abs x-pos-raw))
								mxflat:border-offset
							)
							(if (> x-pos-raw 0)
								mxflat:button-right-offset
								mxflat:button-left-offset
							)
						)
					))

					; different top positions for different mxflat:button-y-alignment
					; but only in case mxflat:customize-button-size is true
					; because in the other case the buttons height and width is equal
					; to the titlebar height
					(y-pos (lambda ()
						(if mxflat:customize-button-size
							(case mxflat:button-y-alignment
								((top)
									(get-neg-title-height)
								)
								((bottom)
									(+
										(get-neg-title-height)
										(-
											mxflat:title-height
											(get-button-size)
										)
									)
								)
								; this is the center case
								(t
									(+
										(get-neg-title-height)
										(/
											(-
												mxflat:title-height
												(get-button-size)
											)
											2
										)
									)
								)
							)
							; in case the size is the default we start on top
							; of the titlebar
							(get-neg-title-height)
						)
					))
				)
				
				; in case of a gradient titlebar bg
				; change the button bg to rendering
				(cond
					(
						(and
							mxflat:title-gradient
							(or
								(eq mxflat:gradient-direction 'vertical)
								(eq mxflat:gradient-direction 'diagonal)
							)
						)
						(setq backgroun 'renderer)
						(setq backgroundd render-bg)
					)
				)

				(create-button-frame-part
					; decide which frame part (and indirectly which keymap) to use
					(if (check-symbol "mxflat:customize-" button-type "-keymap")
						(get-symbol button-type "-button-custom")
						(get-symbol button-type "-button")
					)
					; button fg images/icons
					(check-symbol "get-" button-type "-button")
					edge
					x-pos
					y-pos
					backgroun
					backgroundd
				)
			)
		)
	)
)

; create a list of button frame parts and return it
(define (create-buttons)
	; create a list of all custom buttons if the button
	; position model is not none (none means no buttons)
	(if (not (eq mxflat:button-position-model 'none))
		(list
			(create-button "menu" -1)
			(create-button "shade" -2)
			(create-button "iconify" +3)
			(create-button "maximize" +2)
			(create-button "close" +1)
			(create-button "sticky")
			(create-button "next")
			(create-button "previous")
			(create-button "configure")
			(create-button "info")
			(create-button "always-on-top")
			(create-button "lock")
		)
	)
)

;   -------
;;; CORNERS
;   -------

;
;; corners:size
;

(define (get-corner-width-adjustment w #!optional side)
	(if
		(or
			(and
				(window-maximized-vertically-p w)
				(or
					(eq side 'top)
					(eq side 'bottom)
				)
			)
			(and
				(window-maximized-horizontally-p w)
				(or
					(eq side 'left)
					(eq side 'right)
				)
			)
		)
		mxflat:corner-width-adjustment-maximized
		mxflat:corner-width-adjustment
	)
)

(define (get-corner-width w #!optional frame-type side)
	(if
		(and
			(eq frame-type 'shaded)
			(> (get-corner-width-adjustment w side) (/ (get-title-height) 2))
			(eq mxflat:corner-direction 'inside)
		)
		(/ (1+ (get-title-height)) 2)
		(+
			(get-corner-width-adjustment w side)
			(get-border-width w side)
		)
	)
)

(define (get-corner-length w #!optional frame-type)
	(if (= mxflat:corner-length-adjustment -1)
		0
		(+
			(+
				(if (and
						(eq frame-type 'shaded)
						(> mxflat:corner-length-adjustment (/ (get-title-height) 2))
					)
					(/ (1+ (get-title-height)) 2)
					mxflat:corner-length-adjustment
				)
				(get-border-width w 'top)
			)
			(if (eq mxflat:corner-direction 'inside)
				0
				(get-corner-width-adjustment w)
			)
		)
	)
)

;
;; corners:position
;

; get top or bottom position
(define (get-corner-1-pos w side part #!optional frame-type)
	(-
		(if (eq side 'bottom)
			(if (eq frame-type 'shaded)
				(+
					(if (eq part 'b)
						(+
							(- (get-corner-length w frame-type))
							(get-corner-width w frame-type side)
						)
						0
					)
					(-
						(get-border-offset)
						mxflat:border-width-titlebar-contents
					)
				)
				(get-neg-border-width w side)
			)
			(if (eq frame-type 'transient)
				(get-neg-border-width w 'top)
				(get-neg-titlebar-height w)
			)
		)
		(if
			(and
				(eq frame-type 'shaded)
				(eq side 'bottom)
			)
			(if (eq mxflat:corner-direction 'inside)
				(get-corner-width-adjustment w side)
				0
			)
			(if (eq mxflat:corner-direction 'inside)
				0
				(get-corner-width-adjustment w side)
			)
		)
	)
)

; get left or right position
(define (get-corner-2-pos w side)
		(-
			(get-neg-border-width w side)
			(if (eq mxflat:corner-direction 'inside)
				0
				(get-corner-width-adjustment w side)
			)
		)
)

;
;; corners:creation
;

; create the two corner lists a real corner consists of
(define (create-corner classs edgee1 side1 edgee2 side2 frame-type)
	`(
		(
			(class			. ,classs)
			(background		. ,get-border-color)
			(width			. ,get-corner-length)
			(height			. ,(lambda (w) (get-corner-width w frame-type side1)))
			(,edgee1			. ,(lambda (w) (get-corner-1-pos w side1 'a frame-type)))
			(,edgee2			. ,(lambda (w) (get-corner-2-pos w side2)))
		)
		(
			(class			. ,classs)
			(background		. ,get-border-color)
			(width			. ,(lambda (w) (get-corner-width w frame-type side2)))
			(height			. ,(lambda (w) (get-corner-length w frame-type)))
			(,edgee1			. ,(lambda (w) (get-corner-1-pos w side1 'b frame-type)))
			(,edgee2			. ,(lambda (w) (get-corner-2-pos w side2)))
		)
	)
)
; this function actually creates the 4 window corners and returns them
(define (create-corners #!optional frame-type)
	(if mxflat:show-corners
		(let
			(
				(edgee1 'top-edge)
				(edgee2 'bottom-edge)
			)
			(if (eq frame-type 'shaded)
				(setq edgee2 'top-edge)
			)
			(append
				(create-corner 'top-left-corner edgee1 'top 'left-edge 'left frame-type)
				(create-corner 'top-right-corner edgee1 'top 'right-edge 'right frame-type)
				(create-corner 'bottom-left-corner edgee2 'bottom 'left-edge 'left frame-type)
				(create-corner 'bottom-right-corner edgee2 'bottom 'right-edge 'right frame-type)
			)
		)
	)
)

;   --------------
;;; REGISTER THEME
;   --------------

#|

that is from the manual (http://sawmill.sourceforge.net/prog-manual.html)

Frame Types

default
	The normal frame type. Includes all decorations, both borders and the title bar.
	transient
	The frame for a transient window. This usually does not include a title bar,
	but does have all four borders.
shaped
	Shaped windows are normally decorated with only a title-bar, since their boundary
	is not rectangular it makes no sense to surround them with a rectangular border.
shaped-transient
	A combination of the shaped and transient types, normally just a very small title
	border with no text.
shaded
	A shaded window (normally just the title bar).
shaded-transient
	A shaded transient window.
unframed
	No frame at all, just the client window itself. The predefined nil-frame variable
	contains a null frame that may be used for this frame type.

"Normal" 'default
"Title-only" 'shaped
"Border-only" 'transient
"Top-border" 'shaped-transient
"None" 'unframed

|#

; return the window frame and decorations
(define (get-frame w frame-type)
	(let*
		(
			; window with all decorations
			(defaul (lambda ()
				(append (titlebar) (create-titlebar-contents-border)
					(create-borders w) (create-buttons) (create-corners))
			))

			; window with contents frame only
			(transien (lambda ()
				(append (create-borders w 'transient) (create-corners 'transient))
			))

			(titleba (lambda (#!optional mode)
				(if mode
					(titlebar)
					(append (titlebar) (create-buttons))
				)
			))

			; window that is shaded
			(shade (lambda (#!optional titlebar-mode)
				(if mxflat:keep-borders-when-shaded
					(append (titleba titlebar-mode) (create-titlebar-contents-border)
						(create-borders w) (create-corners))
					(append (titleba titlebar-mode)
						(create-borders w 'shaded) (create-corners 'shaded))
				)
			))

			(check (lambda (mode)
				(cond
					((eq mxflat:decoration-mode 'none)
						(transien)
					)
					((eq mxflat:decoration-mode 'all)
						(defaul)
					)
					(t
						(mode)
					)
				)
			))
		)

		(case frame-type
			((default)
				(check defaul)
			)
			((transient)
				(check transien)
			)
			((shaped shaded)
				(if (eq mxflat:decoration-mode 'none)
					(shade 'no-buttons)
					(shade)
				)
			)
			((shaded-transient)
				(if (eq mxflat:decoration-mode 'all)
					(shade)
					(shade 'no-buttons)
				)
			)
			((shaped-transient)
				(create-top-border)
			)
		)
	)
)

; setup initial colors
(recolor-all)
(after-set-title-update-interval)
; register theme with sawfish
(add-frame-style 'mxflat get-frame)

;;; END
