atEditor.py 17.4 KB
Newer Older
1
# addWindow.py - UI code for adding an at record
2 3
# Copyright (C) 2004, 2005 Philip Van Hoof <me at freax dot org>
# Copyright (C) 2004, 2005 Gaute Hope <eg at gaute dot eu dot org>
4
# Copyright (C) 2004, 2005 Kristof Vansant <de_lupus at pandora dot be>
5 6 7 8 9 10 11 12 13 14 15 16 17 18 19

# 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 Library General Public License for more details.
#
# You should have received a copy of the GNU General Public License
# along with this program; if not, write to the Free Software
# Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.

20
#pygtk modules
21
import gtk
22 23 24 25
import gobject

#python modules
import re
26
import time
27
import calendar
28 29

#custom modules
30
import preset
31

32 33 34
##
## I18N
##
35
import gettext
36
domain = 'gnome-schedule'
37 38 39
gettext.bindtextdomain(domain)
gettext.textdomain(domain)
_ = gettext.gettext
40

41

Kristof Vansant's avatar
Kristof Vansant committed
42
class AtEditor:
43
	def __init__(self, parent, backend, scheduler,defaultIcon):
44 45
		self.ParentClass = parent
		self.xml = self.ParentClass.xml
46 47 48
		self.backend = backend
		self.scheduler = scheduler
		
Kristof Vansant's avatar
Kristof Vansant committed
49

50 51
		self.widget = self.xml.get_widget("atEditor")
		self.widget.connect("delete-event", self.on_cancel_button_clicked)
Kristof Vansant's avatar
Kristof Vansant committed
52
		
53
		self.fieldRegex = re.compile('^(\*)$|^([0-9]+)$|^\*\\\([0-9]+)$|^([0-9]+)-([0-9]+)$|(^([0-9]+[,])+([0-9]+)$)')
54
		self.nooutputRegex = re.compile('([^#\n$]*)>(\s|)/dev/null\s2>&1')
55
		
56
		self.defaultIcon = defaultIcon
57
		
58
		#self.editing = False
59 60 61
		self.noevents = False
		self.NOACTION = False	#getting alot of these now.. this is for abosultely noactions of the syncing and templates stuff
		self.noupdate = False
Kristof Vansant's avatar
Kristof Vansant committed
62 63
		self.template_combobox_model = None
		self.first = 0
64
		self.combo_trigger = False
65
	
66
		self.save_button = self.xml.get_widget ("at_save_button")
67
		self.remove_button = self.xml.get_widget ("at_delete_button")
68 69
		self.title_entry = self.xml.get_widget ("at_title_entry")
		self.script_textview = self.xml.get_widget ("at_script_textview")
70
		self.script_textview_buffer = self.script_textview.get_buffer()
71

72 73 74 75
		self.help_button = self.xml.get_widget ("at_help_button")
		self.cancel_button = self.xml.get_widget ("at_cancel_button")
		self.ok_button = self.xml.get_widget ("at_ok_button")
		self.image_button = self.xml.get_widget ("at_image_button")
Kristof Vansant's avatar
Kristof Vansant committed
76 77

		##template combobox config		
78
		self.template_combobox = self.xml.get_widget ("at_template_combobox")
Kristof Vansant's avatar
Kristof Vansant committed
79 80 81 82
		self.template_combobox_model = gtk.ListStore(gobject.TYPE_STRING, gobject.TYPE_STRING, gobject.TYPE_PYOBJECT)
		self.template_combobox.set_model (self.template_combobox_model)
		##
		
83
		self.template_image = self.xml.get_widget ("at_template_image")
Philip Van Hoof's avatar
Philip Van Hoof committed
84
		self.template_image_size = gtk.icon_size_register ("at_template_image_size", 60, 60)
85
		self.template_label = self.xml.get_widget ("at_template_label")
Kristof Vansant's avatar
Kristof Vansant committed
86
		
87 88
		self.calendar = self.xml.get_widget ("at_calendar")
		self.hour_spinbutton = self.xml.get_widget ("at_hour_spinbutton")
