#!/usr/bin/env python

#    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 3 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, see <http://www.gnu.org/licenses/>.

#  TextDateTimeScreenlet (c) 2009 Michael O'Rourke

import screenlets
from screenlets.options import BoolOption, ColorOption, FontOption, IntOption, StringOption
from screenlets import DefaultMenuItem
import pango
import gobject
import datetime
import gtk

# use gettext for translation
import gettext

_ = screenlets.utils.get_translator(__file__)

def tdoc(obj):
	obj.__doc__ = _(obj.__doc__)
	return obj

@tdoc

class TextDateTimeScreenlet (screenlets.Screenlet):
	"""Displays the current time and/or date in simple text format with customizable fonts and colors."""
	
	# default meta-info for Screenlets (should be removed and put into metainfo)
	__name__	= 'TextDateTimeScreenlet'
	__version__	= '2009.08.10v.0.1'
	__author__	= 'Michael O\'Rourke, a.k.a. Momo'
	__website__ = 'http://www.cosmicat.com/software/textdatetime-screenlet/'
	__desc__	= __doc__	# set description to docstring of class
	
	# member variables
	init_complete = False
	main_fontdesc = None
	main_textalign = None
	datetime_rect = None
	datetime_shadow_rect = None
	datetime_format = ''
	date = ''
	
	# frame options
	frame_enabled = True
	frame_color = ( 0, 0, 0, 0.6 )
	frame_width = 200
	frame_height = 100
	frame_padding_x = 1
	frame_padding_y = 1
	
	# text options
	main_font = 'Sans 16'
	text_color = ( 1, 1, 1, 1 )
	text_shadow_enabled = True
	text_shadow_color = ( 0, 0, 0, 0.6 )
	text_shadow_distance = 1
	text_align = _('Center')
	text_transform = 'None'
	datetime_format_choice = _('Custom')
	datetime_format_custom = '%c'
	
	# constructor
	def __init__ (self, **keyword_args):
		screenlets.Screenlet.__init__(self, width=self.frame_width, height=self.frame_height, 
			uses_pango=True, uses_theme=False, ask_on_option_override=False, **keyword_args)
		
		# OPTIONS GROUP 'Frame'
		frame_group_desc = _('Frame Options\n\n' \
			'The frame determines the drawable area of the screenlet,\n' \
			'even if it is hidden.')
		self.add_options_group(_('Frame'), frame_group_desc)
		
		self.add_option(BoolOption(_('Frame'), 'frame_enabled',
			bool(self.frame_enabled), _('Show frame'), _('Draw a frame')))

		self.add_option(ColorOption(_('Frame'), 'frame_color', 
			self.frame_color, _('Frame color'), ''))
		
		self.add_option(IntOption(_('Frame'), 'frame_width',
			int(self.frame_width), _('Frame width'), '',
			min=10, max=1280),
			realtime=False)
		
		self.add_option(IntOption(_('Frame'), 'frame_height',
			int(self.frame_height), _('Frame height'), '',
			min=10, max=1024),
			realtime=False)
		
		self.add_option(IntOption(_('Frame'), 'frame_padding_x',
			int(self.frame_padding_x), _('Side padding'), '',
			min=0, max=800))
		
		self.add_option(IntOption(_('Frame'), 'frame_padding_y',
			int(self.frame_padding_y), _('Top padding'), '',
			min=0, max=600))

		# OPTIONS GROUP _('Text')
		self.add_options_group(_('Text'), _('Text Options'))
		
		self.add_option(FontOption(_('Text'), 'main_font',
			self.main_font, _('Text font'), ''))

		self.add_option(ColorOption(_('Text'), 'text_color', 
			self.text_color, _('Text color'), ''))

		self.add_option(BoolOption(_('Text'), 'text_shadow_enabled',
			bool(self.text_shadow_enabled), _('Text shadow'), _('Draw a text shadow')))

		self.add_option(ColorOption(_('Text'), 'text_shadow_color', 
			self.text_shadow_color, _('Text shadow color'), ''))
		
		self.add_option(IntOption(_('Text'), 'text_shadow_distance',
			int(self.text_shadow_distance), _('Text shadow distance'), '',
			min=1, max=600))
	
		self.add_option(StringOption(_('Text'), 'text_align',
			self.text_align, _('Text alignment'), '',
			choices=( _('Left'), _('Center'), _('Right') )))
	
		self.add_option(StringOption(_('Text'), 'text_transform',
			self.text_transform, _('Text transform'), '',
			choices=( _('None'), _('Uppercase'), _('Lowercase'), _('Swapcase') )))

		format_group_desc = _('Formatting Options\n\n' \
			'This screenlet uses \'strftime\' for date/time formatting.\n' \
			'More info: http://linux.die.net/man/3/strftime')
		self.add_options_group(_('Format'), format_group_desc)
		
		self.add_option(StringOption(_('Format'), 'datetime_format_choice',
			self.datetime_format_choice, _('Text format'), '',
			choices=( _('Custom'), _('Date and time'), _('Date (default)'), _('Date (yyyy-mm-dd)'), _('Date (verbose)'), _('Time (12h)'),
			_('Time (12h, no seconds)'), _('Time (24h)'), _('Time (24h, no seconds)'), _('Year'), _('Month (full)'),
			_('Month (abbreviated)'), _('Day of month'), _('Weekday (full)'), _('Weekday (abbreviated)'), _('Epoch seconds') )))

		self.add_option(StringOption(_('Format'), 'datetime_format_custom',
			self.datetime_format_custom, _('Custom text format'),
			_('This is only used if _("Text format") above is set to _("Custom").')),
			realtime=False)
	
	
	def on_init (self):
		"""Called when the Screenlet's options have been applied and the 
		screenlet finished its initialization."""
		
		# add default menu items
		self.add_default_menuitems()
		
		# initialize translated values
		self.update_main_fontdesc()
		self.update_main_textalign()
		self.update_datetime_format()
		
		# initialize layout rects
		self.datetime_rect = gtk.gdk.Rectangle()
		self.datetime_shadow_rect = gtk.gdk.Rectangle()
		self.update_rects_x()
		self.update_rects_y()
		
		self.init_complete = True
		
		self.timer = gobject.timeout_add(1000, self.update)
		self.update()
	
	
	def update (self):
		try:
			newdate = datetime.datetime.now().strftime(self.datetime_format)
			
			if self.text_transform == _('None'):
				pass
				
			elif self.text_transform == _('Uppercase'):
				newdate = newdate.upper()
				
			elif self.text_transform == _('Lowercase'):
				newdate = newdate.lower()
				
			elif self.text_transform == _('Swapcase'):
				newdate = newdate.swapcase()
			
			if newdate != self.date:
				self.date = newdate
				self.redraw_canvas()
		except:
			self.date = '[invalid format]'
			self.redraw_canvas()
		
		return True # keep running this event
		
	
	def init_options_from_metadata (self):
		pass


	def __setattr__(self, name, value):

		screenlets.Screenlet.__setattr__(self, name, value)
		
		if name == 'frame_width':
			self.width = value
			
		elif name == 'frame_height':
			self.height = value
	

	def on_after_set_atribute (self, name, value):
		"""Called after setting screenlet atributes"""
		
		if name == 'frame_width':
			self.update_rects_x()
			
		elif name == 'frame_height':
			self.update_rects_y()
		
		elif name == 'text_align':
			self.update_main_textalign()
			self.update_rects_x()
		
		elif name == 'text_shadow_distance':
			self.update_rects_x()
			self.update_rects_y()
	
		elif name == 'frame_padding_x':
			self.update_rects_x()
				
		elif name == 'frame_padding_y':
			self.update_rects_y()
			
		elif name == 'main_font':
			self.update_main_fontdesc()

		elif name == 'datetime_format_choice':
			self.update_datetime_format()

		elif name == 'datetime_format_custom':
			self.update_datetime_format()
		
		self.redraw_canvas()
		

	def on_before_set_atribute(self, name, value):
		"""Called before setting screenlet atributes"""
		pass

	def on_create_drag_icon (self):
		"""Called when the screenlet's drag-icon is created. You can supply
		your own icon and mask by returning them as a 2-tuple."""
		return (None, None)

	def on_composite_changed(self):
		"""Called when composite state has changed"""
		pass

	def on_drag_begin (self, drag_context):
		"""Called when the Screenlet gets dragged."""
		pass
	
	def on_drag_enter (self, drag_context, x, y, timestamp):
		"""Called when something gets dragged into the Screenlets area."""
		pass
	
	def on_drag_leave (self, drag_context, timestamp):
		"""Called when something gets dragged out of the Screenlets area."""
		pass

	def on_drop (self, x, y, sel_data, timestamp):
		"""Called when a selection is dropped on this Screenlet."""
		return False
		
	def on_focus (self, event):
		"""Called when the Screenlet's window receives focus."""
		pass
	
	def on_hide (self):
		"""Called when the Screenlet gets hidden."""
		pass

	def on_key_down(self, keycode, keyvalue, event):
		"""Called when a keypress-event occured in Screenlet's window."""
		pass
	
	def on_load_theme (self):
		"""Called when the theme is reloaded (after loading, before redraw)."""
		pass
	
	def on_menuitem_select (self, id):
		"""Called when a menuitem is selected."""
		
		pass

	def on_mouse_move (self, event):
		"""Called when the mouse moves in the Screenlet's window."""
		pass

	def on_mouse_up (self, event):
		"""Called when a buttonrelease-event occured in Screenlet's window. 
		Returning True causes the event to be not further propagated."""
		return False
		
	def on_realize (self):
		""""Callback for handling the realize-event."""
	
	def on_scale (self):
		"""Called when Screenlet.scale is changed."""
		pass
	
	def on_scroll_up (self):
		"""Called when mousewheel is scrolled up (button4)."""
		pass

	def on_scroll_down (self):
		"""Called when mousewheel is scrolled down (button5)."""
		pass
	
	def on_show (self):
		"""Called when the Screenlet gets shown after being hidden."""
		pass
	
	def on_switch_widget_state (self, state):
		"""Called when the Screenlet enters/leaves "Widget"-state."""
		pass
	
	def on_unfocus (self, event):
		"""Called when the Screenlet's window loses focus."""
		pass

	def update_main_textalign (self):
		"""Updates the Pango alignment based on a StringOption selection."""
		if self.text_align == _('Center'):
			self.main_textalign = pango.ALIGN_CENTER

		elif self.text_align == _('Left'):
			self.main_textalign = pango.ALIGN_LEFT

		elif self.text_align == _('Right'):
			self.main_textalign = pango.ALIGN_RIGHT


	def update_main_fontdesc (self):
		"""Updates the Pango font description based on a font name."""
		if self.main_font != '':
			self.main_fontdesc = pango.FontDescription(self.main_font)

	
	def update_datetime_format (self):
		"""Needs to be called whenever the datetime format choice or custom format is changed."""
		if self.datetime_format_choice == _('Custom'):
			if self.datetime_format_custom == '':
				self.datetime_format = '%c'
			else:
				self.datetime_format = self.datetime_format_custom

		elif self.datetime_format_choice == _('Date and time'):
			self.datetime_format = '%c'

		elif self.datetime_format_choice == _('Date (default)'):
			self.datetime_format = '%x'

		elif self.datetime_format_choice == _('Date (yyyy-mm-dd)'):
			self.datetime_format = '%F'

		elif self.datetime_format_choice == _('Date (verbose)'):
			self.datetime_format = '%B %e, %Y'

		elif self.datetime_format_choice == _('Time (12h)'):
			self.datetime_format = '%l:%M:%S %p'

		elif self.datetime_format_choice == _('Time (12h, no seconds)'):
			self.datetime_format = '%l:%M %p'

		elif self.datetime_format_choice == _('Time (24h)'):
			self.datetime_format = '%H:%M:%S'

		elif self.datetime_format_choice == _('Time (24h, no seconds)'):
			self.datetime_format = '%H:%M'

		elif self.datetime_format_choice == _('Year'):
			self.datetime_format = '%Y'

		elif self.datetime_format_choice == _('Month (full)'):
			self.datetime_format = '%B'

		elif self.datetime_format_choice == _('Month (abbreviated)'):
			self.datetime_format = '%b'

		elif self.datetime_format_choice == _('Day of month'):
			self.datetime_format = '%e'

		elif self.datetime_format_choice == _('Weekday (full)'):
			self.datetime_format = '%A'

		elif self.datetime_format_choice == _('Weekday (abbreviated)'):
			self.datetime_format = '%a'

		elif self.datetime_format_choice == _('Epoch seconds'):
			self.datetime_format = '%s'

		else: # for backwards compatibility
			self.datetime_format = '%c'
	

	def update_rects_x (self):
		"""Calculates layout rectangle dimensions along the x-axis"""
		if self.datetime_rect == None:
			return
		
		if self.text_align == _('Center'):
			self.datetime_rect.x = 0
			self.datetime_rect.width = self.frame_width - self.text_shadow_distance
			
		elif self.text_align == _('Left'):
			self.datetime_rect.x = self.frame_padding_x
			self.datetime_rect.width = self.frame_width - self.datetime_rect.x - self.text_shadow_distance
			
		elif self.text_align == _('Right'):
			self.datetime_rect.x = 0
			self.datetime_rect.width = self.frame_width - self.frame_padding_x - self.text_shadow_distance
		
		self.datetime_shadow_rect.x = self.datetime_rect.x + self.text_shadow_distance
		self.datetime_shadow_rect.width = self.datetime_rect.width
		
		
	def update_rects_y (self):
		"""Calculates layout rectangle dimensions along the y-axis"""
		if self.datetime_rect == None:
			return
		
		self.datetime_rect.y = self.frame_padding_y
		self.datetime_rect.height = self.frame_height - self.datetime_rect.y - self.text_shadow_distance
		self.datetime_shadow_rect.y = self.datetime_rect.y + self.text_shadow_distance
		self.datetime_shadow_rect.height = self.datetime_rect.height
		
		
	def on_draw (self, ctx):
		"""In here we draw"""
		if not self.init_complete:
			return
		
		ctx.scale(self.scale, self.scale)

		if self.frame_enabled:
			# draw the frame
			ctx.set_source_rgba(*self.frame_color)
			self.draw_rectangle_advanced(ctx, 0, 0, self.width, self.height, rounded_angles=(5,5,5,5), fill=True)
		
		if self.text_shadow_enabled:
			# draw the shadow
			self.draw_text_ex(ctx, self.datetime_shadow_rect, self.date, self.text_shadow_color, self.main_fontdesc,
					self.main_textalign)
		
		#draw the text
		self.draw_text_ex(ctx, self.datetime_rect, self.date, self.text_color, self.main_fontdesc, self.main_textalign)
		
		
	def draw_text_ex(self, ctx, rect, text, text_color, fontdesc, alignment, ellipsize=pango.ELLIPSIZE_NONE):
		"""Draws text using provided dimensions and font descriptions"""
		ctx.save()
		ctx.translate(rect.x, rect.y)
		ctx.set_source_rgba(*text_color)
		if self.p_layout == None:
			self.p_layout = ctx.create_layout()
		else:
			ctx.update_layout(self.p_layout)
		self.p_layout.set_font_description(fontdesc)
		self.p_layout.set_width(rect.width * pango.SCALE)
		self.p_layout.set_alignment(alignment)
		self.p_layout.set_ellipsize(ellipsize)
		self.p_layout.set_text(text)
		ctx.show_layout(self.p_layout)
		ctx.restore()


	def on_draw_shape (self, ctx):
		self.on_draw(ctx)
	
	
# If the program is run directly or passed as an argument to the python
# interpreter then create a Screenlet instance and show it
if __name__ == "__main__":
	# create new session
	import screenlets.session
	screenlets.session.create_session(TextDateTimeScreenlet)

