def load():
def save():
def _test(): def load(path): def load(path): def init(path=None, allow=['*'], deny=[], ssl=False):
def commit(path=None):
def clone(src=None, dest=None, user=None, pswd=None):
def make_dest_url(dest, user, pswd):
def pull(src=None, dest=None, user=None, pswd=None):
def push(src=None, dest=None, user=None, pswd=None): class Chart(ChartCalc):
	"""Chart object including display and drawing helpers."""
	
	__slots__ = ChartCalc.__slots__ + ['_hidden']
	
	def _get_hidden(self):
		"""Get hidden flag.
		
		:rtype: bool
		"""
		return self._hidden
	
	def _set_hidden(self, boolean):
		"""Set hidden flag.
		
		:type boolean: bool
		"""
		self._hidden = bool(boolean)
	
	# from ChartFile
	path = ChartCalc.path
	name = ChartCalc.name
	datetime = ChartCalc.datetime
	calendar = ChartCalc.calendar
	location = ChartCalc.location
	latitude = ChartCalc.latitude
	longitude = ChartCalc.longitude
	altitude = ChartCalc.altitude
	country = ChartCalc.country
	zoneinfo = ChartCalc.zoneinfo
	timezone = ChartCalc.timezone
	comment = ChartCalc.comment
	keywords = ChartCalc.keywords
	dst = ChartCalc.dst
	utcoffset = ChartCalc.utcoffset
	# from ChartDate
	julday = ChartCalc.julday
	local_datetime = ChartCalc.local_datetime
	utc_datetime = ChartCalc.utc_datetime
	local_mean_datetime = ChartCalc.local_mean_datetime
	sidtime = ChartCalc.sidtime
	local_sidtime = ChartCalc.local_sidtime
	# from ChartCalc
	filter = ChartCalc.filter
	ecl_nut = ChartCalc.ecl_nut
	planets = ChartCalc.planets
	houses = ChartCalc.houses
	aspects = ChartCalc.aspects
	midpoints = ChartCalc.midpoints
	midp_aspects = ChartCalc.midp_aspects
	# Additional properties
	hidden = property(_get_hidden, _set_hidden,
		doc='Chart hidden state flag.')
	
	def __init__(self, path=None, set_default=True, do_calc=True):
		ChartCalc.__init__(self, path, set_default, do_calc)
		self._hidden = False
	
	def hide(self):
		"""Set hidden flag to True."""
		self._hidden = True
	
	def show(self):
		"""Set hidden flag to False."""
		self._hidden = False
	
	def _all_draw_planets(self):
		"""Return a list of all points to draw (incl. midpoints).
		
		:rtype: PlanetDataList
		"""
		ret = PlanetDataList()
		ret.extend(self._planets)
		if self._filter._draw_midp:
			ret.extend(self._midp_aspects.get_midpoints())
		return ret
	
	def _all_draw_aspects(self):
		"""Return a list of all aspects to draw (incl. midpoints).
		
		:rtype: AspectDataList
		"""
		ret = AspectDataList()
		ret.extend(self._aspects)
		if self._filter._draw_midp:
			ret.extend(self._midp_aspects)
		return ret
	
	def __iter__(self):
		"""Iterate over chart properties.
		
		:rtype: generator
		"""
		return (x for x in (self._path, self._name, self._datetime,
			self._calendar, self._location, self._latitude, self._longitude,
			self._altitude, self._country, self._zoneinfo, self._timezone,
			self._comment, self._keywords, self._dst, self._utcoffset,
			self._julday, self._local_datetime, self._utc_datetime,
			self._local_mean_datetime, self._sidtime, self._local_sidtime,
			self._filter, self._ecl_nut, self._houses, self._planets,
			self._aspects, self._midpoints, self._midp_aspects, self._hidden))
	
	def __repr__(self):
		if self._path != None:
			return "Chart('''%s''')" % self._path
		else:
			return repr(tuple(repr(x) for x in self)) class Filter(db.Object):
	"""Filters set and options."""
	
	__slots__ = ('_idx_', '_name', '_bg_color', '_ephe_type', '_ephe_path',
		'_hsys', '_sid_mode', '_sid_t0', '_sid_ayan_t0', '_true_pos',
		'_xcentric', '_calc_midp', '_draw_midp', '_comment',
		# sub-filters:
		'_planets', '_aspects', '_orbs', '_asprestr', '_orbrestr',
		'_midpoints')
	
	def _get_name(self):
		"""Get filter name.
		
		:rtype: str
		"""
		return self._name
	
	def _set_name(self, name):
		"""Set filter name.
		
		:type name: str
		"""
		self._name = name
	
	def _get_bg_color(self):
		"""Get background color.
		
		:rtype: str
		"""
		return self._bg_color
	
	def _set_bg_color(self, clr):
		"""Set background color ('black', 'white').
		
		:type clr: str
		:raise ValueError: invalid color
		"""
		if clr not in ('black', 'white'):
			raise ValueError('Invalid background color %s.' % clr)
		self._bg_color = clr
	
	def _get_ephe_type(self):
		"""Get ephemeris type.
		
		:rtype: str
		"""
		return self._ephe_type
	
	def _set_ephe_type(self, ephe='swiss'):
		"""Set ephemeris type ('swiss', 'jpl', 'moshier').
		
		:type ephe: str
		:raise ValueError: invalid ephemeris type
		"""
		if ephe not in ('swiss', 'jpl', 'moshier'):
			raise ValueError('Invalid ephemeris type %s.' % ephe)
		self._ephe_type = ephe
	
	def _get_ephe_path(self):
		"""Get ephemeris files directory (or file for jpl) path.
		
		:rtype: str
		"""
		return self._ephe_path
	
	def _set_ephe_path(self, path):
		"""Set ephemeris directory/file.
		
		:type path: str
		"""
		self._ephe_path = str(path) # str() till pyswisseph accepts unicode!
	
	def _get_hsys(self):
		"""Get house system.
		
		:rtype: str
		"""
		return self._hsys
	
	def _set_hsys(self, hsys='P'):
		"""Set house system.
		
		One in 'PKORCAEVXHTBG'. See swisseph docs.
		
		:type hsys: str
		"""
		if hsys not in 'PKRCBOAEHVXGU':
			raise ValueError('Invalid house system %s.' % hsys)
		self._hsys = str(hsys) ## till pyswisseph accepts unicode
	
	def _get_sid_mode(self):
		return self._sid_mode
	
	def _set_sid_mode(self, sidmode=-1):
		"""Set sidereal mode.
		
			- -1 -> western tropical zodiac
			- 0+ -> sidereal modes
			- 255 -> user-defined sidereal (see sid_t0 and sid_ayan_t0)
		
		:type sidmode: int
		:raise ValueError: invalid sidereal mode
		"""
		if sidmode < -1 or sidmode > 255:
			raise ValueError('Invalid sidereal mode %s.' % sidmode)
		self._sid_mode = sidmode
	
	def _get_sid_t0(self):
		"""Get sidereal time 0.
		
		:rtype: float
		"""
		return self._sid_t0
	
	def _set_sid_t0(self, sidt0):
		"""Set sidereal time 0.
		
		:type sidt0: numeric
		"""
		self._sid_t0 = float(sidt0)
	
	def _get_sid_ayan_t0(self):
		"""Get sidereal ayanamsa at t0.
		
		:rtype: float
		"""
		return self._sid_ayan_t0
	
	def _set_sid_ayan_t0(self, ayan_t0):
		"""Set sidereal ayanamsa at t0.
		
		:type ayan_t0: numeric
		"""
		self._sid_ayan_t0 = float(ayan_t0)
	
	def _get_true_pos(self):
		"""Get true positions flag.
		
		:rtype: bool
		"""
		return self._true_pos
	
	def _set_true_pos(self, boolean=False):
		"""Set true positions flag
		
		:type boolean: bool
		"""
		self._true_pos = bool(boolean)
	
	def _get_xcentric(self):
		"""Get observer mode.
		
		:rtype: str
		"""
		return self._xcentric
	
	def _set_xcentric(self, xcentric='geo'):
		"""Set geo/topo/helio/bary centric mode.
		
		:type xcentric: str
		:raise ValueError: invalid mode
		"""
		if xcentric not in ('geo', 'topo', 'helio', 'bary'):
			raise ValueError('Invalid xcentric mode %s.' % xcentric)
		self._xcentric = xcentric
	
	def _get_calc_midp(self):
		"""Calculate mid-points or not (boolean).
		
		:rtype: bool
		"""
		return self._calc_midp
	
	def _set_calc_midp(self, boolean):
		"""Set midpoints flag.
		
		:type boolean: bool
		"""
		if boolean in (True, 1, 'True', '1', 'yes'):
			self._calc_midp = True
		else:
			self._calc_midp = False
	
	def _get_draw_midp(self):
		"""Draw mid-points aspects or not (boolean).
		
		:rtype: bool
		"""
		return self._draw_midp
	
	def _set_draw_midp(self, boolean):
		"""Set mid-points drawing flag.
		
		:type boolean: bool
		"""
		if boolean in (True, 1, 'True', '1', 'yes'):
			self._draw_midp = True
		else:
			self._draw_midp = False
	
	def _get_comment(self):
		"""Get comment
		
		:rtype: str
		"""
		return self._comment
	
	def _set_comment(self, comment):
		"""Set comment.
		
		:type comment: str
		"""
		self._comment = comment
	
	# sub filters
	
	def _get_planets(self):
		"""Get planets filter.
		
		:rtype: PlanetsFilter
		"""
		return self._planets
	
	def _set_planets(self, filt):
		"""Set planets filter.
		
		:type filt: PlanetsFilter, str or int
		"""
		if not isinstance(filt, PlanetsFilter):
			self._planets = PlanetsFilter(filt)
		else:
			self._planets = filt
	
	def _get_aspects(self):
		"""Get aspects filter.
		
		:rtype: AspectsFilter
		"""
		return self._aspects
	
	def _set_aspects(self, filt):
		"""Set aspects filter.
		
		:type filt: AspectsFilter, str or int
		"""
		if not isinstance(filt, AspectsFilter):
			self._aspects = AspectsFilter(filt)
		else:
			self._aspects = filt
	
	def _get_orbs(self):
		"""Get orbs filter.
		
		:rtype: OrbsFilter
		"""
		return self._orbs
	
	def _set_orbs(self, filt):
		"""Set orbs filter.
		
		:type filt: OrbsFilter, str or int
		"""
		if not isinstance(filt, OrbsFilter):
			self._orbs = OrbsFilter(filt)
		else:
			self._orbs = filt
	
	def _get_asprestr(self):
		"""Get aspects restrictions.
		
		:rtype: AspectsRestrictions
		"""
		return self._asprestr
	
	def _set_asprestr(self, filt):
		"""Set aspects restrictions.
		
		:type filt: AspectsRestrictions, str or int
		"""
		if not isinstance(filt, AspectsRestrictions):
			self._asprestr = AspectsRestrictions(filt)
		else:
			self._asprestr = filt
	
	def _get_orbrestr(self):
		"""Get orbs restrictions.
		
		:rtype: OrbsRestrictions
		"""
		return self._orbrestr
	
	def _set_orbrestr(self, filt):
		"""Set orbs restrictions.
		
		:type filt: OrbsRestrictions, str or int
		"""
		if not isinstance(filt, OrbsRestrictions):
			self._orbrestr = OrbsRestrictions(filt)
		else:
			self._orbrestr = filt
	
	def _get_midpoints(self):
		"""Get midpoints filters.
		
		:rtype: MidPointsFilter
		"""
		return self._midpoints
	
	def _set_midpoints(self, filt):
		"""Set mid-points filters.
		
		:type filt: MidPointsFilter, str or int
		"""
		if not isinstance(filt, MidPointsFilter):
			self._midpoints = MidPointsFilter(filt)
		else:
			self._midpoints = filt
	
	_idx = property(db.Object._get_idx, db.Object._set_idx,
		doc='Filter db index value.')
	name = property(_get_name, _set_name,
		doc='Filter name.')
	bg_color = property(_get_bg_color, _set_bg_color,
		doc='Background color (black|white).')
	ephe_type = property(_get_ephe_type, _set_ephe_type,
		doc='Ephemeris type (swiss|jpl|moshier).')
	ephe_path = property(_get_ephe_path, _set_ephe_path,
		doc='Ephemeris directory/files path.')
	hsys = property(_get_hsys, _set_hsys,
		doc='House system (PKORCAEVXHTBG). See swisseph docs.')
	sid_mode = property(_get_sid_mode, _set_sid_mode,
		doc='Sidereal mode (-1=tropical). See swisseph docs.')
	sid_t0 = property(_get_sid_t0, _set_sid_t0,
		doc='Sidereal mode reference time.')
	sid_ayan_t0 = property(_get_sid_ayan_t0, _set_sid_ayan_t0,
		doc='Sidereal mode ayanmsa.')
	true_pos = property(_get_true_pos, _set_true_pos,
		doc='True positions mode (boolean).')
	xcentric = property(_get_xcentric, _set_xcentric,
		doc='geo/topo/helio/bary centric mode.')
	calc_midp = property(_get_calc_midp, _set_calc_midp,
		doc='Calculate mid-points.')
	draw_midp = property(_get_draw_midp, _set_draw_midp,
		doc='Draw mid-points.')
	planets = property(_get_planets, _set_planets,
		doc='Planets filter.')
	aspects = property(_get_aspects, _set_aspects,
		doc='Aspects filter.')
	orbs = property(_get_orbs, _set_orbs,
		doc='Orbs filter.')
	asprestr = property(_get_asprestr, _set_asprestr,
		doc='Aspects restrictions.')
	orbrestr = property(_get_orbrestr, _set_orbrestr,
		doc='Orbs restrictions.')
	midpoints = property(_get_midpoints, _set_midpoints,
		doc='Mid-points filter.')
	comment = property(_get_comment, _set_comment,
		doc='Filter comment.')
	
	def set(self, _idx=None, name=None, bg_color=None, ephe_type=None,
		ephe_path=None, hsys=None, sid_mode=None, sid_t0=None,
		sid_ayan_t0=None, true_pos=None, xcentric=None, calc_midp=None,
		draw_midp=None, planets=None, aspects=None, orbs=None, asprestr=None,
		orbrestr=None, midpoints=None, comment=None):
		"""Set filter properties."""
		if _idx != None:
			self._idx = _idx
		if name != None:
			self.name = name
		if bg_color != None:
			self.bg_color = bg_color
		if ephe_type != None:
			self.ephe_type = ephe_type
		if ephe_path != None:
			self.ephe_path = ephe_path
		if hsys != None:
			self.hsys = hsys
		if sid_mode != None:
			self.sid_mode = sid_mode
		if sid_t0 != None:
			self.sid_t0 = sid_t0
		if sid_ayan_t0 != None:
			self.sid_ayan_t0 = sid_ayan_t0
		if true_pos != None:
			self.true_pos = true_pos
		if xcentric != None:
			self.xcentric = xcentric
		if calc_midp != None:
			self.calc_midp = calc_midp
		if draw_midp != None:
			self.draw_midp = draw_midp
		if planets != None:
			self.planets = planets
		if aspects != None:
			self.aspects = aspects
		if orbs != None:
			self.orbs = orbs
		if asprestr != None:
			self.asprestr = asprestr
		if orbrestr != None:
			self.orbrestr = orbrestr
		if midpoints != None:
			self.midpoints = midpoints
		if comment != None:
			self.comment = comment
	
	def __init__(self, filt=None, set_default=True):
		"""Init filter.
		
		:type filt: str or int
		:type set_default: bool
		"""
		if filt != None:
			return self._select(filt)
		if set_default:
			self.set_default()
	
	def set_default(self):
		"""Set filter to default values."""
		sql = 'select dft_filter from Config;'
		idx = int(db.execute(sql).fetchone()[0])
		self._select_by_idx(idx)
	
	def reset(self):
		"""Reset filter when database is updated.
		
		Filter may have been deleted, then load the default one.
		
		"""
		try:
			self._select_by_idx(self._idx_)
		except ValueError: # filter deleted
			self.set_default()
	
	def _select(self, filt):
		"""Select filter in database.
		
		:raise TypeError: invalid filter
		"""
		if isinstance(filt, int):
			return self._select_by_idx(filt)
		elif isinstance(filt, basestring): ## not py3
			return self._select_by_name(filt)
		raise TypeError('Invalid filter %s.' % filt)
	
	def _select_by_idx(self, idx):
		"""Select filter in database, by idx.
		
		:type idx: int
		:raise ValueError: not found
		"""
		sql = "select * from Filters where _idx = ?;"
		res = db.execute(sql, (idx,)).fetchall()
		if len(res) == 0:
			raise ValueError('Invalid idx %s.' % idx)
		self.set(*res[0])
	
	def _select_by_name(self, filt):
		"""Select filter in database, by name.
		
		:type filt: str
		:raise ValueError: not found
		"""
		sql = "select * from Filters where name = ?;"
		res = db.execute(sql, (filt,)).fetchall()
		if len(res) == 0:
			raise ValueError('Invalid filter %s.' % filt)
		self.set(*res[0])
	
	def save(self, recursive=False):
		"""Save filter in database.
		
		If recursive is True, save all sub-filters.
		Else raise TypeError if a sub-filter is not saved.
		
		:type recursive: bool
		"""
		if self._idx_ == None:
			self._insert(recursive)
		else:
			self._update(recursive)
	
	def _insert(self, recursive=False):
		"""Insert filter in database.
		
		:type recursive: bool
		:raise ValueError: duplicate
		"""
		self._save_filters(recursive)
		sql1 = """insert into Filters (name, bg_color, ephe_type, ephe_path,
			hsys, sid_mode, sid_t0, sid_ayan_t0, true_pos, xcentric, calc_midp,
			draw_midp, planets, aspects, orbs, asprestr, orbrestr, midpoints,
			comment) values (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?,
			?, ?);"""
		var = (self._name, self._bg_color, self._ephe_type, self._ephe_path,
			self._hsys, self._sid_mode, self._sid_t0, self._sid_ayan_t0,
			self._true_pos, self._xcentric, self._calc_midp, self._draw_midp,
			self._planets._idx_, self._aspects._idx_, self._orbs._idx_,
			self._asprestr._idx_, self._orbrestr._idx_, self._midpoints._idx_,
			self._comment)
		sql2 = "select _idx from Filters where name = ?;"
		try:
			db.execute(sql1, var)
		except: # sqlite3.IntegrityError
			raise ValueError('Duplicate filter %s.' % self._name)
		self._idx = db.execute(sql2,(self._name,)).fetchone()[0]
	
	def _update(self, recursive=False):
		"""Update filter in database.
		
		:raise ValueError: duplicate
		"""
		self._save_filters(recursive)
		sql = """update Filters set name = ?, bg_color = ?, ephe_type = ?,
			ephe_path = ?, hsys = ?, sid_mode = ?, sid_t0 = ?, sid_ayan_t0 = ?,
			true_pos = ?, xcentric = ?, calc_midp = ?, draw_midp = ?,
			planets = ?, aspects = ?, orbs = ?, asprestr = ?, orbrestr = ?,
			midpoints = ?, comment = ? where _idx = ?;"""
		var = (self._name, self._bg_color, self._ephe_type, self._ephe_path,
			self._hsys, self._sid_mode, self._sid_t0, self._sid_ayan_t0,
			self._true_pos, self._xcentric, self._calc_midp, self._draw_midp,
			self._planets._idx_, self._aspects._idx_, self._orbs._idx_,
			self._asprestr._idx_, self._orbrestr._idx_, self._midpoints._idx_,
			self._comment, self._idx_)
		try:
			db.execute(sql, var)
		except: ## integrity error
			raise ValueError('Duplicate filter %s.' % self._name)
	
	def _save_filters(self, recursive):
		"""Save sub-filters if recursive is True, else check they are saved.
		
		Raise TypeError if recursive is False and sub-filter is not saved.
		
		:type recursive: bool
		"""
		for f in (self.planets, self.aspects, self.orbs, self.asprestr,
			self.orbrestr, self.midpoints):
			if recursive:
				f.save()
			else:
				if f._idx_ == None:
					raise TypeError('Sub filter %s is not saved.' % f._name)
	
	def delete(self):
		"""Delete filter in database.
		
		:raise TypeError: invalid index
		:raise ValueError: integrity error
		"""
		if self._idx_ == None:
			raise TypeError('Missing idx')
		sql = "delete from Filters where _idx = ?;"
		try:
			db.execute(sql, (self._idx_,))
		except: # sqlite3 integrity error
			raise ValueError('Cannot delete default filter.')
		self._idx_ = None
	
	def get_calcflag(self):
		"""Return swisseph calculation flag.
		
		:rtype: int
		"""
		flag = 0
		# speed
		flag += swe.FLG_SPEED
		# ephemeris type
		if self._ephe_type == 'swiss':
			flag += swe.FLG_SWIEPH
		elif self._ephe_type == 'jpl':
			flag += swe.FLG_JPLEPH
		elif self._ephe_type == 'moshier':
			flag += swe.FLG_MOSEPH
		# sidereal mode
		if self._sid_mode > -1:
			flag += swe.FLG_SIDEREAL
		# true positions
		if self._true_pos:
			flag += swe.FLG_TRUEPOS
		# xcentric
		if self._xcentric == 'topo':
			flag += swe.FLG_TOPOCTR
		elif self._xcentric == 'helio':
			flag += swe.FLG_HELCTR
		elif self._xcentric == 'bary':
			flag += swe.FLG_BARYCTR
		return flag
	
	def __iter__(self):
		"""Iterate over filter internals.
		
		:rtype: iterator
		"""
		return (x for x in (self._idx_, self._name, self._bg_color,
			self._ephe_type, self._ephe_path, self._hsys, self._sid_mode,
			self._sid_t0, self._sid_ayan_t0, self._true_pos, self._xcentric,
			self._calc_midp, self._draw_midp, self._planets, self._aspects,
			self._orbs, self._asprestr, self._orbrestr, self._midpoints,
			self._comment))
	
	def __str__(self):
		"""Show filter internals."""
		return str(tuple(self))
	
	def __repr__(self):
		if self._idx_ == None:
			return repr(tuple(repr(x) for x in self))
		return "Filter('''%s''')" % self._name
	
	def __getstate__(self):
		"""TODO."""
		pass
	
	def _to_xml(self, with_idx=False):
		"""TODO."""
		pass
	
	def _from_xml(self, elem, with_idx=False):
		"""TODO."""
		pass
	
	def __eq__(self, other):
		"""Return True if other is the same filter in database.
		
		:rtype: bool
		"""
		if other == None:
			return False
		if not isinstance(other, Filter):
			return False
		if other._idx_ == self._idx_:
			return True
		return False
	
	def __ne__(self, other):
		"""Return True if other is not the same filter in database.
		
		:rtype: bool
		"""
		if other == None:
			return True
		if not isinstance(other, Filter):
			return True
		if other._idx_ != self._idx_:
			return True
		return False def appendMultiChart(cht):