89 90 91 92 93
		self.minute_spinbutton = self.xml.get_widget ("at_minute_spinbutton")
		self.time_separator = self.xml.get_widget ("at_time_separator")
		# Translators: Separator between hour and minute entry fields
		self.time_separator.set_label (_(":"))

94
		self.combobox = self.xml.get_widget ("at_combobox")
95
		self.combobox_entry = self.combobox.get_child()	
96
			
97
		self.template_combobox.get_child().connect ("changed", self.on_template_combobox_entry_changed)
98 99 100
		self.xml.signal_connect("on_at_help_button_clicked", self.on_help_button_clicked)
		self.xml.signal_connect("on_at_cancel_button_clicked", self.on_cancel_button_clicked)
		self.xml.signal_connect("on_at_ok_button_clicked", self.on_ok_button_clicked)
101

102
		self.xml.signal_connect("on_at_script_textview_popup_menu", self.on_script_textview_popup_menu)
103
		self.xml.signal_connect("on_at_script_textview_key_release_event", self.on_script_textview_change)
104 105
		self.xml.signal_connect("on_at_template_combobox_changed", self.on_template_combobox_changed)
		self.xml.signal_connect("on_at_title_entry_changed", self.on_title_entry_changed)
106

107 108 109 110 111
		self.xml.signal_connect("on_at_save_button_clicked", self.on_save_button_clicked)
		self.xml.signal_connect("on_at_delete_button_clicked", self.on_delete_button_clicked)
		self.xml.signal_connect("on_at_image_button_clicked", self.on_image_button_clicked)
		self.xml.signal_connect("on_at_calendar_day_selected", self.on_calendar_day_selected)
		self.xml.signal_connect("on_at_calendar_month_changed", self.on_calendar_month_changed)
112
		self.xml.signal_connect("on_at_calendar_year_changed", self.on_calendar_year_changed)
113 114 115
		self.xml.signal_connect("on_at_hour_spinbutton_changed", self.on_hour_spinbutton_changed)
		self.xml.signal_connect("on_at_minute_spinbutton_changed", self.on_minute_spinbutton_changed)
		self.xml.signal_connect("on_at_combobox_changed", self.on_combobox_changed)
116

117
		self.backend.add_scheduler_type("at")
118 119

	def showadd (self, mode):
120
		self.NOACTION = True
121 122
		self.__reset__ ()
		self.title = _("Untitled")
123
		self.editing = False
124
		self.widget.set_title(_("Create a new scheduled task"))
125 126
		self.widget.set_transient_for(self.ParentClass.widget)
		self.widget.set_position(gtk.WIN_POS_CENTER_ON_PARENT)
127 128 129 130
		self.widget.show_all()
		
		self.__loadicon__ ()
		self.__reload_templates__ ()
131
		self.__update_textboxes__()
132
		self.NOACTION = False
133 134

	def showedit (self, record, job_id, iter, mode):
135

136 137
		self.editing = True
		self.NOACTION = True
138

139
		self.job_id = job_id
140 141 142 143 144 145 146
		self.date = self.ParentClass.treemodel.get_value(iter, 9)
		self.time = self.ParentClass.treemodel.get_value(iter, 12)
		self.title = self.ParentClass.treemodel.get_value(iter, 0)
		self.icon = self.ParentClass.treemodel.get_value(iter, 8) 
		self.class_id = self.ParentClass.treemodel.get_value(iter, 9)
		self.user = self.ParentClass.treemodel.get_value(iter, 10)
		self.command = self.ParentClass.treemodel.get_value(iter, 3)
147 148 149 150 151 152 153 154 155 156
		self.runat = self.time + " " + self.date
		#parse 	
		(hour, minute, day, month, year) = self.__parse_time__(self.time, self.date)
		self.calendar.select_month(int(month) - 1, int(year))
		self.calendar.select_day(int(day))
		self.hour_spinbutton.set_text(hour)
		self.minute_spinbutton.set_text(minute)
		self.widget.set_title(_("Edit a scheduled task"))
		self.__update_textboxes__ ()
		self.parentiter = iter
