#!/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/>.

#  DropboxScreenlet (c) spitfire23bc 2009 
#
# INFO:
# - Screenlet that monitors Dropbox
# 
# TODO:
# - Get quota and usage from server
#		Use gnome-keyring ???
#		Split bar to show local + server


import screenlets
from screenlets import DefaultMenuItem
from screenlets.options import ColorOption, StringOption, FloatOption, FontOption
from screenlets.utils import xdg_open
import cairo, rsvg, gtk, os, pango, subprocess, gobject, re

#use gettext for translation
import gettext

_ = screenlets.utils.get_translator(__file__)

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

@tdoc
class DropboxScreenlet (screenlets.Screenlet):
	"""A Screenlet to monitor the current Dropbox status and quickly add files to your Dropbox folder."""
	__name__	= 'DropboxScreenlet'
	__version__ = '0.4.1'
	__requires__ = ['dropbox']
	__author__	= 'spitfire23bc'
	__desc__	= __doc__
	
	update_interval = 1
	mouse_is_hover = False
	mouse_press = False
	dragging = False
	draw_dirlist = False
	dropped = False
	droptext = ''
	
	# editable options
	db_path = '~/Dropbox'
	dragdrop = _('Link')
	dragdrop_options = [_('Link'), _('Copy'), _('Move')]
	storage_total = 2.00
	font_colour = (1,1,1, 0.8)
	back_colour = (0,0,0, 0.2)
	bar_out_colour = (0.68,0.81,0.94, 0.8)
	bar_back_colour = (0.96,0.98,1, 0.8)
	bar_fill_colour = (0.2,0.55,0.83, 0.8)
	font = 'FreeSans 10'

	
	default_space = True
	linespace = ()
	
	def __init__ (self, **keyword_args):
		screenlets.Screenlet.__init__(self, 
			width=280, height=60, uses_theme=True, 
			drag_drop=True, **keyword_args)
		self.update_interval = self.update_interval
		self.__timeout = gobject.timeout_add(1000, self.update)
		
		# Set the default themes
		self.theme_name = "default"		
		self.icontheme = gtk.icon_theme_get_default()
		self.icontheme.connect("changed", self.icons_changed)
				
		# Add user-editable settings
		self.add_options_group(_('Dropbox'), 
			_('Configure Dropbox Screenlet.'))
		self.add_options_group(_('Appearance'), 
			_('Configure screenlet appearance.'))
		
		# Dropbox options group
		# Change Dropbox/ location
		self.add_option(StringOption(_('Dropbox'), 'path', 
			self.db_path,_('Location of Dropbox folder'), '~/Dropbox',),
			realtime=False)
		# Set Drag/drop behaviour
		self.add_option(StringOption(_('Dropbox'), 'drop', 
			self.dragdrop,_('Drag and drop behaviour'), 
			'',	choices = self.dragdrop_options))
		# Set storage quota
		self.add_option(FloatOption(_('Dropbox'), 'storage', 
			self.storage_total, _('Total storage quota (GB)'), 
			_('The total storage space (in gigabytes)'),
			min=2.00, max=200.00, increment=0.25, digits=2))

		# Appearance options group	
		# Text colour
		self.add_option(ColorOption(_('Appearance'),'font_colour', 
			self.font_colour, _('Text colour'),'font_colour'))
		# Background colour
		self.add_option(ColorOption(_('Appearance'),'back_colour', 
			self.back_colour, _('Back colour'), 'back_colour'))
		# Bar outline colour
		self.add_option(ColorOption(_('Appearance'),'bar_out_colour', 
			self.bar_out_colour, _('Bar outline colour'), 'bar_out_colour'))
		# Bar background colour
		self.add_option(ColorOption(_('Appearance'),'bar_back_colour', 
			self.bar_back_colour, _('Bar background colour'), 'bar_back_colour'))
		# Bar fill colour
		self.add_option(ColorOption(_('Appearance'),'bar_fill_colour', 
			self.bar_fill_colour, _('Bar fill colour'), 'bar_fill_colour'))
		# Font
		self.add_option(FontOption(_('Appearance'),'fontchoice', 
			self.font, _('Font'), 'font'))
				
				
	def menuitem_callback(self, widget, id):
		screenlets.Screenlet.menuitem_callback(self, widget, id)
		if id=="web":
			xdg_open('https://www.dropbox.com/home#/')
		elif id=="sys":
			xdg_open(self.db_path)
			
	def icons_changed(self, event):
		self.redraw_canvas()
		self.update()


	def on_init (self):
		print "Screenlet has been initialized."
		# add default menuitems
		self.add_menuitem("web", _("Web interface"))
		self.add_menuitem("sys", _("File Browser"))
		self.add_default_menuitems()
		
	def __setattr__ (self, name, value):
		screenlets.Screenlet.__setattr__(self, name, value)
		if name == 'storage':
			self.storage_total = value
		elif name == 'drop':
			self.dragdrop = value
		elif name == 'path':
			self.db_path = os.path.expanduser(value)
			if self.db_path.endswith('/'):
				self.db_path = self.db_path.rstrip('/')
		elif name == 'fontchoice':
			self.font = value
		else: pass


	# custom functions
	
	def on_mouse_enter (self, event):
		self.mouse_is_hover = True
		self.update()

	def on_mouse_leave (self, event):
		self.mouse_is_hover = False
		self.mouse_press = False
		self.update()
		
	def on_mouse_down (self, event):
		if event.button == 1:
			if self.mouse_is_hover:
				self.mouse_press = True
		return False
	
	def on_mouse_up (self, event):
		# On left-click, open Dropbox folder
		if event.button == 1:
			if self.mouse_is_hover:
				xdg_open(self.db_path)
			self.mouse_press = False
		return False
	
	def draw_highlight (self, ctx, c):
		# draw highlight/flashing effect over a hovered item
		ctx.save()
		ctx.rectangle(0, 0, 50,	50)
		ctx.set_operator(cairo.OPERATOR_ATOP)
		ctx.set_source_rgba(c[0],c[1],c[2], c[3])
		ctx.fill()
		ctx.restore()
		ctx.set_operator(cairo.OPERATOR_OVER)
	
	def on_drag_enter (self, drag_context, x, y, timestamp):
		"""Called when something gets dragged into the Screenlets area."""
		self.dragging = True
		self.update()
		
	def on_drag_leave (self, drag_context, timestamp):
		"""Called when something gets dragged out of the Screenlets area."""
		self.dragging = False
		self.update()
	
	def on_drop (self, x, y, sel_data, timestamp):
		self.dropped = True
		self.dragging = False
		filename = ''
		filename = str(sel_data.get_text())
		filename = str.replace(filename,'%20','\ ')
		linkname = filename
		if filename.startswith('file://'):
			print "Dropped: " + filename + " at " + str(x) + "," + str(y)
			
			# Get filename only, without location
			bool = True
			while bool:
				pos = linkname.find('/') + 1
				linkname = linkname[pos:]
				if pos:	
					bool = True
				else: 
					bool = False
			filename = filename[7:len(filename)-1]
			
			if x>0 and x<50:
				linkname = self.db_path + '/' + linkname[:len(linkname)-1]
				droppath = self.db_path
				self.droptext = _("Dropped in main folder")
			elif x>80 and x<130:
				linkname = self.db_path + '/Photos/' + linkname[:len(linkname)-1]
				droppath = self.db_path + '/Photos/'
				self.droptext = _("Dropped in photos folder")
			elif x>160 and x<210:
				linkname = self.db_path + '/Public/' + linkname[:len(linkname)-1]
				droppath = self.db_path + '/Public/'
				self.droptext = _("Dropped in public folder")
			else: 
				self.droptext = _("No drop made")
				return
			
			if self.dragdrop == _('Link'):
				# Create symlink in Dropbox folder
				os.system('ln -s ' + filename + ' ' + linkname)
				print "Symlink created"
			elif self.dragdrop == _('Copy'):
				# Copy file or folder to Dropbox folder
				os.system('cp -r ' + filename + ' ' + droppath)
				print "Copy created"
			elif self.dragdrop == _('Move'):
				# Move file or folder to Dropbox folder
				os.system('mv ' + filename + ' ' + droppath)
				print "File/folder moved"
			else: pass
		else: pass
		
	
	def get_dropbox_status (self):
		# Returns current dropbox status
		proc = subprocess.Popen('dropbox status', 
			shell='true', stdout=subprocess.PIPE)
		read_proc = proc.stdout.read()
		if read_proc:
			current = re.split('\n',read_proc,1)[0]
		else:
			current = _("Command not found")
		return current
		
	def get_storage (self):
		# Reads size of Dropbox folder
	    out = subprocess.Popen('du -s -L ' + self.db_path, 
	    	shell='true', stdout=subprocess.PIPE)
	    size = out.stdout.read()
	    tab = size.find('\t')
	    size = float(size[0:tab] + '.0')
	    return size
	    
	def clipping (self, input,dp):
		# Clips numbers at a set number of decimal places
		input = str(input)
		if '.' in input:
			point = input.find('.')
			output = input[0:point+dp+1]
		else: 
			output = input
		return output

	def on_draw (self, ctx):
		storage_used = self.get_storage()
		quota = self.storage_total * 1024**2
		perc = storage_used / quota * 100

		perc_str = self.clipping(perc,1) + '% used'
		
		if perc > 50:
			div = 1024**2
			unit = ' GB'
		else: 
			div = 1024
			unit = ' MB'
			
		used_str = self.clipping(storage_used/div,2) + unit
		total_str = self.clipping(self.storage_total,2) + 'GB'
		
		# Get dropbox status and assign line break point
		status = self.get_dropbox_status()
		# Downloading and uploading, with transfer speed
		if 'loading' in status and '(' in status:
			cut = status.find('(')
		elif _('Can\'t sync') in status:
		    cut = status.find(';') + 2
		elif len(status) > 30:
			cut = 30
		else:
			cut = len(status)
		
		# Define each line of text
		line1 = status[0:cut]
		line2 = '  ' + status[cut:60]
		line3 = perc_str + ' (' + used_str + ' of ' + total_str + ')'
		
		# Set constants for text
		fontsize = int(re.findall('\d+',self.font)[0])
		font = re.split(' \d+',self.font,1)[0]
			
		# Set status icons...
		icon_path = self.get_screenlet_dir() + "/themes/" + self.theme_name
		if _("Dropbox isn't") in status or _("Can\'t sync") in status:
			status_icon = icon_path + '/status-unsyncable.png'
		elif _("Couldn\'t get") in status or _("not found") in status:
			status_icon = icon_path + '/status-unsyncable.png'
		elif _('Idle') in status:
			status_icon = icon_path + '/status-uptodate.png'
		else: status_icon = icon_path + "/status-syncing.png"
		# ... and the default Dropbox logo
		logo_icon = icon_path + '/dropbox.png'
		
		ctx.scale(self.scale, self.scale)
		ctx.set_operator(cairo.OPERATOR_OVER)
		
		# Draw background
		ctx.set_source_rgba(*self.back_colour)
		self.draw_rounded_rectangle(ctx,30,0,6,250,60)
		
		# Bar position variables and width percentage
		bar_x = 34
		bar_y = 50
		bar_width = float((perc/100)*238)
		
		# Draw bar
		ctx.set_source_rgba(*self.bar_out_colour)
		self.draw_rectangle(ctx,bar_x,bar_y,242,8)
		ctx.set_source_rgba(*self.bar_back_colour)
		self.draw_rectangle(ctx,bar_x + 1,bar_y + 1,240,6)
		ctx.set_source_rgba(*self.bar_fill_colour)
		self.draw_rectangle(ctx,bar_x + 2,bar_y + 2,bar_width,4)		
			
			
		if not self.dragging:
		# While not dragging anything over the screenlet
		# Normal behaviour
			# Draw Dropbox icon
			self.draw_scaled_image(ctx,0,0,logo_icon,50,50)
	
			# Status placement
			ctx.set_source_rgba(*self.font_colour)
			if self.default_space:
				y = (2, 2 + fontsize * 1.6, 2 + 2*fontsize * 1.6)
			else: y = self.linespace
		
			# Draw text on individual lines
			self.draw_text(ctx,	'<b>' + line1 + '</b>',
				#x,y,font, size, max width
				55,y[0],font,fontsize, 300,
				allignment=pango.ALIGN_LEFT,weight = 10)
		
			self.draw_text(ctx,line2,
				55,y[1],font,fontsize, 300,
				allignment=pango.ALIGN_LEFT,weight = 10)
		
			self.draw_text(ctx,line3,
				55,y[2],font,fontsize, 300,
				allignment=pango.ALIGN_LEFT,weight = 10)
		
		
		else:
		# Whilst dragging over the screenlet
		# Get theme icons for photos and public
			try:
				photos = self.icontheme.lookup_icon('emblem-photos',50,gtk.ICON_LOOKUP_USE_BUILTIN)
				photos = photos.get_filename()
				public = self.icontheme.lookup_icon('emblem-web',50,gtk.ICON_LOOKUP_USE_BUILTIN)
				public = public.get_filename()
			except:
			# Or else use the default gnome icons
				photos = '/usr/share/icons/gnome/scalable/emblems/emblem-web.svg'
				public = '/usr/share/icons/gnome/scalable/emblems/emblem-web.svg'
				
			#Draw said icons
			self.draw_scaled_image(ctx,0,0,logo_icon,50,50)
			self.draw_scaled_image(ctx,80,0,photos,50,50)
			self.draw_scaled_image(ctx,160,0,public,50,50)
					
		
		
		# Give visual feedback of a mouse press
		if self.mouse_press:
			self.draw_highlight(ctx,(0,0,0,0.3))
		# Draw status icon
		self.draw_scaled_image(ctx,25,25,status_icon,25,25)
		

	
	def update (self):
		self.redraw_canvas()
		return True
	
	def on_draw_shape (self, ctx):
		self.on_draw(ctx)
		
	

if __name__ == "__main__":
	# create new session
	import screenlets.session
	screenlets.session.create_session(DropboxScreenlet)