def insertMultiChart(idx, cht):
def replaceMultiChart(idx, cht):
def removeMultiChart(idx):
def replaceChart(idx, num, cht):
def removeChart(idx, num):
def filterUpdatedEvent(idx):
def planetsFilterUpdatedEvent(idx):
def aspectsFilterUpdatedEvent(idx):
def orbsFilterUpdatedEvent(idx):
def aspectsRestrictionsUpdatedEvent(idx):
def orbsRestrictionsUpdatedEvent(idx):
def midPointsFilterUpdatedEvent(idx): def load(lng, app): def createActions(self):
def reset(self, app): def createMenuBar(self): def createToolBar(self): def makeImage(chart, path, ext, width, height, quality=-1):
def makeSvg(chart, path, width, height): class DockWidget(QDockWidget):
	"""Charts information area."""
	
	def __init__(self, parent):
		QDockWidget.__init__(self, parent.tr('Charts Information'), parent)
		self._parent = parent
		self.setAllowedAreas(Qt.LeftDockWidgetArea|Qt.RightDockWidgetArea)
		self.setMinimumWidth(300)
		# tabs widget
		self.tabs = MultiChartTabWidget(self)
		self.connect(self.tabs, SIGNAL('currentChanged(int)'),
			parent.dockTabChangedEvent)
		self.setWidget(self.tabs)
	
	def addTab(self, idx):
		"""Create tab for a multichart."""
		self.tabs.addTab(idx)
	
	def removeTab(self, idx):
		"""Destroy multichart."""
		self.tabs.removeTab(idx)
	
	def resetTab(self, idx):
		"""Reload multichart."""
		self.tabs.setTabText(idx, app.desktop.charts[idx][0]._name)
		self.tabs.widget(idx).reset() class CentralWidget(QTabWidget):
	
	def __init__(self, parent):
		QTabWidget.__init__(self, parent)
		self.connect(self, SIGNAL('currentChanged(int)'),
			parent.centralTabChangedEvent)
	
	def addTab(self, idx):
		"""Create a new chart tab."""
		tab = ChartWheelWidget(idx)
		QTabWidget.addTab(self, tab, app.desktop.charts[idx][0]._name)
	
	def tabRemoved(self, idx):
		for i in range(len(app.desktop.charts)):
			self.widget(i).resetIdx(i)
		app.mainwin.resetActions()
	
	def tabInserted(self, idx):
		app.mainwin.resetActions()
	
	def resetTab(self, idx):
		"""Reload chart tab."""
		self.setTabText(idx, app.desktop.charts[idx][0]._name)
		self.widget(idx).reset()
	
	def paintEvent(self, event):
		if len(app.desktop.charts) == 0:
			self.drawOroboros()
	
	def drawOroboros(self):
		"""Paint the Oroboros on empty window."""
		painter = QPainter(self)
		im = QImage(os.path.join(_iconsDir, 'oroboros.png'))
		target = QRect(
			(self.width()/2.0)-170, (self.height()/2.0)-170, 340, 340)
		painter.drawImage(target, im, QRect(0, 0, 340, 340)) class ChartInfoDialog(QDialog):
	"""New/edit chart window."""
	
	def __init__(self, idx=-1, num=0, parent=None):
		QDialog.__init__(self, parent)
		self._parent = parent
		tr = self.tr
		idx = int(idx)
		if idx == -1:
			chart = Chart(do_calc=False)
			title = tr('New Chart')
		else:
			try:
				chart = app.desktop.charts[idx][num]
				title = unicode(tr('Edit Chart \xab %(chart)s \xbb')) % {
					'chart': chart._name}
			except IndexError:
				chart = Chart(do_calc=False)
				title = unicode(tr('New SubChart'))
		self._chart = chart
		self._idx = idx
		self._num = num
		self.setWindowTitle(title)
		# set width/height
		self.setMinimumWidth(280)
		self.setMinimumHeight(400)
		self.setSizeGripEnabled(True)
		# layout
		grid = QGridLayout(self)
		self.setLayout(grid)
		# name
		grid.addWidget(QLabel(tr('Name')), 0, 0)
		self.nameEdit = QLineEdit(self)
		grid.addWidget(self.nameEdit, 0, 1)
		# datetime
		lbl = QLabel(tr('DateTime'))
		lbl.setToolTip(tr('Local date & time'))
		grid.addWidget(lbl, 1, 0)
		self.datetimeEdit = QDateTimeEdit(self)
		self.datetimeEdit.setDisplayFormat(tr('yyyy-MM-dd hh:mm:ss',
			'Datetime format'))
		self.datetimeEdit.setButtonSymbols(QAbstractSpinBox.PlusMinus)
		self.datetimeEdit.setCalendarPopup(True)
		self.datetimeEdit.setMinimumDateTime(QDateTime(-5400, 1, 1, 0, 0))
		self.datetimeEdit.setMaximumDateTime(QDateTime(5400, 1, 1, 0, 0))
		grid.addWidget(self.datetimeEdit, 1, 1)
		# calendar
		grid.addWidget(QLabel(tr('Calendar')), 2, 0)
		self.calendarEdit = QComboBox(self)
		self.calendarEdit.addItems([tr('Gregorian'), tr('Julian')])
		self.calendarEdit.setEditable(False)
		grid.addWidget(self.calendarEdit, 2, 1)
		# location
		lbl = QLabel(tr('<a href="http://www.astro.com/atlas">Location</a>'))
		lbl.setOpenExternalLinks(True)
		grid.addWidget(lbl, 3, 0)
		self.locationEdit = QLineEdit(self)
		grid.addWidget(self.locationEdit, 3, 1)
		# country
		grid.addWidget(QLabel(tr('Country')), 4, 0)
		self.countryEdit = QLineEdit(self)
		grid.addWidget(self.countryEdit, 4, 1)
		# latitude
		grid.addWidget(QLabel(tr('Latitude')), 5, 0)
		self.latitudeEdit = LatitudeEdit(chart._latitude, self)
		grid.addLayout(self.latitudeEdit, 5, 1)
		# longitude
		grid.addWidget(QLabel(tr('Longitude')), 6, 0)
		self.longitudeEdit = LongitudeEdit(chart._longitude, self)
		grid.addLayout(self.longitudeEdit, 6, 1)
		# altitude
		grid.addWidget(QLabel(tr('Altitude')), 7, 0)
		geoLayout = QHBoxLayout() ## geolayout
		grid.addLayout(geoLayout, 7, 1)
		self.altitudeEdit = AltitudeEdit(chart._altitude, self)
		geoLayout.addWidget(self.altitudeEdit)
		# query geonames
		geoButton = QToolButton(self)
		geoButton.setIcon(QIcon(os.path.join(_baseDir, 'icons',
			'earth-icon.png')))
		geoButton.setToolTip(tr('Query GeoNames.org'))
		self.connect(geoButton, SIGNAL('clicked()'), self.queryGeoNames)
		geoLayout.addWidget(geoButton)
		# zoneinfo
		lbl = QLabel(tr('<a href="http://en.wikipedia.org/wiki/List_of_zoneinfo_timezones">Zoneinfo</a>'))
		lbl.setToolTip(tr('Posix timezone file (for charts after 1900)'))
		lbl.setOpenExternalLinks(True)
		grid.addWidget(lbl, 8, 0)
		self.zoneinfoEdit = QComboBox(self)
		alltz = pytz.all_timezones[:]
		alltz.insert(0, '')
		self.zoneinfoEdit.addItems(alltz)
		self.zoneinfoEdit.setEditable(False)
		grid.addWidget(self.zoneinfoEdit, 8, 1)
		# dst
		lbl = QLabel(tr('Dst'))
		lbl.setToolTip(tr('Daylight saving time (for ambiguous dates only)'))
		grid.addWidget(lbl, 9, 0)
		self.dstEdit = QComboBox(self)
		self.dstEdit.addItems([tr('Not needed'), tr('Yes'), tr('No')])
		grid.addWidget(self.dstEdit, 9, 1)
		# timezone
		lbl = QLabel(tr('<a href="http://upload.wikimedia.org/wikipedia/en/e/e7/Timezones2008.png">Timezone</a>'))
		lbl.setToolTip(tr('Standard timezone (for local mean time)'))
		lbl.setOpenExternalLinks(True)
		grid.addWidget(lbl, 10, 0)
		self.timezoneEdit = QComboBox(self)
		alltz = [str(x) for x in timezone.all_timezones]
		alltz.insert(0, '')
		self.timezoneEdit.addItems(alltz)
		self.timezoneEdit.setEditable(False)
		grid.addWidget(self.timezoneEdit, 10, 1)
		# utc offset
		lbl = QLabel(tr('Utc offset'))
		lbl.setToolTip(
			tr('Coordinated universal time offset (for charts before 1900)'))
		grid.addWidget(lbl, 11, 0)
		self.utcoffsetEdit = QDoubleSpinBox(self)
		self.utcoffsetEdit.setDecimals(2)
		self.utcoffsetEdit.setMinimum(-25)
		self.utcoffsetEdit.setMaximum(24)
		self.utcoffsetEdit.setSuffix(tr(' h.'))
		self.utcoffsetEdit.setSpecialValueText(tr('Not needed'))
		self.utcoffsetEdit.setButtonSymbols(QAbstractSpinBox.PlusMinus)
		grid.addWidget(self.utcoffsetEdit, 11, 1)
		# comment
		lbl = QLabel(tr('Comment'))
		lbl.setToolTip(tr('Accepts reStructured Text'))
		grid.addWidget(lbl, 12, 0, Qt.AlignTop)
		self.commentEdit = QTextEdit('', self)
		grid.addWidget(self.commentEdit, 12, 1)
		# keywords
		lbl = QLabel(tr('Keywords'))
		lbl.setToolTip(tr('Line-separated "key: word" pairs'))
		grid.addWidget(lbl, 13, 0, Qt.AlignTop)
		self.keywordsEdit = QTextEdit('', self)
		grid.addWidget(self.keywordsEdit, 13, 1)
		# buttons
		buttonsLayout = QHBoxLayout()
		resetButton = QPushButton(tr('Reset'), self)
		self.connect(resetButton, SIGNAL('clicked()'), self.reset)
		buttonsLayout.addWidget(resetButton)
		cancelButton = QPushButton(tr('Cancel'), self)
		self.connect(cancelButton, SIGNAL('clicked()'), self.reject)
		buttonsLayout.addWidget(cancelButton)
		okButton = QPushButton(tr('Ok'), self)
		okButton.setDefault(True)
		self.connect(okButton, SIGNAL('clicked()'), self.accept)
		buttonsLayout.addWidget(okButton)
		grid.addLayout(buttonsLayout, 14, 0, 1, 2)
		# fill entries
		self.reset()
	
	def queryGeoNames(self):
		"""Query GeoNames.org database."""
		ok, res = GeoNamesQueryDialog(self).exec_()
		if ok:
			self.locationEdit.setText(res[0])
			self.countryEdit.setText(res[1])
			self.latitudeEdit.setLatitude(res[2])
			self.longitudeEdit.setLongitude(res[3])
			self.altitudeEdit.setAltitude(res[4])
			self.zoneinfoEdit.setCurrentIndex(
				pytz.all_timezones.index(res[5]) + 1)
	
	def reset(self):
		"""Set entries with original values."""
		# name
		self.nameEdit.setText(self._chart._name)
		# datetime
		dt = self._chart._datetime
		dt = QDateTime(dt.year, dt.month, dt.day, dt.hour, dt.minute, dt.second)
		self.datetimeEdit.setDateTime(dt)
		# calendar
		if self._chart._calendar == 'gregorian':
			self.calendarEdit.setCurrentIndex(0)
		else:
			self.calendarEdit.setCurrentIndex(1)
		# location
		self.locationEdit.setText(self._chart._location)
		# country
		self.countryEdit.setText(self._chart._country)
		# latitude
		self.latitudeEdit.setLatitude(self._chart._latitude)
		# longitude
		self.longitudeEdit.setLongitude(self._chart._longitude)
		# altitude
		self.altitudeEdit.setAltitude(self._chart._altitude)
		# zoneinfo
		if self._chart._zoneinfo not in (None, ''):
			self.zoneinfoEdit.setCurrentIndex(
				pytz.all_timezones.index(self._chart._zoneinfo) + 1)
		else:
			self.zoneinfoEdit.setCurrentIndex(0)
		# dst
		if self._chart._dst == None:
			self.dstEdit.setCurrentIndex(0)
		elif self._chart._dst == True:
			self.dstEdit.setCurrentIndex(1)
		else:
			self.dstEdit.setCurrentIndex(2)
		# timezone
		if self._chart._timezone not in (None, ''):
			self.timezoneEdit.setCurrentIndex(
				timezone.all_timezones.index(
					timezone.get(self._chart._timezone.utc)) + 1)
		else:
			self.timezoneEdit.setCurrentIndex(0)
		# utc offset
		if self._chart._utcoffset != None:
			self.utcoffsetEdit.setValue(self._chart._utcoffset)
		else:
			self.utcoffsetEdit.setValue(-25)
		# comment
		self.commentEdit.setText(self._chart._comment)
		# keywords
		kw = str(self._chart._keywords).replace(';', '\n')
		self.keywordsEdit.setText(kw)
	
	def accept(self):
		tr = self.tr
		# name
		name = unicode(self.nameEdit.text())
		if name == '':
			QMessageBox.critical(self, tr('Missing Name'),
				tr('Please set chart name.'))
			self.nameEdit.setFocus()
			return
		# datetime
		dt = self.datetimeEdit.dateTime().toPyDateTime()
		# calendar
		if self.calendarEdit.currentIndex() == 0:
			cal = 'gregorian'
		else:
			cal = 'julian'
		# location
		loc = unicode(self.locationEdit.text())
		# country
		cty = unicode(self.countryEdit.text())
		# latitude
		lat = self.latitudeEdit.latitude()
		# longitude
		lon = self.longitudeEdit.longitude()
		# altitude
		alt = self.altitudeEdit.altitude()
		# zoneinfo
		tz = self.zoneinfoEdit.currentIndex()
		if tz == 0:
			tz = ''
		else:
			tz = pytz.all_timezones[tz - 1]
		# dst
		dst = self.dstEdit.currentIndex()
		if dst == 0: ## not needed
			dst = ''
		elif dst == 1:
			dst = True
		else:
			dst = False
		# timezone
		tzone = self.timezoneEdit.currentIndex()
		if tzone == 0:
			tzone = ''
		else:
			tzone = timezone.all_timezones[tzone - 1]
		# utcoffset
		utcof = self.utcoffsetEdit.value()
		if utcof == -25: ## not needed
			utcof = ''
		# comment
		cmt = unicode(self.commentEdit.toPlainText())
		# keywords
		kw = unicode(self.keywordsEdit.toPlainText()).replace('\n', ';')
		# set chart
		self._chart.set(name=name, datetime=dt, calendar=cal, location=loc,
			country=cty, latitude=lat, longitude=lon, altitude=alt, zoneinfo=tz,
			dst=dst, utcoffset=utcof, timezone=tzone, comment=cmt, keywords=kw)
		# check ambiguous datetime
		try:
			self._chart.local_datetime
		except TypeError: ## unable to get local time
			QMessageBox.critical(self, tr('Ambiguous datetime!'),
				tr('Please set DST.'))
			self.dstEdit.setFocus()
			return
		# recalc and close
		self._chart.calc()
		if __name__ != '__main__': ## update app
			if self._idx == -1:
				app.appendMultiChart(self._chart)
			else:
				app.replaceChart(self._idx, self._num, self._chart)
		# done
		self.done(QDialog.Accepted) class NewTabDialog(QDialog):
	
	def __init__(self, parent=None):
		QDialog.__init__(self, parent)
		tr = self.tr
		self.setWindowTitle(tr('New Chart'))
		self.setSizeGripEnabled(True)
		# layout
		layout = QVBoxLayout(self)
		self.setLayout(layout)
		# button group
		self.grpButtons = QButtonGroup(self)
		# here-now button
		hereNowButton = QRadioButton(tr('Here-Now Chart'), self)
		hereNowButton.setChecked(True)
		self.grpButtons.addButton(hereNowButton, 0)
		layout.addWidget(hereNowButton)
		# open chart
		openButton = QRadioButton(tr('Open File'), self)
		self.grpButtons.addButton(openButton, 1)
		layout.addWidget(openButton)
		# custom chart
		customButton = QRadioButton(tr('Custom Chart'), self)
		self.grpButtons.addButton(customButton, 2)
		layout.addWidget(customButton)
		# open astrolog32
		openA32Button = QRadioButton(tr('Open Astrolog32'), self)
		self.grpButtons.addButton(openA32Button, 3)
		layout.addWidget(openA32Button)
		# open skif
		openSkifButton = QRadioButton(tr('Open Skif'), self)
		self.grpButtons.addButton(openSkifButton, 4)
		layout.addWidget(openSkifButton)
		# dialog buttons
		bLayout = QHBoxLayout()
		layout.addLayout(bLayout)
		cancelButton = QPushButton(tr('Cancel'), self)
		self.connect(cancelButton, SIGNAL('clicked()'), self.reject)
		bLayout.addWidget(cancelButton)
		okButton = QPushButton(tr('OK'), self)
		okButton.setDefault(True)
		self.connect(okButton, SIGNAL('clicked()'), self.accept)
		bLayout.addWidget(okButton)
	
	def exec_(self):
		ok = QDialog.exec_(self)
		if ok:
			return ok, self.grpButtons.checkedId()
		else:
			return ok, None class SaveImageDialog(QDialog):
	
	def __init__(self, parent=None):
		QDialog.__init__(self, parent)
		tr = self.tr
		self.setWindowTitle(tr('Save Image'))
		self.setSizeGripEnabled(True)
		self.setMinimumWidth(250)
		# layout
		grid = QGridLayout(self)
		self.setLayout(grid)
		# file name
		grid.addWidget(QLabel(tr('File Name')), 0, 0)
		self.fname = QLineEdit(self)
		self.fname.setReadOnly(True)
		grid.addWidget(self.fname, 0, 1)
		# file chooser
		chooseButton = QToolButton(self)
		chooseButton.setIcon(QIcon(os.path.join(_iconsDir, 'gtk-open.png')))
		chooseButton.setToolTip(tr('Get file name'))
		self.connect(chooseButton, SIGNAL('clicked()'), self.getFileName)
		grid.addWidget(chooseButton, 0, 2)
		# extension/format
		grid.addWidget(QLabel(tr('Extension')), 1, 0)
		self.extBox = QComboBox(self)
		self.extBox.setEditable(False)
		self.extBox.addItems(
			['png', 'jpg', 'bmp', 'ppm', 'tiff', 'xbm', 'xpm', 'svg'])
		grid.addWidget(self.extBox, 1, 1, 1, 2)
		# width
		grid.addWidget(QLabel(tr('Width')), 2, 0)
		self.widthBox = QSpinBox(self)
		self.widthBox.setRange(1, 10000)
		self.widthBox.setSuffix(tr('px', 'Pixels'))
		self.widthBox.setButtonSymbols(QAbstractSpinBox.PlusMinus)
		self.widthBox.setValue(600)
		grid.addWidget(self.widthBox, 2, 1, 1, 2)
		# height
		grid.addWidget(QLabel(tr('Height')), 3, 0)
		self.heightBox = QSpinBox(self)
		self.heightBox.setRange(1, 10000)
		self.heightBox.setSuffix(tr('px', 'Pixels'))
		self.heightBox.setButtonSymbols(QAbstractSpinBox.PlusMinus)
		self.heightBox.setValue(600)
		grid.addWidget(self.heightBox, 3, 1, 1, 2)
		# buttons
		buttonsLayout = QHBoxLayout()
		grid.addLayout(buttonsLayout, 4, 0, 1, 3)
		cancelButton = QPushButton(tr('Cancel'), self)
		self.connect(cancelButton, SIGNAL('clicked()'), self.reject)
		buttonsLayout.addWidget(cancelButton)
		okButton = QPushButton(tr('Save'), self)
		okButton.setDefault(True)
		self.connect(okButton, SIGNAL('clicked()'), self.accept)
		buttonsLayout.addWidget(okButton)
	
	def getFileName(self):
		tr = self.tr
		path = unicode(QFileDialog.getSaveFileName(self,
			tr('Save Image As...'),
			os.path.expanduser(cfg.charts_dir),
			tr('Images (*.png *.jpg *.bmp *.ppm *.tiff *.xbm *.xpm *.svg)')))
		if path != '':
			self.fname.setText(path)
	
	def accept(self):
		"""Check input data."""
		if unicode(self.fname.text()) == '':
			QMessageBox.critical(self, self.tr('Missing File Name'),
				self.tr('Please set file name.'))
			self.fname.setFocus()
			return
		else:
			QDialog.accept(self)
	
	def exec_(self):
		ok = QDialog.exec_(self)
		if not ok:
			return ok, None, None, None, None
		else:
			fname = unicode(self.fname.text())
			ext = unicode(self.extBox.currentText())
			w = self.widthBox.value()
			h = self.heightBox.value()
			return ok, fname, ext, w, h class CfgDialog(QDialog):
	
	def __init__(self, parent=None):
		QDialog.__init__(self, parent)
		self._parent = parent
		tr = self.tr
		# set window title
		self.setWindowTitle(tr('Edit settings'))
		# width/height
		self.setMinimumWidth(280)
		self.setMaximumWidth(400)
		self.setSizeGripEnabled(True)
		# layout
		grid = QGridLayout(self)
		self.setLayout(grid)
		# tab widget
		tabs = QTabWidget(self)
		grid.addWidget(tabs, 0, 0)
		
		# ### main settings ###
		mainWidget = QWidget()
		tabs.addTab(mainWidget, tr('Main', 'Main settings'))
		mainGrid = QGridLayout()
		mainWidget.setLayout(mainGrid)
		# user name
		mainGrid.addWidget(QLabel(tr('User name')), 0, 0)
		self.usernameEdit = QLineEdit(self)
		mainGrid.addWidget(self.usernameEdit, 0, 1)
		# user mail
		mainGrid.addWidget(QLabel(tr('User email')), 1, 0)
		self.usermailEdit = QLineEdit(self)
		mainGrid.addWidget(self.usermailEdit, 1, 1)
		# language
		mainGrid.addWidget(QLabel(tr('Language')), 2, 0)
		self.languageEdit = QComboBox(self)
		alllng = translations.languages
		alllng.insert(0, '')
		self.languageEdit.addItems(alllng)
		self.languageEdit.setEditable(False)
		mainGrid.addWidget(self.languageEdit, 2, 1)
		# charts directory
		mainGrid.addWidget(QLabel(tr('Charts dir')), 3, 0)
		layout = QHBoxLayout()
		self.chartsdirEdit = QLineEdit(self)
		self.chartsdirEdit.setReadOnly(True)
		layout.addWidget(self.chartsdirEdit)
		chtdirButton = QToolButton(self)
		chtdirButton.setIcon(QIcon(os.path.join(_baseDir,
			'icons', 'gtk-directory.png')))
		chtdirButton.setToolTip(tr('Select charts directory'))
		self.connect(chtdirButton, SIGNAL('clicked()'), self.chartsDirSelect)
		layout.addWidget(chtdirButton)
		mainGrid.addLayout(layout, 3, 1)
		# use docutils
		self.useDocutilsBox = QCheckBox(tr('Use docutils'), self)
		mainGrid.addWidget(self.useDocutilsBox, 4, 0, 1, 2)
		
		# ### default chart ###
		dftChartWidget = QWidget()
		tabs.addTab(dftChartWidget, tr('Default Chart'))
		dftChartGrid = QGridLayout()
		dftChartWidget.setLayout(dftChartGrid)
		# default location
		lbl = QLabel(tr('<a href="http://www.astro.com/atlas">Location</a>'))
		lbl.setOpenExternalLinks(True)
		dftChartGrid.addWidget(lbl, 0, 0)
		self.locationEdit = QLineEdit(self)
		dftChartGrid.addWidget(self.locationEdit, 0, 1)
		# default country
		dftChartGrid.addWidget(QLabel(tr('Country')), 1, 0)
		self.countryEdit = QLineEdit(self)
		dftChartGrid.addWidget(self.countryEdit, 1, 1)
		# default zoneinfo
		lbl = QLabel(tr('<a href="http://en.wikipedia.org/wiki/List_of_zoneinfo_timezones">Zoneinfo</a>'))
		lbl.setToolTip(tr('Posix timezone file (for charts after 1900)'))
		lbl.setOpenExternalLinks(True)
		dftChartGrid.addWidget(lbl, 2, 0)
		self.zoneinfoEdit = QComboBox(self)
		alltz = pytz.all_timezones[:]
		alltz.insert(0, '')
		self.zoneinfoEdit.addItems(alltz)
		self.zoneinfoEdit.setEditable(False)
		dftChartGrid.addWidget(self.zoneinfoEdit, 2, 1)
		# default timezone
		lbl = QLabel(tr('<a href="http://upload.wikimedia.org/wikipedia/en/e/e7/Timezones2008.png">Timezone</a>'))
		lbl.setToolTip(tr('Standard timezone (for local mean time)'))
		lbl.setOpenExternalLinks(True)
		dftChartGrid.addWidget(lbl, 3, 0)
		self.timezoneEdit = QComboBox(self)
		alltz = [str(x) for x in timezone.all_timezones]
		alltz.insert(0, '')
		self.timezoneEdit.addItems(alltz)
		self.timezoneEdit.setEditable(False)
		dftChartGrid.addWidget(self.timezoneEdit, 3, 1)
		# latitude
		dftChartGrid.addWidget(QLabel(tr('Latitude')), 4, 0)
		self.latitudeEdit = LatitudeEdit(cfg.dft_latitude, self)
		dftChartGrid.addLayout(self.latitudeEdit, 4, 1)
		# longitude
		dftChartGrid.addWidget(QLabel(tr('Longitude')), 5, 0)
		self.longitudeEdit = LongitudeEdit(cfg.dft_longitude, self)
		dftChartGrid.addLayout(self.longitudeEdit, 5, 1)
		# altitude
		dftChartGrid.addWidget(QLabel(tr('Altitude')), 6, 0)
		geoLayout = QHBoxLayout() # geo layout
		dftChartGrid.addLayout(geoLayout, 6, 1)
		self.altitudeEdit = AltitudeEdit(cfg.dft_altitude, self)
		geoLayout.addWidget(self.altitudeEdit)
		# geonames query
		geoButton = QToolButton(self)
		geoButton.setIcon(QIcon(os.path.join(_baseDir, 'icons',
			'earth-icon.png')))
		geoButton.setToolTip(tr('Query GeoNames.org'))
		self.connect(geoButton, SIGNAL('clicked()'), self.queryGeoNames)
		geoLayout.addWidget(geoButton)
		# filter
		dftChartGrid.addWidget(QLabel(tr('Filter')), 7, 0)
		filtLayout = QHBoxLayout()
		self.filterEdit = QComboBox(self)
		self.filterEdit.addItems(all_filters_names())
		self.filterEdit.setEditable(False)
		filtLayout.addWidget(self.filterEdit)
		filtButton = QToolButton(self)
		filtButton.setIcon(QIcon(os.path.join(_baseDir, 'icons',
			'gtk-execute.png')))
		filtButton.setToolTip(tr('Edit filter'))
		self.connect(filtButton, SIGNAL('clicked()'), self.editFilter)
		filtLayout.addWidget(filtButton)
		dftChartGrid.addLayout(filtLayout, 7, 1)
		
		# ### mercurial settings ###
		hgWidget = QWidget()
		tabs.addTab(hgWidget, tr('Mercurial'))
		hgGrid = QGridLayout()
		hgWidget.setLayout(hgGrid)
		# use hg
		self.useHgBox = QCheckBox(tr('Use Mercurial'), self)
		self.useHgBox.setToolTip(
			tr('Pull changes on startup and push changes on shutdown'))
		hgGrid.addWidget(self.useHgBox, 0, 0, 1, 2)
		# hg repo
		lbl = QLabel(tr('Hg repo'))
		lbl.setToolTip(tr('Distant repository address'))
		hgGrid.addWidget(lbl, 1, 0)
		self.hgRepoEdit = QLineEdit(self)
		hgGrid.addWidget(self.hgRepoEdit, 1, 1)
		# hg user
		hgGrid.addWidget(QLabel(tr('Username')), 2, 0)
		self.hgUserEdit = QLineEdit(self)
		hgGrid.addWidget(self.hgUserEdit, 2, 1)
		# hg pswd
		hgGrid.addWidget(QLabel(tr('Password')), 3, 0)
		self.hgPswdEdit = QLineEdit(self)
		self.hgPswdEdit.setEchoMode(QLineEdit.Password)
		hgGrid.addWidget(self.hgPswdEdit, 3, 1)
		
		# ### buttons ###
		buttonsLayout = QHBoxLayout()
		resetButton = QPushButton(tr('Reset'), self)
		self.connect(resetButton, SIGNAL('clicked()'), self.reset)
		buttonsLayout.addWidget(resetButton)
		cancelButton = QPushButton(tr('Cancel'), self)
		self.connect(cancelButton, SIGNAL('clicked()'), self.reject)
		buttonsLayout.addWidget(cancelButton)
		okButton = QPushButton(tr('Ok'), self)
		okButton.setDefault(True)
		self.connect(okButton, SIGNAL('clicked()'), self.accept)
		buttonsLayout.addWidget(okButton)
		grid.addLayout(buttonsLayout, 1, 0)
		# fill entries
		self.reset()
	
	def chartsDirSelect(self):
		"""Select charts directory."""
		path = unicode(QFileDialog.getExistingDirectory(self,
			self.tr('Set charts directory'),
			os.path.expanduser('~')))
		if path != '':
			self.chartsdirEdit.setText(path)
	
	def queryGeoNames(self):
		"""Query GeoNames.org database."""
		ok, res = GeoNamesQueryDialog(self).exec_()
		if ok:
			self.locationEdit.setText(res[0])
			self.countryEdit.setText(res[1])
			self.latitudeEdit.setLatitude(res[2])
			self.longitudeEdit.setLongitude(res[3])
			self.altitudeEdit.setAltitude(res[4])
			self.zoneinfoEdit.setCurrentIndex(
				pytz.all_timezones.index(res[5]) + 1)
	
	def editFilter(self):
		"""Open filter editor."""
		filt = Filter(all_filters_names()[self.filterEdit.currentIndex()])
		edit = FilterDialog(self, filt)
		ok = edit.exec_()
		if ok: # reload filter box
			# cfg is already updated by filter dialog
			self.resetFilters()
	
	def resetFilters(self):
		"""Reset filter box."""
		self.filterEdit.clear()
		all = all_filters_names()
		self.filterEdit.addItems(all)
		self.filterEdit.setCurrentIndex(all.index(cfg.dft_filter._name))
	
	def reset(self):
		"""Set entries with original values."""
		# ### main ###
		# user name
		self.usernameEdit.setText(cfg.username)
		# user email
		self.usermailEdit.setText(cfg.usermail)
		# language
		if cfg.language != '':
			self.languageEdit.setCurrentIndex(
				translations.languages.index(cfg.language))
		else:
			self.languageEdit.setCurrentIndex(0)
		# charts dir
		self.chartsdirEdit.setText(os.path.expanduser(cfg.charts_dir))
		# use docutils
		self.useDocutilsBox.setChecked(cfg.use_docutils)
		# ### default chart ###
		# default location
		self.locationEdit.setText(cfg.dft_location)
		# default country
		self.countryEdit.setText(cfg.dft_country)
		# zoneinfo
		if cfg.dft_zoneinfo in ('', None):
			self.zoneinfoEdit.setCurrentIndex(0)
		else:
			self.zoneinfoEdit.setCurrentIndex(
				pytz.all_timezones.index(cfg.dft_zoneinfo) + 1)
		# timezone
		if cfg.dft_timezone == None:
			self.timezoneEdit.setCurrentIndex(0)
		else:
			self.timezoneEdit.setCurrentIndex(
				timezone.all_timezones.index(cfg.dft_timezone) + 1)
		# latitude
		self.latitudeEdit.setLatitude(cfg.dft_latitude)
		# longitude
		self.longitudeEdit.setLongitude(cfg.dft_longitude)
		# altitude
		self.altitudeEdit.setAltitude(cfg.dft_altitude)
		# filter
		self.resetFilters()
		# ### mercurial ###
		# use hg
		self.useHgBox.setChecked(cfg.use_hg)
		# hg repo
		self.hgRepoEdit.setText(cfg.hg_repo)
		# hg user
		self.hgUserEdit.setText(cfg.hg_user)
		# hg pswd
		self.hgPswdEdit.setText(cfg.hg_pswd)
	
	def accept(self):
		# ### main ###
		# username
		cfg.username = unicode(self.usernameEdit.text())
		# usermail
		cfg.usermail = unicode(self.usermailEdit.text())
		# language
		lng = self.languageEdit.currentIndex()
		if lng == 0:
			lng = ''
		else:
			lng = translations.languages[lng-1]
		cfg.language = lng
		# charts dir
		cfg.charts_dir = unicode(self.chartsdirEdit.text())
		# use docutils
		cfg.use_docutils = self.useDocutilsBox.isChecked()
		# ### default chart ###
		# location
		cfg.dft_location = unicode(self.locationEdit.text())
		# country
		cfg.dft_country = unicode(self.countryEdit.text())
		# zoneinfo
		tz = self.zoneinfoEdit.currentIndex()
		if tz == 0:
			cfg.dft_zoneinfo == ''
		else:
			cfg.dft_zoneinfo = pytz.all_timezones[tz - 1]
		# timezone
		tzone = self.timezoneEdit.currentIndex()
		if tzone == 0:
			cfg.dft_timezone = ''
		else:
			cfg.dft_timezone = timezone.all_timezones[tzone - 1]
		# latitude
		cfg.dft_latitude = self.latitudeEdit.latitude()
		# longitude
		cfg.dft_longitude = self.longitudeEdit.longitude()
		# altitude
		cfg.dft_altitude = self.altitudeEdit.altitude()
		# filter
		filt = self.filterEdit.currentIndex()
		cfg.dft_filter = Filter(all_filters_names()[filt])
		# ### mercurial ###
		# use hg
		cfg.use_hg = self.useHgBox.isChecked()
		# hg repo
		cfg.hg_repo = unicode(self.hgRepoEdit.text())
		# hg user
		cfg.hg_user = unicode(self.hgUserEdit.text())
		# hg pswd
		cfg.hg_pswd = unicode(self.hgPswdEdit.text())
		
		# save settings
		cfg.save()
		# done
		self.done(QDialog.Accepted) class ProfectionDialog(QDialog):
	
	def __init__(self, parent=None):
		QDialog.__init__(self, parent)
		tr = self.tr
		self.setWindowTitle(tr('Add Degrees'))
		layout = QGridLayout(self)
		self.setLayout(layout)
		# input value
		layout.addWidget(QLabel(tr('Value')), 0, 0)
		self.valueEdit = QDoubleSpinBox(self)
		self.valueEdit.setRange(-360, 360)
		self.valueEdit.setSuffix(tr('\xb0', 'Degrees'))
		self.valueEdit.setDecimals(6)
		self.valueEdit.setButtonSymbols(QAbstractSpinBox.PlusMinus)
		self.valueEdit.setValue(30)
		layout.addWidget(self.valueEdit, 0, 1)
		# profection mode
		self.profMode = QCheckBox(tr('Profection'), self)
		self.connect(self.profMode, SIGNAL('stateChanged(int)'),
			self.setProfMode)
		layout.addWidget(self.profMode, 1, 0)
		# profection unit
		self.profUnit = QComboBox(self)
		self.profUnit.setEditable(False)
		self.profUnit.setDisabled(True)
		units = [tr('Per year'), tr('Per day'), tr('Per hour')]
		self.profUnit.addItems(units)
		layout.addWidget(self.profUnit, 1, 1)
		# datetime
		layout.addWidget(QLabel(tr('DateTime')), 2, 0)
		self.datetimeEdit = QDateTimeEdit(QDateTime.currentDateTime(), self)
		self.datetimeEdit.setCalendarPopup(True)
		self.datetimeEdit.setDisplayFormat(tr('yyyy-MM-dd hh:mm:ss',
			'Datetime format'))
		self.datetimeEdit.setMinimumDateTime(QDateTime(-5400, 1, 1, 0, 0))
		self.datetimeEdit.setMaximumDateTime(QDateTime(5400, 1, 1, 0, 0))
		self.datetimeEdit.setButtonSymbols(QAbstractSpinBox.PlusMinus)
		self.datetimeEdit.setDisabled(True)
		layout.addWidget(self.datetimeEdit, 2, 1)
		# buttons
		buttonsLayout = QHBoxLayout()
		layout.addLayout(buttonsLayout, 3, 0, 1, 2)
		cancelButton = QPushButton(tr('Cancel'), self)
		self.connect(cancelButton, SIGNAL('clicked()'), self.reject)
		buttonsLayout.addWidget(cancelButton)
		okButton = QPushButton(tr('Ok'), self)
		okButton.setDefault(True)
		self.connect(okButton, SIGNAL('clicked()'), self.accept)
		buttonsLayout.addWidget(okButton)
	
	def setProfMode(self, i):
		"""Enable/disable profection."""
		if self.profMode.isChecked():
			self.profUnit.setEnabled(True)
			self.datetimeEdit.setEnabled(True)
		else:
			self.profUnit.setDisabled(True)
			self.datetimeEdit.setDisabled(True)
	
	def exec_(self):
		"""Return ok, value, profection, profection unit, datetime."""
		ok = QDialog.exec_(self)
		if ok:
			ret = (QDialog.Accepted, self.valueEdit.value(),
				self.profMode.isChecked(), self.profUnit.currentIndex(),
				self.datetimeEdit.dateTime().toPyDateTime())
			return ret
		else:
			return QDialog.Rejected, 0, False, -1, 0 class HarmonicsDialog(QDialog):
	
	def __init__(self, parent=None):
		QDialog.__init__(self, parent)
		tr = self.tr
		self.setWindowTitle(tr('Multiply Degrees'))
		layout = QGridLayout(self)
		self.setLayout(layout)
		# input value
		layout.addWidget(QLabel(tr('Value')), 0, 0)
		self.valueEdit = QDoubleSpinBox(self)
		self.valueEdit.setRange(0, 360)
		self.valueEdit.setSuffix(tr('\xb0', 'Degrees'))
		self.valueEdit.setDecimals(6)
		self.valueEdit.setButtonSymbols(QAbstractSpinBox.PlusMinus)
		self.valueEdit.setValue(2)
		layout.addWidget(self.valueEdit, 0, 1)
		# profection mode
		self.profMode = QCheckBox(tr('Profection'), self)
		self.connect(self.profMode, SIGNAL('stateChanged(int)'),
			self.setProfMode)
		layout.addWidget(self.profMode, 1, 0)
		# profection unit
		self.profUnit = QComboBox(self)
		self.profUnit.setEditable(False)
		self.profUnit.setDisabled(True)
		units = [tr('Per year'), tr('Per day'), tr('Per hour')]
		self.profUnit.addItems(units)
		layout.addWidget(self.profUnit, 1, 1)
		# datetime
		layout.addWidget(QLabel(tr('DateTime')), 2, 0)
		self.datetimeEdit = QDateTimeEdit(QDateTime.currentDateTime(), self)
		self.datetimeEdit.setCalendarPopup(True)
		self.datetimeEdit.setDisplayFormat(tr('yyyy-MM-dd hh:mm:ss',
			'Datetime format'))
		self.datetimeEdit.setMinimumDateTime(QDateTime(-5400, 1, 1, 0, 0))
		self.datetimeEdit.setMaximumDateTime(QDateTime(5400, 1, 1, 0, 0))
		self.datetimeEdit.setButtonSymbols(QAbstractSpinBox.PlusMinus)
		self.datetimeEdit.setDisabled(True)
		layout.addWidget(self.datetimeEdit, 2, 1)
		# buttons
		buttonsLayout = QHBoxLayout()
		layout.addLayout(buttonsLayout, 3, 0, 1, 2)
		cancelButton = QPushButton(tr('Cancel'), self)
		self.connect(cancelButton, SIGNAL('clicked()'), self.reject)
		buttonsLayout.addWidget(cancelButton)
		okButton = QPushButton(tr('Ok'), self)
		okButton.setDefault(True)
		self.connect(okButton, SIGNAL('clicked()'), self.accept)
		buttonsLayout.addWidget(okButton)
	
	def setProfMode(self, i):
		"""Enable/disable profection."""
		if self.profMode.isChecked():
			self.profUnit.setEnabled(True)
			self.datetimeEdit.setEnabled(True)
		else:
			self.profUnit.setDisabled(True)
			self.datetimeEdit.setDisabled(True)
	
	def exec_(self):
		"""Return ok, value, profection, profection unit, datetime."""
		ok = QDialog.exec_(self)
		if ok:
			ret = (QDialog.Accepted, self.valueEdit.value(),
				self.profMode.isChecked(), self.profUnit.currentIndex(),
				self.datetimeEdit.dateTime().toPyDateTime())
			return ret
		else:
			return QDialog.Rejected, 0, False, -1, 0 class FiltersManagerDialog(QDialog):
	
	def __init__(self, parent=None, mode='man'):
		QDialog.__init__(self, parent)
		self._parent = parent
		self._mode = mode # either 'man' or 'select'
		tr = self.tr
		self.setWindowTitle(tr('Filters Manager'))
		self.setSizeGripEnabled(True)
		# layout
		grid = QGridLayout(self)
		self.setLayout(grid)
		# list of filters
		self.filtersBox = QComboBox(self)
		self.filtersBox.setEditable(False)
		self.filtersBox.addItems(all_filters_names())
		grid.addWidget(self.filtersBox, 0, 0)
		# buttons
		buttonsLayout = QHBoxLayout()
		grid.addLayout(buttonsLayout, 1, 0)
		newButton = QPushButton(tr('New'), self)
		self.connect(newButton, SIGNAL('clicked()'), self.newFilterEvent)
		buttonsLayout.addWidget(newButton)
		editButton = QPushButton(tr('Edit'), self)
		self.connect(editButton, SIGNAL('clicked()'), self.editFilterEvent)
		buttonsLayout.addWidget(editButton)
		deleteButton = QPushButton(tr('Delete'), self)
		self.connect(deleteButton, SIGNAL('clicked()'), self.deleteFilterEvent)
		buttonsLayout.addWidget(deleteButton)
		if mode == 'man':
			closeButton = QPushButton(tr('Close'), self)
			self.connect(closeButton, SIGNAL('clicked()'), SLOT('close()'))
		elif mode == 'select':
			closeButton = QPushButton(tr('Select'), self)
			self.connect(closeButton, SIGNAL('clicked()'), self.accept)
		closeButton.setDefault(True)
		buttonsLayout.addWidget(closeButton)
	
	def newFilterEvent(self):
		new = FilterDialog(self)
		ok = new.exec_()
		if ok:
			self.reset()
	
	def editFilterEvent(self):
		filt = Filter(all_filters_names()[self.filtersBox.currentIndex()])
		edit = FilterDialog(self, filt)
		ok = edit.exec_()
		if ok:
			self.reset()
	
	def deleteFilterEvent(self):
		tr = self.tr
		name = all_filters_names()[self.filtersBox.currentIndex()]
		ret = QMessageBox.warning(self, tr('Delete Filter'),
			unicode(tr('Are you sure you want to delete filter \xab %(filter)s \xbb ?')) % {
				'filter': name},
			QMessageBox.Cancel|QMessageBox.Yes, QMessageBox.Yes)
		if ret == QMessageBox.Yes:
			filt = Filter(name)
			try:
				filt.delete()
			except ValueError: # cannot delete
				QMessageBox.critical(self, tr('Default Filter'),
					tr('Cannot delete default filter.'))
			self.reset()
	
	def reset(self):
		"""Reload filters list."""
		self.filtersBox.clear()
		self.filtersBox.addItems(all_filters_names())
	
	def exec_(self):
		if self._mode == 'select':
			ok = QDialog.exec_(self)
			if ok:
				name = all_filters_names()[self.filtersBox.currentIndex()]
				return ok, name
			else:
				return ok, None
		else:
			return QDialog.exec_(self) class FilterDialog(QDialog):
	
	def __init__(self, parent=None, filt=None):
		QDialog.__init__(self, parent)
		self._parent = parent
		tr = self.tr
		if isinstance(filt, Filter):
			self._filt = filt
			title = unicode(tr('Edit Filter \xab %(filter)s \xbb')) % {
				'filter': filt._name}
		else:
			self._filt = Filter(cfg.dft_filter._idx_)
			self._filt._idx_ = None # copy dft filter and set idx to None
			title = tr('New Filter')
		self.setWindowTitle(title)
		# size
		self.setMinimumWidth(300)
		self.setMinimumHeight(380)
		self.setSizeGripEnabled(True)
		# layout
		layout = QVBoxLayout(self)
		self.setLayout(layout)
		# tabs
		tabs = QTabWidget(self)
		layout.addWidget(tabs)
		
		# ### general settings ###
		generalWidget = QWidget()
		tabs.addTab(generalWidget, tr('Main', 'Main settings'))
		# layout
		grid = QGridLayout()
		generalWidget.setLayout(grid)
		# filter name
		grid.addWidget(QLabel(tr('Filter name')), 0, 0)
		self.nameEdit = QLineEdit(self)
		grid.addWidget(self.nameEdit, 0, 1)
		# bg color
		grid.addWidget(QLabel(tr('Background')), 1, 0)
		self.bgcolorEdit = QComboBox(self)
		self.bgcolorEdit.setEditable(False)
		self.bgcolorEdit.addItems([tr('Black'), tr('White')])
		grid.addWidget(self.bgcolorEdit, 1, 1)
		# ephe type
		grid.addWidget(QLabel(tr('Ephemeris')), 2, 0)
		self.ephetypeEdit = QComboBox(self)
		self.ephetypeEdit.setEditable(False)
		self.ephetypeEdit.addItems([tr('Swiss'), tr('JPL'), tr('Moshier')])
		grid.addWidget(self.ephetypeEdit, 2, 1)
		# ephe path
		grid.addWidget(QLabel(tr('Ephem. path')), 3, 0)
		ephLayout = QHBoxLayout()
		self.ephepathEdit = QLineEdit(self)
		self.ephepathEdit.setReadOnly(True)
		ephLayout.addWidget(self.ephepathEdit)
		selectButton = QToolButton(self)
		selectButton.setIcon(QIcon(os.path.join(_baseDir,
			'icons', 'gtk-directory.png')))
		selectButton.setToolTip(tr('Select ephemeris path'))
		self.connect(selectButton, SIGNAL('clicked()'), self.ephePathSelect)
		ephLayout.addWidget(selectButton)
		grid.addLayout(ephLayout, 3, 1)
		# house system
		grid.addWidget(QLabel(tr('Domification')), 4, 0)
		self.hsysEdit = QComboBox(self)
		self.hsysEdit.setEditable(False)
		self.hsysEdit.addItems([y for x, y in names.houseSystems])
		grid.addWidget(self.hsysEdit, 4, 1)
		# sidereal mode
		grid.addWidget(QLabel(tr('Sidereal mode')), 5, 0)
		self.sidmodeEdit = QComboBox(self)
		self.sidmodeEdit.setEditable(False)
		self.sidmodeEdit.addItems([y for x, y in names.sidModes])
		grid.addWidget(self.sidmodeEdit, 5, 1)
		# sidereal t0
		grid.addWidget(QLabel(tr('Sidereal T0')), 6, 0)
		self.sidt0Edit = QDoubleSpinBox(self)
		self.sidt0Edit.setDecimals(6)
		self.sidt0Edit.setMaximum(9999999)
		self.sidt0Edit.setSuffix(tr(' JD', 'Julian day suffix'))
		self.sidt0Edit.setButtonSymbols(QAbstractSpinBox.PlusMinus)
		grid.addWidget(self.sidt0Edit, 6, 1)
		# sidereal ayanamsa at t0
		grid.addWidget(QLabel(tr('Ayanamsa T0')), 7, 0)
		self.sidayant0Edit = QDoubleSpinBox(self)
		self.sidayant0Edit.setDecimals(6)
		self.sidayant0Edit.setMaximum(360)
		self.sidayant0Edit.setSuffix(tr('\xb0', 'Degrees'))
		self.sidayant0Edit.setButtonSymbols(QAbstractSpinBox.PlusMinus)
		grid.addWidget(self.sidayant0Edit, 7, 1)
		# true positions
		grid.addWidget(QLabel(tr('Calc. positions')), 8, 0)
		self.trueposEdit = QComboBox(self)
		self.trueposEdit.setEditable(False)
		self.trueposEdit.addItems(
			[tr('Apparent', 'Positions'), tr('True', 'Positions')])
		grid.addWidget(self.trueposEdit, 8, 1)
		# xcentric
		grid.addWidget(QLabel(tr('Situation')), 9, 0)
		self.xcentricEdit = QComboBox(self)
		self.xcentricEdit.setEditable(False)
		self.xcentricEdit.addItems(
			[tr('Geocentric'), tr('Topocentric'), tr('Heliocentric'),
			tr('Barycentric')])
		grid.addWidget(self.xcentricEdit, 9, 1)
		# comment
		grid.addWidget(QLabel(tr('Comment')), 10, 0, Qt.AlignTop)
		self.commentEdit = QTextEdit('', self)
		grid.addWidget(self.commentEdit, 10, 1)
		
		# ### filters settings ###
		filtersWidget = QWidget()
		tabs.addTab(filtersWidget, tr('Filters'))
		# layout
		grid = QGridLayout()
		filtersWidget.setLayout(grid)
		# planets filter
		grid.addWidget(QLabel(tr('Planets filter')), 0, 0)
		pfLayout = QHBoxLayout()
		self.planetsEdit = QComboBox(self)
		self.planetsEdit.setEditable(False)
		pfLayout.addWidget(self.planetsEdit)
		pfButton = QToolButton(self)
		pfButton.setIcon(QIcon(os.path.join(_baseDir, 'icons',
			'gtk-execute.png')))
		pfButton.setToolTip(tr('Edit planets filter'))
		self.connect(pfButton, SIGNAL('clicked()'), self.editPlanetsFilter)
		pfLayout.addWidget(pfButton)
		grid.addLayout(pfLayout, 0, 1)
		# aspects filter
		grid.addWidget(QLabel(tr('Aspects filter')), 1, 0)
		afLayout = QHBoxLayout()
		self.aspectsEdit = QComboBox(self)
		self.aspectsEdit.setEditable(False)
		afLayout.addWidget(self.aspectsEdit)
		afButton = QToolButton(self)
		afButton.setIcon(QIcon(os.path.join(_baseDir, 'icons',
			'gtk-execute.png')))
		afButton.setToolTip(tr('Edit aspects filter'))
		self.connect(afButton, SIGNAL('clicked()'), self.editAspectsFilter)
		afLayout.addWidget(afButton)
		grid.addLayout(afLayout, 1, 1)
		# orbs filter
		grid.addWidget(QLabel(tr('Orbs filter')), 2, 0)
		ofLayout = QHBoxLayout()
		self.orbsEdit = QComboBox(self)
		self.orbsEdit.setEditable(False)
		ofLayout.addWidget(self.orbsEdit)
		ofButton = QToolButton(self)
		ofButton.setIcon(QIcon(os.path.join(_baseDir, 'icons',
			'gtk-execute.png')))
		ofButton.setToolTip(tr('Edit orbs filter'))
		self.connect(ofButton, SIGNAL('clicked()'), self.editOrbsFilter)
		ofLayout.addWidget(ofButton)
		grid.addLayout(ofLayout, 2, 1)
		# aspects restrictions
		grid.addWidget(QLabel(tr('Aspects restrictions')), 3, 0)
		arLayout = QHBoxLayout()
		self.asprestrEdit = QComboBox(self)
		self.asprestrEdit.setEditable(False)
		arLayout.addWidget(self.asprestrEdit)
		arButton = QToolButton(self)
		arButton.setIcon(QIcon(os.path.join(_baseDir, 'icons',
			'gtk-execute.png')))
		arButton.setToolTip(tr('Edit aspects restrictions'))
		self.connect(arButton, SIGNAL('clicked()'), self.editAspectsRestrictions)
		arLayout.addWidget(arButton)
		grid.addLayout(arLayout, 3, 1)
		# orbs restrictions
		grid.addWidget(QLabel(tr('Orbs modifiers')), 4, 0)
		orLayout = QHBoxLayout()
		self.orbrestrEdit = QComboBox(self)
		self.orbrestrEdit.setEditable(False)
		orLayout.addWidget(self.orbrestrEdit)
		orButton = QToolButton(self)
		orButton.setIcon(QIcon(os.path.join(_baseDir, 'icons',
			'gtk-execute.png')))
		orButton.setToolTip(tr('Edit orbs restrictions'))
		self.connect(orButton, SIGNAL('clicked()'), self.editOrbsRestrictions)
		orLayout.addWidget(orButton)
		grid.addLayout(orLayout, 4, 1)
		# midpoints settings
		## calc midpoints
		self.calc_midpEdit = QCheckBox(tr('Calc. midpoints'), self)
		self.connect(self.calc_midpEdit, SIGNAL('stateChanged(int)'),
			self.calc_midpEditChanged)
		grid.addWidget(self.calc_midpEdit, 5, 0)
		## draw midpoints
		self.draw_midpEdit = QCheckBox(tr('Draw midpoints aspects'), self)
		self.connect(self.draw_midpEdit, SIGNAL('stateChanged(int)'),
			self.draw_midpEditChanged)
		grid.addWidget(self.draw_midpEdit, 5, 1)
		## filters
		grid.addWidget(QLabel(tr('MidPoints filter')), 6, 0)
		mpLayout = QHBoxLayout()
		self.midpointsEdit = QComboBox(self)
		self.midpointsEdit.setEditable(False)
		mpLayout.addWidget(self.midpointsEdit)
		mpButton = QToolButton(self)
		mpButton.setIcon(QIcon(os.path.join(_baseDir, 'icons',
			'gtk-execute.png')))
		mpButton.setToolTip(tr('Edit MidPoints filter'))
		self.connect(mpButton, SIGNAL('clicked()'), self.editMidPointsFilter)
		mpLayout.addWidget(mpButton)
		grid.addLayout(mpLayout, 6, 1)
		
		# ### buttons ###
		buttonsLayout = QHBoxLayout()
		resetButton = QPushButton(tr('Reset'), self)
		self.connect(resetButton, SIGNAL('clicked()'), self.reset)
		buttonsLayout.addWidget(resetButton)
		cancelButton = QPushButton(tr('Cancel'), self)
		self.connect(cancelButton, SIGNAL('clicked()'), self.reject)
		buttonsLayout.addWidget(cancelButton)
		okButton = QPushButton(tr('Ok'), self)
		okButton.setDefault(True)
		self.connect(okButton, SIGNAL('clicked()'), self.accept)
		buttonsLayout.addWidget(okButton)
		layout.addLayout(buttonsLayout)
		
		# load entries
		self.reset()
	
	def ephePathSelect(self):
		"""Select directory (for swisseph) or a file (for jpl)."""
		path = ''
		if self.ephetypeEdit.currentIndex() == 0: # swisseph
			path = unicode(QFileDialog.getExistingDirectory(self,
				self.tr('Set Swisseph directory'),
				os.path.expanduser('~')))
		elif self.ephetypeEdit.currentIndex() == 1: # jpl
			path = unicode(QFileDialog.getOpenFileName(self,
				self.tr('Set JPL file'),
				os.path.expanduser('~')))
		else: # moshier
			QMessageBox.information(self, self.tr('Moshier ephemeris'),
				self.tr('No ephemeris path needed for Moshier ephemeris.'))
		if path != '':
				self.ephepathEdit.setText(path)
	
	def calc_midpEditChanged(self, val):
		if not self.calc_midpEdit.isChecked():
			self.draw_midpEdit.setChecked(False)
	
	def draw_midpEditChanged(self, val):
		if self.draw_midpEdit.isChecked():
			self.calc_midpEdit.setChecked(True)
	
	# ### edit filters ###
	
	def editPlanetsFilter(self):
		"""Open planets filter edit dialog."""
		pf = PlanetsFilter(
			all_planets_filters_names()[self.planetsEdit.currentIndex()])
		edit = PlanetsFilterDialog(self, pf)
		ok = edit.exec_()
		if ok: ## reset our filters
			if pf == self._filt._planets:
				self._filt._planets.reset()
			self.resetPlanetsFilters()
	
	def editAspectsFilter(self):
		"""Open aspects filter edit dialog."""
		af = AspectsFilter(
			all_aspects_filters_names()[self.aspectsEdit.currentIndex()])
		edit = AspectsFilterDialog(self, af)
		ok = edit.exec_()
		if ok: ## reset our filters
			if af == self._filt._aspects:
				self._filt._aspects.reset()
			self.resetAspectsFilters()
	
	def editOrbsFilter(self):
		of = OrbsFilter(
			all_orbs_filters_names()[self.orbsEdit.currentIndex()])
		edit = OrbsFilterDialog(self, of)
		ok = edit.exec_()
		if ok: ## reset our filters
			if of == self._filt._orbs:
				self._filt._orbs.reset()
			self.resetOrbsFilters()
	
	def editAspectsRestrictions(self):
		ar = AspectsRestrictions(
			all_aspects_restrictions_names()[self.asprestrEdit.currentIndex()])
		edit = AspectsRestrictionsDialog(self, ar)
		ok = edit.exec_()
		if ok: ## reset our filters
			if ar == self._filt._asprestr:
				self._filt._asprestr.reset()
			self.resetAspectsRestrictions()
	
	def editOrbsRestrictions(self):
		orb = OrbsRestrictions(
			all_orbs_restrictions_names()[self.orbrestrEdit.currentIndex()])
		edit = OrbsRestrictionsDialog(self, orb)
		ok = edit.exec_()
		if ok: ## reset our filters
			if orb == self._filt._orbrestr:
				self._filt._orbrestr.reset()
			self.resetOrbsRestrictions()
	
	def editMidPointsFilter(self):
		mp = MidPointsFilter(
			all_midpoints_filters_names()[self.midpointsEdit.currentIndex()])
		edit = MidPointsFilterDialog(self, mp)
		ok = edit.exec_()
		if ok: ## reset our filters
			if mp == self._filt._midpoints:
				self._filt._midpoints.reset()
			self.resetMidPointsFilters()
	
	# ### reset filters boxes ###
	
	def resetPlanetsFilters(self):
		"""Reset planets filter box."""
		self.planetsEdit.clear()
		all = all_planets_filters_names()
		self.planetsEdit.addItems(all)
		i = all.index(self._filt._planets._name)
		self.planetsEdit.setCurrentIndex(i)
	
	def resetAspectsFilters(self):
		"""Reset aspects filters box."""
		self.aspectsEdit.clear()
		all = all_aspects_filters_names()
		self.aspectsEdit.addItems(all)
		i = all.index(self._filt._aspects._name)
		self.aspectsEdit.setCurrentIndex(i)
	
	def resetOrbsFilters(self):
		"""Reset orbs filters box."""
		self.orbsEdit.clear()
		all = all_orbs_filters_names()
		self.orbsEdit.addItems(all)
		i = all.index(self._filt._orbs._name)
		self.orbsEdit.setCurrentIndex(i)
	
	def resetAspectsRestrictions(self):
		"""Reset aspects restrictions box."""
		self.asprestrEdit.clear()
		all = all_aspects_restrictions_names()
		self.asprestrEdit.addItems(all)
		i = all.index(self._filt._asprestr._name)
	
	def resetOrbsRestrictions(self):
		"""Reset orbs restrictions box."""
		self.orbrestrEdit.clear()
		all = all_orbs_restrictions_names()
		self.orbrestrEdit.addItems(all)
		i = all.index(self._filt._orbrestr._name)
		self.orbrestrEdit.setCurrentIndex(i)
	
	def resetMidPointsFilters(self):
		"""Reset midpoints filters box."""
		self.midpointsEdit.clear()
		all = all_midpoints_filters_names()
		self.midpointsEdit.addItems(all)
		i = all.index(self._filt._midpoints._name)
		self.midpointsEdit.setCurrentIndex(i)
	
	def reset(self):
		"""Reset entries."""
		filt = self._filt
		# name
		self.nameEdit.setText(filt._name)
		# bg color
		self.bgcolorEdit.setCurrentIndex(0 if filt._bg_color == 'black' else 1)
		# ephe type
		t = filt._ephe_type
		if t == 'swiss':
			t = 0
		elif t == 'jpl':
			t = 1
		else:
			t = 2
		self.ephetypeEdit.setCurrentIndex(t)
		# ephe path
		self.ephepathEdit.setText(os.path.expanduser(filt._ephe_path))
		# domification
		hsys = filt._hsys
		for i, a in enumerate(names.houseSystems):
			if a[0] == hsys:
				self.hsysEdit.setCurrentIndex(i)
				break
		# sid mode
		sidm = filt._sid_mode
		for i, a in enumerate(names.sidModes):
			if a[0] == sidm:
				self.sidmodeEdit.setCurrentIndex(i)
				break
		# sid t0
		self.sidt0Edit.setValue(filt._sid_t0)
		# sid ayan t0
		self.sidayant0Edit.setValue(filt._sid_ayan_t0)
		# true pos
		self.trueposEdit.setCurrentIndex(0 if filt._true_pos == False else 1)
		# xcentric
		xcentric = filt._xcentric
		if xcentric == 'geo':
			i = 0
		elif xcentric == 'topo':
			i = 1
		elif xcentric == 'helio':
			i = 2
		else: # bary
			i = 3
		self.xcentricEdit.setCurrentIndex(i)
		# planets
		self.resetPlanetsFilters()
		# aspects
		self.resetAspectsFilters()
		# orbs
		self.resetOrbsFilters()
		# aspects restrictions
		self.resetAspectsRestrictions()
		# orbs modifiers
		self.resetOrbsRestrictions()
		# calc midp
		self.calc_midpEdit.setChecked(self._filt._calc_midp)
		# draw midp
		self.draw_midpEdit.setChecked(self._filt._draw_midp)
		# midpoints filter
		self.resetMidPointsFilters()
		# comment
		self.commentEdit.setPlainText(filt._comment)
	
	def accept(self):
		"""Set filter new values and save."""
		tr = self.tr
		# name
		name = unicode(self.nameEdit.text())
		if name == '':
			QMessageBox.critical(self, tr('Missing Name'),
				tr('Please set filter name.'))
			self.nameEdit.setFocus()
			return
		# bg color
		bg = self.bgcolorEdit.currentIndex()
		if bg == 0:
			bg = 'black'
		else:
			bg = 'white'
		# ephe type
		ephetype = self.ephetypeEdit.currentIndex()
		if ephetype == 0:
			ephetype = 'swiss'
		elif ephetype == 1:
			ephetype = 'jpl'
		else:
			ephetype = 'moshier'
		# ephe path
		ephepath = unicode(self.ephepathEdit.text())
		# hsys
		hsys = names.houseSystems[self.hsysEdit.currentIndex()][0]
		# sidereal mode
		sidm = names.sidModes[self.sidmodeEdit.currentIndex()][0]
		# sidereal t0
		sidt0 = self.sidt0Edit.value()
		# sidereal ayanamsa t0
		sidayant0 = self.sidayant0Edit.value()
		# true pos
		truepos = False if self.trueposEdit.currentIndex() == 0 else True
		# xcentric
		xcentric = self.xcentricEdit.currentIndex()
		if xcentric == 0:
			xcentric = 'geo'
		elif xcentric == 1:
			xcentric = 'topo'
		elif xcentric == 2:
			xcentric = 'helio'
		else:
			xcentric = 'bary'
		# planets filter
		planets = all_planets_filters_names()[self.planetsEdit.currentIndex()]
		# aspects filter
		aspects = all_aspects_filters_names()[self.aspectsEdit.currentIndex()]
		# orbs filter
		orbs = all_orbs_filters_names()[self.orbsEdit.currentIndex()]
		# aspects restrictions
		asprestr = all_aspects_restrictions_names()[self.asprestrEdit.currentIndex()]
		# orbs restrictions
		orbrestr = all_orbs_restrictions_names()[self.orbrestrEdit.currentIndex()]
		# midpoints settings
		calc_midp = self.calc_midpEdit.isChecked()
		draw_midp = self.draw_midpEdit.isChecked()
		# midpoints filter
		midpoints = all_midpoints_filters_names()[self.midpointsEdit.currentIndex()]
		# comment
		cmt = unicode(self.commentEdit.toPlainText())
		# ### set filter ###
		self._filt.set(name=name, bg_color=bg, ephe_type=ephetype,
			ephe_path=ephepath, hsys=hsys, sid_mode=sidm, sid_t0=sidt0,
			sid_ayan_t0=sidayant0, true_pos=truepos, xcentric=xcentric,
			calc_midp=calc_midp, draw_midp=draw_midp,
			planets=planets, aspects=aspects, orbs=orbs, asprestr=asprestr,
			orbrestr=orbrestr, midpoints=midpoints, comment=cmt)
		# save
		try:
			self._filt.save()
		except ValueError: # duplicate filter
			QMessageBox.critical(self, tr('Error'),
				unicode(tr('Duplicate filter name \xab %(filter)s \xbb.')) % {
					'filter': self._filt._name})
			self.nameEdit.setFocus()
			return
		# reload cfg in case filter is default,
		# and opened charts if using this filter
		if __name__ != '__main__':
			app.filterUpdatedEvent(self._filt._idx_)
		# done
		self.done(QDialog.Accepted) class PlanetsFiltersManagerDialog(QDialog):
	
	def __init__(self, parent=None):
		QDialog.__init__(self, parent)
		self._parent = parent
		tr = self.tr
		self.setWindowTitle(tr('Planets Filters Manager'))
		self.setSizeGripEnabled(True)
		# layout
		grid = QGridLayout(self)
		self.setLayout(grid)
		# list of filters
		self.filtersBox = QComboBox(self)
		self.filtersBox.setEditable(False)
		self.filtersBox.addItems(all_planets_filters_names())
		grid.addWidget(self.filtersBox, 0, 0)
		# buttons
		buttonsLayout = QHBoxLayout()
		grid.addLayout(buttonsLayout, 1, 0)
		newButton = QPushButton(tr('New'), self)
		self.connect(newButton, SIGNAL('clicked()'), self.newFilterEvent)
		buttonsLayout.addWidget(newButton)
		editButton = QPushButton(tr('Edit'), self)
		self.connect(editButton, SIGNAL('clicked()'), self.editFilterEvent)
		buttonsLayout.addWidget(editButton)
		deleteButton = QPushButton(tr('Delete'), self)
		self.connect(deleteButton, SIGNAL('clicked()'), self.deleteFilterEvent)
		buttonsLayout.addWidget(deleteButton)
		closeButton = QPushButton(tr('Close'), self)
		closeButton.setDefault(True)
		self.connect(closeButton, SIGNAL('clicked()'), SLOT('close()'))
		buttonsLayout.addWidget(closeButton)
	
	def newFilterEvent(self):
		new = PlanetsFilterDialog(self)
		ok = new.exec_()
		if ok:
			self.reset()
	
	def editFilterEvent(self):
		filt = PlanetsFilter(
			all_planets_filters_names()[self.filtersBox.currentIndex()])
		edit = PlanetsFilterDialog(self, filt)
		ok = edit.exec_()
		if ok:
			self.reset()
	
	def deleteFilterEvent(self):
		tr = self.tr
		name = all_planets_filters_names()[self.filtersBox.currentIndex()]
		ret = QMessageBox.warning(self, tr('Delete Planets Filter'),
			unicode(tr('Are you sure you want to delete planets filter \xab %(filter)s \xbb ?')) % {
				'filter': name},
			QMessageBox.Cancel|QMessageBox.Yes, QMessageBox.Yes)
		if ret == QMessageBox.Yes:
			filt = PlanetsFilter(name)
			try:
				filt.delete()
			except ValueError: # cannot delete default filter
				QMessageBox.critical(self, tr('Required Planets Filter'),
					tr('Cannot delete required filter.'))
			self.reset()
	
	def reset(self):
		"""Reload filters list."""
		self.filtersBox.clear()
		self.filtersBox.addItems(all_planets_filters_names()) class AspectsFiltersManagerDialog(QDialog):
	
	def __init__(self, parent=None):
		QDialog.__init__(self, parent)
		self._parent = parent
		tr = self.tr
		self.setWindowTitle(tr('Aspects Filters Manager'))
		self.setSizeGripEnabled(True)
		# layout
		grid = QGridLayout(self)
		self.setLayout(grid)
		# list of filters
		self.filtersBox = QComboBox(self)
		self.filtersBox.setEditable(False)
		self.filtersBox.addItems(all_aspects_filters_names())
		grid.addWidget(self.filtersBox, 0, 0)
		# buttons
		buttonsLayout = QHBoxLayout()
		grid.addLayout(buttonsLayout, 1, 0)
		newButton = QPushButton(tr('New'), self)
		self.connect(newButton, SIGNAL('clicked()'), self.newFilterEvent)
		buttonsLayout.addWidget(newButton)
		editButton = QPushButton(tr('Edit'), self)
		self.connect(editButton, SIGNAL('clicked()'), self.editFilterEvent)
		buttonsLayout.addWidget(editButton)
		deleteButton = QPushButton(tr('Delete'), self)
		self.connect(deleteButton, SIGNAL('clicked()'), self.deleteFilterEvent)
		buttonsLayout.addWidget(deleteButton)
		closeButton = QPushButton(tr('Close'), self)
		closeButton.setDefault(True)
		self.connect(closeButton, SIGNAL('clicked()'), SLOT('close()'))
		buttonsLayout.addWidget(closeButton)
	
	def newFilterEvent(self):
		new = AspectsFilterDialog(self)
		ok = new.exec_()
		if ok:
			self.reset()
	
	def editFilterEvent(self):
		filt = AspectsFilter(
			all_aspects_filters_names()[self.filtersBox.currentIndex()])
		edit = AspectsFilterDialog(self, filt)
		ok = edit.exec_()
		if ok:
			self.reset()
	
	def deleteFilterEvent(self):
		tr = self.tr
		name = all_aspects_filters_names()[self.filtersBox.currentIndex()]
		ret = QMessageBox.warning(self, tr('Delete Aspects Filter'),
			unicode(tr('Are you sure you want to delete aspects filter \xab %(filter)s \xbb ?')) % {
				'filter': name},
			QMessageBox.Cancel|QMessageBox.Yes, QMessageBox.Yes)
		if ret == QMessageBox.Yes:
			filt = AspectsFilter(name)
			try:
				filt.delete()
			except ValueError: # cannot delete default filter
				QMessageBox.critical(self, tr('Required Aspects Filter'),
					tr('Cannot delete required filter.'))
			self.reset()
	
	def reset(self):
		"""Reload filters list."""
		self.filtersBox.clear()
		self.filtersBox.addItems(all_aspects_filters_names()) class OrbsFiltersManagerDialog(QDialog):
	
	def __init__(self, parent=None):
		QDialog.__init__(self, parent)
		self._parent = parent
		tr = self.tr
		self.setWindowTitle(tr('Orbs Filters Manager'))
		self.setSizeGripEnabled(True)
		# layout
		grid = QGridLayout(self)
		self.setLayout(grid)
		# list of filters
		self.filtersBox = QComboBox(self)
		self.filtersBox.setEditable(False)
		self.filtersBox.addItems(all_orbs_filters_names())
		grid.addWidget(self.filtersBox, 0, 0)
		# buttons
		buttonsLayout = QHBoxLayout()
		grid.addLayout(buttonsLayout, 1, 0)
		newButton = QPushButton(tr('New'), self)
		self.connect(newButton, SIGNAL('clicked()'), self.newFilterEvent)
		buttonsLayout.addWidget(newButton)
		editButton = QPushButton(tr('Edit'), self)
		self.connect(editButton, SIGNAL('clicked()'), self.editFilterEvent)
		buttonsLayout.addWidget(editButton)
		deleteButton = QPushButton(tr('Delete'), self)
		self.connect(deleteButton, SIGNAL('clicked()'), self.deleteFilterEvent)
		buttonsLayout.addWidget(deleteButton)
		closeButton = QPushButton(tr('Close'), self)
		closeButton.setDefault(True)
		self.connect(closeButton, SIGNAL('clicked()'), SLOT('close()'))
		buttonsLayout.addWidget(closeButton)
	
	def newFilterEvent(self):
		new = OrbsFilterDialog(self)
		ok = new.exec_()
		if ok:
			self.reset()
	
	def editFilterEvent(self):
		filt = OrbsFilter(
			all_orbs_filters_names()[self.filtersBox.currentIndex()])
		edit = OrbsFilterDialog(self, filt)
		ok = edit.exec_()
		if ok:
			self.reset()
	
	def deleteFilterEvent(self):
		tr = self.tr
		name = all_orbs_filters_names()[self.filtersBox.currentIndex()]
		ret = QMessageBox.warning(self, tr('Delete Orbs Filter'),
			unicode(tr('Are you sure you want to delete orbs filter \xab %(filter)s \xbb ?')) % {
				'filter': name},
			QMessageBox.Cancel|QMessageBox.Yes, QMessageBox.Yes)
		if ret == QMessageBox.Yes:
			filt = OrbsFilter(name)
			try:
				filt.delete()
			except ValueError: # cannot delete default filter
				QMessageBox.critical(self, tr('Required Orbs Filter'),
					tr('Cannot delete required filter.'))
			self.reset()
	
	def reset(self):
		"""Reload filters list."""
		self.filtersBox.clear()
		self.filtersBox.addItems(all_orbs_filters_names()) class AspectsRestrictionsManagerDialog(QDialog):
	
	def __init__(self, parent=None):
		QDialog.__init__(self, parent)
		self._parent = parent
		tr = self.tr
		self.setWindowTitle(tr('Aspects Restrictions Manager'))
		self.setSizeGripEnabled(True)
		# layout
		grid = QGridLayout(self)
		self.setLayout(grid)
		# list of filters
		self.filtersBox = QComboBox(self)
		self.filtersBox.setEditable(False)
		self.filtersBox.addItems(all_aspects_restrictions_names())
		grid.addWidget(self.filtersBox, 0, 0)
		# buttons
		buttonsLayout = QHBoxLayout()
		grid.addLayout(buttonsLayout, 1, 0)
		newButton = QPushButton(tr('New'), self)
		self.connect(newButton, SIGNAL('clicked()'), self.newFilterEvent)
		buttonsLayout.addWidget(newButton)
		editButton = QPushButton(tr('Edit'), self)
		self.connect(editButton, SIGNAL('clicked()'), self.editFilterEvent)
		buttonsLayout.addWidget(editButton)
		deleteButton = QPushButton(tr('Delete'), self)
		self.connect(deleteButton, SIGNAL('clicked()'), self.deleteFilterEvent)
		buttonsLayout.addWidget(deleteButton)
		closeButton = QPushButton(tr('Close'), self)
		closeButton.setDefault(True)
		self.connect(closeButton, SIGNAL('clicked()'), SLOT('close()'))
		buttonsLayout.addWidget(closeButton)
	
	def newFilterEvent(self):
		new = AspectsRestrictionsDialog(self)
		ok = new.exec_()
		if ok:
			self.reset()
	
	def editFilterEvent(self):
		filt = AspectsRestrictions(
			all_aspects_restrictions_names()[self.filtersBox.currentIndex()])
		edit = AspectsRestrictionsDialog(self, filt)
		ok = edit.exec_()
		if ok:
			self.reset()
	
	def deleteFilterEvent(self):
		tr = self.tr
		name = all_aspects_restrictions_names()[self.filtersBox.currentIndex()]
		ret = QMessageBox.warning(self, tr('Delete Aspects Restrictions'),
			unicode(tr('Are you sure you want to delete aspects restrictions \xab %(filter)s \xbb ?')) % {
				'filter': name},
			QMessageBox.Cancel|QMessageBox.Yes, QMessageBox.Yes)
		if ret == QMessageBox.Yes:
			filt = AspectsRestrictions(name)
			try:
				filt.delete()
			except ValueError: # cannot delete default filter
				QMessageBox.critical(self, tr('Required Aspects Restrictions'),
					tr('Cannot delete required aspects restrictions.'))
			self.reset()
	
	def reset(self):
		"""Reload filters list."""
		self.filtersBox.clear()
		self.filtersBox.addItems(all_aspects_restrictions_names()) class OrbsRestrictionsManagerDialog(QDialog):
	
	def __init__(self, parent=None):
		QDialog.__init__(self, parent)
		self._parent = parent
		tr = self.tr
		self.setWindowTitle(tr('Orbs Restrictions Manager'))
		self.setSizeGripEnabled(True)
		# layout
		grid = QGridLayout(self)
		self.setLayout(grid)
		# list of filters
		self.filtersBox = QComboBox(self)
		self.filtersBox.setEditable(False)
		self.filtersBox.addItems(all_orbs_restrictions_names())
		grid.addWidget(self.filtersBox, 0, 0)
		# buttons
		buttonsLayout = QHBoxLayout()
		grid.addLayout(buttonsLayout, 1, 0)
		newButton = QPushButton(tr('New'), self)
		self.connect(newButton, SIGNAL('clicked()'), self.newFilterEvent)
		buttonsLayout.addWidget(newButton)
		editButton = QPushButton(tr('Edit'), self)
		self.connect(editButton, SIGNAL('clicked()'), self.editFilterEvent)
		buttonsLayout.addWidget(editButton)
		deleteButton = QPushButton(tr('Delete'), self)
		self.connect(deleteButton, SIGNAL('clicked()'), self.deleteFilterEvent)
		buttonsLayout.addWidget(deleteButton)
		closeButton = QPushButton(tr('Close'), self)
		closeButton.setDefault(True)
		self.connect(closeButton, SIGNAL('clicked()'), SLOT('close()'))
		buttonsLayout.addWidget(closeButton)
	
	def newFilterEvent(self):
		new = OrbsRestrictionsDialog(self)
		ok = new.exec_()
		if ok:
			self.reset()
	
	def editFilterEvent(self):
		filt = OrbsRestrictions(
			all_orbs_restrictions_names()[self.filtersBox.currentIndex()])
		edit = OrbsRestrictionsDialog(self, filt)
		ok = edit.exec_()
		if ok:
			self.reset()
	
	def deleteFilterEvent(self):
		tr = self.tr
		name = all_orbs_restrictions_names()[self.filtersBox.currentIndex()]
		ret = QMessageBox.warning(self, tr('Delete Orbs Restrictions'),
			unicode(tr('Are you sure you want to delete orbs restrictions \xab %(filter)s \xbb ?')) % {
				'filter': name},
			QMessageBox.Cancel|QMessageBox.Yes, QMessageBox.Yes)
		if ret == QMessageBox.Yes:
			filt = OrbsRestrictions(name)
			try:
				filt.delete()
			except ValueError: # cannot delete
				QMessageBox.critical(self, tr('Required Orbs Restrictions'),
					tr('Cannot delete required orbs restrictions.'))
			self.reset()
	
	def reset(self):
		"""Reload filters list."""
		self.filtersBox.clear()
		self.filtersBox.addItems(all_orbs_restrictions_names()) class MidPointsFiltersManagerDialog(QDialog):
	
	def __init__(self, parent=None):
		QDialog.__init__(self, parent)
		self._parent = parent
		tr = self.tr
		self.setWindowTitle(tr('MidPoints Filters Manager'))
		self.setSizeGripEnabled(True)
		# layout
		grid = QGridLayout(self)
		self.setLayout(grid)
		# list of filters
		self.filtersBox = QComboBox(self)
		self.filtersBox.setEditable(False)
		self.filtersBox.addItems(all_midpoints_filters_names())
		grid.addWidget(self.filtersBox, 0, 0)
		# buttons
		buttonsLayout = QHBoxLayout()
		grid.addLayout(buttonsLayout, 1, 0)
		newButton = QPushButton(tr('New'), self)
		self.connect(newButton, SIGNAL('clicked()'), self.newFilterEvent)
		buttonsLayout.addWidget(newButton)
		editButton = QPushButton(tr('Edit'), self)
		self.connect(editButton, SIGNAL('clicked()'), self.editFilterEvent)
		buttonsLayout.addWidget(editButton)
		deleteButton = QPushButton(tr('Delete'), self)
		self.connect(deleteButton, SIGNAL('clicked()'), self.deleteFilterEvent)
		buttonsLayout.addWidget(deleteButton)
		closeButton = QPushButton(tr('Close'), self)
		closeButton.setDefault(True)
		self.connect(closeButton, SIGNAL('clicked()'), SLOT('close()'))
		buttonsLayout.addWidget(closeButton)
	
	def newFilterEvent(self):
		new = MidPointsFilterDialog(self)
		ok = new.exec_()
		if ok:
			self.reset()
	
	def editFilterEvent(self):
		filt = MidPointsFilter(
			all_midpoints_filters_names()[self.filtersBox.currentIndex()])
		edit = MidPointsFilterDialog(self, filt)
		ok = edit.exec_()
		if ok:
			self.reset()
	
	def deleteFilterEvent(self):
		tr = self.tr
		name = all_midpoints_filters_names()[self.filtersBox.currentIndex()]
		ret = QMessageBox.warning(self, tr('Delete MidPoints Filter'),
			unicode(tr('Are you sure you want to delete mid-points filter \xab %(filter)s \xbb ?')) % {
				'filter': name},
			QMessageBox.Cancel|QMessageBox.Yes, QMessageBox.Yes)
		if ret == QMessageBox.Yes:
			filt = MidPointsFilter(name)
			try:
				filt.delete()
			except ValueError: # cannot delete default filter
				QMessageBox.critical(self, tr('Default MidPoints Filter'),
					tr('Cannot delete filter used as default.'))
			self.reset()
	
	def reset(self):
		"""Reload filters list."""
		self.filtersBox.clear()
		self.filtersBox.addItems(all_midpoints_filters_names())