157 158
		self.widget.set_transient_for(self.ParentClass.widget)
		self.widget.set_position(gtk.WIN_POS_CENTER_ON_PARENT)
159
		self.widget.show ()
160
		self.__reload_templates__ ()
161
		self.NOACTION = False
162

163
	def on_worded_label_event (self, *args):
Kristof Vansant's avatar
Kristof Vansant committed
164
		#TODO highlight on mouseover
165 166 167
		pass

	def on_defined_label_event (self, *args):
Kristof Vansant's avatar
Kristof Vansant committed
168
		#TODO highlight on mouseover
169 170 171 172
		# enable control_option on click
		pass

	def on_script_textview_popup_menu (self, *args):
Kristof Vansant's avatar
Kristof Vansant committed
173
		#TODO show at_script_menuons: install t
174 175
		# don't forget to attach eventhandling to this popup
		pass
176 177 178 179 180
	
	def on_script_textview_change (self, *args):
		start = self.script_textview_buffer.get_start_iter()
		end = self.script_textview_buffer.get_end_iter()
		self.command = self.script_textview_buffer.get_text(start, end)
181

182 183

	def on_title_entry_changed (self, *args):
184
		self.title = self.title_entry.get_text()
185

186
	def on_calendar_day_selected (self, *args):		
187
		self.__update_time_cal__()
188 189

	def on_calendar_month_changed (self, *args):
190
		self.__update_time_cal__()
191 192
	
	def on_calendar_year_changed (self, *args):
193
		self.__update_time_cal__()
194

195
	def on_hour_spinbutton_changed (self, *args):
196
		self.__update_time_cal__()
197 198

	def on_minute_spinbutton_changed (self, *args):
199 200
		self.__update_time_cal__()

201
	
202
	def __update_time_cal__ (self):
203
		if self.NOACTION != True:
204 205 206
			(year, month, day) = self.calendar.get_date()
			hour = self.hour_spinbutton.get_text()
			minute = self.minute_spinbutton.get_text()
207 208 209 210 211 212
			month = month + 1 #months start at 0
			year = str(year)
			if hour:
				hour = int(hour)
			else:
				return
213

214 215 216 217 218 219 220
			if minute:
				minute = int(minute)
			else:
				return
	
			if hour < 10:
				hour = "0" + str(hour)
221 222
			else:
				hour = str(hour)
223 224 225
	
			if minute < 10:
				minute = "0" + str(minute)
226 227
			else:
				minute = str(minute)
228 229 230 231 232 233 234 235 236 237 238 239
			
			if month < 10:
				month = "0" + str(month)
			else:
				month = str(month)

			if day < 10:
				day = "0" + str(day)
			else:
				day = str(day)

			self.runat = hour + ":" + minute + " " + year + "-" + month + "-" + day
240 241
			self.noupdate = True
			if self.combo_trigger == False:
242
				self.__update_textboxes__()
243

244
			self.noupdate = False
245

246

247
	def __update_time_combo__ (self):
248
		if self.NOACTION != True:
249

Gaute Hope's avatar
Gaute Hope committed
250
			#update variables, set calendar
251 252
			runat = self.combobox_entry.get_text ()
			self.runat = runat
253 254 255
			regexp = re.compile("([0-9][0-9]):([0-9][0-9])\ ([0-9][0-9][0-9][0-9])-([0-9][0-9])-([0-9][0-9])")
			runat_g = regexp.match(self.runat)
			if runat_g:
256

257 258 259 260 261 262 263 264
				(hour, minute, year, month, day) =  runat_g.groups()
				year = int(year)
				month = int(month)
				day = int(day)
				self.calendar.select_month(month - 1, year)
				self.calendar.select_day(day)
				self.hour_spinbutton.set_text(hour)
				self.minute_spinbutton.set_text(minute)
265

266
			self.__update_textboxes__ (0)
267

