Web   ·   Wiki   ·   Activities   ·   Blog   ·   Lists   ·   Chat   ·   Meeting   ·   Bugs   ·   Git   ·   Translate   ·   Archive   ·   People   ·   Donate
summaryrefslogtreecommitdiffstats
path: root/Experior.Activity/sbpython.py
blob: b80554f6d8d79206a23eb694101747a1e2cfad10 (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
485
486
487
488
489
490
491
492
493
494
495
496
497
498
499
500
501
502
503
504
505
506
507
508
509
510
511
512
513
514
515
516
517
518
519
520
521
522
523
524
525
526
527
528
529
530
531
532
533
534
535
536
537
538
539
540
541
542
543
544
545
546
547
548
549
550
551
552
553
554
555
556
557
558
559
560
561
#!/usr/bin/env python
# encoding: utf-8
"""
sbpython.py

This file is part of sugarbot.

sugarbot 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.

sugarbot 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 sugarbot.  If not, see <http://www.gnu.org/licenses/>.
"""

import sys
import time
import gtk
import logging
from gtk import gdk
import gobject
from sbdecorators import *

import sugar
from sugar import graphics
from sugar.graphics.toolcombobox import ToolComboBox

class NotSupportedError(NotImplementedError):
	def __init__(self, widget=None, command=None):
		self.widget 	= widget
		self.command 	= command
	def __str__(self):
		return repr('The widget %s [%s] does not support the command(s) %s' \
		 				% (self.widget, self.widget.__class__, self.command))
		
class WidgetDoesNotExist(ValueError):
	def __init__(self, widget=None):
		self.widget 	= widget
	def __str__(self):
		return repr('The widget %s could not be identified.' % self.widget)
	

class wrappedWidget(object):
	raiseExceptions = True
	
	def __init__(self, widget, name):
		self.widget = widget
		self.name	= name
		self.log	= logging.getLogger('w(%s)' % name)
		
	def __getitem__(self, index):
		"""
		Returns the indexed item.  
		
		For ComboBox and similar items, this will effectively be the n'th object.
		Uses a helper function for each class type.
		"""
		indexMethods = \
		{ 
			gtk.ComboBox: self.getList_GtkComboBox,
		 	ToolComboBox: self.getList_SugarGraphicsCombo,
			# gtk.Container: self.getList_Container
		}

		for classType in indexMethods:
			if isinstance(self.widget, classType):
				return indexMethods[classType]()[index]

	def notSupportedError(self, args):
		"""
		Wrapper for raising NotSupportedError exceptions.
		"""
		if self.raiseExceptions:
			raise NotSupportedError, (self.widget, args)
		return None

	def supportsSignal(self, signalName):
		"""
		Checks to see if a GTK object supports a signal.
		
		Checks to see if a GTK object supports a signal, via the
		gobject.signal_lookup method.  Returns True if it is supported,
		false otherwise.
		"""
		return gobject.signal_lookup(signalName, self.widget.__class__) != 0
		
	def click(self):
		"""
		Simulates a user click.
		
		Simulates a user click by:
		 1) Emitting a 'clicked' signal
		 2) Calling the 'activate' method
		"""
		self.log.info("Clicking %s" % self.name)
		
		widgetClass	= self.widget.__class__
		
		clickedSignal	= 'clicked'
		activateMethod	= 'activate'
		
		# Attempt to emit the 'clicked' signal.
		if self.supportsSignal(clickedSignal):		
			self.widget.emit(clickedSignal)		
			return True
			
		# Attempt to simply 'activate' the widget.
		elif hasattr(self.widget, activateMethod):
			getattr(self.widget, activateMethod)()
			return True
			
		# Fail gracefully
		return self.notSupportedError((clickedSignal, activateMethod))
		
	
	def getText(self):
		"""
		Returns the text in the widget.
		
		Returns whatever text is being stored by the widget.  This function
		gives priority to Widget functions in the following order:
	 	 1) get_text method
		 2) label (same as getLabel)
		 3) title (same as getTitle)
 		"""
		self.log.info("Getting text for %s" % self.name)
		
		getText		= "get_text"

		# Get the text from the widget		
		# try:	return self.__simpleGetter(getText)
		# except NotSupportedError: 	pass
		if hasattr(self.widget, getText):
			return getattr(self.widget, getText)()
		
		# Special handling for sugar ToolComboBox
		if isinstance(self.widget, ToolComboBox):
			logging.fatal("YES!!!")
			return self.widget.combo.get_active_item()[0]
			
		# Try the label
		try: 	return self.label
		except NotSupportedError: 	pass
		
		# Try the title
		try:	return self.title
		except NotSupportedError:	pass
	
		# Fail
		return self.notSupportedError((getText,"label","title"))
		
	def typeText(self, val):
		"""
		Simulates user text entry.
		
		Simulates a user typing into a widget.  Inserts the text at the
		current insertion location, via:
		 1) Emits the 'insert-at-cursor' signal.
		"""
		self.log.info("Adding text for %s to %s" % (self.name, val))
		
		terminalMethod 	= "feed_child"
		insertAtCursor	= 'insert-at-cursor'
		
		if self.supportsSignal(insertAtCursor):
			self.widget.emit(insertAtCursor, val)
			return True
		
		# --- TERMINAL.ACTIVITY HACK ---	
		# vte.Terminal-specific typing
		if hasattr(self.widget, terminalMethod):
			self.widget.feed_child(val)
			return True

		return self.notSupportedError(insertAtCursor, terminalMethod)
		
	def setText(self, val):
		"""
		Sets the text for the widget.
		
		Sets the text for the widget to the user-provided string.
		Uses the following procedures to attempt to set the text:
		 1) Calls 'set_text' method
		 2) Set the 'label' property
		 3) Set the 'title' property
		 4) A Terminal-specific method, 'feed_child'
		 5) Set the text via 'typeText'
		"""
		self.log.info("Setting text for %s to %s" % (self.name, val))
		
		setText			= "set_text"
							
		# --- GENERAL APPROACH ---
		# Try simply setting the text...
		try:
			self.__simpleSetter(setText, val)
			return True
		except NotSupportedError: pass
		# if hasattr(self.widget, setText):
		# 	getattr(self.widget, setText)(val)
		# 	return True
			
		# Try setting the label
		try:
			self.label = val
			return True
		except NotSupportedError:	pass
		
		# Try setting the title
		try:	
			self.title = val
			return True
		except NotSupportedError: pass
				
		# --- TEXT APPENDING ---
		# Try to just insert the text at the given point...
		try:
			self.typeText(val)
			return True
		except NotSupportedError:
			pass

		# Fail
		return self.notSupportedError((setText,"label","title"))
					
	def delete(self, numberOfTimes, deleteType):
		"""
		Simulates user pressing the 'delete' key.
		
		Simulates user pressing the 'delete' key a given number of times,
		with a flexible deletion type (e.g. characters, words, lines)
		"""
		# Note that we must negate numberOfTimes here, because it is
		# re-negated inside of the 'backspace' call.
		return backspace(-numberOfTimes, deleteType)
			
	def backspace(self, numberOfTimes=1, deleteType=gtk.DELETE_CHARS):
		"""
		Simulates the backspace keypress.
		
		Simulates the backspace keypress. numberOfTimes times.
		If numberOfTimes is negative, simulate the 'delete' keypress.
		"""
		backspace 			= 'backspace'
		deleteFromCursor 	= 'delete-from-cursor'
		
		# Try the 'deleteFromCursor' approach, as it is very flexible in the
		# number of ways to delete content.
		if self.supportsSignal(deleteFromCursor):
			self.widget.emit(deleteFromCursor, deleteType, -numberOfTimes)
			
		# Try the standard 'backspace' command.  Note that this will not work
		# for negative quantities.
		elif self.supportsSignal(backspace) and numberOfTimes >= 0:
			for i in range(0, numberOfTimes):
				self.widget.emit(backspace)
			return True
			
		else:
			return self.notSupportedError((backspace, deleteFromCursor))
	
	def doFocus(self, setFocus=None):
		"""
		Either sets or gets the focus for the widget.
		"""
		if setFocus is None:
			return self.widget.flags() & gtk.HAS_FOCUS
		elif setFocus:
			self.widget.grab_focus()
			return True
	
	def getInfo(self):
		"""
		Returns a bunch of information about the widget.
		"""
		def getClasses(object):
			"""			
			Returns a list containing the class that the object is an instance of,
			and all of the classes that the instance inherits from.
			For example:
					>>> class A: pass
					>>> class B(A): pass
					>>> class C(B): pass
					>>> x = C()
					>>> list = getClasses(x)
					>>> list
					[<class __main__.C at 0x6a1e0>, <class __main__.B at 0x6a1b0>, 
						<class __main__.A at 0x6a180>]
					>>> for i in list:
					...   print "Is instance of " + str(i) + "? " + str(isinstance(x,i))
					... 
					Is instance of __main__.C? True
					Is instance of __main__.B? True
					Is instance of __main__.A? True
			"""
			# If the passed object is an instance of a class, it will have a
			# __class__ attribute.
			if hasattr(object,"__class__"):
				recursionData = [object.__class__]

				# Include the lowest-level class in the heirarchy tree
				# Iterate through all of the base classes
				for _class in object.__class__.__bases__:
					if not _class is object:
						recursionData.append(_class)
						recursionData.extend(getClasses(_class))
			# Otherwise, the object -is- a class, and it will have a __bases__
			# attribute.
			elif hasattr(object,"__bases__"):
				# Iterate through all of the base classes.   Note that we do NOT add
				# the class itself here, as that is only done above, on the first
				# call.
				for _class in object.__bases__:
					recursionData.append(_class)
					recursionData.extend(getClasses(_class))
			return recursionData
		
		# ------ BEGIN INFO() METHOD -------
		infoStr = "\n%s" % getClasses(self.widget)

		l = dir(self.widget)
		for i in l:
			infoStr+= "\n> %s" % i

		return infoStr
	
	def __simpleGetter(self, method):
		"""
		Wrapper for simple 'get' methods

		Wrapper for simple 'get' methods (like get_label), that fails gracefully
		if the method is not available for the widget object.
		"""
		self.log.info("Getting %s for %s" % (method, self.name))
		if hasattr(self.widget, method):
			return getattr(self.widget, method)
			
		return self.notSupportedError(method)

	def __simpleSetter(self, method, val):
		"""
		Wrapper for simple 'set' methods
		
		Wrapper for simple 'set' methods (like get_label), that fails
		gracefully if the method is not available for the widget object.
		"""
		self.log.info("Setting %s to %s for %s" % (method, val, self.name))
		if hasattr(self.widget, method):
			getattr(self.widget, method)(val)
			return True
			
		return self.notSupportedError(method)
				
	def getTitle(self):
		"""
		Returns the widget's title.
		"""
		return self.__simpleGetter('get_title')
		
	def setTitle(self, val):
		"""
		Sets the widget's title
		"""
		return self.__simpleSetter('set_title', val)
		
	def getLabel(self):
		"""
		Returns the widget's label.
		"""
		return self.__simpleGetter('get_label')
		
	def setLabel(self, val):
		"""
		Sets the widget's label
		"""
		return self.__simpleSetter('set_label', val)
	
	def getListFormat(self, item):
		"""
		Converts a ComboBox or ToolComboBox's entries into a list
		
		Converts a ComboBox or ToolComboBox's entries into a list.  This is
		useful in enumerating the entries in a ComboBox for selection.
		"""
		retVal = []
		
		if isinstance(item, ToolComboBox):
			retVal = self.getList_GtkComboBox(item)
		elif isinstance(item, gtk.ComboBox):
			retVal = self.getList_SugarGraphicsCombo()
						
		return retVal
				
	def getList_GtkComboBox(self, combo):
		"""
		getListFormat handler for gtk.ComboBo objects.
		"""
		index = combo.get_active()
		if index == -1:
			index = 0
		
		model = combo.get_model() 
		row = model.iter_nth_child(None, index)
		
		if not row:
			return None
		
		listOfValues = []
		
		for i in range(0, len(model)):
			try:
				item = model[i]
				listOfValues.append(item)
			except IndexError:
				break
		return listOfValues
		
	def getList_SugarGraphicsCombo(self):
		"""
		getListFormat handler for sugar.graphics.ComboBox objects.
		"""
		tempList = self.getList_GtkComboBox(self.widget.combo)
		
		returnList = []
		for entry in tempList:
			returnList.append(entry[1])
		
		return returnList 
	
	def getSelected_SugarGraphicsCombo(self):
		"""
		getSelected handler for sugar.graphics.combo* objects.
		"""
		active = self.getSelected_ComboBox(self.widget.combo)
		
		if len(active) > 1:
			return active[1]
		elif len(active) == 1:
			return active[0]
		return active

	def getSelected_ComboBox(self, combo=None):
		"""
		getSelected handler for gtk.ComboBox objects.
		"""
		if combo is None:
			combo = self.widget
			
		index = combo.get_active()
		if index == -1:
			index = 0

		row = combo.get_model().iter_nth_child(None, index)
		if not row:
			return None
		return combo.get_model()[row]

	def getSelected(self):
		"""
		Returns the item selected in a gtk.ComboBox or sugar.graphics.ToolComboBox
		"""
		self.log.info("Getting selected entry")
		# Handle for the ComboBox...
		if isinstance(self.widget, gtk.ComboBox):
			return self.getSelected_ComboBox(self.widget)
			
		# Special handling for sugar ToolComboBox
		if isinstance(self.widget, ToolComboBox):
			return self.getSelected_SugarGraphicsCombo()			

		try:	return self.__simpleGetter('get_active_text')
		except NotSupportedError: pass
		
		try:
			i 	= self.widget.get_active()
			mod	= self.widget.get_model()
			return mod[i]
		except:
			pass
			
		return self.notSupportedError('get_active_text', 'get_model')
	
	def setSelected(self, val):
		"""
		Sets the item selected in a gtk.ComboBox or sugar.graphics.ToolComboBox
		"""
		self.log.info("Setting selected entry")
		# If textual: Enumerate all of the selections
		# If numeric: set selected
		combo = self.widget
		
		# Special handling for ToolComboBox
		if isinstance(self.widget, ToolComboBox):
			combo = self.widget.combo
		
		if isinstance(combo, gtk.ComboBox):
			# If we were provided with a string, get the numerical offset of
			# the provided entry.
			if isinstance(val, str):
				l = self.getListFormat(combo)
				if val in l:
					val = l.index(val)
			
			# Use the index to set the active combobox item
			if isinstance(val, int):
				combo.set_active(val)
				return
				
		return self.notSupportedError('set_active')
			
	
	text 		= property(getText, setText)
	title		= property(getTitle, setTitle)
	label		= property(getLabel, setLabel)
	
	focus		= property(doFocus, doFocus)
	selected	= property(getSelected, setSelected)
	info		= property(getInfo)
	

class widgetRegistry():
	gui	= None
	
	def __init__(self):		
		self.widgets = {}
		self.log	= logging.getLogger('wrappedWidget')
		self.log.info("Instantiated widgetRegistry")

	def __getitem__(self, which):
		"""
		Allows us to select widgets like a dictionary.
		"""
		
		# If we do not already have the Widget wrapped in a wrappedWidget,
		# do so now.
		if which not in self.widgets:
			widget = self.gui.getWidgetByName(which)
			
			self.log.info("Fetched widget %s [%s]" % (which, which.__class__))
			
			if widget is None:
				raise WidgetDoesNotExist, which
			
			wrapped = wrappedWidget(widget, which)
			self.setWidget(which, wrapped)
		
		# Return the wrappedWidget object.
		return self.widgets[which]
		
	def setWidget(self, which, value):
		self.widgets[which]=value

# Create the 'sbwidgets' instance.
if not globals().has_key("sbwidgets"):
	sbwidgets = widgetRegistry()