import sys
import os.path
import oroboros
from PyQt4.QtCore import *
from PyQt4.QtGui import *
from oroboros.core import cfg
from oroboros.core import astrolog32
from oroboros.core import skylendar
from oroboros.core import hgrepo
from oroboros.core.charts import Chart
from oroboros.core.filters import Filter
from oroboros.gui import app
from oroboros.gui import translations
from oroboros.gui import actions
from oroboros.gui import menubar
from oroboros.gui import toolbar
from oroboros.gui import chtimage
from oroboros.gui.dockwidget import DockWidget
from oroboros.gui.centralwidget import CentralWidget
from oroboros.gui.chtinfodialog import ChartInfoDialog
from oroboros.gui.newtabdialog import NewTabDialog
from oroboros.gui.saveimagedialog import SaveImageDialog
from oroboros.gui.cfgdialog import CfgDialog
from oroboros.gui.profectiondialog import ProfectionDialog
from oroboros.gui.harmonicsdialog import HarmonicsDialog
from oroboros.gui.filtersmandialog import FiltersManagerDialog
from oroboros.gui.filterdialog import FilterDialog
from oroboros.gui.plntfiltersmandialog import PlanetsFiltersManagerDialog
from oroboros.gui.aspfiltersmandialog import AspectsFiltersManagerDialog
from oroboros.gui.orbfiltersmandialog import OrbsFiltersManagerDialog
from oroboros.gui.asprestrmandialog import AspectsRestrictionsManagerDialog
from oroboros.gui.orbrestrmandialog import OrbsRestrictionsManagerDialog
from oroboros.gui.midpfiltersmandialog import MidPointsFiltersManagerDialog
		menubar.createMenuBar(self)
		# tool bar
		toolbar.createToolBar(self)
		# dock widget
		self.dock = DockWidget(self)
		self.addDockWidget(Qt.LeftDockWidgetArea, self.dock)
		# central widget
		self.central = CentralWidget(self)
		self.setCentralWidget(self.central)
	
	def addTabs(self, idx):
		"""Create the tabs for a new chart."""
		self.dock.addTab(idx)
		self.central.addTab(idx)
		self.resetActions()
	
	def resetTabs(self, idx):
		"""Force reloading a chart."""
		self.dock.resetTab(idx)
		self.central.resetTab(idx)
		self.resetActions()
	
	def removeTabs(self, idx):
		"""Destroy chart tabs."""
		self.dock.removeTab(idx)
		self.central.removeTab(idx)
		self.resetActions()
	
	def dockTabChangedEvent(self):
		idx = self.dock.tabs.currentIndex()
		self.central.setCurrentIndex(idx)
		self.resetActions()
	
	def centralTabChangedEvent(self):
		idx = self.central.currentIndex()
		self.dock.tabs.setCurrentIndex(idx)
		self.resetActions()
	
	def resetActions(self):
		actions.reset(self, app)
	
	# ### about actions events ####
	
	def aboutQtEvent(self):
		QMessageBox.aboutQt(self, self.tr('About Qt'))
	
	def aboutOroborosEvent(self):
		msg = unicode(self.tr("""<qt>This is <b><i>Oroboros</i></b> astrology software (version %(version)s).<br><br>

Copyright &copy; 2008 Stanislas Marquis <a href="mailto:stnsls@gmail.com">stnsls@gmail.com</a><br>
Homepage <a href="http://pypi.python.org/pypi/oroboros">http://pypi.python.org/pypi/oroboros</a><br><br>

This is free software; see the license for copying conditions.<br>
There is NO warranty; not even for MERCHANTABILITY or<br>
FITNESS FOR A PARTICULAR PURPOSE.</qt>""")) % {'version': oroboros.__version__}
		QMessageBox.about(self, self.tr('About Oroboros'), msg)
	
	# ### file actions events ###
	
	def newMultiChartEvent(self):
		"""Open the Newtab dialog."""
		ok, do = NewTabDialog(self).exec_()
		if not ok:
			return
		if do == 0:
			self.hereNowMultiChartEvent()
		elif do == 1:
			self.openMultiChartEvent()
		elif do == 2:
			self.customMultiChartEvent()
		elif do == 3:
			self.openMultiChartA32Event()
		elif do == 4:
			self.openMultiChartSkifEvent()
	
	def hereNowMultiChartEvent(self):
		"""Create a default here-now chart."""
		app.appendMultiChart(Chart())
	
	def openMultiChartEvent(self):
		"""Open oroboros chart file."""
		tr = self.tr
		path = unicode(QFileDialog.getOpenFileName(self, tr('Open chart'),
			os.path.expanduser(cfg.charts_dir), tr('Oroboros (*.xml)')))
		if path != '':
			cht = Chart(path)
			app.appendMultiChart(cht)
	
	def openMultiChartA32Event(self):
		"""Open astrolog32 chart file."""
		tr = self.tr
		path = unicode(QFileDialog.getOpenFileName(self,
			tr('Import Astrolog32 chart'),
			os.path.expanduser(cfg.charts_dir), tr('Astrolog32 (*.dat)')))
		if path != '':
			cht = astrolog32.load(path)
			cht.calc()
			app.appendMultiChart(cht)
	
	def openMultiChartSkifEvent(self):
		"""Open skylendar chart file."""
		tr = self.tr
		path = unicode(QFileDialog.getOpenFileName(self,
			tr('Import Skylendar chart'),
			os.path.expanduser(cfg.charts_dir), tr('Skylendar (*.skif)')))
		if path != '':
			cht = skylendar.load(path)
			cht.calc()
			app.appendMultiChart(cht)
	
	def customMultiChartEvent(self):
		"""Open chart dialog for a new tab."""
		ChartInfoDialog(-1, 0, self).exec_()
	
	def saveImageEvent(self):
		"""Open image dialog and create image file."""
		idx = self.dock.tabs.currentIndex()
		ok, fname, ext, w, h = SaveImageDialog(self).exec_()
		if ok:
			if ext == 'svg':

				chtimage.makeSvg(app.desktop.charts[idx], fname, w, h)