Kristof Vansant's avatar
Kristof Vansant committed
268
				
269
	def on_combobox_changed (self, *args):
270 271 272
		if self.NOACTION != True:
			if self.noupdate == False:	
				self.combo_trigger = True
273
				self.__update_time_combo__()
274
				self.combo_trigger = False
275

276

277 278
	def template_doesnot_exist (self, message):
		box = gtk.MessageDialog(self.widget, gtk.DIALOG_MODAL, gtk.MESSAGE_INFO, gtk.BUTTONS_OK, message)
279
		box.set_response_sensitive(gtk.RESPONSE_OK, True)
280 281 282 283
		run = box.run()
		box.hide()

			
284
	def on_delete_button_clicked (self, *args):
285 286 287 288 289
		firstiter = self.template_combobox_model.get_iter_first()
		notemplate = self.template_combobox_model.get_value(firstiter,0)
		entry = self.template_combobox.get_child().get_text()
		if notemplate != entry:
			iter = self.template_combobox.get_active_iter ()
290 291 292 293
			if iter != None:
				template = self.template_combobox_model.get_value(iter, 2)
				icon_uri, command, timeexpression, title, name = template
				self.template_combobox.set_active (0)
294
				self.backend.removetemplate ("at", name)
295
			else: 
296
				self.template_doesnot_exist("The preset has not been saved")
297
		else:
Philip Van Hoof's avatar
Philip Van Hoof committed
298
			self.template_doesnot_exist("To delete a preset, you first need to select one")
Kristof Vansant's avatar
Kristof Vansant committed
299

300

301 302
	def on_save_button_clicked (self, *args):
		# Uses SaveTemplate (will call it if OK is pressed)
303 304 305 306 307
		firstiter = self.template_combobox_model.get_iter_first()
		notemplate = self.template_combobox_model.get_value(firstiter,0)
		entry = self.template_combobox.get_child().get_text()
		if notemplate != entry:
			self.__SaveTemplate__ (self.template_combobox.get_child().get_text())
308
		else:
Philip Van Hoof's avatar
Philip Van Hoof committed
309
			self.template_doesnot_exist("To save a preset, you first have to choose a name for it")
310
		
311
		
312
	def __SaveTemplate__ (self, template_name):
313
		#TODO: validate record
314
		self.backend.savetemplate ("at", template_name, self.runat, self.title, self.icon, self.command)
315
			
316
	
317
	def __reload_templates__ (self):
318

319
		self.template_names = self.backend.gettemplatenames ("at")
320
		if not (self.template_names == None or len (self.template_names) <= 0):
321 322 323 324 325
			active = self.template_combobox.get_active ()
			if active == -1:
				active = 0
	
		self.template_combobox_model.clear ()
326
		self.template_combobox_model.append ([_("Don't use a preset"), None, None])
327
		
328 329 330

		if self.template_names == None or len (self.template_names) <= 0:
			active = 0
331 332
			# self.remove_button.set_sensitive (False)
			# self.save_button.set_sensitive (False)
333 334 335 336
			self.template_combobox.set_active (0)
		else:
			
			for template_name in self.template_names:
337
				thetemplate = self.backend.gettemplate ("at",template_name)
338 339 340 341 342 343
				icon_uri, command, runat, title, name  = thetemplate
				#print "icon_uri: " + icon_uri
				#print "command: " + command
				#print "runat: " + runat
				#print "title: " + title
				#print "name: " + name
344 345
				self.template_combobox_model.append([name, template_name, thetemplate])
						
346
			# self.remove_button.set_sensitive (True)
Kristof Vansant's avatar
Kristof Vansant committed
347
			
348
		self.template_combobox.set_active (active)
349
		
350

351
	def on_template_combobox_entry_changed (self, widget):
352
		if self.NOACTION != True:
353 354 355
			firstiter = self.template_combobox_model.get_iter_first()
			notemplate = self.template_combobox_model.get_value(firstiter,0)
			entry = self.template_combobox.get_child().get_text()
356
			# if notemplate != entry:
