Web   ·   Wiki   ·   Activities   ·   Blog   ·   Lists   ·   Chat   ·   Meeting   ·   Bugs   ·   Git   ·   Translate   ·   Archive   ·   People   ·   Donate
summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorroot <root@ghunt-desktop.(none)>2010-09-13 03:42:28 (GMT)
committer root <root@ghunt-desktop.(none)>2010-09-13 03:42:28 (GMT)
commite27f087758e900341bfe9ef590b8db661656cd80 (patch)
tree6907b7640d5dbfa40081dbb77288e1beacfe142c
parente46eba6769a1e12606c79d462bedeeaebae7d856 (diff)
scroll bar page up, reorder stacks, add stack to trash
-rw-r--r--MANIFEST3
-rw-r--r--activity/activity.info2
-rw-r--r--data_cache.sqlitebin3072 -> 3072 bytes
-rw-r--r--data_cache.sqlite.templatebin3072 -> 5120 bytes
-rw-r--r--dbphoto.py251
-rw-r--r--display.py413
-rw-r--r--ezscroll/ezscroll.py3
-rw-r--r--help/xophoto.html11
-rw-r--r--photo_toolbar.py14
-rw-r--r--sinks.py34
-rw-r--r--sources.py154
-rw-r--r--xophoto.sqlite.templatebin12288 -> 12288 bytes
-rw-r--r--xophotoactivity.py196
13 files changed, 734 insertions, 347 deletions
diff --git a/MANIFEST b/MANIFEST
index 3187472..4b1925d 100644
--- a/MANIFEST
+++ b/MANIFEST
@@ -80,8 +80,9 @@ help/CSS/Accessible_Design.css
help/CSS/_notes/dwsync.xml
assets/handcursor_mask.xbm
assets/handcursor.xbm
-help/imagess
+
help/images/slide_start.png
help/images/delete_composite.png
help/images/initial_screen.jpg
help/images/the_new_album.png
+mkdir_Pictures
diff --git a/activity/activity.info b/activity/activity.info
index 37078a2..e59af4c 100644
--- a/activity/activity.info
+++ b/activity/activity.info
@@ -1,7 +1,7 @@
[Activity]
name = XoPhoto
bundle_id = org.laptop.XoPhoto
-activity_version = 7
+activity_version = 8
show_launcher = yes
icon = XoPhoto
class = xophotoactivity.XoPhotoActivity
diff --git a/data_cache.sqlite b/data_cache.sqlite
index a6a4a57..fd6089f 100644
--- a/data_cache.sqlite
+++ b/data_cache.sqlite
Binary files differ
diff --git a/data_cache.sqlite.template b/data_cache.sqlite.template
index 14ade6d..9da66f5 100644
--- a/data_cache.sqlite.template
+++ b/data_cache.sqlite.template
Binary files differ
diff --git a/dbphoto.py b/dbphoto.py
index 180ae48..fded745 100644
--- a/dbphoto.py
+++ b/dbphoto.py
@@ -86,6 +86,21 @@ class DbAccess():
self.opendb(self.dbfilename)
return self.con
+ def vacuum_data_cache(self):
+ if self.is_open():
+ self.closedb()
+ dbfilename = os.path.join(os.getcwd(),'data_cache.sqlite')
+ try:
+ conn = sqlite3.connect(dbfilename)
+ cursor = conn.cursor()
+ cursor.execute('vacuum')
+ conn.commit()
+ conn.close()
+ self.opendb(self.dbfilename)
+ except Exception,e:
+ _logger.debug('vacuum error %s'%e)
+
+
def is_open(self):
if self.con: return True
return False
@@ -116,7 +131,7 @@ class DbAccess():
def get_albums(self):
cursor = self.connection().cursor()
- cursor.execute('select * from groups where category = ?',('albums',))
+ cursor.execute("select * from groups where category = 'albums' order by seq asc")
return cursor.fetchall()
def get_albums_containing(self,jobject_id):
@@ -190,8 +205,9 @@ class DbAccess():
cur.execute(sql)
rows_md5 = cur.fetchall()
if len(rows_md5) >0:
- pass
- _logger.debug('duplicate picture, ojbect_id %s path: %s'%(object_id,fn,))
+ #pass
+ _logger.debug('duplicate picture, ojbect_id %s path: %s'%(object_id,fn,))
+ return 0
sql = "select * from data_cache.picture where jobject_id = '%s'"%(object_id,)
cur.execute(sql)
rows = cur.fetchall()
@@ -200,9 +216,9 @@ class DbAccess():
info = os.stat(fn)
if len(rows) == 0:
sql = """insert into data_cache.picture \
- (in_ds, mount_point, orig_size, create_date,jobject_id, md5_sum, duplicate) \
- values (%s,'%s',%s,'%s','%s','%s',%s)""" % \
- (1, fn, info.st_size, info.st_ctime, object_id, md5_hash,len(rows_md5),)
+ (in_ds, mount_point, orig_size, create_date,jobject_id, md5_sum) \
+ values (%s,'%s',%s,'%s','%s','%s')""" % \
+ (1, fn, info.st_size, info.st_ctime, object_id, md5_hash,)
cursor = self.connection().cursor()
cursor.execute(sql)
self.con.commit()
@@ -217,6 +233,18 @@ class DbAccess():
_logger.debug('%s seconds to update'%(time.clock()-start))
return 0
+ def is_picture_in_ds(self,fn):
+ md5_hash = Md5Tools().md5sum(fn)
+ sql = "select * from data_cache.picture where md5_sum = '%s'"%(md5_hash,)
+ conn = self.connection()
+ cur = conn.cursor()
+ cur.execute(sql)
+ rows_md5 = cur.fetchall()
+ if len(rows_md5) >0:
+ return True
+ return False
+
+
def put_ds_into_picture(self,jobject_id):
self.cur.execute("select * from data_cache.picture where jobject_id = ?",(jobject_id,))
rows = self.cur.fetchall()
@@ -319,7 +347,7 @@ class DbAccess():
rows = cursor.fetchmany()
if len(rows) == 1:
try:
- cursor.execute('update groups set seq = ? where id = ?',(count,rows[0]['id']))
+ cursor.execute('update groups set stack_size = ? where id = ?',(count,rows[0]['id']))
self.con.commit()
except Exception,e:
_logger.debug('set album count error:%s'%e)
@@ -336,19 +364,60 @@ class DbAccess():
return 0
def create_update_album(self,album_id,name):
- cursor = self.connection().cursor()
+ conn = self.connection()
+ cursor = conn.cursor()
cursor.execute('select * from groups where category = ? and subcategory = ?',\
('albums',str(album_id,)))
rows = cursor.fetchmany()
_logger.debug('create-update found %s records. album:%s. Name:%s'%(len(rows),album_id,name,))
- if len(rows)>0 : #pick up the name
+ if len(rows)>0 : #pick up the id
id = rows[0]['id']
- cursor.execute("update groups set subcategory = ?, jobject_id = ? where id = ?",\
+ cursor.execute("update groups set subcategory = ?, stack_name = ? where id = ?",\
(str(album_id),name,id))
else:
- cursor.execute("insert into groups (category,subcategory,jobject_id,seq) values (?,?,?,?)",\
- ('albums',str(album_id),name,0))
- self.con.commit()
+ cursor.execute("insert into groups (category,subcategory,stack_name,seq) values (?,?,?,?)",\
+ ('albums',str(album_id),name,30))
+ conn.commit()
+ if len(rows)>0:
+ return
+
+ rows = self.get_albums()
+ if len(rows)>0:
+ #now go through and rewrite the sequence numbers
+ #need another cursor
+ update_cursor = conn.cursor()
+ num = 0
+ for row in rows:
+ update_cursor.execute("update groups set seq = ? where id = ?",(num,row['id'],))
+ num += 20
+ conn.commit()
+ else:
+ _logger.debug('failed to get albums for resequence in create_update_album')
+
+ def reorder_albums(self,album_id,seq):
+ conn = self.connection()
+ cursor = conn.cursor()
+ cursor.execute('select * from groups where category = "albums" and subcategory = ?',\
+ (str(album_id),))
+ rows = cursor.fetchmany()
+ _logger.debug('reorder_albums found %s records. album:%s.'%(len(rows),album_id,))
+ if len(rows)>0 : #pick up the id
+ id = rows[0]['id']
+ cursor.execute("update groups set seq = ? where id = ?",(seq,id))
+ conn.commit()
+ rows = self.get_albums()
+ if len(rows)>0:
+ #now go through and rewrite the sequence numbers
+ #need another cursor
+ update_cursor = conn.cursor()
+ num = 0
+ for row in rows:
+ update_cursor.execute("update groups set seq = ? where id = ?",(num,row['id'],))
+ num += 20
+ conn.commit()
+ else:
+ _logger.debug('failed to get albums for resequence in create_update_album')
+
def add_image_to_album(self, album_id, jobject_id):
cursor = self.connection().cursor()
@@ -376,7 +445,7 @@ class DbAccess():
(str(album_id), str(jobject_id),))
conn.commit()
- def write_transform(self,jobject_id,w,h,x_thumb,y_thumb,image_blob,rec_id = None,transform_type='thumb',rotate_left=1,seq=0):
+ def write_transform(self,jobject_id,w,h,x_thumb,y_thumb,image_blob,rec_id = None,transform_type='thumb',rotate_left=0,seq=0):
_logger.debug('write_transform for rec_id %s'%rec_id)
if image_blob:
thumbstr = pygame.image.tostring(image_blob,'RGB')
@@ -390,8 +459,9 @@ class DbAccess():
cursor.execute("""update data_cache.transforms set thumb = ?, scaled_x = ?, scaled_y = ?, rotate_left = ?
where id = ?""",(thumb_binary,x_thumb,y_thumb,rotate_left,rec_id))
else:
- cursor.execute("""insert into data_cache.transforms (jobject_id,original_x,original_y,scaled_x,scaled_y,thumb,transform_type,seq)
-values (?,?,?,?,?,?,?,?)""",(jobject_id,w,h,x_thumb,y_thumb,thumb_binary,transform_type,seq))
+ cursor.execute("""insert into data_cache.transforms (jobject_id,original_x,original_y,
+ scaled_x,scaled_y,thumb,transform_type,rotate_left,seq)
+values (?,?,?,?,?,?,?,?,?)""",(jobject_id,w,h,x_thumb,y_thumb,thumb_binary,transform_type,rotate_left,seq))
except sqlite3.Error,e:
_logger.debug('write thumbnail error %s'%e)
return None
@@ -406,27 +476,33 @@ values (?,?,?,?,?,?,?,?)""",(jobject_id,w,h,x_thumb,y_thumb,thumb_binary,transfo
def delete_all_references_to(self,jobject_id):
conn = self.connection()
cursor = conn.cursor()
- try:
- cursor.execute('begin transaction')
- cursor.execute("delete from groups where jobject_id = ?",\
- (str(jobject_id),))
- cursor.execute("delete from groups where subcategory = ?",\
- (str(jobject_id),))
- cursor.execute("delete from data_cache.picture where jobject_id = ?",\
- (str(jobject_id),))
- cursor.execute("delete from data_cache.transforms where jobject_id = ?",\
- (str(jobject_id),))
- conn.commit()
- except Exception,e:
- cursor.execute('rollback transaction')
- _logger.error('error deleting all references for object:%s. Error: ;%s'%(jobject_id,e,))
-
+ self.delete_if_exists('groups','jobject_id',jobject_id)
+ self.delete_if_exists('groups','subcategory',jobject_id)
+ self.delete_if_exists('data_cache.picture','jobject_id',jobject_id)
+ self.delete_if_exists('data_cache.transforms','jobject_id',jobject_id)
+
+ def delete_if_exists(self,table,field,value):
+ conn = self.connection()
+ cursor = conn.cursor()
+ sql = 'select * from %s where %s = ?'%(table, field,)
+ cursor.execute(sql,(value,))
+ rows = cursor.fetchall()
+ if len(rows) > 0:
+ try:
+ sql = "delete from %s where %s = '%s'"%(table,field,str(value),)
+ cursor.execute(sql)
+ conn.commit()
+ except Exception,e:
+ _logger.error('error deleting all references for object:%s sql:%s. Error: ;%s'%(jobject_id,sql,e,))
+
+
+
def set_config(self,name,value):
cursor = self.connection().cursor()
cursor.execute('select * from config where name = ?',(name,))
rows = cursor.fetchall()
if len(rows)>0:
- cursor.execute("update config set value = ? where id = ?",(album_id,rows[0]['id']))
+ cursor.execute("update config set value = ? where id = ?",(value,rows[0]['id']))
else:
cursor.execute("insert into config (name,value) values (?,?)",(name,value,))
self.con.commit()
@@ -440,6 +516,39 @@ values (?,?,?,?,?,?,?,?)""",(jobject_id,w,h,x_thumb,y_thumb,thumb_binary,transfo
else:
return ''
+ def set_lookup(self,name,value):
+ cursor = self.connection().cursor()
+ cursor.execute('select * from data_cache.lookup where name = ?',(name,))
+ rows = cursor.fetchall()
+ if len(rows)>0:
+ cursor.execute("update data_cache.lookup set value = ? where id = ?",(value,rows[0]['id']))
+ else:
+ cursor.execute("insert into data_cache.lookup (name,value) values (?,?)",(name,value,))
+ self.con.commit()
+
+ def get_lookup(self,name):
+ cursor = self.connection().cursor()
+ cursor.execute('select * from data_cache.lookup where name = ?',(name,))
+ rows = cursor.fetchall()
+ if len(rows)>0:
+ return rows[0]['value']
+ else:
+ return ''
+
+ def change_album_id(self,from_id,to_id):
+ conn = self.connection()
+ cur = conn.cursor()
+ cur.execute('select * from groups where category = ?',(from_id,))
+ rows = cur.fetchall()
+ if len(rows) == 0:
+ _logger.debug('change_album_id did not fetch category=%s'%from_id)
+ return
+ #need another cursor
+ update_cursor = conn.cursor()
+ for row in rows:
+ update_cursor.execute("update groups set category = ? where id = ?",(to_id,row['id'],))
+ conn.commit()
+
def table_exists(self,table):
try:
sql = 'select * from %s'%table
@@ -508,25 +617,75 @@ values (?,?,?,?,?,?,?,?)""",(jobject_id,w,h,x_thumb,y_thumb,thumb_binary,transfo
except sqlite.Error, e:
print sql+'\n'
return False,e
-"""
-class osfsys():
- def havewriteaccess(self,writefile):
+ def upgrade_db_copy_data(self,new_db,old_db):
try:
- fh=open(writefile,'w+')
- fh.close()
- return True
- except:
+ conn = sqlite3.connect(new_db)
+ cursor = conn.cursor()
+ sql = "attach '%s' as data"%old_db
+ _logger.debug('attaching using sql %s'%sql)
+ cursor.execute(sql)
+ old_conn = sqlite3.connect(old_db)
+ old_cursor = old_conn.cursor()
+ except Exception,e:
+ if not os.path.isfile(new_db) or not os.path.isfile(old_db):
+ _logger.debug('upgrade_db path problems new:%s old:%s'%(new_db,old_db,))
+ _logger.debug('open database failed. exception :%s '%(e,))
return False
+ cursor.execute("select tbl_name from sqlite_master where type = 'table'")
+ table_rows = cursor.fetchall()
+
+ #get the tables in the old database
+ cursor.execute("select tbl_name from data.sqlite_master where type = 'table'")
+ data_rows = cursor.fetchall()
+
+ for table in table_rows:
+ for data_row in data_rows:
+ if table[0] == data_row[0]:
+ #the two databases have the same table, continue
+ pragma_spec = 'pragma table_info(%s)'%table[0]
+ cursor.execute(pragma_spec)
+ new_fields = cursor.fetchall()
- def havereadaccess(self,readfile):
+ pragma_spec = 'pragma table_info(%s)'%table[0]
+ old_cursor.execute(pragma_spec)
+ old_fields = old_cursor.fetchall()
+
+ #if both tables have a field, try to transfer the data
+ field_list = []
+ for new_field in new_fields:
+ if new_field[1] == 'id' or new_field[1] == 'rowid': continue
+ for old_field in old_fields:
+ if new_field[1] == old_field[1]:
+ field_list.append(old_field[1])
+ fields = ', '.join(field_list)
+ sql = 'insert into %s (%s) select %s from data.%s'%(table[0],fields,fields,table[0],)
+ _logger.debug('upgrade sql:%s'%sql)
+ try:
+ cursor.execute(sql)
+ except Exception,e:
+ _logger.debug('insert into %s database failed. exception :%s '%(table[0],e,))
+ return False
+ conn.commit()
+ conn.close()
+ return True
+
+ def get_user_version(self,path):
try:
- fh=open(readfile,'r')
- fh.close()
- return True
- except:
- return False
-"""
+ conn = sqlite3.connect(path)
+ cursor = conn.cursor()
+ except Exception,e:
+ if not os.path.isfile(path):
+ _logger.debug('upgrade_db path problems new:%s old:%s'%(new_db,old_db,))
+ _logger.debug('get user version. exception :%s '%(e,))
+ return -1
+ cursor.execute('pragma user_version')
+ rows = cursor.fetchall()
+ conn.close()
+ if len(rows)>0:
+ return rows[0][0]
+ return 0
+
class Md5Tools():
def md5sum_buffer(self, buffer, hash = None):
if hash == None:
diff --git a/display.py b/display.py
index bee090e..97b86da 100644
--- a/display.py
+++ b/display.py
@@ -233,7 +233,7 @@ class OneThumbnail():
try:
self.db.create_picture_record(ds_obj.object_id,fn)
except PhotoException,e:
- _logger.debug('create_picture_record returned exception %s'%e)
+ _logger.error('create_picture_record returned exception %s'%e)
return None
finally:
ds_obj.destroy()
@@ -381,7 +381,7 @@ class OneAlbum():
#as we fetch the thumbnails record the number for the left column display
self.db.set_album_count(self.album_id,len(self.rows))
- _logger.debug('number of thumbnails found for %s was %s'%(self.album_id,len(self.rows)))
+ #_logger.debug('number of thumbnails found for %s was %s'%(self.album_id,len(self.rows)))
#protect from an empty database
#if len(self.rows) == 0: return
@@ -389,12 +389,17 @@ class OneAlbum():
if self.thumbnail_world and num_rows == self.num_rows and not new_surface:
self.repaint()
return
-
- self.thumbnail_world = pygame.Surface((screen_w-album_column_width-thick,self.xy_size*num_rows))
+
+ #bug 2234 deleted images not deleted from screen
+ world_y = self.xy_size*num_rows
+ if world_y < screen_h:
+ world_y = screen_h
+
+ self.thumbnail_world = pygame.Surface((screen_w-album_column_width-thick,world_y))
self.thumbnail_world.fill(background_color)
num_pict = len(self.rows)
self.num_rows = num_rows
- _logger.debug('display many thumbnails in range %s,world y:%s'%(num_pict, self.xy_size*num_rows,))
+ #_logger.debug('display many thumbnails in range %s,world y:%s'%(num_pict, self.xy_size*num_rows,))
start_time = time.clock()
for i in range(num_pict):
self.pict_dict[i] = OneThumbnail(self.rows,self.db,self.thumbnail_world,i)
@@ -414,6 +419,7 @@ class OneAlbum():
_logger.debug('paint thumbnail %s of %s in %s seconds'%(i,num_pict,(time.clock() - start_time)))
start_time = time.clock()
self.release_cycles()
+ self.make_visible(i)
self.repaint()
def repaint(self):
@@ -493,7 +499,7 @@ class OneAlbum():
#bug 2234 thumbnail count incorrect until click on left column
count = self.db.get_thumbnail_count(rows[index]['subcategory'])
- album = rows[index]['jobject_id']
+ album = rows[index]['stack_name']
if album_id == trash_id:
if count > 0:
@@ -513,7 +519,7 @@ class OneAlbum():
text_rect = text.get_rect()
text_rect.midbottom = surf.get_rect().midbottom
surf.blit(text,text_rect)
- _logger.debug('one album %s'%album)
+ #_logger.debug('one album %s selected:%s'%(album,selected,))
return surf
def put_image_on_stack(self,album_id):
@@ -549,35 +555,31 @@ class OneAlbum():
def set_top_image(self,jobject_id):
self.jobject_id = jobject_id
- def insert_after(self,after_index, thumbnail_index):
- #get the seq of the row[after_index]
- if after_index < 0 or after_index >= len(self.rows): return
- if after_index < 0:
- adder = -1
- else:
- adder = +1
- seq = self.rows[after_index]['seq']
- id = self.rows[thumbnail_index]['id']
- self.db.update_resequence(id,seq+adder)
- self.paint(True)
-
- def click(self,x,y):
+ def get_thumb_index_at_xy(self,screen_x,screen_y):
+ """returns thumb index or -1 if error"""
+ if screen_x < album_column_width or screen_y > screen_h: return -1
#map from thumbnail_surface to thumbnail_world
if self.sb:
(sb_x,sb_y) = self.sb.get_scrolled()
else:
(sb_x,sb_y) = [0,0]
- thumb_index = int(((y + sb_y) // self.xy_size) * self.pict_per_row + \
- (x - album_column_width) // self.xy_size)
- _logger.debug('click index:%s'%thumb_index)
+ thumb_index = int(((screen_y + sb_y) // self.xy_size) * self.pict_per_row + \
+ (screen_x - album_column_width) // self.xy_size)
if thumb_index < 0: thumb_index = 0
if thumb_index < len(self.rows):
- self.thumb_index = thumb_index
+ thumb_index = thumb_index
else:
- self.thumb_index = len(self.rows) - 1
- self.select_pict(self.thumb_index)
- self._parent._activity.edit_toolbar.set_jobject_id(self.rows[self.thumb_index]['jobject_id'])
- return self.thumb_index
+ thumb_index = len(self.rows) - 1
+ return thumb_index
+
+ def click(self,x,y):
+ t_index = self.get_thumb_index_at_xy(x,y)
+ if t_index > -1:
+ self.thumb_index =t_index
+ self.select_pict(self.thumb_index)
+ self._parent._activity.edit_toolbar.set_jobject_id(self.rows[self.thumb_index]['jobject_id'])
+ return self.thumb_index
+ return 0
def get_jobject_id_at_xy(self,x,y):
"""returns the thumbnail jobject_id at x,y"""
@@ -604,31 +606,6 @@ class OneAlbum():
return None
- def toggle(self,x,y):
- if not self.large_displayed:
- self.large_displayed = True
- #restore the number of rows
- self.num_rows_save = self.num_rows
- self.num_rows = 1
- self.origin_row = self.thumb_index // self.pict_per_row
- self.thumbnail_world.fill(background_color)
- self.one_large()
- else:
- self.large_displayed = False
- self.num_rows = self.num_rows_save
- self.thumbnail_world.fill(background_color)
- #following call paints the thumnails
- self.paint()
-
- def one_large(self):
- #figure out what size to paint
- y_size = screen_h - self.xy_size
- x_pos = (screen_w - album_column_width - y_size) / 2
- disp_one = OneThumbnail(self.rows,self.db,self.thumbnail_world,self.thumb_index)
- disp_one.position(x_pos,self.xy_size)
- disp_one.size(y_size,y_size)
- disp_one.paint_thumbnail() #this one will be larger than a thumbnail
-
def screen_width(self,width):
self.screen_width = width
@@ -674,15 +651,30 @@ class OneAlbum():
self.sb.scroll(num)
return
- row = num // self.pict_per_row
- min_y = row * self.xy_size
- if min_y < y:
- self.sb.scroll(-self.xy_size * self.sb.ratio)
- max_y = (row + 1) * self.xy_size
- if max_y > y + screen_h:
- self.sb.scroll(self.xy_size * self.sb.ratio)
+ def page_down(self):
+ _logger.debug('page down thumbnails')
+ if not self.sb: return
+ x,y = self.sb.get_scrolled()
+ num_rows = screen_h // self.xy_size
+ scroll_pixels = (num_rows -1) * self.xy_size
+ if scroll_pixels > y:
+ scroll_pixels = y
+ self.sb.scroll(-scroll_pixels * self.sb.ratio)
self.repaint()
+
+ def page_up(self):
+ _logger.debug('page up thumbnails')
+ if not self.sb: return
+ x,y = self.sb.get_scrolled()
+ num_rows = screen_h // self.xy_size
+ scroll_pixels = (num_rows -1) * self.xy_size
+ w,h = self.thumbnail_world.get_size()
+ if scroll_pixels + y > h - self.xy_size:
+ scroll_pixels = h - self.xy_size - y
+ self.sb.scroll(scroll_pixels * self.sb.ratio)
+ self.repaint()
+
def select_pict(self,num):
if self.last_selected:
@@ -690,8 +682,6 @@ class OneAlbum():
if not self.pict_dict.has_key(num): return
self.make_visible(num)
self.last_selected = self.pict_dict[num].select()
- if self.large_displayed:
- self.one_large()
#screen.blit(self.thumbnail_world,(album_column_width,0))
#self.thumbnail_panel(self.thumbnail_world)
self.repaint()
@@ -760,7 +750,7 @@ class DisplayAlbums():
self.album_height = album_height
#figure out how many albums can be displayed
- self.max_albums_displayed = screen_h // self.album_height
+ self.max_albums_displayed = 0
#prepare a surface to clear the albums
self.album_surface = pygame.Surface((album_column_width,screen_h)).convert()
@@ -773,32 +763,18 @@ class DisplayAlbums():
if len(rows) == 0: #it is not initialized
#first put the predefined names in the list of albums
for album_tup in self.predefined_albums:
- sql = """insert into groups (category,subcategory,jobject_id,seq) \
+ sql = """insert into groups (category,subcategory,stack_name,seq) \
values ('%s','%s','%s',%s)"""%('albums',album_tup[0],album_tup[1],i,)
self.db.dbtry(sql)
+ i += 20
self.db.commit()
- """this needs to be done whenever new pictures are added to journal so not now
- #then put the journal picutres into the journal album
- rows, cur = self.db.dbdo('select * from data_cache.picture')
- i = 0
- conn = self.db.get_connection()
- cursor = conn.cursor()
- if len(rows)>0:
- for row in rows:
- sql = "insert into groups (category,subcategory,jobject_id,seq) values ('%s','%s','%s',%s)"%\
- (self.predefined_albums[0][0],self.predefined_albums[0][1],row['jobject_id'],i,)
- cursor.execute(sql)
- i += 20
- conn.commit()
- """
+
#initialize the list of album objects from the database
album_rows = self.db.get_albums()
- _logger.debug('initializing albums. %s found'%len(album_rows))
+ #_logger.debug('initializing albums. %s found'%len(album_rows))
for row in album_rows:
id = str(row['subcategory'])
self.album_objects[id] = OneAlbum(self.db,id,self)
- #the initial screen will show the contents of the journal
- #self.display_journal()
def display_thumbnails(self,album_id,new_surface=False):
"""uses the album (a datetime str) as value for category in table groups
@@ -868,11 +844,12 @@ class DisplayAlbums():
self.refresh_album_rows()
if len(self.album_rows) > 0:
self.clear_albums()
- self.world = pygame.Surface((album_column_width, 8 * self.album_height)) #len(self.album_rows) * self.album_height))
+ self.world = pygame.Surface((album_column_width, len(self.album_rows) * self.album_height))
self.world.fill(album_background_color)
- #if this is first time through, init the scroll bar system
- if not self.album_sb:
+ #if this is first time through, init the scroll bar system or if world is changed, scroll new world
+ if not self.album_sb or self.max_albums_displayed != len(self.album_rows):
+ self.max_albums_displayed = len(self.album_rows)
surf = self.album_panel(self.world)
num_albums = len(self.album_rows)
@@ -891,19 +868,15 @@ class DisplayAlbums():
pygame.display.flip()
def refresh_album_rows(self):
- """ rows array, will need to change if albums become reorderable"""
- sql = "select * from groups where category = 'albums' order by id"
- rows,cur = self.db.dbdo(sql)
- self.number_of_albums = len(rows)
- #keep a permanent reference to the list of albums
- self.album_rows = rows
-
+ self.album_rows = self.db.get_albums()
+ self.number_of_albums = len(self.album_rows)
+
def click(self,x,y):
"""select the pointed to album"""
#get the y index
sb_x,sb_y = self.album_sb.get_scrolled()
y_index = (y + sb_y) // self.album_height
- _logger.debug('y:%s sb_y:%s Index:%s in_click'%(y,sb_y,y_index,))
+ #_logger.debug('y:%s sb_y:%s Index:%s in_click'%(y,sb_y,y_index,))
self.album_index = int(y_index)
self.refresh_album_rows()
if self.album_index >= len(self.album_rows) :
@@ -914,7 +887,7 @@ class DisplayAlbums():
#now change the thumbnail side of the screen
try:
album_timestamp = self.album_rows[int(self.album_index)]['subcategory']
- album_title = self.album_rows[int(self.album_index)]['jobject_id']
+ album_title = self.album_rows[int(self.album_index)]['stack_name']
except Exception,e:
album_timestamp = journal_id #the journal
_logger.debug('exception fetching thumbnails %s'%e)
@@ -924,8 +897,8 @@ class DisplayAlbums():
self._activity.activity_toolbar.empty_journal_button.show()
else:
self._activity.activity_toolbar.empty_journal_button.hide()
- _logger.debug('now display album at index %s thumbnails with the album identifier %s'%
- (self.album_index,album_timestamp))
+ #_logger.debug('now display album at index %s thumbnails with the album identifier %s'%
+ #(self.album_index,album_timestamp))
change_name = True
for id,name in self.predefined_albums:
if album_timestamp == id:
@@ -967,7 +940,7 @@ class DisplayAlbums():
if album_object and album_object.last_selected:
album_object.last_selected.unselect()
album_object.thumb_index = self.db.get_thumbnail_count(self.accumulation_target) - 1
- _logger.debug('get_thumbnail_count returned %s'%album_object.thumb_index)
+ #_logger.debug('get_thumbnail_count returned %s'%album_object.thumb_index)
self.db.set_album_count(self.accumulation_target,album_object.thumb_index + 1)
@@ -981,7 +954,7 @@ class DisplayAlbums():
def set_name(self,name):
album_timestamp = str(self.album_rows[self.album_index]['subcategory'])
for id,predefined_name in self.predefined_albums:
- _logger.debug('album_timestamp: %s id:%s'%(album_timestamp,id,))
+ #_logger.debug('album_timestamp: %s id:%s'%(album_timestamp,id,))
if album_timestamp == id:
return
self.db.create_update_album(album_timestamp,name)
@@ -1000,7 +973,7 @@ class DisplayAlbums():
self.paint_albums()
self.album_index = self.get_index_of_album_id(self.accumulation_target)
- def delete_album(self,album_id):
+ def delete_album(self,album_id,selected=journal_id):
_logger.debug('delete album action routine. deleting id(timestamp):%s'%album_id)
""" need to delete the album pointer, the album records, and album object"""
if self.album_objects.has_key(album_id):
@@ -1011,9 +984,9 @@ class DisplayAlbums():
cursor.execute('delete from groups where category = ?',(str(album_id),))
cursor.execute('delete from config where name = ? and value = ?',('last_album',str(album_id),))
conn.commit()
- self.album_index = 0
- self.selected_album_id = self.journal_id
- self.display_thumbnails(self.selected_album_id)
+ self.album_index = self.get_index_of_album_id(selected)
+ self.selected_album_id = selected
+ self.display_thumbnails(self.selected_album_id,new_surface=True)
self.paint_albums()
@@ -1036,14 +1009,16 @@ class DisplayAlbums():
return str(self.album_rows[index]['subcategory'])
def get_current_album_name(self):
- return str(self.album_rows[self.album_index]['jobject_id'])
+ return str(self.album_rows[self.album_index]['stack_name'])
def get_album_index_at_xy(self,x,y):
- if x > album_column_width - thick: return None
+ if x > album_column_width - thick: return -1
#get the y index
sb_x,sb_y = self.album_sb.get_scrolled()
y_index = (y + sb_y) // self.album_height
index = int(y_index)
+ if index > len(self.album_rows) - 1:
+ return -1
return index
def add_to_album_at_xy(self,x,y):
@@ -1065,6 +1040,9 @@ class DisplayAlbums():
in_grab = True
self.start_grab_x = x
self.start_grab_y = y
+ self.set_hand_cursor()
+
+ def set_hand_cursor(self):
fn = os.path.join(os.getcwd(),'assets','closed_hand.xbm')
bitstring = self.xbm_to_data(fn)
if len(bitstring) == 0: return #error state
@@ -1076,7 +1054,7 @@ class DisplayAlbums():
bitmask = gtk.gdk.bitmap_create_from_data(None,maskstring,48,48)
self._activity.window.set_cursor(gtk.gdk.Cursor(bitmask,bitpattern,\
- gtk.gdk.Color(255,255,255),gtk.gdk.Color(255,255,255),24,24))
+ gtk.gdk.Color(255,255,255),gtk.gdk.Color(0,0,0),24,24))
def xbm_to_data(self,xbm_file):
if not os.path.isfile(xbm_file):return ''
@@ -1131,74 +1109,125 @@ class DisplayAlbums():
_logger.debug('drop_image start_x:%s start_y:%s drop_x:%s drop_y:%s'%(start_x,start_y,drop_x,drop_y,))
if drop_x < album_column_width: #we are dropping on album side of screen
- jobject_id = self.album_objects[self.selected_album_id].get_jobject_id_at_xy(start_x,start_y)
- index = self.get_album_index_at_xy(drop_x, drop_y)
- if not index or not jobject_id: return
- current_album_id = self.get_current_album_identifier()
- _logger.debug('drop_image index:%s jobject_id:%s current_album_id:%s'%(index,jobject_id,current_album_id,))
+ if start_x < album_column_width: #we started dragging on the album side of the screen
+ self.drag_album_onto_album(start_x,start_y,drop_x,drop_y)
+ else: #dragging a thumbnail
+ self.drag_thumbnail_onto_album(start_x,start_y,drop_x,drop_y)
+ #the drop was on thumbnail side of screen, this is a reorder request
+ else:
+ #map from thumbnail_surface to thumbnail_world
+ start_index = self.album_objects[self.selected_album_id].get_thumb_index_at_xy(start_x, start_y)
+ if start_index == -1: return
+ drop_index = self.album_objects[self.selected_album_id].get_thumb_index_at_xy(drop_x, drop_y)
+ drop_seq = self.album_objects[self.selected_album_id].rows[drop_index]['seq']
+ #the journal is displayed last to first
+ if self.selected_album_id == journal_id:
+ if start_index > drop_index:
+ new_seq = drop_seq + 1
+ else:
+ new_seq = drop_seq - 1
+ else:
+ if start_index < drop_index:
+ new_seq = drop_seq + 1
+ else:
+ new_seq = drop_seq - 1
+ groups_rec_id = self.album_objects[self.selected_album_id].rows[start_index]['id']
+ _logger.debug('calling update_resequendce with params %s,%s start_index:%s drop_index: %s drop_seq:%s new_seq:%s'%
+ (groups_rec_id,new_seq,start_index,drop_index,drop_seq,new_seq,))
+ self.db.update_resequence(groups_rec_id,new_seq)
+ self.album_objects[self.selected_album_id].paint(True)
- #if item dragged from trash, put it back in journal_id as well as target stack
- if current_album_id == trash_id:
- self.db.delete_image(trash_id,jobject_id)
- self.album_objects[current_album_id].paint(True)
-
- #unconditionally add the thumbnail back into the list of journal pictures
- self.db.add_image_to_album(journal_id, jobject_id)
- self.album_objects[journal_id].thumbnail_redo_world = True
- if current_album_id != journal_id: #add it to the target also
- if index > len(self.album_rows)-1:
- self.create_new_album(self.default_name)
- self.db.add_image_to_album(self.accumulation_target, jobject_id)
- self.album_objects[self.accumulation_target].set_top_image(jobject_id)
- else:
- self.db.add_image_to_album(self.album_rows[index]['subcategory'], jobject_id)
- #guarantee full repaint of thumbnails on next repaint
- self.album_objects[trash_id].thumbnail_redo_world = True
- self.refresh_album_rows()
- self.paint_albums()
- return
-
- #if dropped on the trash icon
- if self.get_album_id_at_index(index) == trash_id: #a request to delete
- self.db.delete_image(current_album_id,jobject_id)
- self.album_objects[current_album_id].paint(True)
- if current_album_id == journal_id:
- self.db.add_image_to_album(self.album_rows[index]['subcategory'], jobject_id)
- self.refresh_album_rows()
- self.paint_albums()
- return
-
- #if index is larger than max, we want a new album
- _logger.debug('index:%s length of rows:%s jobject_id %s'%(index,len(self.album_rows),jobject_id,))
- if index > len(self.album_rows)-1:
- self.create_new_album(self.default_name)
- self.db.add_image_to_album(self.accumulation_target, jobject_id)
- self.album_objects[self.accumulation_target].set_top_image(jobject_id)
+ def drag_album_onto_album(self,start_x,start_y,drop_x,drop_y):
+ from_index = self.get_album_index_at_xy(start_x, start_y)
+ to_index = self.get_album_index_at_xy(drop_x, drop_y)
+ if from_index == -1 or to_index == -1:
+ _logger.debug('drag_album_onto_album lookup failure from index:%s to index:%s '%(from_index,to_index,))
+ return
+ from_album_id = self.get_album_id_at_index(from_index)
+ to_album_id = self.get_album_id_at_index(to_index)
+ if to_album_id == journal_id: return #silently do nothing
+ if to_album_id == trash_id: #this is a album delete-all request
+ caption = _('Caution -- This is a multiple image delete.')
+ message = _('Please confirm that you want to transfer all these images to the Trash')
+ alert = self._activity.util.confirmation_alert(message,caption,self.trash_them_cb)
+ alert.from_album_id = from_album_id
+ return
+ if from_album_id == journal_id or from_album_id == trash_id:
+ #issue a no and exit
+ caption = _('Not Permitted')
+ message = _('The "All Pictures" and "Trash" must remain at their current locations.')
+ alert = self._activity.util.alert(message,caption)
+ return
+ else: #only gets here if this is a request to reorder the stacks
+ drop_seq = self.album_rows[to_index]['seq']
+ if from_index < to_index:
+ new_index = drop_seq + 1
else:
- self.accumulation_target = self.album_rows[index]['subcategory']
- self.db.add_image_to_album(self.accumulation_target, jobject_id)
+ new_index = drop_seq - 1
+ self.db.reorder_albums(from_album_id,new_index)
self.refresh_album_rows()
self.paint_albums()
- #guarantee that the next time the thumb nails are painted, the new one is included
- self.album_objects[self.accumulation_target].thumbnail_redo_world = True
- #the drop was on thumbnail side of screen, this is a reorder request
- else:
- #map from thumbnail_surface to thumbnail_world
- start_index = self.album_objects[self.selected_album_id].click(start_x, start_y)
- if self.album_objects[self.selected_album_id].sb:
- (sb_x,sb_y) = self.album_objects[self.selected_album_id].sb.get_scrolled()
- else:
- (sb_x,sb_y) = [0,0]
- xy_size = self.album_objects[self.selected_album_id].xy_size
- pict_per_row = self.album_objects[self.selected_album_id].pict_per_row
- thumb_index = int(((drop_y + sb_y) // xy_size) * pict_per_row + \
- math.floor(drop_x - album_column_width) // xy_size)
- if thumb_index > len(self.album_objects[self.selected_album_id].rows) - 1:
- thumb_index =len(self.album_objects[self.selected_album_id].rows) - 1
- _logger.debug('insert after %s this image at index:%s'%(thumb_index,start_index,))
- self.album_objects[self.selected_album_id].insert_after(thumb_index,start_index)
+ def trash_them_cb(self,alert,confirmation):
+ if not confirmation == gtk.RESPONSE_OK: return
+ #go ahead and move them to the trash, then display trash
+ self.db.change_album_id(alert.from_album_id,trash_id)
+ self.delete_album(alert.from_album_id,trash_id)
+
+ def drag_thumbnail_onto_album(self,start_x,start_y,drop_x,drop_y):
+ jobject_id = self.album_objects[self.selected_album_id].get_jobject_id_at_xy(start_x,start_y)
+ index = self.get_album_index_at_xy(drop_x, drop_y)
+ if index == -1 or not jobject_id:
+ _logger.debug('drop_image lookup failure index:%s jobject_id:%s '%(index,jobject_id,))
+ return
+ current_album_id = self.get_current_album_identifier()
+ _logger.debug('drop_image index:%s jobject_id:%s current_album_id:%s'%(index,jobject_id,current_album_id,))
+
+ #if item dragged from trash, put it back in journal_id as well as target stack
+ if current_album_id == trash_id:
+ self.db.delete_image(trash_id,jobject_id)
+ self.album_objects[current_album_id].paint(True)
+ #unconditionally add the thumbnail back into the list of journal pictures
+ self.db.add_image_to_album(journal_id, jobject_id)
+ self.album_objects[journal_id].thumbnail_redo_world = True
+ if current_album_id != journal_id: #add it to the target also
+ if index > len(self.album_rows)-1:
+ self.create_new_album(self.default_name)
+ self.db.add_image_to_album(self.accumulation_target, jobject_id)
+ self.album_objects[self.accumulation_target].set_top_image(jobject_id)
+ else:
+ self.db.add_image_to_album(self.album_rows[index]['subcategory'], jobject_id)
+ #guarantee full repaint of thumbnails on next repaint
+ self.album_objects[trash_id].thumbnail_redo_world = True
+ self.refresh_album_rows()
+ self.paint_albums()
+ return
+
+ #if dropped on the trash icon
+ if self.get_album_id_at_index(index) == trash_id: #a request to delete
+ self.db.delete_image(current_album_id,jobject_id)
+ self.album_objects[current_album_id].paint(True)
+ if current_album_id == journal_id:
+ self.db.add_image_to_album(self.album_rows[index]['subcategory'], jobject_id)
+ self.refresh_album_rows()
+ self.paint_albums()
+ return
+
+ #if index is larger than max, we want a new album
+ _logger.debug('index:%s length of rows:%s jobject_id %s'%(index,len(self.album_rows),jobject_id,))
+ if index > len(self.album_rows)-1:
+ self.create_new_album(self.default_name)
+ self.db.add_image_to_album(self.accumulation_target, jobject_id)
+ self.album_objects[self.accumulation_target].set_top_image(jobject_id)
+ else:
+ self.accumulation_target = self.album_rows[index]['subcategory']
+ self.db.add_image_to_album(self.accumulation_target, jobject_id)
+ self.refresh_album_rows()
+ self.paint_albums()
+ #guarantee that the next time the thumb nails are painted, the new one is included
+ self.album_objects[self.accumulation_target].thumbnail_redo_world = True
+
def rotate_selected_album_thumbnail_left_90(self):
album_object = self.album_objects.get(self.selected_album_id)
thumb_object = None
@@ -1207,7 +1236,31 @@ class DisplayAlbums():
if thumb_object:
thumb_object.rotate_thumbnail_left_90(album_object.get_selected_jobject_id())
album_object.repaint()
-
+
+ def page_up(self):
+ _logger.debug('page up albums')
+ if not self.album_sb: return
+ x,y = self.album_sb.get_scrolled()
+ num_rows = screen_h // self.album_height
+ actual_rows = len(self.album_rows)
+ scroll_pixels = (num_rows -1) * self.album_height
+ if scroll_pixels > (actual_rows - 1) * self.album_height:
+ scroll_pixels = (actual_rows - 1) * self.album_height
+ self.album_sb.scroll(scroll_pixels * self.album_sb.ratio)
+ _logger.debug('actual_rows:%s scroll_pixels:%s'%(num_rows,scroll_pixels,))
+ self.paint_albums()
+
+ def page_down(self):
+ _logger.debug('page down albums')
+ if not self.album_sb: return
+ x,y = self.album_sb.get_scrolled()
+ num_rows = screen_h // self.album_height
+ scroll_pixels = (num_rows -1) * self.album_height
+ if scroll_pixels > y:
+ scroll_pixels = y
+ self.album_sb.scroll(-scroll_pixels * self.album_sb.ratio)
+ self.paint_albums()
+
##################### ALERT ROUTINES ##################################
class Utilities():
def __init__(self,activity):
@@ -1299,7 +1352,7 @@ class Application():
number_of_pictures = self.db.get_thumbnail_count(journal_id)
if number_of_pictures < 10:
_logger.error('failed to initalize the datastore with at least 10 pictures')
- exit(0)
+ #exit(0)
def change_album_name(self,name):
if self.album_collection:
@@ -1359,10 +1412,11 @@ class Application():
#if the picture table is empty, populate it from the journal, and initialize
#fix for bug 2223 loads images repeatedly into journal
- start_images_loaded = self.db.get_config('image_init')
+ start_images_loaded = self.db.get_lookup('image_init')
+ _logger.debug('config image_init:%s'%(start_images_loaded,))
if ds_count < 10 and start_images_loaded == '':
+ self.db.set_lookup('image_init','True')
self.first_run_setup()
- self.db.set_config('image_init','True')
self.album_collection = DisplayAlbums(self.db, self._activity)
self.album_collection.paint_albums()
@@ -1417,9 +1471,9 @@ class Application():
self.ds_sql = Datastore_SQLite(self.db)
screen = pygame.display.get_surface()
- info = pygame.display.Info()
- screen_w = info.current_w
- screen_h = info.current_h
+ #info = pygame.display.Info()
+ #screen_w = info.current_w
+ screen_w,screen_h = screen.get_size()
_logger.debug('startup screen sizes w:%s h:%s '%(screen_w,screen_h,))
# Clear Display
@@ -1535,7 +1589,8 @@ class Application():
#self._activity._pygamecanvas._socket.window.set_cursor(None)
self._activity.window.set_cursor(gtk.gdk.Cursor(gtk.gdk.PLUS))
elif x > album_column_width:
- self._activity.window.set_cursor(None)
+ #self._activity.window.set_cursor(None)
+ self.album_collection.set_hand_cursor()
def drop(self,event):
@@ -1586,6 +1641,16 @@ class Application():
in_grab = False
else:
self.album_collection.album_objects[self.album_collection.selected_album_id].click(x,y)
+ elif x < album_column_width and x > album_column_width -thick:
+ if y < self.album_collection.album_sb.knob.top:
+ self.album_collection.page_down()
+ elif y > self.album_collection.album_sb.knob.bottom:
+ self.album_collection.page_up()
+ elif x > screen_w -thick:
+ if y < self.album_collection.album_objects[self.album_collection.selected_album_id].sb.knob.top:
+ self.album_collection.album_objects[self.album_collection.selected_album_id].page_down()
+ elif y > self.album_collection.album_objects[self.album_collection.selected_album_id].sb.knob.bottom:
+ self.album_collection.album_objects[self.album_collection.selected_album_id].page_up()
pygame.display.flip()
def process_mouse_double_click(self,event):
diff --git a/ezscroll/ezscroll.py b/ezscroll/ezscroll.py
index 338b643..ad5cf00 100644
--- a/ezscroll/ezscroll.py
+++ b/ezscroll/ezscroll.py
@@ -121,8 +121,9 @@ class ScrollPane():
""" Called by end user to get the scroll pane results """
return self.pane
+#class ScrollBar(pygame.sprite.DirtySprite):
-class ScrollBar(pygame.sprite.DirtySprite):
+class ScrollBar(pygame.sprite.Sprite):
""" Same interface as sprite.Group.
Get result of update() in pixels scrolled, from get_scrolled()
"""
diff --git a/help/xophoto.html b/help/xophoto.html
index 7b1edd7..273bb84 100644
--- a/help/xophoto.html
+++ b/help/xophoto.html
@@ -48,14 +48,15 @@ Images can be removed from the stack by dragging them to the trash. When items
<ol>
<li>Open the Terminal Activity</li>
<li>Type the characters &quot;cd&quot; and hit the enter key. </li>
- <li>Type the characters &quot;./A&quot; and hit the &lt;tab&gt; key. The auto-completion will complete the &quot;./Activities/&quot; pathname for you. Then type &quot;X&quot;, &lt;tab&gt;. The path will become &quot;./Activities/XoPhoto.activity/&quot;. Then type 'm',&lt;tab&gt;. The auto-completed path will be &quot;./Activities/XoPhoto.activity/mkdir_Pictures&quot;. Then hit enter, and a short program will be executed which will properly set up your &quot;Pictures&quot; folder. </li>
+ <li>Type the characters &quot;A&quot; and hit the &lt;tab&gt; key. The auto-completion will complete the &quot;Activities/&quot; pathname for you. Then type &quot;X&quot;, &lt;tab&gt;. The path will become &quot;Activities/XoPhoto.activity/&quot;. Then type 'm',&lt;tab&gt;. The auto-completed path will be &quot;Activities/XoPhoto.activity/mkdir_Pictures&quot;. Then hit enter, and a short program will be executed which will properly set up your &quot;Pictures&quot; folder. </li>
</ol>
<p>Thereafter, whenever you export a stack of pictures, by default they will be placed in the &quot;/home/olpc/Pictures&quot; folder, and they will be available from to the Browse Activity for upload .</p>
<h3>How to Help Improve XoPhoto . . .Report Bugs </h3>
-<p>You can help make XoPhoto better by reporting the Bugs, and suggestions you have for improvement. Fill out a Bug Report at </p>
-<p><a href="http://bugs.sugarlabs.org/newticket?component=XoPhoto>
-http://bugs.sugarlabs.org/newticket?component=XoPhoto</a>
-<p>or send email with &quot;XoPhoto Bug&quot; in the title to &quot;georgejhunt@gmail.com&quot; describing the problem, and the software Build number from &quot;My Settings&quot; -- &quot;About My Computer&quot; (described in next paragraph). </p>
+<p>See what bugs have already been reported at: <a href="http://bugs.sugarlabs.org/query?status=accepted&amp;status=assigned&amp;status=new&amp;status=reopened&amp;component=XoPhoto">SugarLabs Trac</a></p>
+<p>You can see the history, and links to the resources related to XoPhoto at the <a href="http://wiki.laptop.org/go/Xophoto">OLPC wiki</a> </p>
+<p>Leave comments, read archived blog posts about XoPhoto at <a href="http://XoPhoto.wordpress.com">XoPhoto.wordpress.com</a> </p>
+<p>You can help make XoPhoto better by reporting the Bugs, and suggestions you have for improvement. </p>
+<p>Please send email with &quot;XoPhoto Bug&quot; in the title to &quot;georgejhunt@gmail.com&quot; describing the problem, and the software Build number from &quot;My Settings&quot; -- &quot;About My Computer&quot; (described in next paragraph). I will enter your bug into the tracking system, and work to address the bugs in priority order. </p>
<p> The author of XoPhoto will make every effort to provide quick turnaround on fixes, and ensure that the software update activity function provides a convenient means of obtaining the improved activity (From the circle home screen, right click on the XO figure in the center, left click on &quot;My Settings&quot;, left click on &quot;softare update&quot; -- on a recent build you need to use the horizontal scroll bar to make &quot;software Update&quot; visible). </p>
<p>&nbsp;</p>
<p>&nbsp;</p>
diff --git a/photo_toolbar.py b/photo_toolbar.py
index 8795ec8..0ee0fba 100644
--- a/photo_toolbar.py
+++ b/photo_toolbar.py
@@ -111,20 +111,22 @@ class ActivityToolbar(gtk.Toolbar):
self.insert(separator, -1)
separator.show()
- self.keep = ToolButton(tooltip=_('Save and Start New'))
+ self.keep = ToolButton()
+ self.keep.set_tooltip(_('Save and Start New'))
#client = gconf.client_get_default()
#color = XoColor(client.get_string('/desktop/sugar/user/color'))
#keep_icon = Icon(icon_name='document-save', xo_color=color)
keep_icon = Icon(icon_name='document-save')
keep_icon.show()
self.keep.set_icon_widget(keep_icon)
- self.keep.props.accelerator = '<Ctrl>S'
+ #self.keep.props.accelerator = '<Ctrl>S'
self.keep.connect('clicked', self.__keep_clicked_cb)
self.insert(self.keep, -1)
self.keep.show()
- self.stop = ToolButton('activity-stop', tooltip=_('Stop'))
- self.stop.props.accelerator = '<Ctrl>Q'
+ self.stop = ToolButton('activity-stop')
+ self.stop.set_tooltip(_('Stop'))
+ #self.stop.props.accelerator = '<Ctrl>Q'
self.stop.connect('clicked', self.__stop_clicked_cb)
self.insert(self.stop, -1)
self.stop.show()
@@ -183,8 +185,8 @@ class ActivityToolbar(gtk.Toolbar):
def __title_changed_cb(self, entry):
if not self._update_title_sid:
- self._update_title_sid = gobject.timeout_add_seconds(
- 1, self.__update_title_cb)
+ self._update_title_sid = gobject.timeout_add(
+ 1000, self.__update_title_cb)
def __update_title_cb(self, entry=None):
title = self.title.get_text()
diff --git a/sinks.py b/sinks.py
index eacdd6d..3e13cbe 100644
--- a/sinks.py
+++ b/sinks.py
@@ -237,7 +237,7 @@ class ViewSlides():
class ExportAlbum():
- def __init__(self,parent,rows,db,base_path,path):
+ def __init__(self,parent,rows,db,base_path,path,name):
"""inputs =rows is an array or records from table xophoto.sqlite.groups
=db is a class object which has functions for reading database
=path is writeable path indicating location for new exported images
@@ -249,9 +249,10 @@ class ExportAlbum():
self.base_path = base_path
self.path = path
self.album = str(path.split('/')[-1])
+ self.name = name
def do_export(self):
-
+ disable_write = False
if not os.path.isdir(self.path):
try:
os.makedirs(self.path)
@@ -268,13 +269,11 @@ class ExportAlbum():
fd.close()
os.unlink(fn)
except Exception, e:
+ disable_write = True
_logger.debug('attempting to write pictures exception %s'%e)
- self._parent.game.util.alert(_('Write permission not set for path ')+
- '%s'%self.base_path+
- _(' Please see help for iinstructions to correct this problem.'),
- _('Cannot Write Pictures'))
- return
index = 1
+ lookup = {'image/png':'.png','image/jpg':'.jpg','image/jpeg':'.jpg','image/gif':'.gif','image/tif':'.tif'}
+ ok_exts = ['png','jpg','jpeg','jpe','gif','tif',]
for row in self.rows:
jobject_id = row['jobject_id']
ds_object = datastore.get(jobject_id)
@@ -283,8 +282,8 @@ class ExportAlbum():
#if the picture was deleted from the datastore, we'll just ignore the error
continue
fn = ds_object.get_file_path()
- mime_type = self.db.get_mime_type(jobject_id)
- lookup = {'image/png':'.png','image/jpg':'.jpg','image/jpeg':'.jpg','image/gif':'.gif','image/tif':'.tif'}
+ #mime_type = self.db.get_mime_type(jobject_id)
+ mime_type = ds_object.metadata.get('mime_type','')
#base = os.path.basename(fn).split('.')
base = self._parent.DbAccess_object.get_title_in_picture(jobject_id)
title = self._parent.DbAccess_object.get_title_in_picture(jobject_id)
@@ -295,14 +294,14 @@ class ExportAlbum():
base = base + lookup.get(mime_type,'')
base = base.replace(' ','_')
else:
- base = os.path.basename(fn)
- base = base + lookup.get(mime_type,'')
+ base = self.name + lookup.get(mime_type,'')
base = '%03d'%index +'_' + base
_logger.debug('exporting %s to %s'%(fn,os.path.join(self.path,base),))
- shutil.copy(fn,os.path.join(self.path,base))
+ if not disable_write:
+ shutil.copy(fn,os.path.join(self.path,base))
ds_object.destroy()
- #now update the metadata associated with this pictures
+ #now update the metadata associated with this picture
ds_object = datastore.get(jobject_id)
md = ds_object.get_metadata()
if md:
@@ -314,12 +313,17 @@ class ExportAlbum():
if len(tag) == 0:
md['tags'] = self.album
else:
- md['tags'] = md['tags'] + ', ' +self.album
+ if tag.find(self.album) < 0:
+ md['tags'] = md['tags'] + ', ' +self.album
try:
datastore.write(ds_object)
except Exception, e:
_logger.debug('datastore write exception %s'%e)
ds_object.destroy()
index += 1
-
+ if disable_write:
+ self._parent.game.util.alert(_('Write permission not set for path ')+
+ '%s'%self.base_path+
+ _(' Please see help for instructions to correct this problem.'),
+ _('Cannot Write Pictures'))
diff --git a/sources.py b/sources.py
index a14412a..c1e0e95 100644
--- a/sources.py
+++ b/sources.py
@@ -39,6 +39,8 @@ from dbphoto import *
import display
#pick up activity globals
from xophotoactivity import *
+import xophotoactivity
+
import logging
@@ -91,13 +93,39 @@ class Datastore_SQLite():
"""scans the journal for pictures that are not in database, records jobject_id if found in
table groups with the journal id in category. Can be faster because we don't have to fetch file itself.
"""
+ ds_list = []
+ num_found = 0
mime_list = ['image/jpg','image/png','image/jpeg','image/gif',]
- (results,count) = datastore.find({'mime_type':mime_list})
- _logger.debug('Journal/datastore entries found:%s'%count)
+
+ #build 650 doesn't seem to understand correctly the dictionary with a list right hand side
+ info = xophotoactivity.sugar_version()
+ if len(info)>0:
+ (major,minor,micro,release) = info
+ _logger.debug('sugar version major:%s minor:%s micro:%s release:%s'%info)
+ else:
+ _logger.debug('sugar version failure')
+ minor = 70
+ if minor > 80:
+ (results,count) = datastore.find({'mime_type': ['image/jpeg','image/jpg', 'image/png','image/gif']})
+ else:
+ (results,count) = datastore.find({'mime_type': 'image/jpeg'})
+ ds_list.extend(results)
+ num_found += count
+ (results,count) = datastore.find({'mime_type': 'image/jpg'})
+ ds_list.extend(results)
+ num_found += count
+ (results,count) = datastore.find({'mime_type': 'image/png'})
+ ds_list.extend(results)
+ num_found += count
+ (results,count) = datastore.find({'mime_type': 'image/gif'})
+ ds_list.extend(results)
+ num_found += count
+
+ _logger.debug('Journal/datastore entries found:%s'%num_found)
added = 0
a_row_found = False
cursor = self.db.connection().cursor()
- for ds in results:
+ for ds in ds_list:
#at least for now assume that the newest images are returned first
if not a_row_found:
dict = ds.get_metadata().get_dictionary()
@@ -111,11 +139,12 @@ class Datastore_SQLite():
self.db.add_image_to_album(display.journal_id,ds.object_id)
added += 1
else: #assume that pictures are returned in last in first out order
+ #no longer true since we are getting each mime_type separately (build 650 kludge)
#a_row_found = True
pass
ds.destroy()
_logger.debug('scan found %s. Added %s datastore object ids from datastore to picture'%(count,added,))
- return (count,added,)
+ return (num_found,added,)
def make_one_thumbnail(self):
if not self.db.is_open(): return
@@ -178,40 +207,50 @@ class FileTree():
self._activity = activity
self.dialog = None
- def get_path(self):
+ def get_path(self,get_dir=False):
_logger.debug('dialog to get user path for importing into journal')
- if not self.dialog:
- self.dialog = gtk.FileChooserDialog("Select Folder..",
+ path = '/home/olpc/Pictures'
+ try:
+ os.makedirs(path)
+ except:
+ pass
+ if get_dir:
+ action = gtk.FILE_CHOOSER_ACTION_SELECT_FOLDER
+ else:
+ action = gtk.FILE_CHOOSER_ACTION_OPEN
+ dialog = gtk.FileChooserDialog("Select Folder..",
None,
- gtk.FILE_CHOOSER_ACTION_SELECT_FOLDER,
+ action,
(gtk.STOCK_CANCEL, gtk.RESPONSE_CANCEL,
gtk.STOCK_OPEN, gtk.RESPONSE_OK))
- else:
- self.dialog.show_all()
- self.dialog.set_default_response(gtk.RESPONSE_OK)
- #self.dialog.set_current_folder(os.path.dirname(self.last_filename))
- self.dialog.set_current_folder('/home/olpc/Pictures')
+ dialog.set_default_response(gtk.RESPONSE_OK)
+ #dialog.set_current_folder(os.path.dirname(self.last_filename))
+ dialog.set_current_folder('/home/olpc/Pictures')
+ dialog.set_select_multiple(True)
filter = gtk.FileFilter()
filter.set_name("All files")
filter.add_pattern("*")
- self.dialog.add_filter(filter)
+ dialog.add_filter(filter)
filter = gtk.FileFilter()
filter.set_name("Pictures")
filter.add_pattern("*.png,*.jpg,*jpeg,*.gif")
- self.dialog.add_filter(filter)
+ dialog.add_filter(filter)
- response = self.dialog.run()
+ response = dialog.run()
if response == gtk.RESPONSE_OK:
- _logger.debug('%s selected'%self.dialog.get_filename() )
- fname = self.dialog.get_filename()
+ _logger.debug('%s selected'%dialog.get_filename() )
+ if get_dir:
+ fname = [dialog.get_filename(),]
+ else:
+ fname = dialog.get_filenames()
+ fname = fname
self.last_filename = fname
elif response == gtk.RESPONSE_CANCEL:
- fname = None
+ fname = []
_logger.debug( 'File chooseer closed, no files selected')
- self.dialog.hide_all()
- self.dialog.destroy()
- self.dialog = None
+ dialog.hide()
+ dialog.destroy()
return fname
def _response_cb(self,alert,response):
@@ -221,45 +260,78 @@ class FileTree():
def copy_tree_to_ds(self,path):
+ _logger.debug('copy tree received path:%s'%(path,))
+ dirlist = os.listdir(path)
+ abs_fn_list = []
+ for fn in dirlist:
+ abs_fn_list.append(os.path.join(path,fn))
+ self.copy_list_to_ds(abs_fn_list)
+
+ def copy_list_to_ds(self,file_list):
+ """receives list of absolute file names to be copied to datastore"""
added = 0
+ reserve_at_least = 50000000L #don't fill the last 50M of the journal
+ #reserve_at_least = 5000000000L #force it to complain for testing
self.cancel = False
proc_start = time.clock()
- dirlist = os.listdir(path)
- num = len(dirlist)
+ if len(file_list) == 0: return
+
+ #is the requested set of images going to fit in the XO datastore?
+ tot = 0.0
+ acceptable_extensions = ['jpg','jpeg','png','gif']
+ for filename in file_list:
+ chunks = filename.split('.')
+ ext = ''
+ if len(chunks)>1:
+ ext = chunks[-1]
+ if ext in acceptable_extensions:
+ file_info = os.stat(filename)
+ tot += file_info.st_size
+ info = os.statvfs(file_list[0])
+ free_space = info.f_bsize * info.f_bavail
+ #does it fit?
+ if tot > free_space - reserve_at_least: #don't fill the last 50M of the journal
+ message1 = _('Selected images total ')
+ message2 = _(' Megabytes but available memory is only ')
+ message = message1 + '%.2f'%(tot / 1000000) + message2 + str((free_space - reserve_at_least) // 1000000)
+ title = _('Please select a smaller number of images for import.')
+ alert = self._activity.util.alert(msg=message,title=title)
+ self._activity.add_alert(alert)
+ _logger.debug('total free space message:%s free space:%d tot:%d'%(message,free_space,tot,))
+ return
+ num = len(file_list)
message = _('Number of images to copy to the XO Journal: ') + str(num)
pa_title = _('Please be patient')
alert = display.ProgressAlert(msg=message,title=pa_title)
self._activity.add_alert(alert)
alert.connect('response',self._response_cb)
- for filename in dirlist:
+ for filename in file_list:
start = time.clock()
- abspath = os.path.join(path, filename)
- #print abs_path
mtype = ''
- chunks = abspath.split('.')
+ chunks = filename.split('.')
if len(chunks)>1:
ext = chunks[-1]
if ext == 'jpg' or ext == 'jpeg':
- mtype = 'image/jpg'
+ mtype = 'image/jpeg'
elif ext == 'gif':
mtype = 'image/gif'
elif ext == 'png':
mtype = 'image/png'
if mtype == '': continue
- info = os.stat(abspath)
+ info = os.stat(filename)
size = info.st_size
- #if path and size are equal to image already loaded, abort
- if self.db.check_in_ds(abspath,size): continue
+ #if the md5_sum is already in ds, abort
+ if self.db.is_picture_in_ds(filename): continue
ds = datastore.create()
- ds.metadata['filename'] = abspath
- ds.metadata['title'] = filename
+ ds.metadata['filename'] = filename
+ ds.metadata['title'] = os.path.basename(filename)
ds.metadata['mime_type'] = mtype
- dest = os.path.join(os.environ['SUGAR_ACTIVITY_ROOT'],'instance',filename)
- shutil.copyfile(abspath,dest)
+ dest = os.path.join(os.environ['SUGAR_ACTIVITY_ROOT'],'instance',os.path.basename(filename))
+ shutil.copyfile(filename,dest)
ds.set_file_path(dest)
datastore.write(ds,transfer_ownership=True)
- self.db.create_picture_record(ds.object_id,abspath)
+ self.db.create_picture_record(ds.object_id,filename)
ds.destroy()
_logger.debug('writing one image to datastore took %f seconds'%(time.clock()-start))
added += 1
@@ -268,17 +340,9 @@ class FileTree():
gtk.main_iteration()
if self.cancel:
break
-
_logger.debug('writing all images to datastore took %f seconds'%(time.clock()-proc_start))
self._activity.remove_alert(alert)
return added
-
- def fill_ds(self):
- path = self.get_path()
- if path:
- return self.copy_tree_to_ds(path)
-
-
if __name__ == '__main__':
db = DbAccess('/home/olpc/.sugar/default/org.laptop.XoPhoto/data/xophoto.sqlite')
diff --git a/xophoto.sqlite.template b/xophoto.sqlite.template
index 11c5721..b7c059d 100644
--- a/xophoto.sqlite.template
+++ b/xophoto.sqlite.template
Binary files differ
diff --git a/xophotoactivity.py b/xophotoactivity.py
index a6f2a3e..e4ad2c5 100644
--- a/xophotoactivity.py
+++ b/xophotoactivity.py
@@ -58,14 +58,11 @@ import dbphoto
#help interface
HOME = os.path.join(activity.get_bundle_path(), 'help/XO_Introduction.html')
-#HOME = "http://website.com/something.html"
HELP_TAB = 3
-
-#Application Globals
+#Application Globals --see global functions at the end of this file
album_column_width = 200
startup_clock = time.clock()
-#db can be resumed, new instance, or recovering from db error
import logging
_logger = logging.getLogger('xophoto')
@@ -89,7 +86,7 @@ class XoPhotoActivity(activity.Activity):
self.game = None
self.kept_once = False
self.util = Utilities(self)
- self.db_sanity_check = False
+ self.db_sanity_checked = False
gobject.GObject.__init__(self)
self.jobject_id = None
@@ -268,7 +265,7 @@ class XoPhotoActivity(activity.Activity):
number_of_references += 1
if number_of_references > 0:
number = str(number_of_references)
- detail = _('These images are used in ') + number + _(' other stacks and will be deleted from them also.')
+ detail = _('These images are selected ') + number + _(' times in other stacks and will be deleted from them also.')
else:
detail = _('Are you sure you want to proceed?')
self.util.confirmation_alert(detail,\
@@ -285,28 +282,25 @@ class XoPhotoActivity(activity.Activity):
if self.game.album_collection:
self.game.album_collection.display_thumbnails(trash_id,new_surface=True)
self.game.album_collection.paint_albums()
+ #following doesn't seem to work, disable it so no unintended consequences
+ #self.DbAccess_object.vacuum_data_cache()
- def command_line(self,cmd, alert_error=False):
- _logger.debug('command_line cmd:%s'%cmd)
- p1 = Popen(cmd,stdout=PIPE, shell=True)
- output = p1.communicate()
- if p1.returncode != 0 :
- _logger.debug('error returned from shell command: %s was %s'%(cmd,output[0]))
- if alert_error: self.util.alert(_('%s Command returned non zero\n'%cmd+output[0]))
- return output[0],p1.returncode
-
-
- def copy(self):
- """processing when the keep icon is pressed"""
- _logger.debug('entered copy which will save and re-init sql database')
- dict = self.get_metadata()
-
+ def compact_db(self):
#compact the database
conn = self.DbAccess_object.connection()
cursor = conn.cursor()
cursor.execute('vacuum')
+
+ def copy(self):
+ """processing when the keep icon is pressed"""
+ _logger.debug('entered copy which will save and re-init sql database')
+
+ self.compact_db()
self.DbAccess_object.closedb()
self.DbAccess_object = None
+
+ dict = self.get_metadata()
+
source = os.path.join(os.environ['SUGAR_ACTIVITY_ROOT'],'data','xophoto.sqlite')
ds = datastore.create()
@@ -326,7 +320,7 @@ class XoPhotoActivity(activity.Activity):
#sanity check forces check of integrity of thumbnails and picture data, causes
# reload of template if sanity check fails
- self.db_sanity_check = False
+ self.db_sanity_checked = False
self.read_file(None,initialize=True)
self.metadata['title'] = default_title
self.activity_toolbar.title.set_text(default_title)
@@ -334,20 +328,34 @@ class XoPhotoActivity(activity.Activity):
self.save()
# Start the game running again.
+ #but don't reload the startup images
+ self.game.db.set_lookup('image_init','True')
self.game.do_startup()
def edit_toolbar_doimport_cb(self, view_toolbar):
if not self.file_tree:
self.file_tree = FileTree(self.game.db,self)
- path = self.file_tree.get_path()
- pygame.display.flip()
- if path:
- self.file_tree.copy_tree_to_ds(path)
- Datastore_SQLite(self.game.db).check_for_recent_images()
-
+ path_list = self.file_tree.get_path()
+ if len(path_list) == 0: return
+
+ if os.path.isdir(path_list[0]):
+ self.file_tree.copy_tree_to_ds(path_list[0])
+ else:
+ self.file_tree.copy_list_to_ds(path_list)
+ Datastore_SQLite(self.game.db).check_for_recent_images()
+ self.game.album_collection.album_objects[journal_id].thumbnail_redo_world = True
+ self.game.album_collection.display_journal()
+
def edit_toolbar_doinitialize_cb(self, view_toolbar):
- #self.empty_trash_cb(None,gtk.RESPONSE_OK,journal_id) this deletes all images in the journal
- self.read_file(None,initialize=True)
+ self.util.confirmation_alert(_('You are about to delete photo data and restart with clean empty Databases?'),_('Caution!'),\
+ self.restart_dbs_cb)
+
+ def restart_dbs_cb(self,alert,response):
+ self.read_file(None,initialize=True,new_data_db=True)
+ self.save()
+ # Start the game running again
+ #self.game.db.set_lookup('image_init','True')
+ self.game.do_startup()
def edit_toolbar_do_rotate_cb(self, view_toolbar):
self.game.album_collection.rotate_selected_album_thumbnail_left_90()
@@ -356,7 +364,7 @@ class XoPhotoActivity(activity.Activity):
def use_toolbar_doexport_cb(self,use_toolbar):
if not self.file_tree:
self.file_tree = FileTree(self.game.db,self)
- base_path = self.file_tree.get_path()
+ base_path = self.file_tree.get_path(True)[0]
#think about writing the whole journal, and the trash (would need to add these to the selectable paths)
pygame.display.flip
@@ -368,22 +376,23 @@ class XoPhotoActivity(activity.Activity):
album_object = self.game.album_collection
album_id = album_object.album_rows[int(album_object.album_index)]['subcategory']
- album_name = album_object.album_rows[int(album_object.album_index)]['jobject_id']
+ album_name = album_object.album_rows[int(album_object.album_index)]['stack_name']
safe_name = album_name.replace(' ','_')
new_path = self.non_conflicting(base_path,safe_name)
_logger.debug('album_id is %s new path:%s'%(album_id,new_path))
sql = """select pict.*, grp.* from data_cache.picture as pict, groups as grp \
- where grp.category = ? and grp.jobject_id = pict.jobject_id"""
+ where grp.category = ? and grp.jobject_id = pict.jobject_id order by grp.seq"""
cursor = album_object.db.con.cursor()
cursor.execute(sql,(album_id,))
rows = cursor.fetchall()
_logger.debug('album to export: %s. Number of pictures found: %s'%(album_name,len(rows),))
#def __init__(self,rows,db,sources,path):
- exporter = ExportAlbum(self,rows,self.game.db,base_path,new_path)
+ exporter = ExportAlbum(self,rows,self.game.db,base_path,new_path,safe_name)
exporter.do_export()
def use_toolbar_do_fullscreen_cb(self,use_toolbar):
+
self.fullscreen()
def use_toolbar_doslideshow_cb(self,use_toolbar):
@@ -435,7 +444,7 @@ class XoPhotoActivity(activity.Activity):
- def read_file(self, file_path, initialize=False):
+ def read_file(self, file_path, initialize=False, new_data_db=False):
_logger.debug('started read_file: %s. make_file flag %s. initialize:%s'%(file_path,self.make_jobject,initialize))
if self.make_jobject or initialize: #make jobject is flag signifying that we are not resuming activity
_logger.debug(' copied template rather than resuming')
@@ -454,7 +463,7 @@ class XoPhotoActivity(activity.Activity):
#now do the same for the thumbnails if they don't already exist
source = os.path.join(os.getcwd(),'data_cache.sqlite.template')
dest = os.path.join(os.environ['SUGAR_ACTIVITY_ROOT'],'data','data_cache.sqlite')
- if not os.path.isfile(dest) or initialize:
+ if not os.path.isfile(dest) or new_data_db:
try:
shutil.copy(source,dest)
except Exception,e:
@@ -483,18 +492,18 @@ class XoPhotoActivity(activity.Activity):
exit()
#if this is the first time the databases are to be used this invocation?
- #if the databases are not well formed, rebuild from a template
- if not self.db_sanity_check:
- self.db_sanity_check = True
+ if not self.db_sanity_checked:
+ self.db_sanity_checked = True
conn = self.DbAccess_object.connection()
+ #if the databases are not well formed, rebuild from a template
c = conn.cursor()
- c.execute('pragma quick_check')
+ c.execute('pragma integrity_check')
rows = c.fetchall()
if len(rows) == 1 and str(rows[0][0]) == 'ok':
#main database is ok
- _logger.debug('xophoto database passes quick_check')
+ _logger.debug('xophoto database passes integrity_check')
else:
- #need to start over with a new template and regenerate the thumbnails
+ #need to start over with a new template
_logger.debug('swapping in template for xophoto.sqlite database')
try:
@@ -505,12 +514,70 @@ class XoPhotoActivity(activity.Activity):
_logger.debug('xophoto template failed to copy error:%s'%e)
exit()
+
+ #if the version of template is different from version in journal, upgrade database
+ #first deal with the xophoto.sqlite db which is stored in the Journal
+ template_path = os.path.join(os.getcwd(),'xophoto.sqlite.template')
+ local_path = os.path.join(os.environ['SUGAR_ACTIVITY_ROOT'],'data','xophoto.sqlite')
+ template_version = self.DbAccess_object.get_user_version(template_path)
+ current_version = self.DbAccess_object.get_user_version(local_path)
+ if template_version > current_version:
+ _logger.debug('upgrading xophoto.sqlite from version %s to %s'%(current_version,template_version))
+ try:
+ new_empty_path = os.path.join(os.environ['SUGAR_ACTIVITY_ROOT'],'data','new_xophoto.sqlite')
+ shutil.copy(template_path,new_empty_path)
+ except Exception,e:
+ _logger.debug('xophoto upgrade template failed to copy error:%s'%e)
+ exit()
+ old_db = os.path.join(os.environ['SUGAR_ACTIVITY_ROOT'],'data','xophoto.sqlite')
+ success = self.DbAccess_object.upgrade_db_copy_data(new_empty_path,old_db)
+ if success: #swap in the updated database
+ if os.path.isfile(old_db):
+ os.unlink(old_db)
+ cmd = 'mv %s %s'%(new_empty_path,old_db)
+ rsp,err = command_line(cmd)
+ if err:
+ _logger.debug('unable to rename:%s response:%s'%(cmd,rsp,))
+ else: #put the new database away for safekeeping
+ self.save()
+
#check the thumbnails
- c.execute('pragma data_cache.quick_check')
+ c.execute('pragma data_cache.integrity_check')
rows = c.fetchall()
if len(rows) == 1 and str(rows[0][0]) == 'ok':
#thumbnails database is ok
- _logger.debug('thumbnail database passes quick_check')
+ _logger.debug('thumbnail database passes integrity_check')
+ #then deal with the possibility that data_cache in data directory needs upgrading
+ template_path = os.path.join(os.getcwd(),'data_cache.sqlite.template')
+ local_path = os.path.join(os.environ['SUGAR_ACTIVITY_ROOT'],'data','data_cache.sqlite')
+ template_version = self.DbAccess_object.get_user_version(template_path)
+ current_version = self.DbAccess_object.get_user_version(local_path)
+ if template_version > current_version:
+ _logger.debug('upgrading data_cache.sqlite from version %s to %s'%(current_version,template_version))
+ try:
+ new_empty_path = os.path.join(os.environ['SUGAR_ACTIVITY_ROOT'],'data','new_data_cache.sqlite')
+ shutil.copy(template_path,new_empty_path)
+ except Exception,e:
+ _logger.debug('xophoto upgrade template failed to copy error:%s'%e)
+ exit()
+ old_db = os.path.join(os.environ['SUGAR_ACTIVITY_ROOT'],'data','data_cache.sqlite')
+ success = self.DbAccess_object.upgrade_db_copy_data(new_empty_path,old_db)
+ if success: #swap in the updated database
+ if os.path.isfile(old_db):
+ os.unlink(old_db)
+ cmd = 'mv %s %s'%(new_empty_path,old_db)
+ rsp,err = command_line(cmd)
+ if err:
+ _logger.debug('unable to rename:%s response:%s'%(cmd,rsp,))
+ self.DbAccess_object.closedb()
+ dest = os.path.join(os.environ['SUGAR_ACTIVITY_ROOT'],'data','xophoto.sqlite')
+ try:
+ self.DbAccess_object = DbAccess(dest)
+ if self.game:
+ self.game.db = self.DbAccess_object
+ except Exception,e:
+ _logger.debug('database failed to re-open after rewrite on data_cache file. error:%s'%e)
+ #exit()
else:
#need to start over with a new template and regenerate the thumbnails
_logger.debug('swapping in template for transforms (thumbnail) database')
@@ -550,7 +617,7 @@ class XoPhotoActivity(activity.Activity):
if os.path.isfile(backup_path):
os.unlink(backup_path)
cmd = 'mv %s %s'%(local_path,backup_path)
- rsp,err = self.command_line(cmd)
+ rsp,err = command_line(cmd)
try: #putting in an empty template makes it easier to make a distributable activity
source = os.path.join(os.getcwd(),'xophoto.sqlite.template')
@@ -655,8 +722,9 @@ class EditToolbar(gtk.Toolbar):
self.insert(separator, -1)
separator.show()
- self.stop = ToolButton('activity-stop', tooltip=_('Stop'))
- self.stop.props.accelerator = '<Ctrl>Q'
+ self.stop = ToolButton('activity-stop')
+ self.stop.set_tooltip(_('Stop'))
+ #self.stop.props.accelerator = '<Ctrl>Q'
self.stop.connect('clicked', self.dostop_cb)
self.insert(self.stop, -1)
self.stop.show()
@@ -666,8 +734,8 @@ class EditToolbar(gtk.Toolbar):
def _title_changed_cb(self,button):
if not self._update_title_sid:
- self._update_title_sid = gobject.timeout_add_seconds(
- 1, self.__update_title_cb)
+ self._update_title_sid = gobject.timeout_add(
+ 1000, self.__update_title_cb)
def __update_title_cb(self):
self._parent.DbAccess_object.set_title_in_picture(self.jobject_id,self.title_entry.get_text())
#the following calls to the Datastore introduce too much latency -- do at time of export
@@ -678,8 +746,8 @@ class EditToolbar(gtk.Toolbar):
def _comment_changed_cb(self,button):
if not self._update_comment_sid:
- self._update_comment_sid = gobject.timeout_add_seconds(
- 1, self.__update_comment_cb)
+ self._update_comment_sid = gobject.timeout_add(
+ 1000, self.__update_comment_cb)
def __update_comment_cb(self):
self._parent.DbAccess_object.set_comment_in_picture(self.jobject_id,self.comment_entry.get_text())
#Datastore_SQLite(self._parent.game.db).update_metadata(\
@@ -832,8 +900,9 @@ class UseToolbar(gtk.Toolbar):
self.insert(separator, -1)
separator.show()
- self.stop = ToolButton('activity-stop', tooltip=_('Stop'))
- self.stop.props.accelerator = '<Ctrl>Q'
+ self.stop = ToolButton('activity-stop')
+ self.stop.set_tooltip(_('Stop'))
+ #self.stop.props.accelerator = '<Ctrl>Q'
self.stop.connect('clicked', self.dostop_cb)
self.insert(self.stop, -1)
self.stop.show()
@@ -903,4 +972,25 @@ class UseToolbar(gtk.Toolbar):
def dostop_cb(self, button):
self.emit('do-stop')
+#global functions
+def command_line(cmd, alert_error=False):
+ _logger.debug('command_line cmd:%s'%cmd)
+ p1 = Popen(cmd,stdout=PIPE, shell=True)
+ output = p1.communicate()
+ if p1.returncode != 0 :
+ _logger.debug('error returned from shell command: %s was %s'%(cmd,output[0]))
+ #if alert_error: self.util.alert(_('%s Command returned non zero\n'%cmd+output[0]))
+ return output[0],p1.returncode
+
+def sugar_version():
+ cmd = 'rpm -q sugar'
+ reply,err = command_line(cmd)
+ if reply and reply.find('sugar') > -1:
+ version = reply.split('-')[1]
+ version_chunks = version.split('.')
+ release_holder = reply.split('-')[2]
+ release = release_holder.split('.')[0]
+ return (int(version_chunks[0]),int(version_chunks[1]),int(version_chunks[2]),int(release),)
+ return ()
+