357
				# self.save_button.set_sensitive (True)
358
			# else:
359
				# self.save_button.set_sensitive (False)
360 361 362
	

	def on_template_combobox_changed (self, *args):
363 364
		if self.NOACTION != True:
			if self.noevents == False:
365 366 367 368 369
				iter = self.template_combobox.get_active_iter ()
				if iter == None:
					return
				template = self.template_combobox_model.get_value(iter, 2)
				if template != None:
370
					# self.remove_button.set_sensitive (True)
371 372
					icon_uri, command, runat, title, name = template
					if icon_uri != None:
Philip Van Hoof's avatar
Philip Van Hoof committed
373 374 375 376 377 378 379
						try:
							pixbuf = gtk.gdk.pixbuf_new_from_file_at_size (icon_uri, 60, 60)
							self.template_image.set_from_pixbuf(pixbuf)
							self.icon = icon_uri
						except gobject.GError:
							self.__loadicon__ ()

380 381 382 383 384 385 386 387 388 389 390 391
					else:
						self.__loadicon__ ()
					self.title = title
					self.command = command
					if runat != None:
						self.runat = runat

						self.__update_textboxes__ ()

						self.on_combobox_changed()
					else:		
						self.__update_textboxes__ ()
392
				else:
393 394
					# self.remove_button.set_sensitive (False)
					# self.save_button.set_sensitive (False)
395
					self.__loadicon__ ()
Kristof Vansant's avatar
Kristof Vansant committed
396

397 398 399
					self.__reset__ ()
	
	
400 401 402 403 404 405 406 407 408 409 410
	def on_image_button_clicked (self, *args):
		preview = gtk.Image()
		preview.show()
		iconopendialog = gtk.FileChooserDialog(_("Pick an icon for this scheduled task"), self.widget, gtk.FILE_CHOOSER_ACTION_OPEN, (gtk.STOCK_OK, gtk.RESPONSE_ACCEPT, gtk.STOCK_CANCEL, gtk.RESPONSE_REJECT), "")
		# Preview stuff appears to be highly unstable :-(
		# iconopendialog.set_preview_widget(preview)
		# iconopendialog.connect("update-preview", self.update_preview_cb, preview)
		res = iconopendialog.run()
		if res != gtk.RESPONSE_REJECT:
			self.icon = iconopendialog.get_filename()
		iconopendialog.destroy ()
411

412
		self.__update_textboxes__ ()
413 414 415 416 417 418

#	def update_preview_cb(self, file_chooser, preview):
#		filename = file_chooser.get_preview_filename()
#		try:
#			pixbuf = gtk.gdk.pixbuf_new_from_file_at_size(filename, 128, 128)
#			preview.set_from_pixbuf(pixbuf)
419
#			have_preview = True
420
#		except:
421
#			have_preview = False
422 423 424 425
#			file_chooser.set_preview_widget_active(have_preview)
#		return


426
	def __loadicon__ (self):
Philip Van Hoof's avatar
Philip Van Hoof committed
427 428 429 430 431 432 433
		try:
			pixbuf = gtk.gdk.pixbuf_new_from_file_at_size (self.defaultIcon, 60, 60)
			self.icon = self.defaultIcon
		except gobject.GError:
			pixbuf = gtk.Widget.render_icon (self.widget, gtk.STOCK_MISSING_IMAGE, self.template_image_size, None)
			self.icon = ""

434
		self.template_image.set_from_pixbuf(pixbuf)
Kristof Vansant's avatar
Kristof Vansant committed
435

436
	
437
	def __reset__ (self):
438 439
		self.title = "Untitled"
		self.command = ""
440
		self.icon = self.defaultIcon
441

442 443
		ctime = time.gmtime()
		year = ctime[0]
Gaute Hope's avatar
Gaute Hope committed
444
		month = ctime[1]
445
		day = ctime[2]
446 447
		hour = ctime[3]
		minute = ctime[4]
Kristof Vansant's avatar
Kristof Vansant committed
448
		
449 450 451


		firstday, ndays = calendar.monthrange(year,month)
452 453
		
		if day == ndays:
454
			if month != 12:
455 456 457 458
				month = month + 1
			else:
				month = 1
				year = year + 1
459
			day = 1
460
		else:
461 462
			day = day + 1
		
463 464
		self.runat = str(hour) + ":" + str(minute) + " " + str(year) + "-" + str(month) + "-" + str(day)
		self.calendar.select_month(month - 1, year)
465
		
466 467 468
		self.calendar.select_day(day)
		self.hour_spinbutton.set_text(str(hour))
		self.minute_spinbutton.set_text(str(minute))
469

470
		self.__update_textboxes__ () #update_textboxes inside
471 472
		

473
	def __update_textboxes__(self, update_runat = 1):
474

475
		self.noevents = True
476 477 478
		if self.title == None:
			self.title = "Untitled"

479 480
		self.title_entry.set_text(self.title)
		self.script_textview_buffer.set_text(self.command)
481
		if update_runat:
482 483
			if self.combobox_entry.get_text() != self.runat:
				self.combobox_entry.set_text(self.runat)
484

485
		if self.icon != None:
Philip Van Hoof's avatar
Philip Van Hoof committed
486 487 488 489 490
			try:
				pixbuf = gtk.gdk.pixbuf_new_from_file_at_size (self.icon, 60, 60)
				self.template_image.set_from_pixbuf(pixbuf)
			except gobject.GError:
				self.__loadicon__ ()
Gaute Hope's avatar
Gaute Hope committed
491

492
		else:
493
			self.__loadicon__ ()
494

495
		self.noevents = False
496 497


498
	def __parse_time__ (self, time, date):
499 500
		regexp_date = re.compile("([0-9][0-9][0-9][0-9])-([0-9][0-9])-([0-9][0-9])")
		regexp_time = re.compile("([0-9][0-9]):([0-9][0-9])")
501 502 503 504 505 506 507 508 509 510

		time_g = regexp_time.match(time)
		if time_g:
			(hour, minute) = time_g.groups()

		date_g = regexp_date.match(date)
		if date_g:
			(year, month, day) = date_g.groups()	
		
		return hour, minute, day, month, year
511

Kristof Vansant's avatar
Kristof Vansant committed
512

513
	def on_help_button_clicked (self, *args):
514 515 516 517 518 519
		help_page = "file://" + config.getDocdir() + "/addingandediting.html"
		path = config.getGnomehelpbin ()
		pid = os.fork()
		if not pid:
			os.execv(path, [path, help_page])

Kristof Vansant's avatar
Kristof Vansant committed
520

521 522
	def on_cancel_button_clicked (self, *args):
		self.widget.hide()
523
		#return True
524

Kristof Vansant's avatar
Kristof Vansant committed
525

526
	def __WrongRecordDialog__ (self, x):
527
		self.wrongdialog = gtk.MessageDialog(self.widget, gtk.DIALOG_MODAL|gtk.DIALOG_DESTROY_WITH_PARENT, gtk.MESSAGE_ERROR, gtk.BUTTONS_OK, (_("This is an invalid record! The problem could be: %s") % (x)))
528 529 530
		self.wrongdialog.run()
		self.wrongdialog.destroy()

Kristof Vansant's avatar
Kristof Vansant committed
531

532
	def on_ok_button_clicked (self, *args):
533
		(validate, reason) = self.scheduler.checkfield(self.runat)
534
		if validate == False:
535
			self.__WrongRecordDialog__ (reason)
Gaute Hope's avatar
Gaute Hope committed
536
			return
537
		# TODO: Fill record
538
		
539
		if self.editing != False:
540
			self.scheduler.update (self.job_id, self.runat, self.command, self.title, self.icon)
541
		else:
542
			self.scheduler.append (self.runat, self.command, self.title, self.icon)
543
		
544
		self.ParentClass.schedule_reload ("at")
Kristof Vansant's avatar
Kristof Vansant committed
545
			
546
		self.widget.hide ()